@withpotter/pages-bookings 0.13.0-next.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/(bookings)/attendees/page.js +8 -0
- package/dist/dashboard/(bookings)/attendees/page.js.map +1 -0
- package/dist/dashboard/(bookings)/availability/_components/availability-screen.js +714 -0
- package/dist/dashboard/(bookings)/availability/_components/availability-screen.js.map +1 -0
- package/dist/dashboard/(bookings)/availability/_components/month-availability.js +130 -0
- package/dist/dashboard/(bookings)/availability/_components/month-availability.js.map +1 -0
- package/dist/dashboard/(bookings)/availability/page.js +10 -0
- package/dist/dashboard/(bookings)/availability/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/assign-resource-modal.js +133 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/assign-resource-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-constants.js +26 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-constants.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-desktop-layout.js +194 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-desktop-layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-layout-props.js +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-layout-props.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-lede.js +189 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-lede.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-meta.js +38 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-meta.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-layout.js +315 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-screen.js +94 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-screen.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-money.js +54 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-money.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-split.js +71 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-split.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancel-booking-modal.js +102 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancel-booking-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancellation-notice.js +33 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancellation-notice.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/customer-info-card.js +65 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/customer-info-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/index.js +23 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/intake-answers-card.js +19 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/intake-answers-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/refund-booking-modal.js +156 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/refund-booking-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/reschedule-booking-modal.js +120 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/reschedule-booking-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/security-deposit-card.js +63 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/security-deposit-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/settle-deposit-modal.js +141 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/settle-deposit-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/ticket-row-actions.js +49 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/ticket-row-actions.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/waiting-queue-notice.js +42 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/waiting-queue-notice.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components.js +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/index.js +19 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/use-booking-detail-data.js +352 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/use-booking-detail-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks.js +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/layout.js +33 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/page.js +356 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/appointment-calendar-layout.js +34 -0
- package/dist/dashboard/(bookings)/bookings/_components/appointment-calendar-layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-constants.js +92 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-constants.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-tab-hints.js +32 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-tab-hints.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-filter-bar.js +131 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-filter-bar.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-mobile-controls.js +119 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-mobile-controls.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-screen.js +414 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-screen.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-table.js +192 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-table.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/cannot-take-bookings.js +34 -0
- package/dist/dashboard/(bookings)/bookings/_components/cannot-take-bookings.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/demo-data.js +176 -0
- package/dist/dashboard/(bookings)/bookings/_components/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/index.js +9 -0
- package/dist/dashboard/(bookings)/bookings/_components/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/booking-card.js +126 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/booking-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/demo-data.js +142 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/handover.js +128 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/handover.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/index.js +17 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/past-band.js +69 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/past-band.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/resource-board.js +111 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/resource-board.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/today-band.js +61 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/today-band.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/upcoming-band.js +113 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/upcoming-band.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/waiting-queue.js +62 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/waiting-queue.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today-board.js +192 -0
- package/dist/dashboard/(bookings)/bookings/_components/today-board.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today.js +1 -0
- package/dist/dashboard/(bookings)/bookings/_components.js +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/index.js +5 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-bookings-data.js +331 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-bookings-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-today-data.js +309 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-today-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks.js +1 -0
- package/dist/dashboard/(bookings)/bookings/_lib/booking-link.js +13 -0
- package/dist/dashboard/(bookings)/bookings/_lib/booking-link.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/attendee-cards.js +60 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/attendee-cards.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/qr-scanner-modal.js +225 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/qr-scanner-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/demo-data.js +195 -0
- package/dist/dashboard/(bookings)/bookings/attendees/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/page.js +341 -0
- package/dist/dashboard/(bookings)/bookings/attendees/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/new/page.js +129 -0
- package/dist/dashboard/(bookings)/bookings/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/page.js +10 -0
- package/dist/dashboard/(bookings)/bookings/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/save-buttons.js +36 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/save-buttons.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/session-creator.js +386 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/session-creator.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_lib/session-payload.js +62 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_lib/session-payload.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/page.js +10 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-nav.js +84 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-nav.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-range.js +84 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-range.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/day-board-view.js +279 -0
- package/dist/dashboard/(bookings)/calendar/_components/day-board-view.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/event-detail-modal.js +113 -0
- package/dist/dashboard/(bookings)/calendar/_components/event-detail-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/format.js +22 -0
- package/dist/dashboard/(bookings)/calendar/_components/format.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/month-grid.js +109 -0
- package/dist/dashboard/(bookings)/calendar/_components/month-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/resource-day-grid.js +325 -0
- package/dist/dashboard/(bookings)/calendar/_components/resource-day-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/slot-action-sheet.js +209 -0
- package/dist/dashboard/(bookings)/calendar/_components/slot-action-sheet.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-block-drag.js +77 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-block-drag.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-calendar-nav.js +69 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-calendar-nav.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/week-grid.js +202 -0
- package/dist/dashboard/(bookings)/calendar/_components/week-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/year-grid.js +111 -0
- package/dist/dashboard/(bookings)/calendar/_components/year-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/page.js +8 -0
- package/dist/dashboard/(bookings)/calendar/page.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feed-modal.js +126 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feed-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feeds-section.js +99 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feeds-section.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/demo-data.js +64 -0
- package/dist/dashboard/(bookings)/calendar-sync/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/page.js +15 -0
- package/dist/dashboard/(bookings)/calendar-sync/page.js.map +1 -0
- package/dist/dashboard/(bookings)/check-in/page.js +252 -0
- package/dist/dashboard/(bookings)/check-in/page.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-attendees.js +108 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-attendees.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-share.js +73 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-share.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/layout.js +10 -0
- package/dist/dashboard/(bookings)/events/[eventId]/layout.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/page.js +247 -0
- package/dist/dashboard/(bookings)/events/[eventId]/page.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_components/event-grid.js +44 -0
- package/dist/dashboard/(bookings)/events/_components/event-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_components/event-poster.js +17 -0
- package/dist/dashboard/(bookings)/events/_components/event-poster.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_hooks/use-events-data.js +55 -0
- package/dist/dashboard/(bookings)/events/_hooks/use-events-data.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_lib/event-facts.js +68 -0
- package/dist/dashboard/(bookings)/events/_lib/event-facts.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-composer.js +295 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-composer.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-poster.js +65 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-poster.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/_components/ticket-tiers.js +75 -0
- package/dist/dashboard/(bookings)/events/new/_components/ticket-tiers.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/page.js +10 -0
- package/dist/dashboard/(bookings)/events/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/events/page.js +214 -0
- package/dist/dashboard/(bookings)/events/page.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/layout.js +36 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/layout.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/page.js +351 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/page.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/block-time-modal.js +130 -0
- package/dist/dashboard/(bookings)/resources/_components/block-time-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/category-card.js +74 -0
- package/dist/dashboard/(bookings)/resources/_components/category-card.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/category-form-dialog.js +197 -0
- package/dist/dashboard/(bookings)/resources/_components/category-form-dialog.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/day-editor.js +194 -0
- package/dist/dashboard/(bookings)/resources/_components/day-editor.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/group-dialog.js +170 -0
- package/dist/dashboard/(bookings)/resources/_components/group-dialog.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/group-header.js +90 -0
- package/dist/dashboard/(bookings)/resources/_components/group-header.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/index.js +22 -0
- package/dist/dashboard/(bookings)/resources/_components/index.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/mobile-category-form-sheet.js +256 -0
- package/dist/dashboard/(bookings)/resources/_components/mobile-category-form-sheet.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-form.js +420 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-form.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-picker.js +144 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-picker.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-type-picker-empty.js +109 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-type-picker-empty.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-empty.js +103 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-empty.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-grid.js +160 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-editor.js +244 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-editor.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-timezone-field.js +25 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-timezone-field.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/solo-schedule-view.js +146 -0
- package/dist/dashboard/(bookings)/resources/_components/solo-schedule-view.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/team-onboarding-dialog.js +161 -0
- package/dist/dashboard/(bookings)/resources/_components/team-onboarding-dialog.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/week-grid.js +175 -0
- package/dist/dashboard/(bookings)/resources/_components/week-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components.js +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/index.js +8 -0
- package/dist/dashboard/(bookings)/resources/_hooks/index.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-resource-categories-data.js +86 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-resource-categories-data.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster-page.js +138 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster-page.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster.js +182 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks.js +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/presets.js +147 -0
- package/dist/dashboard/(bookings)/resources/_lib/presets.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/schedule-timezone.js +17 -0
- package/dist/dashboard/(bookings)/resources/_lib/schedule-timezone.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/shapes.js +105 -0
- package/dist/dashboard/(bookings)/resources/_lib/shapes.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/week.js +122 -0
- package/dist/dashboard/(bookings)/resources/_lib/week.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/page.js +391 -0
- package/dist/dashboard/(bookings)/resources/page.js.map +1 -0
- package/dist/dashboard/(bookings)/schedule/page.js +8 -0
- package/dist/dashboard/(bookings)/schedule/page.js.map +1 -0
- package/dist/dashboard/(bookings)/today/page.js +14 -0
- package/dist/dashboard/(bookings)/today/page.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/_components/demo-data.js +99 -0
- package/dist/dashboard/(bookings)/waitlist/_components/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/_components/offer-spot-modal.js +71 -0
- package/dist/dashboard/(bookings)/waitlist/_components/offer-spot-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/_components/waitlist-entry-editor.js +150 -0
- package/dist/dashboard/(bookings)/waitlist/_components/waitlist-entry-editor.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/new/page.js +59 -0
- package/dist/dashboard/(bookings)/waitlist/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/page.js +317 -0
- package/dist/dashboard/(bookings)/waitlist/page.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/bookings/_hooks/use-today-data.ts"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { bookingsApi } from '@withpotter/lib/api/bookings';\nimport { contentApi } from '@withpotter/lib/api/content';\nimport { resourcesApi } from '@withpotter/lib/api/resources';\nimport { tabsApi } from '@withpotter/lib/api/tabs';\nimport type {\n BookingItem,\n ResourceItem,\n WaitlistItem,\n Tab,\n} from '@withpotter/lib/api/types';\nimport { waitlistApi } from '@withpotter/lib/api/waitlist';\nimport { useApiQuery } from '@withpotter/hooks/use-api-query';\nimport { useDemoMode } from '@withpotter/hooks/use-demo-mode';\nimport {\n DEMO_TODAY_BOOKINGS,\n DEMO_TODAY_QUEUE,\n DEMO_TODAY_RESOURCES,\n DEMO_TODAY_TABS,\n} from '../_components/today/demo-data';\nimport { useHasIntent } from '@withpotter/hooks/use-tenant-intents';\nimport type { BookableService } from '@withpotter/components/features/use-booking-form';\n\n/** Local YYYY-MM-DD for \"today\" in the operator's timezone. */\nfunction localToday(): string {\n const d = new Date();\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\n/** Minutes-since-midnight for a booking's start/end, tolerant of ISO or HH:mm. */\nexport function toMinutes(value?: string | null): number | null {\n if (!value) return null;\n if (value.includes('T')) {\n const d = new Date(value);\n if (Number.isNaN(d.getTime())) return null;\n return d.getHours() * 60 + d.getMinutes();\n }\n const m = value.match(/^(\\d{1,2}):(\\d{2})/);\n if (!m) return null;\n return Number(m[1]) * 60 + Number(m[2]);\n}\n\nexport function isPaid(b: BookingItem): boolean {\n return (b.data as { isPaid?: boolean } | undefined)?.isPaid === true;\n}\n\nexport function bookingTotal(b: BookingItem): number {\n const fromData = b.data as\n { total?: number; paymentAmount?: number } | undefined;\n return Number(\n fromData?.total ?? fromData?.paymentAmount ?? b.content?.price ?? 0,\n );\n}\n\nconst LIVE_STATUSES = new Set([\n 'pending',\n 'confirmed',\n 'checked_in',\n 'in_progress',\n]);\n\n/** One column of the day board. */\nexport interface DayLane {\n id: string;\n title: string;\n bookings: BookingItem[];\n /**\n * Money currently open on this lane.\n *\n * Only dine-in has this: a table can be reserved (a booking) and be running a\n * bill (a tab) at the same time, and those lived on two screens. The board\n * showed which tables were reserved while the money sitting on them was a\n * click away in Tabs.\n */\n openTab?: { id: string; total: number; currency: string; itemCount: number };\n}\n\n/** Columns for a business whose bookings are not resource-backed. */\nconst TIME_BANDS = [\n { id: 'morning', title: 'Morning', from: 0, to: 12 * 60 },\n { id: 'afternoon', title: 'Afternoon', from: 12 * 60, to: 17 * 60 },\n { id: 'evening', title: 'Evening', from: 17 * 60, to: 24 * 60 },\n];\n\nconst byStartTime = (a: BookingItem, b: BookingItem) =>\n (toMinutes(a.startTime) ?? 0) - (toMinutes(b.startTime) ?? 0);\n\n/**\n * The board's columns, and what decides them.\n *\n * Resources when the business has them: a club reads its courts, a salon\n * reads chairs, a restaurant reads tables. Time of day when it does not.\n *\n * That second case is the whole point. The board used to be gated on\n * `resources.length === 0` and rendered an empty state instead, which is right\n * for a club that has not set its courts up yet and wrong for every archetype\n * whose bookings are not resource-backed at all. A short-let operator's\n * properties are catalogue content carrying `rentable`, never rows in\n * `/bookings/resources`, so a landlord with ten check-ins today got a screen\n * telling them to go and add properties. The bookings were there the whole\n * time; only the columns were missing.\n *\n * Pure, and exported, because this rule is the difference between a business\n * seeing its day and not seeing it.\n */\nexport function buildDayLanes(\n resources: ResourceItem[],\n bookings: BookingItem[],\n openTabs: Tab[] = [],\n /** Today, as YYYY-MM-DD. Given, the day reads as arrivals and departures. */\n today?: string,\n): DayLane[] {\n /**\n * A day of stays has a shape, and it is not morning-afternoon-evening.\n *\n * Time bands answer \"when is this happening\", which is the right question for\n * an appointment and a meaningless one for a guest who has been in the flat\n * since Tuesday — they land under \"Afternoon\" on the strength of a 4pm\n * check-in three days ago. What a desk needs to see is who is coming, who is\n * going, and who is staying put.\n */\n if (today && bookings.some((b) => b.endDate && b.endDate !== b.date)) {\n const arriving: BookingItem[] = [];\n const leaving: BookingItem[] = [];\n const staying: BookingItem[] = [];\n for (const b of bookings) {\n if (b.date === today) arriving.push(b);\n else if (b.endDate === today) leaving.push(b);\n else staying.push(b);\n }\n return [\n {\n id: 'arriving',\n title: 'Arriving',\n bookings: arriving.sort(byStartTime),\n },\n { id: 'leaving', title: 'Leaving', bookings: leaving.sort(byStartTime) },\n { id: 'staying', title: 'Staying', bookings: staying.sort(byStartTime) },\n ].filter((lane) => lane.bookings.length > 0);\n }\n\n if (resources.length > 0) {\n const byId = new Map<string, BookingItem[]>(\n resources.map((r) => [r.id, [] as BookingItem[]]),\n );\n const unassigned: BookingItem[] = [];\n for (const b of bookings) {\n const lane = b.resourceId ? byId.get(b.resourceId) : undefined;\n (lane ?? unassigned).push(b);\n }\n\n const tabByResource = new Map<string, Tab>();\n for (const tab of openTabs) {\n if (tab.resourceId) tabByResource.set(tab.resourceId, tab);\n }\n\n const lanes: DayLane[] = resources.map((r) => {\n const tab = tabByResource.get(r.id);\n return {\n id: r.id,\n title: r.name,\n bookings: (byId.get(r.id) ?? []).sort(byStartTime),\n openTab: tab\n ? {\n id: tab.id,\n total: tab.total,\n currency: tab.currency,\n itemCount: tab.items.reduce((n, i) => n + i.quantity, 0),\n }\n : undefined,\n };\n });\n // Never dropped: a booking against a resource this business no longer has\n // is exactly the one someone needs to find.\n if (unassigned.length) {\n lanes.push({\n id: 'unassigned',\n title: 'Unassigned',\n bookings: unassigned.sort(byStartTime),\n });\n }\n return lanes;\n }\n\n if (bookings.length === 0) return [];\n\n const bands: DayLane[] = TIME_BANDS.map((band) => ({\n id: band.id,\n title: band.title,\n bookings: [],\n }));\n const unscheduled: BookingItem[] = [];\n for (const b of bookings) {\n const start = toMinutes(b.startTime);\n if (start == null) {\n unscheduled.push(b);\n continue;\n }\n const index = TIME_BANDS.findIndex(\n (band) => start >= band.from && start < band.to,\n );\n bands[index === -1 ? bands.length - 1 : index].bookings.push(b);\n }\n for (const band of bands) band.bookings.sort(byStartTime);\n if (unscheduled.length) {\n bands.push({\n id: 'unscheduled',\n title: 'No time set',\n bookings: unscheduled,\n });\n }\n // Empty bands are dropped here rather than in the renderer: an empty court is\n // information (\"free all day\"), an empty morning is just a hole in the grid.\n return bands.filter((band) => band.bookings.length > 0);\n}\n\nexport function useTodayData({\n types,\n}: {\n /** The booking types to list; absent means every type. Declared by the route. */\n types?: readonly string[];\n} = {}) {\n const { tenant } = useAuth();\n const today = localToday();\n\n const [nowMinutes] = useState(() => {\n const d = new Date();\n return d.getHours() * 60 + d.getMinutes();\n });\n\n const {\n data: bookingsData,\n isLoading: bookingsLoading,\n refetch: refetchBookings,\n } = useApiQuery(\n ['today-bookings', today, types?.join('+') ?? 'all'],\n (slug) =>\n bookingsApi.list(slug, {\n dateFrom: today,\n dateTo: today,\n limit: 200,\n type: types ? [...types] : undefined,\n }),\n // The board is read standing up, while the day changes underneath it.\n { live: true },\n );\n\n const { data: resourcesData, isLoading: resourcesLoading } = useApiQuery(\n ['today-resources'],\n (slug) => resourcesApi.list(slug),\n );\n\n /**\n * What this business sells time on, for the walk-in form.\n *\n * A walk-in needs one of two things: a resource to put someone on, or a\n * service to book them into. The board only ever fetched the first, so the\n * Walk-in button was disabled for every business whose bookable thing is a\n * catalogue item — the same businesses `buildDayLanes` was fixed to show a\n * day for.\n */\n const { data: servicesData } = useApiQuery(\n ['today-bookable-services'],\n (slug) =>\n contentApi.list(slug, 'product', { behaviors: ['bookable'], limit: 100 }),\n );\n\n // Who is waiting for today. The board is where a slot comes free, so it is\n // where the queue for that slot belongs.\n const { data: queueData, refetch: refetchQueue } = useApiQuery(\n ['today-waitlist', today],\n (slug) => waitlistApi.queue(slug, today),\n { live: true },\n );\n\n // Only dine-in runs tabs, so only dine-in pays for the request.\n const isDineIn = useHasIntent('dine-in');\n const { data: tabsData } = useApiQuery(\n ['today-open-tabs'],\n (slug) => tabsApi.list(slug, 'open'),\n { enabled: isDineIn, live: true },\n );\n // Seed replaces each of the four feeds at the point it is read, so the\n // lanes, the queue and the running tabs all come from one consistent day.\n const demoActive = useDemoMode();\n\n const openTabs: Tab[] = useMemo(\n () =>\n demoActive ? (DEMO_TODAY_TABS as unknown as Tab[]) : (tabsData ?? []),\n [demoActive, tabsData],\n );\n\n const bookings = useMemo(\n () =>\n (demoActive ? DEMO_TODAY_BOOKINGS : (bookingsData?.data ?? [])).filter(\n (b) =>\n LIVE_STATUSES.has(b.status) ||\n b.status === 'completed' ||\n b.status === 'no_show',\n ),\n [bookingsData, demoActive],\n );\n\n const resources: ResourceItem[] = useMemo(\n () =>\n demoActive\n ? (DEMO_TODAY_RESOURCES as unknown as ResourceItem[])\n : (resourcesData?.data ?? []).filter((r) => r.status === 'active'),\n [resourcesData, demoActive],\n );\n\n /** Bookable catalogue items, flattened to what the walk-in form reads. */\n const services: BookableService[] = useMemo(\n () =>\n (servicesData?.data ?? []).map((item) => {\n const bookable = (item.behaviors?.bookable ?? {}) as {\n durationMinutes?: number;\n };\n const legacy = item.data as { durationMinutes?: number } | undefined;\n return {\n id: item.id,\n title: item.title,\n durationMinutes: bookable.durationMinutes ?? legacy?.durationMinutes,\n price: item.price,\n // The desk board only counts these to decide whether a walk-in has\n // anything to be booked into; the questions belong to the create\n // screen, which loads them itself.\n intake: [],\n };\n }),\n [servicesData],\n );\n\n // Group bookings by resource, each sorted by start time. Bookings with no\n // resource fall into an \"Unassigned\" bucket so nothing is hidden.\n const byResource = useMemo(() => {\n const map = new Map<string, BookingItem[]>();\n for (const c of resources) map.set(c.id, []);\n const unassigned: BookingItem[] = [];\n for (const b of bookings) {\n if (b.resourceId && map.has(b.resourceId)) {\n map.get(b.resourceId)!.push(b);\n } else {\n unassigned.push(b);\n }\n }\n for (const list of map.values()) {\n list.sort(\n (a, b) => (toMinutes(a.startTime) ?? 0) - (toMinutes(b.startTime) ?? 0),\n );\n }\n unassigned.sort(\n (a, b) => (toMinutes(a.startTime) ?? 0) - (toMinutes(b.startTime) ?? 0),\n );\n return { map, unassigned };\n }, [bookings, resources]);\n\n /**\n * The board's columns, and what decides them.\n *\n * Resources when the business has them: a club reads its courts, a salon\n * reads chairs, a restaurant reads tables. Time of day when it does not.\n *\n * That second case is the whole point. The board used to be gated on\n * `resources.length === 0` and render an empty state instead, which is fine\n * for a club that has not set up its courts yet and wrong for every archetype\n * whose bookings are not resource-backed at all. A short-let operator's\n * properties are catalogue content, never `/bookings/resources`, so the\n * landlord had ten check-ins today and a screen telling them to add\n * properties. Bookings exist; only the columns were missing.\n */\n const lanes: DayLane[] = useMemo(\n () => buildDayLanes(resources, bookings, openTabs, today),\n [resources, bookings, openTabs, today],\n );\n\n // The controller returns a bare array from this usecase while most others\n // return a { data } envelope; accept both rather than guess at the shape.\n const waiting: WaitlistItem[] = useMemo(() => {\n const queue = demoActive\n ? DEMO_TODAY_QUEUE\n : Array.isArray(queueData)\n ? queueData\n : (queueData?.data ?? []);\n return queue.filter((e) => e.status === 'waiting');\n }, [queueData, demoActive]);\n\n /**\n * A stay is not a slot, and the desk watches different things.\n *\n * The clock summary below cannot express a booking that crosses midnight: a\n * stay running 16:00 Tuesday to 12:00 Saturday asks whether\n * `960 <= now < 720`, which is never true — so a fully occupied property\n * reported nobody on now, nobody to check in, and four zeros across the top\n * while a guest was asleep upstairs.\n */\n const staySummary = useMemo(() => {\n const live = bookings.filter(\n (b) =>\n b.status !== 'cancelled' &&\n b.status !== 'no_show' &&\n b.status !== 'completed',\n );\n let arrivals = 0;\n let departures = 0;\n let inHouse = 0;\n for (const b of live) {\n if (b.date === today) arrivals += 1;\n if (b.endDate && b.endDate === today) departures += 1;\n if (b.date < today && (!b.endDate || b.endDate > today)) inHouse += 1;\n }\n return { arrivals, departures, inHouse };\n }, [bookings, today]);\n\n /**\n * Derived from the DATA rather than the archetype, so a business that both\n * rents rooms and takes appointments is read correctly either way.\n */\n const hasStays = useMemo(\n () => bookings.some((b) => !!b.endDate && b.endDate !== b.date),\n [bookings],\n );\n\n // Front-desk summary — the four things a desk actually watches.\n const summary = useMemo(() => {\n let onNow = 0;\n let dueNext = 0;\n let awaitingPayment = 0;\n let toCheckIn = 0;\n for (const b of bookings) {\n if (\n b.status === 'cancelled' ||\n b.status === 'no_show' ||\n b.status === 'completed'\n ) {\n continue;\n }\n const start = toMinutes(b.startTime);\n const end =\n toMinutes(b.endTime) ??\n (start != null ? start + (b.durationMinutes ?? 60) : null);\n if (\n start != null &&\n end != null &&\n start <= nowMinutes &&\n nowMinutes < end\n )\n onNow += 1;\n if (start != null && start > nowMinutes && start - nowMinutes <= 120)\n dueNext += 1;\n if (b.status !== 'pending' && !isPaid(b)) awaitingPayment += 1;\n if (b.status === 'confirmed' && !b.checkedInAt) toCheckIn += 1;\n }\n return { onNow, dueNext, awaitingPayment, toCheckIn };\n }, [bookings, nowMinutes]);\n\n // Demo never loads. With the API unreachable both flags stay true and the\n // board would sit on a skeleton over a full day of seeded rows.\n const loading = demoActive ? false : bookingsLoading || resourcesLoading;\n\n return {\n tenantSlug: tenant?.slug,\n today,\n nowMinutes,\n loading,\n bookings,\n resources,\n services,\n byResource,\n lanes,\n waiting,\n summary,\n staySummary,\n hasStays,\n refetch: refetchBookings,\n refetchQueue,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './_hooks/index.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const BOOKING_KEYS = /* @__PURE__ */ new Set(["bookable", "schedulable"]);
|
|
2
|
+
function shouldOfferBookingLink(readiness) {
|
|
3
|
+
if (!readiness) return false;
|
|
4
|
+
return !(readiness.blockers ?? []).some((b) => BOOKING_KEYS.has(b.key));
|
|
5
|
+
}
|
|
6
|
+
function bookingPageUrl(storefront) {
|
|
7
|
+
return `${storefront.replace(/\/+$/, "")}/booking`;
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
bookingPageUrl,
|
|
11
|
+
shouldOfferBookingLink
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=booking-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/bookings/_lib/booking-link.ts"],"sourcesContent":["import type { StorefrontReadiness } from '@withpotter/lib/api/types';\n\n/**\n * The booking keys `CannotTakeBookings` speaks for. Kept identical on purpose:\n * the two notices share this slot and must never both be right.\n */\nconst BOOKING_KEYS = new Set(['bookable', 'schedulable']);\n\n/**\n * Whether to hand the merchant the booking link.\n *\n * Silent until proven bookable, which is the opposite default to the blocker\n * notice next door and deliberately so. `readiness` is undefined while the\n * check is in flight and on any backend that does not serve the route; offering\n * the address there would point customers at a page that may show them a\n * fortnight of empty days — the exact failure the blocker notice exists to\n * prevent.\n */\nexport function shouldOfferBookingLink(\n readiness: StorefrontReadiness | undefined,\n): boolean {\n if (!readiness) return false;\n return !(readiness.blockers ?? []).some((b) => BOOKING_KEYS.has(b.key));\n}\n\n/** The public booking page for a storefront address. */\nexport function bookingPageUrl(storefront: string): string {\n return `${storefront.replace(/\\/+$/, '')}/booking`;\n}\n"],"mappings":"AAMA,MAAM,eAAe,oBAAI,IAAI,CAAC,YAAY,aAAa,CAAC;AAYjD,SAAS,uBACd,WACS;AACT,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,EAAE,UAAU,YAAY,CAAC,GAAG,KAAK,CAAC,MAAM,aAAa,IAAI,EAAE,GAAG,CAAC;AACxE;AAGO,SAAS,eAAe,YAA4B;AACzD,SAAO,GAAG,WAAW,QAAQ,QAAQ,EAAE,CAAC;AAC1C;","names":[]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { ChevronRight } from "lucide-react";
|
|
5
|
+
import { StatusBadge } from "@withpotter/components/shared/status-badge";
|
|
6
|
+
function AttendeeCards({
|
|
7
|
+
attendees,
|
|
8
|
+
statuses
|
|
9
|
+
}) {
|
|
10
|
+
return /* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx("div", { className: "bg-[var(--bg-subtle)] rounded-ios-lg overflow-hidden divide-y divide-[var(--border-subtle)]", children: attendees.map((attendee) => {
|
|
11
|
+
const statusConfig = statuses(attendee.type)[attendee.status] || statuses(attendee.type).pending;
|
|
12
|
+
const tierName = attendee.data?.tierName || "";
|
|
13
|
+
const isCheckedIn = attendee.status === "checked_in" || !!attendee.checkedInAt;
|
|
14
|
+
return /* @__PURE__ */ jsxs(
|
|
15
|
+
Link,
|
|
16
|
+
{
|
|
17
|
+
href: `/dashboard/bookings/${attendee.id}`,
|
|
18
|
+
className: "flex items-center gap-3 px-3 py-3 active:bg-[var(--bg-muted)] transition-colors",
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
21
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-primary)] truncate block", children: attendee.customerName || "Guest" }),
|
|
22
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [
|
|
23
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--text-secondary)] truncate", children: attendee.customerEmail }),
|
|
24
|
+
tierName && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx("span", { className: "text-[var(--text-muted)]", children: "\xB7" }),
|
|
26
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-[var(--text-muted)] flex-shrink-0", children: [
|
|
27
|
+
tierName,
|
|
28
|
+
" \xD7 ",
|
|
29
|
+
attendee.quantity || 1
|
|
30
|
+
] })
|
|
31
|
+
] })
|
|
32
|
+
] }),
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
34
|
+
/* @__PURE__ */ jsx(
|
|
35
|
+
StatusBadge,
|
|
36
|
+
{
|
|
37
|
+
status: attendee.status,
|
|
38
|
+
label: statusConfig.label
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
/* @__PURE__ */ jsx(
|
|
42
|
+
StatusBadge,
|
|
43
|
+
{
|
|
44
|
+
status: isCheckedIn ? "checked_in" : "pending",
|
|
45
|
+
label: isCheckedIn ? "Checked in" : "Not checked in"
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
] })
|
|
49
|
+
] }),
|
|
50
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "size-4 text-[var(--text-muted)] flex-shrink-0" })
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
attendee.id
|
|
54
|
+
);
|
|
55
|
+
}) }) });
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
AttendeeCards
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=attendee-cards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/dashboard/%28bookings%29/bookings/attendees/_components/attendee-cards.tsx"],"sourcesContent":["'use client';\n\nimport Link from 'next/link';\nimport { ChevronRight } from 'lucide-react';\nimport { StatusBadge } from '@withpotter/components/shared/status-badge';\nimport type { BookingItem } from '@withpotter/lib/api/types';\nimport type { BookingStatus } from '../../_components/booking-constants';\nimport type { useBookingStatusConfig } from '../../_components/booking-constants';\n\n/**\n * The attendee list at phone and tablet width.\n *\n * Lifted out of the page, which had grown past the 500-line house limit while\n * carrying two renderings of the same rows. Nothing about the cards changed in\n * the move except their status pills, which are the app's `StatusBadge` now\n * rather than a fourth hand-drawn copy of one.\n */\nexport function AttendeeCards({\n attendees,\n statuses,\n}: {\n attendees: BookingItem[];\n /** The archetype-aware status words, from `useBookingStatusConfig`. */\n statuses: ReturnType<typeof useBookingStatusConfig>;\n}) {\n return (\n <div className=\"lg:hidden\">\n <div className=\"bg-[var(--bg-subtle)] rounded-ios-lg overflow-hidden divide-y divide-[var(--border-subtle)]\">\n {attendees.map((attendee) => {\n const statusConfig =\n statuses(attendee.type)[attendee.status as BookingStatus] ||\n statuses(attendee.type).pending;\n const tierName = (attendee.data?.tierName as string) || '';\n const isCheckedIn =\n attendee.status === 'checked_in' || !!attendee.checkedInAt;\n\n return (\n <Link\n key={attendee.id}\n href={`/dashboard/bookings/${attendee.id}`}\n className=\"flex items-center gap-3 px-3 py-3 active:bg-[var(--bg-muted)] transition-colors\"\n >\n <div className=\"flex-1 min-w-0\">\n <span className=\"text-sm font-medium text-[var(--text-primary)] truncate block\">\n {attendee.customerName || 'Guest'}\n </span>\n <div className=\"flex items-center gap-2 mt-0.5\">\n <span className=\"text-sm text-[var(--text-secondary)] truncate\">\n {attendee.customerEmail}\n </span>\n {tierName && (\n <>\n <span className=\"text-[var(--text-muted)]\">·</span>\n <span className=\"text-sm text-[var(--text-muted)] flex-shrink-0\">\n {tierName} × {attendee.quantity || 1}\n </span>\n </>\n )}\n </div>\n <div className=\"flex items-center gap-2 mt-2\">\n <StatusBadge\n status={attendee.status}\n label={statusConfig.label}\n />\n <StatusBadge\n status={isCheckedIn ? 'checked_in' : 'pending'}\n label={isCheckedIn ? 'Checked in' : 'Not checked in'}\n />\n </div>\n </div>\n <ChevronRight className=\"size-4 text-[var(--text-muted)] flex-shrink-0\" />\n </Link>\n );\n })}\n </div>\n </div>\n );\n}\n"],"mappings":";AA2CgB,SAQI,UARJ,KAUM,YAVN;AAzChB,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAarB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AACF,GAIG;AACD,SACE,oBAAC,SAAI,WAAU,aACb,8BAAC,SAAI,WAAU,+FACZ,oBAAU,IAAI,CAAC,aAAa;AAC3B,UAAM,eACJ,SAAS,SAAS,IAAI,EAAE,SAAS,MAAuB,KACxD,SAAS,SAAS,IAAI,EAAE;AAC1B,UAAM,WAAY,SAAS,MAAM,YAAuB;AACxD,UAAM,cACJ,SAAS,WAAW,gBAAgB,CAAC,CAAC,SAAS;AAEjD,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,MAAM,uBAAuB,SAAS,EAAE;AAAA,QACxC,WAAU;AAAA,QAEV;AAAA,+BAAC,SAAI,WAAU,kBACb;AAAA,gCAAC,UAAK,WAAU,iEACb,mBAAS,gBAAgB,SAC5B;AAAA,YACA,qBAAC,SAAI,WAAU,kCACb;AAAA,kCAAC,UAAK,WAAU,iDACb,mBAAS,eACZ;AAAA,cACC,YACC,iCACE;AAAA,oCAAC,UAAK,WAAU,4BAA2B,kBAAQ;AAAA,gBACnD,qBAAC,UAAK,WAAU,kDACb;AAAA;AAAA,kBAAS;AAAA,kBAAU,SAAS,YAAY;AAAA,mBAC3C;AAAA,iBACF;AAAA,eAEJ;AAAA,YACA,qBAAC,SAAI,WAAU,gCACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,QAAQ,SAAS;AAAA,kBACjB,OAAO,aAAa;AAAA;AAAA,cACtB;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,QAAQ,cAAc,eAAe;AAAA,kBACrC,OAAO,cAAc,eAAe;AAAA;AAAA,cACtC;AAAA,eACF;AAAA,aACF;AAAA,UACA,oBAAC,gBAAa,WAAU,iDAAgD;AAAA;AAAA;AAAA,MAhCnE,SAAS;AAAA,IAiChB;AAAA,EAEJ,CAAC,GACH,GACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState, useCallback } from "react";
|
|
4
|
+
import { Html5Qrcode } from "html5-qrcode";
|
|
5
|
+
import { X, Camera, CheckCircle2, XCircle, Loader2 } from "lucide-react";
|
|
6
|
+
import { Button } from "@withpotter/ui-kit/button";
|
|
7
|
+
import { useModalBehaviour } from "@withpotter/hooks/use-modal-behaviour";
|
|
8
|
+
function QrScannerModal({ open, onClose, onScan }) {
|
|
9
|
+
const scannerRef = useRef(null);
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
const [scanning, setScanning] = useState(false);
|
|
12
|
+
const [processing, setProcessing] = useState(false);
|
|
13
|
+
const [result, setResult] = useState(null);
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
const processedRef = useRef(/* @__PURE__ */ new Set());
|
|
16
|
+
const stopScanner = useCallback(async () => {
|
|
17
|
+
if (scannerRef.current) {
|
|
18
|
+
try {
|
|
19
|
+
const state = scannerRef.current.getState();
|
|
20
|
+
if (state === 2) {
|
|
21
|
+
await scannerRef.current.stop();
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
scannerRef.current = null;
|
|
26
|
+
}
|
|
27
|
+
setScanning(false);
|
|
28
|
+
}, []);
|
|
29
|
+
const startScanner = useCallback(async () => {
|
|
30
|
+
if (!containerRef.current || scannerRef.current) return;
|
|
31
|
+
setError(null);
|
|
32
|
+
setResult(null);
|
|
33
|
+
if (typeof window !== "undefined") {
|
|
34
|
+
const isLocalhost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname === "[::1]";
|
|
35
|
+
const isSecure = window.isSecureContext || window.location.protocol === "https:" || isLocalhost;
|
|
36
|
+
if (!isSecure) {
|
|
37
|
+
setError(
|
|
38
|
+
"Camera requires a secure connection. Open this page over HTTPS (or use the manual ticket code field below) and try again."
|
|
39
|
+
);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!navigator.mediaDevices || typeof navigator.mediaDevices.getUserMedia !== "function") {
|
|
43
|
+
setError(
|
|
44
|
+
"This browser does not expose camera access. Try Safari or Chrome, or enter the ticket code manually."
|
|
45
|
+
);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const scannerId = "qr-scanner-region";
|
|
50
|
+
let scanEl = document.getElementById(scannerId);
|
|
51
|
+
if (!scanEl) {
|
|
52
|
+
scanEl = document.createElement("div");
|
|
53
|
+
scanEl.id = scannerId;
|
|
54
|
+
containerRef.current.appendChild(scanEl);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const scanner = new Html5Qrcode(scannerId);
|
|
58
|
+
scannerRef.current = scanner;
|
|
59
|
+
await scanner.start(
|
|
60
|
+
{ facingMode: "environment" },
|
|
61
|
+
{
|
|
62
|
+
fps: 10,
|
|
63
|
+
qrbox: { width: 250, height: 250 },
|
|
64
|
+
aspectRatio: 1
|
|
65
|
+
},
|
|
66
|
+
async (decodedText) => {
|
|
67
|
+
if (processedRef.current.has(decodedText) || processing) return;
|
|
68
|
+
processedRef.current.add(decodedText);
|
|
69
|
+
setProcessing(true);
|
|
70
|
+
try {
|
|
71
|
+
await scanner.pause();
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const res = await onScan(decodedText);
|
|
76
|
+
setResult(res);
|
|
77
|
+
setTimeout(async () => {
|
|
78
|
+
setResult(null);
|
|
79
|
+
setProcessing(false);
|
|
80
|
+
processedRef.current.delete(decodedText);
|
|
81
|
+
try {
|
|
82
|
+
scanner.resume();
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}, 2500);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
setResult({
|
|
88
|
+
message: err?.message || "Scan failed",
|
|
89
|
+
success: false
|
|
90
|
+
});
|
|
91
|
+
setProcessing(false);
|
|
92
|
+
processedRef.current.delete(decodedText);
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
setResult(null);
|
|
95
|
+
try {
|
|
96
|
+
scanner.resume();
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
}, 2500);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
() => {
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
setScanning(true);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
const name = err?.name || "";
|
|
108
|
+
const msg = err?.message || "";
|
|
109
|
+
let friendly = "Failed to start camera. Try again, or enter the ticket code manually.";
|
|
110
|
+
if (name === "NotAllowedError" || /permission/i.test(msg)) {
|
|
111
|
+
friendly = "Camera permission denied. Allow camera access in your browser settings, then tap Try Again.";
|
|
112
|
+
} else if (name === "NotFoundError" || name === "OverconstrainedError") {
|
|
113
|
+
friendly = "No camera found on this device. Use the manual ticket code field instead.";
|
|
114
|
+
} else if (name === "NotReadableError" || name === "TrackStartError") {
|
|
115
|
+
friendly = "Camera is in use by another app. Close other apps using the camera and try again.";
|
|
116
|
+
} else if (name === "SecurityError") {
|
|
117
|
+
friendly = "Camera blocked by browser security. Open this page over HTTPS or use localhost, then try again.";
|
|
118
|
+
} else if (msg) {
|
|
119
|
+
friendly = `${friendly} (${msg})`;
|
|
120
|
+
}
|
|
121
|
+
setError(friendly);
|
|
122
|
+
scannerRef.current = null;
|
|
123
|
+
}
|
|
124
|
+
}, [onScan, processing]);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (open) {
|
|
127
|
+
const timeout = setTimeout(startScanner, 300);
|
|
128
|
+
return () => clearTimeout(timeout);
|
|
129
|
+
} else {
|
|
130
|
+
stopScanner();
|
|
131
|
+
processedRef.current.clear();
|
|
132
|
+
setResult(null);
|
|
133
|
+
setError(null);
|
|
134
|
+
setProcessing(false);
|
|
135
|
+
}
|
|
136
|
+
}, [open]);
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
return () => {
|
|
139
|
+
stopScanner();
|
|
140
|
+
};
|
|
141
|
+
}, []);
|
|
142
|
+
const close = useCallback(() => {
|
|
143
|
+
void stopScanner();
|
|
144
|
+
onClose();
|
|
145
|
+
}, [stopScanner, onClose]);
|
|
146
|
+
const panelRef = useModalBehaviour(open, close);
|
|
147
|
+
if (!open) return null;
|
|
148
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[var(--z-modal)] flex items-center justify-center", children: [
|
|
149
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/60", onClick: close }),
|
|
150
|
+
/* @__PURE__ */ jsxs(
|
|
151
|
+
"div",
|
|
152
|
+
{
|
|
153
|
+
ref: panelRef,
|
|
154
|
+
role: "dialog",
|
|
155
|
+
"aria-modal": "true",
|
|
156
|
+
"aria-label": "Scan ticket QR",
|
|
157
|
+
tabIndex: -1,
|
|
158
|
+
className: "relative w-full max-w-md mx-4 bg-[var(--bg-base)] rounded-ios-lg lg:rounded-md overflow-hidden shadow-xl",
|
|
159
|
+
children: [
|
|
160
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-[var(--border-default)]", children: [
|
|
161
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
162
|
+
/* @__PURE__ */ jsx(Camera, { className: "size-5 text-[var(--text-secondary)]" }),
|
|
163
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-[var(--text-primary)]", children: "Scan ticket QR" })
|
|
164
|
+
] }),
|
|
165
|
+
/* @__PURE__ */ jsx(
|
|
166
|
+
"button",
|
|
167
|
+
{
|
|
168
|
+
"aria-label": "Close",
|
|
169
|
+
type: "button",
|
|
170
|
+
onClick: close,
|
|
171
|
+
className: "p-2 -mr-2 text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors",
|
|
172
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-5" })
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
] }),
|
|
176
|
+
/* @__PURE__ */ jsxs("div", { className: "relative bg-black", children: [
|
|
177
|
+
/* @__PURE__ */ jsx(
|
|
178
|
+
"div",
|
|
179
|
+
{
|
|
180
|
+
ref: containerRef,
|
|
181
|
+
className: "w-full aspect-square max-h-[400px]"
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
!scanning && !error && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black/80", children: [
|
|
185
|
+
/* @__PURE__ */ jsx(Loader2, { className: "size-8 text-white animate-spin mb-2" }),
|
|
186
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-white/80", children: "Starting camera..." })
|
|
187
|
+
] }),
|
|
188
|
+
result && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/70", children: /* @__PURE__ */ jsxs(
|
|
189
|
+
"div",
|
|
190
|
+
{
|
|
191
|
+
className: `flex flex-col items-center gap-2 p-6 rounded-ios-lg ${result.success ? "bg-[var(--success-bg)]" : "bg-[var(--error-bg)]"}`,
|
|
192
|
+
children: [
|
|
193
|
+
result.success ? /* @__PURE__ */ jsx(CheckCircle2, { className: "size-12 text-[var(--success)]" }) : /* @__PURE__ */ jsx(XCircle, { className: "size-12 text-[var(--error)]" }),
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
"p",
|
|
196
|
+
{
|
|
197
|
+
className: `text-sm font-medium text-center max-w-[200px] ${result.success ? "text-[var(--success)]" : "text-[var(--error)]"}`,
|
|
198
|
+
children: result.message
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
) })
|
|
204
|
+
] }),
|
|
205
|
+
error && /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
206
|
+
/* @__PURE__ */ jsx("div", { className: "bg-[var(--error-bg)] border border-[var(--error)]/20 rounded-ios-sm lg:rounded-md p-3", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--error)]", children: error }) }),
|
|
207
|
+
/* @__PURE__ */ jsx(
|
|
208
|
+
Button,
|
|
209
|
+
{
|
|
210
|
+
className: "mt-3 w-full lg:rounded-md",
|
|
211
|
+
onClick: startScanner,
|
|
212
|
+
children: "Try again"
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
] }),
|
|
216
|
+
scanning && !error && /* @__PURE__ */ jsx("div", { className: "px-4 py-3 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-tertiary)]", children: "Point your camera at a ticket QR code to check in" }) })
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
)
|
|
220
|
+
] });
|
|
221
|
+
}
|
|
222
|
+
export {
|
|
223
|
+
QrScannerModal
|
|
224
|
+
};
|
|
225
|
+
//# sourceMappingURL=qr-scanner-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/dashboard/%28bookings%29/bookings/attendees/_components/qr-scanner-modal.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState, useCallback } from 'react';\nimport { Html5Qrcode } from 'html5-qrcode';\nimport { X, Camera, CheckCircle2, XCircle, Loader2 } from 'lucide-react';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { useModalBehaviour } from '@withpotter/hooks/use-modal-behaviour';\n\ninterface QrScannerModalProps {\n open: boolean;\n onClose: () => void;\n onScan: (\n ticketCode: string,\n ) => Promise<{ message: string; success: boolean }>;\n}\n\nexport function QrScannerModal({ open, onClose, onScan }: QrScannerModalProps) {\n const scannerRef = useRef<Html5Qrcode | null>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const [scanning, setScanning] = useState(false);\n const [processing, setProcessing] = useState(false);\n const [result, setResult] = useState<{\n message: string;\n success: boolean;\n } | null>(null);\n const [error, setError] = useState<string | null>(null);\n const processedRef = useRef<Set<string>>(new Set());\n\n const stopScanner = useCallback(async () => {\n if (scannerRef.current) {\n try {\n const state = scannerRef.current.getState();\n // 2 = SCANNING\n if (state === 2) {\n await scannerRef.current.stop();\n }\n } catch {\n // ignore\n }\n scannerRef.current = null;\n }\n setScanning(false);\n }, []);\n\n const startScanner = useCallback(async () => {\n if (!containerRef.current || scannerRef.current) return;\n\n setError(null);\n setResult(null);\n\n // Pre-flight checks: secure context + mediaDevices availability.\n // iOS Safari and most browsers require HTTPS (or localhost) for camera access.\n if (typeof window !== 'undefined') {\n const isLocalhost =\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '[::1]';\n const isSecure =\n window.isSecureContext ||\n window.location.protocol === 'https:' ||\n isLocalhost;\n if (!isSecure) {\n setError(\n 'Camera requires a secure connection. Open this page over HTTPS (or use the manual ticket code field below) and try again.',\n );\n return;\n }\n if (\n !navigator.mediaDevices ||\n typeof navigator.mediaDevices.getUserMedia !== 'function'\n ) {\n setError(\n 'This browser does not expose camera access. Try Safari or Chrome, or enter the ticket code manually.',\n );\n return;\n }\n }\n\n const scannerId = 'qr-scanner-region';\n\n // Ensure container has the element\n let scanEl = document.getElementById(scannerId);\n if (!scanEl) {\n scanEl = document.createElement('div');\n scanEl.id = scannerId;\n containerRef.current.appendChild(scanEl);\n }\n\n try {\n const scanner = new Html5Qrcode(scannerId);\n scannerRef.current = scanner;\n\n await scanner.start(\n { facingMode: 'environment' },\n {\n fps: 10,\n qrbox: { width: 250, height: 250 },\n aspectRatio: 1,\n },\n async (decodedText) => {\n // Avoid processing the same code multiple times in quick succession\n if (processedRef.current.has(decodedText) || processing) return;\n processedRef.current.add(decodedText);\n setProcessing(true);\n\n try {\n // Pause scanning while processing\n await scanner.pause();\n } catch {\n // ignore\n }\n\n try {\n const res = await onScan(decodedText);\n setResult(res);\n\n // Auto-resume scanning after showing result\n setTimeout(async () => {\n setResult(null);\n setProcessing(false);\n processedRef.current.delete(decodedText);\n try {\n scanner.resume();\n } catch {\n // ignore\n }\n }, 2500);\n } catch (err) {\n setResult({\n message: (err as Error)?.message || 'Scan failed',\n success: false,\n });\n setProcessing(false);\n processedRef.current.delete(decodedText);\n setTimeout(() => {\n setResult(null);\n try {\n scanner.resume();\n } catch {\n // ignore\n }\n }, 2500);\n }\n },\n () => {\n // QR code scan error (no QR found in frame) — ignore\n },\n );\n\n setScanning(true);\n } catch (err) {\n // A camera failure arrives as a DOMException, whose `name` is the part\n // worth branching on — NotAllowedError, NotFoundError.\n const name: string = (err as Error)?.name || '';\n const msg: string = (err as Error)?.message || '';\n let friendly =\n 'Failed to start camera. Try again, or enter the ticket code manually.';\n if (name === 'NotAllowedError' || /permission/i.test(msg)) {\n friendly =\n 'Camera permission denied. Allow camera access in your browser settings, then tap Try Again.';\n } else if (name === 'NotFoundError' || name === 'OverconstrainedError') {\n friendly =\n 'No camera found on this device. Use the manual ticket code field instead.';\n } else if (name === 'NotReadableError' || name === 'TrackStartError') {\n friendly =\n 'Camera is in use by another app. Close other apps using the camera and try again.';\n } else if (name === 'SecurityError') {\n friendly =\n 'Camera blocked by browser security. Open this page over HTTPS or use localhost, then try again.';\n } else if (msg) {\n friendly = `${friendly} (${msg})`;\n }\n setError(friendly);\n scannerRef.current = null;\n }\n }, [onScan, processing]);\n\n useEffect(() => {\n if (open) {\n // Small delay to let the modal DOM render\n const timeout = setTimeout(startScanner, 300);\n return () => clearTimeout(timeout);\n } else {\n stopScanner();\n processedRef.current.clear();\n setResult(null);\n setError(null);\n setProcessing(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n stopScanner();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n /** Stop the camera first, then let the panel go. */\n const close = useCallback(() => {\n void stopScanner();\n onClose();\n }, [stopScanner, onClose]);\n\n // Escape, focus trap, scroll lock, focus restore.\n const panelRef = useModalBehaviour(open, close);\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-[var(--z-modal)] flex items-center justify-center\">\n {/* Backdrop */}\n <div className=\"absolute inset-0 bg-black/60\" onClick={close} />\n\n {/* Modal. Not FormDialog — this is a camera viewport rather than a form,\n and it owns its own dismissal (the scanner has to be stopped before\n the panel goes). It still owes the four things every overlay owes,\n which is what the hook supplies. */}\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Scan ticket QR\"\n tabIndex={-1}\n className=\"relative w-full max-w-md mx-4 bg-[var(--bg-base)] rounded-ios-lg lg:rounded-md overflow-hidden shadow-xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-[var(--border-default)]\">\n <div className=\"flex items-center gap-2\">\n <Camera className=\"size-5 text-[var(--text-secondary)]\" />\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">\n Scan ticket QR\n </h2>\n </div>\n <button\n aria-label=\"Close\"\n type=\"button\"\n onClick={close}\n className=\"p-2 -mr-2 text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Scanner viewport */}\n <div className=\"relative bg-black\">\n <div\n ref={containerRef}\n className=\"w-full aspect-square max-h-[400px]\"\n />\n\n {/* Loading overlay */}\n {!scanning && !error && (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center bg-black/80\">\n <Loader2 className=\"size-8 text-white animate-spin mb-2\" />\n <p className=\"text-sm text-white/80\">Starting camera...</p>\n </div>\n )}\n\n {/* Result overlay */}\n {result && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/70\">\n <div\n className={`flex flex-col items-center gap-2 p-6 rounded-ios-lg ${\n result.success\n ? 'bg-[var(--success-bg)]'\n : 'bg-[var(--error-bg)]'\n }`}\n >\n {result.success ? (\n <CheckCircle2 className=\"size-12 text-[var(--success)]\" />\n ) : (\n <XCircle className=\"size-12 text-[var(--error)]\" />\n )}\n <p\n className={`text-sm font-medium text-center max-w-[200px] ${\n result.success\n ? 'text-[var(--success)]'\n : 'text-[var(--error)]'\n }`}\n >\n {result.message}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {/* Error state */}\n {error && (\n <div className=\"p-4\">\n <div className=\"bg-[var(--error-bg)] border border-[var(--error)]/20 rounded-ios-sm lg:rounded-md p-3\">\n <p className=\"text-sm text-[var(--error)]\">{error}</p>\n </div>\n <Button\n className=\"mt-3 w-full lg:rounded-md\"\n onClick={startScanner}\n >\n Try again\n </Button>\n </div>\n )}\n\n {/* Footer hint */}\n {scanning && !error && (\n <div className=\"px-4 py-3 text-center\">\n <p className=\"text-xs text-[var(--text-tertiary)]\">\n Point your camera at a ticket QR code to check in\n </p>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
const day = (offsetDays, time = "19:00") => {
|
|
2
|
+
const base = /* @__PURE__ */ new Date(`2026-08-11T${time}:00.000Z`);
|
|
3
|
+
base.setDate(base.getDate() + offsetDays);
|
|
4
|
+
return base.toISOString();
|
|
5
|
+
};
|
|
6
|
+
const dateOnly = (offsetDays) => day(offsetDays).slice(0, 10);
|
|
7
|
+
function mkEvent(e) {
|
|
8
|
+
return {
|
|
9
|
+
tenantId: "demo",
|
|
10
|
+
type: "product",
|
|
11
|
+
isActive: true,
|
|
12
|
+
currency: "NGN",
|
|
13
|
+
createdAt: day(-20),
|
|
14
|
+
updatedAt: day(-2),
|
|
15
|
+
...e
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const DEMO_TICKETED_EVENTS = [
|
|
19
|
+
mkEvent({
|
|
20
|
+
id: "demo-ev1",
|
|
21
|
+
title: "Saturday Night Session",
|
|
22
|
+
slug: "saturday-night-session",
|
|
23
|
+
price: 22500,
|
|
24
|
+
behaviors: { ticketed: true, sellable: true },
|
|
25
|
+
data: { eventDate: dateOnly(3), venue: "The Yard, Lekki", capacity: 180 }
|
|
26
|
+
}),
|
|
27
|
+
mkEvent({
|
|
28
|
+
id: "demo-ev2",
|
|
29
|
+
title: "Sunday Listening Room",
|
|
30
|
+
slug: "sunday-listening-room",
|
|
31
|
+
price: 15e3,
|
|
32
|
+
behaviors: { ticketed: true, sellable: true },
|
|
33
|
+
data: { eventDate: dateOnly(-4), venue: "The Yard, Lekki", capacity: 90 }
|
|
34
|
+
})
|
|
35
|
+
];
|
|
36
|
+
function mkAttendee(b) {
|
|
37
|
+
return {
|
|
38
|
+
tenantId: "demo",
|
|
39
|
+
type: "event",
|
|
40
|
+
quantity: 1,
|
|
41
|
+
createdAt: day(-6),
|
|
42
|
+
updatedAt: day(-6),
|
|
43
|
+
...b
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const DEMO_ATTENDEES = [
|
|
47
|
+
mkAttendee({
|
|
48
|
+
id: "demo-a1",
|
|
49
|
+
bookingNumber: "BK-3081",
|
|
50
|
+
contentId: "demo-ev1",
|
|
51
|
+
content: {
|
|
52
|
+
id: "demo-ev1",
|
|
53
|
+
title: "Saturday Night Session",
|
|
54
|
+
slug: "saturday-night-session",
|
|
55
|
+
price: 22500
|
|
56
|
+
},
|
|
57
|
+
customerName: "Ifeanyi Obi",
|
|
58
|
+
customerEmail: "ifeanyi.obi@gmail.com",
|
|
59
|
+
customerPhone: "+2348031120945",
|
|
60
|
+
status: "checked_in",
|
|
61
|
+
checkedInAt: day(0, "20:14"),
|
|
62
|
+
ticketCode: "SNS-4H2K",
|
|
63
|
+
quantity: 2,
|
|
64
|
+
date: dateOnly(3),
|
|
65
|
+
startTime: "20:00"
|
|
66
|
+
}),
|
|
67
|
+
mkAttendee({
|
|
68
|
+
id: "demo-a2",
|
|
69
|
+
bookingNumber: "BK-3080",
|
|
70
|
+
contentId: "demo-ev1",
|
|
71
|
+
content: {
|
|
72
|
+
id: "demo-ev1",
|
|
73
|
+
title: "Saturday Night Session",
|
|
74
|
+
slug: "saturday-night-session",
|
|
75
|
+
price: 22500
|
|
76
|
+
},
|
|
77
|
+
customerName: "Chidinma Eze",
|
|
78
|
+
customerEmail: "chidinma.eze@gmail.com",
|
|
79
|
+
customerPhone: "+2348024410982",
|
|
80
|
+
status: "confirmed",
|
|
81
|
+
ticketCode: "SNS-9QW1",
|
|
82
|
+
quantity: 2,
|
|
83
|
+
date: dateOnly(3),
|
|
84
|
+
startTime: "20:00"
|
|
85
|
+
}),
|
|
86
|
+
mkAttendee({
|
|
87
|
+
id: "demo-a3",
|
|
88
|
+
bookingNumber: "BK-3079",
|
|
89
|
+
contentId: "demo-ev1",
|
|
90
|
+
content: {
|
|
91
|
+
id: "demo-ev1",
|
|
92
|
+
title: "Saturday Night Session",
|
|
93
|
+
slug: "saturday-night-session",
|
|
94
|
+
price: 22500
|
|
95
|
+
},
|
|
96
|
+
customerName: "Tunde Bakare",
|
|
97
|
+
customerEmail: "tunde.bakare@outlook.com",
|
|
98
|
+
status: "checked_in",
|
|
99
|
+
checkedInAt: day(0, "20:31"),
|
|
100
|
+
ticketCode: "SNS-7B3D",
|
|
101
|
+
date: dateOnly(3),
|
|
102
|
+
startTime: "20:00"
|
|
103
|
+
}),
|
|
104
|
+
mkAttendee({
|
|
105
|
+
id: "demo-a4",
|
|
106
|
+
bookingNumber: "BK-3078",
|
|
107
|
+
contentId: "demo-ev1",
|
|
108
|
+
content: {
|
|
109
|
+
id: "demo-ev1",
|
|
110
|
+
title: "Saturday Night Session",
|
|
111
|
+
slug: "saturday-night-session",
|
|
112
|
+
price: 22500
|
|
113
|
+
},
|
|
114
|
+
customerName: "Amaka Nwosu",
|
|
115
|
+
customerEmail: "amaka@studioline.ng",
|
|
116
|
+
customerPhone: "+2347066120114",
|
|
117
|
+
status: "confirmed",
|
|
118
|
+
ticketCode: "SNS-2M8P",
|
|
119
|
+
date: dateOnly(3),
|
|
120
|
+
startTime: "20:00"
|
|
121
|
+
}),
|
|
122
|
+
mkAttendee({
|
|
123
|
+
id: "demo-a5",
|
|
124
|
+
bookingNumber: "BK-3077",
|
|
125
|
+
contentId: "demo-ev1",
|
|
126
|
+
content: {
|
|
127
|
+
id: "demo-ev1",
|
|
128
|
+
title: "Saturday Night Session",
|
|
129
|
+
slug: "saturday-night-session",
|
|
130
|
+
price: 22500
|
|
131
|
+
},
|
|
132
|
+
customerName: "Segun Adeyemi",
|
|
133
|
+
customerEmail: "segun.a@gmail.com",
|
|
134
|
+
status: "cancelled",
|
|
135
|
+
ticketCode: "SNS-5T0R",
|
|
136
|
+
date: dateOnly(3),
|
|
137
|
+
startTime: "20:00"
|
|
138
|
+
}),
|
|
139
|
+
mkAttendee({
|
|
140
|
+
id: "demo-a6",
|
|
141
|
+
bookingNumber: "BK-3062",
|
|
142
|
+
contentId: "demo-ev2",
|
|
143
|
+
content: {
|
|
144
|
+
id: "demo-ev2",
|
|
145
|
+
title: "Sunday Listening Room",
|
|
146
|
+
slug: "sunday-listening-room",
|
|
147
|
+
price: 15e3
|
|
148
|
+
},
|
|
149
|
+
customerName: "Zainab Musa",
|
|
150
|
+
customerEmail: "zainab.musa@gmail.com",
|
|
151
|
+
customerPhone: "+2348139902411",
|
|
152
|
+
status: "checked_in",
|
|
153
|
+
checkedInAt: day(-4, "18:52"),
|
|
154
|
+
ticketCode: "SLR-1K9V",
|
|
155
|
+
quantity: 2,
|
|
156
|
+
date: dateOnly(-4),
|
|
157
|
+
startTime: "18:30"
|
|
158
|
+
}),
|
|
159
|
+
mkAttendee({
|
|
160
|
+
id: "demo-a7",
|
|
161
|
+
bookingNumber: "BK-3061",
|
|
162
|
+
contentId: "demo-ev2",
|
|
163
|
+
content: {
|
|
164
|
+
id: "demo-ev2",
|
|
165
|
+
title: "Sunday Listening Room",
|
|
166
|
+
slug: "sunday-listening-room",
|
|
167
|
+
price: 15e3
|
|
168
|
+
},
|
|
169
|
+
customerName: "Ngozi Umeh",
|
|
170
|
+
customerEmail: "ngozi.umeh@gmail.com",
|
|
171
|
+
// Bought, never arrived. The row the merchant is looking for when they ask
|
|
172
|
+
// how many seats they lost.
|
|
173
|
+
status: "no_show",
|
|
174
|
+
ticketCode: "SLR-6C4X",
|
|
175
|
+
date: dateOnly(-4),
|
|
176
|
+
startTime: "18:30"
|
|
177
|
+
})
|
|
178
|
+
];
|
|
179
|
+
function filterDemoAttendees(filter) {
|
|
180
|
+
const term = filter.search?.trim().toLowerCase();
|
|
181
|
+
return DEMO_ATTENDEES.filter((a) => {
|
|
182
|
+
if (filter.contentId && a.contentId !== filter.contentId) return false;
|
|
183
|
+
if (term) {
|
|
184
|
+
const haystack = `${a.customerName ?? ""} ${a.customerEmail ?? ""} ${a.ticketCode ?? ""} ${a.bookingNumber}`.toLowerCase();
|
|
185
|
+
if (!haystack.includes(term)) return false;
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
DEMO_ATTENDEES,
|
|
192
|
+
DEMO_TICKETED_EVENTS,
|
|
193
|
+
filterDemoAttendees
|
|
194
|
+
};
|
|
195
|
+
//# sourceMappingURL=demo-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/bookings/attendees/demo-data.ts"],"sourcesContent":["import type { BookingItem, ContentItem } from '@withpotter/lib/api/types';\n\n/**\n * Seed events and attendees for `?demo=1`.\n *\n * One module for two screens. Attendees and Check-in ask the API the same two\n * questions — the ticketed events for their selector, and the bookings against\n * the chosen one — so seeding them separately would let a demo show an event on\n * one screen that does not exist on the other, and check-in totals that\n * disagree with the list they came from.\n *\n * Shapes mirror `ContentItem` and `BookingItem`, so the scanner, the export and\n * the row actions all read the fields they read in production.\n */\n\nconst day = (offsetDays: number, time = '19:00'): string => {\n // Fixed anchor rather than `Date.now()`. A door list that reshuffles itself\n // between renders is impossible to talk about, and two screenshots of the\n // same demo have to agree.\n const base = new Date(`2026-08-11T${time}:00.000Z`);\n base.setDate(base.getDate() + offsetDays);\n return base.toISOString();\n};\n\nconst dateOnly = (offsetDays: number): string => day(offsetDays).slice(0, 10);\n\nfunction mkEvent(e: Partial<ContentItem> & { id: string }): ContentItem {\n return {\n tenantId: 'demo',\n type: 'product',\n isActive: true,\n currency: 'NGN',\n createdAt: day(-20),\n updatedAt: day(-2),\n ...e,\n } as ContentItem;\n}\n\n/**\n * Two events, deliberately at different points in their life: one selling for\n * next weekend, one that already happened. A selector holding a single future\n * event never shows what the screen does the morning after a show, which is\n * when a merchant actually reads it.\n */\nexport const DEMO_TICKETED_EVENTS: ContentItem[] = [\n mkEvent({\n id: 'demo-ev1',\n title: 'Saturday Night Session',\n slug: 'saturday-night-session',\n price: 22500,\n behaviors: { ticketed: true, sellable: true },\n data: { eventDate: dateOnly(3), venue: 'The Yard, Lekki', capacity: 180 },\n }),\n mkEvent({\n id: 'demo-ev2',\n title: 'Sunday Listening Room',\n slug: 'sunday-listening-room',\n price: 15000,\n behaviors: { ticketed: true, sellable: true },\n data: { eventDate: dateOnly(-4), venue: 'The Yard, Lekki', capacity: 90 },\n }),\n];\n\nfunction mkAttendee(b: Partial<BookingItem> & { id: string }): BookingItem {\n return {\n tenantId: 'demo',\n type: 'event',\n quantity: 1,\n createdAt: day(-6),\n updatedAt: day(-6),\n ...b,\n } as BookingItem;\n}\n\n/**\n * The door list.\n *\n * Mixed on purpose: people already scanned in, people still to arrive, a\n * cancellation and a no-show. A list where everyone is checked in makes the\n * check-in counter read 100% and the scanner look like it has nothing to do,\n * which is the opposite of the state this screen exists for.\n */\nexport const DEMO_ATTENDEES: BookingItem[] = [\n mkAttendee({\n id: 'demo-a1',\n bookingNumber: 'BK-3081',\n contentId: 'demo-ev1',\n content: {\n id: 'demo-ev1',\n title: 'Saturday Night Session',\n slug: 'saturday-night-session',\n price: 22500,\n },\n customerName: 'Ifeanyi Obi',\n customerEmail: 'ifeanyi.obi@gmail.com',\n customerPhone: '+2348031120945',\n status: 'checked_in',\n checkedInAt: day(0, '20:14'),\n ticketCode: 'SNS-4H2K',\n quantity: 2,\n date: dateOnly(3),\n startTime: '20:00',\n }),\n mkAttendee({\n id: 'demo-a2',\n bookingNumber: 'BK-3080',\n contentId: 'demo-ev1',\n content: {\n id: 'demo-ev1',\n title: 'Saturday Night Session',\n slug: 'saturday-night-session',\n price: 22500,\n },\n customerName: 'Chidinma Eze',\n customerEmail: 'chidinma.eze@gmail.com',\n customerPhone: '+2348024410982',\n status: 'confirmed',\n ticketCode: 'SNS-9QW1',\n quantity: 2,\n date: dateOnly(3),\n startTime: '20:00',\n }),\n mkAttendee({\n id: 'demo-a3',\n bookingNumber: 'BK-3079',\n contentId: 'demo-ev1',\n content: {\n id: 'demo-ev1',\n title: 'Saturday Night Session',\n slug: 'saturday-night-session',\n price: 22500,\n },\n customerName: 'Tunde Bakare',\n customerEmail: 'tunde.bakare@outlook.com',\n status: 'checked_in',\n checkedInAt: day(0, '20:31'),\n ticketCode: 'SNS-7B3D',\n date: dateOnly(3),\n startTime: '20:00',\n }),\n mkAttendee({\n id: 'demo-a4',\n bookingNumber: 'BK-3078',\n contentId: 'demo-ev1',\n content: {\n id: 'demo-ev1',\n title: 'Saturday Night Session',\n slug: 'saturday-night-session',\n price: 22500,\n },\n customerName: 'Amaka Nwosu',\n customerEmail: 'amaka@studioline.ng',\n customerPhone: '+2347066120114',\n status: 'confirmed',\n ticketCode: 'SNS-2M8P',\n date: dateOnly(3),\n startTime: '20:00',\n }),\n mkAttendee({\n id: 'demo-a5',\n bookingNumber: 'BK-3077',\n contentId: 'demo-ev1',\n content: {\n id: 'demo-ev1',\n title: 'Saturday Night Session',\n slug: 'saturday-night-session',\n price: 22500,\n },\n customerName: 'Segun Adeyemi',\n customerEmail: 'segun.a@gmail.com',\n status: 'cancelled',\n ticketCode: 'SNS-5T0R',\n date: dateOnly(3),\n startTime: '20:00',\n }),\n mkAttendee({\n id: 'demo-a6',\n bookingNumber: 'BK-3062',\n contentId: 'demo-ev2',\n content: {\n id: 'demo-ev2',\n title: 'Sunday Listening Room',\n slug: 'sunday-listening-room',\n price: 15000,\n },\n customerName: 'Zainab Musa',\n customerEmail: 'zainab.musa@gmail.com',\n customerPhone: '+2348139902411',\n status: 'checked_in',\n checkedInAt: day(-4, '18:52'),\n ticketCode: 'SLR-1K9V',\n quantity: 2,\n date: dateOnly(-4),\n startTime: '18:30',\n }),\n mkAttendee({\n id: 'demo-a7',\n bookingNumber: 'BK-3061',\n contentId: 'demo-ev2',\n content: {\n id: 'demo-ev2',\n title: 'Sunday Listening Room',\n slug: 'sunday-listening-room',\n price: 15000,\n },\n customerName: 'Ngozi Umeh',\n customerEmail: 'ngozi.umeh@gmail.com',\n // Bought, never arrived. The row the merchant is looking for when they ask\n // how many seats they lost.\n status: 'no_show',\n ticketCode: 'SLR-6C4X',\n date: dateOnly(-4),\n startTime: '18:30',\n }),\n];\n\n/**\n * The same narrowing the API does, so the event selector and the search box\n * still work on seeded data. A filter that changes nothing teaches that the\n * control is decorative.\n */\nexport function filterDemoAttendees(filter: {\n contentId?: string;\n search?: string;\n}): BookingItem[] {\n const term = filter.search?.trim().toLowerCase();\n return DEMO_ATTENDEES.filter((a) => {\n if (filter.contentId && a.contentId !== filter.contentId) return false;\n if (term) {\n const haystack =\n `${a.customerName ?? ''} ${a.customerEmail ?? ''} ${a.ticketCode ?? ''} ${a.bookingNumber}`.toLowerCase();\n if (!haystack.includes(term)) return false;\n }\n return true;\n });\n}\n"],"mappings":"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","names":[]}
|