Edit Draft Mission

This commit is contained in:
Nicolas
2025-04-28 09:50:58 +02:00
parent e1c3f51809
commit 64fcab59af
5 changed files with 126 additions and 45 deletions

View File

@@ -7,11 +7,18 @@ interface PannelStore {
setOpen: (isOpen: boolean) => void; setOpen: (isOpen: boolean) => void;
missionFormValues?: Partial<MissionOptionalDefaults>; missionFormValues?: Partial<MissionOptionalDefaults>;
setMissionFormValues: (values: Partial<MissionOptionalDefaults>) => void; setMissionFormValues: (values: Partial<MissionOptionalDefaults>) => void;
isEditingMission: boolean;
editingMissionId: string | null;
setEditingMission: (isEditing: boolean, missionId: string | null) => void;
} }
export const usePannelStore = create<PannelStore>((set) => ({ export const usePannelStore = create<PannelStore>((set) => ({
isOpen: false, // DEBUG, REMOVE LATER FOR PROD isOpen: false,
setOpen: (isOpen) => set({ isOpen }), setOpen: (isOpen) => set({ isOpen }),
missionFormValues: undefined, missionFormValues: undefined,
setMissionFormValues: (values) => set({ missionFormValues: values }), setMissionFormValues: (values) => set({ missionFormValues: values }),
isEditingMission: false,
editingMissionId: null,
setEditingMission: (isEditing, missionId) =>
set({ isEditingMission: isEditing, editingMissionId: missionId }),
})); }));

View File

@@ -2,6 +2,7 @@ import { useMissionsStore } from "_store/missionsStore";
import { Marker, useMap } from "react-leaflet"; import { Marker, useMap } from "react-leaflet";
import { DivIcon, Marker as LMarker, Popup as LPopup } from "leaflet"; import { DivIcon, Marker as LMarker, Popup as LPopup } from "leaflet";
import { useMapStore } from "_store/mapStore"; import { useMapStore } from "_store/mapStore";
import { usePannelStore } from "_store/pannelStore";
import { import {
Fragment, Fragment,
useCallback, useCallback,
@@ -18,6 +19,7 @@ import {
Minimize2, Minimize2,
Route, Route,
SmartphoneNfc, SmartphoneNfc,
PencilLine,
} from "lucide-react"; } from "lucide-react";
import { import {
calculateAnchor, calculateAnchor,
@@ -35,6 +37,7 @@ export const MISSION_STATUS_COLORS: Record<MissionState, string> = {
draft: "#0092b8", draft: "#0092b8",
running: "#155dfc", running: "#155dfc",
finished: "#155dfc", finished: "#155dfc",
attention: "rgb(186,105,0)",
}; };
export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = { export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = {
@@ -44,6 +47,7 @@ export const MISSION_STATUS_TEXT_COLORS: Record<MissionState, string> = {
}; };
const MissionPopupContent = ({ mission }: { mission: Mission }) => { const MissionPopupContent = ({ mission }: { mission: Mission }) => {
const { setEditingMission } = usePannelStore();
const setMissionMarker = useMapStore((state) => state.setOpenMissionMarker); const setMissionMarker = useMapStore((state) => state.setOpenMissionMarker);
const currentTab = useMapStore( const currentTab = useMapStore(
(state) => (state) =>
@@ -85,6 +89,7 @@ const MissionPopupContent = ({ mission }: { mission: Mission }) => {
(state) => state.setOpenMissionMarker, (state) => state.setOpenMissionMarker,
); );
const { anchor } = useSmartPopup(); const { anchor } = useSmartPopup();
const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
return ( return (
<> <>
@@ -167,8 +172,30 @@ const MissionPopupContent = ({ mission }: { mission: Mission }) => {
> >
<SmartphoneNfc className="text-sm" /> <SmartphoneNfc className="text-sm" />
</div> </div>
{mission.state === "draft" && (
<div <div
className="p-2 px-4 flex justify-center items-center cursor-pointer ml-auto" className="p-2 px-4 flex justify-center items-center cursor-pointer ml-auto"
style={{
backgroundColor: `${MISSION_STATUS_COLORS["attention"]}`,
borderBottom: "5px solid transparent",
}}
onClick={() => {
setMissionFormValues({
...mission,
state: "draft",
});
setEditingMission(true, mission.id);
setOpen(true);
}}
>
<PencilLine className="text-sm" />
</div>
)}
<div
className={cn(
"p-2 px-4 flex justify-center items-center cursor-pointer",
mission.state !== "draft" && "ml-auto",
)}
style={{ style={{
backgroundColor: `${MISSION_STATUS_COLORS[mission.state]}`, backgroundColor: `${MISSION_STATUS_COLORS[mission.state]}`,
borderBottom: borderBottom:

View File

@@ -28,7 +28,7 @@ import { useSession } from "next-auth/react";
const Einsatzdetails = ({ mission }: { mission: Mission }) => { const Einsatzdetails = ({ mission }: { mission: Mission }) => {
const { deleteMission } = useMissionsStore((state) => state); const { deleteMission } = useMissionsStore((state) => state);
const { setMissionFormValues } = usePannelStore((state) => state); const { setMissionFormValues, setOpen } = usePannelStore((state) => state);
return ( return (
<div className="p-4 text-base-content"> <div className="p-4 text-base-content">
<h2 className="flex items-center gap-2 text-lg font-bold mb-3"> <h2 className="flex items-center gap-2 text-lg font-bold mb-3">
@@ -81,6 +81,7 @@ const Einsatzdetails = ({ mission }: { mission: Mission }) => {
hpgLocationLng: undefined, hpgLocationLng: undefined,
state: "draft", state: "draft",
}); });
setOpen(true);
}} }}
> >
<Repeat2 size={18} /> Daten übernehmen <Repeat2 size={18} /> Daten übernehmen

View File

@@ -16,7 +16,10 @@ import { toast } from "react-hot-toast";
import { useMissionsStore } from "_store/missionsStore"; import { useMissionsStore } from "_store/missionsStore";
export const MissionForm = () => { export const MissionForm = () => {
const { isEditingMission, editingMissionId, setEditingMission } =
usePannelStore();
const createMission = useMissionsStore((state) => state.createMission); const createMission = useMissionsStore((state) => state.createMission);
const { deleteMission } = useMissionsStore((state) => state);
const session = useSession(); const session = useSession();
const defaultFormValues = React.useMemo( const defaultFormValues = React.useMemo(
() => () =>
@@ -41,7 +44,7 @@ export const MissionForm = () => {
resolver: zodResolver(MissionOptionalDefaultsSchema), resolver: zodResolver(MissionOptionalDefaultsSchema),
defaultValues: defaultFormValues, defaultValues: defaultFormValues,
}); });
const { missionFormValues } = usePannelStore((state) => state); const { missionFormValues, setOpen } = usePannelStore((state) => state);
useEffect(() => { useEffect(() => {
if (session.data?.user.id) { if (session.data?.user.id) {
@@ -290,6 +293,33 @@ export const MissionForm = () => {
<div className="form-control min-h-[140px] max-w-[320px]"> <div className="form-control min-h-[140px] max-w-[320px]">
<div className="flex gap-2"> <div className="flex gap-2">
{isEditingMission && editingMissionId ? (
<button
type="button"
className="btn btn-primary btn-block"
onClick={form.handleSubmit(
async (mission: MissionOptionalDefaults) => {
try {
deleteMission(editingMissionId);
const newMission = await createMission(mission);
toast.success(
`Einsatz ${newMission.id} erfolgreich aktualisiert`,
);
setEditingMission(false, null); // Reset editing state
form.reset(); // Reset the form
setOpen(false);
} catch (error) {
toast.error(
`Fehler beim Aktualisieren des Einsatzes: ${(error as Error).message}`,
);
}
},
)}
>
Änderungen speichern
</button>
) : (
<>
<button <button
type="submit" type="submit"
className="btn btn-warning" className="btn btn-warning"
@@ -299,6 +329,7 @@ export const MissionForm = () => {
const newMission = await createMission(mission); const newMission = await createMission(mission);
toast.success(`Einsatz ${newMission.id} erstellt`); toast.success(`Einsatz ${newMission.id} erstellt`);
// TODO: Einsatz alarmieren // TODO: Einsatz alarmieren
setOpen(false);
} catch (error) { } catch (error) {
toast.error( toast.error(
`Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`, `Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`,
@@ -318,6 +349,7 @@ export const MissionForm = () => {
const newMission = await createMission(mission); const newMission = await createMission(mission);
toast.success(`Einsatz ${newMission.id} erstellt`); toast.success(`Einsatz ${newMission.id} erstellt`);
form.reset(); form.reset();
setOpen(false);
} catch (error) { } catch (error) {
toast.error( toast.error(
`Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`, `Fehler beim Erstellen des Einsatzes: ${(error as Error).message}`,
@@ -328,6 +360,8 @@ export const MissionForm = () => {
> >
<BookmarkPlus className="h-5 w-5" /> Einsatz vorbereiten <BookmarkPlus className="h-5 w-5" /> Einsatz vorbereiten
</button> </button>
</>
)}
</div> </div>
</div> </div>
</form> </form>

View File

@@ -5,22 +5,34 @@ import { Rss, Trash2Icon } from "lucide-react";
export const Pannel = () => { export const Pannel = () => {
const { setOpen, setMissionFormValues } = usePannelStore(); const { setOpen, setMissionFormValues } = usePannelStore();
const { isEditingMission, editingMissionId, setEditingMission } =
usePannelStore();
return ( return (
<div className={cn("flex-1 max-w-[600px] z-9999999")}> <div className={cn("flex-1 max-w-[600px] z-9999999")}>
<div className="bg-base-100 min-h-screen h-full max-h-screen w-full overflow-auto"> <div className="bg-base-100 min-h-screen h-full max-h-screen w-full overflow-auto">
<div className="flex flex-row justify-between items-center p-4"> <div className="flex flex-row justify-between items-center p-4">
<h1 className="text-xl font-bold flex items-center gap-2"> <h1 className="text-xl font-bold flex items-center gap-2">
<Rss /> Neuer Einsatz <Rss /> {isEditingMission ? "Einsatz bearbeiten" : "Neuer Einsatz"}
</h1> </h1>
<div> <div>
<button <button
className="btn btn-ghost btn-sm mr-2 btn-warning" className="btn btn-ghost btn-sm mr-2 btn-warning"
onClick={() => setMissionFormValues({})} onClick={() => {
setMissionFormValues({});
setEditingMission(false, null);
}}
> >
<Trash2Icon size={18} /> <Trash2Icon size={18} />
</button> </button>
<button className="btn" onClick={() => setOpen(false)}> <button
className="btn"
onClick={() => {
setOpen(false);
setEditingMission(false, null);
setMissionFormValues({});
}}
>
Abbrechen Abbrechen
</button> </button>
</div> </div>