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