@jmruthers/pace-core 0.5.92 → 0.5.94

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 (176) hide show
  1. package/dist/{DataTable-HC5S4RKB.js → DataTable-CHX2EFO3.js} +6 -6
  2. package/dist/{PublicLoadingSpinner-n74JgA9h.d.ts → PublicLoadingSpinner-BWUD6bLU.d.ts} +24 -3
  3. package/dist/{UnifiedAuthProvider-ZM7VUC45.js → UnifiedAuthProvider-H7RI4KYD.js} +3 -3
  4. package/dist/{chunk-AZ2QJYKU.js → chunk-2KLAOD4M.js} +3 -3
  5. package/dist/{chunk-HW5BGOWB.js → chunk-2ZYHCFUO.js} +4 -4
  6. package/dist/{chunk-AAM57AEU.js → chunk-5RYPBJYL.js} +16 -19
  7. package/dist/chunk-5RYPBJYL.js.map +1 -0
  8. package/dist/{chunk-XIBSVWJW.js → chunk-7TQDRDSM.js} +5 -5
  9. package/dist/{chunk-GP3HU6WS.js → chunk-G7UUVEAP.js} +3 -3
  10. package/dist/{chunk-M52CQP5W.js → chunk-MKMKUCPF.js} +762 -12
  11. package/dist/chunk-MKMKUCPF.js.map +1 -0
  12. package/dist/{chunk-OXFOS62D.js → chunk-MVNOAHOP.js} +2 -2
  13. package/dist/{chunk-5LAY74WM.js → chunk-O6GASC4Q.js} +877 -788
  14. package/dist/chunk-O6GASC4Q.js.map +1 -0
  15. package/dist/{chunk-AYC2P377.js → chunk-ORACUZ7H.js} +2 -2
  16. package/dist/{chunk-TZXYSZT3.js → chunk-PRM6EYO3.js} +298 -238
  17. package/dist/{chunk-TZXYSZT3.js.map → chunk-PRM6EYO3.js.map} +1 -1
  18. package/dist/{chunk-6WFM22A4.js → chunk-ZGCVJ7WW.js} +2 -2
  19. package/dist/components.d.ts +1 -1
  20. package/dist/components.js +8 -8
  21. package/dist/hooks.d.ts +94 -3
  22. package/dist/hooks.js +20 -8
  23. package/dist/hooks.js.map +1 -1
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +17 -11
  26. package/dist/index.js.map +1 -1
  27. package/dist/providers.js +2 -2
  28. package/dist/rbac/index.js +7 -7
  29. package/dist/{usePublicRouteParams-BlgwXweB.d.ts → usePublicRouteParams-BwMR2uub.d.ts} +93 -1
  30. package/dist/utils.js +1 -1
  31. package/docs/api/classes/ColumnFactory.md +1 -1
  32. package/docs/api/classes/ErrorBoundary.md +1 -1
  33. package/docs/api/classes/InvalidScopeError.md +1 -1
  34. package/docs/api/classes/MissingUserContextError.md +1 -1
  35. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  36. package/docs/api/classes/PermissionDeniedError.md +1 -1
  37. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  38. package/docs/api/classes/RBACAuditManager.md +1 -1
  39. package/docs/api/classes/RBACCache.md +1 -1
  40. package/docs/api/classes/RBACEngine.md +1 -1
  41. package/docs/api/classes/RBACError.md +1 -1
  42. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  43. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  44. package/docs/api/classes/StorageUtils.md +1 -1
  45. package/docs/api/enums/FileCategory.md +1 -1
  46. package/docs/api/interfaces/AggregateConfig.md +1 -1
  47. package/docs/api/interfaces/ButtonProps.md +1 -1
  48. package/docs/api/interfaces/CardProps.md +1 -1
  49. package/docs/api/interfaces/ColorPalette.md +1 -1
  50. package/docs/api/interfaces/ColorShade.md +1 -1
  51. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  52. package/docs/api/interfaces/DataRecord.md +1 -1
  53. package/docs/api/interfaces/DataTableAction.md +1 -1
  54. package/docs/api/interfaces/DataTableColumn.md +1 -1
  55. package/docs/api/interfaces/DataTableProps.md +1 -1
  56. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  57. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  58. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  59. package/docs/api/interfaces/EventLogoProps.md +1 -1
  60. package/docs/api/interfaces/FileDisplayProps.md +26 -11
  61. package/docs/api/interfaces/FileMetadata.md +1 -1
  62. package/docs/api/interfaces/FileReference.md +1 -1
  63. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  64. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  65. package/docs/api/interfaces/FileUploadProps.md +1 -1
  66. package/docs/api/interfaces/FooterProps.md +1 -1
  67. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  68. package/docs/api/interfaces/InputProps.md +1 -1
  69. package/docs/api/interfaces/LabelProps.md +1 -1
  70. package/docs/api/interfaces/LoginFormProps.md +1 -1
  71. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  72. package/docs/api/interfaces/NavigationContextType.md +1 -1
  73. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  74. package/docs/api/interfaces/NavigationItem.md +1 -1
  75. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  76. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  77. package/docs/api/interfaces/Organisation.md +1 -1
  78. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  79. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  80. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  81. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  82. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  83. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  84. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  85. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  86. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  87. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  88. package/docs/api/interfaces/PaletteData.md +1 -1
  89. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  90. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  91. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  92. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  93. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  94. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  95. package/docs/api/interfaces/PublicPageHeaderProps.md +24 -11
  96. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  97. package/docs/api/interfaces/RBACConfig.md +1 -1
  98. package/docs/api/interfaces/RBACLogger.md +1 -1
  99. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  100. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  101. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  102. package/docs/api/interfaces/RouteConfig.md +1 -1
  103. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  104. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  105. package/docs/api/interfaces/StorageConfig.md +1 -1
  106. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  107. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  108. package/docs/api/interfaces/StorageListOptions.md +1 -1
  109. package/docs/api/interfaces/StorageListResult.md +1 -1
  110. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  111. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  112. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  113. package/docs/api/interfaces/StyleImport.md +1 -1
  114. package/docs/api/interfaces/SwitchProps.md +1 -1
  115. package/docs/api/interfaces/ToastActionElement.md +1 -1
  116. package/docs/api/interfaces/ToastProps.md +1 -1
  117. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  118. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  119. package/docs/api/interfaces/UseEventLogoOptions.md +1 -1
  120. package/docs/api/interfaces/UseEventLogoReturn.md +1 -1
  121. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  122. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  123. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  124. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  125. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  126. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  127. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +47 -0
  128. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +120 -0
  129. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  130. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  131. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  132. package/docs/api/interfaces/UserEventAccess.md +1 -1
  133. package/docs/api/interfaces/UserMenuProps.md +1 -1
  134. package/docs/api/interfaces/UserProfile.md +1 -1
  135. package/docs/api/modules.md +105 -19
  136. package/docs/api-reference/components.md +146 -1
  137. package/docs/best-practices/common-patterns.md +26 -8
  138. package/docs/getting-started/examples/README.md +10 -26
  139. package/docs/getting-started/quick-reference.md +23 -0
  140. package/docs/implementation-guides/authentication.md +39 -16
  141. package/docs/implementation-guides/file-reference-system.md +15 -0
  142. package/docs/implementation-guides/file-upload-storage.md +16 -0
  143. package/package.json +1 -1
  144. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +9 -7
  145. package/src/components/DataTable/components/DataTableCore.tsx +8 -1
  146. package/src/components/DataTable/components/EditableRow.tsx +62 -22
  147. package/src/components/DataTable/components/UnifiedTableBody.tsx +25 -101
  148. package/src/components/FileDisplay/FileDisplay.test.tsx +263 -39
  149. package/src/components/FileDisplay/FileDisplay.tsx +605 -83
  150. package/src/components/ProtectedRoute/README.md +164 -0
  151. package/src/components/PublicLayout/EventLogo.tsx +8 -2
  152. package/src/components/PublicLayout/PublicPageHeader.tsx +20 -18
  153. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +75 -29
  154. package/src/components/Select/Select.test.tsx +83 -6
  155. package/src/components/Select/Select.tsx +236 -16
  156. package/src/examples/CorrectPublicPageImplementation.tsx +16 -13
  157. package/src/examples/PublicEventPage.tsx +9 -6
  158. package/src/examples/PublicPageApp.tsx +9 -6
  159. package/src/examples/PublicPageUsageExample.tsx +9 -7
  160. package/src/hooks/index.ts +4 -0
  161. package/src/hooks/public/index.ts +2 -0
  162. package/src/hooks/public/usePublicFileDisplay.ts +355 -0
  163. package/src/hooks/useFileDisplay.ts +370 -0
  164. package/src/services/AuthService.ts +19 -22
  165. package/dist/chunk-5LAY74WM.js.map +0 -1
  166. package/dist/chunk-AAM57AEU.js.map +0 -1
  167. package/dist/chunk-M52CQP5W.js.map +0 -1
  168. /package/dist/{DataTable-HC5S4RKB.js.map → DataTable-CHX2EFO3.js.map} +0 -0
  169. /package/dist/{UnifiedAuthProvider-ZM7VUC45.js.map → UnifiedAuthProvider-H7RI4KYD.js.map} +0 -0
  170. /package/dist/{chunk-AZ2QJYKU.js.map → chunk-2KLAOD4M.js.map} +0 -0
  171. /package/dist/{chunk-HW5BGOWB.js.map → chunk-2ZYHCFUO.js.map} +0 -0
  172. /package/dist/{chunk-XIBSVWJW.js.map → chunk-7TQDRDSM.js.map} +0 -0
  173. /package/dist/{chunk-GP3HU6WS.js.map → chunk-G7UUVEAP.js.map} +0 -0
  174. /package/dist/{chunk-OXFOS62D.js.map → chunk-MVNOAHOP.js.map} +0 -0
  175. /package/dist/{chunk-AYC2P377.js.map → chunk-ORACUZ7H.js.map} +0 -0
  176. /package/dist/{chunk-6WFM22A4.js.map → chunk-ZGCVJ7WW.js.map} +0 -0
@@ -1,17 +1,21 @@
1
1
  import {
2
2
  init_useOrganisations,
3
3
  useOrganisations
4
- } from "./chunk-OXFOS62D.js";
4
+ } from "./chunk-MVNOAHOP.js";
5
5
  import {
6
6
  init_UnifiedAuthProvider
7
- } from "./chunk-AYC2P377.js";
7
+ } from "./chunk-ORACUZ7H.js";
8
8
  import {
9
9
  OrganisationServiceContext,
10
10
  OrganisationServiceProvider,
11
11
  init_OrganisationServiceProvider,
12
12
  useOrganisationService,
13
13
  useUnifiedAuth
14
- } from "./chunk-AAM57AEU.js";
14
+ } from "./chunk-5RYPBJYL.js";
15
+ import {
16
+ init_organisationContext,
17
+ setOrganisationContext
18
+ } from "./chunk-BDZUMRBD.js";
15
19
  import {
16
20
  __esm,
17
21
  __export
@@ -646,8 +650,746 @@ async function archiveFile(supabase, path, options) {
646
650
  }
647
651
  }
648
652
 
653
+ // src/hooks/public/usePublicFileDisplay.ts
654
+ import { useState, useEffect, useCallback } from "react";
655
+ var publicFileCache = /* @__PURE__ */ new Map();
656
+ function usePublicFileDisplay(table_name, record_id, organisation_id, category, options) {
657
+ const {
658
+ cacheTtl = 30 * 60 * 1e3,
659
+ // 30 minutes
660
+ enableCache = true,
661
+ supabase
662
+ } = options;
663
+ const [fileUrl, setFileUrl] = useState(null);
664
+ const [fileReference, setFileReference] = useState(null);
665
+ const [fileReferences, setFileReferences] = useState([]);
666
+ const [fileUrls, setFileUrls] = useState(/* @__PURE__ */ new Map());
667
+ const [fileCount, setFileCount] = useState(0);
668
+ const [isLoading, setIsLoading] = useState(false);
669
+ const [error, setError] = useState(null);
670
+ const fetchFiles = useCallback(async () => {
671
+ if (!table_name || !record_id || !organisation_id || !supabase) {
672
+ setFileUrl(null);
673
+ setFileReference(null);
674
+ setFileReferences([]);
675
+ setFileUrls(/* @__PURE__ */ new Map());
676
+ setFileCount(0);
677
+ setIsLoading(false);
678
+ return;
679
+ }
680
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
681
+ if (!uuidRegex.test(organisation_id)) {
682
+ console.warn("[usePublicFileDisplay] Invalid organisationId format (not a valid UUID):", organisation_id);
683
+ }
684
+ const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
685
+ if (enableCache) {
686
+ const cached = publicFileCache.get(cacheKey);
687
+ if (cached && Date.now() - cached.timestamp < cached.ttl) {
688
+ const cachedData = cached.data;
689
+ setFileUrl(cachedData.fileUrl || null);
690
+ setFileReference(cachedData.fileReference || null);
691
+ setFileReferences(cachedData.fileReferences || []);
692
+ setFileUrls(cachedData.fileUrls || /* @__PURE__ */ new Map());
693
+ setFileCount(cachedData.fileCount || 0);
694
+ setIsLoading(false);
695
+ setError(null);
696
+ return;
697
+ }
698
+ }
699
+ try {
700
+ setIsLoading(true);
701
+ setError(null);
702
+ let files = [];
703
+ if (category) {
704
+ const { data, error: rpcError } = await supabase.rpc("data_file_reference_by_category_list", {
705
+ p_table_name: table_name,
706
+ p_record_id: record_id,
707
+ p_category: category,
708
+ p_organisation_id: organisation_id
709
+ });
710
+ if (rpcError) {
711
+ throw new Error(rpcError.message || "Failed to fetch file reference");
712
+ }
713
+ files = data || [];
714
+ } else {
715
+ const { data: fileIds, error: rpcError } = await supabase.rpc("data_file_reference_list", {
716
+ p_table_name: table_name,
717
+ p_record_id: record_id,
718
+ p_organisation_id: organisation_id
719
+ });
720
+ if (rpcError) {
721
+ throw new Error(rpcError.message || "Failed to fetch file references");
722
+ }
723
+ if (!fileIds || fileIds.length === 0) {
724
+ files = [];
725
+ } else {
726
+ const ids = fileIds.map((item) => item.id);
727
+ const { data: fullData, error: fetchError } = await supabase.from("file_references").select("*").in("id", ids).eq("is_public", true);
728
+ if (fetchError) {
729
+ throw new Error(fetchError.message || "Failed to fetch file references");
730
+ }
731
+ files = fullData || [];
732
+ }
733
+ }
734
+ const publicFiles = files.filter((f) => f.is_public === true);
735
+ if (publicFiles.length === 0) {
736
+ setFileUrl(null);
737
+ setFileReference(null);
738
+ setFileReferences([]);
739
+ setFileUrls(/* @__PURE__ */ new Map());
740
+ setFileCount(0);
741
+ if (enableCache) {
742
+ publicFileCache.set(cacheKey, {
743
+ data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: /* @__PURE__ */ new Map(), fileCount: 0 },
744
+ timestamp: Date.now(),
745
+ ttl: cacheTtl
746
+ });
747
+ }
748
+ return;
749
+ }
750
+ const fileRefs = publicFiles.map((f) => ({
751
+ id: f.id,
752
+ table_name: f.table_name,
753
+ record_id: f.record_id,
754
+ file_path: f.file_path,
755
+ file_metadata: f.file_metadata || {},
756
+ organisation_id: f.organisation_id,
757
+ app_id: f.app_id,
758
+ is_public: f.is_public ?? true,
759
+ created_at: f.created_at,
760
+ updated_at: f.updated_at
761
+ }));
762
+ setFileReferences(fileRefs);
763
+ setFileCount(fileRefs.length);
764
+ if (category && fileRefs.length > 0) {
765
+ const firstFile = fileRefs[0];
766
+ setFileReference(firstFile);
767
+ const url = getPublicUrl(supabase, firstFile.file_path, true);
768
+ setFileUrl(url);
769
+ } else {
770
+ const urlMap = /* @__PURE__ */ new Map();
771
+ for (const fileRef of fileRefs) {
772
+ const url = getPublicUrl(supabase, fileRef.file_path, true);
773
+ if (url) {
774
+ urlMap.set(fileRef.id, url);
775
+ }
776
+ }
777
+ setFileUrls(urlMap);
778
+ setFileReference(null);
779
+ setFileUrl(null);
780
+ }
781
+ if (enableCache) {
782
+ publicFileCache.set(cacheKey, {
783
+ data: {
784
+ fileUrl: category ? fileRefs.length > 0 ? getPublicUrl(supabase, fileRefs[0].file_path, true) : null : null,
785
+ fileReference: category && fileRefs.length > 0 ? fileRefs[0] : null,
786
+ fileReferences: fileRefs,
787
+ fileUrls: category ? /* @__PURE__ */ new Map() : (() => {
788
+ const urlMap = /* @__PURE__ */ new Map();
789
+ for (const fileRef of fileRefs) {
790
+ const url = getPublicUrl(supabase, fileRef.file_path, true);
791
+ if (url) {
792
+ urlMap.set(fileRef.id, url);
793
+ }
794
+ }
795
+ return urlMap;
796
+ })(),
797
+ fileCount: fileRefs.length
798
+ },
799
+ timestamp: Date.now(),
800
+ ttl: cacheTtl
801
+ });
802
+ }
803
+ } catch (err) {
804
+ console.error("[usePublicFileDisplay] Error fetching files:", err);
805
+ const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
806
+ setError(error2);
807
+ setFileUrl(null);
808
+ setFileReference(null);
809
+ setFileReferences([]);
810
+ setFileUrls(/* @__PURE__ */ new Map());
811
+ setFileCount(0);
812
+ } finally {
813
+ setIsLoading(false);
814
+ }
815
+ }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
816
+ useEffect(() => {
817
+ if (table_name && record_id && organisation_id) {
818
+ fetchFiles();
819
+ } else {
820
+ setFileUrl(null);
821
+ setFileReference(null);
822
+ setFileReferences([]);
823
+ setFileUrls(/* @__PURE__ */ new Map());
824
+ setFileCount(0);
825
+ setIsLoading(false);
826
+ setError(null);
827
+ }
828
+ }, [fetchFiles, table_name, record_id, organisation_id]);
829
+ const refetch = useCallback(async () => {
830
+ if (!table_name || !record_id || !organisation_id) return;
831
+ if (enableCache) {
832
+ const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
833
+ publicFileCache.delete(cacheKey);
834
+ }
835
+ await fetchFiles();
836
+ }, [fetchFiles, table_name, record_id, organisation_id, category, enableCache]);
837
+ return {
838
+ fileUrl,
839
+ fileReference,
840
+ fileReferences,
841
+ fileUrls,
842
+ fileCount,
843
+ isLoading,
844
+ error,
845
+ refetch
846
+ };
847
+ }
848
+ function clearPublicFileDisplayCache() {
849
+ for (const [key] of publicFileCache) {
850
+ if (key.startsWith("public_file_")) {
851
+ publicFileCache.delete(key);
852
+ }
853
+ }
854
+ }
855
+ function getPublicFileDisplayCacheStats() {
856
+ const keys = Array.from(publicFileCache.keys()).filter((key) => key.startsWith("public_file_"));
857
+ return {
858
+ size: keys.length,
859
+ keys
860
+ };
861
+ }
862
+
863
+ // src/hooks/useFileDisplay.ts
864
+ import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
865
+
866
+ // src/utils/file-reference.ts
867
+ init_organisationContext();
868
+ var FileReferenceServiceImpl = class {
869
+ constructor(supabase) {
870
+ this.supabase = supabase;
871
+ }
872
+ async createFileReference(options, file) {
873
+ try {
874
+ if (!options.organisation_id) {
875
+ throw new Error("organisation_id is required for file upload");
876
+ }
877
+ if (!options.table_name) {
878
+ throw new Error("table_name is required for file upload");
879
+ }
880
+ if (!options.record_id) {
881
+ throw new Error("record_id is required for file upload");
882
+ }
883
+ const uploadResult = await uploadFile(this.supabase, file, {
884
+ appName: "file-reference",
885
+ orgId: options.organisation_id,
886
+ isPublic: options.is_public || false,
887
+ customPath: options.category
888
+ // Use category as the custom path
889
+ });
890
+ if (!uploadResult.success) {
891
+ throw new Error(`Failed to upload file: ${uploadResult.error}`);
892
+ }
893
+ if (!uploadResult.path) {
894
+ throw new Error("File upload did not return a path");
895
+ }
896
+ const filePath = uploadResult.path;
897
+ const metadata = await extractFileMetadata(file, {
898
+ appName: "file-reference",
899
+ orgId: options.organisation_id,
900
+ isPublic: options.is_public || false
901
+ }, "system");
902
+ await setOrganisationContext(this.supabase, options.organisation_id);
903
+ const { data, error } = await this.supabase.rpc("data_file_reference_create", {
904
+ p_table_name: options.table_name,
905
+ p_record_id: options.record_id,
906
+ p_file_path: filePath,
907
+ p_organisation_id: options.organisation_id,
908
+ p_app_id: options.app_id,
909
+ p_file_metadata: {
910
+ fileName: file.name,
911
+ fileType: file.type,
912
+ fileSize: file.size,
913
+ category: options.category,
914
+ ...metadata,
915
+ ...options.custom_metadata
916
+ },
917
+ p_is_public: options.is_public || false
918
+ });
919
+ if (error) {
920
+ await deleteFile(this.supabase, filePath, options.is_public || false);
921
+ throw new Error(`Failed to create file reference: ${error.message}`);
922
+ }
923
+ const { data: fileRef, error: fetchError } = await this.supabase.from("file_references").select("*").eq("id", data).single();
924
+ if (fetchError || !fileRef) {
925
+ throw new Error(`Failed to fetch created file reference: ${fetchError?.message}`);
926
+ }
927
+ return fileRef;
928
+ } catch (error) {
929
+ console.error("Error creating file reference:", error);
930
+ throw error;
931
+ }
932
+ }
933
+ async getFileReference(table_name, record_id, organisation_id) {
934
+ try {
935
+ const { data, error } = await this.supabase.from("file_references").select("*").eq("table_name", table_name).eq("record_id", record_id).eq("organisation_id", organisation_id).single();
936
+ if (error) {
937
+ if (error.code === "PGRST116") {
938
+ return null;
939
+ }
940
+ throw new Error(`Failed to get file reference: ${error.message}`);
941
+ }
942
+ return data;
943
+ } catch (error) {
944
+ console.error("Error getting file reference:", error);
945
+ throw error;
946
+ }
947
+ }
948
+ async getFileUrl(table_name, record_id, organisation_id) {
949
+ try {
950
+ const fileRef = await this.getFileReference(table_name, record_id, organisation_id);
951
+ if (!fileRef) {
952
+ return null;
953
+ }
954
+ if (fileRef.is_public) {
955
+ const { data: pathData } = await this.supabase.rpc("data_file_reference_url_get", {
956
+ p_table_name: table_name,
957
+ p_record_id: record_id,
958
+ p_organisation_id: organisation_id
959
+ });
960
+ if (!pathData) {
961
+ return null;
962
+ }
963
+ return getPublicUrl(this.supabase, pathData, true);
964
+ } else {
965
+ return await this.getSignedUrl(table_name, record_id, organisation_id);
966
+ }
967
+ } catch (error) {
968
+ console.error("Error getting file URL:", error);
969
+ throw error;
970
+ }
971
+ }
972
+ async getSignedUrl(table_name, record_id, organisation_id, expires_in = 3600) {
973
+ try {
974
+ const { data: filePath, error } = await this.supabase.rpc("data_file_reference_signed_url_get", {
975
+ p_table_name: table_name,
976
+ p_record_id: record_id,
977
+ p_organisation_id: organisation_id,
978
+ p_expires_in: expires_in
979
+ });
980
+ if (error) {
981
+ throw new Error(`Failed to get signed URL: ${error.message}`);
982
+ }
983
+ if (!filePath) {
984
+ return null;
985
+ }
986
+ const signedUrlResult = await getSignedUrl(this.supabase, filePath, {
987
+ appName: "file-reference",
988
+ orgId: organisation_id,
989
+ expiresIn: expires_in
990
+ });
991
+ return signedUrlResult?.url || null;
992
+ } catch (error) {
993
+ console.error("Error getting signed URL:", error);
994
+ throw error;
995
+ }
996
+ }
997
+ async updateFileReference(id, updates) {
998
+ try {
999
+ const { data, error } = await this.supabase.from("file_references").update(updates).eq("id", id).select().single();
1000
+ if (error) {
1001
+ throw new Error(`Failed to update file reference: ${error.message}`);
1002
+ }
1003
+ return data;
1004
+ } catch (error) {
1005
+ console.error("Error updating file reference:", error);
1006
+ throw error;
1007
+ }
1008
+ }
1009
+ async deleteFileReference(table_name, record_id, organisation_id, delete_file = false) {
1010
+ try {
1011
+ const fileRef = await this.getFileReference(table_name, record_id, organisation_id);
1012
+ const { error } = await this.supabase.rpc("data_file_reference_delete", {
1013
+ p_table_name: table_name,
1014
+ p_record_id: record_id,
1015
+ p_organisation_id: organisation_id,
1016
+ p_delete_file: delete_file
1017
+ });
1018
+ if (error) {
1019
+ throw new Error(`Failed to delete file reference: ${error.message}`);
1020
+ }
1021
+ if (delete_file && fileRef) {
1022
+ await deleteFile(this.supabase, fileRef.file_path, fileRef.is_public || false);
1023
+ }
1024
+ return true;
1025
+ } catch (error) {
1026
+ console.error("Error deleting file reference:", error);
1027
+ throw error;
1028
+ }
1029
+ }
1030
+ async listFileReferences(table_name, record_id, organisation_id) {
1031
+ try {
1032
+ const { data, error } = await this.supabase.rpc("data_file_reference_list", {
1033
+ p_table_name: table_name,
1034
+ p_record_id: record_id,
1035
+ p_organisation_id: organisation_id
1036
+ });
1037
+ if (error) {
1038
+ throw new Error(`Failed to list file references: ${error.message}`);
1039
+ }
1040
+ if (!data || data.length === 0) {
1041
+ return [];
1042
+ }
1043
+ const ids = data.map((item) => item.id);
1044
+ const { data: fullData, error: fetchError } = await this.supabase.from("file_references").select("*").in("id", ids);
1045
+ if (fetchError) {
1046
+ throw new Error(`Failed to fetch file references: ${fetchError.message}`);
1047
+ }
1048
+ return fullData || [];
1049
+ } catch (error) {
1050
+ console.error("Error listing file references:", error);
1051
+ throw error;
1052
+ }
1053
+ }
1054
+ async getFileCount(table_name, record_id, organisation_id) {
1055
+ try {
1056
+ const { data, error } = await this.supabase.rpc("data_file_reference_count_get", {
1057
+ p_table_name: table_name,
1058
+ p_record_id: record_id,
1059
+ p_organisation_id: organisation_id
1060
+ });
1061
+ if (error) {
1062
+ throw new Error(`Failed to get file count: ${error.message}`);
1063
+ }
1064
+ return data || 0;
1065
+ } catch (error) {
1066
+ console.error("Error getting file count:", error);
1067
+ throw error;
1068
+ }
1069
+ }
1070
+ async getFileReferenceById(id, organisation_id) {
1071
+ try {
1072
+ const { data, error } = await this.supabase.rpc("data_file_reference_get", {
1073
+ p_file_reference_id: id,
1074
+ p_organisation_id: organisation_id
1075
+ });
1076
+ if (error) {
1077
+ throw new Error(`Failed to get file reference by ID: ${error.message}`);
1078
+ }
1079
+ if (!data || data.length === 0) {
1080
+ return null;
1081
+ }
1082
+ return data[0];
1083
+ } catch (error) {
1084
+ console.error("Error getting file reference by ID:", error);
1085
+ throw error;
1086
+ }
1087
+ }
1088
+ async getFilesByCategory(table_name, record_id, category, organisation_id) {
1089
+ try {
1090
+ const { data, error } = await this.supabase.rpc("data_file_reference_by_category_list", {
1091
+ p_table_name: table_name,
1092
+ p_record_id: record_id,
1093
+ p_category: category,
1094
+ p_organisation_id: organisation_id
1095
+ });
1096
+ if (error) {
1097
+ throw new Error(`Failed to get files by category: ${error.message}`);
1098
+ }
1099
+ if (!data || data.length === 0) {
1100
+ return [];
1101
+ }
1102
+ const ids = data.map((item) => item.id);
1103
+ const { data: fullData, error: fetchError } = await this.supabase.from("file_references").select("*").in("id", ids);
1104
+ if (fetchError) {
1105
+ throw new Error(`Failed to fetch file references: ${fetchError.message}`);
1106
+ }
1107
+ return fullData || [];
1108
+ } catch (error) {
1109
+ console.error("Error getting files by category:", error);
1110
+ throw error;
1111
+ }
1112
+ }
1113
+ async uploadMultipleFiles(options, files) {
1114
+ const success = [];
1115
+ const failed = [];
1116
+ const results = [];
1117
+ for (const file of files) {
1118
+ try {
1119
+ const fileReference = await this.createFileReference(options, file);
1120
+ success.push(fileReference);
1121
+ results.push({
1122
+ file,
1123
+ result: {
1124
+ file_reference: fileReference,
1125
+ file_url: fileReference.is_public ? getPublicUrl(this.supabase, fileReference.file_path, true) : ""
1126
+ }
1127
+ });
1128
+ } catch (error) {
1129
+ const message = error instanceof Error ? error.message : "Unknown error";
1130
+ failed.push({ file, error: message });
1131
+ results.push({ file, result: null, error: message });
1132
+ }
1133
+ }
1134
+ return {
1135
+ success,
1136
+ failed,
1137
+ total: results.length,
1138
+ successful: success.length,
1139
+ results
1140
+ };
1141
+ }
1142
+ };
1143
+ function createFileReferenceService(supabase) {
1144
+ return new FileReferenceServiceImpl(supabase);
1145
+ }
1146
+ async function uploadFileWithReference(supabase, options, file) {
1147
+ const service = createFileReferenceService(supabase);
1148
+ const fileReference = await service.createFileReference(options, file);
1149
+ const fileUrl = options.is_public ? getPublicUrl(supabase, fileReference.file_path, true) : await getSignedUrl(supabase, fileReference.file_path, {
1150
+ appName: "file-reference",
1151
+ orgId: options.organisation_id,
1152
+ expiresIn: 3600
1153
+ });
1154
+ const urlString = typeof fileUrl === "string" ? fileUrl : fileUrl?.url || "";
1155
+ return {
1156
+ file_reference: fileReference,
1157
+ file_url: urlString,
1158
+ signed_url: options.is_public ? void 0 : urlString || void 0
1159
+ };
1160
+ }
1161
+
1162
+ // src/hooks/useFileDisplay.ts
1163
+ var authenticatedFileCache = /* @__PURE__ */ new Map();
1164
+ var MAX_CACHE_SIZE = 100;
1165
+ function cleanupCache() {
1166
+ const now = Date.now();
1167
+ const entries = Array.from(authenticatedFileCache.entries());
1168
+ const expiredKeys = [];
1169
+ entries.forEach(([key, value]) => {
1170
+ if (now - value.timestamp >= value.ttl) {
1171
+ expiredKeys.push(key);
1172
+ }
1173
+ });
1174
+ expiredKeys.forEach((key) => authenticatedFileCache.delete(key));
1175
+ if (authenticatedFileCache.size > MAX_CACHE_SIZE) {
1176
+ const sorted = entries.filter(([key]) => !expiredKeys.includes(key)).sort((a, b) => a[1].timestamp - b[1].timestamp);
1177
+ const toRemove = sorted.slice(0, authenticatedFileCache.size - MAX_CACHE_SIZE);
1178
+ toRemove.forEach(([key]) => authenticatedFileCache.delete(key));
1179
+ }
1180
+ }
1181
+ function useFileDisplay(table_name, record_id, organisation_id, category, options) {
1182
+ const {
1183
+ cacheTtl = 30 * 60 * 1e3,
1184
+ // 30 minutes
1185
+ enableCache = true,
1186
+ supabase
1187
+ } = options;
1188
+ const [fileUrl, setFileUrl] = useState2(null);
1189
+ const [fileReference, setFileReference] = useState2(null);
1190
+ const [fileReferences, setFileReferences] = useState2([]);
1191
+ const [fileUrls, setFileUrls] = useState2(/* @__PURE__ */ new Map());
1192
+ const [fileCount, setFileCount] = useState2(0);
1193
+ const [isLoading, setIsLoading] = useState2(false);
1194
+ const [error, setError] = useState2(null);
1195
+ const fetchFiles = useCallback2(async () => {
1196
+ if (!table_name || !record_id || !organisation_id || !supabase) {
1197
+ setFileUrl(null);
1198
+ setFileReference(null);
1199
+ setFileReferences([]);
1200
+ setFileUrls(/* @__PURE__ */ new Map());
1201
+ setFileCount(0);
1202
+ setIsLoading(false);
1203
+ return;
1204
+ }
1205
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1206
+ if (!uuidRegex.test(organisation_id)) {
1207
+ console.warn("[useFileDisplay] Invalid organisationId format (not a valid UUID):", organisation_id);
1208
+ }
1209
+ const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
1210
+ if (enableCache) {
1211
+ const cached = authenticatedFileCache.get(cacheKey);
1212
+ if (cached && Date.now() - cached.timestamp < cached.ttl) {
1213
+ const cachedData = cached.data;
1214
+ setFileUrl(cachedData.fileUrl || null);
1215
+ setFileReference(cachedData.fileReference || null);
1216
+ setFileReferences(cachedData.fileReferences || []);
1217
+ setFileUrls(cachedData.fileUrls || /* @__PURE__ */ new Map());
1218
+ setFileCount(cachedData.fileCount || 0);
1219
+ setIsLoading(false);
1220
+ setError(null);
1221
+ return;
1222
+ }
1223
+ }
1224
+ try {
1225
+ setIsLoading(true);
1226
+ setError(null);
1227
+ const service = createFileReferenceService(supabase);
1228
+ let files = [];
1229
+ if (category) {
1230
+ files = await service.getFilesByCategory(
1231
+ table_name,
1232
+ record_id,
1233
+ category,
1234
+ organisation_id
1235
+ );
1236
+ } else {
1237
+ files = await service.listFileReferences(
1238
+ table_name,
1239
+ record_id,
1240
+ organisation_id
1241
+ );
1242
+ }
1243
+ if (files.length === 0) {
1244
+ setFileUrl(null);
1245
+ setFileReference(null);
1246
+ setFileReferences([]);
1247
+ setFileUrls(/* @__PURE__ */ new Map());
1248
+ setFileCount(0);
1249
+ if (enableCache) {
1250
+ authenticatedFileCache.set(cacheKey, {
1251
+ data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: /* @__PURE__ */ new Map(), fileCount: 0 },
1252
+ timestamp: Date.now(),
1253
+ ttl: cacheTtl
1254
+ });
1255
+ cleanupCache();
1256
+ }
1257
+ return;
1258
+ }
1259
+ setFileReferences(files);
1260
+ setFileCount(files.length);
1261
+ if (category && files.length > 0) {
1262
+ const firstFile = files[0];
1263
+ setFileReference(firstFile);
1264
+ let url = null;
1265
+ if (firstFile.is_public) {
1266
+ url = getPublicUrl(supabase, firstFile.file_path, true);
1267
+ } else {
1268
+ const signedUrlResult = await getSignedUrl(supabase, firstFile.file_path, {
1269
+ appName: "pace-core",
1270
+ orgId: organisation_id,
1271
+ expiresIn: 3600
1272
+ });
1273
+ url = signedUrlResult?.url || null;
1274
+ }
1275
+ setFileUrl(url);
1276
+ } else {
1277
+ const urlMap = /* @__PURE__ */ new Map();
1278
+ for (const fileRef of files) {
1279
+ let url = null;
1280
+ if (fileRef.is_public) {
1281
+ url = getPublicUrl(supabase, fileRef.file_path, true);
1282
+ } else {
1283
+ const signedUrlResult = await getSignedUrl(supabase, fileRef.file_path, {
1284
+ appName: "pace-core",
1285
+ orgId: organisation_id,
1286
+ expiresIn: 3600
1287
+ });
1288
+ url = signedUrlResult?.url || null;
1289
+ }
1290
+ if (url) {
1291
+ urlMap.set(fileRef.id, url);
1292
+ }
1293
+ }
1294
+ setFileUrls(urlMap);
1295
+ setFileReference(null);
1296
+ setFileUrl(null);
1297
+ }
1298
+ if (enableCache) {
1299
+ let cacheData = {
1300
+ fileReference: category && files.length > 0 ? files[0] : null,
1301
+ fileReferences: files,
1302
+ fileUrls: /* @__PURE__ */ new Map(),
1303
+ fileCount: files.length
1304
+ };
1305
+ if (category && files.length > 0) {
1306
+ const firstFile = files[0];
1307
+ let url = null;
1308
+ if (firstFile.is_public) {
1309
+ url = getPublicUrl(supabase, firstFile.file_path, true);
1310
+ }
1311
+ cacheData.fileUrl = url;
1312
+ } else {
1313
+ const urlMap = /* @__PURE__ */ new Map();
1314
+ for (const fileRef of files) {
1315
+ if (fileRef.is_public) {
1316
+ const url = getPublicUrl(supabase, fileRef.file_path, true);
1317
+ if (url) {
1318
+ urlMap.set(fileRef.id, url);
1319
+ }
1320
+ }
1321
+ }
1322
+ cacheData.fileUrls = urlMap;
1323
+ }
1324
+ authenticatedFileCache.set(cacheKey, {
1325
+ data: cacheData,
1326
+ timestamp: Date.now(),
1327
+ ttl: cacheTtl
1328
+ });
1329
+ cleanupCache();
1330
+ }
1331
+ } catch (err) {
1332
+ console.error("[useFileDisplay] Error fetching files:", err);
1333
+ const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
1334
+ setError(error2);
1335
+ setFileUrl(null);
1336
+ setFileReference(null);
1337
+ setFileReferences([]);
1338
+ setFileUrls(/* @__PURE__ */ new Map());
1339
+ setFileCount(0);
1340
+ } finally {
1341
+ setIsLoading(false);
1342
+ }
1343
+ }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
1344
+ useEffect2(() => {
1345
+ if (table_name && record_id && organisation_id && supabase) {
1346
+ fetchFiles();
1347
+ } else {
1348
+ setFileUrl(null);
1349
+ setFileReference(null);
1350
+ setFileReferences([]);
1351
+ setFileUrls(/* @__PURE__ */ new Map());
1352
+ setFileCount(0);
1353
+ setIsLoading(false);
1354
+ setError(null);
1355
+ }
1356
+ }, [fetchFiles, table_name, record_id, organisation_id, supabase]);
1357
+ const refetch = useCallback2(async () => {
1358
+ if (!table_name || !record_id || !organisation_id || !supabase) return;
1359
+ if (enableCache) {
1360
+ const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || "all"}`;
1361
+ authenticatedFileCache.delete(cacheKey);
1362
+ }
1363
+ await fetchFiles();
1364
+ }, [fetchFiles, table_name, record_id, organisation_id, category, supabase, enableCache]);
1365
+ return {
1366
+ fileUrl,
1367
+ fileReference,
1368
+ fileReferences,
1369
+ fileUrls,
1370
+ fileCount,
1371
+ isLoading,
1372
+ error,
1373
+ refetch
1374
+ };
1375
+ }
1376
+ function clearFileDisplayCache() {
1377
+ for (const [key] of authenticatedFileCache) {
1378
+ if (key.startsWith("file_")) {
1379
+ authenticatedFileCache.delete(key);
1380
+ }
1381
+ }
1382
+ }
1383
+ function getFileDisplayCacheStats() {
1384
+ const keys = Array.from(authenticatedFileCache.keys()).filter((key) => key.startsWith("file_"));
1385
+ return {
1386
+ size: keys.length,
1387
+ keys
1388
+ };
1389
+ }
1390
+
649
1391
  // src/hooks/public/usePublicEventLogo.ts
650
- import { useState, useEffect, useCallback, useMemo as useMemo3 } from "react";
1392
+ import { useState as useState3, useEffect as useEffect3, useCallback as useCallback3, useMemo as useMemo4 } from "react";
651
1393
  var publicDataCache = /* @__PURE__ */ new Map();
652
1394
  function defaultGenerateFallbackText(eventName) {
653
1395
  if (!eventName) return "EV";
@@ -662,13 +1404,13 @@ function usePublicEventLogo(eventId, eventName, organisationId, options) {
662
1404
  generateFallbackText = defaultGenerateFallbackText,
663
1405
  supabase
664
1406
  } = options;
665
- const [logoUrl, setLogoUrl] = useState(null);
666
- const [isLoading, setIsLoading] = useState(false);
667
- const [error, setError] = useState(null);
668
- const fallbackText = useMemo3(() => {
1407
+ const [logoUrl, setLogoUrl] = useState3(null);
1408
+ const [isLoading, setIsLoading] = useState3(false);
1409
+ const [error, setError] = useState3(null);
1410
+ const fallbackText = useMemo4(() => {
669
1411
  return eventName ? generateFallbackText(eventName) : "EV";
670
1412
  }, [eventName, generateFallbackText]);
671
- const fetchLogo = useCallback(async () => {
1413
+ const fetchLogo = useCallback3(async () => {
672
1414
  if (!eventId || !organisationId || !supabase) {
673
1415
  setLogoUrl(null);
674
1416
  setIsLoading(false);
@@ -738,7 +1480,7 @@ function usePublicEventLogo(eventId, eventName, organisationId, options) {
738
1480
  setIsLoading(false);
739
1481
  }
740
1482
  }, [eventId, organisationId, supabase, cacheTtl, enableCache, validateImage]);
741
- useEffect(() => {
1483
+ useEffect3(() => {
742
1484
  if (eventId && organisationId) {
743
1485
  fetchLogo();
744
1486
  } else {
@@ -747,7 +1489,7 @@ function usePublicEventLogo(eventId, eventName, organisationId, options) {
747
1489
  setError(null);
748
1490
  }
749
1491
  }, [fetchLogo, eventId, organisationId]);
750
- const refetch = useCallback(async () => {
1492
+ const refetch = useCallback3(async () => {
751
1493
  if (!eventId || !organisationId) return;
752
1494
  if (enableCache) {
753
1495
  const cacheKey = `public_logo_${eventId}_${organisationId}`;
@@ -806,8 +1548,16 @@ export {
806
1548
  listFiles,
807
1549
  downloadFile,
808
1550
  archiveFile,
1551
+ createFileReferenceService,
1552
+ uploadFileWithReference,
1553
+ usePublicFileDisplay,
1554
+ clearPublicFileDisplayCache,
1555
+ getPublicFileDisplayCacheStats,
1556
+ useFileDisplay,
1557
+ clearFileDisplayCache,
1558
+ getFileDisplayCacheStats,
809
1559
  usePublicEventLogo,
810
1560
  clearPublicLogoCache,
811
1561
  getPublicLogoCacheStats
812
1562
  };
813
- //# sourceMappingURL=chunk-M52CQP5W.js.map
1563
+ //# sourceMappingURL=chunk-MKMKUCPF.js.map