@withpotter/pages-bookings 0.13.0-next.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/(bookings)/attendees/page.js +8 -0
- package/dist/dashboard/(bookings)/attendees/page.js.map +1 -0
- package/dist/dashboard/(bookings)/availability/_components/availability-screen.js +714 -0
- package/dist/dashboard/(bookings)/availability/_components/availability-screen.js.map +1 -0
- package/dist/dashboard/(bookings)/availability/_components/month-availability.js +130 -0
- package/dist/dashboard/(bookings)/availability/_components/month-availability.js.map +1 -0
- package/dist/dashboard/(bookings)/availability/page.js +10 -0
- package/dist/dashboard/(bookings)/availability/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/assign-resource-modal.js +133 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/assign-resource-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-constants.js +26 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-constants.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-desktop-layout.js +194 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-desktop-layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-layout-props.js +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-layout-props.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-lede.js +189 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-lede.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-meta.js +38 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-meta.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-layout.js +315 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-screen.js +94 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-screen.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-money.js +54 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-money.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-split.js +71 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-split.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancel-booking-modal.js +102 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancel-booking-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancellation-notice.js +33 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancellation-notice.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/customer-info-card.js +65 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/customer-info-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/index.js +23 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/intake-answers-card.js +19 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/intake-answers-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/refund-booking-modal.js +156 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/refund-booking-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/reschedule-booking-modal.js +120 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/reschedule-booking-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/security-deposit-card.js +63 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/security-deposit-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/settle-deposit-modal.js +141 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/settle-deposit-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/ticket-row-actions.js +49 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/ticket-row-actions.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/waiting-queue-notice.js +42 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/waiting-queue-notice.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_components.js +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/index.js +19 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/use-booking-detail-data.js +352 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/use-booking-detail-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks.js +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/layout.js +33 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/page.js +356 -0
- package/dist/dashboard/(bookings)/bookings/[bookingId]/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/appointment-calendar-layout.js +34 -0
- package/dist/dashboard/(bookings)/bookings/_components/appointment-calendar-layout.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-constants.js +92 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-constants.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-tab-hints.js +32 -0
- package/dist/dashboard/(bookings)/bookings/_components/booking-tab-hints.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-filter-bar.js +131 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-filter-bar.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-mobile-controls.js +119 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-mobile-controls.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-screen.js +414 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-screen.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-table.js +192 -0
- package/dist/dashboard/(bookings)/bookings/_components/bookings-table.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/cannot-take-bookings.js +34 -0
- package/dist/dashboard/(bookings)/bookings/_components/cannot-take-bookings.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/demo-data.js +176 -0
- package/dist/dashboard/(bookings)/bookings/_components/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/index.js +9 -0
- package/dist/dashboard/(bookings)/bookings/_components/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/booking-card.js +126 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/booking-card.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/demo-data.js +142 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/handover.js +128 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/handover.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/index.js +17 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/past-band.js +69 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/past-band.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/resource-board.js +111 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/resource-board.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/today-band.js +61 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/today-band.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/upcoming-band.js +113 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/upcoming-band.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/waiting-queue.js +62 -0
- package/dist/dashboard/(bookings)/bookings/_components/today/waiting-queue.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today-board.js +192 -0
- package/dist/dashboard/(bookings)/bookings/_components/today-board.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_components/today.js +1 -0
- package/dist/dashboard/(bookings)/bookings/_components.js +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/index.js +5 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/index.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-bookings-data.js +331 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-bookings-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-today-data.js +309 -0
- package/dist/dashboard/(bookings)/bookings/_hooks/use-today-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/_hooks.js +1 -0
- package/dist/dashboard/(bookings)/bookings/_lib/booking-link.js +13 -0
- package/dist/dashboard/(bookings)/bookings/_lib/booking-link.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/attendee-cards.js +60 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/attendee-cards.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/qr-scanner-modal.js +225 -0
- package/dist/dashboard/(bookings)/bookings/attendees/_components/qr-scanner-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/demo-data.js +195 -0
- package/dist/dashboard/(bookings)/bookings/attendees/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/attendees/page.js +341 -0
- package/dist/dashboard/(bookings)/bookings/attendees/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/new/page.js +129 -0
- package/dist/dashboard/(bookings)/bookings/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/page.js +10 -0
- package/dist/dashboard/(bookings)/bookings/page.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/save-buttons.js +36 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/save-buttons.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/session-creator.js +386 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_components/session-creator.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_lib/session-payload.js +62 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/_lib/session-payload.js.map +1 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/page.js +10 -0
- package/dist/dashboard/(bookings)/bookings/sessions/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-nav.js +84 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-nav.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-range.js +84 -0
- package/dist/dashboard/(bookings)/calendar/_components/calendar-range.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/day-board-view.js +279 -0
- package/dist/dashboard/(bookings)/calendar/_components/day-board-view.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/event-detail-modal.js +113 -0
- package/dist/dashboard/(bookings)/calendar/_components/event-detail-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/format.js +22 -0
- package/dist/dashboard/(bookings)/calendar/_components/format.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/month-grid.js +109 -0
- package/dist/dashboard/(bookings)/calendar/_components/month-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/resource-day-grid.js +325 -0
- package/dist/dashboard/(bookings)/calendar/_components/resource-day-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/slot-action-sheet.js +209 -0
- package/dist/dashboard/(bookings)/calendar/_components/slot-action-sheet.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-block-drag.js +77 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-block-drag.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-calendar-nav.js +69 -0
- package/dist/dashboard/(bookings)/calendar/_components/use-calendar-nav.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/week-grid.js +202 -0
- package/dist/dashboard/(bookings)/calendar/_components/week-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/_components/year-grid.js +111 -0
- package/dist/dashboard/(bookings)/calendar/_components/year-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar/page.js +8 -0
- package/dist/dashboard/(bookings)/calendar/page.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feed-modal.js +126 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feed-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feeds-section.js +99 -0
- package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feeds-section.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/demo-data.js +64 -0
- package/dist/dashboard/(bookings)/calendar-sync/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/calendar-sync/page.js +15 -0
- package/dist/dashboard/(bookings)/calendar-sync/page.js.map +1 -0
- package/dist/dashboard/(bookings)/check-in/page.js +252 -0
- package/dist/dashboard/(bookings)/check-in/page.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-attendees.js +108 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-attendees.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-share.js +73 -0
- package/dist/dashboard/(bookings)/events/[eventId]/_components/event-share.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/layout.js +10 -0
- package/dist/dashboard/(bookings)/events/[eventId]/layout.js.map +1 -0
- package/dist/dashboard/(bookings)/events/[eventId]/page.js +247 -0
- package/dist/dashboard/(bookings)/events/[eventId]/page.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_components/event-grid.js +44 -0
- package/dist/dashboard/(bookings)/events/_components/event-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_components/event-poster.js +17 -0
- package/dist/dashboard/(bookings)/events/_components/event-poster.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_hooks/use-events-data.js +55 -0
- package/dist/dashboard/(bookings)/events/_hooks/use-events-data.js.map +1 -0
- package/dist/dashboard/(bookings)/events/_lib/event-facts.js +68 -0
- package/dist/dashboard/(bookings)/events/_lib/event-facts.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-composer.js +295 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-composer.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-poster.js +65 -0
- package/dist/dashboard/(bookings)/events/new/_components/event-poster.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/_components/ticket-tiers.js +75 -0
- package/dist/dashboard/(bookings)/events/new/_components/ticket-tiers.js.map +1 -0
- package/dist/dashboard/(bookings)/events/new/page.js +10 -0
- package/dist/dashboard/(bookings)/events/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/events/page.js +214 -0
- package/dist/dashboard/(bookings)/events/page.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/layout.js +36 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/layout.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/page.js +351 -0
- package/dist/dashboard/(bookings)/resources/[resourceId]/page.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/block-time-modal.js +130 -0
- package/dist/dashboard/(bookings)/resources/_components/block-time-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/category-card.js +74 -0
- package/dist/dashboard/(bookings)/resources/_components/category-card.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/category-form-dialog.js +197 -0
- package/dist/dashboard/(bookings)/resources/_components/category-form-dialog.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/day-editor.js +194 -0
- package/dist/dashboard/(bookings)/resources/_components/day-editor.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/group-dialog.js +170 -0
- package/dist/dashboard/(bookings)/resources/_components/group-dialog.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/group-header.js +90 -0
- package/dist/dashboard/(bookings)/resources/_components/group-header.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/index.js +22 -0
- package/dist/dashboard/(bookings)/resources/_components/index.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/mobile-category-form-sheet.js +256 -0
- package/dist/dashboard/(bookings)/resources/_components/mobile-category-form-sheet.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-form.js +420 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-form.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-picker.js +144 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-picker.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-type-picker-empty.js +109 -0
- package/dist/dashboard/(bookings)/resources/_components/resource-type-picker-empty.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-empty.js +103 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-empty.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-grid.js +160 -0
- package/dist/dashboard/(bookings)/resources/_components/roster-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-editor.js +244 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-editor.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-timezone-field.js +25 -0
- package/dist/dashboard/(bookings)/resources/_components/schedule-timezone-field.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/solo-schedule-view.js +146 -0
- package/dist/dashboard/(bookings)/resources/_components/solo-schedule-view.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/team-onboarding-dialog.js +161 -0
- package/dist/dashboard/(bookings)/resources/_components/team-onboarding-dialog.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components/week-grid.js +175 -0
- package/dist/dashboard/(bookings)/resources/_components/week-grid.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_components.js +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/index.js +8 -0
- package/dist/dashboard/(bookings)/resources/_hooks/index.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-resource-categories-data.js +86 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-resource-categories-data.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster-page.js +138 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster-page.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster.js +182 -0
- package/dist/dashboard/(bookings)/resources/_hooks/use-roster.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_hooks.js +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/presets.js +147 -0
- package/dist/dashboard/(bookings)/resources/_lib/presets.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/schedule-timezone.js +17 -0
- package/dist/dashboard/(bookings)/resources/_lib/schedule-timezone.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/shapes.js +105 -0
- package/dist/dashboard/(bookings)/resources/_lib/shapes.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/_lib/week.js +122 -0
- package/dist/dashboard/(bookings)/resources/_lib/week.js.map +1 -0
- package/dist/dashboard/(bookings)/resources/page.js +391 -0
- package/dist/dashboard/(bookings)/resources/page.js.map +1 -0
- package/dist/dashboard/(bookings)/schedule/page.js +8 -0
- package/dist/dashboard/(bookings)/schedule/page.js.map +1 -0
- package/dist/dashboard/(bookings)/today/page.js +14 -0
- package/dist/dashboard/(bookings)/today/page.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/_components/demo-data.js +99 -0
- package/dist/dashboard/(bookings)/waitlist/_components/demo-data.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/_components/offer-spot-modal.js +71 -0
- package/dist/dashboard/(bookings)/waitlist/_components/offer-spot-modal.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/_components/waitlist-entry-editor.js +150 -0
- package/dist/dashboard/(bookings)/waitlist/_components/waitlist-entry-editor.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/new/page.js +59 -0
- package/dist/dashboard/(bookings)/waitlist/new/page.js.map +1 -0
- package/dist/dashboard/(bookings)/waitlist/page.js +317 -0
- package/dist/dashboard/(bookings)/waitlist/page.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/calendar/_components/week-grid.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { motion, useReducedMotion } from 'motion/react';\nimport { Plus } from 'lucide-react';\nimport { springs, eases, PRESS_SCALE_LARGE } from '@withpotter/lib/motion';\nimport { cn } from '@withpotter/lib/utils';\nimport type { CalendarEventItem } from '@withpotter/lib/api/types';\nimport { weekOf, parseISO, toISODate } from './calendar-range';\nimport { Button } from '@withpotter/ui-kit/button';\n\nconst HOUR_PX = 48;\n\nfunction clockLabel(h: number) {\n const suffix = h >= 12 ? 'pm' : 'am';\n const hour = h % 12 === 0 ? 12 : h % 12;\n return `${hour}${suffix}`;\n}\n\nfunction hourOf(iso: string) {\n const d = new Date(iso);\n return d.getHours() + d.getMinutes() / 60;\n}\n\nexport interface WeekGridProps {\n anchor: string;\n events: CalendarEventItem[];\n openHour?: number;\n closeHour?: number;\n onBookSlot: (date: string, startTime: string, endTime: string) => void;\n onSelectEvent: (event: CalendarEventItem) => void;\n onOpenDay: (date: string) => void;\n}\n\n/**\n * Seven days of time, drawn in the same language as the day board.\n *\n * The day board can hold resource columns because it only spans one day; a week\n * cannot, so here the columns are days and a booking carries its resource in\n * the label instead. The rule that survives is the important one: free time is\n * still the pressable element, not the leftover space between blocks.\n */\nexport function WeekGrid({\n anchor,\n events,\n openHour = 8,\n closeHour = 20,\n onBookSlot,\n onSelectEvent,\n onOpenDay,\n}: WeekGridProps) {\n const reduce = useReducedMotion();\n const days = useMemo(() => weekOf(anchor), [anchor]);\n const hours = useMemo(() => {\n const out: number[] = [];\n for (let h = openHour; h < closeHour; h += 1) out.push(h);\n return out;\n }, [openHour, closeHour]);\n\n const byDay = useMemo(() => {\n const map = new Map<string, CalendarEventItem[]>();\n for (const d of days) map.set(d, []);\n for (const e of events) {\n if (e.isAllDay) continue;\n const d = e.start.slice(0, 10);\n if (map.has(d)) map.get(d)!.push(e);\n }\n return map;\n }, [events, days]);\n\n const gridHeight = hours.length * HOUR_PX;\n const today = toISODate(new Date());\n\n const col = {\n hidden: {},\n shown: { transition: { staggerChildren: reduce ? 0 : 0.02 } },\n };\n const blockV = {\n hidden: reduce ? {} : { opacity: 0, y: 6 },\n shown: {\n opacity: 1,\n y: 0,\n transition: { duration: 0.22, ease: eases.settle },\n },\n };\n\n return (\n /* ONE GREY ACROSS THE CALENDAR. The day board is `surface`, the house card\n fill; this drew itself on `--bg-elevated` and the month and year grids on\n something else again, so stepping Day → Week changed the colour of the\n board under you. */\n <div className=\"surface overflow-hidden\">\n {/* THE CALENDAR SCROLLS, NOT THE PAGE. John, 2026-09-10: \"i shodl be\n able to scroll just insdie the calendar when on week view\".\n\n Twelve hours at 48px is 576px of grid, and it sat in the page's own\n flow — so reaching 6pm pushed the day headers, the view switch and the\n page title off the top, and you lost which column was Thursday at\n exactly the moment you were reading down one. One element scrolls both\n ways here; the day row pins to its top and the hour gutter to its\n left, so the two things that say WHERE you are stay put while the\n diary moves under them.\n\n The height is bounded against the viewport rather than the content:\n `15rem` is the shell margin, the page header, the view switch and the\n booking-link strip above it. Below that it is the grid's own height,\n so a short opening day does not leave a well of empty rows. */}\n <div className=\"max-h-[calc(100dvh-15rem)] overflow-auto\">\n <div\n className=\"grid min-w-max\"\n style={{ gridTemplateColumns: `56px repeat(7, minmax(130px, 1fr))` }}\n >\n {/* The corner belongs to both frozen bands, so it outranks both. */}\n <div className=\"sticky left-0 top-0 z-40 border-b border-r border-[var(--border-default)] bg-[var(--card-bg)]\" />\n {days.map((d) => {\n const dt = parseISO(d);\n return (\n /* A HEADER CELL, not a button in a box. `Button variant=\"secondary\"`\n gave each day its own border and radius, so the header read as\n seven pills floating over a grid whose columns they were supposed\n to be labelling — and the pill's edges did not line up with the\n column edges below them. It is still pressable; it just looks\n like what it is. */\n <button\n type=\"button\"\n className=\"sticky top-0 z-30 flex flex-col items-start border-b border-r border-[var(--border-default)] bg-[var(--card-bg)] px-3 py-2 text-left transition-colors last:border-r-0 hover:bg-[var(--interactive-hover)]\"\n key={d}\n onClick={() => onOpenDay(d)}\n >\n <span className=\"block text-3xs uppercase tracking-wide text-[var(--text-muted)]\">\n {dt.toLocaleDateString('en-US', { weekday: 'short' })}\n </span>\n <span\n className={cn(\n 'block text-lg font-semibold tabular-nums',\n d === today\n ? 'text-brand-500'\n : 'text-[var(--text-primary)]',\n )}\n >\n {dt.getDate()}\n </span>\n </button>\n );\n })}\n\n <div\n className=\"sticky left-0 z-20 border-r border-[var(--border-subtle)] bg-[var(--card-bg)]\"\n style={{ height: gridHeight }}\n >\n {hours.map((h) => (\n <div\n key={h}\n className=\"relative border-b border-[var(--border-subtle)] last:border-b-0\"\n style={{ height: HOUR_PX }}\n >\n {/* The first label has no line above it to straddle, and half of\n it was being cut off by the card's own edge. */}\n <span\n className={cn(\n 'absolute right-2 text-2xs tabular-nums text-[var(--text-muted)]',\n h === hours[0] ? 'top-1' : '-top-2',\n )}\n >\n {clockLabel(h)}\n </span>\n </div>\n ))}\n </div>\n\n {days.map((d) => (\n <motion.div\n key={d}\n variants={col}\n initial=\"hidden\"\n animate=\"shown\"\n className=\"relative border-r border-[var(--border-subtle)] last:border-r-0\"\n style={{ height: gridHeight }}\n >\n {hours.map((h) => (\n <motion.button\n key={h}\n type=\"button\"\n aria-label={`Book ${clockLabel(h)}`}\n onClick={() =>\n onBookSlot(\n d,\n `${String(h).padStart(2, '0')}:00`,\n `${String(h + 1).padStart(2, '0')}:00`,\n )\n }\n whileHover={reduce ? undefined : { scale: 1.02 }}\n whileTap={reduce ? undefined : { scale: PRESS_SCALE_LARGE }}\n transition={springs.snappy}\n className=\"group absolute inset-x-1 flex items-center justify-center rounded-md border border-transparent transition-colors duration-[var(--motion-fast)] hover:border-brand-500/30 hover:bg-brand-500/[0.06]\"\n style={{\n top: (h - openHour) * HOUR_PX + 1,\n height: HOUR_PX - 2,\n }}\n >\n <Plus className=\"text-brand-500 opacity-0 transition-opacity duration-[var(--motion-fast)] group-hover:opacity-100\" />\n </motion.button>\n ))}\n\n {(byDay.get(d) ?? []).map((e) => {\n const top = (hourOf(e.start) - openHour) * HOUR_PX;\n const height = Math.max(\n (hourOf(e.end) - hourOf(e.start)) * HOUR_PX,\n 20,\n );\n if (top + height < 0 || top > gridHeight) return null;\n const isBlock = e.type === 'block';\n return (\n <motion.button\n key={e.id}\n type=\"button\"\n variants={blockV}\n onClick={() => onSelectEvent(e)}\n whileHover={reduce ? undefined : { scale: 1.01 }}\n transition={springs.snappy}\n className={cn(\n 'absolute inset-x-1 z-10 overflow-hidden rounded-md px-2 py-1 text-left',\n isBlock\n ? 'border border-dashed border-[var(--warning)]/40 bg-[var(--warning-bg)] text-[var(--warning)]'\n : e.type === 'synced'\n ? 'border border-[var(--border-default)] bg-[var(--bg-muted)] text-[var(--text-secondary)]'\n : 'bg-brand-500/[0.16] text-[var(--text-primary)] ring-1 ring-inset ring-brand-500/30',\n )}\n style={{\n top,\n height,\n boxShadow:\n isBlock || e.type === 'synced'\n ? undefined\n : 'var(--shadow-md)',\n }}\n >\n <span className=\"block truncate text-2xs font-medium\">\n {e.customerName || e.title}\n </span>\n </motion.button>\n );\n })}\n </motion.div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { motion, useReducedMotion } from "motion/react";
|
|
5
|
+
import { eases } from "@withpotter/lib/motion";
|
|
6
|
+
import { cn } from "@withpotter/lib/utils";
|
|
7
|
+
import { parseISO, toISODate, bookingsByDay, fullness } from "./calendar-range";
|
|
8
|
+
const MONTHS = [
|
|
9
|
+
"Jan",
|
|
10
|
+
"Feb",
|
|
11
|
+
"Mar",
|
|
12
|
+
"Apr",
|
|
13
|
+
"May",
|
|
14
|
+
"Jun",
|
|
15
|
+
"Jul",
|
|
16
|
+
"Aug",
|
|
17
|
+
"Sep",
|
|
18
|
+
"Oct",
|
|
19
|
+
"Nov",
|
|
20
|
+
"Dec"
|
|
21
|
+
];
|
|
22
|
+
function YearGrid({
|
|
23
|
+
anchor,
|
|
24
|
+
events,
|
|
25
|
+
resourceCount,
|
|
26
|
+
onOpenDay
|
|
27
|
+
}) {
|
|
28
|
+
const reduce = useReducedMotion();
|
|
29
|
+
const year = parseISO(anchor).getFullYear();
|
|
30
|
+
const counts = useMemo(() => bookingsByDay(events), [events]);
|
|
31
|
+
const today = toISODate(/* @__PURE__ */ new Date());
|
|
32
|
+
const months = useMemo(
|
|
33
|
+
() => MONTHS.map((label, m) => {
|
|
34
|
+
const last = new Date(year, m + 1, 0).getDate();
|
|
35
|
+
const days = Array.from(
|
|
36
|
+
{ length: last },
|
|
37
|
+
(_, i) => toISODate(new Date(year, m, i + 1))
|
|
38
|
+
);
|
|
39
|
+
const total = days.reduce((sum, d) => sum + (counts.get(d) ?? 0), 0);
|
|
40
|
+
return { label, m, days, total };
|
|
41
|
+
}),
|
|
42
|
+
[year, counts]
|
|
43
|
+
);
|
|
44
|
+
const busiest = Math.max(...months.map((mo) => mo.total), 1);
|
|
45
|
+
return /* @__PURE__ */ jsx("div", { className: "surface grid grid-cols-2 gap-4 p-4 sm:grid-cols-3 lg:grid-cols-4", children: months.map(({ label, days, total }, idx) => /* @__PURE__ */ jsxs(
|
|
46
|
+
motion.div,
|
|
47
|
+
{
|
|
48
|
+
initial: reduce ? false : { opacity: 0, y: 8 },
|
|
49
|
+
animate: { opacity: 1, y: 0 },
|
|
50
|
+
transition: {
|
|
51
|
+
duration: 0.24,
|
|
52
|
+
delay: reduce ? 0 : idx * 0.02,
|
|
53
|
+
ease: eases.settle
|
|
54
|
+
},
|
|
55
|
+
className: "space-y-2",
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
58
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-[var(--text-primary)]", children: label }),
|
|
59
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xs tabular-nums text-[var(--text-muted)]", children: total || "" })
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsxs("div", { className: "grid w-fit grid-cols-7 gap-[2px]", children: [
|
|
62
|
+
Array.from({ length: parseISO(days[0]).getDay() }).map((_, i) => /* @__PURE__ */ jsx("span", { className: "h-[11px] w-[11px]" }, `b-${i}`)),
|
|
63
|
+
days.map((d) => {
|
|
64
|
+
const booked = counts.get(d) ?? 0;
|
|
65
|
+
const fill = fullness(booked, resourceCount);
|
|
66
|
+
return /* @__PURE__ */ jsx(
|
|
67
|
+
"button",
|
|
68
|
+
{
|
|
69
|
+
type: "button",
|
|
70
|
+
onClick: () => onOpenDay(d),
|
|
71
|
+
title: `${d}: ${booked} booked`,
|
|
72
|
+
"aria-label": `${d}, ${booked} booked`,
|
|
73
|
+
className: cn(
|
|
74
|
+
"h-[11px] w-[11px] rounded-[2px] transition-transform duration-[var(--motion-fast)] hover:scale-150",
|
|
75
|
+
// Empty days need to be visible or the month reads as a
|
|
76
|
+
// gap in the UI rather than a quiet month.
|
|
77
|
+
booked === 0 && "bg-[var(--text-primary)]/[0.07]",
|
|
78
|
+
d === today && "ring-1 ring-brand-500"
|
|
79
|
+
),
|
|
80
|
+
style: booked > 0 ? {
|
|
81
|
+
// One hue, opacity carrying the load. A multi-colour
|
|
82
|
+
// scale would imply categories where there is only
|
|
83
|
+
// more and less.
|
|
84
|
+
backgroundColor: `color-mix(in srgb, var(--brand-500) ${Math.round(
|
|
85
|
+
25 + fill * 75
|
|
86
|
+
)}%, transparent)`
|
|
87
|
+
} : void 0
|
|
88
|
+
},
|
|
89
|
+
d
|
|
90
|
+
);
|
|
91
|
+
})
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsx("span", { className: "block h-1 rounded-full bg-[var(--bg-muted)]", children: /* @__PURE__ */ jsx(
|
|
94
|
+
motion.span,
|
|
95
|
+
{
|
|
96
|
+
className: "block h-1 rounded-full bg-brand-500/60",
|
|
97
|
+
initial: reduce ? false : { scaleX: 0 },
|
|
98
|
+
animate: { scaleX: total / busiest },
|
|
99
|
+
style: { transformOrigin: "left" },
|
|
100
|
+
transition: { duration: 0.45, ease: eases.settle }
|
|
101
|
+
}
|
|
102
|
+
) })
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
label
|
|
106
|
+
)) });
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
YearGrid
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=year-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/calendar/_components/year-grid.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { motion, useReducedMotion } from 'motion/react';\nimport { eases } from '@withpotter/lib/motion';\nimport { cn } from '@withpotter/lib/utils';\nimport type { CalendarEventItem } from '@withpotter/lib/api/types';\nimport { parseISO, toISODate, bookingsByDay, fullness } from './calendar-range';\n\nexport interface YearGridProps {\n anchor: string;\n events: CalendarEventItem[];\n resourceCount: number;\n onOpenDay: (date: string) => void;\n}\n\nconst MONTHS = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n];\n\n/**\n * The year as twelve small heat maps.\n *\n * Nobody schedules a year, so this view answers a different question from the\n * others: when is this business busy? Seasonality, dead months, the week the\n * bookings stopped. Each square is a day tinted by how full it was; pressing\n * one goes straight to that day.\n *\n * Deliberately not a twelve-month calendar with legible dates — at this zoom\n * the pattern is the information and the dates are noise.\n */\nexport function YearGrid({\n anchor,\n events,\n resourceCount,\n onOpenDay,\n}: YearGridProps) {\n const reduce = useReducedMotion();\n const year = parseISO(anchor).getFullYear();\n const counts = useMemo(() => bookingsByDay(events), [events]);\n const today = toISODate(new Date());\n\n const months = useMemo(\n () =>\n MONTHS.map((label, m) => {\n const last = new Date(year, m + 1, 0).getDate();\n const days = Array.from({ length: last }, (_, i) =>\n toISODate(new Date(year, m, i + 1)),\n );\n const total = days.reduce((sum, d) => sum + (counts.get(d) ?? 0), 0);\n return { label, m, days, total };\n }),\n [year, counts],\n );\n\n const busiest = Math.max(...months.map((mo) => mo.total), 1);\n\n return (\n <div className=\"surface grid grid-cols-2 gap-4 p-4 sm:grid-cols-3 lg:grid-cols-4\">\n {months.map(({ label, days, total }, idx) => (\n <motion.div\n key={label}\n initial={reduce ? false : { opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{\n duration: 0.24,\n delay: reduce ? 0 : idx * 0.02,\n ease: eases.settle,\n }}\n className=\"space-y-2\"\n >\n <div className=\"flex items-baseline justify-between\">\n <span className=\"text-xs font-medium text-[var(--text-primary)]\">\n {label}\n </span>\n <span className=\"text-2xs tabular-nums text-[var(--text-muted)]\">\n {total || ''}\n </span>\n </div>\n\n {/* Fixed 11px cells rather than aspect-square: in a fluid grid each\n square inflated to ~50px, which is a calendar, not a heat map. The\n pattern only reads when a whole month fits in a glance. */}\n <div className=\"grid w-fit grid-cols-7 gap-[2px]\">\n {Array.from({ length: parseISO(days[0]).getDay() }).map((_, i) => (\n <span key={`b-${i}`} className=\"h-[11px] w-[11px]\" />\n ))}\n {days.map((d) => {\n const booked = counts.get(d) ?? 0;\n const fill = fullness(booked, resourceCount);\n return (\n <button\n type=\"button\"\n key={d}\n onClick={() => onOpenDay(d)}\n title={`${d}: ${booked} booked`}\n aria-label={`${d}, ${booked} booked`}\n className={cn(\n 'h-[11px] w-[11px] rounded-[2px] transition-transform duration-[var(--motion-fast)] hover:scale-150',\n // Empty days need to be visible or the month reads as a\n // gap in the UI rather than a quiet month.\n booked === 0 && 'bg-[var(--text-primary)]/[0.07]',\n d === today && 'ring-1 ring-brand-500',\n )}\n style={\n booked > 0\n ? {\n // One hue, opacity carrying the load. A multi-colour\n // scale would imply categories where there is only\n // more and less.\n backgroundColor: `color-mix(in srgb, var(--brand-500) ${Math.round(\n 25 + fill * 75,\n )}%, transparent)`,\n }\n : undefined\n }\n />\n );\n })}\n </div>\n\n <span className=\"block h-1 rounded-full bg-[var(--bg-muted)]\">\n <motion.span\n className=\"block h-1 rounded-full bg-brand-500/60\"\n initial={reduce ? false : { scaleX: 0 }}\n animate={{ scaleX: total / busiest }}\n style={{ transformOrigin: 'left' }}\n transition={{ duration: 0.45, ease: eases.settle }}\n />\n </span>\n </motion.div>\n ))}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/%28bookings%29/calendar/page.tsx"],"sourcesContent":["import { redirect } from 'next/navigation';\n\n/**\n * Calendar is no longer its own destination.\n *\n * It and Appointments were sibling nav items over the same records: the list\n * could act but could not show time, the grid could show time but could not\n * act. They are now one surface with a view switch, so this route keeps working\n * for old links and bookmarks and lands on the calendar rendering of it.\n */\nexport default function CalendarPage() {\n redirect('/dashboard/bookings?view=calendar');\n}\n"],"mappings":"AAAA,SAAS,gBAAgB;AAUV,SAAR,eAAgC;AACrC,WAAS,mCAAmC;AAC9C;","names":[]}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Dialog,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogHeader,
|
|
8
|
+
DialogTitle,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogFooter
|
|
11
|
+
} from "@withpotter/ui-kit/dialog";
|
|
12
|
+
import { Button } from "@withpotter/ui-kit/button";
|
|
13
|
+
import { Select } from "@withpotter/ui-kit/select";
|
|
14
|
+
import { BrandMark } from "@withpotter/ui-kit/brand-mark";
|
|
15
|
+
import { Input, ErrorBanner } from "@withpotter/ui-kit";
|
|
16
|
+
const CHANNELS = [
|
|
17
|
+
"Airbnb",
|
|
18
|
+
"Booking.com",
|
|
19
|
+
"Vrbo",
|
|
20
|
+
"Agoda",
|
|
21
|
+
"Expedia",
|
|
22
|
+
"Tripadvisor",
|
|
23
|
+
"Google Calendar",
|
|
24
|
+
"Other"
|
|
25
|
+
];
|
|
26
|
+
function ChannelFeedModal({
|
|
27
|
+
properties,
|
|
28
|
+
loadingProperties,
|
|
29
|
+
busy,
|
|
30
|
+
error,
|
|
31
|
+
onSubscribe,
|
|
32
|
+
onClose
|
|
33
|
+
}) {
|
|
34
|
+
const [propertyId, setPropertyId] = useState("");
|
|
35
|
+
const [name, setName] = useState("Airbnb");
|
|
36
|
+
const [feedUrl, setFeedUrl] = useState("");
|
|
37
|
+
const canSubmit = !!propertyId && !!feedUrl.trim() && !busy;
|
|
38
|
+
return /* @__PURE__ */ jsx(
|
|
39
|
+
Dialog,
|
|
40
|
+
{
|
|
41
|
+
open: true,
|
|
42
|
+
onOpenChange: (o) => {
|
|
43
|
+
if (!o) onClose();
|
|
44
|
+
},
|
|
45
|
+
children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-md", children: [
|
|
46
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
47
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Add a channel calendar" }),
|
|
48
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Bookings made on the channel will hold the property here, so the same dates cannot be sold twice." })
|
|
49
|
+
] }),
|
|
50
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
51
|
+
error && /* @__PURE__ */ jsx(ErrorBanner, { children: error }),
|
|
52
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
53
|
+
/* @__PURE__ */ jsx("label", { className: "mb-2 block text-xs font-medium text-[var(--text-tertiary)]", children: "Property" }),
|
|
54
|
+
/* @__PURE__ */ jsx(
|
|
55
|
+
Select,
|
|
56
|
+
{
|
|
57
|
+
value: propertyId,
|
|
58
|
+
onChange: setPropertyId,
|
|
59
|
+
placeholder: loadingProperties ? "Loading properties..." : "Choose a property",
|
|
60
|
+
options: properties.map((p) => ({
|
|
61
|
+
value: p.id,
|
|
62
|
+
label: p.title
|
|
63
|
+
}))
|
|
64
|
+
}
|
|
65
|
+
),
|
|
66
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-[var(--text-muted)]", children: "A calendar link covers one listing, so it blocks only this one." })
|
|
67
|
+
] }),
|
|
68
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
69
|
+
/* @__PURE__ */ jsx("label", { className: "mb-2 block text-xs font-medium text-[var(--text-tertiary)]", children: "Channel" }),
|
|
70
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2", children: CHANNELS.map((c) => {
|
|
71
|
+
const active = name === c;
|
|
72
|
+
return /* @__PURE__ */ jsxs(
|
|
73
|
+
"button",
|
|
74
|
+
{
|
|
75
|
+
type: "button",
|
|
76
|
+
onClick: () => setName(c),
|
|
77
|
+
"aria-pressed": active,
|
|
78
|
+
className: `flex items-center gap-2 rounded-lg border px-3 py-2 text-left text-sm transition-colors ${active ? "border-brand-500/60 bg-brand-500/10 text-[var(--text-primary)]" : "border-[var(--border-subtle)] text-[var(--text-secondary)] hover:border-[var(--border-default)] hover:text-[var(--text-primary)]"}`,
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsx(BrandMark, { name: c, size: 15 }),
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: c })
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
c
|
|
85
|
+
);
|
|
86
|
+
}) }),
|
|
87
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-[var(--text-muted)]", children: "Anywhere that exports a calendar works, not just these. Pick Other and name it yourself." })
|
|
88
|
+
] }),
|
|
89
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
90
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-[var(--text-tertiary)]", children: "Calendar link" }),
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
Input,
|
|
93
|
+
{
|
|
94
|
+
type: "url",
|
|
95
|
+
value: feedUrl,
|
|
96
|
+
onChange: (e) => setFeedUrl(e.target.value),
|
|
97
|
+
placeholder: "https://www.airbnb.com/calendar/ical/12345.ics"
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-[var(--text-muted)]", children: "Wherever the space is listed, look for export or share calendar. The link ends in .ics. On Airbnb it is under Availability, then Connect calendars." })
|
|
101
|
+
] })
|
|
102
|
+
] }),
|
|
103
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
104
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Cancel" }),
|
|
105
|
+
/* @__PURE__ */ jsx(
|
|
106
|
+
Button,
|
|
107
|
+
{
|
|
108
|
+
onClick: () => onSubscribe({
|
|
109
|
+
feedUrl: feedUrl.trim(),
|
|
110
|
+
name,
|
|
111
|
+
propertyId
|
|
112
|
+
}),
|
|
113
|
+
loading: busy,
|
|
114
|
+
disabled: !canSubmit,
|
|
115
|
+
children: busy ? "Reading the calendar\u2026" : "Connect and import"
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
] })
|
|
119
|
+
] })
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
ChannelFeedModal
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=channel-feed-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/calendar-sync/_components/channel-feed-modal.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@withpotter/ui-kit/dialog';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { Select } from '@withpotter/ui-kit/select';\nimport { BrandMark } from '@withpotter/ui-kit/brand-mark';\nimport { Input, ErrorBanner } from '@withpotter/ui-kit';\n\n/**\n * Where a property or space is also listed.\n *\n * The lodging sites first because they are the common case, then the generic\n * options: this archetype covers venues too, and a hall is usually double-booked\n * through an events site or a shared Google Calendar rather than Airbnb.\n *\n * VERIFIED AGAINST THE IMPORTER, not guessed at. `import-ical-feed.usecase.ts`\n * fetches a URL and parses it; there is no per-vendor branching anywhere in it\n * or in `parse-ical.ts` — the vendor names appear only in comments. So every\n * channel that publishes an .ics export genuinely works, and naming more of\n * them is a description of what the thing does rather than a promise it cannot\n * keep. `Other` stays last and carries the general case.\n */\nconst CHANNELS = [\n 'Airbnb',\n 'Booking.com',\n 'Vrbo',\n 'Agoda',\n 'Expedia',\n 'Tripadvisor',\n 'Google Calendar',\n 'Other',\n];\n\nexport interface PropertyOption {\n id: string;\n title: string;\n}\n\ninterface ChannelFeedModalProps {\n properties: PropertyOption[];\n loadingProperties: boolean;\n busy: boolean;\n error?: string | null;\n onSubscribe: (data: {\n feedUrl: string;\n name: string;\n propertyId: string;\n }) => void;\n onClose: () => void;\n}\n\n/**\n * Subscribe a property to a channel's exported calendar.\n *\n * The property is a required choice rather than an optional one, because a feed\n * is per LISTING. Left unscoped, one flat's Airbnb calendar would black out\n * every other property the operator runs, which is a worse failure than not\n * connecting at all: it silently removes availability nobody asked it to.\n */\nexport function ChannelFeedModal({\n properties,\n loadingProperties,\n busy,\n error,\n onSubscribe,\n onClose,\n}: ChannelFeedModalProps) {\n const [propertyId, setPropertyId] = useState('');\n const [name, setName] = useState('Airbnb');\n const [feedUrl, setFeedUrl] = useState('');\n\n const canSubmit = !!propertyId && !!feedUrl.trim() && !busy;\n\n return (\n <Dialog\n open\n onOpenChange={(o) => {\n if (!o) onClose();\n }}\n >\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Add a channel calendar</DialogTitle>\n <DialogDescription>\n Bookings made on the channel will hold the property here, so the\n same dates cannot be sold twice.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n {error && <ErrorBanner>{error}</ErrorBanner>}\n\n <div>\n <label className=\"mb-2 block text-xs font-medium text-[var(--text-tertiary)]\">\n Property\n </label>\n <Select\n value={propertyId}\n onChange={setPropertyId}\n placeholder={\n loadingProperties\n ? 'Loading properties...'\n : 'Choose a property'\n }\n options={properties.map((p) => ({\n value: p.id,\n label: p.title,\n }))}\n />\n <p className=\"mt-1 text-xs text-[var(--text-muted)]\">\n A calendar link covers one listing, so it blocks only this one.\n </p>\n </div>\n\n <div>\n <label className=\"mb-2 block text-xs font-medium text-[var(--text-tertiary)]\">\n Channel\n </label>\n {/* The logos, not a list of strings.\n\n This was a dropdown of plain names, which reads as a form field\n you have to fill in rather than a thing you are connecting to.\n A merchant scanning for \"the one I use\" recognises the Airbnb\n mark a great deal faster than they read the word, and the marks\n are also the honest signal that these are real integrations.\n\n A grid rather than a Select for the same reason: six options\n that each have a face should be shown, not hidden behind a\n click. */}\n <div className=\"grid grid-cols-3 gap-2\">\n {CHANNELS.map((c) => {\n const active = name === c;\n return (\n <button\n key={c}\n type=\"button\"\n onClick={() => setName(c)}\n aria-pressed={active}\n className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-left text-sm transition-colors ${\n active\n ? 'border-brand-500/60 bg-brand-500/10 text-[var(--text-primary)]'\n : 'border-[var(--border-subtle)] text-[var(--text-secondary)] hover:border-[var(--border-default)] hover:text-[var(--text-primary)]'\n }`}\n >\n <BrandMark name={c} size={15} />\n <span className=\"min-w-0 truncate\">{c}</span>\n </button>\n );\n })}\n </div>\n <p className=\"mt-2 text-xs text-[var(--text-muted)]\">\n Anywhere that exports a calendar works, not just these. Pick Other\n and name it yourself.\n </p>\n </div>\n\n <div>\n <label className=\"mb-1 block text-xs font-medium text-[var(--text-tertiary)]\">\n Calendar link\n </label>\n <Input\n type=\"url\"\n value={feedUrl}\n onChange={(e) => setFeedUrl(e.target.value)}\n placeholder=\"https://www.airbnb.com/calendar/ical/12345.ics\"\n />\n <p className=\"mt-1 text-xs text-[var(--text-muted)]\">\n Wherever the space is listed, look for export or share calendar.\n The link ends in .ics. On Airbnb it is under Availability, then\n Connect calendars.\n </p>\n </div>\n </div>\n\n <DialogFooter>\n <Button variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n onClick={() =>\n onSubscribe({\n feedUrl: feedUrl.trim(),\n name,\n propertyId,\n })\n }\n loading={busy}\n disabled={!canSubmit}\n >\n {busy ? 'Reading the calendar…' : 'Connect and import'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Plus, RefreshCw, Trash2 } from "lucide-react";
|
|
4
|
+
import { BrandMark } from "@withpotter/ui-kit/brand-mark";
|
|
5
|
+
import { Button } from "@withpotter/ui-kit/button";
|
|
6
|
+
function ChannelFeedsSection({
|
|
7
|
+
feeds,
|
|
8
|
+
propertyNameById,
|
|
9
|
+
syncingId,
|
|
10
|
+
deletingId,
|
|
11
|
+
onAdd,
|
|
12
|
+
onSync,
|
|
13
|
+
onDelete,
|
|
14
|
+
formatDateTime
|
|
15
|
+
}) {
|
|
16
|
+
return /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
|
|
17
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
18
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
19
|
+
/* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold text-[var(--text-primary)]", children: "Channel calendars" }),
|
|
20
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-sm text-[var(--text-secondary)]", children: "Bookings made anywhere else your space is listed hold your dates here too." })
|
|
21
|
+
] }),
|
|
22
|
+
/* @__PURE__ */ jsx(
|
|
23
|
+
Button,
|
|
24
|
+
{
|
|
25
|
+
variant: "secondary",
|
|
26
|
+
onClick: onAdd,
|
|
27
|
+
className: "shrink-0",
|
|
28
|
+
icon: /* @__PURE__ */ jsx(Plus, {}),
|
|
29
|
+
children: "Add channel"
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
] }),
|
|
33
|
+
feeds.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-[var(--border-default)] px-4 py-6 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-secondary)]", children: "No channel calendars yet. A space listed somewhere else can be booked there without this." }) }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: feeds.map((feed) => {
|
|
34
|
+
const property = feed.teamMemberId ? propertyNameById[feed.teamMemberId] : null;
|
|
35
|
+
const syncing = syncingId === feed.id;
|
|
36
|
+
return /* @__PURE__ */ jsxs(
|
|
37
|
+
"li",
|
|
38
|
+
{
|
|
39
|
+
className: "surface flex items-center justify-between gap-3 px-4 py-3",
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsx(BrandMark, { name: feed.name, size: 18, className: "shrink-0" }),
|
|
42
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
43
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-sm font-medium text-[var(--text-primary)]", children: [
|
|
44
|
+
feed.name,
|
|
45
|
+
property && /* @__PURE__ */ jsxs("span", { className: "font-normal text-[var(--text-secondary)]", children: [
|
|
46
|
+
" ",
|
|
47
|
+
"on ",
|
|
48
|
+
property
|
|
49
|
+
] })
|
|
50
|
+
] }),
|
|
51
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-[var(--text-secondary)]", children: feed.status === "error" ? /* @__PURE__ */ jsx("span", { className: "text-[var(--error)]", children: feed.lastErrorMessage || "Could not read this calendar" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
52
|
+
feed.syncedEventsCount,
|
|
53
|
+
" block",
|
|
54
|
+
feed.syncedEventsCount === 1 ? "" : "s",
|
|
55
|
+
" held",
|
|
56
|
+
feed.lastSyncAt && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
" \xB7 checked ",
|
|
58
|
+
formatDateTime(feed.lastSyncAt)
|
|
59
|
+
] })
|
|
60
|
+
] }) })
|
|
61
|
+
] }),
|
|
62
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
63
|
+
/* @__PURE__ */ jsx(
|
|
64
|
+
Button,
|
|
65
|
+
{
|
|
66
|
+
variant: "ghost",
|
|
67
|
+
size: "icon",
|
|
68
|
+
onClick: () => onSync(feed.id),
|
|
69
|
+
loading: syncing,
|
|
70
|
+
title: "Check this calendar now",
|
|
71
|
+
"aria-label": "Check this calendar now",
|
|
72
|
+
children: /* @__PURE__ */ jsx(RefreshCw, {})
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
/* @__PURE__ */ jsx(
|
|
76
|
+
Button,
|
|
77
|
+
{
|
|
78
|
+
variant: "ghost",
|
|
79
|
+
size: "icon",
|
|
80
|
+
onClick: () => onDelete(feed.id),
|
|
81
|
+
loading: deletingId === feed.id,
|
|
82
|
+
title: "Remove this calendar",
|
|
83
|
+
"aria-label": "Remove this calendar",
|
|
84
|
+
className: "hover:bg-[var(--error-bg)] hover:text-[var(--error)]",
|
|
85
|
+
children: /* @__PURE__ */ jsx(Trash2, {})
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
] })
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
feed.id
|
|
92
|
+
);
|
|
93
|
+
}) })
|
|
94
|
+
] });
|
|
95
|
+
}
|
|
96
|
+
export {
|
|
97
|
+
ChannelFeedsSection
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=channel-feeds-section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/calendar-sync/_components/channel-feeds-section.tsx"],"sourcesContent":["'use client';\n\nimport { Plus, RefreshCw, Trash2 } from 'lucide-react';\nimport type { CalendarConnectionItem } from '@withpotter/lib/api';\nimport { BrandMark } from '@withpotter/ui-kit/brand-mark';\nimport { Button } from '@withpotter/ui-kit/button';\n\ninterface ChannelFeedsSectionProps {\n feeds: CalendarConnectionItem[];\n propertyNameById: Record<string, string>;\n syncingId: string | null;\n deletingId: string | null;\n onAdd: () => void;\n onSync: (id: string) => void;\n onDelete: (id: string) => void;\n formatDateTime: (iso: string) => string;\n}\n\n/**\n * The calendars that belong to somebody else.\n *\n * Kept apart from the Google connection above it because the two run in\n * opposite directions, and a page that lists them together cannot say which is\n * which. Google is an export: Box's bookings appear in a calendar the\n * merchant reads. A channel feed is an import: the channel's bookings take\n * availability away here. Presenting both as \"connected calendars\" is how an\n * operator ends up believing they are protected from double-booking when\n * nothing is actually reading anyone else's calendar.\n */\nexport function ChannelFeedsSection({\n feeds,\n propertyNameById,\n syncingId,\n deletingId,\n onAdd,\n onSync,\n onDelete,\n formatDateTime,\n}: ChannelFeedsSectionProps) {\n return (\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h2 className=\"text-sm font-semibold text-[var(--text-primary)]\">\n Channel calendars\n </h2>\n <p className=\"mt-0.5 text-sm text-[var(--text-secondary)]\">\n Bookings made anywhere else your space is listed hold your dates\n here too.\n </p>\n </div>\n <Button\n variant=\"secondary\"\n onClick={onAdd}\n className=\"shrink-0\"\n icon={<Plus />}\n >\n Add channel\n </Button>\n </div>\n\n {feeds.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-[var(--border-default)] px-4 py-6 text-center\">\n <p className=\"text-sm text-[var(--text-secondary)]\">\n No channel calendars yet. A space listed somewhere else can be\n booked there without this.\n </p>\n </div>\n ) : (\n <ul className=\"space-y-2\">\n {feeds.map((feed) => {\n const property = feed.teamMemberId\n ? propertyNameById[feed.teamMemberId]\n : null;\n const syncing = syncingId === feed.id;\n return (\n <li\n key={feed.id}\n // `surface`, not a hand-rolled border and fill. globals.css\n // calls a bare border+bg div \"always the flat-card bug\", and\n // that is what these rows were: flat, while the Google card\n // beside them lifted off the page. One recipe, one look.\n className=\"surface flex items-center justify-between gap-3 px-4 py-3\"\n >\n {/* The channel's own mark, so a row is identifiable before it\n is read. `feed.name` is the channel the merchant picked. */}\n <BrandMark name={feed.name} size={18} className=\"shrink-0\" />\n\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-[var(--text-primary)]\">\n {feed.name}\n {property && (\n <span className=\"font-normal text-[var(--text-secondary)]\">\n {' '}\n on {property}\n </span>\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-[var(--text-secondary)]\">\n {feed.status === 'error' ? (\n <span className=\"text-[var(--error)]\">\n {feed.lastErrorMessage ||\n 'Could not read this calendar'}\n </span>\n ) : (\n <>\n {feed.syncedEventsCount} block\n {feed.syncedEventsCount === 1 ? '' : 's'} held\n {feed.lastSyncAt && (\n <> · checked {formatDateTime(feed.lastSyncAt)}</>\n )}\n </>\n )}\n </p>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onSync(feed.id)}\n loading={syncing}\n title=\"Check this calendar now\"\n aria-label=\"Check this calendar now\"\n >\n <RefreshCw />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => onDelete(feed.id)}\n loading={deletingId === feed.id}\n title=\"Remove this calendar\"\n aria-label=\"Remove this calendar\"\n className=\"hover:bg-[var(--error-bg)] hover:text-[var(--error)]\"\n >\n <Trash2 />\n </Button>\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </section>\n );\n}\n"],"mappings":";AA0CQ,SAmEkB,UAlEhB,KADF;AAxCR,SAAS,MAAM,WAAW,cAAc;AAExC,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AAwBhB,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,SACE,qBAAC,aAAQ,WAAU,aACjB;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA,2BAAC,SACC;AAAA,4BAAC,QAAG,WAAU,oDAAmD,+BAEjE;AAAA,QACA,oBAAC,OAAE,WAAU,+CAA8C,wFAG3D;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS;AAAA,UACT,WAAU;AAAA,UACV,MAAM,oBAAC,QAAK;AAAA,UACb;AAAA;AAAA,MAED;AAAA,OACF;AAAA,IAEC,MAAM,WAAW,IAChB,oBAAC,SAAI,WAAU,wFACb,8BAAC,OAAE,WAAU,wCAAuC,uGAGpD,GACF,IAEA,oBAAC,QAAG,WAAU,aACX,gBAAM,IAAI,CAAC,SAAS;AACnB,YAAM,WAAW,KAAK,eAClB,iBAAiB,KAAK,YAAY,IAClC;AACJ,YAAM,UAAU,cAAc,KAAK;AACnC,aACE;AAAA,QAAC;AAAA;AAAA,UAMC,WAAU;AAAA,UAIV;AAAA,gCAAC,aAAU,MAAM,KAAK,MAAM,MAAM,IAAI,WAAU,YAAW;AAAA,YAE3D,qBAAC,SAAI,WAAU,kBACb;AAAA,mCAAC,OAAE,WAAU,2DACV;AAAA,qBAAK;AAAA,gBACL,YACC,qBAAC,UAAK,WAAU,4CACb;AAAA;AAAA,kBAAI;AAAA,kBACD;AAAA,mBACN;AAAA,iBAEJ;AAAA,cACA,oBAAC,OAAE,WAAU,+CACV,eAAK,WAAW,UACf,oBAAC,UAAK,WAAU,uBACb,eAAK,oBACJ,gCACJ,IAEA,iCACG;AAAA,qBAAK;AAAA,gBAAkB;AAAA,gBACvB,KAAK,sBAAsB,IAAI,KAAK;AAAA,gBAAI;AAAA,gBACxC,KAAK,cACJ,iCAAE;AAAA;AAAA,kBAAY,eAAe,KAAK,UAAU;AAAA,mBAAE;AAAA,iBAElD,GAEJ;AAAA,eACF;AAAA,YAEA,qBAAC,SAAI,WAAU,oCACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MAAM,OAAO,KAAK,EAAE;AAAA,kBAC7B,SAAS;AAAA,kBACT,OAAM;AAAA,kBACN,cAAW;AAAA,kBAEX,8BAAC,aAAU;AAAA;AAAA,cACb;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS,MAAM,SAAS,KAAK,EAAE;AAAA,kBAC/B,SAAS,eAAe,KAAK;AAAA,kBAC7B,OAAM;AAAA,kBACN,cAAW;AAAA,kBACX,WAAU;AAAA,kBAEV,8BAAC,UAAO;AAAA;AAAA,cACV;AAAA,eACF;AAAA;AAAA;AAAA,QA7DK,KAAK;AAAA,MA8DZ;AAAA,IAEJ,CAAC,GACH;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const day = (offsetDays, time = "09:00") => {
|
|
2
|
+
const base = /* @__PURE__ */ new Date(`2026-08-11T${time}:00.000Z`);
|
|
3
|
+
base.setDate(base.getDate() + offsetDays);
|
|
4
|
+
return base.toISOString();
|
|
5
|
+
};
|
|
6
|
+
function mk(c) {
|
|
7
|
+
return {
|
|
8
|
+
tenantId: "demo",
|
|
9
|
+
provider: "ical",
|
|
10
|
+
externalCalendarId: null,
|
|
11
|
+
status: "connected",
|
|
12
|
+
lastErrorMessage: null,
|
|
13
|
+
lastSyncAt: day(0, "07:30"),
|
|
14
|
+
syncedEventsCount: 0,
|
|
15
|
+
teamMemberId: null,
|
|
16
|
+
blockExternalEvents: true,
|
|
17
|
+
showExternalEvents: true,
|
|
18
|
+
eventColor: null,
|
|
19
|
+
syncDirection: "one_way_import",
|
|
20
|
+
createdAt: day(-30),
|
|
21
|
+
updatedAt: day(0, "07:30"),
|
|
22
|
+
...c
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const DEMO_CALENDAR_CONNECTIONS = [
|
|
26
|
+
mk({
|
|
27
|
+
id: "demo-cal1",
|
|
28
|
+
name: "Airbnb \xB7 Lekki Two-Bed",
|
|
29
|
+
provider: "ical",
|
|
30
|
+
syncedEventsCount: 18,
|
|
31
|
+
lastSyncAt: day(0, "07:30")
|
|
32
|
+
}),
|
|
33
|
+
mk({
|
|
34
|
+
id: "demo-cal2",
|
|
35
|
+
name: "Booking.com \xB7 Lekki Two-Bed",
|
|
36
|
+
provider: "ical",
|
|
37
|
+
status: "error",
|
|
38
|
+
// The row that earns the screen. A feed that stopped importing is a
|
|
39
|
+
// double-booking waiting to happen, and nothing else on the page says so.
|
|
40
|
+
lastErrorMessage: "The calendar URL returned 404. It may have been reset.",
|
|
41
|
+
syncedEventsCount: 11,
|
|
42
|
+
lastSyncAt: day(-3, "07:30")
|
|
43
|
+
}),
|
|
44
|
+
mk({
|
|
45
|
+
id: "demo-cal3",
|
|
46
|
+
name: "Google Calendar \xB7 Front desk",
|
|
47
|
+
provider: "google",
|
|
48
|
+
externalCalendarId: "frontdesk@thelekkiplace.com",
|
|
49
|
+
status: "connected",
|
|
50
|
+
syncDirection: "one_way_export",
|
|
51
|
+
blockExternalEvents: false,
|
|
52
|
+
syncedEventsCount: 42,
|
|
53
|
+
lastSyncAt: day(0, "08:05")
|
|
54
|
+
})
|
|
55
|
+
];
|
|
56
|
+
const DEMO_FEED_PROPERTIES = [
|
|
57
|
+
{ id: "demo-prop1", title: "Lekki Two-Bed Apartment" },
|
|
58
|
+
{ id: "demo-prop2", title: "Ikoyi Studio" }
|
|
59
|
+
];
|
|
60
|
+
export {
|
|
61
|
+
DEMO_CALENDAR_CONNECTIONS,
|
|
62
|
+
DEMO_FEED_PROPERTIES
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=demo-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/%28bookings%29/calendar-sync/demo-data.ts"],"sourcesContent":["import type { CalendarConnectionItem } from '@withpotter/lib/api/types';\n\n/**\n * Seed calendar connections for `?demo=1`.\n *\n * The page splits its list in two by provider — Google and friends are an\n * EXPORT of our bookings, an `ical` feed is an IMPORT of somebody else's — so\n * the seed has to carry both or half the screen stays empty and the distinction\n * the page is built to make is invisible.\n */\n\nconst day = (offsetDays: number, time = '09:00'): string => {\n // Fixed anchor rather than Date.now(), so \"last synced\" does not drift while\n // you look at it and two screenshots of the demo agree.\n const base = new Date(`2026-08-11T${time}:00.000Z`);\n base.setDate(base.getDate() + offsetDays);\n return base.toISOString();\n};\n\nfunction mk(\n c: Partial<CalendarConnectionItem> & { id: string; name: string },\n): CalendarConnectionItem {\n return {\n tenantId: 'demo',\n provider: 'ical',\n externalCalendarId: null,\n status: 'connected',\n lastErrorMessage: null,\n lastSyncAt: day(0, '07:30'),\n syncedEventsCount: 0,\n teamMemberId: null,\n blockExternalEvents: true,\n showExternalEvents: true,\n eventColor: null,\n syncDirection: 'one_way_import',\n createdAt: day(-30),\n updatedAt: day(0, '07:30'),\n ...c,\n } as CalendarConnectionItem;\n}\n\n/**\n * Three states, on purpose: a feed working, a feed erroring, and the outbound\n * calendar. A list where everything says \"connected\" never shows what the page\n * does on the morning a channel changes its URL, which is the only morning\n * anybody opens it.\n */\nexport const DEMO_CALENDAR_CONNECTIONS: CalendarConnectionItem[] = [\n mk({\n id: 'demo-cal1',\n name: 'Airbnb · Lekki Two-Bed',\n provider: 'ical',\n syncedEventsCount: 18,\n lastSyncAt: day(0, '07:30'),\n }),\n mk({\n id: 'demo-cal2',\n name: 'Booking.com · Lekki Two-Bed',\n provider: 'ical',\n status: 'error',\n // The row that earns the screen. A feed that stopped importing is a\n // double-booking waiting to happen, and nothing else on the page says so.\n lastErrorMessage: 'The calendar URL returned 404. It may have been reset.',\n syncedEventsCount: 11,\n lastSyncAt: day(-3, '07:30'),\n }),\n mk({\n id: 'demo-cal3',\n name: 'Google Calendar · Front desk',\n provider: 'google',\n externalCalendarId: 'frontdesk@thelekkiplace.com',\n status: 'connected',\n syncDirection: 'one_way_export',\n blockExternalEvents: false,\n syncedEventsCount: 42,\n lastSyncAt: day(0, '08:05'),\n }),\n];\n\n/** The rentable listings a channel feed can be attached to. */\nexport const DEMO_FEED_PROPERTIES = [\n { id: 'demo-prop1', title: 'Lekki Two-Bed Apartment' },\n { id: 'demo-prop2', title: 'Ikoyi Studio' },\n];\n"],"mappings":"AAWA,MAAM,MAAM,CAAC,YAAoB,OAAO,YAAoB;AAG1D,QAAM,OAAO,oBAAI,KAAK,cAAc,IAAI,UAAU;AAClD,OAAK,QAAQ,KAAK,QAAQ,IAAI,UAAU;AACxC,SAAO,KAAK,YAAY;AAC1B;AAEA,SAAS,GACP,GACwB;AACxB,SAAO;AAAA,IACL,UAAU;AAAA,IACV,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,QAAQ;AAAA,IACR,kBAAkB;AAAA,IAClB,YAAY,IAAI,GAAG,OAAO;AAAA,IAC1B,mBAAmB;AAAA,IACnB,cAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,WAAW,IAAI,GAAG;AAAA,IAClB,WAAW,IAAI,GAAG,OAAO;AAAA,IACzB,GAAG;AAAA,EACL;AACF;AAQO,MAAM,4BAAsD;AAAA,EACjE,GAAG;AAAA,IACD,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,mBAAmB;AAAA,IACnB,YAAY,IAAI,GAAG,OAAO;AAAA,EAC5B,CAAC;AAAA,EACD,GAAG;AAAA,IACD,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA;AAAA;AAAA,IAGR,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,YAAY,IAAI,IAAI,OAAO;AAAA,EAC7B,CAAC;AAAA,EACD,GAAG;AAAA,IACD,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,YAAY,IAAI,GAAG,OAAO;AAAA,EAC5B,CAAC;AACH;AAGO,MAAM,uBAAuB;AAAA,EAClC,EAAE,IAAI,cAAc,OAAO,0BAA0B;AAAA,EACrD,EAAE,IAAI,cAAc,OAAO,eAAe;AAC5C;","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { useRouter } from "next/navigation";
|
|
4
|
+
function CalendarSyncRedirect() {
|
|
5
|
+
const router = useRouter();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const query = window.location.search;
|
|
8
|
+
router.replace(`/dashboard/settings/calendar-sync${query}`);
|
|
9
|
+
}, [router]);
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
CalendarSyncRedirect as default
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/%28bookings%29/calendar-sync/page.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport { useRouter } from 'next/navigation';\n\n/**\n * Calendar connections are a setting, for every archetype.\n *\n * This screen used to be three things at once: a tab on appointments, a row\n * called Calendar Sync on events, and a row called Availability on rentals —\n * one page under three names in three places, which is the drift the Resources\n * page was fixed for a month ago and this had quietly kept.\n *\n * It lives at Settings → Operations → Calendar sync now. The route stays so\n * that every existing link, bookmark and OAuth callback still lands.\n */\nexport default function CalendarSyncRedirect() {\n const router = useRouter();\n\n useEffect(() => {\n // The Google callback comes back here with ?connected=true or ?error=…,\n // and the settings page reads exactly those params, so the query survives.\n const query = window.location.search;\n router.replace(`/dashboard/settings/calendar-sync${query}`);\n }, [router]);\n\n return null;\n}\n"],"mappings":";AAEA,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAaX,SAAR,uBAAwC;AAC7C,QAAM,SAAS,UAAU;AAEzB,YAAU,MAAM;AAGd,UAAM,QAAQ,OAAO,SAAS;AAC9B,WAAO,QAAQ,oCAAoC,KAAK,EAAE;AAAA,EAC5D,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AACT;","names":[]}
|