@jmruthers/pace-core 0.5.11 → 0.5.13
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-32L3DDeG.d.ts → DataTable-Da0D1BHX.d.ts} +1 -1
- package/dist/{DataTable-7LYDPZXT.js → DataTable-NNCMQSDG.js} +2 -2
- package/dist/{chunk-JV73V2W5.js → chunk-5E3YF7HA.js} +15 -1
- package/dist/chunk-5E3YF7HA.js.map +1 -0
- package/dist/{chunk-ROHDZOP7.js → chunk-BUWLPWDA.js} +14 -12
- package/dist/chunk-BUWLPWDA.js.map +1 -0
- package/dist/{chunk-7KDZXMES.js → chunk-PILT65PA.js} +2 -2
- package/dist/components.d.ts +2 -2
- package/dist/components.js +2 -2
- package/dist/hooks.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/rbac/index.js +1 -1
- package/dist/{types-CczCVCDt.d.ts → types-zOqe4P1s.d.ts} +11 -3
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +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/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/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/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/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/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.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/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/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.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 +2 -2
- package/docs/implementation-guides/datatable-rbac-usage.md +31 -1
- package/package.json +1 -1
- package/src/components/DataTable/components/ActionButtons.tsx +2 -1
- package/src/components/DataTable/components/DataTableCore.tsx +19 -13
- package/src/components/DataTable/components/UnifiedTableBody.tsx +4 -2
- package/src/components/DataTable/types.ts +11 -3
- package/src/rbac/components/PagePermissionGuard.tsx +16 -0
- package/dist/chunk-JV73V2W5.js.map +0 -1
- package/dist/chunk-ROHDZOP7.js.map +0 -1
- /package/dist/{DataTable-7LYDPZXT.js.map → DataTable-NNCMQSDG.js.map} +0 -0
- /package/dist/{chunk-7KDZXMES.js.map → chunk-PILT65PA.js.map} +0 -0
package/docs/api/modules.md
CHANGED
|
@@ -28,7 +28,7 @@ function DishesPage() {
|
|
|
28
28
|
data={dishes}
|
|
29
29
|
columns={columns}
|
|
30
30
|
rbac={{
|
|
31
|
-
|
|
31
|
+
pageName: 'dishes' // Page name for permissions (recommended)
|
|
32
32
|
}}
|
|
33
33
|
features={{
|
|
34
34
|
search: true,
|
|
@@ -47,6 +47,36 @@ function DishesPage() {
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
### Page Identification Options
|
|
51
|
+
|
|
52
|
+
The DataTable supports two ways to identify the page for RBAC permissions:
|
|
53
|
+
|
|
54
|
+
#### Option 1: Page Name (Recommended)
|
|
55
|
+
```tsx
|
|
56
|
+
<DataTable
|
|
57
|
+
data={data}
|
|
58
|
+
columns={columns}
|
|
59
|
+
rbac={{
|
|
60
|
+
pageName: 'dishes' // Will be resolved to page ID by RBAC engine
|
|
61
|
+
}}
|
|
62
|
+
features={features}
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Option 2: Page ID (Direct)
|
|
67
|
+
```tsx
|
|
68
|
+
<DataTable
|
|
69
|
+
data={data}
|
|
70
|
+
columns={columns}
|
|
71
|
+
rbac={{
|
|
72
|
+
pageId: 'uuid-here' // Use page ID directly
|
|
73
|
+
}}
|
|
74
|
+
features={features}
|
|
75
|
+
/>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Note:** You must provide either `pageName` or `pageId`, but not both. If both are provided, `pageId` takes precedence.
|
|
79
|
+
|
|
50
80
|
### Permission Mapping
|
|
51
81
|
|
|
52
82
|
The DataTable now checks the following page-based permissions:
|
package/package.json
CHANGED
|
@@ -316,38 +316,48 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
316
316
|
|
|
317
317
|
// MANDATORY: Check all permissions upfront - ALWAYS enforce RBAC
|
|
318
318
|
// Use page-based permissions exclusively
|
|
319
|
-
const pageId = rbac
|
|
319
|
+
const pageId = rbac?.pageId;
|
|
320
|
+
const pageName = rbac?.pageName;
|
|
321
|
+
|
|
322
|
+
// Early validation - need either pageId or pageName
|
|
323
|
+
if (!pageId && !pageName) {
|
|
324
|
+
throw new Error('DataTable requires either rbac.pageId or rbac.pageName for permission checking');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Use pageId if provided, otherwise use pageName (will be resolved by RBAC engine)
|
|
328
|
+
const effectivePageId = pageId || pageName;
|
|
329
|
+
|
|
320
330
|
const permissions = {
|
|
321
331
|
canRead: useCan(user?.id || '', {
|
|
322
332
|
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
323
333
|
eventId: user?.user_metadata?.eventId || user?.app_metadata?.eventId,
|
|
324
334
|
appId: user?.user_metadata?.appId || user?.app_metadata?.appId,
|
|
325
|
-
}, `read:page.${
|
|
335
|
+
}, `read:page.${effectivePageId}` as any, effectivePageId),
|
|
326
336
|
canCreate: useCan(user?.id || '', {
|
|
327
337
|
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
328
338
|
eventId: user?.user_metadata?.eventId || user?.app_metadata?.eventId,
|
|
329
339
|
appId: user?.user_metadata?.appId || user?.app_metadata?.appId,
|
|
330
|
-
}, `create:page.${
|
|
340
|
+
}, `create:page.${effectivePageId}` as any, effectivePageId),
|
|
331
341
|
canUpdate: useCan(user?.id || '', {
|
|
332
342
|
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
333
343
|
eventId: user?.user_metadata?.eventId || user?.app_metadata?.eventId,
|
|
334
344
|
appId: user?.user_metadata?.appId || user?.app_metadata?.appId,
|
|
335
|
-
}, `update:page.${
|
|
345
|
+
}, `update:page.${effectivePageId}` as any, effectivePageId),
|
|
336
346
|
canDelete: useCan(user?.id || '', {
|
|
337
347
|
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
338
348
|
eventId: user?.user_metadata?.eventId || user?.app_metadata?.eventId,
|
|
339
349
|
appId: user?.user_metadata?.appId || user?.app_metadata?.appId,
|
|
340
|
-
}, `delete:page.${
|
|
350
|
+
}, `delete:page.${effectivePageId}` as any, effectivePageId),
|
|
341
351
|
canExport: useCan(user?.id || '', {
|
|
342
352
|
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
343
353
|
eventId: user?.user_metadata?.eventId || user?.app_metadata?.eventId,
|
|
344
354
|
appId: user?.user_metadata?.appId || user?.app_metadata?.appId,
|
|
345
|
-
}, `manage:page.${
|
|
355
|
+
}, `manage:page.${effectivePageId}` as any, effectivePageId), // Using manage for export/import
|
|
346
356
|
canImport: useCan(user?.id || '', {
|
|
347
357
|
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
348
358
|
eventId: user?.user_metadata?.eventId || user?.app_metadata?.eventId,
|
|
349
359
|
appId: user?.user_metadata?.appId || user?.app_metadata?.appId,
|
|
350
|
-
}, `manage:page.${
|
|
360
|
+
}, `manage:page.${effectivePageId}` as any, effectivePageId), // Using manage for export/import
|
|
351
361
|
};
|
|
352
362
|
|
|
353
363
|
// ============================================================================
|
|
@@ -777,14 +787,10 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
777
787
|
// RBAC VALIDATION AND EARLY RETURNS - AFTER ALL HOOKS
|
|
778
788
|
// ============================================================================
|
|
779
789
|
|
|
780
|
-
// MANDATORY: Every DataTable must have a user
|
|
790
|
+
// MANDATORY: Every DataTable must have a user
|
|
781
791
|
if (!user) {
|
|
782
792
|
throw new Error('DataTable requires authenticated user for RBAC');
|
|
783
793
|
}
|
|
784
|
-
|
|
785
|
-
if (!rbac?.pageId) {
|
|
786
|
-
throw new Error('DataTable requires rbac.pageId for permission checking');
|
|
787
|
-
}
|
|
788
794
|
|
|
789
795
|
const scope = {
|
|
790
796
|
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
@@ -794,7 +800,7 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
794
800
|
|
|
795
801
|
// MANDATORY: No data access without read permission
|
|
796
802
|
if (!permissions.canRead.can) {
|
|
797
|
-
return <AccessDeniedPage resource={
|
|
803
|
+
return <AccessDeniedPage resource={effectivePageId || 'unknown-page'} operation="read" />;
|
|
798
804
|
}
|
|
799
805
|
|
|
800
806
|
// ============================================================================
|
|
@@ -133,7 +133,8 @@ interface UnifiedTableBodyProps<TData extends Record<string, any>> {
|
|
|
133
133
|
}>;
|
|
134
134
|
// MANDATORY RBAC
|
|
135
135
|
rbac?: {
|
|
136
|
-
pageId
|
|
136
|
+
pageId?: string;
|
|
137
|
+
pageName?: string;
|
|
137
138
|
};
|
|
138
139
|
permissions?: {
|
|
139
140
|
canUpdate: { can: boolean; isLoading: boolean };
|
|
@@ -268,7 +269,8 @@ const MemoizedRow = ({
|
|
|
268
269
|
childLabel?: string;
|
|
269
270
|
}>;
|
|
270
271
|
rbac?: {
|
|
271
|
-
pageId
|
|
272
|
+
pageId?: string;
|
|
273
|
+
pageName?: string;
|
|
272
274
|
};
|
|
273
275
|
permissions?: {
|
|
274
276
|
canUpdate: { can: boolean; isLoading: boolean };
|
|
@@ -406,14 +406,22 @@ export interface DataTableFeatureConfig {
|
|
|
406
406
|
* ```tsx
|
|
407
407
|
* <DataTable
|
|
408
408
|
* data={dishes}
|
|
409
|
-
* rbac={{
|
|
409
|
+
* rbac={{ pageName: 'dishes' }} // Use page name (recommended)
|
|
410
|
+
* features={features}
|
|
411
|
+
* />
|
|
412
|
+
*
|
|
413
|
+
* <DataTable
|
|
414
|
+
* data={dishes}
|
|
415
|
+
* rbac={{ pageId: 'uuid-here' }} // Use page ID directly
|
|
410
416
|
* features={features}
|
|
411
417
|
* />
|
|
412
418
|
* ```
|
|
413
419
|
*/
|
|
414
420
|
export interface DataTableRBACConfig {
|
|
415
|
-
/** Page
|
|
416
|
-
|
|
421
|
+
/** Page name for page-based permissions - will be resolved to page ID */
|
|
422
|
+
pageName?: string;
|
|
423
|
+
/** Page ID for page-based permissions - used directly if provided */
|
|
424
|
+
pageId?: string;
|
|
417
425
|
}
|
|
418
426
|
|
|
419
427
|
/**
|
|
@@ -216,6 +216,7 @@ export function PagePermissionGuard({
|
|
|
216
216
|
};
|
|
217
217
|
console.log('[PagePermissionGuard] Setting resolved scope:', resolvedScope);
|
|
218
218
|
setResolvedScope(resolvedScope);
|
|
219
|
+
setCheckError(null); // Clear any previous errors
|
|
219
220
|
return;
|
|
220
221
|
}
|
|
221
222
|
|
|
@@ -250,6 +251,7 @@ export function PagePermissionGuard({
|
|
|
250
251
|
};
|
|
251
252
|
console.log('[PagePermissionGuard] Setting resolved scope (org only):', resolvedScope);
|
|
252
253
|
setResolvedScope(resolvedScope);
|
|
254
|
+
setCheckError(null); // Clear any previous errors
|
|
253
255
|
return;
|
|
254
256
|
}
|
|
255
257
|
|
|
@@ -267,6 +269,7 @@ export function PagePermissionGuard({
|
|
|
267
269
|
...eventScope,
|
|
268
270
|
appId: appId || eventScope.appId
|
|
269
271
|
});
|
|
272
|
+
setCheckError(null); // Clear any previous errors
|
|
270
273
|
} catch (error) {
|
|
271
274
|
setCheckError(error as Error);
|
|
272
275
|
setResolvedScope(null); // Ensure we don't proceed with incomplete scope
|
|
@@ -324,6 +327,7 @@ export function PagePermissionGuard({
|
|
|
324
327
|
);
|
|
325
328
|
|
|
326
329
|
console.log('[PagePermissionGuard] useCan returned:', { can, canIsLoading, canError });
|
|
330
|
+
console.log('[PagePermissionGuard] can type and value:', { type: typeof can, value: can, isBoolean: typeof can === 'boolean' });
|
|
327
331
|
|
|
328
332
|
// Combine loading states - we're loading if either scope is resolving OR permission check is loading
|
|
329
333
|
const isLoading = !resolvedScope || !shouldCheckPermissions || canIsLoading;
|
|
@@ -379,6 +383,18 @@ export function PagePermissionGuard({
|
|
|
379
383
|
}
|
|
380
384
|
}, [strictMode, hasChecked, isLoading, can, pageName, operation, user?.id, resolvedScope]);
|
|
381
385
|
|
|
386
|
+
// Debug: Log final render state
|
|
387
|
+
console.log('[PagePermissionGuard] Final render state:', {
|
|
388
|
+
isLoading,
|
|
389
|
+
resolvedScope: !!resolvedScope,
|
|
390
|
+
checkError: checkError?.message,
|
|
391
|
+
can,
|
|
392
|
+
willShowLoading: isLoading || !resolvedScope,
|
|
393
|
+
willShowError: !!checkError,
|
|
394
|
+
willShowAccessDenied: !can,
|
|
395
|
+
willShowContent: !isLoading && !!resolvedScope && !checkError && can
|
|
396
|
+
});
|
|
397
|
+
|
|
382
398
|
// Show loading state
|
|
383
399
|
if (isLoading || !resolvedScope) {
|
|
384
400
|
return <>{loading}</>;
|