@jmruthers/pace-core 0.5.109 → 0.5.111

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 (240) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{AuthService-DrHrvXNZ.d.ts → AuthService-CVgsgtaZ.d.ts} +8 -0
  3. package/dist/{DataTable-5HITILXS.js → DataTable-5W2HVLLV.js} +8 -8
  4. package/dist/{UnifiedAuthProvider-A7I23UCN.js → UnifiedAuthProvider-LUM3QLS5.js} +3 -3
  5. package/dist/{api-5I3E47G2.js → api-SIZPFBFX.js} +5 -3
  6. package/dist/{audit-65VNHEV2.js → audit-5JI5T3SL.js} +2 -2
  7. package/dist/{chunk-P72NKAT5.js → chunk-2BIDKXQU.js} +157 -120
  8. package/dist/chunk-2BIDKXQU.js.map +1 -0
  9. package/dist/{chunk-S4D3Z723.js → chunk-ACYQNYHB.js} +7 -7
  10. package/dist/{chunk-D6MEKC27.js → chunk-EFVQBYFN.js} +2 -2
  11. package/dist/{chunk-EZ64QG2I.js → chunk-I5YM5BGS.js} +2 -2
  12. package/dist/{chunk-Q7APDV6H.js → chunk-IWJYNWXN.js} +13 -5
  13. package/dist/chunk-IWJYNWXN.js.map +1 -0
  14. package/dist/{chunk-YFMENCR4.js → chunk-JE2GFA3O.js} +3 -3
  15. package/dist/{chunk-AUXS7XSO.js → chunk-MW73E7SP.js} +35 -11
  16. package/dist/chunk-MW73E7SP.js.map +1 -0
  17. package/dist/{chunk-F6TSYCKP.js → chunk-PXXS26G5.js} +68 -29
  18. package/dist/chunk-PXXS26G5.js.map +1 -0
  19. package/dist/{chunk-UW2DE6JX.js → chunk-TD4BXGPE.js} +4 -4
  20. package/dist/{chunk-EYSXQ756.js → chunk-TDFBX7KJ.js} +2 -2
  21. package/dist/{chunk-WWNOVFDC.js → chunk-UGVU7L7N.js} +52 -90
  22. package/dist/chunk-UGVU7L7N.js.map +1 -0
  23. package/dist/{chunk-2W4WKJVF.js → chunk-X7SPKHYZ.js} +290 -255
  24. package/dist/chunk-X7SPKHYZ.js.map +1 -0
  25. package/dist/{chunk-3TKTL5AZ.js → chunk-ZL45MG76.js} +60 -60
  26. package/dist/chunk-ZL45MG76.js.map +1 -0
  27. package/dist/components.js +10 -10
  28. package/dist/hooks.d.ts +11 -1
  29. package/dist/hooks.js +9 -7
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +13 -13
  33. package/dist/providers.d.ts +2 -2
  34. package/dist/providers.js +2 -2
  35. package/dist/rbac/index.d.ts +46 -29
  36. package/dist/rbac/index.js +9 -9
  37. package/dist/utils.js +1 -1
  38. package/docs/api/classes/ColumnFactory.md +1 -1
  39. package/docs/api/classes/ErrorBoundary.md +1 -1
  40. package/docs/api/classes/InvalidScopeError.md +4 -4
  41. package/docs/api/classes/MissingUserContextError.md +4 -4
  42. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  43. package/docs/api/classes/PermissionDeniedError.md +4 -4
  44. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  45. package/docs/api/classes/RBACAuditManager.md +8 -8
  46. package/docs/api/classes/RBACCache.md +8 -8
  47. package/docs/api/classes/RBACEngine.md +9 -8
  48. package/docs/api/classes/RBACError.md +4 -4
  49. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  50. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  51. package/docs/api/classes/StorageUtils.md +1 -1
  52. package/docs/api/enums/FileCategory.md +1 -1
  53. package/docs/api/interfaces/AggregateConfig.md +1 -1
  54. package/docs/api/interfaces/ButtonProps.md +1 -1
  55. package/docs/api/interfaces/CardProps.md +1 -1
  56. package/docs/api/interfaces/ColorPalette.md +1 -1
  57. package/docs/api/interfaces/ColorShade.md +1 -1
  58. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  59. package/docs/api/interfaces/DataRecord.md +1 -1
  60. package/docs/api/interfaces/DataTableAction.md +1 -1
  61. package/docs/api/interfaces/DataTableColumn.md +1 -1
  62. package/docs/api/interfaces/DataTableProps.md +1 -1
  63. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  64. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  65. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  66. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  67. package/docs/api/interfaces/FileMetadata.md +1 -1
  68. package/docs/api/interfaces/FileReference.md +1 -1
  69. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  70. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  71. package/docs/api/interfaces/FileUploadProps.md +1 -1
  72. package/docs/api/interfaces/FooterProps.md +1 -1
  73. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  74. package/docs/api/interfaces/InputProps.md +1 -1
  75. package/docs/api/interfaces/LabelProps.md +1 -1
  76. package/docs/api/interfaces/LoginFormProps.md +1 -1
  77. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  78. package/docs/api/interfaces/NavigationContextType.md +1 -1
  79. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  80. package/docs/api/interfaces/NavigationItem.md +1 -1
  81. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  82. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  83. package/docs/api/interfaces/Organisation.md +1 -1
  84. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  85. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  86. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  87. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  88. package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
  89. package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
  90. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  91. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  92. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  93. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  94. package/docs/api/interfaces/PaletteData.md +1 -1
  95. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  96. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  97. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  98. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  99. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  100. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  101. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  102. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  103. package/docs/api/interfaces/RBACConfig.md +19 -8
  104. package/docs/api/interfaces/RBACLogger.md +5 -5
  105. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  106. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  107. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  108. package/docs/api/interfaces/RouteConfig.md +19 -6
  109. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  110. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  111. package/docs/api/interfaces/StorageConfig.md +1 -1
  112. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  113. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  114. package/docs/api/interfaces/StorageListOptions.md +1 -1
  115. package/docs/api/interfaces/StorageListResult.md +1 -1
  116. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  117. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  118. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  119. package/docs/api/interfaces/StyleImport.md +1 -1
  120. package/docs/api/interfaces/SwitchProps.md +1 -1
  121. package/docs/api/interfaces/ToastActionElement.md +1 -1
  122. package/docs/api/interfaces/ToastProps.md +1 -1
  123. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  124. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  125. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  126. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  127. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  128. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  129. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  130. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  131. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  132. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  133. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  134. package/docs/api/interfaces/UserEventAccess.md +1 -1
  135. package/docs/api/interfaces/UserMenuProps.md +1 -1
  136. package/docs/api/interfaces/UserProfile.md +1 -1
  137. package/docs/api/modules.md +44 -43
  138. package/docs/api-reference/hooks.md +8 -4
  139. package/docs/architecture/rpc-function-standards.md +3 -1
  140. package/docs/best-practices/common-patterns.md +3 -3
  141. package/docs/best-practices/deployment.md +10 -4
  142. package/docs/best-practices/performance.md +11 -3
  143. package/docs/core-concepts/organisations.md +8 -8
  144. package/docs/core-concepts/permissions.md +133 -72
  145. package/docs/documentation-index.md +0 -2
  146. package/docs/migration/rbac-migration.md +65 -66
  147. package/docs/rbac/README.md +114 -38
  148. package/docs/rbac/advanced-patterns.md +15 -22
  149. package/docs/rbac/api-reference.md +63 -16
  150. package/docs/rbac/examples.md +12 -12
  151. package/docs/rbac/getting-started.md +19 -19
  152. package/docs/rbac/quick-start.md +110 -35
  153. package/docs/rbac/troubleshooting.md +127 -3
  154. package/package.json +1 -1
  155. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +913 -0
  156. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +609 -0
  157. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +434 -0
  158. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +120 -0
  159. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +519 -0
  160. package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +316 -0
  161. package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +211 -0
  162. package/src/components/FileUpload/FileUpload.tsx +2 -8
  163. package/src/components/NavigationMenu/NavigationMenu.test.tsx +38 -4
  164. package/src/components/NavigationMenu/NavigationMenu.tsx +71 -6
  165. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +193 -63
  166. package/src/components/PaceAppLayout/PaceAppLayout.tsx +102 -135
  167. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +41 -2
  168. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +61 -6
  169. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +71 -21
  170. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +113 -41
  171. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +155 -45
  172. package/src/components/PaceLoginPage/PaceLoginPage.tsx +30 -1
  173. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +63 -5
  174. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +156 -72
  175. package/src/hooks/__tests__/useRBAC.unit.test.ts +4 -38
  176. package/src/hooks/index.ts +1 -1
  177. package/src/hooks/useFileDisplay.ts +51 -0
  178. package/src/hooks/usePermissionCache.test.ts +112 -68
  179. package/src/hooks/usePermissionCache.ts +55 -15
  180. package/src/rbac/README.md +81 -39
  181. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +3 -3
  182. package/src/rbac/__tests__/engine.comprehensive.test.ts +15 -6
  183. package/src/rbac/__tests__/rbac-core.test.tsx +1 -1
  184. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +57 -4
  185. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +3 -2
  186. package/src/rbac/adapters.tsx +4 -4
  187. package/src/rbac/api.test.ts +39 -15
  188. package/src/rbac/api.ts +27 -9
  189. package/src/rbac/audit.test.ts +2 -2
  190. package/src/rbac/audit.ts +14 -5
  191. package/src/rbac/cache.test.ts +12 -0
  192. package/src/rbac/cache.ts +29 -9
  193. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +1 -1
  194. package/src/rbac/components/NavigationGuard.tsx +14 -14
  195. package/src/rbac/components/NavigationProvider.test.tsx +1 -1
  196. package/src/rbac/components/PagePermissionGuard.tsx +22 -38
  197. package/src/rbac/components/PagePermissionProvider.test.tsx +1 -1
  198. package/src/rbac/components/PermissionEnforcer.tsx +19 -15
  199. package/src/rbac/components/RoleBasedRouter.tsx +16 -9
  200. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +123 -107
  201. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
  202. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +121 -103
  203. package/src/rbac/config.ts +2 -0
  204. package/src/rbac/docs/event-based-apps.md +6 -6
  205. package/src/rbac/engine.ts +27 -7
  206. package/src/rbac/hooks/useCan.test.ts +29 -2
  207. package/src/rbac/hooks/usePermissions.test.ts +25 -25
  208. package/src/rbac/hooks/usePermissions.ts +47 -23
  209. package/src/rbac/hooks/useRBAC.simple.test.ts +1 -8
  210. package/src/rbac/hooks/useRBAC.test.ts +3 -40
  211. package/src/rbac/hooks/useRBAC.ts +0 -55
  212. package/src/rbac/hooks/useResolvedScope.ts +23 -31
  213. package/src/rbac/permissions.test.ts +11 -7
  214. package/src/rbac/security.test.ts +2 -2
  215. package/src/rbac/security.ts +23 -8
  216. package/src/rbac/types.test.ts +2 -2
  217. package/src/rbac/types.ts +1 -2
  218. package/src/services/EventService.ts +41 -13
  219. package/src/services/__tests__/EventService.test.ts +25 -4
  220. package/src/services/interfaces/IEventService.ts +1 -0
  221. package/src/utils/file-reference.ts +9 -0
  222. package/dist/chunk-2W4WKJVF.js.map +0 -1
  223. package/dist/chunk-3TKTL5AZ.js.map +0 -1
  224. package/dist/chunk-AUXS7XSO.js.map +0 -1
  225. package/dist/chunk-F6TSYCKP.js.map +0 -1
  226. package/dist/chunk-P72NKAT5.js.map +0 -1
  227. package/dist/chunk-Q7APDV6H.js.map +0 -1
  228. package/dist/chunk-WWNOVFDC.js.map +0 -1
  229. package/docs/rbac/breaking-changes-v3.md +0 -222
  230. package/docs/rbac/migration-guide.md +0 -260
  231. /package/dist/{DataTable-5HITILXS.js.map → DataTable-5W2HVLLV.js.map} +0 -0
  232. /package/dist/{UnifiedAuthProvider-A7I23UCN.js.map → UnifiedAuthProvider-LUM3QLS5.js.map} +0 -0
  233. /package/dist/{api-5I3E47G2.js.map → api-SIZPFBFX.js.map} +0 -0
  234. /package/dist/{audit-65VNHEV2.js.map → audit-5JI5T3SL.js.map} +0 -0
  235. /package/dist/{chunk-S4D3Z723.js.map → chunk-ACYQNYHB.js.map} +0 -0
  236. /package/dist/{chunk-D6MEKC27.js.map → chunk-EFVQBYFN.js.map} +0 -0
  237. /package/dist/{chunk-EZ64QG2I.js.map → chunk-I5YM5BGS.js.map} +0 -0
  238. /package/dist/{chunk-YFMENCR4.js.map → chunk-JE2GFA3O.js.map} +0 -0
  239. /package/dist/{chunk-UW2DE6JX.js.map → chunk-TD4BXGPE.js.map} +0 -0
  240. /package/dist/{chunk-EYSXQ756.js.map → chunk-TDFBX7KJ.js.map} +0 -0
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  init_useOrganisations,
3
3
  useOrganisations
4
- } from "./chunk-EZ64QG2I.js";
4
+ } from "./chunk-I5YM5BGS.js";
5
5
  import {
6
6
  init_UnifiedAuthProvider
7
- } from "./chunk-EYSXQ756.js";
7
+ } from "./chunk-TDFBX7KJ.js";
8
8
  import {
9
9
  OrganisationServiceContext,
10
10
  OrganisationServiceProvider,
11
11
  init_OrganisationServiceProvider,
12
12
  useOrganisationService,
13
13
  useUnifiedAuth
14
- } from "./chunk-AUXS7XSO.js";
14
+ } from "./chunk-MW73E7SP.js";
15
15
  import {
16
16
  init_organisationContext,
17
17
  setOrganisationContext
@@ -651,244 +651,8 @@ async function archiveFile(supabase, path, options) {
651
651
  }
652
652
  }
653
653
 
654
- // src/hooks/public/usePublicFileDisplay.ts
655
- import { useState, useEffect, useCallback } from "react";
656
- var publicFileCache = /* @__PURE__ */ new Map();
657
- function usePublicFileDisplay(table_name, record_id, organisation_id, category, options) {
658
- const {
659
- cacheTtl = 30 * 60 * 1e3,
660
- // 30 minutes
661
- enableCache = true,
662
- supabase
663
- } = options;
664
- const [fileUrl, setFileUrl] = useState(null);
665
- const [fileReference, setFileReference] = useState(null);
666
- const [fileReferences, setFileReferences] = useState([]);
667
- const [fileUrls, setFileUrls] = useState(/* @__PURE__ */ new Map());
668
- const [fileCount, setFileCount] = useState(0);
669
- const [isLoading, setIsLoading] = useState(false);
670
- const [error, setError] = useState(null);
671
- const fetchFiles = useCallback(async () => {
672
- if (!table_name || !record_id || !organisation_id || !supabase) {
673
- setFileUrl(null);
674
- setFileReference(null);
675
- setFileReferences([]);
676
- setFileUrls(/* @__PURE__ */ new Map());
677
- setFileCount(0);
678
- setIsLoading(false);
679
- return;
680
- }
681
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
682
- if (!uuidRegex.test(organisation_id)) {
683
- console.warn("[usePublicFileDisplay] Invalid organisationId format (not a valid UUID):", organisation_id);
684
- }
685
- const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
686
- if (enableCache) {
687
- const cached = publicFileCache.get(cacheKey);
688
- if (cached && Date.now() - cached.timestamp < cached.ttl) {
689
- const cachedData = cached.data;
690
- setFileUrl(cachedData.fileUrl || null);
691
- setFileReference(cachedData.fileReference || null);
692
- setFileReferences(cachedData.fileReferences || []);
693
- setFileUrls(cachedData.fileUrls || /* @__PURE__ */ new Map());
694
- setFileCount(cachedData.fileCount || 0);
695
- setIsLoading(false);
696
- setError(null);
697
- return;
698
- }
699
- }
700
- try {
701
- setIsLoading(true);
702
- setError(null);
703
- let files = [];
704
- if (category) {
705
- console.log("[usePublicFileDisplay] Using RPC function for category filtering:", {
706
- table_name,
707
- record_id,
708
- category,
709
- organisation_id
710
- });
711
- const { data, error: rpcError } = await supabase.rpc("data_file_reference_by_category_list", {
712
- p_table_name: table_name,
713
- p_record_id: record_id,
714
- p_category: category,
715
- p_organisation_id: organisation_id
716
- });
717
- if (rpcError) {
718
- throw new Error(rpcError.message || "Failed to fetch file reference");
719
- }
720
- if (!data || data.length === 0) {
721
- files = [];
722
- } else {
723
- files = data.filter((item) => {
724
- return item.is_public === true && item.id && item.file_path && item.file_metadata;
725
- }).map((item) => {
726
- return {
727
- id: item.id,
728
- table_name,
729
- record_id,
730
- file_path: item.file_path,
731
- file_metadata: item.file_metadata || {},
732
- organisation_id,
733
- app_id: item.file_metadata?.app_id || null,
734
- is_public: true,
735
- // RPC already filtered for public files
736
- created_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
737
- updated_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString()
738
- };
739
- });
740
- }
741
- } else {
742
- const { data: fileIds, error: rpcError } = await supabase.rpc("data_file_reference_list", {
743
- p_table_name: table_name,
744
- p_record_id: record_id,
745
- p_organisation_id: organisation_id
746
- });
747
- if (rpcError) {
748
- throw new Error(rpcError.message || "Failed to fetch file references");
749
- }
750
- if (!fileIds || fileIds.length === 0) {
751
- files = [];
752
- } else {
753
- const ids = fileIds.map((item) => item.id);
754
- const { data: fullData, error: fetchError } = await supabase.from("file_references").select("*").in("id", ids).eq("is_public", true);
755
- if (fetchError) {
756
- throw new Error(fetchError.message || "Failed to fetch file references");
757
- }
758
- files = fullData || [];
759
- }
760
- }
761
- const publicFiles = files.filter((f) => f.is_public === true);
762
- if (publicFiles.length === 0) {
763
- setFileUrl(null);
764
- setFileReference(null);
765
- setFileReferences([]);
766
- setFileUrls(/* @__PURE__ */ new Map());
767
- setFileCount(0);
768
- if (enableCache) {
769
- publicFileCache.set(cacheKey, {
770
- data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: /* @__PURE__ */ new Map(), fileCount: 0 },
771
- timestamp: Date.now(),
772
- ttl: cacheTtl
773
- });
774
- }
775
- return;
776
- }
777
- const fileRefs = publicFiles.map((f) => ({
778
- id: f.id,
779
- table_name: f.table_name,
780
- record_id: f.record_id,
781
- file_path: f.file_path,
782
- file_metadata: f.file_metadata || {},
783
- organisation_id: f.organisation_id,
784
- app_id: f.app_id,
785
- is_public: f.is_public ?? true,
786
- created_at: f.created_at,
787
- updated_at: f.updated_at
788
- }));
789
- setFileReferences(fileRefs);
790
- setFileCount(fileRefs.length);
791
- if (category && fileRefs.length > 0) {
792
- const firstFile = fileRefs[0];
793
- setFileReference(firstFile);
794
- const url = getPublicUrl(supabase, firstFile.file_path, true);
795
- setFileUrl(url);
796
- } else {
797
- const urlMap = /* @__PURE__ */ new Map();
798
- for (const fileRef of fileRefs) {
799
- const url = getPublicUrl(supabase, fileRef.file_path, true);
800
- if (url) {
801
- urlMap.set(fileRef.id, url);
802
- }
803
- }
804
- setFileUrls(urlMap);
805
- setFileReference(null);
806
- setFileUrl(null);
807
- }
808
- if (enableCache) {
809
- publicFileCache.set(cacheKey, {
810
- data: {
811
- fileUrl: category ? fileRefs.length > 0 ? getPublicUrl(supabase, fileRefs[0].file_path, true) : null : null,
812
- fileReference: category && fileRefs.length > 0 ? fileRefs[0] : null,
813
- fileReferences: fileRefs,
814
- fileUrls: category ? /* @__PURE__ */ new Map() : (() => {
815
- const urlMap = /* @__PURE__ */ new Map();
816
- for (const fileRef of fileRefs) {
817
- const url = getPublicUrl(supabase, fileRef.file_path, true);
818
- if (url) {
819
- urlMap.set(fileRef.id, url);
820
- }
821
- }
822
- return urlMap;
823
- })(),
824
- fileCount: fileRefs.length
825
- },
826
- timestamp: Date.now(),
827
- ttl: cacheTtl
828
- });
829
- }
830
- } catch (err) {
831
- console.error("[usePublicFileDisplay] Error fetching files:", err);
832
- const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
833
- setError(error2);
834
- setFileUrl(null);
835
- setFileReference(null);
836
- setFileReferences([]);
837
- setFileUrls(/* @__PURE__ */ new Map());
838
- setFileCount(0);
839
- } finally {
840
- setIsLoading(false);
841
- }
842
- }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
843
- useEffect(() => {
844
- if (table_name && record_id && organisation_id) {
845
- fetchFiles();
846
- } else {
847
- setFileUrl(null);
848
- setFileReference(null);
849
- setFileReferences([]);
850
- setFileUrls(/* @__PURE__ */ new Map());
851
- setFileCount(0);
852
- setIsLoading(false);
853
- setError(null);
854
- }
855
- }, [fetchFiles, table_name, record_id, organisation_id]);
856
- const refetch = useCallback(async () => {
857
- if (!table_name || !record_id || !organisation_id) return;
858
- if (enableCache) {
859
- const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
860
- publicFileCache.delete(cacheKey);
861
- }
862
- await fetchFiles();
863
- }, [fetchFiles, table_name, record_id, organisation_id, category, enableCache]);
864
- return {
865
- fileUrl,
866
- fileReference,
867
- fileReferences,
868
- fileUrls,
869
- fileCount,
870
- isLoading,
871
- error,
872
- refetch
873
- };
874
- }
875
- function clearPublicFileDisplayCache() {
876
- for (const [key] of publicFileCache) {
877
- if (key.startsWith("public_file_")) {
878
- publicFileCache.delete(key);
879
- }
880
- }
881
- }
882
- function getPublicFileDisplayCacheStats() {
883
- const keys = Array.from(publicFileCache.keys()).filter((key) => key.startsWith("public_file_"));
884
- return {
885
- size: keys.length,
886
- keys
887
- };
888
- }
889
-
890
654
  // src/hooks/useFileDisplay.ts
891
- import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
655
+ import { useState, useEffect, useCallback } from "react";
892
656
 
893
657
  // src/utils/file-reference.ts
894
658
  init_organisationContext();
@@ -966,6 +730,12 @@ var FileReferenceServiceImpl = class {
966
730
  if (fetchError || !fileRef) {
967
731
  throw new Error(`Failed to fetch created file reference: ${fetchError?.message}`);
968
732
  }
733
+ invalidateFileDisplayCache(
734
+ options.table_name,
735
+ options.record_id,
736
+ options.organisation_id,
737
+ options.category
738
+ );
969
739
  return fileRef;
970
740
  } catch (error) {
971
741
  console.error("Error creating file reference:", error);
@@ -1281,14 +1051,14 @@ function useFileDisplay(table_name, record_id, organisation_id, category, option
1281
1051
  enableCache = true,
1282
1052
  supabase
1283
1053
  } = options;
1284
- const [fileUrl, setFileUrl] = useState2(null);
1285
- const [fileReference, setFileReference] = useState2(null);
1286
- const [fileReferences, setFileReferences] = useState2([]);
1287
- const [fileUrls, setFileUrls] = useState2(/* @__PURE__ */ new Map());
1288
- const [fileCount, setFileCount] = useState2(0);
1289
- const [isLoading, setIsLoading] = useState2(false);
1290
- const [error, setError] = useState2(null);
1291
- const fetchFiles = useCallback2(async () => {
1054
+ const [fileUrl, setFileUrl] = useState(null);
1055
+ const [fileReference, setFileReference] = useState(null);
1056
+ const [fileReferences, setFileReferences] = useState([]);
1057
+ const [fileUrls, setFileUrls] = useState(/* @__PURE__ */ new Map());
1058
+ const [fileCount, setFileCount] = useState(0);
1059
+ const [isLoading, setIsLoading] = useState(false);
1060
+ const [error, setError] = useState(null);
1061
+ const fetchFiles = useCallback(async () => {
1292
1062
  if (!table_name || !record_id || !organisation_id || !supabase) {
1293
1063
  setFileUrl(null);
1294
1064
  setFileReference(null);
@@ -1307,6 +1077,26 @@ function useFileDisplay(table_name, record_id, organisation_id, category, option
1307
1077
  const cached = authenticatedFileCache.get(cacheKey);
1308
1078
  if (cached && Date.now() - cached.timestamp < cached.ttl) {
1309
1079
  const cachedData = cached.data;
1080
+ if (cachedData.fileReference && !cachedData.fileUrl && cachedData.fileReference.is_public === false && supabase) {
1081
+ try {
1082
+ const signedUrlResult = await getSignedUrl(supabase, cachedData.fileReference.file_path, {
1083
+ appName: "pace-core",
1084
+ orgId: organisation_id,
1085
+ expiresIn: 3600
1086
+ });
1087
+ const regeneratedUrl = signedUrlResult?.url || null;
1088
+ setFileUrl(regeneratedUrl);
1089
+ setFileReference(cachedData.fileReference);
1090
+ setFileReferences(cachedData.fileReferences || []);
1091
+ setFileUrls(cachedData.fileUrls || /* @__PURE__ */ new Map());
1092
+ setFileCount(cachedData.fileCount || 0);
1093
+ setIsLoading(false);
1094
+ setError(null);
1095
+ return;
1096
+ } catch (err) {
1097
+ console.warn("[useFileDisplay] Failed to regenerate signed URL from cache, falling back to fetch:", err);
1098
+ }
1099
+ }
1310
1100
  setFileUrl(cachedData.fileUrl || null);
1311
1101
  setFileReference(cachedData.fileReference || null);
1312
1102
  setFileReferences(cachedData.fileReferences || []);
@@ -1453,7 +1243,7 @@ function useFileDisplay(table_name, record_id, organisation_id, category, option
1453
1243
  setIsLoading(false);
1454
1244
  }
1455
1245
  }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
1456
- useEffect2(() => {
1246
+ useEffect(() => {
1457
1247
  if (table_name && record_id && organisation_id && supabase) {
1458
1248
  fetchFiles();
1459
1249
  } else {
@@ -1466,7 +1256,7 @@ function useFileDisplay(table_name, record_id, organisation_id, category, option
1466
1256
  setError(null);
1467
1257
  }
1468
1258
  }, [fetchFiles, table_name, record_id, organisation_id, supabase]);
1469
- const refetch = useCallback2(async () => {
1259
+ const refetch = useCallback(async () => {
1470
1260
  if (!table_name || !record_id || !organisation_id || !supabase) return;
1471
1261
  if (enableCache) {
1472
1262
  const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
@@ -1499,6 +1289,250 @@ function getFileDisplayCacheStats() {
1499
1289
  keys
1500
1290
  };
1501
1291
  }
1292
+ function invalidateFileDisplayCache(table_name, record_id, organisation_id, category) {
1293
+ const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
1294
+ authenticatedFileCache.delete(cacheKey);
1295
+ if (category) {
1296
+ const allCategoryKey = `file_${table_name}_${record_id}_${organisation_id}_all`;
1297
+ authenticatedFileCache.delete(allCategoryKey);
1298
+ }
1299
+ }
1300
+
1301
+ // src/hooks/public/usePublicFileDisplay.ts
1302
+ import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
1303
+ var publicFileCache = /* @__PURE__ */ new Map();
1304
+ function usePublicFileDisplay(table_name, record_id, organisation_id, category, options) {
1305
+ const {
1306
+ cacheTtl = 30 * 60 * 1e3,
1307
+ // 30 minutes
1308
+ enableCache = true,
1309
+ supabase
1310
+ } = options;
1311
+ const [fileUrl, setFileUrl] = useState2(null);
1312
+ const [fileReference, setFileReference] = useState2(null);
1313
+ const [fileReferences, setFileReferences] = useState2([]);
1314
+ const [fileUrls, setFileUrls] = useState2(/* @__PURE__ */ new Map());
1315
+ const [fileCount, setFileCount] = useState2(0);
1316
+ const [isLoading, setIsLoading] = useState2(false);
1317
+ const [error, setError] = useState2(null);
1318
+ const fetchFiles = useCallback2(async () => {
1319
+ if (!table_name || !record_id || !organisation_id || !supabase) {
1320
+ setFileUrl(null);
1321
+ setFileReference(null);
1322
+ setFileReferences([]);
1323
+ setFileUrls(/* @__PURE__ */ new Map());
1324
+ setFileCount(0);
1325
+ setIsLoading(false);
1326
+ return;
1327
+ }
1328
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1329
+ if (!uuidRegex.test(organisation_id)) {
1330
+ console.warn("[usePublicFileDisplay] Invalid organisationId format (not a valid UUID):", organisation_id);
1331
+ }
1332
+ const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
1333
+ if (enableCache) {
1334
+ const cached = publicFileCache.get(cacheKey);
1335
+ if (cached && Date.now() - cached.timestamp < cached.ttl) {
1336
+ const cachedData = cached.data;
1337
+ setFileUrl(cachedData.fileUrl || null);
1338
+ setFileReference(cachedData.fileReference || null);
1339
+ setFileReferences(cachedData.fileReferences || []);
1340
+ setFileUrls(cachedData.fileUrls || /* @__PURE__ */ new Map());
1341
+ setFileCount(cachedData.fileCount || 0);
1342
+ setIsLoading(false);
1343
+ setError(null);
1344
+ return;
1345
+ }
1346
+ }
1347
+ try {
1348
+ setIsLoading(true);
1349
+ setError(null);
1350
+ let files = [];
1351
+ if (category) {
1352
+ console.log("[usePublicFileDisplay] Using RPC function for category filtering:", {
1353
+ table_name,
1354
+ record_id,
1355
+ category,
1356
+ organisation_id
1357
+ });
1358
+ const { data, error: rpcError } = await supabase.rpc("data_file_reference_by_category_list", {
1359
+ p_table_name: table_name,
1360
+ p_record_id: record_id,
1361
+ p_category: category,
1362
+ p_organisation_id: organisation_id
1363
+ });
1364
+ if (rpcError) {
1365
+ throw new Error(rpcError.message || "Failed to fetch file reference");
1366
+ }
1367
+ if (!data || data.length === 0) {
1368
+ files = [];
1369
+ } else {
1370
+ files = data.filter((item) => {
1371
+ return item.is_public === true && item.id && item.file_path && item.file_metadata;
1372
+ }).map((item) => {
1373
+ return {
1374
+ id: item.id,
1375
+ table_name,
1376
+ record_id,
1377
+ file_path: item.file_path,
1378
+ file_metadata: item.file_metadata || {},
1379
+ organisation_id,
1380
+ app_id: item.file_metadata?.app_id || null,
1381
+ is_public: true,
1382
+ // RPC already filtered for public files
1383
+ created_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString(),
1384
+ updated_at: item.created_at || (/* @__PURE__ */ new Date()).toISOString()
1385
+ };
1386
+ });
1387
+ }
1388
+ } else {
1389
+ const { data: fileIds, error: rpcError } = await supabase.rpc("data_file_reference_list", {
1390
+ p_table_name: table_name,
1391
+ p_record_id: record_id,
1392
+ p_organisation_id: organisation_id
1393
+ });
1394
+ if (rpcError) {
1395
+ throw new Error(rpcError.message || "Failed to fetch file references");
1396
+ }
1397
+ if (!fileIds || fileIds.length === 0) {
1398
+ files = [];
1399
+ } else {
1400
+ const ids = fileIds.map((item) => item.id);
1401
+ const { data: fullData, error: fetchError } = await supabase.from("file_references").select("*").in("id", ids).eq("is_public", true);
1402
+ if (fetchError) {
1403
+ throw new Error(fetchError.message || "Failed to fetch file references");
1404
+ }
1405
+ files = fullData || [];
1406
+ }
1407
+ }
1408
+ const publicFiles = files.filter((f) => f.is_public === true);
1409
+ if (publicFiles.length === 0) {
1410
+ setFileUrl(null);
1411
+ setFileReference(null);
1412
+ setFileReferences([]);
1413
+ setFileUrls(/* @__PURE__ */ new Map());
1414
+ setFileCount(0);
1415
+ if (enableCache) {
1416
+ publicFileCache.set(cacheKey, {
1417
+ data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: /* @__PURE__ */ new Map(), fileCount: 0 },
1418
+ timestamp: Date.now(),
1419
+ ttl: cacheTtl
1420
+ });
1421
+ }
1422
+ return;
1423
+ }
1424
+ const fileRefs = publicFiles.map((f) => ({
1425
+ id: f.id,
1426
+ table_name: f.table_name,
1427
+ record_id: f.record_id,
1428
+ file_path: f.file_path,
1429
+ file_metadata: f.file_metadata || {},
1430
+ organisation_id: f.organisation_id,
1431
+ app_id: f.app_id,
1432
+ is_public: f.is_public ?? true,
1433
+ created_at: f.created_at,
1434
+ updated_at: f.updated_at
1435
+ }));
1436
+ setFileReferences(fileRefs);
1437
+ setFileCount(fileRefs.length);
1438
+ if (category && fileRefs.length > 0) {
1439
+ const firstFile = fileRefs[0];
1440
+ setFileReference(firstFile);
1441
+ const url = getPublicUrl(supabase, firstFile.file_path, true);
1442
+ setFileUrl(url);
1443
+ } else {
1444
+ const urlMap = /* @__PURE__ */ new Map();
1445
+ for (const fileRef of fileRefs) {
1446
+ const url = getPublicUrl(supabase, fileRef.file_path, true);
1447
+ if (url) {
1448
+ urlMap.set(fileRef.id, url);
1449
+ }
1450
+ }
1451
+ setFileUrls(urlMap);
1452
+ setFileReference(null);
1453
+ setFileUrl(null);
1454
+ }
1455
+ if (enableCache) {
1456
+ publicFileCache.set(cacheKey, {
1457
+ data: {
1458
+ fileUrl: category ? fileRefs.length > 0 ? getPublicUrl(supabase, fileRefs[0].file_path, true) : null : null,
1459
+ fileReference: category && fileRefs.length > 0 ? fileRefs[0] : null,
1460
+ fileReferences: fileRefs,
1461
+ fileUrls: category ? /* @__PURE__ */ new Map() : (() => {
1462
+ const urlMap = /* @__PURE__ */ new Map();
1463
+ for (const fileRef of fileRefs) {
1464
+ const url = getPublicUrl(supabase, fileRef.file_path, true);
1465
+ if (url) {
1466
+ urlMap.set(fileRef.id, url);
1467
+ }
1468
+ }
1469
+ return urlMap;
1470
+ })(),
1471
+ fileCount: fileRefs.length
1472
+ },
1473
+ timestamp: Date.now(),
1474
+ ttl: cacheTtl
1475
+ });
1476
+ }
1477
+ } catch (err) {
1478
+ console.error("[usePublicFileDisplay] Error fetching files:", err);
1479
+ const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
1480
+ setError(error2);
1481
+ setFileUrl(null);
1482
+ setFileReference(null);
1483
+ setFileReferences([]);
1484
+ setFileUrls(/* @__PURE__ */ new Map());
1485
+ setFileCount(0);
1486
+ } finally {
1487
+ setIsLoading(false);
1488
+ }
1489
+ }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
1490
+ useEffect2(() => {
1491
+ if (table_name && record_id && organisation_id) {
1492
+ fetchFiles();
1493
+ } else {
1494
+ setFileUrl(null);
1495
+ setFileReference(null);
1496
+ setFileReferences([]);
1497
+ setFileUrls(/* @__PURE__ */ new Map());
1498
+ setFileCount(0);
1499
+ setIsLoading(false);
1500
+ setError(null);
1501
+ }
1502
+ }, [fetchFiles, table_name, record_id, organisation_id]);
1503
+ const refetch = useCallback2(async () => {
1504
+ if (!table_name || !record_id || !organisation_id) return;
1505
+ if (enableCache) {
1506
+ const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
1507
+ publicFileCache.delete(cacheKey);
1508
+ }
1509
+ await fetchFiles();
1510
+ }, [fetchFiles, table_name, record_id, organisation_id, category, enableCache]);
1511
+ return {
1512
+ fileUrl,
1513
+ fileReference,
1514
+ fileReferences,
1515
+ fileUrls,
1516
+ fileCount,
1517
+ isLoading,
1518
+ error,
1519
+ refetch
1520
+ };
1521
+ }
1522
+ function clearPublicFileDisplayCache() {
1523
+ for (const [key] of publicFileCache) {
1524
+ if (key.startsWith("public_file_")) {
1525
+ publicFileCache.delete(key);
1526
+ }
1527
+ }
1528
+ }
1529
+ function getPublicFileDisplayCacheStats() {
1530
+ const keys = Array.from(publicFileCache.keys()).filter((key) => key.startsWith("public_file_"));
1531
+ return {
1532
+ size: keys.length,
1533
+ keys
1534
+ };
1535
+ }
1502
1536
 
1503
1537
  export {
1504
1538
  OrganisationProvider_exports,
@@ -1528,13 +1562,14 @@ export {
1528
1562
  listFiles,
1529
1563
  downloadFile,
1530
1564
  archiveFile,
1565
+ useFileDisplay,
1566
+ clearFileDisplayCache,
1567
+ getFileDisplayCacheStats,
1568
+ invalidateFileDisplayCache,
1531
1569
  createFileReferenceService,
1532
1570
  uploadFileWithReference,
1533
1571
  usePublicFileDisplay,
1534
1572
  clearPublicFileDisplayCache,
1535
- getPublicFileDisplayCacheStats,
1536
- useFileDisplay,
1537
- clearFileDisplayCache,
1538
- getFileDisplayCacheStats
1573
+ getPublicFileDisplayCacheStats
1539
1574
  };
1540
- //# sourceMappingURL=chunk-2W4WKJVF.js.map
1575
+ //# sourceMappingURL=chunk-X7SPKHYZ.js.map