@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,295 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { useRouter } from "next/navigation";
|
|
5
|
+
import { CalendarDays, CalendarRange, MapPin, Ticket } from "lucide-react";
|
|
6
|
+
import {
|
|
7
|
+
Button,
|
|
8
|
+
Card,
|
|
9
|
+
DetailPage,
|
|
10
|
+
NumberField,
|
|
11
|
+
OptionButton,
|
|
12
|
+
DateField,
|
|
13
|
+
Input,
|
|
14
|
+
Textarea,
|
|
15
|
+
TimeField,
|
|
16
|
+
useToast
|
|
17
|
+
} from "@withpotter/ui-kit";
|
|
18
|
+
import { useApiDispatch } from "@withpotter/hooks/use-api-dispatch";
|
|
19
|
+
import { useAuth } from "@withpotter/lib/auth-context";
|
|
20
|
+
import { useFormatCurrency } from "@withpotter/hooks/use-format-currency";
|
|
21
|
+
import { ErrorBanner } from "@withpotter/ui-kit/error-banner";
|
|
22
|
+
import { TicketTiers, newTier } from "./ticket-tiers";
|
|
23
|
+
import { EventPoster } from "./event-poster";
|
|
24
|
+
function EventComposer() {
|
|
25
|
+
const router = useRouter();
|
|
26
|
+
const { tenant } = useAuth();
|
|
27
|
+
const { save } = useApiDispatch();
|
|
28
|
+
const { toast } = useToast();
|
|
29
|
+
const formatCurrency = useFormatCurrency();
|
|
30
|
+
const [title, setTitle] = useState("");
|
|
31
|
+
const [date, setDate] = useState("");
|
|
32
|
+
const [startTime, setStartTime] = useState("");
|
|
33
|
+
const [endTime, setEndTime] = useState("");
|
|
34
|
+
const [venueName, setVenueName] = useState("");
|
|
35
|
+
const [venueAddress, setVenueAddress] = useState("");
|
|
36
|
+
const [onlineUrl, setOnlineUrl] = useState("");
|
|
37
|
+
const [description, setDescription] = useState("");
|
|
38
|
+
const [tiers, setTiers] = useState([newTier()]);
|
|
39
|
+
const [poster, setPoster] = useState(null);
|
|
40
|
+
const [capacity, setCapacity] = useState(null);
|
|
41
|
+
const [isPublic, setIsPublic] = useState(true);
|
|
42
|
+
const [saving, setSaving] = useState(false);
|
|
43
|
+
const [error, setError] = useState(null);
|
|
44
|
+
const currencySymbol = formatCurrency(0).replace(/[\d.,\s]/g, "") || "\u20A6";
|
|
45
|
+
const canSave = title.trim().length > 0 && !saving;
|
|
46
|
+
const onCreate = useCallback(async () => {
|
|
47
|
+
if (!canSave || !tenant?.slug) return;
|
|
48
|
+
setSaving(true);
|
|
49
|
+
setError(null);
|
|
50
|
+
try {
|
|
51
|
+
const priced = tiers.filter((t) => t.name.trim() || t.price !== null);
|
|
52
|
+
await save(
|
|
53
|
+
{
|
|
54
|
+
title: title.trim(),
|
|
55
|
+
slug: "",
|
|
56
|
+
description: description.trim() || void 0,
|
|
57
|
+
isActive: isPublic,
|
|
58
|
+
heroImage: poster,
|
|
59
|
+
/**
|
|
60
|
+
* The cheapest way in, because that is what a listing quotes.
|
|
61
|
+
* Storefronts read `price` for the card; the tiers below are what is
|
|
62
|
+
* actually bought, and quoting the dearest would tell somebody the
|
|
63
|
+
* door costs ₦50,000 when the regular ticket is ₦5,000.
|
|
64
|
+
*/
|
|
65
|
+
price: priced.length ? Math.min(...priced.map((t) => t.price ?? 0)) : 0,
|
|
66
|
+
trackInventory: false,
|
|
67
|
+
quantity: 0,
|
|
68
|
+
productType: "ticket",
|
|
69
|
+
behaviorConfigs: {
|
|
70
|
+
ticketed: {
|
|
71
|
+
enabled: true,
|
|
72
|
+
eventDate: date || void 0,
|
|
73
|
+
eventStartTime: startTime || void 0,
|
|
74
|
+
eventEndTime: endTime || void 0,
|
|
75
|
+
venueCapacity: capacity ?? void 0,
|
|
76
|
+
venueName: venueName.trim() || void 0,
|
|
77
|
+
venueAddress: venueAddress.trim() || void 0,
|
|
78
|
+
onlineEventUrl: onlineUrl.trim() || void 0,
|
|
79
|
+
tiers: priced.map((t) => ({
|
|
80
|
+
id: t.id,
|
|
81
|
+
name: t.name.trim() || "Ticket",
|
|
82
|
+
price: t.price ?? 0,
|
|
83
|
+
quantity: t.quantity ?? 0
|
|
84
|
+
}))
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
intent: "event-tickets"
|
|
88
|
+
},
|
|
89
|
+
"products"
|
|
90
|
+
);
|
|
91
|
+
toast({ title: "Event created", description: "It is on your list." });
|
|
92
|
+
router.replace("/dashboard/events");
|
|
93
|
+
} catch (err) {
|
|
94
|
+
setError(
|
|
95
|
+
err instanceof Error ? err.message : "Could not create that event."
|
|
96
|
+
);
|
|
97
|
+
setSaving(false);
|
|
98
|
+
}
|
|
99
|
+
}, [
|
|
100
|
+
canSave,
|
|
101
|
+
capacity,
|
|
102
|
+
date,
|
|
103
|
+
description,
|
|
104
|
+
endTime,
|
|
105
|
+
isPublic,
|
|
106
|
+
onlineUrl,
|
|
107
|
+
poster,
|
|
108
|
+
router,
|
|
109
|
+
save,
|
|
110
|
+
startTime,
|
|
111
|
+
tenant?.slug,
|
|
112
|
+
tiers,
|
|
113
|
+
title,
|
|
114
|
+
toast,
|
|
115
|
+
venueAddress,
|
|
116
|
+
venueName
|
|
117
|
+
]);
|
|
118
|
+
const headerActions = useMemo(
|
|
119
|
+
() => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
120
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => router.back(), children: "Cancel" }),
|
|
121
|
+
/* @__PURE__ */ jsx(Button, { disabled: !canSave, onClick: () => void onCreate(), children: saving ? "Creating\u2026" : "Create event" })
|
|
122
|
+
] }),
|
|
123
|
+
[canSave, onCreate, router, saving]
|
|
124
|
+
);
|
|
125
|
+
return /* @__PURE__ */ jsx(
|
|
126
|
+
DetailPage,
|
|
127
|
+
{
|
|
128
|
+
breadcrumbs: [
|
|
129
|
+
{
|
|
130
|
+
label: "Events",
|
|
131
|
+
href: "/dashboard/events",
|
|
132
|
+
icon: /* @__PURE__ */ jsx(CalendarRange, {})
|
|
133
|
+
},
|
|
134
|
+
{ label: "New event" }
|
|
135
|
+
],
|
|
136
|
+
actions: headerActions,
|
|
137
|
+
mobileActions: headerActions,
|
|
138
|
+
children: /* @__PURE__ */ jsxs("div", { className: "grid w-full gap-4 lg:grid-cols-[minmax(0,1fr)_300px]", children: [
|
|
139
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-4", children: [
|
|
140
|
+
/* @__PURE__ */ jsx(ErrorBanner, { children: error }),
|
|
141
|
+
/* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(
|
|
142
|
+
Input,
|
|
143
|
+
{
|
|
144
|
+
value: title,
|
|
145
|
+
onChange: (e) => setTitle(e.target.value),
|
|
146
|
+
placeholder: "Event name",
|
|
147
|
+
"aria-label": "Event name",
|
|
148
|
+
name: "event-name",
|
|
149
|
+
className: "!text-2xl"
|
|
150
|
+
}
|
|
151
|
+
) }),
|
|
152
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
153
|
+
/* @__PURE__ */ jsxs("p", { className: "mb-3 flex items-center gap-2 text-sm font-medium text-[var(--text-secondary)]", children: [
|
|
154
|
+
/* @__PURE__ */ jsx(CalendarDays, { className: "size-4" }),
|
|
155
|
+
"When"
|
|
156
|
+
] }),
|
|
157
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [
|
|
158
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-[180px] flex-1", children: /* @__PURE__ */ jsx(
|
|
159
|
+
DateField,
|
|
160
|
+
{
|
|
161
|
+
value: date,
|
|
162
|
+
onChange: setDate,
|
|
163
|
+
name: "event-date",
|
|
164
|
+
"aria-label": "Event date"
|
|
165
|
+
}
|
|
166
|
+
) }),
|
|
167
|
+
/* @__PURE__ */ jsx("div", { className: "w-32", children: /* @__PURE__ */ jsx(
|
|
168
|
+
TimeField,
|
|
169
|
+
{
|
|
170
|
+
value: startTime,
|
|
171
|
+
onChange: setStartTime,
|
|
172
|
+
name: "event-start",
|
|
173
|
+
"aria-label": "Start time"
|
|
174
|
+
}
|
|
175
|
+
) }),
|
|
176
|
+
/* @__PURE__ */ jsx("div", { className: "w-32", children: /* @__PURE__ */ jsx(
|
|
177
|
+
TimeField,
|
|
178
|
+
{
|
|
179
|
+
value: endTime,
|
|
180
|
+
onChange: setEndTime,
|
|
181
|
+
name: "event-end",
|
|
182
|
+
"aria-label": "End time"
|
|
183
|
+
}
|
|
184
|
+
) })
|
|
185
|
+
] })
|
|
186
|
+
] }),
|
|
187
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
188
|
+
/* @__PURE__ */ jsxs("p", { className: "mb-3 flex items-center gap-2 text-sm font-medium text-[var(--text-secondary)]", children: [
|
|
189
|
+
/* @__PURE__ */ jsx(MapPin, { className: "size-4" }),
|
|
190
|
+
"Where"
|
|
191
|
+
] }),
|
|
192
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
193
|
+
/* @__PURE__ */ jsx(
|
|
194
|
+
Input,
|
|
195
|
+
{
|
|
196
|
+
label: "Venue",
|
|
197
|
+
value: venueName,
|
|
198
|
+
onChange: (e) => setVenueName(e.target.value),
|
|
199
|
+
placeholder: "The Grand Junction Hall",
|
|
200
|
+
name: "venue-name"
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
/* @__PURE__ */ jsx(
|
|
204
|
+
Input,
|
|
205
|
+
{
|
|
206
|
+
label: "Address",
|
|
207
|
+
value: venueAddress,
|
|
208
|
+
onChange: (e) => setVenueAddress(e.target.value),
|
|
209
|
+
placeholder: "Street, area, city",
|
|
210
|
+
name: "venue-address"
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
/* @__PURE__ */ jsx(
|
|
214
|
+
Input,
|
|
215
|
+
{
|
|
216
|
+
label: "Or a link, for an online event",
|
|
217
|
+
value: onlineUrl,
|
|
218
|
+
onChange: (e) => setOnlineUrl(e.target.value),
|
|
219
|
+
placeholder: "https://",
|
|
220
|
+
name: "online-url"
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
] })
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
226
|
+
/* @__PURE__ */ jsxs("p", { className: "mb-3 flex items-center gap-2 text-sm font-medium text-[var(--text-secondary)]", children: [
|
|
227
|
+
/* @__PURE__ */ jsx(Ticket, { className: "size-4" }),
|
|
228
|
+
"Tickets"
|
|
229
|
+
] }),
|
|
230
|
+
/* @__PURE__ */ jsx(
|
|
231
|
+
TicketTiers,
|
|
232
|
+
{
|
|
233
|
+
tiers,
|
|
234
|
+
onChange: setTiers,
|
|
235
|
+
currencySymbol
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
] }),
|
|
239
|
+
/* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(
|
|
240
|
+
Textarea,
|
|
241
|
+
{
|
|
242
|
+
label: "About this event",
|
|
243
|
+
value: description,
|
|
244
|
+
onChange: (e) => setDescription(e.target.value),
|
|
245
|
+
placeholder: "What is happening, who it is for, what to bring.",
|
|
246
|
+
rows: 4,
|
|
247
|
+
name: "event-description"
|
|
248
|
+
}
|
|
249
|
+
) })
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4 lg:sticky lg:top-4 lg:self-start", children: [
|
|
252
|
+
/* @__PURE__ */ jsx(EventPoster, { value: poster, onChange: setPoster }),
|
|
253
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
254
|
+
/* @__PURE__ */ jsx("p", { className: "mb-3 text-sm font-medium text-[var(--text-secondary)]", children: "Who and how many" }),
|
|
255
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-4", children: [
|
|
256
|
+
/* @__PURE__ */ jsx("div", { className: "w-40", children: /* @__PURE__ */ jsx(
|
|
257
|
+
NumberField,
|
|
258
|
+
{
|
|
259
|
+
label: "Capacity",
|
|
260
|
+
value: capacity,
|
|
261
|
+
onChange: setCapacity,
|
|
262
|
+
min: 0,
|
|
263
|
+
placeholder: "Unlimited"
|
|
264
|
+
}
|
|
265
|
+
) }),
|
|
266
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
267
|
+
/* @__PURE__ */ jsx(
|
|
268
|
+
OptionButton,
|
|
269
|
+
{
|
|
270
|
+
selected: isPublic,
|
|
271
|
+
onClick: () => setIsPublic(true),
|
|
272
|
+
children: "Public"
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ jsx(
|
|
276
|
+
OptionButton,
|
|
277
|
+
{
|
|
278
|
+
selected: !isPublic,
|
|
279
|
+
onClick: () => setIsPublic(false),
|
|
280
|
+
children: "Private"
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
] })
|
|
284
|
+
] }),
|
|
285
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-[var(--text-tertiary)]", children: isPublic ? "On your website, and anyone with the link can buy." : "Off your website. Only people you send the link to will find it." })
|
|
286
|
+
] })
|
|
287
|
+
] })
|
|
288
|
+
] })
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
export {
|
|
293
|
+
EventComposer
|
|
294
|
+
};
|
|
295
|
+
//# sourceMappingURL=event-composer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/dashboard/%28bookings%29/events/new/_components/event-composer.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { useRouter } from 'next/navigation';\nimport { CalendarDays, CalendarRange, MapPin, Ticket } from 'lucide-react';\nimport {\n Button,\n Card,\n DetailPage,\n NumberField,\n OptionButton,\n DateField,\n Input,\n Textarea,\n TimeField,\n useToast,\n} from '@withpotter/ui-kit';\nimport { useApiDispatch } from '@withpotter/hooks/use-api-dispatch';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { useFormatCurrency } from '@withpotter/hooks/use-format-currency';\nimport { ErrorBanner } from '@withpotter/ui-kit/error-banner';\nimport { TicketTiers, newTier, type Tier } from './ticket-tiers';\nimport { EventPoster } from './event-poster';\nimport type { ProductMedia } from '@withpotter/lib/product-types';\n\n/**\n * PUTTING ON A NIGHT — the form Luma opens with, in our components.\n *\n * John, 2026-09-03: *\"I try to create an event and it still looks like I'm\n * trying to create a product, and the breadcrumbs still say products\"*, then\n * *\"Luma is open on my Chrome, just look at how it's done.\"*\n *\n * What Luma asks, in its order: a cover, a name, start and end (date and time\n * side by side), a location that can be a room or a link, a description, and\n * then options — price, approval, capacity. Everything else it can do lives\n * behind those, after the event exists. Read against our product editor the\n * difference is not the field list, it is the ORDER: ours opens on Price,\n * Track inventory and Low stock alert, and folds the date, the room and the\n * tickets into a section called \"How it sells\". A merchant filling that in is\n * being asked about a warehouse.\n *\n * So this asks for the event, in the order somebody would say it out loud.\n * Nothing here is new storage: it writes the same `ticketed` behaviour to the\n * same record through the same dispatch the editor uses, so an event made here\n * opens in the editor and vice versa. One record, one save path, two doors.\n *\n * WHAT IS DELIBERATELY NOT ASKED. No stock tracking — tickets are the tiers\n * below and capacity is their sum. No category, no SKU, no compare-at price.\n * The editor still has all of it for anyone who wants it, one press away, and\n * an event that never goes near it is the normal case rather than the\n * incomplete one.\n */\nexport function EventComposer() {\n const router = useRouter();\n const { tenant } = useAuth();\n const { save } = useApiDispatch();\n const { toast } = useToast();\n const formatCurrency = useFormatCurrency();\n\n const [title, setTitle] = useState('');\n const [date, setDate] = useState('');\n const [startTime, setStartTime] = useState('');\n const [endTime, setEndTime] = useState('');\n const [venueName, setVenueName] = useState('');\n const [venueAddress, setVenueAddress] = useState('');\n const [onlineUrl, setOnlineUrl] = useState('');\n const [description, setDescription] = useState('');\n const [tiers, setTiers] = useState<Tier[]>([newTier()]);\n const [poster, setPoster] = useState<ProductMedia | null>(null);\n /**\n * How many can come, and who can see it.\n *\n * John, 2026-09-03: *\"we should be able to set the capacity of an event, we\n * can set unlimited too, and also if it's a public or private event.\"* Both\n * are Luma's two Event Options, and both are decisions a host makes while\n * they are still writing rather than afterwards.\n *\n * `null` capacity IS unlimited — one field, not a toggle beside a number. A\n * toggle stores two facts that can disagree (\"unlimited\" ticked with 200 in\n * the box), and `NumberField` already answers null for an empty box, which\n * is the whole reason it exists.\n *\n * Private means `isActive: false`: the listing is off the storefront and\n * shared by link, which is the only privacy this platform can honestly offer\n * today — there is no members-only tier on a content record.\n */\n const [capacity, setCapacity] = useState<number | null>(null);\n const [isPublic, setIsPublic] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n /** The symbol only — the whole formatter would print a zero beside a label. */\n const currencySymbol = formatCurrency(0).replace(/[\\d.,\\s]/g, '') || '₦';\n\n const canSave = title.trim().length > 0 && !saving;\n\n const onCreate = useCallback(async () => {\n if (!canSave || !tenant?.slug) return;\n setSaving(true);\n setError(null);\n try {\n const priced = tiers.filter((t) => t.name.trim() || t.price !== null);\n await save(\n {\n title: title.trim(),\n slug: '',\n description: description.trim() || undefined,\n isActive: isPublic,\n heroImage: poster,\n /**\n * The cheapest way in, because that is what a listing quotes.\n * Storefronts read `price` for the card; the tiers below are what is\n * actually bought, and quoting the dearest would tell somebody the\n * door costs ₦50,000 when the regular ticket is ₦5,000.\n */\n price: priced.length\n ? Math.min(...priced.map((t) => t.price ?? 0))\n : 0,\n trackInventory: false,\n quantity: 0,\n productType: 'ticket',\n behaviorConfigs: {\n ticketed: {\n enabled: true,\n eventDate: date || undefined,\n eventStartTime: startTime || undefined,\n eventEndTime: endTime || undefined,\n venueCapacity: capacity ?? undefined,\n venueName: venueName.trim() || undefined,\n venueAddress: venueAddress.trim() || undefined,\n onlineEventUrl: onlineUrl.trim() || undefined,\n tiers: priced.map((t) => ({\n id: t.id,\n name: t.name.trim() || 'Ticket',\n price: t.price ?? 0,\n quantity: t.quantity ?? 0,\n })),\n },\n },\n intent: 'event-tickets',\n },\n 'products',\n );\n\n toast({ title: 'Event created', description: 'It is on your list.' });\n /**\n * BACK TO EVENTS, not into the product editor.\n *\n * John, 2026-09-03: *\"after creating an event why does it go to\n * products, the nav changes to product, the back button shows\n * products.\"* It landed on `/dashboard/catalog/<id>` because that is\n * where the editor lands, and the editor is where the rest of a\n * listing's settings live — but arriving there rewrites the whole frame\n * around a merchant who has just finished a task: the rail lights\n * Products, the breadcrumb says Products, and the thing they made is\n * nowhere on screen.\n *\n * The list is where it IS. It shows up there, dated, with its tickets\n * counted, which is the confirmation the toast is only claiming.\n */\n router.replace('/dashboard/events');\n } catch (err) {\n setError(\n err instanceof Error ? err.message : 'Could not create that event.',\n );\n setSaving(false);\n }\n }, [\n canSave,\n capacity,\n date,\n description,\n endTime,\n isPublic,\n onlineUrl,\n poster,\n router,\n save,\n startTime,\n tenant?.slug,\n tiers,\n title,\n toast,\n venueAddress,\n venueName,\n ]);\n\n const headerActions = useMemo(\n () => (\n <>\n <Button variant=\"secondary\" onClick={() => router.back()}>\n Cancel\n </Button>\n <Button disabled={!canSave} onClick={() => void onCreate()}>\n {saving ? 'Creating…' : 'Create event'}\n </Button>\n </>\n ),\n [canSave, onCreate, router, saving],\n );\n\n return (\n <DetailPage\n /* NO TITLE — John, 2026-09-03: *\"the page needs no title since we have\n the breadcrumb.\"* The trail already ends on \"New event\" a line above,\n so the heading was the same two words twice, forty pixels apart, and\n the second copy pushed the form down for nothing. */\n breadcrumbs={[\n {\n label: 'Events',\n href: '/dashboard/events',\n icon: <CalendarRange />,\n },\n { label: 'New event' },\n ]}\n /* THE COMMIT IS ON THE HEADER ROW — John, 2026-09-03: *\"the create event\n button should be at the top of the page, aligned with the\n breadcrumb.\"* Every other editor in the app saves from there, and on a\n form this tall the button sat below the fold before a merchant had\n typed anything: the one control that finishes the job was the one you\n had to go looking for. It is why `DetailPage` takes `actions`. */\n actions={headerActions}\n mobileActions={headerActions}\n >\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 <ErrorBanner>{error}</ErrorBanner>\n\n <Card>\n {/* The name is the field, not a labelled box under a heading. Luma puts\n it at display size for a reason: it is the only thing a merchant has\n certainly decided before opening the form. */}\n <Input\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"Event name\"\n aria-label=\"Event name\"\n name=\"event-name\"\n className=\"!text-2xl\"\n />\n </Card>\n\n <Card>\n <p className=\"mb-3 flex items-center gap-2 text-sm font-medium text-[var(--text-secondary)]\">\n <CalendarDays className=\"size-4\" />\n When\n </p>\n <div className=\"flex flex-wrap items-end gap-3\">\n <div className=\"min-w-[180px] flex-1\">\n <DateField\n value={date}\n onChange={setDate}\n name=\"event-date\"\n aria-label=\"Event date\"\n />\n </div>\n <div className=\"w-32\">\n <TimeField\n value={startTime}\n onChange={setStartTime}\n name=\"event-start\"\n aria-label=\"Start time\"\n />\n </div>\n <div className=\"w-32\">\n <TimeField\n value={endTime}\n onChange={setEndTime}\n name=\"event-end\"\n aria-label=\"End time\"\n />\n </div>\n </div>\n {/* A date is not required to save, which is the whole reason the Events\n list has an All that shows undated listings: a merchant blocks out\n the idea before the venue confirms. */}\n </Card>\n\n <Card>\n <p className=\"mb-3 flex items-center gap-2 text-sm font-medium text-[var(--text-secondary)]\">\n <MapPin className=\"size-4\" />\n Where\n </p>\n <div className=\"space-y-3\">\n <Input\n label=\"Venue\"\n value={venueName}\n onChange={(e) => setVenueName(e.target.value)}\n placeholder=\"The Grand Junction Hall\"\n name=\"venue-name\"\n />\n <Input\n label=\"Address\"\n value={venueAddress}\n onChange={(e) => setVenueAddress(e.target.value)}\n placeholder=\"Street, area, city\"\n name=\"venue-address\"\n />\n {/* One field, both kinds of event — Luma's \"offline location or\n virtual link\" as two rows rather than a mode switch, because a\n hybrid night has both and a toggle would make somebody choose. */}\n <Input\n label=\"Or a link, for an online event\"\n value={onlineUrl}\n onChange={(e) => setOnlineUrl(e.target.value)}\n placeholder=\"https://\"\n name=\"online-url\"\n />\n </div>\n </Card>\n\n <Card>\n <p className=\"mb-3 flex items-center gap-2 text-sm font-medium text-[var(--text-secondary)]\">\n <Ticket className=\"size-4\" />\n Tickets\n </p>\n <TicketTiers\n tiers={tiers}\n onChange={setTiers}\n currencySymbol={currencySymbol}\n />\n </Card>\n\n <Card>\n <Textarea\n label=\"About this event\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"What is happening, who it is for, what to bring.\"\n rows={4}\n name=\"event-description\"\n />\n </Card>\n </div>\n\n {/* THE POSTER SITS RIGHT — John, 2026-09-03: *\"the event image should be\n at the right side not the left.\"* Luma puts it left and this is not\n Luma: every other editor in this app keeps its media in a right-hand\n rail, and a merchant arriving from the product editor should not have\n to find it on the other side of the screen. Below `lg` it stacks\n under the form, where a picture is a finishing touch rather than the\n first question. */}\n {/* AND THE RAIL CARRIES MORE THAN THE PICTURE — John, 2026-09-03:\n *\"look at the creation of products page, we have more stuff on the\n right column.\"* The product editor puts the picture, the status and\n the organising there and keeps the left for what the thing IS,\n which is the shape a merchant coming from that screen already\n knows. So the poster travels with the two decisions that are about\n an event's REACH rather than its content: how many can come, and\n who can see it. */}\n <div className=\"space-y-4 lg:sticky lg:top-4 lg:self-start\">\n <EventPoster value={poster} onChange={setPoster} />\n\n <Card>\n <p className=\"mb-3 text-sm font-medium text-[var(--text-secondary)]\">\n Who and how many\n </p>\n <div className=\"flex flex-wrap items-end gap-4\">\n <div className=\"w-40\">\n <NumberField\n label=\"Capacity\"\n value={capacity}\n onChange={setCapacity}\n min={0}\n placeholder=\"Unlimited\"\n />\n </div>\n {/* Chosen, not fired, so `OptionButton` rather than a Button with\n a conditional variant — and a pair rather than a switch,\n because public and private are two named things and a toggle\n would make a merchant work out which way is which. */}\n <div className=\"flex gap-2\">\n <OptionButton\n selected={isPublic}\n onClick={() => setIsPublic(true)}\n >\n Public\n </OptionButton>\n <OptionButton\n selected={!isPublic}\n onClick={() => setIsPublic(false)}\n >\n Private\n </OptionButton>\n </div>\n </div>\n <p className=\"mt-2 text-sm text-[var(--text-tertiary)]\">\n {isPublic\n ? 'On your website, and anyone with the link can buy.'\n : 'Off your website. Only people you send the link to will find it.'}\n </p>\n </Card>\n </div>\n </div>\n </DetailPage>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { ImagePlus, Loader2 } from "lucide-react";
|
|
5
|
+
import { Button, FileInput } from "@withpotter/ui-kit";
|
|
6
|
+
import { uploadImage } from "@withpotter/lib/media-service";
|
|
7
|
+
function EventPoster({
|
|
8
|
+
value,
|
|
9
|
+
onChange
|
|
10
|
+
}) {
|
|
11
|
+
const [uploading, setUploading] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const onFiles = async (files) => {
|
|
14
|
+
const file = files[0];
|
|
15
|
+
if (!file) return;
|
|
16
|
+
setUploading(true);
|
|
17
|
+
setError(null);
|
|
18
|
+
try {
|
|
19
|
+
const result = await uploadImage(file);
|
|
20
|
+
onChange({
|
|
21
|
+
url: result.url,
|
|
22
|
+
type: "image",
|
|
23
|
+
width: result.width,
|
|
24
|
+
height: result.height
|
|
25
|
+
});
|
|
26
|
+
} catch (err) {
|
|
27
|
+
setError(err instanceof Error ? err.message : "That upload failed.");
|
|
28
|
+
} finally {
|
|
29
|
+
setUploading(false);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs(
|
|
34
|
+
"label",
|
|
35
|
+
{
|
|
36
|
+
htmlFor: "event-poster",
|
|
37
|
+
className: "surface-interactive grid aspect-square w-full cursor-pointer place-items-center overflow-hidden",
|
|
38
|
+
children: [
|
|
39
|
+
/* @__PURE__ */ jsx(
|
|
40
|
+
FileInput,
|
|
41
|
+
{
|
|
42
|
+
id: "event-poster",
|
|
43
|
+
accept: "image/*",
|
|
44
|
+
onFiles: (files) => void onFiles(files),
|
|
45
|
+
"aria-label": "Event poster"
|
|
46
|
+
}
|
|
47
|
+
),
|
|
48
|
+
/* @__PURE__ */ jsx("div", { className: "grid size-full place-items-center", children: value?.url ? (
|
|
49
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
50
|
+
/* @__PURE__ */ jsx("img", { src: value.url, alt: "", className: "size-full object-cover" })
|
|
51
|
+
) : /* @__PURE__ */ jsxs("span", { className: "flex flex-col items-center gap-2 text-sm text-[var(--text-tertiary)]", children: [
|
|
52
|
+
uploading ? /* @__PURE__ */ jsx(Loader2, { className: "size-5 animate-spin" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "size-5" }),
|
|
53
|
+
uploading ? "Uploading\u2026" : "Add a poster"
|
|
54
|
+
] }) })
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
),
|
|
58
|
+
value?.url && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => onChange(null), children: "Remove poster" }),
|
|
59
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--error)]", children: error })
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
EventPoster
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=event-poster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/dashboard/%28bookings%29/events/new/_components/event-poster.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { ImagePlus, Loader2 } from 'lucide-react';\nimport { Button, FileInput } from '@withpotter/ui-kit';\nimport { uploadImage } from '@withpotter/lib/media-service';\nimport type { ProductMedia } from '@withpotter/lib/product-types';\n\n/**\n * THE POSTER — John, 2026-09-03: *\"where's the second column that should have\n * the event poster.\"*\n *\n * Luma's composer is a poster on the left and the event on the right, and the\n * poster is not decoration there: it is what the event will look like when\n * somebody is sent the link, which is the only thing most guests will ever see\n * of it. A form that asks for it last treats it as an afterthought, and the\n * merchants who most need to sell tickets are exactly the ones with a flyer\n * already made.\n *\n * SQUARE, and that is a decision. Luma's is square, Partiful's is square, and\n * an event flyer coming out of Canva or a phone is square far more often than\n * it is 16:9 — the ratio the rest of our catalogue assumes for a product\n * photograph. Cropping a poster to a letterbox cuts the date off the bottom.\n *\n * It uploads on choose rather than on save, so the picture is on screen while\n * the merchant is still typing the name. The cost is an orphaned upload if\n * they abandon the form, which is the trade every editor in the app already\n * makes.\n */\nexport function EventPoster({\n value,\n onChange,\n}: {\n value: ProductMedia | null;\n onChange: (media: ProductMedia | null) => void;\n}) {\n const [uploading, setUploading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const onFiles = async (files: File[]) => {\n const file = files[0];\n if (!file) return;\n setUploading(true);\n setError(null);\n try {\n const result = await uploadImage(file);\n onChange({\n url: result.url,\n type: 'image',\n width: result.width,\n height: result.height,\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'That upload failed.');\n } finally {\n setUploading(false);\n }\n };\n\n return (\n <div className=\"space-y-2\">\n {/* A <label> is the visible control, so `FileInput` stays reachable and\n labelable rather than display:none — see its own note. */}\n <label\n htmlFor=\"event-poster\"\n className=\"surface-interactive grid aspect-square w-full cursor-pointer place-items-center overflow-hidden\"\n >\n <FileInput\n id=\"event-poster\"\n accept=\"image/*\"\n onFiles={(files) => void onFiles(files)}\n aria-label=\"Event poster\"\n />\n <div className=\"grid size-full place-items-center\">\n {value?.url ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img src={value.url} alt=\"\" className=\"size-full object-cover\" />\n ) : (\n <span className=\"flex flex-col items-center gap-2 text-sm text-[var(--text-tertiary)]\">\n {uploading ? (\n <Loader2 className=\"size-5 animate-spin\" />\n ) : (\n <ImagePlus className=\"size-5\" />\n )}\n {uploading ? 'Uploading…' : 'Add a poster'}\n </span>\n )}\n </div>\n </label>\n\n {value?.url && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => onChange(null)}>\n Remove poster\n </Button>\n )}\n {error && <p className=\"text-sm text-[var(--error)]\">{error}</p>}\n </div>\n );\n}\n"],"mappings":";AAmEQ,cAWI,YAXJ;AAjER,SAAS,gBAAgB;AACzB,SAAS,WAAW,eAAe;AACnC,SAAS,QAAQ,iBAAiB;AAClC,SAAS,mBAAmB;AAwBrB,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,QAAM,UAAU,OAAO,UAAkB;AACvC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,CAAC,KAAM;AACX,iBAAa,IAAI;AACjB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,MAAM,YAAY,IAAI;AACrC,eAAS;AAAA,QACP,KAAK,OAAO;AAAA,QACZ,MAAM;AAAA,QACN,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,qBAAqB;AAAA,IACrE,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAU,aAGb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,WAAU;AAAA,QAEV;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,QAAO;AAAA,cACP,SAAS,CAAC,UAAU,KAAK,QAAQ,KAAK;AAAA,cACtC,cAAW;AAAA;AAAA,UACb;AAAA,UACA,oBAAC,SAAI,WAAU,qCACZ,iBAAO;AAAA;AAAA,YAEN,oBAAC,SAAI,KAAK,MAAM,KAAK,KAAI,IAAG,WAAU,0BAAyB;AAAA,cAE/D,qBAAC,UAAK,WAAU,wEACb;AAAA,wBACC,oBAAC,WAAQ,WAAU,uBAAsB,IAEzC,oBAAC,aAAU,WAAU,UAAS;AAAA,YAE/B,YAAY,oBAAe;AAAA,aAC9B,GAEJ;AAAA;AAAA;AAAA,IACF;AAAA,IAEC,OAAO,OACN,oBAAC,UAAO,MAAK,MAAK,SAAQ,SAAQ,SAAS,MAAM,SAAS,IAAI,GAAG,2BAEjE;AAAA,IAED,SAAS,oBAAC,OAAE,WAAU,+BAA+B,iBAAM;AAAA,KAC9D;AAEJ;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Plus, X } from "lucide-react";
|
|
4
|
+
import { Button, Input, NumberField } from "@withpotter/ui-kit";
|
|
5
|
+
const newTier = () => ({
|
|
6
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
7
|
+
name: "",
|
|
8
|
+
price: null,
|
|
9
|
+
quantity: null
|
|
10
|
+
});
|
|
11
|
+
function TicketTiers({
|
|
12
|
+
tiers,
|
|
13
|
+
onChange,
|
|
14
|
+
currencySymbol
|
|
15
|
+
}) {
|
|
16
|
+
const update = (id, patch) => onChange(tiers.map((t) => t.id === id ? { ...t, ...patch } : t));
|
|
17
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
18
|
+
tiers.map((tier, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
19
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
20
|
+
Input,
|
|
21
|
+
{
|
|
22
|
+
label: index === 0 ? "Ticket" : void 0,
|
|
23
|
+
value: tier.name,
|
|
24
|
+
onChange: (e) => update(tier.id, { name: e.target.value }),
|
|
25
|
+
placeholder: index === 0 ? "Regular" : "VIP, Table of ten\u2026",
|
|
26
|
+
name: `tier-name-${tier.id}`
|
|
27
|
+
}
|
|
28
|
+
) }),
|
|
29
|
+
/* @__PURE__ */ jsx("div", { className: "w-32", children: /* @__PURE__ */ jsx(
|
|
30
|
+
NumberField,
|
|
31
|
+
{
|
|
32
|
+
label: index === 0 ? `Price (${currencySymbol})` : void 0,
|
|
33
|
+
value: tier.price,
|
|
34
|
+
onChange: (value) => update(tier.id, { price: value }),
|
|
35
|
+
min: 0
|
|
36
|
+
}
|
|
37
|
+
) }),
|
|
38
|
+
/* @__PURE__ */ jsx("div", { className: "w-28", children: /* @__PURE__ */ jsx(
|
|
39
|
+
NumberField,
|
|
40
|
+
{
|
|
41
|
+
label: index === 0 ? "How many" : void 0,
|
|
42
|
+
value: tier.quantity,
|
|
43
|
+
onChange: (value) => update(tier.id, { quantity: value }),
|
|
44
|
+
min: 0
|
|
45
|
+
}
|
|
46
|
+
) }),
|
|
47
|
+
/* @__PURE__ */ jsx(
|
|
48
|
+
Button,
|
|
49
|
+
{
|
|
50
|
+
size: "icon",
|
|
51
|
+
variant: "ghost",
|
|
52
|
+
"aria-label": `Remove ${tier.name || "ticket"}`,
|
|
53
|
+
disabled: tiers.length === 1,
|
|
54
|
+
onClick: () => onChange(tiers.filter((t) => t.id !== tier.id)),
|
|
55
|
+
children: /* @__PURE__ */ jsx(X, {})
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
] }, tier.id)),
|
|
59
|
+
/* @__PURE__ */ jsx(
|
|
60
|
+
Button,
|
|
61
|
+
{
|
|
62
|
+
size: "sm",
|
|
63
|
+
variant: "secondary",
|
|
64
|
+
icon: /* @__PURE__ */ jsx(Plus, {}),
|
|
65
|
+
onClick: () => onChange([...tiers, newTier()]),
|
|
66
|
+
children: "Add ticket type"
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
] });
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
TicketTiers,
|
|
73
|
+
newTier
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=ticket-tiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/dashboard/%28bookings%29/events/new/_components/ticket-tiers.tsx"],"sourcesContent":["'use client';\n\nimport { Plus, X } from 'lucide-react';\nimport { Button, Input, NumberField } from '@withpotter/ui-kit';\n\nexport interface Tier {\n id: string;\n name: string;\n price: number | null;\n quantity: number | null;\n}\n\nexport const newTier = (): Tier => ({\n id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,\n name: '',\n price: null,\n quantity: null,\n});\n\n/**\n * WHAT YOU CAN BUY, AND HOW MANY OF IT — John, 2026-09-03: *\"when creating you\n * should be able to create ticket levels too, regular, VIP etc, depending on\n * what you want to name, and tables.\"*\n *\n * The names are typed, never chosen from a list. Regular and VIP are the two\n * everyone reaches for and they are the reason a fixed list is wrong: the third\n * one is always somebody's own — Early bird, Table of ten, Gold circle,\n * Owambe. A dropdown of our words would make the common case one press faster\n * and the real case impossible, which is the wrong trade on the field that\n * carries the price.\n *\n * A TABLE IS A TIER. It is a name, a price and a number available, which is the\n * shape already here; what makes it a table is what the merchant calls it and\n * what they charge. Modelling seating as its own thing would buy a floor plan\n * nobody asked for and split the one number a venue watches — how many are\n * left — across two systems.\n *\n * Quantity is per tier and capacity is the sum, which is why the room's own\n * capacity is not asked for here: seats held back were never for sale, and a\n * venue that sold 40 of a 500-seat room while only offering 60 needs the 60.\n */\nexport function TicketTiers({\n tiers,\n onChange,\n currencySymbol,\n}: {\n tiers: Tier[];\n onChange: (tiers: Tier[]) => void;\n currencySymbol: string;\n}) {\n const update = (id: string, patch: Partial<Tier>) =>\n onChange(tiers.map((t) => (t.id === id ? { ...t, ...patch } : t)));\n\n return (\n <div className=\"space-y-2\">\n {tiers.map((tier, index) => (\n <div key={tier.id} className=\"flex items-end gap-2\">\n <div className=\"min-w-0 flex-1\">\n <Input\n label={index === 0 ? 'Ticket' : undefined}\n value={tier.name}\n onChange={(e) => update(tier.id, { name: e.target.value })}\n placeholder={index === 0 ? 'Regular' : 'VIP, Table of ten…'}\n name={`tier-name-${tier.id}`}\n />\n </div>\n <div className=\"w-32\">\n <NumberField\n label={index === 0 ? `Price (${currencySymbol})` : undefined}\n value={tier.price}\n onChange={(value) => update(tier.id, { price: value })}\n min={0}\n />\n </div>\n <div className=\"w-28\">\n <NumberField\n label={index === 0 ? 'How many' : undefined}\n value={tier.quantity}\n onChange={(value) => update(tier.id, { quantity: value })}\n min={0}\n />\n </div>\n {/* The last one cannot go: an event with no tickets is not on sale,\n and a row you can empty but not remove is clearer than an empty\n list with an Add button where the tickets used to be. */}\n <Button\n size=\"icon\"\n variant=\"ghost\"\n aria-label={`Remove ${tier.name || 'ticket'}`}\n disabled={tiers.length === 1}\n onClick={() => onChange(tiers.filter((t) => t.id !== tier.id))}\n >\n <X />\n </Button>\n </div>\n ))}\n\n <Button\n size=\"sm\"\n variant=\"secondary\"\n icon={<Plus />}\n onClick={() => onChange([...tiers, newTier()])}\n >\n Add ticket type\n </Button>\n </div>\n );\n}\n"],"mappings":";AAwDQ,SAEI,KAFJ;AAtDR,SAAS,MAAM,SAAS;AACxB,SAAS,QAAQ,OAAO,mBAAmB;AASpC,MAAM,UAAU,OAAa;AAAA,EAClC,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,EAC3D,MAAM;AAAA,EACN,OAAO;AAAA,EACP,UAAU;AACZ;AAwBO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,SAAS,CAAC,IAAY,UAC1B,SAAS,MAAM,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,GAAG,MAAM,IAAI,CAAE,CAAC;AAEnE,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,UAAM,IAAI,CAAC,MAAM,UAChB,qBAAC,SAAkB,WAAU,wBAC3B;AAAA,0BAAC,SAAI,WAAU,kBACb;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,UAAU,IAAI,WAAW;AAAA,UAChC,OAAO,KAAK;AAAA,UACZ,UAAU,CAAC,MAAM,OAAO,KAAK,IAAI,EAAE,MAAM,EAAE,OAAO,MAAM,CAAC;AAAA,UACzD,aAAa,UAAU,IAAI,YAAY;AAAA,UACvC,MAAM,aAAa,KAAK,EAAE;AAAA;AAAA,MAC5B,GACF;AAAA,MACA,oBAAC,SAAI,WAAU,QACb;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,UAAU,IAAI,UAAU,cAAc,MAAM;AAAA,UACnD,OAAO,KAAK;AAAA,UACZ,UAAU,CAAC,UAAU,OAAO,KAAK,IAAI,EAAE,OAAO,MAAM,CAAC;AAAA,UACrD,KAAK;AAAA;AAAA,MACP,GACF;AAAA,MACA,oBAAC,SAAI,WAAU,QACb;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,UAAU,IAAI,aAAa;AAAA,UAClC,OAAO,KAAK;AAAA,UACZ,UAAU,CAAC,UAAU,OAAO,KAAK,IAAI,EAAE,UAAU,MAAM,CAAC;AAAA,UACxD,KAAK;AAAA;AAAA,MACP,GACF;AAAA,MAIA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,cAAY,UAAU,KAAK,QAAQ,QAAQ;AAAA,UAC3C,UAAU,MAAM,WAAW;AAAA,UAC3B,SAAS,MAAM,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,CAAC;AAAA,UAE7D,8BAAC,KAAE;AAAA;AAAA,MACL;AAAA,SArCQ,KAAK,EAsCf,CACD;AAAA,IAED;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,MAAM,oBAAC,QAAK;AAAA,QACZ,SAAS,MAAM,SAAS,CAAC,GAAG,OAAO,QAAQ,CAAC,CAAC;AAAA,QAC9C;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { EventComposer } from "./_components/event-composer";
|
|
4
|
+
function NewEventPage() {
|
|
5
|
+
return /* @__PURE__ */ jsx(EventComposer, {});
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
NewEventPage as default
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/events/new/page.tsx"],"sourcesContent":["'use client';\n\nimport { EventComposer } from './_components/event-composer';\n\n/**\n * Making an event, in a form that is about an event.\n *\n * John, 2026-09-03, three times: *\"when I click on create it goes to create\n * products\"*, then *\"if I click on create event it takes me to the products\n * page\"*, then *\"I try to create an event and it still looks like I'm trying\n * to create a product, and the breadcrumbs still say products.\"*\n *\n * The first two were the scope and the address. This is the third and it is\n * the real one: the product editor asks about Price, Track inventory and Low\n * stock alert before it asks when the night is, and folds the date, the room\n * and the tickets into a section called \"How it sells\". A merchant filling\n * that in is being asked about a warehouse.\n *\n * A CREATE SCREEN IS A DETAIL SCREEN — house rule — so it keeps the sidebar\n * and carries breadcrumbs, and those breadcrumbs say Events, which is where\n * the merchant came from and where the record will appear. They said Products\n * because the editor resolves its noun through the merchant's own\n * `moduleLabels`, and this workspace has renamed the catalogue module to\n * Products; on a screen that exists only to make an event, the archetype's\n * word is the true one.\n *\n * The editor is still there and still better for everything else: variants,\n * collections, gating, custom fields, media galleries. An event made here\n * opens in it, because this writes the same `ticketed` behaviour to the same\n * record through the same dispatch. One record, one save path, two doors.\n */\nexport default function NewEventPage() {\n return <EventComposer />;\n}\n"],"mappings":";AAgCS;AA9BT,SAAS,qBAAqB;AA6Bf,SAAR,eAAgC;AACrC,SAAO,oBAAC,iBAAc;AACxB;","names":[]}
|