186 lines
6.9 KiB
TypeScript
186 lines
6.9 KiB
TypeScript
import { AppLayout, BreadcrumbGroup, Button, Container, Flashbar, FlashbarProps, Form, FormField, Header, Link, SpaceBetween } from "@cloudscape-design/components";
|
|
import { useLoaderData, useSearchParams } from "react-router-dom";
|
|
import { DoorResponse } from "../types/DoorResponse";
|
|
import { useForm } from "react-hook-form";
|
|
import { InputTokenGroup } from "../components/InputTokenGroup";
|
|
import { ReactNode, useState } from "react";
|
|
|
|
export type DoorEditForm = DoorResponse & { pin: string, fallbackNumber: string, discordUser: string };
|
|
|
|
export const EditPage = () => {
|
|
const doorResponse = useLoaderData() as DoorResponse;
|
|
const door = doorResponse.id;
|
|
const { register, setValue, setError, getValues, watch, formState, clearErrors } = useForm<DoorEditForm>({
|
|
defaultValues: doorResponse,
|
|
mode: "all",
|
|
});
|
|
|
|
|
|
const [ alerts, setAlerts ] = useState<FlashbarProps.MessageDefinition[]>([]);
|
|
|
|
const dismissAlert = (id: string) => {
|
|
setAlerts(alerts => alerts.filter(alert => alert.id !== id));
|
|
}
|
|
|
|
|
|
const createAlert = (type: FlashbarProps.Type, content: ReactNode): FlashbarProps.MessageDefinition => {
|
|
const id = `${Math.random()}`;
|
|
return {
|
|
id,
|
|
type,
|
|
content,
|
|
dismissible: type !== 'in-progress',
|
|
onDismiss: () => dismissAlert(id),
|
|
}
|
|
}
|
|
|
|
const addAlert = (type: FlashbarProps.Type, content: ReactNode): string => {
|
|
const newAlert = createAlert(type, content);
|
|
setAlerts(alerts => [
|
|
newAlert,
|
|
...alerts,
|
|
]);
|
|
|
|
return newAlert.id as string;
|
|
}
|
|
|
|
|
|
const fallbackNumbers = watch("fallbackNumbers");
|
|
const discordUsers = watch("discordUsers");
|
|
|
|
const fallbackNumbersError = formState.errors.fallbackNumbers?.message;
|
|
const discordUsersError = formState.errors.discordUsers?.message;
|
|
|
|
return (
|
|
<AppLayout
|
|
contentType="form"
|
|
navigationHide
|
|
toolsHide
|
|
breadcrumbs={
|
|
<BreadcrumbGroup
|
|
items={[
|
|
{ text: 'Door', href: '#' },
|
|
{ text: door, href: `?edit&door=${door}` },
|
|
]}
|
|
/>
|
|
}
|
|
notifications={
|
|
<Flashbar
|
|
stackItems={true}
|
|
items={alerts}
|
|
/>
|
|
}
|
|
content={
|
|
<Form
|
|
actions={
|
|
<SpaceBetween direction="horizontal" size="xs">
|
|
<Button formAction="none" variant="link" href={`?door=${door}`}>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
variant="primary"
|
|
onClick={() => {
|
|
fetch(`/api/door/edit?door=${door}&newConfig=${encodeURIComponent(JSON.stringify(getValues()))}`)
|
|
.then(res => res.json())
|
|
.then(res => {
|
|
addAlert("success", `Created approval, check Discord notifcation from Doorman to confirm and approve the changes`);
|
|
})
|
|
}}
|
|
>
|
|
Submit
|
|
</Button>
|
|
</SpaceBetween>
|
|
}
|
|
header={<Header variant="h1">Edit Door</Header>}
|
|
>
|
|
<Container
|
|
header={
|
|
<Header variant="h2">
|
|
Door - {door}
|
|
</Header>
|
|
}
|
|
>
|
|
<SpaceBetween direction="vertical" size="l">
|
|
<FormField label="Buzzer Number (read-only)" constraintText="The phone number of your buzzer system">
|
|
<input readOnly {...register("buzzer")} />
|
|
</FormField>
|
|
<FormField label="PIN" constraintText={"The code to unlock the buzzer"}>
|
|
<input type="password" {...register("pin")}/>
|
|
</FormField>
|
|
<FormField label="Buzzer Code" constraintText={"The number that you dial on your buzzer"}>
|
|
<input {...register("buzzerCode")} />
|
|
</FormField>
|
|
<FormField label="Timeout" constraintText="Time in seconds for the door to remain unlocked">
|
|
<input type="number" {...register("timeout", {
|
|
valueAsNumber: true,
|
|
})} />
|
|
</FormField>
|
|
<FormField label="Unlock key" constraintText="Key to press to buzz up on Intercom">
|
|
<input {...register("pressKey")} />
|
|
</FormField>
|
|
<FormField
|
|
errorText={fallbackNumbersError}
|
|
label="Fallback numbers"
|
|
constraintText="Phone numbers to dial through in case door is not unlocked"
|
|
>
|
|
<InputTokenGroup
|
|
registerInput={register("fallbackNumber")}
|
|
tokenGroupProps={{ items: fallbackNumbers.map(n => ({ label: n }))}}
|
|
onAdd={() => {
|
|
clearErrors("fallbackNumbers");
|
|
setValue("fallbackNumbers", [...fallbackNumbers, getValues().fallbackNumber])
|
|
setValue("fallbackNumber", "")
|
|
}}
|
|
onDismiss={(i) => {
|
|
clearErrors("fallbackNumbers");
|
|
|
|
if (fallbackNumbers.length === 1) {
|
|
setError("fallbackNumbers", { message: "Can't remove last entry", type: "value" })
|
|
return;
|
|
}
|
|
fallbackNumbers.splice(i, 1);
|
|
setValue("fallbackNumbers", [...fallbackNumbers]);
|
|
}}
|
|
/>
|
|
</FormField>
|
|
<FormField
|
|
label="Discord Users"
|
|
errorText={discordUsersError}
|
|
constraintText={
|
|
<>
|
|
<Link fontSize={"body-s"} href="https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID#h_01HRSTXPS5H5D7JBY2QKKPVKNA">Discord users</Link> to receive notifications
|
|
</>
|
|
}
|
|
>
|
|
<InputTokenGroup
|
|
type="string"
|
|
registerInput={register("discordUser")}
|
|
tokenGroupProps={{ items: discordUsers.map(n => ({ label: n }))}}
|
|
onAdd={() => {
|
|
clearErrors("discordUsers");
|
|
setValue("discordUsers", [...discordUsers, getValues().discordUser])
|
|
setValue("discordUser", "")
|
|
}}
|
|
onDismiss={(i) => {
|
|
clearErrors("discordUsers");
|
|
|
|
if (discordUsers.length === 1) {
|
|
setError("discordUsers", { message: "Can't remove last entry" })
|
|
return;
|
|
}
|
|
discordUsers.splice(i, 1);
|
|
setValue("discordUsers", [...discordUsers]);
|
|
}}
|
|
/>
|
|
</FormField>
|
|
<FormField label="Welcome Message" constraintText="Message to display after a successful unlock">
|
|
<textarea {...register("greeting")}/>
|
|
</FormField>
|
|
</SpaceBetween>
|
|
</Container>
|
|
</Form>
|
|
}
|
|
/>
|
|
);
|
|
};
|