diff --git a/public/index.html b/public/index.html index 989096f..5dfecfb 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - HPOS Sickel Cell Test Dashboard + HPOS Sickle Cell Test Dashboard diff --git a/src/App.js b/src/App.js index 6ea6a9d..1affe6d 100644 --- a/src/App.js +++ b/src/App.js @@ -81,7 +81,7 @@ function App() {
img -
HPOS Sickel Cell Test Dashboard
+
HPOS Sickle Cell Test Dashboard
img img img diff --git a/src/components/AbsorbanceGraph.js b/src/components/AbsorbanceGraph.js index e8df3ba..9c7488e 100644 --- a/src/components/AbsorbanceGraph.js +++ b/src/components/AbsorbanceGraph.js @@ -52,7 +52,7 @@ class AbsorbanceGraph extends Component { .then(response => response.data.text()) .then((text) => { const testData = d3.csvParse(text.replaceAll('"', '')).filter(x => { - if (x.NM > 350 && x.NM < 600 + if (x.NM > 350 && x.NM < 600 && x.CA !== "-Infinity" && x.CA !== "Infinity" && !isNaN(x.CA)) return x; }); diff --git a/src/components/Devices.js b/src/components/Devices.js index f4ca8bb..6f095d2 100644 --- a/src/components/Devices.js +++ b/src/components/Devices.js @@ -21,6 +21,8 @@ const Devices = () => { const changeDevice = ({ value }) => { setDevice(value); + setTestDate(null); + setCurTest(null); setDays([...new Set(allTests .filter((x) => x.deviceSerialNumber === value) .map(x => moment(x.testTime).format("YYYY-MM-DD")))] diff --git a/src/components/TestAnalytics.js b/src/components/TestAnalytics.js index 44cfbc1..89d83b6 100644 --- a/src/components/TestAnalytics.js +++ b/src/components/TestAnalytics.js @@ -1,12 +1,9 @@ import React, { useEffect, useState } from 'react'; import { query, collection, onSnapshot, getDocs } from 'firebase/firestore'; import * as _ from 'lodash'; -import Dropdown from 'react-dropdown'; import 'react-dropdown/style.css'; import moment from 'moment'; -import Select from 'react-select'; -import DeviceGraph from "./DeviceGraph"; import db from '../firebase'; import './TestAnalytics.css'; import TestAnalyticsGraph from './TestAnalyticsGraph'; @@ -47,49 +44,6 @@ const TestAnalytics = () => { const [curDayTests, setCurDayTests] = useState(null); const [dailyCategoryCounts, setDailyCategoryCounts] = useState(null); - const changeDevice = ({ value }) => { - setDevice(value); - setDays([...new Set(allTests - .filter((x) => { - if (x.deviceSerialNumber === value) return x; - }) - .map(x => moment(x.testTime).format("YYYY-MM-DD")))] - .sort() - .reverse()); - } - - const changeTestDate = ({ value }) => { - setTestDate(value); - setCurTest(null); - setCurTestData(null); - setShowGraph(false); - setTests(allTests - .filter((x) => { - if (x.deviceSerialNumber === device && moment(x.testTime).isBetween(moment(value).startOf('day'), moment(value).endOf('day'))) return x; - }) - .map(x => x._id) - .sort()); - } - - const changeTest = ({ value }) => { - setCurTestData(null); setShowGraph(false); - setCurTest(value); - } - - const showTestGraph = () => { - if (!curTest) alert('Select a test'); - setCurTestData(allTests.find(x => x._id === curTest)); - setShowGraph(true); - } - - const toggleShowGraph = () => { - if (!curTestData) { - showTestGraph(); - } else { - setCurTest(null); setCurTestData(null); - } - } - useEffect(() => { if (!allTests) { const q = query(collection(db, "testData")); diff --git a/src/components/TestAnalyticsGraph.css b/src/components/TestAnalyticsGraph.css index d49aa6b..2d1fcee 100644 --- a/src/components/TestAnalyticsGraph.css +++ b/src/components/TestAnalyticsGraph.css @@ -6,6 +6,7 @@ .tests-count-card { width: 80vw; + height: 80vh; background-color: white; margin: 10px; } @@ -14,4 +15,15 @@ text-align: center; font-weight: 600; padding: 15px; - } \ No newline at end of file + } + + .axis-grid line { + /* stroke: #def; */ + stroke: lightgrey; + /* stroke-opacity: 0.7; */ + shape-rendering: crispEdges; +} + +.bar { + fill: steelblue; +} \ No newline at end of file diff --git a/src/components/TestAnalyticsGraph.js b/src/components/TestAnalyticsGraph.js index aea1865..5d57cea 100644 --- a/src/components/TestAnalyticsGraph.js +++ b/src/components/TestAnalyticsGraph.js @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import * as d3 from 'd3'; import moment from 'moment'; +import { sgg } from 'ml-savitzky-golay-generalized'; import './TestAnalyticsGraph.css'; @@ -10,7 +11,98 @@ class TestAnalyticsGraph extends Component { this.state = { data: this.props.data, show: this.props.show }; } componentDidMount() { - this.drawChart(this.state.data); + // this.drawChart(this.state.data); + this.drawBar(this.state.data); + } + + drawBar(data) { + if (!data) return; + + data.reverse(); + + var margin = { top: 20, right: 20, bottom: 30, left: 50 }, + width = 900 - margin.left - margin.right, + height = 500 - margin.top - margin.bottom; + + var svg = d3.select("#tests-count-graph").append('svg') + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g").attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); + + + var xScale = d3.scaleBand().range([0, width]).padding(0.4), + yScale = d3.scaleLinear().range([height, 0]); + + var g = svg.append("g") + .attr("transform", "translate(" + 10 + "," + 10 + ")"); + + + xScale.domain(data.map(function (d) { return d.date; })); + yScale.domain([0, d3.max(data, function (d) { return d.count; })]); + + + g.append("g") + .attr("transform", "translate(0," + height + ")") + .call(d3.axisBottom(xScale)); + + g.append("g") + .call(d3.axisLeft(yScale).tickFormat(function (d) { + return " " + d; + }).ticks(10)) + .append("text") + .attr("y", 6) + .attr("dy", "0.71em") + .attr("text-anchor", "end") + .text("value"); + + g.selectAll(".bar") + .data(data) + .enter().append("rect") + .attr("class", "bar") + .on("mouseover", (event, d) => { + // console.log("event", event); + d3.select(event.srcElement).attr('class', 'highlight'); + d3.select(event.srcElement).transition() + .duration(400) + .attr('width', xScale.bandwidth() + 5) + .attr("y", function (d) { return yScale(d.count) - 10; }) + .attr("height", function (d) { return height - yScale(d.count) + 10; }); + + g.append("text") + .attr('class', 'val') // add class to text label + .attr('x', function () { + return xScale(d.date); + }) + .attr('y', function () { + return yScale(d.count) - 15; + }) + .text(function () { + return [' ' + d.count]; // Value of the text + }); + }) + .on("mouseout", (event, d) => { + d3.select(event.srcElement).attr('class', 'bar'); + d3.select(event.srcElement) + .transition() // adds animation + .duration(400) + .attr('width', xScale.bandwidth()) + .attr("y", function (d) { return yScale(d.count); }) + .attr("height", function (d) { return height - yScale(d.count); }); + + d3.selectAll('.val') + .remove() + }) + .attr("x", function (d) { return xScale(d.date); }) + .attr("y", function (d) { return yScale(d.count); }) + .attr("width", xScale.bandwidth()) + .transition() + .ease(d3.easeLinear) + .duration(400) + .delay(function (d, i) { + return i * 50; + }) + .attr("height", function (d) { return height - yScale(d.count); }); } drawChart(data) { @@ -19,12 +111,21 @@ class TestAnalyticsGraph extends Component { width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; + const INNER_WIDTH = width;// - margin.left - margin.right; + const INNER_HEIGHT = height - margin.top - margin.bottom; + // set the ranges var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); + const xAxis = d3.axisBottom(x).ticks(10); + const yAxis = d3.axisLeft(y).ticks(10); + const xAxisGrid = d3.axisBottom(x).tickSize(-INNER_HEIGHT).tickFormat('').ticks(10); + const yAxisGrid = d3.axisLeft(y).tickSize(-INNER_WIDTH).tickFormat('').ticks(10); + // define the line var valueline = d3.line() + .curve(d3.curveCatmullRom.alpha(0.5)) // curve .x(function (d) { return x(moment(d.date)); }) .y(function (d) { return y(d.count); }); @@ -40,11 +141,11 @@ class TestAnalyticsGraph extends Component { // derivative: 0, // polynomial: 3, // }; - // const sggResult = sgg(data.map(x => x.CA), (Math.PI * 2) / data.length, options); + // const sggResult = sgg(data.map(x => x.count), (Math.PI * 2) / data.length, options); // data.forEach(function (d, index) { - // d.NM = d.NM; - // d.CA = sggResult[index]; + // d.date = d.date; + // d.count = sggResult[index]; // }); // data.forEach(function (d) { // d.date = d.date; @@ -57,7 +158,7 @@ class TestAnalyticsGraph extends Component { svg.append("path") .data([data]) .attr("class", "line") - .attr("d", valueline); + .attr("d", valueline(data)); svg.append("g") .attr("transform", "translate(0," + height + ")") @@ -71,7 +172,9 @@ class TestAnalyticsGraph extends Component { .text("Test time"); svg.append("g") - .call(d3.axisLeft(y)) + .attr('class', 'y axis-grid') + .call(yAxisGrid) + // .call(d3.axisLeft(y)) .append("text") .attr("transform", "rotate(-90)") .attr("x", -160) @@ -79,6 +182,12 @@ class TestAnalyticsGraph extends Component { .attr("dy", "0.71em") .attr("fill", "#000") .text("Tests Count"); + + svg.append('g') + .attr('class', 'y axis') + .call(yAxis); + + } render() { diff --git a/src/components/TestAnalyticsPie.js b/src/components/TestAnalyticsPie.js index 95726e9..a188b85 100644 --- a/src/components/TestAnalyticsPie.js +++ b/src/components/TestAnalyticsPie.js @@ -41,16 +41,12 @@ class TestAnalyticsPie extends Component { var path = d3.arc() .outerRadius(radius - 10) // .innerRadius(0); - .innerRadius(100); + .innerRadius(130); var label = d3.arc() .outerRadius(radius) .innerRadius(radius - 80); - // d3.csv("browseruse.csv", function(error, data) { - // if (error) { - // throw error; - // } var arc = g.selectAll(".arc") .data(pie(data)) .enter().append("g")