24 Commits

Author SHA1 Message Date
Mariya Varghese
cbd252b6ad Upload New File 2023-08-08 09:56:12 +00:00
Pritimay Sarkar
31a4b3f278 Merge branch 'Sruthikbs-main-patch-74927' into 'main'
Updated report format

See merge request sminnovations/hpos-web!2
2023-08-08 03:04:33 +00:00
Pritimay Sarkar
f8a8c6a789 make font italic 2023-08-07 14:24:02 +05:30
Pritimay Sarkar
0715733163 update report text 2023-08-07 14:19:50 +05:30
Pritimay Sarkar
aefcecf90f fix two page problem 2023-08-07 14:08:58 +05:30
Pritimay Sarkar
ebd9d23f12 add colors for print 2023-08-07 13:41:09 +05:30
Pritimay Sarkar
e681a4a9bf iadd logo 2023-08-07 13:33:16 +05:30
Pritimay Sarkar
f17408f5ce Merge branch 'main' of https://gitlab.com/sminnovations/hpos-web 2023-08-07 13:13:28 +05:30
Pritimay Sarkar
60a8044041 update to new format 2023-08-07 13:12:36 +05:30
Sruthi K
2d040ff1c8 Updated report format 2023-08-07 06:15:41 +00:00
Pritimay Sarkar
e9da7a7983 Merge branch 'Sruthikbs-main-patch-93603' into 'main'
Update report_template.html

See merge request sminnovations/hpos-web!1
2023-08-07 06:14:13 +00:00
Sruthi K
4579ad914d Update report_template.html 2023-08-07 06:12:08 +00:00
Pritimay Sarkar
b4ab8cbff0 fix css for bar graph and main screen 2023-08-07 09:56:23 +05:30
Pritimay Sarkar
d35510d39a change margins 2023-08-06 15:34:59 +05:30
Pritimay Sarkar
5af8197664 format the template 2023-08-06 15:25:56 +05:30
Pritimay Sarkar
29b7db446e add text content in template 2023-08-06 14:01:52 +05:30
Pritimay Sarkar
ca71641341 add report html template
- change arduino stub output to generate error
2023-08-06 13:55:17 +05:30
Pritimay Sarkar
45aecc658e adjust for ring buffer consideration 2023-08-05 10:38:52 +05:30
Pritimay Sarkar
5524f135ff rename file 2023-08-05 00:29:08 +05:30
Pritimay Sarkar
65f5fdc428 add commands 2023-08-05 00:22:39 +05:30
Pritimay Sarkar
4bf75a2473 add sample flow 2023-08-04 23:57:32 +05:30
Pritimay Sarkar
87e4ca7c9a add dev firmware for hpos app 2023-08-04 23:50:47 +05:30
Pritimay Sarkar
e34a618198 remove scroll in analytics page
- add searchable dropdown in analytics page
2023-08-03 11:51:14 +05:30
Pritimay Sarkar
f9c054c62b calibration 2023-08-03 00:27:26 +05:30
75 changed files with 586 additions and 4713 deletions

View File

@@ -1,18 +1,5 @@
{
"projects": {
"default": "hpos-af3cc"
},
"targets": {
"hpos-af3cc": {
"hosting": {
"dev1": [
"hpos-dev1"
],
"qa": [
"hpos-qa"
]
}
}
},
"etags": {}
}
}
}

4
.gitignore vendored
View File

@@ -27,7 +27,3 @@ yarn-error.log*
src/components/testData.json
public/world-110m.json
public/india-states.json
*.apk
scripts/reports
scripts/data

View File

@@ -14,22 +14,4 @@
http://localhost:3000
### add target (multi site), if not already defined for that site
firebase target:apply hosting dev1 hpos-dev1
### add or change to firebase.json
"target": "dev1",
### run build
npm build
### deploy
firebase deploy --only hosting:dev1
pyinstaller --noconfirm --noconsole -i "smi.ico" --windowed --clean hemocube.py

View File

@@ -1,6 +1,5 @@
{
"hosting": {
"target": "qa",
"public": "build",
"ignore": [
"firebase.json",

View File

@@ -0,0 +1,45 @@
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("SN:182829");
// while (Serial.available() == 0) {} //wait for data available
// String bufferCommand = Serial.readString(); //read until timeout
// bufferCommand.trim(); // remove any \r \n whitespace at the end of the String
// if (bufferCommand == "B") {
// Serial.println("#Start Buffer");
// delay(2000);
// Serial.println("#Buffer Completed");
// }
// while (Serial.available() == 0) {} //wait for data available
// String SampleCommand = Serial.readString(); //read until timeout
// SampleCommand.trim(); // remove any \r \n whitespace at the end of the String
// if (SampleCommand == "S") {
// Serial.println("#Sample Started");
// delay(2000);
// Serial.println("#Sample Completed");
// }
// while (Serial.available() == 0) {} //wait for data available
// String resultCommand = Serial.readString(); //read until timeout
// resultCommand.trim(); // remove any \r \n whitespace at the end of the String
// if (resultCommand == "R") {
// delay(3000);
// Serial.println("RESULT SN 18291 1937.23 2828.11 28211.20 121829.12 REND");
// delay(15000);
// }
while (Serial.available() == 0) {} //wait for data available
String resultCommand = Serial.readString(); //read until timeout
resultCommand.trim(); // remove any \r \n whitespace at the end of the String
if (resultCommand == "B") {
delay(3000);
Serial.println("ERROR 500 REND");
delay(15000);
}
}

3007
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,11 +3,6 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.1",
"@mui/material": "^5.14.2",
"@reduxjs/toolkit": "^1.9.5",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
@@ -19,12 +14,10 @@
"ml-savitzky-golay-generalized": "^4.0.1",
"moment": "^2.29.4",
"react": "^18.2.0",
"react-datepicker": "^4.16.0",
"react-dom": "^18.2.0",
"react-dropdown": "^1.11.0",
"react-flip-numbers": "^3.0.8",
"react-icons": "^4.10.1",
"react-redux": "^8.1.2",
"react-router-dom": "^6.14.2",
"react-scripts": "5.0.1",
"react-select": "^5.7.4",
@@ -56,9 +49,5 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"csv-parser": "^3.0.0",
"html-pdf-node": "^1.0.8"
}
}

Binary file not shown.

64
scripts/UserCollection.py Normal file
View File

@@ -0,0 +1,64 @@
import os
import firebase_admin
from IPython.core.display import Image
from IPython.core.display_functions import display
from firebase_admin import credentials
from firebase_admin import firestore
import pandas as pd
import datetime
print("pritimay")
# Initialize Firebase Admin SDK
cred = credentials.Certificate(r'C:\Users\smila\PycharmProjects\pythonProject\hpos-prod-firebase-adminsdk-bionp-5486f7becd.json') # Replace with your own service account key path
firebase_admin.initialize_app(cred)
# Get a reference to the Firestore database
db = firestore.client()
# Specify the collections
patient_collection = db.collection("patientData")
test_collection = db.collection("testData")
start_date = input("Please enter the start date (yyyy-mm-dd): ")
end_date = input("Please enter the end date (yyyy-mm-dd): ")
query = patient_collection.where("createdAt", ">=", start_date).where("createdAt", "<", end_date)
patient_docs = query.stream()
# Prepare data to store in CSV
data = []
for patient_doc in patient_docs:
patient_data = patient_doc.to_dict()
patient_id = patient_data["_id"]
# Query the document from testData collection based on the common _id
test_docs = test_collection.where("_id", "==", patient_id).stream()
for test_doc in test_docs:
test_data = test_doc.to_dict()
# Combine the data from both collections into a single dictionary
combined_data = {**patient_data, **test_data}
# Skip if the csvPath is not a valid URL
data.append(combined_data)
# Convert the data to a DataFrame
df = pd.DataFrame(data)
print(df.size)
# Save the DataFrame to a CSV file
output_filename = "data.csv"
df.to_csv(output_filename, index=False)
downloads_dir = os.path.join(os.path.expanduser("~"), "Downloads")
output_path = os.path.join(downloads_dir, output_filename)
df.to_csv(output_path, index=False)
print(f"Data saved to '{output_path}'")
# Close the Firebase Admin SDK
firebase_admin.delete_app(firebase_admin.get_app())

BIN
scripts/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,235 @@
<!DOCTYPE html>
<html>
<head>
<style>
@media print {
body {
-webkit-print-color-adjust: exact;
}
}
.person-details-row {
display: flex;
flex-direction: row;
}
.person-details-col {
display: flex;
flex-direction: column;
height: 90px;
width: 50%;
margin: 1px;
border: 0;
}
.test-details-row {
border: 0;
}
.test-details-col {
height: 100px;
}
.test-cell-div {
/* border: 1px solid black; */
border-collapse: collapse;
height: 20px;
padding: 10px;
}
.table-header {
border: 1px solid black;
margin: 0 0 -10px 10px;
background-color: rgb(191, 191, 191);
text-align: center;
justify-content: center;
align-items: center;
display: flex;
font-weight: 600;
height: 50px;
width: 98%;
}
@media print {
.table-header {
background-color: rgb(191, 191, 191) !important;
print-color-adjust: exact;
}
}
@media print {
.vendorListHeading th {
color: white !important;
}
}
.test-method {
font-weight: 200 !important;
color: rgb(191, 191, 191);
}
.result-value {
justify-content: center;
align-items: center;
display: flex;
}
.end-of-report {
display: flex;
align-items: center;
justify-content: center;
}
.logo {
display: flex;
justify-content: center;
align-items: center;
}
table,
th,
td {
border: 1px solid black;
border-collapse: collapse;
}
ul {
list-style-type: none;
/* margin: 0; */
/* padding: 0; */
}
</style>
</head>
<body>
<div>
<div class="logo">
<img src="./logo_nobg.png" width="128" height="128" alt="sickle cell logo" />
</div>
<table style="border: 1px solid black; margin: 10px; width: 98%;">
<tr class="person-details-row">
<td class="person-details-col" style="border-right: 2; width: 70%;">
<div><strong>Name:</strong></div>
<div><strong>Age / Gender:</strong></div>
<div><strong>Sample type:</strong></div>
<div><strong>Family History of Sickle Cell Anemia:</strong></div>
</td>
<td class="person-details-col" style="width: 30%;">
<div><strong>Marital Status:</strong></div>
<div><strong>Test Date:</strong></div>
<div><strong>ABHA ID:</strong></div>
<div><strong>Sample ID:</strong></div>
</td>
</tr>
</table>
</div>
<div style="height: 2px;width: 98%; background-color: rgb(56, 105, 166); margin: 30px 10px 40px 10px;"></div>
<div>
<div class="table-header">POINT OF CARE SICKLE CELL ANEMIA TEST</div>
<table class="test-details-table" style="border: 1px solid black; margin: 10px; width: 98%;">
<tr style="height: 50px;">
<th>
Test Description
</th>
<th>
RESULT
</th>
<th>
REFERENCE RANGES
</th>
</tr>
<tr class="test-details-row">
<td class="test-details-col" style="width: 20%; margin: 10px;">
<div style="margin: 10px;">Sickle Cell
Anemia
<div class="test-method">(Method: HPOS)</div>
</div>
</td>
<td style="width: 30%;">
<div class="result-value">Ra = 0.16</div>
<!-- <div class="test-cell-div">Normal</div>
<div class="test-cell-div">Sickle Cell Trait</div>
<div class="test-cell-div">Sickle Cell Disease</div>
<div class="test-cell-div">Negative Borderline</div>
<div class="test-cell-div">Positive Borderline</div> -->
</td>
<td style="width: 50%;">
<div class="test-cell-div">
< 0.16: Normal (HbA)</div>
<div class="test-cell-div">0.165 0.235: Sickle-cell Trait (HbAS)</div>
<div class="test-cell-div">> 0.24: Sickle-cell Disease (HbSS)</div>
<div class="test-cell-div">0.16-0.165: Inconclusive (Negative Borderline)</div>
<div class="test-cell-div">0.235 0.24: Inconclusive (Positive Borderline)</div>
</td>
<!-- <td style="width: 25%;">
<div class="test-cell-div">Normal</div>
<div class="test-cell-div">Sickle Cell Trait</div>
<div class="test-cell-div">Sickle Cell Disease</div>
<div class="test-cell-div">Recommended for HPLC or Electrophoresis Tests</div>
<div class="test-cell-div">Recommended for HPLC or Electrophore</div>
</td> -->
</tr>
</table>
</div>
<div>
<!-- <div style="font-weight: 600; margin: 10px;">INTERPRETATION:</div> -->
<div style="margin: 10px;">
<strong>Test Principle:</strong> This point of care quantitative diagnostic test for sickle-cell anemia
works on the principle of absorption
spectroscopy. The test helps in differentiating heterozygous/homozygous hemoglobin from normal hemoglobin.
</div>
<div style="margin: 10px;">
<strong>Method:</strong> High Performance Optical Spectroscopy (HPOS) for detection of Sickle cell trait and
sickle cell disease in whole blood capillary blood samples.
</div>
<div style="margin: 10px;">
<strong> Note:</strong>
<!-- <ul>
<li>a. Blood transfusion may have an impact on the test results.</li>
<li>
b. Patients already on sickle-cell medications may impact test results.
</li>
</ul> -->
Borderline cases are reported as inconclusive. It may occur due to several factors such as medication,
transfusion, field conditions and assay process. Further clinical tests are recommended in these cases for
diagnosis.
</div>
<div
style="margin: 80px 10px 20px 50px; width: 88%; display: flex; flex-direction: row; justify-content: space-between; align-items: center;">
<div style="margin: 5px;">DATE:</div>
<div style="margin: 5px;">Hematologist</div>
</div>
<!-- <div style="margin: 30px;">
This report is for the perusal of doctor only. Not for medico legal cases. Clinical correlation is
essential.
Please contact us in case of unexpected result.
</div> -->
<div style="height: 3px;width: 98%; background-color: black;"></div>
<div class="end-of-report">
*** END OF REPORT ***</div>
<div style="display: flex; justify-content: center; align-items: center; flex-direction: column;">
<div style="font-style: italic;">
This is an electronically generated report. Generated at HH:MM hrs on DD-MMM-YYYY.
</div>
<div>
Note: Assay results should be correlated clinically with other clinical findings
</div>
</div>
</div>
</body>
</html>

View File

@@ -50,7 +50,7 @@ body {
display: inline-flex;
flex-direction: row;
font-size: xx-large;
background-color: rgb(215, 192, 252);
background-color: rgb(207, 235, 250);
}
.last-update {
@@ -146,7 +146,7 @@ body {
text-align: center;
align-self: center;
font-size: xx-large;
background-color: rgb(215, 192, 252);
background-color: rgb(207, 235, 250);
}
.nscm-logo {
@@ -206,7 +206,7 @@ body {
text-align: center;
align-self: center;
font-size: xx-large;
background-color: rgb(215, 192, 252);
background-color: rgb(207, 235, 250);
}
}
@@ -253,7 +253,7 @@ body {
text-align: center;
align-self: center;
font-size: xx-large;
background-color: rgb(215, 192, 252);
background-color: rgb(207, 235, 250);
}
.title-text {
@@ -289,7 +289,7 @@ body {
}
.cards-container {
height: 80vh;
height: 90vh;
justify-content: center;
align-items: center;
display: flex;
@@ -307,7 +307,7 @@ body {
text-align: center;
align-self: center;
font-size: xx-large;
background-color: rgb(215, 192, 252);
background-color: rgb(207, 235, 250);
}
.title-text {
@@ -356,7 +356,7 @@ body {
}
.cards-container {
height: 80vh;
height: 90vh;
justify-content: center;
align-items: center;
display: flex;
@@ -372,7 +372,7 @@ body {
.dashboard-title {
text-align: center;
align-self: center;
background-color: rgb(215, 192, 252);
background-color: rgb(207, 235, 250);
}
.title-text {

View File

@@ -2,27 +2,11 @@ import './App.css';
import React, { useEffect, useState } from 'react';
import moment from 'moment';
import { BrowserRouter, Route, Routes } from "react-router-dom";
import MainLayout from "./components/MainLayout";
import { routes } from "./routes";
import { collection, onSnapshot, query } from "firebase/firestore";
import Card from './components/Card';
import db from './firebase';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<MainLayout />}>
{routes}
</Route>
</Routes>
</BrowserRouter>
);
}
function App1() {
const [totalScreened, setTotalScreened] = useState(0);
const [males, setMales] = useState(0);
const [females, setFemales] = useState(0);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,63 +0,0 @@
.button {
background-color: #4CAF50;
/* Green */
border: none;
color: white;
/* padding: 15px 32px; */
padding: 5px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 15px;
}
.file-row {
flex-direction: row;
display: flex;
justify-content: center;
align-items: center;
margin: 1.5rem;
}
.file-label {
margin: 10px;
}
.dates {
flex-direction: row;
display: flex;
justify-content: center;
align-items: center;
margin: 1.5rem;
}
.start-date {
margin: 10px;
}
.end-date {
margin: 10px;
}
.download-row {
flex-direction: row;
display: flex;
justify-content: center;
align-items: center;
margin: 1.5rem;
}
.loading-icon {
animation: rotate 2s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -1,107 +0,0 @@
import React, { useState } from 'react';
import { FaInfoCircle } from "react-icons/fa";
import { AiOutlineLoading } from "react-icons/ai";
import DatePicker from "react-datepicker";
import moment from 'moment';
import axios from 'axios';
import "react-datepicker/dist/react-datepicker.css";
import './Accuracy.css';
function Accuracy({ show }) {
const [showCardInfo, setShowCardInfo] = useState(false);
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const [selectedFile, setSelectedFile] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null); // New state for handling errors
const handleFileChange = (event) => {
setSelectedFile(event.target.files[0]);
};
const fetchFile = async (start_date, end_date) => {
try {
setLoading(true);
setError(null); // Reset error state before making a new request
if (selectedFile) {
const formData = new FormData();
formData.append('file', selectedFile);
const queryString = `start_date=${moment(startDate).format(
'YYYY-MM-DD'
)}&end_date=${moment(endDate).format('YYYY-MM-DD')}`;
const url = `https://asia-south1-hpos-qa.cloudfunctions.net/performance?${queryString}`;
// Make a POST request using the Fetch API
const response = await fetch(url, {
method: 'POST',
body: formData,
headers: {
// Add any necessary headers
// 'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
},
});
// Check if the request was successful (status code 2xx)
if (response.ok) {
// Create a blob from the response data
const blob = await response.blob();
// Create a download link and trigger a click to download the file
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'autoanlysis.xlsx'; // Set the desired file name
document.body.appendChild(link);
link.click();
// Clean up
document.body.removeChild(link);
} else {
console.error('Error downloading file:', response.statusText);
setError('Error downloading file. Please try again.'); // Set error message
}
} else {
setError("Select file from reference device");
}
} catch (error) {
console.error('Error downloading file:', error);
setError('Error downloading file. Please try again.'); // Set error message
} finally {
setLoading(false); // Set loading back to false when the request completes
}
};
return (
<div>
<h1>Auto Analysis</h1>
<div className='file-row'>
<div className='file-label'>Reference file</div>
<input type="file" onChange={handleFileChange} />
</div>
<div className='dates'>
<div className='start-date'>
<DatePicker selected={startDate} onChange={(date) => setStartDate(date)} placeholderText='start date' />
</div>
<div className='end-date'>
<DatePicker selected={endDate} onChange={(date) => setEndDate(date)} placeholderText='end date' />
</div>
</div>
<div className='download-row'>
<div className='button' onClick={() => fetchFile(startDate, endDate)}>
{loading ? (
<AiOutlineLoading className="loading-icon" />
) : (
'Download Excel'
)}
</div>
</div>
{error && <div className="error-message">{error}</div>} {/* Display error message if there's an error */}
</div>
);
}
export default Accuracy;

View File

@@ -1,39 +0,0 @@
import React, { useEffect, useState, useRef } from 'react';
import { query, collection, onSnapshot } from 'firebase/firestore';
import './Alerts.css';
import db from '../firebase';
const Alerts = () => {
const [alerts, setAlerts] = useState([]);
useEffect(() => {
if (!allTests) {
const q = query(collection(db, "alerts"));
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);
});
const groups = [...testDevices];
// setDevices(groups);
setAlerts(hposTests);
});
return () => unsub;
}
}, []);
return (
<div>
{alerts.length}
</div>
)
}
export default Alerts;

View File

@@ -1,4 +0,0 @@
.app-qrcode {
height: 250px;
width: 250px;
}

View File

@@ -1,38 +0,0 @@
import React, { useEffect, useState, useRef } from 'react';
import { query, collection, onSnapshot } from 'firebase/firestore';
import './AppInstallation.css';
import db from '../firebase';
const AppInstallation = () => {
const [alerts, setAlerts] = useState([]);
useEffect(() => {
}, []);
return (
<div>
<h1>App Installation</h1>
<h3>First try the firebase app distribution which is email linked. After adding the email from backend. Check the email to get started.</h3>
{/* <div>
<div>Testing 2.1.27 (Preprod - 07-11-2023)</div>
<a href="https://www.dropbox.com/scl/fi/v0hux8qrby54rjns8mhum/hpos-testing_preprod_2.1.27.apk?rlkey=ias0fe0zq72avy01rk4cvzmvp&dl=1">
<img className="app-qrcode" src={require('../assets/hpos-testing_preprod.png')} alt='icon'></img>
</a>
</div>
<div>
<p>Steps to install:</p>
<p>
<ul>
<li>use an QR code scanner app to download the app</li>
<li>if the phone browser prompts an warning, select download anyway</li>
<li>install the downloaded apk and give permissions</li>
</ul>
</p>
</div> */}
</div>
)
}
export default AppInstallation;

View File

@@ -0,0 +1,9 @@
.page-title {
/* width: 100vw; */
padding: 10px;
text-align: center;
background: rgb(207, 235, 250);
color: black;
font-size: 30px;
font-family: sans-serif;
}

View File

@@ -0,0 +1,78 @@
import React, { useEffect, useState } from 'react';
import { query, collection, onSnapshot } from 'firebase/firestore';
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
import moment from 'moment';
import db from '../firebase';
import './Calibrations.css';
const Calibrations = () => {
const [device, setDevice] = useState("Select a device");
const [devices, setDevices] = useState([]);
const [curDeviceData, setCurDeviceData] = useState(null);
const changeDevice = ({ value }) => {
setDevice(value);
}
const toggleShowDevice = () => {
console.log(devices.filter(x => x.deviceId === device))
setCurDeviceData(devices.find(x => x.deviceId === device));
// if (!device) {
// setCurDeviceData(devices.filter(x => x.deviceId === device));
// } else {
// setDevice(null);
// }
}
useEffect(() => {
if (!devices?.length) {
const q = query(collection(db, "devices"));
const unsub = onSnapshot(q, (querySnapshot) => {
const testDevices = [];
querySnapshot.forEach((document) => {
const deviceData = document.data();
testDevices.push(deviceData);
});
setDevices(testDevices);
});
return () => unsub;
}
}, [device, curDeviceData]);
return (
<div className="calibrations-page">
<div className='page-title'>Calibrations</div>
<div className='dropdown-group'>
<Dropdown className='device-dropdown' options={devices?.map(x => x.deviceId)} onChange={changeDevice} value={device} placeholder="Select an device" />
<button className='clear-button' onClick={toggleShowDevice}><img className="graph-icon" src={require('../assets/graph_icon.png')} alt='icon'></img>{device ? 'Clear' : 'Show'}</button>
</div>
<div className='graph-person-conatiner'>
{curDeviceData && <div className='test-details'>
<table>
<tr>
<th>key</th>
<th>value</th>
</tr>
<tr>
<td>device</td> <td>{curDeviceData?.deviceId} </td>
</tr>
<tr>
<td>calibratedAt</td> <td>{curDeviceData?.calibratedAt} </td>
</tr>
<tr>
<td>coefficients</td> <td>{curDeviceData?.coefficients?.toString()} </td>
</tr>
</table>
</div>}
</div>
</div>
)
}
export default Calibrations;

View File

@@ -1,56 +0,0 @@
import React, { useEffect, useState } from 'react';
import { FaInfoCircle } from "react-icons/fa";
import axios from 'axios';
import './CurveFit.css';
import ImageDownloader from './ImageDownloader';
function CurveFit({ show }) {
const [showCardInfo, setShowCardInfo] = useState(false);
const [svgData, setSvgData] = useState(null);
const fetchSvg = () => {
axios({
method: 'get',
url: "https://asia-south1-hpos-qa.cloudfunctions.net/curvefit",
data: {
"_id": "id",
"underMedication": false,
"underTransfusion": false,
"userImageURL": "https://hpos-prod.web.app/logo.png"
},
withCredentials: false,
secure: false,
headers: {
"Content-Type": "application/json",
// "x-client": "e16a1bd15af2ee640f5a7a18c8d8333f5f01f7c66e003bbc21ee52870f1bce27",
},
})
.then(response => response.data)
.then((data) => {
// setMessage(`${id} is being created`);
// setSvgData(data?.plot?.replace('\"', '').replace('\n', '').replace('"', "'"));
})
.catch((err) => {
console.log(err);
// setMessage(`error: ${err?.message}`);
});
}
useEffect(() => {
if (!svgData) {
// fetchSvg();
}
}, []);
return (
<div>
<h1>Curve Fit</h1>
{/* <img alt='' src={ svgData }/>; */}
<img src="https://asia-south1-hpos-qa.cloudfunctions.net/curvefit" width="450" height="340" />
{/* <ImageDownloader /> */}
</div>
);
}
export default CurveFit;

View File

@@ -2,12 +2,14 @@
display: none;
}
.title {
.insights-title {
/* width: 100vw; */
padding: 10px;
text-align: center;
background: rgb(215, 192, 252);
background: rgb(207, 235, 250);
color: black;
font-size: 30px;
font-family: sans-serif;
}
.device-dropdown {
@@ -79,7 +81,7 @@
background-color: rgb(207, 235, 250);
width: 10vw;
height: 5vh;
margin: 5px;
/* margin: 5px; */
border-radius: 10px;
font-size: medium;
font-weight: 600;

View File

@@ -1,8 +1,9 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { query, collection, onSnapshot } from 'firebase/firestore';
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';
@@ -18,13 +19,17 @@ const Devices = () => {
const [tests, setTests] = useState([]);
const [allTests, setAllTests] = useState(null);
const [showGraph, setShowGraph] = useState(false);
const selectDateDropdownRef = useRef();
const selectTestDropdownRef = useRef();
const changeDevice = ({ value }) => {
setDevice(value);
setTestDate(null);
setCurTest(null);
selectDateDropdownRef.current?.setValue("");
selectTestDropdownRef.current?.setValue("");
setDays([...new Set(allTests
.filter((x) => x.deviceSerialNumber === value || x.deviceId === value)
.filter((x) => x.deviceSerialNumber === value)
.map(x => moment(x.testTime).format("YYYY-MM-DD")))]
.sort()
.reverse());
@@ -32,11 +37,12 @@ const Devices = () => {
const changeTestDate = ({ value }) => {
setTestDate(value);
selectTestDropdownRef.current?.setValue("");
setCurTest(null);
setCurTestData(null);
setShowGraph(false);
setTests(allTests
.filter((x) => (x.deviceSerialNumber === device || x.deviceId === device) && moment(x.testTime).isBetween(moment(value).startOf('day'), moment(value).endOf('day')))
.filter((x) => x.deviceSerialNumber === device && moment(x.testTime).isBetween(moment(value).startOf('day'), moment(value).endOf('day')))
.map(x => x._id)
.sort());
}
@@ -70,13 +76,8 @@ const Devices = () => {
querySnapshot.forEach((document) => {
const hposTestData = document.data();
hposTests.push(hposTestData);
const { deviceType, deviceId, deviceSerialNumber } = hposTestData;
if (deviceType === 'HEMOCUBE' && deviceId) {
testDevices.add(deviceId);
}
if (deviceType !== 'HEMOCUBE' && deviceSerialNumber) {
testDevices.add(deviceSerialNumber);
}
const { deviceSerialNumber } = hposTestData;
testDevices.add(deviceSerialNumber);
});
const groups = [...testDevices];
setDevices(groups);
@@ -89,22 +90,45 @@ const Devices = () => {
return (
<div className="devices-page">
<div className='title'>Insights</div>
<div className='insights-title'>Insights</div>
<div className='dropdown-group'>
<Dropdown className='device-dropdown' options={devices} onChange={changeDevice} value={device} placeholder="Select an device" />
{/* <Dropdown className='device-dropdown' options={devices} onChange={changeDevice} value={device} placeholder="Select an device" />
<Dropdown className='day-dropdown' options={days} onChange={changeTestDate} value={testDate} placeholder="Select an test date" />
<Dropdown className='test-dropdown' options={tests} onChange={changeTest} value={curTest} placeholder="Select an test" />
<Dropdown className='test-dropdown' options={tests} onChange={changeTest} value={curTest} placeholder="Select an test" /> */}
{/* <Select className='device-dropdown' options={devices.map(x => { return { value: x, label: x }; })} menuPlacement='auto' maxMenuHeight={250} menuShouldScrollIntoView={true} onChange={changeDevice} />
<Select className='day-dropdown' options={days.map(x => { return { value: x, label: x }; })} menuPlacement='auto' maxMenuHeight={250} menuShouldScrollIntoView={true} onChange={changeTestDate} />
<Select className='test-dropdown' options={tests.map(x => { return { value: x, label: x }; })} menuPlacement='auto' maxMenuHeight={220} menuShouldScrollIntoView={true} onChange={changeTestDate} /> */}
<Select
className='device-dropdown'
defaultValue={{ label: "Select a device", value: "Select a device" }}
options={devices.map(x => { return { value: x, label: x }; })}
menuPlacement='auto'
maxMenuHeight={250}
menuShouldScrollIntoView={true}
onChange={changeDevice} />
<Select
ref={selectDateDropdownRef}
className='day-dropdown'
defaultValue={{ label: "Select a test date", value: "Select a test date" }}
options={days.map(x => { return { value: x, label: x }; })}
menuPlacement='auto'
maxMenuHeight={250}
menuShouldScrollIntoView={true}
onChange={changeTestDate} />
<Select
ref={selectTestDropdownRef}
className='test-dropdown'
defaultValue={{ label: "Select a test", value: "Select a test" }}
options={tests.map(x => { return { value: x, label: x }; })}
menuPlacement='auto'
maxMenuHeight={220}
menuShouldScrollIntoView={true}
onChange={changeTest} />
<button className='clear-button' onClick={toggleShowGraph}><img className="graph-icon" src={require('../assets/graph_icon.png')} alt='icon'></img>{curTestData ? 'Clear' : 'Show'}</button>
</div>
<div className='graph-person-conatiner'>
{!curTestData && <div className='refresh-graph-message'>Select test device, date and person to get the detailed graph. Then, click show button to render graph.</div>}
{curTestData?.csvPath && <DeviceGraph url={curTestData?.csvPath} show={showGraph} />}
{curTestData?.csvPath && curTestData?.testType !== 'HEMOCUBE' && <DeviceGraph url={curTestData?.csvPath} show={showGraph} />}
{curTestData && <div className='test-details'>
<table>
@@ -127,6 +151,26 @@ const Devices = () => {
<tr>
<td>result</td> <td>{curTestData?.result}</td>
</tr>
{curTestData?.testType === 'HEMOCUBE' && <tr>
<td>calculatedRatio</td>
<td>{curTestData?.calculatedRatio}</td>
</tr>}
{curTestData?.testType === 'HEMOCUBE' && <tr>
<td>deviceRatio</td>
<td>{curTestData?.deviceRatio}</td>
</tr>}
{curTestData?.testType === 'HEMOCUBE' && <tr>
<td>green led Average</td>
<td>{curTestData?.led1Average}</td>
</tr>}
{curTestData?.testType === 'HEMOCUBE' && <tr>
<td>blue led Average</td>
<td>{curTestData?.led2Average}</td>
</tr>}
{curTestData?.testType === 'HEMOCUBE' && <tr>
<td>blue led Average</td>
<td>{curTestData?.led2Average}</td>
</tr>}
</table>
{/* <img src={curTestData?.userImageURL} width={100} height={150} /> */}
</div>}

View File

@@ -1,29 +0,0 @@
.button {
background-color: #4CAF50;
/* Green */
border: none;
color: white;
/* padding: 15px 32px; */
padding: 5px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 15px;
}
.dates {
flex-direction: row;
display: flex;
justify-content: center;
align-items: center;
margin: 1.5rem;
}
.start-date {
margin: 10px;
}
.end-date {
margin: 10px;
}

View File

@@ -1,32 +0,0 @@
import React, { useState } from 'react';
import DatePicker from "react-datepicker";
import moment from 'moment';
import "react-datepicker/dist/react-datepicker.css";
import './ExportData.css';
function ExportData({ url, show }) {
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const fetchFile = (start_date, end_date) => {
window.location.href = `https://asia-south1-hpos-qa.cloudfunctions.net/consolidation?start_date=${moment(startDate).format("YYYY-MM-DD")}&end_date=${moment(endDate).format("YYYY-MM-DD")}`;
}
return (
<div>
<div className='dates'>
<div className='start-date'>
<DatePicker selected={startDate} onChange={(date) => setStartDate(date)} placeholderText='start date' />
</div>
<div className='end-date'>
<DatePicker selected={endDate} onChange={(date) => setEndDate(date)} placeholderText='end date' />
</div>
<div className='button' onClick={() => fetchFile(startDate, endDate)}>Download Excel</div>
</div>
</div>
);
}
export default ExportData;

View File

@@ -1,19 +0,0 @@
.step-title {
font-weight: 600;
margin: 10px;
margin-top: 50px;
}
.help-image {
margin: 10px;
}
.step-description {
margin: 10px;
}
.help-feature {
font-weight: 900;
font-size: 1.5rem;
margin: 10px;
}

View File

@@ -1,128 +0,0 @@
import React, { useState } from 'react';
import Accordion from '@mui/material/Accordion';
import AccordionDetails from '@mui/material/AccordionDetails';
import AccordionSummary from '@mui/material/AccordionSummary';
import Typography from '@mui/material/Typography';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import './Help.css';
function Help({ url, show }) {
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const [expanded, setExpanded] = useState(false);
const handleChange =
(panel) => (event, isExpanded) => {
setExpanded(isExpanded ? panel : false);
};
return (
<div>
<Accordion expanded={expanded === 'panel1'} onChange={handleChange('panel1')}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1bh-content"
id="panel1bh-header"
>
<Typography sx={{ width: '33%', flexShrink: 0 }}>
Auto Analysis
</Typography>
<Typography sx={{ color: 'text.secondary' }}>Auto Analysis of device data with dye and HB</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<div className='step-title'>Step 1:</div>
<div className='step-description'>Click on the start date tab and select the necessary date. Repeat the same process on the end date tab as well.</div>
<img className="help-image" src={require('../assets/auto_analysis_help-1.png')} alt='icon'></img>
<div className='step-title'>Step 2:</div>
<div className='step-description'>Select an end date, it has to be at least one date post the start date.</div>
<img className="help-image" src={require('../assets/auto_analysis_help-2.png')} alt='icon'></img>
<div className='step-title'>Step 3:</div>
<div className='step-description'>Click on the Download Excel button and wait for the document to download.</div>
<img className="help-image" src={require('../assets/auto_analysis_help-3.png')} alt='icon'></img>
</Typography>
</AccordionDetails>
</Accordion>
<Accordion expanded={expanded === 'panel2'} onChange={handleChange('panel2')}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel2bh-content"
id="panel2bh-header"
>
<Typography sx={{ width: '33%', flexShrink: 0 }}>Raw Data</Typography>
<Typography sx={{ color: 'text.secondary' }}>
Download raw data
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<div className='step-title'>Step 1:</div>
<div className='step-description'>Click on the <strong>start date</strong> tab and select the necessary date. Repeat the same process on the <strong>end date</strong> tab as well.</div>
<img className="help-image" src={require('../assets/raw_data_help_1.png')} alt='icon'></img>
<img className="help-image" src={require('../assets/raw_data_help_2.png')} alt='icon'></img>
<div className='step-title'>Step 2:</div>
<div className='step-description'>Click on the <strong>Download Excel</strong> button and wait for the document to download.</div>
<img className="help-image" src={require('../assets/raw_data_help_3.png')} alt='icon'></img>
</Typography>
</AccordionDetails>
</Accordion>
<Accordion expanded={expanded === 'panel3'} onChange={handleChange('panel3')}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel3bh-content"
id="panel3bh-header"
>
<Typography sx={{ width: '33%', flexShrink: 0 }}>
Registration
</Typography>
<Typography sx={{ color: 'text.secondary' }}>
Create and clear entries for taking readings in devices
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<div className='step-title'>Step 1:</div>
<div className='step-description'>Enter the sample or solution name in the <strong>name</strong> tab with appropriate format given below.</div>
<img className="help-image" src={require('../assets/registration_help_1.png')} alt='icon'></img>
<div className='step-title'>Step 2:</div>
<div className='step-description'>Click on the create button to get an ID and you will receive a confirmation message.
If you wish to clear the generated IDs click on the clear button and you will receive a confirmation message.</div>
<img className="help-image" src={require('../assets/raw_data_help_2.png')} alt='icon'></img>
<img className="help-image" src={require('../assets/raw_data_help_3.png')} alt='icon'></img>
</Typography>
</AccordionDetails>
</Accordion>
{/* <Accordion expanded={expanded === 'panel4'} onChange={handleChange('panel4')}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel4bh-content"
id="panel4bh-header"
>
<Typography sx={{ width: '33%', flexShrink: 0 }}>Personal data</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
Nunc vitae orci ultricies, auctor nunc in, volutpat nisl. Integer sit
amet egestas eros, vitae egestas augue. Duis vel est augue.
</Typography>
</AccordionDetails>
</Accordion> */}
</div>
);
}
export default Help;

View File

@@ -1,9 +0,0 @@
import React from 'react';
const HomePage = (props) => {
return (
<div>HomePage</div>
);
};
export default HomePage;

View File

@@ -1,47 +0,0 @@
import React, { useState } from 'react';
const ImageDownloader = () => {
const [imageUrl, setImageUrl] = useState('');
const [imageData, setImageData] = useState(null);
const handleInputChange = (e) => {
setImageUrl(e.target.value);
};
const handleFetchImage = async () => {
try {
const response = await fetch(imageUrl, { mode: 'no-cors'});
const blob = await response.blob();
console.log(blob);
setImageData(URL.createObjectURL(blob));
} catch (error) {
console.error('Error fetching image:', error);
}
};
const handleDownloadImage = () => {
const link = document.createElement('a');
link.href = imageData;
link.download = 'downloaded_image';
link.click();
};
return (
<div>
<label>
Image URL:
<input type="text" value={imageUrl} onChange={handleInputChange} />
</label>
<button onClick={handleFetchImage}>Fetch Image</button>
{imageData && (
<div>
<img src={imageData} alt="Downloadable" style={{ maxWidth: '100%' }} />
<button onClick={handleDownloadImage}>Download Image</button>
</div>
)}
</div>
);
};
export default ImageDownloader;

View File

@@ -1,6 +1,6 @@
.locations-card {
width: 40vw;
height: 45vh;
height: 40vh;
background-color: white;
margin: 10px;
display: flex;
@@ -15,7 +15,7 @@
.locations-graph-card {
width: 40vw;
height: 45vh;
height: 40vh;
background-color: white;
margin: 10px;
border-style: solid;
@@ -27,7 +27,7 @@
.locations-card-title {
text-align: center;
font-weight: 600;
padding-top: 10px;
/* padding-top: 10px; */
display: flex;
align-items: center;
justify-content: center;

View File

@@ -1,38 +0,0 @@
import { Outlet } from "react-router-dom";
import { Box, Toolbar } from "@mui/material";
import colorConfigs from "../configs/colorConfigs";
import sizeConfigs from "../configs/sizeConfigs";
import Sidebar from "./nav/Sidebar";
import Topbar from "./nav/Topbar";
const MainLayout = () => {
return (
<Box sx={{ display: "flex" }}>
<Topbar />
<Box
component="nav"
sx={{
width: sizeConfigs.sidebar.width,
flexShrink: 0
}}
>
<Sidebar />
</Box>
<Box
component="main"
sx={{
flexGrow: 1,
p: 3,
width: `calc(100% - ${sizeConfigs.sidebar.width})`,
minHeight: "100vh",
backgroundColor: colorConfigs.mainBg
}}
>
<Toolbar />
<Outlet />
</Box>
</Box>
);
};
export default MainLayout;

View File

@@ -1,19 +0,0 @@
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { setAppState } from "../store/appStateSlice";
const PageWrapper = (props) => {
const dispatch = useDispatch();
useEffect(() => {
if (props.state) {
dispatch(setAppState(props.state));
}
}, [dispatch, props]);
return (
<>{props.children}</>
);
};
export default PageWrapper;

View File

@@ -1,15 +0,0 @@
import React, { useState } from 'react';
import { FaInfoCircle } from "react-icons/fa";
import './Precision.css';
function Precision({ show }) {
const [showCardInfo, setShowCardInfo] = useState(false);
return (
<div>
<h1>Precision</h1>
</div>
);
}
export default Precision;

View File

@@ -1,32 +0,0 @@
.message {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 80vw;
height: 50px;
background-color: rgb(187, 218, 174);
}
.user-creation {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.clear-users-button {
background-color: #b62200;
border: none;
color: white;
padding: 5px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 5px;
}
.format-text {
color: red;
}

View File

@@ -1,107 +0,0 @@
import React, { useState } from 'react';
import moment from 'moment';
import axios from 'axios';
import './Registration.css';
function Registration() {
const [userId, setUserId] = useState("2808202300103SMIBLR");
const [counter, setCounter] = useState(0);
const [message, setMessage] = useState("");
const [name, setName] = useState("");
const createUser = () => {
setMessage("");
const id = moment().unix() + counter + "SMIBLR";
setCounter(counter + 1);
axios({
method: 'post',
url: "https://asia-south1-hpos-qa.cloudfunctions.net/registerUser",
data: {
"_id": id,
"aadharId": "2808202300001",
"abhaId": "12434949797979",
"birthYear": "2000",
"bloodGroup": "",
"careOf": "",
"caste": "",
"city": "Bengaluru",
"district": "Bengaluru",
"gender": "Male",
"house": "42",
"maritalStatus": "Single",
"name": name ? name : "Web",
"phoneNumber": "9123456789",
"pinCode": "560060",
"registeredBy": "admin",
"registrationCenterName": "SMI",
"sickleCellHistory": null,
"state": "Karnataka",
"subCaste": "",
"testStatus": false,
"incubationTime": moment().subtract('15', 'minutes').utcOffset("+05:30").format("YYYY-MM-DD HH:mm:ss"),
"underMedication": false,
"underTransfusion": false,
"userImageURL": "https://hpos-prod.web.app/logo.png"
},
withCredentials: false,
secure: false,
headers: {
"Content-Type": "application/json",
"x-client": "e16a1bd15af2ee640f5a7a18c8d8333f5f01f7c66e003bbc21ee52870f1bce27",
},
})
.then(response => response.data)
.then((data) => {
setMessage(`${id} is being created`);
})
.catch((err) => {
console.log(err);
setMessage(`error: ${err?.message}`);
});
}
const clearsUsers = () => {
setMessage("");
axios({
method: 'get',
url: "https://asia-south1-hpos-qa.cloudfunctions.net/clearUsers",
withCredentials: false,
secure: false,
headers: {
"Content-Type": "application/json",
// "x-client": "e16a1bd15af2ee640f5a7a18c8d8333f5f01f7c66e003bbc21ee52870f1bce27",
},
})
.then(response => response.data)
.then((data) => {
setMessage(`${data}`);
})
.catch((err) => {
console.log(err);
setMessage(`error: ${err?.message}`);
});
}
const onNameChange = (event) => {
setName(event.target.value);
}
return (
<div className="user-creation">
<label>
name:
<input name="name" value={name} placeholder="350 UMOL KMNO4" onChange={onNameChange} />
</label>
<h5 className='format-text'>Correct formats for automatic analysis are: 100 UMOL KMNO4, 290uM KMnO4, KMnO4-720umol, 350 UMOL TAR, Tar-120umol, HB-13-N-4, HB-11-4</h5>
<div className='dates'>
<div className='button' onClick={() => createUser()}>Create</div>
</div>
<div className='dates'>
<div className='clear-users-button' onClick={() => clearsUsers()}>Clear</div>
</div>
<div>{message && <div className='message'>{message}</div>}</div>
</div>
);
}
export default Registration;

View File

@@ -1,38 +0,0 @@
#samples-table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
}
#samples-table td,
#samples-table th {
border: 1px solid #ddd;
padding: 8px;
width: 10vw;
}
#samples-table td {
width: 15vw;
}
#samples-table tr:nth-child(even) {
background-color: #f2f2f2;
}
#samples-table tr:hover {
background-color: #ddd;
}
#samples-table th {
padding-top: 12px;
padding-bottom: 12px;
text-align: center;
background-color: rgb(144, 183, 255);
color: black;
width: 5vw;
}
.samples-container {
overflow-x: auto;
overflow-y: auto;
}

View File

@@ -1,77 +0,0 @@
import React, { useEffect, useState, useRef } from 'react';
import { query, collection, onSnapshot, where, orderBy, limit } from 'firebase/firestore';
import './Samples.css';
import db from '../firebase';
import moment from 'moment';
const Samples = () => {
const [samples, setSamples] = useState([]);
useEffect(() => {
if (!samples.length) {
const q = query(collection(db, "testData"), where("testTime", ">=", moment().format("YYYY-MM-DD").toString()), orderBy("testTime", "desc"));
const unsub = onSnapshot(q, (querySnapshot) => {
const samples = [];
querySnapshot.forEach((document) => {
console.log(document.data());
const alert = document.data();
samples.push(alert);
});
setSamples(samples);
});
return () => unsub;
}
}, []);
return (
<div>
{/* <div className='samples-heading'><h3>Samples page for denovix useage</h3></div>
{samples.length ? (
samples.map((alert, index) => (
<div key={index} className='samples-card'>
<div tabIndex={index}><strong>{alert._id}</strong></div>
<p className='sample-time'>{alert.testTime}</p>
<p className='sample-time-ago'>{moment(alert.testTime).fromNow()}</p>
</div>
))
) : (
<div className='no-samples-message'>Samples will appear as in when a test is recorded</div>
)} */}
<h1>Samples</h1>
<div className='samples-container'>
<table id="samples-table">
<tr>
<th>Sample Name</th>
<th>Device</th>
<th>Classification</th>
<th>abs1</th>
<th>abs2</th>
<th>abs3</th>
<th>PDR</th>
<th>Time</th>
</tr>
{samples.map((sample, index) => (
<tr key={index} className='samples-table-tr'>
<td>{sample.name}</td>
<td>{sample.deviceId}</td>
<td>{sample.prdClassification || 'NA'}</td>
<td>{sample.abs1}</td>
<td>{sample.abs2 || 'NA'}</td>
<td>{sample.abs3 || 'NA'}</td>
<td>{sample.predictedDenovixRatio}</td>
<td>{sample.testTime}</td>
</tr>
))}
</table>
</div>
</div>
)
}
export default Samples;

View File

@@ -6,7 +6,7 @@
.resultratio-graph-card {
width: 40vw;
height: 45vh;
height: 40vh;
background-color: white;
margin: 10px;
border-style: solid;

View File

@@ -24,7 +24,7 @@ class ScatterPlot extends Component {
if (!data) return;
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 60 },
var margin = { top: 10, right: 30, bottom: 40, left: 60 },
width = 460 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;

View File

@@ -1,17 +1,38 @@
.container {
.analytics-page::-webkit-scrollbar {
display: none;
}
.analytics-title::-webkit-scrollbar {
display: none;
}
.analytics-page {
width: 100vw;
}
.analytics-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 50vh;
font-family: sans-serif;
}
.analytics-title {
padding: 10px;
text-align: center;
background: rgb(207, 235, 250);
color: black;
font-size: 30px;
font-family: sans-serif;
}
@media screen and (min-width: 900px) {
.container {
.analytics-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 50vh;
height: 45vh;
}
}

View File

@@ -82,8 +82,8 @@ const TestAnalytics = () => {
}, [curTestData, curTest, device, testDate]);
return (
<div className="devices-page">
<div className='title'>Analytics</div>
<div className="analytics-page">
<div className='analytics-title'>Analytics</div>
{/* <div className='dropdown-group'>
<Dropdown className='device-dropdown' options={devices} onChange={changeDevice} value={device} placeholder="Select an device" />
<Dropdown className='day-dropdown' options={days} onChange={changeTestDate} value={testDate} placeholder="Select an test date" />
@@ -92,11 +92,11 @@ const TestAnalytics = () => {
<button className='clear-button' onClick={toggleShowGraph}><img className="graph-icon" src={require('./graph_icon.png')}></img>{curTestData ? 'Clear' : 'Show'}</button>
</div> */}
<div className='container'>
<div className='analytics-container'>
{dailyCategoryCounts && <TestAnalyticsPie data={dailyCategoryCounts} />}
{dailyResults && <TestAnalyticsGraph data={dailyResults} />}
</div>
<div className='container'>
<div className='analytics-container'>
{allTests && <ScatterPlot data={allTests} />}
{allTests && <Locations />}
</div>

View File

@@ -6,7 +6,7 @@
.tests-count-card {
width: 40vw;
height: 45vh;
height: 40vh;
background-color: white;
margin: 10px;
/* display: flex;
@@ -58,4 +58,18 @@
font-size: x-small;
font-weight: 400;
padding: 0.5em;
}
div#tests-count-graph {
height: 180px;
width: 430px;
/* border: 2px solid #000; */
overflow-y: auto;
}
svg#bargraph {
height: 180px; /* 500px; */
width: 700px; /* 1100px; */
/* border: 1px dotted #ccc; */
/* background-color: #ccc; */
}

View File

@@ -26,10 +26,11 @@ class TestAnalyticsGraph extends Component {
data.reverse();
var margin = { top: 30, right: 20, bottom: 50, left: 15 },
width = 1500 - margin.left - margin.right,
width = 700 - margin.left - margin.right,
height = 180 - margin.top - margin.bottom;
var svg = d3.select("#tests-count-graph").append('svg')
.attr("id", "bargraph")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g").attr("transform",
@@ -40,7 +41,7 @@ class TestAnalyticsGraph extends Component {
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 10 + "," + 10 + ")");
.attr("transform", "translate(" + 5 + "," + 5 + ")");
xScale.domain(data.map(function (d) { return moment(d.date).format("MM-DD"); }));

View File

@@ -6,7 +6,7 @@
.category-graph-card {
width: 40vw;
height: 45vh;
height: 40vh;
background-color: white;
margin: 10px;
border-style: solid;

View File

@@ -24,7 +24,7 @@ class TestAnalyticsPie extends Component {
drawChart(data) {
if (!data) return;
var margin = { top: 30, right: 10, bottom: 15, left: 25 },
var margin = { top: 15, right: 10, bottom: 45, left: 10 },
width = 480 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom,
radius = Math.min(width, height) / 2;

View File

@@ -44,7 +44,7 @@ const WorldMap = () => {
}
return (
<svg width={ '40vw' } height={ '50vh' } viewBox="0 0 800 450">
<svg width={ '40vw' } height={ '35vh' } viewBox="0 0 800 450">
<g className="countries">
{
geographies.map((d,i) => (

View File

@@ -1,50 +0,0 @@
import { Avatar, Drawer, List, Stack, Toolbar } from "@mui/material";
// import assets from "../../assets";
import colorConfigs from "../../configs/colorConfigs";
import sizeConfigs from "../../configs/sizeConfigs";
import appRoutes from "../../routes/appRoutes";
import SidebarItem from "./SidebarItem";
import SidebarItemCollapse from "./SidebarItemCollapse";
const Sidebar = () => {
return (
<Drawer
variant="permanent"
sx={{
width: sizeConfigs.sidebar.width,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: sizeConfigs.sidebar.width,
boxSizing: "border-box",
borderRight: "0px",
backgroundColor: colorConfigs.sidebar.bg,
color: colorConfigs.sidebar.color
}
}}
>
<List disablePadding>
<Toolbar sx={{ marginBottom: "20px" }}>
<Stack
sx={{ width: "100%" }}
direction="row"
justifyContent="center"
>
<Avatar src={require('../../smi-logo-120x120.webp')} />
</Stack>
</Toolbar>
{appRoutes.map((route, index) => (
route.sidebarProps ? (
route.child ? (
<SidebarItemCollapse item={route} key={index} />
) : (
<SidebarItem item={route} key={index} />
)
) : null
))}
</List>
</Drawer>
);
};
export default Sidebar;

View File

@@ -1,34 +0,0 @@
import { ListItemButton, ListItemIcon } from "@mui/material";
import { useSelector } from "react-redux";
import { Link } from "react-router-dom";
import colorConfigs from "../../configs/colorConfigs";
const SidebarItem = ({ item }) => {
const { appState } = useSelector((state) => state.appState);
return (
item.sidebarProps && item.path ? (
<ListItemButton
component={Link}
to={item.path}
sx={{
"&: hover": {
backgroundColor: colorConfigs.sidebar.hoverBg
},
backgroundColor: appState === item.state ? colorConfigs.sidebar.activeBg : "unset",
paddingY: "12px",
paddingX: "24px"
}}
>
<ListItemIcon sx={{
color: colorConfigs.sidebar.color
}}>
{item.sidebarProps.icon && item.sidebarProps.icon}
</ListItemIcon>
{item.sidebarProps.displayText}
</ListItemButton>
) : null
);
};
export default SidebarItem;

View File

@@ -1,66 +0,0 @@
import { Collapse, List, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material";
import { useEffect, useState } from "react";
import colorConfigs from "../../configs/colorConfigs";
import ExpandLessOutlinedIcon from '@mui/icons-material/ExpandLessOutlined';
import ExpandMoreOutlinedIcon from '@mui/icons-material/ExpandMoreOutlined';
import SidebarItem from "./SidebarItem";
import { useSelector } from "react-redux";
const SidebarItemCollapse = ({ item }) => {
const [open, setOpen] = useState(false);
const { appState } = useSelector((state) => state.appState);
useEffect(() => {
if (appState.includes(item.state)) {
setOpen(true);
}
}, [appState, item]);
return (
item.sidebarProps ? (
<>
<ListItemButton
onClick={() => setOpen(!open)}
sx={{
"&: hover": {
backgroundColor: colorConfigs.sidebar.hoverBg
},
paddingY: "12px",
paddingX: "24px"
}}
>
<ListItemIcon sx={{
color: colorConfigs.sidebar.color
}}>
{item.sidebarProps.icon && item.sidebarProps.icon}
</ListItemIcon>
<ListItemText
disableTypography
primary={
<Typography>
{item.sidebarProps.displayText}
</Typography>
}
/>
{open ? <ExpandLessOutlinedIcon /> : <ExpandMoreOutlinedIcon />}
</ListItemButton>
<Collapse in={open} timeout="auto">
<List>
{item.child?.map((route, index) => (
route.sidebarProps ? (
route.child ? (
<SidebarItemCollapse item={route} key={index} />
) : (
<SidebarItem item={route} key={index} />
)
) : null
))}
</List>
</Collapse>
</>
) : null
);
};
export default SidebarItemCollapse;

View File

@@ -1,26 +0,0 @@
import { AppBar, Toolbar, Typography } from "@mui/material";
import colorConfigs from "../../configs/colorConfigs";
import sizeConfigs from "../../configs/sizeConfigs";
const Topbar = () => {
return (
<AppBar
position="fixed"
sx={{
width: `calc(100% - ${sizeConfigs.sidebar.width})`,
ml: sizeConfigs.sidebar.width,
boxShadow: "unset",
backgroundColor: colorConfigs.topbar.bg,
color: colorConfigs.topbar.color
}}
>
<Toolbar>
<Typography variant="h6">
HPOS Web - Quality / QA
</Typography>
</Toolbar>
</AppBar>
);
};
export default Topbar;

View File

@@ -1,17 +0,0 @@
import { colors } from "@mui/material";
const colorConfigs = {
sidebar: {
bg: "#233044",
color: "#eeeeee",
hoverBg: "#1e293a",
activeBg: "#1e253a"
},
topbar: {
bg: "#fff",
color: "#000"
},
mainBg: colors.grey["100"]
};
export default colorConfigs;

View File

@@ -1,7 +0,0 @@
const sizeConfigs = {
sidebar: {
width: "300px"
}
};
export default sizeConfigs;

View File

@@ -14,39 +14,17 @@ import { getAnalytics } from "firebase/analytics";
// measurementId: "G-6JHQ47S9DD"
// };
/* qa: hpos-qa */
const firebaseConfig = {
apiKey: "AIzaSyCF7BNZ5vOMQIpKRi_vxquzWP5pIjcb59I",
authDomain: "hpos-qa.firebaseapp.com",
projectId: "hpos-qa",
storageBucket: "hpos-qa.appspot.com",
messagingSenderId: "1004619739289",
appId: "1:1004619739289:web:c3ee0c0127f0d073e5c808",
measurementId: "G-4MSTJHE64W"
};
// /* preprod */
// const firebaseConfig = {
// apiKey: "AIzaSyB2-suWmKh2PgQbRmx7rzPjYhgoovQIsQ8",
// authDomain: "hpos-preprod.firebaseapp.com",
// projectId: "hpos-preprod",
// storageBucket: "hpos-preprod.appspot.com",
// messagingSenderId: "121176529204",
// appId: "1:121176529204:web:6c0db8e642992192bbed61",
// measurementId: "G-GYM6LRQ8KR"
// };
// /* prod */
// const firebaseConfig = {
// apiKey: "AIzaSyBF4pLZReAedU4dWX1eRJNFNIaOIz2xk4s",
// authDomain: "hpos-prod.firebaseapp.com",
// projectId: "hpos-prod",
// storageBucket: "hpos-prod.appspot.com",
// messagingSenderId: "1012719870714",
// appId: "1:1012719870714:web:fcd00acef93f6612de5f43",
// measurementId: "G-B0NZ7DEF69"
// };
/* prod */
const firebaseConfig = {
apiKey: "AIzaSyBF4pLZReAedU4dWX1eRJNFNIaOIz2xk4s",
authDomain: "hpos-prod.firebaseapp.com",
projectId: "hpos-prod",
storageBucket: "hpos-prod.appspot.com",
messagingSenderId: "1012719870714",
appId: "1:1012719870714:web:fcd00acef93f6612de5f43",
measurementId: "G-B0NZ7DEF69"
};
const app = initializeApp(firebaseConfig);

View File

@@ -4,9 +4,6 @@ import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
import { Provider } from 'react-redux';
import { store } from './store/store';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
@@ -14,7 +11,7 @@ import Devices from './components/Devices';
import TestAnalytics from './components/TestAnalytics';
import WorldMap from './components/WorldMap';
import RegistraionChart from './RegistraionChart';
import TestAnalyticsGraph from './components/TestAnalyticsGraph';
import Calibrations from './components/Calibrations';
const router = createBrowserRouter([
{
@@ -33,24 +30,20 @@ const router = createBrowserRouter([
path: "/analytics",
element: <TestAnalytics />,
},
{
path: "/hposdata",
element: <TestAnalyticsGraph />,
},
{
path: "/map",
element: <RegistraionChart />,
},
{
path: "/calibrations",
element: <Calibrations />,
}
]);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
{/* <CssBaseline /> */}
{/* <RouterProvider router={router} /> */}
<App />
</Provider>
<RouterProvider router={router} />
);
// If you want to start measuring performance in your app, pass a function

View File

@@ -1,116 +0,0 @@
import DashboardOutlinedIcon from '@mui/icons-material/DashboardOutlined';
import AppsOutlinedIcon from '@mui/icons-material/AppsOutlined';
import TestAnalytics from "../components/TestAnalytics";
import HomePage from '../components/HomePage';
import Devices from '../components/Devices';
import { Download, FactCheck } from '@mui/icons-material';
import ExportData from '../components/ExportData';
import Registration from '../components/Registration';
import { FaAndroid, FaArrowCircleLeft, FaBars, FaBezierCurve, FaChalkboardTeacher, FaChartArea, FaChartLine, FaCheck, FaExclamation, FaFlask, FaGolfBall, FaPhone, FaQuestionCircle, FaSearch, FaUser } from 'react-icons/fa';
import AppInstallation from '../components/AppInstallation';
import Samples from '../components/Samples';
import Precision from '../components/Precision';
import Accuracy from '../components/Accuracy';
import CurveFit from '../components/CurveFit';
import Help from '../components/Help';
const appRoutes = [
{
index: true,
element: <HomePage />,
state: "home"
},
{
path: "/analytics",
element: <TestAnalytics />,
state: "analytics",
sidebarProps: {
displayText: "Analytics",
icon: <DashboardOutlinedIcon />
}
},
{
path: "/tests/insights",
element: <Devices />,
state: "insights",
sidebarProps: {
displayText: "Insights",
icon: <AppsOutlinedIcon />
}
},
{
path: "/downloaddata",
element: <ExportData />,
state: "downloaddata",
sidebarProps: {
displayText: "Raw Data",
icon: <Download />
}
},
{
path: "/registration",
element: <Registration />,
state: "registration",
sidebarProps: {
displayText: "Registration",
icon: <FaUser />
}
},
{
path: "/appinstallation",
element: <AppInstallation />,
state: "appinstallation",
sidebarProps: {
displayText: "App Installation",
icon: <FaAndroid />
}
},
{
path: "/samples",
element: <Samples />,
state: "Samples",
sidebarProps: {
displayText: "Samples",
icon: <FaFlask />
}
},
// {
// path: "/precision",
// element: <Precision />,
// state: "precision",
// sidebarProps: {
// displayText: "Precision",
// icon: <FaCheck />
// }
// },
{
path: "/autoanalysis",
element: <Accuracy />,
state: "autoanalysis",
sidebarProps: {
displayText: "Auto Analysis (NEW)",
icon: <FaChartLine />
}
},
// {
// path: "/curvefit",
// element: <CurveFit />,
// state: "curvefit",
// sidebarProps: {
// displayText: "Linear Fit",
// icon: <FaBezierCurve />
// }
// },
{
path: "/helpcenter",
element: <Help />,
state: "helpcenter",
sidebarProps: {
displayText: "Help Center",
icon: <FaQuestionCircle />
}
},
]
export default appRoutes;

View File

@@ -1,34 +0,0 @@
import { Route } from "react-router-dom";
import PageWrapper from "../components/PageWrapper";
import appRoutes from "./appRoutes";
const generateRoute = (routes) => {
return routes.map((route, index) => (
route.index ? (
<Route
index
path={route.path}
element={<PageWrapper state={route.state}>
{route.element}
</PageWrapper>}
key={index}
/>
) : (
<Route
path={route.path}
element={
<PageWrapper state={route.child ? undefined : route.state}>
{route.element}
</PageWrapper>
}
key={index}
>
{route.child && (
generateRoute(route.child)
)}
</Route>
)
));
};
export const routes = generateRoute(appRoutes);

View File

@@ -1,21 +0,0 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
const initialState = {
appState: ""
};
export const appStateSlice = createSlice({
name: "appState",
initialState,
reducers: {
setAppState: (state, action) => {
state.appState = action.payload;
}
}
});
export const {
setAppState
} = appStateSlice.actions;
export default appStateSlice.reducer;

View File

@@ -1,8 +0,0 @@
import { configureStore } from "@reduxjs/toolkit";
import appStateSlice from "./appStateSlice";
export const store = configureStore({
reducer: {
appState: appStateSlice
}
});