Select test device, date and person to get the detailed graph. Then, refresh or click clear button to render another entry.
Select test device, date and person to get the detailed graph. Then, click show button to render graph.
diff --git a/src/components/TestAnalytics.js b/src/components/TestAnalytics.js
new file mode 100644
index 0000000..945c66a
--- /dev/null
+++ b/src/components/TestAnalytics.js
@@ -0,0 +1,150 @@
+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 './Devices.css';
+import TestAnalyticsGraph from './TestAnalyticsGraph';
+
+const styles = {
+ menuList: (base) => ({
+ ...base,
+
+ "::-webkit-scrollbar": {
+ width: "4px",
+ height: "0px",
+ },
+ "::-webkit-scrollbar-track": {
+ background: "#f1f1f1"
+ },
+ "::-webkit-scrollbar-thumb": {
+ background: "#888"
+ },
+ "::-webkit-scrollbar-thumb:hover": {
+ background: "#555"
+ }
+ })
+};
+
+const TestAnalytics = () => {
+ const [device, setDevice] = useState("Select a device");
+ const [testDate, setTestDate] = useState("Select a test date");
+ const [curTest, setCurTest] = useState("Select a test");
+ const [curTestData, setCurTestData] = useState(null);
+ const [devices, setDevices] = useState([]);
+ const [days, setDays] = useState([]);
+ const [tests, setTests] = useState([]);
+ const [allTests, setAllTests] = useState(null);
+ const [showGraph, setShowGraph] = useState(false);
+ const [dailyResults, setDailyResults] = useState(null);
+
+ const changeDevice = ({ value }) => {
+ console.log(value);
+ // console.log(event);
+
+ 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"));
+
+ const unsub = onSnapshot(q, (querySnapshot) => {
+ const testDevices = new Set();
+ const hposTests = [];
+ querySnapshot.forEach((document) => {
+ const hposTestData = document.data();
+ hposTests.push(hposTestData);
+ const { deviceSerialNumber } = hposTestData;
+ testDevices.add(deviceSerialNumber);
+ // console.log(document.data());
+ });
+ const groups = [...testDevices];
+ // console.log(groups);
+ setDevices(groups);
+ setAllTests(hposTests);
+
+ // let rsult;
+ const dailyCounts = hposTests.reduce(function (result, test) {
+ const day = moment(test.testTime).format("YYYY-MM-DD");
+ if (!result[day]) {
+ result[day] = 0;
+ }
+ result[day]++;
+ return result;
+ }, {});
+
+ setDailyResults(Object.entries(dailyCounts).sort().map(x => {
+ return { date: x[0], count: x[1] };
+ }));
+ const rss = _.groupBy(hposTests, function (test) {
+ return moment(test.testTime).startOf('day').format();
+ });
+ // setDailyResults(rss);
+ });
+
+ return () => unsub;
+ }
+ }, [curTestData, curTest, device, testDate]);
+
+ return (
+
+
Analytics
+ {/*
+
+
+
+
+
+
*/}
+
+ {dailyResults &&
}
+
+ )
+}
+
+export default TestAnalytics;
\ No newline at end of file
diff --git a/src/components/TestAnalyticsGraph.js b/src/components/TestAnalyticsGraph.js
new file mode 100644
index 0000000..ea1d459
--- /dev/null
+++ b/src/components/TestAnalyticsGraph.js
@@ -0,0 +1,97 @@
+import React, { Component } from 'react';
+import * as d3 from 'd3';
+import { sgg } from 'ml-savitzky-golay-generalized';
+import axios from 'axios';
+import moment from 'moment';
+
+import './DeviceGraph.css';
+
+class TestAnalyticsGraph extends Component {
+ constructor(props) {
+ super(props);
+ this.state = { data: this.props.data, show: this.props.show };
+ }
+ componentDidMount() {
+ this.drawChart(this.state.data);
+ }
+
+ drawChart(data) {
+ if (!data) return;
+ var margin = { top: 20, right: 20, bottom: 30, left: 50 },
+ width = 960 - margin.left - margin.right,
+ height = 500 - margin.top - margin.bottom;
+
+ // set the ranges
+ var x = d3.scaleTime().range([0, width]);
+ var y = d3.scaleLinear().range([height, 0]);
+
+ // define the line
+ var valueline = d3.line()
+ .x(function (d) { return x(moment(d.date)); })
+ .y(function (d) { return y(d.count); });
+
+ var svg = d3.select("#result-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 g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+ // const options = {
+ // windowSize: 5,
+ // derivative: 0,
+ // polynomial: 3,
+ // };
+ // const sggResult = sgg(data.map(x => x.CA), (Math.PI * 2) / data.length, options);
+
+ // data.forEach(function (d, index) {
+ // d.NM = d.NM;
+ // d.CA = sggResult[index];
+ // });
+ // data.forEach(function (d) {
+ // d.date = d.date;
+ // d.count = +d.count;
+ // });
+
+ x.domain(d3.extent(data, function (d) { return moment(d.date); }));
+ y.domain([0, d3.max(data, function (d) { return d.count; })]);
+
+ svg.append("path")
+ .data([data])
+ .attr("class", "line")
+ .attr("d", valueline);
+
+ svg.append("g")
+ .attr("transform", "translate(0," + height + ")")
+ .call(d3.axisBottom(x))
+ .append("text")
+ // .attr("transform", "rotate(-90)")
+ .attr("x", 400)
+ .attr("y", 30)
+ .attr("dx", "0.71em")
+ .attr("fill", "#000")
+ .text("Test time");
+
+ svg.append("g")
+ .call(d3.axisLeft(y))
+ .append("text")
+ .attr("transform", "rotate(-90)")
+ .attr("y", 6)
+ .attr("dy", "0.71em")
+ .attr("fill", "#000")
+ .text("Tests Count");
+ }
+
+ render() {
+ return (
+
+ )
+ };
+}
+export default TestAnalyticsGraph;
\ No newline at end of file
diff --git a/src/components/graph_icon.png b/src/components/graph_icon.png
new file mode 100644
index 0000000..766ca64
Binary files /dev/null and b/src/components/graph_icon.png differ
diff --git a/src/index.js b/src/index.js
index 974e4aa..8aba1ee 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,6 +8,7 @@ import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Devices from './components/Devices';
+import TestAnalytics from './components/TestAnalytics';
const router = createBrowserRouter([
{
@@ -21,6 +22,10 @@ const router = createBrowserRouter([
{
path: "/insights",
element:
,
+ },
+ {
+ path: "/analytics",
+ element:
,
}
]);