@jmruthers/pace-core 0.6.6 → 0.6.7
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/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +12 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
- package/cursor-rules/02-project-structure.mdc +37 -5
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
- package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +26 -16
- package/dist/{chunk-FENMYN2U.js → chunk-5X4QLXRG.js} +1 -3
- package/dist/{chunk-4T7OBVTU.js → chunk-6F3IILHI.js} +1 -1
- package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
- package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
- package/dist/{chunk-7TYHROIV.js → chunk-BM4CQ5P3.js} +50 -8
- package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
- package/dist/{chunk-OHIK3MIO.js → chunk-GHYHJTYV.js} +2 -2
- package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
- package/dist/{chunk-LAZMKTTF.js → chunk-JGWDVX64.js} +281 -347
- package/dist/{chunk-MAGBIDNS.js → chunk-L4XMVJKY.js} +2 -2
- package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
- package/dist/{chunk-ZS5VO5JB.js → chunk-Q7Q7V5NV.js} +406 -451
- package/dist/{chunk-3O3WHILE.js → chunk-VBCS3DUA.js} +236 -60
- package/dist/{chunk-BVP2BCJF.js → chunk-ZKAWKYT4.js} +8 -8
- package/dist/components.d.ts +5 -4
- package/dist/components.js +27 -32
- package/dist/eslint-rules/index.cjs +22 -9
- package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +26 -10
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +18 -17
- package/dist/rbac/index.js +6 -6
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
- package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +8 -8
- package/docs/README.md +1 -1
- package/docs/api/modules.md +47 -31
- package/docs/api-reference/components.md +18 -20
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +1 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/README.md +5 -5
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -1
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +204 -185
- package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
- package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +203 -104
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/eslint-config-pace-core.cjs +21 -10
- package/package.json +6 -5
- package/scripts/install-cursor-rules.cjs +11 -243
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +10 -10
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +137 -153
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
- package/src/components/DataTable/components/EditableRow.tsx +5 -7
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/types.ts +0 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +7 -8
- package/src/components/Dialog/Dialog.test.tsx +1 -0
- package/src/components/Dialog/Dialog.tsx +25 -8
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +86 -77
- package/src/components/Select/types.ts +3 -0
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/public/usePublicEvent.ts +5 -5
- package/src/hooks/public/usePublicEventLogo.ts +5 -5
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +2 -2
- package/src/hooks/useEventTheme.test.ts +7 -7
- package/src/hooks/useEventTheme.ts +1 -4
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/storage/README.md +1 -1
- package/cursor-rules/01-standards-compliance.mdc +0 -285
- package/cursor-rules/04-testing-standards.mdc +0 -270
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
- package/cursor-rules/06-code-quality.mdc +0 -311
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
- package/cursor-rules/10-error-handling-patterns.mdc +0 -179
- package/cursor-rules/11-performance-optimization.mdc +0 -169
- package/cursor-rules/12-ci-cd-integration.mdc +0 -150
- package/dist/DataTable-LRJL4IRV.js +0 -15
- package/dist/eslint-rules/rules/compliance.cjs +0 -348
- package/dist/eslint-rules/rules/components.cjs +0 -113
- package/dist/eslint-rules/rules/imports.cjs +0 -102
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -604
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-standards-compliance.md +0 -188
- package/docs/standards/03-solid-principles.md +0 -39
- package/docs/standards/04-testing-standards.md +0 -36
- package/docs/standards/05-bug-reports-and-features.md +0 -27
- package/docs/standards/06-code-quality.md +0 -34
- package/docs/standards/07-tech-stack-compliance.md +0 -30
- package/docs/standards/10-error-handling-patterns.md +0 -401
- package/docs/standards/11-performance-optimization.md +0 -348
- package/docs/standards/12-ci-cd-integration.md +0 -370
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
- package/scripts/audit/audit-compliance.cjs +0 -1295
- package/scripts/audit/audit-components.cjs +0 -260
- package/scripts/audit/audit-rbac.cjs +0 -954
- package/scripts/audit/audit-standards.cjs +0 -1268
- package/scripts/audit/index.cjs +0 -1927
- package/src/components/DataTable/components/DataTableBody.tsx +0 -478
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/index.cjs +0 -22
- package/src/eslint-rules/rules/components.cjs +0 -113
- package/src/eslint-rules/rules/imports.cjs +0 -102
- package/src/eslint-rules/rules/rbac.cjs +0 -790
- package/src/eslint-rules/utils/helpers.cjs +0 -42
- package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
|
@@ -449,7 +449,7 @@ function AuthenticatedApp() {
|
|
|
449
449
|
|
|
450
450
|
// Don't track inactivity if not authenticated
|
|
451
451
|
if (loading || !user) {
|
|
452
|
-
return <
|
|
452
|
+
return <p>Loading...</p>;
|
|
453
453
|
}
|
|
454
454
|
|
|
455
455
|
return (
|
|
@@ -831,5 +831,5 @@ When user extends session during warning:
|
|
|
831
831
|
|
|
832
832
|
- **[Authentication Integration](./permission-enforcement.md)** - Security patterns
|
|
833
833
|
- **[Performance Optimization](./performance.md)** - Large application considerations
|
|
834
|
-
- **[Accessibility
|
|
835
|
-
- **[Security
|
|
834
|
+
- **[Accessibility Requirements](../standards/4-code-quality-standards.md#accessibility-requirements)** - Accessibility requirements
|
|
835
|
+
- **[Security & RBAC Standards](../standards/6-security-rbac-standards.md)** - Security considerations
|
|
@@ -10,11 +10,12 @@ This guide addresses the common issues when working with large datasets (9,000+
|
|
|
10
10
|
|
|
11
11
|
**Problem**: Header and body columns were misaligned in virtualized mode due to separate table layouts.
|
|
12
12
|
|
|
13
|
-
**Solution**:
|
|
13
|
+
**Solution**: `UnifiedTableBody` component automatically handles virtualization with:
|
|
14
14
|
- Synchronized column sizing between header and body tables
|
|
15
15
|
- `table-fixed` layout for consistent column widths
|
|
16
16
|
- Dynamic column width calculation and synchronization
|
|
17
|
-
-
|
|
17
|
+
- Automatic virtualization when data exceeds 1000 records
|
|
18
|
+
- Proper virtualization handling for optimal performance
|
|
18
19
|
|
|
19
20
|
```tsx
|
|
20
21
|
// ✅ Now properly aligned
|
|
@@ -819,5 +819,5 @@ When invalid organisation IDs are provided:
|
|
|
819
819
|
|
|
820
820
|
- **[RBAC System](../rbac/README.md)** - Role-based access control
|
|
821
821
|
- **[Permission Enforcement](./permission-enforcement.md)** - UI permission patterns
|
|
822
|
-
- **[Security
|
|
823
|
-
- **[
|
|
822
|
+
- **[Security & RBAC Standards](../standards/6-security-rbac-standards.md)** - Security guidelines
|
|
823
|
+
- **[Operations Standards](../standards/9-operations-standards.md)** - Error handling and monitoring
|
|
@@ -299,9 +299,9 @@ EnhancedDataTable
|
|
|
299
299
|
│ ├── SearchIndex
|
|
300
300
|
│ ├── PerformanceMonitor
|
|
301
301
|
│ └── VisibilityTracker
|
|
302
|
-
├──
|
|
302
|
+
├── UnifiedTableBody (with automatic virtualization)
|
|
303
303
|
│ ├── MemoizedRow
|
|
304
|
-
│ └──
|
|
304
|
+
│ └── Virtualized rows (when data > 1000 records)
|
|
305
305
|
└── EnhancedPaginationControls
|
|
306
306
|
├── Performance metrics display
|
|
307
307
|
├── Jump to page functionality
|
|
@@ -701,7 +701,7 @@ useEffect(() => {
|
|
|
701
701
|
```tsx
|
|
702
702
|
const { selectedOrganisationId } = useUnifiedAuth();
|
|
703
703
|
if (!selectedOrganisationId) {
|
|
704
|
-
return <
|
|
704
|
+
return <p>Please select an organisation first</p>;
|
|
705
705
|
}
|
|
706
706
|
```
|
|
707
707
|
|
|
@@ -60,7 +60,7 @@ function OrganisationContextGuard({ children }) {
|
|
|
60
60
|
}, [selectedOrganisation, supabase]);
|
|
61
61
|
|
|
62
62
|
if (!contextReady) {
|
|
63
|
-
return <
|
|
63
|
+
return <p>Setting up organisation context...</p>;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
return <>{children}</>;
|
|
@@ -239,7 +239,7 @@ function UserActions() {
|
|
|
239
239
|
'delete:users'
|
|
240
240
|
);
|
|
241
241
|
|
|
242
|
-
if (isLoadingEdit || isLoadingDelete) return <
|
|
242
|
+
if (isLoadingEdit || isLoadingDelete) return <p>Loading permissions...</p>;
|
|
243
243
|
|
|
244
244
|
return (
|
|
245
245
|
<div>
|
|
@@ -375,7 +375,7 @@ const { can: canEdit, isLoading } = useCan(
|
|
|
375
375
|
'update:users'
|
|
376
376
|
);
|
|
377
377
|
|
|
378
|
-
if (isLoading) return <
|
|
378
|
+
if (isLoading) return <p>Loading...</p>;
|
|
379
379
|
return canEdit ? <EditButton /> : null;
|
|
380
380
|
```
|
|
381
381
|
|
|
@@ -406,7 +406,7 @@ const canCreate = hasPermission('create:users');
|
|
|
406
406
|
const canUpdate = hasPermission('update:users');
|
|
407
407
|
const canDelete = hasPermission('delete:users');
|
|
408
408
|
|
|
409
|
-
if (isLoading) return <
|
|
409
|
+
if (isLoading) return <p>Loading permissions...</p>;
|
|
410
410
|
```
|
|
411
411
|
|
|
412
412
|
### Pattern 3: Component Guards
|
|
@@ -504,8 +504,8 @@ function UserActions({ userId, scope }) {
|
|
|
504
504
|
checkPermission();
|
|
505
505
|
}, [hasPermission, isLoading, userId, scope]);
|
|
506
506
|
|
|
507
|
-
if (error) return <
|
|
508
|
-
if (isLoading) return <
|
|
507
|
+
if (error) return <p>Error checking permissions</p>;
|
|
508
|
+
if (isLoading) return <p>Loading...</p>;
|
|
509
509
|
|
|
510
510
|
return (
|
|
511
511
|
<div>
|
|
@@ -595,7 +595,7 @@ const canEdit = await hasPermission('update:users', { userId, scope });
|
|
|
595
595
|
```tsx
|
|
596
596
|
const { hasPermission, isLoading } = useCan();
|
|
597
597
|
|
|
598
|
-
if (isLoading) return <
|
|
598
|
+
if (isLoading) return <p>Loading permissions...</p>;
|
|
599
599
|
```
|
|
600
600
|
|
|
601
601
|
### Getting Help
|
package/docs/rbac/README.md
CHANGED
|
@@ -56,7 +56,7 @@ function MyComponent() {
|
|
|
56
56
|
<PagePermissionGuard
|
|
57
57
|
pageName="users"
|
|
58
58
|
operation="read"
|
|
59
|
-
fallback={<
|
|
59
|
+
fallback={<p>Access Denied</p>}
|
|
60
60
|
>
|
|
61
61
|
<UserList />
|
|
62
62
|
</PagePermissionGuard>
|
|
@@ -232,9 +232,9 @@ function UsersPage() {
|
|
|
232
232
|
<PagePermissionGuard
|
|
233
233
|
pageName="users"
|
|
234
234
|
operation="read"
|
|
235
|
-
fallback={<
|
|
235
|
+
fallback={<p>You don't have permission to view this page</p>}
|
|
236
236
|
>
|
|
237
|
-
<
|
|
237
|
+
<section>
|
|
238
238
|
<h1>User Management</h1>
|
|
239
239
|
|
|
240
240
|
{/* Multiple operations on same page */}
|
|
@@ -261,7 +261,7 @@ function UsersPage() {
|
|
|
261
261
|
>
|
|
262
262
|
<DeleteUserButtons />
|
|
263
263
|
</PagePermissionGuard>
|
|
264
|
-
</
|
|
264
|
+
</section>
|
|
265
265
|
</PagePermissionGuard>
|
|
266
266
|
);
|
|
267
267
|
}
|
|
@@ -282,7 +282,7 @@ function AdminPanel() {
|
|
|
282
282
|
<PagePermissionGuard
|
|
283
283
|
pageName="admin"
|
|
284
284
|
operation="read"
|
|
285
|
-
fallback={<
|
|
285
|
+
fallback={<p>Access denied</p>}
|
|
286
286
|
>
|
|
287
287
|
<UserManagement />
|
|
288
288
|
</PagePermissionGuard>
|
|
@@ -379,7 +379,7 @@ export function withPermission(permission: string, fallback?: React.ComponentTyp
|
|
|
379
379
|
|
|
380
380
|
// Usage
|
|
381
381
|
const ProtectedUserList = withPermission('read:users', () => (
|
|
382
|
-
<
|
|
382
|
+
<p>Access denied to user list</p>
|
|
383
383
|
))(UserList);
|
|
384
384
|
|
|
385
385
|
// In component
|
|
@@ -436,16 +436,16 @@ export const useOrganisationPermissions = createPermissionHook('organisations');
|
|
|
436
436
|
function UserManagement({ userId, scope }) {
|
|
437
437
|
const { permissions, isLoading } = useUserPermissions(userId, scope);
|
|
438
438
|
|
|
439
|
-
if (isLoading) return <
|
|
439
|
+
if (isLoading) return <p>Loading permissions...</p>;
|
|
440
440
|
|
|
441
441
|
return (
|
|
442
|
-
<
|
|
442
|
+
<section>
|
|
443
443
|
{permissions.read && <UserList />}
|
|
444
444
|
{permissions.create && <CreateUserButton />}
|
|
445
445
|
{permissions.update && <EditUserButton />}
|
|
446
446
|
{permissions.delete && <DeleteUserButton />}
|
|
447
447
|
{permissions.manage && <UserManagementPanel />}
|
|
448
|
-
</
|
|
448
|
+
</section>
|
|
449
449
|
);
|
|
450
450
|
}
|
|
451
451
|
```
|
|
@@ -647,7 +647,7 @@ import { withPermission } from './permission-decorators';
|
|
|
647
647
|
|
|
648
648
|
// Mock component
|
|
649
649
|
const TestComponent = ({ data }: { data: string }) => (
|
|
650
|
-
<
|
|
650
|
+
<p>Test Component: {data}</p>
|
|
651
651
|
);
|
|
652
652
|
|
|
653
653
|
describe('Permission Decorators', () => {
|
|
@@ -684,7 +684,7 @@ describe('Permission Decorators', () => {
|
|
|
684
684
|
})
|
|
685
685
|
}));
|
|
686
686
|
|
|
687
|
-
const FallbackComponent = () => <
|
|
687
|
+
const FallbackComponent = () => <p>Access Denied</p>;
|
|
688
688
|
const ProtectedComponent = withPermission('read:users', FallbackComponent)(TestComponent);
|
|
689
689
|
|
|
690
690
|
render(
|
|
@@ -44,13 +44,13 @@ function UserActions() {
|
|
|
44
44
|
undefined // No pageId for event-app permissions
|
|
45
45
|
);
|
|
46
46
|
|
|
47
|
-
if (error) return <
|
|
48
|
-
if (isLoading) return <
|
|
47
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
48
|
+
if (isLoading) return <p>Checking permissions...</p>;
|
|
49
49
|
|
|
50
50
|
return (
|
|
51
|
-
<
|
|
51
|
+
<section>
|
|
52
52
|
{can && <EventActions />}
|
|
53
|
-
</
|
|
53
|
+
</section>
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
```
|
|
@@ -87,19 +87,19 @@ function PermissionDisplay() {
|
|
|
87
87
|
}
|
|
88
88
|
);
|
|
89
89
|
|
|
90
|
-
if (isLoading) return <
|
|
91
|
-
if (error) return <
|
|
90
|
+
if (isLoading) return <p>Loading permissions...</p>;
|
|
91
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
92
92
|
|
|
93
93
|
return (
|
|
94
|
-
<
|
|
94
|
+
<section>
|
|
95
95
|
<h3>User Permissions</h3>
|
|
96
96
|
{Object.entries(permissions).map(([pageId, operations]) => (
|
|
97
|
-
<
|
|
97
|
+
<p key={pageId}>
|
|
98
98
|
<strong>{pageId}:</strong> {operations.join(', ')}
|
|
99
|
-
</
|
|
99
|
+
</p>
|
|
100
100
|
))}
|
|
101
101
|
<button onClick={refetch}>Refresh</button>
|
|
102
|
-
</
|
|
102
|
+
</section>
|
|
103
103
|
);
|
|
104
104
|
}
|
|
105
105
|
```
|
|
@@ -128,13 +128,13 @@ function AccessLevelDisplay({ userId, scope }) {
|
|
|
128
128
|
scope
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
if (isLoading) return <
|
|
132
|
-
if (error) return <
|
|
131
|
+
if (isLoading) return <p>Loading access level...</p>;
|
|
132
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
133
133
|
|
|
134
134
|
return (
|
|
135
|
-
<
|
|
135
|
+
<section>
|
|
136
136
|
<p>Access Level: {accessLevel || 'None'}</p>
|
|
137
|
-
</
|
|
137
|
+
</section>
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
140
|
```
|
|
@@ -183,12 +183,12 @@ function UserManagement({ userId, scope }) {
|
|
|
183
183
|
}, [checkMultiple, isLoading, userId, scope]);
|
|
184
184
|
|
|
185
185
|
return (
|
|
186
|
-
<
|
|
186
|
+
<section>
|
|
187
187
|
{permissions['read:users'] && <UserList />}
|
|
188
188
|
{permissions['create:users'] && <CreateUserButton />}
|
|
189
189
|
{permissions['update:users'] && <EditUserButton />}
|
|
190
190
|
{permissions['delete:users'] && <DeleteUserButton />}
|
|
191
|
-
</
|
|
191
|
+
</section>
|
|
192
192
|
);
|
|
193
193
|
}
|
|
194
194
|
```
|
|
@@ -221,12 +221,12 @@ import { PermissionEnforcer } from '@jmruthers/pace-core/rbac';
|
|
|
221
221
|
|
|
222
222
|
function UserActions() {
|
|
223
223
|
return (
|
|
224
|
-
<
|
|
224
|
+
<section>
|
|
225
225
|
{/* Pattern 1: PermissionEnforcer - automatic scope resolution */}
|
|
226
226
|
<PermissionEnforcer
|
|
227
227
|
permissions={['read:users']}
|
|
228
228
|
operation="user-management"
|
|
229
|
-
fallback={<
|
|
229
|
+
fallback={<p>Access Denied</p>}
|
|
230
230
|
>
|
|
231
231
|
<UserList />
|
|
232
232
|
</PermissionEnforcer>
|
|
@@ -236,11 +236,11 @@ function UserActions() {
|
|
|
236
236
|
permissions={['create:users', 'update:users']}
|
|
237
237
|
operation="user-crud"
|
|
238
238
|
requireAll={true} // User needs ALL permissions
|
|
239
|
-
fallback={<
|
|
239
|
+
fallback={<p>You need create and update permissions</p>}
|
|
240
240
|
>
|
|
241
241
|
<UserManagementPanel />
|
|
242
242
|
</PermissionEnforcer>
|
|
243
|
-
</
|
|
243
|
+
</section>
|
|
244
244
|
);
|
|
245
245
|
}
|
|
246
246
|
```
|
|
@@ -491,7 +491,7 @@ export function Dashboard() {
|
|
|
491
491
|
const { user, selectedEventId, selectedOrganisationId } = useUnifiedAuth()
|
|
492
492
|
|
|
493
493
|
if (!user) {
|
|
494
|
-
return <
|
|
494
|
+
return <p>Please log in</p>
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
return (
|
|
@@ -580,7 +580,7 @@ export function Participants() {
|
|
|
580
580
|
const { user, selectedEventId, selectedOrganisationId } = useUnifiedAuth()
|
|
581
581
|
|
|
582
582
|
if (!user) {
|
|
583
|
-
return <
|
|
583
|
+
return <p>Please log in</p>
|
|
584
584
|
}
|
|
585
585
|
|
|
586
586
|
return (
|
|
@@ -748,7 +748,7 @@ setupRBAC(supabase) // Must be called BEFORE rendering app
|
|
|
748
748
|
setSelectedEventId('your-event-id');
|
|
749
749
|
}, []);
|
|
750
750
|
|
|
751
|
-
return <
|
|
751
|
+
return <p>...</p>;
|
|
752
752
|
}
|
|
753
753
|
```
|
|
754
754
|
|
package/docs/rbac/examples.md
CHANGED
|
@@ -17,7 +17,7 @@ import { PagePermissionGuard } from '@jmruthers/pace-core/rbac';
|
|
|
17
17
|
|
|
18
18
|
function UserProfile() {
|
|
19
19
|
return (
|
|
20
|
-
<
|
|
20
|
+
<section>
|
|
21
21
|
<h1>User Profile</h1>
|
|
22
22
|
<PagePermissionGuard
|
|
23
23
|
pageName="profile"
|
|
@@ -26,7 +26,7 @@ function UserProfile() {
|
|
|
26
26
|
>
|
|
27
27
|
<EditButton />
|
|
28
28
|
</PagePermissionGuard>
|
|
29
|
-
</
|
|
29
|
+
</section>
|
|
30
30
|
);
|
|
31
31
|
}
|
|
32
32
|
```
|
|
@@ -86,7 +86,7 @@ import { PagePermissionGuard } from '@jmruthers/pace-core/rbac';
|
|
|
86
86
|
|
|
87
87
|
function AdminPanel() {
|
|
88
88
|
return (
|
|
89
|
-
<
|
|
89
|
+
<section>
|
|
90
90
|
<h1>Admin Panel</h1>
|
|
91
91
|
|
|
92
92
|
<PagePermissionGuard
|
|
@@ -100,11 +100,11 @@ function AdminPanel() {
|
|
|
100
100
|
<PagePermissionGuard
|
|
101
101
|
pageName="system"
|
|
102
102
|
operation="manage"
|
|
103
|
-
fallback={<
|
|
103
|
+
fallback={<p>System management access required</p>}
|
|
104
104
|
>
|
|
105
105
|
<SystemSettings />
|
|
106
106
|
</PagePermissionGuard>
|
|
107
|
-
</
|
|
107
|
+
</section>
|
|
108
108
|
);
|
|
109
109
|
}
|
|
110
110
|
```
|
|
@@ -116,14 +116,14 @@ import { AccessLevelGuard } from '@jmruthers/pace-core/rbac';
|
|
|
116
116
|
|
|
117
117
|
function EventManagement({ userId, scope }) {
|
|
118
118
|
return (
|
|
119
|
-
<
|
|
119
|
+
<section>
|
|
120
120
|
<h1>Event Management</h1>
|
|
121
121
|
|
|
122
122
|
<AccessLevelGuard
|
|
123
123
|
userId={userId}
|
|
124
124
|
scope={scope}
|
|
125
125
|
requiredLevel="planner"
|
|
126
|
-
fallback={<
|
|
126
|
+
fallback={<p>Planner access required</p>}
|
|
127
127
|
>
|
|
128
128
|
<EventPlanning />
|
|
129
129
|
</AccessLevelGuard>
|
|
@@ -132,11 +132,11 @@ function EventManagement({ userId, scope }) {
|
|
|
132
132
|
userId={userId}
|
|
133
133
|
scope={scope}
|
|
134
134
|
requiredLevel="admin"
|
|
135
|
-
fallback={<
|
|
135
|
+
fallback={<p>Admin access required</p>}
|
|
136
136
|
>
|
|
137
137
|
<EventAdminPanel />
|
|
138
138
|
</AccessLevelGuard>
|
|
139
|
-
</
|
|
139
|
+
</section>
|
|
140
140
|
);
|
|
141
141
|
}
|
|
142
142
|
```
|
|
@@ -185,7 +185,7 @@ function UsersTable({ users, userId, scope }) {
|
|
|
185
185
|
id: 'actions',
|
|
186
186
|
header: 'Actions',
|
|
187
187
|
cell: ({ row }) => (
|
|
188
|
-
<
|
|
188
|
+
<section className="flex space-x-2">
|
|
189
189
|
{permissions.canEdit && (
|
|
190
190
|
<button onClick={() => editUser(row.original.id)}>
|
|
191
191
|
Edit
|
|
@@ -196,7 +196,7 @@ function UsersTable({ users, userId, scope }) {
|
|
|
196
196
|
Delete
|
|
197
197
|
</button>
|
|
198
198
|
)}
|
|
199
|
-
</
|
|
199
|
+
</section>
|
|
200
200
|
),
|
|
201
201
|
},
|
|
202
202
|
];
|
|
@@ -246,31 +246,31 @@ function UserForm({ userId, scope, userData }) {
|
|
|
246
246
|
|
|
247
247
|
return (
|
|
248
248
|
<form>
|
|
249
|
-
<
|
|
249
|
+
<section>
|
|
250
250
|
<label>Name</label>
|
|
251
251
|
<input defaultValue={userData.name} />
|
|
252
|
-
</
|
|
252
|
+
</section>
|
|
253
253
|
|
|
254
|
-
<
|
|
254
|
+
<section>
|
|
255
255
|
<label>Email</label>
|
|
256
256
|
<input defaultValue={userData.email} />
|
|
257
|
-
</
|
|
257
|
+
</section>
|
|
258
258
|
|
|
259
259
|
{permissions.canEditRole && (
|
|
260
|
-
<
|
|
260
|
+
<section>
|
|
261
261
|
<label>Role</label>
|
|
262
262
|
<select defaultValue={userData.role}>
|
|
263
263
|
<option value="user">User</option>
|
|
264
264
|
<option value="admin">Admin</option>
|
|
265
265
|
</select>
|
|
266
|
-
</
|
|
266
|
+
</section>
|
|
267
267
|
)}
|
|
268
268
|
|
|
269
269
|
{permissions.canEditPermissions && (
|
|
270
|
-
<
|
|
270
|
+
<section>
|
|
271
271
|
<label>Permissions</label>
|
|
272
272
|
<PermissionSelector defaultValue={userData.permissions} />
|
|
273
|
-
</
|
|
273
|
+
</section>
|
|
274
274
|
)}
|
|
275
275
|
|
|
276
276
|
<button type="submit">Save</button>
|
|
@@ -318,7 +318,7 @@ function Navigation({ userId, scope }) {
|
|
|
318
318
|
checkNavigationPermissions();
|
|
319
319
|
}, [hasPermission, isLoading, userId, scope]);
|
|
320
320
|
|
|
321
|
-
if (isLoading) return <
|
|
321
|
+
if (isLoading) return <p>Loading navigation...</p>;
|
|
322
322
|
|
|
323
323
|
return (
|
|
324
324
|
<nav>
|
|
@@ -449,12 +449,12 @@ function OptimizedUserList({ userId, scope }) {
|
|
|
449
449
|
}, [hasPermission, isLoading, userId, scope]);
|
|
450
450
|
|
|
451
451
|
return (
|
|
452
|
-
<
|
|
452
|
+
<section>
|
|
453
453
|
{permissions.canRead && <UserList />}
|
|
454
454
|
{permissions.canCreate && <CreateUserButton />}
|
|
455
455
|
{permissions.canUpdate && <EditUserButton />}
|
|
456
456
|
{permissions.canDelete && <DeleteUserButton />}
|
|
457
|
-
</
|
|
457
|
+
</section>
|
|
458
458
|
);
|
|
459
459
|
}
|
|
460
460
|
```
|
|
@@ -477,11 +477,11 @@ function UserActions({ userId, scope }) {
|
|
|
477
477
|
};
|
|
478
478
|
|
|
479
479
|
return (
|
|
480
|
-
<
|
|
480
|
+
<section>
|
|
481
481
|
<button onClick={() => handleUserUpdate(userData)}>
|
|
482
482
|
Update User
|
|
483
483
|
</button>
|
|
484
|
-
</
|
|
484
|
+
</section>
|
|
485
485
|
);
|
|
486
486
|
}
|
|
487
487
|
```
|
|
@@ -525,18 +525,18 @@ function UserManagement({ userId, scope }) {
|
|
|
525
525
|
}, [hasPermission, isLoading, userId, scope]);
|
|
526
526
|
|
|
527
527
|
if (error) {
|
|
528
|
-
return <
|
|
528
|
+
return <p>Error: {error.message}</p>;
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
if (isLoading) {
|
|
532
|
-
return <
|
|
532
|
+
return <p>Checking permissions...</p>;
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
return (
|
|
536
|
-
<
|
|
537
|
-
{errorMessage && <
|
|
536
|
+
<section>
|
|
537
|
+
{errorMessage && <p className="error">{errorMessage}</p>}
|
|
538
538
|
{canEdit && <EditUserButton />}
|
|
539
|
-
</
|
|
539
|
+
</section>
|
|
540
540
|
);
|
|
541
541
|
}
|
|
542
542
|
```
|
|
@@ -571,9 +571,9 @@ function CustomUserActions({ userId, scope, user }) {
|
|
|
571
571
|
}, [hasPermission, userId, scope, user]);
|
|
572
572
|
|
|
573
573
|
return (
|
|
574
|
-
<
|
|
574
|
+
<section>
|
|
575
575
|
{canEdit && <EditUserButton userId={user.id} />}
|
|
576
|
-
</
|
|
576
|
+
</section>
|
|
577
577
|
);
|
|
578
578
|
}
|
|
579
579
|
```
|
|
@@ -610,11 +610,11 @@ function ProtectedRoute({ children, requiredPermission, userId, scope }) {
|
|
|
610
610
|
}, [hasPermission, isLoading, requiredPermission, userId, scope, navigate]);
|
|
611
611
|
|
|
612
612
|
if (isLoading) {
|
|
613
|
-
return <
|
|
613
|
+
return <p>Checking access...</p>;
|
|
614
614
|
}
|
|
615
615
|
|
|
616
616
|
if (!hasAccess) {
|
|
617
|
-
return <
|
|
617
|
+
return <p>Access denied</p>;
|
|
618
618
|
}
|
|
619
619
|
|
|
620
620
|
return children;
|
|
@@ -679,9 +679,9 @@ test('renders fallback when user lacks permission', () => {
|
|
|
679
679
|
userId="user-123"
|
|
680
680
|
scope={{ organisationId: "org-456" }}
|
|
681
681
|
permission="read:users"
|
|
682
|
-
fallback={<
|
|
682
|
+
fallback={<p>Access Denied</p>}
|
|
683
683
|
>
|
|
684
|
-
<
|
|
684
|
+
<section>User List</section>
|
|
685
685
|
</PermissionGuard>
|
|
686
686
|
);
|
|
687
687
|
|
|
@@ -739,7 +739,7 @@ function UserManagement() {
|
|
|
739
739
|
>
|
|
740
740
|
<CreateUserButton />
|
|
741
741
|
</PermissionEnforcer>
|
|
742
|
-
</
|
|
742
|
+
</section>
|
|
743
743
|
);
|
|
744
744
|
}
|
|
745
745
|
```
|
|
@@ -771,7 +771,7 @@ function EventDashboard() {
|
|
|
771
771
|
>
|
|
772
772
|
<ParticipantManagement />
|
|
773
773
|
</PermissionEnforcer>
|
|
774
|
-
</
|
|
774
|
+
</section>
|
|
775
775
|
);
|
|
776
776
|
}
|
|
777
777
|
```
|
|
@@ -795,7 +795,7 @@ function AdaptiveComponent({ appId }) {
|
|
|
795
795
|
loadAppConfig();
|
|
796
796
|
}, [appId]);
|
|
797
797
|
|
|
798
|
-
if (!appConfig) return <
|
|
798
|
+
if (!appConfig) return <p>Loading app configuration...</p>;
|
|
799
799
|
|
|
800
800
|
// Determine required context based on app type
|
|
801
801
|
const scope = appConfig.requires_event
|
|
@@ -858,15 +858,15 @@ function ContextAwareComponent({ appId }) {
|
|
|
858
858
|
getAppConfig(appId).then(setAppConfig);
|
|
859
859
|
}, [appId]);
|
|
860
860
|
|
|
861
|
-
if (!appConfig) return <
|
|
861
|
+
if (!appConfig) return <p>Loading...</p>;
|
|
862
862
|
|
|
863
863
|
const getContextError = () => {
|
|
864
864
|
if (appConfig.requires_event) {
|
|
865
865
|
return (
|
|
866
|
-
<
|
|
866
|
+
<section>
|
|
867
867
|
<p>Access denied</p>
|
|
868
868
|
<p>This app requires event context</p>
|
|
869
|
-
</
|
|
869
|
+
</section>
|
|
870
870
|
);
|
|
871
871
|
} else {
|
|
872
872
|
return (
|