@jmruthers/pace-core 0.5.87 → 0.5.88
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-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-I7O3RSMN.js → chunk-CJIZS3UE.js} +1298 -769
- package/dist/chunk-CJIZS3UE.js.map +1 -0
- 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-ZFLOV3OM.js → chunk-H3P2RGKZ.js} +352 -16
- package/dist/chunk-H3P2RGKZ.js.map +1 -0
- package/dist/{chunk-RIXPZJUB.js → chunk-KTPG5VCH.js} +2 -2
- package/dist/{chunk-WUXCWRL6.js → chunk-XJ2HZOBU.js} +6 -1
- package/dist/chunk-XJ2HZOBU.js.map +1 -0
- package/dist/{chunk-2FQEQUJT.js → chunk-XXVM53P4.js} +4 -4
- 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/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 +631 -0
- package/src/utils/storage/helpers.ts +112 -14
- 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-XXVM53P4.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,348 +1,355 @@
|
|
|
1
|
-
#
|
|
1
|
+
# File Reference System
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A comprehensive file storage and management system with database tracking, organisation-scoped access control, and bucket management.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
|
-
import {
|
|
8
|
+
import { FileUpload, useFileReference, FileCategory } from '@jmruthers/pace-core';
|
|
9
9
|
|
|
10
|
-
// Basic usage
|
|
11
|
-
const { uploadFile
|
|
12
|
-
supabase,
|
|
13
|
-
appName: 'TRAC',
|
|
14
|
-
orgId: 'your-org-id'
|
|
15
|
-
});
|
|
10
|
+
// Basic usage with new file reference system
|
|
11
|
+
const { uploadFile } = useFileReference(supabase);
|
|
16
12
|
|
|
17
|
-
// File upload component
|
|
13
|
+
// File upload component with progress tracking
|
|
18
14
|
<FileUpload
|
|
19
15
|
supabase={supabase}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
table_name="person"
|
|
17
|
+
record_id="person-123"
|
|
18
|
+
organisation_id="org-123"
|
|
19
|
+
app_id="app-123" // Optional - auto-resolved if not provided
|
|
20
|
+
category={FileCategory.PROFILE_PHOTOS}
|
|
21
|
+
isPublic={false}
|
|
22
|
+
showProgress={true}
|
|
23
|
+
showPreview={true}
|
|
24
|
+
onUploadSuccess={(result) => console.log('Uploaded:', result)}
|
|
25
|
+
onUploadError={(error, file) => console.error('Failed:', error)}
|
|
23
26
|
/>
|
|
24
27
|
```
|
|
25
28
|
|
|
26
|
-
##
|
|
29
|
+
## Key Features
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
- **Database Tracking**: All files are tracked in the `file_references` table
|
|
32
|
+
- **Bucket Management**: Automatic selection between `files` (private) and `public-files` (public) buckets
|
|
33
|
+
- **Organisation Scoped**: Files are automatically scoped to organisations
|
|
34
|
+
- **Progress Tracking**: Real-time upload progress with visual feedback
|
|
35
|
+
- **File Previews**: Automatic image previews for uploaded files
|
|
36
|
+
- **Validation**: Client-side file size and type validation
|
|
37
|
+
- **URL Management**: Automatic generation of public and signed URLs
|
|
38
|
+
- **RPC Integration**: Secure database operations via RPC functions
|
|
39
|
+
- **App ID Resolution**: Automatic app ID resolution from app name configuration
|
|
40
|
+
- **Memory Management**: Automatic cleanup to prevent memory leaks
|
|
41
|
+
- **Error Handling**: Comprehensive error handling with user-friendly messages
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
## Components
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
### FileUpload
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
- `supabase: SupabaseClient` - Supabase client instance
|
|
36
|
-
- `appName: string` - Application name (TRAC, CAKE, PACE, etc.)
|
|
37
|
-
- `orgId: string` - Organization ID
|
|
38
|
-
- `debug?: boolean` - Enable debug logging (optional)
|
|
47
|
+
Enhanced file upload component with progress tracking, previews, and validation.
|
|
39
48
|
|
|
40
|
-
**Returns:**
|
|
41
49
|
```typescript
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
<FileUpload
|
|
51
|
+
supabase={supabase}
|
|
52
|
+
table_name="person" // Table name for the record
|
|
53
|
+
record_id="person-123" // ID of the record
|
|
54
|
+
organisation_id="org-123" // Organisation ID
|
|
55
|
+
app_id="app-123" // Application ID (optional - auto-resolved)
|
|
56
|
+
category={FileCategory.PROFILE_PHOTOS} // File category
|
|
57
|
+
isPublic={false} // Use public bucket?
|
|
58
|
+
showProgress={true} // Show progress bar
|
|
59
|
+
showPreview={true} // Show image previews
|
|
60
|
+
accept="image/*" // Accepted file types
|
|
61
|
+
maxSize={10 * 1024 * 1024} // Max file size (10MB)
|
|
62
|
+
multiple={true} // Allow multiple files
|
|
63
|
+
disabled={false} // Disable upload
|
|
64
|
+
className="custom-class" // Custom CSS classes
|
|
65
|
+
onUploadSuccess={(result) => {}} // Success callback
|
|
66
|
+
onUploadError={(error, file) => {}} // Error callback
|
|
67
|
+
onProgress={(progress) => {}} // Progress callback
|
|
68
|
+
>
|
|
69
|
+
{/* Optional custom upload UI */}
|
|
70
|
+
<div>Custom upload area</div>
|
|
71
|
+
</FileUpload>
|
|
63
72
|
```
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
### FileDisplay
|
|
66
75
|
|
|
67
|
-
|
|
76
|
+
Display file references with automatic URL generation.
|
|
68
77
|
|
|
69
78
|
```typescript
|
|
70
|
-
|
|
71
|
-
supabase
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
<FileDisplay
|
|
80
|
+
supabase={supabase}
|
|
81
|
+
table_name="person"
|
|
82
|
+
record_id="person-123"
|
|
83
|
+
organisation_id="org-123"
|
|
84
|
+
category={FileCategory.PROFILE_PHOTOS}
|
|
85
|
+
showDelete={true}
|
|
86
|
+
/>
|
|
75
87
|
```
|
|
76
88
|
|
|
77
|
-
|
|
89
|
+
## Hooks
|
|
78
90
|
|
|
79
|
-
|
|
91
|
+
### useFileReference
|
|
80
92
|
|
|
81
|
-
|
|
93
|
+
Main hook for file reference operations.
|
|
82
94
|
|
|
83
95
|
```typescript
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
const {
|
|
97
|
+
uploadFile,
|
|
98
|
+
getFileReference,
|
|
99
|
+
getFileReferenceById,
|
|
100
|
+
getFilesByCategory,
|
|
101
|
+
deleteFileReference,
|
|
102
|
+
isLoading,
|
|
103
|
+
error
|
|
104
|
+
} = useFileReference(supabase);
|
|
105
|
+
|
|
106
|
+
// Upload a file
|
|
107
|
+
const result = await uploadFile({
|
|
108
|
+
table_name: 'person',
|
|
109
|
+
record_id: 'person-123',
|
|
110
|
+
organisation_id: 'org-123',
|
|
111
|
+
app_id: 'app-123',
|
|
112
|
+
category: FileCategory.PROFILE_PHOTOS,
|
|
113
|
+
is_public: false
|
|
114
|
+
}, file);
|
|
115
|
+
|
|
116
|
+
// Get files by category
|
|
117
|
+
const files = await getFilesByCategory(
|
|
118
|
+
'person',
|
|
119
|
+
'person-123',
|
|
120
|
+
FileCategory.PROFILE_PHOTOS,
|
|
121
|
+
'org-123'
|
|
122
|
+
);
|
|
97
123
|
```
|
|
98
124
|
|
|
99
|
-
###
|
|
125
|
+
### useFileReferenceById
|
|
100
126
|
|
|
101
|
-
|
|
102
|
-
import { uploadFile, getPublicUrl, getSignedUrl, deleteFile, archiveFile } from '@jmruthers/pace-core';
|
|
127
|
+
Fetch a single file reference by ID.
|
|
103
128
|
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
```typescript
|
|
130
|
+
const { fileReference, fileUrl, isLoading, error } = useFileReferenceById(
|
|
131
|
+
supabase,
|
|
132
|
+
fileRefId,
|
|
133
|
+
organisationId
|
|
134
|
+
);
|
|
135
|
+
```
|
|
110
136
|
|
|
111
|
-
|
|
112
|
-
const { getPublicUrl, getSignedUrl } = useStorage({
|
|
113
|
-
supabase,
|
|
114
|
-
appName: 'TRAC',
|
|
115
|
-
orgId: 'your-org-id'
|
|
116
|
-
});
|
|
137
|
+
### useFilesByCategory
|
|
117
138
|
|
|
118
|
-
|
|
119
|
-
const signedUrl = await getSignedUrl('org-id/category/private.pdf', 3600);
|
|
139
|
+
Fetch multiple file references by category.
|
|
120
140
|
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
141
|
+
```typescript
|
|
142
|
+
const { fileReferences, fileUrls, isLoading, error } = useFilesByCategory(
|
|
143
|
+
supabase,
|
|
144
|
+
table_name,
|
|
145
|
+
record_id,
|
|
146
|
+
category,
|
|
147
|
+
organisation_id
|
|
148
|
+
);
|
|
127
149
|
```
|
|
128
150
|
|
|
129
|
-
|
|
151
|
+
### useEventLogo
|
|
130
152
|
|
|
131
|
-
|
|
153
|
+
Fetch event logos for both public and authenticated contexts with automatic fallback.
|
|
132
154
|
|
|
155
|
+
```typescript
|
|
156
|
+
const { logoUrl, fallbackText, isLoading, error, refetch } = useEventLogo(
|
|
157
|
+
supabase,
|
|
158
|
+
eventId,
|
|
159
|
+
eventName,
|
|
160
|
+
organisationId,
|
|
161
|
+
{
|
|
162
|
+
validateImage: true,
|
|
163
|
+
enableCache: true,
|
|
164
|
+
cacheTtl: 30 * 60 * 1000 // 30 minutes
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Display logo with fallback
|
|
169
|
+
{logoUrl ? (
|
|
170
|
+
<img src={logoUrl} alt={`${eventName} logo`} />
|
|
171
|
+
) : (
|
|
172
|
+
<div className="logo-fallback">{fallbackText}</div>
|
|
173
|
+
)}
|
|
133
174
|
```
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
175
|
+
|
|
176
|
+
## File Categories
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
enum FileCategory {
|
|
180
|
+
PROFILE_PHOTOS = 'profile_photos',
|
|
181
|
+
EVENT_LOGOS = 'event_logos',
|
|
182
|
+
DOCUMENTS = 'documents',
|
|
183
|
+
CAKE_DISH = 'cake_dish',
|
|
184
|
+
TRAC_ACCOMMODATION = 'trac_accommodation',
|
|
185
|
+
TRAC_ACTIVITY = 'trac_activity',
|
|
186
|
+
TRAC_JOURNAL = 'trac_journal',
|
|
187
|
+
TRAC_TRANSPORT = 'trac_transport'
|
|
188
|
+
}
|
|
142
189
|
```
|
|
143
190
|
|
|
144
|
-
|
|
191
|
+
## Storage Structure
|
|
145
192
|
|
|
146
|
-
|
|
193
|
+
Files are stored in Supabase storage with the following structure:
|
|
147
194
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
195
|
+
```
|
|
196
|
+
files/ (private bucket)
|
|
197
|
+
├── {orgId}/
|
|
198
|
+
│ ├── profile_photos/
|
|
199
|
+
│ │ └── filename.jpg
|
|
200
|
+
│ ├── event_logos/
|
|
201
|
+
│ │ └── logo.png
|
|
202
|
+
│ └── documents/
|
|
203
|
+
│ └── document.pdf
|
|
204
|
+
|
|
205
|
+
public-files/ (public bucket)
|
|
206
|
+
├── {orgId}/
|
|
207
|
+
│ ├── profile_photos/
|
|
208
|
+
│ │ └── filename.jpg
|
|
209
|
+
│ └── event_logos/
|
|
210
|
+
│ └── logo.png
|
|
211
|
+
```
|
|
155
212
|
|
|
156
|
-
##
|
|
213
|
+
## URL Generation
|
|
214
|
+
|
|
215
|
+
### Public Files
|
|
216
|
+
```typescript
|
|
217
|
+
import { getPublicUrl } from '@jmruthers/pace-core';
|
|
157
218
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- **Path-based Access Control**: Organization ID in path enables easy access control
|
|
161
|
-
- **App Access Control**: Users must have access to the specific application
|
|
162
|
-
- **File References Table**: Centralized metadata tracking in `file_references` table
|
|
219
|
+
const url = getPublicUrl(supabase, filePath, true);
|
|
220
|
+
```
|
|
163
221
|
|
|
164
|
-
###
|
|
222
|
+
### Private Files (Signed URLs)
|
|
223
|
+
```typescript
|
|
224
|
+
import { getSignedUrl } from '@jmruthers/pace-core';
|
|
165
225
|
|
|
166
|
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
-
|
|
226
|
+
const { url } = await getSignedUrl(supabase, filePath, {
|
|
227
|
+
appName: 'my-app',
|
|
228
|
+
orgId: 'org-123',
|
|
229
|
+
expiresIn: 3600
|
|
230
|
+
});
|
|
231
|
+
```
|
|
171
232
|
|
|
172
|
-
|
|
233
|
+
## Database Schema
|
|
173
234
|
|
|
174
|
-
The system uses
|
|
235
|
+
The system uses the `file_references` table:
|
|
175
236
|
|
|
176
237
|
```sql
|
|
177
238
|
CREATE TABLE file_references (
|
|
178
239
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
179
|
-
app_name TEXT NOT NULL,
|
|
180
240
|
table_name TEXT NOT NULL,
|
|
181
|
-
record_id
|
|
241
|
+
record_id TEXT NOT NULL,
|
|
182
242
|
file_path TEXT NOT NULL,
|
|
183
|
-
file_metadata JSONB,
|
|
184
243
|
organisation_id UUID NOT NULL,
|
|
185
|
-
|
|
244
|
+
app_id UUID,
|
|
245
|
+
file_metadata JSONB,
|
|
246
|
+
is_public BOOLEAN DEFAULT FALSE,
|
|
186
247
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
187
248
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
188
249
|
);
|
|
189
250
|
```
|
|
190
251
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
## File Size Limits
|
|
194
|
-
|
|
195
|
-
| Type | Limit |
|
|
196
|
-
|------|-------|
|
|
197
|
-
| Images (JPEG, PNG, WebP) | 5MB |
|
|
198
|
-
| GIF | 10MB |
|
|
199
|
-
| SVG | 1MB |
|
|
200
|
-
| PDF | 50MB |
|
|
201
|
-
| Office Documents | 25MB |
|
|
202
|
-
| Archives (ZIP, RAR) | 100MB |
|
|
203
|
-
| Default | 10MB |
|
|
252
|
+
## RPC Functions
|
|
204
253
|
|
|
205
|
-
|
|
254
|
+
The system uses several RPC functions for secure database operations:
|
|
206
255
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
256
|
+
- `data_file_reference_create` - Create a new file reference
|
|
257
|
+
- `data_file_reference_get` - Get a file reference by ID
|
|
258
|
+
- `data_file_reference_list` - List file references for a record
|
|
259
|
+
- `data_file_reference_by_category_list` - List files by category
|
|
260
|
+
- `data_file_reference_count_get` - Get file count for a record
|
|
261
|
+
- `data_file_reference_delete` - Delete a file reference
|
|
262
|
+
- `data_file_reference_url_get` - Get file URL info
|
|
263
|
+
- `data_file_reference_signed_url_get` - Get signed URL info
|
|
211
264
|
|
|
212
|
-
|
|
213
|
-
const path = `org-id/files/file.pdf`;
|
|
214
|
-
```
|
|
265
|
+
## Migration from Legacy System
|
|
215
266
|
|
|
216
|
-
###
|
|
267
|
+
### Before (Legacy)
|
|
217
268
|
```typescript
|
|
218
|
-
const
|
|
219
|
-
if (!allowedTypes.includes(file.type)) {
|
|
220
|
-
throw new Error('File type not allowed');
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### 3. Handle Errors Gracefully
|
|
225
|
-
```typescript
|
|
226
|
-
const handleUpload = async (file: File) => {
|
|
227
|
-
try {
|
|
228
|
-
const result = await uploadFile(file);
|
|
229
|
-
if (result.success) {
|
|
230
|
-
showSuccess('File uploaded successfully');
|
|
231
|
-
} else {
|
|
232
|
-
showError(result.error);
|
|
233
|
-
}
|
|
234
|
-
} catch (error) {
|
|
235
|
-
showError('Upload failed. Please try again.');
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### 4. Use Signed URLs for Private Files
|
|
241
|
-
```typescript
|
|
242
|
-
// For private files, always use signed URLs
|
|
243
|
-
const { getSignedUrl } = useStorage({
|
|
269
|
+
const { uploadFile } = useStorage({
|
|
244
270
|
supabase,
|
|
245
|
-
appName: '
|
|
246
|
-
orgId: '
|
|
271
|
+
appName: 'my-app',
|
|
272
|
+
orgId: 'org-123'
|
|
247
273
|
});
|
|
248
274
|
|
|
249
|
-
|
|
275
|
+
await uploadFile(file, { customPath: 'photos' });
|
|
250
276
|
```
|
|
251
277
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
**Code Migration:**
|
|
278
|
+
### After (New System)
|
|
255
279
|
```typescript
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
});
|
|
267
|
-
const result = await uploadFile(file);
|
|
280
|
+
const { uploadFile } = useFileReference(supabase);
|
|
281
|
+
|
|
282
|
+
await uploadFile({
|
|
283
|
+
table_name: 'person',
|
|
284
|
+
record_id: 'person-123',
|
|
285
|
+
organisation_id: 'org-123',
|
|
286
|
+
app_id: 'app-123',
|
|
287
|
+
category: FileCategory.PROFILE_PHOTOS,
|
|
288
|
+
is_public: false
|
|
289
|
+
}, file);
|
|
268
290
|
```
|
|
269
291
|
|
|
270
|
-
|
|
292
|
+
## Best Practices
|
|
293
|
+
|
|
294
|
+
1. **Always specify organization context** - Files are automatically scoped to organizations
|
|
295
|
+
2. **Use appropriate file categories** - This helps with organization and access control
|
|
296
|
+
3. **Choose the right bucket** - Use `is_public: true` for files that don't need authentication
|
|
297
|
+
4. **Handle errors gracefully** - The system provides detailed error messages
|
|
298
|
+
5. **Clean up unused files** - Use the delete functions to remove old files
|
|
299
|
+
6. **Monitor file sizes** - Set appropriate `maxSize` limits for your use case
|
|
271
300
|
|
|
272
301
|
## Troubleshooting
|
|
273
302
|
|
|
274
303
|
### Common Issues
|
|
275
304
|
|
|
276
|
-
1. **"
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
305
|
+
1. **"Invalid key: undefined/files/..."**
|
|
306
|
+
- Ensure `organisation_id` is provided and valid
|
|
307
|
+
- Check that app context is properly set
|
|
308
|
+
- Verify the file path generation is working correctly
|
|
309
|
+
|
|
310
|
+
2. **"Permission denied" / "RLS policy violation"**
|
|
311
|
+
- Check RLS policies on `file_references` table
|
|
312
|
+
- Verify user has active organisation membership
|
|
313
|
+
- Ensure organisation context is set in database session
|
|
314
|
+
- For super admins, check `rbac_global_roles` table
|
|
315
|
+
|
|
316
|
+
3. **"File not found"**
|
|
317
|
+
- Verify the file reference exists in the database
|
|
318
|
+
- Check if file is in the correct bucket (`files` vs `public-files`)
|
|
319
|
+
- Ensure file path matches the stored `file_path` in database
|
|
320
|
+
|
|
321
|
+
4. **"URL generation failed"**
|
|
322
|
+
- Check if the file is in the correct bucket
|
|
323
|
+
- Verify Supabase client configuration
|
|
324
|
+
- For signed URLs, ensure expiration time is valid
|
|
325
|
+
|
|
326
|
+
5. **"App ID resolution failed"**
|
|
327
|
+
- Ensure app name is set via `setRBACAppName()` or provide `app_id` prop
|
|
328
|
+
- Check that app exists in `rbac_apps` table
|
|
329
|
+
- Verify app name matches exactly (case-sensitive)
|
|
330
|
+
|
|
331
|
+
6. **Memory leak / "Maximum update depth exceeded"**
|
|
332
|
+
- Ensure proper cleanup in useEffect hooks
|
|
333
|
+
- Check for infinite re-render loops in components
|
|
334
|
+
- Use `clearEventLogoCache()` when unmounting components
|
|
280
335
|
|
|
281
336
|
### Debug Mode
|
|
282
337
|
|
|
283
|
-
|
|
284
|
-
const { uploadFile } = useStorage({
|
|
285
|
-
supabase,
|
|
286
|
-
appName: 'TRAC',
|
|
287
|
-
orgId: 'your-org-id',
|
|
288
|
-
debug: true // Enable debug logging
|
|
289
|
-
});
|
|
290
|
-
```
|
|
338
|
+
Enable debug logging by checking the browser console for detailed information about file operations:
|
|
291
339
|
|
|
292
|
-
## Examples
|
|
293
|
-
|
|
294
|
-
### File Upload with Progress
|
|
295
340
|
```typescript
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### File List with Pagination
|
|
313
|
-
```typescript
|
|
314
|
-
const [files, setFiles] = useState([]);
|
|
315
|
-
const [page, setPage] = useState(0);
|
|
316
|
-
|
|
317
|
-
const loadFiles = async (pageNum = 0) => {
|
|
318
|
-
const result = await listFiles({
|
|
319
|
-
appName: 'TRAC',
|
|
320
|
-
orgId: 'your-org-id',
|
|
321
|
-
limit: 50,
|
|
322
|
-
offset: pageNum * 50
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
setFiles(prev => pageNum === 0 ? result.files : [...prev, ...result.files]);
|
|
326
|
-
};
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Error Handling
|
|
330
|
-
```typescript
|
|
331
|
-
const handleUpload = async (file: File) => {
|
|
332
|
-
try {
|
|
333
|
-
const result = await uploadFile(file);
|
|
334
|
-
|
|
335
|
-
if (result.success) {
|
|
336
|
-
showSuccess('File uploaded successfully');
|
|
337
|
-
} else {
|
|
338
|
-
showError(result.error);
|
|
339
|
-
}
|
|
340
|
-
} catch (error) {
|
|
341
|
-
showError('Upload failed. Please try again.');
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
---
|
|
347
|
-
|
|
348
|
-
This storage system provides a simple, secure way to manage files with organization-scoped access control. For more complex use cases, refer to the Supabase Storage documentation.
|
|
341
|
+
// Check file reference status
|
|
342
|
+
const { data } = await supabase
|
|
343
|
+
.from('file_references')
|
|
344
|
+
.select('*')
|
|
345
|
+
.eq('table_name', 'person')
|
|
346
|
+
.eq('record_id', recordId)
|
|
347
|
+
.eq('organisation_id', orgId);
|
|
348
|
+
|
|
349
|
+
console.log('File references:', data);
|
|
350
|
+
|
|
351
|
+
// Check bucket selection
|
|
352
|
+
import { getBucketName } from '@jmruthers/pace-core';
|
|
353
|
+
const bucket = getBucketName(isPublic);
|
|
354
|
+
console.log('Using bucket:', bucket);
|
|
355
|
+
```
|
|
@@ -64,6 +64,15 @@ export function getFileSizeLimit(mimeType: string): number {
|
|
|
64
64
|
return STORAGE_CONFIG.fileSizeLimits[mimeType] || STORAGE_CONFIG.defaultFileSizeLimit;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Get the bucket name based on whether the file is public or private
|
|
69
|
+
* @param isPublic - Whether the file should be publicly accessible
|
|
70
|
+
* @returns The bucket name: 'public-files' for public files, 'files' for private files
|
|
71
|
+
*/
|
|
72
|
+
export function getBucketName(isPublic: boolean): 'files' | 'public-files' {
|
|
73
|
+
return isPublic ? 'public-files' : 'files';
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
/**
|
|
68
77
|
* Validate file size against limits
|
|
69
78
|
*/
|