From b19cc864d32bec62062fef1cd799ec8838b19c86 Mon Sep 17 00:00:00 2001 From: nocnico Date: Mon, 21 Apr 2025 17:46:08 +0200 Subject: [PATCH 1/5] Add MissionForm WIP --- apps/dispatch/app/_components/Select.tsx | 119 ++++++++++++ apps/dispatch/app/_store/pannelStore.ts | 2 +- .../_components/pannel/MissionForm.tsx | 178 ++++++++++++++++++ .../dispatch/_components/pannel/Pannel.tsx | 16 +- 4 files changed, 308 insertions(+), 7 deletions(-) create mode 100644 apps/dispatch/app/_components/Select.tsx create mode 100644 apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx diff --git a/apps/dispatch/app/_components/Select.tsx b/apps/dispatch/app/_components/Select.tsx new file mode 100644 index 00000000..6752abb8 --- /dev/null +++ b/apps/dispatch/app/_components/Select.tsx @@ -0,0 +1,119 @@ +"use client"; +import { + FieldValues, + Path, + RegisterOptions, + UseFormReturn, +} from "react-hook-form"; +import SelectTemplate, { + Props as SelectTemplateProps, + StylesConfig, +} from "react-select"; +import { cn } from "helpers/cn"; +import dynamic from "next/dynamic"; +import { CSSProperties } from "react"; + +interface SelectProps + extends Omit { + label?: any; + name: Path; + form: UseFormReturn | any; + formOptions?: RegisterOptions; + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} + +const customStyles: StylesConfig = { + control: (provided) => ({ + ...provided, + backgroundColor: "var(--color-base-100)", + borderColor: "color-mix(in oklab, var(--color-base-content) 20%, #0000);", + borderRadius: "0.5rem", + padding: "0.25rem", + boxShadow: "none", + "&:hover": { + borderColor: "color-mix(in oklab, var(--color-base-content) 20%, #0000);", + }, + }), + option: (provided, state) => ({ + ...provided, + backgroundColor: state.isSelected ? "hsl(var(--p))" : "hsl(var(--b1))", + color: "var(--color-primary-content)", + "&:hover": { backgroundColor: "var(--color-base-200)" }, // DaisyUI secondary color + }), + multiValueLabel: (provided) => ({ + ...provided, + color: "var(--color-primary-content)", + }), + singleValue: (provided) => ({ + ...provided, + color: "var(--color-primary-content)", + }), + multiValue: (provided) => ({ + ...provided, + backgroundColor: "var(--color-base-300)", + }), + menu: (provided) => ({ + ...provided, + backgroundColor: "var(--color-base-100)", + borderRadius: "0.5rem", + }), +}; + +const SelectCom = ({ + name, + label = name, + placeholder = label, + form, + formOptions, + className, + ...inputProps +}: SelectProps) => { + return ( +
+ + {label} + + { + if (Array.isArray(newValue)) { + form.setValue(name, newValue.map((v: any) => v.value) as any, { + shouldDirty: true, + }); + } else { + form.setValue(name, newValue.value, { + shouldDirty: true, + }); + } + form.trigger(name); + form.Dirty; + }} + value={ + (inputProps as any)?.isMulti + ? (inputProps as any).options?.filter((o: any) => + form.watch(name)?.includes(o.value), + ) + : (inputProps as any).options?.find( + (o: any) => o.value === form.watch(name), + ) + } + styles={customStyles as any} + className={cn("w-full placeholder:text-neutral-600", className)} + placeholder={placeholder} + {...inputProps} + /> + {form.formState.errors[name]?.message && ( +

+ {form.formState.errors[name].message as string} +

+ )} +
+ ); +}; + +const SelectWrapper = (props: SelectProps) => ( + +); + +export const Select = dynamic(() => Promise.resolve(SelectWrapper), { + ssr: false, +}); diff --git a/apps/dispatch/app/_store/pannelStore.ts b/apps/dispatch/app/_store/pannelStore.ts index c60f76f6..615ffbbf 100644 --- a/apps/dispatch/app/_store/pannelStore.ts +++ b/apps/dispatch/app/_store/pannelStore.ts @@ -7,6 +7,6 @@ interface PannelStore { } export const usePannelStore = create((set) => ({ - isOpen: false, + isOpen: true, // DEBUG, REMOVE LATER FOR PROD setOpen: (isOpen) => set({ isOpen }), })); diff --git a/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx b/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx new file mode 100644 index 00000000..1e3dc6a9 --- /dev/null +++ b/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx @@ -0,0 +1,178 @@ +"use client"; +import React, { useState } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { MissionSchema } from "@repo/db/zod"; +import { Mission } from "@repo/db/zod"; +import { Trash2 } from "lucide-react"; +import { Select } from "_components/Select"; + +const clearBtn = () => { + return ( + + ); +}; + +const missionFormSchema = MissionSchema.pick({ + addressLat: true, + addressLng: true, + addressStreet: true, + addressCity: true, + addressZip: true, + missionCategory: true, + missionKeyword: true, + missionAdditionalInfo: true, + missionPatientInfo: true, +}); + +type MissionFormValues = z.infer; + +const dummyRettungsmittel = [ + "Christoph 31", + "Christoph 100", + "Christoph Berlin", + "Christophorus 1", +]; + +export const MissionForm: React.FC = () => { + const [missionCategory, setMissionCategory] = useState<"PRIMÄR" | "SEKUNDÄR">( + "PRIMÄR", + ); + const form = useForm({ + resolver: zodResolver(missionFormSchema), + defaultValues: { + addressLat: 0, + addressLng: 0, + missionCategory: "PRIMÄR", + }, + }); + const { control, register, handleSubmit, watch } = form; + + const onSubmit = (data: MissionFormValues) => { + console.log(data); + }; + + return ( +
+ {/* Koorinaten Section */} +
+

Koordinaten

+
+ + +
+
+ + {/* Adresse Section */} +
+

Adresse

+ +
+ + +
+ +
+ + {/* Rettungsmittel Section */} +
+

Rettungsmittel

+ ( + + setMissionCategory(e.target.value as "PRIMÄR" | "SEKUNDÄR") + } + > + + + + +