@jmruthers/pace-core 0.5.121 → 0.5.123
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-D4646R4b.d.ts → AuthService-DYuQPJj6.d.ts} +0 -9
- package/dist/{DataTable-DGZDJUYM.js → DataTable-WTS4IRF2.js} +7 -8
- package/dist/{PublicLoadingSpinner-DgDWTFqn.d.ts → PublicLoadingSpinner-CaoRbHvJ.d.ts} +30 -4
- package/dist/{UnifiedAuthProvider-UACKFATV.js → UnifiedAuthProvider-6C47WIML.js} +3 -4
- package/dist/{chunk-D6BOFXYR.js → chunk-35ZDPMBM.js} +3 -3
- package/dist/{chunk-CGURJ27Z.js → chunk-4MXVZVNS.js} +2 -2
- package/dist/{chunk-ZYJ6O5CA.js → chunk-C43QIDN3.js} +2 -2
- package/dist/{chunk-VKOCWWVY.js → chunk-CX5M4ZAG.js} +1 -6
- package/dist/{chunk-VKOCWWVY.js 3.map → chunk-CX5M4ZAG.js.map} +1 -1
- package/dist/{chunk-HFBOFZ3Z.js → chunk-DHMFMXFV.js} +258 -243
- package/dist/chunk-DHMFMXFV.js.map +1 -0
- package/dist/{chunk-RIEJGKD3.js → chunk-ESJTIADP.js} +15 -6
- package/dist/{chunk-RIEJGKD3.js.map → chunk-ESJTIADP.js.map} +1 -1
- package/dist/{chunk-SMJZMKYN.js → chunk-GEVIB2UB.js} +43 -10
- package/dist/chunk-GEVIB2UB.js.map +1 -0
- package/dist/{chunk-TDNI6ZWL.js → chunk-IJOZZOGT.js} +7 -7
- package/dist/chunk-IJOZZOGT.js.map +1 -0
- package/dist/{chunk-GZRXOUBE.js → chunk-M6DDYFUD.js} +2 -2
- package/dist/chunk-M6DDYFUD.js.map +1 -0
- package/dist/{chunk-B4GZ2BXO.js → chunk-NZGLXZGP.js} +3 -3
- package/dist/{chunk-NZ32EONV.js → chunk-QWNJCQXZ.js} +2 -2
- package/dist/{chunk-FKFHZUGF.js → chunk-XN6GWKMV.js} +43 -56
- package/dist/chunk-XN6GWKMV.js.map +1 -0
- package/dist/{chunk-BHWIUEYH.js → chunk-ZBLK676C.js} +1 -61
- package/dist/chunk-ZBLK676C.js.map +1 -0
- package/dist/{chunk-QPI2CCBA.js → chunk-ZPJMYGEP.js} +149 -96
- package/dist/chunk-ZPJMYGEP.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +11 -11
- package/dist/{formatting-B1jSqgl-.d.ts → formatting-DFcCxUEk.d.ts} +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +9 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +19 -17
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +2 -3
- package/dist/rbac/index.js +7 -8
- package/dist/styles/index.d.ts +1 -1
- package/dist/styles/index.js +5 -3
- package/dist/theming/runtime.d.ts +73 -1
- package/dist/theming/runtime.js +5 -5
- package/dist/{usePublicRouteParams-BdF8bZgs.d.ts → usePublicRouteParams-Dyt1tzI9.d.ts} +60 -8
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +5 -5
- 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 +6 -6
- 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 +6 -6
- 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/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- 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/GrantEventAppRoleParams.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 +7 -7
- package/docs/api/interfaces/PublicErrorBoundaryState.md +5 -5
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +7 -7
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +51 -12
- package/docs/api/interfaces/PublicPageLayoutProps.md +72 -12
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.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/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.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 +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.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 +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +140 -30
- package/docs/best-practices/README.md +1 -1
- package/docs/implementation-guides/datatable-filtering.md +313 -0
- package/docs/implementation-guides/datatable-rbac-usage.md +317 -0
- package/docs/implementation-guides/hierarchical-datatable.md +850 -0
- package/docs/implementation-guides/large-datasets.md +281 -0
- package/docs/implementation-guides/performance.md +403 -0
- package/docs/implementation-guides/public-pages.md +4 -4
- package/docs/migration/quick-migration-guide.md +320 -0
- package/docs/rbac/quick-start.md +16 -16
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +1 -1
- package/docs/troubleshooting/debugging.md +1117 -0
- package/docs/troubleshooting/migration.md +918 -0
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +30 -30
- package/examples/public-pages/PublicEventPage.tsx +41 -41
- package/examples/public-pages/PublicPageApp.tsx +33 -33
- package/examples/public-pages/PublicPageUsageExample.tsx +30 -30
- package/package.json +4 -4
- package/src/__tests__/hooks/usePermissions.test.ts +265 -0
- package/src/components/DataTable/DataTable.test.tsx +9 -38
- package/src/components/DataTable/DataTable.tsx +0 -7
- package/src/components/DataTable/components/DataTableCore.tsx +66 -136
- package/src/components/DataTable/components/DataTableModals.tsx +25 -22
- package/src/components/DataTable/components/EditableRow.tsx +118 -42
- package/src/components/DataTable/components/UnifiedTableBody.tsx +129 -76
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +33 -14
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +17 -5
- package/src/components/DataTable/utils/exportUtils.ts +3 -2
- package/src/components/Dialog/Dialog.tsx +1 -1
- package/src/components/Dialog/README.md +24 -24
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +2 -2
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +6 -6
- package/src/components/Dialog/examples/HtmlDialogExample.tsx +2 -2
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +3 -3
- package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +4 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +12 -1
- package/src/components/PublicLayout/EventLogo.tsx +175 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +22 -18
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +22 -14
- package/src/components/PublicLayout/PublicPageHeader.tsx +133 -40
- package/src/components/PublicLayout/PublicPageLayout.tsx +75 -72
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +1 -1
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +8 -8
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +23 -16
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +86 -14
- package/src/examples/CorrectPublicPageImplementation.tsx +30 -30
- package/src/examples/PublicEventPage.tsx +41 -41
- package/src/examples/PublicPageApp.tsx +33 -33
- package/src/examples/PublicPageUsageExample.tsx +30 -30
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +10 -3
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +285 -0
- package/src/hooks/public/usePublicRouteParams.ts +21 -4
- package/src/hooks/useEventTheme.test.ts +119 -43
- package/src/hooks/useEventTheme.ts +84 -55
- package/src/index.ts +3 -1
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +630 -0
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +496 -0
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
- package/src/rbac/secureClient.ts +4 -2
- package/src/services/EventService.ts +0 -66
- package/src/services/__tests__/EventService.eventColours.test.ts +44 -40
- package/src/styles/index.ts +1 -1
- package/src/theming/__tests__/parseEventColours.test.ts +209 -0
- package/src/theming/parseEventColours.ts +123 -0
- package/src/theming/runtime.ts +3 -0
- package/src/types/__tests__/file-reference.test.ts +447 -0
- package/src/utils/formatDate.test.ts +11 -11
- package/src/utils/formatting.ts +3 -2
- package/dist/chunk-BDZUMRBD.js 3.map +0 -1
- package/dist/chunk-BHWIUEYH.js.map +0 -1
- package/dist/chunk-CGURJ27Z.js.map +0 -1
- package/dist/chunk-FKFHZUGF.js.map +0 -1
- package/dist/chunk-GKHF54DI 2.js +0 -619
- package/dist/chunk-GKHF54DI.js 2.map +0 -1
- package/dist/chunk-GZRXOUBE.js.map +0 -1
- package/dist/chunk-HFBOFZ3Z.js.map +0 -1
- package/dist/chunk-NZ32EONV.js.map +0 -1
- package/dist/chunk-O3NWNXDY 2.js +0 -76
- package/dist/chunk-QPI2CCBA.js.map +0 -1
- package/dist/chunk-SMJZMKYN.js.map +0 -1
- package/dist/chunk-TDNI6ZWL.js 2.map +0 -1
- package/dist/chunk-TDNI6ZWL.js.map +0 -1
- package/dist/chunk-VKOCWWVY.js.map +0 -1
- package/dist/chunk-WP5I5GLN 2.js +0 -1564
- package/dist/index 3.js +0 -856
- package/dist/providers 3.js +0 -38
- package/dist/providers.js 3.map +0 -1
- package/dist/types 3.js +0 -128
- package/dist/types.js 3.map +0 -1
- package/dist/useInactivityTracker-MRUU55XI.js 3.map +0 -1
- package/dist/utils.js 3.map +0 -1
- package/dist/validation 3.js +0 -479
- package/src/styles/semantic.css +0 -24
- /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-WTS4IRF2.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-UACKFATV.js.map → UnifiedAuthProvider-6C47WIML.js.map} +0 -0
- /package/dist/{chunk-D6BOFXYR.js.map → chunk-35ZDPMBM.js.map} +0 -0
- /package/dist/{chunk-CGURJ27Z.js 2.map → chunk-4MXVZVNS.js.map} +0 -0
- /package/dist/{chunk-ZYJ6O5CA.js.map → chunk-C43QIDN3.js.map} +0 -0
- /package/dist/{chunk-B4GZ2BXO.js.map → chunk-NZGLXZGP.js.map} +0 -0
- /package/dist/{chunk-NZ32EONV.js 2.map → chunk-QWNJCQXZ.js.map} +0 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file File Reference Type Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Types/__tests__
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import type {
|
|
10
|
+
FileReference,
|
|
11
|
+
FileUploadOptions,
|
|
12
|
+
FileUploadResult,
|
|
13
|
+
FileValidationError,
|
|
14
|
+
FileMetadata,
|
|
15
|
+
FileStorageConfig,
|
|
16
|
+
FileAccessLevel
|
|
17
|
+
} from '../file-reference';
|
|
18
|
+
|
|
19
|
+
describe('[types] File Reference Types', () => {
|
|
20
|
+
describe('FileReference interface', () => {
|
|
21
|
+
it('validates FileReference type structure', () => {
|
|
22
|
+
const fileRef: FileReference = {
|
|
23
|
+
id: 'test-file-id',
|
|
24
|
+
filename: 'test-file.pdf',
|
|
25
|
+
originalName: 'Test File.pdf',
|
|
26
|
+
mimeType: 'application/pdf',
|
|
27
|
+
size: 1024,
|
|
28
|
+
url: 'https://example.com/files/test-file.pdf',
|
|
29
|
+
uploadedAt: '2024-01-01T00:00:00Z',
|
|
30
|
+
uploadedBy: 'user-123',
|
|
31
|
+
organisationId: 'org-123',
|
|
32
|
+
eventId: 'event-123',
|
|
33
|
+
accessLevel: 'private',
|
|
34
|
+
metadata: {
|
|
35
|
+
description: 'Test file description',
|
|
36
|
+
tags: ['document', 'test']
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
expect(fileRef).toHaveProperty('id');
|
|
41
|
+
expect(fileRef).toHaveProperty('filename');
|
|
42
|
+
expect(fileRef).toHaveProperty('originalName');
|
|
43
|
+
expect(fileRef).toHaveProperty('mimeType');
|
|
44
|
+
expect(fileRef).toHaveProperty('size');
|
|
45
|
+
expect(fileRef).toHaveProperty('url');
|
|
46
|
+
expect(fileRef).toHaveProperty('uploadedAt');
|
|
47
|
+
expect(fileRef).toHaveProperty('uploadedBy');
|
|
48
|
+
expect(fileRef).toHaveProperty('organisationId');
|
|
49
|
+
expect(fileRef).toHaveProperty('eventId');
|
|
50
|
+
expect(fileRef).toHaveProperty('accessLevel');
|
|
51
|
+
expect(fileRef).toHaveProperty('metadata');
|
|
52
|
+
|
|
53
|
+
expect(typeof fileRef.id).toBe('string');
|
|
54
|
+
expect(typeof fileRef.filename).toBe('string');
|
|
55
|
+
expect(typeof fileRef.originalName).toBe('string');
|
|
56
|
+
expect(typeof fileRef.mimeType).toBe('string');
|
|
57
|
+
expect(typeof fileRef.size).toBe('number');
|
|
58
|
+
expect(typeof fileRef.url).toBe('string');
|
|
59
|
+
expect(typeof fileRef.uploadedAt).toBe('string');
|
|
60
|
+
expect(typeof fileRef.uploadedBy).toBe('string');
|
|
61
|
+
expect(typeof fileRef.organisationId).toBe('string');
|
|
62
|
+
expect(typeof fileRef.eventId).toBe('string');
|
|
63
|
+
expect(typeof fileRef.accessLevel).toBe('string');
|
|
64
|
+
expect(typeof fileRef.metadata).toBe('object');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('validates FileReference with minimal required fields', () => {
|
|
68
|
+
const fileRef: FileReference = {
|
|
69
|
+
id: 'test-file-id',
|
|
70
|
+
filename: 'test-file.pdf',
|
|
71
|
+
originalName: 'Test File.pdf',
|
|
72
|
+
mimeType: 'application/pdf',
|
|
73
|
+
size: 1024,
|
|
74
|
+
url: 'https://example.com/files/test-file.pdf',
|
|
75
|
+
uploadedAt: '2024-01-01T00:00:00Z',
|
|
76
|
+
uploadedBy: 'user-123',
|
|
77
|
+
organisationId: 'org-123',
|
|
78
|
+
accessLevel: 'private'
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
expect(fileRef).toHaveProperty('id');
|
|
82
|
+
expect(fileRef).toHaveProperty('filename');
|
|
83
|
+
expect(fileRef.eventId).toBeUndefined();
|
|
84
|
+
expect(fileRef.metadata).toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('FileUploadOptions interface', () => {
|
|
89
|
+
it('validates FileUploadOptions type structure', () => {
|
|
90
|
+
const uploadOptions: FileUploadOptions = {
|
|
91
|
+
maxSize: 5 * 1024 * 1024, // 5MB
|
|
92
|
+
allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
|
|
93
|
+
allowedExtensions: ['.jpg', '.jpeg', '.png', '.pdf'],
|
|
94
|
+
generateThumbnail: true,
|
|
95
|
+
compressImage: true,
|
|
96
|
+
quality: 0.8,
|
|
97
|
+
resize: {
|
|
98
|
+
width: 1920,
|
|
99
|
+
height: 1080,
|
|
100
|
+
maintainAspectRatio: true
|
|
101
|
+
},
|
|
102
|
+
metadata: {
|
|
103
|
+
description: 'Uploaded file',
|
|
104
|
+
tags: ['upload']
|
|
105
|
+
},
|
|
106
|
+
accessLevel: 'private',
|
|
107
|
+
organisationId: 'org-123',
|
|
108
|
+
eventId: 'event-123'
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
expect(uploadOptions).toHaveProperty('maxSize');
|
|
112
|
+
expect(uploadOptions).toHaveProperty('allowedTypes');
|
|
113
|
+
expect(uploadOptions).toHaveProperty('allowedExtensions');
|
|
114
|
+
expect(uploadOptions).toHaveProperty('generateThumbnail');
|
|
115
|
+
expect(uploadOptions).toHaveProperty('compressImage');
|
|
116
|
+
expect(uploadOptions).toHaveProperty('quality');
|
|
117
|
+
expect(uploadOptions).toHaveProperty('resize');
|
|
118
|
+
expect(uploadOptions).toHaveProperty('metadata');
|
|
119
|
+
expect(uploadOptions).toHaveProperty('accessLevel');
|
|
120
|
+
expect(uploadOptions).toHaveProperty('organisationId');
|
|
121
|
+
expect(uploadOptions).toHaveProperty('eventId');
|
|
122
|
+
|
|
123
|
+
expect(typeof uploadOptions.maxSize).toBe('number');
|
|
124
|
+
expect(Array.isArray(uploadOptions.allowedTypes)).toBe(true);
|
|
125
|
+
expect(Array.isArray(uploadOptions.allowedExtensions)).toBe(true);
|
|
126
|
+
expect(typeof uploadOptions.generateThumbnail).toBe('boolean');
|
|
127
|
+
expect(typeof uploadOptions.compressImage).toBe('boolean');
|
|
128
|
+
expect(typeof uploadOptions.quality).toBe('number');
|
|
129
|
+
expect(typeof uploadOptions.resize).toBe('object');
|
|
130
|
+
expect(typeof uploadOptions.metadata).toBe('object');
|
|
131
|
+
expect(typeof uploadOptions.accessLevel).toBe('string');
|
|
132
|
+
expect(typeof uploadOptions.organisationId).toBe('string');
|
|
133
|
+
expect(typeof uploadOptions.eventId).toBe('string');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('validates FileUploadOptions with minimal fields', () => {
|
|
137
|
+
const uploadOptions: FileUploadOptions = {
|
|
138
|
+
maxSize: 1024 * 1024, // 1MB
|
|
139
|
+
allowedTypes: ['image/jpeg'],
|
|
140
|
+
accessLevel: 'public'
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
expect(uploadOptions).toHaveProperty('maxSize');
|
|
144
|
+
expect(uploadOptions).toHaveProperty('allowedTypes');
|
|
145
|
+
expect(uploadOptions).toHaveProperty('accessLevel');
|
|
146
|
+
expect(uploadOptions.allowedExtensions).toBeUndefined();
|
|
147
|
+
expect(uploadOptions.generateThumbnail).toBeUndefined();
|
|
148
|
+
expect(uploadOptions.compressImage).toBeUndefined();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('FileUploadResult interface', () => {
|
|
153
|
+
it('validates FileUploadResult success structure', () => {
|
|
154
|
+
const successResult: FileUploadResult = {
|
|
155
|
+
success: true,
|
|
156
|
+
file: {
|
|
157
|
+
id: 'test-file-id',
|
|
158
|
+
filename: 'test-file.pdf',
|
|
159
|
+
originalName: 'Test File.pdf',
|
|
160
|
+
mimeType: 'application/pdf',
|
|
161
|
+
size: 1024,
|
|
162
|
+
url: 'https://example.com/files/test-file.pdf',
|
|
163
|
+
uploadedAt: '2024-01-01T00:00:00Z',
|
|
164
|
+
uploadedBy: 'user-123',
|
|
165
|
+
organisationId: 'org-123',
|
|
166
|
+
accessLevel: 'private'
|
|
167
|
+
},
|
|
168
|
+
thumbnailUrl: 'https://example.com/thumbnails/test-file.jpg',
|
|
169
|
+
message: 'File uploaded successfully'
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
expect(successResult).toHaveProperty('success');
|
|
173
|
+
expect(successResult).toHaveProperty('file');
|
|
174
|
+
expect(successResult).toHaveProperty('thumbnailUrl');
|
|
175
|
+
expect(successResult).toHaveProperty('message');
|
|
176
|
+
expect(successResult.success).toBe(true);
|
|
177
|
+
expect(typeof successResult.file).toBe('object');
|
|
178
|
+
expect(typeof successResult.thumbnailUrl).toBe('string');
|
|
179
|
+
expect(typeof successResult.message).toBe('string');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('validates FileUploadResult error structure', () => {
|
|
183
|
+
const errorResult: FileUploadResult = {
|
|
184
|
+
success: false,
|
|
185
|
+
error: 'File size exceeds maximum allowed size',
|
|
186
|
+
code: 'FILE_TOO_LARGE',
|
|
187
|
+
details: {
|
|
188
|
+
maxSize: 5 * 1024 * 1024,
|
|
189
|
+
actualSize: 10 * 1024 * 1024
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
expect(errorResult).toHaveProperty('success');
|
|
194
|
+
expect(errorResult).toHaveProperty('error');
|
|
195
|
+
expect(errorResult).toHaveProperty('code');
|
|
196
|
+
expect(errorResult).toHaveProperty('details');
|
|
197
|
+
expect(errorResult.success).toBe(false);
|
|
198
|
+
expect(typeof errorResult.error).toBe('string');
|
|
199
|
+
expect(typeof errorResult.code).toBe('string');
|
|
200
|
+
expect(typeof errorResult.details).toBe('object');
|
|
201
|
+
expect(errorResult.file).toBeUndefined();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('FileValidationError interface', () => {
|
|
206
|
+
it('validates FileValidationError type structure', () => {
|
|
207
|
+
const validationError: FileValidationError = {
|
|
208
|
+
field: 'file',
|
|
209
|
+
message: 'File type not allowed',
|
|
210
|
+
code: 'INVALID_FILE_TYPE',
|
|
211
|
+
value: 'test.exe',
|
|
212
|
+
allowedValues: ['image/jpeg', 'image/png', 'application/pdf']
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
expect(validationError).toHaveProperty('field');
|
|
216
|
+
expect(validationError).toHaveProperty('message');
|
|
217
|
+
expect(validationError).toHaveProperty('code');
|
|
218
|
+
expect(validationError).toHaveProperty('value');
|
|
219
|
+
expect(validationError).toHaveProperty('allowedValues');
|
|
220
|
+
expect(typeof validationError.field).toBe('string');
|
|
221
|
+
expect(typeof validationError.message).toBe('string');
|
|
222
|
+
expect(typeof validationError.code).toBe('string');
|
|
223
|
+
expect(typeof validationError.value).toBe('string');
|
|
224
|
+
expect(Array.isArray(validationError.allowedValues)).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('FileMetadata interface', () => {
|
|
229
|
+
it('validates FileMetadata type structure', () => {
|
|
230
|
+
const metadata: FileMetadata = {
|
|
231
|
+
description: 'Test file description',
|
|
232
|
+
tags: ['document', 'test', 'important'],
|
|
233
|
+
category: 'documents',
|
|
234
|
+
author: 'John Doe',
|
|
235
|
+
version: '1.0',
|
|
236
|
+
customFields: {
|
|
237
|
+
department: 'IT',
|
|
238
|
+
project: 'Test Project',
|
|
239
|
+
priority: 'high'
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
expect(metadata).toHaveProperty('description');
|
|
244
|
+
expect(metadata).toHaveProperty('tags');
|
|
245
|
+
expect(metadata).toHaveProperty('category');
|
|
246
|
+
expect(metadata).toHaveProperty('author');
|
|
247
|
+
expect(metadata).toHaveProperty('version');
|
|
248
|
+
expect(metadata).toHaveProperty('customFields');
|
|
249
|
+
expect(typeof metadata.description).toBe('string');
|
|
250
|
+
expect(Array.isArray(metadata.tags)).toBe(true);
|
|
251
|
+
expect(typeof metadata.category).toBe('string');
|
|
252
|
+
expect(typeof metadata.author).toBe('string');
|
|
253
|
+
expect(typeof metadata.version).toBe('string');
|
|
254
|
+
expect(typeof metadata.customFields).toBe('object');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('validates FileMetadata with minimal fields', () => {
|
|
258
|
+
const metadata: FileMetadata = {
|
|
259
|
+
description: 'Test file'
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
expect(metadata).toHaveProperty('description');
|
|
263
|
+
expect(metadata.tags).toBeUndefined();
|
|
264
|
+
expect(metadata.category).toBeUndefined();
|
|
265
|
+
expect(metadata.author).toBeUndefined();
|
|
266
|
+
expect(metadata.version).toBeUndefined();
|
|
267
|
+
expect(metadata.customFields).toBeUndefined();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('FileStorageConfig interface', () => {
|
|
272
|
+
it('validates FileStorageConfig type structure', () => {
|
|
273
|
+
const storageConfig: FileStorageConfig = {
|
|
274
|
+
provider: 'supabase',
|
|
275
|
+
bucket: 'pace-files',
|
|
276
|
+
region: 'us-east-1',
|
|
277
|
+
cdnUrl: 'https://cdn.example.com',
|
|
278
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB
|
|
279
|
+
allowedMimeTypes: ['image/*', 'application/pdf', 'text/*'],
|
|
280
|
+
compressionEnabled: true,
|
|
281
|
+
encryptionEnabled: true,
|
|
282
|
+
retentionPolicy: {
|
|
283
|
+
days: 365,
|
|
284
|
+
autoDelete: true
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
expect(storageConfig).toHaveProperty('provider');
|
|
289
|
+
expect(storageConfig).toHaveProperty('bucket');
|
|
290
|
+
expect(storageConfig).toHaveProperty('region');
|
|
291
|
+
expect(storageConfig).toHaveProperty('cdnUrl');
|
|
292
|
+
expect(storageConfig).toHaveProperty('maxFileSize');
|
|
293
|
+
expect(storageConfig).toHaveProperty('allowedMimeTypes');
|
|
294
|
+
expect(storageConfig).toHaveProperty('compressionEnabled');
|
|
295
|
+
expect(storageConfig).toHaveProperty('encryptionEnabled');
|
|
296
|
+
expect(storageConfig).toHaveProperty('retentionPolicy');
|
|
297
|
+
expect(typeof storageConfig.provider).toBe('string');
|
|
298
|
+
expect(typeof storageConfig.bucket).toBe('string');
|
|
299
|
+
expect(typeof storageConfig.region).toBe('string');
|
|
300
|
+
expect(typeof storageConfig.cdnUrl).toBe('string');
|
|
301
|
+
expect(typeof storageConfig.maxFileSize).toBe('number');
|
|
302
|
+
expect(Array.isArray(storageConfig.allowedMimeTypes)).toBe(true);
|
|
303
|
+
expect(typeof storageConfig.compressionEnabled).toBe('boolean');
|
|
304
|
+
expect(typeof storageConfig.encryptionEnabled).toBe('boolean');
|
|
305
|
+
expect(typeof storageConfig.retentionPolicy).toBe('object');
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe('FileAccessLevel type', () => {
|
|
310
|
+
it('validates FileAccessLevel values', () => {
|
|
311
|
+
const accessLevels: FileAccessLevel[] = ['public', 'private', 'restricted'];
|
|
312
|
+
|
|
313
|
+
expect(accessLevels).toContain('public');
|
|
314
|
+
expect(accessLevels).toContain('private');
|
|
315
|
+
expect(accessLevels).toContain('restricted');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('validates FileAccessLevel type usage', () => {
|
|
319
|
+
const accessLevel: FileAccessLevel = 'private';
|
|
320
|
+
expect(typeof accessLevel).toBe('string');
|
|
321
|
+
expect(['public', 'private', 'restricted']).toContain(accessLevel);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe('[types] File Reference Usage Patterns', () => {
|
|
327
|
+
describe('File Upload Flow', () => {
|
|
328
|
+
it('validates complete file upload flow types', () => {
|
|
329
|
+
interface FileUploadFlow {
|
|
330
|
+
file: File;
|
|
331
|
+
options: FileUploadOptions;
|
|
332
|
+
onProgress: (progress: number) => void;
|
|
333
|
+
onSuccess: (result: FileUploadResult) => void;
|
|
334
|
+
onError: (error: FileValidationError) => void;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const uploadFlow: FileUploadFlow = {
|
|
338
|
+
file: new File(['test'], 'test.txt', { type: 'text/plain' }),
|
|
339
|
+
options: {
|
|
340
|
+
maxSize: 1024 * 1024,
|
|
341
|
+
allowedTypes: ['text/plain'],
|
|
342
|
+
accessLevel: 'private'
|
|
343
|
+
},
|
|
344
|
+
onProgress: (progress) => {
|
|
345
|
+
expect(typeof progress).toBe('number');
|
|
346
|
+
expect(progress).toBeGreaterThanOrEqual(0);
|
|
347
|
+
expect(progress).toBeLessThanOrEqual(100);
|
|
348
|
+
},
|
|
349
|
+
onSuccess: (result) => {
|
|
350
|
+
expect(result).toHaveProperty('success');
|
|
351
|
+
expect(result).toHaveProperty('file');
|
|
352
|
+
},
|
|
353
|
+
onError: (error) => {
|
|
354
|
+
expect(error).toHaveProperty('field');
|
|
355
|
+
expect(error).toHaveProperty('message');
|
|
356
|
+
expect(error).toHaveProperty('code');
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
expect(uploadFlow).toHaveProperty('file');
|
|
361
|
+
expect(uploadFlow).toHaveProperty('options');
|
|
362
|
+
expect(uploadFlow).toHaveProperty('onProgress');
|
|
363
|
+
expect(uploadFlow).toHaveProperty('onSuccess');
|
|
364
|
+
expect(uploadFlow).toHaveProperty('onError');
|
|
365
|
+
expect(uploadFlow.file).toBeInstanceOf(File);
|
|
366
|
+
expect(typeof uploadFlow.options).toBe('object');
|
|
367
|
+
expect(typeof uploadFlow.onProgress).toBe('function');
|
|
368
|
+
expect(typeof uploadFlow.onSuccess).toBe('function');
|
|
369
|
+
expect(typeof uploadFlow.onError).toBe('function');
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
describe('File Management Operations', () => {
|
|
374
|
+
it('validates file management operation types', () => {
|
|
375
|
+
interface FileManagementOperation {
|
|
376
|
+
operation: 'upload' | 'download' | 'delete' | 'update' | 'list';
|
|
377
|
+
fileId?: string;
|
|
378
|
+
options?: FileUploadOptions;
|
|
379
|
+
metadata?: FileMetadata;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const operations: FileManagementOperation[] = [
|
|
383
|
+
{ operation: 'upload', options: { maxSize: 1024, allowedTypes: ['image/*'] } },
|
|
384
|
+
{ operation: 'download', fileId: 'file-123' },
|
|
385
|
+
{ operation: 'delete', fileId: 'file-123' },
|
|
386
|
+
{ operation: 'update', fileId: 'file-123', metadata: { description: 'Updated' } },
|
|
387
|
+
{ operation: 'list' }
|
|
388
|
+
];
|
|
389
|
+
|
|
390
|
+
operations.forEach(op => {
|
|
391
|
+
expect(op).toHaveProperty('operation');
|
|
392
|
+
expect(['upload', 'download', 'delete', 'update', 'list']).toContain(op.operation);
|
|
393
|
+
if (op.fileId) {
|
|
394
|
+
expect(typeof op.fileId).toBe('string');
|
|
395
|
+
}
|
|
396
|
+
if (op.options) {
|
|
397
|
+
expect(typeof op.options).toBe('object');
|
|
398
|
+
}
|
|
399
|
+
if (op.metadata) {
|
|
400
|
+
expect(typeof op.metadata).toBe('object');
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe('File Validation Rules', () => {
|
|
407
|
+
it('validates file validation rule types', () => {
|
|
408
|
+
interface FileValidationRule {
|
|
409
|
+
field: keyof FileReference;
|
|
410
|
+
validator: (value: any) => boolean;
|
|
411
|
+
message: string;
|
|
412
|
+
required?: boolean;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const validationRules: FileValidationRule[] = [
|
|
416
|
+
{
|
|
417
|
+
field: 'filename',
|
|
418
|
+
validator: (value) => typeof value === 'string' && value.length > 0,
|
|
419
|
+
message: 'Filename is required',
|
|
420
|
+
required: true
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
field: 'size',
|
|
424
|
+
validator: (value) => typeof value === 'number' && value > 0,
|
|
425
|
+
message: 'File size must be greater than 0'
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
field: 'mimeType',
|
|
429
|
+
validator: (value) => typeof value === 'string' && value.includes('/'),
|
|
430
|
+
message: 'Invalid MIME type format'
|
|
431
|
+
}
|
|
432
|
+
];
|
|
433
|
+
|
|
434
|
+
validationRules.forEach(rule => {
|
|
435
|
+
expect(rule).toHaveProperty('field');
|
|
436
|
+
expect(rule).toHaveProperty('validator');
|
|
437
|
+
expect(rule).toHaveProperty('message');
|
|
438
|
+
expect(typeof rule.field).toBe('string');
|
|
439
|
+
expect(typeof rule.validator).toBe('function');
|
|
440
|
+
expect(typeof rule.message).toBe('string');
|
|
441
|
+
if (rule.required !== undefined) {
|
|
442
|
+
expect(typeof rule.required).toBe('boolean');
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
});
|
|
@@ -21,7 +21,7 @@ describe('formatDate Utility', () => {
|
|
|
21
21
|
const date = new Date('2024-01-15T10:30:00.000Z');
|
|
22
22
|
const result = formatDate(date);
|
|
23
23
|
|
|
24
|
-
expect(result).toMatch(
|
|
24
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
it('formats different Date objects', () => {
|
|
@@ -33,7 +33,7 @@ describe('formatDate Utility', () => {
|
|
|
33
33
|
|
|
34
34
|
dates.forEach(date => {
|
|
35
35
|
const result = formatDate(date);
|
|
36
|
-
expect(result).toMatch(
|
|
36
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
39
|
});
|
|
@@ -41,12 +41,12 @@ describe('formatDate Utility', () => {
|
|
|
41
41
|
describe('String Input', () => {
|
|
42
42
|
it('formats ISO date strings correctly', () => {
|
|
43
43
|
const result = formatDate('2024-01-15T10:30:00.000Z');
|
|
44
|
-
expect(result).toMatch(
|
|
44
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it('formats date-only strings correctly', () => {
|
|
48
48
|
const result = formatDate('2024-01-15');
|
|
49
|
-
expect(result).toMatch(
|
|
49
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
it('formats various date string formats', () => {
|
|
@@ -59,7 +59,7 @@ describe('formatDate Utility', () => {
|
|
|
59
59
|
|
|
60
60
|
dateStrings.forEach(dateString => {
|
|
61
61
|
const result = formatDate(dateString);
|
|
62
|
-
expect(result).toMatch(
|
|
62
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
63
63
|
});
|
|
64
64
|
});
|
|
65
65
|
});
|
|
@@ -68,13 +68,13 @@ describe('formatDate Utility', () => {
|
|
|
68
68
|
it('formats timestamp numbers correctly', () => {
|
|
69
69
|
const timestamp = new Date('2024-01-15T10:30:00.000Z').getTime();
|
|
70
70
|
const result = formatDate(timestamp);
|
|
71
|
-
expect(result).toMatch(
|
|
71
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
it('formats Unix timestamps correctly', () => {
|
|
75
75
|
const unixTimestamp = Math.floor(new Date('2024-01-15T10:30:00.000Z').getTime() / 1000);
|
|
76
76
|
const result = formatDate(unixTimestamp * 1000); // Convert back to milliseconds
|
|
77
|
-
expect(result).toMatch(
|
|
77
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
78
78
|
});
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -154,14 +154,14 @@ describe('formatDate Utility', () => {
|
|
|
154
154
|
const result = formatDate(date);
|
|
155
155
|
|
|
156
156
|
// Should use the default locale format
|
|
157
|
-
expect(result).toMatch(
|
|
157
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
it('handles different locales correctly', () => {
|
|
161
161
|
const date = new Date('2024-01-15T10:30:00.000Z');
|
|
162
162
|
|
|
163
163
|
const result = formatDate(date);
|
|
164
|
-
expect(result).toMatch(
|
|
164
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
165
165
|
});
|
|
166
166
|
});
|
|
167
167
|
|
|
@@ -171,7 +171,7 @@ describe('formatDate Utility', () => {
|
|
|
171
171
|
const result = formatDate(date);
|
|
172
172
|
|
|
173
173
|
// Should format consistently regardless of timezone
|
|
174
|
-
expect(result).toMatch(
|
|
174
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
175
175
|
});
|
|
176
176
|
});
|
|
177
177
|
|
|
@@ -231,7 +231,7 @@ describe('formatDate Utility', () => {
|
|
|
231
231
|
const result = formatDate(date);
|
|
232
232
|
|
|
233
233
|
// Should match pattern: "MMM DD, YYYY"
|
|
234
|
-
expect(result).toMatch(
|
|
234
|
+
expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
|
|
235
235
|
});
|
|
236
236
|
});
|
|
237
237
|
});
|
package/src/utils/formatting.ts
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Format a date as a readable string
|
|
6
|
+
* Format a date as a readable string in "dd mmm yyyy" format (e.g., "15 Jun 2024")
|
|
7
7
|
*/
|
|
8
8
|
export function formatDate(date: Date | string | number): string {
|
|
9
9
|
const dateObj = typeof date === 'string' || typeof date === 'number'
|
|
10
10
|
? new Date(date)
|
|
11
11
|
: date;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Use 'en-GB' locale to ensure "dd mmm yyyy" format (e.g., "15 Jun 2024")
|
|
14
|
+
return dateObj.toLocaleDateString('en-GB', {
|
|
14
15
|
year: 'numeric',
|
|
15
16
|
month: 'short',
|
|
16
17
|
day: 'numeric'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/organisationContext.ts"],"sourcesContent":["/**\n * @file Organisation Context Utility\n * @package @jmruthers/pace-core\n * @module Utils/OrganisationContext\n * @since 0.4.0\n *\n * Utility functions for managing organisation context in database sessions.\n * Provides fallback mechanisms for when database functions are not available.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\n\n/**\n * Set organisation context in the database session\n * \n * This function attempts to set the organisation context using a database function.\n * If the function is not available, it falls back gracefully without throwing errors.\n * \n * @param supabase - Supabase client instance\n * @param organisationId - The organisation ID to set as context\n * @returns Promise that resolves when context is set (or falls back gracefully)\n */\nexport async function setOrganisationContext(\n supabase: SupabaseClient,\n organisationId: string\n): Promise<void> {\n if (!supabase || !organisationId) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n // Add timeout to prevent hanging RPC calls\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('RPC timeout after 3 seconds')), 3000);\n });\n \n // Call the database function to set organisation context\n const rpcPromise = supabase.rpc('set_organisation_context', {\n org_id: organisationId\n });\n\n const { error } = await Promise.race([rpcPromise, timeoutPromise]) as any;\n\n if (error) {\n // Function might not exist yet - this is expected during migration\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] RPC function not available or failed, continuing without database context');\n } else {\n console.log('[organisationContext] Organisation context set in database successfully');\n }\n } catch (error) {\n // Handle any other errors gracefully\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] Failed to set database context, continuing without it:', error);\n }\n}\n\n/**\n * Clear organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves when context is cleared\n */\nexport async function clearOrganisationContext(\n supabase: SupabaseClient\n): Promise<void> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n const { error } = await supabase.rpc('rbac_audit_log', {\n p_event_type: 'organisation_switched',\n p_metadata: { action: 'clear_context' }\n });\n \n if (error) {\n // Silent fail - function not available\n // TODO: Replace with proper logging service integration\n } else {\n // TODO: Replace with proper logging service integration\n }\n } catch (error) {\n // Silent fail - error occurred\n // TODO: Replace with proper logging service integration\n }\n}\n\n/**\n * Get current organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to the current organisation ID or null\n */\nexport async function getOrganisationContext(\n supabase: SupabaseClient\n): Promise<string | null> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n\n try {\n // For now, return null since we're not using database context\n // This will be replaced with proper context management\n const data = null;\n const error = null;\n \n if (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n \n // Validate that data is a string (allow empty strings)\n if (typeof data === 'string') {\n return data;\n }\n \n // Return null for invalid data formats\n return null;\n } catch (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n}\n\n/**\n * Check if organisation context functions are available in the database\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to true if functions are available\n */\nexport async function isOrganisationContextAvailable(\n supabase: SupabaseClient\n): Promise<boolean> {\n if (!supabase) {\n return false;\n }\n\n try {\n const { error } = await supabase.rpc('get_organisation_context');\n \n if (error) {\n return false;\n }\n \n return true;\n } catch (error) {\n return false;\n }\n} "],"mappings":";;;;;AAsBA,eAAsB,uBACpB,UACA,gBACe;AACf,MAAI,CAAC,YAAY,CAAC,gBAAgB;AAEhC;AAAA,EACF;AAEA,MAAI;AAEF,UAAM,iBAAiB,IAAI,QAAQ,CAAC,GAAG,WAAW;AAChD,iBAAW,MAAM,OAAO,IAAI,MAAM,6BAA6B,CAAC,GAAG,GAAI;AAAA,IACzE,CAAC;AAGD,UAAM,aAAa,SAAS,IAAI,4BAA4B;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,EAAE,MAAM,IAAI,MAAM,QAAQ,KAAK,CAAC,YAAY,cAAc,CAAC;AAEjE,QAAI,OAAO;AAGT,cAAQ,IAAI,iGAAiG;AAAA,IAC/G,OAAO;AACL,cAAQ,IAAI,yEAAyE;AAAA,IACvF;AAAA,EACF,SAAS,OAAO;AAGd,YAAQ,IAAI,gFAAgF,KAAK;AAAA,EACnG;AACF;AAQA,eAAsB,yBACpB,UACe;AACf,MAAI,CAAC,UAAU;AAEb;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,kBAAkB;AAAA,MACrD,cAAc;AAAA,MACd,YAAY,EAAE,QAAQ,gBAAgB;AAAA,IACxC,CAAC;AAED,QAAI,OAAO;AAAA,IAGX,OAAO;AAAA,IAEP;AAAA,EACF,SAAS,OAAO;AAAA,EAGhB;AACF;AAQA,eAAsB,uBACpB,UACwB;AACxB,MAAI,CAAC,UAAU;AAEb,WAAO;AAAA,EACT;AAEA,MAAI;AAGF,UAAM,OAAO;AACb,UAAM,QAAQ;AAEd,QAAI,OAAO;AAET,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,SAAS,UAAU;AAC5B,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT,SAAS,OAAO;AAEd,WAAO;AAAA,EACT;AACF;AAQA,eAAsB,+BACpB,UACkB;AAClB,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,0BAA0B;AAE/D,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAxJA;AAAA;AAAA;AAAA;AAAA;","names":[]}
|