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.
This commit is contained in:
2026-10-01 03:45:55 +05:30
parent 0ba1e6a9e6
commit 698fd4239b
13 changed files with 752 additions and 55 deletions
+9
View File
@@ -0,0 +1,9 @@
import { apiRequest } from "@/lib/api/core-http"
import type { BookingInvoice, GetBookingInvoicesFilters } from "@/lib/models/booking-invoice"
/** Booking payment receipts — kept separate from the membership-based Customer Payments view. */
function getBookingInvoices(filters: GetBookingInvoicesFilters = {}) {
return apiRequest<BookingInvoice[]>("client/bookings/invoices", { query: filters })
}
export { getBookingInvoices }
+12 -4
View File
@@ -1,10 +1,13 @@
import { apiRequest } from "@/lib/api/core-http"
import type { BookingDashboardSummary } from "@/lib/models/booking-dashboard"
import type {
Booking,
CompleteBookingInput,
CreateAdhocBookingInput,
CreateGuestAdhocBookingInput,
CreateGuestBookingInput,
GetBookingsFilters,
MarkGuestBookingPaidInput,
ReassignBookingInput,
RescheduleBookingInput,
} from "@/lib/models/booking"
@@ -41,10 +44,10 @@ function checkInBooking(otpCode: string, rfidTagId?: string) {
})
}
function markGuestBookingPaid(id: string, amountCharged: number) {
function markGuestBookingPaid(id: string, input: MarkGuestBookingPaidInput) {
return apiRequest<Booking>(`client/bookings/${id}/mark-paid`, {
method: "POST",
body: { bookingId: id, amountCharged },
body: { bookingId: id, ...input },
})
}
@@ -66,8 +69,12 @@ function markNoShow(id: string) {
return apiRequest<Booking>(`client/bookings/${id}/no-show`, { method: "POST" })
}
function completeBooking(id: string) {
return apiRequest<Booking>(`client/bookings/${id}/complete`, { method: "POST" })
function completeBooking(id: string, input: CompleteBookingInput = {}) {
return apiRequest<Booking>(`client/bookings/${id}/complete`, { method: "POST", body: input })
}
function getBookingDashboardSummary() {
return apiRequest<BookingDashboardSummary>("client/bookings/dashboard-summary")
}
export {
@@ -83,4 +90,5 @@ export {
rescheduleBooking,
markNoShow,
completeBooking,
getBookingDashboardSummary,
}
+8
View File
@@ -0,0 +1,8 @@
/** Mirrors BookingDashboardSummaryDto. */
export interface BookingDashboardSummary {
bookingsToday: number
revenueToday: number
checkedInNow: number
upcomingConfirmed: number
outstandingUnpaid: number
}
+23
View File
@@ -0,0 +1,23 @@
import type { PaymentMethod } from "@/lib/models/booking"
/** Mirrors BookingInvoiceDto. A booking payment receipt — deliberately separate from
* membership-based Customer Payments/invoices. */
export interface BookingInvoice {
bookingInvoiceId: string
bookingId: string
amount: number
paymentMethod: PaymentMethod
guestName: string
guestPhone?: string | null
facilityName: string
bookingStartTime: string
bookingEndTime: string
issuedAt: string
/** Public, unauthenticated link to this receipt's printable page. */
shareUrl?: string | null
}
export type GetBookingInvoicesFilters = {
fromDate?: string
toDate?: string
}
+21
View File
@@ -1,4 +1,5 @@
export type BookingStatus = "Confirmed" | "CheckedIn" | "Cancelled" | "NoShow" | "Completed"
export type PaymentMethod = "Cash" | "Bank"
/** Mirrors BookingDto. Covers both Scheduled (facilitySessionId set) and AdHoc
* (facilitySessionId null, exclusive-unit) bookings. */
@@ -30,6 +31,9 @@ export interface Booking {
paymentStatus: string
amountCharged: number
/** Set once paymentStatus becomes "Paid". */
paymentMethod?: PaymentMethod | null
paidAt?: string | null
status: BookingStatus
bookedAt: string
checkedInAt?: string | null
@@ -51,6 +55,8 @@ export interface CreateGuestBookingInput {
facilitySessionId: string
guestName: string
guestPhone: string
/** Optional link to an existing registered customer — identification/auto-fill only. */
customerId?: string
}
export interface CreateAdhocBookingInput {
@@ -69,6 +75,8 @@ export interface CreateGuestAdhocBookingInput {
partySize?: number
/** Overrides the facility's own price per hour for this booking's charge only. */
pricePerHour?: number
/** Optional link to an existing registered customer — identification/auto-fill only. */
customerId?: string
}
export interface ReassignBookingInput {
@@ -82,3 +90,16 @@ export interface RescheduleBookingInput {
durationMinutes?: number
facilitySessionId?: string
}
/** Only required when the target booking is a guest booking still Unpaid — the backend
* rejects checkout otherwise. Ignored for a member booking or a guest booking already Paid. */
export interface CompleteBookingInput {
markPaid?: boolean
amountCollected?: number
paymentMethod?: PaymentMethod
}
export interface MarkGuestBookingPaidInput {
amountCharged: number
paymentMethod?: PaymentMethod
}