@withpotter/pages-bookings 0.13.0-next.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/dist/dashboard/(bookings)/attendees/page.js +8 -0
  2. package/dist/dashboard/(bookings)/attendees/page.js.map +1 -0
  3. package/dist/dashboard/(bookings)/availability/_components/availability-screen.js +714 -0
  4. package/dist/dashboard/(bookings)/availability/_components/availability-screen.js.map +1 -0
  5. package/dist/dashboard/(bookings)/availability/_components/month-availability.js +130 -0
  6. package/dist/dashboard/(bookings)/availability/_components/month-availability.js.map +1 -0
  7. package/dist/dashboard/(bookings)/availability/page.js +10 -0
  8. package/dist/dashboard/(bookings)/availability/page.js.map +1 -0
  9. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/assign-resource-modal.js +133 -0
  10. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/assign-resource-modal.js.map +1 -0
  11. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-constants.js +26 -0
  12. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-constants.js.map +1 -0
  13. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-desktop-layout.js +194 -0
  14. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-desktop-layout.js.map +1 -0
  15. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-layout-props.js +1 -0
  16. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-layout-props.js.map +1 -0
  17. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-lede.js +189 -0
  18. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-lede.js.map +1 -0
  19. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-meta.js +38 -0
  20. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-meta.js.map +1 -0
  21. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-layout.js +315 -0
  22. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-layout.js.map +1 -0
  23. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-screen.js +94 -0
  24. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-mobile-screen.js.map +1 -0
  25. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-money.js +54 -0
  26. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-money.js.map +1 -0
  27. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-split.js +71 -0
  28. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/booking-split.js.map +1 -0
  29. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancel-booking-modal.js +102 -0
  30. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancel-booking-modal.js.map +1 -0
  31. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancellation-notice.js +33 -0
  32. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/cancellation-notice.js.map +1 -0
  33. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/customer-info-card.js +65 -0
  34. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/customer-info-card.js.map +1 -0
  35. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/index.js +23 -0
  36. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/index.js.map +1 -0
  37. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/intake-answers-card.js +19 -0
  38. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/intake-answers-card.js.map +1 -0
  39. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/refund-booking-modal.js +156 -0
  40. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/refund-booking-modal.js.map +1 -0
  41. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/reschedule-booking-modal.js +120 -0
  42. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/reschedule-booking-modal.js.map +1 -0
  43. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/security-deposit-card.js +63 -0
  44. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/security-deposit-card.js.map +1 -0
  45. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/settle-deposit-modal.js +141 -0
  46. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/settle-deposit-modal.js.map +1 -0
  47. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/ticket-row-actions.js +49 -0
  48. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/ticket-row-actions.js.map +1 -0
  49. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/waiting-queue-notice.js +42 -0
  50. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components/waiting-queue-notice.js.map +1 -0
  51. package/dist/dashboard/(bookings)/bookings/[bookingId]/_components.js +1 -0
  52. package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/index.js +19 -0
  53. package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/index.js.map +1 -0
  54. package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/use-booking-detail-data.js +352 -0
  55. package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks/use-booking-detail-data.js.map +1 -0
  56. package/dist/dashboard/(bookings)/bookings/[bookingId]/_hooks.js +1 -0
  57. package/dist/dashboard/(bookings)/bookings/[bookingId]/layout.js +33 -0
  58. package/dist/dashboard/(bookings)/bookings/[bookingId]/layout.js.map +1 -0
  59. package/dist/dashboard/(bookings)/bookings/[bookingId]/page.js +356 -0
  60. package/dist/dashboard/(bookings)/bookings/[bookingId]/page.js.map +1 -0
  61. package/dist/dashboard/(bookings)/bookings/_components/appointment-calendar-layout.js +34 -0
  62. package/dist/dashboard/(bookings)/bookings/_components/appointment-calendar-layout.js.map +1 -0
  63. package/dist/dashboard/(bookings)/bookings/_components/booking-constants.js +92 -0
  64. package/dist/dashboard/(bookings)/bookings/_components/booking-constants.js.map +1 -0
  65. package/dist/dashboard/(bookings)/bookings/_components/booking-tab-hints.js +32 -0
  66. package/dist/dashboard/(bookings)/bookings/_components/booking-tab-hints.js.map +1 -0
  67. package/dist/dashboard/(bookings)/bookings/_components/bookings-filter-bar.js +131 -0
  68. package/dist/dashboard/(bookings)/bookings/_components/bookings-filter-bar.js.map +1 -0
  69. package/dist/dashboard/(bookings)/bookings/_components/bookings-mobile-controls.js +119 -0
  70. package/dist/dashboard/(bookings)/bookings/_components/bookings-mobile-controls.js.map +1 -0
  71. package/dist/dashboard/(bookings)/bookings/_components/bookings-screen.js +414 -0
  72. package/dist/dashboard/(bookings)/bookings/_components/bookings-screen.js.map +1 -0
  73. package/dist/dashboard/(bookings)/bookings/_components/bookings-table.js +192 -0
  74. package/dist/dashboard/(bookings)/bookings/_components/bookings-table.js.map +1 -0
  75. package/dist/dashboard/(bookings)/bookings/_components/cannot-take-bookings.js +34 -0
  76. package/dist/dashboard/(bookings)/bookings/_components/cannot-take-bookings.js.map +1 -0
  77. package/dist/dashboard/(bookings)/bookings/_components/demo-data.js +176 -0
  78. package/dist/dashboard/(bookings)/bookings/_components/demo-data.js.map +1 -0
  79. package/dist/dashboard/(bookings)/bookings/_components/index.js +9 -0
  80. package/dist/dashboard/(bookings)/bookings/_components/index.js.map +1 -0
  81. package/dist/dashboard/(bookings)/bookings/_components/today/booking-card.js +126 -0
  82. package/dist/dashboard/(bookings)/bookings/_components/today/booking-card.js.map +1 -0
  83. package/dist/dashboard/(bookings)/bookings/_components/today/demo-data.js +142 -0
  84. package/dist/dashboard/(bookings)/bookings/_components/today/demo-data.js.map +1 -0
  85. package/dist/dashboard/(bookings)/bookings/_components/today/handover.js +128 -0
  86. package/dist/dashboard/(bookings)/bookings/_components/today/handover.js.map +1 -0
  87. package/dist/dashboard/(bookings)/bookings/_components/today/index.js +17 -0
  88. package/dist/dashboard/(bookings)/bookings/_components/today/index.js.map +1 -0
  89. package/dist/dashboard/(bookings)/bookings/_components/today/past-band.js +69 -0
  90. package/dist/dashboard/(bookings)/bookings/_components/today/past-band.js.map +1 -0
  91. package/dist/dashboard/(bookings)/bookings/_components/today/resource-board.js +111 -0
  92. package/dist/dashboard/(bookings)/bookings/_components/today/resource-board.js.map +1 -0
  93. package/dist/dashboard/(bookings)/bookings/_components/today/today-band.js +61 -0
  94. package/dist/dashboard/(bookings)/bookings/_components/today/today-band.js.map +1 -0
  95. package/dist/dashboard/(bookings)/bookings/_components/today/upcoming-band.js +113 -0
  96. package/dist/dashboard/(bookings)/bookings/_components/today/upcoming-band.js.map +1 -0
  97. package/dist/dashboard/(bookings)/bookings/_components/today/waiting-queue.js +62 -0
  98. package/dist/dashboard/(bookings)/bookings/_components/today/waiting-queue.js.map +1 -0
  99. package/dist/dashboard/(bookings)/bookings/_components/today-board.js +192 -0
  100. package/dist/dashboard/(bookings)/bookings/_components/today-board.js.map +1 -0
  101. package/dist/dashboard/(bookings)/bookings/_components/today.js +1 -0
  102. package/dist/dashboard/(bookings)/bookings/_components.js +1 -0
  103. package/dist/dashboard/(bookings)/bookings/_hooks/index.js +5 -0
  104. package/dist/dashboard/(bookings)/bookings/_hooks/index.js.map +1 -0
  105. package/dist/dashboard/(bookings)/bookings/_hooks/use-bookings-data.js +331 -0
  106. package/dist/dashboard/(bookings)/bookings/_hooks/use-bookings-data.js.map +1 -0
  107. package/dist/dashboard/(bookings)/bookings/_hooks/use-today-data.js +309 -0
  108. package/dist/dashboard/(bookings)/bookings/_hooks/use-today-data.js.map +1 -0
  109. package/dist/dashboard/(bookings)/bookings/_hooks.js +1 -0
  110. package/dist/dashboard/(bookings)/bookings/_lib/booking-link.js +13 -0
  111. package/dist/dashboard/(bookings)/bookings/_lib/booking-link.js.map +1 -0
  112. package/dist/dashboard/(bookings)/bookings/attendees/_components/attendee-cards.js +60 -0
  113. package/dist/dashboard/(bookings)/bookings/attendees/_components/attendee-cards.js.map +1 -0
  114. package/dist/dashboard/(bookings)/bookings/attendees/_components/qr-scanner-modal.js +225 -0
  115. package/dist/dashboard/(bookings)/bookings/attendees/_components/qr-scanner-modal.js.map +1 -0
  116. package/dist/dashboard/(bookings)/bookings/attendees/demo-data.js +195 -0
  117. package/dist/dashboard/(bookings)/bookings/attendees/demo-data.js.map +1 -0
  118. package/dist/dashboard/(bookings)/bookings/attendees/page.js +341 -0
  119. package/dist/dashboard/(bookings)/bookings/attendees/page.js.map +1 -0
  120. package/dist/dashboard/(bookings)/bookings/new/page.js +129 -0
  121. package/dist/dashboard/(bookings)/bookings/new/page.js.map +1 -0
  122. package/dist/dashboard/(bookings)/bookings/page.js +10 -0
  123. package/dist/dashboard/(bookings)/bookings/page.js.map +1 -0
  124. package/dist/dashboard/(bookings)/bookings/sessions/new/_components/save-buttons.js +36 -0
  125. package/dist/dashboard/(bookings)/bookings/sessions/new/_components/save-buttons.js.map +1 -0
  126. package/dist/dashboard/(bookings)/bookings/sessions/new/_components/session-creator.js +386 -0
  127. package/dist/dashboard/(bookings)/bookings/sessions/new/_components/session-creator.js.map +1 -0
  128. package/dist/dashboard/(bookings)/bookings/sessions/new/_lib/session-payload.js +62 -0
  129. package/dist/dashboard/(bookings)/bookings/sessions/new/_lib/session-payload.js.map +1 -0
  130. package/dist/dashboard/(bookings)/bookings/sessions/new/page.js +10 -0
  131. package/dist/dashboard/(bookings)/bookings/sessions/new/page.js.map +1 -0
  132. package/dist/dashboard/(bookings)/calendar/_components/calendar-nav.js +84 -0
  133. package/dist/dashboard/(bookings)/calendar/_components/calendar-nav.js.map +1 -0
  134. package/dist/dashboard/(bookings)/calendar/_components/calendar-range.js +84 -0
  135. package/dist/dashboard/(bookings)/calendar/_components/calendar-range.js.map +1 -0
  136. package/dist/dashboard/(bookings)/calendar/_components/day-board-view.js +279 -0
  137. package/dist/dashboard/(bookings)/calendar/_components/day-board-view.js.map +1 -0
  138. package/dist/dashboard/(bookings)/calendar/_components/event-detail-modal.js +113 -0
  139. package/dist/dashboard/(bookings)/calendar/_components/event-detail-modal.js.map +1 -0
  140. package/dist/dashboard/(bookings)/calendar/_components/format.js +22 -0
  141. package/dist/dashboard/(bookings)/calendar/_components/format.js.map +1 -0
  142. package/dist/dashboard/(bookings)/calendar/_components/month-grid.js +109 -0
  143. package/dist/dashboard/(bookings)/calendar/_components/month-grid.js.map +1 -0
  144. package/dist/dashboard/(bookings)/calendar/_components/resource-day-grid.js +325 -0
  145. package/dist/dashboard/(bookings)/calendar/_components/resource-day-grid.js.map +1 -0
  146. package/dist/dashboard/(bookings)/calendar/_components/slot-action-sheet.js +209 -0
  147. package/dist/dashboard/(bookings)/calendar/_components/slot-action-sheet.js.map +1 -0
  148. package/dist/dashboard/(bookings)/calendar/_components/use-block-drag.js +77 -0
  149. package/dist/dashboard/(bookings)/calendar/_components/use-block-drag.js.map +1 -0
  150. package/dist/dashboard/(bookings)/calendar/_components/use-calendar-nav.js +69 -0
  151. package/dist/dashboard/(bookings)/calendar/_components/use-calendar-nav.js.map +1 -0
  152. package/dist/dashboard/(bookings)/calendar/_components/week-grid.js +202 -0
  153. package/dist/dashboard/(bookings)/calendar/_components/week-grid.js.map +1 -0
  154. package/dist/dashboard/(bookings)/calendar/_components/year-grid.js +111 -0
  155. package/dist/dashboard/(bookings)/calendar/_components/year-grid.js.map +1 -0
  156. package/dist/dashboard/(bookings)/calendar/page.js +8 -0
  157. package/dist/dashboard/(bookings)/calendar/page.js.map +1 -0
  158. package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feed-modal.js +126 -0
  159. package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feed-modal.js.map +1 -0
  160. package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feeds-section.js +99 -0
  161. package/dist/dashboard/(bookings)/calendar-sync/_components/channel-feeds-section.js.map +1 -0
  162. package/dist/dashboard/(bookings)/calendar-sync/demo-data.js +64 -0
  163. package/dist/dashboard/(bookings)/calendar-sync/demo-data.js.map +1 -0
  164. package/dist/dashboard/(bookings)/calendar-sync/page.js +15 -0
  165. package/dist/dashboard/(bookings)/calendar-sync/page.js.map +1 -0
  166. package/dist/dashboard/(bookings)/check-in/page.js +252 -0
  167. package/dist/dashboard/(bookings)/check-in/page.js.map +1 -0
  168. package/dist/dashboard/(bookings)/events/[eventId]/_components/event-attendees.js +108 -0
  169. package/dist/dashboard/(bookings)/events/[eventId]/_components/event-attendees.js.map +1 -0
  170. package/dist/dashboard/(bookings)/events/[eventId]/_components/event-share.js +73 -0
  171. package/dist/dashboard/(bookings)/events/[eventId]/_components/event-share.js.map +1 -0
  172. package/dist/dashboard/(bookings)/events/[eventId]/layout.js +10 -0
  173. package/dist/dashboard/(bookings)/events/[eventId]/layout.js.map +1 -0
  174. package/dist/dashboard/(bookings)/events/[eventId]/page.js +247 -0
  175. package/dist/dashboard/(bookings)/events/[eventId]/page.js.map +1 -0
  176. package/dist/dashboard/(bookings)/events/_components/event-grid.js +44 -0
  177. package/dist/dashboard/(bookings)/events/_components/event-grid.js.map +1 -0
  178. package/dist/dashboard/(bookings)/events/_components/event-poster.js +17 -0
  179. package/dist/dashboard/(bookings)/events/_components/event-poster.js.map +1 -0
  180. package/dist/dashboard/(bookings)/events/_hooks/use-events-data.js +55 -0
  181. package/dist/dashboard/(bookings)/events/_hooks/use-events-data.js.map +1 -0
  182. package/dist/dashboard/(bookings)/events/_lib/event-facts.js +68 -0
  183. package/dist/dashboard/(bookings)/events/_lib/event-facts.js.map +1 -0
  184. package/dist/dashboard/(bookings)/events/new/_components/event-composer.js +295 -0
  185. package/dist/dashboard/(bookings)/events/new/_components/event-composer.js.map +1 -0
  186. package/dist/dashboard/(bookings)/events/new/_components/event-poster.js +65 -0
  187. package/dist/dashboard/(bookings)/events/new/_components/event-poster.js.map +1 -0
  188. package/dist/dashboard/(bookings)/events/new/_components/ticket-tiers.js +75 -0
  189. package/dist/dashboard/(bookings)/events/new/_components/ticket-tiers.js.map +1 -0
  190. package/dist/dashboard/(bookings)/events/new/page.js +10 -0
  191. package/dist/dashboard/(bookings)/events/new/page.js.map +1 -0
  192. package/dist/dashboard/(bookings)/events/page.js +214 -0
  193. package/dist/dashboard/(bookings)/events/page.js.map +1 -0
  194. package/dist/dashboard/(bookings)/resources/[resourceId]/layout.js +36 -0
  195. package/dist/dashboard/(bookings)/resources/[resourceId]/layout.js.map +1 -0
  196. package/dist/dashboard/(bookings)/resources/[resourceId]/page.js +351 -0
  197. package/dist/dashboard/(bookings)/resources/[resourceId]/page.js.map +1 -0
  198. package/dist/dashboard/(bookings)/resources/_components/block-time-modal.js +130 -0
  199. package/dist/dashboard/(bookings)/resources/_components/block-time-modal.js.map +1 -0
  200. package/dist/dashboard/(bookings)/resources/_components/category-card.js +74 -0
  201. package/dist/dashboard/(bookings)/resources/_components/category-card.js.map +1 -0
  202. package/dist/dashboard/(bookings)/resources/_components/category-form-dialog.js +197 -0
  203. package/dist/dashboard/(bookings)/resources/_components/category-form-dialog.js.map +1 -0
  204. package/dist/dashboard/(bookings)/resources/_components/day-editor.js +194 -0
  205. package/dist/dashboard/(bookings)/resources/_components/day-editor.js.map +1 -0
  206. package/dist/dashboard/(bookings)/resources/_components/group-dialog.js +170 -0
  207. package/dist/dashboard/(bookings)/resources/_components/group-dialog.js.map +1 -0
  208. package/dist/dashboard/(bookings)/resources/_components/group-header.js +90 -0
  209. package/dist/dashboard/(bookings)/resources/_components/group-header.js.map +1 -0
  210. package/dist/dashboard/(bookings)/resources/_components/index.js +22 -0
  211. package/dist/dashboard/(bookings)/resources/_components/index.js.map +1 -0
  212. package/dist/dashboard/(bookings)/resources/_components/mobile-category-form-sheet.js +256 -0
  213. package/dist/dashboard/(bookings)/resources/_components/mobile-category-form-sheet.js.map +1 -0
  214. package/dist/dashboard/(bookings)/resources/_components/resource-form.js +420 -0
  215. package/dist/dashboard/(bookings)/resources/_components/resource-form.js.map +1 -0
  216. package/dist/dashboard/(bookings)/resources/_components/resource-picker.js +144 -0
  217. package/dist/dashboard/(bookings)/resources/_components/resource-picker.js.map +1 -0
  218. package/dist/dashboard/(bookings)/resources/_components/resource-type-picker-empty.js +109 -0
  219. package/dist/dashboard/(bookings)/resources/_components/resource-type-picker-empty.js.map +1 -0
  220. package/dist/dashboard/(bookings)/resources/_components/roster-empty.js +103 -0
  221. package/dist/dashboard/(bookings)/resources/_components/roster-empty.js.map +1 -0
  222. package/dist/dashboard/(bookings)/resources/_components/roster-grid.js +160 -0
  223. package/dist/dashboard/(bookings)/resources/_components/roster-grid.js.map +1 -0
  224. package/dist/dashboard/(bookings)/resources/_components/schedule-editor.js +244 -0
  225. package/dist/dashboard/(bookings)/resources/_components/schedule-editor.js.map +1 -0
  226. package/dist/dashboard/(bookings)/resources/_components/schedule-timezone-field.js +25 -0
  227. package/dist/dashboard/(bookings)/resources/_components/schedule-timezone-field.js.map +1 -0
  228. package/dist/dashboard/(bookings)/resources/_components/solo-schedule-view.js +146 -0
  229. package/dist/dashboard/(bookings)/resources/_components/solo-schedule-view.js.map +1 -0
  230. package/dist/dashboard/(bookings)/resources/_components/team-onboarding-dialog.js +161 -0
  231. package/dist/dashboard/(bookings)/resources/_components/team-onboarding-dialog.js.map +1 -0
  232. package/dist/dashboard/(bookings)/resources/_components/week-grid.js +175 -0
  233. package/dist/dashboard/(bookings)/resources/_components/week-grid.js.map +1 -0
  234. package/dist/dashboard/(bookings)/resources/_components.js +1 -0
  235. package/dist/dashboard/(bookings)/resources/_hooks/index.js +8 -0
  236. package/dist/dashboard/(bookings)/resources/_hooks/index.js.map +1 -0
  237. package/dist/dashboard/(bookings)/resources/_hooks/use-resource-categories-data.js +86 -0
  238. package/dist/dashboard/(bookings)/resources/_hooks/use-resource-categories-data.js.map +1 -0
  239. package/dist/dashboard/(bookings)/resources/_hooks/use-roster-page.js +138 -0
  240. package/dist/dashboard/(bookings)/resources/_hooks/use-roster-page.js.map +1 -0
  241. package/dist/dashboard/(bookings)/resources/_hooks/use-roster.js +182 -0
  242. package/dist/dashboard/(bookings)/resources/_hooks/use-roster.js.map +1 -0
  243. package/dist/dashboard/(bookings)/resources/_hooks.js +1 -0
  244. package/dist/dashboard/(bookings)/resources/_lib/presets.js +147 -0
  245. package/dist/dashboard/(bookings)/resources/_lib/presets.js.map +1 -0
  246. package/dist/dashboard/(bookings)/resources/_lib/schedule-timezone.js +17 -0
  247. package/dist/dashboard/(bookings)/resources/_lib/schedule-timezone.js.map +1 -0
  248. package/dist/dashboard/(bookings)/resources/_lib/shapes.js +105 -0
  249. package/dist/dashboard/(bookings)/resources/_lib/shapes.js.map +1 -0
  250. package/dist/dashboard/(bookings)/resources/_lib/week.js +122 -0
  251. package/dist/dashboard/(bookings)/resources/_lib/week.js.map +1 -0
  252. package/dist/dashboard/(bookings)/resources/page.js +391 -0
  253. package/dist/dashboard/(bookings)/resources/page.js.map +1 -0
  254. package/dist/dashboard/(bookings)/schedule/page.js +8 -0
  255. package/dist/dashboard/(bookings)/schedule/page.js.map +1 -0
  256. package/dist/dashboard/(bookings)/today/page.js +14 -0
  257. package/dist/dashboard/(bookings)/today/page.js.map +1 -0
  258. package/dist/dashboard/(bookings)/waitlist/_components/demo-data.js +99 -0
  259. package/dist/dashboard/(bookings)/waitlist/_components/demo-data.js.map +1 -0
  260. package/dist/dashboard/(bookings)/waitlist/_components/offer-spot-modal.js +71 -0
  261. package/dist/dashboard/(bookings)/waitlist/_components/offer-spot-modal.js.map +1 -0
  262. package/dist/dashboard/(bookings)/waitlist/_components/waitlist-entry-editor.js +150 -0
  263. package/dist/dashboard/(bookings)/waitlist/_components/waitlist-entry-editor.js.map +1 -0
  264. package/dist/dashboard/(bookings)/waitlist/new/page.js +59 -0
  265. package/dist/dashboard/(bookings)/waitlist/new/page.js.map +1 -0
  266. package/dist/dashboard/(bookings)/waitlist/page.js +317 -0
  267. package/dist/dashboard/(bookings)/waitlist/page.js.map +1 -0
  268. package/dist/index.js +1 -0
  269. package/dist/index.js.map +1 -0
  270. package/package.json +60 -0
@@ -0,0 +1,420 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import {
5
+ kindToResourceType
6
+ } from "@withpotter/lib/resource-types";
7
+ import { ScheduleEditor, getDefaultSchedule } from "./schedule-editor";
8
+ import { availabilityWords } from "../_lib/shapes";
9
+ import { Button } from "@withpotter/ui-kit/button";
10
+ import { FormDialog } from "@withpotter/ui-kit/form-dialog";
11
+ import { ErrorBanner } from "@withpotter/ui-kit/error-banner";
12
+ import { Input, NumberField, Select } from "@withpotter/ui-kit";
13
+ import { ImageUpload } from "@withpotter/components/image-upload";
14
+ import { guessTimezone } from "../_lib/schedule-timezone";
15
+ import { ScheduleTimezoneField } from "./schedule-timezone-field";
16
+ import { useBusinessLocations } from "@withpotter/lib/use-business-locations";
17
+ import { Textarea } from "@withpotter/ui-kit";
18
+ const FORM_ID = "resource-form";
19
+ function ResourceForm({
20
+ open,
21
+ category,
22
+ categories,
23
+ resource,
24
+ onSave,
25
+ onClose
26
+ }) {
27
+ const isEditing = !!resource;
28
+ const [categoryId, setCategoryId] = useState(category.id);
29
+ const groups = categories ?? [];
30
+ const active = groups.find((c) => c.id === categoryId) ?? category;
31
+ const { byTheNight } = availabilityWords(active.kind);
32
+ const [name, setName] = useState("");
33
+ const [description, setDescription] = useState("");
34
+ const [imageUrl, setImageUrl] = useState("");
35
+ const [role, setRole] = useState("");
36
+ const [skills, setSkills] = useState("");
37
+ const [schedule, setSchedule] = useState(getDefaultSchedule());
38
+ const [defaultDuration, setDefaultDuration] = useState(60);
39
+ const [bufferMinutes, setBufferMinutes] = useState(0);
40
+ const [advanceBookingDays, setAdvanceBookingDays] = useState(30);
41
+ const [maxBookingsPerSlot, setMaxBookingsPerSlot] = useState(1);
42
+ const [price, setPrice] = useState("");
43
+ const [locationId, setLocationId] = useState("");
44
+ const [timezone, setTimezone] = useState(guessTimezone);
45
+ const [saving, setSaving] = useState(false);
46
+ const { locations, isMultiSite } = useBusinessLocations();
47
+ const [error, setError] = useState(null);
48
+ useEffect(() => {
49
+ if (resource) {
50
+ setName(resource.name);
51
+ setDescription(resource.description || "");
52
+ setImageUrl(resource.imageUrl || "");
53
+ setRole(
54
+ typeof resource.metadata?.role === "string" ? resource.metadata.role : ""
55
+ );
56
+ setSkills(resource.skills?.join(", ") || "");
57
+ setSchedule(resource.schedule || getDefaultSchedule());
58
+ setDefaultDuration(resource.defaultDurationMinutes || 60);
59
+ setBufferMinutes(resource.bufferMinutes || 0);
60
+ setAdvanceBookingDays(resource.advanceBookingDays || 30);
61
+ setMaxBookingsPerSlot(resource.maxBookingsPerSlot || 1);
62
+ setPrice(resource.price != null ? String(resource.price) : "");
63
+ setLocationId(resource.locationId ?? "");
64
+ setTimezone(resource.timezone || guessTimezone());
65
+ setCategoryId(resource.categoryId ?? category.id);
66
+ } else {
67
+ setName("");
68
+ setDescription("");
69
+ setImageUrl("");
70
+ setRole("");
71
+ setSkills("");
72
+ setSchedule(getDefaultSchedule());
73
+ setDefaultDuration(60);
74
+ setBufferMinutes(0);
75
+ setAdvanceBookingDays(30);
76
+ setMaxBookingsPerSlot(active.kind === "space" ? 10 : 1);
77
+ setPrice("");
78
+ setLocationId("");
79
+ setTimezone(guessTimezone());
80
+ setCategoryId(category.id);
81
+ }
82
+ setError(null);
83
+ }, [resource, open, category.id, active.kind]);
84
+ const handleSubmit = async (e) => {
85
+ e.preventDefault();
86
+ if (!name.trim()) {
87
+ setError("Name is required");
88
+ return;
89
+ }
90
+ setSaving(true);
91
+ setError(null);
92
+ try {
93
+ const metadata = { ...resource?.metadata ?? {} };
94
+ if (role.trim()) metadata.role = role.trim();
95
+ else delete metadata.role;
96
+ const data = {
97
+ name: name.trim(),
98
+ categoryId,
99
+ type: kindToResourceType(active.kind),
100
+ description: description.trim() || void 0,
101
+ imageUrl: imageUrl.trim() || void 0,
102
+ skills: skills ? skills.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
103
+ metadata,
104
+ schedule,
105
+ timezone,
106
+ defaultDurationMinutes: defaultDuration,
107
+ bufferMinutes,
108
+ advanceBookingDays,
109
+ maxBookingsPerSlot,
110
+ price: price ? Number(price) : void 0,
111
+ // Omitted rather than sent empty: the API reads absence as "the main
112
+ // location", which is the right default and not the same instruction
113
+ // as an empty string.
114
+ ...locationId ? { locationId } : {}
115
+ };
116
+ await onSave(data);
117
+ } catch (err) {
118
+ setError(err instanceof Error ? err.message : "Failed to save");
119
+ } finally {
120
+ setSaving(false);
121
+ }
122
+ };
123
+ if (!open) return null;
124
+ const nameLabel = active.kind === "person" ? "Full name" : active.kind === "space" ? "Space name" : active.kind === "unit" ? "Unit name" : "Equipment name";
125
+ const namePlaceholder = active.kind === "person" ? "e.g., Dr. Sarah Johnson" : active.kind === "space" ? "e.g., Studio A" : active.kind === "unit" ? "e.g., Ocean View Suite" : "e.g., Projector HD-1";
126
+ return (
127
+ /* FormDialog, not a right-edge slide-over. The panel this drew is the one
128
+ FormDialog was written to delete — five surfaces had each hand-rolled the
129
+ same edge panel, and none of them closed on Escape, trapped Tab or
130
+ restored focus. `lg` because the body genuinely uses the width: a
131
+ two-column grid and the week's schedule editor. */
132
+ /* @__PURE__ */ jsx(
133
+ FormDialog,
134
+ {
135
+ title: isEditing ? `Edit ${active.label.toLowerCase()}` : `Add ${active.label.toLowerCase()}`,
136
+ size: "lg",
137
+ onClose,
138
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
139
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Cancel" }),
140
+ /* @__PURE__ */ jsx(Button, { type: "submit", form: FORM_ID, loading: saving, children: saving ? "Saving\u2026" : isEditing ? "Save changes" : `Add ${active.label.toLowerCase()}` })
141
+ ] }),
142
+ children: /* @__PURE__ */ jsxs("form", { id: FORM_ID, onSubmit: handleSubmit, className: "space-y-4", children: [
143
+ /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-4", children: [
144
+ active.kind === "person" && /* @__PURE__ */ jsx(
145
+ ImageUpload,
146
+ {
147
+ value: imageUrl || void 0,
148
+ onChange: (url) => setImageUrl(url ?? ""),
149
+ label: "Photo",
150
+ aspectRatio: "square",
151
+ size: "avatar"
152
+ }
153
+ ),
154
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
155
+ /* @__PURE__ */ jsx(
156
+ "label",
157
+ {
158
+ htmlFor: "resource-name",
159
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
160
+ children: nameLabel
161
+ }
162
+ ),
163
+ /* @__PURE__ */ jsx(
164
+ Input,
165
+ {
166
+ id: "resource-name",
167
+ type: "text",
168
+ value: name,
169
+ onChange: (e) => setName(e.target.value),
170
+ placeholder: namePlaceholder,
171
+ className: "w-full"
172
+ }
173
+ )
174
+ ] })
175
+ ] }),
176
+ groups.length > 1 && /* @__PURE__ */ jsxs("div", { children: [
177
+ /* @__PURE__ */ jsx(
178
+ "label",
179
+ {
180
+ htmlFor: "resource-group",
181
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
182
+ children: "Group"
183
+ }
184
+ ),
185
+ /* @__PURE__ */ jsx(
186
+ Select,
187
+ {
188
+ id: "resource-group",
189
+ value: categoryId,
190
+ onChange: setCategoryId,
191
+ options: groups.map((g) => ({
192
+ value: g.id,
193
+ label: g.labelPlural
194
+ }))
195
+ }
196
+ )
197
+ ] }),
198
+ isMultiSite && /* @__PURE__ */ jsxs("div", { children: [
199
+ /* @__PURE__ */ jsx(
200
+ "label",
201
+ {
202
+ htmlFor: "resource-location",
203
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
204
+ children: "Location"
205
+ }
206
+ ),
207
+ /* @__PURE__ */ jsx(
208
+ Select,
209
+ {
210
+ id: "resource-location",
211
+ value: locationId,
212
+ onChange: setLocationId,
213
+ options: locations.map((l) => ({
214
+ value: l.id,
215
+ label: l.name
216
+ })),
217
+ placeholder: "Main location"
218
+ }
219
+ )
220
+ ] }),
221
+ /* @__PURE__ */ jsxs("div", { children: [
222
+ /* @__PURE__ */ jsx(
223
+ "label",
224
+ {
225
+ htmlFor: "resource-description",
226
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
227
+ children: "Description"
228
+ }
229
+ ),
230
+ /* @__PURE__ */ jsx(
231
+ Textarea,
232
+ {
233
+ id: "resource-description",
234
+ value: description,
235
+ onChange: (e) => setDescription(e.target.value),
236
+ placeholder: "Brief description...",
237
+ rows: 3,
238
+ className: "w-full resize-none"
239
+ }
240
+ )
241
+ ] }),
242
+ active.kind !== "person" && /* @__PURE__ */ jsx(
243
+ ImageUpload,
244
+ {
245
+ value: imageUrl || void 0,
246
+ onChange: (url) => setImageUrl(url ?? ""),
247
+ label: "Image",
248
+ aspectRatio: "video"
249
+ }
250
+ ),
251
+ active.kind === "person" && /* @__PURE__ */ jsxs("div", { children: [
252
+ /* @__PURE__ */ jsx(
253
+ "label",
254
+ {
255
+ htmlFor: "resource-role",
256
+ className: "mb-2 block text-sm font-medium text-[var(--text-primary)]",
257
+ children: "Role"
258
+ }
259
+ ),
260
+ /* @__PURE__ */ jsx(
261
+ Input,
262
+ {
263
+ id: "resource-role",
264
+ type: "text",
265
+ value: role,
266
+ onChange: (e) => setRole(e.target.value),
267
+ placeholder: "e.g., Physiotherapist",
268
+ className: "w-full"
269
+ }
270
+ )
271
+ ] }),
272
+ active.kind === "person" && /* @__PURE__ */ jsxs("div", { children: [
273
+ /* @__PURE__ */ jsx(
274
+ "label",
275
+ {
276
+ htmlFor: "resource-skills",
277
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
278
+ children: "Skills / specialties"
279
+ }
280
+ ),
281
+ /* @__PURE__ */ jsx(
282
+ Input,
283
+ {
284
+ id: "resource-skills",
285
+ type: "text",
286
+ value: skills,
287
+ onChange: (e) => setSkills(e.target.value),
288
+ placeholder: "e.g., Coloring, Braids, Styling",
289
+ className: "w-full"
290
+ }
291
+ ),
292
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-[var(--text-tertiary)]", children: "Comma-separated list of skills" })
293
+ ] }),
294
+ (active.kind === "space" || active.kind === "equipment") && /* @__PURE__ */ jsxs("div", { children: [
295
+ /* @__PURE__ */ jsx(
296
+ "label",
297
+ {
298
+ htmlFor: "resource-capacity",
299
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
300
+ children: "At once"
301
+ }
302
+ ),
303
+ /* @__PURE__ */ jsx(
304
+ NumberField,
305
+ {
306
+ id: "resource-capacity",
307
+ min: 1,
308
+ decimal: false,
309
+ value: maxBookingsPerSlot,
310
+ onChange: (v) => setMaxBookingsPerSlot(v ?? 1),
311
+ className: "w-full"
312
+ }
313
+ ),
314
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-[var(--text-tertiary)]", children: "How many bookings can share it" })
315
+ ] }),
316
+ /* @__PURE__ */ jsxs("div", { className: "border-t border-[var(--border-default)] pt-4", children: [
317
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-[var(--text-primary)] mb-4", children: "How it is booked" }),
318
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 mb-4", children: [
319
+ !byTheNight && /* @__PURE__ */ jsxs("div", { children: [
320
+ /* @__PURE__ */ jsx(
321
+ "label",
322
+ {
323
+ htmlFor: "resource-duration",
324
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
325
+ children: "Duration (minutes)"
326
+ }
327
+ ),
328
+ /* @__PURE__ */ jsx(
329
+ NumberField,
330
+ {
331
+ id: "resource-duration",
332
+ min: 5,
333
+ decimal: false,
334
+ value: defaultDuration,
335
+ onChange: (v) => setDefaultDuration(v ?? 5),
336
+ className: "w-full"
337
+ }
338
+ )
339
+ ] }),
340
+ !byTheNight && /* @__PURE__ */ jsxs("div", { children: [
341
+ /* @__PURE__ */ jsx(
342
+ "label",
343
+ {
344
+ htmlFor: "resource-buffer",
345
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
346
+ children: "Gap after (minutes)"
347
+ }
348
+ ),
349
+ /* @__PURE__ */ jsx(
350
+ NumberField,
351
+ {
352
+ id: "resource-buffer",
353
+ min: 0,
354
+ decimal: false,
355
+ value: bufferMinutes,
356
+ onChange: (v) => setBufferMinutes(v ?? 0),
357
+ className: "w-full"
358
+ }
359
+ )
360
+ ] }),
361
+ /* @__PURE__ */ jsxs("div", { children: [
362
+ /* @__PURE__ */ jsx(
363
+ "label",
364
+ {
365
+ htmlFor: "resource-advance",
366
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
367
+ children: "Booked ahead (days)"
368
+ }
369
+ ),
370
+ /* @__PURE__ */ jsx(
371
+ NumberField,
372
+ {
373
+ id: "resource-advance",
374
+ min: 1,
375
+ decimal: false,
376
+ value: advanceBookingDays,
377
+ onChange: (v) => setAdvanceBookingDays(v ?? 1),
378
+ className: "w-full"
379
+ }
380
+ )
381
+ ] }),
382
+ /* @__PURE__ */ jsxs("div", { children: [
383
+ /* @__PURE__ */ jsx(
384
+ "label",
385
+ {
386
+ htmlFor: "resource-price",
387
+ className: "block text-sm font-medium text-[var(--text-primary)] mb-2",
388
+ children: byTheNight ? "Price a night" : "Price"
389
+ }
390
+ ),
391
+ /* @__PURE__ */ jsx(
392
+ Input,
393
+ {
394
+ id: "resource-price",
395
+ type: "number",
396
+ min: 0,
397
+ step: "0.01",
398
+ value: price,
399
+ onChange: (e) => setPrice(e.target.value),
400
+ placeholder: "0.00",
401
+ className: "w-full"
402
+ }
403
+ )
404
+ ] })
405
+ ] }),
406
+ !byTheNight && /* @__PURE__ */ jsxs(Fragment, { children: [
407
+ /* @__PURE__ */ jsx(ScheduleEditor, { schedule, onChange: setSchedule }),
408
+ /* @__PURE__ */ jsx(ScheduleTimezoneField, { value: timezone, onChange: setTimezone })
409
+ ] })
410
+ ] }),
411
+ /* @__PURE__ */ jsx(ErrorBanner, { children: error })
412
+ ] })
413
+ }
414
+ )
415
+ );
416
+ }
417
+ export {
418
+ ResourceForm
419
+ };
420
+ //# sourceMappingURL=resource-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/resources/_components/resource-form.tsx"],"sourcesContent":["'use client';\n\nimport { useState, useEffect } from 'react';\nimport {\n kindToResourceType,\n type Resource,\n type ResourceCategory,\n type WeeklySchedule,\n} from '@withpotter/lib/resource-types';\nimport { ScheduleEditor, getDefaultSchedule } from './schedule-editor';\nimport { availabilityWords } from '../_lib/shapes';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { FormDialog } from '@withpotter/ui-kit/form-dialog';\nimport { ErrorBanner } from '@withpotter/ui-kit/error-banner';\nimport { Input, NumberField, Select } from '@withpotter/ui-kit';\nimport { ImageUpload } from '@withpotter/components/image-upload';\nimport { guessTimezone } from '../_lib/schedule-timezone';\nimport { ScheduleTimezoneField } from './schedule-timezone-field';\nimport { useBusinessLocations } from '@withpotter/lib/use-business-locations';\nimport { Textarea } from '@withpotter/ui-kit';\n\n/** The footer's submit button lives outside the <form>, so it points at this. */\nconst FORM_ID = 'resource-form';\n\n// ─── Props ───────────────────────────────────────────────────────────────────\n\ninterface ResourceFormProps {\n open: boolean;\n /** The group it lands in unless the merchant picks another. */\n category: ResourceCategory;\n /**\n * Every group, so a business with more than one is asked which.\n *\n * The roster creates from the page header now rather than from inside a\n * group's own screen, so the group is no longer implied by where you pressed.\n * One group and the question does not exist; two and it must be asked, or\n * the new court quietly joins the coaches.\n */\n categories?: ResourceCategory[];\n resource?: Resource | null;\n onSave: (data: Record<string, unknown>) => Promise<void>;\n onClose: () => void;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport function ResourceForm({\n open,\n category,\n categories,\n resource,\n onSave,\n onClose,\n}: ResourceFormProps) {\n const isEditing = !!resource;\n const [categoryId, setCategoryId] = useState(category.id);\n const groups = categories ?? [];\n /** Labels and defaults follow the group actually chosen, not the one passed. */\n const active = groups.find((c) => c.id === categoryId) ?? category;\n const { byTheNight } = availabilityWords(active.kind);\n\n // Form state\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [imageUrl, setImageUrl] = useState('');\n const [role, setRole] = useState('');\n const [skills, setSkills] = useState('');\n const [schedule, setSchedule] =\n useState<WeeklySchedule>(getDefaultSchedule());\n const [defaultDuration, setDefaultDuration] = useState(60);\n const [bufferMinutes, setBufferMinutes] = useState(0);\n const [advanceBookingDays, setAdvanceBookingDays] = useState(30);\n const [maxBookingsPerSlot, setMaxBookingsPerSlot] = useState(1);\n const [price, setPrice] = useState('');\n const [locationId, setLocationId] = useState('');\n const [timezone, setTimezone] = useState(guessTimezone);\n const [saving, setSaving] = useState(false);\n\n // A court, room or chair sits at one address. Only asked once a second\n // branch exists — before that there is one answer and the API supplies it.\n const { locations, isMultiSite } = useBusinessLocations();\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (resource) {\n setName(resource.name);\n setDescription(resource.description || '');\n setImageUrl(resource.imageUrl || '');\n setRole(\n typeof resource.metadata?.role === 'string'\n ? resource.metadata.role\n : '',\n );\n setSkills(resource.skills?.join(', ') || '');\n setSchedule(resource.schedule || getDefaultSchedule());\n setDefaultDuration(resource.defaultDurationMinutes || 60);\n setBufferMinutes(resource.bufferMinutes || 0);\n setAdvanceBookingDays(resource.advanceBookingDays || 30);\n setMaxBookingsPerSlot(resource.maxBookingsPerSlot || 1);\n setPrice(resource.price != null ? String(resource.price) : '');\n setLocationId(resource.locationId ?? '');\n setTimezone(resource.timezone || guessTimezone());\n setCategoryId(resource.categoryId ?? category.id);\n } else {\n setName('');\n setDescription('');\n setImageUrl('');\n setRole('');\n setSkills('');\n setSchedule(getDefaultSchedule());\n setDefaultDuration(60);\n setBufferMinutes(0);\n setAdvanceBookingDays(30);\n setMaxBookingsPerSlot(active.kind === 'space' ? 10 : 1);\n setPrice('');\n setLocationId('');\n setTimezone(guessTimezone());\n setCategoryId(category.id);\n }\n setError(null);\n }, [resource, open, category.id, active.kind]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) {\n setError('Name is required');\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n const metadata = { ...(resource?.metadata ?? {}) };\n if (role.trim()) metadata.role = role.trim();\n else delete metadata.role;\n\n const data = {\n name: name.trim(),\n categoryId,\n type: kindToResourceType(active.kind),\n description: description.trim() || undefined,\n imageUrl: imageUrl.trim() || undefined,\n skills: skills\n ? skills\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined,\n metadata,\n schedule,\n timezone,\n defaultDurationMinutes: defaultDuration,\n bufferMinutes,\n advanceBookingDays,\n maxBookingsPerSlot,\n price: price ? Number(price) : undefined,\n // Omitted rather than sent empty: the API reads absence as \"the main\n // location\", which is the right default and not the same instruction\n // as an empty string.\n ...(locationId ? { locationId } : {}),\n };\n await onSave(data);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to save');\n } finally {\n setSaving(false);\n }\n };\n\n if (!open) return null;\n\n // Labels that adapt based on category kind\n const nameLabel =\n active.kind === 'person'\n ? 'Full name'\n : active.kind === 'space'\n ? 'Space name'\n : active.kind === 'unit'\n ? 'Unit name'\n : 'Equipment name';\n\n const namePlaceholder =\n active.kind === 'person'\n ? 'e.g., Dr. Sarah Johnson'\n : active.kind === 'space'\n ? 'e.g., Studio A'\n : active.kind === 'unit'\n ? 'e.g., Ocean View Suite'\n : 'e.g., Projector HD-1';\n\n return (\n /* FormDialog, not a right-edge slide-over. The panel this drew is the one\n FormDialog was written to delete — five surfaces had each hand-rolled the\n same edge panel, and none of them closed on Escape, trapped Tab or\n restored focus. `lg` because the body genuinely uses the width: a\n two-column grid and the week's schedule editor. */\n <FormDialog\n title={\n isEditing\n ? `Edit ${active.label.toLowerCase()}`\n : `Add ${active.label.toLowerCase()}`\n }\n size=\"lg\"\n onClose={onClose}\n footer={\n <>\n <Button variant=\"secondary\" onClick={onClose}>\n Cancel\n </Button>\n {/* `form`, because the footer sits outside the <form> element. */}\n <Button type=\"submit\" form={FORM_ID} loading={saving}>\n {saving\n ? 'Saving…'\n : isEditing\n ? 'Save changes'\n : `Add ${active.label.toLowerCase()}`}\n </Button>\n </>\n }\n >\n <form id={FORM_ID} onSubmit={handleSubmit} className=\"space-y-4\">\n {/* THE FACE SITS WITH THE NAME.\n A person's photo was a full-width dashed zone further down the form,\n which read as a header across the top of the fields rather than as\n one of them. It is 80px beside the name now — the shape a staff form\n takes everywhere a merchant has seen one. A room or a court keeps\n the wide field lower down, where a picture of a place is a picture\n of a place. */}\n <div className=\"flex items-end gap-4\">\n {active.kind === 'person' && (\n <ImageUpload\n value={imageUrl || undefined}\n onChange={(url) => setImageUrl(url ?? '')}\n label=\"Photo\"\n aspectRatio=\"square\"\n size=\"avatar\"\n />\n )}\n <div className=\"min-w-0 flex-1\">\n <label\n htmlFor=\"resource-name\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n {nameLabel}\n </label>\n <Input\n id=\"resource-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={namePlaceholder}\n className=\"w-full\"\n />\n </div>\n </div>\n\n {/* Which group it joins. One group and there is nothing to ask. */}\n {groups.length > 1 && (\n <div>\n <label\n htmlFor=\"resource-group\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n Group\n </label>\n <Select\n id=\"resource-group\"\n value={categoryId}\n onChange={setCategoryId}\n options={groups.map((g) => ({\n value: g.id,\n label: g.labelPlural,\n }))}\n />\n </div>\n )}\n\n {/* Which branch it is at. Hidden on a single-site business. */}\n {isMultiSite && (\n <div>\n <label\n htmlFor=\"resource-location\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n Location\n </label>\n <Select\n id=\"resource-location\"\n value={locationId}\n onChange={setLocationId}\n options={locations.map((l) => ({\n value: l.id,\n label: l.name,\n }))}\n placeholder=\"Main location\"\n />\n </div>\n )}\n\n {/* Description */}\n <div>\n <label\n htmlFor=\"resource-description\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n Description\n </label>\n <Textarea\n id=\"resource-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Brief description...\"\n rows={3}\n className=\"w-full resize-none\"\n />\n </div>\n\n {/* THE PICTURE, WITH SOMEWHERE TO PUT ONE.\n *\n * This was a bare `type=\"url\"` field placeholdered `https://...`, which\n * asks a merchant adding a member of staff to go and host a photograph\n * somewhere first and come back with a link. John, 2026-08-23: *\"when\n * adding a staff it's showing import image with url, there's no\n * affordance to upload an image\"*.\n *\n * `ImageUpload` is the app's image field and has been all along — drop\n * it, browse for it, or paste a link, with the link input still there\n * under the zone. This screen was simply not using it. Nothing about\n * the value changes: both produce a URL in `imageUrl`.\n *\n * A person is not here: a face is 80px beside the name field at the\n * top of this form, not a banner across it. This is the wide zone,\n * and a picture of a room or a court IS wide.\n */}\n {active.kind !== 'person' && (\n <ImageUpload\n value={imageUrl || undefined}\n onChange={(url) => setImageUrl(url ?? '')}\n label=\"Image\"\n aspectRatio=\"video\"\n />\n )}\n\n {/* Skills (person kind only) */}\n {active.kind === 'person' && (\n <div>\n <label\n htmlFor=\"resource-role\"\n className=\"mb-2 block text-sm font-medium text-[var(--text-primary)]\"\n >\n Role\n </label>\n <Input\n id=\"resource-role\"\n type=\"text\"\n value={role}\n onChange={(e) => setRole(e.target.value)}\n placeholder=\"e.g., Physiotherapist\"\n className=\"w-full\"\n />\n </div>\n )}\n\n {active.kind === 'person' && (\n <div>\n <label\n htmlFor=\"resource-skills\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n Skills / specialties\n </label>\n <Input\n id=\"resource-skills\"\n type=\"text\"\n value={skills}\n onChange={(e) => setSkills(e.target.value)}\n placeholder=\"e.g., Coloring, Braids, Styling\"\n className=\"w-full\"\n />\n <p className=\"mt-1 text-xs text-[var(--text-tertiary)]\">\n Comma-separated list of skills\n </p>\n </div>\n )}\n\n {/* Capacity (space kind) */}\n {/* SHARED, NOT JUST SPATIAL.\n\n Capacity was asked only of a space, which left a merchant with ten\n interchangeable rackets no way to say so: either ten rows nobody\n picks between, or one row that takes one booking a slot. The rule is\n the same for both — if a customer would say \"that one\" it is a row,\n and if they would only say \"one of those\" it is this number. A\n person is one person and a let is one let, so neither is asked. */}\n {(active.kind === 'space' || active.kind === 'equipment') && (\n <div>\n <label\n htmlFor=\"resource-capacity\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n At once\n </label>\n <NumberField\n id=\"resource-capacity\"\n min={1}\n decimal={false}\n value={maxBookingsPerSlot}\n onChange={(v) => setMaxBookingsPerSlot(v ?? 1)}\n className=\"w-full\"\n />\n <p className=\"mt-1 text-xs text-[var(--text-tertiary)]\">\n How many bookings can share it\n </p>\n </div>\n )}\n\n {/* Scheduling section */}\n <div className=\"border-t border-[var(--border-default)] pt-4\">\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] mb-4\">\n How it is booked\n </h3>\n\n {/* A NIGHT IS NOT A SESSION.\n\n Duration, the gap after, and a week of opening hours are the three\n questions an hour-shaped booking answers. An apartment answers\n none of them — it is taken by the night — and asking anyway put\n \"9am to 5pm, Monday to Friday\" on a holiday let and a 60-minute\n duration on a three-night stay. The shape decides which questions\n exist; it is the same form otherwise. */}\n <div className=\"grid grid-cols-2 gap-4 mb-4\">\n {/* Duration */}\n {!byTheNight && (\n <div>\n <label\n htmlFor=\"resource-duration\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n Duration (minutes)\n </label>\n <NumberField\n id=\"resource-duration\"\n min={5}\n decimal={false}\n value={defaultDuration}\n onChange={(v) => setDefaultDuration(v ?? 5)}\n className=\"w-full\"\n />\n </div>\n )}\n\n {/* Buffer */}\n {!byTheNight && (\n <div>\n <label\n htmlFor=\"resource-buffer\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n Gap after (minutes)\n </label>\n <NumberField\n id=\"resource-buffer\"\n min={0}\n decimal={false}\n value={bufferMinutes}\n onChange={(v) => setBufferMinutes(v ?? 0)}\n className=\"w-full\"\n />\n </div>\n )}\n\n {/* Advance booking */}\n <div>\n <label\n htmlFor=\"resource-advance\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n Booked ahead (days)\n </label>\n <NumberField\n id=\"resource-advance\"\n min={1}\n decimal={false}\n value={advanceBookingDays}\n onChange={(v) => setAdvanceBookingDays(v ?? 1)}\n className=\"w-full\"\n />\n </div>\n\n {/* Price */}\n <div>\n <label\n htmlFor=\"resource-price\"\n className=\"block text-sm font-medium text-[var(--text-primary)] mb-2\"\n >\n {byTheNight ? 'Price a night' : 'Price'}\n </label>\n <Input\n id=\"resource-price\"\n type=\"number\"\n min={0}\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n placeholder=\"0.00\"\n className=\"w-full\"\n />\n </div>\n </div>\n\n {/* Schedule Editor — see the note above: not for a let. */}\n {!byTheNight && (\n <>\n <ScheduleEditor schedule={schedule} onChange={setSchedule} />\n <ScheduleTimezoneField value={timezone} onChange={setTimezone} />\n </>\n )}\n </div>\n\n <ErrorBanner>{error}</ErrorBanner>\n </form>\n </FormDialog>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,144 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { AnimatePresence, motion } from "motion/react";
5
+ import { Building2, DoorOpen, Plus, Users, Wrench } from "lucide-react";
6
+ import { Button } from "@withpotter/ui-kit/button";
7
+ import { Input } from "@withpotter/ui-kit";
8
+ import { OptionButton } from "@withpotter/ui-kit/option-button";
9
+ import { allPresets, customPreset } from "../_lib/presets";
10
+ const ICON_BY_KIND = {
11
+ person: Users,
12
+ space: DoorOpen,
13
+ equipment: Wrench,
14
+ unit: Building2
15
+ };
16
+ const HEADING_BY_KIND = {
17
+ person: "People",
18
+ space: "Places",
19
+ equipment: "Kit",
20
+ unit: "Stays"
21
+ };
22
+ function ResourcePicker({
23
+ selectedId,
24
+ category,
25
+ onPick,
26
+ onCategoryChange
27
+ }) {
28
+ const filterKind = category ?? "";
29
+ const categoryOpen = !!category;
30
+ const matchingPresets = allPresets();
31
+ const filteredPresets = filterKind ? matchingPresets.filter((preset) => preset.kind === filterKind) : matchingPresets;
32
+ const order = [
33
+ "person",
34
+ "space",
35
+ "equipment",
36
+ "unit"
37
+ ];
38
+ const visibleKinds = filterKind ? [filterKind] : order;
39
+ const blocks = visibleKinds.map((kind) => ({
40
+ kind,
41
+ items: (() => {
42
+ const all = filteredPresets.filter((p) => p.kind === kind);
43
+ return all;
44
+ })()
45
+ })).filter((block) => block.items.length > 0);
46
+ const closeCategory = () => {
47
+ onCategoryChange(null);
48
+ };
49
+ const [ownName, setOwnName] = useState("");
50
+ const own = filterKind ? customPreset(filterKind, ownName) : null;
51
+ const addOwn = () => {
52
+ if (!own) return;
53
+ closeCategory();
54
+ onPick(own);
55
+ };
56
+ const blockContent = blocks.map(({ kind, items }) => {
57
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
58
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-3", children: items.map((preset) => /* @__PURE__ */ jsx(
59
+ OptionButton,
60
+ {
61
+ shape: "card",
62
+ selected: selectedId === preset.id,
63
+ className: "min-h-14 justify-start bg-[var(--bg-muted)]/60 px-4 text-base",
64
+ onClick: () => {
65
+ if (categoryOpen) closeCategory();
66
+ onPick(preset);
67
+ },
68
+ children: preset.plural
69
+ },
70
+ preset.id
71
+ )) }),
72
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
73
+ /* @__PURE__ */ jsx(
74
+ Input,
75
+ {
76
+ value: ownName,
77
+ onChange: (e) => setOwnName(e.target.value),
78
+ onKeyDown: (e) => {
79
+ if (e.key === "Enter") {
80
+ e.preventDefault();
81
+ addOwn();
82
+ }
83
+ },
84
+ placeholder: "Not listed? Type your own, e.g. Master barbers",
85
+ "aria-label": "Name your own"
86
+ }
87
+ ),
88
+ /* @__PURE__ */ jsx(
89
+ Button,
90
+ {
91
+ type: "button",
92
+ variant: "secondary",
93
+ onClick: addOwn,
94
+ disabled: !own,
95
+ icon: /* @__PURE__ */ jsx(Plus, { className: "size-4" }),
96
+ children: "Add"
97
+ }
98
+ )
99
+ ] })
100
+ ] }, kind);
101
+ });
102
+ return /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", initial: false, children: categoryOpen && filterKind ? /* @__PURE__ */ jsx(
103
+ motion.div,
104
+ {
105
+ initial: { opacity: 0, x: 12 },
106
+ animate: { opacity: 1, x: 0 },
107
+ exit: { opacity: 0, x: -12 },
108
+ transition: { duration: 0.16 },
109
+ children: blockContent
110
+ },
111
+ `presets-${filterKind}`
112
+ ) : /* @__PURE__ */ jsx(
113
+ motion.div,
114
+ {
115
+ initial: { opacity: 0, x: -12 },
116
+ animate: { opacity: 1, x: 0 },
117
+ exit: { opacity: 0, x: 12 },
118
+ transition: { duration: 0.16 },
119
+ className: "grid grid-cols-2 gap-2 sm:grid-cols-4",
120
+ children: ["person", "space", "equipment", "unit"].map((kind) => {
121
+ const Icon = ICON_BY_KIND[kind];
122
+ return /* @__PURE__ */ jsxs(
123
+ OptionButton,
124
+ {
125
+ shape: "card",
126
+ selected: filterKind === kind,
127
+ className: "min-h-24 flex-col items-start justify-between gap-3 bg-[var(--bg-muted)]/60 p-4 text-left",
128
+ onClick: () => onCategoryChange(kind),
129
+ children: [
130
+ /* @__PURE__ */ jsx(Icon, { className: "size-5 text-[var(--text-tertiary)]" }),
131
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-semibold", children: HEADING_BY_KIND[kind] })
132
+ ]
133
+ },
134
+ kind
135
+ );
136
+ })
137
+ },
138
+ "categories"
139
+ ) }) });
140
+ }
141
+ export {
142
+ ResourcePicker
143
+ };
144
+ //# sourceMappingURL=resource-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/dashboard/%28bookings%29/resources/_components/resource-picker.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport { Building2, DoorOpen, Plus, Users, Wrench } from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { Button } from '@withpotter/ui-kit/button';\nimport { Input } from '@withpotter/ui-kit';\nimport { OptionButton } from '@withpotter/ui-kit/option-button';\nimport type { ResourceCategoryKind } from '@withpotter/lib/resource-types';\nimport { allPresets, customPreset, type ResourcePreset } from '../_lib/presets';\n\nconst ICON_BY_KIND: Record<ResourceCategoryKind, LucideIcon> = {\n person: Users,\n space: DoorOpen,\n equipment: Wrench,\n unit: Building2,\n};\n\n/**\n * The heading over each block of nouns.\n *\n * NOT the shape titles. \"Someone\" and \"A stay\" are answers to the question\n * *what does a booking take up* — they read as a sentence because they were\n * the whole control. Standing above a row of concrete words they have to be\n * the category those words belong to instead, or the grid reads \"Someone:\n * Stylist, Barber\", which is nobody's English.\n */\nconst HEADING_BY_KIND: Record<ResourceCategoryKind, string> = {\n person: 'People',\n space: 'Places',\n equipment: 'Kit',\n unit: 'Stays',\n};\n\n/**\n * The grid of things this business could be booking.\n *\n * A merchant's own trade first, because that is the list with their word in\n * it, and everything else one press away — a kart track, a storage yard and a\n * hotel with a restaurant in it all exist and none of them is an archetype.\n * The picker stays focused on the supported resource types and presets.\n */\nexport function ResourcePicker({\n selectedId,\n category,\n onPick,\n onCategoryChange,\n}: {\n intentKeys: string[];\n selectedId: string | null;\n showingAll: boolean;\n category: ResourceCategoryKind | null;\n onPick: (preset: ResourcePreset) => void;\n onCategoryChange: (kind: ResourceCategoryKind | null) => void;\n}) {\n const filterKind = category ?? '';\n const categoryOpen = !!category;\n const matchingPresets = allPresets();\n const filteredPresets = filterKind\n ? matchingPresets.filter((preset) => preset.kind === filterKind)\n : matchingPresets;\n\n // The trade's leading shape leads the grid, the same order `shapesFor` puts\n // the shapes in — a restaurant opens on Places, a shortlet on Stays.\n const order: ResourceCategoryKind[] = [\n 'person',\n 'space',\n 'equipment',\n 'unit',\n ];\n const visibleKinds = filterKind ? [filterKind] : order;\n const blocks = visibleKinds\n .map((kind) => ({\n kind,\n items: (() => {\n const all = filteredPresets.filter((p) => p.kind === kind);\n return all;\n })(),\n }))\n .filter((block) => block.items.length > 0);\n\n const closeCategory = () => {\n onCategoryChange(null);\n };\n\n /* The list ends in a blank, because a list of nouns is never finished. */\n const [ownName, setOwnName] = useState('');\n const own = filterKind ? customPreset(filterKind, ownName) : null;\n const addOwn = () => {\n if (!own) return;\n closeCategory();\n onPick(own);\n };\n\n const blockContent = blocks.map(({ kind, items }) => {\n return (\n <div key={kind} className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3\">\n {items.map((preset) => (\n <OptionButton\n key={preset.id}\n shape=\"card\"\n selected={selectedId === preset.id}\n className=\"min-h-14 justify-start bg-[var(--bg-muted)]/60 px-4 text-base\"\n onClick={() => {\n if (categoryOpen) closeCategory();\n onPick(preset);\n }}\n >\n {preset.plural}\n </OptionButton>\n ))}\n </div>\n <div className=\"flex gap-2\">\n <Input\n value={ownName}\n onChange={(e) => setOwnName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n addOwn();\n }\n }}\n placeholder=\"Not listed? Type your own, e.g. Master barbers\"\n aria-label=\"Name your own\"\n />\n <Button\n type=\"button\"\n variant=\"secondary\"\n onClick={addOwn}\n disabled={!own}\n icon={<Plus className=\"size-4\" />}\n >\n Add\n </Button>\n </div>\n </div>\n );\n });\n\n return (\n <div className=\"space-y-4\">\n <AnimatePresence mode=\"wait\" initial={false}>\n {categoryOpen && filterKind ? (\n <motion.div\n key={`presets-${filterKind}`}\n initial={{ opacity: 0, x: 12 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: -12 }}\n transition={{ duration: 0.16 }}\n >\n {blockContent}\n </motion.div>\n ) : (\n <motion.div\n key=\"categories\"\n initial={{ opacity: 0, x: -12 }}\n animate={{ opacity: 1, x: 0 }}\n exit={{ opacity: 0, x: 12 }}\n transition={{ duration: 0.16 }}\n className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\"\n >\n {(['person', 'space', 'equipment', 'unit'] as const).map((kind) => {\n const Icon = ICON_BY_KIND[kind];\n return (\n <OptionButton\n key={kind}\n shape=\"card\"\n selected={filterKind === kind}\n className=\"min-h-24 flex-col items-start justify-between gap-3 bg-[var(--bg-muted)]/60 p-4 text-left\"\n onClick={() => onCategoryChange(kind)}\n >\n <Icon className=\"size-5 text-[var(--text-tertiary)]\" />\n <span className=\"block text-sm font-semibold\">\n {HEADING_BY_KIND[kind]}\n </span>\n </OptionButton>\n );\n })}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n"],"mappings":"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","names":[]}