Skip to content

Commit

Permalink
Feat: calendar implementation on booking page
Browse files Browse the repository at this point in the history
  • Loading branch information
Mauritzskog committed Aug 31, 2024
1 parent ed35b28 commit 40bc528
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 112 deletions.
13 changes: 7 additions & 6 deletions frontend/src/app/for_studenten/booking/bookings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
"use client";

import BookingView from "@/components/ForStudenten/booking/booking-view";
import { ArrowLeft } from "lucide-react";
import { ArrowLeft, ArrowRight } from "lucide-react";
import Link from "next/link";

const MineBookingerPage = () => {
return (
<div className="flex flex-col items-center justify-center bg-[#225654] rounded-b-md gap-y-4 px-4">
<div className="flex justify-start w-full">
<div className="flex flex-col items-center justify-center bg-[#225654] rounded-b-md gap-y-4 p-4">
<div className="flex justify-between w-full ">
<Link href="/for_studenten/booking" className="flex px-4 font-light text-md items-center text-underscore"><ArrowLeft/>Tilbake</Link>
<Link href="/for_studenten/booking/soundbox" className="flex px-4 font-light text-md items-center text-underscore">Soundbox<ArrowRight/></Link>
</div>
<div className="flex flex-col space-y-4">
<h1 className=" text-white text-center font-semibold text-4xl w-full">
Soundbox
Bookinger
</h1>
<p className="text-white text-md text-center">
Her er din bookingoversikt{" "}
<p className="text-white text-sm text-center">
Under finner du en oversikt over bookingene dine{" "}
</p>
</div>
<BookingView />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/for_studenten/booking/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const BookingPage = () => {
description:
"Emil-kontoret kan bookes hver dag fra 12 - 20. Dersom det skal drikkes alkohol inne på kontoret må man sende melding til vaktmesteren på gløs for å få dette godkjent på forhånd",
buttonLabel: "Book kontoret",
href: "https://youtube.com",
href: "https://docs.google.com/spreadsheets/d/1L4CxgaByY3gxWAfNoT2y6EcCrThJMDowLDtLoXx7y38/edit?gid=1481163696#gid=1481163696",
},
{
title: "Hytte",
Expand Down
147 changes: 111 additions & 36 deletions frontend/src/app/for_studenten/booking/soundbox/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ import router from "next/router";

const SoundboxPage = () => {
const [bookings, setBookings] = useState<Booking[]>([]);

const [selectedDate, setSelectedDate] = useState<Date | null>(null);
const [openForm, setOpenform] = useState<boolean>(false)
useEffect(() => {
fetchBookings();
console.log(bookings);
}, []);

const toggleForm = () => {
setOpenform((prevState) => !prevState);
};

const fetchBookings = async () => {
try {
const bookings = await getBookingList();
Expand All @@ -32,86 +36,152 @@ const SoundboxPage = () => {
console.error("Could not fetch bookings:", err);
}
};
const handleStatusChange = (status: string) => {

const handleStatusChange = ({status, lukk}: {status:string, lukk: boolean}) => {
if (lukk){
toggleForm();
}
if (status === "Booking successful!") {
fetchBookings();
}
};

// Handle date selection
const handleDateChange = (date: Date) => {
toggleForm();
setSelectedDate(date);
};

return (
<div className="flex flex-col items-center justify-center bg-[#225654] rounded-b-md gap-y-4 px-4">
<div className="flex flex-col items-center justify-center bg-[#225654] rounded-b-md gap-y-4 px-2 sm:px-4">
<div className="flex justify-start w-full">
<Link href="/for_studenten/booking" className="flex px-4 font-light text-md items-center text-underscore"><ArrowLeft/>Tilbake</Link>
<Link
href="/for_studenten/booking"
className="flex px-2 sm:px-4 font-light text-md items-center text-underscore"
>
<ArrowLeft />
Tilbake
</Link>
</div>
<div className="max-w-[512px]">
<h1 className=" text-white text-center font-semibold text-2xl w-full">
<div className="max-w-[512px] w-full">
<h1 className="text-white text-center font-semibold text-xl sm:text-2xl w-full">
Soundbox
</h1>
<p className="text-white text-sm text-center">
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<span className="text-[#9DDBAD] text-underscore"><Link href="/for_studenten/booking/bookings"> dine bookinger</Link></span>
<p className="text-white text-sm text-left">
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{" "}
<span className="text-[#9DDBAD] text-underscore">
<Link href="/for_studenten/booking/bookings">dine bookinger</Link>
</span>
.
</p>
<br></br>
<div className="bg-[#25504F] rounded-md p-2 sm:p-4 text-sm">
<p>Følgende regler gjelder for bruk av soundbox:</p>
<ul className="list-disc pl-4 sm:pl-6 text-sm">
<li className="pl-2 leading-relaxed">
Soundboks må returneres til kontoret dagen etter bruk innen 14:00
og den må være ladet.
</li>
<li className="pl-2 leading-relaxed">
Soundboks skal <span className="underline">aldri</span> ligge med
malt grill ned mot bakken.
</li>
<li className="pl-2 leading-relaxed">
Soundboxen skal kun brukes i arrangementer hvor de går til nytte
for emilstudenter.
</li>
</ul>
</div>
</div>
<div className="flex flex-col lg:flex-row justify-center items-center lg:gap-y-4 pb-4">
<div className="w-full max-w-[800px]">
<div className="text-2xl font-bold flex h-[550px] flex-col items-center ">
<div className="flex flex-col lg:flex-row justify-center items-center lg:gap-y-4 pb-4 w-full">
<div className="w-full px-2 pb-8">
<div className="text-xl sm:text-2xl font-bold flex h-auto lg:h-[650px] flex-col items-center gap-y-4">
<p className="w-full flex justify-center">Bookinger</p>
<div className="flex justify-start font-normal">
<div className="flex gap-x-2 items-center px-4">
<p className="text-xs sm:text-sm font-light px-4 sm:px-16 text-center">
Hvis en dato ikke er markert i kalenderen er begge soundboxene
ledige.
</p>
<div className="flex flex-wrap justify-start font-normal px-4 sm:px-8">
<div className="flex gap-x-2 items-center">
<div className="w-2 h-2 bg-yellow-500 rounded-full"></div>
<span className="text-sm lg:text-base">En ledig soundbox</span>
<span className="text-xs sm:text-sm">En booket soundbox</span>
<div className="w-2 h-2 bg-red-500 rounded-full"></div>
<span className="text-sm lg:text-base">
Ingen ledige soundboxer
<span className="text-xs sm:text-sm">
To bookede soundboxer
</span>
<div className="w-2 h-2 text-orange-500 flex justify-center items-center text-xs sm:text-sm">
?
</div>
<span className="text-xs sm:text-sm">
Booking avventer bekreftelse
</span>
</div>
</div>
<Calendar
locale="nb"
className="text-white p-4 font-normal text-sm rounded-md flex items-center justify-center flex-col gap-y-4 lg:px-12"
className="text-white p-2 sm:p-4 font-normal text-xs sm:text-sm rounded-md flex items-center justify-center flex-col gap-y-4 lg:px-12"
tileClassName={({ date, view }) => {
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";
}}
? "bg-[#579783] text-white font-bold border border-white lg:h-[5rem] p-2 flex flex-col justify-center items-center relative"
: "hover:bg-slate-400 p-2 border border-white h-[5rem] flex flex-col justify-center items-center relative";
}}
tileContent={({ date, view }) => {
if (view === "month") {
const matchingBookings = bookings.filter((booking) => {
const bookingDate = new Date(
booking.bookedAt,
booking.bookedAt
).toDateString();
return bookingDate === date.toDateString();
});

// Count the number of "ONE_SOUNDBOX" bookings
if (
matchingBookings.some(
(booking) => booking.status === "PENDING"
)
) {
return (
<div className="flex items-center justify-center text-md text-orange-500">
?
</div>
);
}

const oneSoundboxCount = matchingBookings.filter(
(booking) => booking.item === "ONE_SOUNDBOX",
(booking) =>
booking.item === "ONE_SOUNDBOX" &&
booking.status === "CONFIRMED"
).length;

// Check if there is a "TWO_SOUNDBOXES" booking
const hasTwoSoundboxes = matchingBookings.some(
(booking) => booking.item === "TWO_SOUNDBOXES",
(booking) =>
booking.item === "TWO_SOUNDBOXES" &&
booking.status === "CONFIRMED"
);

// 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 ? (
return matchingBookings.some(
(booking) => booking.status === "CONFIRMED"
) ? (
<div className="flex items-center justify-center">
<div className={`w-2 h-2 ${dotColor} rounded-full`}></div>
<div
className={`w-2 h-2 ${dotColor} rounded-full`}
></div>
</div>
) : null;
}
}}
navigationLabel={({ date, label, locale, view }) => (
<div className="text-md w-[150px] flex justify-center flex-shrink-0 font-semibold text-white icon-hover">
navigationLabel={({ label }) => (
<div className="text-md w-[120px] sm:w-[150px] flex justify-center flex-shrink-0 font-semibold text-white icon-hover">
{label.charAt(0).toUpperCase() + label.slice(1)}
</div>
)}
Expand All @@ -123,12 +193,17 @@ const SoundboxPage = () => {
prevLabel={
<span className="text-white font-bold text-xl"></span>
}
onClickDay={handleDateChange} // Handle calendar day clicks
/>
</div>
</div>
<div className="flex justify-start items-start">
<BookingWindow onStatusChange={handleStatusChange} />
</div>
{openForm && ( // Conditionally render BookingWindow based on selected date
<div className="fixed inset-0 bg-[#579783] bg-opacity-30 flex items-center justify-center z-50">
<div className="flex justify-start items-start py-4">
<BookingWindow onStatusChange={handleStatusChange} selectedDate={selectedDate}/>
</div>
</div>
)}
</div>
</div>
);
Expand Down
31 changes: 22 additions & 9 deletions frontend/src/components/ForStudenten/booking/booking-card.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
"use client";

import * as z from "zod";

import { useState, useTransition } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { format } from "date-fns";
import { nb } from "date-fns/locale";

import {
Form,
FormControl,
Expand All @@ -17,7 +15,6 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";

import { CardWrapper } from "@/components/auth/card-wrapper";
import { Button } from "@/components/ui/button";
import { FormError } from "@/components/form-error";
Expand All @@ -42,20 +39,33 @@ import { useCurrentUser } from "@/hooks/use-current-user";
import { checkBooking } from "@/utils/actions/checkBooking";
import { bookingFormSchema } from "@/schemas/booking";

export const BookingCard = ({ onStatusChange }: { onStatusChange: (status: string) => void }) => {
interface BookingCardProps {
onStatusChange: ({ status, lukk }: { status: string, lukk: boolean }) => void;
selectedDate: Date | null; // Add selectedDate prop
}

export const BookingCard = ({ onStatusChange, selectedDate }: BookingCardProps) => {
const user = useCurrentUser();
const [error, setError] = useState<string | undefined>("");
const [success, setSuccess] = useState<string | undefined>("");
const [lukk, setLukk] = useState<boolean>(false);

const [isPending, startTransition] = useTransition();

const form = useForm<z.infer<typeof bookingFormSchema>>({
resolver: zodResolver(bookingFormSchema),
defaultValues: {
bookingDate: undefined,
bookingDate: selectedDate || undefined, // Use selectedDate from props
},
});

// Function to handle the close button
const handleClose = () => {
setLukk(true);
onStatusChange({ status: "Closed", lukk: true }); // Send the close action to the parent component
};

// Function to handle form submission
const onSubmit = (values: z.infer<typeof bookingFormSchema>) => {
setError("");
setSuccess("");
Expand All @@ -66,14 +76,14 @@ export const BookingCard = ({ onStatusChange }: { onStatusChange: (status: strin

if (error) {
setError(error);
onStatusChange(error); // Send the error to the parent component
onStatusChange({ status: error, lukk }); // Send the error status to the parent component
} else if (success) {
setSuccess(success);
onStatusChange(success); // Send the success message to the parent component
onStatusChange({ status: success, lukk }); // Send the success status to the parent component
}

toast({
title: "Booking gjennomført!:",
title: "Booking gjennomført!",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">
Expand All @@ -94,7 +104,7 @@ export const BookingCard = ({ onStatusChange }: { onStatusChange: (status: strin
backButtonHref="/for_studenten/booking/bookings"
>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 py-4">
<div className="space-y-4">
<FormField
control={form.control}
Expand Down Expand Up @@ -173,6 +183,9 @@ export const BookingCard = ({ onStatusChange }: { onStatusChange: (status: strin
</Button>
</form>
</Form>
<Button className="w-full" onClick={handleClose}>
Lukk
</Button>
</CardWrapper>
);
};
Loading

0 comments on commit 40bc528

Please sign in to comment.