@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,341 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FLAT_CONTROL, TOOLBAR_H } from "@withpotter/ui-kit/toolbar-parts";
|
|
4
|
+
import { LIST_PAGE_SIZE } from "@withpotter/lib/list-page-size";
|
|
5
|
+
import { useState, useCallback, useMemo } from "react";
|
|
6
|
+
import { useSearchParams } from "next/navigation";
|
|
7
|
+
import Link from "next/link";
|
|
8
|
+
import { CheckCircle2, Clock, QrCode, Camera, Ticket } from "lucide-react";
|
|
9
|
+
import { useApiQuery } from "@withpotter/hooks/use-api-query";
|
|
10
|
+
import { useDemoMode } from "@withpotter/hooks/use-demo-mode";
|
|
11
|
+
import { useTicketScan } from "@withpotter/hooks/use-ticket-scan";
|
|
12
|
+
import { DEMO_TICKETED_EVENTS, filterDemoAttendees } from "./demo-data";
|
|
13
|
+
import { bookingsApi } from "@withpotter/lib/api/bookings";
|
|
14
|
+
import { contentApi } from "@withpotter/lib/api/content";
|
|
15
|
+
import { useScreenHeader } from "@withpotter/hooks/use-screen-header";
|
|
16
|
+
import { useDestinationLabel } from "@withpotter/lib/intents/use-destination-label";
|
|
17
|
+
import { MobilePageHeader } from "@withpotter/components/mobile/mobile-page-header";
|
|
18
|
+
import { useRegisterRefresh } from "@withpotter/components/mobile/pull-to-refresh-context";
|
|
19
|
+
import { useAuth } from "@withpotter/lib/auth-context";
|
|
20
|
+
import { useToast } from "@withpotter/ui-kit/use-toast";
|
|
21
|
+
import {
|
|
22
|
+
useBookingStatusConfig
|
|
23
|
+
} from "../_components/booking-constants";
|
|
24
|
+
import { TablePagination } from "@withpotter/ui-kit/table-pagination";
|
|
25
|
+
import { TableShell, Th, TableRow } from "@withpotter/ui-kit/table-shell";
|
|
26
|
+
import { ListFrame } from "@withpotter/ui-kit/list-frame";
|
|
27
|
+
import { ListStates } from "@withpotter/ui-kit/list-states";
|
|
28
|
+
import { StateView } from "@withpotter/ui-kit/state-view";
|
|
29
|
+
import { FilterBar } from "@withpotter/ui-kit/filter-bar";
|
|
30
|
+
import { FilterField } from "@withpotter/ui-kit/filter-menu";
|
|
31
|
+
import { StatusBadge } from "@withpotter/components/shared/status-badge";
|
|
32
|
+
import { IdentityCell } from "@withpotter/ui-kit/table-cells";
|
|
33
|
+
import { ListPage } from "@withpotter/ui-kit/list-page";
|
|
34
|
+
import { QrScannerModal } from "./_components/qr-scanner-modal";
|
|
35
|
+
import { AttendeeCards } from "./_components/attendee-cards";
|
|
36
|
+
import { OverflowMenu, OverflowItem } from "@withpotter/ui-kit/overflow-menu";
|
|
37
|
+
import { Button } from "@withpotter/ui-kit/button";
|
|
38
|
+
import { Input, Select } from "@withpotter/ui-kit";
|
|
39
|
+
function AttendeesPage() {
|
|
40
|
+
const statuses = useBookingStatusConfig();
|
|
41
|
+
const searchParams = useSearchParams();
|
|
42
|
+
const { tenant } = useAuth();
|
|
43
|
+
const tenantSlug = tenant?.slug;
|
|
44
|
+
const initialContentId = searchParams.get("contentId") || "";
|
|
45
|
+
const [selectedEventId, setSelectedEventId] = useState(initialContentId);
|
|
46
|
+
const [search, setSearch] = useState("");
|
|
47
|
+
const [page, setPage] = useState(1);
|
|
48
|
+
const [showScanner, setShowScanner] = useState(false);
|
|
49
|
+
const { toast } = useToast();
|
|
50
|
+
const screenTitle = useDestinationLabel("attendees") ?? "Attendees";
|
|
51
|
+
useScreenHeader({ title: screenTitle });
|
|
52
|
+
const limit = LIST_PAGE_SIZE;
|
|
53
|
+
const { data: eventsData } = useApiQuery(
|
|
54
|
+
["ticketed-events"],
|
|
55
|
+
(slug) => contentApi.list(slug, "products", {
|
|
56
|
+
behaviors: ["ticketed"],
|
|
57
|
+
limit: 50
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
const demoActive = useDemoMode();
|
|
61
|
+
const events = useMemo(
|
|
62
|
+
() => demoActive ? DEMO_TICKETED_EVENTS : eventsData?.data ?? [],
|
|
63
|
+
[demoActive, eventsData?.data]
|
|
64
|
+
);
|
|
65
|
+
const { data, isLoading, error, refetch } = useApiQuery(
|
|
66
|
+
["attendees", selectedEventId, search, page],
|
|
67
|
+
(slug) => bookingsApi.list(slug, {
|
|
68
|
+
contentId: selectedEventId || void 0,
|
|
69
|
+
type: "event",
|
|
70
|
+
search: search || void 0,
|
|
71
|
+
page,
|
|
72
|
+
limit
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
useRegisterRefresh(refetch);
|
|
76
|
+
const {
|
|
77
|
+
scanInput,
|
|
78
|
+
setScanInput,
|
|
79
|
+
scanResult,
|
|
80
|
+
scan,
|
|
81
|
+
submitTyped,
|
|
82
|
+
pending: scanPending
|
|
83
|
+
} = useTicketScan({ onScanned: () => refetch() });
|
|
84
|
+
const demoAttendees = useMemo(
|
|
85
|
+
() => demoActive ? filterDemoAttendees({ contentId: selectedEventId, search }) : [],
|
|
86
|
+
[demoActive, selectedEventId, search]
|
|
87
|
+
);
|
|
88
|
+
const attendees = demoActive ? demoAttendees : data?.data || [];
|
|
89
|
+
const total = demoActive ? demoAttendees.length : data?.total || 0;
|
|
90
|
+
const totalPages = Math.ceil(total / limit);
|
|
91
|
+
const checkedInCount = attendees.filter(
|
|
92
|
+
(a) => a.status === "checked_in" || a.checkedInAt
|
|
93
|
+
).length;
|
|
94
|
+
const handleExportCsv = useCallback(async () => {
|
|
95
|
+
if (!tenantSlug) return;
|
|
96
|
+
try {
|
|
97
|
+
const csv = await bookingsApi.exportCsv(tenantSlug, {
|
|
98
|
+
contentId: selectedEventId || void 0,
|
|
99
|
+
type: "event"
|
|
100
|
+
});
|
|
101
|
+
const blob = new Blob([csv], { type: "text/csv" });
|
|
102
|
+
const url = URL.createObjectURL(blob);
|
|
103
|
+
const a = document.createElement("a");
|
|
104
|
+
a.href = url;
|
|
105
|
+
a.download = "attendees.csv";
|
|
106
|
+
a.click();
|
|
107
|
+
URL.revokeObjectURL(url);
|
|
108
|
+
} catch {
|
|
109
|
+
toast({
|
|
110
|
+
title: "Export failed",
|
|
111
|
+
description: "Could not generate the CSV. Please try again.",
|
|
112
|
+
variant: "destructive"
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}, [tenantSlug, selectedEventId, toast]);
|
|
116
|
+
const handleScan = submitTyped;
|
|
117
|
+
const handleQrScan = scan;
|
|
118
|
+
return (
|
|
119
|
+
/* The shared list frame. This drew its own column, its own vertical
|
|
120
|
+
rhythm and its own <h1> reading a literal "Attendees" — while two lines
|
|
121
|
+
up the same file resolved that noun properly for the mobile chrome. No
|
|
122
|
+
title is passed: ListPage takes it from the row that opened the screen,
|
|
123
|
+
so a merchant who renames Attendees sees it renamed here too. */
|
|
124
|
+
/* @__PURE__ */ jsxs(
|
|
125
|
+
ListPage,
|
|
126
|
+
{
|
|
127
|
+
largeTitle: true,
|
|
128
|
+
mobileHeader: /* @__PURE__ */ jsx(
|
|
129
|
+
MobilePageHeader,
|
|
130
|
+
{
|
|
131
|
+
title: screenTitle,
|
|
132
|
+
action: {
|
|
133
|
+
label: "Scan QR",
|
|
134
|
+
onClick: () => setShowScanner(true)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx(OverflowMenu, { toolbar: true, children: /* @__PURE__ */ jsx(OverflowItem, { onClick: handleExportCsv, children: "Export CSV" }) }),
|
|
140
|
+
/* @__PURE__ */ jsxs(
|
|
141
|
+
Button,
|
|
142
|
+
{
|
|
143
|
+
className: `${TOOLBAR_H} ${FLAT_CONTROL} sm:px-4`,
|
|
144
|
+
onClick: () => setShowScanner(true),
|
|
145
|
+
children: [
|
|
146
|
+
/* @__PURE__ */ jsx(Camera, { className: "size-4" }),
|
|
147
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Scan QR" })
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
] }),
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
154
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
155
|
+
/* @__PURE__ */ jsx(QrCode, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-[var(--text-muted)]" }),
|
|
156
|
+
/* @__PURE__ */ jsx(
|
|
157
|
+
Input,
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
value: scanInput,
|
|
161
|
+
onChange: (e) => setScanInput(e.target.value),
|
|
162
|
+
onKeyDown: (e) => e.key === "Enter" && handleScan(),
|
|
163
|
+
placeholder: "Enter a ticket code to check someone in",
|
|
164
|
+
className: "w-full pl-10 pr-4"
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
] }),
|
|
168
|
+
/* @__PURE__ */ jsx(
|
|
169
|
+
Button,
|
|
170
|
+
{
|
|
171
|
+
onClick: handleScan,
|
|
172
|
+
disabled: !scanInput.trim() || scanPending,
|
|
173
|
+
children: "Check in"
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
] }),
|
|
177
|
+
scanResult && /* @__PURE__ */ jsxs(
|
|
178
|
+
"div",
|
|
179
|
+
{
|
|
180
|
+
className: `px-3 py-2 rounded-md text-sm flex items-center gap-2 ${scanResult.success ? "bg-[var(--success-bg)] text-[var(--success)]" : "bg-[var(--error-bg)] text-[var(--error)]"}`,
|
|
181
|
+
children: [
|
|
182
|
+
scanResult.success ? /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4 flex-shrink-0" }) : /* @__PURE__ */ jsx(Clock, { className: "size-4 flex-shrink-0" }),
|
|
183
|
+
scanResult.message
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
),
|
|
187
|
+
/* @__PURE__ */ jsx(
|
|
188
|
+
ListFrame,
|
|
189
|
+
{
|
|
190
|
+
toolbar: /* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(
|
|
191
|
+
FilterBar,
|
|
192
|
+
{
|
|
193
|
+
toolbar: true,
|
|
194
|
+
search,
|
|
195
|
+
onSearchChange: (v) => {
|
|
196
|
+
setSearch(v);
|
|
197
|
+
setPage(1);
|
|
198
|
+
},
|
|
199
|
+
placeholder: "Search name, email or booking number",
|
|
200
|
+
activeCount: selectedEventId ? 1 : 0,
|
|
201
|
+
trailing: total > 0 ? /* @__PURE__ */ jsxs("span", { className: "text-xs tabular-nums text-[var(--text-tertiary)]", children: [
|
|
202
|
+
checkedInCount,
|
|
203
|
+
" of ",
|
|
204
|
+
total,
|
|
205
|
+
" checked in"
|
|
206
|
+
] }) : void 0,
|
|
207
|
+
children: /* @__PURE__ */ jsx(FilterField, { label: "Event", children: /* @__PURE__ */ jsx(
|
|
208
|
+
Select,
|
|
209
|
+
{
|
|
210
|
+
value: selectedEventId,
|
|
211
|
+
onChange: (value) => {
|
|
212
|
+
setSelectedEventId(value);
|
|
213
|
+
setPage(1);
|
|
214
|
+
},
|
|
215
|
+
ariaLabel: "Filter by event",
|
|
216
|
+
options: [
|
|
217
|
+
{ value: "", label: "All events" },
|
|
218
|
+
...events.map((event) => ({
|
|
219
|
+
value: event.id,
|
|
220
|
+
label: event.title
|
|
221
|
+
}))
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
) })
|
|
225
|
+
}
|
|
226
|
+
) }),
|
|
227
|
+
footer: totalPages > 1 ? /* @__PURE__ */ jsx(
|
|
228
|
+
TablePagination,
|
|
229
|
+
{
|
|
230
|
+
page,
|
|
231
|
+
totalPages,
|
|
232
|
+
pageSize: limit,
|
|
233
|
+
totalItems: total,
|
|
234
|
+
entityName: "attendees",
|
|
235
|
+
onPageChange: setPage
|
|
236
|
+
}
|
|
237
|
+
) : void 0,
|
|
238
|
+
children: /* @__PURE__ */ jsxs(
|
|
239
|
+
ListStates,
|
|
240
|
+
{
|
|
241
|
+
framed: true,
|
|
242
|
+
loading: !demoActive && isLoading,
|
|
243
|
+
isEmpty: attendees.length === 0,
|
|
244
|
+
error: !demoActive && error ? "Could not load attendees. The server may be temporarily unavailable." : null,
|
|
245
|
+
onRetry: () => refetch(),
|
|
246
|
+
empty: /* @__PURE__ */ jsx(
|
|
247
|
+
StateView,
|
|
248
|
+
{
|
|
249
|
+
kind: "empty",
|
|
250
|
+
icon: Ticket,
|
|
251
|
+
title: selectedEventId ? "No attendees for this event" : "No attendees yet",
|
|
252
|
+
description: "They appear here as customers buy tickets."
|
|
253
|
+
}
|
|
254
|
+
),
|
|
255
|
+
children: [
|
|
256
|
+
/* @__PURE__ */ jsx("div", { className: "hidden lg:block", children: /* @__PURE__ */ jsx(
|
|
257
|
+
TableShell,
|
|
258
|
+
{
|
|
259
|
+
bare: true,
|
|
260
|
+
minWidth: 760,
|
|
261
|
+
head: /* @__PURE__ */ jsxs("tr", { children: [
|
|
262
|
+
/* @__PURE__ */ jsx(Th, { children: "Name" }),
|
|
263
|
+
!selectedEventId && /* @__PURE__ */ jsx(Th, { children: "Event" }),
|
|
264
|
+
/* @__PURE__ */ jsx(Th, { children: "Tier" }),
|
|
265
|
+
/* @__PURE__ */ jsx(Th, { align: "right", children: "Qty" }),
|
|
266
|
+
/* @__PURE__ */ jsx(Th, { children: "Status" }),
|
|
267
|
+
/* @__PURE__ */ jsx(Th, { children: "Check-in" }),
|
|
268
|
+
/* @__PURE__ */ jsx(Th, { children: "Booking #" })
|
|
269
|
+
] }),
|
|
270
|
+
children: attendees.map((attendee) => {
|
|
271
|
+
const statusConfig = statuses(attendee.type)[attendee.status] || statuses(attendee.type).pending;
|
|
272
|
+
const tierName = attendee.data?.tierName || "";
|
|
273
|
+
const isCheckedIn = attendee.status === "checked_in" || !!attendee.checkedInAt;
|
|
274
|
+
const eventName = attendee.content?.title || "";
|
|
275
|
+
return /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
276
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(
|
|
277
|
+
IdentityCell,
|
|
278
|
+
{
|
|
279
|
+
name: attendee.customerName || "Guest",
|
|
280
|
+
secondary: attendee.customerEmail,
|
|
281
|
+
avatarSeed: attendee.customerName || attendee.customerEmail || "Guest"
|
|
282
|
+
}
|
|
283
|
+
) }),
|
|
284
|
+
!selectedEventId && /* @__PURE__ */ jsx("td", { className: "text-[var(--text-secondary)]", children: eventName || "\u2014" }),
|
|
285
|
+
/* @__PURE__ */ jsx("td", { className: "text-[var(--text-secondary)]", children: tierName || "\u2014" }),
|
|
286
|
+
/* @__PURE__ */ jsx("td", { className: "text-right tabular-nums text-[var(--text-secondary)]", children: attendee.quantity || 1 }),
|
|
287
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(
|
|
288
|
+
StatusBadge,
|
|
289
|
+
{
|
|
290
|
+
status: attendee.status,
|
|
291
|
+
label: statusConfig.label
|
|
292
|
+
}
|
|
293
|
+
) }),
|
|
294
|
+
/* @__PURE__ */ jsx("td", { children: isCheckedIn ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-[var(--success)]", children: [
|
|
295
|
+
/* @__PURE__ */ jsx(CheckCircle2, {}),
|
|
296
|
+
attendee.checkedInAt ? new Date(attendee.checkedInAt).toLocaleTimeString(
|
|
297
|
+
"en-US",
|
|
298
|
+
{ hour: "numeric", minute: "2-digit" }
|
|
299
|
+
) : "Yes"
|
|
300
|
+
] }) : /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-[var(--text-muted)]", children: [
|
|
301
|
+
/* @__PURE__ */ jsx(Clock, {}),
|
|
302
|
+
"Not yet"
|
|
303
|
+
] }) }),
|
|
304
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs(
|
|
305
|
+
Link,
|
|
306
|
+
{
|
|
307
|
+
href: attendee.orderId ? `/dashboard/orders/${attendee.orderId}` : `/dashboard/bookings/${attendee.id}`,
|
|
308
|
+
className: "font-medium text-brand-500 hover:underline",
|
|
309
|
+
children: [
|
|
310
|
+
"#",
|
|
311
|
+
attendee.bookingNumber
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
) })
|
|
315
|
+
] }, attendee.id);
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
) }),
|
|
319
|
+
/* @__PURE__ */ jsx(AttendeeCards, { attendees, statuses })
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
),
|
|
325
|
+
/* @__PURE__ */ jsx(
|
|
326
|
+
QrScannerModal,
|
|
327
|
+
{
|
|
328
|
+
open: showScanner,
|
|
329
|
+
onClose: () => setShowScanner(false),
|
|
330
|
+
onScan: handleQrScan
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
]
|
|
334
|
+
}
|
|
335
|
+
)
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
export {
|
|
339
|
+
AttendeesPage as default
|
|
340
|
+
};
|
|
341
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/bookings/attendees/page.tsx"],"sourcesContent":["'use client';\n\nimport { FLAT_CONTROL, TOOLBAR_H } from '@withpotter/ui-kit/toolbar-parts';\nimport { LIST_PAGE_SIZE } from '@withpotter/lib/list-page-size';\nimport { useState, useCallback, useMemo } from 'react';\nimport { useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { CheckCircle2, Clock, QrCode, Camera, Ticket } from 'lucide-react';\nimport { useApiQuery } from '@withpotter/hooks/use-api-query';\nimport { useDemoMode } from '@withpotter/hooks/use-demo-mode';\nimport { useTicketScan } from '@withpotter/hooks/use-ticket-scan';\nimport { DEMO_TICKETED_EVENTS, filterDemoAttendees } from './demo-data';\nimport { bookingsApi } from '@withpotter/lib/api/bookings';\nimport { contentApi } from '@withpotter/lib/api/content';\nimport type { BookingListResponse, ContentListResponse } from '@withpotter/lib/api/types';\nimport { useScreenHeader } from '@withpotter/hooks/use-screen-header';\nimport { useDestinationLabel } from '@withpotter/lib/intents/use-destination-label';\nimport { MobilePageHeader } from '@withpotter/components/mobile/mobile-page-header';\nimport { useRegisterRefresh } from '@withpotter/components/mobile/pull-to-refresh-context';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { useToast } from '@withpotter/ui-kit/use-toast';\nimport {\n useBookingStatusConfig,\n type BookingStatus,\n} from '../_components/booking-constants';\nimport { TablePagination } from '@withpotter/ui-kit/table-pagination';\nimport { TableShell, Th, TableRow } from '@withpotter/ui-kit/table-shell';\nimport { ListFrame } from '@withpotter/ui-kit/list-frame';\nimport { ListStates } from '@withpotter/ui-kit/list-states';\nimport { StateView } from '@withpotter/ui-kit/state-view';\nimport { FilterBar } from '@withpotter/ui-kit/filter-bar';\nimport { FilterField } from '@withpotter/ui-kit/filter-menu';\nimport { StatusBadge } from '@withpotter/components/shared/status-badge';\nimport { IdentityCell } from '@withpotter/ui-kit/table-cells';\nimport { ListPage } from '@withpotter/ui-kit/list-page';\nimport { QrScannerModal } from './_components/qr-scanner-modal';\nimport { AttendeeCards } from './_components/attendee-cards';\nimport { OverflowMenu, OverflowItem } from '@withpotter/ui-kit/overflow-menu';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { Input, Select } from '@withpotter/ui-kit';\n\nexport default function AttendeesPage() {\n const statuses = useBookingStatusConfig();\n const searchParams = useSearchParams();\n const { tenant } = useAuth();\n const tenantSlug = tenant?.slug;\n const initialContentId = searchParams.get('contentId') || '';\n\n const [selectedEventId, setSelectedEventId] = useState(initialContentId);\n const [search, setSearch] = useState('');\n const [page, setPage] = useState(1);\n const [showScanner, setShowScanner] = useState(false);\n const { toast } = useToast();\n\n // One name for the screen, from the row that opened it.\n const screenTitle = useDestinationLabel('attendees') ?? 'Attendees';\n useScreenHeader({ title: screenTitle });\n\n const limit = LIST_PAGE_SIZE;\n\n // Fetch ticketed events for the filter dropdown\n const { data: eventsData } = useApiQuery<ContentListResponse>(\n ['ticketed-events'],\n (slug) =>\n contentApi.list(slug, 'products', {\n behaviors: ['ticketed'],\n limit: 50,\n }),\n );\n\n const demoActive = useDemoMode();\n // Memoised because `eventsData?.data || []` is a fresh array on every render,\n // which would give the lookup below a new dependency each time.\n const events = useMemo(\n () => (demoActive ? DEMO_TICKETED_EVENTS : (eventsData?.data ?? [])),\n [demoActive, eventsData?.data],\n );\n\n // Fetch attendees\n const { data, isLoading, error, refetch } = useApiQuery<BookingListResponse>(\n ['attendees', selectedEventId, search, page],\n (slug) =>\n bookingsApi.list(slug, {\n contentId: selectedEventId || undefined,\n type: 'event',\n search: search || undefined,\n page,\n limit,\n }),\n );\n\n useRegisterRefresh(refetch);\n\n const {\n scanInput,\n setScanInput,\n scanResult,\n scan,\n submitTyped,\n pending: scanPending,\n } = useTicketScan({ onScanned: () => refetch() });\n\n // Seeded rows honour the event selector and the search box, so both controls\n // still narrow the list rather than sitting there doing nothing.\n const demoAttendees = useMemo(\n () =>\n demoActive\n ? filterDemoAttendees({ contentId: selectedEventId, search })\n : [],\n [demoActive, selectedEventId, search],\n );\n\n const attendees = demoActive ? demoAttendees : data?.data || [];\n const total = demoActive ? demoAttendees.length : data?.total || 0;\n const totalPages = Math.ceil(total / limit);\n\n const checkedInCount = attendees.filter(\n (a) => a.status === 'checked_in' || a.checkedInAt,\n ).length;\n\n const handleExportCsv = useCallback(async () => {\n if (!tenantSlug) return;\n try {\n const csv = await bookingsApi.exportCsv(tenantSlug, {\n contentId: selectedEventId || undefined,\n type: 'event',\n });\n const blob = new Blob([csv as unknown as string], { type: 'text/csv' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'attendees.csv';\n a.click();\n URL.revokeObjectURL(url);\n } catch {\n toast({\n title: 'Export failed',\n description: 'Could not generate the CSV. Please try again.',\n variant: 'destructive',\n });\n }\n }, [tenantSlug, selectedEventId, toast]);\n\n const handleScan = submitTyped;\n const handleQrScan = scan;\n\n return (\n /* The shared list frame. This drew its own column, its own vertical\n rhythm and its own <h1> reading a literal \"Attendees\" — while two lines\n up the same file resolved that noun properly for the mobile chrome. No\n title is passed: ListPage takes it from the row that opened the screen,\n so a merchant who renames Attendees sees it renamed here too. */\n <ListPage\n largeTitle\n mobileHeader={\n <MobilePageHeader\n title={screenTitle}\n action={{\n label: 'Scan QR',\n onClick: () => setShowScanner(true),\n }}\n />\n }\n actions={\n <>\n {/* Export in the overflow, so the header keeps its width for Scan\n QR — the thing someone standing at a door actually needs. */}\n <OverflowMenu toolbar>\n <OverflowItem onClick={handleExportCsv}>Export CSV</OverflowItem>\n </OverflowMenu>\n <Button\n className={`${TOOLBAR_H} ${FLAT_CONTROL} sm:px-4`}\n onClick={() => setShowScanner(true)}\n >\n <Camera className=\"size-4\" />\n <span className=\"hidden sm:inline\">Scan QR</span>\n </Button>\n </>\n }\n >\n {/* The door tool, first and on its own.\n A code typed at the desk is an ACTION — it checks somebody in — not a\n way of narrowing the list, so it stays out of the filter row. It used\n to sit between the event Select and a search field, three full-width\n bars stacked down the page, which read as a form to fill in before\n the screen would work. */}\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <QrCode className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-[var(--text-muted)]\" />\n <Input\n type=\"text\"\n value={scanInput}\n onChange={(e) => setScanInput(e.target.value)}\n onKeyDown={(e) => e.key === 'Enter' && handleScan()}\n placeholder=\"Enter a ticket code to check someone in\"\n className=\"w-full pl-10 pr-4\"\n />\n </div>\n <Button\n onClick={handleScan}\n disabled={!scanInput.trim() || scanPending}\n >\n Check in\n </Button>\n </div>\n {scanResult && (\n <div\n className={`px-3 py-2 rounded-md text-sm flex items-center gap-2 ${\n scanResult.success\n ? 'bg-[var(--success-bg)] text-[var(--success)]'\n : 'bg-[var(--error-bg)] text-[var(--error)]'\n }`}\n >\n {scanResult.success ? (\n <CheckCircle2 className=\"size-4 flex-shrink-0\" />\n ) : (\n <Clock className=\"size-4 flex-shrink-0\" />\n )}\n {scanResult.message}\n </div>\n )}\n\n {/* One card: the controls that drive the list, the list, and its\n pagination — attached to each other rather than floating as three\n strips on the page wash. */}\n <ListFrame\n toolbar={\n <div className=\"w-full\">\n <FilterBar\n toolbar\n search={search}\n onSearchChange={(v) => {\n setSearch(v);\n setPage(1);\n }}\n placeholder=\"Search name, email or booking number\"\n activeCount={selectedEventId ? 1 : 0}\n /* The whole of the old four-tile band, in the words a person at\n a door would use. Total, Checked in and Pending were three\n ways of saying two numbers, and Check-In Rate was 0/0 painted\n as \"0%\" on every event that had not started. */\n trailing={\n total > 0 ? (\n <span className=\"text-xs tabular-nums text-[var(--text-tertiary)]\">\n {checkedInCount} of {total} checked in\n </span>\n ) : undefined\n }\n >\n <FilterField label=\"Event\">\n <Select\n value={selectedEventId}\n onChange={(value) => {\n setSelectedEventId(value);\n setPage(1);\n }}\n ariaLabel=\"Filter by event\"\n options={[\n { value: '', label: 'All events' },\n ...events.map((event) => ({\n value: event.id,\n label: event.title,\n })),\n ]}\n />\n </FilterField>\n </FilterBar>\n </div>\n }\n footer={\n totalPages > 1 ? (\n <TablePagination\n page={page}\n totalPages={totalPages}\n pageSize={limit}\n totalItems={total}\n entityName=\"attendees\"\n onPageChange={setPage}\n />\n ) : undefined\n }\n >\n {/* Demo neither loads nor fails. With the API unreachable both are\n true, and the skeleton or the error would cover a full door list. */}\n <ListStates\n framed\n loading={!demoActive && isLoading}\n isEmpty={attendees.length === 0}\n error={\n !demoActive && error\n ? 'Could not load attendees. The server may be temporarily unavailable.'\n : null\n }\n onRetry={() => refetch()}\n empty={\n <StateView\n kind=\"empty\"\n icon={Ticket}\n title={\n selectedEventId\n ? 'No attendees for this event'\n : 'No attendees yet'\n }\n description=\"They appear here as customers buy tickets.\"\n />\n }\n >\n {/* Desktop table */}\n <div className=\"hidden lg:block\">\n <TableShell\n bare\n minWidth={760}\n head={\n <tr>\n <Th>Name</Th>\n {!selectedEventId && <Th>Event</Th>}\n <Th>Tier</Th>\n <Th align=\"right\">Qty</Th>\n <Th>Status</Th>\n <Th>Check-in</Th>\n <Th>Booking #</Th>\n </tr>\n }\n >\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 const eventName = attendee.content?.title || '';\n\n return (\n <TableRow key={attendee.id}>\n {/* Name and Email were two columns of the same person,\n which is what the identity cell is. It also gets the\n attendee a face — every other list in the app shows one\n beside a customer's name. */}\n <td>\n <IdentityCell\n name={attendee.customerName || 'Guest'}\n secondary={attendee.customerEmail}\n avatarSeed={\n attendee.customerName ||\n attendee.customerEmail ||\n 'Guest'\n }\n />\n </td>\n {!selectedEventId && (\n <td className=\"text-[var(--text-secondary)]\">\n {eventName || '\\u2014'}\n </td>\n )}\n <td className=\"text-[var(--text-secondary)]\">\n {tierName || '\\u2014'}\n </td>\n <td className=\"text-right tabular-nums text-[var(--text-secondary)]\">\n {attendee.quantity || 1}\n </td>\n <td>\n {/* The app's pill, not a third private copy of one. The\n archetype config still chooses the WORD — an events\n merchant's \"Checked In\" is not a gym's \"Entered\" — and\n StatusBadge decides only how it is painted. */}\n <StatusBadge\n status={attendee.status}\n label={statusConfig.label}\n />\n </td>\n <td>\n {isCheckedIn ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-[var(--success)]\">\n <CheckCircle2 />\n {attendee.checkedInAt\n ? new Date(attendee.checkedInAt).toLocaleTimeString(\n 'en-US',\n { hour: 'numeric', minute: '2-digit' },\n )\n : 'Yes'}\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1 text-xs text-[var(--text-muted)]\">\n <Clock />\n Not yet\n </span>\n )}\n </td>\n <td>\n <Link\n href={\n attendee.orderId\n ? `/dashboard/orders/${attendee.orderId}`\n : `/dashboard/bookings/${attendee.id}`\n }\n className=\"font-medium text-brand-500 hover:underline\"\n >\n #{attendee.bookingNumber}\n </Link>\n </td>\n </TableRow>\n );\n })}\n </TableShell>\n </div>\n\n <AttendeeCards attendees={attendees} statuses={statuses} />\n </ListStates>\n </ListFrame>\n\n {/* QR Scanner Modal */}\n <QrScannerModal\n open={showScanner}\n onClose={() => setShowScanner(false)}\n onScan={handleQrScan}\n />\n </ListPage>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { CalendarClock } from "lucide-react";
|
|
6
|
+
import { DetailPage } from "@withpotter/ui-kit/detail-page";
|
|
7
|
+
import { Button } from "@withpotter/ui-kit/button";
|
|
8
|
+
import { BookingEditor } from "@withpotter/components/features/booking-editor";
|
|
9
|
+
import { useEntityLabel } from "@withpotter/lib/intents/use-entity-label";
|
|
10
|
+
import { useResourceLabel } from "@withpotter/lib/intents/use-resource-label";
|
|
11
|
+
import { resolveBookingQueryStyle } from "@withpotter/lib/intents/booking-query-style";
|
|
12
|
+
import { useIntentConfig } from "@withpotter/lib/intents/use-intent-config";
|
|
13
|
+
function NewBookingPage() {
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
const [saving, setSaving] = useState(false);
|
|
16
|
+
const [createdBookingId, setCreatedBookingId] = useState(null);
|
|
17
|
+
const searchParams = useSearchParams();
|
|
18
|
+
const bookingLabel = useEntityLabel("booking");
|
|
19
|
+
const resourceLabel = useResourceLabel();
|
|
20
|
+
const intentConfig = useIntentConfig();
|
|
21
|
+
const queryStyle = resolveBookingQueryStyle(intentConfig.active);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!createdBookingId) return;
|
|
24
|
+
const timeout = window.setTimeout(
|
|
25
|
+
() => router.push(`/dashboard/bookings/${createdBookingId}`),
|
|
26
|
+
800
|
|
27
|
+
);
|
|
28
|
+
return () => window.clearTimeout(timeout);
|
|
29
|
+
}, [createdBookingId, router]);
|
|
30
|
+
const prefill = {
|
|
31
|
+
date: searchParams.get("date") ?? void 0,
|
|
32
|
+
startTime: searchParams.get("start") ?? void 0,
|
|
33
|
+
endTime: searchParams.get("end") ?? void 0,
|
|
34
|
+
resourceId: searchParams.get("resource") ?? void 0,
|
|
35
|
+
contentId: searchParams.get("service") ?? void 0,
|
|
36
|
+
/**
|
|
37
|
+
* Who it is for, carried over from a booking being repeated.
|
|
38
|
+
*
|
|
39
|
+
* Rebooking is the single biggest retention lever a business that sells
|
|
40
|
+
* time has, and re-typing a name and a number at the desk while the
|
|
41
|
+
* customer stands there is how it stops happening.
|
|
42
|
+
*/
|
|
43
|
+
customerName: searchParams.get("name") ?? void 0,
|
|
44
|
+
customerEmail: searchParams.get("email") ?? void 0,
|
|
45
|
+
customerPhone: searchParams.get("phone") ?? void 0
|
|
46
|
+
};
|
|
47
|
+
return /* @__PURE__ */ jsx(
|
|
48
|
+
DetailPage,
|
|
49
|
+
{
|
|
50
|
+
breadcrumbs: [
|
|
51
|
+
{
|
|
52
|
+
label: bookingLabel.plural,
|
|
53
|
+
href: "/dashboard/bookings",
|
|
54
|
+
icon: /* @__PURE__ */ jsx(CalendarClock, {})
|
|
55
|
+
},
|
|
56
|
+
{ label: `New ${bookingLabel.singular.toLowerCase()}` }
|
|
57
|
+
],
|
|
58
|
+
maxWidth: "max-w-6xl",
|
|
59
|
+
mobileActions: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
Button,
|
|
62
|
+
{
|
|
63
|
+
variant: "secondary",
|
|
64
|
+
size: "md",
|
|
65
|
+
type: "button",
|
|
66
|
+
form: "booking-editor",
|
|
67
|
+
onClick: () => router.push("/dashboard/bookings"),
|
|
68
|
+
disabled: saving,
|
|
69
|
+
children: "Cancel"
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
Button,
|
|
74
|
+
{
|
|
75
|
+
variant: createdBookingId ? "success" : "primary",
|
|
76
|
+
size: "md",
|
|
77
|
+
type: "submit",
|
|
78
|
+
form: "booking-editor",
|
|
79
|
+
loading: saving,
|
|
80
|
+
disabled: Boolean(createdBookingId),
|
|
81
|
+
children: saving ? "Creating\u2026" : createdBookingId ? "Created" : "Create"
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
] }),
|
|
85
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
Button,
|
|
88
|
+
{
|
|
89
|
+
variant: "secondary",
|
|
90
|
+
type: "button",
|
|
91
|
+
onClick: () => router.push("/dashboard/bookings"),
|
|
92
|
+
disabled: saving,
|
|
93
|
+
children: "Cancel"
|
|
94
|
+
}
|
|
95
|
+
),
|
|
96
|
+
/* @__PURE__ */ jsx(
|
|
97
|
+
Button,
|
|
98
|
+
{
|
|
99
|
+
variant: createdBookingId ? "success" : "primary",
|
|
100
|
+
type: "submit",
|
|
101
|
+
form: "booking-editor",
|
|
102
|
+
loading: saving,
|
|
103
|
+
disabled: Boolean(createdBookingId),
|
|
104
|
+
children: saving ? "Creating\u2026" : createdBookingId ? "Created" : "Create booking"
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
] }),
|
|
108
|
+
children: /* @__PURE__ */ jsx(
|
|
109
|
+
BookingEditor,
|
|
110
|
+
{
|
|
111
|
+
entityLabel: resourceLabel.singular,
|
|
112
|
+
bookingLabel: bookingLabel.singular,
|
|
113
|
+
prefill,
|
|
114
|
+
initialMode: searchParams.get("walkin") === "1" ? "walk_in_staff_created" : queryStyle?.defaultBookingMode,
|
|
115
|
+
initialType: queryStyle?.defaultBookingType,
|
|
116
|
+
allowedTypes: queryStyle?.bookingTypes.length ? queryStyle.bookingTypes : void 0,
|
|
117
|
+
allowedModes: queryStyle?.bookingModes.length ? queryStyle.bookingModes : void 0,
|
|
118
|
+
onSaved: (booking) => setCreatedBookingId(booking.id),
|
|
119
|
+
onClose: () => router.push("/dashboard/bookings"),
|
|
120
|
+
onSavingChange: setSaving
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
NewBookingPage as default
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/bookings/new/page.tsx"],"sourcesContent":["'use client';\n\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport { useEffect, useState } from 'react';\nimport { CalendarClock } from 'lucide-react';\nimport { DetailPage } from '@withpotter/ui-kit/detail-page';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { BookingEditor } from '@withpotter/components/features/booking-editor';\nimport { useEntityLabel } from '@withpotter/lib/intents/use-entity-label';\nimport { useResourceLabel } from '@withpotter/lib/intents/use-resource-label';\nimport { resolveBookingQueryStyle } from '@withpotter/lib/intents/booking-query-style';\nimport { useIntentConfig } from '@withpotter/lib/intents/use-intent-config';\n\n/**\n * Creating a booking, as a screen rather than a panel.\n *\n * Two buttons used to open one slide-over in two moods — \"Add appointment\" and\n * \"Quick walk-in\" — which is two doors to one room, and an edge panel besides:\n * this app's rule is that a create screen is a detail screen, keeping the\n * sidebar and wearing breadcrumbs like CustomerEditor and ProductEditor do.\n *\n * Prefill arrives in the URL so the calendar can still hand over the gap that\n * was clicked without opening a different form to do it.\n */\nexport default function NewBookingPage() {\n const router = useRouter();\n const [saving, setSaving] = useState(false);\n const [createdBookingId, setCreatedBookingId] = useState<string | null>(null);\n const searchParams = useSearchParams();\n const bookingLabel = useEntityLabel('booking');\n const resourceLabel = useResourceLabel();\n const intentConfig = useIntentConfig();\n // Across every archetype that books here, so the create form offers what the\n // business does rather than what `primary` last happened to be.\n const queryStyle = resolveBookingQueryStyle(intentConfig.active);\n\n useEffect(() => {\n if (!createdBookingId) return;\n const timeout = window.setTimeout(\n () => router.push(`/dashboard/bookings/${createdBookingId}`),\n 800,\n );\n return () => window.clearTimeout(timeout);\n }, [createdBookingId, router]);\n\n const prefill = {\n date: searchParams.get('date') ?? undefined,\n startTime: searchParams.get('start') ?? undefined,\n endTime: searchParams.get('end') ?? undefined,\n resourceId: searchParams.get('resource') ?? undefined,\n contentId: searchParams.get('service') ?? undefined,\n /**\n * Who it is for, carried over from a booking being repeated.\n *\n * Rebooking is the single biggest retention lever a business that sells\n * time has, and re-typing a name and a number at the desk while the\n * customer stands there is how it stops happening.\n */\n customerName: searchParams.get('name') ?? undefined,\n customerEmail: searchParams.get('email') ?? undefined,\n customerPhone: searchParams.get('phone') ?? undefined,\n };\n\n return (\n <DetailPage\n /* Two crumbs, the first iconed, like every other create screen in the\n app — Segments, Products, Orders. */\n breadcrumbs={[\n {\n label: bookingLabel.plural,\n href: '/dashboard/bookings',\n icon: <CalendarClock />,\n },\n { label: `New ${bookingLabel.singular.toLowerCase()}` },\n ]}\n maxWidth=\"max-w-6xl\"\n mobileActions={\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"secondary\"\n size=\"md\"\n type=\"button\"\n form=\"booking-editor\"\n onClick={() => router.push('/dashboard/bookings')}\n disabled={saving}\n >\n Cancel\n </Button>\n <Button\n variant={createdBookingId ? 'success' : 'primary'}\n size=\"md\"\n type=\"submit\"\n form=\"booking-editor\"\n loading={saving}\n disabled={Boolean(createdBookingId)}\n >\n {saving ? 'Creating…' : createdBookingId ? 'Created' : 'Create'}\n </Button>\n </div>\n }\n actions={\n <>\n <Button\n variant=\"secondary\"\n type=\"button\"\n onClick={() => router.push('/dashboard/bookings')}\n disabled={saving}\n >\n Cancel\n </Button>\n <Button\n variant={createdBookingId ? 'success' : 'primary'}\n type=\"submit\"\n form=\"booking-editor\"\n loading={saving}\n disabled={Boolean(createdBookingId)}\n >\n {saving\n ? 'Creating…'\n : createdBookingId\n ? 'Created'\n : 'Create booking'}\n </Button>\n </>\n }\n >\n <BookingEditor\n entityLabel={resourceLabel.singular}\n bookingLabel={bookingLabel.singular}\n prefill={prefill}\n initialMode={\n searchParams.get('walkin') === '1'\n ? 'walk_in_staff_created'\n : queryStyle?.defaultBookingMode\n }\n initialType={queryStyle?.defaultBookingType}\n allowedTypes={\n queryStyle?.bookingTypes.length ? queryStyle.bookingTypes : undefined\n }\n allowedModes={\n queryStyle?.bookingModes.length ? queryStyle.bookingModes : undefined\n }\n onSaved={(booking) => setCreatedBookingId(booking.id)}\n onClose={() => router.push('/dashboard/bookings')}\n onSavingChange={setSaving}\n />\n </DetailPage>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { BookingsScreen } from "./_components/bookings-screen";
|
|
4
|
+
function BookingsPage() {
|
|
5
|
+
return /* @__PURE__ */ jsx(BookingsScreen, {});
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
BookingsPage as default
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/%28bookings%29/bookings/page.tsx"],"sourcesContent":["'use client';\n\nimport { BookingsScreen } from './_components/bookings-screen';\n\nexport default function BookingsPage() {\n return <BookingsScreen />;\n}\n"],"mappings":";AAKS;AAHT,SAAS,sBAAsB;AAEhB,SAAR,eAAgC;AACrC,SAAO,oBAAC,kBAAe;AACzB;","names":[]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "@withpotter/ui-kit/button";
|
|
3
|
+
function SaveButtons({
|
|
4
|
+
saving,
|
|
5
|
+
onSave,
|
|
6
|
+
size,
|
|
7
|
+
buttonClassName
|
|
8
|
+
}) {
|
|
9
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10
|
+
/* @__PURE__ */ jsx(
|
|
11
|
+
Button,
|
|
12
|
+
{
|
|
13
|
+
variant: "secondary",
|
|
14
|
+
size,
|
|
15
|
+
className: buttonClassName,
|
|
16
|
+
onClick: () => onSave(true),
|
|
17
|
+
disabled: saving,
|
|
18
|
+
children: saving ? "Saving\u2026" : "Save draft"
|
|
19
|
+
}
|
|
20
|
+
),
|
|
21
|
+
/* @__PURE__ */ jsx(
|
|
22
|
+
Button,
|
|
23
|
+
{
|
|
24
|
+
size,
|
|
25
|
+
className: buttonClassName,
|
|
26
|
+
onClick: () => onSave(false),
|
|
27
|
+
disabled: saving,
|
|
28
|
+
children: saving ? "Publishing\u2026" : "Publish"
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
] });
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
SaveButtons
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=save-buttons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/dashboard/%28bookings%29/bookings/sessions/new/_components/save-buttons.tsx"],"sourcesContent":["import { Button } from '@withpotter/ui-kit/button';\n\n/**\n * Save draft and Publish, once. The header, the phone header and the floating\n * bar each drew this pair by hand, so a change to either had three places to\n * miss.\n */\nexport function SaveButtons({\n saving,\n onSave,\n size,\n buttonClassName,\n}: {\n saving: boolean;\n onSave: (asDraft: boolean) => void;\n size?: 'sm';\n buttonClassName?: string;\n}) {\n return (\n <>\n <Button\n variant=\"secondary\"\n size={size}\n className={buttonClassName}\n onClick={() => onSave(true)}\n disabled={saving}\n >\n {saving ? 'Saving…' : 'Save draft'}\n </Button>\n <Button\n size={size}\n className={buttonClassName}\n onClick={() => onSave(false)}\n disabled={saving}\n >\n {saving ? 'Publishing…' : 'Publish'}\n </Button>\n </>\n );\n}\n"],"mappings":"AAmBI,mBACE,KADF;AAnBJ,SAAS,cAAc;AAOhB,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR;AAAA,QACA,WAAW;AAAA,QACX,SAAS,MAAM,OAAO,IAAI;AAAA,QAC1B,UAAU;AAAA,QAET,mBAAS,iBAAY;AAAA;AAAA,IACxB;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,QACX,SAAS,MAAM,OAAO,KAAK;AAAA,QAC3B,UAAU;AAAA,QAET,mBAAS,qBAAgB;AAAA;AAAA,IAC5B;AAAA,KACF;AAEJ;","names":[]}
|