Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d327fecf8c | ||
|
|
43375556bf | ||
|
|
92feb4081c | ||
|
|
bb968b4781 | ||
|
|
a751b55647 | ||
|
|
099b3f08fa | ||
|
|
1f55978521 | ||
|
|
84740e846f | ||
|
|
09d18106b8 | ||
|
|
5dbff78d79 | ||
|
|
75ad2aba82 | ||
|
|
3a355ffe57 | ||
|
|
4da937673c | ||
|
|
48c9bacb4f | ||
|
|
4dd9a31a75 | ||
|
|
5d5325d51d | ||
|
|
f857e57173 | ||
|
|
c20197c40a | ||
|
|
ca5106d613 | ||
|
|
9c782b7cd1 | ||
|
|
707e338d3a | ||
|
|
367c36e3f8 | ||
|
|
fc31aea95b | ||
|
|
eaf87791bd | ||
|
|
89090ff86b | ||
|
|
515ad41329 | ||
|
|
920858a523 | ||
|
|
b43aba97f2 | ||
|
|
16aa545862 | ||
|
|
a836335ed3 | ||
|
|
65723718c6 | ||
|
|
fb3d715831 | ||
|
|
683c6049af | ||
|
|
34a770ceed | ||
|
|
67a7414d5c | ||
|
|
260e6ec734 |
16
.firebaserc
@@ -1,5 +1,21 @@
|
|||||||
{
|
{
|
||||||
"projects": {
|
"projects": {
|
||||||
"default": "hpos-af3cc"
|
"default": "hpos-af3cc"
|
||||||
|
},
|
||||||
|
"targets": {
|
||||||
|
"hpos-af3cc": {
|
||||||
|
"hosting": {
|
||||||
|
"dev1": [
|
||||||
|
"hpos-dev1"
|
||||||
|
],
|
||||||
|
"qa": [
|
||||||
|
"hpos-qa"
|
||||||
|
],
|
||||||
|
"dev": [
|
||||||
|
"hpos-dev"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"etags": {}
|
||||||
}
|
}
|
||||||
2
.gitignore
vendored
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
@@ -31,3 +32,4 @@ public/india-states.json
|
|||||||
|
|
||||||
scripts/reports
|
scripts/reports
|
||||||
scripts/data
|
scripts/data
|
||||||
|
firebase-debug.log
|
||||||
|
|||||||
18
README.md
@@ -14,4 +14,22 @@
|
|||||||
http://localhost:3000
|
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:dev
|
||||||
|
|
||||||
pyinstaller --noconfirm --noconsole -i "smi.ico" --windowed --clean hemocube.py
|
pyinstaller --noconfirm --noconsole -i "smi.ico" --windowed --clean hemocube.py
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"hosting": {
|
"hosting": {
|
||||||
|
"target": "preprod",
|
||||||
"public": "build",
|
"public": "build",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"firebase.json",
|
"firebase.json",
|
||||||
|
|||||||
BIN
src/assets/auto_analysis_help-1.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
src/assets/auto_analysis_help-2.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
src/assets/auto_analysis_help-3.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
src/assets/raw_data_help_1.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
src/assets/raw_data_help_2.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
src/assets/raw_data_help_3.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
src/assets/registration_help_1.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
src/assets/registration_help_2.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
src/assets/registration_help_3.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
63
src/components/Accuracy.css
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/components/Accuracy.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
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-af3cc.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;
|
||||||
@@ -12,7 +12,10 @@ const AppInstallation = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<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>
|
<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">
|
<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>
|
<img className="app-qrcode" src={require('../assets/hpos-testing_preprod.png')} alt='icon'></img>
|
||||||
@@ -27,7 +30,7 @@ const AppInstallation = () => {
|
|||||||
<li>install the downloaded apk and give permissions</li>
|
<li>install the downloaded apk and give permissions</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/components/CurveFit.css
Normal file
56
src/components/CurveFit.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
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;
|
||||||
@@ -11,7 +11,7 @@ function ExportData({ url, show }) {
|
|||||||
const [endDate, setEndDate] = useState("");
|
const [endDate, setEndDate] = useState("");
|
||||||
|
|
||||||
const fetchFile = (start_date, end_date) => {
|
const fetchFile = (start_date, end_date) => {
|
||||||
window.location.href = `https://asia-south1-hpos-preprod.cloudfunctions.net/consolidation?start_date=${moment(startDate).format("YYYY-MM-DD")}&end_date=${moment(endDate).format("YYYY-MM-DD")}`;
|
window.location.href = `https://asia-south1-${process.env.REACT_APP_FIREBASE_PROJECT_ID}.cloudfunctions.net/consolidation?start_date=${moment(startDate).format("YYYY-MM-DD")}&end_date=${moment(endDate).format("YYYY-MM-DD")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
19
src/components/Help.css
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
128
src/components/Help.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
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;
|
||||||
47
src/components/ImageDownloader.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
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;
|
||||||
0
src/components/Precision.css
Normal file
15
src/components/Precision.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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;
|
||||||
@@ -26,3 +26,7 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.format-text {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ function Registration() {
|
|||||||
setCounter(counter + 1);
|
setCounter(counter + 1);
|
||||||
axios({
|
axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: "https://asia-south1-hpos-preprod.cloudfunctions.net/registerUser",
|
url: `https://asia-south1-${process.env.REACT_APP_FIREBASE_PROJECT_ID}.cloudfunctions.net/registerUser`,
|
||||||
data: {
|
data: {
|
||||||
"_id": id,
|
"_id": id,
|
||||||
"aadharId": "2808202300001",
|
"aadharId": "2808202300001",
|
||||||
@@ -65,7 +65,7 @@ function Registration() {
|
|||||||
setMessage("");
|
setMessage("");
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: "https://asia-south1-hpos-preprod.cloudfunctions.net/clearUsers",
|
url: `https://asia-south1-${process.env.REACT_APP_FIREBASE_PROJECT_ID}.cloudfunctions.net/clearUsers`,
|
||||||
withCredentials: false,
|
withCredentials: false,
|
||||||
secure: false,
|
secure: false,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -91,10 +91,11 @@ function Registration() {
|
|||||||
<div className="user-creation">
|
<div className="user-creation">
|
||||||
<label>
|
<label>
|
||||||
name:
|
name:
|
||||||
<input name="name" value={name} placeholder="350umol kmon04" onChange={onNameChange} />
|
<input name="name" value={name} placeholder="350 UMOL KMNO4" onChange={onNameChange} />
|
||||||
</label>
|
</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='dates'>
|
||||||
<div className='button' onClick={() => createUser()}>Create User</div>
|
<div className='button' onClick={() => createUser()}>Create</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='dates'>
|
<div className='dates'>
|
||||||
<div className='clear-users-button' onClick={() => clearsUsers()}>Clear</div>
|
<div className='clear-users-button' onClick={() => clearsUsers()}>Clear</div>
|
||||||
|
|||||||
39
src/components/Samples.css
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#samples-table {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
89
src/components/Samples.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
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.id);
|
||||||
|
const data = document.data();
|
||||||
|
const id = document.id;
|
||||||
|
samples.push({id, ...data});
|
||||||
|
});
|
||||||
|
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>Pdr Class</th>
|
||||||
|
<th>Dev Ratio Class</th>
|
||||||
|
<th>SR Class</th>
|
||||||
|
<th>PDR</th>
|
||||||
|
<th>Dev Ratio</th>
|
||||||
|
<th>Slope Ratio</th>
|
||||||
|
<th>led1Sample</th>
|
||||||
|
{/* <th>led2Average</th> */}
|
||||||
|
<th>led2Sample</th>
|
||||||
|
<th>led3Sample</th>
|
||||||
|
<th>led4Sample</th>
|
||||||
|
<th>Device</th>
|
||||||
|
<th>Time</th>
|
||||||
|
</tr>
|
||||||
|
{samples.map((sample, index) => (
|
||||||
|
<tr key={index} className='samples-table-tr'>
|
||||||
|
<td>{sample.name || sample._id}</td>
|
||||||
|
<td>{sample.prdClassification}</td>
|
||||||
|
<td>{sample.deviceRatioClass}</td>
|
||||||
|
<td>{sample.slopeRatioClass}</td>
|
||||||
|
<td>{sample?.predictedDenovixRatio?.toFixed(4)}</td>
|
||||||
|
<td>{sample?.deviceRatio?.toFixed(4)}</td>
|
||||||
|
<td>{sample?.slopeRatio?.toFixed(4)}</td>
|
||||||
|
<td>{sample.led1Sample || 'NA'}</td>
|
||||||
|
<td>{sample.led2Sample || 'NA'}</td>
|
||||||
|
<td>{sample.led3Sample || 'NA'}</td>
|
||||||
|
<td>{sample.led4Sample || 'NA'}</td>
|
||||||
|
<td>{sample.deviceId}</td>
|
||||||
|
<td>{moment(sample.testTime).format('HH:MM:SS')}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Samples;
|
||||||
@@ -53,6 +53,9 @@ const TestAnalytics = () => {
|
|||||||
const dailyCountsList = Object.entries(dailyCounts).sort().map(x => {
|
const dailyCountsList = Object.entries(dailyCounts).sort().map(x => {
|
||||||
return { date: x[0], count: x[1] };
|
return { date: x[0], count: x[1] };
|
||||||
});
|
});
|
||||||
|
if (dailyCountsList?.length > 7)
|
||||||
|
setDailyResults(dailyCountsList.slice(dailyCountsList.length - 7, dailyCountsList.length));
|
||||||
|
else
|
||||||
setDailyResults(dailyCountsList);
|
setDailyResults(dailyCountsList);
|
||||||
const rss = _.groupBy(hposTests, function (test) {
|
const rss = _.groupBy(hposTests, function (test) {
|
||||||
return moment(test.testTime).startOf('day').format();
|
return moment(test.testTime).startOf('day').format();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class TestAnalyticsGraph extends Component {
|
|||||||
data.reverse();
|
data.reverse();
|
||||||
|
|
||||||
var margin = { top: 30, right: 20, bottom: 50, left: 15 },
|
var margin = { top: 30, right: 20, bottom: 50, left: 15 },
|
||||||
width = 1500 - margin.left - margin.right,
|
width = 400 - margin.left - margin.right,
|
||||||
height = 180 - margin.top - margin.bottom;
|
height = 180 - margin.top - margin.bottom;
|
||||||
|
|
||||||
var svg = d3.select("#tests-count-graph").append('svg')
|
var svg = d3.select("#tests-count-graph").append('svg')
|
||||||
@@ -113,7 +113,7 @@ class TestAnalyticsGraph extends Component {
|
|||||||
drawChart(data) {
|
drawChart(data) {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
|
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
|
||||||
width = 960 - margin.left - margin.right,
|
width = 420 - margin.left - margin.right,
|
||||||
height = 500 - margin.top - margin.bottom;
|
height = 500 - margin.top - margin.bottom;
|
||||||
|
|
||||||
const INNER_WIDTH = width;// - margin.left - margin.right;
|
const INNER_WIDTH = width;// - margin.left - margin.right;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const Topbar = () => {
|
|||||||
>
|
>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
HPOS dashboard
|
HPOS Web - {process.env.REACT_APP_TOPBAR_TITLE}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|||||||
@@ -14,17 +14,51 @@ import { getAnalytics } from "firebase/analytics";
|
|||||||
// measurementId: "G-6JHQ47S9DD"
|
// 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 */
|
/* preprod */
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
apiKey: "AIzaSyB2-suWmKh2PgQbRmx7rzPjYhgoovQIsQ8",
|
apiKey: "AIzaSyBFl_YkJ5PMIvMY3G70313SyrqemjFnUv8",
|
||||||
authDomain: "hpos-preprod.firebaseapp.com",
|
authDomain: "hpos-af3cc.firebaseapp.com",
|
||||||
projectId: "hpos-preprod",
|
projectId: "hpos-af3cc",
|
||||||
storageBucket: "hpos-preprod.appspot.com",
|
storageBucket: "hpos-af3cc.appspot.com",
|
||||||
messagingSenderId: "121176529204",
|
messagingSenderId: "650071678820",
|
||||||
appId: "1:121176529204:web:6c0db8e642992192bbed61",
|
appId: "1:650071678820:web:ce50538bf46632d46c6471",
|
||||||
measurementId: "G-GYM6LRQ8KR"
|
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 */
|
// /* prod */
|
||||||
// const firebaseConfig = {
|
// const firebaseConfig = {
|
||||||
|
|||||||
@@ -4,11 +4,16 @@ import AppsOutlinedIcon from '@mui/icons-material/AppsOutlined';
|
|||||||
import TestAnalytics from "../components/TestAnalytics";
|
import TestAnalytics from "../components/TestAnalytics";
|
||||||
import HomePage from '../components/HomePage';
|
import HomePage from '../components/HomePage';
|
||||||
import Devices from '../components/Devices';
|
import Devices from '../components/Devices';
|
||||||
import { Download } from '@mui/icons-material';
|
import { Download, FactCheck } from '@mui/icons-material';
|
||||||
import ExportData from '../components/ExportData';
|
import ExportData from '../components/ExportData';
|
||||||
import Registration from '../components/Registration';
|
import Registration from '../components/Registration';
|
||||||
import { FaAndroid, FaPhone, FaUser } from 'react-icons/fa';
|
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 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 = [
|
const appRoutes = [
|
||||||
{
|
{
|
||||||
@@ -39,7 +44,7 @@ const appRoutes = [
|
|||||||
element: <ExportData />,
|
element: <ExportData />,
|
||||||
state: "downloaddata",
|
state: "downloaddata",
|
||||||
sidebarProps: {
|
sidebarProps: {
|
||||||
displayText: "Data Download",
|
displayText: "Raw Data",
|
||||||
icon: <Download />
|
icon: <Download />
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -61,6 +66,51 @@ const appRoutes = [
|
|||||||
icon: <FaAndroid />
|
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;
|
export default appRoutes;
|
||||||