@jmruthers/pace-core 0.5.136 → 0.5.139
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataTable-CYOHOX3O.js → DataTable-JXFCA2BJ.js} +10 -9
- package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
- package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
- package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
- package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
- package/dist/chunk-4C7EXCAR.js.map +1 -0
- package/dist/{chunk-NOHEVYVX.js → chunk-5JMOHWDI.js} +417 -319
- package/dist/chunk-5JMOHWDI.js.map +1 -0
- package/dist/{chunk-FHWWBIHA.js → chunk-6DXZ6V5Q.js} +5 -5
- package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
- package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
- package/dist/chunk-7QCC6MCP.js.map +1 -0
- package/dist/chunk-BJPBT3CU.js +21 -0
- package/dist/chunk-BJPBT3CU.js.map +1 -0
- package/dist/{chunk-L6PGMCMD.js → chunk-BOOI7GK2.js} +38 -12
- package/dist/chunk-BOOI7GK2.js.map +1 -0
- package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
- package/dist/chunk-JISYG63F.js +70 -0
- package/dist/chunk-JISYG63F.js.map +1 -0
- package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
- package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
- package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
- package/dist/{chunk-HJGGOMQ6.js → chunk-TLT2ZR3L.js} +147 -103
- package/dist/chunk-TLT2ZR3L.js.map +1 -0
- package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
- package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
- package/dist/components.d.ts +1 -1
- package/dist/components.js +12 -11
- package/dist/components.js.map +1 -1
- package/dist/hooks.js +8 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +15 -14
- package/dist/index.js.map +1 -1
- package/dist/providers.js +3 -4
- package/dist/rbac/index.js +8 -9
- package/dist/schema-DTDZQe2u.d.ts +28 -0
- package/dist/types.d.ts +152 -3
- package/dist/types.js +51 -16
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +89 -4
- package/dist/utils.js +214 -96
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +1 -343
- package/dist/validation.js +3 -100
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +27 -0
- 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/EventLogoProps.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.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 +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/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/SessionRestorationLoaderProps.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 +84 -15
- package/docs/architecture/README.md +0 -1
- package/docs/styles/README.md +0 -2
- package/examples/RBAC/CompleteRBACExample.tsx +324 -0
- package/examples/RBAC/EventBasedApp.tsx +239 -0
- package/examples/RBAC/PermissionExample.tsx +151 -0
- package/examples/RBAC/index.ts +13 -0
- package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
- package/examples/public-pages/PublicEventPage.tsx +274 -0
- package/examples/public-pages/PublicPageApp.tsx +308 -0
- package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
- package/examples/public-pages/index.ts +14 -0
- package/package.json +1 -10
- package/src/__tests__/TEST_STANDARD.md +92 -0
- package/src/components/Badge/Badge.test.tsx +314 -0
- package/src/components/Badge/Badge.tsx +304 -0
- package/src/components/Badge/index.ts +3 -0
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
- package/src/components/DataTable/__tests__/styles.test.ts +1 -1
- package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
- package/src/components/DataTable/components/DataTableBody.tsx +461 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
- package/src/components/DataTable/components/FilterRow.tsx +9 -3
- package/src/components/DataTable/components/PaginationControls.tsx +1 -0
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
- package/src/components/DataTable/core/ActionManager.ts +235 -0
- package/src/components/DataTable/core/ColumnManager.ts +205 -0
- package/src/components/DataTable/core/DataManager.ts +188 -0
- package/src/components/DataTable/core/DataTableContext.tsx +181 -0
- package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
- package/src/components/DataTable/core/PluginRegistry.ts +229 -0
- package/src/components/DataTable/core/StateManager.ts +311 -0
- package/src/components/DataTable/core/interfaces.ts +338 -0
- package/src/components/DataTable/styles.ts +27 -6
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/columnUtils.ts +40 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/Dialog/README.md +804 -0
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
- package/src/components/Dialog/utils/safeHtml.ts +185 -0
- package/src/components/Footer/Footer.test.tsx +1 -1
- package/src/components/Form/Form.test.tsx +1 -1
- package/src/components/Form/FormErrorSummary.tsx +113 -0
- package/src/components/Form/FormFieldset.tsx +127 -0
- package/src/components/Form/FormLiveRegion.tsx +198 -0
- package/src/components/LoginForm/LoginForm.test.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
- package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
- package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +20 -8
- package/src/components/Table/__tests__/Table.test.tsx +1 -1
- package/src/components/index.ts +3 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
- package/src/index.ts +4 -0
- package/src/rbac/hooks/useCan.test.ts +24 -0
- package/src/rbac/hooks/usePermissions.ts +49 -12
- package/src/styles/core.css +3 -0
- package/src/utils/appConfig.ts +47 -0
- package/src/utils/appIdResolver.test.ts +499 -0
- package/src/utils/appIdResolver.ts +130 -0
- package/src/utils/appNameResolver.simple.test.ts +212 -0
- package/src/utils/appNameResolver.test.ts +121 -0
- package/src/utils/appNameResolver.ts +191 -0
- package/src/utils/audit.ts +127 -0
- package/src/utils/auth-utils.ts +96 -0
- package/src/utils/bundleAnalysis.ts +129 -0
- package/src/utils/cn.ts +7 -0
- package/src/utils/debugLogger.ts +67 -0
- package/src/utils/deviceFingerprint.ts +215 -0
- package/src/utils/dynamicUtils.ts +105 -0
- package/src/utils/file-reference.test.ts +788 -0
- package/src/utils/file-reference.ts +519 -0
- package/src/utils/formatDate.test.ts +237 -0
- package/src/utils/formatting.ts +133 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/lazyLoad.tsx +44 -0
- package/src/utils/logger.ts +179 -0
- package/src/utils/organisationContext.test.ts +322 -0
- package/src/utils/organisationContext.ts +153 -0
- package/src/utils/performanceBenchmark.ts +64 -0
- package/src/utils/performanceBudgets.ts +110 -0
- package/src/utils/permissionTypes.ts +37 -0
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +34 -0
- package/src/utils/sanitization.ts +264 -0
- package/src/utils/schemaUtils.ts +37 -0
- package/src/utils/secureDataAccess.test.ts +711 -0
- package/src/utils/secureDataAccess.ts +377 -0
- package/src/utils/secureErrors.ts +79 -0
- package/src/utils/secureStorage.ts +244 -0
- package/src/utils/security.ts +156 -0
- package/src/utils/securityMonitor.ts +45 -0
- package/src/utils/sessionTracking.ts +126 -0
- package/src/utils/validation.ts +111 -0
- package/src/utils/validationUtils.ts +120 -0
- package/src/validation/index.ts +2 -2
- package/dist/chunk-444EZN6N.js.map +0 -1
- package/dist/chunk-APIBCTL2.js +0 -670
- package/dist/chunk-APIBCTL2.js.map +0 -1
- package/dist/chunk-HJGGOMQ6.js.map +0 -1
- package/dist/chunk-K2WWTH7O.js +0 -94
- package/dist/chunk-K2WWTH7O.js.map +0 -1
- package/dist/chunk-L6PGMCMD.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-NOHEVYVX.js.map +0 -1
- package/dist/chunk-TVYPTYOY.js.map +0 -1
- package/dist/validation-8npbysjg.d.ts +0 -177
- /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-JXFCA2BJ.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
- /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
- /package/dist/{chunk-FHWWBIHA.js.map → chunk-6DXZ6V5Q.js.map} +0 -0
- /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
- /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
- /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
- /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
- /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
- /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
- /package/examples/{components → components 2}/DataTable/index.ts +0 -0
- /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
- /package/examples/{components → components 2}/Dialog/index.ts +0 -0
- /package/examples/{components → components 2}/index.ts +0 -0
|
@@ -299,97 +299,95 @@ describe('useFileUrl Hook', () => {
|
|
|
299
299
|
});
|
|
300
300
|
|
|
301
301
|
it.skip('handles signed URL generation failure', async () => {
|
|
302
|
-
// SKIPPED: This test
|
|
303
|
-
// The
|
|
304
|
-
//
|
|
305
|
-
//
|
|
302
|
+
// SKIPPED: This test has issues with async promise rejection handling in the test environment.
|
|
303
|
+
// The mock is being called correctly, but the promise rejection isn't being caught by the hook's
|
|
304
|
+
// error handling. This may be due to:
|
|
305
|
+
// - Timing issues between beforeEach mock reset and test mock setup
|
|
306
|
+
// - The hook's useEffect dependencies causing callback recreation
|
|
307
|
+
// - How vitest handles promise rejections in this specific scenario
|
|
308
|
+
//
|
|
309
|
+
// TODO: Investigate hook's async error handling and test environment setup.
|
|
310
|
+
// Consider manually triggering loadUrl() instead of relying on autoLoad, or using act() to wrap async operations.
|
|
311
|
+
|
|
306
312
|
const error = new Error('Failed to generate signed URL');
|
|
307
|
-
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
vi.mocked(getSignedUrl).mockRejectedValue(error);
|
|
313
|
+
|
|
314
|
+
// Override both mocks to reject - use the same pattern as success test
|
|
315
|
+
// The hoisted mock is what the module uses, but we also update the imported one
|
|
316
|
+
mockGetSignedUrl.mockImplementation(() => Promise.reject(error));
|
|
317
|
+
(getSignedUrl as any).mockImplementation(() => Promise.reject(error));
|
|
313
318
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
()
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// Then verify loading is false - when an Error is thrown, the hook preserves the original error message
|
|
343
|
-
expect(result.current.isLoading).toBe(false);
|
|
344
|
-
expect(result.current.error?.message).toBe('Failed to generate signed URL');
|
|
345
|
-
expect(result.current.url).toBe(null);
|
|
346
|
-
});
|
|
319
|
+
const { result } = renderHook(() =>
|
|
320
|
+
useFileUrl(mockPrivateFileReference, {
|
|
321
|
+
organisation_id: 'org-123',
|
|
322
|
+
supabase: mockSupabase,
|
|
323
|
+
autoLoad: true
|
|
324
|
+
})
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
await waitFor(
|
|
328
|
+
() => {
|
|
329
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
330
|
+
expect(result.current.isLoading).toBe(false);
|
|
331
|
+
},
|
|
332
|
+
{ timeout: 5000 }
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
expect(mockGetSignedUrl).toHaveBeenCalledWith(
|
|
336
|
+
mockSupabase,
|
|
337
|
+
mockPrivateFileReference.file_path,
|
|
338
|
+
expect.objectContaining({
|
|
339
|
+
appName: 'file-reference',
|
|
340
|
+
orgId: 'org-123',
|
|
341
|
+
expiresIn: 3600
|
|
342
|
+
})
|
|
343
|
+
);
|
|
344
|
+
expect(result.current.error?.message).toBe('Failed to generate signed URL');
|
|
345
|
+
expect(result.current.url).toBe(null);
|
|
347
346
|
});
|
|
348
347
|
|
|
349
348
|
it.skip('handles null signed URL result', async () => {
|
|
350
|
-
// SKIPPED: This test
|
|
351
|
-
// The
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
//
|
|
359
|
-
|
|
349
|
+
// SKIPPED: This test has issues with async promise resolution handling in the test environment.
|
|
350
|
+
// The mock is being called correctly, but the promise resolution isn't completing properly,
|
|
351
|
+
// causing isLoading to remain true. This may be due to:
|
|
352
|
+
// - Timing issues between beforeEach mock reset and test mock setup
|
|
353
|
+
// - The hook's useEffect dependencies causing callback recreation
|
|
354
|
+
// - How vitest handles promise resolutions when mockImplementation is overridden
|
|
355
|
+
//
|
|
356
|
+
// TODO: Investigate hook's async state management and test environment setup.
|
|
357
|
+
// Consider manually triggering loadUrl() instead of relying on autoLoad, or using act() to wrap async operations.
|
|
358
|
+
|
|
359
|
+
// Clear and override both mocks - beforeEach sets mockImplementation, so we need to reset first
|
|
360
|
+
mockGetSignedUrl.mockReset();
|
|
361
|
+
mockGetSignedUrl.mockResolvedValue({ url: null, expiresAt: null });
|
|
362
|
+
(getSignedUrl as any).mockReset();
|
|
363
|
+
(getSignedUrl as any).mockResolvedValue({ url: null, expiresAt: null });
|
|
360
364
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
()
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
// URL should be null when getSignedUrl returns null
|
|
390
|
-
expect(result.current.url).toBe(null);
|
|
391
|
-
expect(result.current.error).toBe(null);
|
|
392
|
-
});
|
|
365
|
+
const { result } = renderHook(() =>
|
|
366
|
+
useFileUrl(mockPrivateFileReference, {
|
|
367
|
+
organisation_id: 'org-123',
|
|
368
|
+
supabase: mockSupabase,
|
|
369
|
+
autoLoad: true
|
|
370
|
+
})
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
await waitFor(
|
|
374
|
+
() => {
|
|
375
|
+
expect(result.current.isLoading).toBe(false);
|
|
376
|
+
expect(result.current.url).toBe(null);
|
|
377
|
+
},
|
|
378
|
+
{ timeout: 5000 }
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
expect(getSignedUrl).toHaveBeenCalledWith(
|
|
382
|
+
mockSupabase,
|
|
383
|
+
mockPrivateFileReference.file_path,
|
|
384
|
+
expect.objectContaining({
|
|
385
|
+
appName: 'file-reference',
|
|
386
|
+
orgId: 'org-123',
|
|
387
|
+
expiresIn: 3600
|
|
388
|
+
})
|
|
389
|
+
);
|
|
390
|
+
expect(result.current.error).toBe(null);
|
|
393
391
|
});
|
|
394
392
|
});
|
|
395
393
|
|
package/src/index.ts
CHANGED
|
@@ -79,6 +79,9 @@ export type { LabelProps } from './components/Label/Label';
|
|
|
79
79
|
export { Alert, AlertTitle, AlertDescription } from './components/Alert/Alert';
|
|
80
80
|
export { Avatar, AvatarImage, AvatarFallback } from './components/Avatar/Avatar';
|
|
81
81
|
|
|
82
|
+
export { Badge } from './components/Badge/Badge';
|
|
83
|
+
export type { BadgeProps, BadgeVariant } from './components/Badge/Badge';
|
|
84
|
+
|
|
82
85
|
export { Checkbox } from './components/Checkbox/Checkbox';
|
|
83
86
|
export { Switch } from './components/Switch/Switch';
|
|
84
87
|
export type { SwitchProps } from './components/Switch/Switch';
|
|
@@ -141,6 +144,7 @@ export {
|
|
|
141
144
|
type AggregateConfig,
|
|
142
145
|
type EmptyStateConfig,
|
|
143
146
|
type GetRowId,
|
|
147
|
+
type DataTableFeatureConfig,
|
|
144
148
|
ColumnFactory
|
|
145
149
|
} from './components/DataTable';
|
|
146
150
|
|
|
@@ -679,6 +679,30 @@ describe('useCan Hook', () => {
|
|
|
679
679
|
expect(mockIsPermitted).not.toHaveBeenCalled();
|
|
680
680
|
});
|
|
681
681
|
});
|
|
682
|
+
|
|
683
|
+
it('handles undefined scope gracefully', async () => {
|
|
684
|
+
const { result } = renderHook(() =>
|
|
685
|
+
useCan(mockUserId, undefined as any, mockPermission, undefined, false)
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
await waitFor(() => {
|
|
689
|
+
expect(result.current.isLoading).toBe(true);
|
|
690
|
+
expect(result.current.can).toBe(false);
|
|
691
|
+
expect(mockIsPermitted).not.toHaveBeenCalled();
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('handles null scope gracefully', async () => {
|
|
696
|
+
const { result } = renderHook(() =>
|
|
697
|
+
useCan(mockUserId, null as any, mockPermission, undefined, false)
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
await waitFor(() => {
|
|
701
|
+
expect(result.current.isLoading).toBe(true);
|
|
702
|
+
expect(result.current.can).toBe(false);
|
|
703
|
+
expect(mockIsPermitted).not.toHaveBeenCalled();
|
|
704
|
+
});
|
|
705
|
+
});
|
|
682
706
|
});
|
|
683
707
|
|
|
684
708
|
describe('Performance', () => {
|
|
@@ -228,9 +228,15 @@ export function useCan(
|
|
|
228
228
|
const [isLoading, setIsLoading] = useState(true);
|
|
229
229
|
const [error, setError] = useState<Error | null>(null);
|
|
230
230
|
|
|
231
|
+
// Validate scope parameter - handle undefined/null scope gracefully
|
|
232
|
+
const isValidScope = scope && typeof scope === 'object';
|
|
233
|
+
const organisationId = isValidScope ? scope.organisationId : undefined;
|
|
234
|
+
const eventId = isValidScope ? scope.eventId : undefined;
|
|
235
|
+
const appId = isValidScope ? scope.appId : undefined;
|
|
236
|
+
|
|
231
237
|
// Add timeout for missing organisation context (3 seconds)
|
|
232
238
|
useEffect(() => {
|
|
233
|
-
if (!
|
|
239
|
+
if (!isValidScope || !organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === '')) {
|
|
234
240
|
const timeoutId = setTimeout(() => {
|
|
235
241
|
setError(new Error('Organisation context is required for permission checks'));
|
|
236
242
|
setIsLoading(false);
|
|
@@ -243,7 +249,7 @@ export function useCan(
|
|
|
243
249
|
if (error?.message === 'Organisation context is required for permission checks') {
|
|
244
250
|
setError(null);
|
|
245
251
|
}
|
|
246
|
-
}, [
|
|
252
|
+
}, [isValidScope, organisationId, error]);
|
|
247
253
|
|
|
248
254
|
// Use refs to track the last values to prevent unnecessary re-runs
|
|
249
255
|
const lastUserIdRef = useRef<UUID | null>(null);
|
|
@@ -253,8 +259,8 @@ export function useCan(
|
|
|
253
259
|
const lastUseCacheRef = useRef<boolean | null>(null);
|
|
254
260
|
|
|
255
261
|
useEffect(() => {
|
|
256
|
-
// Create a scope key to track changes
|
|
257
|
-
const scopeKey = `${
|
|
262
|
+
// Create a scope key to track changes - use safe property access
|
|
263
|
+
const scopeKey = isValidScope ? `${organisationId}-${eventId}-${appId}` : 'invalid-scope';
|
|
258
264
|
|
|
259
265
|
// Only run if something has actually changed
|
|
260
266
|
if (
|
|
@@ -278,9 +284,18 @@ export function useCan(
|
|
|
278
284
|
return;
|
|
279
285
|
}
|
|
280
286
|
|
|
287
|
+
// Validate scope before accessing properties
|
|
288
|
+
if (!isValidScope) {
|
|
289
|
+
setIsLoading(true);
|
|
290
|
+
setCan(false);
|
|
291
|
+
setError(null);
|
|
292
|
+
// Timeout is handled in separate useEffect
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
281
296
|
// Don't check permissions if scope is invalid (e.g., organisationId is null/empty)
|
|
282
297
|
// Wait for organisation context to resolve
|
|
283
|
-
if (!
|
|
298
|
+
if (!organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === '')) {
|
|
284
299
|
setIsLoading(true);
|
|
285
300
|
setCan(false);
|
|
286
301
|
setError(null);
|
|
@@ -292,9 +307,16 @@ export function useCan(
|
|
|
292
307
|
setIsLoading(true);
|
|
293
308
|
setError(null);
|
|
294
309
|
|
|
310
|
+
// Create a valid scope object for the API call
|
|
311
|
+
const validScope: Scope = {
|
|
312
|
+
organisationId,
|
|
313
|
+
...(eventId ? { eventId } : {}),
|
|
314
|
+
...(appId ? { appId } : {})
|
|
315
|
+
};
|
|
316
|
+
|
|
295
317
|
const result = useCache
|
|
296
|
-
? await isPermittedCached({ userId, scope, permission, pageId })
|
|
297
|
-
: await isPermitted({ userId, scope, permission, pageId });
|
|
318
|
+
? await isPermittedCached({ userId, scope: validScope, permission, pageId })
|
|
319
|
+
: await isPermitted({ userId, scope: validScope, permission, pageId });
|
|
298
320
|
|
|
299
321
|
setCan(result);
|
|
300
322
|
} catch (err) {
|
|
@@ -309,7 +331,7 @@ export function useCan(
|
|
|
309
331
|
|
|
310
332
|
checkPermission();
|
|
311
333
|
}
|
|
312
|
-
}, [userId,
|
|
334
|
+
}, [userId, isValidScope, organisationId, eventId, appId, permission, pageId, useCache]);
|
|
313
335
|
|
|
314
336
|
const refetch = useCallback(async () => {
|
|
315
337
|
if (!userId) {
|
|
@@ -318,8 +340,16 @@ export function useCan(
|
|
|
318
340
|
return;
|
|
319
341
|
}
|
|
320
342
|
|
|
343
|
+
// Validate scope before accessing properties
|
|
344
|
+
if (!isValidScope) {
|
|
345
|
+
setCan(false);
|
|
346
|
+
setIsLoading(true);
|
|
347
|
+
setError(null);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
321
351
|
// Don't check permissions if scope is invalid (e.g., organisationId is null/empty)
|
|
322
|
-
if (!
|
|
352
|
+
if (!organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === '')) {
|
|
323
353
|
setCan(false);
|
|
324
354
|
setIsLoading(true);
|
|
325
355
|
setError(null);
|
|
@@ -330,9 +360,16 @@ export function useCan(
|
|
|
330
360
|
setIsLoading(true);
|
|
331
361
|
setError(null);
|
|
332
362
|
|
|
363
|
+
// Create a valid scope object for the API call
|
|
364
|
+
const validScope: Scope = {
|
|
365
|
+
organisationId,
|
|
366
|
+
...(eventId ? { eventId } : {}),
|
|
367
|
+
...(appId ? { appId } : {})
|
|
368
|
+
};
|
|
369
|
+
|
|
333
370
|
const result = useCache
|
|
334
|
-
? await isPermittedCached({ userId, scope, permission, pageId })
|
|
335
|
-
: await isPermitted({ userId, scope, permission, pageId });
|
|
371
|
+
? await isPermittedCached({ userId, scope: validScope, permission, pageId })
|
|
372
|
+
: await isPermitted({ userId, scope: validScope, permission, pageId });
|
|
336
373
|
|
|
337
374
|
setCan(result);
|
|
338
375
|
} catch (err) {
|
|
@@ -341,7 +378,7 @@ export function useCan(
|
|
|
341
378
|
} finally {
|
|
342
379
|
setIsLoading(false);
|
|
343
380
|
}
|
|
344
|
-
}, [userId,
|
|
381
|
+
}, [userId, isValidScope, organisationId, eventId, appId, permission, pageId, useCache]);
|
|
345
382
|
|
|
346
383
|
// Memoize the return object to prevent unnecessary re-renders
|
|
347
384
|
return useMemo(() => ({
|
package/src/styles/core.css
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
--font-serif: "Open Sans", sans-serif;
|
|
8
8
|
--font-mono: "Reddit Mono", monospace;
|
|
9
9
|
--font-heading: "Georama", sans-serif;
|
|
10
|
+
--shadow-badge-soft: 0 0 0 0.1rem var(--color-main-600), 0 0 0 5rem var(--color-main-600) inset, 0 0 0.25rem 0.25rem var(--color-main-600);
|
|
11
|
+
--shadow-badge-soft-xl: 0 0 0 0.1rem var(--color-main-600), 0 0 0 5rem var(--color-main-600) inset, 0 0 0.25rem 0.75rem var(--color-main-600);
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
@layer base {
|
|
@@ -237,6 +239,7 @@
|
|
|
237
239
|
@layer utilities {
|
|
238
240
|
/* Custom utility styles go here */
|
|
239
241
|
|
|
242
|
+
|
|
240
243
|
/* Hide spinner arrows on number inputs in DataTable */
|
|
241
244
|
.datatable-number-no-spinners::-webkit-inner-spin-button,
|
|
242
245
|
.datatable-number-no-spinners::-webkit-outer-spin-button {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Application configuration utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface AppConfig {
|
|
7
|
+
appName: string;
|
|
8
|
+
appId: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let currentAppConfig: AppConfig | null = null;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Set the current application configuration
|
|
15
|
+
*/
|
|
16
|
+
export function setAppConfig(config: AppConfig) {
|
|
17
|
+
currentAppConfig = config;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the current application configuration
|
|
22
|
+
*/
|
|
23
|
+
export function getAppConfig(): AppConfig {
|
|
24
|
+
if (!currentAppConfig) {
|
|
25
|
+
// Fallback to environment or default
|
|
26
|
+
const appName = import.meta.env.REACT_APP_NAME || 'PACE';
|
|
27
|
+
return {
|
|
28
|
+
appName,
|
|
29
|
+
appId: appName
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return currentAppConfig;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get the current app name
|
|
37
|
+
*/
|
|
38
|
+
export function getCurrentAppName(): string {
|
|
39
|
+
return getAppConfig().appName;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the current app ID
|
|
44
|
+
*/
|
|
45
|
+
export function getCurrentAppId(): string {
|
|
46
|
+
return getAppConfig().appId;
|
|
47
|
+
}
|