Files
Client_Fe/src/components/hexa/views/booking-calendar.tsx
T
HarithaRandunu 698fd4239b feat: integrate FullCalendar for booking management and add booking dashboard
- Added FullCalendar component to display bookings in a calendar view.
- Implemented BookingDashboardTab to show summary statistics including bookings today, revenue, checked-in guests, and outstanding payments.
- Created BookingInvoicesTab to manage and display booking payment receipts separately from membership invoices.
- Enhanced AdhocBookingTab to allow selection of existing members for guest bookings.
- Updated BookingRosterTab to include payment methods and improved UI for guest identification.
- Introduced new API endpoints for fetching booking invoices and dashboard summaries.
- Updated models to support new features including payment methods and invoice structures.
2026-10-01 03:45:55 +05:30

84 lines
2.8 KiB
TypeScript

"use client"
import * as React from "react"
import FullCalendar from "@fullcalendar/react"
import dayGridPlugin from "@fullcalendar/daygrid"
import timeGridPlugin from "@fullcalendar/timegrid"
import interactionPlugin from "@fullcalendar/interaction"
import type { DatesSetArg, EventInput } from "@fullcalendar/core"
import { getBookings } from "@/lib/api/bookings"
import { ApiError } from "@/lib/api/core-http"
import type { Booking, BookingStatus } from "@/lib/models/booking"
const STATUS_COLORS: Record<BookingStatus, string> = {
Confirmed: "#2563eb",
CheckedIn: "#16a34a",
Completed: "#6b7280",
NoShow: "#dc2626",
Cancelled: "#dc2626",
}
function bookingToEvent(booking: Booking): EventInput {
const color = STATUS_COLORS[booking.status] ?? "#2563eb"
return {
id: booking.bookingId,
title: `${booking.facilityName ?? "Facility"} — ${booking.isGuest ? booking.guestName ?? "Guest" : "Member"}`,
start: booking.startTime,
end: booking.endTime,
backgroundColor: color,
borderColor: color,
}
}
/** Read-only calendar view of bookings — reuses the existing roster's getBookings query,
* refetched for whatever range FullCalendar currently has visible. */
function BookingCalendar() {
const [events, setEvents] = React.useState<EventInput[]>([])
const [loading, setLoading] = React.useState(false)
const [error, setError] = React.useState<string | null>(null)
async function handleDatesSet(arg: DatesSetArg) {
setLoading(true)
setError(null)
try {
const bookings = await getBookings({
fromDate: arg.start.toISOString(),
toDate: arg.end.toISOString(),
})
setEvents(bookings.map(bookingToEvent))
} catch (err) {
setError(err instanceof ApiError ? err.message : "Couldn't load bookings for this range.")
} finally {
setLoading(false)
}
}
return (
<div className="rounded-lg border p-3">
<div className="mb-3 flex flex-wrap items-center gap-3 text-xs text-muted-foreground">
{loading && <span>Loading…</span>}
{error && <span className="text-destructive">{error}</span>}
{(Object.keys(STATUS_COLORS) as BookingStatus[]).map((status) => (
<span key={status} className="flex items-center gap-1.5">
<span className="inline-block size-2.5 rounded-full" style={{ backgroundColor: STATUS_COLORS[status] }} />
{status}
</span>
))}
</div>
<FullCalendar
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
initialView="timeGridWeek"
headerToolbar={{ left: "prev,next today", center: "title", right: "timeGridDay,timeGridWeek,dayGridMonth" }}
height="auto"
nowIndicator
events={events}
datesSet={handleDatesSet}
eventDisplay="block"
/>
</div>
)
}
export { BookingCalendar }