Adds manual ID number entry screen.

Former-commit-id: ffbc16d1223ac8cf4e144a06fd58d3d421f85903
This commit is contained in:
Aaron Louie 2020-09-02 12:13:16 -04:00
parent 0a1c5c3399
commit 5729def6ca
7 changed files with 96 additions and 12 deletions

33
App.tsx
View File

@ -14,7 +14,7 @@ import {
import {expo as appExpo} from './app.json'; import {expo as appExpo} from './app.json';
import {CancelButton} from './components/Common'; import {CancelButton} from './components/Common';
import {BarCodeDisplay, PrintButton, PrintingMessage} from './components/Print'; import {BarCodeDisplay, PrintButton, PrintingMessage} from './components/Print';
import {ScanButton, Scanner} from './components/Scan'; import {IdNumberInput, InputIdButton, ScanButton, Scanner} from './components/Scan';
import {colors, styles} from './components/Styles'; import {colors, styles} from './components/Styles';
import {BarcodeScannerAppState} from './models/BarcodeScannerAppState'; import {BarcodeScannerAppState} from './models/BarcodeScannerAppState';
import {ElementProps, StateProps} from './models/ElementProps'; import {ElementProps, StateProps} from './models/ElementProps';
@ -29,6 +29,7 @@ export default function Main() {
const [barCodeData, setBarCodeData] = useState<string>(''); const [barCodeData, setBarCodeData] = useState<string>('');
const [date, setDate] = useState<Date>(new Date()); const [date, setDate] = useState<Date>(new Date());
const [locationStr, setLocationStr] = useState<string>('4321'); const [locationStr, setLocationStr] = useState<string>('4321');
const [errorMessage, setErrorMessage] = useState<string>('');
useEffect(() => { useEffect(() => {
BarCodeScanner.requestPermissionsAsync().then((value: PermissionResponse) => { BarCodeScanner.requestPermissionsAsync().then((value: PermissionResponse) => {
@ -41,20 +42,31 @@ export default function Main() {
}, []); }, []);
const _doNothing = () => {}; const _doNothing = () => {};
const _scan = () => setAppState(BarcodeScannerAppState.SCANNING); const _scan = () => {
setErrorMessage('');
setAppState(BarcodeScannerAppState.SCANNING);
};
const _inputIdNumber = () => {
setErrorMessage('');
setAppState(BarcodeScannerAppState.INPUT);
};
const _print = () => setAppState(BarcodeScannerAppState.PRINTING); const _print = () => setAppState(BarcodeScannerAppState.PRINTING);
const _home = () => setAppState(BarcodeScannerAppState.DEFAULT); const _home = () => setAppState(BarcodeScannerAppState.DEFAULT);
const _settings = () => setAppState(BarcodeScannerAppState.SETTINGS); const _settings = () => setAppState(BarcodeScannerAppState.SETTINGS);
const handleBarCodeScanned = (e: BarCodeEvent) => { const handleBarCodeScanned = (e: BarCodeEvent) => {
// Make sure the data is the right length. // Make sure the data is the right length.
// Scanned barcodes will be exactly 14 digits long.
// Manually-entered ID numbers will be exactly 9 digits long.
const barCodeString = e.data; const barCodeString = e.data;
const pattern = /^[\d]{14}$/; const pattern = /^[\d]{14}$|^[\d]{9}$/;
if (pattern.test(barCodeString)) { if (pattern.test(barCodeString)) {
setBarCodeData(e.data); const cardId = e.data.slice(0, 9);
setBarCodeData(cardId);
setDate(new Date()); setDate(new Date());
setAppState(BarcodeScannerAppState.SCANNED); setAppState(BarcodeScannerAppState.SCANNED);
} else { } else {
setErrorMessage(`The barcode data "${e.data}" is not from a valid ID card.`);
setAppState(BarcodeScannerAppState.ERROR); setAppState(BarcodeScannerAppState.ERROR);
} }
}; };
@ -63,12 +75,13 @@ export default function Main() {
return <View style={styles.fullScreen}> return <View style={styles.fullScreen}>
<View style={styles.container}> <View style={styles.container}>
<ScanButton onClicked={_scan}/> <ScanButton onClicked={_scan}/>
<InputIdButton onClicked={_inputIdNumber}/>
</View> </View>
<Snackbar <Snackbar
visible={appState === BarcodeScannerAppState.ERROR} visible={appState === BarcodeScannerAppState.ERROR}
onDismiss={_doNothing} onDismiss={_doNothing}
style={styles.error} style={styles.error}
>Something went wrong. Try again.</Snackbar> >{errorMessage === '' ? 'Something went wrong. Try again.' : errorMessage}</Snackbar>
</View> </View>
} }
@ -90,7 +103,7 @@ export default function Main() {
</View> </View>
} }
function SettingsModal(props: ElementProps): ReactElement { function SettingsScreen(props: ElementProps): ReactElement {
const [inputStr, setInputStr] = useState<string>(locationStr); const [inputStr, setInputStr] = useState<string>(locationStr);
const pattern = /^[\d]{4}$/; const pattern = /^[\d]{4}$/;
const hasErrors = () => { const hasErrors = () => {
@ -141,6 +154,7 @@ export default function Main() {
case BarcodeScannerAppState.DEFAULT: case BarcodeScannerAppState.DEFAULT:
return <View style={styles.container}> return <View style={styles.container}>
<ScanButton onClicked={_scan}/> <ScanButton onClicked={_scan}/>
<InputIdButton onClicked={_inputIdNumber}/>
</View>; </View>;
case BarcodeScannerAppState.PRINTED: case BarcodeScannerAppState.PRINTED:
return <SuccessMessage/>; return <SuccessMessage/>;
@ -160,8 +174,13 @@ export default function Main() {
onScanned={handleBarCodeScanned} onScanned={handleBarCodeScanned}
onCancel={_home} onCancel={_home}
/>; />;
case BarcodeScannerAppState.INPUT:
return <IdNumberInput
onScanned={handleBarCodeScanned}
onCancel={_home}
/>;
case BarcodeScannerAppState.SETTINGS: case BarcodeScannerAppState.SETTINGS:
return <SettingsModal/>; return <SettingsScreen/>;
default: default:
return <ErrorMessage/>; return <ErrorMessage/>;
} }

View File

@ -19,7 +19,7 @@
], ],
"ios": { "ios": {
"supportsTablet": true, "supportsTablet": true,
"bundleIdentifier": "com.ajlouie.uvacovid19testingkiosk" "bundleIdentifier": "com.sartography.uvacovid19testingkiosk"
}, },
"web": { "web": {
"favicon": "./assets/favicon.png" "favicon": "./assets/favicon.png"
@ -31,7 +31,7 @@
"web" "web"
], ],
"android": { "android": {
"package": "com.ajlouie.uvacovid19testingkiosk" "package": "com.sartography.uvacovid19testingkiosk"
} }
} }
} }

View File

@ -7,6 +7,7 @@ import {BarCodeProps, ButtonProps, PrintingProps} from '../models/ElementProps';
import {colors, styles} from './Styles'; import {colors, styles} from './Styles';
import AsyncStorage from '@react-native-community/async-storage'; import AsyncStorage from '@react-native-community/async-storage';
import * as Print from 'expo-print'; import * as Print from 'expo-print';
import {format} from 'date-fns'
enum PrintStatus { enum PrintStatus {
SAVING = 'SAVING', SAVING = 'SAVING',
@ -35,7 +36,7 @@ const _renderBarCodeRects = (props: PrintingProps): string => {
} }
const _propsToDataString = (props: BarCodeProps): string => { const _propsToDataString = (props: BarCodeProps): string => {
return `${props.id}-${props.date.getTime()}-${props.location}`; return props.id + format(props.date, 'yyyymmdd') + props.location;
} }
const _save = (props: PrintingProps): Promise<void> => { const _save = (props: PrintingProps): Promise<void> => {
@ -45,6 +46,8 @@ const _save = (props: PrintingProps): Promise<void> => {
date: props.date, date: props.date,
location: props.location, location: props.location,
}; };
console.log('storageKey', storageKey);
console.log('storageVal', storageVal);
return AsyncStorage.setItem(storageKey, JSON.stringify(storageVal)); return AsyncStorage.setItem(storageKey, JSON.stringify(storageVal));
} }

View File

@ -1,7 +1,7 @@
import {BarCodeScanner} from 'expo-barcode-scanner'; import {BarCodeEvent, BarCodeScanner} from 'expo-barcode-scanner';
import React, {ReactElement, useState} from 'react'; import React, {ReactElement, useState} from 'react';
import {View, Text} from 'react-native'; import {View, Text} from 'react-native';
import {Button} from 'react-native-paper'; import {Button, DefaultTheme, HelperText, Subheading, TextInput, Title} from 'react-native-paper';
import {ButtonProps, ScannerProps} from '../models/ElementProps'; import {ButtonProps, ScannerProps} from '../models/ElementProps';
import {colors, styles} from './Styles'; import {colors, styles} from './Styles';
@ -63,3 +63,58 @@ export const ScanButton = (props: ButtonProps): ReactElement => {
labelStyle={styles.btnLg} labelStyle={styles.btnLg}
>Scan Barcode</Button>; >Scan Barcode</Button>;
}; };
export const InputIdButton = (props: ButtonProps): ReactElement => {
return <Button
icon="keyboard"
mode="text"
color={colors.onBackground}
onPress={props.onClicked}
>Enter ID Number Manually</Button>;
};
export const IdNumberInput = (props: ScannerProps): ReactElement => {
const [inputStr, setInputStr] = useState<string>('');
const pattern = /^[\d]{9}$/;
const hasErrors = () => {
return !pattern.test(inputStr);
};
const onSubmit = () => {
console.log('onSubmit inputStr =', inputStr);
props.onScanned({type: '', data: inputStr});
}
return <View style={styles.settings}>
<Title style={styles.headingInverse}>Settings</Title>
<View style={{marginBottom: 10}}>
<Subheading style={{color: DefaultTheme.colors.text, marginBottom: 60}}>
Please double check that you have entered the number correctly. Entering an incorrect ID number will prevent patients from receiving their test results.
</Subheading>
<TextInput
label="ID #"
value={inputStr}
onChangeText={inputStr => setInputStr(inputStr)}
mode="outlined"
theme={DefaultTheme}
/>
<HelperText type="error" visible={hasErrors()}>
ID number must be exactly 9 digits. No other characters are allowed.
</HelperText>
<Button
icon="check"
mode="contained"
color={colors.primary}
style={{marginBottom: 10}}
disabled={hasErrors()}
onPress={onSubmit}
>Submit</Button>
<Button
icon="cancel"
mode="outlined"
color={colors.primary}
onPress={props.onCancel}
>Cancel</Button>
</View>
</View>;
};

View File

@ -3,6 +3,7 @@ export enum BarcodeScannerAppState {
DEFAULT = 'DEFAULT', DEFAULT = 'DEFAULT',
SCANNING = 'SCANNING', SCANNING = 'SCANNING',
SCANNED = 'SCANNED', SCANNED = 'SCANNED',
INPUT = 'INPUT',
PRINTING = 'PRINTING', PRINTING = 'PRINTING',
PRINTED = 'PRINTED', PRINTED = 'PRINTED',
ERROR = 'ERROR', ERROR = 'ERROR',

5
package-lock.json generated
View File

@ -5440,6 +5440,11 @@
"whatwg-url": "^7.0.0" "whatwg-url": "^7.0.0"
} }
}, },
"date-fns": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
"integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ=="
},
"dayjs": { "dayjs": {
"version": "1.8.34", "version": "1.8.34",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.34.tgz", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.34.tgz",

View File

@ -8,6 +8,7 @@
"dependencies": { "dependencies": {
"@react-native-community/async-storage": "^1.12.0", "@react-native-community/async-storage": "^1.12.0",
"@react-native-community/netinfo": "^5.9.6", "@react-native-community/netinfo": "^5.9.6",
"date-fns": "^2.16.1",
"expo": "~38.0.8", "expo": "~38.0.8",
"expo-barcode-scanner": "~8.2.1", "expo-barcode-scanner": "~8.2.1",
"expo-print": "~9.0.1", "expo-print": "~9.0.1",