From cf199150fe06d4a6162ba9f9dcc9d2a21d8d39f3 Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Sat, 20 Sep 2025 22:16:23 +0200 Subject: [PATCH] futher booking stuff --- apps/hub/app/(app)/_querys/bookings.ts | 3 +- apps/hub/app/(app)/_querys/stations.ts | 14 ++++ .../app/_components/BookingTimelineModal.tsx | 11 ++- apps/hub/app/_components/NewBookingModal.tsx | 73 ++++++------------- apps/hub/app/api/bookings/route.ts | 2 +- .../app/api/{station => stations}/route.ts | 2 +- .../migration.sql | 2 + packages/database/prisma/schema/user.prisma | 1 + 8 files changed, 50 insertions(+), 58 deletions(-) create mode 100644 apps/hub/app/(app)/_querys/stations.ts rename apps/hub/app/api/{station => stations}/route.ts (95%) create mode 100644 packages/database/prisma/schema/migrations/20250920195433_admin_booking_permission/migration.sql diff --git a/apps/hub/app/(app)/_querys/bookings.ts b/apps/hub/app/(app)/_querys/bookings.ts index fc4c5f19..b13805d2 100644 --- a/apps/hub/app/(app)/_querys/bookings.ts +++ b/apps/hub/app/(app)/_querys/bookings.ts @@ -18,9 +18,10 @@ export const getBookingsAPI = async (filter: Prisma.BookingWhereInput) => { return res.data; }; -export const createBookingAPI = async (booking: Prisma.BookingCreateInput) => { +export const createBookingAPI = async (booking: Omit) => { const response = await axios.post("/api/bookings", booking); if (response.status !== 201) { + console.error("Error creating booking:", response); throw new Error("Failed to create booking"); } return response.data; diff --git a/apps/hub/app/(app)/_querys/stations.ts b/apps/hub/app/(app)/_querys/stations.ts new file mode 100644 index 00000000..ad16cf2e --- /dev/null +++ b/apps/hub/app/(app)/_querys/stations.ts @@ -0,0 +1,14 @@ +import { Prisma, Station } from "@repo/db"; +import axios from "axios"; + +export const getStationsAPI = async (filter: Prisma.StationWhereInput) => { + const res = await axios.get("/api/stations", { + params: { + filter: JSON.stringify(filter), + }, + }); + if (res.status !== 200) { + throw new Error("Failed to fetch stations"); + } + return res.data; +}; diff --git a/apps/hub/app/_components/BookingTimelineModal.tsx b/apps/hub/app/_components/BookingTimelineModal.tsx index 1f4002b8..d564f8ef 100644 --- a/apps/hub/app/_components/BookingTimelineModal.tsx +++ b/apps/hub/app/_components/BookingTimelineModal.tsx @@ -86,18 +86,15 @@ export const BookingTimelineModal = ({ // Check if user can delete a booking const canDeleteBooking = (bookingUserPublicId: string) => { if (!currentUser) return false; + if (currentUser.permissions.includes("ADMIN_BOOKING")) return true; // User can delete their own bookings if they meet basic requirements - if ( - bookingUserPublicId === currentUser.publicId && - currentUser.emailVerified && - !currentUser.isBanned - ) { + if (bookingUserPublicId === currentUser.publicId) { return true; } // Admins can delete any booking - return currentUser.permissions.includes("ADMIN_KICK"); + return false; }; const navigate = (direction: "prev" | "next") => { @@ -240,6 +237,8 @@ export const BookingTimelineModal = ({ const groupedBookings = groupBookingsByResource(); + console.log("Grouped Bookings:", groupedBookings); + return (
diff --git a/apps/hub/app/_components/NewBookingModal.tsx b/apps/hub/app/_components/NewBookingModal.tsx index 2d3e0091..6bac19f5 100644 --- a/apps/hub/app/_components/NewBookingModal.tsx +++ b/apps/hub/app/_components/NewBookingModal.tsx @@ -4,6 +4,9 @@ import { useState, useEffect } from "react"; import { useForm } from "react-hook-form"; import { X, CalendarIcon, Clock } from "lucide-react"; import toast from "react-hot-toast"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getStationsAPI } from "(app)/_querys/stations"; +import { createBookingAPI } from "(app)/_querys/bookings"; interface Station { id: number; @@ -36,10 +39,24 @@ export const NewBookingModal = ({ onBookingCreated, userPermissions, }: NewBookingModalProps) => { - const [stations, setStations] = useState([]); - const [loading, setLoading] = useState(false); const [submitting, setSubmitting] = useState(false); + const queryClient = useQueryClient(); + const { data: stations, isLoading: isLoadingStations } = useQuery({ + queryKey: ["stations"], + queryFn: () => getStationsAPI({}), + }); + + const { mutate: createBooking, isPending: isCreateBookingLoading } = useMutation({ + mutationKey: ["createBooking"], + mutationFn: createBookingAPI, + + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["bookings"] }); + onBookingCreated(); + }, + }); + const { register, handleSubmit, @@ -52,30 +69,6 @@ export const NewBookingModal = ({ const selectedType = watch("type"); const hasDISPOPermission = userPermissions.includes("DISPO"); - // Fetch stations for selection - useEffect(() => { - const fetchStations = async () => { - setLoading(true); - try { - const response = await fetch("/api/station"); - if (!response.ok) { - throw new Error("Failed to fetch stations"); - } - const data = await response.json(); - setStations(data.stations || []); - } catch (error) { - console.error("Error fetching stations:", error); - toast.error("Fehler beim Laden der Stationen"); - } finally { - setLoading(false); - } - }; - - if (isOpen) { - fetchStations(); - } - }, [isOpen]); - // Reset form when modal opens useEffect(() => { if (isOpen) { @@ -106,24 +99,9 @@ export const NewBookingModal = ({ return; } - const response = await fetch("/api/booking", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }); + const response = await createBooking(data); - const result = await response.json(); - - if (!response.ok) { - if (response.status === 409) { - toast.error(result.error || "Konflikt: Zeitraum bereits gebucht"); - } else { - toast.error(result.error || "Fehler beim Erstellen der Buchung"); - } - return; - } + console.log("Booking created:", response); toast.success("Buchung erfolgreich erstellt!"); onBookingCreated(); @@ -167,10 +145,7 @@ export const NewBookingModal = ({ {hasDISPOPermission && ( <> - - - - + )} @@ -187,7 +162,7 @@ export const NewBookingModal = ({ - {loading ? ( + {isLoadingStations ? (
) : (