@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/resources/%5BresourceId%5D/page.tsx"],"sourcesContent":["'use client';\n\nimport { useItemLinks } from '@withpotter/lib/use-item-links';\nimport { useEffect, useState } from 'react';\nimport { useParams, useRouter } from 'next/navigation';\nimport { Power, Trash2, Boxes } from 'lucide-react';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { useApiQuery } from '@withpotter/hooks/use-api-query';\nimport { useBusinessLocations } from '@withpotter/lib/use-business-locations';\nimport { useDetailHeader } from '@withpotter/hooks/use-detail-header';\nimport { useEntityLabel } from '@withpotter/lib/intents/use-entity-label';\nimport { useModuleLabel } from '@withpotter/lib/use-module-label';\nimport { resourceCategoriesApi } from '@withpotter/lib/api/resource-categories';\nimport { resourcesApi } from '@withpotter/lib/api/resources';\nimport type {\n Resource,\n ResourceCategory,\n WeeklySchedule,\n} from '@withpotter/lib/resource-types';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { DetailPage } from '@withpotter/ui-kit/detail-page';\nimport { DetailStates } from '@withpotter/ui-kit/detail-states';\nimport { EditorSection } from '@withpotter/ui-kit/editor-section';\nimport { ErrorBanner } from '@withpotter/ui-kit/error-banner';\nimport { Field } from '@withpotter/ui-kit/field';\nimport { Input, NumberField, Select, Textarea } from '@withpotter/ui-kit';\nimport { GeneratedAvatar } from '@withpotter/ui-kit/generated-avatar';\nimport { OverflowMenu, OverflowItem } from '@withpotter/ui-kit/overflow-menu';\nimport { StatusBadge } from '@withpotter/components/shared/status-badge';\nimport { useConfirm } from '@withpotter/ui-kit/confirm-dialog';\nimport { ImageUpload } from '@withpotter/components/image-upload';\nimport { ScheduleEditor, getDefaultSchedule } from '../_components';\nimport { availabilityWords } from '../_lib/shapes';\n\n/**\n * One bookable thing: what a customer sees, how it is booked, and its week.\n *\n * It used to live at `/resources/[categoryId]/[resourceId]`, under a group page\n * that listed the same rows the roster already lists. The group is a heading,\n * not a place, so the record hangs off the roster directly.\n *\n * The frame is `DetailPage`, not a hand-drawn header: this one drew its own\n * back arrow, its own avatar, its own title and its own error box, and headed\n * its cards \"Basic Information\" and \"Danger Zone\".\n */\nexport default function ResourceRecordPage() {\n const params = useParams();\n const router = useRouter();\n const back = useItemLinks().resources;\n const { tenant } = useAuth();\n const confirm = useConfirm();\n const resourceId = params.resourceId as string;\n const slug = tenant?.slug ?? '';\n\n const { locations, isMultiSite } = useBusinessLocations();\n const resourceLabel = useEntityLabel('resource');\n const listTitle = useModuleLabel('resources', resourceLabel.plural);\n\n const {\n data: resource,\n isLoading,\n error: loadError,\n refetch,\n } = useApiQuery<Resource | null>(['resource', resourceId], async (s) => {\n const res = (await resourcesApi.get(s, resourceId)) as unknown as {\n data?: Resource;\n } & Resource;\n return res?.data ?? res ?? null;\n });\n\n const { data: categories } = useApiQuery<ResourceCategory[]>(\n ['resource-categories'],\n (s) => resourceCategoriesApi.list(s),\n );\n\n const [categoryId, setCategoryId] = useState('');\n const [locationId, setLocationId] = useState('');\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [imageUrl, setImageUrl] = useState('');\n const [skills, setSkills] = useState('');\n const [schedule, setSchedule] =\n useState<WeeklySchedule>(getDefaultSchedule());\n const [defaultDuration, setDefaultDuration] = useState(60);\n const [bufferMinutes, setBufferMinutes] = useState(0);\n const [advanceBookingDays, setAdvanceBookingDays] = useState(30);\n const [maxBookingsPerSlot, setMaxBookingsPerSlot] = useState(1);\n const [price, setPrice] = useState('');\n const [saving, setSaving] = useState(false);\n const [saved, setSaved] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n /** Read off the chosen group, not the saved one, so the form follows a move. */\n const groups = Array.isArray(categories) ? categories : [];\n const category = groups.find((c) => c.id === categoryId) ?? null;\n const kind = category?.kind ?? 'person';\n /** A let has no session length, no gap after it, and no opening hours. */\n const { byTheNight } = availabilityWords(kind);\n\n useEffect(() => {\n if (!resource) return;\n setCategoryId(resource.categoryId ?? '');\n setLocationId(resource.locationId ?? '');\n setName(resource.name);\n setDescription(resource.description || '');\n setImageUrl(resource.imageUrl || '');\n setSkills(resource.skills?.join(', ') || '');\n setSchedule(resource.schedule || getDefaultSchedule());\n setDefaultDuration(resource.defaultDurationMinutes || 60);\n setBufferMinutes(resource.bufferMinutes || 0);\n setAdvanceBookingDays(resource.advanceBookingDays || 30);\n setMaxBookingsPerSlot(resource.maxBookingsPerSlot || 1);\n setPrice(resource.price != null ? String(resource.price) : '');\n }, [resource]);\n\n useDetailHeader({\n title: resource?.name || resourceLabel.singular,\n onBack: () => router.push(back),\n });\n\n const handleSave = async () => {\n if (!resource) return;\n if (!name.trim()) {\n setError('Give it a name.');\n return;\n }\n setSaving(true);\n setError(null);\n try {\n await resourcesApi.update(slug, resource.id, {\n ...(categoryId ? { categoryId } : {}),\n // Omitted rather than sent empty: the API reads absence as the main\n // location, which is not the same instruction as an empty string.\n ...(locationId ? { locationId } : {}),\n name: name.trim(),\n description: description.trim() || undefined,\n imageUrl: imageUrl.trim() || undefined,\n skills: skills\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n schedule,\n defaultDurationMinutes: defaultDuration,\n bufferMinutes,\n advanceBookingDays,\n maxBookingsPerSlot,\n price: price ? Number(price) : undefined,\n });\n await refetch();\n setSaved(true);\n setTimeout(() => setSaved(false), 2000);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'That did not save.');\n } finally {\n setSaving(false);\n }\n };\n\n const handleToggleStatus = async () => {\n if (!resource) return;\n setError(null);\n try {\n if (resource.status === 'active') {\n await resourcesApi.deactivate(slug, resource.id);\n } else {\n await resourcesApi.activate(slug, resource.id);\n }\n await refetch();\n } catch {\n setError('Could not change whether it can be booked.');\n }\n };\n\n const handleDelete = async () => {\n if (!resource) return;\n const ok = await confirm({\n title: `Delete ${resource.name}?`,\n description:\n 'Bookings already taken keep their record. This cannot be undone.',\n confirmLabel: 'Delete',\n tone: 'danger',\n });\n if (!ok) return;\n try {\n await resourcesApi.delete(slug, resource.id);\n router.push(back);\n } catch {\n setError('Could not delete it.');\n }\n };\n\n if (isLoading || loadError || !resource) {\n return (\n <DetailStates\n loading={isLoading}\n error={loadError?.message}\n notFoundLabel={`That ${resourceLabel.singular.toLowerCase()} is not here.`}\n back={{ href: back, label: listTitle }}\n onRetry={refetch}\n maxWidth=\"max-w-4xl\"\n />\n );\n }\n\n const inactive = resource.status !== 'active';\n\n return (\n <DetailPage\n maxWidth=\"max-w-4xl\"\n /* Two crumbs, not three. The group is not a place to go back to — and on\n a single-group business it is the same word as the list, so the trail\n read \"Staff / Staff / Ada\". */\n breadcrumbs={[\n { label: listTitle, href: back, icon: <Boxes /> },\n { label: resource.name },\n ]}\n leading={\n resource.imageUrl ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={resource.imageUrl}\n alt=\"\"\n className=\"size-10 squircle object-cover\"\n />\n ) : (\n <GeneratedAvatar name={resource.name} className=\"size-10\" />\n )\n }\n title={resource.name}\n badges={<StatusBadge status={inactive ? 'inactive' : 'active'} />}\n actions={\n <>\n <OverflowMenu>\n <OverflowItem icon={<Power />} onClick={handleToggleStatus}>\n {inactive ? 'Make bookable' : 'Take off the board'}\n </OverflowItem>\n <OverflowItem destructive icon={<Trash2 />} onClick={handleDelete}>\n Delete\n </OverflowItem>\n </OverflowMenu>\n <Button onClick={handleSave} loading={saving}>\n {saved ? 'Saved' : 'Save'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-4\">\n <ErrorBanner>{error}</ErrorBanner>\n\n <EditorSection title=\"What a customer sees\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n {/* THE FACE SITS WITH THE NAME.\n\n A person's photo was the last field on the form and a full\n width dashed zone, so the first thing under \"What a customer\n sees\" was a banner-shaped hole and the identity arrived after\n the description. That is not the shape of an edit-profile\n screen anywhere a merchant has used one — the avatar is small,\n it is at the top, and the name is beside it.\n\n It is 80px in the name's own cell now, which leaves the second\n column to Group and keeps the two-column rhythm the rest of the\n section is written in. A room or a court is unchanged: a\n picture of a place IS wide, and it stays the wide field lower\n down. */}\n <div className=\"flex items-start gap-4\">\n {kind === 'person' && (\n <ImageUpload\n value={imageUrl || undefined}\n onChange={(url) => setImageUrl(url ?? '')}\n label=\"Photo\"\n aspectRatio=\"square\"\n size=\"avatar\"\n />\n )}\n <div className=\"min-w-0 flex-1\">\n <Field\n label={kind === 'person' ? 'Full name' : 'Name'}\n required\n >\n <Input\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n </Field>\n </div>\n </div>\n {/* WHICH GROUP IT IS IN, AND A WAY TO CHANGE IT.\n\n The roster tells a row with no group that it can be put in one\n — \"open one to put it somewhere\" — and the record it opened had\n no such control, so the instruction was false. It is also how a\n court gets moved out of a group called Staff, which is the state\n a lot of these businesses are already in. */}\n {groups.length > 0 && (\n // Bottom-aligned with the name beside it: the photo makes that\n // cell tall, and the name sits at its foot, so a top-aligned\n // Group read a row higher than the field next to it.\n <div className={kind === 'person' ? 'md:self-end' : undefined}>\n <Field label=\"Group\">\n <Select\n value={categoryId}\n onChange={setCategoryId}\n options={groups.map((g) => ({\n value: g.id,\n label: g.labelPlural,\n }))}\n placeholder=\"In no group\"\n />\n </Field>\n </div>\n )}\n {/* Which branch it is at. A single-site business is not asked. */}\n {isMultiSite && (\n <Field label=\"Where\">\n <Select\n value={locationId}\n onChange={setLocationId}\n options={locations.map((l) => ({\n value: l.id,\n label: l.name,\n }))}\n placeholder=\"Main location\"\n />\n </Field>\n )}\n {kind === 'person' && (\n <Field\n label=\"Skills\"\n hint=\"Comma separated. A service can ask for one of these.\"\n >\n <Input\n value={skills}\n onChange={(e) => setSkills(e.target.value)}\n placeholder=\"e.g. Coloring, Braids, Styling\"\n />\n </Field>\n )}\n <div className=\"md:col-span-2\">\n <Field label=\"Description\">\n <Textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n className=\"resize-none\"\n />\n </Field>\n </div>\n {kind !== 'person' && (\n <div className=\"md:col-span-2\">\n <ImageUpload\n value={imageUrl || undefined}\n onChange={(url) => setImageUrl(url ?? '')}\n label=\"Image\"\n aspectRatio=\"video\"\n />\n </div>\n )}\n </div>\n </EditorSection>\n\n <EditorSection title=\"How it is booked\">\n <div className=\"grid grid-cols-2 gap-4 md:grid-cols-4\">\n {!byTheNight && (\n <Field label=\"Duration\" hint=\"Minutes\">\n <NumberField\n min={5}\n decimal={false}\n value={defaultDuration}\n onChange={(v) => setDefaultDuration(v ?? 5)}\n />\n </Field>\n )}\n {!byTheNight && (\n <Field label=\"Gap after\" hint=\"Minutes\">\n <NumberField\n min={0}\n decimal={false}\n value={bufferMinutes}\n onChange={(v) => setBufferMinutes(v ?? 0)}\n />\n </Field>\n )}\n <Field label=\"Booked ahead\" hint=\"Days\">\n <NumberField\n min={1}\n decimal={false}\n value={advanceBookingDays}\n onChange={(v) => setAdvanceBookingDays(v ?? 1)}\n />\n </Field>\n <Field label={byTheNight ? 'Price a night' : 'Price'}>\n <Input\n type=\"number\"\n min={0}\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n placeholder=\"0.00\"\n />\n </Field>\n {(kind === 'space' || kind === 'equipment') && (\n <Field label=\"At once\" hint=\"How many can share it\">\n <NumberField\n min={1}\n decimal={false}\n value={maxBookingsPerSlot}\n onChange={(v) => setMaxBookingsPerSlot(v ?? 1)}\n />\n </Field>\n )}\n </div>\n </EditorSection>\n\n {!byTheNight && (\n <EditorSection title=\"The week it works\">\n <ScheduleEditor\n title={null}\n schedule={schedule}\n onChange={setSchedule}\n />\n </EditorSection>\n )}\n </div>\n </DetailPage>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button } from "@withpotter/ui-kit/button";
|
|
5
|
+
import { Select } from "@withpotter/ui-kit/select";
|
|
6
|
+
import { DateField } from "@withpotter/ui-kit/date-field";
|
|
7
|
+
import { TimeField } from "@withpotter/ui-kit/time-field";
|
|
8
|
+
import { FormDialog } from "@withpotter/ui-kit/form-dialog";
|
|
9
|
+
import { Field } from "@withpotter/ui-kit/field";
|
|
10
|
+
import { ErrorBanner } from "@withpotter/ui-kit/error-banner";
|
|
11
|
+
import { Input } from "@withpotter/ui-kit";
|
|
12
|
+
function BlockTimeModal({
|
|
13
|
+
resources,
|
|
14
|
+
defaultResourceId,
|
|
15
|
+
resourceLabel,
|
|
16
|
+
defaultDate,
|
|
17
|
+
defaultEndDate,
|
|
18
|
+
dateRange = false,
|
|
19
|
+
byTheNight,
|
|
20
|
+
embedded = false,
|
|
21
|
+
saving,
|
|
22
|
+
error,
|
|
23
|
+
onBlock,
|
|
24
|
+
onClose
|
|
25
|
+
}) {
|
|
26
|
+
const [resourceId, setResourceId] = useState(
|
|
27
|
+
defaultResourceId ?? resources[0]?.id ?? ""
|
|
28
|
+
);
|
|
29
|
+
const [date, setDate] = useState(defaultDate);
|
|
30
|
+
const [endDate, setEndDate] = useState(defaultEndDate ?? defaultDate);
|
|
31
|
+
const [startTime, setStartTime] = useState("");
|
|
32
|
+
const [endTime, setEndTime] = useState("");
|
|
33
|
+
const [reason, setReason] = useState("");
|
|
34
|
+
const canSubmit = byTheNight ? Boolean(resourceId && date && endDate && endDate >= date) : Boolean(resourceId && date && startTime && endTime);
|
|
35
|
+
const content = /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
36
|
+
/* @__PURE__ */ jsx(ErrorBanner, { children: error }),
|
|
37
|
+
resources.length > 1 ? /* @__PURE__ */ jsx(Field, { label: resourceLabel, required: true, children: /* @__PURE__ */ jsx(
|
|
38
|
+
Select,
|
|
39
|
+
{
|
|
40
|
+
value: resourceId,
|
|
41
|
+
onChange: setResourceId,
|
|
42
|
+
options: resources.map((r) => ({ value: r.id, label: r.name })),
|
|
43
|
+
searchable: resources.length > 15
|
|
44
|
+
}
|
|
45
|
+
) }) : null,
|
|
46
|
+
byTheNight ? /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
47
|
+
/* @__PURE__ */ jsx(Field, { label: "First night", required: true, children: /* @__PURE__ */ jsx(
|
|
48
|
+
DateField,
|
|
49
|
+
{
|
|
50
|
+
value: date,
|
|
51
|
+
onChange: (next) => {
|
|
52
|
+
setDate(next);
|
|
53
|
+
if (endDate < next) setEndDate(next);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
) }),
|
|
57
|
+
/* @__PURE__ */ jsx(Field, { label: "Last night", required: true, children: /* @__PURE__ */ jsx(DateField, { value: endDate, onChange: setEndDate, min: date }) })
|
|
58
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
59
|
+
embedded ? null : /* @__PURE__ */ jsx(Field, { label: "Date", required: true, children: /* @__PURE__ */ jsx(DateField, { value: date, onChange: setDate }) }),
|
|
60
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
61
|
+
/* @__PURE__ */ jsx(Field, { label: "Starts at", required: true, children: /* @__PURE__ */ jsx(TimeField, { value: startTime, onChange: setStartTime }) }),
|
|
62
|
+
/* @__PURE__ */ jsx(Field, { label: "Ends at", required: true, children: /* @__PURE__ */ jsx(TimeField, { value: endTime, onChange: setEndTime }) })
|
|
63
|
+
] })
|
|
64
|
+
] }),
|
|
65
|
+
/* @__PURE__ */ jsx(
|
|
66
|
+
Field,
|
|
67
|
+
{
|
|
68
|
+
label: "Internal note",
|
|
69
|
+
hint: "Optional. This is never shown to customers.",
|
|
70
|
+
children: /* @__PURE__ */ jsx(
|
|
71
|
+
Input,
|
|
72
|
+
{
|
|
73
|
+
value: reason,
|
|
74
|
+
onChange: (e) => setReason(e.target.value),
|
|
75
|
+
placeholder: "e.g. annual leave, private hire"
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
] });
|
|
81
|
+
const footer = /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
82
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Cancel" }),
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
Button,
|
|
85
|
+
{
|
|
86
|
+
onClick: () => onBlock({
|
|
87
|
+
resourceId,
|
|
88
|
+
date,
|
|
89
|
+
// The whole day, every day in the span. There is no half a
|
|
90
|
+
// night to give back.
|
|
91
|
+
...byTheNight ? {
|
|
92
|
+
endDate,
|
|
93
|
+
startTime: "00:00",
|
|
94
|
+
endTime: "23:59"
|
|
95
|
+
} : { startTime, endTime },
|
|
96
|
+
...dateRange && !byTheNight ? { endDate } : {},
|
|
97
|
+
reason: reason.trim() || void 0
|
|
98
|
+
}),
|
|
99
|
+
loading: saving,
|
|
100
|
+
disabled: !canSubmit,
|
|
101
|
+
children: saving ? "Blocking\u2026" : byTheNight ? "Take it off" : "Block time"
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] });
|
|
105
|
+
if (embedded) {
|
|
106
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-t border-[var(--border-subtle)] pt-4", children: [
|
|
107
|
+
/* @__PURE__ */ jsxs("h3", { className: "text-lg font-semibold text-ink", children: [
|
|
108
|
+
"Block time",
|
|
109
|
+
" ",
|
|
110
|
+
/* @__PURE__ */ jsx("span", { children: dateRange ? `from ${(/* @__PURE__ */ new Date(`${date}T00:00:00`)).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })} to ${(/* @__PURE__ */ new Date(`${endDate}T00:00:00`)).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}` : `on ${(/* @__PURE__ */ new Date(`${date}T00:00:00`)).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}` })
|
|
111
|
+
] }),
|
|
112
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3", children: content }),
|
|
113
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4", children: footer })
|
|
114
|
+
] });
|
|
115
|
+
}
|
|
116
|
+
return /* @__PURE__ */ jsx(
|
|
117
|
+
FormDialog,
|
|
118
|
+
{
|
|
119
|
+
title: "Block time",
|
|
120
|
+
onClose,
|
|
121
|
+
scrollable: false,
|
|
122
|
+
footer,
|
|
123
|
+
children: content
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
BlockTimeModal
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=block-time-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/resources/_components/block-time-modal.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { Select } from '@withpotter/ui-kit/select';\nimport { DateField } from '@withpotter/ui-kit/date-field';\nimport { TimeField } from '@withpotter/ui-kit/time-field';\nimport { FormDialog } from '@withpotter/ui-kit/form-dialog';\nimport { Field } from '@withpotter/ui-kit/field';\nimport { ErrorBanner } from '@withpotter/ui-kit/error-banner';\nimport { Input } from '@withpotter/ui-kit';\n\n/**\n * Take a slot off the board: a day off, a maintenance window, a private hire.\n *\n * `POST /bookings/availability/block` has existed the whole time and had no\n * door in the dashboard, so the only way to stop something being booked was to\n * edit its weekly schedule — which changes every week, not the one Tuesday the\n * chair is being re-covered.\n */\nexport function BlockTimeModal({\n resources,\n defaultResourceId,\n resourceLabel,\n defaultDate,\n defaultEndDate,\n dateRange = false,\n byTheNight,\n embedded = false,\n saving,\n error,\n onBlock,\n onClose,\n}: {\n /** Only the two fields the picker draws — a full Resource satisfies it. */\n resources: Array<{ id: string; name: string }>;\n /** The row the merchant pressed, when they came from one. */\n defaultResourceId?: string;\n resourceLabel: string;\n defaultDate: string;\n defaultEndDate?: string;\n dateRange?: boolean;\n /**\n * A stay is closed in nights, not in hours.\n *\n * Taking an apartment off the market for a week through a form that asks for\n * a date and two clock times means doing it seven times. The same form asks\n * for two dates instead, and the times it does not ask for are the whole day.\n */\n byTheNight?: boolean;\n /** Render inside an existing dialog instead of opening a second one. */\n embedded?: boolean;\n saving: boolean;\n error?: string | null;\n onBlock: (data: {\n resourceId: string;\n date: string;\n endDate?: string;\n startTime: string;\n endTime: string;\n reason?: string;\n }) => void;\n onClose: () => void;\n}) {\n const [resourceId, setResourceId] = useState(\n defaultResourceId ?? resources[0]?.id ?? '',\n );\n const [date, setDate] = useState(defaultDate);\n const [endDate, setEndDate] = useState(defaultEndDate ?? defaultDate);\n const [startTime, setStartTime] = useState('');\n const [endTime, setEndTime] = useState('');\n const [reason, setReason] = useState('');\n\n const canSubmit = byTheNight\n ? Boolean(resourceId && date && endDate && endDate >= date)\n : Boolean(resourceId && date && startTime && endTime);\n\n const content = (\n <div className=\"space-y-4\">\n <ErrorBanner>{error}</ErrorBanner>\n\n {resources.length > 1 ? (\n <Field label={resourceLabel} required>\n <Select\n value={resourceId}\n onChange={setResourceId}\n options={resources.map((r) => ({ value: r.id, label: r.name }))}\n searchable={resources.length > 15}\n />\n </Field>\n ) : null}\n\n {byTheNight ? (\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"First night\" required>\n <DateField\n value={date}\n onChange={(next) => {\n setDate(next);\n // A span cannot end before it starts, and correcting that\n // for the merchant is better than refusing their first date.\n if (endDate < next) setEndDate(next);\n }}\n />\n </Field>\n <Field label=\"Last night\" required>\n <DateField value={endDate} onChange={setEndDate} min={date} />\n </Field>\n </div>\n ) : (\n <>\n {embedded ? null : (\n <Field label=\"Date\" required>\n <DateField value={date} onChange={setDate} />\n </Field>\n )}\n\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Starts at\" required>\n <TimeField value={startTime} onChange={setStartTime} />\n </Field>\n <Field label=\"Ends at\" required>\n <TimeField value={endTime} onChange={setEndTime} />\n </Field>\n </div>\n </>\n )}\n\n <Field\n label=\"Internal note\"\n hint=\"Optional. This is never shown to customers.\"\n >\n <Input\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n placeholder=\"e.g. annual leave, private hire\"\n />\n </Field>\n </div>\n );\n\n const footer = (\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n <Button\n onClick={() =>\n onBlock({\n resourceId,\n date,\n // The whole day, every day in the span. There is no half a\n // night to give back.\n ...(byTheNight\n ? {\n endDate,\n startTime: '00:00',\n endTime: '23:59',\n }\n : { startTime, endTime }),\n ...(dateRange && !byTheNight ? { endDate } : {}),\n reason: reason.trim() || undefined,\n })\n }\n loading={saving}\n disabled={!canSubmit}\n >\n {saving ? 'Blocking…' : byTheNight ? 'Take it off' : 'Block time'}\n </Button>\n </div>\n );\n\n if (embedded) {\n return (\n <div className=\"border-t border-[var(--border-subtle)] pt-4\">\n <h3 className=\"text-lg font-semibold text-ink\">\n Block time{' '}\n <span>\n {dateRange\n ? `from ${new Date(`${date}T00:00:00`).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })} to ${new Date(`${endDate}T00:00:00`).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}`\n : `on ${new Date(`${date}T00:00:00`).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}`}\n </span>\n </h3>\n <div className=\"mt-3\">{content}</div>\n <div className=\"mt-4\">{footer}</div>\n </div>\n );\n }\n\n return (\n <FormDialog\n title=\"Block time\"\n onClose={onClose}\n scrollable={false}\n footer={footer}\n >\n {content}\n </FormDialog>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { Pencil, Users } from "lucide-react";
|
|
5
|
+
import { KIND_OPTIONS } from "@withpotter/lib/resource-types";
|
|
6
|
+
import { InlineDeleteButton } from "@withpotter/ui-kit/inline-delete-button";
|
|
7
|
+
function CategoryCard({
|
|
8
|
+
category,
|
|
9
|
+
onEdit,
|
|
10
|
+
onDelete
|
|
11
|
+
}) {
|
|
12
|
+
const kindOption = KIND_OPTIONS.find((k) => k.value === category.kind);
|
|
13
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-[var(--bg-base)] border border-[var(--border-default)] rounded-md hover:border-[var(--border-strong)] transition-colors", children: [
|
|
14
|
+
/* @__PURE__ */ jsx(Link, { href: `/dashboard/resources/${category.id}`, className: "block p-5", children: /* @__PURE__ */ jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
15
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl", children: category.icon || kindOption?.emoji || "\u{1F4E6}" }),
|
|
16
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
17
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-medium text-[var(--text-primary)]", children: category.labelPlural }),
|
|
18
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-[var(--text-tertiary)] mt-0.5", children: [
|
|
19
|
+
kindOption?.label || category.kind,
|
|
20
|
+
" \xB7",
|
|
21
|
+
" ",
|
|
22
|
+
category.resourceCount,
|
|
23
|
+
" ",
|
|
24
|
+
category.resourceCount === 1 ? category.label.toLowerCase() : category.labelPlural.toLowerCase()
|
|
25
|
+
] })
|
|
26
|
+
] })
|
|
27
|
+
] }) }) }),
|
|
28
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-3 border-t border-[var(--border-default)] bg-[var(--bg-subtle)]", children: [
|
|
29
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm text-[var(--text-secondary)]", children: [
|
|
30
|
+
/* @__PURE__ */ jsx(Users, { className: "w-3.5 h-3.5" }),
|
|
31
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
32
|
+
category.resourceCount,
|
|
33
|
+
" ",
|
|
34
|
+
category.resourceCount === 1 ? "resource" : "resources"
|
|
35
|
+
] })
|
|
36
|
+
] }),
|
|
37
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
38
|
+
/* @__PURE__ */ jsx(
|
|
39
|
+
"button",
|
|
40
|
+
{
|
|
41
|
+
"aria-label": "Edit",
|
|
42
|
+
onClick: (e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
e.stopPropagation();
|
|
45
|
+
onEdit(category);
|
|
46
|
+
},
|
|
47
|
+
className: "p-1.5 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--interactive-hover)] rounded-md cursor-pointer",
|
|
48
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "w-3.5 h-3.5" })
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
/* @__PURE__ */ jsx(
|
|
52
|
+
"div",
|
|
53
|
+
{
|
|
54
|
+
onClick: (e) => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
e.stopPropagation();
|
|
57
|
+
},
|
|
58
|
+
children: /* @__PURE__ */ jsx(
|
|
59
|
+
InlineDeleteButton,
|
|
60
|
+
{
|
|
61
|
+
onDelete: async () => onDelete(category.id),
|
|
62
|
+
confirmText: `Delete "${category.label}"?`
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
] })
|
|
68
|
+
] })
|
|
69
|
+
] });
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
CategoryCard
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=category-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/resources/_components/category-card.tsx"],"sourcesContent":["'use client';\n\nimport Link from 'next/link';\nimport { Pencil, Users } from 'lucide-react';\nimport type { ResourceCategory } from '@withpotter/lib/resource-types';\nimport { KIND_OPTIONS } from '@withpotter/lib/resource-types';\nimport { InlineDeleteButton } from '@withpotter/ui-kit/inline-delete-button';\n\n// ─── Props ───────────────────────────────────────────────────────────────────\n\ninterface CategoryCardProps {\n category: ResourceCategory & { resourceCount: number };\n onEdit: (category: ResourceCategory) => void;\n onDelete: (id: string) => void;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport function CategoryCard({\n category,\n onEdit,\n onDelete,\n}: CategoryCardProps) {\n const kindOption = KIND_OPTIONS.find((k) => k.value === category.kind);\n\n return (\n <div className=\"bg-[var(--bg-base)] border border-[var(--border-default)] rounded-md hover:border-[var(--border-strong)] transition-colors\">\n <Link href={`/dashboard/resources/${category.id}`} className=\"block p-5\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <span className=\"text-2xl\">\n {category.icon || kindOption?.emoji || '📦'}\n </span>\n <div>\n <h3 className=\"text-base font-medium text-[var(--text-primary)]\">\n {category.labelPlural}\n </h3>\n <p className=\"text-xs text-[var(--text-tertiary)] mt-0.5\">\n {kindOption?.label || category.kind} ·{' '}\n {category.resourceCount}{' '}\n {category.resourceCount === 1\n ? category.label.toLowerCase()\n : category.labelPlural.toLowerCase()}\n </p>\n </div>\n </div>\n </div>\n </Link>\n\n {/* Stats row */}\n <div className=\"flex items-center justify-between px-5 py-3 border-t border-[var(--border-default)] bg-[var(--bg-subtle)]\">\n <div className=\"flex items-center gap-1.5 text-sm text-[var(--text-secondary)]\">\n <Users className=\"w-3.5 h-3.5\" />\n <span>\n {category.resourceCount}{' '}\n {category.resourceCount === 1 ? 'resource' : 'resources'}\n </span>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-1\">\n <button\n aria-label=\"Edit\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onEdit(category);\n }}\n className=\"p-1.5 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--interactive-hover)] rounded-md cursor-pointer\"\n >\n <Pencil className=\"w-3.5 h-3.5\" />\n </button>\n <div\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n }}\n >\n <InlineDeleteButton\n onDelete={async () => onDelete(category.id)}\n confirmText={`Delete \"${category.label}\"?`}\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";AA8BY,cAOE,YAPF;AA5BZ,OAAO,UAAU;AACjB,SAAS,QAAQ,aAAa;AAE9B,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AAY5B,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,aAAa,aAAa,KAAK,CAAC,MAAM,EAAE,UAAU,SAAS,IAAI;AAErE,SACE,qBAAC,SAAI,WAAU,8HACb;AAAA,wBAAC,QAAK,MAAM,wBAAwB,SAAS,EAAE,IAAI,WAAU,aAC3D,8BAAC,SAAI,WAAU,oCACb,+BAAC,SAAI,WAAU,2BACb;AAAA,0BAAC,UAAK,WAAU,YACb,mBAAS,QAAQ,YAAY,SAAS,aACzC;AAAA,MACA,qBAAC,SACC;AAAA,4BAAC,QAAG,WAAU,oDACX,mBAAS,aACZ;AAAA,QACA,qBAAC,OAAE,WAAU,8CACV;AAAA,sBAAY,SAAS,SAAS;AAAA,UAAK;AAAA,UAAU;AAAA,UAC7C,SAAS;AAAA,UAAe;AAAA,UACxB,SAAS,kBAAkB,IACxB,SAAS,MAAM,YAAY,IAC3B,SAAS,YAAY,YAAY;AAAA,WACvC;AAAA,SACF;AAAA,OACF,GACF,GACF;AAAA,IAGA,qBAAC,SAAI,WAAU,6GACb;AAAA,2BAAC,SAAI,WAAU,kEACb;AAAA,4BAAC,SAAM,WAAU,eAAc;AAAA,QAC/B,qBAAC,UACE;AAAA,mBAAS;AAAA,UAAe;AAAA,UACxB,SAAS,kBAAkB,IAAI,aAAa;AAAA,WAC/C;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,cAAW;AAAA,YACX,SAAS,CAAC,MAAM;AACd,gBAAE,eAAe;AACjB,gBAAE,gBAAgB;AAClB,qBAAO,QAAQ;AAAA,YACjB;AAAA,YACA,WAAU;AAAA,YAEV,8BAAC,UAAO,WAAU,eAAc;AAAA;AAAA,QAClC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,CAAC,MAAM;AACd,gBAAE,eAAe;AACjB,gBAAE,gBAAgB;AAAA,YACpB;AAAA,YAEA;AAAA,cAAC;AAAA;AAAA,gBACC,UAAU,YAAY,SAAS,SAAS,EAAE;AAAA,gBAC1C,aAAa,WAAW,SAAS,KAAK;AAAA;AAAA,YACxC;AAAA;AAAA,QACF;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import { KIND_OPTIONS } from "@withpotter/lib/resource-types";
|
|
6
|
+
function CategoryFormDialog({
|
|
7
|
+
open,
|
|
8
|
+
category,
|
|
9
|
+
preselectedKind,
|
|
10
|
+
onSave,
|
|
11
|
+
onClose
|
|
12
|
+
}) {
|
|
13
|
+
const isEditing = !!category;
|
|
14
|
+
const [label, setLabel] = useState("");
|
|
15
|
+
const [labelPlural, setLabelPlural] = useState("");
|
|
16
|
+
const [kind, setKind] = useState("person");
|
|
17
|
+
const [icon, setIcon] = useState("");
|
|
18
|
+
const [saving, setSaving] = useState(false);
|
|
19
|
+
const [error, setError] = useState(null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (category) {
|
|
22
|
+
setLabel(category.label);
|
|
23
|
+
setLabelPlural(category.labelPlural);
|
|
24
|
+
setKind(category.kind);
|
|
25
|
+
setIcon(category.icon || "");
|
|
26
|
+
} else {
|
|
27
|
+
const resolvedKind = preselectedKind === "solo" ? "person" : preselectedKind ?? "person";
|
|
28
|
+
setKind(resolvedKind);
|
|
29
|
+
setLabel("");
|
|
30
|
+
setLabelPlural("");
|
|
31
|
+
setIcon("");
|
|
32
|
+
}
|
|
33
|
+
setError(null);
|
|
34
|
+
}, [category, open, preselectedKind]);
|
|
35
|
+
const handleLabelChange = (value) => {
|
|
36
|
+
setLabel(value);
|
|
37
|
+
if (!isEditing || labelPlural === "" || labelPlural === label + "s") {
|
|
38
|
+
setLabelPlural(value ? value + "s" : "");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const handleSubmit = async (e) => {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
if (!label.trim()) {
|
|
44
|
+
setError("Label is required");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!labelPlural.trim()) {
|
|
48
|
+
setError("Plural label is required");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
setSaving(true);
|
|
52
|
+
setError(null);
|
|
53
|
+
try {
|
|
54
|
+
const data = {
|
|
55
|
+
label: label.trim(),
|
|
56
|
+
labelPlural: labelPlural.trim(),
|
|
57
|
+
kind,
|
|
58
|
+
...icon.trim() ? { icon: icon.trim() } : {}
|
|
59
|
+
};
|
|
60
|
+
await onSave(data);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
setError(err instanceof Error ? err.message : "Failed to save");
|
|
63
|
+
} finally {
|
|
64
|
+
setSaving(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
if (!open) return null;
|
|
68
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
69
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50", onClick: onClose }),
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "relative z-10 w-full max-w-lg bg-[var(--bg-base)] border border-[var(--border-default)] rounded-[2px] mx-4", children: [
|
|
71
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--border-default)]", children: [
|
|
72
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-[var(--text-primary)]", children: isEditing ? "Edit Category" : "New Resource Category" }),
|
|
73
|
+
/* @__PURE__ */ jsx(
|
|
74
|
+
"button",
|
|
75
|
+
{
|
|
76
|
+
"aria-label": "Close",
|
|
77
|
+
onClick: onClose,
|
|
78
|
+
className: "p-1 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] cursor-pointer",
|
|
79
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
] }),
|
|
83
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "px-6 py-5 space-y-5", children: [
|
|
84
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
85
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-[var(--text-primary)] mb-2", children: "Type" }),
|
|
86
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: KIND_OPTIONS.map((option) => /* @__PURE__ */ jsxs(
|
|
87
|
+
"button",
|
|
88
|
+
{
|
|
89
|
+
type: "button",
|
|
90
|
+
onClick: () => setKind(option.value),
|
|
91
|
+
className: `flex items-center gap-3 p-3 rounded-md border text-left cursor-pointer transition-colors ${kind === option.value ? "border-brand-500 bg-[var(--accent-secondary)] text-brand-700" : "border-[var(--border-default)] hover:border-[var(--border-strong)] text-[var(--text-secondary)]"}`,
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ jsx("span", { className: "text-xl", children: option.emoji }),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
95
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: option.label }),
|
|
96
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--text-tertiary)]", children: option.description })
|
|
97
|
+
] })
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
option.value
|
|
101
|
+
)) })
|
|
102
|
+
] }),
|
|
103
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
"label",
|
|
106
|
+
{
|
|
107
|
+
htmlFor: "category-label",
|
|
108
|
+
className: "block text-sm font-medium text-[var(--text-primary)] mb-1.5",
|
|
109
|
+
children: "Label (singular)"
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
/* @__PURE__ */ jsx(
|
|
113
|
+
"input",
|
|
114
|
+
{
|
|
115
|
+
id: "category-label",
|
|
116
|
+
type: "text",
|
|
117
|
+
value: label,
|
|
118
|
+
onChange: (e) => handleLabelChange(e.target.value),
|
|
119
|
+
placeholder: 'e.g., "Doctor", "Stylist", "Room"',
|
|
120
|
+
maxLength: 100,
|
|
121
|
+
className: "w-full px-3 py-2 text-sm border border-[var(--border-default)] rounded-md focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500"
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
] }),
|
|
125
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
126
|
+
/* @__PURE__ */ jsx(
|
|
127
|
+
"label",
|
|
128
|
+
{
|
|
129
|
+
htmlFor: "category-label-plural",
|
|
130
|
+
className: "block text-sm font-medium text-[var(--text-primary)] mb-1.5",
|
|
131
|
+
children: "Label (plural)"
|
|
132
|
+
}
|
|
133
|
+
),
|
|
134
|
+
/* @__PURE__ */ jsx(
|
|
135
|
+
"input",
|
|
136
|
+
{
|
|
137
|
+
id: "category-label-plural",
|
|
138
|
+
type: "text",
|
|
139
|
+
value: labelPlural,
|
|
140
|
+
onChange: (e) => setLabelPlural(e.target.value),
|
|
141
|
+
placeholder: 'e.g., "Doctors", "Stylists", "Rooms"',
|
|
142
|
+
maxLength: 100,
|
|
143
|
+
className: "w-full px-3 py-2 text-sm border border-[var(--border-default)] rounded-md focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500"
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
] }),
|
|
147
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
148
|
+
/* @__PURE__ */ jsx(
|
|
149
|
+
"label",
|
|
150
|
+
{
|
|
151
|
+
htmlFor: "category-icon",
|
|
152
|
+
className: "block text-sm font-medium text-[var(--text-primary)] mb-1.5",
|
|
153
|
+
children: "Icon (optional emoji)"
|
|
154
|
+
}
|
|
155
|
+
),
|
|
156
|
+
/* @__PURE__ */ jsx(
|
|
157
|
+
"input",
|
|
158
|
+
{
|
|
159
|
+
id: "category-icon",
|
|
160
|
+
type: "text",
|
|
161
|
+
value: icon,
|
|
162
|
+
onChange: (e) => setIcon(e.target.value),
|
|
163
|
+
placeholder: "\u{1FA7A} or \u{1F487}",
|
|
164
|
+
maxLength: 10,
|
|
165
|
+
className: "w-full px-3 py-2 text-sm border border-[var(--border-default)] rounded-md focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500"
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
] }),
|
|
169
|
+
error && /* @__PURE__ */ jsx("div", { className: "text-sm text-[var(--error)] bg-[var(--error-bg)] px-3 py-2 rounded-md border border-[var(--error)]/20", children: error }),
|
|
170
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 pt-2", children: [
|
|
171
|
+
/* @__PURE__ */ jsx(
|
|
172
|
+
"button",
|
|
173
|
+
{
|
|
174
|
+
type: "button",
|
|
175
|
+
onClick: onClose,
|
|
176
|
+
className: "px-4 py-2 text-sm font-medium text-[var(--text-secondary)] bg-[var(--bg-base)] border border-[var(--border-default)] rounded-md hover:bg-[var(--interactive-hover)] cursor-pointer",
|
|
177
|
+
children: "Cancel"
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
"button",
|
|
182
|
+
{
|
|
183
|
+
type: "submit",
|
|
184
|
+
disabled: saving,
|
|
185
|
+
className: "px-4 py-2 text-sm font-medium text-[var(--text-inverse)] bg-brand-500 rounded-md hover:bg-brand-600 disabled:opacity-50 cursor-pointer",
|
|
186
|
+
children: saving ? "Saving..." : isEditing ? "Save Changes" : "Create Category"
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
] })
|
|
190
|
+
] })
|
|
191
|
+
] })
|
|
192
|
+
] });
|
|
193
|
+
}
|
|
194
|
+
export {
|
|
195
|
+
CategoryFormDialog
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=category-form-dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/resources/_components/category-form-dialog.tsx"],"sourcesContent":["'use client';\n\nimport { useState, useEffect } from 'react';\nimport { X } from 'lucide-react';\nimport type {\n ResourceCategory,\n ResourceCategoryKind,\n CreateResourceCategoryInput,\n UpdateResourceCategoryInput,\n} from '@withpotter/lib/resource-types';\nimport { KIND_OPTIONS } from '@withpotter/lib/resource-types';\n\n// ─── Props ───────────────────────────────────────────────────────────────────\n\ninterface CategoryFormDialogProps {\n open: boolean;\n category?: ResourceCategory | null;\n /** Pre-select a kind when opened from the empty-state picker */\n preselectedKind?: string | null;\n onSave: (\n data: CreateResourceCategoryInput & Partial<UpdateResourceCategoryInput>,\n ) => Promise<void>;\n onClose: () => void;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport function CategoryFormDialog({\n open,\n category,\n preselectedKind,\n onSave,\n onClose,\n}: CategoryFormDialogProps) {\n const isEditing = !!category;\n const [label, setLabel] = useState('');\n const [labelPlural, setLabelPlural] = useState('');\n const [kind, setKind] = useState<ResourceCategoryKind>('person');\n const [icon, setIcon] = useState('');\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (category) {\n setLabel(category.label);\n setLabelPlural(category.labelPlural);\n setKind(category.kind);\n setIcon(category.icon || '');\n } else {\n // Apply preselected kind (from empty-state picker)\n const resolvedKind =\n preselectedKind === 'solo'\n ? 'person'\n : ((preselectedKind as ResourceCategoryKind) ?? 'person');\n setKind(resolvedKind);\n setLabel('');\n setLabelPlural('');\n setIcon('');\n }\n setError(null);\n }, [category, open, preselectedKind]);\n\n // Auto-generate plural from label\n const handleLabelChange = (value: string) => {\n setLabel(value);\n if (!isEditing || labelPlural === '' || labelPlural === label + 's') {\n setLabelPlural(value ? value + 's' : '');\n }\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!label.trim()) {\n setError('Label is required');\n return;\n }\n if (!labelPlural.trim()) {\n setError('Plural label is required');\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n const data = {\n label: label.trim(),\n labelPlural: labelPlural.trim(),\n kind,\n ...(icon.trim() ? { icon: icon.trim() } : {}),\n };\n await onSave(data);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to save');\n } finally {\n setSaving(false);\n }\n };\n\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div className=\"absolute inset-0 bg-black/50\" onClick={onClose} />\n\n {/* Dialog */}\n <div className=\"relative z-10 w-full max-w-lg bg-[var(--bg-base)] border border-[var(--border-default)] rounded-[2px] mx-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-[var(--border-default)]\">\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">\n {isEditing ? 'Edit Category' : 'New Resource Category'}\n </h2>\n <button\n aria-label=\"Close\"\n onClick={onClose}\n className=\"p-1 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] cursor-pointer\"\n >\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"px-6 py-5 space-y-5\">\n {/* Kind Selection */}\n <div>\n <label className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\">\n Type\n </label>\n <div className=\"grid grid-cols-2 gap-2\">\n {KIND_OPTIONS.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => setKind(option.value)}\n className={`flex items-center gap-3 p-3 rounded-md border text-left cursor-pointer transition-colors ${\n kind === option.value\n ? 'border-brand-500 bg-[var(--accent-secondary)] text-brand-700'\n : 'border-[var(--border-default)] hover:border-[var(--border-strong)] text-[var(--text-secondary)]'\n }`}\n >\n <span className=\"text-xl\">{option.emoji}</span>\n <div>\n <div className=\"text-sm font-medium\">{option.label}</div>\n <div className=\"text-xs text-[var(--text-tertiary)]\">\n {option.description}\n </div>\n </div>\n </button>\n ))}\n </div>\n </div>\n\n {/* Label */}\n <div>\n <label\n htmlFor=\"category-label\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-1.5\"\n >\n Label (singular)\n </label>\n <input\n id=\"category-label\"\n type=\"text\"\n value={label}\n onChange={(e) => handleLabelChange(e.target.value)}\n placeholder='e.g., \"Doctor\", \"Stylist\", \"Room\"'\n maxLength={100}\n className=\"w-full px-3 py-2 text-sm border border-[var(--border-default)] rounded-md focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500\"\n />\n </div>\n\n {/* Label Plural */}\n <div>\n <label\n htmlFor=\"category-label-plural\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-1.5\"\n >\n Label (plural)\n </label>\n <input\n id=\"category-label-plural\"\n type=\"text\"\n value={labelPlural}\n onChange={(e) => setLabelPlural(e.target.value)}\n placeholder='e.g., \"Doctors\", \"Stylists\", \"Rooms\"'\n maxLength={100}\n className=\"w-full px-3 py-2 text-sm border border-[var(--border-default)] rounded-md focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500\"\n />\n </div>\n\n {/* Icon (optional) */}\n <div>\n <label\n htmlFor=\"category-icon\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-1.5\"\n >\n Icon (optional emoji)\n </label>\n <input\n id=\"category-icon\"\n type=\"text\"\n value={icon}\n onChange={(e) => setIcon(e.target.value)}\n placeholder=\"🩺 or 💇\"\n maxLength={10}\n className=\"w-full px-3 py-2 text-sm border border-[var(--border-default)] rounded-md focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500\"\n />\n </div>\n\n {/* Error */}\n {error && (\n <div className=\"text-sm text-[var(--error)] bg-[var(--error-bg)] px-3 py-2 rounded-md border border-[var(--error)]/20\">\n {error}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-[var(--text-secondary)] bg-[var(--bg-base)] border border-[var(--border-default)] rounded-md hover:bg-[var(--interactive-hover)] cursor-pointer\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={saving}\n className=\"px-4 py-2 text-sm font-medium text-[var(--text-inverse)] bg-brand-500 rounded-md hover:bg-brand-600 disabled:opacity-50 cursor-pointer\"\n >\n {saving\n ? 'Saving...'\n : isEditing\n ? 'Save Changes'\n : 'Create Category'}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|