From 35beb38724bfa6f5615a8b1ca2cd40790afb3a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauritz=20Skog=C3=B8y?= Date: Sat, 31 Aug 2024 12:29:54 +0200 Subject: [PATCH] Feat: continue implementing UI for booking --- frontend/prisma/schema.prisma | 18 ++- .../app/for_studenten/arrangement/page.tsx | 2 +- .../for_studenten/booking/bookings/page.tsx | 7 +- .../for_studenten/booking/soundbox/page.tsx | 127 +++++++++++++----- .../ForStudenten/booking/booking-card.tsx | 18 ++- .../ForStudenten/booking/booking-window.tsx | 9 +- frontend/src/schemas/booking.ts | 2 +- frontend/src/utils/booking/booking.ts | 2 + 8 files changed, 135 insertions(+), 50 deletions(-) diff --git a/frontend/prisma/schema.prisma b/frontend/prisma/schema.prisma index e928268..2fd66b7 100644 --- a/frontend/prisma/schema.prisma +++ b/frontend/prisma/schema.prisma @@ -107,16 +107,16 @@ model Arrangement { } model Booking { - id String @id @default(cuid()) + id String @id @default(cuid()) userID String komiteID String? item BookedItem bookedAt DateTime - duration Int? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - komite komite? @relation(fields: [komiteID], references: [id]) - user User @relation(fields: [userID], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + status BookingStatus @default(PENDING) + komite komite? @relation(fields: [komiteID], references: [id]) + user User @relation(fields: [userID], references: [id]) } model ArrangementPaamelding { @@ -157,3 +157,9 @@ enum UserRole { USER SUPER_USER } + +enum BookingStatus { + PENDING + CONFIRMED + REJECTED +} diff --git a/frontend/src/app/for_studenten/arrangement/page.tsx b/frontend/src/app/for_studenten/arrangement/page.tsx index 7e814a9..27f6aa2 100644 --- a/frontend/src/app/for_studenten/arrangement/page.tsx +++ b/frontend/src/app/for_studenten/arrangement/page.tsx @@ -217,7 +217,7 @@ const ForStudentenPage = () => {

Påmelding til arrangementer skjer både gjennom nettsiden og gjennom emils - Facebookgruppe. Ved + Facebookgruppe. Ved spørsmål angående arrangementer kan man henvende seg til arrangør eller hovedstyret.

diff --git a/frontend/src/app/for_studenten/booking/bookings/page.tsx b/frontend/src/app/for_studenten/booking/bookings/page.tsx index b809734..6e7804c 100644 --- a/frontend/src/app/for_studenten/booking/bookings/page.tsx +++ b/frontend/src/app/for_studenten/booking/bookings/page.tsx @@ -1,10 +1,15 @@ "use client"; import BookingView from "@/components/ForStudenten/booking/booking-view"; +import { ArrowLeft } from "lucide-react"; +import Link from "next/link"; const MineBookingerPage = () => { return ( -
+
+
+ Tilbake +

Soundbox diff --git a/frontend/src/app/for_studenten/booking/soundbox/page.tsx b/frontend/src/app/for_studenten/booking/soundbox/page.tsx index a00d519..13c45fe 100644 --- a/frontend/src/app/for_studenten/booking/soundbox/page.tsx +++ b/frontend/src/app/for_studenten/booking/soundbox/page.tsx @@ -6,67 +6,128 @@ import { Booking } from "@/schemas/booking"; import { useEffect, useState } from "react"; import Calendar from "react-calendar"; import { getBookingList } from "@/utils/booking/booking"; +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { ArrowLeft } from "lucide-react"; +import router from "next/router"; const SoundboxPage = () => { - const [bookings, setBookings] = useState(); + const [bookings, setBookings] = useState([]); useEffect(() => { fetchBookings(); console.log(bookings); - }, []) + }, []); const fetchBookings = async () => { try { const bookings = await getBookingList(); - console.log(bookings); - if (bookings){ - // setBookings(bookings); + if (bookings) { + const transformedBookings = bookings.map((booking) => ({ + ...booking, + })); + setBookings(transformedBookings); } } catch (err) { console.error("Could not fetch bookings:", err); } }; + const handleStatusChange = (status: string) => { + if (status === "Booking successful!") { + fetchBookings(); + } + }; + return (
+
+ Tilbake +

Soundbox

- Emil har 2 soundboxer som kan bookes. Se oversikten nedenfor og finn - en dato.{" "} + Emil har 2 soundboxer som kan bookes. Se oversikt over tilgjengelige datoer nedenfor og legg inn en booking. + Når du har lagt inn en booking vil styret godkjenne bookingen din og den kommer opp i oversikten + under dine bookinger

-
-
- -
+
-
+

Bookinger

- {/* RENDER COLOURS FOR BOOKED INSTANCES */} - { - const dateString = date.toDateString(); - const isToday = dateString === new Date().toDateString(); - - return view === "month" && isToday - ? "bg-[#579783] text-white font-bold border border-white lg:h-[4rem] p-2 flex flex-col justify-center items-center relative" - : "p-2 border border-white h-[4rem] flex flex-col justify-center items-center relative"; - }} - navigationLabel={({ date, label, locale, view }) => ( -
- {label.charAt(0).toUpperCase() + label.slice(1)} +
+
+
+ En ledig soundbox +
+ + Ingen ledige soundboxer +
- )} - next2Label={null} - prev2Label={null} - nextLabel={} - prevLabel={} - />
+ { + const dateString = date.toDateString(); + const isToday = dateString === new Date().toDateString(); + + return view === "month" && isToday + ? "bg-[#579783] text-white font-bold border border-white lg:h-[4rem] p-2 flex flex-col justify-center items-center relative cursor-default" + : "p-2 border border-white h-[4rem] flex flex-col justify-center items-center relative cursor-default"; + }} + tileContent={({ date, view }) => { + if (view === "month") { + const matchingBookings = bookings.filter((booking) => { + const bookingDate = new Date( + booking.bookedAt, + ).toDateString(); + return bookingDate === date.toDateString(); + }); + + // Count the number of "ONE_SOUNDBOX" bookings + const oneSoundboxCount = matchingBookings.filter( + (booking) => booking.item === "ONE_SOUNDBOX", + ).length; + + // Check if there is a "TWO_SOUNDBOXES" booking + const hasTwoSoundboxes = matchingBookings.some( + (booking) => booking.item === "TWO_SOUNDBOXES", + ); + + // Determine the dot color: red if there is a "TWO_SOUNDBOXES" booking or more than one "ONE_SOUNDBOX" booking + const dotColor = + hasTwoSoundboxes || oneSoundboxCount > 1 + ? "bg-red-500" + : "bg-yellow-500"; + + return matchingBookings.length > 0 ? ( +
+
+
+ ) : null; + } + }} + navigationLabel={({ date, label, locale, view }) => ( +
+ {label.charAt(0).toUpperCase() + label.slice(1)} +
+ )} + next2Label={null} + prev2Label={null} + nextLabel={ + + } + prevLabel={ + + } + /> +
+
+
+
diff --git a/frontend/src/components/ForStudenten/booking/booking-card.tsx b/frontend/src/components/ForStudenten/booking/booking-card.tsx index 6cfaf04..22c1cfe 100644 --- a/frontend/src/components/ForStudenten/booking/booking-card.tsx +++ b/frontend/src/components/ForStudenten/booking/booking-card.tsx @@ -42,7 +42,7 @@ import { useCurrentUser } from "@/hooks/use-current-user"; import { checkBooking } from "@/utils/actions/checkBooking"; import { bookingFormSchema } from "@/schemas/booking"; -export const BookingCard = () => { +export const BookingCard = ({ onStatusChange }: { onStatusChange: (status: string) => void }) => { const user = useCurrentUser(); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); @@ -62,8 +62,16 @@ export const BookingCard = () => { startTransition(() => { checkBooking(values, user?.id || "").then((data) => { - setError(data?.error); - setSuccess(data?.success); + const { error, success } = data || {}; + + if (error) { + setError(error); + onStatusChange(error); // Send the error to the parent component + } else if (success) { + setSuccess(success); + onStatusChange(success); // Send the success message to the parent component + } + toast({ title: "Booking gjennomført!:", description: ( @@ -82,8 +90,8 @@ export const BookingCard = () => {
diff --git a/frontend/src/components/ForStudenten/booking/booking-window.tsx b/frontend/src/components/ForStudenten/booking/booking-window.tsx index 6d3efad..76fba22 100644 --- a/frontend/src/components/ForStudenten/booking/booking-window.tsx +++ b/frontend/src/components/ForStudenten/booking/booking-window.tsx @@ -2,15 +2,18 @@ import { Label } from "@radix-ui/react-label"; import { DatePickerForm } from "../ui/date-picker-form"; -import { ReactNode } from "react"; +import { ReactNode, useState } from "react"; import BookingForm from "@/components/ForStudenten/booking/booking-form"; import { BookingCard } from "./booking-card"; -const BookingWindow = () => { +const BookingWindow = ({ onStatusChange }: { onStatusChange: (status: string) => void }) => { + const handleChange = (status: string) => { + onStatusChange(status); + } return (
{/* */} - +
); }; diff --git a/frontend/src/schemas/booking.ts b/frontend/src/schemas/booking.ts index 1015705..c3e3192 100644 --- a/frontend/src/schemas/booking.ts +++ b/frontend/src/schemas/booking.ts @@ -5,7 +5,7 @@ import { nb } from "date-fns/locale"; export const BookingSchema = z.object({ id: z.string(), userID: z.string(), - komiteID: z.string().optional(), + komiteID: z.string().nullable().optional(), item: z.enum(["KONTOR", "ONE_SOUNDBOX", "TWO_SOUNDBOXES"]), bookedAt: z.string().transform((str) => new Date(str)), duration: z.number().optional(), diff --git a/frontend/src/utils/booking/booking.ts b/frontend/src/utils/booking/booking.ts index 8342445..08590a3 100644 --- a/frontend/src/utils/booking/booking.ts +++ b/frontend/src/utils/booking/booking.ts @@ -1,3 +1,5 @@ +'use server' + import { db } from "@/lib/db"; import { format } from "date-fns"; import { nb } from "date-fns/locale";