submit by hitting next
All checks were successful
Build and push image for doorman / docker (push) Successful in 55s
Build and push image for doorman / deploy-portainer (push) Successful in 25s

This commit is contained in:
Martin Dimitrov 2024-03-02 15:40:29 -08:00
parent c956db367f
commit 672b62b3ef
2 changed files with 50 additions and 29 deletions

View File

@ -1,5 +1,5 @@
import { Button, FormField, Input, SpaceBetween } from "@cloudscape-design/components"; import { Button, FormField, Icon, Input, SpaceBetween } from "@cloudscape-design/components";
import { useState } from "react"; import { useEffect, useState } from "react";
// COPIED FROM SERVER // COPIED FROM SERVER
export enum IAuthMode { export enum IAuthMode {
@ -13,31 +13,38 @@ export interface IAuthComponentProps {
authMode: IAuthMode, authMode: IAuthMode,
secret: string | null; secret: string | null;
onUnlock: () => void; onUnlock: () => void;
onError: (res: any) => void; onError?: (res: any) => void;
runCheck?: number;
} }
export const AuthComponent = ({ door, secret, authMode, onError, onUnlock }: IAuthComponentProps) => { export const AuthComponent = ({ door, secret, authMode, onError, onUnlock, runCheck }: IAuthComponentProps) => {
const [ key, setKey ] = useState(secret); const [ key, setKey ] = useState(secret);
const [ error, setError ] = useState("");
useEffect(() => {
if (runCheck) {
onSubmit();
}
}, [runCheck])
const onSubmit = () => {
fetch(`/api/door/${door}/auth?key=${key}&rotatingKey=${key}`)
.then(async res => {
if (res.status !== 200) {
setError("Incorrect PIN");
onError && onError(await res.json());
return;
}
onUnlock();
})
}
return ( return (
<> <>
<SpaceBetween size='l'> <SpaceBetween size='l'>
<FormField label={"PIN"} constraintText={"Enter the PIN to unlock the door"}> <FormField errorText={error} label={"PIN"} constraintText={"Enter the PIN to unlock the door"}>
<Input readOnly={secret !== null} value={key || ""} onChange={({ detail }) => setKey(detail.value)} /> <Input readOnly={secret !== null} value={key || ""} onChange={({ detail }) => setKey(detail.value)} />
</FormField> </FormField>
<Button
onClick={() =>
fetch(`/api/door/${door}/auth?key=${key}&rotatingKey=${key}`)
.then(async res => {
if (res.status !== 200) {
onError(await res.json());
return;
}
onUnlock();
})
}
>
Unlock the door
</Button>
</SpaceBetween> </SpaceBetween>
</> </>
); );

View File

@ -35,12 +35,16 @@ export function DoorPage() {
const secret = searchParams.get('key') || searchParams.get('rotatingKey'); const secret = searchParams.get('key') || searchParams.get('rotatingKey');
const [ alerts, setAlerts ] = useState<FlashbarProps.MessageDefinition[]>([]); const [ alerts, setAlerts ] = useState<FlashbarProps.MessageDefinition[]>([]);
const [ polling, setPolling ] = useState(false); const [ polling, setPolling ] = useState(false);
const [ inProgressAlert, setInProgressAlert ] = useState(""); const [ submitPin, setSubmitPin ] = useState(0);
const dismissAlert = (id: string) => { const dismissAlert = (id: string) => {
setAlerts(alerts => alerts.filter(alert => alert.id !== id)); setAlerts(alerts => alerts.filter(alert => alert.id !== id));
} }
const dismissInProgressAlerts = () => {
setAlerts(alerts => alerts.filter(alert => alert.type !== 'in-progress'));
}
const createAlert = (type: FlashbarProps.Type, content: ReactNode): FlashbarProps.MessageDefinition => { const createAlert = (type: FlashbarProps.Type, content: ReactNode): FlashbarProps.MessageDefinition => {
const id = `${Math.random()}`; const id = `${Math.random()}`;
return { return {
@ -54,7 +58,7 @@ export function DoorPage() {
const addAlert = (type: FlashbarProps.Type, content: ReactNode): string => { const addAlert = (type: FlashbarProps.Type, content: ReactNode): string => {
const newAlert = createAlert(type, content); const newAlert = createAlert(type, content);
setAlerts([ setAlerts(alerts => [
newAlert, newAlert,
...alerts, ...alerts,
]); ]);
@ -77,13 +81,16 @@ export function DoorPage() {
setStep(3); setStep(3);
setPolling(false); setPolling(false);
setAlerts(alerts => setAlerts(alerts =>
[createAlert("success", "Buzzer successfully unlocked"), ...alerts.filter(alert => alert.id !== inProgressAlert)] [createAlert("success", "Buzzer successfully unlocked"), ...alerts.filter(alert => alert.type !== 'in-progress')]
); );
} }
}, 1000); }, 1000);
return () => clearInterval(timer); return () => {
clearInterval(timer);
dismissInProgressAlerts();
}
}, [polling]); }, [polling]);
return ( return (
@ -131,8 +138,15 @@ export function DoorPage() {
}} }}
activeStepIndex={step} activeStepIndex={step}
onNavigate={({ detail }) => { onNavigate={({ detail }) => {
if (detail.requestedStepIndex < step) {
dismissInProgressAlerts();
setSubmitPin(0);
setPolling(false);
setStep(detail.requestedStepIndex);
return;
}
if (detail.requestedStepIndex === 2) { if (detail.requestedStepIndex === 2) {
addAlert("error", "You must unlock the door to proceed"); setSubmitPin(submitPin + 1);
return; return;
} }
@ -144,6 +158,8 @@ export function DoorPage() {
}} }}
onCancel={() => { onCancel={() => {
setStep(0); setStep(0);
dismissInProgressAlerts();
setSubmitPin(0);
}} }}
steps={[ steps={[
{ {
@ -174,14 +190,13 @@ export function DoorPage() {
> >
<AuthComponent <AuthComponent
authMode={IAuthMode.FIXED_PIN} authMode={IAuthMode.FIXED_PIN}
runCheck={submitPin}
door={door} door={door}
secret={secret} secret={secret}
onError={(res) => {
addAlert("error", "Authentication failed, double check the credentials");
}}
onUnlock={() => { onUnlock={() => {
setStep(2); setStep(2);
const inprogAlert = addAlert("in-progress", ( dismissInProgressAlerts();
addAlert("in-progress", (
<CountdownBar <CountdownBar
timeSeconds={doorResponse.timeout} timeSeconds={doorResponse.timeout}
onExpiry={() => { onExpiry={() => {
@ -192,7 +207,6 @@ export function DoorPage() {
/> />
)); ));
setInProgressAlert(inprogAlert);
setPolling(true); setPolling(true);
}} }}
/> />