Adds manual ID number entry screen.
Former-commit-id: ffbc16d1223ac8cf4e144a06fd58d3d421f85903
This commit is contained in:
parent
0a1c5c3399
commit
5729def6ca
33
App.tsx
33
App.tsx
|
@ -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/>;
|
||||||
}
|
}
|
||||||
|
|
4
app.json
4
app.json
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue