From ada041bd4a1fb9c0e49c2bc638370fdf1650bbfb Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Wed, 1 Oct 2025 22:23:27 +0200 Subject: [PATCH] Falsche Zeitzone bei Buchungen und HTTP status codes als Fehlermeldung behoben --- apps/hub/app/(app)/_querys/bookings.ts | 2 + .../app/_components/BookingTimelineModal.tsx | 4 + apps/hub/app/_components/NewBookingModal.tsx | 100 ++++++++++-------- apps/hub/app/_components/ui/DateInput.tsx | 2 +- 4 files changed, 64 insertions(+), 44 deletions(-) diff --git a/apps/hub/app/(app)/_querys/bookings.ts b/apps/hub/app/(app)/_querys/bookings.ts index b13805d2..44d90ca9 100644 --- a/apps/hub/app/(app)/_querys/bookings.ts +++ b/apps/hub/app/(app)/_querys/bookings.ts @@ -20,10 +20,12 @@ export const getBookingsAPI = async (filter: Prisma.BookingWhereInput) => { export const createBookingAPI = async (booking: Omit) => { const response = await axios.post("/api/bookings", booking); + console.log("Response from createBookingAPI:", response); if (response.status !== 201) { console.error("Error creating booking:", response); throw new Error("Failed to create booking"); } + console.log("Booking created:", response.data); return response.data; }; diff --git a/apps/hub/app/_components/BookingTimelineModal.tsx b/apps/hub/app/_components/BookingTimelineModal.tsx index f3205ee1..34cb1bdc 100644 --- a/apps/hub/app/_components/BookingTimelineModal.tsx +++ b/apps/hub/app/_components/BookingTimelineModal.tsx @@ -7,6 +7,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { deleteBookingAPI, getBookingsAPI } from "(app)/_querys/bookings"; import { Button } from "@repo/shared-components"; import { formatTimeRange } from "../../helper/timerange"; +import toast from "react-hot-toast"; interface BookingTimelineModalProps { isOpen: boolean; @@ -75,6 +76,9 @@ export const BookingTimelineModal = ({ await deleteBookingAPI(bookingId); queryClient.invalidateQueries({ queryKey: ["bookings"] }); }, + onSuccess: () => { + toast.success("Buchung erfolgreich gelöscht"); + }, }); // Check if user can create bookings diff --git a/apps/hub/app/_components/NewBookingModal.tsx b/apps/hub/app/_components/NewBookingModal.tsx index fa0e0593..b1a81cfc 100644 --- a/apps/hub/app/_components/NewBookingModal.tsx +++ b/apps/hub/app/_components/NewBookingModal.tsx @@ -9,6 +9,10 @@ import { getStationsAPI } from "(app)/_querys/stations"; import { createBookingAPI } from "(app)/_querys/bookings"; import { Button } from "@repo/shared-components"; import { useRouter } from "next/navigation"; +import { AxiosError } from "axios"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { DateInput } from "_components/ui/DateInput"; interface NewBookingFormData { type: "STATION" | "LST_01" | "LST_02" | "LST_03" | "LST_04"; @@ -41,13 +45,46 @@ export const NewBookingModal = ({ const { mutate: createBooking, isPending: isCreateBookingLoading } = useMutation({ mutationKey: ["createBooking"], mutationFn: createBookingAPI, - + onMutate() { + // Optionally show a loading toast or indicator + toast.loading("Buchung wird erstellt...", { id: "createBooking" }); + }, + onError(error: AxiosError) { + const errorData = error.response?.data as { error?: string }; + toast.error(errorData?.error || "Fehler beim Erstellen der Buchung", { id: "createBooking" }); + }, onSuccess: () => { + toast.success("Buchung erfolgreich erstellt", { id: "createBooking" }); queryClient.invalidateQueries({ queryKey: ["bookings"] }); onBookingCreated(); + onClose(); + router.refresh(); }, }); + const newBookingSchema = z + .object({ + type: z.enum(["STATION", "LST_01", "LST_02", "LST_03", "LST_04"], { + message: "Bitte wähle einen Typ aus", + }), + stationId: z.number().optional(), + startTime: z.string(), + endTime: z.string(), + title: z.string().optional(), + description: z.string().optional(), + }) + .superRefine((data, ctx) => { + const start = new Date(data.startTime); + const end = new Date(data.endTime); + if (end <= start) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Endzeit muss nach der Startzeit liegen", + path: ["endTime"], + }); + } + }); + const { register, handleSubmit, @@ -55,7 +92,9 @@ export const NewBookingModal = ({ setValue, reset, formState: { errors }, - } = useForm(); + } = useForm({ + resolver: zodResolver(newBookingSchema), + }); const selectedType = watch("type"); const hasDISPOPermission = userPermissions.includes("DISPO"); @@ -81,28 +120,6 @@ export const NewBookingModal = ({ } }, [isOpen, reset, setValue]); - const onSubmit = async (data: NewBookingFormData) => { - try { - // Validate that end time is after start time - if (new Date(data.endTime) <= new Date(data.startTime)) { - toast.error("Endzeit muss nach der Startzeit liegen"); - return; - } - - const response = await createBooking(data); - - console.log("Booking created:", response); - - toast.success("Buchung erfolgreich erstellt!"); - onBookingCreated(); - onClose(); - router.refresh(); - } catch (error) { - console.error("Error creating booking:", error); - toast.error("Fehler beim Erstellen der Buchung"); - } - }; - if (!isOpen) return null; return ( @@ -120,16 +137,13 @@ export const NewBookingModal = ({ {/* Form */} -
+ createBooking(data))} className="space-y-6"> {/* Resource Type Selection */}
- {hasDISPOPermission && ( @@ -186,19 +200,16 @@ export const NewBookingModal = ({ Startzeit * - { - if (new Date(e.target.value) >= new Date(watch("endTime"))) { - const newEndTime = new Date( - new Date(e.target.value).getTime() + 60 * 60 * 3000, - ); - setValue("endTime", newEndTime.toISOString().slice(0, 16)); - } - }, - })} + value={new Date(watch("startTime") || Date.now())} + onChange={(date) => { + setValue("startTime", date.toISOString()); + if (new Date(date) >= new Date(watch("endTime"))) { + const newEndTime = new Date(new Date(date).getTime() + 60 * 60 * 3000); + setValue("endTime", newEndTime.toISOString().slice(0, 16)); + } + }} className="input input-bordered w-full" /> {errors.startTime && ( @@ -215,9 +226,12 @@ export const NewBookingModal = ({ Endzeit * - { + setValue("endTime", date.toISOString()); + }} className="input input-bordered w-full" /> {errors.endTime && ( diff --git a/apps/hub/app/_components/ui/DateInput.tsx b/apps/hub/app/_components/ui/DateInput.tsx index c95bced7..f2c9b9f1 100644 --- a/apps/hub/app/_components/ui/DateInput.tsx +++ b/apps/hub/app/_components/ui/DateInput.tsx @@ -12,7 +12,7 @@ export const DateInput = ({ { const date = e.target.value ? new Date(e.target.value) : null; if (!date) return;