@jmruthers/pace-core 0.5.87 → 0.5.89
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/{AuthService-Df3IozMG.d.ts → AuthService-DcTI5Ov4.d.ts} +9 -0
- package/dist/{DataTable-FA6EUX5M.js → DataTable-PWBMKMOG.js} +7 -7
- package/dist/{PublicLoadingSpinner-DecuJBX0.d.ts → PublicLoadingSpinner-BQXD1fbO.d.ts} +160 -130
- package/dist/{UnifiedAuthProvider-K2IZAY5F.js → UnifiedAuthProvider-5D3HEQND.js} +4 -4
- package/dist/{UnifiedAuthProvider-B391Aqum.d.ts → UnifiedAuthProvider-BVKmQd9u.d.ts} +4 -0
- package/dist/auth-DReDSLq9.d.ts +16 -0
- package/dist/{chunk-CBSD3BZ3.js → chunk-3RZBKQ5Y.js} +2 -6
- package/dist/{chunk-CBSD3BZ3.js.map → chunk-3RZBKQ5Y.js.map} +1 -1
- package/dist/{chunk-NTW3KGS4.js → chunk-6UHXQH7P.js} +5 -5
- package/dist/{chunk-ZFLOV3OM.js → chunk-7VJDS5QD.js} +401 -16
- package/dist/chunk-7VJDS5QD.js.map +1 -0
- package/dist/{chunk-YVUZWLQG.js → chunk-AQGF5OG7.js} +3 -3
- package/dist/{chunk-CVMVPYAL.js → chunk-BDZUMRBD.js} +3 -5
- package/dist/chunk-BDZUMRBD.js.map +1 -0
- package/dist/{chunk-KAY3K5TP.js → chunk-BNXBJOGL.js} +4 -4
- package/dist/{chunk-S3JKDMD5.js → chunk-CXKMRKRF.js} +4 -4
- package/dist/{chunk-5BN3YGNK.js → chunk-DP5X5ORK.js} +217 -27
- package/dist/chunk-DP5X5ORK.js.map +1 -0
- package/dist/{chunk-RIXPZJUB.js → chunk-KTPG5VCH.js} +2 -2
- package/dist/{chunk-2FQEQUJT.js → chunk-KWICIQVK.js} +4 -4
- package/dist/{chunk-WUXCWRL6.js → chunk-XJ2HZOBU.js} +6 -1
- package/dist/chunk-XJ2HZOBU.js.map +1 -0
- package/dist/{chunk-I7O3RSMN.js → chunk-YWAFPVJA.js} +1298 -769
- package/dist/chunk-YWAFPVJA.js.map +1 -0
- package/dist/{chunk-I2VVV5PQ.js → chunk-YY4YYM3E.js} +2 -2
- package/dist/components.d.ts +6 -55
- package/dist/components.js +24 -205
- package/dist/components.js.map +1 -1
- package/dist/{file-reference-9xUOnwyt.d.ts → file-reference-C9isKNPn.d.ts} +67 -2
- package/dist/hooks.js +9 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +152 -26
- package/dist/index.js +64 -194
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +5 -3
- package/dist/providers.js +3 -3
- package/dist/rbac/index.js +8 -8
- package/dist/types.d.ts +2 -1
- package/dist/types.js +3 -3
- package/dist/utils.js +2 -2
- package/docs/DOCUMENTATION_AUDIT.md +6 -6
- package/docs/DOCUMENTATION_STANDARD.md +137 -0
- package/docs/README.md +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 +83 -40
- package/docs/api/enums/FileCategory.md +56 -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 +11 -11
- package/docs/api/interfaces/FileDisplayProps.md +10 -10
- 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 +8 -8
- package/docs/api/interfaces/FileUploadProps.md +137 -42
- 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/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 +1 -1
- 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 +83 -50
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseEventLogoOptions.md +74 -0
- package/docs/api/interfaces/UseEventLogoReturn.md +81 -0
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +6 -6
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- 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 +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +290 -95
- package/docs/api-reference/components.md +1 -18
- package/docs/api-reference/hooks.md +1 -4
- package/docs/best-practices/testing.md +2 -0
- package/docs/documentation-index.md +1 -1
- package/docs/getting-started/faq.md +1 -1
- package/docs/implementation-guides/file-reference-system.md +592 -58
- package/docs/implementation-guides/file-upload-storage.md +137 -73
- package/docs/implementation-guides/public-pages-advanced.md +10 -0
- package/docs/rbac/super-admin-guide.md +18 -70
- package/docs/testing/README.md +2 -0
- package/package.json +1 -1
- package/src/__tests__/TEST_STANDARD.md +674 -0
- package/src/__tests__/helpers/test-utils.tsx +3 -2
- package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx.skip → DataTable.comprehensive.test.tsx} +17 -18
- package/src/components/DataTable/__tests__/{DataTable.test.tsx.skip → DataTable.test.tsx} +14 -22
- package/src/components/DataTable/__tests__/{ssr.strict-mode.test.tsx.skip → ssr.strict-mode.test.tsx} +42 -47
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +1 -1
- package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +13 -4
- package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +1 -1
- package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +10 -6
- package/src/components/FileDisplay/FileDisplay.test.tsx +257 -0
- package/src/components/{FileDisplay.tsx → FileDisplay/FileDisplay.tsx} +111 -10
- package/src/components/FileDisplay/index.tsx +4 -0
- package/src/components/FileUpload/FileUpload.test.tsx +171 -621
- package/src/components/FileUpload/FileUpload.tsx +512 -168
- package/src/components/FileUpload/index.tsx +4 -0
- package/src/components/Progress/Progress.test.tsx +38 -0
- package/src/components/PublicLayout/EventLogo.tsx +6 -4
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/SessionRestorationLoader.tsx +48 -0
- package/src/components/Toast/Toast.tsx +13 -8
- package/src/components/index.ts +16 -16
- package/src/hooks/__tests__/ServiceHooks.test.tsx +615 -0
- package/src/hooks/public/usePublicEventLogo.ts +16 -20
- package/src/hooks/useEventLogo.ts +316 -0
- package/src/hooks/useEvents.ts +0 -5
- package/src/hooks/useFileReference.test.ts +659 -0
- package/src/hooks/useFileReference.ts +207 -3
- package/src/hooks/useSessionRestoration.ts +64 -0
- package/src/index.ts +17 -5
- package/src/providers/{UnifiedAuthProvider.test.simple.tsx → UnifiedAuthProvider.smoke.test.tsx} +81 -60
- package/src/providers/services/AuthServiceProvider.tsx +27 -3
- package/src/providers/services/UnifiedAuthProvider.tsx +34 -5
- package/src/rbac/{engine.test.simple.ts → RBACEngine.smoke.test.ts} +17 -12
- package/src/services/AuthService.ts +142 -20
- package/src/services/EventService.ts +0 -4
- package/src/types/auth.ts +15 -0
- package/src/types/file-reference.ts +73 -1
- package/src/types/index.ts +1 -0
- package/src/utils/__tests__/organisationContext.unit.test.ts +2 -4
- package/src/utils/appNameResolver.simple.test.ts +99 -29
- package/src/utils/file-reference.test.ts +535 -0
- package/src/utils/file-reference.ts +200 -30
- package/src/utils/organisationContext.test.ts +5 -19
- package/src/utils/organisationContext.ts +3 -5
- package/src/utils/storage/README.md +269 -262
- package/src/utils/storage/config.ts +9 -0
- package/src/utils/storage/helpers.test.ts +735 -0
- package/src/utils/storage/helpers.ts +189 -16
- package/src/utils/storage/index.ts +3 -0
- package/src/validation/__tests__/sanitization.unit.test.ts +1 -1
- package/src/validation/__tests__/schemaUtils.unit.test.ts +1 -1
- package/src/validation/__tests__/user.unit.test.ts +1 -1
- package/dist/chunk-5BN3YGNK.js.map +0 -1
- package/dist/chunk-CVMVPYAL.js.map +0 -1
- package/dist/chunk-I7O3RSMN.js.map +0 -1
- package/dist/chunk-WUXCWRL6.js.map +0 -1
- package/dist/chunk-ZFLOV3OM.js.map +0 -1
- package/docs/CONTENT_AUDIT_REPORT.md +0 -253
- package/docs/STYLE_GUIDE.md +0 -37
- package/examples/RBAC/__tests__/PermissionExample.test.tsx +0 -150
- package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +0 -159
- package/src/__tests__/TEST_GUIDE_CURSOR.md +0 -1605
- package/src/__tests__/TEST_GUIDE_HUMAN.md +0 -103
- package/src/components/FileUpload/FileUpload.example.tsx +0 -218
- package/src/components/FileUpload/index.ts +0 -6
- package/src/components/FileUpload.tsx +0 -176
- package/src/components/Progress/index.ts +0 -3
- package/src/components/PublicLayout/__tests__/EventLogo.test.tsx +0 -666
- package/src/components/SuperAdminGuard.tsx +0 -116
- package/src/components/__tests__/FileDisplay.test.tsx +0 -575
- package/src/components/__tests__/FileUpload.test.tsx +0 -446
- package/src/components/__tests__/SuperAdminGuard.test.tsx +0 -627
- package/src/components/examples/PermissionExample.tsx +0 -173
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -583
- package/src/hooks/__tests__/usePublicEventLogo.unit.test.ts +0 -640
- package/src/types/__tests__/file-reference.test.ts +0 -447
- package/src/utils/__tests__/file-reference.test.ts +0 -383
- /package/dist/{DataTable-FA6EUX5M.js.map → DataTable-PWBMKMOG.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-K2IZAY5F.js.map → UnifiedAuthProvider-5D3HEQND.js.map} +0 -0
- /package/dist/{chunk-NTW3KGS4.js.map → chunk-6UHXQH7P.js.map} +0 -0
- /package/dist/{chunk-YVUZWLQG.js.map → chunk-AQGF5OG7.js.map} +0 -0
- /package/dist/{chunk-KAY3K5TP.js.map → chunk-BNXBJOGL.js.map} +0 -0
- /package/dist/{chunk-S3JKDMD5.js.map → chunk-CXKMRKRF.js.map} +0 -0
- /package/dist/{chunk-RIXPZJUB.js.map → chunk-KTPG5VCH.js.map} +0 -0
- /package/dist/{chunk-2FQEQUJT.js.map → chunk-KWICIQVK.js.map} +0 -0
- /package/dist/{chunk-I2VVV5PQ.js.map → chunk-YY4YYM3E.js.map} +0 -0
- /package/src/providers/{OrganisationProvider.test.simple.tsx → OrganisationProvider.context.test.tsx} +0 -0
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
// Provides a file display interface using the file reference system
|
|
3
|
-
|
|
4
|
-
import React, { useState, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
5
2
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
6
|
-
import { FileReference, FileCategory } from '
|
|
7
|
-
import { useFileReferenceForRecord } from '
|
|
3
|
+
import { FileReference, FileCategory } from '../../types/file-reference';
|
|
4
|
+
import { useFileReferenceForRecord } from '../../hooks/useFileReference';
|
|
5
|
+
import { getPublicUrl, getSignedUrl } from '../../utils/storage/helpers';
|
|
8
6
|
|
|
9
7
|
export interface FileDisplayProps {
|
|
10
8
|
supabase: SupabaseClient;
|
|
@@ -45,6 +43,25 @@ export function FileDisplay({
|
|
|
45
43
|
} = useFileReferenceForRecord(supabase, table_name, record_id, organisation_id);
|
|
46
44
|
|
|
47
45
|
const [imageError, setImageError] = useState(false);
|
|
46
|
+
const [fileUrls, setFileUrls] = useState<Map<string, string>>(new Map());
|
|
47
|
+
const [loadingUrls, setLoadingUrls] = useState<Set<string>>(new Set());
|
|
48
|
+
const loadedFilesRef = useRef<Set<string>>(new Set());
|
|
49
|
+
const loadingUrlsRef = useRef<Set<string>>(new Set());
|
|
50
|
+
const fileReferencesRef = useRef<FileReference[]>([]);
|
|
51
|
+
|
|
52
|
+
// Track file references to detect when they change
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const currentIds = fileReferences.map(f => f.id).join(',');
|
|
55
|
+
const prevIds = fileReferencesRef.current.map(f => f.id).join(',');
|
|
56
|
+
|
|
57
|
+
if (currentIds !== prevIds) {
|
|
58
|
+
fileReferencesRef.current = fileReferences;
|
|
59
|
+
// Reset loaded files ref when file references change
|
|
60
|
+
loadedFilesRef.current.clear();
|
|
61
|
+
setFileUrls(new Map());
|
|
62
|
+
setLoadingUrls(new Set());
|
|
63
|
+
}
|
|
64
|
+
}, [fileReferences]);
|
|
48
65
|
|
|
49
66
|
// Load file data on mount
|
|
50
67
|
useEffect(() => {
|
|
@@ -63,6 +80,70 @@ export function FileDisplay({
|
|
|
63
80
|
}
|
|
64
81
|
}, [fileReference, loadFileUrl]);
|
|
65
82
|
|
|
83
|
+
// Fetch URLs for all file references (for multiple files view)
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (category || fileReferences.length === 0) return;
|
|
86
|
+
|
|
87
|
+
const loadFileUrls = async () => {
|
|
88
|
+
// Find files that need URLs loaded
|
|
89
|
+
const urlsToLoad = fileReferences.filter(fileRef => {
|
|
90
|
+
return !loadedFilesRef.current.has(fileRef.id) && !loadingUrlsRef.current.has(fileRef.id);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (urlsToLoad.length === 0) return;
|
|
94
|
+
|
|
95
|
+
// Mark files as loading (update both state and ref)
|
|
96
|
+
setLoadingUrls(prev => {
|
|
97
|
+
const updated = new Set(prev);
|
|
98
|
+
urlsToLoad.forEach(fileRef => {
|
|
99
|
+
updated.add(fileRef.id);
|
|
100
|
+
loadingUrlsRef.current.add(fileRef.id);
|
|
101
|
+
});
|
|
102
|
+
return updated;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Load URLs for files that need them
|
|
106
|
+
for (const fileRef of urlsToLoad) {
|
|
107
|
+
try {
|
|
108
|
+
let url: string | null = null;
|
|
109
|
+
|
|
110
|
+
if (fileRef.is_public) {
|
|
111
|
+
// Public files: generate public URL
|
|
112
|
+
url = getPublicUrl(supabase, fileRef.file_path, true);
|
|
113
|
+
} else {
|
|
114
|
+
// Private files: generate signed URL
|
|
115
|
+
const signedUrlResult = await getSignedUrl(supabase, fileRef.file_path, {
|
|
116
|
+
appName: 'file-reference',
|
|
117
|
+
orgId: organisation_id,
|
|
118
|
+
expiresIn: 3600
|
|
119
|
+
});
|
|
120
|
+
url = signedUrlResult?.url || null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (url) {
|
|
124
|
+
setFileUrls(prev => {
|
|
125
|
+
const updated = new Map(prev);
|
|
126
|
+
updated.set(fileRef.id, url!);
|
|
127
|
+
return updated;
|
|
128
|
+
});
|
|
129
|
+
loadedFilesRef.current.add(fileRef.id);
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(`Failed to load URL for file ${fileRef.id}:`, error);
|
|
133
|
+
} finally {
|
|
134
|
+
setLoadingUrls(prev => {
|
|
135
|
+
const updated = new Set(prev);
|
|
136
|
+
updated.delete(fileRef.id);
|
|
137
|
+
loadingUrlsRef.current.delete(fileRef.id);
|
|
138
|
+
return updated;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
loadFileUrls();
|
|
145
|
+
}, [category, fileReferences.map(f => f.id).join(','), supabase, organisation_id]);
|
|
146
|
+
|
|
66
147
|
const handleDelete = async () => {
|
|
67
148
|
if (window.confirm('Are you sure you want to delete this file?')) {
|
|
68
149
|
const success = await deleteFile(true);
|
|
@@ -147,6 +228,7 @@ export function FileDisplay({
|
|
|
147
228
|
onClick={handleDelete}
|
|
148
229
|
className="absolute top-2 right-2 bg-acc-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm hover:bg-acc-600"
|
|
149
230
|
title="Delete file"
|
|
231
|
+
aria-label="Delete file"
|
|
150
232
|
>
|
|
151
233
|
×
|
|
152
234
|
</button>
|
|
@@ -171,6 +253,7 @@ export function FileDisplay({
|
|
|
171
253
|
onClick={handleDelete}
|
|
172
254
|
className="text-acc-500 hover:text-acc-700 p-1"
|
|
173
255
|
title="Delete file"
|
|
256
|
+
aria-label="Delete file"
|
|
174
257
|
>
|
|
175
258
|
×
|
|
176
259
|
</button>
|
|
@@ -187,13 +270,17 @@ export function FileDisplay({
|
|
|
187
270
|
<div className={`space-y-2 ${className}`}>
|
|
188
271
|
{fileReferences.map((fileRef) => {
|
|
189
272
|
const isImage = fileRef.file_metadata.fileType?.startsWith('image/');
|
|
190
|
-
const fileUrl = fileRef.
|
|
191
|
-
|
|
192
|
-
|
|
273
|
+
const fileUrl = fileUrls.get(fileRef.id) || null;
|
|
274
|
+
const isLoadingUrl = loadingUrls.has(fileRef.id);
|
|
275
|
+
const canDownload = !isImage && fileUrl;
|
|
193
276
|
|
|
194
277
|
return (
|
|
195
278
|
<div key={fileRef.id} className="flex items-center space-x-3 p-3 bg-sec-50 rounded-lg border border-sec-200">
|
|
196
|
-
{
|
|
279
|
+
{isLoadingUrl ? (
|
|
280
|
+
<div className="w-12 h-12 flex items-center justify-center">
|
|
281
|
+
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-main-500"></div>
|
|
282
|
+
</div>
|
|
283
|
+
) : isImage && fileUrl ? (
|
|
197
284
|
<img
|
|
198
285
|
src={fileUrl}
|
|
199
286
|
alt={fileRef.file_metadata.fileName || 'File'}
|
|
@@ -215,6 +302,17 @@ export function FileDisplay({
|
|
|
215
302
|
{fileRef.file_metadata.category && ` • ${fileRef.file_metadata.category}`}
|
|
216
303
|
</div>
|
|
217
304
|
</div>
|
|
305
|
+
<div className="flex items-center space-x-2">
|
|
306
|
+
{canDownload && (
|
|
307
|
+
<a
|
|
308
|
+
href={fileRef.file_path}
|
|
309
|
+
download={fileRef.file_metadata.fileName || 'download'}
|
|
310
|
+
className="text-main-500 hover:text-main-700 p-1"
|
|
311
|
+
title="Download file"
|
|
312
|
+
>
|
|
313
|
+
↓
|
|
314
|
+
</a>
|
|
315
|
+
)}
|
|
218
316
|
{showDelete && (
|
|
219
317
|
<button
|
|
220
318
|
onClick={() => deleteFile(true)}
|
|
@@ -224,6 +322,7 @@ export function FileDisplay({
|
|
|
224
322
|
×
|
|
225
323
|
</button>
|
|
226
324
|
)}
|
|
325
|
+
</div>
|
|
227
326
|
</div>
|
|
228
327
|
);
|
|
229
328
|
})}
|
|
@@ -231,3 +330,5 @@ export function FileDisplay({
|
|
|
231
330
|
</div>
|
|
232
331
|
);
|
|
233
332
|
}
|
|
333
|
+
|
|
334
|
+
|