From 5aabc3927801bddf45012519a51278732937b6fb Mon Sep 17 00:00:00 2001
From: fOppenheimer <80682747+fOppenheimer@users.noreply.github.com>
Date: Tue, 4 May 2021 13:35:04 +0200
Subject: [PATCH] initial mask for recovery (#46)
---
src/assets/SCSS/custom.scss | 4 +
src/assets/i18n/de/translation.json | 12 +-
src/assets/i18n/en/translation.json | 15 +-
src/components/landing-page.component.tsx | 1 +
.../record-recovery-cert-data.component.tsx | 551 ++++++++++++++++++
src/components/show-certificate.component.tsx | 3 +
src/misc/navigation.tsx | 4 +
src/routing.component.tsx | 8 +
8 files changed, 582 insertions(+), 16 deletions(-)
create mode 100644 src/components/record-recovery-cert-data.component.tsx
diff --git a/src/assets/SCSS/custom.scss b/src/assets/SCSS/custom.scss
index d863870..92d5d63 100644
--- a/src/assets/SCSS/custom.scss
+++ b/src/assets/SCSS/custom.scss
@@ -338,4 +338,8 @@ hr {
display: flex;
flex-direction: column;
height: 100%;
+ }
+
+ .space-five {
+ padding: 5px;
}
\ No newline at end of file
diff --git a/src/assets/i18n/de/translation.json b/src/assets/i18n/de/translation.json
index 12515b5..8b35e2f 100644
--- a/src/assets/i18n/de/translation.json
+++ b/src/assets/i18n/de/translation.json
@@ -67,7 +67,8 @@
"totalTestCount": "Gesamtanzahl der Tests",
"positiveTestCount": "Anzahl der positiven Tests",
"successfull-transferred": "Die Daten wurden erfolgreich übermittelt",
- "record-vaccination-cert-dat": "Impfpass erstellen",
+ "record-vaccination-cert-dat": "Impfzertifikat erstellen",
+ "record-recovery-cert-dat": "Genesungszertifikat erstellen",
"vaccination-cert": "Impfpass erstellen",
"identifierType": "Art des Ausweises",
"country": "Land",
@@ -90,11 +91,6 @@
"def-lot": "Eine unverwechselbare Kombination von Zahlen und / oder Buchstaben, die eine Charge spezifisch identifiziert",
"adm": "Verwaltungzentrum",
"def-adm": "Name oder Code des Verwaltungszentrums (z. B. Region Halland)",
- "PPN": "Ausweisnummer",
- "NN": "Nationale Personenkennung",
- "CZ": "Staatsbürgerschaftskartennummer",
- "HC": "Gesundheitskartennummer",
- "NI": "Nationale eindeutige individuelle Kennung",
- "MB": "Mitgliedsnummer",
- "NH": "National Health Plan Identifier"
+ "recovery-first-date": "First Positive Test"
+
}
\ No newline at end of file
diff --git a/src/assets/i18n/en/translation.json b/src/assets/i18n/en/translation.json
index 051c40e..8504b75 100644
--- a/src/assets/i18n/en/translation.json
+++ b/src/assets/i18n/en/translation.json
@@ -71,6 +71,7 @@
"successfull-transferred": "Data successfully transferred",
"record-vaccination-cert-dat": "Record vaccination certification",
"record-test-cert-dat": "Record test certification",
+ "record-recovery-cert-dat": "Record recovery certification",
"vaccination-cert": "Record Vaccination Certification",
"test-cert": "Record Test Certification",
"identifierType": "Identifier Type",
@@ -96,18 +97,16 @@
"certificateIssuer": "Certificate Issuer",
"adm": "Administering Centre",
"def-adm": "Name or code of administering centre (e.g. Region Halland)",
- "PPN": "Passport Number",
- "NN": "National Person Identifier",
- "CZ": "Citizenship Card Number",
- "HC": "Health Card number",
- "NI": "National Unique Individual Identifier",
- "MB": "Member Number",
- "NH": "National Health Plan Identifier",
"testManufacturers": "RAT Test name and manufacturer",
"testResult": "Test Result",
"testType": "Type of Test",
"testName": "NAA Test Name",
"sampleDateTime": "Date/Time of Sample Collection",
"testDateTime": "Date/Time of Test Result",
- "testCenter": "Testing Centre"
+ "testCenter": "Testing Centre",
+ "recovery-first-date": "First Positive Test Result",
+ "recovery-test-country": "Country of Test",
+ "cert-valid-from-go": "Certificate Valid From - To",
+ "valid-from": "Certificate Valid From",
+ "valid-to": "Certificate Valid To"
}
\ No newline at end of file
diff --git a/src/components/landing-page.component.tsx b/src/components/landing-page.component.tsx
index f8dabc0..b058df1 100644
--- a/src/components/landing-page.component.tsx
+++ b/src/components/landing-page.component.tsx
@@ -46,6 +46,7 @@ const LandingPage = (props: any) => {
+
)
diff --git a/src/components/record-recovery-cert-data.component.tsx b/src/components/record-recovery-cert-data.component.tsx
new file mode 100644
index 0000000..d2bd507
--- /dev/null
+++ b/src/components/record-recovery-cert-data.component.tsx
@@ -0,0 +1,551 @@
+/*
+ * eu-digital-green-certificates/ dgca-issuance-web
+ *
+ * (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, Card, Col, Form, FormControlProps, Row } from 'react-bootstrap';
+
+import '../i18n';
+import { useTranslation } from 'react-i18next';
+import useLocalStorage from '../misc/local-storage';
+
+import useNavigation from '../misc/navigation';
+import Spinner from './spinner/spinner.component';
+import { IdentifierType } from '../misc/enum';
+
+import DatePicker from "react-datepicker";
+import { registerLocale } from "react-datepicker";
+import "react-datepicker/dist/react-datepicker.css";
+//import de from 'date-fns/locale/de';
+
+import { EUDGC, RecoveryEntry, DiseaseAgentTargeted } from '../generated-files/dgc-combined-schema';
+import { useGetDiseaseAgents, IValueSet } from '../api';
+
+import schema from '../generated-files/DGC.combined-schema.json';
+import { Validator } from 'jsonschema';
+import utils from '../misc/utils';
+const validator = new Validator();
+const iso3311a2 = require('iso-3166-1-alpha-2');
+
+//registerLocale('de', de)
+
+
+const RecordRecoveryCertData = (props: any) => {
+
+ const navigation = useNavigation();
+ const { t } = useTranslation();
+
+ // data read from the API
+ const diseaseAgentsData = useGetDiseaseAgents();
+
+ const [isInit, setIsInit] = React.useState(false)
+
+ const [givenName, setGivenName] = React.useState('');
+ const [familyName, setFamilyName] = React.useState('');
+
+ const [standardisedGivenName, setStandardisedGivenName] = React.useState('');
+ const [standardisedFamilyName, setStandardisedFamilyName] = React.useState('');
+
+ const [dateOfBirth, setDateOfBirth] = React.useState();
+
+ const [disease, setDisease] = React.useState('');
+
+ const [diseasOptions, setDiseasOptions] = React.useState();
+
+ const [firstPositiveResultDate, setFirstPositiveResultDate] = React.useState();
+ const [certificateIssuer, setCertificateIssuer] = React.useState('');
+ const [testCountryCode, setTestCountryCode] = React.useState('');
+ const [ dateValidFrom, setDateValidFrom] = React.useState();
+ const [ dateValidTo, setDateValidTo] = React.useState();
+
+ const [isoCountryOptions, setIsoCountryOptions] = React.useState();
+ const [defaultTestCountryCode, setDefaultTestCountryCode] = useLocalStorage('defaultTestCountryCode', '');
+
+
+ React.useEffect(() => {
+ if (!props.eudgc) {
+ return;
+ }
+
+ const eudgc: EUDGC = props.eudgc;
+
+ setFamilyName(eudgc.nam!.fn!);
+ setStandardisedFamilyName(eudgc.nam!.fnt!);
+ setGivenName(eudgc.nam!.gn!);
+ setStandardisedGivenName(eudgc.nam!.gnt!);
+ setDateOfBirth(new Date(eudgc.dob!));
+ setDisease(eudgc.r![0].tg!);
+ setFirstPositiveResultDate(new Date(eudgc.r![0].fr!));
+ setTestCountryCode(eudgc.r![0].co!);
+ setCertificateIssuer(eudgc.r![0].is!);
+ setDateValidFrom(new Date(eudgc.r![0].df!))
+ setDateValidTo(new Date(eudgc.r![0].du!))
+ }, [props.eudgc]);
+
+ React.useEffect(() => {
+ setIso3311a2();
+ }, []);
+
+ React.useEffect(() => {
+ if(!testCountryCode) {
+ setTestCountryCode(defaultTestCountryCode);
+ }
+
+ }, [defaultTestCountryCode]);
+
+ React.useEffect(() => {
+ if (testCountryCode !== defaultTestCountryCode) {
+ setDefaultTestCountryCode(testCountryCode);
+ }
+
+ }, [testCountryCode]);
+
+
+ React.useEffect(() => {
+ if (navigation) {
+ setTimeout(setIsInit, 200, true);
+ }
+ }, [navigation]);
+
+
+ React.useEffect(() => {
+ if (diseaseAgentsData) {
+ const options = getOptionsForValueSet(diseaseAgentsData)
+ setDiseasOptions(options);
+ }
+ }, [diseaseAgentsData])
+
+ const getOptionsForValueSet = (valueSet: IValueSet): JSX.Element[] => {
+ const result: JSX.Element[] = [];
+ for (const key of Object.keys(valueSet)) {
+ result.push()
+ }
+
+ return result;
+ }
+
+ const setIso3311a2 = () => {
+ const options: JSX.Element[] = [];
+ const codes: string[] = iso3311a2.getCodes().sort();
+
+ // options.push();
+
+ for (const code of codes) {
+ options.push()
+ }
+
+ setIsoCountryOptions(options);
+ }
+
+ const handleError = (error: any) => {
+ let msg = '';
+
+ if (error) {
+ msg = error.message
+ }
+ props.setError({ error: error, message: msg, onCancel: navigation!.toLanding });
+ }
+
+ const handleStandardisedNameChanged = (changedValue: string, setStandardisedName: (value: string) => void) => {
+ const upperCaseChangedValue = changedValue.toUpperCase();
+
+ if (utils.isStandardisedNameValid(upperCaseChangedValue)) {
+ setStandardisedName(upperCaseChangedValue);
+ }
+ }
+
+ const handleDateOfBirthChange = (evt: Date | [Date, Date] | null) => {
+ const date = handleDateChange(evt);
+ setDateOfBirth(date);
+ }
+
+ const handleFirstPositiveResultDate = (evt: Date | [Date, Date] | null) => {
+ const date = handleDateChange(evt);
+ setFirstPositiveResultDate(date);
+ }
+
+ const handleDateValidFrom = (evt: Date | [Date, Date] | null) => {
+ const date = handleDateChange(evt);
+ setDateValidFrom(date);
+ }
+
+ 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;
+ }
+
+ const handleCancel = () => {
+ props.setEudgc(undefined);
+ navigation?.toLanding();
+ }
+
+ const handleSubmit = (event: React.FormEvent) => {
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ const form = event.currentTarget;
+
+ if (form.checkValidity()) {
+
+ const r: RecoveryEntry = {
+ tg: disease,
+ fr: firstPositiveResultDate!.toISOString().split('T')[0],
+ co: testCountryCode,
+ is: certificateIssuer,
+ df: dateValidFrom!.toISOString().split('T')[0],
+ du: dateValidTo!.toISOString().split('T')[0],
+ ci: ''
+ };
+
+ const eudgc: EUDGC = {
+ ver: '1.0.0',
+ nam: {
+ fn: familyName,
+ fnt: standardisedFamilyName!,
+ gn: givenName,
+ gnt: standardisedGivenName
+ },
+ dob: dateOfBirth!.toISOString().split('T')[0],
+ r: [r]
+ }
+
+ var result = validator.validate(eudgc, schema);
+
+ if (result.valid) {
+ //console.log(JSON.stringify(eudgc));
+
+ props.setEudgc(eudgc);
+ setTimeout(navigation!.toShowCert, 200);
+ }
+ else {
+ console.error(result);
+ props.setError({ error: result, message: result.errors[0].message, onCancel: navigation!.toLanding });
+ }
+ }
+ }
+
+ const formatDate = (date: Date): string => `${date.toISOString().substr(0, 10)}`;
+
+
+ return (
+ !isInit ? :
+ <>
+
+
+
+
+ {/*
+ header with title and id card query
+ */}
+
+
+ {/*
+ content area with patient inputs and check box
+ */}
+
+
+ {/* first name input */}
+
+ {t('translation:first-name') + '*'}
+
+
+ setGivenName(event.target.value)}
+ placeholder={t('translation:first-name')}
+ type='text'
+ required
+ maxLength={50}
+ />
+
+
+
+ {/* name input */}
+
+ {t('translation:name') + '*'}
+
+
+ setFamilyName(event.target.value)}
+ placeholder={t('translation:name')}
+ type='text'
+ required
+ maxLength={50}
+ />
+
+
+
+
+
+ {/* standardised first name input */}
+
+ {t('translation:standardised-first-name') + '*'}
+
+
+ handleStandardisedNameChanged(evt.target.value, setStandardisedGivenName)}
+ placeholder={t('translation:standardised-first-name')}
+ type='text'
+ required
+ pattern={utils.pattern.standardisedName}
+ maxLength={50}
+ />
+
+
+
+ {/*standardised name input */}
+
+ {t('translation:standardised-name') + '*'}
+
+
+ handleStandardisedNameChanged(evt.target.value, setStandardisedFamilyName)}
+ placeholder={t('translation:standardised-name')}
+ type='text'
+ required
+ pattern={utils.pattern.standardisedName}
+ maxLength={50}
+ />
+
+
+
+
+
+ {/* date of birth input */}
+
+ {t('translation:date-of-birth') + '*'}
+
+
+
+
+
+
+
+
+ {/* combobox disease */}
+
+ {t('translation:disease-agent') + '*'}
+
+
+ setDisease(event.target.value)}
+ placeholder={t('translation:def-disease-agent')}
+ required
+ >
+
+ {diseasOptions}
+
+
+
+
+
+
+ {/* Date of First Positive Test Result */}
+
+ {t('translation:recovery-first-date') + '*'}
+
+
+
+
+
+
+ {/* Combobox for the vaccin countries in iso-3166-1-alpha-2 */}
+
+ {t('translation:recovery-test-country') + '*'}
+
+
+ setTestCountryCode(event.target.value)}
+ placeholder={t('translation:country')}
+ required
+ >
+
+ {isoCountryOptions}
+
+
+
+
+
+
+
+
+ {/* certificateIssuer */}
+
+ {t('translation:certificateIssuer') + '*'}
+
+
+ setCertificateIssuer(event.target.value)}
+ placeholder={t('translation:certificateIssuer')}
+ type='text'
+ required
+ maxLength={50}
+ />
+
+
+
+ {/* Date: Certificate Valid From - To */}
+
+ {t('translation:cert-valid-from-go') + '*'}
+
+
+
+ { '-'}
+
+
+
+
+
+
+
+ {/*
+ footer with clear and nex button
+ */}
+
+
+
+
+ >
+ )
+}
+
+export default RecordRecoveryCertData;
\ No newline at end of file
diff --git a/src/components/show-certificate.component.tsx b/src/components/show-certificate.component.tsx
index b50d777..6b0d002 100644
--- a/src/components/show-certificate.component.tsx
+++ b/src/components/show-certificate.component.tsx
@@ -134,6 +134,9 @@ const ShowCertificate = (props: any) => {
if (eudgc.t) {
navigation!.toRecordTest();
}
+ if (eudgc.t) {
+ navigation!.toRecordRecovery();
+ }
if (eudgc.r) {
navigation!.toLanding();
}
diff --git a/src/misc/navigation.tsx b/src/misc/navigation.tsx
index 8d538e6..8438407 100644
--- a/src/misc/navigation.tsx
+++ b/src/misc/navigation.tsx
@@ -33,6 +33,7 @@ export interface INavigation {
toLanding: () => void,
toRecordVac: () => void,
toRecordTest: () => void,
+ toRecordRecovery: () => void,
toShowCert: () => void
}
@@ -45,6 +46,7 @@ export const useRoutes = () => {
landing: basePath,
recordVac: basePath + '/record/vac',
recordTest: basePath + '/record/test',
+ recordRecovery: basePath + '/record/recovery',
showCert: basePath + '/record/show'
}
@@ -68,6 +70,7 @@ export const useNavigation = () => {
c.landing = routes.landing.replace(':mandant', mandant as string);
c.recordVac = routes.recordVac.replace(':mandant', mandant as string);
c.recordTest = routes.recordTest.replace(':mandant', mandant as string);
+ c.recordRecovery = routes.recordRecovery.replace(':mandant', mandant as string);
c.showCert = routes.showCert.replace(':mandant', mandant as string);
setCalculatedRoutes(c);
@@ -84,6 +87,7 @@ export const useNavigation = () => {
toLanding: () => { history.push(calculatedRoutes.landing); },
toRecordVac: () => { history.push(calculatedRoutes.recordVac); },
toRecordTest: () => { history.push(calculatedRoutes.recordTest); },
+ toRecordRecovery: () => { history.push(calculatedRoutes.recordRecovery); },
toShowCert: () => { history.push(calculatedRoutes.showCert); }
}
diff --git a/src/routing.component.tsx b/src/routing.component.tsx
index 6338ae2..0a7908b 100644
--- a/src/routing.component.tsx
+++ b/src/routing.component.tsx
@@ -40,6 +40,7 @@ import RecordVaccinationCertData from './components/record-vaccination-cert-data
import ShowCertificate from './components/show-certificate.component';
import { EUDGC } from './generated-files/dgc-combined-schema';
import RecordTestCertData from './components/record-test-cert-data.component';
+import RecordRecoveryCertData from './components/record-recovery-cert-data.component';
const Routing = (props: any) => {
@@ -102,6 +103,13 @@ const Routing = (props: any) => {
+
+
+
+