@liorian/sdk 0.28.0-alpha.56010ca

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 (362) hide show
  1. package/README.md +483 -0
  2. package/package.json +94 -0
  3. package/schemas/module.schema.json +818 -0
  4. package/src/application/index.ts +3 -0
  5. package/src/application/module-instance.ts +36 -0
  6. package/src/application/service/auth-api-service.ts +291 -0
  7. package/src/application/service/auth-user.service.ts +108 -0
  8. package/src/application/service/index.ts +12 -0
  9. package/src/application/service/module-activation-api.service.ts +105 -0
  10. package/src/application/service/notifications-api-service.ts +38 -0
  11. package/src/application/service/notifications-push.service.ts +299 -0
  12. package/src/application/service/notifications-socket-service.ts +76 -0
  13. package/src/application/service/organizations-api-service.ts +104 -0
  14. package/src/application/service/otp-api-service.ts +11 -0
  15. package/src/application/service/storage-api-service.ts +77 -0
  16. package/src/application/service/storefront-api.service.ts +72 -0
  17. package/src/application/service/user-preferences-api.service.ts +57 -0
  18. package/src/domain/config/app.config.ts +35 -0
  19. package/src/domain/config/auth.config.ts +30 -0
  20. package/src/domain/config/index.ts +4 -0
  21. package/src/domain/config/logo.config.ts +17 -0
  22. package/src/domain/entities/activities.interface.ts +18 -0
  23. package/src/domain/entities/catalog.interface.ts +96 -0
  24. package/src/domain/entities/index.ts +13 -0
  25. package/src/domain/entities/media.ts +22 -0
  26. package/src/domain/entities/module-activation.interface.ts +173 -0
  27. package/src/domain/entities/module.interface.ts +101 -0
  28. package/src/domain/entities/notification.interface.ts +23 -0
  29. package/src/domain/entities/objectable.type.ts +3 -0
  30. package/src/domain/entities/organization.interface.ts +81 -0
  31. package/src/domain/entities/user-auth.interface.ts +17 -0
  32. package/src/domain/entities/user-phone.interface.ts +11 -0
  33. package/src/domain/entities/user-profile.interface.ts +23 -0
  34. package/src/domain/entities/user.interface.ts +79 -0
  35. package/src/domain/enums/coloring.enum.ts +6 -0
  36. package/src/domain/enums/domains.enum.ts +282 -0
  37. package/src/domain/enums/index.ts +8 -0
  38. package/src/domain/enums/module-category.enum.ts +57 -0
  39. package/src/domain/enums/module.enum.ts +97 -0
  40. package/src/domain/enums/routine.enum.ts +12 -0
  41. package/src/domain/enums/user-profile.enum.ts +25 -0
  42. package/src/domain/enums/user-status.enum.ts +7 -0
  43. package/src/domain/index.ts +6 -0
  44. package/src/domain/typing/auth-caches.ts +12 -0
  45. package/src/domain/typing/auth-oauth.types.ts +172 -0
  46. package/src/domain/typing/devise.ts +2 -0
  47. package/src/domain/typing/index.ts +10 -0
  48. package/src/domain/typing/response.ts +30 -0
  49. package/src/domain/typing/routine.types.ts +113 -0
  50. package/src/domain/typing/session.interface.ts +28 -0
  51. package/src/domain/typing/statisticals.ts +11 -0
  52. package/src/domain/typing/user-media-upload.ts +9 -0
  53. package/src/domain/typing/values.ts +6 -0
  54. package/src/index.tsx +168 -0
  55. package/src/infrastructure/capabilities/cache-storage/capability.ts +129 -0
  56. package/src/infrastructure/capabilities/cache-storage/enum.ts +5 -0
  57. package/src/infrastructure/capabilities/cache-storage/index.ts +4 -0
  58. package/src/infrastructure/capabilities/cache-storage/types.ts +13 -0
  59. package/src/infrastructure/capabilities/index.ts +3 -0
  60. package/src/infrastructure/capabilities/stackable/capability.ts +55 -0
  61. package/src/infrastructure/capabilities/stackable/index.ts +3 -0
  62. package/src/infrastructure/capabilities/stackable/types.ts +24 -0
  63. package/src/infrastructure/common-classname.constant.ts +4 -0
  64. package/src/infrastructure/data/countries.ts +208 -0
  65. package/src/infrastructure/data/gender.ts +5 -0
  66. package/src/infrastructure/data/index.ts +3 -0
  67. package/src/infrastructure/dataset/index.ts +3 -0
  68. package/src/infrastructure/dataset/sign-in.dataset.ts +21 -0
  69. package/src/infrastructure/dataset/sign-up.dataset.ts +64 -0
  70. package/src/infrastructure/hooks/index.ts +10 -0
  71. package/src/infrastructure/hooks/use-activity-lock-draft.ts +69 -0
  72. package/src/infrastructure/hooks/use-auth.ts +24 -0
  73. package/src/infrastructure/hooks/use-elastic-interaction.ts +180 -0
  74. package/src/infrastructure/hooks/use-is-desktop.ts +25 -0
  75. package/src/infrastructure/hooks/use-is-tauri.ts +17 -0
  76. package/src/infrastructure/hooks/use-mobile.ts +19 -0
  77. package/src/infrastructure/hooks/use-notifications.ts +128 -0
  78. package/src/infrastructure/hooks/use-recent-colors.ts +113 -0
  79. package/src/infrastructure/hooks/use-recent-modules.ts +76 -0
  80. package/src/infrastructure/index.tsx +11 -0
  81. package/src/infrastructure/library/design-system.ts +95 -0
  82. package/src/infrastructure/library/index.tsx +5 -0
  83. package/src/infrastructure/library/motion-utils.ts +77 -0
  84. package/src/infrastructure/library/motion.tsx +31 -0
  85. package/src/infrastructure/library/scroll-progress.tsx +93 -0
  86. package/src/infrastructure/providers/auth-guard.provider.tsx +47 -0
  87. package/src/infrastructure/providers/auth.provider.tsx +168 -0
  88. package/src/infrastructure/providers/index.tsx +8 -0
  89. package/src/infrastructure/providers/module-usage.provider.tsx +95 -0
  90. package/src/infrastructure/providers/modules-guard.provider.tsx +64 -0
  91. package/src/infrastructure/providers/modules-routines.provider.tsx +33 -0
  92. package/src/infrastructure/providers/modules-switcher.provider.tsx +25 -0
  93. package/src/infrastructure/providers/query.provider.tsx +27 -0
  94. package/src/infrastructure/routines/__tests__/routine.test.ts +368 -0
  95. package/src/infrastructure/routines/index.tsx +3 -0
  96. package/src/infrastructure/routines/routine.hook.ts +121 -0
  97. package/src/infrastructure/routines/routine.ts +283 -0
  98. package/src/infrastructure/routines/routine.tsx +283 -0
  99. package/src/infrastructure/stores/activity-lock.store.ts +22 -0
  100. package/src/infrastructure/stores/auth-user-connected.store.ts +24 -0
  101. package/src/infrastructure/stores/dataset.store.ts +106 -0
  102. package/src/infrastructure/stores/index.ts +6 -0
  103. package/src/infrastructure/stores/module.store.ts +266 -0
  104. package/src/infrastructure/stores/upload.store.ts +198 -0
  105. package/src/infrastructure/utilities/access-label.util.ts +158 -0
  106. package/src/infrastructure/utilities/activities.util.tsx +54 -0
  107. package/src/infrastructure/utilities/activity-lock.service.ts +263 -0
  108. package/src/infrastructure/utilities/activity-tracker.service.ts +205 -0
  109. package/src/infrastructure/utilities/api-service.ts +237 -0
  110. package/src/infrastructure/utilities/auth-logout.util.ts +23 -0
  111. package/src/infrastructure/utilities/classname.util.ts +7 -0
  112. package/src/infrastructure/utilities/encryption.service.ts +125 -0
  113. package/src/infrastructure/utilities/form-snapshot.service.ts +304 -0
  114. package/src/infrastructure/utilities/format.util.ts +40 -0
  115. package/src/infrastructure/utilities/index.tsx +23 -0
  116. package/src/infrastructure/utilities/media-label.service.ts +47 -0
  117. package/src/infrastructure/utilities/modules.ts +11 -0
  118. package/src/infrastructure/utilities/permission.util.ts +22 -0
  119. package/src/infrastructure/utilities/push-permission.util.ts +43 -0
  120. package/src/infrastructure/utilities/route-access.ts +38 -0
  121. package/src/infrastructure/utilities/socket-service.ts +123 -0
  122. package/src/infrastructure/utilities/sso-auth.util.ts +188 -0
  123. package/src/infrastructure/utilities/stringable.util.ts +16 -0
  124. package/src/infrastructure/utilities/strings.util.ts +18 -0
  125. package/src/infrastructure/utilities/tauri.util.ts +15 -0
  126. package/src/infrastructure/utilities/utils.ts +6 -0
  127. package/src/infrastructure/utilities/values.ts +5 -0
  128. package/src/presentation/8starlabs-ui/button.tsx +94 -0
  129. package/src/presentation/8starlabs-ui/flip-clock.tsx +368 -0
  130. package/src/presentation/8starlabs-ui/heatmap.tsx +430 -0
  131. package/src/presentation/8starlabs-ui/hooks/index.tsx +2 -0
  132. package/src/presentation/8starlabs-ui/hooks/use-mobile.ts +21 -0
  133. package/src/presentation/8starlabs-ui/index.tsx +20 -0
  134. package/src/presentation/8starlabs-ui/json-viewer.tsx +972 -0
  135. package/src/presentation/8starlabs-ui/marquee.tsx +134 -0
  136. package/src/presentation/8starlabs-ui/partition-bar.tsx +198 -0
  137. package/src/presentation/8starlabs-ui/scroll-fade.tsx +158 -0
  138. package/src/presentation/8starlabs-ui/status-indicator.tsx +86 -0
  139. package/src/presentation/8starlabs-ui/system-banner.tsx +50 -0
  140. package/src/presentation/8starlabs-ui/timeline.tsx +576 -0
  141. package/src/presentation/8starlabs-ui/transport-badge.tsx +249 -0
  142. package/src/presentation/8starlabs-ui/ui/copy-button.tsx +60 -0
  143. package/src/presentation/8starlabs-ui/ui/index.tsx +2 -0
  144. package/src/presentation/analytics-section.tsx +85 -0
  145. package/src/presentation/app-sidebar.tsx +234 -0
  146. package/src/presentation/charts/area-widget.chart-skeleton.tsx +61 -0
  147. package/src/presentation/charts/area-widget.chart.tsx +128 -0
  148. package/src/presentation/charts/chart-widgets.types.ts +81 -0
  149. package/src/presentation/charts/index.tsx +16 -0
  150. package/src/presentation/charts/line-widget.chart-skeleton.tsx +64 -0
  151. package/src/presentation/charts/line-widget.chart.tsx +105 -0
  152. package/src/presentation/charts/pie-widget.chart-skeleton.tsx +56 -0
  153. package/src/presentation/charts/pie-widget.chart.tsx +168 -0
  154. package/src/presentation/charts/radar-widget.chart-skeleton.tsx +67 -0
  155. package/src/presentation/charts/radar-widget.chart.tsx +80 -0
  156. package/src/presentation/charts/radial-stacked-widget.chart-skeleton.tsx +66 -0
  157. package/src/presentation/charts/radial-stacked-widget.chart.tsx +120 -0
  158. package/src/presentation/charts/radial-widget.chart-skeleton.tsx +61 -0
  159. package/src/presentation/charts/radial-widget.chart.tsx +87 -0
  160. package/src/presentation/charts/tooltip-widget.chart-skeleton.tsx +64 -0
  161. package/src/presentation/charts/tooltip-widget.chart.tsx +89 -0
  162. package/src/presentation/components/activity-actions.tsx +20 -0
  163. package/src/presentation/components/activity-container.tsx +55 -0
  164. package/src/presentation/components/activity-content.tsx +20 -0
  165. package/src/presentation/components/activity-descriptor-parser.tsx +37 -0
  166. package/src/presentation/components/activity-header.tsx +20 -0
  167. package/src/presentation/components/activity-loader.tsx +19 -0
  168. package/src/presentation/components/activity-title.tsx +32 -0
  169. package/src/presentation/components/activity.tsx +22 -0
  170. package/src/presentation/components/auth/index.tsx +4 -0
  171. package/src/presentation/components/auth/sso-callback.view.tsx +44 -0
  172. package/src/presentation/components/auth/sso-disconnect.view.tsx +41 -0
  173. package/src/presentation/components/auth/sso-sign-in.view.tsx +29 -0
  174. package/src/presentation/components/auth-lock-screen.tsx +309 -0
  175. package/src/presentation/components/auth-session.view.tsx +17 -0
  176. package/src/presentation/components/data-table.tsx +814 -0
  177. package/src/presentation/components/dynamic-icon.tsx +16 -0
  178. package/src/presentation/components/floating-upload.tsx +291 -0
  179. package/src/presentation/components/index.tsx +40 -0
  180. package/src/presentation/components/local-time.tsx +170 -0
  181. package/src/presentation/components/markdown-parser.tsx +201 -0
  182. package/src/presentation/components/message-scroller.tsx +131 -0
  183. package/src/presentation/components/module-first-access-config.view.tsx +181 -0
  184. package/src/presentation/components/module-first-access-pending.view.tsx +38 -0
  185. package/src/presentation/components/module-guard.tsx +34 -0
  186. package/src/presentation/components/modules-first-access.guard.tsx +88 -0
  187. package/src/presentation/components/modules-guard-forbidden.tsx +46 -0
  188. package/src/presentation/components/notification-details.modal.tsx +145 -0
  189. package/src/presentation/components/notification-item.tsx +127 -0
  190. package/src/presentation/components/notifications-popover.tsx +128 -0
  191. package/src/presentation/components/push-notifications.provider.tsx +65 -0
  192. package/src/presentation/components/upload-progress-dialog.tsx +140 -0
  193. package/src/presentation/components/waiting-bar.tsx +22 -0
  194. package/src/presentation/components/waiting-section.tsx +15 -0
  195. package/src/presentation/components/waiting.tsx +14 -0
  196. package/src/presentation/data-grid/data-grid-empty.tsx +24 -0
  197. package/src/presentation/data-grid/data-grid-search-engine.tsx +32 -0
  198. package/src/presentation/data-grid/data-grid.tsx +892 -0
  199. package/src/presentation/data-grid/index.tsx +4 -0
  200. package/src/presentation/form-screen.tsx +95 -0
  201. package/src/presentation/icons/custom.tsx +20 -0
  202. package/src/presentation/icons/glyphIcons.tsx +53 -0
  203. package/src/presentation/icons/icons.enum.ts +4 -0
  204. package/src/presentation/icons/icons.util.ts +6 -0
  205. package/src/presentation/icons/index.tsx +8 -0
  206. package/src/presentation/icons/lucide.tsx +17 -0
  207. package/src/presentation/icons/remix.tsx +33 -0
  208. package/src/presentation/icons/types.ts +27 -0
  209. package/src/presentation/index.tsx +45 -0
  210. package/src/presentation/modals/components/ModalBox.tsx +74 -0
  211. package/src/presentation/modals/components/ModalPortal.tsx +31 -0
  212. package/src/presentation/modals/components/ModalStepper.tsx +319 -0
  213. package/src/presentation/modals/components/ModalWrapper.tsx +65 -0
  214. package/src/presentation/modals/components/index.tsx +5 -0
  215. package/src/presentation/modals/hooks/index.ts +2 -0
  216. package/src/presentation/modals/hooks/useModal.ts +38 -0
  217. package/src/presentation/modals/index.tsx +5 -0
  218. package/src/presentation/modals/stores/index.ts +2 -0
  219. package/src/presentation/modals/stores/useModalStore.ts +90 -0
  220. package/src/presentation/modals/types/index.ts +2 -0
  221. package/src/presentation/modals/types/modal.type.ts +33 -0
  222. package/src/presentation/module-menubar.tsx +300 -0
  223. package/src/presentation/module-widget.tsx +233 -0
  224. package/src/presentation/nav-documents.tsx +89 -0
  225. package/src/presentation/nav-main.tsx +62 -0
  226. package/src/presentation/nav-secondary.tsx +41 -0
  227. package/src/presentation/nav-user.tsx +109 -0
  228. package/src/presentation/permission-action-badge.tsx +35 -0
  229. package/src/presentation/reui/index.tsx +3 -0
  230. package/src/presentation/reui/stepper.tsx +508 -0
  231. package/src/presentation/reui/timeline.tsx +226 -0
  232. package/src/presentation/sheets/index.tsx +2 -0
  233. package/src/presentation/sheets/legacy-sheet-header.tsx +0 -0
  234. package/src/presentation/sheets/legacy-sheet.tsx +37 -0
  235. package/src/presentation/size.enum.ts +19 -0
  236. package/src/presentation/system/index.tsx +3 -0
  237. package/src/presentation/system/logo.theme.tsx +21 -0
  238. package/src/presentation/system/types.ts +10 -0
  239. package/src/presentation/theme/index.tsx +6 -0
  240. package/src/presentation/theme/prefer-color-scheme.tsx +50 -0
  241. package/src/presentation/theme/switch-color-scheme.tsx +16 -0
  242. package/src/presentation/theme/theme-switcher-button.tsx +20 -0
  243. package/src/presentation/theme/theme.enum.ts +4 -0
  244. package/src/presentation/theme/theme.store.ts +87 -0
  245. package/src/presentation/themes/index.tsx +2 -0
  246. package/src/presentation/themes/katon/dashboard-layout.tsx +1080 -0
  247. package/src/presentation/themes/katon/edge-section.tsx +19 -0
  248. package/src/presentation/themes/katon/header-menubar.tsx +270 -0
  249. package/src/presentation/themes/katon/header-modules-available.tsx +44 -0
  250. package/src/presentation/themes/katon/header-modules-recents.tsx +52 -0
  251. package/src/presentation/themes/katon/header-modules-store.tsx +25 -0
  252. package/src/presentation/themes/katon/header-options.tsx +13 -0
  253. package/src/presentation/themes/katon/header-organization-select.tsx +146 -0
  254. package/src/presentation/themes/katon/header-routines.tsx +74 -0
  255. package/src/presentation/themes/katon/header-tasks-connected-user.tsx +150 -0
  256. package/src/presentation/themes/katon/index.tsx +16 -0
  257. package/src/presentation/themes/katon/main-wrapper.tsx +34 -0
  258. package/src/presentation/themes/katon/module-item-link.tsx +32 -0
  259. package/src/presentation/themes/katon/view-frame.tsx +25 -0
  260. package/src/presentation/themes/katon/view-helmet.tsx +53 -0
  261. package/src/presentation/themes/katon/view-status.tsx +32 -0
  262. package/src/presentation/themes/katon/view-wrapper.tsx +15 -0
  263. package/src/presentation/themes/katon/view.tsx +42 -0
  264. package/src/presentation/ui/accordion-multiselect.tsx +206 -0
  265. package/src/presentation/ui/accordion.tsx +81 -0
  266. package/src/presentation/ui/alert-dialog.tsx +199 -0
  267. package/src/presentation/ui/alert.tsx +76 -0
  268. package/src/presentation/ui/aspect-ratio.tsx +11 -0
  269. package/src/presentation/ui/attachment.tsx +204 -0
  270. package/src/presentation/ui/avatar.tsx +112 -0
  271. package/src/presentation/ui/badge.tsx +49 -0
  272. package/src/presentation/ui/breadcrumb.tsx +122 -0
  273. package/src/presentation/ui/bubble.tsx +125 -0
  274. package/src/presentation/ui/button-group.tsx +83 -0
  275. package/src/presentation/ui/button.tsx +87 -0
  276. package/src/presentation/ui/calendar.tsx +222 -0
  277. package/src/presentation/ui/card.tsx +106 -0
  278. package/src/presentation/ui/carousel.tsx +242 -0
  279. package/src/presentation/ui/chart.tsx +373 -0
  280. package/src/presentation/ui/checkbox.tsx +33 -0
  281. package/src/presentation/ui/clickable.tsx +58 -0
  282. package/src/presentation/ui/collapsible.tsx +33 -0
  283. package/src/presentation/ui/color-swatch-selector.tsx +81 -0
  284. package/src/presentation/ui/combobox.tsx +299 -0
  285. package/src/presentation/ui/command.tsx +195 -0
  286. package/src/presentation/ui/context-menu.tsx +263 -0
  287. package/src/presentation/ui/dialog.tsx +168 -0
  288. package/src/presentation/ui/direction.tsx +22 -0
  289. package/src/presentation/ui/drawer.tsx +134 -0
  290. package/src/presentation/ui/dropdown-menu.tsx +269 -0
  291. package/src/presentation/ui/empty.tsx +104 -0
  292. package/src/presentation/ui/field.tsx +238 -0
  293. package/src/presentation/ui/granularity-selector.tsx +58 -0
  294. package/src/presentation/ui/hover-card.tsx +44 -0
  295. package/src/presentation/ui/index.tsx +74 -0
  296. package/src/presentation/ui/input-group.tsx +156 -0
  297. package/src/presentation/ui/input-otp.tsx +87 -0
  298. package/src/presentation/ui/input.tsx +19 -0
  299. package/src/presentation/ui/item.tsx +196 -0
  300. package/src/presentation/ui/kpi-card.tsx +203 -0
  301. package/src/presentation/ui/label-selector.tsx +100 -0
  302. package/src/presentation/ui/label.tsx +24 -0
  303. package/src/presentation/ui/legacy-birth-date-input.tsx +43 -0
  304. package/src/presentation/ui/legacy-color-picker/index.tsx +42 -0
  305. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.content.tsx +37 -0
  306. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.context.tsx +42 -0
  307. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.hex-input.tsx +97 -0
  308. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.picker.tsx +266 -0
  309. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.presets.ts +22 -0
  310. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.quick-list.tsx +49 -0
  311. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.recent.tsx +43 -0
  312. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.swatches.tsx +111 -0
  313. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.trigger.tsx +128 -0
  314. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.tsx +110 -0
  315. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.types.ts +1 -0
  316. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.utils.ts +114 -0
  317. package/src/presentation/ui/legacy-country-input.tsx +171 -0
  318. package/src/presentation/ui/legacy-gender-input.tsx +22 -0
  319. package/src/presentation/ui/legacy-input.tsx +106 -0
  320. package/src/presentation/ui/legacy-phone-input.tsx +213 -0
  321. package/src/presentation/ui/legacy-select-input.tsx +114 -0
  322. package/src/presentation/ui/legacy-text-area.tsx +111 -0
  323. package/src/presentation/ui/list-items.tsx +59 -0
  324. package/src/presentation/ui/marker.tsx +69 -0
  325. package/src/presentation/ui/media-player/index.tsx +12 -0
  326. package/src/presentation/ui/media-player/media-player.context.tsx +36 -0
  327. package/src/presentation/ui/media-player/media-player.tsx +167 -0
  328. package/src/presentation/ui/media-player/player-controls.tsx +207 -0
  329. package/src/presentation/ui/media-player/players/audio-player.tsx +78 -0
  330. package/src/presentation/ui/media-player/players/index.tsx +4 -0
  331. package/src/presentation/ui/media-player/players/video-player.tsx +193 -0
  332. package/src/presentation/ui/media-player/players/voice-note-player.tsx +108 -0
  333. package/src/presentation/ui/media-player/use-media-player.hook.ts +263 -0
  334. package/src/presentation/ui/menubar.tsx +281 -0
  335. package/src/presentation/ui/message.tsx +92 -0
  336. package/src/presentation/ui/navigation-menu.tsx +172 -0
  337. package/src/presentation/ui/pagination.tsx +129 -0
  338. package/src/presentation/ui/popover.tsx +89 -0
  339. package/src/presentation/ui/progress.tsx +31 -0
  340. package/src/presentation/ui/resizable.tsx +50 -0
  341. package/src/presentation/ui/select.tsx +192 -0
  342. package/src/presentation/ui/separator.tsx +28 -0
  343. package/src/presentation/ui/sheet.tsx +147 -0
  344. package/src/presentation/ui/sidebar.tsx +702 -0
  345. package/src/presentation/ui/skeleton.tsx +13 -0
  346. package/src/presentation/ui/slide-to-confirm.tsx +129 -0
  347. package/src/presentation/ui/slider.tsx +59 -0
  348. package/src/presentation/ui/sonner.tsx +58 -0
  349. package/src/presentation/ui/switch.tsx +33 -0
  350. package/src/presentation/ui/table.tsx +116 -0
  351. package/src/presentation/ui/tabs.tsx +90 -0
  352. package/src/presentation/ui/text-gradient.tsx +84 -0
  353. package/src/presentation/ui/textarea.tsx +18 -0
  354. package/src/presentation/ui/timeline-steps.tsx +235 -0
  355. package/src/presentation/ui/toggle-group.tsx +89 -0
  356. package/src/presentation/ui/toggle.tsx +47 -0
  357. package/src/presentation/ui/tooltip.tsx +57 -0
  358. package/src/presentation/uploading/index.tsx +3 -0
  359. package/src/presentation/uploading/media-upload-field.tsx +152 -0
  360. package/src/presentation/uploading/storage-media.tsx +210 -0
  361. package/src/styles.css +403 -0
  362. package/src/themes.css +1038 -0
@@ -0,0 +1,201 @@
1
+ import {Fragment, ReactNode, createElement} from "react";
2
+ import {cn} from "../../infrastructure/utilities/utils";
3
+
4
+ export interface MarkdownParserProps {
5
+ text: string;
6
+ className?: string;
7
+ }
8
+
9
+ type MarkdownBlock =
10
+ | { type: "paragraph"; lines: string[] }
11
+ | { type: "heading"; level: number; text: string }
12
+ | { type: "list"; items: string[] }
13
+ | { type: "quote"; lines: string[] }
14
+ | { type: "code"; content: string };
15
+
16
+ const INLINE_SOURCE = "(?<bold>\\*\\*[^*]+\\*\\*)|(?<italic>\\*[^*]+\\*)|(?<code>`[^`]+`)|(?<link>\\[(?<linkText>[^\\]]+)\\]\\((?<linkUrl>[^)]+)\\))|(?<strike>~~[^~]+~~)";
17
+
18
+ function parseBlocks(text: string): MarkdownBlock[] {
19
+ const lines = text.split(/\r?\n/);
20
+ const blocks: MarkdownBlock[] = [];
21
+ let index = 0;
22
+
23
+ while (index < lines.length) {
24
+ const line = lines[index];
25
+ const trimmed = line.trim();
26
+
27
+ if (!trimmed) {
28
+ index++;
29
+ continue;
30
+ }
31
+
32
+ if (/^```/.test(trimmed)) {
33
+ const content: string[] = [];
34
+ index++;
35
+ while (index < lines.length && !/^```/.test(lines[index].trim())) {
36
+ content.push(lines[index]);
37
+ index++;
38
+ }
39
+ index++;
40
+ blocks.push({type: "code", content: content.join("\n")});
41
+ continue;
42
+ }
43
+
44
+ const heading = trimmed.match(/^(#{1,6})\s+(.*)$/);
45
+ if (heading) {
46
+ blocks.push({type: "heading", level: heading[1].length, text: heading[2]});
47
+ index++;
48
+ continue;
49
+ }
50
+
51
+ const list = trimmed.match(/^[-*]\s+(.*)$/);
52
+ if (list) {
53
+ const items = [list[1]];
54
+ index++;
55
+ while (index < lines.length && /^[-*]\s+/.test(lines[index].trim())) {
56
+ items.push(lines[index].trim().replace(/^[-*]\s+/, ""));
57
+ index++;
58
+ }
59
+ blocks.push({type: "list", items});
60
+ continue;
61
+ }
62
+
63
+ const quote = trimmed.match(/^>\s?(.*)$/);
64
+ if (quote) {
65
+ const quoteLines = [quote[1]];
66
+ index++;
67
+ while (index < lines.length && /^>\s?/.test(lines[index].trim())) {
68
+ quoteLines.push(lines[index].trim().replace(/^>\s?/, ""));
69
+ index++;
70
+ }
71
+ blocks.push({type: "quote", lines: quoteLines});
72
+ continue;
73
+ }
74
+
75
+ const paragraph: string[] = [line];
76
+ index++;
77
+ while (
78
+ index < lines.length &&
79
+ lines[index].trim() !== "" &&
80
+ !/^#{1,6}\s/.test(lines[index].trim()) &&
81
+ !/^[-*]\s/.test(lines[index].trim()) &&
82
+ !/^>\s?/.test(lines[index].trim()) &&
83
+ !/^```/.test(lines[index].trim())
84
+ ) {
85
+ paragraph.push(lines[index]);
86
+ index++;
87
+ }
88
+ blocks.push({type: "paragraph", lines: paragraph});
89
+ }
90
+
91
+ return blocks;
92
+ }
93
+
94
+ function renderInline(text: string): ReactNode[] {
95
+ const regex = new RegExp(INLINE_SOURCE, "g");
96
+ const nodes: ReactNode[] = [];
97
+ let lastIndex = 0;
98
+ let key = 0;
99
+ let match: RegExpExecArray | null;
100
+
101
+ while ((match = regex.exec(text)) !== null) {
102
+ if (match.index > lastIndex) {
103
+ nodes.push(text.slice(lastIndex, match.index));
104
+ }
105
+
106
+ const groups = match.groups ?? {};
107
+
108
+ if (groups.bold) {
109
+ nodes.push(<strong key={key++}>{renderInline(groups.bold.slice(2, -2))}</strong>);
110
+ } else if (groups.italic) {
111
+ nodes.push(<em key={key++}>{renderInline(groups.italic.slice(1, -1))}</em>);
112
+ } else if (groups.strike) {
113
+ nodes.push(<del key={key++}>{renderInline(groups.strike.slice(2, -2))}</del>);
114
+ } else if (groups.code) {
115
+ nodes.push(
116
+ <code key={key++} className="rounded bg-muted px-1.5 py-0.5 font-mono text-[0.9em]">
117
+ {groups.code.slice(1, -1)}
118
+ </code>
119
+ );
120
+ } else if (groups.link) {
121
+ nodes.push(
122
+ <a
123
+ key={key++}
124
+ href={groups.linkUrl}
125
+ target="_blank"
126
+ rel="noopener noreferrer"
127
+ className="text-primary underline underline-offset-2 hover:text-primary/80"
128
+ >
129
+ {renderInline(groups.linkText)}
130
+ </a>
131
+ );
132
+ }
133
+
134
+ lastIndex = regex.lastIndex;
135
+ }
136
+
137
+ if (lastIndex < text.length) {
138
+ nodes.push(text.slice(lastIndex));
139
+ }
140
+
141
+ return nodes;
142
+ }
143
+
144
+ const HEADING_CLASS: Record<number, string> = {
145
+ 1: "text-2xl font-bold",
146
+ 2: "text-xl font-bold",
147
+ 3: "text-lg font-semibold",
148
+ 4: "text-base font-semibold",
149
+ 5: "text-sm font-medium",
150
+ 6: "text-sm font-medium text-muted-foreground",
151
+ };
152
+
153
+ function renderBlock(block: MarkdownBlock, index: number): ReactNode {
154
+ switch (block.type) {
155
+ case "paragraph":
156
+ return <p key={index} className="m-0">{renderInline(block.lines.join(" "))}</p>;
157
+ case "heading":
158
+ return createElement(
159
+ `h${block.level}`,
160
+ {key: index, className: cn("mt-0 mb-2", HEADING_CLASS[block.level])},
161
+ renderInline(block.text)
162
+ );
163
+ case "list":
164
+ return (
165
+ <ul key={index} className="m-0 list-disc space-y-1 pl-5">
166
+ {block.items.map((item, itemIndex) => (
167
+ <li key={itemIndex}>{renderInline(item)}</li>
168
+ ))}
169
+ </ul>
170
+ );
171
+ case "quote":
172
+ return (
173
+ <blockquote key={index} className="m-0 border-l-2 border-primary pl-3 italic">
174
+ {block.lines.map((line, lineIndex) => (
175
+ <Fragment key={lineIndex}>
176
+ {renderInline(line)}
177
+ {lineIndex < block.lines.length - 1 && <br/>}
178
+ </Fragment>
179
+ ))}
180
+ </blockquote>
181
+ );
182
+ case "code":
183
+ return (
184
+ <pre key={index} className="m-0 overflow-x-auto rounded-md bg-muted p-3">
185
+ <code className="font-mono text-sm">{block.content}</code>
186
+ </pre>
187
+ );
188
+ default:
189
+ return null;
190
+ }
191
+ }
192
+
193
+ export function MarkdownParser({text, className}: MarkdownParserProps) {
194
+ const content = parseBlocks(text).map(renderBlock);
195
+
196
+ if (className) {
197
+ return <div className={cn("text-sm", className)}>{content}</div>;
198
+ }
199
+
200
+ return <Fragment>{content}</Fragment>;
201
+ }
@@ -0,0 +1,131 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ MessageScroller as MessageScrollerPrimitive,
6
+ useMessageScroller,
7
+ useMessageScrollerScrollable,
8
+ useMessageScrollerVisibility,
9
+ } from "@shadcn/react/message-scroller"
10
+
11
+ import { cn } from "../../infrastructure/utilities/utils"
12
+ import { Button } from "../ui/button"
13
+ import { ArrowDownIcon } from "lucide-react"
14
+
15
+ function MessageScrollerProvider(
16
+ props: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>
17
+ ) {
18
+ return <MessageScrollerPrimitive.Provider {...props} />
19
+ }
20
+
21
+ function MessageScroller({
22
+ className,
23
+ ...props
24
+ }: React.ComponentProps<typeof MessageScrollerPrimitive.Root>) {
25
+ return (
26
+ <MessageScrollerPrimitive.Root
27
+ data-slot="message-scroller"
28
+ className={cn(
29
+ "group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden",
30
+ className
31
+ )}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function MessageScrollerViewport({
38
+ className,
39
+ ...props
40
+ }: React.ComponentProps<typeof MessageScrollerPrimitive.Viewport>) {
41
+ return (
42
+ <MessageScrollerPrimitive.Viewport
43
+ data-slot="message-scroller-viewport"
44
+ className={cn(
45
+ "size-full min-h-0 min-w-0 scroll-fade-b scrollbar-thin scrollbar-gutter-stable overflow-y-auto overscroll-contain contain-content data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ function MessageScrollerContent({
54
+ className,
55
+ ...props
56
+ }: React.ComponentProps<typeof MessageScrollerPrimitive.Content>) {
57
+ return (
58
+ <MessageScrollerPrimitive.Content
59
+ data-slot="message-scroller-content"
60
+ className={cn("flex h-max min-h-full flex-col gap-6", className)}
61
+ {...props}
62
+ />
63
+ )
64
+ }
65
+
66
+ function MessageScrollerItem({
67
+ className,
68
+ scrollAnchor = false,
69
+ ...props
70
+ }: React.ComponentProps<typeof MessageScrollerPrimitive.Item>) {
71
+ return (
72
+ <MessageScrollerPrimitive.Item
73
+ data-slot="message-scroller-item"
74
+ scrollAnchor={scrollAnchor}
75
+ className={cn(
76
+ "min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ function MessageScrollerButton({
85
+ direction = "end",
86
+ className,
87
+ children,
88
+ render,
89
+ variant = "secondary",
90
+ size = "icon-sm",
91
+ ...props
92
+ }: React.ComponentProps<typeof MessageScrollerPrimitive.Button> &
93
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
94
+ return (
95
+ <MessageScrollerPrimitive.Button
96
+ data-slot="message-scroller-button"
97
+ data-direction={direction}
98
+ data-variant={variant}
99
+ data-size={size}
100
+ direction={direction}
101
+ className={cn(
102
+ "absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180",
103
+ className
104
+ )}
105
+ render={render ?? <Button variant={variant} size={size} />}
106
+ {...props}
107
+ >
108
+ {children ?? (
109
+ <>
110
+ <ArrowDownIcon
111
+ />
112
+ <span className="sr-only">
113
+ {direction === "end" ? "Scroll to end" : "Scroll to start"}
114
+ </span>
115
+ </>
116
+ )}
117
+ </MessageScrollerPrimitive.Button>
118
+ )
119
+ }
120
+
121
+ export {
122
+ MessageScrollerProvider,
123
+ MessageScroller,
124
+ MessageScrollerViewport,
125
+ MessageScrollerContent,
126
+ MessageScrollerItem,
127
+ MessageScrollerButton,
128
+ useMessageScroller,
129
+ useMessageScrollerScrollable,
130
+ useMessageScrollerVisibility,
131
+ }
@@ -0,0 +1,181 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query"
5
+ import {toast} from "sonner"
6
+ import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from "../ui/card"
7
+ import {Input} from "../ui/input"
8
+ import {Label} from "../ui/label"
9
+ import {Button} from "../ui/button"
10
+ import {Separator} from "../ui/separator"
11
+ import {DynamicIcon} from "./dynamic-icon"
12
+ import {Activity} from "./activity"
13
+ import {ModuleDeclarationInterface} from "../../domain/entities/module.interface"
14
+ import {
15
+ ModuleConfigSettingsFieldInterface,
16
+ ModuleConfigSettingsFieldType
17
+ } from "../../domain/entities/module-activation.interface"
18
+ import {ModuleActivationApiService} from "../../application/service/module-activation-api.service"
19
+ import {OrganizationsApiService} from "../../application/service/organizations-api-service"
20
+ import {useAuth} from "../../infrastructure/hooks/use-auth"
21
+
22
+ export function getModuleConfigSettingsPreferenceLabel(moduleId: string): string {
23
+ return `module_config_${moduleId}`
24
+ }
25
+
26
+ export function formatModuleSerialKey(raw: string): string {
27
+ const cleaned = raw.toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 32)
28
+ return cleaned.replace(/(.{4})/g, "$1-").replace(/-$/, "")
29
+ }
30
+
31
+ const INPUT_TYPES: Record<ModuleConfigSettingsFieldType, string> = {
32
+ TEXT: "text",
33
+ NUMBER: "number",
34
+ EMAIL: "email",
35
+ PASSWORD: "password",
36
+ URL: "url",
37
+ TEL: "tel",
38
+ SELECT: "text",
39
+ TOGGLE: "text",
40
+ }
41
+
42
+ interface ModulesFirstAccessConfigViewProps {
43
+ module: ModuleDeclarationInterface
44
+ organizationId: string
45
+ settingsFields?: ModuleConfigSettingsFieldInterface[]
46
+ onConfigured?: () => void | Promise<void>
47
+ }
48
+
49
+ export function ModulesFirstAccessConfigView({module, organizationId, settingsFields = [], onConfigured}: ModulesFirstAccessConfigViewProps) {
50
+ const {user} = useAuth()
51
+ const queryClient = useQueryClient()
52
+ const moduleId = module.key ?? module.identifier
53
+
54
+ const [serialKey, setSerialKey] = React.useState("")
55
+ const [settings, setSettings] = React.useState<Record<string, string>>({})
56
+
57
+ const isSerialKeyComplete = serialKey.replace(/-/g, "").length === 32
58
+ const isMissingRequiredSetting = settingsFields.some(field => field.required && !(settings[field.key] ?? "").trim())
59
+
60
+ const {data: savedSettings} = useQuery<Record<string, string>>({
61
+ queryKey: ["organizations", organizationId, "preferences", "module-config", moduleId],
62
+ queryFn: async () => {
63
+ const response = await OrganizationsApiService.getPreferenceByOrgId(organizationId)
64
+ const preferences = response.data?.data
65
+ if (!Array.isArray(preferences)) return {}
66
+ const found = preferences.find(preference => preference?.label === getModuleConfigSettingsPreferenceLabel(moduleId))
67
+ try {
68
+ const parsed = found ? JSON.parse(found.value) : {}
69
+ return typeof parsed === "object" && parsed !== null ? parsed : {}
70
+ } catch {
71
+ return {}
72
+ }
73
+ },
74
+ })
75
+
76
+ React.useEffect(() => {
77
+ const defaults: Record<string, string> = {}
78
+ settingsFields.forEach(field => {
79
+ if (field.defaultValue !== undefined) defaults[field.key] = field.defaultValue
80
+ })
81
+ setSettings({...defaults, ...(savedSettings ?? {})})
82
+ }, [savedSettings])
83
+
84
+ const configureMutation = useMutation({
85
+ mutationFn: async () => {
86
+ await ModuleActivationApiService.activateModule(organizationId, {
87
+ moduleIdentifier: module.identifier,
88
+ serialKey,
89
+ auditId: user?.auditId,
90
+ })
91
+ if (settingsFields.length > 0) {
92
+ await OrganizationsApiService.createPreference({
93
+ organization_id: organizationId,
94
+ label: getModuleConfigSettingsPreferenceLabel(moduleId),
95
+ value: JSON.stringify(settings),
96
+ })
97
+ }
98
+ },
99
+ onSuccess: async () => {
100
+ await queryClient.invalidateQueries({queryKey: ["module-activation"]})
101
+ await queryClient.invalidateQueries({queryKey: ["organizations", organizationId, "preferences"]})
102
+ toast.success(`Module « ${module.name} » configuré avec succès`)
103
+ await onConfigured?.()
104
+ },
105
+ })
106
+
107
+ const canSubmit = isSerialKeyComplete && !isMissingRequiredSetting && !configureMutation.isPending
108
+
109
+ return (
110
+ <div className="flex min-h-svh items-center justify-center p-4">
111
+ <Card className="w-full max-w-md gap-4">
112
+ <CardHeader>
113
+ <div className="flex items-center gap-3">
114
+ <div className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
115
+ <DynamicIcon name={module.icon} className="size-5"/>
116
+ </div>
117
+ <div className="min-w-0">
118
+ <CardTitle>Configurer «&nbsp;{module.name}&nbsp;»</CardTitle>
119
+ <CardDescription>
120
+ Ce module doit être activé avant sa première utilisation.
121
+ </CardDescription>
122
+ </div>
123
+ </div>
124
+ </CardHeader>
125
+ <CardContent className="flex flex-col gap-5">
126
+ <div className="flex flex-col gap-1.5">
127
+ <Label htmlFor={`${moduleId}-serial-key`}>Clé d&apos;activation</Label>
128
+ <Input
129
+ id={`${moduleId}-serial-key`}
130
+ placeholder="XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX"
131
+ value={serialKey}
132
+ onChange={(event) => setSerialKey(formatModuleSerialKey(event.target.value))}
133
+ onKeyDown={(event) => event.key === "Enter" && canSubmit && configureMutation.mutate()}
134
+ autoComplete="off"
135
+ spellCheck={false}
136
+ className="font-mono tracking-wide"
137
+ />
138
+ <p className="text-xs text-muted-foreground">Format : 8 blocs de 4 caractères.</p>
139
+ </div>
140
+
141
+ {settingsFields.length > 0 && (
142
+ <>
143
+ <Separator/>
144
+ <div className="flex flex-col gap-3">
145
+ <p className="text-sm font-medium">Paramètres du module</p>
146
+ {settingsFields.map((field) => (
147
+ <div key={field.key} className="flex flex-col gap-1.5">
148
+ <Label htmlFor={`${moduleId}-${field.key}`}>
149
+ {field.label}
150
+ {field.required && <span className="text-destructive">&nbsp;*</span>}
151
+ </Label>
152
+ <Input
153
+ id={`${moduleId}-${field.key}`}
154
+ type={INPUT_TYPES[field.type ?? "TEXT"]}
155
+ placeholder={field.placeholder}
156
+ value={settings[field.key] ?? ""}
157
+ onChange={(event) => setSettings(previous => ({...previous, [field.key]: event.target.value}))}
158
+ autoComplete="off"
159
+ />
160
+ {field.description && (
161
+ <p className="text-xs text-muted-foreground">{field.description}</p>
162
+ )}
163
+ </div>
164
+ ))}
165
+ </div>
166
+ </>
167
+ )}
168
+ </CardContent>
169
+ <CardFooter>
170
+ <Button
171
+ className="w-full"
172
+ disabled={!canSubmit}
173
+ onClick={() => configureMutation.mutate()}
174
+ >
175
+ {configureMutation.isPending ? <Activity.Loader size={16}/> : "Activer le module"}
176
+ </Button>
177
+ </CardFooter>
178
+ </Card>
179
+ </div>
180
+ )
181
+ }
@@ -0,0 +1,38 @@
1
+ "use client"
2
+
3
+ import {useRouter} from "next/navigation"
4
+ import {Button} from "../ui/button"
5
+ import {Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle} from "../ui/empty"
6
+ import {DynamicIcon} from "./dynamic-icon"
7
+ import {ModuleDeclarationInterface} from "../../domain/entities/module.interface"
8
+
9
+ interface ModulesFirstAccessPendingViewProps {
10
+ module: ModuleDeclarationInterface
11
+ }
12
+
13
+ export function ModulesFirstAccessPendingView({module}: ModulesFirstAccessPendingViewProps) {
14
+ const router = useRouter()
15
+
16
+ return (
17
+ <div className="flex min-h-svh items-center justify-center p-4">
18
+ <Empty className="max-w-md">
19
+ <EmptyMedia variant="icon">
20
+ <DynamicIcon name="SettingsIcon" className="size-5"/>
21
+ </EmptyMedia>
22
+ <EmptyHeader>
23
+ <EmptyTitle>Configuration requise</EmptyTitle>
24
+ </EmptyHeader>
25
+ <EmptyDescription>
26
+ Le module «&nbsp;{module.name}&nbsp;» n&apos;est pas encore configuré pour votre organisation.
27
+ Renseignez la clé d&apos;activation et les paramètres initiaux pour y accéder.
28
+ </EmptyDescription>
29
+ <EmptyDescription className="text-xs text-muted-foreground">
30
+ Cette configuration est réservée aux administrateurs de l&apos;organisation.
31
+ </EmptyDescription>
32
+ <Button size="lg" onClick={() => router.push('/dashboard')}>
33
+ Retour au tableau de bord
34
+ </Button>
35
+ </Empty>
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,34 @@
1
+ "use client"
2
+
3
+ import { usePathname, useRouter } from "next/navigation"
4
+ import { useModuleStore } from "../../infrastructure/stores/module.store"
5
+ import * as React from "react"
6
+
7
+ export function ModuleGuard({ children }: { children: React.ReactNode }) {
8
+ const pathname = usePathname()
9
+ const { modules } = useModuleStore()
10
+ const router = useRouter()
11
+ const [mounted, setMounted] = React.useState(false)
12
+
13
+ React.useEffect(() => {
14
+ setMounted(true)
15
+ }, [])
16
+
17
+ React.useEffect(() => {
18
+ if (!mounted) return
19
+
20
+ // On ignore la page dashboard et la gestion des modules eux-mêmes
21
+ if (pathname === '/dashboard' || pathname === '/store' || pathname.startsWith('/store/') || pathname === '/') return
22
+
23
+ // Trouver si le chemin actuel appartient à un module
24
+ // On trie par longueur d'URL décroissante pour matcher le plus spécifique d'abord
25
+ const sortedModules = [...modules].sort((a, b) => b.uri.length - a.uri.length)
26
+ const currentModule = sortedModules.find(m => pathname.startsWith(m.uri))
27
+
28
+ if (currentModule && !currentModule.isEnabled) {
29
+ router.replace('/dashboard')
30
+ }
31
+ }, [pathname, modules, router, mounted])
32
+
33
+ return <>{children}</>
34
+ }
@@ -0,0 +1,88 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {usePathname} from "next/navigation"
5
+ import {useQuery} from "@tanstack/react-query"
6
+ import {useModuleStore} from "../../infrastructure/stores/module.store"
7
+ import {useAuth} from "../../infrastructure/hooks/use-auth"
8
+ import {isAdminOrAbove} from "../../infrastructure/utilities/permission.util"
9
+ import {ModuleDeclarationInterface} from "../../domain/entities/module.interface"
10
+ import {ModuleConfigSettingsFieldInterface} from "../../domain/entities/module-activation.interface"
11
+ import {ModuleActivationApiService} from "../../application/service/module-activation-api.service"
12
+ import {Activity} from "./activity"
13
+ import {ModulesFirstAccessConfigView} from "./module-first-access-config.view"
14
+ import {ModulesFirstAccessPendingView} from "./module-first-access-pending.view"
15
+
16
+ const EXCLUDED_PATHS = ["/", "/dashboard", "/store"]
17
+
18
+ interface ModulesFirstAccessGuardProps {
19
+ children: React.ReactNode
20
+ /** Identifiants (id ou clé) des modules soumis à la configuration initiale. Par défaut : tous les modules. */
21
+ moduleIds?: string[]
22
+ /** Paramètres additionnels à définir lors de la première configuration */
23
+ settingsFields?: ModuleConfigSettingsFieldInterface[]
24
+ /** Appelé lorsqu'un administrateur termine la configuration d'un module */
25
+ onConfigured?: (module: ModuleDeclarationInterface) => void | Promise<void>
26
+ }
27
+
28
+ export function ModulesFirstAccessGuard({children, moduleIds, settingsFields, onConfigured}: ModulesFirstAccessGuardProps) {
29
+ const pathname = usePathname()
30
+ const {modules} = useModuleStore()
31
+ const {user, currentOrganization} = useAuth()
32
+ const [mounted, setMounted] = React.useState(false)
33
+
34
+ React.useEffect(() => setMounted(true), [])
35
+
36
+ const currentModule = React.useMemo<ModuleDeclarationInterface | null>(() => {
37
+ if (!mounted) return null
38
+ if (EXCLUDED_PATHS.includes(pathname) || pathname.startsWith("/store/") || pathname.startsWith("/auth")) return null
39
+ return [...modules]
40
+ .sort((a, b) => b.uri.length - a.uri.length)
41
+ .find(module => pathname.startsWith(module.uri)) ?? null
42
+ }, [mounted, pathname, modules])
43
+
44
+ const organizationId = currentOrganization?.id
45
+ const moduleId = currentModule ? currentModule.key ?? currentModule.identifier : null
46
+ const isTargeted = !!currentModule && (
47
+ !moduleIds || moduleIds.length === 0 ||
48
+ moduleIds.includes(currentModule.identifier) || moduleIds.includes(currentModule.key ?? "")
49
+ )
50
+ const isAdmin = isAdminOrAbove(user, organizationId)
51
+
52
+ const validationQuery = useQuery<boolean>({
53
+ queryKey: ["module-activation", "validate", organizationId, moduleId],
54
+ enabled: mounted && isTargeted && !!organizationId && !!moduleId,
55
+ staleTime: 30_000,
56
+ retry: false,
57
+ queryFn: async () => {
58
+ const identifier = currentModule?.identifier ?? moduleId
59
+ const response = await ModuleActivationApiService.validateActivation(organizationId!, identifier!)
60
+ return response.data?.data?.isValid ?? false
61
+ },
62
+ })
63
+
64
+ if (!mounted) return <>{children}</>
65
+ if (!isTargeted || !currentModule || !organizationId || !moduleId) return <>{children}</>
66
+
67
+ if (validationQuery.isPending) {
68
+ return (
69
+ <div className="flex min-h-svh flex-col items-center justify-center gap-4 p-4">
70
+ <Activity.Loader size={40}/>
71
+ <p className="text-sm text-muted-foreground">Vérification de l&apos;activation du module…</p>
72
+ </div>
73
+ )
74
+ }
75
+
76
+ if (validationQuery.isError || validationQuery.data) return <>{children}</>
77
+
78
+ return isAdmin ? (
79
+ <ModulesFirstAccessConfigView
80
+ module={currentModule}
81
+ organizationId={organizationId}
82
+ settingsFields={settingsFields}
83
+ onConfigured={() => onConfigured?.(currentModule)}
84
+ />
85
+ ) : (
86
+ <ModulesFirstAccessPendingView module={currentModule}/>
87
+ )
88
+ }