change edit form to be cloudscape friendly

This commit is contained in:
Martin Dimitrov 2025-06-03 18:00:29 -07:00
parent 16f1e96f83
commit 4e730c4648
4 changed files with 44 additions and 24 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -15,6 +15,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.53.2",
"react-hook-form-cloudscape": "^1.7.7",
"react-qr-code": "^2.0.12",
"react-router-dom": "^6.15.0",
"typescript": "^4.4.2",

View File

@ -1,8 +1,10 @@
import { Button, ColumnLayout, Input, SpaceBetween, TokenGroup, TokenGroupProps } from "@cloudscape-design/components";
import { Button, ColumnLayout, Input, InputProps, SpaceBetween, TokenGroup, TokenGroupProps } from "@cloudscape-design/components";
import CInput from "react-hook-form-cloudscape/components/input";
export type InputTokenGroupProps = {
type?: React.HTMLInputTypeAttribute;
registerInput: any,
type?: InputProps.Type;
control: any;
name: string;
tokenGroupProps: TokenGroupProps,
onAdd: () => void,
onDismiss: (i: number) => void,
@ -12,9 +14,13 @@ export const InputTokenGroup = (props: InputTokenGroupProps) => {
return (
<SpaceBetween size="xs">
<ColumnLayout columns={2}>
<input
<CInput
type={props.type}
{...props.registerInput}
name={props.name}
rules={{
minLength: 1
}}
control={props.control}
/>
<Button onClick={() => props.onAdd()}>Add</Button>
</ColumnLayout>

View File

@ -1,16 +1,18 @@
import { AppLayout, BreadcrumbGroup, Button, Container, Flashbar, FlashbarProps, Form, FormField, Header, Link, SpaceBetween } from "@cloudscape-design/components";
import { AppLayout, BreadcrumbGroup, Button, Container, Flashbar, FlashbarProps, Form, FormField, Header, Input, 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";
import CInput from "react-hook-form-cloudscape/components/input";
import CTextArea from "react-hook-form-cloudscape/components/textarea";
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>({
const { register, setValue, setError, getValues, watch, formState, clearErrors, control } = useForm<DoorEditForm>({
defaultValues: doorResponse,
mode: "all",
});
@ -22,7 +24,6 @@ export const EditPage = () => {
setAlerts(alerts => alerts.filter(alert => alert.id !== id));
}
const createAlert = (type: FlashbarProps.Type, content: ReactNode): FlashbarProps.MessageDefinition => {
const id = `${Math.random()}`;
return {
@ -80,7 +81,12 @@ export const EditPage = () => {
<Button
variant="primary"
onClick={() => {
fetch(`/api/door/edit?door=${door}&newConfig=${encodeURIComponent(JSON.stringify(getValues()))}`)
const form = {
...getValues(),
timeout: parseInt("" + getValues("timeout"))
};
fetch(`/api/door/edit?door=${door}&newConfig=${encodeURIComponent(JSON.stringify(form))}`)
.then(res => res.json())
.then(res => {
addAlert("success", `Created approval, check Discord notifcation from Doorman to confirm and approve the changes`);
@ -102,21 +108,19 @@ export const EditPage = () => {
>
<SpaceBetween direction="vertical" size="l">
<FormField label="Buzzer Number (read-only)" constraintText="The phone number of your buzzer system">
<input readOnly {...register("buzzer")} />
<CInput readOnly disabled name="buzzer" control={control} />
</FormField>
<FormField label="PIN" constraintText={"The code to unlock the buzzer"}>
<input type="password" {...register("pin")}/>
<CInput type="password" name="pin" control={control} />
</FormField>
<FormField label="Buzzer Code" constraintText={"The number that you dial on your buzzer"}>
<input {...register("buzzerCode")} />
<CInput name="buzzerCode" control={control} />
</FormField>
<FormField label="Timeout" constraintText="Time in seconds for the door to remain unlocked">
<input type="number" {...register("timeout", {
valueAsNumber: true,
})} />
<CInput type="number" name="timeout" control={control} />
</FormField>
<FormField label="Unlock key" constraintText="Key to press to buzz up on Intercom">
<input {...register("pressKey")} />
<CInput name="pressKey" control={control} />
</FormField>
<FormField
errorText={fallbackNumbersError}
@ -124,12 +128,17 @@ export const EditPage = () => {
constraintText="Phone numbers to dial through in case door is not unlocked"
>
<InputTokenGroup
registerInput={register("fallbackNumber")}
name={"fallbackNumber"}
control={control}
tokenGroupProps={{ items: fallbackNumbers.map(n => ({ label: n }))}}
onAdd={() => {
const newValue = getValues().fallbackNumber;
if (newValue.length === 0) {
return;
}
clearErrors("fallbackNumbers");
setValue("fallbackNumbers", [...fallbackNumbers, getValues().fallbackNumber])
setValue("fallbackNumber", "")
setValue("fallbackNumbers", [...fallbackNumbers, newValue]);
setValue("fallbackNumber", "");
}}
onDismiss={(i) => {
clearErrors("fallbackNumbers");
@ -153,13 +162,17 @@ export const EditPage = () => {
}
>
<InputTokenGroup
type="string"
registerInput={register("discordUser")}
name={"discordUser"}
control={control}
tokenGroupProps={{ items: discordUsers.map(n => ({ label: n }))}}
onAdd={() => {
const newValue = getValues().discordUser;
if (newValue.length === 0) {
return;
}
clearErrors("discordUsers");
setValue("discordUsers", [...discordUsers, getValues().discordUser])
setValue("discordUser", "")
setValue("discordUsers", [...discordUsers, newValue]);
setValue("discordUser", "");
}}
onDismiss={(i) => {
clearErrors("discordUsers");
@ -174,7 +187,7 @@ export const EditPage = () => {
/>
</FormField>
<FormField label="Welcome Message" constraintText="Message to display after a successful unlock">
<textarea {...register("greeting")}/>
<CTextArea name="greeting" control={control} />
</FormField>
</SpaceBetween>
</Container>