From 169a05ed8fc7b7dd560c9d6b5053fc3ae00dabbc Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Tue, 1 Jul 2025 00:58:26 -0700 Subject: [PATCH 1/2] Fixed Chat height --- apps/dispatch/app/_components/left/Chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dispatch/app/_components/left/Chat.tsx b/apps/dispatch/app/_components/left/Chat.tsx index 8ef1cbc4..b3a9a4bf 100644 --- a/apps/dispatch/app/_components/left/Chat.tsx +++ b/apps/dispatch/app/_components/left/Chat.tsx @@ -68,7 +68,7 @@ export const Chat = () => { {chatOpen && (
@@ -371,11 +358,12 @@ export const MissionForm = () => { form.setValue("hpgMissionString", e.target.value); const [name] = e.target.value.split(":"); const allHpgMissionTypes = keywords?.map((k) => k.hpgMissionTypes).flat(); + const missionAdditionalInfo = form.watch("missionAdditionalInfo"); if ( - !form.watch("missionAdditionalInfo") || + !missionAdditionalInfo || allHpgMissionTypes?.find((t) => { const [hpgName] = t.split(":"); - return hpgName === form.watch("missionAdditionalInfo"); + return hpgName === missionAdditionalInfo; }) ) { form.setValue("missionAdditionalInfo", name || ""); @@ -444,7 +432,7 @@ export const MissionForm = () => { try { console.log("Saving mission", mission.addressOSMways); const newMission = await saveMission(mission); - toast.success(`Einsatz ${newMission.id} erfolgreich aktualisiert`); + toast.success(`Einsatz ${newMission.publicId} aktualisiert`); setSearchElements([]); // Reset search elements setEditingMission(null); // Reset editing state form.reset(); // Reset the form diff --git a/apps/dispatch/app/_components/Select.tsx b/apps/dispatch/app/_components/Select.tsx index 60ea8e27..81e38ff4 100644 --- a/apps/dispatch/app/_components/Select.tsx +++ b/apps/dispatch/app/_components/Select.tsx @@ -1,16 +1,8 @@ "use client"; -import { FieldValues, Path, RegisterOptions, UseFormReturn } from "react-hook-form"; +import { FieldValues, Path } from "react-hook-form"; import SelectTemplate, { Props as SelectTemplateProps, StylesConfig } from "react-select"; import { cn } from "@repo/shared-components"; import dynamic from "next/dynamic"; -import { CSSProperties } from "react"; - -interface SelectProps extends Omit { - label?: any; - name: Path; - form: UseFormReturn | any; - formOptions?: RegisterOptions; -} const customStyles: StylesConfig = { control: (provided) => ({ @@ -28,7 +20,7 @@ const customStyles: StylesConfig = { ...provided, backgroundColor: state.isSelected ? "hsl(var(--p))" : "hsl(var(--b1))", color: "var(--color-primary-content)", - "&:hover": { backgroundColor: "var(--color-base-200)" }, // DaisyUI secondary color + "&:hover": { backgroundColor: "var(--color-base-200)" }, }), multiValueLabel: (provided) => ({ ...provided, @@ -46,53 +38,55 @@ const customStyles: StylesConfig = { ...provided, backgroundColor: "var(--color-base-100)", borderRadius: "0.5rem", + zIndex: 9999, }), }; -const SelectCom = ({ - name, - label = name, +interface SelectProps extends Omit { + label?: any; + value: any; + onChange: (value: any) => void; + error?: string; +} + +const SelectCom = ({ + label, placeholder = label, - form, - formOptions, + value, + onChange, + error, className, ...inputProps -}: SelectProps) => { +}: SelectProps) => { return ( -
+
{label} { - if (Array.isArray(newValue)) { - form.setValue(name, newValue.map((v: any) => v.value) as any, { - shouldDirty: true, - }); + if ((inputProps as any)?.isMulti) { + onChange(Array.isArray(newValue) ? newValue.map((v: any) => v.value) : []); } else { - form.setValue(name, newValue.value, { - shouldDirty: true, - }); + onChange(newValue ? newValue.value : null); } - 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)) + ? (inputProps as any).options?.filter( + (o: any) => Array.isArray(value) && value.includes(o.value), + ) + : (inputProps as any).options?.find((o: any) => o.value === value) } 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}

- )} + {error &&

{error}

}
); }; -const SelectWrapper = (props: SelectProps) => ; +const SelectWrapper = (props: SelectProps) => ; export const Select = dynamic(() => Promise.resolve(SelectWrapper), { ssr: false, diff --git a/apps/dispatch/app/_components/left/Chat.tsx b/apps/dispatch/app/_components/left/Chat.tsx index b3a9a4bf..79ae6b36 100644 --- a/apps/dispatch/app/_components/left/Chat.tsx +++ b/apps/dispatch/app/_components/left/Chat.tsx @@ -140,6 +140,7 @@ export const Chat = () => { {chat.notification && }
+ {/* So macht man kein overflow handeling, weiß ich. Aber es funktioniert... */} {chat.messages.map((chatMessage) => { const isSender = chatMessage.senderId === session.data?.user.id; return ( diff --git a/apps/dispatch/app/_components/map/BaseMaps.tsx b/apps/dispatch/app/_components/map/BaseMaps.tsx index 6c377602..3da4b31a 100644 --- a/apps/dispatch/app/_components/map/BaseMaps.tsx +++ b/apps/dispatch/app/_components/map/BaseMaps.tsx @@ -1,7 +1,7 @@ "use client"; import { usePannelStore } from "_store/pannelStore"; import { Control, Icon, LatLngExpression } from "leaflet"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { LayerGroup, LayersControl, @@ -73,9 +73,9 @@ const StationsLayer = ({ attribution }: { attribution: Control.Attribution }) => queryKey: ["stations"], queryFn: () => getStationsAPI(), }); + console.log("StationsLayer: stations", stations); const [selectedStations, setSelectedStations] = useState([]); - const [stationsWithIcon, setStationsWithIcon] = useState<(Station & { icon?: string })[]>([]); // Zustand für die Stationen mit Icon const attributionText = ""; const resetSelection = () => { @@ -94,27 +94,29 @@ const StationsLayer = ({ attribution }: { attribution: Control.Attribution }) => } }; - useEffect(() => { - // Erstelle die Icons für alle Stationen + const [stationsWithIcon, setStationsWithIcon] = useState<(Station & { icon: string })[]>([]); + useEffect(() => { + if (!stations) { + setStationsWithIcon([]); + return; + } const fetchIcons = async () => { - if (!stations) return; const urls = await Promise.all( stations.map(async (station) => { - return createCustomMarker(station.operator); + return await createCustomMarker(station.operator); }), ); - setStationsWithIcon(stations.map((station, index) => ({ ...station, icon: urls[index] }))); + setStationsWithIcon(stations.map((station, index) => ({ ...station, icon: urls[index]! }))); }; - fetchIcons(); }, [stations]); return ( - {stationsWithIcon.map((station) => { + {stationsWithIcon?.map((station) => { const coordinates: LatLngExpression = [station.latitude, station.longitude]; - const typeLabel = station.bosUse.charAt(0).toUpperCase(); + const typeLabel = station.bosUse?.charAt(0).toUpperCase(); return ( { }, }); - const stationsOptions = [ - ...(allStations - ?.filter((s) => !mission.missionStationIds.includes(s.id)) - ?.map((station) => ({ - label: station.bosCallsign, - value: station.id, - type: "station" as const, - isOnline: !!connectedAircrafts?.find((a) => a.stationId === station.id), - })) || []), - ...(!mission.hpgFireEngineState || mission.hpgFireEngineState === "NOT_REQUESTED" - ? [{ label: "Feuerwehr", value: "FW", type: "vehicle" as const }] - : []), - ...(!mission.hpgAmbulanceState || mission.hpgAmbulanceState === "NOT_REQUESTED" - ? [{ label: "Rettungsdienst", value: "RTW", type: "vehicle" as const }] - : []), - ...(!mission.hpgPoliceState || mission.hpgPoliceState === "NOT_REQUESTED" - ? [{ label: "Polizei", value: "POL", type: "vehicle" as const }] - : []), - ].sort((a, b) => { - // 1. Vehicles first - if (a.type === "vehicle" && b.type !== "vehicle") return -1; - if (a.type !== "vehicle" && b.type === "vehicle") return 1; - - // 2. Online stations before offline stations - if (a.type === "station" && b.type === "station") { - if (a.isOnline && !b.isOnline) return -1; - if (!a.isOnline && b.isOnline) return 1; - } - - // 3. Otherwise, sort alphabetically by label - return a.label.localeCompare(b.label); - }); - const dispatcherConnected = useDispatchConnectionStore((s) => s.status) === "connected"; const HPGVehicle = ({ state, name }: { state: HpgState; name: string }) => ( @@ -503,70 +471,57 @@ const Rettungsmittel = ({ mission }: { mission: Mission }) => { const connectedAircraft = connectedAircrafts?.find( (aircraft) => aircraft.stationId === station.id, ); + console.log("connectedAircraft", connectedAircraft); return (
  • - {connectedAircraft && ( - - {connectedAircraft.fmsStatus} - - )} - -
    - {station.bosCallsign} - {/* {item.min > 0 && ( - <> -
    - Ankunft in ca. {item.min} min - - )} */} -
    + + {connectedAircraft?.fmsStatus || "6"} + + + {station.bosCallsign} + {!connectedAircraft && ( + Kein Benutzer verbunden + )}
  • ); })} - {mission.hpgAmbulanceState && } - {mission.hpgFireEngineState && ( + {mission.hpgAmbulanceState && mission.hpgAmbulanceState !== "NOT_REQUESTED" && ( + + )} + {mission.hpgFireEngineState && mission.hpgFireEngineState !== "NOT_REQUESTED" && ( )} - {mission.hpgPoliceState && } + {mission.hpgPoliceState && mission.hpgPoliceState !== "NOT_REQUESTED" && ( + + )} {dispatcherConnected && (
    {/* TODO: make it a small multiselect */} - + selectedStations={mission.missionStationIds} + filterSelected + vehicleStates={{ + hpgAmbulanceState: mission.hpgAmbulanceState || undefined, + hpgFireEngineState: mission.hpgFireEngineState || undefined, + hpgPoliceState: mission.hpgPoliceState || undefined, + }} + />