@jmruthers/pace-core 0.5.189 → 0.5.190

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 (420) hide show
  1. package/core-usage-manifest.json +0 -4
  2. package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
  3. package/dist/{DataTable-GUFUNZ3N.js → DataTable-ON3IXISJ.js} +8 -8
  4. package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-C4uxosp6.d.ts} +83 -24
  5. package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
  6. package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-X5NXANVI.js} +4 -2
  7. package/dist/{api-YP7XD5L6.js → api-I6UCQ5S6.js} +4 -2
  8. package/dist/{chunk-DDM4CCYT.js → chunk-4QYC5L4K.js} +60 -35
  9. package/dist/chunk-4QYC5L4K.js.map +1 -0
  10. package/dist/{chunk-IM4QE42D.js → chunk-73HSNNOQ.js} +141 -326
  11. package/dist/chunk-73HSNNOQ.js.map +1 -0
  12. package/dist/{chunk-YHCN776L.js → chunk-DZWK57KZ.js} +2 -75
  13. package/dist/chunk-DZWK57KZ.js.map +1 -0
  14. package/dist/{chunk-3GOZZZYH.js → chunk-HQVPB5MZ.js} +238 -301
  15. package/dist/chunk-HQVPB5MZ.js.map +1 -0
  16. package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
  17. package/dist/chunk-HW3OVDUF.js.map +1 -0
  18. package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
  19. package/dist/chunk-I7PSE6JW.js.map +1 -0
  20. package/dist/{chunk-VGZZXKBR.js → chunk-J2XXC7R5.js} +280 -52
  21. package/dist/chunk-J2XXC7R5.js.map +1 -0
  22. package/dist/{chunk-UCQSRW7Z.js → chunk-NIU6J6OX.js} +425 -378
  23. package/dist/chunk-NIU6J6OX.js.map +1 -0
  24. package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
  25. package/dist/{chunk-HEHYGYOX.js → chunk-RUYZKXOD.js} +401 -46
  26. package/dist/chunk-RUYZKXOD.js.map +1 -0
  27. package/dist/{chunk-2UUZZJFT.js → chunk-SDMHPX3X.js} +176 -160
  28. package/dist/{chunk-2UUZZJFT.js.map → chunk-SDMHPX3X.js.map} +1 -1
  29. package/dist/{chunk-MX64ZF6I.js → chunk-STYK4OH2.js} +11 -11
  30. package/dist/chunk-STYK4OH2.js.map +1 -0
  31. package/dist/{chunk-YGPFYGA6.js → chunk-VVBAW5A5.js} +822 -498
  32. package/dist/chunk-VVBAW5A5.js.map +1 -0
  33. package/dist/chunk-Y4BUBBHD.js +614 -0
  34. package/dist/chunk-Y4BUBBHD.js.map +1 -0
  35. package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
  36. package/dist/chunk-ZSAAAMVR.js.map +1 -0
  37. package/dist/components.d.ts +3 -4
  38. package/dist/components.js +19 -19
  39. package/dist/components.js.map +1 -1
  40. package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
  41. package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
  42. package/dist/hooks.d.ts +10 -5
  43. package/dist/hooks.js +14 -8
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +13 -11
  46. package/dist/index.js +79 -69
  47. package/dist/index.js.map +1 -1
  48. package/dist/providers.d.ts +3 -3
  49. package/dist/providers.js +3 -1
  50. package/dist/rbac/index.d.ts +76 -12
  51. package/dist/rbac/index.js +12 -9
  52. package/dist/types.d.ts +1 -1
  53. package/dist/types.js +1 -1
  54. package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-DxIDS4bC.d.ts} +16 -9
  55. package/dist/utils.js +16 -16
  56. package/docs/README.md +2 -2
  57. package/docs/api/classes/ColumnFactory.md +1 -1
  58. package/docs/api/classes/ErrorBoundary.md +1 -1
  59. package/docs/api/classes/InvalidScopeError.md +2 -2
  60. package/docs/api/classes/Logger.md +1 -1
  61. package/docs/api/classes/MissingUserContextError.md +2 -2
  62. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  63. package/docs/api/classes/PermissionDeniedError.md +1 -1
  64. package/docs/api/classes/RBACAuditManager.md +1 -1
  65. package/docs/api/classes/RBACCache.md +1 -1
  66. package/docs/api/classes/RBACEngine.md +4 -4
  67. package/docs/api/classes/RBACError.md +1 -1
  68. package/docs/api/classes/RBACNotInitializedError.md +2 -2
  69. package/docs/api/classes/SecureSupabaseClient.md +21 -16
  70. package/docs/api/classes/StorageUtils.md +7 -4
  71. package/docs/api/enums/FileCategory.md +1 -1
  72. package/docs/api/enums/LogLevel.md +1 -1
  73. package/docs/api/enums/RBACErrorCode.md +1 -1
  74. package/docs/api/enums/RPCFunction.md +1 -1
  75. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  76. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  77. package/docs/api/interfaces/AggregateConfig.md +1 -1
  78. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  79. package/docs/api/interfaces/AvatarProps.md +1 -1
  80. package/docs/api/interfaces/BadgeProps.md +1 -1
  81. package/docs/api/interfaces/ButtonProps.md +1 -1
  82. package/docs/api/interfaces/CalendarProps.md +20 -6
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/ComplianceResult.md +1 -1
  87. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  88. package/docs/api/interfaces/DataRecord.md +1 -1
  89. package/docs/api/interfaces/DataTableAction.md +1 -1
  90. package/docs/api/interfaces/DataTableColumn.md +1 -1
  91. package/docs/api/interfaces/DataTableProps.md +1 -1
  92. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  93. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  94. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  95. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  96. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  97. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  98. package/docs/api/interfaces/ExportColumn.md +1 -1
  99. package/docs/api/interfaces/ExportOptions.md +1 -1
  100. package/docs/api/interfaces/FileDisplayProps.md +62 -16
  101. package/docs/api/interfaces/FileMetadata.md +1 -1
  102. package/docs/api/interfaces/FileReference.md +2 -2
  103. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  104. package/docs/api/interfaces/FileUploadOptions.md +26 -12
  105. package/docs/api/interfaces/FileUploadProps.md +30 -19
  106. package/docs/api/interfaces/FooterProps.md +1 -1
  107. package/docs/api/interfaces/FormFieldProps.md +1 -1
  108. package/docs/api/interfaces/FormProps.md +1 -1
  109. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  110. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  111. package/docs/api/interfaces/InputProps.md +1 -1
  112. package/docs/api/interfaces/LabelProps.md +1 -1
  113. package/docs/api/interfaces/LoggerConfig.md +1 -1
  114. package/docs/api/interfaces/LoginFormProps.md +1 -1
  115. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  116. package/docs/api/interfaces/NavigationContextType.md +9 -9
  117. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  118. package/docs/api/interfaces/NavigationItem.md +1 -1
  119. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  120. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  121. package/docs/api/interfaces/Organisation.md +1 -1
  122. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  123. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  124. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  125. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  126. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  127. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  128. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  129. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  130. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  131. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  132. package/docs/api/interfaces/PaletteData.md +1 -1
  133. package/docs/api/interfaces/ParsedAddress.md +1 -1
  134. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  135. package/docs/api/interfaces/ProgressProps.md +3 -11
  136. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  137. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  138. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  139. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  140. package/docs/api/interfaces/QuickFix.md +1 -1
  141. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  142. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  143. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  144. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  145. package/docs/api/interfaces/RBACConfig.md +1 -1
  146. package/docs/api/interfaces/RBACContext.md +1 -1
  147. package/docs/api/interfaces/RBACLogger.md +1 -1
  148. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  149. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  150. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  151. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  152. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  153. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  154. package/docs/api/interfaces/RBACResult.md +1 -1
  155. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  156. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  157. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  158. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  160. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  161. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  162. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  163. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  164. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  165. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  166. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  167. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  168. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  169. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  170. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  171. package/docs/api/interfaces/RouteConfig.md +10 -10
  172. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  173. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  174. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  175. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  176. package/docs/api/interfaces/SetupIssue.md +1 -1
  177. package/docs/api/interfaces/StorageConfig.md +4 -4
  178. package/docs/api/interfaces/StorageFileInfo.md +7 -7
  179. package/docs/api/interfaces/StorageFileMetadata.md +25 -14
  180. package/docs/api/interfaces/StorageListOptions.md +22 -9
  181. package/docs/api/interfaces/StorageListResult.md +4 -4
  182. package/docs/api/interfaces/StorageUploadOptions.md +21 -8
  183. package/docs/api/interfaces/StorageUploadResult.md +6 -6
  184. package/docs/api/interfaces/StorageUrlOptions.md +19 -6
  185. package/docs/api/interfaces/StyleImport.md +1 -1
  186. package/docs/api/interfaces/SwitchProps.md +1 -1
  187. package/docs/api/interfaces/TabsContentProps.md +1 -1
  188. package/docs/api/interfaces/TabsListProps.md +1 -1
  189. package/docs/api/interfaces/TabsProps.md +1 -1
  190. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  191. package/docs/api/interfaces/TextareaProps.md +1 -1
  192. package/docs/api/interfaces/ToastActionElement.md +1 -1
  193. package/docs/api/interfaces/ToastProps.md +1 -1
  194. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  195. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  196. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  197. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  198. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  199. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  200. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  201. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  202. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  203. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  205. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  207. package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
  208. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  209. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  210. package/docs/api/interfaces/UserEventAccess.md +11 -11
  211. package/docs/api/interfaces/UserMenuProps.md +1 -1
  212. package/docs/api/interfaces/UserProfile.md +1 -1
  213. package/docs/api/modules.md +151 -92
  214. package/docs/api-reference/components.md +15 -7
  215. package/docs/api-reference/providers.md +2 -2
  216. package/docs/api-reference/rpc-functions.md +1 -0
  217. package/docs/best-practices/README.md +1 -1
  218. package/docs/best-practices/deployment.md +8 -8
  219. package/docs/getting-started/examples/README.md +2 -2
  220. package/docs/getting-started/installation-guide.md +4 -4
  221. package/docs/getting-started/quick-start.md +3 -3
  222. package/docs/migration/MIGRATION_GUIDE.md +3 -3
  223. package/docs/rbac/compliance/compliance-guide.md +2 -2
  224. package/docs/rbac/event-based-apps.md +2 -2
  225. package/docs/rbac/getting-started.md +2 -2
  226. package/docs/rbac/quick-start.md +2 -2
  227. package/docs/security/README.md +4 -4
  228. package/docs/standards/07-rbac-and-rls-standard.md +430 -7
  229. package/docs/troubleshooting/README.md +2 -2
  230. package/docs/troubleshooting/migration.md +3 -3
  231. package/package.json +1 -3
  232. package/scripts/check-pace-core-compliance.cjs +1 -1
  233. package/scripts/check-pace-core-compliance.js +1 -1
  234. package/src/__tests__/fixtures/supabase.ts +301 -0
  235. package/src/__tests__/public-recipe-view.test.ts +9 -9
  236. package/src/__tests__/rls-policies.test.ts +197 -61
  237. package/src/components/AddressField/AddressField.test.tsx +42 -0
  238. package/src/components/AddressField/AddressField.tsx +71 -60
  239. package/src/components/AddressField/README.md +1 -0
  240. package/src/components/Alert/Alert.test.tsx +50 -10
  241. package/src/components/Alert/Alert.tsx +5 -3
  242. package/src/components/Avatar/Avatar.test.tsx +95 -43
  243. package/src/components/Avatar/Avatar.tsx +16 -16
  244. package/src/components/Button/Button.test.tsx +2 -1
  245. package/src/components/Button/Button.tsx +3 -3
  246. package/src/components/Calendar/Calendar.test.tsx +53 -37
  247. package/src/components/Calendar/Calendar.tsx +409 -82
  248. package/src/components/Card/Card.test.tsx +7 -4
  249. package/src/components/Card/Card.tsx +3 -6
  250. package/src/components/Checkbox/Checkbox.tsx +2 -2
  251. package/src/components/DataTable/components/ActionButtons.tsx +5 -5
  252. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  253. package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
  254. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
  255. package/src/components/DataTable/components/DataTableBody.tsx +12 -12
  256. package/src/components/DataTable/components/DataTableCore.tsx +3 -3
  257. package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
  258. package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
  259. package/src/components/DataTable/components/EditableRow.tsx +2 -2
  260. package/src/components/DataTable/components/EmptyState.tsx +3 -3
  261. package/src/components/DataTable/components/GroupHeader.tsx +2 -2
  262. package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +4 -4
  264. package/src/components/DataTable/components/LoadingState.tsx +1 -1
  265. package/src/components/DataTable/components/PaginationControls.tsx +11 -11
  266. package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
  267. package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
  268. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
  269. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
  270. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
  271. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
  272. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
  273. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
  274. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
  275. package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
  276. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
  277. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
  278. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
  280. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
  281. package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
  282. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
  283. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
  284. package/src/components/Dialog/Dialog.tsx +2 -2
  285. package/src/components/EventSelector/EventSelector.tsx +7 -7
  286. package/src/components/FileDisplay/FileDisplay.tsx +291 -179
  287. package/src/components/FileUpload/FileUpload.tsx +7 -4
  288. package/src/components/Header/Header.test.tsx +28 -0
  289. package/src/components/Header/Header.tsx +22 -9
  290. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
  291. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
  292. package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
  293. package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
  294. package/src/components/OrganisationSelector/OrganisationSelector.tsx +8 -8
  295. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
  296. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
  297. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
  298. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
  299. package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
  300. package/src/components/PaceAppLayout/test-setup.tsx +1 -0
  301. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
  302. package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
  303. package/src/components/Progress/Progress.test.tsx +18 -19
  304. package/src/components/Progress/Progress.tsx +31 -32
  305. package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
  306. package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
  307. package/src/components/Select/Select.tsx +5 -5
  308. package/src/components/Switch/Switch.test.tsx +2 -1
  309. package/src/components/Switch/Switch.tsx +1 -1
  310. package/src/components/Toast/Toast.tsx +1 -1
  311. package/src/components/Tooltip/Tooltip.test.tsx +8 -2
  312. package/src/components/UserMenu/UserMenu.tsx +3 -3
  313. package/src/eslint-rules/pace-core-compliance.cjs +0 -2
  314. package/src/eslint-rules/pace-core-compliance.js +0 -2
  315. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
  316. package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
  317. package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
  318. package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
  319. package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
  320. package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
  321. package/src/hooks/__tests__/{usePublicEvent.unit.test.ts → usePublicEvent.test.ts} +28 -1
  322. package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
  323. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +58 -16
  324. package/src/hooks/index.ts +1 -1
  325. package/src/hooks/public/usePublicEvent.ts +2 -2
  326. package/src/hooks/public/usePublicFileDisplay.ts +173 -87
  327. package/src/hooks/useAppConfig.ts +24 -5
  328. package/src/hooks/useFileDisplay.ts +297 -34
  329. package/src/hooks/useFileReference.ts +56 -11
  330. package/src/hooks/useFileUrl.ts +1 -1
  331. package/src/hooks/useInactivityTracker.ts +16 -7
  332. package/src/hooks/usePermissionCache.test.ts +85 -8
  333. package/src/hooks/useQueryCache.ts +21 -0
  334. package/src/hooks/useSecureDataAccess.test.ts +80 -35
  335. package/src/hooks/useSecureDataAccess.ts +80 -37
  336. package/src/providers/services/EventServiceProvider.tsx +37 -17
  337. package/src/providers/services/InactivityServiceProvider.tsx +4 -4
  338. package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
  339. package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
  340. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
  341. package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
  342. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
  343. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
  344. package/src/rbac/api.ts +240 -36
  345. package/src/rbac/cache-invalidation.ts +21 -7
  346. package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
  347. package/src/rbac/components/NavigationGuard.tsx +23 -63
  348. package/src/rbac/components/NavigationProvider.test.tsx +52 -23
  349. package/src/rbac/components/NavigationProvider.tsx +13 -11
  350. package/src/rbac/components/PagePermissionGuard.tsx +77 -203
  351. package/src/rbac/components/PagePermissionProvider.tsx +13 -11
  352. package/src/rbac/components/PermissionEnforcer.tsx +24 -62
  353. package/src/rbac/components/RoleBasedRouter.tsx +14 -12
  354. package/src/rbac/components/SecureDataProvider.tsx +13 -11
  355. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
  356. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
  357. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
  358. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
  359. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
  360. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
  361. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
  362. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
  363. package/src/rbac/engine.ts +4 -2
  364. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
  365. package/src/rbac/hooks/index.ts +3 -0
  366. package/src/rbac/hooks/useCan.test.ts +101 -53
  367. package/src/rbac/hooks/usePermissions.ts +108 -41
  368. package/src/rbac/hooks/useRBAC.test.ts +11 -3
  369. package/src/rbac/hooks/useRBAC.ts +83 -40
  370. package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
  371. package/src/rbac/hooks/useResolvedScope.ts +128 -70
  372. package/src/rbac/hooks/useSecureSupabase.ts +36 -19
  373. package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
  374. package/src/rbac/request-deduplication.ts +1 -1
  375. package/src/rbac/secureClient.ts +72 -12
  376. package/src/rbac/security.ts +29 -23
  377. package/src/rbac/types.ts +10 -0
  378. package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
  379. package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
  380. package/src/rbac/utils/__tests__/eventContext.test.ts +6 -1
  381. package/src/rbac/utils/contextValidator.ts +288 -0
  382. package/src/rbac/utils/eventContext.ts +48 -2
  383. package/src/services/EventService.ts +165 -21
  384. package/src/services/OrganisationService.ts +37 -2
  385. package/src/services/__tests__/EventService.test.ts +26 -21
  386. package/src/types/file-reference.ts +13 -10
  387. package/src/utils/app/appNameResolver.test.ts +346 -73
  388. package/src/utils/context/superAdminOverride.ts +58 -0
  389. package/src/utils/file-reference/index.ts +61 -33
  390. package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
  391. package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
  392. package/src/utils/storage/helpers.test.ts +1 -1
  393. package/src/utils/storage/helpers.ts +38 -19
  394. package/src/utils/storage/types.ts +15 -8
  395. package/src/utils/validation/__tests__/csrf.test.ts +105 -0
  396. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
  397. package/src/vite-env.d.ts +2 -2
  398. package/dist/chunk-3GOZZZYH.js.map +0 -1
  399. package/dist/chunk-DDM4CCYT.js.map +0 -1
  400. package/dist/chunk-E7UAOUMY.js +0 -75
  401. package/dist/chunk-E7UAOUMY.js.map +0 -1
  402. package/dist/chunk-F2IMUDXZ.js.map +0 -1
  403. package/dist/chunk-HEHYGYOX.js.map +0 -1
  404. package/dist/chunk-IM4QE42D.js.map +0 -1
  405. package/dist/chunk-MX64ZF6I.js.map +0 -1
  406. package/dist/chunk-SAUPYVLF.js.map +0 -1
  407. package/dist/chunk-THRPYOFK.js.map +0 -1
  408. package/dist/chunk-UCQSRW7Z.js.map +0 -1
  409. package/dist/chunk-VGZZXKBR.js.map +0 -1
  410. package/dist/chunk-YGPFYGA6.js.map +0 -1
  411. package/dist/chunk-YHCN776L.js.map +0 -1
  412. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  413. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  414. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -703
  415. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  416. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -428
  417. /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-ON3IXISJ.js.map} +0 -0
  418. /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-X5NXANVI.js.map} +0 -0
  419. /package/dist/{api-YP7XD5L6.js.map → api-I6UCQ5S6.js.map} +0 -0
  420. /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
@@ -10,7 +10,7 @@
10
10
  * Features:
11
11
  * - Single, range, and multiple date selection modes
12
12
  * - Date disabling (past dates, weekends, etc.)
13
- * - Localization support
13
+ * - Localization support (defaults to Australian locale with Monday as first day of week)
14
14
  * - Keyboard navigation
15
15
  * - Accessible date selection
16
16
  * - Customizable styling
@@ -56,22 +56,53 @@
56
56
  */
57
57
 
58
58
  import * as React from 'react';
59
- import { DayPicker, type DayPickerProps } from 'react-day-picker';
59
+ import {
60
+ DayPicker,
61
+ type DayPickerProps,
62
+ useDayPicker,
63
+ type DayProps,
64
+ type MonthsProps,
65
+ type DateRange,
66
+ } from 'react-day-picker';
67
+ import { enAU } from 'date-fns/locale';
60
68
  import { cn } from '../../utils/core/cn';
61
69
 
70
+ // Define custom types for components that don't have exported types
71
+ type MonthGridProps = React.TableHTMLAttributes<HTMLTableElement>;
72
+ type WeekdaysProps = React.HTMLAttributes<HTMLTableRowElement> & {
73
+ children?: React.ReactNode;
74
+ };
75
+ type MonthProps = {
76
+ calendarMonth: { date: Date };
77
+ displayIndex: number;
78
+ className?: string;
79
+ children?: React.ReactNode;
80
+ };
81
+ type RootProps = {
82
+ children?: React.ReactNode;
83
+ rootRef?: React.Ref<HTMLDivElement>;
84
+ className?: string;
85
+ style?: React.CSSProperties;
86
+ };
87
+
62
88
  // ============================================================================
63
89
  // CALENDAR COMPONENT
64
90
  // ============================================================================
65
91
 
66
92
  export interface CalendarProps extends Omit<DayPickerProps, 'className' | 'classNames' | 'styles' | 'onSelect'> {
67
93
  /**
68
- * Additional CSS classes to apply to the calendar wrapper
94
+ * Additional CSS classes to apply to the calendar table
69
95
  */
70
96
  className?: string;
71
97
  /**
72
98
  * Custom classNames for DayPicker sub-components
73
99
  */
74
100
  classNames?: DayPickerProps['classNames'];
101
+ /**
102
+ * Currently selected value; mirrors DayPicker's `selected` prop so callers
103
+ * can control the selection state.
104
+ */
105
+ selected?: Date | Date[] | DateRange | undefined;
75
106
  /**
76
107
  * Date selection handler. Signature depends on mode:
77
108
  * - mode="single": (date: Date | undefined) => void
@@ -81,18 +112,45 @@ export interface CalendarProps extends Omit<DayPickerProps, 'className' | 'class
81
112
  onSelect?: ((date: Date | undefined) => void) | ((range: { from: Date; to?: Date } | undefined) => void) | ((dates: Date[]) => void);
82
113
  }
83
114
 
115
+ type StoredRootProps = {
116
+ className?: string;
117
+ style?: React.CSSProperties;
118
+ rootRef?: React.Ref<HTMLDivElement>;
119
+ restProps?: React.HTMLAttributes<HTMLTableElement>;
120
+ };
121
+
122
+ const assignToRef = <T,>(ref: React.Ref<T | null> | undefined, value: T | null) => {
123
+ if (!ref) {
124
+ return;
125
+ }
126
+ if (typeof ref === 'function') {
127
+ ref(value);
128
+ return;
129
+ }
130
+ (ref as React.MutableRefObject<T | null>).current = value;
131
+ };
132
+
84
133
  /**
85
134
  * Calendar component
86
135
  * A flexible, accessible calendar component for date selection.
87
136
  * Built on react-day-picker with pace-core styling.
88
137
  *
138
+ * Defaults to Australian locale (en-AU) with Monday as the first day of the week.
139
+ * The locale can be overridden by passing a `locale` prop.
140
+ *
141
+ * Month navigation is automatically managed internally when `month` and `onMonthChange` props
142
+ * are not provided. The displayed month will sync with the selected date when available,
143
+ * or default to the current month. Navigation buttons (prev/next) work automatically.
144
+ *
145
+ * For controlled month state, pass `month` and `onMonthChange` props.
146
+ *
89
147
  * @param props - Calendar configuration and styling
90
148
  * @param ref - Forwarded ref (not used directly, but maintained for API consistency)
91
149
  * @returns JSX.Element - The rendered calendar element
92
150
  *
93
151
  * @example
94
152
  * ```tsx
95
- * // Single date selection
153
+ * // Single date selection (uses default Australian locale)
96
154
  * <Calendar
97
155
  * mode="single"
98
156
  * selected={date}
@@ -106,88 +164,357 @@ export interface CalendarProps extends Omit<DayPickerProps, 'className' | 'class
106
164
  * onSelect={setDate}
107
165
  * disabled={(date) => date < new Date()}
108
166
  * />
167
+ *
168
+ * // Override locale (e.g., US locale with Sunday as first day)
169
+ * import { enUS } from 'date-fns/locale';
170
+ * <Calendar
171
+ * mode="single"
172
+ * selected={date}
173
+ * onSelect={setDate}
174
+ * locale={enUS}
175
+ * />
176
+ *
177
+ * // Controlled month state (optional - month is auto-managed if not provided)
178
+ * const [month, setMonth] = useState(new Date());
179
+ * <Calendar
180
+ * mode="single"
181
+ * selected={date}
182
+ * onSelect={setDate}
183
+ * month={month}
184
+ * onMonthChange={setMonth}
185
+ * />
109
186
  * ```
110
187
  */
111
- const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(
112
- ({ className, classNames, mode, components, ...props }, ref) => {
113
- const defaultComponents = {
114
- IconLeft: ({ ...iconProps }) => (
115
- <svg
116
- xmlns="http://www.w3.org/2000/svg"
117
- viewBox="0 0 24 24"
118
- fill="none"
119
- stroke="currentColor"
120
- strokeWidth="2"
121
- strokeLinecap="round"
122
- strokeLinejoin="round"
123
- className="h-4 w-4"
124
- {...iconProps}
125
- >
126
- <path d="m15 18-6-6 6-6" />
127
- </svg>
128
- ),
129
- IconRight: ({ ...iconProps }) => (
130
- <svg
131
- xmlns="http://www.w3.org/2000/svg"
132
- viewBox="0 0 24 24"
133
- fill="none"
134
- stroke="currentColor"
135
- strokeWidth="2"
136
- strokeLinecap="round"
137
- strokeLinejoin="round"
138
- className="h-4 w-4"
139
- {...iconProps}
140
- >
141
- <path d="m9 18 6-6-6-6" />
142
- </svg>
143
- ),
144
- ...components,
145
- };
188
+ const Calendar = React.forwardRef<HTMLTableElement, CalendarProps>(
189
+ ({ className, classNames, mode, components, locale, month: controlledMonth, onMonthChange: controlledOnMonthChange, onSelect, ...props }, ref) => {
190
+ const tableRef = React.useRef<HTMLTableElement | null>(null);
191
+ const setForwardedRef = React.useCallback(
192
+ (node: HTMLTableElement | null) => {
193
+ tableRef.current = node;
194
+ if (!ref) {
195
+ return;
196
+ }
197
+ if (typeof ref === 'function') {
198
+ ref(node);
199
+ } else {
200
+ (ref as React.MutableRefObject<HTMLTableElement | null>).current = node;
201
+ }
202
+ },
203
+ [ref]
204
+ );
205
+
206
+ // Store root props so we can re-apply them to the table element
207
+ const rootPropsRef = React.useRef<StoredRootProps | null>(null);
208
+ // Get selected from props to avoid TypeScript union type issues
209
+ const selected = (props as any).selected;
210
+ // Determine if we're in controlled or uncontrolled mode for month
211
+ const isMonthControlled = controlledMonth !== undefined;
212
+
213
+ // Internal state for uncontrolled month mode - always default to current month
214
+ const [internalMonth, setInternalMonth] = React.useState<Date>(() => {
215
+ const now = new Date();
216
+ return new Date(now.getFullYear(), now.getMonth(), 1);
217
+ });
218
+
219
+ // Use controlled month if provided, otherwise use internal state
220
+ // Normalize month to first day of month to ensure consistency
221
+ const month = React.useMemo(() => {
222
+ const monthToUse = isMonthControlled ? controlledMonth : internalMonth;
223
+ if (!monthToUse) {
224
+ const now = new Date();
225
+ return new Date(now.getFullYear(), now.getMonth(), 1);
226
+ }
227
+ // Ensure month is normalized to first day
228
+ const normalized = new Date(monthToUse.getFullYear(), monthToUse.getMonth(), 1);
229
+ return normalized;
230
+ }, [isMonthControlled, controlledMonth, internalMonth]);
231
+
232
+ // Handler for month changes - must be stable reference
233
+ const handleMonthChange = React.useCallback((newMonth: Date) => {
234
+ if (!isMonthControlled) {
235
+ setInternalMonth(newMonth);
236
+ }
237
+ controlledOnMonthChange?.(newMonth);
238
+ }, [isMonthControlled, controlledOnMonthChange]);
239
+
240
+ // Create a wrapped handler to ensure it's always a function
241
+ const wrappedHandleMonthChange = React.useCallback((newMonth: Date) => {
242
+ handleMonthChange(newMonth);
243
+ }, [handleMonthChange]);
244
+
245
+ // Removed mount logging - was for debugging only
246
+
247
+ // Custom Root: capture provided props so we can attach them to the table directly
248
+ const CustomRoot = React.memo(({ children, rootRef: dayPickerRootRef, ...rootProps }: RootProps) => {
249
+ const {
250
+ className: rootClassName,
251
+ style: rootStyle,
252
+ children: _ignoredChildren,
253
+ ...restProps
254
+ } = rootProps as React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode };
255
+
256
+ rootPropsRef.current = {
257
+ className: rootClassName,
258
+ style: rootStyle as React.CSSProperties | undefined,
259
+ rootRef: dayPickerRootRef,
260
+ restProps: restProps as React.HTMLAttributes<HTMLTableElement>,
261
+ };
262
+ return <>{children}</>;
263
+ });
264
+ CustomRoot.displayName = 'CustomRoot';
265
+
266
+ // Custom Months: Remove wrapper div, return children directly
267
+ const CustomMonths = React.memo(({ children }: MonthsProps) => {
268
+ return <>{children}</>;
269
+ });
270
+ CustomMonths.displayName = 'CustomMonths';
271
+
272
+ const CustomMonthGrid = React.forwardRef<HTMLTableElement, MonthGridProps>((props, forwardedRef) => {
273
+ return <table ref={forwardedRef} {...props} />;
274
+ });
275
+ CustomMonthGrid.displayName = 'CustomMonthGrid';
276
+
277
+ // Custom Month: inject caption + navigation directly inside the <table>
278
+ const CustomMonth = React.memo(({ calendarMonth, displayIndex, className, children }: MonthProps) => {
279
+ const { formatters, components, labels, classNames, previousMonth, nextMonth, goToMonth } = useDayPicker();
280
+ const caption = formatters.formatCaption(calendarMonth.date, {});
281
+ const Chevron = components?.Chevron;
282
+
283
+ const childrenArray = React.Children.toArray(children);
284
+ const monthGridIndex = childrenArray.findIndex((child: any) => {
285
+ if (!React.isValidElement(child)) return false;
286
+ const childType = child.type as any;
287
+ return (typeof childType === 'function' && childType.displayName === 'MonthGrid') ||
288
+ child.type === 'table';
289
+ });
290
+
291
+ return (
292
+ <>
293
+ {childrenArray.map((child, index) => {
294
+ if (React.isValidElement(child) && (child.type as any)?.displayName === 'MonthCaption') {
295
+ return null;
296
+ }
297
+
298
+ if (index === monthGridIndex && React.isValidElement(child)) {
299
+ const monthGridChild = child as React.ReactElement<MonthGridProps>;
300
+ const applyRootProps = displayIndex === 0 && index === monthGridIndex;
301
+ const storedRootProps = applyRootProps ? rootPropsRef.current : null;
302
+
303
+ const { children: monthGridChildren, className: monthGridClassName, style: monthGridStyle, ...monthGridRest } =
304
+ monthGridChild.props as React.TableHTMLAttributes<HTMLTableElement>;
305
+
306
+ const mergedClassName = cn(
307
+ 'w-full border-collapse rounded-md border border-sec-200 bg-background',
308
+ applyRootProps ? storedRootProps?.className : undefined,
309
+ className,
310
+ monthGridClassName
311
+ );
312
+
313
+ const mergedStyle = {
314
+ ...(applyRootProps ? storedRootProps?.style ?? {} : {}),
315
+ ...(monthGridStyle ?? {}),
316
+ };
317
+
318
+ const tableProps: React.TableHTMLAttributes<HTMLTableElement> = {
319
+ ...(applyRootProps && storedRootProps?.restProps ? storedRootProps.restProps : {}),
320
+ ...monthGridRest,
321
+ className: mergedClassName,
322
+ ...(Object.keys(mergedStyle).length ? { style: mergedStyle } : {}),
323
+ };
324
+
325
+ if (mode && (tableProps as Record<string, unknown>)['data-mode'] === undefined) {
326
+ (tableProps as Record<string, unknown>)['data-mode'] = mode;
327
+ }
328
+
329
+ const shouldAttachRef = applyRootProps || storedRootProps?.rootRef;
330
+ const handleTableRef = shouldAttachRef
331
+ ? (node: HTMLTableElement | null) => {
332
+ if (applyRootProps) {
333
+ setForwardedRef(node);
334
+ }
335
+ if (storedRootProps?.rootRef) {
336
+ assignToRef(storedRootProps.rootRef as React.Ref<HTMLTableElement | null>, node);
337
+ }
338
+ }
339
+ : undefined;
340
+
341
+ const handlePreviousClick = (event: React.MouseEvent<HTMLButtonElement>) => {
342
+ event.preventDefault();
343
+ if (!previousMonth) return;
344
+ goToMonth(previousMonth);
345
+ };
346
+
347
+ const handleNextClick = (event: React.MouseEvent<HTMLButtonElement>) => {
348
+ event.preventDefault();
349
+ if (!nextMonth) return;
350
+ goToMonth(nextMonth);
351
+ };
352
+
353
+ const monthGridElement = monthGridChild as React.ReactElement & {
354
+ ref?: React.Ref<HTMLTableElement | null>;
355
+ };
356
+ const mergedRef =
357
+ handleTableRef || monthGridElement.ref
358
+ ? (node: HTMLTableElement | null) => {
359
+ if (handleTableRef) {
360
+ handleTableRef(node);
361
+ }
362
+ assignToRef(monthGridElement.ref as React.Ref<HTMLTableElement | null> | undefined, node);
363
+ }
364
+ : undefined;
365
+
366
+ return React.cloneElement(
367
+ monthGridElement,
368
+ {
369
+ key: child.key ?? `month-grid-${displayIndex}`,
370
+ ...tableProps,
371
+ ...(mergedRef ? { ref: mergedRef } : {}),
372
+ },
373
+ <>
374
+ <caption className="relative">
375
+ <nav className="relative flex items-center justify-center gap-1">
376
+ <button
377
+ type="button"
378
+ className={cn(
379
+ 'h-7 w-7 bg-transparent p-0',
380
+ 'inline-flex items-center justify-center rounded-md',
381
+ 'hover:bg-acc-100',
382
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main-600 focus-visible:ring-offset-2',
383
+ 'disabled:opacity-50 disabled:pointer-events-none',
384
+ classNames?.button_previous
385
+ )}
386
+ tabIndex={previousMonth ? undefined : -1}
387
+ aria-disabled={previousMonth ? undefined : true}
388
+ aria-label={previousMonth ? labels.labelPrevious(previousMonth) : undefined}
389
+ onClick={handlePreviousClick}
390
+ disabled={!previousMonth}
391
+ >
392
+ {Chevron ? <Chevron orientation="left" className="size-4" disabled={!previousMonth} /> : <span>‹</span>}
393
+ </button>
394
+ <span className="text-sm font-medium">{caption}</span>
395
+ <button
396
+ type="button"
397
+ className={cn(
398
+ 'h-7 w-7 bg-transparent p-0',
399
+ 'inline-flex items-center justify-center rounded-md',
400
+ 'hover:bg-acc-100',
401
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main-600 focus-visible:ring-offset-2',
402
+ 'disabled:opacity-50 disabled:pointer-events-none',
403
+ classNames?.button_next
404
+ )}
405
+ tabIndex={nextMonth ? undefined : -1}
406
+ aria-disabled={nextMonth ? undefined : true}
407
+ aria-label={nextMonth ? labels.labelNext(nextMonth) : undefined}
408
+ onClick={handleNextClick}
409
+ disabled={!nextMonth}
410
+ >
411
+ {Chevron ? <Chevron orientation="right" className="size-4" disabled={!nextMonth} /> : <span>›</span>}
412
+ </button>
413
+ </nav>
414
+ </caption>
415
+ {monthGridChildren}
416
+ </>
417
+ );
418
+ }
419
+
420
+ return child;
421
+ })}
422
+ </>
423
+ );
424
+ });
425
+ CustomMonth.displayName = 'CustomMonth';
426
+
427
+ // Custom Weekdays: render table head with semantic row
428
+ const CustomWeekdays = React.memo(({ className, children, ...props }: WeekdaysProps) => {
429
+ return (
430
+ <thead>
431
+ <tr className={cn('text-xs text-sec-500', className)} {...props}>
432
+ {children}
433
+ </tr>
434
+ </thead>
435
+ );
436
+ });
437
+ CustomWeekdays.displayName = 'CustomWeekdays';
438
+
439
+ // Memoize components to ensure stable references
440
+ const defaultComponents = React.useMemo(() => ({
441
+ Root: CustomRoot,
442
+ Months: CustomMonths,
443
+ Month: CustomMonth,
444
+ MonthGrid: CustomMonthGrid,
445
+ // MonthCaption is now handled inside CustomMonth (injected into table)
446
+ Weekdays: CustomWeekdays,
447
+ // Spread user components AFTER ours so ours take precedence
448
+ ...(components || {}),
449
+ }), [components, CustomRoot, CustomMonths, CustomMonth, CustomWeekdays]);
146
450
 
147
451
  return (
148
- <div ref={ref} className={cn('p-3', className)}>
149
- {/* Type assertion needed due to TypeScript limitation with discriminated unions when mode is optional */}
150
- <DayPicker
151
- {...(mode ? { mode } : {})}
152
- {...(props as any)}
153
- className="rounded-md border border-sec-200 bg-background"
154
- classNames={{
155
- months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
156
- month: 'space-y-4',
157
- caption: 'flex justify-center pt-1 relative items-center',
158
- caption_label: 'text-sm font-medium',
159
- nav: 'space-x-1 flex items-center',
160
- nav_button: cn(
161
- 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
162
- 'border border-input hover:bg-acc-100',
163
- 'inline-flex items-center justify-center rounded-md',
164
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main-600 focus-visible:ring-offset-2'
165
- ),
166
- nav_button_previous: 'absolute left-1',
167
- nav_button_next: 'absolute right-1',
168
- table: 'w-full border-collapse space-y-1',
169
- head_row: 'flex',
170
- head_cell: 'text-sec-600 rounded-md w-9 font-normal text-[0.8rem]',
171
- row: 'flex w-full mt-2',
172
- cell: 'h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-acc-50/50 [&:has([aria-selected])]:bg-acc-100 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
173
- day: cn(
174
- 'h-9 w-9 p-0 font-normal aria-selected:opacity-100',
175
- 'hover:bg-acc-100 hover:text-main-600',
176
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main-600 focus-visible:ring-offset-2',
177
- 'inline-flex items-center justify-center rounded-md'
178
- ),
179
- day_range_end: 'day-range-end',
180
- day_selected: 'bg-main-600 text-main-50 hover:bg-main-600 hover:text-main-50 focus:bg-main-600 focus:text-main-50',
181
- day_today: 'bg-sec-100 text-main-600 font-semibold',
182
- day_outside: 'day-outside text-sec-400 opacity-50 aria-selected:bg-acc-50/50 aria-selected:text-sec-400 aria-selected:opacity-30',
183
- day_disabled: 'text-sec-400 opacity-50 cursor-not-allowed',
184
- day_range_middle: 'aria-selected:bg-acc-100 aria-selected:text-main-600',
185
- day_hidden: 'invisible',
186
- ...classNames,
187
- }}
188
- components={defaultComponents}
189
- />
190
- </div>
452
+ <DayPicker
453
+ {...(mode ? { mode } : {})}
454
+ locale={locale ?? enAU}
455
+ hideNavigation={true}
456
+ {...(props as any)}
457
+ // CRITICAL: Explicit props must come AFTER spread to ensure they override any props from spread
458
+ // This ensures month, onMonthChange, selected, and onSelect are always correct
459
+ selected={selected}
460
+ onSelect={onSelect}
461
+ month={month}
462
+ onMonthChange={wrappedHandleMonthChange}
463
+ className={className}
464
+ classNames={{
465
+ // v9 API: Updated for table-based structure
466
+ months: '', // No wrapper - removed by CustomMonths
467
+ month: '', // No wrapper - removed by CustomMonth
468
+ month_caption: '', // Now handled by custom component (renders as <caption>)
469
+ caption_label: 'text-sm font-medium',
470
+ nav: 'relative flex items-center justify-center space-x-1',
471
+ // v9: button_previous and button_next for navigation buttons
472
+ button_previous: cn(
473
+ 'absolute left-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
474
+ 'border border-input hover:bg-acc-100',
475
+ 'inline-flex items-center justify-center rounded-md',
476
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main-600 focus-visible:ring-offset-2'
477
+ ),
478
+ button_next: cn(
479
+ 'absolute right-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
480
+ 'border border-input hover:bg-acc-100',
481
+ 'inline-flex items-center justify-center rounded-md',
482
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main-600 focus-visible:ring-offset-2'
483
+ ),
484
+ // v9: table -> month_grid (now a proper <table>)
485
+ month_grid: '', // Styles applied directly to table in CustomMonth
486
+ // v9: head_row -> weekdays (now wrapped in <thead> by custom component)
487
+ weekdays: '', // Styles applied to <tr> inside <thead>
488
+ weekday: 'text-sec-600 rounded-md w-9 font-normal text-[0.8rem]',
489
+ // v9: row -> week (now a proper <tr>)
490
+ week: 'mt-2',
491
+ // v9: cell -> day (now a proper <td> by custom component)
492
+ day: '', // Styles moved to <td> in custom component
493
+ // v9: day -> day_button (the button inside the cell)
494
+ day_button: cn(
495
+ 'h-9 w-9 p-0 font-normal aria-selected:opacity-100',
496
+ 'hover:bg-acc-100 hover:text-main-600',
497
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main-600 focus-visible:ring-offset-2',
498
+ 'inline-flex items-center justify-center rounded-md'
499
+ ),
500
+ // v9: day_range_end -> range_end
501
+ range_end: 'range-end',
502
+ // v9: day_selected -> selected
503
+ selected: 'bg-main-600 text-main-50 hover:bg-main-600 hover:text-main-50 focus:bg-main-600 focus:text-main-50',
504
+ // v9: day_today -> today
505
+ today: 'bg-sec-100 text-main-600 font-semibold',
506
+ // v9: day_outside -> outside
507
+ outside: 'outside text-sec-400 opacity-50 aria-selected:bg-acc-50/50 aria-selected:text-sec-400 aria-selected:opacity-30',
508
+ // v9: day_disabled -> disabled
509
+ disabled: 'text-sec-400 opacity-50 cursor-not-allowed',
510
+ // v9: day_range_middle -> range_middle
511
+ range_middle: 'aria-selected:bg-acc-100 aria-selected:text-main-600',
512
+ // v9: day_hidden -> hidden
513
+ hidden: 'invisible',
514
+ ...classNames,
515
+ }}
516
+ components={defaultComponents}
517
+ />
191
518
  );
192
519
  }
193
520
  );
@@ -146,7 +146,8 @@ describe('Card Component Suite', () => {
146
146
  it('has proper semantic structure', () => {
147
147
  renderWithProviders(<CardHeader>Header content</CardHeader>);
148
148
  const header = screen.getByRole('banner');
149
- expect(header).toHaveClass('flex', 'flex-col', 'space-y-1.5', 'p-6');
149
+ expect(header.tagName).toBe('HEADER');
150
+ expect(header).toHaveClass('p-6', 'min-w-0', 'w-full');
150
151
  });
151
152
  });
152
153
  });
@@ -172,10 +173,11 @@ describe('Card Component Suite', () => {
172
173
  expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
173
174
  });
174
175
 
175
- it('has proper heading styles', () => {
176
+ it('uses semantic heading element', () => {
176
177
  renderWithProviders(<CardTitle>Card Title</CardTitle>);
177
178
  const title = screen.getByRole('heading', { level: 3 });
178
- expect(title).toHaveClass('text-2xl', 'font-semibold', 'leading-none', 'tracking-tight');
179
+ expect(title.tagName).toBe('H3');
180
+ expect(title.className).toBe('');
179
181
  });
180
182
  });
181
183
  });
@@ -256,7 +258,8 @@ describe('Card Component Suite', () => {
256
258
  it('has proper semantic structure', () => {
257
259
  renderWithProviders(<CardFooter>Footer content</CardFooter>);
258
260
  const footer = screen.getByRole('contentinfo');
259
- expect(footer).toHaveClass('flex', 'items-center', 'p-6', 'pt-0');
261
+ expect(footer.tagName).toBe('FOOTER');
262
+ expect(footer).toHaveClass('p-6', 'pt-0', 'min-w-0', 'w-full');
260
263
  });
261
264
  });
262
265
  });
@@ -191,7 +191,7 @@ const CardHeader = React.forwardRef<
191
191
  >(({ className, ...props }, ref) => (
192
192
  <header
193
193
  ref={ref}
194
- className={cn("flex flex-col space-y-1.5 p-6 min-w-0 w-full", className)}
194
+ className={cn("p-6 min-w-0 w-full", className)}
195
195
  {...props}
196
196
  />
197
197
  ))
@@ -203,10 +203,7 @@ const CardTitle = React.forwardRef<
203
203
  >(({ className, ...props }, ref) => (
204
204
  <h3
205
205
  ref={ref}
206
- className={cn(
207
- 'text-2xl font-semibold leading-none tracking-tight',
208
- className
209
- )}
206
+ className={cn(className)}
210
207
  {...props}
211
208
  />
212
209
  ))
@@ -242,7 +239,7 @@ const CardFooter = React.forwardRef<
242
239
  >(({ className, ...props }, ref) => (
243
240
  <footer
244
241
  ref={ref}
245
- className={cn("flex items-center p-6 pt-0 min-w-0 w-full", className)}
242
+ className={cn("p-6 pt-0 min-w-0 w-full", className)}
246
243
  {...props}
247
244
  />
248
245
  ))
@@ -58,7 +58,7 @@ const Checkbox = React.forwardRef<
58
58
  <CheckboxPrimitive.Root
59
59
  ref={ref}
60
60
  className={cn(
61
- "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
61
+ "peer size-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
62
62
  className
63
63
  )}
64
64
  {...props}
@@ -66,7 +66,7 @@ const Checkbox = React.forwardRef<
66
66
  <CheckboxPrimitive.Indicator
67
67
  className={cn("flex items-center justify-center text-current")}
68
68
  >
69
- <Check className="h-4 w-4" />
69
+ <Check className="size-4" />
70
70
  </CheckboxPrimitive.Indicator>
71
71
  </CheckboxPrimitive.Root>
72
72
  ))
@@ -136,9 +136,9 @@ function ActionButtonsComponent<TData extends DataRecord = DataRecord>({
136
136
  aria-disabled={isDisabled}
137
137
  data-testid={action.testId}
138
138
  aria-label={label}
139
- className="h-8 w-8 p-0"
139
+ className="size-8 p-0"
140
140
  >
141
- {Icon && <Icon className="h-4 w-4" />}
141
+ {Icon && <Icon className="size-4" />}
142
142
  </Button>
143
143
  );
144
144
  })}
@@ -149,9 +149,9 @@ function ActionButtonsComponent<TData extends DataRecord = DataRecord>({
149
149
  return (
150
150
  <Select>
151
151
  <SelectTrigger asChild>
152
- <Button variant="ghost" className="h-8 w-8 p-0">
152
+ <Button variant="ghost" className="size-8 p-0">
153
153
  <span className="sr-only">Open menu</span>
154
- <MoreHorizontal className="h-4 w-4" />
154
+ <MoreHorizontal className="size-4" />
155
155
  </Button>
156
156
  </SelectTrigger>
157
157
  <SelectContent className="!bg-main-50 border border-sec-200 shadow-lg z-[9999]" style={{ backgroundColor: 'white' }}>
@@ -172,7 +172,7 @@ function ActionButtonsComponent<TData extends DataRecord = DataRecord>({
172
172
  className="flex items-center gap-2"
173
173
  aria-disabled={isDisabled}
174
174
  >
175
- {Icon && <Icon className="h-4 w-4" />}
175
+ {Icon && <Icon className="size-4" />}
176
176
  <span>{label}</span>
177
177
  </SelectItem>
178
178
  );