@licklist/design 0.78.5-dev.107 → 0.78.5-dev.108
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/bitbucket-pipelines.yml +4 -13
- package/dist/Maintenance/Maintenance.scss.js +1 -1
- package/dist/index.js +2 -0
- package/dist/product-set/form/ProductsControl.d.ts +1 -2
- package/dist/product-set/form/ProductsControl.d.ts.map +1 -1
- package/dist/product-set/form/ProductsControl.js +24 -0
- package/dist/v2/components/ActionMenu/ActionMenu.scss.js +1 -1
- package/dist/v2/components/Badge/Badge.scss.js +1 -1
- package/dist/v2/components/Button/Button.scss.js +1 -1
- package/dist/v2/components/Button/GhostButton.scss.js +1 -1
- package/dist/v2/components/Checkbox/Checkbox.scss.js +1 -1
- package/dist/v2/components/DataTable/DataTable.d.ts.map +1 -1
- package/dist/v2/components/DataTable/DataTable.js +2 -86
- package/dist/v2/components/IconButton/IconButton.scss.js +1 -1
- package/dist/v2/components/Modal/DeleteModal.d.ts.map +1 -1
- package/dist/v2/components/Modal/DeleteModal.js +11 -13
- package/dist/v2/components/Modal/DeleteModal.scss.js +1 -1
- package/dist/v2/components/NPSScore/NPSScore.scss.js +1 -1
- package/dist/v2/components/NewTabs/NewTabs.scss.js +1 -1
- package/dist/v2/components/PeriodCard/PeriodCard.d.ts +66 -0
- package/dist/v2/components/PeriodCard/PeriodCard.d.ts.map +1 -0
- package/dist/v2/components/PeriodCard/PeriodCard.js +351 -0
- package/dist/v2/components/PeriodCard/PeriodCard.scss.js +6 -0
- package/dist/v2/components/PeriodCard/index.d.ts +3 -0
- package/dist/v2/components/PeriodCard/index.d.ts.map +1 -0
- package/dist/v2/components/ReorderRow/ReorderRow.d.ts +24 -0
- package/dist/v2/components/ReorderRow/ReorderRow.d.ts.map +1 -0
- package/dist/v2/components/ReorderRow/ReorderRow.js +109 -0
- package/dist/v2/components/ReorderRow/ReorderRow.scss.js +6 -0
- package/dist/v2/components/ReorderRow/index.d.ts +3 -0
- package/dist/v2/components/ReorderRow/index.d.ts.map +1 -0
- package/dist/v2/components/Select/Select.scss.js +1 -1
- package/dist/v2/components/StatusBadge/StatusBadge.scss.js +1 -1
- package/dist/v2/components/StepIndicator/StepIndicator.scss.js +1 -1
- package/dist/v2/components/Tabs/Tabs.scss.js +1 -1
- package/dist/v2/components/Toggle/Toggle.d.ts.map +1 -1
- package/dist/v2/components/Toggle/Toggle.js +5 -8
- package/dist/v2/components/Tooltip/Tooltip.scss.js +1 -1
- package/dist/v2/components/UserAvatar/UserAvatar.scss.js +1 -1
- package/dist/v2/components/UserPanel/UserPanel.scss.js +1 -1
- package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.scss.js +1 -1
- package/dist/v2/components/ZoneCard/ZoneCard.scss.js +1 -1
- package/dist/v2/components/index.d.ts +4 -0
- package/dist/v2/components/index.d.ts.map +1 -1
- package/dist/v2/dashboard-analytics/chart/Chart.scss.js +1 -1
- package/dist/v2/dashboard-analytics/metric-card/MetricCard.scss.js +1 -1
- package/dist/v2/dashboard-analytics/venue-card/VenueCard.scss.js +1 -1
- package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.scss.js +1 -1
- package/dist/v2/icons/index.js +16 -1
- package/dist/v2/index.d.ts +8 -0
- package/dist/v2/index.d.ts.map +1 -1
- package/dist/v2/navigation/DashboardLayout/AdminSidebar.scss.js +1 -1
- package/dist/v2/navigation/DashboardLayout/DashboardLayout.scss.js +1 -1
- package/dist/v2/navigation/DashboardLayout/ProviderSidebar.scss.js +1 -1
- package/dist/v2/navigation/DashboardLayout/TopNavigation.scss.js +1 -1
- package/dist/v2/pages/Settings/SettingsTabs.scss.js +1 -1
- package/dist/v2/pages/Settings/components/SidebarCustomisation.js +5 -0
- package/dist/v2/pages/Settings/components/SidebarCustomisation.scss.js +1 -1
- package/dist/v2/pages/Settings/components/SidebarNavItem.js +5 -0
- package/dist/v2/pages/auth/AuthLayout/AuthLayout.scss.js +1 -1
- package/dist/v2/shadcn/components/ui/accordion.d.ts +8 -0
- package/dist/v2/shadcn/components/ui/accordion.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/alert-dialog.d.ts +21 -0
- package/dist/v2/shadcn/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/alert.d.ts +9 -0
- package/dist/v2/shadcn/components/ui/alert.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/aspect-ratio.d.ts +4 -0
- package/dist/v2/shadcn/components/ui/aspect-ratio.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/avatar.d.ts +7 -0
- package/dist/v2/shadcn/components/ui/avatar.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/badge.d.ts +10 -0
- package/dist/v2/shadcn/components/ui/badge.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/breadcrumb.d.ts +20 -0
- package/dist/v2/shadcn/components/ui/breadcrumb.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/button.d.ts +14 -0
- package/dist/v2/shadcn/components/ui/button.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/calendar.d.ts +9 -0
- package/dist/v2/shadcn/components/ui/calendar.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/card.d.ts +9 -0
- package/dist/v2/shadcn/components/ui/card.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/carousel.d.ts +19 -0
- package/dist/v2/shadcn/components/ui/carousel.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/checkbox.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/checkbox.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/checkbox.js +115 -0
- package/dist/v2/shadcn/components/ui/checkbox.scss.js +6 -0
- package/dist/v2/shadcn/components/ui/collapsible.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/collapsible.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/command.d.ts +83 -0
- package/dist/v2/shadcn/components/ui/command.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/context-menu.d.ts +28 -0
- package/dist/v2/shadcn/components/ui/context-menu.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/dialog.d.ts +20 -0
- package/dist/v2/shadcn/components/ui/dialog.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/dialog.js +169 -0
- package/dist/v2/shadcn/components/ui/drawer.d.ts +23 -0
- package/dist/v2/shadcn/components/ui/drawer.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/dropdown-menu.d.ts +28 -0
- package/dist/v2/shadcn/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/form.d.ts +24 -0
- package/dist/v2/shadcn/components/ui/form.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/hover-card.d.ts +7 -0
- package/dist/v2/shadcn/components/ui/hover-card.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/input-otp.d.ts +35 -0
- package/dist/v2/shadcn/components/ui/input-otp.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/input.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/input.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/label.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/label.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/menubar.d.ts +34 -0
- package/dist/v2/shadcn/components/ui/menubar.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/navigation-menu.d.ts +13 -0
- package/dist/v2/shadcn/components/ui/navigation-menu.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/pagination.d.ts +29 -0
- package/dist/v2/shadcn/components/ui/pagination.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/popover.d.ts +7 -0
- package/dist/v2/shadcn/components/ui/popover.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/progress.d.ts +5 -0
- package/dist/v2/shadcn/components/ui/progress.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/radio-card.d.ts +12 -0
- package/dist/v2/shadcn/components/ui/radio-card.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/radio-group.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/radio-group.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/scroll-area.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/select.d.ts +14 -0
- package/dist/v2/shadcn/components/ui/select.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/separator.d.ts +5 -0
- package/dist/v2/shadcn/components/ui/separator.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/sheet.d.ts +26 -0
- package/dist/v2/shadcn/components/ui/sheet.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/sidebar.d.ts +67 -0
- package/dist/v2/shadcn/components/ui/sidebar.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/skeleton.d.ts +3 -0
- package/dist/v2/shadcn/components/ui/skeleton.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/slider.d.ts +5 -0
- package/dist/v2/shadcn/components/ui/slider.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/switch.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/switch.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/switch.js +115 -0
- package/dist/v2/shadcn/components/ui/switch.scss.js +6 -0
- package/dist/v2/shadcn/components/ui/table-pagination.d.ts +11 -0
- package/dist/v2/shadcn/components/ui/table-pagination.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/table.d.ts +11 -0
- package/dist/v2/shadcn/components/ui/table.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/tabs.d.ts +8 -0
- package/dist/v2/shadcn/components/ui/tabs.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/textarea.d.ts +6 -0
- package/dist/v2/shadcn/components/ui/textarea.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/toast.d.ts +16 -0
- package/dist/v2/shadcn/components/ui/toast.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/toaster.d.ts +2 -0
- package/dist/v2/shadcn/components/ui/toaster.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/toggle-group.d.ts +13 -0
- package/dist/v2/shadcn/components/ui/toggle-group.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/toggle.d.ts +13 -0
- package/dist/v2/shadcn/components/ui/toggle.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/tooltip.d.ts +8 -0
- package/dist/v2/shadcn/components/ui/tooltip.d.ts.map +1 -0
- package/dist/v2/shadcn/components/ui/use-toast.d.ts +3 -0
- package/dist/v2/shadcn/components/ui/use-toast.d.ts.map +1 -0
- package/dist/v2/shadcn/hooks/use-mobile.d.ts +2 -0
- package/dist/v2/shadcn/hooks/use-mobile.d.ts.map +1 -0
- package/dist/v2/shadcn/hooks/use-toast.d.ts +45 -0
- package/dist/v2/shadcn/hooks/use-toast.d.ts.map +1 -0
- package/dist/v2/shadcn/index.d.ts +20 -0
- package/dist/v2/shadcn/index.d.ts.map +1 -0
- package/dist/v2/shadcn/lib/utils.d.ts +3 -0
- package/dist/v2/shadcn/lib/utils.d.ts.map +1 -0
- package/dist/v2/shadcn/lib/utils.js +11 -0
- package/dist/v2/shadcn/styles/globals.css +112 -0
- package/dist/v2/styles/form/NewInput.scss.js +1 -1
- package/package.json +6 -6
- package/rollup.config.js +2 -16
- package/src/iframe/payment/payment-status-page/PaymentStatusPage.tsx +1 -1
- package/src/product-set/form/ProductsControl.tsx +1 -2
- package/src/v2/components/DataTable/DataTable.tsx +1 -23
- package/src/v2/components/Modal/DeleteModal.tsx +20 -12
- package/src/v2/components/PeriodCard/PeriodCard.scss +157 -0
- package/src/v2/components/PeriodCard/PeriodCard.stories.tsx +245 -0
- package/src/v2/components/PeriodCard/PeriodCard.tsx +350 -0
- package/src/v2/components/PeriodCard/index.ts +8 -0
- package/src/v2/components/ReorderRow/ReorderRow.scss +68 -0
- package/src/v2/components/ReorderRow/ReorderRow.stories.tsx +124 -0
- package/src/v2/components/ReorderRow/ReorderRow.tsx +88 -0
- package/src/v2/components/ReorderRow/index.ts +2 -0
- package/src/v2/components/Toggle/Toggle.tsx +5 -6
- package/src/v2/components/index.ts +6 -0
- package/src/v2/index.ts +82 -0
- package/src/v2/shadcn/_reference/AccountManagerCard.tsx +45 -0
- package/src/v2/shadcn/_reference/AffiliatesTable.tsx +178 -0
- package/src/v2/shadcn/_reference/AuditArchive.tsx +165 -0
- package/src/v2/shadcn/_reference/AuditContent.tsx +270 -0
- package/src/v2/shadcn/_reference/AutomationsGeneralSettings.tsx +251 -0
- package/src/v2/shadcn/_reference/AvatarUpload.tsx +150 -0
- package/src/v2/shadcn/_reference/BookingsSummaryCard.tsx +268 -0
- package/src/v2/shadcn/_reference/CodeCleanUpAudit.tsx +274 -0
- package/src/v2/shadcn/_reference/CompaniesTable.tsx +387 -0
- package/src/v2/shadcn/_reference/ComponentAudit.tsx +239 -0
- package/src/v2/shadcn/_reference/ConfigureSettingsCard.tsx +95 -0
- package/src/v2/shadcn/_reference/CustomerCard.tsx +155 -0
- package/src/v2/shadcn/_reference/DashboardCards.tsx +50 -0
- package/src/v2/shadcn/_reference/DashboardFooter.tsx +18 -0
- package/src/v2/shadcn/_reference/DiarySettings.tsx +187 -0
- package/src/v2/shadcn/_reference/DiaryView.tsx +998 -0
- package/src/v2/shadcn/_reference/EmptyState.tsx +76 -0
- package/src/v2/shadcn/_reference/EntityInfoCard.tsx +48 -0
- package/src/v2/shadcn/_reference/ExistingUserAssignments.tsx +131 -0
- package/src/v2/shadcn/_reference/FeatureToggle.tsx +72 -0
- package/src/v2/shadcn/_reference/FlowCard.tsx +170 -0
- package/src/v2/shadcn/_reference/FlowsContent.tsx +688 -0
- package/src/v2/shadcn/_reference/FlowsGeneralSettings.tsx +27 -0
- package/src/v2/shadcn/_reference/GeneralSettings.tsx +33 -0
- package/src/v2/shadcn/_reference/InventoryGeneralSettings.tsx +82 -0
- package/src/v2/shadcn/_reference/LanguageSelector.tsx +97 -0
- package/src/v2/shadcn/_reference/LoadingScreen.tsx +25 -0
- package/src/v2/shadcn/_reference/LoadingSpinner.tsx +41 -0
- package/src/v2/shadcn/_reference/ManagedClientsList.tsx +121 -0
- package/src/v2/shadcn/_reference/NPSScore.tsx +379 -0
- package/src/v2/shadcn/_reference/NPSSummaryCard.tsx +181 -0
- package/src/v2/shadcn/_reference/NotificationBanner.tsx +129 -0
- package/src/v2/shadcn/_reference/NotificationPanel.tsx +208 -0
- package/src/v2/shadcn/_reference/OnlineUsersCard.tsx +73 -0
- package/src/v2/shadcn/_reference/ProtectedRoute.tsx +39 -0
- package/src/v2/shadcn/_reference/ProvidersTable.tsx +353 -0
- package/src/v2/shadcn/_reference/QuickAddPanel.tsx +1057 -0
- package/src/v2/shadcn/_reference/QuickFilters.tsx +112 -0
- package/src/v2/shadcn/_reference/ScheduleView.tsx +410 -0
- package/src/v2/shadcn/_reference/ScrollToTop.tsx +14 -0
- package/src/v2/shadcn/_reference/SecondaryNav.tsx +50 -0
- package/src/v2/shadcn/_reference/SecuritySettings.tsx +258 -0
- package/src/v2/shadcn/_reference/SessionDetailView.tsx +294 -0
- package/src/v2/shadcn/_reference/Sidebar.tsx +14 -0
- package/src/v2/shadcn/_reference/SidebarAwareLayout.tsx +30 -0
- package/src/v2/shadcn/_reference/SidebarLabelCustomization.tsx +285 -0
- package/src/v2/shadcn/_reference/SimulationBanner.tsx +57 -0
- package/src/v2/shadcn/_reference/SortControls.tsx +65 -0
- package/src/v2/shadcn/_reference/StatusBadge.tsx +49 -0
- package/src/v2/shadcn/_reference/StyleGuideContent.tsx +331 -0
- package/src/v2/shadcn/_reference/TableActionMenu.tsx +126 -0
- package/src/v2/shadcn/_reference/ThemeProvider.tsx +119 -0
- package/src/v2/shadcn/_reference/ThemeSettings.tsx +73 -0
- package/src/v2/shadcn/_reference/TopNavigation.tsx +332 -0
- package/src/v2/shadcn/_reference/UserActivityHistory.tsx +209 -0
- package/src/v2/shadcn/_reference/UserLanguageSettings.tsx +94 -0
- package/src/v2/shadcn/_reference/UserPanel.tsx +472 -0
- package/src/v2/shadcn/_reference/UsersTable.tsx +1023 -0
- package/src/v2/shadcn/_reference/WaiverForm.tsx +301 -0
- package/src/v2/shadcn/_reference/WaiversGeneralSettings.tsx +46 -0
- package/src/v2/shadcn/_reference/WaiversTable.tsx +290 -0
- package/src/v2/shadcn/_reference/WaiversTemplatesSettings.tsx +416 -0
- package/src/v2/shadcn/_reference/ai/AIChatPanel.tsx +313 -0
- package/src/v2/shadcn/_reference/ai/AIChatSearchBar.tsx +36 -0
- package/src/v2/shadcn/_reference/ai/ChatInteractiveBlock.tsx +298 -0
- package/src/v2/shadcn/_reference/ai/ChatMessageContent.tsx +40 -0
- package/src/v2/shadcn/_reference/ai/parseInteractiveBlocks.ts +142 -0
- package/src/v2/shadcn/_reference/auth/AuthLayout.tsx +55 -0
- package/src/v2/shadcn/_reference/auth/CreatePasswordForm.tsx +285 -0
- package/src/v2/shadcn/_reference/auth/CreatePasswordPanel.tsx +20 -0
- package/src/v2/shadcn/_reference/auth/LoginFooter.tsx +14 -0
- package/src/v2/shadcn/_reference/auth/LoginForm.tsx +205 -0
- package/src/v2/shadcn/_reference/auth/LoginPanel.tsx +41 -0
- package/src/v2/shadcn/_reference/auth/ResetPasswordForm.tsx +102 -0
- package/src/v2/shadcn/_reference/auth/ResetPasswordPanel.tsx +20 -0
- package/src/v2/shadcn/_reference/auth/VerifyEmailForm.tsx +95 -0
- package/src/v2/shadcn/_reference/auth/VerifyEmailPanel.tsx +20 -0
- package/src/v2/shadcn/_reference/email/EmailAttachment.tsx +119 -0
- package/src/v2/shadcn/_reference/email/EmailAutomation.tsx +92 -0
- package/src/v2/shadcn/_reference/email/EmailPlaceholders.tsx +64 -0
- package/src/v2/shadcn/_reference/email/UnlayerEmailEditor.tsx +41 -0
- package/src/v2/shadcn/_reference/email/emailTemplateData.ts +53 -0
- package/src/v2/shadcn/_reference/emptyStateIcons.tsx +103 -0
- package/src/v2/shadcn/_reference/games/MazeGame.tsx +394 -0
- package/src/v2/shadcn/_reference/games/RunnerGame.tsx +497 -0
- package/src/v2/shadcn/_reference/logos/BookedLogoFull.tsx +36 -0
- package/src/v2/shadcn/_reference/logos/BookedLogoMark.tsx +31 -0
- package/src/v2/shadcn/_reference/logos/BookedLogoNew.tsx +36 -0
- package/src/v2/shadcn/_reference/pricing/DynamicPricingRulesEditor.tsx +401 -0
- package/src/v2/shadcn/_reference/pricing/DynamicPricingTierCard.tsx +77 -0
- package/src/v2/shadcn/_reference/pricing/DynamicPricingTiersList.tsx +218 -0
- package/src/v2/shadcn/_reference/pricing/PricingCalendar.tsx +810 -0
- package/src/v2/shadcn/_reference/pricing/PricingPeriodCard.tsx +152 -0
- package/src/v2/shadcn/_reference/pricing/PricingPeriodForm.tsx +377 -0
- package/src/v2/shadcn/_reference/pricing/PricingPeriodsList.tsx +213 -0
- package/src/v2/shadcn/_reference/pricing/getRuleSummary.ts +39 -0
- package/src/v2/shadcn/_reference/products/AvailabilityRulesSection.tsx +184 -0
- package/src/v2/shadcn/_reference/products/AvailabilitySection.tsx +677 -0
- package/src/v2/shadcn/_reference/products/BookingTypeConfigOptions.tsx +40 -0
- package/src/v2/shadcn/_reference/products/CapacityPeriodsSection.tsx +238 -0
- package/src/v2/shadcn/_reference/products/DynamicPricingTiersSection.tsx +131 -0
- package/src/v2/shadcn/_reference/products/GiftCardOrdersTab.tsx +192 -0
- package/src/v2/shadcn/_reference/products/GiftCardSettings.tsx +342 -0
- package/src/v2/shadcn/_reference/products/PackageProductsSection.tsx +322 -0
- package/src/v2/shadcn/_reference/products/PricingSection.tsx +173 -0
- package/src/v2/shadcn/_reference/products/ProductTypeFields.tsx +353 -0
- package/src/v2/shadcn/_reference/products/ProductTypeIcon.tsx +95 -0
- package/src/v2/shadcn/_reference/products/VariablePricingSection.tsx +140 -0
- package/src/v2/shadcn/_reference/products/productTypeConfig.ts +182 -0
- package/src/v2/shadcn/_reference/shared/BackButton.tsx +50 -0
- package/src/v2/shadcn/_reference/shared/CancelConfirmationDialog.tsx +18 -0
- package/src/v2/shadcn/_reference/shared/ConfirmationDialog.tsx +136 -0
- package/src/v2/shadcn/_reference/shared/DeleteConfirmationDialog.tsx +18 -0
- package/src/v2/shadcn/_reference/shared/DeleteEntityPage.tsx +221 -0
- package/src/v2/shadcn/_reference/shared/SidebarIcons.tsx +108 -0
- package/src/v2/shadcn/_reference/shared/UnifiedSidebar.tsx +722 -0
- package/src/v2/shadcn/_reference/tables/BulkActionsBar.tsx +68 -0
- package/src/v2/shadcn/_reference/tables/DataTable.tsx +221 -0
- package/src/v2/shadcn/_reference/tables/TableControls.tsx +94 -0
- package/src/v2/shadcn/_reference/tables/index.ts +3 -0
- package/src/v2/shadcn/_reference/tables/types.ts +79 -0
- package/src/v2/shadcn/_reference/zones/LegacyZoneSettings.tsx +299 -0
- package/src/v2/shadcn/components/ui/accordion.stories.tsx +63 -0
- package/src/v2/shadcn/components/ui/accordion.tsx +52 -0
- package/src/v2/shadcn/components/ui/alert-dialog.stories.tsx +44 -0
- package/src/v2/shadcn/components/ui/alert-dialog.tsx +104 -0
- package/src/v2/shadcn/components/ui/alert.stories.tsx +44 -0
- package/src/v2/shadcn/components/ui/alert.tsx +43 -0
- package/src/v2/shadcn/components/ui/aspect-ratio.stories.tsx +46 -0
- package/src/v2/shadcn/components/ui/aspect-ratio.tsx +5 -0
- package/src/v2/shadcn/components/ui/avatar.stories.tsx +39 -0
- package/src/v2/shadcn/components/ui/avatar.tsx +38 -0
- package/src/v2/shadcn/components/ui/badge.stories.tsx +17 -0
- package/src/v2/shadcn/components/ui/badge.tsx +30 -0
- package/src/v2/shadcn/components/ui/breadcrumb.stories.tsx +91 -0
- package/src/v2/shadcn/components/ui/breadcrumb.tsx +90 -0
- package/src/v2/shadcn/components/ui/button.stories.tsx +20 -0
- package/src/v2/shadcn/components/ui/button.tsx +60 -0
- package/src/v2/shadcn/components/ui/calendar.stories.tsx +61 -0
- package/src/v2/shadcn/components/ui/calendar.tsx +54 -0
- package/src/v2/shadcn/components/ui/card.stories.tsx +37 -0
- package/src/v2/shadcn/components/ui/card.tsx +43 -0
- package/src/v2/shadcn/components/ui/carousel.stories.tsx +92 -0
- package/src/v2/shadcn/components/ui/carousel.tsx +224 -0
- package/src/v2/shadcn/components/ui/checkbox.scss +38 -0
- package/src/v2/shadcn/components/ui/checkbox.stories.tsx +23 -0
- package/src/v2/shadcn/components/ui/checkbox.tsx +24 -0
- package/src/v2/shadcn/components/ui/collapsible.stories.tsx +59 -0
- package/src/v2/shadcn/components/ui/collapsible.tsx +9 -0
- package/src/v2/shadcn/components/ui/command.stories.tsx +70 -0
- package/src/v2/shadcn/components/ui/command.tsx +132 -0
- package/src/v2/shadcn/components/ui/context-menu.stories.tsx +72 -0
- package/src/v2/shadcn/components/ui/context-menu.tsx +178 -0
- package/src/v2/shadcn/components/ui/dialog.stories.tsx +67 -0
- package/src/v2/shadcn/components/ui/dialog.tsx +95 -0
- package/src/v2/shadcn/components/ui/drawer.stories.tsx +50 -0
- package/src/v2/shadcn/components/ui/drawer.tsx +87 -0
- package/src/v2/shadcn/components/ui/dropdown-menu.stories.tsx +73 -0
- package/src/v2/shadcn/components/ui/dropdown-menu.tsx +179 -0
- package/src/v2/shadcn/components/ui/form.stories.tsx +105 -0
- package/src/v2/shadcn/components/ui/form.tsx +129 -0
- package/src/v2/shadcn/components/ui/hover-card.stories.tsx +35 -0
- package/src/v2/shadcn/components/ui/hover-card.tsx +27 -0
- package/src/v2/shadcn/components/ui/input-otp.stories.tsx +72 -0
- package/src/v2/shadcn/components/ui/input-otp.tsx +61 -0
- package/src/v2/shadcn/components/ui/input.stories.tsx +16 -0
- package/src/v2/shadcn/components/ui/input.tsx +25 -0
- package/src/v2/shadcn/components/ui/label.stories.tsx +13 -0
- package/src/v2/shadcn/components/ui/label.tsx +17 -0
- package/src/v2/shadcn/components/ui/menubar.stories.tsx +86 -0
- package/src/v2/shadcn/components/ui/menubar.tsx +207 -0
- package/src/v2/shadcn/components/ui/navigation-menu.stories.tsx +68 -0
- package/src/v2/shadcn/components/ui/navigation-menu.tsx +120 -0
- package/src/v2/shadcn/components/ui/pagination.stories.tsx +78 -0
- package/src/v2/shadcn/components/ui/pagination.tsx +81 -0
- package/src/v2/shadcn/components/ui/popover.stories.tsx +44 -0
- package/src/v2/shadcn/components/ui/popover.tsx +29 -0
- package/src/v2/shadcn/components/ui/progress.stories.tsx +17 -0
- package/src/v2/shadcn/components/ui/progress.tsx +23 -0
- package/src/v2/shadcn/components/ui/radio-card.stories.tsx +68 -0
- package/src/v2/shadcn/components/ui/radio-card.tsx +52 -0
- package/src/v2/shadcn/components/ui/radio-group.stories.tsx +77 -0
- package/src/v2/shadcn/components/ui/radio-group.tsx +35 -0
- package/src/v2/shadcn/components/ui/scroll-area.stories.tsx +56 -0
- package/src/v2/shadcn/components/ui/scroll-area.tsx +38 -0
- package/src/v2/shadcn/components/ui/select.stories.tsx +60 -0
- package/src/v2/shadcn/components/ui/select.tsx +148 -0
- package/src/v2/shadcn/components/ui/separator.stories.tsx +30 -0
- package/src/v2/shadcn/components/ui/separator.tsx +20 -0
- package/src/v2/shadcn/components/ui/sheet.stories.tsx +115 -0
- package/src/v2/shadcn/components/ui/sheet.tsx +107 -0
- package/src/v2/shadcn/components/ui/sidebar.stories.tsx +167 -0
- package/src/v2/shadcn/components/ui/sidebar.tsx +637 -0
- package/src/v2/shadcn/components/ui/skeleton.stories.tsx +36 -0
- package/src/v2/shadcn/components/ui/skeleton.tsx +7 -0
- package/src/v2/shadcn/components/ui/slider.stories.tsx +16 -0
- package/src/v2/shadcn/components/ui/slider.tsx +23 -0
- package/src/v2/shadcn/components/ui/switch.scss +63 -0
- package/src/v2/shadcn/components/ui/switch.stories.tsx +23 -0
- package/src/v2/shadcn/components/ui/switch.tsx +24 -0
- package/src/v2/shadcn/components/ui/table-pagination.stories.tsx +81 -0
- package/src/v2/shadcn/components/ui/table-pagination.tsx +61 -0
- package/src/v2/shadcn/components/ui/table.stories.tsx +40 -0
- package/src/v2/shadcn/components/ui/table.tsx +72 -0
- package/src/v2/shadcn/components/ui/tabs.stories.tsx +85 -0
- package/src/v2/shadcn/components/ui/tabs.tsx +53 -0
- package/src/v2/shadcn/components/ui/textarea.stories.tsx +15 -0
- package/src/v2/shadcn/components/ui/textarea.tsx +21 -0
- package/src/v2/shadcn/components/ui/toast.stories.tsx +77 -0
- package/src/v2/shadcn/components/ui/toast.tsx +111 -0
- package/src/v2/shadcn/components/ui/toaster.stories.tsx +46 -0
- package/src/v2/shadcn/components/ui/toaster.tsx +24 -0
- package/src/v2/shadcn/components/ui/toggle-group.stories.tsx +95 -0
- package/src/v2/shadcn/components/ui/toggle-group.tsx +49 -0
- package/src/v2/shadcn/components/ui/toggle.stories.tsx +18 -0
- package/src/v2/shadcn/components/ui/toggle.tsx +37 -0
- package/src/v2/shadcn/components/ui/tooltip.stories.tsx +57 -0
- package/src/v2/shadcn/components/ui/tooltip.tsx +28 -0
- package/src/v2/shadcn/components/ui/use-toast.ts +3 -0
- package/src/v2/shadcn/hooks/use-mobile.tsx +19 -0
- package/src/v2/shadcn/hooks/use-toast.ts +184 -0
- package/src/v2/shadcn/index.ts +76 -0
- package/src/v2/shadcn/lib/utils.ts +6 -0
- package/src/v2/shadcn/styles/globals.css +112 -0
- package/.vscode/settings.json +0 -3
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import { Input } from '../ui/input';
|
|
3
|
+
import { Label } from '../ui/label';
|
|
4
|
+
import { Switch } from '../ui/switch';
|
|
5
|
+
import { Button } from '../ui/button';
|
|
6
|
+
import { IconPlusThick, IconDelete, IconGiftCard } from '../../../icons';
|
|
7
|
+
import { supabase } from '@/integrations/supabase/client';
|
|
8
|
+
import { getListingThumbnailStyle } from '@/utils/imageAspectRatio';
|
|
9
|
+
|
|
10
|
+
export interface Denomination {
|
|
11
|
+
value: string;
|
|
12
|
+
image_url: string | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface GiftCardConfig {
|
|
16
|
+
denominations: Denomination[];
|
|
17
|
+
allow_custom_value: boolean;
|
|
18
|
+
custom_min: string;
|
|
19
|
+
custom_max: string;
|
|
20
|
+
custom_multiples_of: string;
|
|
21
|
+
allow_partial_payment: boolean; // true = allow partial, false = one-time use
|
|
22
|
+
allow_send_to_someone: boolean;
|
|
23
|
+
allow_schedule_delivery: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_GIFT_CARD_CONFIG: GiftCardConfig = {
|
|
27
|
+
denominations: [{ value: '', image_url: null }],
|
|
28
|
+
allow_custom_value: false,
|
|
29
|
+
custom_min: '',
|
|
30
|
+
custom_max: '',
|
|
31
|
+
custom_multiples_of: '',
|
|
32
|
+
allow_partial_payment: true,
|
|
33
|
+
allow_send_to_someone: false,
|
|
34
|
+
allow_schedule_delivery: false,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function parseGiftCardConfig(priceTiers: any): GiftCardConfig {
|
|
38
|
+
if (!priceTiers || typeof priceTiers !== 'object') return { ...DEFAULT_GIFT_CARD_CONFIG };
|
|
39
|
+
|
|
40
|
+
const config = priceTiers as any;
|
|
41
|
+
return {
|
|
42
|
+
denominations: Array.isArray(config.denominations) && config.denominations.length > 0
|
|
43
|
+
? config.denominations
|
|
44
|
+
: [{ value: '', image_url: null }],
|
|
45
|
+
allow_custom_value: !!config.allow_custom_value,
|
|
46
|
+
custom_min: config.custom_min?.toString() || '',
|
|
47
|
+
custom_max: config.custom_max?.toString() || '',
|
|
48
|
+
custom_multiples_of: config.custom_multiples_of?.toString() || '',
|
|
49
|
+
allow_partial_payment: config.allow_partial_payment !== false,
|
|
50
|
+
allow_send_to_someone: !!config.allow_send_to_someone,
|
|
51
|
+
allow_schedule_delivery: !!config.allow_schedule_delivery,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function serializeGiftCardConfig(config: GiftCardConfig): Record<string, any> {
|
|
56
|
+
return {
|
|
57
|
+
denominations: config.denominations.filter(d => d.value !== ''),
|
|
58
|
+
allow_custom_value: config.allow_custom_value,
|
|
59
|
+
custom_min: config.custom_min ? parseFloat(config.custom_min) : null,
|
|
60
|
+
custom_max: config.custom_max ? parseFloat(config.custom_max) : null,
|
|
61
|
+
custom_multiples_of: config.custom_multiples_of ? parseFloat(config.custom_multiples_of) : null,
|
|
62
|
+
allow_partial_payment: config.allow_partial_payment,
|
|
63
|
+
allow_send_to_someone: config.allow_send_to_someone,
|
|
64
|
+
allow_schedule_delivery: config.allow_schedule_delivery,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface GiftCardSettingsProps {
|
|
69
|
+
config: GiftCardConfig;
|
|
70
|
+
onChange: (config: GiftCardConfig) => void;
|
|
71
|
+
expiryMonths: string;
|
|
72
|
+
onExpiryChange: (value: string) => void;
|
|
73
|
+
imageAspectRatio?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const GiftCardSettings: React.FC<GiftCardSettingsProps> = ({
|
|
77
|
+
config,
|
|
78
|
+
onChange,
|
|
79
|
+
expiryMonths,
|
|
80
|
+
onExpiryChange,
|
|
81
|
+
imageAspectRatio = '16:9',
|
|
82
|
+
}) => {
|
|
83
|
+
const thumbnailStyle = getListingThumbnailStyle(imageAspectRatio);
|
|
84
|
+
const fileInputRefs = useRef<Record<number, HTMLInputElement | null>>({});
|
|
85
|
+
|
|
86
|
+
const updateDenomination = (index: number, field: keyof Denomination, value: any) => {
|
|
87
|
+
const updated = [...config.denominations];
|
|
88
|
+
updated[index] = { ...updated[index], [field]: value };
|
|
89
|
+
onChange({ ...config, denominations: updated });
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const addDenomination = () => {
|
|
93
|
+
onChange({
|
|
94
|
+
...config,
|
|
95
|
+
denominations: [...config.denominations, { value: '', image_url: null }],
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const removeDenomination = (index: number) => {
|
|
100
|
+
if (config.denominations.length <= 1) return;
|
|
101
|
+
const updated = config.denominations.filter((_, i) => i !== index);
|
|
102
|
+
onChange({ ...config, denominations: updated });
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const handleDenominationImage = async (index: number, file: File) => {
|
|
106
|
+
const validTypes = ['image/jpeg', 'image/png', 'image/webp'];
|
|
107
|
+
if (!validTypes.includes(file.type)) return;
|
|
108
|
+
|
|
109
|
+
const fileExt = file.name.split('.').pop();
|
|
110
|
+
const fileName = `denomination-${Date.now()}-${index}.${fileExt}`;
|
|
111
|
+
const filePath = `products/${fileName}`;
|
|
112
|
+
|
|
113
|
+
const { error } = await supabase.storage.from('products').upload(filePath, file);
|
|
114
|
+
if (error) {
|
|
115
|
+
console.error('Error uploading denomination image:', error);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { data: { publicUrl } } = supabase.storage.from('products').getPublicUrl(filePath);
|
|
120
|
+
updateDenomination(index, 'image_url', publicUrl);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const handleValueBlur = (index: number) => {
|
|
124
|
+
const raw = config.denominations[index].value;
|
|
125
|
+
if (raw === '') return;
|
|
126
|
+
const num = parseFloat(raw);
|
|
127
|
+
if (!isNaN(num)) {
|
|
128
|
+
updateDenomination(index, 'value', num.toFixed(2));
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const handleCustomFieldBlur = (field: 'custom_min' | 'custom_max' | 'custom_multiples_of') => {
|
|
133
|
+
const raw = config[field];
|
|
134
|
+
if (raw === '') return;
|
|
135
|
+
const num = parseFloat(raw);
|
|
136
|
+
if (!isNaN(num)) {
|
|
137
|
+
onChange({ ...config, [field]: num.toFixed(2) });
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div className="flex flex-col gap-4 pt-4 border-t border-border-primary self-stretch">
|
|
143
|
+
<h2 className="text-label-primary text-xl font-semibold">Gift Card Settings</h2>
|
|
144
|
+
|
|
145
|
+
{/* Expires in - matching waiver form pattern */}
|
|
146
|
+
<div className="flex flex-col gap-2">
|
|
147
|
+
<Label htmlFor="expiry-months">Expires in</Label>
|
|
148
|
+
<div className="flex items-center gap-3">
|
|
149
|
+
<Input
|
|
150
|
+
id="expiry-months"
|
|
151
|
+
type="text"
|
|
152
|
+
inputMode="numeric"
|
|
153
|
+
value={expiryMonths}
|
|
154
|
+
onChange={(e) => onExpiryChange(e.target.value)}
|
|
155
|
+
className="bg-surface-secondary w-24"
|
|
156
|
+
/>
|
|
157
|
+
<span className="text-label-primary">Months</span>
|
|
158
|
+
</div>
|
|
159
|
+
<p className="text-label-secondary text-sm">
|
|
160
|
+
How long the gift card will remain valid after purchase.
|
|
161
|
+
</p>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Denominations */}
|
|
165
|
+
<div className="flex flex-col gap-3">
|
|
166
|
+
<Label>Denominations</Label>
|
|
167
|
+
<div className="flex flex-col gap-0 divide-y divide-border-primary">
|
|
168
|
+
{config.denominations.map((denom, index) => (
|
|
169
|
+
<div key={index} className="flex items-center gap-3 py-3">
|
|
170
|
+
{/* Denomination image thumbnail */}
|
|
171
|
+
<label className="cursor-pointer flex-shrink-0 group">
|
|
172
|
+
<input
|
|
173
|
+
ref={(el) => { fileInputRefs.current[index] = el; }}
|
|
174
|
+
type="file"
|
|
175
|
+
accept="image/jpeg,image/png,image/webp"
|
|
176
|
+
onChange={(e) => {
|
|
177
|
+
const file = e.target.files?.[0];
|
|
178
|
+
if (file) handleDenominationImage(index, file);
|
|
179
|
+
}}
|
|
180
|
+
className="hidden"
|
|
181
|
+
/>
|
|
182
|
+
<div style={thumbnailStyle} className="rounded-md bg-surface-secondary border border-border-primary flex items-center justify-center overflow-hidden group-hover:opacity-80 transition-opacity">
|
|
183
|
+
{denom.image_url ? (
|
|
184
|
+
<img src={denom.image_url} alt="" className="w-full h-full object-cover" />
|
|
185
|
+
) : (
|
|
186
|
+
<IconGiftCard className="w-4 h-4 fill-fill-secondary" />
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
</label>
|
|
190
|
+
|
|
191
|
+
{/* Value input with £ prefix */}
|
|
192
|
+
<div className="relative flex-1">
|
|
193
|
+
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-label-secondary text-sm">£</span>
|
|
194
|
+
<Input
|
|
195
|
+
type="text"
|
|
196
|
+
inputMode="decimal"
|
|
197
|
+
value={denom.value}
|
|
198
|
+
onChange={(e) => updateDenomination(index, 'value', e.target.value)}
|
|
199
|
+
onBlur={() => handleValueBlur(index)}
|
|
200
|
+
className="pl-7"
|
|
201
|
+
/>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Remove button - danger soft icon pattern */}
|
|
205
|
+
{config.denominations.length > 1 && (
|
|
206
|
+
<Button
|
|
207
|
+
variant="ghost"
|
|
208
|
+
size="icon"
|
|
209
|
+
onClick={() => removeDenomination(index)}
|
|
210
|
+
className="h-8 w-8 bg-surface-danger-soft border-transparent hover:!bg-surface-danger-soft-hover [&_path]:fill-fill-danger"
|
|
211
|
+
>
|
|
212
|
+
<IconDelete className="w-4 h-4" />
|
|
213
|
+
</Button>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
))}
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<Button variant="ghost" size="sm" onClick={addDenomination} withIcon className="self-start">
|
|
220
|
+
<IconPlusThick className="w-4 h-4" />
|
|
221
|
+
Add Denomination
|
|
222
|
+
</Button>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
{/* Allow custom value toggle */}
|
|
226
|
+
<div className="flex flex-col gap-4 pt-4 border-t border-border-primary">
|
|
227
|
+
<label className="flex items-center gap-3 cursor-pointer">
|
|
228
|
+
<Switch
|
|
229
|
+
checked={config.allow_custom_value}
|
|
230
|
+
onCheckedChange={(checked) => onChange({ ...config, allow_custom_value: checked })}
|
|
231
|
+
/>
|
|
232
|
+
<span className="text-label-primary text-sm font-medium">Allow custom value</span>
|
|
233
|
+
</label>
|
|
234
|
+
|
|
235
|
+
{config.allow_custom_value && (
|
|
236
|
+
<div className="flex flex-col gap-4">
|
|
237
|
+
<div className="flex gap-4">
|
|
238
|
+
<div className="flex flex-col gap-2 flex-1">
|
|
239
|
+
<Label htmlFor="custom-min">Min value</Label>
|
|
240
|
+
<div className="relative">
|
|
241
|
+
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-label-secondary text-sm">£</span>
|
|
242
|
+
<Input
|
|
243
|
+
id="custom-min"
|
|
244
|
+
type="text"
|
|
245
|
+
inputMode="decimal"
|
|
246
|
+
value={config.custom_min}
|
|
247
|
+
onChange={(e) => onChange({ ...config, custom_min: e.target.value })}
|
|
248
|
+
onBlur={() => handleCustomFieldBlur('custom_min')}
|
|
249
|
+
className="pl-7"
|
|
250
|
+
/>
|
|
251
|
+
</div>
|
|
252
|
+
<p className="text-label-secondary text-xs">
|
|
253
|
+
Leave blank to default to the 'Multiples of' value.
|
|
254
|
+
</p>
|
|
255
|
+
</div>
|
|
256
|
+
<div className="flex flex-col gap-2 flex-1">
|
|
257
|
+
<Label htmlFor="custom-max">Max value</Label>
|
|
258
|
+
<div className="relative">
|
|
259
|
+
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-label-secondary text-sm">£</span>
|
|
260
|
+
<Input
|
|
261
|
+
id="custom-max"
|
|
262
|
+
type="text"
|
|
263
|
+
inputMode="decimal"
|
|
264
|
+
value={config.custom_max}
|
|
265
|
+
onChange={(e) => onChange({ ...config, custom_max: e.target.value })}
|
|
266
|
+
onBlur={() => handleCustomFieldBlur('custom_max')}
|
|
267
|
+
className="pl-7"
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
<p className="text-label-secondary text-xs">
|
|
271
|
+
Leave blank for no maximum.
|
|
272
|
+
</p>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
<div className="flex flex-col gap-2">
|
|
276
|
+
<Label htmlFor="custom-multiples">Multiples of</Label>
|
|
277
|
+
<div className="relative w-32">
|
|
278
|
+
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-label-secondary text-sm">£</span>
|
|
279
|
+
<Input
|
|
280
|
+
id="custom-multiples"
|
|
281
|
+
type="text"
|
|
282
|
+
inputMode="decimal"
|
|
283
|
+
value={config.custom_multiples_of}
|
|
284
|
+
onChange={(e) => onChange({ ...config, custom_multiples_of: e.target.value })}
|
|
285
|
+
onBlur={() => handleCustomFieldBlur('custom_multiples_of')}
|
|
286
|
+
className="pl-7"
|
|
287
|
+
/>
|
|
288
|
+
</div>
|
|
289
|
+
<p className="text-label-secondary text-xs">
|
|
290
|
+
e.g. if set to 5, customers can choose 10, 15, 20 etc.
|
|
291
|
+
</p>
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
)}
|
|
295
|
+
</div>
|
|
296
|
+
{/* Redemption & Delivery Options */}
|
|
297
|
+
<div className="flex flex-col gap-4 pt-4 border-t border-border-primary">
|
|
298
|
+
<label className="flex items-center gap-3 cursor-pointer">
|
|
299
|
+
<Switch
|
|
300
|
+
checked={!config.allow_partial_payment}
|
|
301
|
+
onCheckedChange={(checked) => onChange({ ...config, allow_partial_payment: !checked })}
|
|
302
|
+
/>
|
|
303
|
+
<div className="flex flex-col">
|
|
304
|
+
<span className="text-label-primary text-sm font-medium">One time use</span>
|
|
305
|
+
<span className="text-label-secondary text-xs">The full gift card balance must be used in a single transaction.</span>
|
|
306
|
+
</div>
|
|
307
|
+
</label>
|
|
308
|
+
|
|
309
|
+
<label className="flex items-center gap-3 cursor-pointer">
|
|
310
|
+
<Switch
|
|
311
|
+
checked={config.allow_send_to_someone}
|
|
312
|
+
onCheckedChange={(checked) => onChange({
|
|
313
|
+
...config,
|
|
314
|
+
allow_send_to_someone: checked,
|
|
315
|
+
// Reset schedule delivery when send to someone is disabled
|
|
316
|
+
allow_schedule_delivery: checked ? config.allow_schedule_delivery : false,
|
|
317
|
+
})}
|
|
318
|
+
/>
|
|
319
|
+
<div className="flex flex-col">
|
|
320
|
+
<span className="text-label-primary text-sm font-medium">Send to someone</span>
|
|
321
|
+
<span className="text-label-secondary text-xs">Allow purchasers to send the gift card directly to a recipient via email.</span>
|
|
322
|
+
</div>
|
|
323
|
+
</label>
|
|
324
|
+
|
|
325
|
+
{config.allow_send_to_someone && (
|
|
326
|
+
<label className="flex items-center gap-3 cursor-pointer">
|
|
327
|
+
<Switch
|
|
328
|
+
checked={config.allow_schedule_delivery}
|
|
329
|
+
onCheckedChange={(checked) => onChange({ ...config, allow_schedule_delivery: checked })}
|
|
330
|
+
/>
|
|
331
|
+
<div className="flex flex-col">
|
|
332
|
+
<span className="text-label-primary text-sm font-medium">Schedule delivery date</span>
|
|
333
|
+
<span className="text-label-secondary text-xs">Allow purchasers to choose a future date for the gift card email to be sent.</span>
|
|
334
|
+
</div>
|
|
335
|
+
</label>
|
|
336
|
+
)}
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export default GiftCardSettings;
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import React, { useEffect, useState, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
DndContext,
|
|
4
|
+
closestCenter,
|
|
5
|
+
KeyboardSensor,
|
|
6
|
+
PointerSensor,
|
|
7
|
+
useSensor,
|
|
8
|
+
useSensors,
|
|
9
|
+
DragEndEvent,
|
|
10
|
+
} from '@dnd-kit/core';
|
|
11
|
+
import {
|
|
12
|
+
arrayMove,
|
|
13
|
+
SortableContext,
|
|
14
|
+
sortableKeyboardCoordinates,
|
|
15
|
+
verticalListSortingStrategy,
|
|
16
|
+
useSortable,
|
|
17
|
+
} from '@dnd-kit/sortable';
|
|
18
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
19
|
+
import { Button } from '../ui/button';
|
|
20
|
+
import { Input } from '../ui/input';
|
|
21
|
+
import { Label } from '../ui/label';
|
|
22
|
+
import {
|
|
23
|
+
Select,
|
|
24
|
+
SelectContent,
|
|
25
|
+
SelectItem,
|
|
26
|
+
SelectTrigger,
|
|
27
|
+
SelectValue,
|
|
28
|
+
} from '../ui/select';
|
|
29
|
+
import { IconPlus, IconDelete, IconGripVertical } from '../../../icons';
|
|
30
|
+
import { supabase } from '@/integrations/supabase/client';
|
|
31
|
+
|
|
32
|
+
export interface PackageItem {
|
|
33
|
+
id: string; // unique key for sortable
|
|
34
|
+
product_id: string;
|
|
35
|
+
product_name?: string;
|
|
36
|
+
item_type: 'product' | 'break';
|
|
37
|
+
break_minutes?: number;
|
|
38
|
+
break_label?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface PackageProductsSectionProps {
|
|
42
|
+
providerId?: string;
|
|
43
|
+
items: PackageItem[];
|
|
44
|
+
onChange: (items: PackageItem[]) => void;
|
|
45
|
+
packageMode?: 'fixed' | 'flexible';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface ProductOption {
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
type: string;
|
|
52
|
+
duration: number | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─── Sortable Item Row ──────────────────────────────────────────────────────
|
|
56
|
+
const SortablePackageItem: React.FC<{
|
|
57
|
+
item: PackageItem;
|
|
58
|
+
index: number;
|
|
59
|
+
products: ProductOption[];
|
|
60
|
+
loading: boolean;
|
|
61
|
+
onUpdate: (index: number, field: keyof PackageItem, value: any) => void;
|
|
62
|
+
onRemove: (index: number) => void;
|
|
63
|
+
}> = ({ item, index, products, loading, onUpdate, onRemove }) => {
|
|
64
|
+
const {
|
|
65
|
+
attributes,
|
|
66
|
+
listeners,
|
|
67
|
+
setNodeRef,
|
|
68
|
+
transform,
|
|
69
|
+
transition,
|
|
70
|
+
isDragging,
|
|
71
|
+
} = useSortable({ id: item.id });
|
|
72
|
+
|
|
73
|
+
const style = {
|
|
74
|
+
transform: CSS.Transform.toString(transform),
|
|
75
|
+
transition,
|
|
76
|
+
opacity: isDragging ? 0.5 : 1,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div ref={setNodeRef} style={style} className="flex items-end gap-2">
|
|
81
|
+
<div
|
|
82
|
+
{...attributes}
|
|
83
|
+
{...listeners}
|
|
84
|
+
className="p-1.5 mb-1 rounded-md cursor-grab active:cursor-grabbing fill-fill-secondary hover:fill-fill-primary hover:bg-surface-secondary transition-colors flex-shrink-0"
|
|
85
|
+
>
|
|
86
|
+
<IconGripVertical size={20} />
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{item.item_type === 'break' ? (
|
|
90
|
+
<div className="flex-1 flex items-end gap-3">
|
|
91
|
+
<div className="flex-1">
|
|
92
|
+
<Input
|
|
93
|
+
type="text"
|
|
94
|
+
value={item.break_label ?? 'Activity Break'}
|
|
95
|
+
onChange={(e) => onUpdate(index, 'break_label', e.target.value)}
|
|
96
|
+
placeholder="e.g. Safety Briefing"
|
|
97
|
+
className="bg-surface-secondary border-dashed border-border-secondary"
|
|
98
|
+
/>
|
|
99
|
+
</div>
|
|
100
|
+
<div className="w-32">
|
|
101
|
+
<div className="flex items-center gap-2">
|
|
102
|
+
<Input
|
|
103
|
+
type="number"
|
|
104
|
+
value={item.break_minutes ?? ''}
|
|
105
|
+
onChange={(e) => onUpdate(index, 'break_minutes', e.target.value ? parseInt(e.target.value) : '')}
|
|
106
|
+
placeholder="15"
|
|
107
|
+
className="w-20"
|
|
108
|
+
min={1}
|
|
109
|
+
/>
|
|
110
|
+
<span className="text-label-secondary text-xs">min</span>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
) : (
|
|
115
|
+
<div className="flex-1">
|
|
116
|
+
<Select
|
|
117
|
+
value={item.product_id}
|
|
118
|
+
onValueChange={(val) => onUpdate(index, 'product_id', val)}
|
|
119
|
+
>
|
|
120
|
+
<SelectTrigger className="bg-surface-primary">
|
|
121
|
+
<SelectValue placeholder={loading ? 'Loading...' : 'Select product'} />
|
|
122
|
+
</SelectTrigger>
|
|
123
|
+
<SelectContent className="bg-surface-primary border border-border-primary z-50">
|
|
124
|
+
{products.map((product) => (
|
|
125
|
+
<SelectItem key={product.id} value={product.id}>
|
|
126
|
+
{product.name}
|
|
127
|
+
</SelectItem>
|
|
128
|
+
))}
|
|
129
|
+
{products.length === 0 && !loading && (
|
|
130
|
+
<div className="px-2 py-1.5 text-label-secondary text-sm">
|
|
131
|
+
No products available
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
</SelectContent>
|
|
135
|
+
</Select>
|
|
136
|
+
</div>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
<Button
|
|
140
|
+
variant="ghost"
|
|
141
|
+
size="icon"
|
|
142
|
+
type="button"
|
|
143
|
+
onClick={() => onRemove(index)}
|
|
144
|
+
className="h-8 w-8 bg-surface-danger-soft border-transparent hover:!bg-surface-danger-soft-hover [&_path]:fill-fill-danger"
|
|
145
|
+
>
|
|
146
|
+
<IconDelete className="w-4 h-4" />
|
|
147
|
+
</Button>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// ─── Main Component ─────────────────────────────────────────────────────────
|
|
153
|
+
let nextId = 1;
|
|
154
|
+
const generateItemId = () => `pkg-item-${Date.now()}-${nextId++}`;
|
|
155
|
+
|
|
156
|
+
const PackageProductsSection: React.FC<PackageProductsSectionProps> = ({
|
|
157
|
+
providerId,
|
|
158
|
+
items,
|
|
159
|
+
onChange,
|
|
160
|
+
packageMode,
|
|
161
|
+
}) => {
|
|
162
|
+
const [products, setProducts] = useState<ProductOption[]>([]);
|
|
163
|
+
const [loading, setLoading] = useState(false);
|
|
164
|
+
|
|
165
|
+
const sensors = useSensors(
|
|
166
|
+
useSensor(PointerSensor),
|
|
167
|
+
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
const fetchProducts = async () => {
|
|
172
|
+
if (!providerId) return;
|
|
173
|
+
setLoading(true);
|
|
174
|
+
try {
|
|
175
|
+
const { data, error } = await supabase
|
|
176
|
+
.from('products')
|
|
177
|
+
.select('id, name, type, duration')
|
|
178
|
+
.eq('provider_id', providerId)
|
|
179
|
+
.neq('type', 'packages')
|
|
180
|
+
.eq('status', 'active')
|
|
181
|
+
.order('name');
|
|
182
|
+
|
|
183
|
+
if (error) throw error;
|
|
184
|
+
setProducts(data || []);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('Error fetching products:', error);
|
|
187
|
+
} finally {
|
|
188
|
+
setLoading(false);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
fetchProducts();
|
|
193
|
+
}, [providerId]);
|
|
194
|
+
|
|
195
|
+
// Ensure all items have an id for sortable
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
const needsIds = items.some(item => !item.id);
|
|
198
|
+
if (needsIds) {
|
|
199
|
+
onChange(items.map(item => item.id ? item : { ...item, id: generateItemId() }));
|
|
200
|
+
}
|
|
201
|
+
}, []);
|
|
202
|
+
|
|
203
|
+
const calculatedDuration = useMemo(() => {
|
|
204
|
+
let total = 0;
|
|
205
|
+
for (const item of items) {
|
|
206
|
+
if (item.item_type === 'break') {
|
|
207
|
+
total += item.break_minutes || 0;
|
|
208
|
+
} else if (item.product_id) {
|
|
209
|
+
const product = products.find(p => p.id === item.product_id);
|
|
210
|
+
if (product?.duration) {
|
|
211
|
+
total += product.duration;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return total;
|
|
216
|
+
}, [items, products]);
|
|
217
|
+
|
|
218
|
+
const addProduct = () => {
|
|
219
|
+
onChange([...items, { id: generateItemId(), product_id: '', item_type: 'product' }]);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const addBreak = () => {
|
|
223
|
+
onChange([...items, { id: generateItemId(), product_id: '', item_type: 'break', break_minutes: 15, break_label: 'Activity Break' }]);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const updateItem = (index: number, field: keyof PackageItem, value: any) => {
|
|
227
|
+
const newItems = [...items];
|
|
228
|
+
if (field === 'product_id') {
|
|
229
|
+
const product = products.find(p => p.id === value);
|
|
230
|
+
newItems[index] = { ...newItems[index], product_id: value, product_name: product?.name };
|
|
231
|
+
} else {
|
|
232
|
+
newItems[index] = { ...newItems[index], [field]: value };
|
|
233
|
+
}
|
|
234
|
+
onChange(newItems);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const removeItem = (index: number) => {
|
|
238
|
+
onChange(items.filter((_, i) => i !== index));
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const handleDragEnd = (event: DragEndEvent) => {
|
|
242
|
+
const { active, over } = event;
|
|
243
|
+
if (!over || active.id === over.id) return;
|
|
244
|
+
|
|
245
|
+
const oldIndex = items.findIndex(item => item.id === active.id);
|
|
246
|
+
const newIndex = items.findIndex(item => item.id === over.id);
|
|
247
|
+
onChange(arrayMove(items, oldIndex, newIndex));
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const showBreaks = packageMode !== 'flexible';
|
|
251
|
+
const itemIds = items.map(item => item.id).filter(Boolean);
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<div className="flex flex-col gap-6 pt-4 border-t border-border-primary">
|
|
255
|
+
<div>
|
|
256
|
+
<h2 className="text-label-primary text-xl font-semibold">Package Contents</h2>
|
|
257
|
+
<p className="text-label-secondary text-sm mt-1">
|
|
258
|
+
{packageMode === 'flexible'
|
|
259
|
+
? 'Add the products included in this package. Guests will choose their own times for each activity.'
|
|
260
|
+
: 'Build your package schedule. Add products and timed breaks to define the booking timeline.'}
|
|
261
|
+
</p>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
{items.length === 0 && (
|
|
265
|
+
<p className="text-label-secondary text-sm">No items added yet.</p>
|
|
266
|
+
)}
|
|
267
|
+
|
|
268
|
+
{items.length > 0 && (
|
|
269
|
+
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
|
270
|
+
<SortableContext items={itemIds} strategy={verticalListSortingStrategy}>
|
|
271
|
+
<div className="flex flex-col gap-3">
|
|
272
|
+
{items.map((item, index) => (
|
|
273
|
+
<SortablePackageItem
|
|
274
|
+
key={item.id}
|
|
275
|
+
item={item}
|
|
276
|
+
index={index}
|
|
277
|
+
products={products}
|
|
278
|
+
loading={loading}
|
|
279
|
+
onUpdate={updateItem}
|
|
280
|
+
onRemove={removeItem}
|
|
281
|
+
/>
|
|
282
|
+
))}
|
|
283
|
+
</div>
|
|
284
|
+
</SortableContext>
|
|
285
|
+
</DndContext>
|
|
286
|
+
)}
|
|
287
|
+
|
|
288
|
+
<div className="flex gap-2">
|
|
289
|
+
<Button type="button" variant="outline" onClick={addProduct} className="w-fit" withIcon>
|
|
290
|
+
<IconPlus className="w-4 h-4 fill-fill-secondary" />
|
|
291
|
+
Add Product
|
|
292
|
+
</Button>
|
|
293
|
+
{showBreaks && (
|
|
294
|
+
<Button type="button" variant="outline" onClick={addBreak} className="w-fit" withIcon>
|
|
295
|
+
<IconPlus className="w-4 h-4 fill-fill-secondary" />
|
|
296
|
+
Add Break
|
|
297
|
+
</Button>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
{/* Calculated Average Duration */}
|
|
302
|
+
<div className="flex flex-col gap-2 pt-4 border-t border-border-primary">
|
|
303
|
+
<Label>Average Package Duration</Label>
|
|
304
|
+
<p className="text-label-secondary text-sm">
|
|
305
|
+
Automatically calculated from the total of all product durations and breaks.
|
|
306
|
+
</p>
|
|
307
|
+
<div className="flex items-center gap-2">
|
|
308
|
+
<span className="text-label-primary text-sm font-medium">
|
|
309
|
+
{calculatedDuration > 0
|
|
310
|
+
? `${Math.floor(calculatedDuration / 60) > 0 ? `${Math.floor(calculatedDuration / 60)}h ` : ''}${calculatedDuration % 60}min`
|
|
311
|
+
: '—'}
|
|
312
|
+
</span>
|
|
313
|
+
{calculatedDuration > 0 && (
|
|
314
|
+
<span className="text-label-secondary text-xs">({calculatedDuration} minutes)</span>
|
|
315
|
+
)}
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
export default PackageProductsSection;
|