@licklist/design 0.78.5-dev.107 → 0.78.5-dev.109

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.
Files changed (414) hide show
  1. package/bitbucket-pipelines.yml +4 -13
  2. package/dist/Maintenance/Maintenance.scss.js +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/product-set/form/ProductsControl.d.ts +1 -2
  5. package/dist/product-set/form/ProductsControl.d.ts.map +1 -1
  6. package/dist/product-set/form/ProductsControl.js +24 -0
  7. package/dist/v2/components/ActionMenu/ActionMenu.scss.js +1 -1
  8. package/dist/v2/components/Badge/Badge.scss.js +1 -1
  9. package/dist/v2/components/Button/Button.scss.js +1 -1
  10. package/dist/v2/components/Button/GhostButton.scss.js +1 -1
  11. package/dist/v2/components/Checkbox/Checkbox.scss.js +1 -1
  12. package/dist/v2/components/DataTable/DataTable.d.ts.map +1 -1
  13. package/dist/v2/components/DataTable/DataTable.js +2 -86
  14. package/dist/v2/components/IconButton/IconButton.scss.js +1 -1
  15. package/dist/v2/components/Modal/DeleteModal.d.ts.map +1 -1
  16. package/dist/v2/components/Modal/DeleteModal.js +11 -13
  17. package/dist/v2/components/Modal/DeleteModal.scss.js +1 -1
  18. package/dist/v2/components/NPSScore/NPSScore.scss.js +1 -1
  19. package/dist/v2/components/NewTabs/NewTabs.scss.js +1 -1
  20. package/dist/v2/components/PeriodCard/PeriodCard.d.ts +66 -0
  21. package/dist/v2/components/PeriodCard/PeriodCard.d.ts.map +1 -0
  22. package/dist/v2/components/PeriodCard/PeriodCard.js +351 -0
  23. package/dist/v2/components/PeriodCard/PeriodCard.scss.js +6 -0
  24. package/dist/v2/components/PeriodCard/index.d.ts +3 -0
  25. package/dist/v2/components/PeriodCard/index.d.ts.map +1 -0
  26. package/dist/v2/components/ReorderRow/ReorderRow.d.ts +24 -0
  27. package/dist/v2/components/ReorderRow/ReorderRow.d.ts.map +1 -0
  28. package/dist/v2/components/ReorderRow/ReorderRow.js +109 -0
  29. package/dist/v2/components/ReorderRow/ReorderRow.scss.js +6 -0
  30. package/dist/v2/components/ReorderRow/index.d.ts +3 -0
  31. package/dist/v2/components/ReorderRow/index.d.ts.map +1 -0
  32. package/dist/v2/components/Select/Select.scss.js +1 -1
  33. package/dist/v2/components/StatusBadge/StatusBadge.scss.js +1 -1
  34. package/dist/v2/components/StepIndicator/StepIndicator.scss.js +1 -1
  35. package/dist/v2/components/Tabs/Tabs.scss.js +1 -1
  36. package/dist/v2/components/Toggle/Toggle.d.ts.map +1 -1
  37. package/dist/v2/components/Toggle/Toggle.js +5 -8
  38. package/dist/v2/components/Tooltip/Tooltip.scss.js +1 -1
  39. package/dist/v2/components/UserAvatar/UserAvatar.scss.js +1 -1
  40. package/dist/v2/components/UserPanel/UserPanel.scss.js +1 -1
  41. package/dist/v2/components/WYSIWYGEditor/WYSIWYGEditor.scss.js +1 -1
  42. package/dist/v2/components/ZoneCard/ZoneCard.scss.js +1 -1
  43. package/dist/v2/components/index.d.ts +4 -0
  44. package/dist/v2/components/index.d.ts.map +1 -1
  45. package/dist/v2/dashboard-analytics/chart/Chart.scss.js +1 -1
  46. package/dist/v2/dashboard-analytics/metric-card/MetricCard.scss.js +1 -1
  47. package/dist/v2/dashboard-analytics/venue-card/VenueCard.scss.js +1 -1
  48. package/dist/v2/dashboard-analytics/venue-closed-card/VenueClosedCard.scss.js +1 -1
  49. package/dist/v2/icons/index.js +16 -1
  50. package/dist/v2/index.d.ts +8 -0
  51. package/dist/v2/index.d.ts.map +1 -1
  52. package/dist/v2/navigation/DashboardLayout/AdminSidebar.scss.js +1 -1
  53. package/dist/v2/navigation/DashboardLayout/DashboardLayout.scss.js +1 -1
  54. package/dist/v2/navigation/DashboardLayout/ProviderSidebar.scss.js +1 -1
  55. package/dist/v2/navigation/DashboardLayout/TopNavigation.scss.js +1 -1
  56. package/dist/v2/pages/Settings/SettingsTabs.scss.js +1 -1
  57. package/dist/v2/pages/Settings/components/SidebarCustomisation.js +5 -0
  58. package/dist/v2/pages/Settings/components/SidebarCustomisation.scss.js +1 -1
  59. package/dist/v2/pages/Settings/components/SidebarNavItem.js +5 -0
  60. package/dist/v2/pages/auth/AuthLayout/AuthLayout.scss.js +1 -1
  61. package/dist/v2/shadcn/components/ui/accordion.d.ts +8 -0
  62. package/dist/v2/shadcn/components/ui/accordion.d.ts.map +1 -0
  63. package/dist/v2/shadcn/components/ui/alert-dialog.d.ts +21 -0
  64. package/dist/v2/shadcn/components/ui/alert-dialog.d.ts.map +1 -0
  65. package/dist/v2/shadcn/components/ui/alert.d.ts +9 -0
  66. package/dist/v2/shadcn/components/ui/alert.d.ts.map +1 -0
  67. package/dist/v2/shadcn/components/ui/aspect-ratio.d.ts +4 -0
  68. package/dist/v2/shadcn/components/ui/aspect-ratio.d.ts.map +1 -0
  69. package/dist/v2/shadcn/components/ui/avatar.d.ts +7 -0
  70. package/dist/v2/shadcn/components/ui/avatar.d.ts.map +1 -0
  71. package/dist/v2/shadcn/components/ui/badge.d.ts +10 -0
  72. package/dist/v2/shadcn/components/ui/badge.d.ts.map +1 -0
  73. package/dist/v2/shadcn/components/ui/breadcrumb.d.ts +20 -0
  74. package/dist/v2/shadcn/components/ui/breadcrumb.d.ts.map +1 -0
  75. package/dist/v2/shadcn/components/ui/button.d.ts +14 -0
  76. package/dist/v2/shadcn/components/ui/button.d.ts.map +1 -0
  77. package/dist/v2/shadcn/components/ui/calendar.d.ts +9 -0
  78. package/dist/v2/shadcn/components/ui/calendar.d.ts.map +1 -0
  79. package/dist/v2/shadcn/components/ui/card.d.ts +9 -0
  80. package/dist/v2/shadcn/components/ui/card.d.ts.map +1 -0
  81. package/dist/v2/shadcn/components/ui/carousel.d.ts +19 -0
  82. package/dist/v2/shadcn/components/ui/carousel.d.ts.map +1 -0
  83. package/dist/v2/shadcn/components/ui/checkbox.d.ts +6 -0
  84. package/dist/v2/shadcn/components/ui/checkbox.d.ts.map +1 -0
  85. package/dist/v2/shadcn/components/ui/checkbox.js +115 -0
  86. package/dist/v2/shadcn/components/ui/checkbox.scss.js +6 -0
  87. package/dist/v2/shadcn/components/ui/collapsible.d.ts +6 -0
  88. package/dist/v2/shadcn/components/ui/collapsible.d.ts.map +1 -0
  89. package/dist/v2/shadcn/components/ui/command.d.ts +83 -0
  90. package/dist/v2/shadcn/components/ui/command.d.ts.map +1 -0
  91. package/dist/v2/shadcn/components/ui/context-menu.d.ts +28 -0
  92. package/dist/v2/shadcn/components/ui/context-menu.d.ts.map +1 -0
  93. package/dist/v2/shadcn/components/ui/dialog.d.ts +20 -0
  94. package/dist/v2/shadcn/components/ui/dialog.d.ts.map +1 -0
  95. package/dist/v2/shadcn/components/ui/dialog.js +169 -0
  96. package/dist/v2/shadcn/components/ui/drawer.d.ts +23 -0
  97. package/dist/v2/shadcn/components/ui/drawer.d.ts.map +1 -0
  98. package/dist/v2/shadcn/components/ui/dropdown-menu.d.ts +28 -0
  99. package/dist/v2/shadcn/components/ui/dropdown-menu.d.ts.map +1 -0
  100. package/dist/v2/shadcn/components/ui/form.d.ts +24 -0
  101. package/dist/v2/shadcn/components/ui/form.d.ts.map +1 -0
  102. package/dist/v2/shadcn/components/ui/hover-card.d.ts +7 -0
  103. package/dist/v2/shadcn/components/ui/hover-card.d.ts.map +1 -0
  104. package/dist/v2/shadcn/components/ui/input-otp.d.ts +35 -0
  105. package/dist/v2/shadcn/components/ui/input-otp.d.ts.map +1 -0
  106. package/dist/v2/shadcn/components/ui/input.d.ts +6 -0
  107. package/dist/v2/shadcn/components/ui/input.d.ts.map +1 -0
  108. package/dist/v2/shadcn/components/ui/label.d.ts +6 -0
  109. package/dist/v2/shadcn/components/ui/label.d.ts.map +1 -0
  110. package/dist/v2/shadcn/components/ui/menubar.d.ts +34 -0
  111. package/dist/v2/shadcn/components/ui/menubar.d.ts.map +1 -0
  112. package/dist/v2/shadcn/components/ui/navigation-menu.d.ts +13 -0
  113. package/dist/v2/shadcn/components/ui/navigation-menu.d.ts.map +1 -0
  114. package/dist/v2/shadcn/components/ui/pagination.d.ts +29 -0
  115. package/dist/v2/shadcn/components/ui/pagination.d.ts.map +1 -0
  116. package/dist/v2/shadcn/components/ui/popover.d.ts +7 -0
  117. package/dist/v2/shadcn/components/ui/popover.d.ts.map +1 -0
  118. package/dist/v2/shadcn/components/ui/progress.d.ts +5 -0
  119. package/dist/v2/shadcn/components/ui/progress.d.ts.map +1 -0
  120. package/dist/v2/shadcn/components/ui/radio-card.d.ts +12 -0
  121. package/dist/v2/shadcn/components/ui/radio-card.d.ts.map +1 -0
  122. package/dist/v2/shadcn/components/ui/radio-group.d.ts +6 -0
  123. package/dist/v2/shadcn/components/ui/radio-group.d.ts.map +1 -0
  124. package/dist/v2/shadcn/components/ui/scroll-area.d.ts +6 -0
  125. package/dist/v2/shadcn/components/ui/scroll-area.d.ts.map +1 -0
  126. package/dist/v2/shadcn/components/ui/select.d.ts +14 -0
  127. package/dist/v2/shadcn/components/ui/select.d.ts.map +1 -0
  128. package/dist/v2/shadcn/components/ui/separator.d.ts +5 -0
  129. package/dist/v2/shadcn/components/ui/separator.d.ts.map +1 -0
  130. package/dist/v2/shadcn/components/ui/sheet.d.ts +26 -0
  131. package/dist/v2/shadcn/components/ui/sheet.d.ts.map +1 -0
  132. package/dist/v2/shadcn/components/ui/sidebar.d.ts +67 -0
  133. package/dist/v2/shadcn/components/ui/sidebar.d.ts.map +1 -0
  134. package/dist/v2/shadcn/components/ui/skeleton.d.ts +3 -0
  135. package/dist/v2/shadcn/components/ui/skeleton.d.ts.map +1 -0
  136. package/dist/v2/shadcn/components/ui/slider.d.ts +5 -0
  137. package/dist/v2/shadcn/components/ui/slider.d.ts.map +1 -0
  138. package/dist/v2/shadcn/components/ui/switch.d.ts +6 -0
  139. package/dist/v2/shadcn/components/ui/switch.d.ts.map +1 -0
  140. package/dist/v2/shadcn/components/ui/switch.js +115 -0
  141. package/dist/v2/shadcn/components/ui/switch.scss.js +6 -0
  142. package/dist/v2/shadcn/components/ui/table-pagination.d.ts +11 -0
  143. package/dist/v2/shadcn/components/ui/table-pagination.d.ts.map +1 -0
  144. package/dist/v2/shadcn/components/ui/table.d.ts +11 -0
  145. package/dist/v2/shadcn/components/ui/table.d.ts.map +1 -0
  146. package/dist/v2/shadcn/components/ui/tabs.d.ts +8 -0
  147. package/dist/v2/shadcn/components/ui/tabs.d.ts.map +1 -0
  148. package/dist/v2/shadcn/components/ui/textarea.d.ts +6 -0
  149. package/dist/v2/shadcn/components/ui/textarea.d.ts.map +1 -0
  150. package/dist/v2/shadcn/components/ui/toast.d.ts +16 -0
  151. package/dist/v2/shadcn/components/ui/toast.d.ts.map +1 -0
  152. package/dist/v2/shadcn/components/ui/toaster.d.ts +2 -0
  153. package/dist/v2/shadcn/components/ui/toaster.d.ts.map +1 -0
  154. package/dist/v2/shadcn/components/ui/toggle-group.d.ts +13 -0
  155. package/dist/v2/shadcn/components/ui/toggle-group.d.ts.map +1 -0
  156. package/dist/v2/shadcn/components/ui/toggle.d.ts +13 -0
  157. package/dist/v2/shadcn/components/ui/toggle.d.ts.map +1 -0
  158. package/dist/v2/shadcn/components/ui/tooltip.d.ts +8 -0
  159. package/dist/v2/shadcn/components/ui/tooltip.d.ts.map +1 -0
  160. package/dist/v2/shadcn/components/ui/use-toast.d.ts +3 -0
  161. package/dist/v2/shadcn/components/ui/use-toast.d.ts.map +1 -0
  162. package/dist/v2/shadcn/hooks/use-mobile.d.ts +2 -0
  163. package/dist/v2/shadcn/hooks/use-mobile.d.ts.map +1 -0
  164. package/dist/v2/shadcn/hooks/use-toast.d.ts +45 -0
  165. package/dist/v2/shadcn/hooks/use-toast.d.ts.map +1 -0
  166. package/dist/v2/shadcn/index.d.ts +20 -0
  167. package/dist/v2/shadcn/index.d.ts.map +1 -0
  168. package/dist/v2/shadcn/lib/utils.d.ts +3 -0
  169. package/dist/v2/shadcn/lib/utils.d.ts.map +1 -0
  170. package/dist/v2/shadcn/lib/utils.js +11 -0
  171. package/dist/v2/shadcn/styles/globals.css +112 -0
  172. package/dist/v2/styles/form/NewInput.scss.js +1 -1
  173. package/package.json +6 -6
  174. package/rollup.config.js +2 -16
  175. package/src/iframe/payment/payment-status-page/PaymentStatusPage.tsx +1 -1
  176. package/src/product-set/form/ProductsControl.tsx +1 -2
  177. package/src/v2/components/DataTable/DataTable.tsx +1 -23
  178. package/src/v2/components/Modal/DeleteModal.tsx +20 -12
  179. package/src/v2/components/PeriodCard/PeriodCard.scss +157 -0
  180. package/src/v2/components/PeriodCard/PeriodCard.stories.tsx +245 -0
  181. package/src/v2/components/PeriodCard/PeriodCard.tsx +350 -0
  182. package/src/v2/components/PeriodCard/index.ts +8 -0
  183. package/src/v2/components/ReorderRow/ReorderRow.scss +68 -0
  184. package/src/v2/components/ReorderRow/ReorderRow.stories.tsx +124 -0
  185. package/src/v2/components/ReorderRow/ReorderRow.tsx +88 -0
  186. package/src/v2/components/ReorderRow/index.ts +2 -0
  187. package/src/v2/components/Toggle/Toggle.tsx +5 -6
  188. package/src/v2/components/index.ts +6 -0
  189. package/src/v2/index.ts +82 -0
  190. package/src/v2/shadcn/_reference/AccountManagerCard.tsx +45 -0
  191. package/src/v2/shadcn/_reference/AffiliatesTable.tsx +178 -0
  192. package/src/v2/shadcn/_reference/AuditArchive.tsx +165 -0
  193. package/src/v2/shadcn/_reference/AuditContent.tsx +270 -0
  194. package/src/v2/shadcn/_reference/AutomationsGeneralSettings.tsx +251 -0
  195. package/src/v2/shadcn/_reference/AvatarUpload.tsx +150 -0
  196. package/src/v2/shadcn/_reference/BookingsSummaryCard.tsx +268 -0
  197. package/src/v2/shadcn/_reference/CodeCleanUpAudit.tsx +274 -0
  198. package/src/v2/shadcn/_reference/CompaniesTable.tsx +387 -0
  199. package/src/v2/shadcn/_reference/ComponentAudit.tsx +239 -0
  200. package/src/v2/shadcn/_reference/ConfigureSettingsCard.tsx +95 -0
  201. package/src/v2/shadcn/_reference/CustomerCard.tsx +155 -0
  202. package/src/v2/shadcn/_reference/DashboardCards.tsx +50 -0
  203. package/src/v2/shadcn/_reference/DashboardFooter.tsx +18 -0
  204. package/src/v2/shadcn/_reference/DiarySettings.tsx +187 -0
  205. package/src/v2/shadcn/_reference/DiaryView.tsx +998 -0
  206. package/src/v2/shadcn/_reference/EmptyState.tsx +76 -0
  207. package/src/v2/shadcn/_reference/EntityInfoCard.tsx +48 -0
  208. package/src/v2/shadcn/_reference/ExistingUserAssignments.tsx +131 -0
  209. package/src/v2/shadcn/_reference/FeatureToggle.tsx +72 -0
  210. package/src/v2/shadcn/_reference/FlowCard.tsx +170 -0
  211. package/src/v2/shadcn/_reference/FlowsContent.tsx +688 -0
  212. package/src/v2/shadcn/_reference/FlowsGeneralSettings.tsx +27 -0
  213. package/src/v2/shadcn/_reference/GeneralSettings.tsx +33 -0
  214. package/src/v2/shadcn/_reference/InventoryGeneralSettings.tsx +82 -0
  215. package/src/v2/shadcn/_reference/LanguageSelector.tsx +97 -0
  216. package/src/v2/shadcn/_reference/LoadingScreen.tsx +25 -0
  217. package/src/v2/shadcn/_reference/LoadingSpinner.tsx +41 -0
  218. package/src/v2/shadcn/_reference/ManagedClientsList.tsx +121 -0
  219. package/src/v2/shadcn/_reference/NPSScore.tsx +379 -0
  220. package/src/v2/shadcn/_reference/NPSSummaryCard.tsx +181 -0
  221. package/src/v2/shadcn/_reference/NotificationBanner.tsx +129 -0
  222. package/src/v2/shadcn/_reference/NotificationPanel.tsx +208 -0
  223. package/src/v2/shadcn/_reference/OnlineUsersCard.tsx +73 -0
  224. package/src/v2/shadcn/_reference/ProtectedRoute.tsx +39 -0
  225. package/src/v2/shadcn/_reference/ProvidersTable.tsx +353 -0
  226. package/src/v2/shadcn/_reference/QuickAddPanel.tsx +1057 -0
  227. package/src/v2/shadcn/_reference/QuickFilters.tsx +112 -0
  228. package/src/v2/shadcn/_reference/ScheduleView.tsx +410 -0
  229. package/src/v2/shadcn/_reference/ScrollToTop.tsx +14 -0
  230. package/src/v2/shadcn/_reference/SecondaryNav.tsx +50 -0
  231. package/src/v2/shadcn/_reference/SecuritySettings.tsx +258 -0
  232. package/src/v2/shadcn/_reference/SessionDetailView.tsx +294 -0
  233. package/src/v2/shadcn/_reference/Sidebar.tsx +14 -0
  234. package/src/v2/shadcn/_reference/SidebarAwareLayout.tsx +30 -0
  235. package/src/v2/shadcn/_reference/SidebarLabelCustomization.tsx +285 -0
  236. package/src/v2/shadcn/_reference/SimulationBanner.tsx +57 -0
  237. package/src/v2/shadcn/_reference/SortControls.tsx +65 -0
  238. package/src/v2/shadcn/_reference/StatusBadge.tsx +49 -0
  239. package/src/v2/shadcn/_reference/StyleGuideContent.tsx +331 -0
  240. package/src/v2/shadcn/_reference/TableActionMenu.tsx +126 -0
  241. package/src/v2/shadcn/_reference/ThemeProvider.tsx +119 -0
  242. package/src/v2/shadcn/_reference/ThemeSettings.tsx +73 -0
  243. package/src/v2/shadcn/_reference/TopNavigation.tsx +332 -0
  244. package/src/v2/shadcn/_reference/UserActivityHistory.tsx +209 -0
  245. package/src/v2/shadcn/_reference/UserLanguageSettings.tsx +94 -0
  246. package/src/v2/shadcn/_reference/UserPanel.tsx +472 -0
  247. package/src/v2/shadcn/_reference/UsersTable.tsx +1023 -0
  248. package/src/v2/shadcn/_reference/WaiverForm.tsx +301 -0
  249. package/src/v2/shadcn/_reference/WaiversGeneralSettings.tsx +46 -0
  250. package/src/v2/shadcn/_reference/WaiversTable.tsx +290 -0
  251. package/src/v2/shadcn/_reference/WaiversTemplatesSettings.tsx +416 -0
  252. package/src/v2/shadcn/_reference/ai/AIChatPanel.tsx +313 -0
  253. package/src/v2/shadcn/_reference/ai/AIChatSearchBar.tsx +36 -0
  254. package/src/v2/shadcn/_reference/ai/ChatInteractiveBlock.tsx +298 -0
  255. package/src/v2/shadcn/_reference/ai/ChatMessageContent.tsx +40 -0
  256. package/src/v2/shadcn/_reference/ai/parseInteractiveBlocks.ts +142 -0
  257. package/src/v2/shadcn/_reference/auth/AuthLayout.tsx +55 -0
  258. package/src/v2/shadcn/_reference/auth/CreatePasswordForm.tsx +285 -0
  259. package/src/v2/shadcn/_reference/auth/CreatePasswordPanel.tsx +20 -0
  260. package/src/v2/shadcn/_reference/auth/LoginFooter.tsx +14 -0
  261. package/src/v2/shadcn/_reference/auth/LoginForm.tsx +205 -0
  262. package/src/v2/shadcn/_reference/auth/LoginPanel.tsx +41 -0
  263. package/src/v2/shadcn/_reference/auth/ResetPasswordForm.tsx +102 -0
  264. package/src/v2/shadcn/_reference/auth/ResetPasswordPanel.tsx +20 -0
  265. package/src/v2/shadcn/_reference/auth/VerifyEmailForm.tsx +95 -0
  266. package/src/v2/shadcn/_reference/auth/VerifyEmailPanel.tsx +20 -0
  267. package/src/v2/shadcn/_reference/email/EmailAttachment.tsx +119 -0
  268. package/src/v2/shadcn/_reference/email/EmailAutomation.tsx +92 -0
  269. package/src/v2/shadcn/_reference/email/EmailPlaceholders.tsx +64 -0
  270. package/src/v2/shadcn/_reference/email/UnlayerEmailEditor.tsx +41 -0
  271. package/src/v2/shadcn/_reference/email/emailTemplateData.ts +53 -0
  272. package/src/v2/shadcn/_reference/emptyStateIcons.tsx +103 -0
  273. package/src/v2/shadcn/_reference/games/MazeGame.tsx +394 -0
  274. package/src/v2/shadcn/_reference/games/RunnerGame.tsx +497 -0
  275. package/src/v2/shadcn/_reference/logos/BookedLogoFull.tsx +36 -0
  276. package/src/v2/shadcn/_reference/logos/BookedLogoMark.tsx +31 -0
  277. package/src/v2/shadcn/_reference/logos/BookedLogoNew.tsx +36 -0
  278. package/src/v2/shadcn/_reference/pricing/DynamicPricingRulesEditor.tsx +401 -0
  279. package/src/v2/shadcn/_reference/pricing/DynamicPricingTierCard.tsx +77 -0
  280. package/src/v2/shadcn/_reference/pricing/DynamicPricingTiersList.tsx +218 -0
  281. package/src/v2/shadcn/_reference/pricing/PricingCalendar.tsx +810 -0
  282. package/src/v2/shadcn/_reference/pricing/PricingPeriodCard.tsx +152 -0
  283. package/src/v2/shadcn/_reference/pricing/PricingPeriodForm.tsx +377 -0
  284. package/src/v2/shadcn/_reference/pricing/PricingPeriodsList.tsx +213 -0
  285. package/src/v2/shadcn/_reference/pricing/getRuleSummary.ts +39 -0
  286. package/src/v2/shadcn/_reference/products/AvailabilityRulesSection.tsx +184 -0
  287. package/src/v2/shadcn/_reference/products/AvailabilitySection.tsx +677 -0
  288. package/src/v2/shadcn/_reference/products/BookingTypeConfigOptions.tsx +40 -0
  289. package/src/v2/shadcn/_reference/products/CapacityPeriodsSection.tsx +238 -0
  290. package/src/v2/shadcn/_reference/products/DynamicPricingTiersSection.tsx +131 -0
  291. package/src/v2/shadcn/_reference/products/GiftCardOrdersTab.tsx +192 -0
  292. package/src/v2/shadcn/_reference/products/GiftCardSettings.tsx +342 -0
  293. package/src/v2/shadcn/_reference/products/PackageProductsSection.tsx +322 -0
  294. package/src/v2/shadcn/_reference/products/PricingSection.tsx +173 -0
  295. package/src/v2/shadcn/_reference/products/ProductTypeFields.tsx +353 -0
  296. package/src/v2/shadcn/_reference/products/ProductTypeIcon.tsx +95 -0
  297. package/src/v2/shadcn/_reference/products/VariablePricingSection.tsx +140 -0
  298. package/src/v2/shadcn/_reference/products/productTypeConfig.ts +182 -0
  299. package/src/v2/shadcn/_reference/shared/BackButton.tsx +50 -0
  300. package/src/v2/shadcn/_reference/shared/CancelConfirmationDialog.tsx +18 -0
  301. package/src/v2/shadcn/_reference/shared/ConfirmationDialog.tsx +136 -0
  302. package/src/v2/shadcn/_reference/shared/DeleteConfirmationDialog.tsx +18 -0
  303. package/src/v2/shadcn/_reference/shared/DeleteEntityPage.tsx +221 -0
  304. package/src/v2/shadcn/_reference/shared/SidebarIcons.tsx +108 -0
  305. package/src/v2/shadcn/_reference/shared/UnifiedSidebar.tsx +722 -0
  306. package/src/v2/shadcn/_reference/tables/BulkActionsBar.tsx +68 -0
  307. package/src/v2/shadcn/_reference/tables/DataTable.tsx +221 -0
  308. package/src/v2/shadcn/_reference/tables/TableControls.tsx +94 -0
  309. package/src/v2/shadcn/_reference/tables/index.ts +3 -0
  310. package/src/v2/shadcn/_reference/tables/types.ts +79 -0
  311. package/src/v2/shadcn/_reference/zones/LegacyZoneSettings.tsx +299 -0
  312. package/src/v2/shadcn/components/ui/accordion.stories.tsx +63 -0
  313. package/src/v2/shadcn/components/ui/accordion.tsx +52 -0
  314. package/src/v2/shadcn/components/ui/alert-dialog.stories.tsx +44 -0
  315. package/src/v2/shadcn/components/ui/alert-dialog.tsx +104 -0
  316. package/src/v2/shadcn/components/ui/alert.stories.tsx +44 -0
  317. package/src/v2/shadcn/components/ui/alert.tsx +43 -0
  318. package/src/v2/shadcn/components/ui/aspect-ratio.stories.tsx +46 -0
  319. package/src/v2/shadcn/components/ui/aspect-ratio.tsx +5 -0
  320. package/src/v2/shadcn/components/ui/avatar.stories.tsx +39 -0
  321. package/src/v2/shadcn/components/ui/avatar.tsx +38 -0
  322. package/src/v2/shadcn/components/ui/badge.stories.tsx +17 -0
  323. package/src/v2/shadcn/components/ui/badge.tsx +30 -0
  324. package/src/v2/shadcn/components/ui/breadcrumb.stories.tsx +91 -0
  325. package/src/v2/shadcn/components/ui/breadcrumb.tsx +90 -0
  326. package/src/v2/shadcn/components/ui/button.stories.tsx +20 -0
  327. package/src/v2/shadcn/components/ui/button.tsx +60 -0
  328. package/src/v2/shadcn/components/ui/calendar.stories.tsx +61 -0
  329. package/src/v2/shadcn/components/ui/calendar.tsx +54 -0
  330. package/src/v2/shadcn/components/ui/card.stories.tsx +37 -0
  331. package/src/v2/shadcn/components/ui/card.tsx +43 -0
  332. package/src/v2/shadcn/components/ui/carousel.stories.tsx +92 -0
  333. package/src/v2/shadcn/components/ui/carousel.tsx +224 -0
  334. package/src/v2/shadcn/components/ui/checkbox.scss +38 -0
  335. package/src/v2/shadcn/components/ui/checkbox.stories.tsx +23 -0
  336. package/src/v2/shadcn/components/ui/checkbox.tsx +24 -0
  337. package/src/v2/shadcn/components/ui/collapsible.stories.tsx +59 -0
  338. package/src/v2/shadcn/components/ui/collapsible.tsx +9 -0
  339. package/src/v2/shadcn/components/ui/command.stories.tsx +70 -0
  340. package/src/v2/shadcn/components/ui/command.tsx +132 -0
  341. package/src/v2/shadcn/components/ui/context-menu.stories.tsx +72 -0
  342. package/src/v2/shadcn/components/ui/context-menu.tsx +178 -0
  343. package/src/v2/shadcn/components/ui/dialog.stories.tsx +67 -0
  344. package/src/v2/shadcn/components/ui/dialog.tsx +95 -0
  345. package/src/v2/shadcn/components/ui/drawer.stories.tsx +50 -0
  346. package/src/v2/shadcn/components/ui/drawer.tsx +87 -0
  347. package/src/v2/shadcn/components/ui/dropdown-menu.stories.tsx +73 -0
  348. package/src/v2/shadcn/components/ui/dropdown-menu.tsx +179 -0
  349. package/src/v2/shadcn/components/ui/form.stories.tsx +105 -0
  350. package/src/v2/shadcn/components/ui/form.tsx +129 -0
  351. package/src/v2/shadcn/components/ui/hover-card.stories.tsx +35 -0
  352. package/src/v2/shadcn/components/ui/hover-card.tsx +27 -0
  353. package/src/v2/shadcn/components/ui/input-otp.stories.tsx +72 -0
  354. package/src/v2/shadcn/components/ui/input-otp.tsx +61 -0
  355. package/src/v2/shadcn/components/ui/input.stories.tsx +16 -0
  356. package/src/v2/shadcn/components/ui/input.tsx +25 -0
  357. package/src/v2/shadcn/components/ui/label.stories.tsx +13 -0
  358. package/src/v2/shadcn/components/ui/label.tsx +17 -0
  359. package/src/v2/shadcn/components/ui/menubar.stories.tsx +86 -0
  360. package/src/v2/shadcn/components/ui/menubar.tsx +207 -0
  361. package/src/v2/shadcn/components/ui/navigation-menu.stories.tsx +68 -0
  362. package/src/v2/shadcn/components/ui/navigation-menu.tsx +120 -0
  363. package/src/v2/shadcn/components/ui/pagination.stories.tsx +78 -0
  364. package/src/v2/shadcn/components/ui/pagination.tsx +81 -0
  365. package/src/v2/shadcn/components/ui/popover.stories.tsx +44 -0
  366. package/src/v2/shadcn/components/ui/popover.tsx +29 -0
  367. package/src/v2/shadcn/components/ui/progress.stories.tsx +17 -0
  368. package/src/v2/shadcn/components/ui/progress.tsx +23 -0
  369. package/src/v2/shadcn/components/ui/radio-card.stories.tsx +68 -0
  370. package/src/v2/shadcn/components/ui/radio-card.tsx +52 -0
  371. package/src/v2/shadcn/components/ui/radio-group.stories.tsx +77 -0
  372. package/src/v2/shadcn/components/ui/radio-group.tsx +35 -0
  373. package/src/v2/shadcn/components/ui/scroll-area.stories.tsx +56 -0
  374. package/src/v2/shadcn/components/ui/scroll-area.tsx +38 -0
  375. package/src/v2/shadcn/components/ui/select.stories.tsx +60 -0
  376. package/src/v2/shadcn/components/ui/select.tsx +148 -0
  377. package/src/v2/shadcn/components/ui/separator.stories.tsx +30 -0
  378. package/src/v2/shadcn/components/ui/separator.tsx +20 -0
  379. package/src/v2/shadcn/components/ui/sheet.stories.tsx +115 -0
  380. package/src/v2/shadcn/components/ui/sheet.tsx +107 -0
  381. package/src/v2/shadcn/components/ui/sidebar.stories.tsx +167 -0
  382. package/src/v2/shadcn/components/ui/sidebar.tsx +637 -0
  383. package/src/v2/shadcn/components/ui/skeleton.stories.tsx +36 -0
  384. package/src/v2/shadcn/components/ui/skeleton.tsx +7 -0
  385. package/src/v2/shadcn/components/ui/slider.stories.tsx +16 -0
  386. package/src/v2/shadcn/components/ui/slider.tsx +23 -0
  387. package/src/v2/shadcn/components/ui/switch.scss +63 -0
  388. package/src/v2/shadcn/components/ui/switch.stories.tsx +23 -0
  389. package/src/v2/shadcn/components/ui/switch.tsx +24 -0
  390. package/src/v2/shadcn/components/ui/table-pagination.stories.tsx +81 -0
  391. package/src/v2/shadcn/components/ui/table-pagination.tsx +61 -0
  392. package/src/v2/shadcn/components/ui/table.stories.tsx +40 -0
  393. package/src/v2/shadcn/components/ui/table.tsx +72 -0
  394. package/src/v2/shadcn/components/ui/tabs.stories.tsx +85 -0
  395. package/src/v2/shadcn/components/ui/tabs.tsx +53 -0
  396. package/src/v2/shadcn/components/ui/textarea.stories.tsx +15 -0
  397. package/src/v2/shadcn/components/ui/textarea.tsx +21 -0
  398. package/src/v2/shadcn/components/ui/toast.stories.tsx +77 -0
  399. package/src/v2/shadcn/components/ui/toast.tsx +111 -0
  400. package/src/v2/shadcn/components/ui/toaster.stories.tsx +46 -0
  401. package/src/v2/shadcn/components/ui/toaster.tsx +24 -0
  402. package/src/v2/shadcn/components/ui/toggle-group.stories.tsx +95 -0
  403. package/src/v2/shadcn/components/ui/toggle-group.tsx +49 -0
  404. package/src/v2/shadcn/components/ui/toggle.stories.tsx +18 -0
  405. package/src/v2/shadcn/components/ui/toggle.tsx +37 -0
  406. package/src/v2/shadcn/components/ui/tooltip.stories.tsx +57 -0
  407. package/src/v2/shadcn/components/ui/tooltip.tsx +28 -0
  408. package/src/v2/shadcn/components/ui/use-toast.ts +3 -0
  409. package/src/v2/shadcn/hooks/use-mobile.tsx +19 -0
  410. package/src/v2/shadcn/hooks/use-toast.ts +184 -0
  411. package/src/v2/shadcn/index.ts +76 -0
  412. package/src/v2/shadcn/lib/utils.ts +6 -0
  413. package/src/v2/shadcn/styles/globals.css +112 -0
  414. package/.vscode/settings.json +0 -3
@@ -0,0 +1,251 @@
1
+ import React, { useState, useMemo, useEffect } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import { IconMail, IconCreateMessage } from '../../icons';
4
+ import { useConfigurePrefix } from '@/layouts/ConfigurePageLayout';
5
+ import { DataTable } from './tables/DataTable';
6
+ import { ColumnConfig, RowAction } from './tables/types';
7
+ import { QuickFilters } from './QuickFilters';
8
+ import { Button } from './ui/button';
9
+ import EmptyState from './EmptyState';
10
+ import StatusBadge from './StatusBadge';
11
+ import { useProviderSafe } from '@/contexts/ProviderContext';
12
+ import { supabase } from '@/integrations/supabase/client';
13
+
14
+ interface EmailTemplate {
15
+ id: string;
16
+ type: string;
17
+ name: string;
18
+ isDefault: boolean;
19
+ status: 'active' | 'disabled';
20
+ }
21
+
22
+ const AutomationsEmailSettings: React.FC = () => {
23
+ const navigate = useNavigate();
24
+ const prefix = useConfigurePrefix();
25
+ const providerCtx = useProviderSafe();
26
+ const providerId = providerCtx?.provider?.id;
27
+
28
+ const [filter, setFilter] = useState<string>('all');
29
+ const [templates, setTemplates] = useState<EmailTemplate[]>([]);
30
+ const [isLoading, setIsLoading] = useState(true);
31
+
32
+ // Fetch templates from database
33
+ useEffect(() => {
34
+ if (!providerId) {
35
+ setIsLoading(false);
36
+ return;
37
+ }
38
+
39
+ const fetchTemplates = async () => {
40
+ setIsLoading(true);
41
+ const { data, error } = await supabase
42
+ .from('email_templates')
43
+ .select('id, type, name, is_default, status')
44
+ .eq('provider_id', providerId)
45
+ .order('created_at', { ascending: true });
46
+
47
+ if (!error && data) {
48
+ setTemplates(data.map(t => ({
49
+ id: t.id,
50
+ type: t.type,
51
+ name: t.name,
52
+ isDefault: t.is_default,
53
+ status: t.status as 'active' | 'disabled',
54
+ })));
55
+ }
56
+ setIsLoading(false);
57
+ };
58
+
59
+ fetchTemplates();
60
+ }, [providerId]);
61
+
62
+ const handleSetDefault = async (item: EmailTemplate) => {
63
+ if (!providerId) return;
64
+
65
+ // Unset all defaults for this type, then set the new one
66
+ await supabase
67
+ .from('email_templates')
68
+ .update({ is_default: false })
69
+ .eq('provider_id', providerId)
70
+ .eq('type', item.type);
71
+
72
+ await supabase
73
+ .from('email_templates')
74
+ .update({ is_default: true })
75
+ .eq('id', item.id);
76
+
77
+ // Optimistic local update
78
+ setTemplates(prev => prev.map(t => {
79
+ if (t.type === item.type) {
80
+ return { ...t, isDefault: t.id === item.id };
81
+ }
82
+ return t;
83
+ }));
84
+ };
85
+
86
+ const handleToggleStatus = async (item: EmailTemplate) => {
87
+ const newStatus = item.status === 'active' ? 'disabled' : 'active';
88
+ await supabase
89
+ .from('email_templates')
90
+ .update({ status: newStatus })
91
+ .eq('id', item.id);
92
+
93
+ setTemplates(prev => prev.map(t =>
94
+ t.id === item.id ? { ...t, status: newStatus as 'active' | 'disabled' } : t
95
+ ));
96
+ };
97
+
98
+ const handleDelete = async (item: EmailTemplate) => {
99
+ await supabase
100
+ .from('email_templates')
101
+ .delete()
102
+ .eq('id', item.id);
103
+
104
+ setTemplates(prev => prev.filter(t => t.id !== item.id));
105
+ };
106
+
107
+ const handleDuplicate = async (item: EmailTemplate) => {
108
+ if (!providerId) return;
109
+
110
+ const { data } = await supabase
111
+ .from('email_templates')
112
+ .insert({
113
+ provider_id: providerId,
114
+ type: item.type,
115
+ name: `${item.name} (Clone)`,
116
+ is_default: false,
117
+ status: item.status,
118
+ })
119
+ .select('id, type, name, is_default, status')
120
+ .single();
121
+
122
+ if (data) {
123
+ setTemplates(prev => [...prev, {
124
+ id: data.id,
125
+ type: data.type,
126
+ name: data.name,
127
+ isDefault: data.is_default,
128
+ status: data.status as 'active' | 'disabled',
129
+ }]);
130
+ }
131
+ };
132
+
133
+ const filteredTemplates = useMemo(() => {
134
+ if (filter === 'all') return templates;
135
+ return templates.filter(t => t.status === filter);
136
+ }, [templates, filter]);
137
+
138
+ const filters = [
139
+ { value: 'active', label: 'Active' },
140
+ { value: 'disabled', label: 'Disabled' },
141
+ ];
142
+
143
+ const columns: ColumnConfig<EmailTemplate>[] = [
144
+ {
145
+ label: 'Template',
146
+ render: (item) => (
147
+ <div className="flex flex-col gap-0.5">
148
+ <span className="text-label-secondary text-[11px] font-mono leading-tight">
149
+ {item.type}
150
+ </span>
151
+ <span className="text-label-primary text-[13px] font-medium leading-4">
152
+ {item.name}
153
+ </span>
154
+ </div>
155
+ ),
156
+ width: 'flex-1',
157
+ className: '',
158
+ },
159
+ {
160
+ label: 'Status',
161
+ render: (item) => (
162
+ <div className="flex items-center gap-2 justify-end">
163
+ {item.isDefault && (
164
+ <span className="inline-flex justify-center items-center border border-colour-overlay bg-surface-secondary px-2 py-1 rounded-3xl text-label-primary text-[10px] font-mono font-medium leading-[13px]">
165
+ Default
166
+ </span>
167
+ )}
168
+ <StatusBadge status={item.status} />
169
+ </div>
170
+ ),
171
+ width: 'w-[160px]',
172
+ className: 'flex justify-end',
173
+ labelClassName: 'justify-end',
174
+ },
175
+ ];
176
+
177
+ const rowActions = (item: EmailTemplate): RowAction<EmailTemplate>[] => [
178
+ { label: 'Edit', onClick: () => navigate(`${prefix}/settings/automations/edit/${item.id}`) },
179
+ { label: item.status === 'active' ? 'Disable' : 'Enable', onClick: () => handleToggleStatus(item) },
180
+ { label: 'Duplicate', onClick: () => handleDuplicate(item) },
181
+ ...(!item.isDefault ? [{ label: 'Set as Default', onClick: () => handleSetDefault(item) }] : []),
182
+ ...(item.isDefault ? [] : [{ label: 'Delete', onClick: () => handleDelete(item), variant: 'danger' as const }]),
183
+ ];
184
+
185
+ const rowIcon = undefined;
186
+
187
+ if (isLoading) {
188
+ return null;
189
+ }
190
+
191
+ if (templates.length === 0) {
192
+ return (
193
+ <div className="flex flex-col gap-4 w-full">
194
+ <div className="flex justify-end">
195
+ <Button
196
+ variant="ghost"
197
+ withIcon
198
+ onClick={() => navigate(`${prefix}/settings/automations/create`)}
199
+ >
200
+ <IconCreateMessage size={20} className="fill-fill-secondary" />
201
+ Create Template
202
+ </Button>
203
+ </div>
204
+ <EmptyState
205
+ icon={<IconMail size={48} className="fill-fill-secondary" />}
206
+ title="No Email Templates"
207
+ description="Create email templates to automate your customer communications."
208
+ />
209
+ </div>
210
+ );
211
+ }
212
+
213
+ return (
214
+ <div className="flex flex-col gap-4 w-full">
215
+ <div className="flex items-center justify-between gap-4">
216
+ <QuickFilters
217
+ filters={filters}
218
+ activeFilter={filter}
219
+ onFilterChange={setFilter}
220
+ />
221
+ <Button
222
+ variant="ghost"
223
+ withIcon
224
+ onClick={() => navigate(`${prefix}/settings/automations/create`)}
225
+ className="flex-shrink-0"
226
+ >
227
+ <IconCreateMessage size={20} className="fill-fill-secondary" />
228
+ <span className="hidden sm:inline">Create Template</span>
229
+ <span className="sm:hidden">Create</span>
230
+ </Button>
231
+ </div>
232
+ <DataTable
233
+ data={filteredTemplates}
234
+ columns={columns}
235
+ keyExtractor={(item) => item.id}
236
+ rowActions={rowActions}
237
+ rowIcon={rowIcon}
238
+ onRowClick={(item) => navigate(`${prefix}/settings/automations/edit/${item.id}`)}
239
+ emptyState={
240
+ <EmptyState
241
+ icon={<IconMail size={48} className="fill-fill-secondary" />}
242
+ title="No Templates Found"
243
+ description="No templates match your current filters."
244
+ />
245
+ }
246
+ />
247
+ </div>
248
+ );
249
+ };
250
+
251
+ export default AutomationsEmailSettings;
@@ -0,0 +1,150 @@
1
+ import { useState } from "react";
2
+ import { supabase } from "@/integrations/supabase/client";
3
+ import { IconArrowUp, IconUpload } from "../../icons";
4
+
5
+ interface AvatarUploadProps {
6
+ userId: string;
7
+ currentAvatarUrl?: string | null;
8
+ onAvatarUpdate: (url: string | null) => void;
9
+ onError?: (message: string) => void;
10
+ }
11
+
12
+ export const AvatarUpload = ({ userId, currentAvatarUrl, onAvatarUpdate }: AvatarUploadProps) => {
13
+ const [isUploading, setIsUploading] = useState(false);
14
+
15
+ const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
16
+ const file = event.target.files?.[0];
17
+ if (!file) return;
18
+
19
+ try {
20
+ setIsUploading(true);
21
+
22
+ // Delete old avatar if exists
23
+ if (currentAvatarUrl) {
24
+ const oldPath = currentAvatarUrl.split('/').pop();
25
+ if (oldPath) {
26
+ await supabase.storage.from('avatars').remove([`${userId}/${oldPath}`]);
27
+ }
28
+ }
29
+
30
+ // Upload new avatar
31
+ const fileExt = file.name.split('.').pop();
32
+ const fileName = `${Date.now()}.${fileExt}`;
33
+ const filePath = `${userId}/${fileName}`;
34
+
35
+ const { error: uploadError } = await supabase.storage
36
+ .from('avatars')
37
+ .upload(filePath, file);
38
+
39
+ if (uploadError) throw uploadError;
40
+
41
+ // Get public URL
42
+ const { data: { publicUrl } } = supabase.storage
43
+ .from('avatars')
44
+ .getPublicUrl(filePath);
45
+
46
+ // Update profile
47
+ const { error: updateError } = await supabase
48
+ .from('profiles')
49
+ .update({ avatar_url: publicUrl })
50
+ .eq('id', userId);
51
+
52
+ if (updateError) throw updateError;
53
+
54
+ onAvatarUpdate(publicUrl);
55
+ } catch (error) {
56
+ console.error('Error uploading avatar:', error);
57
+ } finally {
58
+ setIsUploading(false);
59
+ }
60
+ };
61
+
62
+ const handleDelete = async () => {
63
+ if (!currentAvatarUrl) return;
64
+
65
+ try {
66
+ setIsUploading(true);
67
+
68
+ const path = currentAvatarUrl.split('/').pop();
69
+ if (path) {
70
+ await supabase.storage.from('avatars').remove([`${userId}/${path}`]);
71
+ }
72
+
73
+ const { error } = await supabase
74
+ .from('profiles')
75
+ .update({ avatar_url: null })
76
+ .eq('id', userId);
77
+
78
+ if (error) throw error;
79
+
80
+ onAvatarUpdate(null);
81
+ } catch (error) {
82
+ console.error('Error deleting avatar:', error);
83
+ } finally {
84
+ setIsUploading(false);
85
+ }
86
+ };
87
+
88
+ return (
89
+ <div className="flex items-start gap-6">
90
+ {/* Avatar Circle */}
91
+ <label className="cursor-pointer group flex-shrink-0">
92
+ <input
93
+ type="file"
94
+ accept="image/jpeg,image/png,image/webp"
95
+ onChange={handleFileSelect}
96
+ disabled={isUploading}
97
+ className="hidden"
98
+ />
99
+ <div className="w-[92px] h-[92px] rounded-full bg-surface-secondary border-2 border-border flex items-center justify-center overflow-hidden relative">
100
+ {currentAvatarUrl ? (
101
+ <>
102
+ <img src={currentAvatarUrl} alt="Profile" className="w-full h-full object-cover" />
103
+ <div className="absolute inset-0 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/60">
104
+ <IconUpload className="w-8 h-8 fill-white text-white" size={32} />
105
+ </div>
106
+ </>
107
+ ) : (
108
+ <IconArrowUp className="w-8 h-8 fill-fill-secondary" />
109
+ )}
110
+ </div>
111
+ </label>
112
+
113
+ {/* Text Content */}
114
+ <div className="flex-1 text-left">
115
+ <h3 className="text-label-primary text-lg font-semibold mb-2">
116
+ Upload a profile image
117
+ </h3>
118
+ <p className="text-label-secondary text-[15px] mb-4">
119
+ Images should be square and 1080px x 1080px as a JPEG, PNG, or WEBP file.
120
+ </p>
121
+
122
+ {/* Action Buttons */}
123
+ <div className="flex gap-4">
124
+ <label className="cursor-pointer">
125
+ <input
126
+ type="file"
127
+ accept="image/jpeg,image/png,image/webp"
128
+ onChange={handleFileSelect}
129
+ disabled={isUploading}
130
+ className="hidden"
131
+ />
132
+ <span className="text-actions-regular text-[15px] font-medium hover:underline transition-colors">
133
+ {currentAvatarUrl ? 'Replace' : 'Upload'}
134
+ </span>
135
+ </label>
136
+
137
+ {currentAvatarUrl && (
138
+ <button
139
+ onClick={handleDelete}
140
+ disabled={isUploading}
141
+ className="text-label-danger text-[15px] font-medium hover:underline transition-colors disabled:opacity-50"
142
+ >
143
+ Delete
144
+ </button>
145
+ )}
146
+ </div>
147
+ </div>
148
+ </div>
149
+ );
150
+ };
@@ -0,0 +1,268 @@
1
+ import React, { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { format, startOfDay, endOfDay, startOfYear } from 'date-fns';
3
+ import { supabase } from '@/integrations/supabase/client';
4
+ import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
5
+ import { ScrollArea } from './ui/scroll-area';
6
+
7
+ interface BookingWithProvider {
8
+ id: string;
9
+ total_amount: number;
10
+ net_amount: number;
11
+ created_at: string;
12
+ provider: {
13
+ id: string;
14
+ name: string;
15
+ avatar_url: string | null;
16
+ } | null;
17
+ }
18
+
19
+ interface BookingsSummaryCardProps {
20
+ className?: string;
21
+ }
22
+
23
+ const CACHE_DURATION_MS = 5 * 60 * 1000; // 5 minutes
24
+
25
+ const BookingsSummaryCard: React.FC<BookingsSummaryCardProps> = ({ className = '' }) => {
26
+ const [bookings, setBookings] = useState<BookingWithProvider[]>([]);
27
+ const [loading, setLoading] = useState(true);
28
+ const [totalRevenue, setTotalRevenue] = useState(0);
29
+ const [totalFees, setTotalFees] = useState(0);
30
+ const [ytdRevenue, setYtdRevenue] = useState(0);
31
+ const [ytdFees, setYtdFees] = useState(0);
32
+ const lastFetchRef = useRef<number>(0);
33
+
34
+ const calculateTotals = useCallback((bookingsList: BookingWithProvider[]) => {
35
+ const revenue = bookingsList.reduce((sum, b) => sum + (b.total_amount || 0), 0);
36
+ const fees = bookingsList.reduce((sum, b) => sum + ((b.total_amount || 0) - (b.net_amount || 0)), 0);
37
+ setTotalRevenue(revenue);
38
+ setTotalFees(fees);
39
+ }, []);
40
+
41
+ const fetchYTDTotals = useCallback(async () => {
42
+ const today = new Date();
43
+ const yearStart = startOfYear(today).toISOString();
44
+ const dayEnd = endOfDay(today).toISOString();
45
+
46
+ const { data, error } = await supabase
47
+ .from('bookings')
48
+ .select('total_amount, net_amount')
49
+ .eq('type', 'booking')
50
+ .gte('created_at', yearStart)
51
+ .lte('created_at', dayEnd);
52
+
53
+ if (error) {
54
+ console.error('Error fetching YTD bookings:', error);
55
+ return;
56
+ }
57
+
58
+ const ytdRev = (data || []).reduce((sum, b) => sum + (b.total_amount || 0), 0);
59
+ const ytdFee = (data || []).reduce((sum, b) => sum + ((b.total_amount || 0) - (b.net_amount || 0)), 0);
60
+ setYtdRevenue(ytdRev);
61
+ setYtdFees(ytdFee);
62
+ }, []);
63
+
64
+ const fetchBookings = useCallback(async (force = false) => {
65
+ const now = Date.now();
66
+
67
+ // Check cache unless forced
68
+ if (!force && lastFetchRef.current && (now - lastFetchRef.current) < CACHE_DURATION_MS) {
69
+ return;
70
+ }
71
+
72
+ setLoading(true);
73
+
74
+ const today = new Date();
75
+ const dayStart = startOfDay(today).toISOString();
76
+ const dayEnd = endOfDay(today).toISOString();
77
+
78
+ const { data, error } = await supabase
79
+ .from('bookings')
80
+ .select(`
81
+ id,
82
+ total_amount,
83
+ net_amount,
84
+ created_at,
85
+ provider:providers(id, name, avatar_url)
86
+ `)
87
+ .eq('type', 'booking')
88
+ .gte('created_at', dayStart)
89
+ .lte('created_at', dayEnd)
90
+ .order('created_at', { ascending: false });
91
+
92
+ if (error) {
93
+ console.error('Error fetching bookings:', error);
94
+ setLoading(false);
95
+ return;
96
+ }
97
+
98
+ const typedData = (data || []) as BookingWithProvider[];
99
+ setBookings(typedData);
100
+ calculateTotals(typedData);
101
+ lastFetchRef.current = now;
102
+ setLoading(false);
103
+ }, [calculateTotals]);
104
+
105
+ // Initial fetch and interval refresh
106
+ useEffect(() => {
107
+ fetchBookings(true);
108
+ fetchYTDTotals();
109
+
110
+ const interval = setInterval(() => {
111
+ fetchBookings(true);
112
+ fetchYTDTotals();
113
+ }, CACHE_DURATION_MS);
114
+
115
+ return () => clearInterval(interval);
116
+ }, [fetchBookings, fetchYTDTotals]);
117
+
118
+ // Real-time subscription for new bookings
119
+ useEffect(() => {
120
+ const channel = supabase
121
+ .channel('bookings-realtime')
122
+ .on(
123
+ 'postgres_changes',
124
+ {
125
+ event: 'INSERT',
126
+ schema: 'public',
127
+ table: 'bookings',
128
+ },
129
+ async (payload) => {
130
+ const newBooking = payload.new as any;
131
+
132
+ // Only process bookings from today
133
+ const today = new Date();
134
+ const bookingDate = new Date(newBooking.created_at);
135
+ if (bookingDate < startOfDay(today) || bookingDate > endOfDay(today)) {
136
+ return;
137
+ }
138
+
139
+ // Only process bookings, not enquiries
140
+ if (newBooking.type !== 'booking') {
141
+ return;
142
+ }
143
+
144
+ // Fetch provider info for the new booking
145
+ const { data: providerData } = await supabase
146
+ .from('providers')
147
+ .select('id, name, avatar_url')
148
+ .eq('id', newBooking.provider_id)
149
+ .maybeSingle();
150
+
151
+ const bookingWithProvider: BookingWithProvider = {
152
+ id: newBooking.id,
153
+ total_amount: newBooking.total_amount,
154
+ net_amount: newBooking.net_amount,
155
+ created_at: newBooking.created_at,
156
+ provider: providerData,
157
+ };
158
+
159
+ setBookings(prev => {
160
+ const updated = [bookingWithProvider, ...prev];
161
+ calculateTotals(updated);
162
+ return updated;
163
+ });
164
+
165
+ // Update YTD totals
166
+ setYtdRevenue(prev => prev + (bookingWithProvider.total_amount || 0));
167
+ setYtdFees(prev => prev + ((bookingWithProvider.total_amount || 0) - (bookingWithProvider.net_amount || 0)));
168
+ }
169
+ )
170
+ .subscribe();
171
+
172
+ return () => {
173
+ supabase.removeChannel(channel);
174
+ };
175
+ }, [calculateTotals]);
176
+
177
+ const formatCurrency = (amount: number) => {
178
+ return new Intl.NumberFormat('en-GB', {
179
+ style: 'currency',
180
+ currency: 'GBP',
181
+ }).format(amount);
182
+ };
183
+
184
+ const getInitials = (name: string) => {
185
+ return name
186
+ .split(' ')
187
+ .map(n => n[0])
188
+ .join('')
189
+ .toUpperCase()
190
+ .slice(0, 2);
191
+ };
192
+
193
+ return (
194
+ <div className={`bg-surface-secondary rounded-xl border border-border-primary flex flex-col ${className}`}>
195
+ {/* Summary Header */}
196
+ <div className="p-4 border-b border-border-primary">
197
+ <div className="grid grid-cols-2 gap-4">
198
+ {/* Today's Metrics */}
199
+ <div>
200
+ <p className="text-label-secondary text-[11px] font-medium uppercase tracking-wide mb-1">Today</p>
201
+ <p className="text-label-primary text-2xl font-bold">{formatCurrency(totalRevenue)}</p>
202
+ <p className="text-label-secondary text-[12px] mt-1">
203
+ Fees: {formatCurrency(totalFees)}
204
+ </p>
205
+ </div>
206
+ {/* YTD Metrics */}
207
+ <div>
208
+ <p className="text-label-secondary text-[11px] font-medium uppercase tracking-wide mb-1">Year to Date</p>
209
+ <p className="text-label-primary text-2xl font-bold">{formatCurrency(ytdRevenue)}</p>
210
+ <p className="text-label-secondary text-[12px] mt-1">
211
+ Fees: {formatCurrency(ytdFees)}
212
+ </p>
213
+ </div>
214
+ </div>
215
+ </div>
216
+
217
+ {/* Bookings List */}
218
+ <ScrollArea className="flex-1 min-h-0">
219
+ <div className="p-2">
220
+ {loading && bookings.length === 0 ? (
221
+ <p className="text-label-secondary text-sm p-2">Loading bookings...</p>
222
+ ) : bookings.length === 0 ? (
223
+ <p className="text-label-secondary text-sm p-2">No bookings today</p>
224
+ ) : (
225
+ <div className="space-y-1">
226
+ {bookings.map((booking) => {
227
+ const fee = (booking.total_amount || 0) - (booking.net_amount || 0);
228
+ return (
229
+ <div
230
+ key={booking.id}
231
+ className="flex items-center gap-3 p-2 rounded-lg hover:bg-surface-primary-hover transition-colors animate-fade-in"
232
+ >
233
+ <Avatar className="h-10 w-10 flex-shrink-0">
234
+ <AvatarImage src={booking.provider?.avatar_url || undefined} />
235
+ <AvatarFallback className="bg-surface-tertiary text-label-secondary text-xs">
236
+ {booking.provider?.name ? getInitials(booking.provider.name) : '?'}
237
+ </AvatarFallback>
238
+ </Avatar>
239
+ <div className="flex-1 min-w-0">
240
+ <p className="text-label-primary text-sm font-semibold truncate">
241
+ {booking.provider?.name || 'Unknown Provider'}
242
+ </p>
243
+ <p className="text-label-secondary text-xs">
244
+ {format(new Date(booking.created_at), 'HH:mm')}
245
+ </p>
246
+ </div>
247
+ <div className="text-right flex-shrink-0">
248
+ <p className="text-label-primary text-sm font-semibold">
249
+ {formatCurrency(booking.total_amount)}
250
+ </p>
251
+ {fee > 0 && (
252
+ <p className="text-label-secondary text-[11px]">
253
+ Fee: {formatCurrency(fee)}
254
+ </p>
255
+ )}
256
+ </div>
257
+ </div>
258
+ );
259
+ })}
260
+ </div>
261
+ )}
262
+ </div>
263
+ </ScrollArea>
264
+ </div>
265
+ );
266
+ };
267
+
268
+ export default BookingsSummaryCard;