@open-mercato/core 0.6.6-develop.6094.1.28b081ea16 → 0.6.6-develop.6121.1.5797a901b3

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 (317) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/catalog_product/index.js +52 -0
  3. package/dist/generated/entities/catalog_product/index.js.map +2 -2
  4. package/dist/generated/entities/catalog_product_variant/index.js +4 -0
  5. package/dist/generated/entities/catalog_product_variant/index.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +28 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/catalog/api/products/route.js +103 -33
  9. package/dist/modules/catalog/api/products/route.js.map +2 -2
  10. package/dist/modules/catalog/api/variants/route.js +4 -0
  11. package/dist/modules/catalog/api/variants/route.js.map +2 -2
  12. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +46 -2
  13. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  14. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +4 -0
  15. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  16. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js +2 -0
  17. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js.map +2 -2
  18. package/dist/modules/catalog/backend/catalog/products/create/page.js +42 -2
  19. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  20. package/dist/modules/catalog/commands/products.js +251 -14
  21. package/dist/modules/catalog/commands/products.js.map +2 -2
  22. package/dist/modules/catalog/commands/variants.js +72 -1
  23. package/dist/modules/catalog/commands/variants.js.map +2 -2
  24. package/dist/modules/catalog/components/products/ProductComplianceSection.js +500 -0
  25. package/dist/modules/catalog/components/products/ProductComplianceSection.js.map +7 -0
  26. package/dist/modules/catalog/components/products/VariantBuilder.js +39 -1
  27. package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
  28. package/dist/modules/catalog/components/products/productForm.js +164 -2
  29. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  30. package/dist/modules/catalog/components/products/variantForm.js +2 -0
  31. package/dist/modules/catalog/components/products/variantForm.js.map +2 -2
  32. package/dist/modules/catalog/data/entities.js +93 -0
  33. package/dist/modules/catalog/data/entities.js.map +2 -2
  34. package/dist/modules/catalog/data/types.js +28 -0
  35. package/dist/modules/catalog/data/types.js.map +2 -2
  36. package/dist/modules/catalog/data/validators.js +100 -5
  37. package/dist/modules/catalog/data/validators.js.map +2 -2
  38. package/dist/modules/catalog/lib/gtin.js +61 -0
  39. package/dist/modules/catalog/lib/gtin.js.map +7 -0
  40. package/dist/modules/catalog/migrations/Migration20260611090000.js +17 -0
  41. package/dist/modules/catalog/migrations/Migration20260611090000.js.map +7 -0
  42. package/dist/modules/catalog/translations.js +1 -1
  43. package/dist/modules/catalog/translations.js.map +2 -2
  44. package/dist/modules/configs/api/cache/route.js +60 -10
  45. package/dist/modules/configs/api/cache/route.js.map +3 -3
  46. package/dist/modules/configs/components/CachePanel.js +52 -29
  47. package/dist/modules/configs/components/CachePanel.js.map +2 -2
  48. package/dist/modules/currencies/api/currencies/options/route.js +18 -4
  49. package/dist/modules/currencies/api/currencies/options/route.js.map +2 -2
  50. package/dist/modules/currencies/services/rateFetchingService.js +35 -18
  51. package/dist/modules/currencies/services/rateFetchingService.js.map +2 -2
  52. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +16 -16
  53. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  54. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +1 -1
  55. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  56. package/dist/modules/customers/backend/calendar/page.js +10 -0
  57. package/dist/modules/customers/backend/calendar/page.js.map +7 -0
  58. package/dist/modules/customers/backend/calendar/page.meta.js +25 -0
  59. package/dist/modules/customers/backend/calendar/page.meta.js.map +7 -0
  60. package/dist/modules/customers/cli.js +1 -0
  61. package/dist/modules/customers/cli.js.map +2 -2
  62. package/dist/modules/customers/components/calendar/AgendaList.js +182 -0
  63. package/dist/modules/customers/components/calendar/AgendaList.js.map +7 -0
  64. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +331 -0
  65. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +7 -0
  66. package/dist/modules/customers/components/calendar/CalendarFooter.js +48 -0
  67. package/dist/modules/customers/components/calendar/CalendarFooter.js.map +7 -0
  68. package/dist/modules/customers/components/calendar/CalendarHeader.js +36 -0
  69. package/dist/modules/customers/components/calendar/CalendarHeader.js.map +7 -0
  70. package/dist/modules/customers/components/calendar/CalendarScreen.js +516 -0
  71. package/dist/modules/customers/components/calendar/CalendarScreen.js.map +7 -0
  72. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +163 -0
  73. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +7 -0
  74. package/dist/modules/customers/components/calendar/CalendarTabs.js +60 -0
  75. package/dist/modules/customers/components/calendar/CalendarTabs.js.map +7 -0
  76. package/dist/modules/customers/components/calendar/CalendarToolbar.js +262 -0
  77. package/dist/modules/customers/components/calendar/CalendarToolbar.js.map +7 -0
  78. package/dist/modules/customers/components/calendar/EventBlock.js +149 -0
  79. package/dist/modules/customers/components/calendar/EventBlock.js.map +7 -0
  80. package/dist/modules/customers/components/calendar/EventPeekPopover.js +72 -0
  81. package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +7 -0
  82. package/dist/modules/customers/components/calendar/MonthGrid.js +214 -0
  83. package/dist/modules/customers/components/calendar/MonthGrid.js.map +7 -0
  84. package/dist/modules/customers/components/calendar/ShortcutsDialog.js +44 -0
  85. package/dist/modules/customers/components/calendar/ShortcutsDialog.js.map +7 -0
  86. package/dist/modules/customers/components/calendar/TimeGrid.js +410 -0
  87. package/dist/modules/customers/components/calendar/TimeGrid.js.map +7 -0
  88. package/dist/modules/customers/components/calendar/UpcomingCards.js +175 -0
  89. package/dist/modules/customers/components/calendar/UpcomingCards.js.map +7 -0
  90. package/dist/modules/customers/components/calendar/editor/CategoryField.js +75 -0
  91. package/dist/modules/customers/components/calendar/editor/CategoryField.js.map +7 -0
  92. package/dist/modules/customers/components/calendar/editor/LocationField.js +30 -0
  93. package/dist/modules/customers/components/calendar/editor/LocationField.js.map +7 -0
  94. package/dist/modules/customers/components/calendar/editor/PeopleField.js +135 -0
  95. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +7 -0
  96. package/dist/modules/customers/components/calendar/editor/RelatedToField.js +218 -0
  97. package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +7 -0
  98. package/dist/modules/customers/components/calendar/editor/RepeatField.js +119 -0
  99. package/dist/modules/customers/components/calendar/editor/RepeatField.js.map +7 -0
  100. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +83 -0
  101. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +7 -0
  102. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js +44 -0
  103. package/dist/modules/customers/components/calendar/editor/SegmentGroup.js.map +7 -0
  104. package/dist/modules/customers/components/calendar/editor/hooks.js +132 -0
  105. package/dist/modules/customers/components/calendar/editor/hooks.js.map +7 -0
  106. package/dist/modules/customers/components/calendar/editor/inputs.js +160 -0
  107. package/dist/modules/customers/components/calendar/editor/inputs.js.map +7 -0
  108. package/dist/modules/customers/components/calendar/editor/lookups.js +107 -0
  109. package/dist/modules/customers/components/calendar/editor/lookups.js.map +7 -0
  110. package/dist/modules/customers/components/calendar/types.js +30 -0
  111. package/dist/modules/customers/components/calendar/types.js.map +7 -0
  112. package/dist/modules/customers/components/calendar/useCalendarItems.js +131 -0
  113. package/dist/modules/customers/components/calendar/useCalendarItems.js.map +7 -0
  114. package/dist/modules/customers/components/calendar/useCalendarPreferences.js +41 -0
  115. package/dist/modules/customers/components/calendar/useCalendarPreferences.js.map +7 -0
  116. package/dist/modules/customers/lib/calendar/categories.js +28 -0
  117. package/dist/modules/customers/lib/calendar/categories.js.map +7 -0
  118. package/dist/modules/customers/lib/calendar/conflicts.js +72 -0
  119. package/dist/modules/customers/lib/calendar/conflicts.js.map +7 -0
  120. package/dist/modules/customers/lib/calendar/editorPayload.js +271 -0
  121. package/dist/modules/customers/lib/calendar/editorPayload.js.map +7 -0
  122. package/dist/modules/customers/lib/calendar/grid.js +48 -0
  123. package/dist/modules/customers/lib/calendar/grid.js.map +7 -0
  124. package/dist/modules/customers/lib/calendar/layout.js +39 -0
  125. package/dist/modules/customers/lib/calendar/layout.js.map +7 -0
  126. package/dist/modules/customers/lib/calendar/mapItem.js +79 -0
  127. package/dist/modules/customers/lib/calendar/mapItem.js.map +7 -0
  128. package/dist/modules/customers/lib/calendar/preferences.js +71 -0
  129. package/dist/modules/customers/lib/calendar/preferences.js.map +7 -0
  130. package/dist/modules/customers/lib/calendar/range.js +46 -0
  131. package/dist/modules/customers/lib/calendar/range.js.map +7 -0
  132. package/dist/modules/customers/lib/calendar/recurrence.js +113 -0
  133. package/dist/modules/customers/lib/calendar/recurrence.js.map +7 -0
  134. package/dist/modules/dashboards/services/widgetDataService.js +5 -5
  135. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  136. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +61 -1
  137. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  138. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +33 -1
  139. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
  140. package/dist/modules/dictionaries/api/[dictionaryId]/route.js +61 -1
  141. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  142. package/dist/modules/dictionaries/api/route.js +33 -1
  143. package/dist/modules/dictionaries/api/route.js.map +2 -2
  144. package/dist/modules/feature_toggles/lib/queries.js +36 -52
  145. package/dist/modules/feature_toggles/lib/queries.js.map +2 -2
  146. package/dist/modules/perspectives/api/[tableId]/[perspectiveId]/route.js +32 -1
  147. package/dist/modules/perspectives/api/[tableId]/[perspectiveId]/route.js.map +2 -2
  148. package/dist/modules/perspectives/api/[tableId]/roles/[roleId]/route.js +32 -1
  149. package/dist/modules/perspectives/api/[tableId]/roles/[roleId]/route.js.map +2 -2
  150. package/dist/modules/perspectives/api/[tableId]/route.js +73 -2
  151. package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
  152. package/dist/modules/perspectives/services/perspectiveService.js +5 -3
  153. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  154. package/dist/modules/planner/api/access.js +9 -0
  155. package/dist/modules/planner/api/access.js.map +2 -2
  156. package/dist/modules/planner/api/availability-date-specific.js +22 -1
  157. package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
  158. package/dist/modules/planner/api/availability-weekly.js +22 -1
  159. package/dist/modules/planner/api/availability-weekly.js.map +2 -2
  160. package/dist/modules/resources/api/resources/tags/assign/route.js +39 -0
  161. package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
  162. package/dist/modules/resources/api/resources/tags/unassign/route.js +39 -0
  163. package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
  164. package/dist/modules/sales/api/settings/document-numbers/route.js +33 -1
  165. package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
  166. package/dist/modules/sales/api/settings/order-editing/route.js +35 -3
  167. package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
  168. package/dist/modules/staff/api/leave-requests/accept/route.js +41 -0
  169. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  170. package/dist/modules/staff/api/leave-requests/reject/route.js +41 -0
  171. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  172. package/dist/modules/staff/api/team-members/self/route.js +40 -0
  173. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  174. package/dist/modules/staff/api/team-members/tags/assign/route.js +41 -0
  175. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  176. package/dist/modules/staff/api/team-members/tags/unassign/route.js +41 -0
  177. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  178. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +61 -1
  179. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  180. package/dist/modules/translations/api/context.js +7 -0
  181. package/dist/modules/translations/api/context.js.map +2 -2
  182. package/dist/modules/translations/api/put/locales.js +33 -1
  183. package/dist/modules/translations/api/put/locales.js.map +2 -2
  184. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +6 -7
  185. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  186. package/dist/modules/workflows/backend/instances/[id]/page.js.map +1 -1
  187. package/dist/modules/workflows/components/EdgeEditDialog.js.map +1 -1
  188. package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js.map +1 -1
  189. package/dist/modules/workflows/components/NodeEditDialog.js.map +1 -1
  190. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +1 -1
  191. package/dist/modules/workflows/components/WorkflowGraph.js.map +1 -1
  192. package/dist/modules/workflows/components/WorkflowGraphImpl.js +17 -9
  193. package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
  194. package/dist/modules/workflows/components/mobile/MobileVisualEditor.js.map +1 -1
  195. package/dist/modules/workflows/frontend/checkout-demo/deriveCurrentStep.js +8 -0
  196. package/dist/modules/workflows/frontend/checkout-demo/deriveCurrentStep.js.map +7 -0
  197. package/dist/modules/workflows/frontend/checkout-demo/page.js +2 -20
  198. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  199. package/dist/modules/workflows/lib/graph-utils.js +8 -0
  200. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  201. package/generated/entities/catalog_product/index.ts +26 -0
  202. package/generated/entities/catalog_product_variant/index.ts +2 -0
  203. package/generated/entity-fields-registry.ts +28 -0
  204. package/package.json +7 -7
  205. package/src/modules/catalog/api/products/route.ts +123 -39
  206. package/src/modules/catalog/api/variants/route.ts +4 -0
  207. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +43 -0
  208. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +14 -0
  209. package/src/modules/catalog/backend/catalog/products/[productId]/variants/create/page.tsx +2 -0
  210. package/src/modules/catalog/backend/catalog/products/create/page.tsx +41 -0
  211. package/src/modules/catalog/commands/products.ts +307 -2
  212. package/src/modules/catalog/commands/variants.ts +90 -1
  213. package/src/modules/catalog/components/products/ProductComplianceSection.tsx +497 -0
  214. package/src/modules/catalog/components/products/VariantBuilder.tsx +43 -0
  215. package/src/modules/catalog/components/products/productForm.ts +262 -0
  216. package/src/modules/catalog/components/products/variantForm.ts +4 -0
  217. package/src/modules/catalog/data/entities.ts +97 -0
  218. package/src/modules/catalog/data/types.ts +36 -0
  219. package/src/modules/catalog/data/validators.ts +159 -3
  220. package/src/modules/catalog/i18n/de.json +64 -0
  221. package/src/modules/catalog/i18n/en.json +64 -0
  222. package/src/modules/catalog/i18n/es.json +64 -0
  223. package/src/modules/catalog/i18n/pl.json +64 -0
  224. package/src/modules/catalog/lib/gtin.ts +66 -0
  225. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +457 -0
  226. package/src/modules/catalog/migrations/Migration20260611090000.ts +17 -0
  227. package/src/modules/catalog/translations.ts +1 -1
  228. package/src/modules/configs/api/cache/route.ts +72 -10
  229. package/src/modules/configs/components/CachePanel.tsx +59 -29
  230. package/src/modules/currencies/api/currencies/options/route.ts +22 -4
  231. package/src/modules/currencies/services/rateFetchingService.ts +63 -26
  232. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +18 -18
  233. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +1 -1
  234. package/src/modules/customer_accounts/i18n/de.json +1 -1
  235. package/src/modules/customer_accounts/i18n/en.json +1 -1
  236. package/src/modules/customer_accounts/i18n/es.json +1 -1
  237. package/src/modules/customer_accounts/i18n/pl.json +1 -1
  238. package/src/modules/customers/backend/calendar/page.meta.ts +23 -0
  239. package/src/modules/customers/backend/calendar/page.tsx +12 -0
  240. package/src/modules/customers/cli.ts +1 -0
  241. package/src/modules/customers/components/calendar/AgendaList.tsx +219 -0
  242. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +386 -0
  243. package/src/modules/customers/components/calendar/CalendarFooter.tsx +61 -0
  244. package/src/modules/customers/components/calendar/CalendarHeader.tsx +40 -0
  245. package/src/modules/customers/components/calendar/CalendarScreen.tsx +590 -0
  246. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +207 -0
  247. package/src/modules/customers/components/calendar/CalendarTabs.tsx +63 -0
  248. package/src/modules/customers/components/calendar/CalendarToolbar.tsx +295 -0
  249. package/src/modules/customers/components/calendar/EventBlock.tsx +205 -0
  250. package/src/modules/customers/components/calendar/EventPeekPopover.tsx +100 -0
  251. package/src/modules/customers/components/calendar/MonthGrid.tsx +236 -0
  252. package/src/modules/customers/components/calendar/ShortcutsDialog.tsx +57 -0
  253. package/src/modules/customers/components/calendar/TimeGrid.tsx +510 -0
  254. package/src/modules/customers/components/calendar/UpcomingCards.tsx +220 -0
  255. package/src/modules/customers/components/calendar/editor/CategoryField.tsx +79 -0
  256. package/src/modules/customers/components/calendar/editor/LocationField.tsx +37 -0
  257. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +148 -0
  258. package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +242 -0
  259. package/src/modules/customers/components/calendar/editor/RepeatField.tsx +132 -0
  260. package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +110 -0
  261. package/src/modules/customers/components/calendar/editor/SegmentGroup.tsx +50 -0
  262. package/src/modules/customers/components/calendar/editor/hooks.ts +162 -0
  263. package/src/modules/customers/components/calendar/editor/inputs.tsx +158 -0
  264. package/src/modules/customers/components/calendar/editor/lookups.ts +160 -0
  265. package/src/modules/customers/components/calendar/types.ts +163 -0
  266. package/src/modules/customers/components/calendar/useCalendarItems.ts +162 -0
  267. package/src/modules/customers/components/calendar/useCalendarPreferences.ts +52 -0
  268. package/src/modules/customers/i18n/de.json +195 -0
  269. package/src/modules/customers/i18n/en.json +195 -0
  270. package/src/modules/customers/i18n/es.json +195 -0
  271. package/src/modules/customers/i18n/pl.json +195 -0
  272. package/src/modules/customers/lib/calendar/categories.ts +26 -0
  273. package/src/modules/customers/lib/calendar/conflicts.ts +117 -0
  274. package/src/modules/customers/lib/calendar/editorPayload.ts +388 -0
  275. package/src/modules/customers/lib/calendar/grid.ts +50 -0
  276. package/src/modules/customers/lib/calendar/layout.ts +47 -0
  277. package/src/modules/customers/lib/calendar/mapItem.ts +94 -0
  278. package/src/modules/customers/lib/calendar/preferences.ts +95 -0
  279. package/src/modules/customers/lib/calendar/range.ts +45 -0
  280. package/src/modules/customers/lib/calendar/recurrence.ts +137 -0
  281. package/src/modules/dashboards/services/widgetDataService.ts +8 -6
  282. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +61 -1
  283. package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +33 -1
  284. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +65 -1
  285. package/src/modules/dictionaries/api/route.ts +36 -1
  286. package/src/modules/feature_toggles/lib/queries.ts +41 -60
  287. package/src/modules/perspectives/api/[tableId]/[perspectiveId]/route.ts +34 -1
  288. package/src/modules/perspectives/api/[tableId]/roles/[roleId]/route.ts +34 -1
  289. package/src/modules/perspectives/api/[tableId]/route.ts +107 -32
  290. package/src/modules/perspectives/services/perspectiveService.ts +9 -5
  291. package/src/modules/planner/api/access.ts +9 -0
  292. package/src/modules/planner/api/availability-date-specific.ts +22 -1
  293. package/src/modules/planner/api/availability-weekly.ts +22 -1
  294. package/src/modules/resources/api/resources/tags/assign/route.ts +42 -0
  295. package/src/modules/resources/api/resources/tags/unassign/route.ts +42 -0
  296. package/src/modules/sales/api/settings/document-numbers/route.ts +35 -1
  297. package/src/modules/sales/api/settings/order-editing/route.ts +37 -3
  298. package/src/modules/staff/api/leave-requests/accept/route.ts +45 -0
  299. package/src/modules/staff/api/leave-requests/reject/route.ts +45 -0
  300. package/src/modules/staff/api/team-members/self/route.ts +43 -0
  301. package/src/modules/staff/api/team-members/tags/assign/route.ts +45 -0
  302. package/src/modules/staff/api/team-members/tags/unassign/route.ts +45 -0
  303. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +65 -1
  304. package/src/modules/translations/api/context.ts +7 -0
  305. package/src/modules/translations/api/put/locales.ts +35 -1
  306. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +11 -9
  307. package/src/modules/workflows/backend/instances/[id]/page.tsx +1 -1
  308. package/src/modules/workflows/components/EdgeEditDialog.tsx +1 -1
  309. package/src/modules/workflows/components/EdgeEditDialogCrudForm.tsx +1 -1
  310. package/src/modules/workflows/components/NodeEditDialog.tsx +1 -1
  311. package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +1 -1
  312. package/src/modules/workflows/components/WorkflowGraph.tsx +2 -2
  313. package/src/modules/workflows/components/WorkflowGraphImpl.tsx +27 -13
  314. package/src/modules/workflows/components/mobile/MobileVisualEditor.tsx +3 -3
  315. package/src/modules/workflows/frontend/checkout-demo/deriveCurrentStep.ts +12 -0
  316. package/src/modules/workflows/frontend/checkout-demo/page.tsx +3 -23
  317. package/src/modules/workflows/lib/graph-utils.ts +21 -1
@@ -0,0 +1,40 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { Plus } from 'lucide-react'
5
+ import { Button } from '@open-mercato/ui/primitives/button'
6
+ import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
7
+ import type { CalendarHeaderProps } from './types'
8
+
9
+ export function CalendarHeader({ view, anchor, onNewEvent }: CalendarHeaderProps) {
10
+ const t = useT()
11
+ const locale = useLocale()
12
+
13
+ const title = React.useMemo(() => {
14
+ if (view === 'agenda') return t('customers.calendar.header.titleAgenda', 'Upcoming')
15
+ if (view === 'month') {
16
+ return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(anchor)
17
+ }
18
+ return new Intl.DateTimeFormat(locale, { month: 'short', day: '2-digit', year: 'numeric' }).format(anchor)
19
+ }, [view, anchor, locale, t])
20
+
21
+ return (
22
+ <header className="flex items-center gap-3 border-b py-4">
23
+ <h1 className="min-w-0 truncate text-xl font-semibold text-foreground">{title}</h1>
24
+ {onNewEvent ? (
25
+ <div className="ml-auto flex shrink-0 items-center">
26
+ <Button
27
+ type="button"
28
+ onClick={onNewEvent}
29
+ aria-label={t('customers.calendar.actions.newEvent', 'New event')}
30
+ >
31
+ <Plus aria-hidden="true" />
32
+ <span className="hidden sm:inline">
33
+ {t('customers.calendar.actions.newEvent', 'New event')}
34
+ </span>
35
+ </Button>
36
+ </div>
37
+ ) : null}
38
+ </header>
39
+ )
40
+ }
@@ -0,0 +1,590 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import dynamic from 'next/dynamic'
5
+ import { addDays } from 'date-fns/addDays'
6
+ import { isSameDay } from 'date-fns/isSameDay'
7
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
8
+ import { apiCall, apiCallOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
9
+ import { matchFeature } from '@open-mercato/shared/lib/auth/featureMatch'
10
+ import {
11
+ buildOptimisticLockHeader,
12
+ extractOptimisticLockConflict,
13
+ } from '@open-mercato/ui/backend/utils/optimisticLock'
14
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
+ import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
16
+ import { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'
17
+ import { Button } from '@open-mercato/ui/primitives/button'
18
+ import { countByCategory } from '../../lib/calendar/categories'
19
+ import { findConflicts } from '../../lib/calendar/conflicts'
20
+ import { getVisibleRange } from '../../lib/calendar/range'
21
+ import { AgendaList } from './AgendaList'
22
+ import { CalendarFooter } from './CalendarFooter'
23
+ import { CalendarHeader } from './CalendarHeader'
24
+ import { CalendarTabs } from './CalendarTabs'
25
+ import { CalendarToolbar } from './CalendarToolbar'
26
+ import { MonthGrid } from './MonthGrid'
27
+ import { ShortcutsDialog } from './ShortcutsDialog'
28
+ import { TimeGrid } from './TimeGrid'
29
+ import { UpcomingCards } from './UpcomingCards'
30
+ import { CalendarSettingsModal } from './CalendarSettingsModal'
31
+ import { useCalendarPreferences } from './useCalendarPreferences'
32
+ import { MAX_WINDOW_ITEMS, useCalendarItems } from './useCalendarItems'
33
+ import type {
34
+ CalendarFiltersValue,
35
+ CalendarItem,
36
+ CalendarRangePreset,
37
+ CalendarTab,
38
+ CalendarView,
39
+ UpcomingCard,
40
+ } from './types'
41
+
42
+ const CalendarEventEditor = dynamic(
43
+ () => import('./CalendarEventEditor').then((mod) => mod.CalendarEventEditor),
44
+ { ssr: false },
45
+ )
46
+
47
+ const SEARCH_DEBOUNCE_MS = 200
48
+ const PHONE_BREAKPOINT_PX = 640
49
+ const HIGHLIGHT_CLEAR_MS = 3000
50
+ const DEFAULT_AGENDA_HORIZON_DAYS = 7
51
+ const UPCOMING_CARDS_COUNT = 4
52
+ const EMPTY_FILTERS: CalendarFiltersValue = { types: [], status: null, ownerUserId: null }
53
+
54
+ type EditorState = { open: boolean; mode: 'create' | 'edit'; item: CalendarItem | null }
55
+
56
+ const MANAGE_FEATURE = 'customers.interactions.manage'
57
+
58
+ function useCanManageInteractions(): boolean {
59
+ const [canManage, setCanManage] = React.useState(false)
60
+ React.useEffect(() => {
61
+ const controller = new AbortController()
62
+ let cancelled = false
63
+ apiCall<{ granted?: unknown[] }>('/api/auth/feature-check', {
64
+ method: 'POST',
65
+ headers: { 'Content-Type': 'application/json' },
66
+ signal: controller.signal,
67
+ body: JSON.stringify({ features: [MANAGE_FEATURE] }),
68
+ })
69
+ .then((call) => {
70
+ if (cancelled || !call.ok) return
71
+ const granted = Array.isArray(call.result?.granted)
72
+ ? call.result.granted.map((feature) => String(feature))
73
+ : []
74
+ setCanManage(granted.some((grantedFeature) => matchFeature(MANAGE_FEATURE, grantedFeature)))
75
+ })
76
+ .catch(() => {
77
+ if (!cancelled && !controller.signal.aborted) setCanManage(false)
78
+ })
79
+ return () => {
80
+ cancelled = true
81
+ controller.abort()
82
+ }
83
+ }, [])
84
+ return canManage
85
+ }
86
+
87
+ function buildTimezoneLabel(): string {
88
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
89
+ const offsetMinutes = -new Date().getTimezoneOffset()
90
+ const sign = offsetMinutes >= 0 ? '+' : '-'
91
+ const absolute = Math.abs(offsetMinutes)
92
+ const hours = Math.floor(absolute / 60)
93
+ const minutes = absolute % 60
94
+ const offset = minutes > 0 ? `${sign}${hours}:${String(minutes).padStart(2, '0')}` : `${sign}${hours}`
95
+ return `${timeZone} (GMT${offset})`
96
+ }
97
+
98
+ function resolveJoinUrl(location: string | null): string | null {
99
+ const trimmed = location?.trim() ?? ''
100
+ if (!trimmed) return null
101
+ if (/^https?:\/\//i.test(trimmed)) return trimmed
102
+ if (/^www\./i.test(trimmed)) return `https://${trimmed}`
103
+ return null
104
+ }
105
+
106
+ function asEditableItem(item: CalendarItem): CalendarItem {
107
+ return item.isRecurringOccurrence ? { ...item, id: item.raw.id } : item
108
+ }
109
+
110
+ export function CalendarScreen() {
111
+ const t = useT()
112
+ const [view, setView] = React.useState<CalendarView>('week')
113
+ const [anchor, setAnchor] = React.useState<Date>(() => new Date())
114
+ const [agendaHorizonDays, setAgendaHorizonDays] = React.useState(DEFAULT_AGENDA_HORIZON_DAYS)
115
+ const [preset, setPreset] = React.useState<CalendarRangePreset | null>('thisWeek')
116
+
117
+ React.useEffect(() => {
118
+ if (window.innerWidth >= PHONE_BREAKPOINT_PX) return
119
+ setView('day')
120
+ setPreset(null)
121
+ }, [])
122
+ const [tab, setTab] = React.useState<CalendarTab>('all')
123
+ const [searchText, setSearchText] = React.useState('')
124
+ const [debouncedSearch, setDebouncedSearch] = React.useState('')
125
+ const [filters, setFilters] = React.useState<CalendarFiltersValue>(EMPTY_FILTERS)
126
+ const [editor, setEditor] = React.useState<EditorState>({ open: false, mode: 'create', item: null })
127
+ const [editorMounted, setEditorMounted] = React.useState(false)
128
+ const [createRange, setCreateRange] = React.useState<{ start: Date; end: Date } | null>(null)
129
+ const [shortcutsOpen, setShortcutsOpen] = React.useState(false)
130
+ const [settingsOpen, setSettingsOpen] = React.useState(false)
131
+ const [highlightItemId, setHighlightItemId] = React.useState<string | null>(null)
132
+ const [hasLoadedOnce, setHasLoadedOnce] = React.useState(false)
133
+ const { preferences, setPreferences, hydrated: preferencesHydrated, userId: currentUserId } = useCalendarPreferences()
134
+
135
+ const range = React.useMemo(
136
+ () => getVisibleRange(view, anchor, agendaHorizonDays),
137
+ [view, anchor, agendaHorizonDays],
138
+ )
139
+ const { items, isLoading, error, truncated, typeLabels, typeColors, refetch } = useCalendarItems(range)
140
+
141
+ React.useEffect(() => {
142
+ if (!isLoading) setHasLoadedOnce(true)
143
+ }, [isLoading])
144
+
145
+ React.useEffect(() => {
146
+ const timer = window.setTimeout(() => setDebouncedSearch(searchText), SEARCH_DEBOUNCE_MS)
147
+ return () => window.clearTimeout(timer)
148
+ }, [searchText])
149
+
150
+ React.useEffect(() => {
151
+ if (!highlightItemId) return
152
+ const timer = window.setTimeout(() => setHighlightItemId(null), HIGHLIGHT_CLEAR_MS)
153
+ return () => window.clearTimeout(timer)
154
+ }, [highlightItemId])
155
+
156
+ const visibleItems = React.useMemo(
157
+ () => items.filter((item) => item.end > range.from && item.start < range.to),
158
+ [items, range],
159
+ )
160
+
161
+ const searchedItems = React.useMemo(() => {
162
+ const query = debouncedSearch.trim().toLowerCase()
163
+ if (!query) return visibleItems
164
+ return visibleItems.filter((item) => {
165
+ if (item.title.toLowerCase().includes(query)) return true
166
+ if (item.location && item.location.toLowerCase().includes(query)) return true
167
+ const rawBody = (item.raw as { body?: unknown }).body
168
+ if (typeof rawBody === 'string' && rawBody.toLowerCase().includes(query)) return true
169
+ return item.participants.some((participant) =>
170
+ (participant.name ?? '').toLowerCase().includes(query),
171
+ )
172
+ })
173
+ }, [visibleItems, debouncedSearch])
174
+
175
+ const baseItems = React.useMemo(
176
+ () =>
177
+ searchedItems.filter((item) => {
178
+ if (filters.types.length > 0 && !filters.types.includes(item.interactionType)) return false
179
+ if (filters.status && item.status !== filters.status) return false
180
+ if (filters.ownerUserId && item.ownerUserId !== filters.ownerUserId) return false
181
+ if (!preferences.showCrmActivities && item.category !== 'meeting' && item.category !== 'event') return false
182
+ return true
183
+ }),
184
+ [searchedItems, filters, preferences.showCrmActivities],
185
+ )
186
+
187
+ const tabCounts = React.useMemo(() => countByCategory(baseItems), [baseItems])
188
+
189
+ const viewItems = React.useMemo(() => {
190
+ if (tab === 'meetings') return baseItems.filter((item) => item.category === 'meeting')
191
+ if (tab === 'events') return baseItems.filter((item) => item.category === 'event')
192
+ return baseItems
193
+ }, [baseItems, tab])
194
+
195
+ const conflictMap = React.useMemo(
196
+ () => findConflicts(baseItems, { scope: preferences.conflictScope, currentUserId }),
197
+ [baseItems, preferences.conflictScope, currentUserId],
198
+ )
199
+ const conflictIds = React.useMemo(() => new Set(conflictMap.keys()), [conflictMap])
200
+
201
+ const upcomingCards = React.useMemo<UpcomingCard[]>(() => {
202
+ const now = new Date()
203
+ const nowMs = now.getTime()
204
+ return baseItems
205
+ .filter((item) => item.start.getTime() >= nowMs)
206
+ .sort((first, second) => first.start.getTime() - second.start.getTime())
207
+ .slice(0, UPCOMING_CARDS_COUNT)
208
+ .map((item) => {
209
+ const conflictCount = preferences.conflictWarnings ? (conflictMap.get(item.id)?.length ?? 0) : 0
210
+ const kind: UpcomingCard['kind'] =
211
+ item.status === 'canceled'
212
+ ? 'cancelled'
213
+ : conflictCount > 0
214
+ ? 'conflicted'
215
+ : isSameDay(item.start, now)
216
+ ? 'today'
217
+ : 'future'
218
+ return { item, kind, conflictCount }
219
+ })
220
+ }, [baseItems, conflictMap, preferences.conflictWarnings])
221
+
222
+ const typeOptions = React.useMemo(() => {
223
+ const values = new Set<string>(Object.keys(typeLabels))
224
+ for (const item of items) values.add(item.interactionType)
225
+ return [...values]
226
+ .sort()
227
+ .map((value) => ({ value, label: typeLabels[value] ?? value }))
228
+ }, [items, typeLabels])
229
+
230
+ const ownerOptions = React.useMemo(() => {
231
+ const participantNames = new Map<string, string>()
232
+ for (const item of items) {
233
+ for (const participant of item.participants) {
234
+ if (participant.name && !participantNames.has(participant.userId)) {
235
+ participantNames.set(participant.userId, participant.name)
236
+ }
237
+ }
238
+ }
239
+ const owners = new Map<string, string>()
240
+ for (const item of items) {
241
+ if (!item.ownerUserId || owners.has(item.ownerUserId)) continue
242
+ owners.set(item.ownerUserId, participantNames.get(item.ownerUserId) ?? item.ownerUserId)
243
+ }
244
+ return [...owners.entries()]
245
+ .map(([value, label]) => ({ value, label }))
246
+ .sort((first, second) => first.label.localeCompare(second.label))
247
+ }, [items])
248
+
249
+ const timezoneLabel = React.useMemo(() => buildTimezoneLabel(), [])
250
+
251
+ const canManage = useCanManageInteractions()
252
+
253
+ const openCreateEditor = React.useCallback(() => {
254
+ if (!canManage) return
255
+ setCreateRange(null)
256
+ setEditorMounted(true)
257
+ setEditor({ open: true, mode: 'create', item: null })
258
+ }, [canManage])
259
+
260
+ const openEditEditor = React.useCallback(
261
+ (item: CalendarItem) => {
262
+ if (!canManage) return
263
+ setCreateRange(null)
264
+ setEditorMounted(true)
265
+ setEditor({ open: true, mode: 'edit', item: asEditableItem(item) })
266
+ },
267
+ [canManage],
268
+ )
269
+
270
+ const handleCreateRange = React.useCallback(
271
+ (start: Date, end: Date) => {
272
+ if (!canManage) return
273
+ setCreateRange({ start, end })
274
+ setEditorMounted(true)
275
+ setEditor({ open: true, mode: 'create', item: null })
276
+ },
277
+ [canManage],
278
+ )
279
+
280
+ const seedActivityTypes = React.useMemo(() => {
281
+ const seen = new Set<string>()
282
+ const labels: string[] = []
283
+ for (const value of Object.keys(typeLabels)) {
284
+ const label = typeLabels[value] ?? value
285
+ if (seen.has(label)) continue
286
+ seen.add(label)
287
+ labels.push(label)
288
+ }
289
+ return labels
290
+ }, [typeLabels])
291
+
292
+ const handleToday = React.useCallback(() => {
293
+ setAnchor(new Date())
294
+ setPreset(null)
295
+ }, [])
296
+
297
+ const handlePresetChange = React.useCallback((next: CalendarRangePreset) => {
298
+ setPreset(next)
299
+ setAnchor(new Date())
300
+ if (next === 'thisWeek') {
301
+ setView('week')
302
+ } else if (next === 'thisMonth') {
303
+ setView('month')
304
+ } else {
305
+ setView('agenda')
306
+ setAgendaHorizonDays(next === 'next30' ? 30 : DEFAULT_AGENDA_HORIZON_DAYS)
307
+ }
308
+ }, [])
309
+
310
+ const handleAnchorChange = React.useCallback((date: Date) => {
311
+ setAnchor(date)
312
+ setPreset(null)
313
+ }, [])
314
+
315
+ const handleViewChange = React.useCallback((next: CalendarView) => {
316
+ setView(next)
317
+ setPreset(null)
318
+ }, [])
319
+
320
+ const handleTimeGridNavigate = React.useCallback((deltaDays: number) => {
321
+ setAnchor((current) => addDays(current, deltaDays))
322
+ setPreset(null)
323
+ }, [])
324
+
325
+ const handleDayOpen = React.useCallback((date: Date) => {
326
+ setView('day')
327
+ setAnchor(date)
328
+ setPreset(null)
329
+ }, [])
330
+
331
+ const handleSeeConflict = React.useCallback((item: CalendarItem) => {
332
+ setView('week')
333
+ setAnchor(item.start)
334
+ setPreset(null)
335
+ setHighlightItemId(item.id)
336
+ }, [])
337
+
338
+ const handleJoin = React.useCallback((item: CalendarItem) => {
339
+ const url = resolveJoinUrl(item.location)
340
+ if (url) window.open(url, '_blank', 'noopener')
341
+ }, [])
342
+
343
+ const { runMutation, retryLastMutation } = useGuardedMutation<{
344
+ formId: string
345
+ resourceKind: string
346
+ resourceId: string
347
+ retryLastMutation: () => Promise<boolean>
348
+ }>({
349
+ contextId: 'customers-calendar-cancel',
350
+ blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),
351
+ })
352
+
353
+ const handleCancelItem = React.useCallback(
354
+ async (item: CalendarItem) => {
355
+ const interactionId = item.raw.id
356
+ try {
357
+ await runMutation({
358
+ operation: () =>
359
+ withScopedApiRequestHeaders(buildOptimisticLockHeader(item.updatedAt), () =>
360
+ apiCallOrThrow('/api/customers/interactions', {
361
+ method: 'PUT',
362
+ headers: { 'content-type': 'application/json' },
363
+ body: JSON.stringify({ id: interactionId, status: 'canceled' }),
364
+ }),
365
+ ),
366
+ mutationPayload: {
367
+ operation: 'cancelCalendarEvent',
368
+ interactionId,
369
+ interactionType: item.interactionType,
370
+ },
371
+ context: {
372
+ formId: 'customers-calendar-cancel',
373
+ resourceKind: 'customers.interaction',
374
+ resourceId: interactionId,
375
+ retryLastMutation,
376
+ },
377
+ })
378
+ flash(t('customers.calendar.cards.cancelSuccess', 'Event cancelled'), 'success')
379
+ refetch()
380
+ } catch (err) {
381
+ // An optimistic-lock 409 is surfaced as the persistent conflict bar by
382
+ // useGuardedMutation (surfaceRecordConflict) — don't re-flash.
383
+ if (extractOptimisticLockConflict(err)) return
384
+ flash(t('customers.calendar.cards.cancelError', 'Failed to cancel event'), 'error')
385
+ }
386
+ },
387
+ [refetch, retryLastMutation, runMutation, t],
388
+ )
389
+
390
+ const focusSearch = React.useCallback(() => {
391
+ const node = document.querySelector('[data-calendar-search]')
392
+ if (node instanceof HTMLInputElement) node.focus()
393
+ else if (node instanceof HTMLElement) node.querySelector('input')?.focus()
394
+ }, [])
395
+
396
+ const editorOpen = editor.open
397
+ React.useEffect(() => {
398
+ const handleKeyDown = (event: KeyboardEvent) => {
399
+ if (event.defaultPrevented) return
400
+ if (event.metaKey || event.ctrlKey || event.altKey) return
401
+ const target = event.target
402
+ if (target instanceof HTMLElement) {
403
+ const tag = target.tagName
404
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || target.isContentEditable) return
405
+ }
406
+ if (editorOpen) return
407
+ switch (event.key) {
408
+ case 't':
409
+ case 'T':
410
+ event.preventDefault()
411
+ setAnchor(new Date())
412
+ setPreset(null)
413
+ break
414
+ case 'd':
415
+ case 'D':
416
+ event.preventDefault()
417
+ setView('day')
418
+ setPreset(null)
419
+ break
420
+ case 'w':
421
+ case 'W':
422
+ event.preventDefault()
423
+ setView('week')
424
+ setPreset(null)
425
+ break
426
+ case 'm':
427
+ case 'M':
428
+ event.preventDefault()
429
+ setView('month')
430
+ setPreset(null)
431
+ break
432
+ case 'a':
433
+ case 'A':
434
+ event.preventDefault()
435
+ setView('agenda')
436
+ setPreset(null)
437
+ break
438
+ case 'n':
439
+ case 'N':
440
+ event.preventDefault()
441
+ openCreateEditor()
442
+ break
443
+ case '/':
444
+ event.preventDefault()
445
+ focusSearch()
446
+ break
447
+ case '?':
448
+ event.preventDefault()
449
+ setShortcutsOpen((open) => !open)
450
+ break
451
+ case 'Escape':
452
+ setHighlightItemId(null)
453
+ break
454
+ default:
455
+ break
456
+ }
457
+ }
458
+ window.addEventListener('keydown', handleKeyDown)
459
+ return () => window.removeEventListener('keydown', handleKeyDown)
460
+ }, [editorOpen, focusSearch, openCreateEditor])
461
+
462
+ const showInitialLoading = (isLoading && !hasLoadedOnce) || !preferencesHydrated
463
+
464
+ let viewArea: React.ReactNode
465
+ if (error) {
466
+ viewArea = (
467
+ <ErrorMessage
468
+ label={t('customers.calendar.errors.loadFailed', 'Failed to load calendar events.')}
469
+ action={
470
+ <Button type="button" variant="outline" size="sm" onClick={refetch}>
471
+ {t('customers.calendar.errors.retry', 'Retry')}
472
+ </Button>
473
+ }
474
+ />
475
+ )
476
+ } else if (showInitialLoading) {
477
+ viewArea = <LoadingMessage label={t('customers.calendar.loading', 'Loading calendar…')} />
478
+ } else if (view === 'month') {
479
+ viewArea = (
480
+ <MonthGrid anchor={anchor} items={viewItems} onItemClick={openEditEditor} onDayOpen={handleDayOpen} />
481
+ )
482
+ } else if (view === 'agenda') {
483
+ viewArea = (
484
+ <AgendaList
485
+ anchor={anchor}
486
+ horizonDays={agendaHorizonDays}
487
+ items={viewItems}
488
+ typeLabels={typeLabels}
489
+ onItemClick={openEditEditor}
490
+ />
491
+ )
492
+ } else {
493
+ viewArea = (
494
+ <TimeGrid
495
+ days={view === 'day' ? 1 : 7}
496
+ anchor={anchor}
497
+ items={viewItems}
498
+ conflictIds={conflictIds}
499
+ showWeekends={preferences.showWeekends}
500
+ showConflicts={preferences.conflictWarnings}
501
+ aiSummaries={preferences.aiSummaries}
502
+ highlightItemId={highlightItemId}
503
+ onItemClick={openEditEditor}
504
+ onJoin={handleJoin}
505
+ onNavigate={handleTimeGridNavigate}
506
+ onCreate={canManage ? openCreateEditor : undefined}
507
+ onCreateRange={canManage ? handleCreateRange : undefined}
508
+ />
509
+ )
510
+ }
511
+
512
+ return (
513
+ <div className="flex min-h-0 flex-1 flex-col gap-4">
514
+ <CalendarHeader view={view} anchor={anchor} onNewEvent={canManage ? openCreateEditor : undefined} />
515
+ <CalendarToolbar
516
+ view={view}
517
+ anchor={anchor}
518
+ range={range}
519
+ preset={preset}
520
+ search={searchText}
521
+ filters={filters}
522
+ typeOptions={typeOptions}
523
+ ownerOptions={ownerOptions}
524
+ onToday={handleToday}
525
+ onPresetChange={handlePresetChange}
526
+ onAnchorChange={handleAnchorChange}
527
+ onSearchChange={setSearchText}
528
+ onFiltersChange={setFilters}
529
+ onOpenSettings={() => setSettingsOpen(true)}
530
+ />
531
+ <UpcomingCards
532
+ cards={upcomingCards}
533
+ canManage={canManage}
534
+ onJoin={handleJoin}
535
+ onSeeConflict={handleSeeConflict}
536
+ onOpen={openEditEditor}
537
+ onEdit={openEditEditor}
538
+ onCancel={handleCancelItem}
539
+ />
540
+ <div className="flex min-h-0 flex-1 flex-col gap-3">
541
+ <CalendarTabs
542
+ tab={tab}
543
+ counts={tabCounts}
544
+ view={view}
545
+ onTabChange={setTab}
546
+ onViewChange={handleViewChange}
547
+ />
548
+ {truncated ? (
549
+ <p className="text-xs text-muted-foreground" role="status">
550
+ {t('customers.calendar.notice.truncated', 'Showing first {count} items for this range.', {
551
+ count: MAX_WINDOW_ITEMS,
552
+ })}
553
+ </p>
554
+ ) : null}
555
+ <div className="flex min-h-[560px] flex-1 flex-col [&>*]:flex-1">{viewArea}</div>
556
+ </div>
557
+ <div className="hidden md:block">
558
+ <CalendarFooter timezoneLabel={timezoneLabel} onOpenShortcuts={() => setShortcutsOpen(true)} />
559
+ </div>
560
+ <ShortcutsDialog open={shortcutsOpen} onOpenChange={setShortcutsOpen} />
561
+ <CalendarSettingsModal
562
+ open={settingsOpen}
563
+ preferences={preferences}
564
+ seedActivityTypes={seedActivityTypes}
565
+ onOpenChange={setSettingsOpen}
566
+ onSave={(next) => {
567
+ setPreferences(next)
568
+ flash(t('customers.calendar.settings.saved', 'Calendar settings saved'), 'success')
569
+ }}
570
+ />
571
+ {editorMounted ? (
572
+ <CalendarEventEditor
573
+ open={editor.open}
574
+ mode={editor.mode}
575
+ item={editor.item}
576
+ defaultDate={anchor}
577
+ defaultRange={createRange}
578
+ typeLabels={typeLabels}
579
+ typeColors={typeColors}
580
+ surfacedTypes={preferences.activityTypes}
581
+ eventCategories={preferences.eventCategories}
582
+ conflictScope={preferences.conflictScope}
583
+ currentUserId={currentUserId}
584
+ onOpenChange={(open) => setEditor((current) => ({ ...current, open }))}
585
+ onSaved={refetch}
586
+ />
587
+ ) : null}
588
+ </div>
589
+ )
590
+ }