@jmruthers/pace-core 0.5.183 → 0.5.185

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 (307) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +60 -1
  3. package/core-usage-manifest.json +312 -0
  4. package/dist/{DataTable-QAB34V6K.js → DataTable-IX2NBUTP.js} +6 -6
  5. package/dist/{DataTable-Bz8ffqyA.d.ts → DataTable-Z9NLVJh0.d.ts} +1 -1
  6. package/dist/{index-Bl--n7-T.d.ts → PublicPageProvider-BABf6JCh.d.ts} +21 -10
  7. package/dist/{UnifiedAuthProvider-7F6T4B6K.js → UnifiedAuthProvider-A4BCQRJY.js} +4 -2
  8. package/dist/{UnifiedAuthProvider-F86d7dSi.d.ts → UnifiedAuthProvider-BG0AL5eE.d.ts} +2 -1
  9. package/dist/{api-ROMBCNKU.js → api-BMFCXVQX.js} +2 -2
  10. package/dist/{chunk-RA3JUFMW.js → chunk-445GEP27.js} +154 -4
  11. package/dist/{chunk-RA3JUFMW.js.map → chunk-445GEP27.js.map} +1 -1
  12. package/dist/{chunk-CSOFYHAG.js → chunk-AISXLWGZ.js} +374 -60
  13. package/dist/chunk-AISXLWGZ.js.map +1 -0
  14. package/dist/{chunk-FUEYYMX5.js → chunk-FXFJRTKI.js} +24 -3
  15. package/dist/chunk-FXFJRTKI.js.map +1 -0
  16. package/dist/{chunk-QETLRQI6.js → chunk-HC67NW5K.js} +380 -360
  17. package/dist/chunk-HC67NW5K.js.map +1 -0
  18. package/dist/chunk-HESYZWZW.js +388 -0
  19. package/dist/chunk-HESYZWZW.js.map +1 -0
  20. package/dist/{chunk-QUVSNGIP.js → chunk-HGPQUCBC.js} +34 -9
  21. package/dist/{chunk-QUVSNGIP.js.map → chunk-HGPQUCBC.js.map} +1 -1
  22. package/dist/{chunk-UHNYIBXL.js → chunk-IXSNYUCT.js} +1 -1
  23. package/dist/chunk-IXSNYUCT.js.map +1 -0
  24. package/dist/{chunk-MI7HBHN3.js → chunk-MX3EIJGQ.js} +4 -3
  25. package/dist/{chunk-MI7HBHN3.js.map → chunk-MX3EIJGQ.js.map} +1 -1
  26. package/dist/{chunk-PWAHJW4G.js → chunk-OKI34GZD.js} +86 -33
  27. package/dist/chunk-OKI34GZD.js.map +1 -0
  28. package/dist/{chunk-W22JP75J.js → chunk-STTZQK2I.js} +3 -3
  29. package/dist/chunk-THRPYOFK.js +215 -0
  30. package/dist/chunk-THRPYOFK.js.map +1 -0
  31. package/dist/{chunk-M7W4CP3M.js → chunk-U6WNSFX5.js} +2 -1
  32. package/dist/chunk-U6WNSFX5.js.map +1 -0
  33. package/dist/{chunk-QCDXODCA.js → chunk-XAUHJD3L.js} +2 -2
  34. package/dist/components.d.ts +182 -6
  35. package/dist/components.js +157 -11
  36. package/dist/components.js.map +1 -1
  37. package/dist/eslint-rules/pace-core-compliance.cjs +406 -0
  38. package/dist/{file-reference-D06mEEWW.d.ts → file-reference-BjR39ktt.d.ts} +7 -1
  39. package/dist/hooks.d.ts +7 -14
  40. package/dist/hooks.js +10 -22
  41. package/dist/hooks.js.map +1 -1
  42. package/dist/index.d.ts +11 -11
  43. package/dist/index.js +79 -16
  44. package/dist/index.js.map +1 -1
  45. package/dist/providers.d.ts +1 -1
  46. package/dist/providers.js +3 -1
  47. package/dist/rbac/index.d.ts +205 -14
  48. package/dist/rbac/index.js +28 -6
  49. package/dist/timezone-_pgH8qrY.d.ts +530 -0
  50. package/dist/{types-_x1f4QBF.d.ts → types-DUyCRSTj.d.ts} +1 -1
  51. package/dist/types.d.ts +1 -1
  52. package/dist/types.js +1 -1
  53. package/dist/{usePublicRouteParams-JJczomYq.d.ts → usePublicRouteParams-CvnC3d-e.d.ts} +113 -2
  54. package/dist/utils.d.ts +109 -151
  55. package/dist/utils.js +128 -138
  56. package/dist/utils.js.map +1 -1
  57. package/docs/api/README.md +60 -1
  58. package/docs/api/classes/ColumnFactory.md +1 -1
  59. package/docs/api/classes/ErrorBoundary.md +1 -1
  60. package/docs/api/classes/InvalidScopeError.md +1 -1
  61. package/docs/api/classes/Logger.md +178 -0
  62. package/docs/api/classes/MissingUserContextError.md +1 -1
  63. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  64. package/docs/api/classes/PermissionDeniedError.md +1 -1
  65. package/docs/api/classes/RBACAuditManager.md +2 -2
  66. package/docs/api/classes/RBACCache.md +1 -1
  67. package/docs/api/classes/RBACEngine.md +2 -2
  68. package/docs/api/classes/RBACError.md +1 -1
  69. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  70. package/docs/api/classes/SecureSupabaseClient.md +5 -5
  71. package/docs/api/classes/StorageUtils.md +1 -1
  72. package/docs/api/enums/FileCategory.md +1 -1
  73. package/docs/api/enums/LogLevel.md +54 -0
  74. package/docs/api/enums/RBACErrorCode.md +1 -1
  75. package/docs/api/enums/RPCFunction.md +1 -1
  76. package/docs/api/interfaces/AggregateConfig.md +1 -1
  77. package/docs/api/interfaces/BadgeProps.md +1 -1
  78. package/docs/api/interfaces/ButtonProps.md +1 -1
  79. package/docs/api/interfaces/CalendarProps.md +18 -2
  80. package/docs/api/interfaces/CardProps.md +1 -1
  81. package/docs/api/interfaces/ColorPalette.md +1 -1
  82. package/docs/api/interfaces/ColorShade.md +1 -1
  83. package/docs/api/interfaces/ComplianceResult.md +30 -0
  84. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  85. package/docs/api/interfaces/DataRecord.md +1 -1
  86. package/docs/api/interfaces/DataTableAction.md +1 -1
  87. package/docs/api/interfaces/DataTableColumn.md +1 -1
  88. package/docs/api/interfaces/DataTableProps.md +1 -1
  89. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  90. package/docs/api/interfaces/DatabaseComplianceResult.md +85 -0
  91. package/docs/api/interfaces/DatabaseIssue.md +41 -0
  92. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  93. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  94. package/docs/api/interfaces/EventAppRoleData.md +6 -6
  95. package/docs/api/interfaces/ExportColumn.md +1 -1
  96. package/docs/api/interfaces/ExportOptions.md +1 -1
  97. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  98. package/docs/api/interfaces/FileMetadata.md +1 -1
  99. package/docs/api/interfaces/FileReference.md +1 -1
  100. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  101. package/docs/api/interfaces/FileUploadOptions.md +24 -8
  102. package/docs/api/interfaces/FileUploadProps.md +24 -13
  103. package/docs/api/interfaces/FooterProps.md +1 -1
  104. package/docs/api/interfaces/FormFieldProps.md +1 -1
  105. package/docs/api/interfaces/FormProps.md +1 -1
  106. package/docs/api/interfaces/GrantEventAppRoleParams.md +9 -9
  107. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  108. package/docs/api/interfaces/InputProps.md +1 -1
  109. package/docs/api/interfaces/LabelProps.md +1 -1
  110. package/docs/api/interfaces/LoggerConfig.md +62 -0
  111. package/docs/api/interfaces/LoginFormProps.md +1 -1
  112. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  113. package/docs/api/interfaces/NavigationContextType.md +1 -1
  114. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  115. package/docs/api/interfaces/NavigationItem.md +1 -1
  116. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  117. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  118. package/docs/api/interfaces/Organisation.md +1 -1
  119. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  120. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  121. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  122. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  123. package/docs/api/interfaces/PaceAppLayoutProps.md +36 -23
  124. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  125. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  126. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  127. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  128. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  129. package/docs/api/interfaces/PaletteData.md +1 -1
  130. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  131. package/docs/api/interfaces/ProgressProps.md +1 -1
  132. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  133. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  134. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  135. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  136. package/docs/api/interfaces/QuickFix.md +52 -0
  137. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  138. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  139. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  140. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  141. package/docs/api/interfaces/RBACConfig.md +4 -4
  142. package/docs/api/interfaces/RBACContext.md +1 -1
  143. package/docs/api/interfaces/RBACLogger.md +1 -1
  144. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  145. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  146. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  147. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  148. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  149. package/docs/api/interfaces/RBACResult.md +1 -1
  150. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  151. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  152. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  153. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  154. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  155. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  156. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  157. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  158. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  159. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  160. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  161. package/docs/api/interfaces/RevokeEventAppRoleParams.md +7 -7
  162. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  163. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  164. package/docs/api/interfaces/RoleManagementResult.md +5 -5
  165. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  166. package/docs/api/interfaces/RouteConfig.md +1 -1
  167. package/docs/api/interfaces/RuntimeComplianceResult.md +55 -0
  168. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  169. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  170. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  171. package/docs/api/interfaces/SetupIssue.md +41 -0
  172. package/docs/api/interfaces/StorageConfig.md +1 -1
  173. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  174. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  175. package/docs/api/interfaces/StorageListOptions.md +1 -1
  176. package/docs/api/interfaces/StorageListResult.md +1 -1
  177. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  178. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  179. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  180. package/docs/api/interfaces/StyleImport.md +1 -1
  181. package/docs/api/interfaces/SwitchProps.md +1 -1
  182. package/docs/api/interfaces/TabsContentProps.md +1 -1
  183. package/docs/api/interfaces/TabsListProps.md +1 -1
  184. package/docs/api/interfaces/TabsProps.md +1 -1
  185. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  186. package/docs/api/interfaces/TextareaProps.md +1 -1
  187. package/docs/api/interfaces/ToastActionElement.md +1 -1
  188. package/docs/api/interfaces/ToastProps.md +1 -1
  189. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  190. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  191. package/docs/api/interfaces/UseFormDialogOptions.md +62 -0
  192. package/docs/api/interfaces/UseFormDialogReturn.md +117 -0
  193. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  194. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  195. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  196. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  197. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  198. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  199. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  200. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  201. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  202. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  203. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  204. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  205. package/docs/api/interfaces/UserEventAccess.md +1 -1
  206. package/docs/api/interfaces/UserMenuProps.md +1 -1
  207. package/docs/api/interfaces/UserProfile.md +1 -1
  208. package/docs/api/modules.md +738 -42
  209. package/docs/api-reference/hooks.md +111 -0
  210. package/docs/api-reference/rpc-functions.md +1 -1
  211. package/docs/api-reference/utilities.md +184 -0
  212. package/docs/getting-started/installation-guide.md +75 -16
  213. package/docs/getting-started/quick-start.md +61 -11
  214. package/docs/implementation-guides/authentication.md +88 -12
  215. package/docs/implementation-guides/file-reference-system.md +2 -1
  216. package/docs/implementation-guides/file-upload-storage.md +21 -0
  217. package/docs/rbac/README.md +1 -0
  218. package/docs/rbac/compliance/compliance-guide.md +544 -0
  219. package/docs/rbac/getting-started.md +158 -33
  220. package/docs/standards/pace-core-compliance.md +432 -0
  221. package/eslint-config-pace-core.cjs +93 -0
  222. package/package.json +15 -3
  223. package/scripts/analyze-bundle.js +232 -0
  224. package/scripts/build-css.js +56 -0
  225. package/scripts/build-docs-incremental.js +1015 -0
  226. package/scripts/check-pace-core-compliance.cjs +2353 -0
  227. package/scripts/generate-docs.js +157 -0
  228. package/scripts/setup-build-cache.js +73 -0
  229. package/scripts/utils/command-runner.js +131 -0
  230. package/scripts/utils/env.js +33 -0
  231. package/scripts/utils/index.js +10 -0
  232. package/scripts/utils/logger.js +88 -0
  233. package/scripts/utils/path-helpers.js +37 -0
  234. package/scripts/validate-formats.js +133 -0
  235. package/scripts/validate-master.js +155 -0
  236. package/scripts/validate-pre-publish.js +140 -0
  237. package/scripts/validate-theme.js +142 -0
  238. package/src/components/Calendar/Calendar.tsx +8 -1
  239. package/src/components/Card/Card.tsx +47 -8
  240. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +314 -0
  241. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +126 -0
  242. package/src/components/DatePickerWithTimezone/README.md +135 -0
  243. package/src/components/DatePickerWithTimezone/index.ts +10 -0
  244. package/src/components/DateTimeField/DateTimeField.test.tsx +358 -0
  245. package/src/components/DateTimeField/DateTimeField.tsx +232 -0
  246. package/src/components/DateTimeField/README.md +148 -0
  247. package/src/components/DateTimeField/index.ts +10 -0
  248. package/src/components/FileUpload/FileUpload.tsx +3 -0
  249. package/src/components/Header/Header.test.tsx +47 -18
  250. package/src/components/Header/Header.tsx +24 -6
  251. package/src/components/PaceAppLayout/PaceAppLayout.tsx +29 -20
  252. package/src/components/PaceAppLayout/README.md +9 -0
  253. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
  254. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
  255. package/src/components/ProtectedRoute/ProtectedRoute.tsx +12 -4
  256. package/src/components/index.ts +8 -0
  257. package/src/eslint-rules/pace-core-compliance.cjs +406 -0
  258. package/src/eslint-rules/pace-core-compliance.js +640 -0
  259. package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
  260. package/src/hooks/index.ts +2 -0
  261. package/src/hooks/useFileReference.test.ts +1 -0
  262. package/src/hooks/useFormDialog.ts +147 -0
  263. package/src/index.ts +27 -0
  264. package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
  265. package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
  266. package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
  267. package/src/rbac/compliance/database-validator.ts +165 -0
  268. package/src/rbac/compliance/index.ts +38 -0
  269. package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
  270. package/src/rbac/compliance/runtime-compliance.ts +77 -0
  271. package/src/rbac/compliance/setup-validator.ts +131 -0
  272. package/src/rbac/components/PagePermissionGuard.tsx +8 -64
  273. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
  274. package/src/rbac/docs/event-based-apps.md +285 -0
  275. package/src/rbac/errors.ts +11 -0
  276. package/src/rbac/hooks/useRoleManagement.ts +292 -12
  277. package/src/rbac/index.ts +30 -0
  278. package/src/services/OrganisationService.ts +4 -0
  279. package/src/types/file-reference.ts +6 -0
  280. package/src/utils/__tests__/timezone.test.ts +345 -0
  281. package/src/utils/file-reference/__tests__/file-reference.test.ts +2 -0
  282. package/src/utils/file-reference/index.ts +1 -0
  283. package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
  284. package/src/utils/formatting/formatting.ts +179 -0
  285. package/src/utils/index.ts +27 -1
  286. package/src/utils/location/index.ts +16 -0
  287. package/src/utils/location/location.test.ts +286 -0
  288. package/src/utils/location/location.ts +175 -0
  289. package/src/utils/timezone/index.ts +17 -0
  290. package/src/utils/timezone/timezone.test.ts +349 -0
  291. package/src/utils/timezone/timezone.ts +281 -0
  292. package/dist/chunk-CSOFYHAG.js.map +0 -1
  293. package/dist/chunk-FUEYYMX5.js.map +0 -1
  294. package/dist/chunk-HKIT6O7W.js +0 -198
  295. package/dist/chunk-HKIT6O7W.js.map +0 -1
  296. package/dist/chunk-KUEN3HFB.js +0 -94
  297. package/dist/chunk-KUEN3HFB.js.map +0 -1
  298. package/dist/chunk-M7W4CP3M.js.map +0 -1
  299. package/dist/chunk-PWAHJW4G.js.map +0 -1
  300. package/dist/chunk-QETLRQI6.js.map +0 -1
  301. package/dist/chunk-UHNYIBXL.js.map +0 -1
  302. package/dist/formatting-5wETwiGF.d.ts +0 -162
  303. /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
  304. /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
  305. /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
  306. /package/dist/{chunk-W22JP75J.js.map → chunk-STTZQK2I.js.map} +0 -0
  307. /package/dist/{chunk-QCDXODCA.js.map → chunk-XAUHJD3L.js.map} +0 -0
@@ -0,0 +1,388 @@
1
+ import {
2
+ formatInTimeZone,
3
+ getTimezoneAbbreviation
4
+ } from "./chunk-THRPYOFK.js";
5
+ import {
6
+ createLogger
7
+ } from "./chunk-PWLANIRT.js";
8
+
9
+ // src/utils/context/sessionTracking.ts
10
+ var log = createLogger("SessionTracking");
11
+ function useSessionTracking(supabaseClient, appName) {
12
+ const resolveAppId = async () => {
13
+ if (!appName) return void 0;
14
+ try {
15
+ const { data, error } = await supabaseClient.from("rbac_apps").select("id").eq("name", appName).eq("is_active", true).single();
16
+ if (error || !data) {
17
+ log.warn("App not found or inactive:", appName);
18
+ return void 0;
19
+ }
20
+ return data.id;
21
+ } catch (error) {
22
+ log.error("Failed to resolve app ID:", error);
23
+ return void 0;
24
+ }
25
+ };
26
+ const trackEventSwitch = async (eventId) => {
27
+ try {
28
+ const { data: { user } } = await supabaseClient.auth.getUser();
29
+ if (!user) {
30
+ log.warn("No authenticated user found for session tracking");
31
+ return;
32
+ }
33
+ const appId = await resolveAppId();
34
+ const params = {
35
+ p_session_type: "event_switch",
36
+ p_event_id: eventId,
37
+ p_app_id: appId
38
+ };
39
+ const { error } = await supabaseClient.rpc("rbac_session_track", {
40
+ p_user_id: user?.id,
41
+ p_session_type: params.p_session_type,
42
+ p_event_id: params.p_event_id,
43
+ p_app_id: params.p_app_id,
44
+ p_ip_address: params.ip_address,
45
+ p_user_agent: params.user_agent
46
+ });
47
+ if (error) {
48
+ log.error("Failed to track event switch session:", error);
49
+ }
50
+ } catch (error) {
51
+ log.error("Failed to track event switch:", error);
52
+ }
53
+ };
54
+ const trackSessionExpired = async () => {
55
+ try {
56
+ const { data: { user } } = await supabaseClient.auth.getUser();
57
+ if (!user) {
58
+ log.warn("No authenticated user found for session tracking");
59
+ return;
60
+ }
61
+ const appId = await resolveAppId();
62
+ const params = {
63
+ p_session_type: "session_expired",
64
+ p_app_id: appId
65
+ };
66
+ const { error } = await supabaseClient.rpc("rbac_session_track", {
67
+ p_user_id: user?.id,
68
+ p_session_type: params.p_session_type,
69
+ p_event_id: params.p_event_id,
70
+ p_app_id: params.p_app_id,
71
+ p_ip_address: params.ip_address,
72
+ p_user_agent: params.user_agent
73
+ });
74
+ if (error) {
75
+ log.error("Failed to track session expiration:", error);
76
+ }
77
+ } catch (error) {
78
+ log.error("Failed to track session expiration:", error);
79
+ }
80
+ };
81
+ return {
82
+ trackEventSwitch,
83
+ trackSessionExpired
84
+ };
85
+ }
86
+
87
+ // src/utils/validation/common.ts
88
+ import { z } from "zod";
89
+ var emailSchema = z.string().min(1, "Email is required").email("Invalid email format").max(254, "Email too long");
90
+ var nameSchema = z.string().min(1, "Name is required").max(100, "Name too long").regex(/^[a-zA-Z\s'-]+$/, "Name contains invalid characters");
91
+ var phoneSchema = z.string().regex(/^\+?[\d\s\-\(\)]+$/, "Invalid phone number format").min(10, "Phone number too short").max(20, "Phone number too long");
92
+ var urlSchema = z.string().url("Invalid URL format").max(2048, "URL too long");
93
+ var dateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format").refine((date) => {
94
+ const parsed = new Date(date);
95
+ return !isNaN(parsed.getTime());
96
+ }, "Invalid date");
97
+
98
+ // src/utils/validation/passwordSchema.ts
99
+ import { z as z2 } from "zod";
100
+ var COMMON_PASSWORDS = /* @__PURE__ */ new Set([
101
+ "password",
102
+ "123456",
103
+ "123456789",
104
+ "qwerty",
105
+ "abc123",
106
+ "password123",
107
+ "admin",
108
+ "letmein",
109
+ "welcome",
110
+ "monkey",
111
+ "1234567890",
112
+ "password1"
113
+ ]);
114
+ var WEAK_PATTERNS = [
115
+ /^(.)\1+$/,
116
+ // All same character
117
+ /^(012|123|234|345|456|567|678|789|890|987|876|765|654|543|432|321|210)+/,
118
+ // Sequential numbers
119
+ /^(abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)+/i
120
+ // Sequential letters
121
+ ];
122
+ var securePasswordSchema = z2.string().min(8, "Password must be at least 8 characters long").max(128, "Password must not exceed 128 characters").refine(
123
+ (password) => /[a-z]/.test(password),
124
+ "Password must contain at least one lowercase letter"
125
+ ).refine(
126
+ (password) => /[A-Z]/.test(password),
127
+ "Password must contain at least one uppercase letter"
128
+ ).refine(
129
+ (password) => /\d/.test(password),
130
+ "Password must contain at least one number"
131
+ ).refine(
132
+ (password) => /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password),
133
+ "Password must contain at least one special character"
134
+ ).refine(
135
+ (password) => !COMMON_PASSWORDS.has(password.toLowerCase()),
136
+ "Password is too common. Please choose a stronger password"
137
+ ).refine(
138
+ (password) => !WEAK_PATTERNS.some((pattern) => pattern.test(password)),
139
+ "Password contains weak patterns. Please choose a more complex password"
140
+ ).refine(
141
+ (password) => {
142
+ const keyboardPatterns = ["qwerty", "asdfgh", "zxcvbn", "1234567890"];
143
+ return !keyboardPatterns.some(
144
+ (pattern) => password.toLowerCase().includes(pattern)
145
+ );
146
+ },
147
+ "Password contains keyboard patterns. Please choose a more secure password"
148
+ );
149
+ var passwordSchema = z2.string().min(6, "Password must be at least 6 characters long").max(128, "Password must not exceed 128 characters");
150
+ function calculatePasswordStrength(password) {
151
+ let score = 0;
152
+ const feedback = [];
153
+ if (password.length >= 8) score += 20;
154
+ else if (password.length >= 6) score += 10;
155
+ else feedback.push("Use at least 8 characters");
156
+ if (/[a-z]/.test(password)) score += 15;
157
+ else feedback.push("Add lowercase letters");
158
+ if (/[A-Z]/.test(password)) score += 15;
159
+ else feedback.push("Add uppercase letters");
160
+ if (/\d/.test(password)) score += 15;
161
+ else feedback.push("Add numbers");
162
+ if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) score += 15;
163
+ else feedback.push("Add special characters");
164
+ if (password.length >= 12) score += 10;
165
+ if (/[^a-zA-Z0-9]/.test(password)) score += 10;
166
+ if (COMMON_PASSWORDS.has(password.toLowerCase())) {
167
+ score -= 30;
168
+ feedback.push("Avoid common passwords");
169
+ }
170
+ if (WEAK_PATTERNS.some((pattern) => pattern.test(password))) {
171
+ score -= 20;
172
+ feedback.push("Avoid predictable patterns");
173
+ }
174
+ let level;
175
+ if (score < 30) level = "very-weak";
176
+ else if (score < 50) level = "weak";
177
+ else if (score < 70) level = "fair";
178
+ else if (score < 90) level = "good";
179
+ else level = "strong";
180
+ return { score: Math.max(0, Math.min(100, score)), feedback, level };
181
+ }
182
+
183
+ // src/utils/app/appConfig.ts
184
+ var currentAppConfig = null;
185
+ function setAppConfig(config) {
186
+ currentAppConfig = config;
187
+ }
188
+ function getAppConfig() {
189
+ if (!currentAppConfig) {
190
+ const appName = import.meta.env.REACT_APP_NAME || "PACE";
191
+ return {
192
+ appName,
193
+ appId: appName
194
+ };
195
+ }
196
+ return currentAppConfig;
197
+ }
198
+ function getCurrentAppName() {
199
+ return getAppConfig().appName;
200
+ }
201
+ function getCurrentAppId() {
202
+ return getAppConfig().appId;
203
+ }
204
+
205
+ // src/utils/formatting/formatting.ts
206
+ import { parseISO, isValid } from "date-fns";
207
+ function formatDate(date) {
208
+ const dateObj = typeof date === "string" || typeof date === "number" ? new Date(date) : date;
209
+ return dateObj.toLocaleDateString("en-GB", {
210
+ year: "numeric",
211
+ month: "short",
212
+ day: "numeric"
213
+ });
214
+ }
215
+ function formatTime(date) {
216
+ const dateObj = typeof date === "string" || typeof date === "number" ? new Date(date) : date;
217
+ return dateObj.toLocaleTimeString("en-GB", {
218
+ hour: "2-digit",
219
+ minute: "2-digit",
220
+ hour12: false
221
+ });
222
+ }
223
+ function formatDateTime(date) {
224
+ const dateObj = typeof date === "string" || typeof date === "number" ? new Date(date) : date;
225
+ return dateObj.toLocaleString("en-GB", {
226
+ year: "numeric",
227
+ month: "short",
228
+ day: "numeric",
229
+ hour: "2-digit",
230
+ minute: "2-digit",
231
+ hour12: false
232
+ });
233
+ }
234
+ function formatCurrency(value, currencyCode = "USD", locale = "en-US") {
235
+ return new Intl.NumberFormat(locale, {
236
+ style: "currency",
237
+ currency: currencyCode
238
+ }).format(value);
239
+ }
240
+ function formatNumber(value, options = {}, locale = "en-US") {
241
+ return new Intl.NumberFormat(locale, options).format(value);
242
+ }
243
+ function formatPercent(value, locale = "en-US", decimalsOrOptions) {
244
+ let decimals;
245
+ if (typeof decimalsOrOptions === "number") {
246
+ decimals = decimalsOrOptions;
247
+ } else if (decimalsOrOptions && typeof decimalsOrOptions === "object") {
248
+ if (decimalsOrOptions.preserveDecimals) {
249
+ const valueStr = value.toString();
250
+ const decimalIndex = valueStr.indexOf(".");
251
+ if (decimalIndex !== -1) {
252
+ const detectedDecimals = valueStr.length - decimalIndex - 1;
253
+ const maxDecimals = decimalsOrOptions.maxDecimals ?? 10;
254
+ decimals = Math.min(detectedDecimals, maxDecimals);
255
+ } else {
256
+ decimals = 0;
257
+ }
258
+ } else {
259
+ decimals = decimalsOrOptions.decimals ?? 1;
260
+ }
261
+ } else {
262
+ decimals = 1;
263
+ }
264
+ return new Intl.NumberFormat(locale, {
265
+ style: "percent",
266
+ minimumFractionDigits: decimals,
267
+ maximumFractionDigits: decimals
268
+ }).format(value / 100);
269
+ }
270
+ function formatCompactNumber(value, locale = "en-US") {
271
+ return new Intl.NumberFormat(locale, {
272
+ notation: "compact",
273
+ compactDisplay: "short"
274
+ }).format(value);
275
+ }
276
+ function formatFileSize(bytes) {
277
+ if (bytes === 0) return "0 Bytes";
278
+ const k = 1024;
279
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
280
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
281
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
282
+ }
283
+ function formatDateTimeForDisplay(utcDate, timezone, options = {}) {
284
+ if (!utcDate) {
285
+ return "";
286
+ }
287
+ if (!timezone) {
288
+ return "";
289
+ }
290
+ try {
291
+ const { includeTimezone = true, format: formatStr = "MMM dd, yyyy HH:mm" } = options;
292
+ let dateObj;
293
+ if (typeof utcDate === "string") {
294
+ dateObj = parseISO(utcDate);
295
+ } else {
296
+ dateObj = utcDate;
297
+ }
298
+ if (!isValid(dateObj)) {
299
+ return "";
300
+ }
301
+ const formatted = formatInTimeZone(dateObj, timezone, formatStr);
302
+ if (includeTimezone) {
303
+ const tzAbbr = getTimezoneAbbreviation(dateObj, timezone);
304
+ return `${formatted} (${tzAbbr})`;
305
+ }
306
+ return formatted;
307
+ } catch {
308
+ return "";
309
+ }
310
+ }
311
+ function formatDateOnlyForDisplay(utcDate) {
312
+ if (!utcDate) {
313
+ return "";
314
+ }
315
+ try {
316
+ let dateObj;
317
+ if (typeof utcDate === "string") {
318
+ dateObj = parseISO(utcDate);
319
+ } else {
320
+ dateObj = utcDate;
321
+ }
322
+ if (!isValid(dateObj)) {
323
+ return "";
324
+ }
325
+ return dateObj.toLocaleDateString("en-GB", {
326
+ year: "numeric",
327
+ month: "long",
328
+ day: "numeric"
329
+ });
330
+ } catch {
331
+ return "";
332
+ }
333
+ }
334
+ function formatDateTimeForTable(utcDate, timezone) {
335
+ return formatDateTimeForDisplay(utcDate, timezone, {
336
+ includeTimezone: true,
337
+ format: "MMM dd, yyyy HH:mm"
338
+ });
339
+ }
340
+ function formatDateTimeForMap(utcDate, timezone) {
341
+ if (!utcDate || !timezone) {
342
+ return "";
343
+ }
344
+ try {
345
+ let dateObj;
346
+ if (typeof utcDate === "string") {
347
+ dateObj = parseISO(utcDate);
348
+ } else {
349
+ dateObj = utcDate;
350
+ }
351
+ if (!isValid(dateObj)) {
352
+ return "";
353
+ }
354
+ const formatted = formatInTimeZone(dateObj, timezone, "MMM dd, HH:mm");
355
+ const tzAbbr = getTimezoneAbbreviation(dateObj, timezone);
356
+ return `${formatted} ${tzAbbr}`;
357
+ } catch {
358
+ return "";
359
+ }
360
+ }
361
+
362
+ export {
363
+ useSessionTracking,
364
+ emailSchema,
365
+ nameSchema,
366
+ phoneSchema,
367
+ urlSchema,
368
+ dateSchema,
369
+ passwordSchema,
370
+ calculatePasswordStrength,
371
+ setAppConfig,
372
+ getAppConfig,
373
+ getCurrentAppName,
374
+ getCurrentAppId,
375
+ formatDate,
376
+ formatTime,
377
+ formatDateTime,
378
+ formatCurrency,
379
+ formatNumber,
380
+ formatPercent,
381
+ formatCompactNumber,
382
+ formatFileSize,
383
+ formatDateTimeForDisplay,
384
+ formatDateOnlyForDisplay,
385
+ formatDateTimeForTable,
386
+ formatDateTimeForMap
387
+ };
388
+ //# sourceMappingURL=chunk-HESYZWZW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/context/sessionTracking.ts","../src/utils/validation/common.ts","../src/utils/validation/passwordSchema.ts","../src/utils/app/appConfig.ts","../src/utils/formatting/formatting.ts"],"sourcesContent":["import type { SupabaseClient } from '@supabase/supabase-js';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('SessionTracking');\n\n// Define the tracking parameters locally since old RBAC types are removed\ninterface TrackUserSessionParams {\n p_session_type: 'event_switch' | 'session_expired';\n p_event_id?: string;\n p_app_id?: string;\n ip_address?: string;\n user_agent?: string;\n}\n\n/**\n * Hook for manual session tracking (event switches and session expiration).\n * \n * Note: Login and logout tracking is automatically handled by UnifiedAuthProvider.\n * You should only use this hook for tracking event switches or session expirations.\n * \n * @param supabaseClient - Supabase client instance\n * @param appName - Optional application name for tracking\n * @returns Object containing tracking functions for event switches and session expiration\n */\nexport function useSessionTracking(supabaseClient: SupabaseClient, appName?: string) {\n // Resolve app name to app_id\n const resolveAppId = async (): Promise<string | undefined> => {\n if (!appName) return undefined;\n \n try {\n const { data, error } = await supabaseClient\n .from('rbac_apps')\n .select('id')\n .eq('name', appName)\n .eq('is_active', true)\n .single();\n \n if (error || !data) {\n log.warn('App not found or inactive:', appName);\n return undefined;\n }\n \n return data.id;\n } catch (error) {\n log.error('Failed to resolve app ID:', error);\n return undefined;\n }\n };\n /**\n * Track an event switch\n * @param eventId - ID of the event being switched to\n */\n const trackEventSwitch = async (eventId: string) => {\n try {\n const { data: { user } } = await supabaseClient.auth.getUser();\n if (!user) {\n log.warn('No authenticated user found for session tracking');\n return;\n }\n\n const appId = await resolveAppId();\n\n const params: TrackUserSessionParams = {\n p_session_type: 'event_switch',\n p_event_id: eventId,\n p_app_id: appId\n };\n\n const { error } = await supabaseClient.rpc('rbac_session_track', {\n p_user_id: user?.id,\n p_session_type: params.p_session_type,\n p_event_id: params.p_event_id,\n p_app_id: params.p_app_id,\n p_ip_address: params.ip_address,\n p_user_agent: params.user_agent\n });\n \n if (error) {\n log.error('Failed to track event switch session:', error);\n }\n } catch (error) {\n log.error('Failed to track event switch:', error);\n }\n };\n\n /**\n * Track a session expiration\n */\n const trackSessionExpired = async () => {\n try {\n const { data: { user } } = await supabaseClient.auth.getUser();\n if (!user) {\n log.warn('No authenticated user found for session tracking');\n return;\n }\n\n const appId = await resolveAppId();\n\n const params: TrackUserSessionParams = {\n p_session_type: 'session_expired',\n p_app_id: appId\n };\n\n const { error } = await supabaseClient.rpc('rbac_session_track', {\n p_user_id: user?.id,\n p_session_type: params.p_session_type,\n p_event_id: params.p_event_id,\n p_app_id: params.p_app_id,\n p_ip_address: params.ip_address,\n p_user_agent: params.user_agent\n });\n \n if (error) {\n log.error('Failed to track session expiration:', error);\n }\n } catch (error) {\n log.error('Failed to track session expiration:', error);\n }\n };\n\n return {\n trackEventSwitch,\n trackSessionExpired\n };\n} ","\n/**\n * @file Common validation schemas\n * @description Reusable validation schemas for common data types\n */\n\nimport { z } from 'zod';\n\n/**\n * Email validation schema\n */\nexport const emailSchema = z\n .string()\n .min(1, 'Email is required')\n .email('Invalid email format')\n .max(254, 'Email too long');\n\n/**\n * Name validation schema\n */\nexport const nameSchema = z\n .string()\n .min(1, 'Name is required')\n .max(100, 'Name too long')\n .regex(/^[a-zA-Z\\s'-]+$/, 'Name contains invalid characters');\n\n/**\n * Phone number validation schema\n */\nexport const phoneSchema = z\n .string()\n .regex(/^\\+?[\\d\\s\\-\\(\\)]+$/, 'Invalid phone number format')\n .min(10, 'Phone number too short')\n .max(20, 'Phone number too long');\n\n/**\n * URL validation schema\n */\nexport const urlSchema = z\n .string()\n .url('Invalid URL format')\n .max(2048, 'URL too long');\n\n/**\n * Date validation schema\n */\nexport const dateSchema = z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'Date must be in YYYY-MM-DD format')\n .refine((date) => {\n const parsed = new Date(date);\n return !isNaN(parsed.getTime());\n }, 'Invalid date');\n","\n/**\n * @file Enhanced Password Schema with Security Validations\n * @description Comprehensive password validation with security checks\n */\n\nimport { z } from 'zod';\n\n// Common weak passwords to check against\nconst COMMON_PASSWORDS = new Set([\n 'password', '123456', '123456789', 'qwerty', 'abc123', 'password123',\n 'admin', 'letmein', 'welcome', 'monkey', '1234567890', 'password1'\n]);\n\n// Common password patterns to avoid\nconst WEAK_PATTERNS = [\n /^(.)\\1+$/, // All same character\n /^(012|123|234|345|456|567|678|789|890|987|876|765|654|543|432|321|210)+/, // Sequential numbers\n /^(abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)+/i, // Sequential letters\n];\n\n/**\n * Enhanced password validation schema with security checks\n */\nexport const securePasswordSchema = z\n .string()\n .min(8, 'Password must be at least 8 characters long')\n .max(128, 'Password must not exceed 128 characters')\n .refine(\n (password) => /[a-z]/.test(password),\n 'Password must contain at least one lowercase letter'\n )\n .refine(\n (password) => /[A-Z]/.test(password),\n 'Password must contain at least one uppercase letter'\n )\n .refine(\n (password) => /\\d/.test(password),\n 'Password must contain at least one number'\n )\n .refine(\n (password) => /[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]/.test(password),\n 'Password must contain at least one special character'\n )\n .refine(\n (password) => !COMMON_PASSWORDS.has(password.toLowerCase()),\n 'Password is too common. Please choose a stronger password'\n )\n .refine(\n (password) => !WEAK_PATTERNS.some(pattern => pattern.test(password)),\n 'Password contains weak patterns. Please choose a more complex password'\n )\n .refine(\n (password) => {\n // Check for keyboard patterns (qwerty, asdf, etc.)\n const keyboardPatterns = ['qwerty', 'asdfgh', 'zxcvbn', '1234567890'];\n return !keyboardPatterns.some(pattern => \n password.toLowerCase().includes(pattern)\n );\n },\n 'Password contains keyboard patterns. Please choose a more secure password'\n );\n\n/**\n * Basic password schema for less strict requirements\n */\nexport const passwordSchema = z\n .string()\n .min(6, 'Password must be at least 6 characters long')\n .max(128, 'Password must not exceed 128 characters');\n\n/**\n * Password strength calculator\n */\nexport function calculatePasswordStrength(password: string): {\n score: number;\n feedback: string[];\n level: 'very-weak' | 'weak' | 'fair' | 'good' | 'strong';\n} {\n let score = 0;\n const feedback: string[] = [];\n\n // Length check\n if (password.length >= 8) score += 20;\n else if (password.length >= 6) score += 10;\n else feedback.push('Use at least 8 characters');\n\n // Character variety\n if (/[a-z]/.test(password)) score += 15;\n else feedback.push('Add lowercase letters');\n\n if (/[A-Z]/.test(password)) score += 15;\n else feedback.push('Add uppercase letters');\n\n if (/\\d/.test(password)) score += 15;\n else feedback.push('Add numbers');\n\n if (/[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]/.test(password)) score += 15;\n else feedback.push('Add special characters');\n\n // Additional complexity\n if (password.length >= 12) score += 10;\n if (/[^a-zA-Z0-9]/.test(password)) score += 10;\n\n // Penalties\n if (COMMON_PASSWORDS.has(password.toLowerCase())) {\n score -= 30;\n feedback.push('Avoid common passwords');\n }\n\n if (WEAK_PATTERNS.some(pattern => pattern.test(password))) {\n score -= 20;\n feedback.push('Avoid predictable patterns');\n }\n\n // Determine level\n let level: 'very-weak' | 'weak' | 'fair' | 'good' | 'strong';\n if (score < 30) level = 'very-weak';\n else if (score < 50) level = 'weak';\n else if (score < 70) level = 'fair';\n else if (score < 90) level = 'good';\n else level = 'strong';\n\n return { score: Math.max(0, Math.min(100, score)), feedback, level };\n}\n","\n/**\n * Application configuration utilities\n */\n\nexport interface AppConfig {\n appName: string;\n appId: string;\n}\n\nlet currentAppConfig: AppConfig | null = null;\n\n/**\n * Set the current application configuration\n */\nexport function setAppConfig(config: AppConfig) {\n currentAppConfig = config;\n}\n\n/**\n * Get the current application configuration\n */\nexport function getAppConfig(): AppConfig {\n if (!currentAppConfig) {\n // Fallback to environment or default\n const appName = import.meta.env.REACT_APP_NAME || 'PACE';\n return {\n appName,\n appId: appName\n };\n }\n return currentAppConfig;\n}\n\n/**\n * Get the current app name\n */\nexport function getCurrentAppName(): string {\n return getAppConfig().appName;\n}\n\n/**\n * Get the current app ID\n */\nexport function getCurrentAppId(): string {\n return getAppConfig().appId;\n}\n","/**\n * Utility functions for formatting data in the application\n */\n\nimport { parseISO, isValid } from 'date-fns';\nimport { formatInTimeZone, getTimezoneAbbreviation } from '../timezone';\n\n/**\n * Format a date as a readable string in \"dd mmm yyyy\" format (e.g., \"15 Jun 2024\")\n */\nexport function formatDate(date: Date | string | number): string {\n const dateObj = typeof date === 'string' || typeof date === 'number' \n ? new Date(date) \n : date;\n \n // Use 'en-GB' locale to ensure \"dd mmm yyyy\" format (e.g., \"15 Jun 2024\")\n return dateObj.toLocaleDateString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric'\n });\n}\n\n/**\n * Format a time as a readable string in \"HH:mm\" format (e.g., \"14:30\")\n * Uses 24-hour format for consistency across pace apps\n */\nexport function formatTime(date: Date | string | number): string {\n const dateObj = typeof date === 'string' || typeof date === 'number' \n ? new Date(date) \n : date;\n \n // Use 'en-GB' locale to ensure \"HH:mm\" format (24-hour format)\n return dateObj.toLocaleTimeString('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: false\n });\n}\n\n/**\n * Format a date and time as a readable string in \"dd mmm yyyy, HH:mm\" format (e.g., \"15 Jun 2024, 14:30\")\n * Uses 24-hour format for consistency across pace apps\n */\nexport function formatDateTime(date: Date | string | number): string {\n const dateObj = typeof date === 'string' || typeof date === 'number' \n ? new Date(date) \n : date;\n \n // Use 'en-GB' locale to ensure consistent format (e.g., \"15 Jun 2024, 14:30\")\n return dateObj.toLocaleString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false\n });\n}\n\n/**\n * Format a number as a currency\n */\nexport function formatCurrency(value: number, currencyCode = 'USD', locale = 'en-US'): string {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: currencyCode,\n }).format(value);\n}\n\n/**\n * Format a number with custom options\n */\nexport function formatNumber(\n value: number,\n options: Intl.NumberFormatOptions = {},\n locale = 'en-US'\n): string {\n return new Intl.NumberFormat(locale, options).format(value);\n}\n\n/**\n * Format a number as a percentage.\n * \n * The third parameter can be either:\n * - A number for fixed decimal places (backward compatible): `formatPercent(0.81, 'en-US', 2)`\n * - An options object with:\n * - `decimals`: Fixed number of decimal places (default: 1)\n * - `preserveDecimals`: Auto-detect and preserve decimal places from the input value\n * - `maxDecimals`: Maximum decimal places when preserving (default: 10)\n * \n * @param value - The percentage value as a decimal (e.g., 0.81 for 0.81%)\n * @param locale - The locale string (default: 'en-US')\n * @param decimalsOrOptions - Either a number for fixed decimals, or an options object with:\n * - `decimals` - Fixed number of decimal places (default: 1)\n * - `preserveDecimals` - Auto-detect and preserve decimal places from the input value\n * - `maxDecimals` - Maximum decimal places when preserving (default: 10)\n * @returns Formatted percentage string (e.g., \"0.81%\", \"81%\")\n * \n * @example\n * ```ts\n * // Fixed decimals (default behavior)\n * formatPercent(0.5) // '0.5%'\n * formatPercent(0.81, 'en-US', 1) // '0.8%' (loses precision)\n * \n * // Preserve decimal places dynamically\n * formatPercent(0.81, 'en-US', { preserveDecimals: true }) // '0.81%'\n * formatPercent(0.8123, 'en-US', { preserveDecimals: true, maxDecimals: 2 }) // '0.81%'\n * ```\n */\nexport function formatPercent(\n value: number,\n locale: string = 'en-US',\n decimalsOrOptions?: number | {\n decimals?: number;\n preserveDecimals?: boolean;\n maxDecimals?: number;\n }\n): string {\n let decimals: number;\n\n // Backward compatibility: if decimalsOrOptions is a number, use it directly\n if (typeof decimalsOrOptions === 'number') {\n decimals = decimalsOrOptions;\n } else if (decimalsOrOptions && typeof decimalsOrOptions === 'object') {\n // New options object: check if we should preserve decimals\n if (decimalsOrOptions.preserveDecimals) {\n const valueStr = value.toString();\n const decimalIndex = valueStr.indexOf('.');\n \n if (decimalIndex !== -1) {\n const detectedDecimals = valueStr.length - decimalIndex - 1;\n const maxDecimals = decimalsOrOptions.maxDecimals ?? 10;\n decimals = Math.min(detectedDecimals, maxDecimals);\n } else {\n decimals = 0;\n }\n } else {\n decimals = decimalsOrOptions.decimals ?? 1;\n }\n } else {\n decimals = 1;\n }\n\n return new Intl.NumberFormat(locale, {\n style: 'percent',\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(value / 100);\n}\n\n/**\n * Format a large number with abbreviations (K, M, B)\n */\nexport function formatCompactNumber(value: number, locale = 'en-US'): string {\n return new Intl.NumberFormat(locale, {\n notation: 'compact',\n compactDisplay: 'short'\n }).format(value);\n}\n\n/**\n * Format a file size in bytes to a human-readable string\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n \n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n}\n\n/**\n * Options for formatting date/time with timezone\n */\nexport interface DateTimeFormatOptions {\n /**\n * Include timezone abbreviation (default: true)\n */\n includeTimezone?: boolean;\n /**\n * Custom format string (default: 'MMM dd, yyyy HH:mm')\n */\n format?: string;\n}\n\n/**\n * Format a UTC date for display in a specific timezone\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @param timezone - IANA timezone string (e.g., 'America/New_York')\n * @param options - Formatting options\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateTimeForDisplay('2024-01-15T10:00:00Z', 'America/New_York');\n * // \"Jan 15, 2024 05:00 (EST)\"\n *\n * formatDateTimeForDisplay('2024-01-15T10:00:00Z', 'America/New_York', { includeTimezone: false });\n * // \"Jan 15, 2024 05:00\"\n * ```\n */\nexport function formatDateTimeForDisplay(\n utcDate: string | Date | undefined,\n timezone: string | undefined,\n options: DateTimeFormatOptions = {}\n): string {\n if (!utcDate) {\n return '';\n }\n\n if (!timezone) {\n return '';\n }\n\n try {\n const { includeTimezone = true, format: formatStr = 'MMM dd, yyyy HH:mm' } = options;\n\n let dateObj: Date;\n if (typeof utcDate === 'string') {\n dateObj = parseISO(utcDate);\n } else {\n dateObj = utcDate;\n }\n\n if (!isValid(dateObj)) {\n return '';\n }\n\n const formatted = formatInTimeZone(dateObj, timezone, formatStr);\n\n if (includeTimezone) {\n const tzAbbr = getTimezoneAbbreviation(dateObj, timezone);\n return `${formatted} (${tzAbbr})`;\n }\n\n return formatted;\n } catch {\n return '';\n }\n}\n\n/**\n * Format a UTC date for display (date only, no time)\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateOnlyForDisplay('2024-01-15T10:00:00Z');\n * // \"15 January 2024\"\n * ```\n */\nexport function formatDateOnlyForDisplay(utcDate: string | Date | undefined): string {\n if (!utcDate) {\n return '';\n }\n\n try {\n let dateObj: Date;\n if (typeof utcDate === 'string') {\n dateObj = parseISO(utcDate);\n } else {\n dateObj = utcDate;\n }\n\n if (!isValid(dateObj)) {\n return '';\n }\n\n // Use 'en-GB' locale for \"dd mmm yyyy\" format\n return dateObj.toLocaleDateString('en-GB', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n } catch {\n return '';\n }\n}\n\n/**\n * Format a UTC date for table display (compact format with timezone)\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @param timezone - IANA timezone string\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateTimeForTable('2024-01-15T10:00:00Z', 'America/New_York');\n * // \"Jan 15, 2024 05:00 (EST)\"\n * ```\n */\nexport function formatDateTimeForTable(\n utcDate: string | Date | undefined,\n timezone: string | undefined\n): string {\n return formatDateTimeForDisplay(utcDate, timezone, {\n includeTimezone: true,\n format: 'MMM dd, yyyy HH:mm'\n });\n}\n\n/**\n * Format a UTC date for map display (compact format)\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @param timezone - IANA timezone string\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateTimeForMap('2024-01-15T10:00:00Z', 'America/New_York');\n * // \"Jan 15, 05:00 EST\"\n * ```\n */\nexport function formatDateTimeForMap(\n utcDate: string | Date | undefined,\n timezone: string | undefined\n): string {\n if (!utcDate || !timezone) {\n return '';\n }\n\n try {\n let dateObj: Date;\n if (typeof utcDate === 'string') {\n dateObj = parseISO(utcDate);\n } else {\n dateObj = utcDate;\n }\n\n if (!isValid(dateObj)) {\n return '';\n }\n\n const formatted = formatInTimeZone(dateObj, timezone, 'MMM dd, HH:mm');\n const tzAbbr = getTimezoneAbbreviation(dateObj, timezone);\n\n return `${formatted} ${tzAbbr}`;\n } catch {\n return '';\n }\n}\n"],"mappings":"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","names":["z"]}
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  useCan,
3
3
  useResolvedScope
4
- } from "./chunk-RA3JUFMW.js";
4
+ } from "./chunk-445GEP27.js";
5
5
  import {
6
6
  toast,
7
7
  useDataTablePerformance
8
8
  } from "./chunk-6C4YBBJM.js";
9
9
  import {
10
10
  useUnifiedAuth
11
- } from "./chunk-FUEYYMX5.js";
11
+ } from "./chunk-FXFJRTKI.js";
12
12
  import {
13
13
  cn,
14
14
  renderSafeHtml
@@ -10436,7 +10436,7 @@ function getCardClasses(variant = "default", size = "default") {
10436
10436
  };
10437
10437
  return `${baseClasses} ${variantClasses[variant]} ${sizeClasses2[size]}`;
10438
10438
  }
10439
- var Card = React18.forwardRef(({ className, variant, size, isLink, link, onClick, ...props }, ref) => {
10439
+ var CardWithNavigation = React18.forwardRef(({ className, variant, size, isLink, link, onClick, ...props }, ref) => {
10440
10440
  const navigate = useNavigate();
10441
10441
  const handleClick = (e) => {
10442
10442
  onClick?.(e);
@@ -10459,19 +10459,44 @@ var Card = React18.forwardRef(({ className, variant, size, isLink, link, onClick
10459
10459
  shouldShowLinkStyles && "cursor-pointer hover:bg-acc-200 hover:shadow-xl/30",
10460
10460
  className
10461
10461
  ),
10462
- onClick: link ? handleClick : onClick,
10463
- role: link ? "link" : void 0,
10464
- tabIndex: link ? 0 : void 0,
10465
- onKeyDown: link ? (e) => {
10462
+ onClick: handleClick,
10463
+ role: "link",
10464
+ tabIndex: 0,
10465
+ onKeyDown: (e) => {
10466
10466
  if (e.key === "Enter" || e.key === " ") {
10467
10467
  e.preventDefault();
10468
10468
  handleClick(e);
10469
10469
  }
10470
- } : void 0,
10470
+ },
10471
+ ...props
10472
+ }
10473
+ );
10474
+ });
10475
+ CardWithNavigation.displayName = "CardWithNavigation";
10476
+ var CardWithoutNavigation = React18.forwardRef(({ className, variant, size, isLink, onClick, ...props }, ref) => {
10477
+ const shouldShowLinkStyles = isLink;
10478
+ return /* @__PURE__ */ jsx23(
10479
+ "article",
10480
+ {
10481
+ ref,
10482
+ className: cn(
10483
+ "grid grid-rows-[auto_1fr_auto] min-w-0 overflow-visible",
10484
+ getCardClasses(variant, size),
10485
+ shouldShowLinkStyles && "cursor-pointer hover:bg-acc-200 hover:shadow-xl/30",
10486
+ className
10487
+ ),
10488
+ onClick,
10471
10489
  ...props
10472
10490
  }
10473
10491
  );
10474
10492
  });
10493
+ CardWithoutNavigation.displayName = "CardWithoutNavigation";
10494
+ var Card = React18.forwardRef(({ link, ...props }, ref) => {
10495
+ if (link) {
10496
+ return /* @__PURE__ */ jsx23(CardWithNavigation, { ref, link, ...props });
10497
+ }
10498
+ return /* @__PURE__ */ jsx23(CardWithoutNavigation, { ref, ...props });
10499
+ });
10475
10500
  Card.displayName = "Card";
10476
10501
  var CardHeader = React18.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx23(
10477
10502
  "header",
@@ -12728,4 +12753,4 @@ lodash/lodash.js:
12728
12753
  * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
12729
12754
  *)
12730
12755
  */
12731
- //# sourceMappingURL=chunk-QUVSNGIP.js.map
12756
+ //# sourceMappingURL=chunk-HGPQUCBC.js.map