@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,158 @@
1
+ import {DomainsEnum} from "../../domain/enums/domains.enum";
2
+
3
+ /** Libellés français des rôles par défaut (clé API → libellé). */
4
+ const ROLE_LABELS: Record<string, string> = {
5
+ Root: "Système",
6
+ SuperAdmin: "Super Administrateur",
7
+ Admin: "Administrateur",
8
+ Manager: "Gestionnaire",
9
+ Editor: "Éditeur",
10
+ Moderator: "Modérateur",
11
+ Analyst: "Analyste",
12
+ Commercial: "Commercial",
13
+ Operator: "Opérateur",
14
+ Contributor: "Contributeur",
15
+ Caissier: "Caissier",
16
+ User: "Utilisateur",
17
+ Guest: "Invité",
18
+ Viewer: "Lecteur",
19
+ };
20
+
21
+ /** Libellés français des domaines de permission (clé API → libellé). */
22
+ const DOMAIN_LABELS: Record<string, string> = {
23
+ // Identity
24
+ [DomainsEnum.User]: "Utilisateur",
25
+ UserDevice: "Appareil utilisateur",
26
+ UserDocument: "Document utilisateur",
27
+ OrganizationDocumentType: "Type de document d'organisation",
28
+
29
+ // Organization
30
+ [DomainsEnum.Organization]: "Organisation",
31
+ [DomainsEnum.OrganizationMember]: "Membre d'organisation",
32
+ [DomainsEnum.ApiAccessKey]: "Clé API",
33
+
34
+ // Access Control
35
+ [DomainsEnum.Role]: "Rôle",
36
+
37
+ // Blogging
38
+ Post: "Publication",
39
+ PostCategory: "Catégorie de publication",
40
+ PostCollaborator: "Collaborateur de publication",
41
+
42
+ // CRM
43
+ Contact: "Contact",
44
+ Company: "Société",
45
+ Lead: "Prospect",
46
+ Deal: "Affaire",
47
+ Activity: "Activité",
48
+ SalesTeam: "Groupe de commerciaux",
49
+
50
+ // Projects
51
+ Project: "Projet",
52
+ Task: "Tâche",
53
+ TaskReminder: "Rappel de tâche",
54
+
55
+ // Messaging / Media
56
+ Media: "Médias",
57
+ Notification: "Notifications",
58
+ OTP: "Code OTP",
59
+ UserPreference: "Préférence utilisateur",
60
+ OrganizationPreference: "Préférence d'organisation",
61
+ UserActivity: "Activité utilisateur",
62
+
63
+ // Billing
64
+ PaymentMethod: "Méthode de paiement",
65
+ Order: "Commande",
66
+ Invoice: "Facture",
67
+
68
+ // Stock
69
+ [DomainsEnum.Stock]: "Stock",
70
+ [DomainsEnum.Product]: "Produit",
71
+ ProductCategory: "Catégorie de produit",
72
+ StockMovement: "Mouvement de stock",
73
+ Warehouse: "Entrepôt",
74
+
75
+ // Restaurant
76
+ RestaurantDish: "Plat restaurant",
77
+ RestaurantMenu: "Menu restaurant",
78
+
79
+ // Analytics
80
+ [DomainsEnum.Analytics]: "Analytiques",
81
+
82
+ // Accounting
83
+ [DomainsEnum.Accounting]: "Comptabilité",
84
+ AccountingSetting: "Paramètres comptables",
85
+ AccountingAccount: "Compte comptable",
86
+ AccountingJournal: "Journal comptable",
87
+ AccountingJournalEntry: "Écriture comptable",
88
+ AccountingLedger: "Grand livre",
89
+ AccountingTrialBalance: "Balance de vérification",
90
+ AccountingBankAccount: "Compte bancaire",
91
+ AccountingTax: "Taxe",
92
+ AccountingBudget: "Budget",
93
+ AccountingClosePeriod: "Clôture de période",
94
+ AccountingReport: "Rapport comptable",
95
+
96
+ // Customer
97
+ [DomainsEnum.Customer]: "Client",
98
+ CustomerModule: "Module client",
99
+
100
+ // POS
101
+ Pos: "Point de vente",
102
+ PosTable: "Table POS",
103
+ PosOrder: "Commande POS",
104
+ PosOrderItem: "Article de commande POS",
105
+ PosBundle: "Pack POS",
106
+ PosBundleItem: "Article de pack POS",
107
+
108
+ // E-commerce
109
+ Ecommerce: "E-commerce",
110
+ EcommerceProductAttribute: "Attribut produit e-commerce",
111
+ EcommerceProductVariant: "Variante produit e-commerce",
112
+ EcommerceProductImage: "Image produit e-commerce",
113
+ EcommerceProductReview: "Avis produit e-commerce",
114
+ EcommerceProductTag: "Étiquette produit e-commerce",
115
+ EcommerceCart: "Panier e-commerce",
116
+ EcommerceCoupon: "Coupon e-commerce",
117
+ EcommerceCarrier: "Transporteur e-commerce",
118
+ EcommerceShippingZone: "Zone de livraison e-commerce",
119
+ EcommerceShippingRate: "Tarif de livraison e-commerce",
120
+ EcommerceShipment: "Expédition e-commerce",
121
+ EcommerceOrder: "Commande e-commerce",
122
+ EcommerceWishlist: "Liste de souhaits e-commerce",
123
+ EcommerceStoreSettings: "Paramètres boutique e-commerce",
124
+
125
+ // Messenger
126
+ MessengerWorkspace: "Espace de messagerie",
127
+ MessengerChannel: "Canal de messagerie",
128
+ MessengerMessage: "Message",
129
+ };
130
+
131
+ /** Transforme une clé en lecture humaine (CamelCase → mots séparés). */
132
+ function humanize(key: string): string {
133
+ const spaced = key
134
+ .replace(/^[a-z]/u, char => char.toUpperCase())
135
+ .replace(/([a-z0-9])([A-Z])/gu, "$1 $2")
136
+ .replace(/([A-Z])([A-Z][a-z])/gu, "$1 $2")
137
+ .trim();
138
+
139
+ return spaced || key;
140
+ }
141
+
142
+ /**
143
+ * Libellé français d'un rôle par défaut (Root, SuperAdmin, Admin…).
144
+ * Retourne un libellé lisible si la clé n'est pas dans le dictionnaire.
145
+ */
146
+ export function getRoleLabel(name?: string | null): string {
147
+ if (!name) return "";
148
+ return ROLE_LABELS[name] ?? humanize(name);
149
+ }
150
+
151
+ /**
152
+ * Libellé français d'un domaine de permission (Organization, User…).
153
+ * Retourne un libellé lisible si la clé n'est pas dans le dictionnaire.
154
+ */
155
+ export function getDomainLabel(domain?: string | null): string {
156
+ if (!domain) return "";
157
+ return DOMAIN_LABELS[domain] ?? humanize(domain);
158
+ }
@@ -0,0 +1,54 @@
1
+ import {ActivityInterface} from "../../domain/entities/activities.interface";
2
+ import {useModuleStore} from "../stores/module.store";
3
+ import {retrieveModule} from "./modules";
4
+ import {Fragment} from "react";
5
+ import {capitalizeFirstLetter} from "./strings.util";
6
+ import {ActivityDescriptorParser} from "../../presentation/components/activity-descriptor-parser";
7
+
8
+
9
+ export function explainActivityAction(method: string): string {
10
+ switch (method.toUpperCase()) {
11
+ default:
12
+ return method;
13
+ case 'GET':
14
+ return 'Lecture';
15
+ case 'POST':
16
+ return 'Création';
17
+ case 'PUT':
18
+ return 'Modification';
19
+ case 'DELETE':
20
+ return 'Suppression';
21
+ }
22
+ }
23
+
24
+ export function explainActivityActionVerb(action?: string) {
25
+ switch (action?.toLowerCase()) {
26
+ case 'get':
27
+ return '*a lu*';
28
+ case 'post':
29
+ return '*a créé*';
30
+ case 'patch':
31
+ case 'put':
32
+ return '*a modifié*';
33
+ case 'delete':
34
+ return '*a supprimé*';
35
+ default:
36
+ return '*a effectué*';
37
+ }
38
+ }
39
+
40
+ export function ActivityDescriptor(activity: ActivityInterface) {
41
+ const module = retrieveModule(activity.module);
42
+ const defaultTemplate = `${
43
+ capitalizeFirstLetter(activity.user?.username)
44
+ } ${
45
+ explainActivityActionVerb(activity.action)
46
+ } ${module ? `dans le module ${module.name}` : 'dans un module'}`
47
+
48
+ return (
49
+ <Fragment>
50
+ <ActivityDescriptorParser template={activity.describe ?? defaultTemplate} context={activity}/>
51
+ </Fragment>
52
+ );
53
+ }
54
+
@@ -0,0 +1,263 @@
1
+ "use client"
2
+
3
+ import {AppConfig} from "../../domain/config/app.config";
4
+ import {UserPreferencesApiService} from "../../application/service/user-preferences-api.service";
5
+ import {ActivityTrackerService} from "./activity-tracker.service";
6
+ import {ACTIVITY_LOCK_EVENT, ACTIVITY_UNLOCK_EVENT, FormSnapshotService} from "./form-snapshot.service";
7
+ import {activityLockStore} from "../stores/activity-lock.store";
8
+
9
+ /** Libellé de préférence utilisateur (persistée) contrôlant le délai d'inactivité. */
10
+ export const ACTIVITY_LOCK_PREFERENCE_LABEL = "ui_activity_tracker_timeout";
11
+
12
+ /** Clé localStorage du verrou : persiste l'état verrouillé même après un rafraîchissement. */
13
+ const ACTIVITY_LOCK_STORAGE_KEY = "@liorian-activity-lock";
14
+
15
+ /** Délai d'inactivité par défaut (repli) : 3 minutes. */
16
+ const DEFAULT_TIMEOUT = 3 * 60 * 1000;
17
+
18
+ export interface ActivityLockState {
19
+ locked: boolean;
20
+ userId?: string;
21
+ lockedAt?: number;
22
+ }
23
+
24
+ export interface ActivityLockOptions {
25
+ /** Identifiant de l'utilisateur connecté. */
26
+ userId?: string;
27
+ /** Délai d'inactivité (ms) avant verrouillage. */
28
+ timeout?: number;
29
+ /** Callback déclenché lorsque l'écran est verrouillé par inactivité. */
30
+ onLocked?: () => void;
31
+ }
32
+
33
+ /**
34
+ * Verrouillage automatique de l'écran après une période d'inactivité.
35
+ *
36
+ * - S'appuie sur `ActivityTrackerService` pour détecter l'activité de l'utilisateur
37
+ * (partagé avec les notifications push).
38
+ * - Le délai est résolu depuis la préférence utilisateur persistée
39
+ * (`ACTIVITY_LOCK_PREFERENCE_LABEL`), sinon depuis `AppConfig.UI_ACTIVITY_TRACKER_TIMEOUT`.
40
+ * - L'état verrouillé est persisté dans le `localStorage` : il survit au rafraîchissement
41
+ * de la page (l'écran reste verrouillé tant que l'utilisateur ne se déverrouille pas).
42
+ * - Le verrouillage est déclenché précisément dès que le délai d'inactivité est atteint
43
+ * (minuterie ré-armée à chaque activité, en plus de la surveillance périodique).
44
+ * - À chaque verrouillage, les formulaires en cours de saisie sont sauvegardés via
45
+ * `FormSnapshotService` puis restaurés au déverrouillage de l'interface.
46
+ */
47
+ export class ActivityLockService {
48
+
49
+ protected static enabled = false;
50
+ protected static userId?: string;
51
+ protected static timeout = 0;
52
+ protected static onLocked?: () => void;
53
+ protected static checkInterval: ReturnType<typeof setInterval> | undefined;
54
+ protected static lockTimer: ReturnType<typeof setTimeout> | undefined;
55
+ protected static unwatch: (() => void) | undefined;
56
+ protected static unwatchActivity: (() => void) | undefined;
57
+ protected static stopTracking: (() => void) | undefined;
58
+ protected static handleVisibility: (() => void) | undefined;
59
+ protected static handleFocus: (() => void) | undefined;
60
+
61
+ static get isEnabled(): boolean {
62
+ return this.enabled;
63
+ }
64
+
65
+ static getPreferenceLabel(): string {
66
+ return ACTIVITY_LOCK_PREFERENCE_LABEL;
67
+ }
68
+
69
+ /** Lit l'état verrouillé persisté (survit au rafraîchissement de la page). */
70
+ static isLocked(userId?: string): boolean {
71
+ if (typeof window === "undefined") return false;
72
+ try {
73
+ const raw = window.localStorage.getItem(ACTIVITY_LOCK_STORAGE_KEY);
74
+ if (!raw) return false;
75
+ const state = JSON.parse(raw) as ActivityLockState;
76
+ if (!state?.locked) return false;
77
+ if (userId && state.userId && state.userId !== userId) return false;
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+
84
+ /** Persiste l'état verrouillé et stoppe le suivi. */
85
+ static lock(userId?: string): void {
86
+ if (typeof window === "undefined") return;
87
+ this.stop();
88
+ try {
89
+ const {isLocked, setLocked} = activityLockStore.getState()
90
+ const state: ActivityLockState = {locked: true, userId, lockedAt: Date.now()};
91
+ window.localStorage.setItem(ACTIVITY_LOCK_STORAGE_KEY, JSON.stringify(state));
92
+ setLocked(true);
93
+ } catch {
94
+ // Stockage indisponible : on ignore, le verrou sera géré en mémoire.
95
+ }
96
+ // Émis avant que l'écran de verrouillage remplace le contenu : permet à
97
+ // `FormSnapshotService` de capturer l'état des formulaires encore montés.
98
+ window.dispatchEvent(new CustomEvent(ACTIVITY_LOCK_EVENT));
99
+ }
100
+
101
+ /** Efface l'état verrouillé. */
102
+ static unlock(): void {
103
+ if (typeof window === "undefined") return;
104
+ try {
105
+ const {isLocked, setLocked} = activityLockStore.getState()
106
+ window.localStorage.removeItem(ACTIVITY_LOCK_STORAGE_KEY);
107
+ setLocked(false);
108
+ } catch {
109
+ // Stockage indisponible : on ignore.
110
+ }
111
+ // Émis avant le remontage des pages : `FormSnapshotService` restaure le brouillon.
112
+ window.dispatchEvent(new CustomEvent(ACTIVITY_UNLOCK_EVENT));
113
+ }
114
+
115
+ /**
116
+ * Résout le délai d'inactivité : préférence utilisateur persistée (si définie),
117
+ * sinon `AppConfig.UI_ACTIVITY_TRACKER_TIMEOUT`, sinon valeur par défaut.
118
+ */
119
+ static async getTimeout(userId?: string): Promise<number> {
120
+ if (userId) {
121
+ const raw = await UserPreferencesApiService
122
+ .getValue(userId, ACTIVITY_LOCK_PREFERENCE_LABEL)
123
+ .catch(() => undefined);
124
+ const parsed = raw ? Number(raw) : NaN;
125
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
126
+ }
127
+ const config = Number(AppConfig.UI_ACTIVITY_TRACKER_TIMEOUT);
128
+ return Number.isFinite(config) && config > 0 ? config : DEFAULT_TIMEOUT;
129
+ }
130
+
131
+ /** Persiste le délai d'inactivité choisi par l'utilisateur (via UserPreference). */
132
+ static async setTimeoutPreference(userId: string, timeout: number): Promise<void> {
133
+ await UserPreferencesApiService.setValue(
134
+ userId,
135
+ ACTIVITY_LOCK_PREFERENCE_LABEL,
136
+ String(Math.round(timeout))
137
+ );
138
+ }
139
+
140
+ /** Met à jour le délai courant sans redémarrer la surveillance (prise d'effet immédiate). */
141
+ static updateTimeout(timeout: number): void {
142
+ this.timeout = timeout;
143
+ this.scheduleLockTimer();
144
+ }
145
+
146
+ /** Démarre la surveillance d'inactivité. */
147
+ static start(options: ActivityLockOptions = {}): void {
148
+ if (this.enabled) this.stop();
149
+
150
+ this.enabled = true;
151
+ this.userId = options.userId;
152
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
153
+ this.onLocked = options.onLocked;
154
+
155
+ // S'assure que le tracker d'activité tourne (partagé avec les notifications push) :
156
+ // une souscription est comptabilisée et libérée dans `stop()`.
157
+ this.stopTracking = ActivityTrackerService.start();
158
+
159
+ this.unwatch = ActivityTrackerService.watch((state) => {
160
+ if (this.enabled && !state.active) this.check();
161
+ });
162
+
163
+ // Ré-arme la minuterie précise à chaque activité réelle : l'écran se verrouille
164
+ // dès que le délai d'inactivité est atteint, sans attendre le passage périodique.
165
+ this.unwatchActivity = ActivityTrackerService.watchActivity(() => this.scheduleLockTimer());
166
+
167
+ // Les minuteries sont bridées en arrière-plan : le verrou doit s'afficher dès
168
+ // le retour de l'utilisateur dans l'onglet/fenêtre si le délai d'inactivité a
169
+ // été dépassé pendant la période hors onglet (le focus ne rafraîchit plus
170
+ // `lastActivityAt`, la vérification peut donc se baser sur l'inactivité réelle).
171
+ if (typeof document !== "undefined") {
172
+ this.handleVisibility = () => {
173
+ if (document.visibilityState === "visible") this.check();
174
+ };
175
+ document.addEventListener("visibilitychange", this.handleVisibility);
176
+ }
177
+ if (typeof window !== "undefined") {
178
+ this.handleFocus = () => this.check();
179
+ window.addEventListener("focus", this.handleFocus);
180
+ }
181
+
182
+ // Sauvegarde/restauration automatique des formulaires en cours de saisie.
183
+ FormSnapshotService.bind();
184
+
185
+ // Vérifie immédiatement (déjà inactif) puis périodiquement.
186
+ this.check();
187
+ this.checkInterval = setInterval(() => this.check(), 15000);
188
+ }
189
+
190
+ static stop(): void {
191
+ this.enabled = false;
192
+ if (this.checkInterval) {
193
+ clearInterval(this.checkInterval);
194
+ this.checkInterval = undefined;
195
+ }
196
+ if (this.lockTimer) {
197
+ clearTimeout(this.lockTimer);
198
+ this.lockTimer = undefined;
199
+ }
200
+ if (this.unwatch) {
201
+ this.unwatch();
202
+ this.unwatch = undefined;
203
+ }
204
+ if (this.unwatchActivity) {
205
+ this.unwatchActivity();
206
+ this.unwatchActivity = undefined;
207
+ }
208
+ if (this.stopTracking) {
209
+ this.stopTracking();
210
+ this.stopTracking = undefined;
211
+ }
212
+ if (typeof document !== "undefined") {
213
+ if (this.handleVisibility) {
214
+ document.removeEventListener("visibilitychange", this.handleVisibility);
215
+ this.handleVisibility = undefined;
216
+ }
217
+ }
218
+ if (typeof window !== "undefined") {
219
+ if (this.handleFocus) {
220
+ window.removeEventListener("focus", this.handleFocus);
221
+ this.handleFocus = undefined;
222
+ }
223
+ }
224
+ this.onLocked = undefined;
225
+ }
226
+
227
+ /**
228
+ * Arme une minuterie qui déclenche le verrouillage exactement `timeout` ms après
229
+ * la dernière activité. Elle est ré-armée à chaque activité et recalculée à chaque
230
+ * passage de `check()` (tolère une activité survenue entre deux événements).
231
+ */
232
+ protected static scheduleLockTimer(): void {
233
+ if (!this.enabled) return;
234
+ if (this.isLocked(this.userId)) return;
235
+ if (!this.timeout || this.timeout <= 0) return;
236
+ if (this.lockTimer) {
237
+ clearTimeout(this.lockTimer);
238
+ this.lockTimer = undefined;
239
+ }
240
+
241
+ const elapsed = Date.now() - ActivityTrackerService.lastActivityAt;
242
+ const remaining = Math.max(0, this.timeout - elapsed);
243
+ this.lockTimer = setTimeout(() => this.check(), remaining);
244
+ }
245
+
246
+ protected static check(): void {
247
+ if (!this.enabled) return;
248
+ if (this.isLocked(this.userId)) return;
249
+ if (!this.timeout || this.timeout <= 0) return;
250
+
251
+ const elapsed = Date.now() - ActivityTrackerService.lastActivityAt;
252
+ if (elapsed >= this.timeout) {
253
+ // `lock()` stoppe le service et efface `onLocked` : on capture le callback
254
+ // AVANT de verrouiller, sinon la notification de verrouillage n'est jamais
255
+ // émise et le déverrouillage ne ré-armerait pas la surveillance.
256
+ const notifyLocked = this.onLocked;
257
+ this.lock(this.userId);
258
+ notifyLocked?.();
259
+ return;
260
+ }
261
+ this.scheduleLockTimer();
262
+ }
263
+ }
@@ -0,0 +1,205 @@
1
+ "use client"
2
+
3
+ export interface ActivityState {
4
+ active: boolean;
5
+ lastActivityAt: number;
6
+ }
7
+
8
+ export interface ActivityTrackerOptions {
9
+ inactivityTimeout?: number;
10
+ onStateChange?: (state: ActivityState) => void;
11
+ }
12
+
13
+ export class ActivityTrackerService {
14
+
15
+ protected static inactivityTimeout = 60000;
16
+ protected static trackingStarted = false;
17
+ protected static active = false;
18
+ protected static lastActivityTimestamp = 0;
19
+ protected static onStateChange: ((state: ActivityState) => void) | undefined;
20
+ protected static watchers: Array<(state: ActivityState) => void> = [];
21
+ protected static activityWatchers: Array<(timestamp: number) => void> = [];
22
+
23
+ protected static activityHandler = (): void => undefined;
24
+ protected static refocusHandler = (): void => undefined;
25
+ protected static blurHandler = (): void => undefined;
26
+ protected static stateSyncInterval: ReturnType<typeof setInterval> | undefined;
27
+
28
+ /** Nombre de consommateurs actifs (verrouillage, notifications push…). */
29
+ protected static consumers = 0;
30
+
31
+ static get isSupported(): boolean {
32
+ return typeof window !== "undefined";
33
+ }
34
+
35
+ static get isStarted(): boolean {
36
+ return this.trackingStarted;
37
+ }
38
+
39
+ static get isActive(): boolean {
40
+ return this.active;
41
+ }
42
+
43
+ static get isInactive(): boolean {
44
+ return !this.active;
45
+ }
46
+
47
+ static get lastActivityAt(): number {
48
+ return this.lastActivityTimestamp;
49
+ }
50
+
51
+ static get state(): ActivityState {
52
+ return {active: this.active, lastActivityAt: this.lastActivityTimestamp};
53
+ }
54
+
55
+ /**
56
+ * Abonne un observateur aux changements d'état d'activité.
57
+ * Permet à plusieurs features (verrouillage automatique, notifications push…)
58
+ * de réagir au même tracker sans démarrer d'instance supplémentaire.
59
+ * Renvoie une fonction pour désabonner l'observateur.
60
+ */
61
+ static watch(callback: (state: ActivityState) => void): () => void {
62
+ this.watchers.push(callback);
63
+ return () => {
64
+ this.watchers = this.watchers.filter((watcher) => watcher !== callback);
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Abonne un observateur à chaque évènement d'activité réelle (mouvement, clic,
70
+ * frappe…). Contrairement à `watch()`, le callback est invoqué à chaque activité
71
+ * et reçoit l'horodatage de cette dernière - utile pour (ré)armer précisément un
72
+ * délai basé sur la dernière activité (ex. verrouillage automatique).
73
+ * Renvoie une fonction pour désabonner l'observateur.
74
+ */
75
+ static watchActivity(callback: (timestamp: number) => void): () => void {
76
+ this.activityWatchers.push(callback);
77
+ return () => {
78
+ this.activityWatchers = this.activityWatchers.filter((watcher) => watcher !== callback);
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Abonne un consommateur au suivi d'activité. Le premier appel démarre le
84
+ * suivi (listeners + intervalle) ; les appels suivants réutilisent l'exécution
85
+ * en cours et re-lient éventuellement `onStateChange`.
86
+ *
87
+ * Renvoie une fonction de libération : le suivi n'est arrêté que lorsque le
88
+ * dernier consommateur l'a libérée. Contrairement à `stop()` (teardown global),
89
+ * libérer une souscription ne détruit ni les watchers ni l'horloge des autres
90
+ * consommateurs (ex. verrouillage automatique partageant le tracker avec le push).
91
+ */
92
+ static start(options?: ActivityTrackerOptions): () => void {
93
+ if (!this.isSupported) return () => undefined;
94
+
95
+ this.consumers += 1;
96
+
97
+ if (options?.onStateChange) {
98
+ this.onStateChange = options.onStateChange;
99
+ }
100
+
101
+ // Chaque nouvelle souscription relance l'horloge d'activité : après un
102
+ // déverrouillage, l'inactivité recommence à zéro (pas de re-verrouillage
103
+ // immédiat même si le tracker était encore actif).
104
+ this.lastActivityTimestamp = Date.now();
105
+
106
+ if (!this.trackingStarted) {
107
+ this.trackingStarted = true;
108
+ this.active = true;
109
+ this.inactivityTimeout = options?.inactivityTimeout ?? 60000;
110
+
111
+ this.activityHandler = () => {
112
+ this.lastActivityTimestamp = Date.now();
113
+ if (!this.active) {
114
+ this.active = true;
115
+ this.emitStateChange();
116
+ }
117
+ const timestamp = this.lastActivityTimestamp;
118
+ for (const watcher of this.activityWatchers) {
119
+ watcher(timestamp);
120
+ }
121
+ };
122
+
123
+ /**
124
+ * Le simple retour de focus ne remet PAS l'horloge d'inactivité à zéro :
125
+ * la dernière activité réelle reste inchangée pour que les consommateurs
126
+ * (ex. verrouillage automatique) constatent que le temps passé hors de
127
+ * l'onglet/fenêtre compte comme de l'inactivité. L'état redevient actif,
128
+ * mais le délai est recalculé à partir de la dernière activité réelle.
129
+ */
130
+ this.refocusHandler = () => {
131
+ if (!this.active) {
132
+ this.active = true;
133
+ this.emitStateChange();
134
+ }
135
+ const timestamp = this.lastActivityTimestamp;
136
+ for (const watcher of this.activityWatchers) {
137
+ watcher(timestamp);
138
+ }
139
+ };
140
+
141
+ this.blurHandler = () => {
142
+ if (this.active) {
143
+ this.active = false;
144
+ this.emitStateChange();
145
+ }
146
+ };
147
+
148
+ window.addEventListener("mousemove", this.activityHandler, {passive: true});
149
+ window.addEventListener("mousedown", this.activityHandler, {passive: true});
150
+ window.addEventListener("keydown", this.activityHandler, {passive: true});
151
+ window.addEventListener("scroll", this.activityHandler, {passive: true});
152
+ window.addEventListener("touchstart", this.activityHandler, {passive: true});
153
+ window.addEventListener("focus", this.refocusHandler);
154
+ window.addEventListener("blur", this.blurHandler);
155
+
156
+ this.stateSyncInterval = setInterval(() => {
157
+ const isInactive = Date.now() - this.lastActivityTimestamp >= this.inactivityTimeout;
158
+ if (isInactive && this.active) {
159
+ this.active = false;
160
+ this.emitStateChange();
161
+ }
162
+ }, 15000);
163
+ }
164
+
165
+ let released = false;
166
+ return () => {
167
+ if (released) return;
168
+ released = true;
169
+ this.consumers = Math.max(0, this.consumers - 1);
170
+ if (this.consumers === 0) {
171
+ this.stop();
172
+ }
173
+ };
174
+ }
175
+
176
+ static stop(): void {
177
+ if (!this.trackingStarted) return;
178
+
179
+ this.trackingStarted = false;
180
+ if (this.stateSyncInterval) clearInterval(this.stateSyncInterval);
181
+ this.stateSyncInterval = undefined;
182
+
183
+ window.removeEventListener("mousemove", this.activityHandler);
184
+ window.removeEventListener("mousedown", this.activityHandler);
185
+ window.removeEventListener("keydown", this.activityHandler);
186
+ window.removeEventListener("scroll", this.activityHandler);
187
+ window.removeEventListener("touchstart", this.activityHandler);
188
+ window.removeEventListener("focus", this.refocusHandler);
189
+ window.removeEventListener("blur", this.blurHandler);
190
+
191
+ this.onStateChange = undefined;
192
+ this.watchers = [];
193
+ this.activityWatchers = [];
194
+ this.consumers = 0;
195
+ }
196
+
197
+ protected static emitStateChange(): void {
198
+ const state = this.state;
199
+ this.onStateChange?.(state);
200
+ for (const watcher of this.watchers) {
201
+ watcher(state);
202
+ }
203
+ }
204
+
205
+ }