diff --git a/src/api.tsx b/src/api.tsx index e26d1e2..b679614 100644 --- a/src/api.tsx +++ b/src/api.tsx @@ -28,7 +28,7 @@ import IQuickTest from './misc/quick-test'; import StatisticData from './misc/statistic-data'; import ITestResult from './misc/test-result'; import IQTArchiv from './misc/qt-archiv'; -import { Sex, TestResult } from './misc/enum'; +import { Sex, TestResult, TestType } from './misc/enum'; import { IUser, IGroupDetails } from './misc/user'; export const api = axios.create({ @@ -61,7 +61,8 @@ export interface IShortHashedGuid { export interface IQuickTestDccAPIResponseModel { dccConsent: boolean, - testResult: number + testResult: number, + testType: TestType } export interface IQuickTestAPIModel { @@ -223,7 +224,7 @@ export const usePostQuickTest = (quickTest: IQuickTest | undefined, processId: s testResultServerHash: quickTest.testResultHash ? quickTest.testResultHash : '0000000000000000000000000000000000000000000000000000000000000000', diseaseAgentTargeted: '840539006', - testType: "LP217198-3", + testType: quickTest.testType, dccConsent: quickTest.dccConsent, @@ -444,6 +445,47 @@ export const useStatistics = (onSuccess?: (status: number) => void, onError?: (e } +export const useGetStatisticsFromTo = (onSuccess?: (status: number) => void, onError?: (error: any) => void) => { + + const { keycloak, initialized } = useKeycloak(); + const [result, setResult] = React.useState(); + + let balseUri = '/api/quickteststatistics'; + + const header = { + "Authorization": initialized ? `Bearer ${keycloak.token}` : "", + 'Content-Type': 'application/json' + }; + + + const getStatisticsFromTo = (dateFrom: Date, dateTo: Date) => { + + if(!dateFrom) { + return; + } + + let requestUri = balseUri + '?dateFrom=' + dateFrom.toISOString() + '&dateTo=' + dateTo.toISOString(); + + api.get(requestUri, { headers: header }) + .then(response => { + setResult(response.data); + if (onSuccess) { + onSuccess(response?.status); + } + }) + .catch(error => { + if (onError) { + onError(error); + } + }); + } + + return [ + result, + getStatisticsFromTo + ] as const; +} + export const useGetKeycloakConfig = (onSuccess?: (status: number) => void, onError?: (error: any) => void) => { const [result, setResult] = React.useState(); diff --git a/src/assets/SCSS/custom.scss b/src/assets/SCSS/custom.scss index 1c7831c..d40a070 100644 --- a/src/assets/SCSS/custom.scss +++ b/src/assets/SCSS/custom.scss @@ -358,6 +358,7 @@ hr { position: inherit; width: 20px; height: 20px; + margin-top: 0.1rem;; } .rdb-label { height: 20px; @@ -501,4 +502,48 @@ td { display: flex; align-self: baseline; margin-left: 0.1rem; +} + +.row-height-half { + height: 50%; +} + +.space-five { + padding: 5px; +} + +.width-one { + width: 1%; +} + +.width-eight-houndred { + min-width: 800px; +} + +.add-statistic { + width: 100%; + display: flex; + direction: row; + align-items: center; + box-shadow: 4px 2px 8px rgba(0, 0, 0, 0.15); + padding-top: 10px; + padding-bottom: 10px; + padding-left: 0px; + padding-right: 0px; + margin: 0px; + background-color: white; + flex-wrap: wrap; +} + +.add-statistic-item { + display: flex; + direction: row; + width: 100%; +} + +.btn-add-statistic-icon { + padding: 0px; + border: 0px; + width:24px; + height:24px; } \ No newline at end of file diff --git a/src/assets/i18n/de/translation.json b/src/assets/i18n/de/translation.json index 406701a..b52b0f7 100644 --- a/src/assets/i18n/de/translation.json +++ b/src/assets/i18n/de/translation.json @@ -45,7 +45,7 @@ "data-submit": "Daten übermitteln", "result-positive": "Positiv", "result-negative": "Negativ", - "result-failed": "Failed", + "result-failed": "Ungültig", "process-number": "Proben-ID", "logout": "Logout", "wrong-process-number": "Falsche Vorgangsnummer", @@ -62,8 +62,11 @@ "statistics": "Auswertungen", "statistics-menu-item": "Auswertungen", "failed-report": "Testergebnisse", - "totalTestCount": "Gesamtanzahl der Tests", - "positiveTestCount": "Anzahl der positiven Tests", + "totalTestCount": "Gesamtanzahl Antigen-Schnelltests", + "positiveTestCount": "Anzahl positiver Antigen-Schnelltests", + "pcrTotalTestCount": "Gesamtanzahl PoC-NAA-Test(s)", + "pcrPositiveTestCount": "Anzahl positiver PoC-NAA-Test(s)", + "addStatisticRow": "Auswertung hinzufügen", "today": "Heute", "thisWeek": "Diese Woche", "thisMonth": "Dieser Monat", @@ -71,6 +74,7 @@ "timerange": "Testzeitraum", "from": "am", "to": "bis", + "statisticFrom": "Von", "filter-none": "alle", "filter-record-result": "Testergebnis", "search": "Suchen", @@ -81,6 +85,7 @@ "standardised-name": "Standardisierter Name", "standardised-name-tooltip": "Der standardisierte Name befindet sich in der untersten Zeile auf der Rückseite des Personalausweises. Es dürfen nur Großbuchstaben und \"<\" verwendet werden. Sonderzeichen, Umlaute und Zahlen sind nicht erlaubt.", "testManufacturers": "Testhersteller und -name", + "pcrTestManufacturers": "PoC-NAA-Testsystem", "testId-input-header": "Eingabe Proben-ID", "error-processId-data-load": "Die eingegebene Proben-ID ist uns nicht bekannt!", "ok": "OK", @@ -145,5 +150,9 @@ "disclaimer-link": "https://map.schnelltestportal.de", "disclaimer-text2-part1": "Um eine Teststelle für unser Schnelltestsuchportal freizuschalten, klicken Sie auf das Bearbeiten-Icon und aktivieren Sie das Kästchen Im Schnelltestsuchportal anzeigen.\n\nSo ermöglichen Sie Ihren Kunden, die Teststelle einfach unter ", "disclaimer-text2-part2": " zu finden.\n\nSie können auch zusätzliche Informationen wie Öffnungszeiten oder Ihre Website hinterlegen.", - "disclaimer-do-not-show": "Benachrichtigung nicht mehr anzeigen." + "disclaimer-do-not-show": "Benachrichtigung nicht mehr anzeigen.", + "enablePcr": "PoC-NAA-Test erlauben", + "LP217198-3": "Antigen-Schnelltest", + "LP6464-4": "PoC-NAA-Test", + "test-type": "Testtyp" } \ No newline at end of file diff --git a/src/assets/i18n/en/translation.json b/src/assets/i18n/en/translation.json index 8bb09f3..82442bf 100644 --- a/src/assets/i18n/en/translation.json +++ b/src/assets/i18n/en/translation.json @@ -4,6 +4,7 @@ "data-privacy": "Dataprivacy", "record-patient-data": "Record Patient Data", "record-result": "Record Test Result", + "record-result2": "Record Patient Data", "record-qr-scan": "Record QR Scan", "welcome": "Welcome", "process": "Process", @@ -30,6 +31,8 @@ "email-address": "E-Mail Address", "test-id": "Test-ID", "test-name": "Test-Name", + "processing-consent-title": "Show test result in CWA (non-named test certificate)", + "patientdata-exclude-title": "Show test result in CWA (named test certificate)", "processing-consent": "Consent to processing in CWA", "bill-status": "bill status", "data-privacy-approve": "Approve Dataprivacy", @@ -40,7 +43,7 @@ "patient-data": "Patient data", "cancel": "Cancel", "data-submit": "Submit Data", - "result-positiv": "Positive", + "result-positive": "Positive", "result-negative": "Negative", "result-failed": "Failed", "process-number": "Process number", @@ -61,6 +64,9 @@ "failed-report": "Test Report", "totalTestCount": "Total test count", "positiveTestCount": "Positive test count", + "pcrTotalTestCount": "Total PoC-NAA-Test(s)", + "pcrPositiveTestCount": "Positive PoC-NAA-Test(s)", + "addStatisticRow": "Add statistic", "today": "Today", "thisWeek": "This week", "thisMonth": "This month", @@ -68,6 +74,7 @@ "timerange": "Timerange", "from": "from", "to": "to", + "statisticFrom": "From", "filter-none": "all", "filter-record-result": "testresult", "search": "search", @@ -76,10 +83,15 @@ "standardised-first-name": "Standardised Given name", "standardised-name": "Standardised Family name", "testManufacturers": "Test manufacturers and names", + "pcrTestManufacturers": "PoC-NAA-Testsystem", "testId-input-header": "Process number input", "error-processId-data-load": "The entered sample ID is not known!", "ok": "OK", "change-password": "Change Password", + "testZertifikat": "COVID test certificate", + "dccConsent": "Patient wants to have an official EU COVID test certificate (DCC)?", + "ja": "Yes", + "nein": "No", "user-management": "User Management", "user": "User", "user-name": "Username", @@ -113,6 +125,8 @@ "searchPortalWebsitePlaceholder": "www.example.de", "searchPortalWebsiteTooltip": "tooltip", "searchPortalOpeningHours": "Opening Hours", + "openening-hours-to-long-error": "Either the specified text is too long or it contains incorrect characters. 64 characters per line are allowed.", + "opening-hours-to-much-lines-error": "Only seven lines are allowed.", "searchPortalAppointmentRequired": "Appointment Required", "RAT-list-info": "The tests listed in the DCC selection are based on a list agreed at EU level and also include tests that are not reimbursable in Germany. The operator himself has to check the eligibility for reimbursement. Comparison with ", "RAT-list-info-link": "https://antigentest.bfarm.de/ords/f?p=110:100:12807215185840:::::&tz=2:00", @@ -127,6 +141,10 @@ "disclaimer-text1-part2": ".", "disclaimer-link": "https://map.schnelltestportal.de", "disclaimer-text2-part1": "To activate a test site for our rapid test search portal, click on the edit icon and activate the Show in the rapid test search portal box\n\nThis enables your customers to easily find the test site at ", - "disclaimer-text2-part2":".\n\nYou can also store additional information such as opening times or your website.", - "disclaimer-do-not-show": "Don't show notification again." + "disclaimer-text2-part2": ".\n\nYou can also store additional information such as opening times or your website.", + "disclaimer-do-not-show": "Don't show notification again.", + "enablePcr": "enable PoC-NAA-Test", + "LP217198-3": "RAT", + "LP6464-4": "PoC-NAA-Test", + "test-type": "Test type" } \ No newline at end of file diff --git a/src/components/modals/group-modal.component.tsx b/src/components/modals/group-modal.component.tsx index df61463..f5aab6f 100644 --- a/src/components/modals/group-modal.component.tsx +++ b/src/components/modals/group-modal.component.tsx @@ -36,6 +36,7 @@ const emptyGroup: IGroupDetails = { pocId: '', name: '', pocDetails: '', + enablePcr: false, searchPortalConsent: true, parentGroup: '', website: '', @@ -350,6 +351,15 @@ const GroupModal = (props: any) => { maxLength={300} /> + updateSearchPortalConsent('enablePcr', evt.currentTarget.checked)} + type='checkbox' + checked={group.enablePcr} + /> + +
+ {/* < FormGroupInput controlId='formBSNRInput' title={t('translation:bsnr')} placeholder={t('translation:bsnr-placeholder')} value={group ? group.bsnr : ''} onChange={(evt: any) => { @@ -362,7 +372,7 @@ const GroupModal = (props: any) => { pattern={utils.pattern.BSNR} /> */} - updateSearchPortalConsent('searchPortalConsent', evt.currentTarget.checked)} type='checkbox' diff --git a/src/components/modules/form-group.component.tsx b/src/components/modules/form-group.component.tsx index b449125..28e9686 100644 --- a/src/components/modules/form-group.component.tsx +++ b/src/components/modules/form-group.component.tsx @@ -186,10 +186,17 @@ export const FormGroupPermissionCkb = (props: any) => { ) } -export const FormGroupSexRadio = (props: any) => { +export const FormGroupInlineRadio = (props: any) => { return (!props ? <> : - + { onChange={props.onChange} required={props.required} /> - {props.title} + {props.title} ) diff --git a/src/components/modules/person-data-inputs.tsx b/src/components/modules/person-data-inputs.tsx index 407ef53..cc8c1b4 100644 --- a/src/components/modules/person-data-inputs.tsx +++ b/src/components/modules/person-data-inputs.tsx @@ -12,7 +12,7 @@ import de from 'date-fns/locale/de'; import { Sex } from "../../misc/enum"; import { IPersonData } from "../../misc/quick-test"; -import { FormGroupInput, FormGroupSexRadio } from "./form-group.component"; +import { FormGroupInput, FormGroupInlineRadio } from "./form-group.component"; registerLocale('de', de) @@ -177,18 +177,18 @@ const PersonInputs = (props: any) => { - setSex(Sex.MALE)} /> - setSex(Sex.FEMALE)} required={true} /> - setSex(Sex.DIVERSE)} /> diff --git a/src/components/modules/statistic-data.component.tsx b/src/components/modules/statistic-data.component.tsx new file mode 100644 index 0000000..9df6524 --- /dev/null +++ b/src/components/modules/statistic-data.component.tsx @@ -0,0 +1,199 @@ +/* + * Corona-Warn-App / cwa-quick-test-frontend + * + * (C) 2021, T-Systems International GmbH + * + * Deutsche Telekom AG and all other contributors / + * copyright owners license this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from "react"; +import { Button, Col, Form, Row, Table, Container } from 'react-bootstrap'; + +import '../../i18n'; +import { useTranslation } from 'react-i18next'; +import DatePicker from 'react-datepicker'; + +import { DisplayStatisticData } from "../../misc/statistic-data"; +import utils from "../../misc/utils"; +import imageAdd from '../../assets/images/icon_add.svg' + +const StatisticDataRow = (props: any) => { + const { t } = useTranslation(); + + const [pcrEnabled, setPcrEnabled] = React.useState(false); + const [statisticRows, setStatisticRows] = React.useState([]); + + React.useEffect(() => { + if (props && props.statisticData) { + const statisticData: DisplayStatisticData[] = props.statisticData; + + setPcrEnabled(props.pcrEnabled); + setStatisticRows(statisticData); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.statisticData]) + + return ( + + + + + + + {!pcrEnabled + ? <> + : <> + + + + } + + + + + {statisticRows.map((statiscData: DisplayStatisticData, i: number) => + + + + + {!pcrEnabled + ? <> + : <> + + + + } + + + )} + +
 {t('translation:totalTestCount')}{t('translation:positiveTestCount')}{t('translation:pcrTotalTestCount')}{t('translation:pcrPositiveTestCount')} 
{statiscData.label}{statiscData.ratTestCount} + {statiscData.ratTestCount > 0 ? statiscData.ratPositiveTestCount + + ' ( ' + (100 * statiscData.ratPositiveTestCount / statiscData.ratTestCount).toFixed(2) + "% )" : 0} + {statiscData.pcrTestCount} + {statiscData.pcrTestCount > 0 ? statiscData.pcrPositiveTestCount + + ' ( ' + (100 * statiscData.pcrPositiveTestCount / statiscData.pcrTestCount).toFixed(2) + "% )" : 0} + +
+ ) +} + +export default StatisticDataRow; + +export const StatisticDateSelectionRow = (props: any) => { + const { t } = useTranslation(); + + const [dateValidFrom, setDateValidFrom] = React.useState(); + const [dateValidTo, setDateValidTo] = React.useState(); + + const handleDateValidFrom = (evt: Date | [Date, Date] | null) => { + const date = handleDateChange(evt); + setDateValidFrom(date); + if (date === null) { + setDateValidTo(undefined); + } + } + + const handleDateValidTo = (evt: Date | [Date, Date] | null) => { + const date = handleDateChange(evt); + setDateValidTo(date); + } + + const handleDateChange = (evt: Date | [Date, Date] | null) => { + let date: Date; + + if (Array.isArray(evt)) + date = evt[0]; + else + date = evt as Date; + + if (date) { + date.setHours(12); + } + + return date; + } + + return ( + + + + + {t('translation:addStatisticRow')} + + + + + + + {t('translation:timerange')} + + + + + + + {'-'} + + + + + + + ) +} + + diff --git a/src/components/modules/test-result-inputs.tsx b/src/components/modules/test-result-inputs.tsx index a067111..ce6d970 100644 --- a/src/components/modules/test-result-inputs.tsx +++ b/src/components/modules/test-result-inputs.tsx @@ -3,7 +3,7 @@ import React from "react"; import '../../i18n'; import { useTranslation } from 'react-i18next'; -import { TestResult } from "../../misc/enum"; +import { TestResult, TestType } from "../../misc/enum"; import { FormGroupInput, FormGroupRadio, FormGroupValueSetSelect } from "./form-group.component"; import useLocalStorage from "../../misc/useLocalStorage"; import ITestResult from "../../misc/test-result"; @@ -22,27 +22,39 @@ const TestResultInputs = (props: any) => { const [testId, setTestId] = useLocalStorage('testId', ''); const [testName, setTestName] = useLocalStorage('testName', ''); + const [pcrTestName, setPcrTestName] = useLocalStorage('pcrTestName', ''); const [testManufacturerId, setTestManufacturerId] = useLocalStorage('testManufacturers', ''); const [testManufacturerDescription, setTestManufacturerDescription] = React.useState(''); const [dccConsent, setDccConsent] = React.useState(); + const [testType, setTestType] = React.useState(); const tests = useGetTests(); React.useEffect(() => { if (testResult) { - const result: ITestResult = { - testBrandId: dccConsent ? undefined : testId, - testBrandName: dccConsent ? undefined : testName, - result: testResult, - dccTestManufacturerId: dccConsent ? testManufacturerId : undefined, - dccTestManufacturerDescription: dccConsent ? testManufacturerDescription : undefined + let result: ITestResult; + + if (testType === TestType.PCR) { + result = { + pcrTestName: pcrTestName, + result: testResult + } + } + else { + result = { + testBrandId: dccConsent ? undefined : testId, + testBrandName: dccConsent ? undefined : testName, + result: testResult, + dccTestManufacturerId: dccConsent ? testManufacturerId : undefined, + dccTestManufacturerDescription: dccConsent ? testManufacturerDescription : undefined + } } props.onChange(result); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [testId, testName, testResult, testManufacturerId, testManufacturerDescription]) + }, [testId, testName, testResult, testManufacturerId, testManufacturerDescription, pcrTestName]) React.useEffect(() => { if (testManufacturerId) { @@ -60,6 +72,7 @@ const TestResultInputs = (props: any) => { React.useEffect(() => { if (props.quickTest) { setDccConsent(props.quickTest.dccConsent === true); + setTestType(props.quickTest.testType ?? TestType.RAT); } }, [props.quickTest]) @@ -123,64 +136,71 @@ const TestResultInputs = (props: any) => { return ( - {/* test-ID input */} - < FormGroupInput controlId='formTestIdInput' title={t('translation:test-id')} - value={testId} - onChange={handleTestIdChange} - required={!dccConsent} - hidden={dccConsent} - maxLength={15} - datalistId='testid-list' - datalist={tests ? tests.sort(byId).map((i: ITests) =>

+ {!pcrEnabled + ? <> + : <> + + {t('translation:test-type') + '*'} + + + + + setTestType(TestType.RAT)} + /> + + + + setTestType(TestType.PCR)} + required={true} + /> + + + + +
+ + } diff --git a/src/components/reports.tsx b/src/components/reports.tsx index 9f89131..a68ed71 100644 --- a/src/components/reports.tsx +++ b/src/components/reports.tsx @@ -33,6 +33,7 @@ import { useGetPDF, useGetPositiveForTimeRange } from '../api'; import { TestResult } from '../misc/enum'; import CardHeader from './modules/card-header.component'; import AppContext from '../misc/appContext'; +import { FormGroupInlineRadio } from './modules/form-group.component'; const Reports = (props: any) => { @@ -66,7 +67,7 @@ const Reports = (props: any) => { } props.setError({ error: error, message: msg, onCancel: context.navigation!.toLanding }); } - + const qtArchive = useGetPositiveForTimeRange(filterTestResult, startDate, endDate, undefined, handleError); const pdf = useGetPDF(selectedHash); @@ -156,58 +157,26 @@ const Reports = (props: any) => { - - - setFilterTestResult(undefined)} - /> - {t('translation:filter-none')} - - - - - setFilterTestResult(TestResult.POSITIVE)} - /> - {t('translation:result-positive')} - - - - - setFilterTestResult(TestResult.NEGATIVE)} - /> - {t('translation:result-negative')} - - - - - setFilterTestResult(TestResult.INVALID)} - /> - {t('translation:result-failed')} - - + setFilterTestResult(undefined)} + /> + setFilterTestResult(TestResult.POSITIVE)} + /> + setFilterTestResult(TestResult.NEGATIVE)} + /> + setFilterTestResult(TestResult.INVALID)} + /> diff --git a/src/components/show-patient-data.component.tsx b/src/components/show-patient-data.component.tsx index dd50c7f..7b75f00 100644 --- a/src/components/show-patient-data.component.tsx +++ b/src/components/show-patient-data.component.tsx @@ -71,10 +71,10 @@ const ShowPatientData = (props: any) => { if (quickTest.includePersData) { - setQrCodeValue(getQrCodeValueString(quickTest.uuId, quickTest.dccConsent, quickTest.personData.givenName, quickTest.personData.familyName, quickTest.personData.dateOfBirth)); + setQrCodeValue(getQrCodeValueString(quickTest.uuId, quickTest.dccConsent, quickTest.testType, quickTest.personData.givenName, quickTest.personData.familyName, quickTest.personData.dateOfBirth)); } if (quickTest.processingConsens) { - setQrCodeValue(getQrCodeValueString(quickTest.uuId, quickTest.dccConsent)); + setQrCodeValue(getQrCodeValueString(quickTest.uuId, quickTest.dccConsent, quickTest.testType)); } } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -90,6 +90,7 @@ const ShowPatientData = (props: any) => { React.useEffect(() => { if (qrCodeValue && qrCodeValue.length > 1) { + console.log(qrCodeValue); quickTest!.testResultHash = qrCodeValue[1]; } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/components/statistics.component.tsx b/src/components/statistics.component.tsx index b1c6bce..bec6b91 100644 --- a/src/components/statistics.component.tsx +++ b/src/components/statistics.component.tsx @@ -20,20 +20,29 @@ */ import React from 'react'; -import { Button, Card, Col, Fade, Form, Row } from 'react-bootstrap' +import { Button, Card, Col, Fade, Form, Row } from 'react-bootstrap'; import '../i18n'; import { useTranslation } from 'react-i18next'; +import { useKeycloak } from '@react-keycloak/web'; import CwaSpinner from './spinner/spinner.component'; -import { useStatistics } from '../api'; +import { useGetStatisticsFromTo, useStatistics } from '../api'; import CardHeader from './modules/card-header.component'; import AppContext from '../misc/appContext'; +import StatisticDataRow, { StatisticDateSelectionRow } from './modules/statistic-data.component'; +import StatisticData from '../misc/statistic-data'; +import utils from '../misc/utils'; +import { format } from "date-fns"; const Statistics = (props: any) => { const context = React.useContext(AppContext); const { t } = useTranslation(); + const { keycloak } = useKeycloak(); + + + const handleError = (error: any) => { let msg = ''; @@ -48,15 +57,83 @@ const Statistics = (props: any) => { props.setError({ error: error, message: msg, onCancel: context.navigation!.toLanding }); } + const [pcrEnabled, setPcrEnabled] = React.useState(false); const [statisticData, thisWeekStatisticData, thisMonthStatisticData] = useStatistics(undefined, handleError); - const [isInit, setIsInit] = React.useState(false) + const [statisticsResult, + getStatisticsFromTo] = useGetStatisticsFromTo(undefined, handleError); + const [isInit, setIsInit] = React.useState(false); + const [statisticRows, setStatisticRows] = React.useState([]); + const [dateValidFrom, setDateValidFrom] = React.useState(); + const [dateValidTo, setDateValidTo] = React.useState(); + + React.useEffect(() => { + + if (keycloak.idTokenParsed) { + setPcrEnabled(!!(keycloak.idTokenParsed as any).pcr_enabled); + } + + }, [keycloak]) React.useEffect(() => { - if (context.navigation && context.valueSets && statisticData) + if (context.navigation && context.valueSets && statisticData && thisWeekStatisticData && thisMonthStatisticData) { + setStatisticRows([ + { ...statisticData, label: t('translation:today'), key: Math.random() }, + { ...thisWeekStatisticData, label: t('translation:thisWeek'), key: Math.random() }, + { ...thisMonthStatisticData, label: t('translation:thisMonth'), key: Math.random() } + ]) setIsInit(true); - }, [context.navigation, context.valueSets, statisticData]) + } + }, [context.navigation, context.valueSets, statisticData, thisWeekStatisticData, thisMonthStatisticData]) + + React.useEffect(() => { + if (dateValidFrom || (dateValidFrom && dateValidTo)) { + + let startDate = new Date(dateValidFrom); + let endDate: Date; + + startDate.setUTCHours(0, 0, 0, 0); + + if (dateValidTo) { + endDate = new Date(dateValidTo); + endDate.setUTCHours(0, 0, 0, 0); + let day = endDate.getDate() + 1; + endDate.setDate(day); + } else { + let fromDate = new Date(startDate); + let day = fromDate.getDate() + 1; + endDate = new Date(fromDate.setDate(day)); + } + + getStatisticsFromTo(startDate, endDate); + } + }, [dateValidFrom, dateValidTo]) + + React.useEffect(() => { + if (statisticsResult) { + + let newLabel: string | undefined = dateValidFrom ? format(dateValidFrom, utils.pickerDateFormat) : undefined; + if (newLabel && dateValidTo) { + newLabel += ' - ' + format(dateValidTo, utils.pickerDateFormat); + } + + setStatisticRows([...statisticRows, { ...statisticsResult, label: newLabel, key: Math.random() }]); + } + }, [statisticsResult]) + + const handleNewStatisticRow = (dateValidFrom: Date, dateValidTo: Date) => { + setDateValidFrom(dateValidFrom); + setDateValidTo(dateValidTo); + } + + const handleDeleteStatisticRow = (key: number) => { + setStatisticRows((prevStatisticRows) => { + return prevStatisticRows.filter((row) => { + return row.key !== key + }); + }) + } return ( !(isInit && context && context.valueSets && statisticData && thisWeekStatisticData && thisMonthStatisticData) @@ -69,78 +146,9 @@ const Statistics = (props: any) => { content area with patient inputs and check box */} - - - {t('translation:today')} - - - - - {t('translation:totalTestCount')} - - - {t('translation:positiveTestCount')} - - - - - {statisticData!.totalTestCount} - - - {statisticData!.totalTestCount > 0 ? statisticData!.positiveTestCount + ' ( ' + (100 * statisticData!.positiveTestCount / statisticData!.totalTestCount).toFixed(2) + "% )" : undefined} - - - - -
- - - {t('translation:thisWeek')} - - - - - {t('translation:totalTestCount')} - - - {t('translation:positiveTestCount')} - - - - - {thisWeekStatisticData!.totalTestCount} - - - {thisWeekStatisticData!.totalTestCount > 0 ? thisWeekStatisticData!.positiveTestCount + ' ( ' + (100 * thisWeekStatisticData!.positiveTestCount / thisWeekStatisticData!.totalTestCount).toFixed(2) + "% )" : undefined} - - - - -
- - - {t('translation:thisMonth')} - - - - - {t('translation:totalTestCount')} - - - {t('translation:positiveTestCount')} - - - - - {thisMonthStatisticData!.totalTestCount} - - - {thisMonthStatisticData!.totalTestCount > 0 ? thisMonthStatisticData!.positiveTestCount + ' ( ' + (100 * thisMonthStatisticData!.positiveTestCount / thisMonthStatisticData!.totalTestCount).toFixed(2) + "% )" : undefined} - - - - +
+
{/* @@ -154,7 +162,7 @@ const Statistics = (props: any) => { block onClick={context.navigation!.toLanding} > - {t('translation:cancel')} + {t('translation:back')} diff --git a/src/misc/enum.tsx b/src/misc/enum.tsx index 54f82bc..02abffb 100644 --- a/src/misc/enum.tsx +++ b/src/misc/enum.tsx @@ -21,6 +21,9 @@ export enum TestResult { + PCR_NEGATIVE = 11, + PCR_POSITIVE = 12, + PCR_INVALID = 13, NEGATIVE = 6, POSITIVE = 7, INVALID = 8, @@ -31,3 +34,8 @@ export enum Sex { FEMALE = 'FEMALE', DIVERSE = 'DIVERSE', } + +export enum TestType { + RAT = 'LP217198-3', + PCR = 'LP6464-4' +} diff --git a/src/misc/qr-code-value.tsx b/src/misc/qr-code-value.tsx index 7daa843..10c9bd0 100644 --- a/src/misc/qr-code-value.tsx +++ b/src/misc/qr-code-value.tsx @@ -22,6 +22,7 @@ import IQuickTest from './quick-test'; import CryptoJS from 'crypto-js'; import vCardParser from './vCard-parser'; +import { TestType } from './enum'; export interface IQRCodeValue { fn?: string, @@ -34,9 +35,10 @@ export interface IQRCodeValue { hash?: string // SHA256 Hash } -const baseUrl = 'https://s.coronawarn.app?v=1#'; +const sBaseUrl = 'https://s.coronawarn.app?v=1#'; +const pBaseUrl = 'https://p.coronawarn.app?v=1#'; -export const getQrCodeValueString = (guid: string, dccConsent?: boolean, fn?: string, ln?: string, dob?: Date) => { +export const getQrCodeValueString = (guid: string, dccConsent?: boolean, testType?: string, fn?: string, ln?: string, dob?: Date) => { let encodedJson = ''; const value: IQRCodeValue = { @@ -57,6 +59,9 @@ export const getQrCodeValueString = (guid: string, dccConsent?: boolean, fn?: st encodedJson = buffer.toString('base64'); + const baseUrl = TestType.PCR === testType ? pBaseUrl : sBaseUrl; + + return [(baseUrl + encodedJson), value.hash]; } diff --git a/src/misc/quick-test.tsx b/src/misc/quick-test.tsx index df3aa9e..c5bb42c 100644 --- a/src/misc/quick-test.tsx +++ b/src/misc/quick-test.tsx @@ -51,6 +51,7 @@ export default interface IQuickTest { emailAddress?: string; testId?: string; testResultHash?: string; + testType?: string; dccConsent?: boolean; additionalInfo?: string; } \ No newline at end of file diff --git a/src/misc/statistic-data.tsx b/src/misc/statistic-data.tsx index e9552a8..02b02a9 100644 --- a/src/misc/statistic-data.tsx +++ b/src/misc/statistic-data.tsx @@ -22,4 +22,13 @@ export default interface StatisticData { totalTestCount: number; positiveTestCount: number; + pcrTestCount: number; + pcrPositiveTestCount: number; + ratTestCount: number; + ratPositiveTestCount: number; +} + +export interface DisplayStatisticData extends StatisticData { + label: string; + key: number; } \ No newline at end of file diff --git a/src/misc/test-result.tsx b/src/misc/test-result.tsx index 5534447..0e2a79a 100644 --- a/src/misc/test-result.tsx +++ b/src/misc/test-result.tsx @@ -25,6 +25,7 @@ export default interface ITestResult { result: TestResult; testBrandId?: string; testBrandName?: string; + pcrTestName?: string; dccTestManufacturerId?: string; dccTestManufacturerDescription?: string; } \ No newline at end of file diff --git a/src/misc/user.tsx b/src/misc/user.tsx index 4716fc9..e94b4e5 100644 --- a/src/misc/user.tsx +++ b/src/misc/user.tsx @@ -32,7 +32,7 @@ export interface IUser { password?: string, } -export interface IDisplayUser extends IUser{ +export interface IDisplayUser extends IUser { displayGroup?: string | null displayRole?: string | null } @@ -41,10 +41,10 @@ export interface IGroup { name: string, id: string, path: string, - children: IGroup[], + children: IGroup[], } -export interface IGroupNode { +export interface IGroupNode { group: IGroup, parentGroup?: string, level: number, @@ -55,6 +55,7 @@ export interface IGroupDetails { // bsnr?: string, name: string, pocDetails: string, + enablePcr: boolean, pocId: string, searchPortalConsent: boolean, website?: string,