Reservation Management — House of Legends
Documented: 2026-05-11
Overview
Staff manage reservations via the admin dashboard — view list, filter, and take actions (cancel, refund, check-in).
Reservations Table
reservations: defineTable({
eventId: v.id("experienceEvents"),
customerFirstName: v.string(),
customerLastName: v.string(),
customerEmail: v.string(),
customerPhone: v.string(),
ticketType: v.union(
v.literal("DINNER_THEATRE"),
v.literal("EXPERIENCE_ONLY"),
),
quantity: v.number(),
addOns: v.record(v.string(), v.number()),
subtotal: v.number(),
totalAmount: v.number(),
dayOfWeekSurcharge: v.number(),
smallPartySurcharge: v.number(),
paymentStatus: v.union(
v.literal("PENDING"),
v.literal("PAID"),
v.literal("REFUNDED"),
v.literal("FAILED"),
v.literal("CANCELLED"),
v.literal("REFUND_PENDING"),
),
paymentGateway: v.optional(v.string()),
onePayOrderId: v.optional(v.string()),
onePayTransactionId: v.optional(v.string()),
vaNumber: v.optional(v.string()),
qrCode: v.optional(v.string()),
qrCodeUrl: v.optional(v.string()),
paymentExpiresAt: v.optional(v.number()),
bookingExpiresAt: v.optional(v.number()),
checkedInAt: v.optional(v.number()),
createdAt: v.number(),
})
.index("by_event", ["eventId"])
.index("by_status", ["paymentStatus"])
.index("by_email", ["customerEmail"]);Reservation List (/dashboard/reservations)
Table Columns
| Column | Source |
|---|---|
| ID | reservation._id (short) |
| Guest | ${firstName} ${lastName} |
customerEmail | |
| Phone | customerPhone |
| Event | experienceEvents lookup |
| Type | ticketType |
| Qty | quantity |
| Total | totalAmount formatted |
| Status | paymentStatus badge |
| Date | createdAt formatted |
Filters
- Status: PENDING, PAID, CANCELLED, REFUNDED
- Date range: Created between dates
- Event: Specific experience or all
- Search: Name, email, phone
Status Badges
| Status | Badge Color | Meaning |
|---|---|---|
| PENDING | Yellow | Awaiting payment |
| PAID | Green | Payment confirmed |
| REFUND_PENDING | Orange | Refund requested |
| REFUNDED | Gray | Refund completed |
| CANCELLED | Red | Cancelled |
| FAILED | Red | Payment failed |
Reservation Detail (Drawer)
Opens on row click:
Guest Info
- Full name, email, phone
- Booking created timestamp
Event Info
- Experience name
- Date + time
- Venue
Ticket Info
- Ticket type (Dinner Theatre / Show Only)
- Quantity
- Unit price
Add-ons
- List of add-ons with quantities
Pricing Summary
- Subtotal
- Day-of-week surcharge
- Small party surcharge
- Total
Payment Info
- Gateway (OnePay)
- Order ID
- Transaction ID
- VA Number (if bank transfer)
- QR code preview
Status History
- Created
- Payment confirmed (if PAID)
- Checked in (if applicable)
Actions
| Action | Mutation | Condition |
|---|---|---|
| Cancel | reservations.cancel | PENDING or PAID |
| Refund | reservations.refund | PAID only |
| Check-in | checkIns.create | PAID not yet checked in |
| Resend confirmation | notifications.sendConfirmation | Any status |
Cancel Flow
- Staff clicks "Cancel"
- Confirmation dialog
reservations.cancelcalled- Status → CANCELLED
event.bookedCountdecremented- Notification sent to guest (if PAID → refund flow)
Refund Flow
- Staff clicks "Refund"
- Confirmation dialog
reservations.refundcalled- Status → REFUND_PENDING
- OnePay refund API called
- Status → REFUNDED on success
- Notification sent to guest
Check-in
Located at /dashboard/checkin:
- QR scanner (camera)
- Manual lookup by reservation ID
On scan/lookup:
- Shows guest details + party size
- "Check In" button
- Creates
checkInsrecord - Updates
reservation.checkedInAt
Backend Functions
| Function | Purpose |
|---|---|
reservations.list | All reservations with filters |
reservations.getById | Single reservation detail |
reservations.cancel | Cancel + release seats |
reservations.refund | Process refund |
checkIns.create | Record check-in |
notifications.sendConfirmation | Resend confirmation email/WhatsApp |