@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,247 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useItemLinks } from "@withpotter/lib/use-item-links";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { useParams, useRouter } from "next/navigation";
|
|
6
|
+
import { CalendarRange, Crown, MapPin, Pencil, Ticket } from "lucide-react";
|
|
7
|
+
import { Button, Card, DetailPage, DetailStates } from "@withpotter/ui-kit";
|
|
8
|
+
import { useToast } from "@withpotter/ui-kit/use-toast";
|
|
9
|
+
import { StatusBadge } from "@withpotter/components/shared/status-badge";
|
|
10
|
+
import { useApiQuery } from "@withpotter/hooks/use-api-query";
|
|
11
|
+
import { useAuth } from "@withpotter/lib/auth-context";
|
|
12
|
+
import { useFormatCurrency } from "@withpotter/hooks/use-format-currency";
|
|
13
|
+
import { bookingsApi } from "@withpotter/lib/api/bookings";
|
|
14
|
+
import { inventoryApi } from "@withpotter/lib/api/inventory";
|
|
15
|
+
import { storefrontPageUrl } from "@withpotter/lib/storefront";
|
|
16
|
+
import {
|
|
17
|
+
eventFacts,
|
|
18
|
+
eventPublicationStatus,
|
|
19
|
+
eventTierFacts,
|
|
20
|
+
whenLabel
|
|
21
|
+
} from "../_lib/event-facts";
|
|
22
|
+
import { EventShare } from "./_components/event-share";
|
|
23
|
+
import { EventAttendees } from "./_components/event-attendees";
|
|
24
|
+
function EventPage() {
|
|
25
|
+
const params = useParams();
|
|
26
|
+
const router = useRouter();
|
|
27
|
+
const links = useItemLinks();
|
|
28
|
+
const { tenant } = useAuth();
|
|
29
|
+
const formatCurrency = useFormatCurrency();
|
|
30
|
+
const eventId = params?.eventId;
|
|
31
|
+
const { toast } = useToast();
|
|
32
|
+
const [creatingTestPurchase, setCreatingTestPurchase] = useState(false);
|
|
33
|
+
const [testPurchasesEnabled, setTestPurchasesEnabled] = useState(false);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const frame = window.requestAnimationFrame(() => {
|
|
36
|
+
setTestPurchasesEnabled(
|
|
37
|
+
window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1"
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
return () => window.cancelAnimationFrame(frame);
|
|
41
|
+
}, []);
|
|
42
|
+
const { data, isLoading, error, refetch } = useApiQuery(
|
|
43
|
+
["event", eventId],
|
|
44
|
+
(slug) => inventoryApi.getById(slug, eventId)
|
|
45
|
+
);
|
|
46
|
+
const event = data;
|
|
47
|
+
const facts = event ? eventFacts(event) : null;
|
|
48
|
+
const publicUrl = tenant?.slug && event?.slug ? storefrontPageUrl(tenant.slug, `/events/${event.slug}`) : "";
|
|
49
|
+
const tiers = event ? eventTierFacts(event) : [];
|
|
50
|
+
async function createTestPurchase() {
|
|
51
|
+
const tier = tiers.find((candidate) => candidate.soldCount < candidate.quantity) ?? tiers[0];
|
|
52
|
+
if (!tenant?.slug || !facts?.date || !tier?.id) {
|
|
53
|
+
toast({
|
|
54
|
+
title: "This event is not ready for a test sale",
|
|
55
|
+
description: "Add an event date and a ticket tier before testing a purchase.",
|
|
56
|
+
variant: "destructive"
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setCreatingTestPurchase(true);
|
|
61
|
+
try {
|
|
62
|
+
await bookingsApi.createTestPurchase(tenant.slug, {
|
|
63
|
+
type: "event",
|
|
64
|
+
contentId: eventId,
|
|
65
|
+
date: facts.date,
|
|
66
|
+
quantity: 1,
|
|
67
|
+
customerName: "Local Test Guest",
|
|
68
|
+
customerEmail: "local-test@example.test",
|
|
69
|
+
total: tier.price,
|
|
70
|
+
metadata: {
|
|
71
|
+
tierId: tier.id,
|
|
72
|
+
tierName: tier.name
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
await refetch();
|
|
76
|
+
toast({
|
|
77
|
+
title: "Test sale recorded",
|
|
78
|
+
description: `${tier.name} is now counted as a paid ticket.`
|
|
79
|
+
});
|
|
80
|
+
} catch (error2) {
|
|
81
|
+
toast({
|
|
82
|
+
title: "Test sale failed",
|
|
83
|
+
description: error2 instanceof Error ? error2.message : "Unable to create the test sale.",
|
|
84
|
+
variant: "destructive"
|
|
85
|
+
});
|
|
86
|
+
} finally {
|
|
87
|
+
setCreatingTestPurchase(false);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const hashtag = `#${(event?.slug || "").replace(/-/g, "")}`;
|
|
91
|
+
if (isLoading || error || !event || !facts) {
|
|
92
|
+
return /* @__PURE__ */ jsx(
|
|
93
|
+
DetailStates,
|
|
94
|
+
{
|
|
95
|
+
loading: isLoading && !event,
|
|
96
|
+
error: error?.message ?? null,
|
|
97
|
+
onRetry: () => void refetch(),
|
|
98
|
+
back: { href: links.list, label: "Events" },
|
|
99
|
+
notFoundLabel: "That event is not here. It may have been deleted."
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return /* @__PURE__ */ jsx(
|
|
104
|
+
DetailPage,
|
|
105
|
+
{
|
|
106
|
+
breadcrumbs: [
|
|
107
|
+
{
|
|
108
|
+
label: "Events",
|
|
109
|
+
href: links.list,
|
|
110
|
+
icon: /* @__PURE__ */ jsx(CalendarRange, {})
|
|
111
|
+
},
|
|
112
|
+
{ label: event.title }
|
|
113
|
+
],
|
|
114
|
+
actions: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
|
|
115
|
+
Button,
|
|
116
|
+
{
|
|
117
|
+
variant: "secondary",
|
|
118
|
+
icon: /* @__PURE__ */ jsx(Pencil, {}),
|
|
119
|
+
onClick: () => router.push(`/dashboard/catalog/${eventId}/edit`),
|
|
120
|
+
children: "Edit"
|
|
121
|
+
}
|
|
122
|
+
) }),
|
|
123
|
+
children: /* @__PURE__ */ jsxs("div", { className: "grid w-full gap-4 lg:grid-cols-[minmax(0,1fr)_300px]", children: [
|
|
124
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-4", children: [
|
|
125
|
+
/* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
126
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
127
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-[var(--text-primary)]", children: [
|
|
128
|
+
/* @__PURE__ */ jsx(CalendarRange, { className: "size-4 shrink-0" }),
|
|
129
|
+
whenLabel(facts)
|
|
130
|
+
] }),
|
|
131
|
+
facts.venue || facts.venueAddress ? (
|
|
132
|
+
/* THE ROOM, AND A WAY TO FIND IT. A venue name on its
|
|
133
|
+
own is a fact; a name that opens a map is a direction,
|
|
134
|
+
and the person reading this on the day is usually
|
|
135
|
+
about to travel to it or tell somebody else how. The
|
|
136
|
+
query is the name and the address together, because a
|
|
137
|
+
hall name alone lands on the wrong city often enough
|
|
138
|
+
to be useless. */
|
|
139
|
+
/* @__PURE__ */ jsxs(
|
|
140
|
+
"a",
|
|
141
|
+
{
|
|
142
|
+
className: "mt-1 flex min-w-0 items-start gap-2 rounded-md text-sm text-[var(--text-link)] underline decoration-[var(--text-link)] underline-offset-4 hover:text-[var(--text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)]",
|
|
143
|
+
href: `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
|
|
144
|
+
[facts.venue, facts.venueAddress].filter(Boolean).join(", ")
|
|
145
|
+
)}`,
|
|
146
|
+
target: "_blank",
|
|
147
|
+
rel: "noreferrer",
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ jsx(MapPin, { className: "size-4 shrink-0" }),
|
|
150
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: [facts.venue, facts.venueAddress].filter(Boolean).join(", ") })
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
) : null
|
|
155
|
+
] }),
|
|
156
|
+
/* @__PURE__ */ jsx(StatusBadge, { status: eventPublicationStatus(event) })
|
|
157
|
+
] }) }),
|
|
158
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
159
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-baseline justify-between gap-3", children: [
|
|
160
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Tickets" }),
|
|
161
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm tabular-nums text-[var(--text-tertiary)]", children: tiers.length === 0 ? "No tickets yet" : facts.capacity > 0 ? `${facts.sold} of ${facts.capacity} sold` : `${facts.sold} sold, no limit` })
|
|
162
|
+
] }),
|
|
163
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1", children: tiers.map((tier, i) => /* @__PURE__ */ jsxs(
|
|
164
|
+
"li",
|
|
165
|
+
{
|
|
166
|
+
className: "flex items-center justify-between gap-3 rounded-lg px-2 py-2 text-sm",
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
169
|
+
tier.name.toLowerCase().includes("vip") ? /* @__PURE__ */ jsx(
|
|
170
|
+
Crown,
|
|
171
|
+
{
|
|
172
|
+
"aria-hidden": "true",
|
|
173
|
+
className: "size-4 shrink-0 text-[var(--text-secondary)]"
|
|
174
|
+
}
|
|
175
|
+
) : /* @__PURE__ */ jsx(
|
|
176
|
+
Ticket,
|
|
177
|
+
{
|
|
178
|
+
"aria-hidden": "true",
|
|
179
|
+
className: "size-4 shrink-0 text-[var(--text-secondary)]"
|
|
180
|
+
}
|
|
181
|
+
),
|
|
182
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-[var(--text-primary)]", children: tier.name })
|
|
183
|
+
] }),
|
|
184
|
+
/* @__PURE__ */ jsxs("span", { className: "shrink-0 tabular-nums text-[var(--text-tertiary)]", children: [
|
|
185
|
+
formatCurrency(tier.price, event.currency),
|
|
186
|
+
" \xB7",
|
|
187
|
+
" ",
|
|
188
|
+
tier.soldCount,
|
|
189
|
+
"/",
|
|
190
|
+
tier.quantity
|
|
191
|
+
] })
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
`${tier.name}-${i}`
|
|
195
|
+
)) }),
|
|
196
|
+
testPurchasesEnabled ? /* @__PURE__ */ jsx(
|
|
197
|
+
Button,
|
|
198
|
+
{
|
|
199
|
+
variant: "secondary",
|
|
200
|
+
className: "mt-3 w-full",
|
|
201
|
+
icon: /* @__PURE__ */ jsx(Ticket, {}),
|
|
202
|
+
disabled: creatingTestPurchase,
|
|
203
|
+
onClick: () => void createTestPurchase(),
|
|
204
|
+
children: creatingTestPurchase ? "Recording test sale\u2026" : "Record test sale"
|
|
205
|
+
}
|
|
206
|
+
) : null
|
|
207
|
+
] }),
|
|
208
|
+
/* @__PURE__ */ jsx(
|
|
209
|
+
EventAttendees,
|
|
210
|
+
{
|
|
211
|
+
eventId,
|
|
212
|
+
doorListHref: `/dashboard/bookings/attendees?contentId=${eventId}`
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
] }),
|
|
216
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4 lg:sticky lg:top-4 lg:self-start", children: [
|
|
217
|
+
typeof event.image === "string" ? (
|
|
218
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
219
|
+
/* @__PURE__ */ jsx(
|
|
220
|
+
"img",
|
|
221
|
+
{
|
|
222
|
+
src: event.image,
|
|
223
|
+
alt: "",
|
|
224
|
+
className: "aspect-square w-full rounded-[var(--radius-card)] object-cover"
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
) : event.image?.url ? (
|
|
228
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
229
|
+
/* @__PURE__ */ jsx(
|
|
230
|
+
"img",
|
|
231
|
+
{
|
|
232
|
+
src: event.image.url,
|
|
233
|
+
alt: "",
|
|
234
|
+
className: "aspect-square w-full rounded-[var(--radius-card)] object-cover"
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
) : null,
|
|
238
|
+
publicUrl && /* @__PURE__ */ jsx(EventShare, { url: publicUrl, title: event.title, hashtag })
|
|
239
|
+
] })
|
|
240
|
+
] })
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
export {
|
|
245
|
+
EventPage as default
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/events/%5BeventId%5D/page.tsx"],"sourcesContent":["'use client';\n\nimport { useItemLinks } from '@withpotter/lib/use-item-links';\nimport { useEffect, useState } from 'react';\nimport { useParams, useRouter } from 'next/navigation';\nimport { CalendarRange, Crown, MapPin, Pencil, Ticket } from 'lucide-react';\nimport { Button, Card, DetailPage, DetailStates } from '@withpotter/ui-kit';\nimport { useToast } from '@withpotter/ui-kit/use-toast';\nimport { StatusBadge } from '@withpotter/components/shared/status-badge';\nimport { useApiQuery } from '@withpotter/hooks/use-api-query';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { useFormatCurrency } from '@withpotter/hooks/use-format-currency';\nimport { bookingsApi } from '@withpotter/lib/api/bookings';\nimport { inventoryApi } from '@withpotter/lib/api/inventory';\nimport { storefrontPageUrl } from '@withpotter/lib/storefront';\nimport type { InventoryProduct } from '@withpotter/lib/api/types';\nimport {\n eventFacts,\n eventPublicationStatus,\n eventTierFacts,\n whenLabel,\n} from '../_lib/event-facts';\nimport { EventShare } from './_components/event-share';\nimport { EventAttendees } from './_components/event-attendees';\n\n/**\n * ONE NIGHT, AND EVERYTHING ABOUT IT.\n *\n * John, 2026-09-03: *\"each event should also have attendees, map location,\n * date, copy link, event page, hashtag\"*, and *\"the individual event should\n * have an attendees section, not a button.\"*\n *\n * ── WHY THIS IS NOT THE PRODUCT EDITOR ──\n *\n * The editor is where a listing is CHANGED, and it is good at that: variants,\n * collections, gating, custom fields. None of it is what somebody opens an\n * event for. On the day, the questions are: is it selling, who is coming, and\n * how do I get this in front of more people — and every one of those is a read\n * the editor has no room for, because a form is laid out around the fields it\n * writes.\n *\n * So this is the read, and Edit is one press away. Nothing here writes; the\n * one thing it changes is on the door, and that lives on the roster.\n *\n * WHAT IS NOT A SECTION HERE, deliberately: check-in. Scanning is a phone job\n * done at a door, for one show, on `/dashboard/bookings/attendees` where the\n * scanner and the manual code box already are — and, from a machine, on\n * `POST /devices/scan`. Putting a scan box on this page would be a third place\n * a ticket can be admitted and a third set of rules to keep in step.\n */\nexport default function EventPage() {\n const params = useParams();\n const router = useRouter();\n const links = useItemLinks();\n const { tenant } = useAuth();\n const formatCurrency = useFormatCurrency();\n const eventId = params?.eventId as string;\n const { toast } = useToast();\n const [creatingTestPurchase, setCreatingTestPurchase] = useState(false);\n const [testPurchasesEnabled, setTestPurchasesEnabled] = useState(false);\n\n useEffect(() => {\n const frame = window.requestAnimationFrame(() => {\n setTestPurchasesEnabled(\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1',\n );\n });\n return () => window.cancelAnimationFrame(frame);\n }, []);\n\n const { data, isLoading, error, refetch } = useApiQuery(\n ['event', eventId],\n (slug) => inventoryApi.getById(slug, eventId),\n );\n\n const event = data as InventoryProduct | undefined;\n const facts = event ? eventFacts(event) : null;\n\n /**\n * The public page, which is also what the QR resolves to.\n *\n * `/{tenant}/events/{slug}` is a route the storefront already serves, with\n * the night, the room, the tiers and a way to pay on it — so \"scan, see\n * details, then pay\" needs no new surface. A bespoke ticket-buying screen\n * behind the code would be a second storefront to keep in step with the\n * first.\n */\n const publicUrl =\n tenant?.slug && event?.slug\n ? storefrontPageUrl(tenant.slug, `/events/${event.slug}`)\n : '';\n\n const tiers = event ? eventTierFacts(event) : [];\n\n async function createTestPurchase() {\n const tier =\n tiers.find((candidate) => candidate.soldCount < candidate.quantity) ??\n tiers[0];\n if (!tenant?.slug || !facts?.date || !tier?.id) {\n toast({\n title: 'This event is not ready for a test sale',\n description:\n 'Add an event date and a ticket tier before testing a purchase.',\n variant: 'destructive',\n });\n return;\n }\n\n setCreatingTestPurchase(true);\n try {\n await bookingsApi.createTestPurchase(tenant.slug, {\n type: 'event',\n contentId: eventId,\n date: facts.date,\n quantity: 1,\n customerName: 'Local Test Guest',\n customerEmail: 'local-test@example.test',\n total: tier.price,\n metadata: {\n tierId: tier.id,\n tierName: tier.name,\n },\n });\n await refetch();\n toast({\n title: 'Test sale recorded',\n description: `${tier.name} is now counted as a paid ticket.`,\n });\n } catch (error) {\n toast({\n title: 'Test sale failed',\n description:\n error instanceof Error\n ? error.message\n : 'Unable to create the test sale.',\n variant: 'destructive',\n });\n } finally {\n setCreatingTestPurchase(false);\n }\n }\n\n /** Derived, not stored — see `event-share`. */\n const hashtag = `#${(event?.slug || '').replace(/-/g, '')}`;\n\n if (isLoading || error || !event || !facts) {\n return (\n <DetailStates\n loading={isLoading && !event}\n error={error?.message ?? null}\n onRetry={() => void refetch()}\n back={{ href: links.list, label: 'Events' }}\n notFoundLabel=\"That event is not here. It may have been deleted.\"\n />\n );\n }\n\n return (\n <DetailPage\n breadcrumbs={[\n {\n label: 'Events',\n href: links.list,\n icon: <CalendarRange />,\n },\n { label: event.title },\n ]}\n actions={\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"secondary\"\n icon={<Pencil />}\n onClick={() => router.push(`/dashboard/catalog/${eventId}/edit`)}\n >\n Edit\n </Button>\n </div>\n }\n >\n {/* `DetailStates` is early-returned rather than wrapped: it is the state\n machine for the two states where no record EXISTS yet, and it renders\n instead of the page rather than around it. */}\n <div className=\"grid w-full gap-4 lg:grid-cols-[minmax(0,1fr)_300px]\">\n <div className=\"min-w-0 space-y-4\">\n <Card>\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"flex items-center gap-2 text-sm font-medium text-[var(--text-primary)]\">\n <CalendarRange className=\"size-4 shrink-0\" />\n {whenLabel(facts)}\n </p>\n {facts.venue || facts.venueAddress ? (\n /* THE ROOM, AND A WAY TO FIND IT. A venue name on its\n own is a fact; a name that opens a map is a direction,\n and the person reading this on the day is usually\n about to travel to it or tell somebody else how. The\n query is the name and the address together, because a\n hall name alone lands on the wrong city often enough\n to be useless. */\n <a\n className=\"mt-1 flex min-w-0 items-start gap-2 rounded-md text-sm text-[var(--text-link)] underline decoration-[var(--text-link)] underline-offset-4 hover:text-[var(--text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)]\"\n href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(\n [facts.venue, facts.venueAddress]\n .filter(Boolean)\n .join(', '),\n )}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n <MapPin className=\"size-4 shrink-0\" />\n <span className=\"min-w-0 break-words\">\n {[facts.venue, facts.venueAddress]\n .filter(Boolean)\n .join(', ')}\n </span>\n </a>\n ) : null}\n </div>\n <StatusBadge status={eventPublicationStatus(event)} />\n </div>\n </Card>\n\n <Card>\n <div className=\"mb-3 flex items-baseline justify-between gap-3\">\n <p className=\"text-sm font-medium text-[var(--text-secondary)]\">\n Tickets\n </p>\n {/* Three states, and they were one line saying \"No tiers yet\"\n for two of them — including an event that HAS a ticket type\n with no quantity on it, which is a merchant being told the\n thing they just made does not exist. A tier with no number is\n on sale without a cap, which is a real and common way to run\n a night. */}\n <p className=\"text-sm tabular-nums text-[var(--text-tertiary)]\">\n {tiers.length === 0\n ? 'No tickets yet'\n : facts.capacity > 0\n ? `${facts.sold} of ${facts.capacity} sold`\n : `${facts.sold} sold, no limit`}\n </p>\n </div>\n <ul className=\"space-y-1\">\n {tiers.map((tier, i) => (\n <li\n key={`${tier.name}-${i}`}\n className=\"flex items-center justify-between gap-3 rounded-lg px-2 py-2 text-sm\"\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n {tier.name.toLowerCase().includes('vip') ? (\n <Crown\n aria-hidden=\"true\"\n className=\"size-4 shrink-0 text-[var(--text-secondary)]\"\n />\n ) : (\n <Ticket\n aria-hidden=\"true\"\n className=\"size-4 shrink-0 text-[var(--text-secondary)]\"\n />\n )}\n <span className=\"truncate text-[var(--text-primary)]\">\n {tier.name}\n </span>\n </span>\n <span className=\"shrink-0 tabular-nums text-[var(--text-tertiary)]\">\n {formatCurrency(tier.price, event.currency)} ·{' '}\n {tier.soldCount}/{tier.quantity}\n </span>\n </li>\n ))}\n </ul>\n {testPurchasesEnabled ? (\n <Button\n variant=\"secondary\"\n className=\"mt-3 w-full\"\n icon={<Ticket />}\n disabled={creatingTestPurchase}\n onClick={() => void createTestPurchase()}\n >\n {creatingTestPurchase\n ? 'Recording test sale…'\n : 'Record test sale'}\n </Button>\n ) : null}\n </Card>\n\n <EventAttendees\n eventId={eventId}\n doorListHref={`/dashboard/bookings/attendees?contentId=${eventId}`}\n />\n </div>\n\n <div className=\"space-y-4 lg:sticky lg:top-4 lg:self-start\">\n {typeof event.image === 'string' ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img\n src={event.image}\n alt=\"\"\n className=\"aspect-square w-full rounded-[var(--radius-card)] object-cover\"\n />\n ) : event.image?.url ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img\n src={event.image.url}\n alt=\"\"\n className=\"aspect-square w-full rounded-[var(--radius-card)] object-cover\"\n />\n ) : null}\n\n {publicUrl && (\n <EventShare url={publicUrl} title={event.title} hashtag={hashtag} />\n )}\n </div>\n </div>\n </DetailPage>\n );\n}\n"],"mappings":"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","names":["error"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { CalendarRange } from "lucide-react";
|
|
5
|
+
import { StatusBadge } from "@withpotter/components/shared/status-badge";
|
|
6
|
+
import {
|
|
7
|
+
eventFacts,
|
|
8
|
+
eventPublicationStatus,
|
|
9
|
+
whenLabel
|
|
10
|
+
} from "../_lib/event-facts";
|
|
11
|
+
import { eventImage } from "./event-poster";
|
|
12
|
+
function EventGrid({ events }) {
|
|
13
|
+
return /* @__PURE__ */ jsx("ul", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: events.map((event) => {
|
|
14
|
+
const facts = eventFacts(event);
|
|
15
|
+
const image = eventImage(event);
|
|
16
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
17
|
+
Link,
|
|
18
|
+
{
|
|
19
|
+
href: `/dashboard/events/${event.id}`,
|
|
20
|
+
className: "surface-interactive block overflow-hidden",
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ jsx("div", { className: "grid aspect-[4/3] w-full place-items-center bg-[var(--bg-muted)]", children: image ? (
|
|
23
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
24
|
+
/* @__PURE__ */ jsx("img", { src: image, alt: "", className: "size-full object-cover" })
|
|
25
|
+
) : /* @__PURE__ */ jsx(CalendarRange, { className: "size-5 text-[var(--text-tertiary)]" }) }),
|
|
26
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3", children: [
|
|
27
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
28
|
+
/* @__PURE__ */ jsx("p", { className: "line-clamp-1 text-md text-[var(--text-primary)]", children: event.title }),
|
|
29
|
+
/* @__PURE__ */ jsx(StatusBadge, { status: eventPublicationStatus(event) })
|
|
30
|
+
] }),
|
|
31
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1 line-clamp-1 text-sm text-[var(--text-tertiary)]", children: [
|
|
32
|
+
whenLabel(facts),
|
|
33
|
+
facts.capacity > 0 && ` \xB7 ${facts.sold} of ${facts.capacity}`
|
|
34
|
+
] })
|
|
35
|
+
] })
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
) }, event.id);
|
|
39
|
+
}) });
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
EventGrid
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=event-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/events/_components/event-grid.tsx"],"sourcesContent":["'use client';\n\nimport Link from 'next/link';\nimport { CalendarRange } from 'lucide-react';\nimport { StatusBadge } from '@withpotter/components/shared/status-badge';\nimport type { InventoryProduct } from '@withpotter/lib/api/types';\nimport {\n eventFacts,\n eventPublicationStatus,\n whenLabel,\n} from '../_lib/event-facts';\nimport { eventImage } from './event-poster';\n\n/**\n * The same events, as posters.\n *\n * A promoter running six nights recognises theirs by the artwork long before\n * they finish reading a title, and a table cannot show artwork without a\n * thumbnail column that makes every row taller for the one thing it is showing.\n *\n * A LISTING WITH NO POSTER IS THE NORMAL CASE and must not read as broken: it\n * falls back to the calendar glyph on the card's own ground, which is what the\n * empty states in this app already do. An invented picture beside a real one\n * teaches the eye that none of them can be trusted.\n */\nexport function EventGrid({ events }: { events: InventoryProduct[] }) {\n return (\n <ul className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {events.map((event) => {\n const facts = eventFacts(event);\n const image = eventImage(event);\n\n return (\n <li key={event.id}>\n <Link\n href={`/dashboard/events/${event.id}`}\n className=\"surface-interactive block overflow-hidden\"\n >\n <div className=\"grid aspect-[4/3] w-full place-items-center bg-[var(--bg-muted)]\">\n {image ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img src={image} alt=\"\" className=\"size-full object-cover\" />\n ) : (\n <CalendarRange className=\"size-5 text-[var(--text-tertiary)]\" />\n )}\n </div>\n\n <div className=\"p-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"line-clamp-1 text-md text-[var(--text-primary)]\">\n {event.title}\n </p>\n <StatusBadge status={eventPublicationStatus(event)} />\n </div>\n <p className=\"mt-1 line-clamp-1 text-sm text-[var(--text-tertiary)]\">\n {whenLabel(facts)}\n {facts.capacity > 0 &&\n ` · ${facts.sold} of ${facts.capacity}`}\n </p>\n </div>\n </Link>\n </li>\n );\n })}\n </ul>\n );\n}\n"],"mappings":";AAyCkB,cAOF,YAPE;AAvClB,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAE5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAcpB,SAAS,UAAU,EAAE,OAAO,GAAmC;AACpE,SACE,oBAAC,QAAG,WAAU,uEACX,iBAAO,IAAI,CAAC,UAAU;AACrB,UAAM,QAAQ,WAAW,KAAK;AAC9B,UAAM,QAAQ,WAAW,KAAK;AAE9B,WACE,oBAAC,QACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,qBAAqB,MAAM,EAAE;AAAA,QACnC,WAAU;AAAA,QAEV;AAAA,8BAAC,SAAI,WAAU,oEACZ;AAAA;AAAA,YAEC,oBAAC,SAAI,KAAK,OAAO,KAAI,IAAG,WAAU,0BAAyB;AAAA,cAE3D,oBAAC,iBAAc,WAAU,sCAAqC,GAElE;AAAA,UAEA,qBAAC,SAAI,WAAU,OACb;AAAA,iCAAC,SAAI,WAAU,0CACb;AAAA,kCAAC,OAAE,WAAU,mDACV,gBAAM,OACT;AAAA,cACA,oBAAC,eAAY,QAAQ,uBAAuB,KAAK,GAAG;AAAA,eACtD;AAAA,YACA,qBAAC,OAAE,WAAU,yDACV;AAAA,wBAAU,KAAK;AAAA,cACf,MAAM,WAAW,KAChB,SAAM,MAAM,IAAI,OAAO,MAAM,QAAQ;AAAA,eACzC;AAAA,aACF;AAAA;AAAA;AAAA,IACF,KA3BO,MAAM,EA4Bf;AAAA,EAEJ,CAAC,GACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CalendarRange } from "lucide-react";
|
|
3
|
+
function eventImage(event) {
|
|
4
|
+
return typeof event.image === "string" ? event.image : event.image?.url;
|
|
5
|
+
}
|
|
6
|
+
function EventThumb({ event }) {
|
|
7
|
+
const image = eventImage(event);
|
|
8
|
+
return /* @__PURE__ */ jsx("div", { className: "grid size-10 shrink-0 place-items-center overflow-hidden rounded-lg bg-[var(--bg-muted)]", children: image ? (
|
|
9
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
10
|
+
/* @__PURE__ */ jsx("img", { src: image, alt: "", className: "size-full object-cover" })
|
|
11
|
+
) : /* @__PURE__ */ jsx(CalendarRange, { className: "size-4 text-[var(--text-tertiary)]" }) });
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
EventThumb,
|
|
15
|
+
eventImage
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=event-poster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/events/_components/event-poster.tsx"],"sourcesContent":["import { CalendarRange } from 'lucide-react';\nimport type { InventoryProduct } from '@withpotter/lib/api/types';\n\n/** An event's poster address, whichever shape the listing stored it in. */\nexport function eventImage(event: InventoryProduct): string | undefined {\n return typeof event.image === 'string' ? event.image : event.image?.url;\n}\n\n/**\n * The poster as a thumbnail, for a list row. A listing with no poster falls\n * back to the calendar glyph on the same ground, as the grid's cards do, so a\n * row without artwork reads as normal rather than broken.\n */\nexport function EventThumb({ event }: { event: InventoryProduct }) {\n const image = eventImage(event);\n return (\n <div className=\"grid size-10 shrink-0 place-items-center overflow-hidden rounded-lg bg-[var(--bg-muted)]\">\n {image ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img src={image} alt=\"\" className=\"size-full object-cover\" />\n ) : (\n <CalendarRange className=\"size-4 text-[var(--text-tertiary)]\" />\n )}\n </div>\n );\n}\n"],"mappings":"AAmBQ;AAnBR,SAAS,qBAAqB;AAIvB,SAAS,WAAW,OAA6C;AACtE,SAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ,MAAM,OAAO;AACtE;AAOO,SAAS,WAAW,EAAE,MAAM,GAAgC;AACjE,QAAM,QAAQ,WAAW,KAAK;AAC9B,SACE,oBAAC,SAAI,WAAU,4FACZ;AAAA;AAAA,IAEC,oBAAC,SAAI,KAAK,OAAO,KAAI,IAAG,WAAU,0BAAyB;AAAA,MAE3D,oBAAC,iBAAc,WAAU,sCAAqC,GAElE;AAEJ;","names":[]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { useApiQuery } from "@withpotter/hooks/use-api-query";
|
|
4
|
+
import { useApiFeatures } from "@withpotter/hooks/use-api-features";
|
|
5
|
+
import { inventoryApi } from "@withpotter/lib/api/inventory";
|
|
6
|
+
import { LIST_PAGE_SIZE } from "@withpotter/lib/list-page-size";
|
|
7
|
+
import { today } from "../_lib/event-facts";
|
|
8
|
+
function useEventsData() {
|
|
9
|
+
const [window, setWindow] = useState("all");
|
|
10
|
+
const [page, setPage] = useState(1);
|
|
11
|
+
const { features } = useApiFeatures();
|
|
12
|
+
const query = useMemo(() => {
|
|
13
|
+
const base = {
|
|
14
|
+
behavior: "ticketed",
|
|
15
|
+
page,
|
|
16
|
+
limit: LIST_PAGE_SIZE,
|
|
17
|
+
// Drafts too. A merchant writing next month's show has to be able to
|
|
18
|
+
// find it. `isActive` cannot ask for both — omitted, it is the default
|
|
19
|
+
// (active only), which is what hid every draft event — so the build's
|
|
20
|
+
// own "include drafts" is used where it has one.
|
|
21
|
+
includeDrafts: features.includeDrafts ? true : void 0
|
|
22
|
+
};
|
|
23
|
+
if (window === "all" || !features.eventWindow) return base;
|
|
24
|
+
const boundary = today();
|
|
25
|
+
return window === "upcoming" ? { ...base, eventFrom: boundary, sortBy: "eventDate", sortOrder: "ASC" } : {
|
|
26
|
+
...base,
|
|
27
|
+
eventUntil: boundary,
|
|
28
|
+
sortBy: "eventDate",
|
|
29
|
+
sortOrder: "DESC"
|
|
30
|
+
};
|
|
31
|
+
}, [window, page, features.eventWindow, features.includeDrafts]);
|
|
32
|
+
const { data, isLoading, error, refetch } = useApiQuery(
|
|
33
|
+
["events", JSON.stringify(query)],
|
|
34
|
+
(slug) => inventoryApi.list(slug, query, { assignedOnly: false })
|
|
35
|
+
);
|
|
36
|
+
return {
|
|
37
|
+
events: data?.items ?? [],
|
|
38
|
+
total: data?.total ?? 0,
|
|
39
|
+
totalPages: data?.totalPages ?? 1,
|
|
40
|
+
page,
|
|
41
|
+
setPage,
|
|
42
|
+
window,
|
|
43
|
+
setWindow: (next) => {
|
|
44
|
+
setWindow(next);
|
|
45
|
+
setPage(1);
|
|
46
|
+
},
|
|
47
|
+
loading: isLoading,
|
|
48
|
+
error: error?.message ?? null,
|
|
49
|
+
refetch
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
useEventsData
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=use-events-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/events/_hooks/use-events-data.ts"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport { useApiQuery } from '@withpotter/hooks/use-api-query';\nimport { useApiFeatures } from '@withpotter/hooks/use-api-features';\nimport { inventoryApi } from '@withpotter/lib/api/inventory';\nimport { LIST_PAGE_SIZE } from '@withpotter/lib/list-page-size';\nimport type { InventoryProduct, InventoryQuery } from '@withpotter/lib/api/types';\nimport { today } from '../_lib/event-facts';\n\nexport type EventWindow = 'upcoming' | 'past' | 'all';\n\n/**\n * The events a business has put on, read forwards or backwards.\n *\n * ONE QUESTION, ASKED OF THE SERVER. A venue with two hundred nights behind it\n * must not pay to fetch them to show the four ahead, and a page that filtered\n * in the browser would also be paging through the wrong list: the pager would\n * count events the merchant cannot see. `eventFrom` / `eventUntil` are a real\n * SQL window on `behaviors.ticketed.eventDate`.\n *\n * UPCOMING AND PAST DO NOT COVER EVERYTHING, deliberately. Both windows are\n * bounded by today, and a listing with no date yet sits in neither — it is a\n * draft somebody is still writing, and putting it under Upcoming would promise\n * a night nobody has decided on. That is why \"All\" exists and is the default:\n * the first question a merchant asks this page is \"what have I got\", and a\n * half-written event they cannot find is the one thing that would make it\n * useless.\n *\n * GATED ON THE BUILD. `/products` runs `forbidNonWhitelisted`, so sending the\n * window to an API that has not got it is a 400 on the whole list rather than\n * a filter that does nothing — the page would come back empty. Without the\n * capability the window is not sent and every event is listed, which is wrong\n * in the direction a merchant can see and work around.\n */\nexport function useEventsData() {\n const [window, setWindow] = useState<EventWindow>('all');\n const [page, setPage] = useState(1);\n const { features } = useApiFeatures();\n\n const query = useMemo<InventoryQuery>(() => {\n const base: InventoryQuery = {\n behavior: 'ticketed',\n page,\n limit: LIST_PAGE_SIZE,\n // Drafts too. A merchant writing next month's show has to be able to\n // find it. `isActive` cannot ask for both — omitted, it is the default\n // (active only), which is what hid every draft event — so the build's\n // own \"include drafts\" is used where it has one.\n includeDrafts: features.includeDrafts ? true : undefined,\n };\n if (window === 'all' || !features.eventWindow) return base;\n const boundary = today();\n return window === 'upcoming'\n ? { ...base, eventFrom: boundary, sortBy: 'eventDate', sortOrder: 'ASC' }\n : {\n ...base,\n eventUntil: boundary,\n sortBy: 'eventDate',\n sortOrder: 'DESC',\n };\n }, [window, page, features.eventWindow, features.includeDrafts]);\n\n const { data, isLoading, error, refetch } = useApiQuery(\n ['events', JSON.stringify(query)],\n (slug) => inventoryApi.list(slug, query, { assignedOnly: false }),\n );\n\n return {\n events: (data?.items ?? []) as InventoryProduct[],\n total: data?.total ?? 0,\n totalPages: data?.totalPages ?? 1,\n page,\n setPage,\n window,\n setWindow: (next: EventWindow) => {\n setWindow(next);\n setPage(1);\n },\n loading: isLoading,\n error: error?.message ?? null,\n refetch,\n };\n}\n"],"mappings":";AAEA,SAAS,SAAS,gBAAgB;AAClC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAE/B,SAAS,aAAa;AA2Bf,SAAS,gBAAgB;AAC9B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAsB,KAAK;AACvD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,CAAC;AAClC,QAAM,EAAE,SAAS,IAAI,eAAe;AAEpC,QAAM,QAAQ,QAAwB,MAAM;AAC1C,UAAM,OAAuB;AAAA,MAC3B,UAAU;AAAA,MACV;AAAA,MACA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAKP,eAAe,SAAS,gBAAgB,OAAO;AAAA,IACjD;AACA,QAAI,WAAW,SAAS,CAAC,SAAS,YAAa,QAAO;AACtD,UAAM,WAAW,MAAM;AACvB,WAAO,WAAW,aACd,EAAE,GAAG,MAAM,WAAW,UAAU,QAAQ,aAAa,WAAW,MAAM,IACtE;AAAA,MACE,GAAG;AAAA,MACH,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,WAAW;AAAA,IACb;AAAA,EACN,GAAG,CAAC,QAAQ,MAAM,SAAS,aAAa,SAAS,aAAa,CAAC;AAE/D,QAAM,EAAE,MAAM,WAAW,OAAO,QAAQ,IAAI;AAAA,IAC1C,CAAC,UAAU,KAAK,UAAU,KAAK,CAAC;AAAA,IAChC,CAAC,SAAS,aAAa,KAAK,MAAM,OAAO,EAAE,cAAc,MAAM,CAAC;AAAA,EAClE;AAEA,SAAO;AAAA,IACL,QAAS,MAAM,SAAS,CAAC;AAAA,IACzB,OAAO,MAAM,SAAS;AAAA,IACtB,YAAY,MAAM,cAAc;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,CAAC,SAAsB;AAChC,gBAAU,IAAI;AACd,cAAQ,CAAC;AAAA,IACX;AAAA,IACA,SAAS;AAAA,IACT,OAAO,OAAO,WAAW;AAAA,IACzB;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { getBehaviorConfig } from "@withpotter/lib/product-types";
|
|
2
|
+
const str = (v) => typeof v === "string" && v ? v : void 0;
|
|
3
|
+
const num = (v) => typeof v === "number" && !Number.isNaN(v) ? v : 0;
|
|
4
|
+
function ticketedConfig(product) {
|
|
5
|
+
return getBehaviorConfig(
|
|
6
|
+
product.behaviors?.ticketed
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
function eventTierFacts(product) {
|
|
10
|
+
const tiers = ticketedConfig(product)?.tiers;
|
|
11
|
+
if (!Array.isArray(tiers)) return [];
|
|
12
|
+
return tiers.map((tier, index) => ({
|
|
13
|
+
// Older events were saved before tier ids were persisted. Keep those
|
|
14
|
+
// events purchasable with a deterministic id until they are edited again.
|
|
15
|
+
id: typeof tier.id === "string" ? tier.id : `tier-${index}`,
|
|
16
|
+
name: str(tier.name) || `Ticket ${index + 1}`,
|
|
17
|
+
price: num(tier.price),
|
|
18
|
+
quantity: num(tier.quantity),
|
|
19
|
+
soldCount: num(tier.soldCount)
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
function eventPublicationStatus(product) {
|
|
23
|
+
if (typeof product.isActive === "boolean") {
|
|
24
|
+
return product.isActive ? "published" : "draft";
|
|
25
|
+
}
|
|
26
|
+
const status = product.status?.toLowerCase();
|
|
27
|
+
return status === "published" || status === "active" || status === "live" ? "published" : "draft";
|
|
28
|
+
}
|
|
29
|
+
function eventFacts(product) {
|
|
30
|
+
const ticketed = ticketedConfig(product);
|
|
31
|
+
const tiers = eventTierFacts(product);
|
|
32
|
+
const tierCapacity = tiers.reduce((sum, t) => sum + num(t.quantity), 0);
|
|
33
|
+
return {
|
|
34
|
+
date: str(ticketed?.eventDate),
|
|
35
|
+
startTime: str(ticketed?.eventStartTime),
|
|
36
|
+
venue: str(ticketed?.venueName),
|
|
37
|
+
venueAddress: str(ticketed?.venueAddress),
|
|
38
|
+
capacity: tierCapacity || num(ticketed?.venueCapacity),
|
|
39
|
+
sold: tiers.reduce((sum, t) => sum + t.soldCount, 0)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function today() {
|
|
43
|
+
const now = /* @__PURE__ */ new Date();
|
|
44
|
+
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
45
|
+
}
|
|
46
|
+
function whenLabel(facts) {
|
|
47
|
+
if (!facts.date) return "Date not set";
|
|
48
|
+
const when = /* @__PURE__ */ new Date(`${facts.date}T${facts.startTime || "00:00"}`);
|
|
49
|
+
const day = when.toLocaleDateString("en-GB", {
|
|
50
|
+
weekday: "short",
|
|
51
|
+
day: "numeric",
|
|
52
|
+
month: "short"
|
|
53
|
+
});
|
|
54
|
+
if (!facts.startTime) return day;
|
|
55
|
+
return `${day}, ${when.toLocaleTimeString("en-GB", {
|
|
56
|
+
hour: "numeric",
|
|
57
|
+
minute: "2-digit"
|
|
58
|
+
})}`;
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
eventFacts,
|
|
62
|
+
eventPublicationStatus,
|
|
63
|
+
eventTierFacts,
|
|
64
|
+
ticketedConfig,
|
|
65
|
+
today,
|
|
66
|
+
whenLabel
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=event-facts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/events/_lib/event-facts.ts"],"sourcesContent":["import type { InventoryProduct } from '@withpotter/lib/api/types';\nimport { getBehaviorConfig, type TicketedConfig } from '@withpotter/lib/product-types';\n\n/**\n * The event inside a product.\n *\n * An event IS a product in Box — a listing with the `ticketed` behaviour —\n * and everything that makes it an event lives in `behaviors.ticketed`: the\n * night, the room, the tiers people buy. The list type calls `behaviors` a\n * `Record<string, boolean>`, which was true when a behaviour was a flag and\n * has not been since they became config objects; the API sends the object\n * either way.\n *\n * So this reads defensively rather than casting the world into shape. A\n * listing half-written by a merchant is the normal case on this screen, not\n * the error case: no date yet, no tiers yet, a venue typed and nothing else.\n * Every field here is optional and every caller has to say what it draws when\n * the answer is nothing.\n */\nexport interface EventFacts {\n /** ISO date, `YYYY-MM-DD`. Absent while the merchant is still deciding. */\n date?: string;\n startTime?: string;\n venue?: string;\n venueAddress?: string;\n /** Seats across every tier, and how many of them have gone. */\n capacity: number;\n sold: number;\n}\n\nexport interface EventTierFacts {\n id?: string;\n name: string;\n price: number;\n quantity: number;\n soldCount: number;\n}\n\nconst str = (v: unknown) => (typeof v === 'string' && v ? v : undefined);\nconst num = (v: unknown) => (typeof v === 'number' && !Number.isNaN(v) ? v : 0);\n\nexport function ticketedConfig(\n product: Pick<InventoryProduct, 'behaviors'>,\n): TicketedConfig | null {\n return getBehaviorConfig<TicketedConfig>(\n (product.behaviors as Record<string, unknown> | undefined)?.ticketed,\n );\n}\n\nexport function eventTierFacts(product: InventoryProduct): EventTierFacts[] {\n const tiers = ticketedConfig(product)?.tiers;\n if (!Array.isArray(tiers)) return [];\n return tiers.map((tier, index) => ({\n // Older events were saved before tier ids were persisted. Keep those\n // events purchasable with a deterministic id until they are edited again.\n id: typeof tier.id === 'string' ? tier.id : `tier-${index}`,\n name: str(tier.name) || `Ticket ${index + 1}`,\n price: num(tier.price),\n quantity: num(tier.quantity),\n soldCount: num(tier.soldCount),\n }));\n}\n\nexport function eventPublicationStatus(\n product: Pick<InventoryProduct, 'isActive' | 'status'>,\n): 'published' | 'draft' {\n if (typeof product.isActive === 'boolean') {\n return product.isActive ? 'published' : 'draft';\n }\n const status = product.status?.toLowerCase();\n return status === 'published' || status === 'active' || status === 'live'\n ? 'published'\n : 'draft';\n}\n\nexport function eventFacts(product: InventoryProduct): EventFacts {\n const ticketed = ticketedConfig(product);\n const tiers = eventTierFacts(product);\n\n /**\n * Capacity is the tiers added up, and the room only when there are none.\n *\n * `venueCapacity` is how many the room holds; the tiers are how many are\n * for sale, which is the smaller and truer number the moment a merchant\n * holds seats back. Falling back the other way would tell a venue it had\n * sold 40 of 500 when only 60 were ever on offer.\n */\n const tierCapacity = tiers.reduce((sum, t) => sum + num(t.quantity), 0);\n\n return {\n date: str(ticketed?.eventDate),\n startTime: str(ticketed?.eventStartTime),\n venue: str(ticketed?.venueName),\n venueAddress: str(ticketed?.venueAddress),\n capacity: tierCapacity || num(ticketed?.venueCapacity),\n sold: tiers.reduce((sum, t) => sum + t.soldCount, 0),\n };\n}\n\n/** Today, as the API compares it. Local, because a night is local. */\nexport function today(): string {\n const now = new Date();\n return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;\n}\n\n/**\n * When it is, in words — \"Sat 12 Sep, 8:00pm\", or the honest absence.\n *\n * A listing with no date is not an error and must not read as one: it is an\n * event somebody is halfway through writing, and \"Date not set\" is the thing\n * that tells them what is left to do.\n */\nexport function whenLabel(facts: EventFacts): string {\n if (!facts.date) return 'Date not set';\n const when = new Date(`${facts.date}T${facts.startTime || '00:00'}`);\n const day = when.toLocaleDateString('en-GB', {\n weekday: 'short',\n day: 'numeric',\n month: 'short',\n });\n if (!facts.startTime) return day;\n return `${day}, ${when.toLocaleTimeString('en-GB', {\n hour: 'numeric',\n minute: '2-digit',\n })}`;\n}\n"],"mappings":"AACA,SAAS,yBAA8C;AAqCvD,MAAM,MAAM,CAAC,MAAgB,OAAO,MAAM,YAAY,IAAI,IAAI;AAC9D,MAAM,MAAM,CAAC,MAAgB,OAAO,MAAM,YAAY,CAAC,OAAO,MAAM,CAAC,IAAI,IAAI;AAEtE,SAAS,eACd,SACuB;AACvB,SAAO;AAAA,IACJ,QAAQ,WAAmD;AAAA,EAC9D;AACF;AAEO,SAAS,eAAe,SAA6C;AAC1E,QAAM,QAAQ,eAAe,OAAO,GAAG;AACvC,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA;AAAA;AAAA,IAGjC,IAAI,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK,QAAQ,KAAK;AAAA,IACzD,MAAM,IAAI,KAAK,IAAI,KAAK,UAAU,QAAQ,CAAC;AAAA,IAC3C,OAAO,IAAI,KAAK,KAAK;AAAA,IACrB,UAAU,IAAI,KAAK,QAAQ;AAAA,IAC3B,WAAW,IAAI,KAAK,SAAS;AAAA,EAC/B,EAAE;AACJ;AAEO,SAAS,uBACd,SACuB;AACvB,MAAI,OAAO,QAAQ,aAAa,WAAW;AACzC,WAAO,QAAQ,WAAW,cAAc;AAAA,EAC1C;AACA,QAAM,SAAS,QAAQ,QAAQ,YAAY;AAC3C,SAAO,WAAW,eAAe,WAAW,YAAY,WAAW,SAC/D,cACA;AACN;AAEO,SAAS,WAAW,SAAuC;AAChE,QAAM,WAAW,eAAe,OAAO;AACvC,QAAM,QAAQ,eAAe,OAAO;AAUpC,QAAM,eAAe,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,IAAI,EAAE,QAAQ,GAAG,CAAC;AAEtE,SAAO;AAAA,IACL,MAAM,IAAI,UAAU,SAAS;AAAA,IAC7B,WAAW,IAAI,UAAU,cAAc;AAAA,IACvC,OAAO,IAAI,UAAU,SAAS;AAAA,IAC9B,cAAc,IAAI,UAAU,YAAY;AAAA,IACxC,UAAU,gBAAgB,IAAI,UAAU,aAAa;AAAA,IACrD,MAAM,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC;AAAA,EACrD;AACF;AAGO,SAAS,QAAgB;AAC9B,QAAM,MAAM,oBAAI,KAAK;AACrB,SAAO,GAAG,IAAI,YAAY,CAAC,IAAI,OAAO,IAAI,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AACtH;AASO,SAAS,UAAU,OAA2B;AACnD,MAAI,CAAC,MAAM,KAAM,QAAO;AACxB,QAAM,OAAO,oBAAI,KAAK,GAAG,MAAM,IAAI,IAAI,MAAM,aAAa,OAAO,EAAE;AACnE,QAAM,MAAM,KAAK,mBAAmB,SAAS;AAAA,IAC3C,SAAS;AAAA,IACT,KAAK;AAAA,IACL,OAAO;AAAA,EACT,CAAC;AACD,MAAI,CAAC,MAAM,UAAW,QAAO;AAC7B,SAAO,GAAG,GAAG,KAAK,KAAK,mBAAmB,SAAS;AAAA,IACjD,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC,CAAC;AACJ;","names":[]}
|