@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.
- package/dist/{DataTable-HC5S4RKB.js → DataTable-CHX2EFO3.js} +6 -6
- package/dist/{PublicLoadingSpinner-n74JgA9h.d.ts → PublicLoadingSpinner-BWUD6bLU.d.ts} +24 -3
- package/dist/{UnifiedAuthProvider-ZM7VUC45.js → UnifiedAuthProvider-H7RI4KYD.js} +3 -3
- package/dist/{chunk-AZ2QJYKU.js → chunk-2KLAOD4M.js} +3 -3
- package/dist/{chunk-HW5BGOWB.js → chunk-2ZYHCFUO.js} +4 -4
- package/dist/{chunk-AAM57AEU.js → chunk-5RYPBJYL.js} +16 -19
- package/dist/chunk-5RYPBJYL.js.map +1 -0
- package/dist/{chunk-XIBSVWJW.js → chunk-7TQDRDSM.js} +5 -5
- package/dist/{chunk-GP3HU6WS.js → chunk-G7UUVEAP.js} +3 -3
- package/dist/{chunk-M52CQP5W.js → chunk-MKMKUCPF.js} +762 -12
- package/dist/chunk-MKMKUCPF.js.map +1 -0
- package/dist/{chunk-OXFOS62D.js → chunk-MVNOAHOP.js} +2 -2
- package/dist/{chunk-5LAY74WM.js → chunk-O6GASC4Q.js} +877 -788
- package/dist/chunk-O6GASC4Q.js.map +1 -0
- package/dist/{chunk-AYC2P377.js → chunk-ORACUZ7H.js} +2 -2
- package/dist/{chunk-TZXYSZT3.js → chunk-PRM6EYO3.js} +298 -238
- package/dist/{chunk-TZXYSZT3.js.map → chunk-PRM6EYO3.js.map} +1 -1
- package/dist/{chunk-6WFM22A4.js → chunk-ZGCVJ7WW.js} +2 -2
- package/dist/components.d.ts +1 -1
- package/dist/components.js +8 -8
- package/dist/hooks.d.ts +94 -3
- package/dist/hooks.js +20 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +17 -11
- package/dist/index.js.map +1 -1
- package/dist/providers.js +2 -2
- package/dist/rbac/index.js +7 -7
- package/dist/{usePublicRouteParams-BlgwXweB.d.ts → usePublicRouteParams-BwMR2uub.d.ts} +93 -1
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +26 -11
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +24 -11
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UseEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +47 -0
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +120 -0
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +105 -19
- package/docs/api-reference/components.md +146 -1
- package/docs/best-practices/common-patterns.md +26 -8
- package/docs/getting-started/examples/README.md +10 -26
- package/docs/getting-started/quick-reference.md +23 -0
- package/docs/implementation-guides/authentication.md +39 -16
- package/docs/implementation-guides/file-reference-system.md +15 -0
- package/docs/implementation-guides/file-upload-storage.md +16 -0
- package/package.json +1 -1
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +9 -7
- package/src/components/DataTable/components/DataTableCore.tsx +8 -1
- package/src/components/DataTable/components/EditableRow.tsx +62 -22
- package/src/components/DataTable/components/UnifiedTableBody.tsx +25 -101
- package/src/components/FileDisplay/FileDisplay.test.tsx +263 -39
- package/src/components/FileDisplay/FileDisplay.tsx +605 -83
- package/src/components/ProtectedRoute/README.md +164 -0
- package/src/components/PublicLayout/EventLogo.tsx +8 -2
- package/src/components/PublicLayout/PublicPageHeader.tsx +20 -18
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +75 -29
- package/src/components/Select/Select.test.tsx +83 -6
- package/src/components/Select/Select.tsx +236 -16
- package/src/examples/CorrectPublicPageImplementation.tsx +16 -13
- package/src/examples/PublicEventPage.tsx +9 -6
- package/src/examples/PublicPageApp.tsx +9 -6
- package/src/examples/PublicPageUsageExample.tsx +9 -7
- package/src/hooks/index.ts +4 -0
- package/src/hooks/public/index.ts +2 -0
- package/src/hooks/public/usePublicFileDisplay.ts +355 -0
- package/src/hooks/useFileDisplay.ts +370 -0
- package/src/services/AuthService.ts +19 -22
- package/dist/chunk-5LAY74WM.js.map +0 -1
- package/dist/chunk-AAM57AEU.js.map +0 -1
- package/dist/chunk-M52CQP5W.js.map +0 -1
- /package/dist/{DataTable-HC5S4RKB.js.map → DataTable-CHX2EFO3.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ZM7VUC45.js.map → UnifiedAuthProvider-H7RI4KYD.js.map} +0 -0
- /package/dist/{chunk-AZ2QJYKU.js.map → chunk-2KLAOD4M.js.map} +0 -0
- /package/dist/{chunk-HW5BGOWB.js.map → chunk-2ZYHCFUO.js.map} +0 -0
- /package/dist/{chunk-XIBSVWJW.js.map → chunk-7TQDRDSM.js.map} +0 -0
- /package/dist/{chunk-GP3HU6WS.js.map → chunk-G7UUVEAP.js.map} +0 -0
- /package/dist/{chunk-OXFOS62D.js.map → chunk-MVNOAHOP.js.map} +0 -0
- /package/dist/{chunk-AYC2P377.js.map → chunk-ORACUZ7H.js.map} +0 -0
- /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-
|
|
4
|
+
} from "./chunk-MVNOAHOP.js";
|
|
5
5
|
import {
|
|
6
6
|
init_UnifiedAuthProvider
|
|
7
|
-
} from "./chunk-
|
|
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-
|
|
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
|
|
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] =
|
|
666
|
-
const [isLoading, setIsLoading] =
|
|
667
|
-
const [error, setError] =
|
|
668
|
-
const fallbackText =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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-
|
|
1563
|
+
//# sourceMappingURL=chunk-MKMKUCPF.js.map
|