@jmruthers/pace-core 0.4.1 → 0.5.3
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/CHANGELOG.md +26 -1
- package/README.md +231 -229
- package/dist/{DataTable-2LB6HI6V.js → DataTable-ZQDRE46Q.js} +15 -17
- package/dist/{DataTable-BDBqkU-i.d.ts → DataTable-ltTFXHS3.d.ts} +25 -51
- package/dist/{Table-CIm9IWqk.d.ts → PublicLoadingSpinner-Bq_-BeK-.d.ts} +635 -122
- package/dist/{UnifiedAuthProvider-V7y63NjT.d.ts → RBACProvider-BO4ilsQB.d.ts} +11 -36
- package/dist/UnifiedAuthProvider-DGQsy-vY.d.ts +103 -0
- package/dist/{api-AIJ3IJX3.js → api-H5A3H4IR.js} +6 -4
- package/dist/{appConfig-fB1pP_v3.d.ts → appConfig-BVGyuvI7.d.ts} +1 -1
- package/dist/appNameResolver-7GHF5ED2.js +22 -0
- package/dist/{audit-PD5L5ZSC.js → audit-BUW3LMJB.js} +3 -3
- package/dist/chunk-5H3C2SWM.js +3293 -0
- package/dist/chunk-5H3C2SWM.js.map +1 -0
- package/dist/chunk-5SIXIV7R.js +1925 -0
- package/dist/chunk-5SIXIV7R.js.map +1 -0
- package/dist/{chunk-YNU5QJ4S.js → chunk-7BNPOCLL.js} +22 -5
- package/dist/chunk-7BNPOCLL.js.map +1 -0
- package/dist/{chunk-4ZTIEYU2.js → chunk-CDQ3PX7L.js} +1 -1
- package/dist/chunk-CDQ3PX7L.js.map +1 -0
- package/dist/chunk-GNTALZV3.js +17 -0
- package/dist/chunk-GNTALZV3.js.map +1 -0
- package/dist/chunk-GWSBHC4J.js +1349 -0
- package/dist/chunk-GWSBHC4J.js.map +1 -0
- package/dist/{chunk-JUUNUW3O.js → chunk-HD7PYDUV.js} +14 -5
- package/dist/chunk-HD7PYDUV.js.map +1 -0
- package/dist/{chunk-H4PZ4B3Y.js → chunk-HXX35Q2M.js} +113 -27
- package/dist/chunk-HXX35Q2M.js.map +1 -0
- package/dist/chunk-K6B7BLSE.js +388 -0
- package/dist/chunk-K6B7BLSE.js.map +1 -0
- package/dist/chunk-M4RW7PIP.js +5441 -0
- package/dist/chunk-M4RW7PIP.js.map +1 -0
- package/dist/chunk-MZBUOP4P.js +119 -0
- package/dist/chunk-MZBUOP4P.js.map +1 -0
- package/dist/chunk-N2EUGZRW.js +98 -0
- package/dist/chunk-N2EUGZRW.js.map +1 -0
- package/dist/chunk-NQ4TOOO6.js +20 -0
- package/dist/chunk-NQ4TOOO6.js.map +1 -0
- package/dist/{chunk-DC5AMYBS.js → chunk-PLDDJCW6.js} +15 -5
- package/dist/chunk-PLDDJCW6.js.map +1 -0
- package/dist/{chunk-IOX76PSM.js → chunk-PVMYVQSM.js} +270 -28
- package/dist/chunk-PVMYVQSM.js.map +1 -0
- package/dist/{chunk-4MCJAK7J.js → chunk-QKHFMQ5R.js} +2155 -4853
- package/dist/chunk-QKHFMQ5R.js.map +1 -0
- package/dist/chunk-QVYBYGT2.js +428 -0
- package/dist/chunk-QVYBYGT2.js.map +1 -0
- package/dist/{chunk-WHLSWC6W.js → chunk-SS3E6QLB.js} +16 -61
- package/dist/chunk-SS3E6QLB.js.map +1 -0
- package/dist/chunk-WJARTBCT.js +128 -0
- package/dist/chunk-WJARTBCT.js.map +1 -0
- package/dist/chunk-YDJW5XTN.js +84 -0
- package/dist/chunk-YDJW5XTN.js.map +1 -0
- package/dist/components.d.ts +907 -10
- package/dist/components.js +3237 -204
- package/dist/components.js.map +1 -1
- package/dist/{database-CAMsquLm.d.ts → database-C3Szpi5J.d.ts} +28 -11
- package/dist/hooks.d.ts +7 -6
- package/dist/hooks.js +33 -11
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +247 -111
- package/dist/index.js +330 -185
- package/dist/index.js.map +1 -1
- package/dist/{organisation-DLNNQhPB.d.ts → organisation-CO3Sh3_D.d.ts} +1 -1
- package/dist/providers.d.ts +5 -4
- package/dist/providers.js +14 -5
- package/dist/rbac/index.d.ts +964 -839
- package/dist/rbac/index.js +58 -1970
- package/dist/rbac/index.js.map +1 -1
- package/dist/styles/core.css +364 -0
- package/dist/styles/fonts/georama-italic.woff2 +0 -0
- package/dist/styles/fonts/georama.woff2 +0 -0
- package/dist/styles/fonts/open-sans-italic.woff2 +0 -0
- package/dist/styles/fonts/open-sans.woff2 +0 -0
- package/dist/styles/fonts/reddit-mono.woff2 +0 -0
- package/dist/styles/index.d.ts +36 -0
- package/dist/styles/index.js +24 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/theming/runtime.d.ts +73 -0
- package/dist/theming/runtime.js +16 -0
- package/dist/theming/runtime.js.map +1 -0
- package/dist/{types-Bavn44NW.d.ts → types-BRDU7N6w.d.ts} +79 -33
- package/dist/types.d.ts +5 -5
- package/dist/types.js +7 -2
- package/dist/types.js.map +1 -1
- package/dist/{unified-BtRpPbmp.d.ts → unified-CMPjE_fv.d.ts} +0 -1
- package/dist/usePublicRouteParams-B2OcAsur.d.ts +477 -0
- package/dist/utils.d.ts +83 -60
- package/dist/utils.js +293 -55651
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +1 -1
- package/docs/INDEX.md +192 -0
- package/docs/README.md +46 -32
- package/docs/api/README.md +231 -229
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +73 -0
- package/docs/api/classes/MissingUserContextError.md +66 -0
- package/docs/api/classes/OrganisationContextRequiredError.md +66 -0
- package/docs/api/classes/PermissionDeniedError.md +73 -0
- package/docs/api/classes/PublicErrorBoundary.md +132 -0
- package/docs/api/classes/RBACAuditManager.md +270 -0
- package/docs/api/classes/RBACCache.md +284 -0
- package/docs/api/classes/RBACEngine.md +141 -0
- package/docs/api/classes/RBACError.md +76 -0
- package/docs/api/classes/RBACNotInitializedError.md +66 -0
- package/docs/api/classes/SecureSupabaseClient.md +135 -0
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- package/docs/api/interfaces/ButtonProps.md +2 -2
- package/docs/api/interfaces/CardProps.md +2 -2
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +96 -0
- package/docs/api/interfaces/DataTableAction.md +98 -7
- package/docs/api/interfaces/DataTableColumn.md +131 -12
- package/docs/api/interfaces/DataTableProps.md +77 -274
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +235 -0
- package/docs/api/interfaces/EventContextType.md +7 -7
- package/docs/api/interfaces/EventLogoProps.md +152 -0
- package/docs/api/interfaces/EventProviderProps.md +2 -2
- package/docs/api/interfaces/FileSizeLimits.md +7 -0
- package/docs/api/interfaces/FileUploadProps.md +154 -0
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +115 -0
- package/docs/api/interfaces/InputProps.md +2 -2
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +107 -0
- package/docs/api/interfaces/NavigationContextType.md +164 -0
- package/docs/api/interfaces/NavigationGuardProps.md +139 -0
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +117 -0
- 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 +26 -26
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +85 -0
- package/docs/api/interfaces/PagePermissionContextType.md +140 -0
- package/docs/api/interfaces/PagePermissionGuardProps.md +153 -0
- package/docs/api/interfaces/PagePermissionProviderProps.md +119 -0
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +153 -0
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +94 -0
- package/docs/api/interfaces/PublicErrorBoundaryState.md +68 -0
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +86 -0
- package/docs/api/interfaces/PublicPageFooterProps.md +112 -0
- package/docs/api/interfaces/PublicPageHeaderProps.md +138 -0
- package/docs/api/interfaces/PublicPageLayoutProps.md +138 -0
- package/docs/api/interfaces/RBACConfig.md +99 -0
- package/docs/api/interfaces/RBACContextType.md +474 -0
- package/docs/api/interfaces/RBACLogger.md +112 -0
- package/docs/api/interfaces/RBACProviderProps.md +107 -0
- package/docs/api/interfaces/RoleBasedRouterContextType.md +151 -0
- package/docs/api/interfaces/RoleBasedRouterProps.md +156 -0
- package/docs/api/interfaces/RouteAccessRecord.md +107 -0
- package/docs/api/interfaces/RouteConfig.md +121 -0
- package/docs/api/interfaces/SecureDataContextType.md +168 -0
- package/docs/api/interfaces/SecureDataProviderProps.md +132 -0
- package/docs/api/interfaces/StorageConfig.md +41 -0
- package/docs/api/interfaces/StorageFileInfo.md +74 -0
- package/docs/api/interfaces/StorageFileMetadata.md +140 -0
- package/docs/api/interfaces/StorageListOptions.md +86 -0
- package/docs/api/interfaces/StorageListResult.md +41 -0
- package/docs/api/interfaces/StorageUploadOptions.md +88 -0
- package/docs/api/interfaces/StorageUploadResult.md +63 -0
- package/docs/api/interfaces/StorageUrlOptions.md +47 -0
- package/docs/api/interfaces/StyleImport.md +2 -2
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +465 -64
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +95 -9
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +136 -0
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +123 -0
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +87 -0
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +81 -0
- package/docs/api/interfaces/UsePublicEventOptions.md +34 -0
- package/docs/api/interfaces/UsePublicEventReturn.md +68 -0
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +94 -0
- package/docs/api/interfaces/UserEventAccess.md +14 -14
- package/docs/api/interfaces/UserMenuProps.md +6 -6
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +4233 -1134
- package/docs/api-reference/components.md +761 -43
- package/docs/api-reference/hooks.md +126 -0
- package/docs/api-reference/providers.md +141 -65
- package/docs/api-reference/types.md +66 -36
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -2
- package/docs/best-practices/README.md +400 -0
- package/docs/consuming-app-example.md +42 -96
- package/docs/consuming-app-vite-config.md +233 -0
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +0 -1
- package/docs/core-concepts/rbac-system.md +23 -10
- package/docs/documentation-style-checklist.md +8 -2
- package/docs/examples/navigation-menu-auth-fix.md +344 -0
- package/docs/getting-started/examples/README.md +15 -1
- package/docs/getting-started/examples/basic-auth-app.md +444 -119
- package/docs/getting-started/examples/full-featured-app.md +6 -6
- package/docs/getting-started/installation.md +231 -52
- package/docs/getting-started/quick-start.md +121 -24
- package/docs/implementation-guides/app-layout.md +133 -108
- package/docs/implementation-guides/data-tables.md +1011 -29
- package/docs/implementation-guides/forms.md +3 -3
- package/docs/implementation-guides/hierarchical-datatable.md +850 -0
- package/docs/implementation-guides/large-datasets.md +2 -2
- package/docs/implementation-guides/navigation.md +1 -1
- package/docs/implementation-guides/permission-enforcement.md +4 -4
- package/docs/implementation-guides/public-pages.md +752 -0
- package/docs/migration/README.md +18 -8
- package/docs/migration/quick-migration-guide.md +320 -0
- package/docs/migration/rbac-migration.md +50 -0
- package/docs/migration/v0.4.15-tailwind-scanning.md +272 -0
- package/docs/migration/v0.4.16-css-first-approach.md +306 -0
- package/docs/migration/v0.4.17-source-path-fix.md +229 -0
- package/docs/migration-guide.md +77 -105
- package/docs/performance/README.md +1 -4
- package/docs/print-components/README.md +258 -0
- package/docs/print-components/api-reference.md +636 -0
- package/docs/print-components/examples/README.md +204 -0
- package/docs/print-components/examples/basic-report.tsx +92 -0
- package/docs/print-components/examples/card-catalog.tsx +149 -0
- package/docs/print-components/examples/cover-page-report.tsx +163 -0
- package/docs/print-components/quick-start.md +363 -0
- package/docs/quick-reference.md +53 -36
- package/docs/rbac/README.md +136 -69
- package/docs/rbac/api-reference.md +39 -8
- package/docs/rbac/examples.md +237 -66
- package/docs/rbac/getting-started.md +131 -16
- package/docs/rbac/quick-start.md +499 -323
- package/docs/rbac/troubleshooting.md +240 -262
- package/docs/security/README.md +50 -1
- package/docs/styles/README.md +226 -111
- package/docs/testing/README.md +6 -10
- package/docs/troubleshooting/README.md +497 -0
- package/docs/troubleshooting/common-issues.md +604 -14
- package/docs/troubleshooting/styling-issues.md +219 -0
- package/docs/troubleshooting/tailwind-content-scanning.md +213 -0
- package/docs/usage.md +54 -91
- package/docs/visual-testing.md +0 -7
- package/package.json +47 -25
- package/src/__mocks__/lucide-react.ts +181 -0
- package/src/__tests__/REBUILD_PLAN.md +223 -0
- package/src/__tests__/TESTING_GUIDELINES.md +341 -0
- package/src/__tests__/fixtures/mocks.ts +93 -0
- package/src/__tests__/helpers/component-test-utils.tsx +145 -0
- package/src/__tests__/helpers/test-utils.tsx +117 -0
- package/src/__tests__/integration/UserProfile.test.tsx +128 -0
- package/src/__tests__/setup.ts +71 -0
- package/src/__tests__/templates/accessibility.test.template.tsx +279 -0
- package/src/__tests__/templates/component.test.template.tsx +144 -0
- package/src/__tests__/templates/hook.test.template.ts +173 -0
- package/src/__tests__/templates/integration.test.template.tsx +199 -0
- package/src/__tests__/types/test.types.ts +106 -0
- package/src/components/Alert/Alert.test.tsx +496 -0
- package/src/components/Alert/Alert.tsx +134 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/Avatar/Avatar.test.tsx +484 -0
- package/src/components/Avatar/Avatar.tsx +84 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/Button/Button.test.tsx +662 -0
- package/src/components/Button/Button.tsx +270 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Card/Card.test.tsx +593 -0
- package/src/components/Card/Card.tsx +271 -0
- package/src/components/Card/index.ts +1 -0
- package/src/components/Checkbox/Checkbox.test.tsx +461 -0
- package/src/components/Checkbox/Checkbox.tsx +75 -0
- package/src/components/Checkbox/__mocks__/Checkbox.tsx +2 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/DataTable/DataTable.tsx +446 -0
- package/src/components/DataTable/__tests__/README.md +145 -0
- package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +66 -0
- package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +103 -0
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +381 -0
- package/src/components/DataTable/__tests__/test-utils.ts +94 -0
- package/src/components/DataTable/components/AccessDeniedPage.tsx +168 -0
- package/src/components/DataTable/components/ActionButtons.tsx +194 -0
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +160 -0
- package/src/components/DataTable/components/ColumnFilter.tsx +114 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +100 -0
- package/src/components/DataTable/components/DataTableBody.tsx +461 -0
- package/src/components/DataTable/components/DataTableCore.tsx +1027 -0
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +214 -0
- package/src/components/DataTable/components/DataTableModals.tsx +87 -0
- package/src/components/DataTable/components/DataTableToolbar.tsx +262 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
- package/src/components/DataTable/components/EditableRow.tsx +159 -0
- package/src/components/DataTable/components/EmptyState.tsx +64 -0
- package/src/components/DataTable/components/ExpandButton.tsx +113 -0
- package/src/components/DataTable/components/FilterRow.tsx +100 -0
- package/src/components/DataTable/components/GroupHeader.tsx +42 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +96 -0
- package/src/components/DataTable/components/ImportModal.tsx +345 -0
- package/src/components/DataTable/components/LoadingState.tsx +12 -0
- package/src/components/DataTable/components/PaginationControls.tsx +332 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +742 -0
- package/src/components/DataTable/components/ViewRowModal.tsx +68 -0
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
- package/src/components/DataTable/components/index.ts +16 -0
- package/src/components/DataTable/context/DataTableContext.tsx +97 -0
- package/src/components/DataTable/core/ActionManager.ts +235 -0
- package/src/components/DataTable/core/ColumnFactory.ts +268 -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 +264 -0
- package/src/components/DataTable/core/PluginRegistry.ts +229 -0
- package/src/components/DataTable/core/StateManager.ts +311 -0
- package/src/components/DataTable/core/index.ts +8 -0
- package/src/components/DataTable/core/interfaces.ts +338 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +419 -0
- package/src/components/DataTable/examples/HierarchicalExample.tsx +475 -0
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +176 -0
- package/src/components/DataTable/examples/PerformanceExample.tsx +505 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +95 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +325 -0
- package/src/components/DataTable/hooks/useHierarchicalState.ts +174 -0
- package/src/components/DataTable/index.ts +68 -0
- package/src/components/DataTable/styles.ts +171 -0
- package/src/components/DataTable/types.ts +511 -0
- package/src/components/DataTable/utils/debugTools.ts +583 -0
- package/src/components/DataTable/utils/errorHandling.ts +494 -0
- package/src/components/DataTable/utils/exportUtils.ts +126 -0
- package/src/components/DataTable/utils/flexibleImport.ts +510 -0
- package/src/components/DataTable/utils/hierarchicalSorting.ts +151 -0
- package/src/components/DataTable/utils/hierarchicalUtils.ts +218 -0
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/DataTable/utils/performanceUtils.ts +351 -0
- package/src/components/Dialog/Dialog.test.tsx +1139 -0
- package/src/components/Dialog/Dialog.tsx +782 -0
- package/src/components/Dialog/README.md +804 -0
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +55 -0
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +68 -0
- package/src/components/Dialog/examples/HtmlDialogExample.tsx +202 -0
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +61 -0
- package/src/components/Dialog/examples/SmartDialogExample.tsx +322 -0
- package/src/components/Dialog/index.ts +12 -0
- package/src/components/Dialog/utils/safeHtml.ts +185 -0
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +752 -0
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +312 -0
- package/src/components/ErrorBoundary/index.ts +8 -0
- package/src/components/EventSelector/EventSelector.tsx +360 -0
- package/src/components/EventSelector/index.ts +3 -0
- package/src/components/EventSelector/types.ts +79 -0
- package/src/components/FileUpload/FileUpload.example.tsx +218 -0
- package/src/components/FileUpload/FileUpload.test.tsx +665 -0
- package/src/components/FileUpload/FileUpload.tsx +237 -0
- package/src/components/FileUpload/index.ts +6 -0
- package/src/components/Footer/Footer.test.tsx +482 -0
- package/src/components/Footer/Footer.tsx +197 -0
- package/src/components/Footer/index.ts +17 -0
- package/src/components/Form/Form.test.tsx +1158 -0
- package/src/components/Form/Form.tsx +166 -0
- package/src/components/Form/FormErrorSummary.tsx +113 -0
- package/src/components/Form/FormField.tsx +249 -0
- package/src/components/Form/FormFieldset.tsx +127 -0
- package/src/components/Form/FormLiveRegion.tsx +198 -0
- package/src/components/Form/index.ts +26 -0
- package/src/components/Header/Header.test.tsx +582 -0
- package/src/components/Header/Header.tsx +301 -0
- package/src/components/Header/index.ts +4 -0
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +489 -0
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +164 -0
- package/src/components/InactivityWarningModal/index.ts +9 -0
- package/src/components/Input/Input.test.tsx +466 -0
- package/src/components/Input/Input.tsx +201 -0
- package/src/components/Input/__mocks__/Input.tsx +2 -0
- package/src/components/Input/index.ts +9 -0
- package/src/components/Label/Label.tsx +186 -0
- package/src/components/Label/index.ts +2 -0
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +450 -0
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +98 -0
- package/src/components/LoadingSpinner/index.ts +3 -0
- package/src/components/LoginForm/LoginForm.test.tsx +816 -0
- package/src/components/LoginForm/LoginForm.tsx +273 -0
- package/src/components/LoginForm/index.ts +3 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +883 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +698 -0
- package/src/components/NavigationMenu/index.ts +10 -0
- package/src/components/NavigationMenu/types.ts +85 -0
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +748 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +304 -0
- package/src/components/OrganisationSelector/index.ts +9 -0
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +891 -0
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +699 -0
- package/src/components/PaceAppLayout/README.md +278 -0
- package/src/components/PaceAppLayout/index.ts +1 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +475 -0
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +221 -0
- package/src/components/PaceLoginPage/index.ts +1 -0
- package/src/components/PasswordReset/PasswordChangeForm.test.tsx +621 -0
- package/src/components/PasswordReset/PasswordChangeForm.tsx +186 -0
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +605 -0
- package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
- package/src/components/PasswordReset/index.ts +4 -0
- package/src/components/PrintButton/PrintButton.tsx +321 -0
- package/src/components/PrintButton/PrintButtonGroup.tsx +84 -0
- package/src/components/PrintButton/PrintToolbar.tsx +94 -0
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +438 -0
- package/src/components/PrintButton/index.ts +33 -0
- package/src/components/PrintButton/types.ts +173 -0
- package/src/components/PrintCard/PrintCard.tsx +154 -0
- package/src/components/PrintCard/PrintCardContent.tsx +57 -0
- package/src/components/PrintCard/PrintCardFooter.tsx +60 -0
- package/src/components/PrintCard/PrintCardGrid.tsx +91 -0
- package/src/components/PrintCard/PrintCardHeader.tsx +78 -0
- package/src/components/PrintCard/PrintCardImage.tsx +81 -0
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +239 -0
- package/src/components/PrintCard/index.ts +34 -0
- package/src/components/PrintCard/types.ts +171 -0
- package/src/components/PrintDataTable/PrintDataTable.tsx +215 -0
- package/src/components/PrintDataTable/PrintTableGroup.tsx +90 -0
- package/src/components/PrintDataTable/PrintTableRow.tsx +76 -0
- package/src/components/PrintDataTable/index.ts +25 -0
- package/src/components/PrintDataTable/types.ts +67 -0
- package/src/components/PrintFooter/PrintFooter.tsx +183 -0
- package/src/components/PrintFooter/PrintFooterContent.tsx +71 -0
- package/src/components/PrintFooter/PrintFooterInfo.tsx +86 -0
- package/src/components/PrintFooter/PrintPageNumber.tsx +90 -0
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +390 -0
- package/src/components/PrintFooter/index.ts +30 -0
- package/src/components/PrintFooter/types.ts +149 -0
- package/src/components/PrintGrid/PrintGrid.tsx +180 -0
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +109 -0
- package/src/components/PrintGrid/PrintGridContainer.tsx +128 -0
- package/src/components/PrintGrid/PrintGridItem.tsx +220 -0
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +359 -0
- package/src/components/PrintGrid/index.ts +31 -0
- package/src/components/PrintGrid/types.ts +159 -0
- package/src/components/PrintHeader/PrintCoverHeader.tsx +230 -0
- package/src/components/PrintHeader/PrintHeader.tsx +150 -0
- package/src/components/PrintHeader/index.ts +17 -0
- package/src/components/PrintHeader/types.ts +42 -0
- package/src/components/PrintLayout/PrintLayout.tsx +122 -0
- package/src/components/PrintLayout/PrintLayoutContext.tsx +66 -0
- package/src/components/PrintLayout/PrintPageBreak.tsx +52 -0
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +230 -0
- package/src/components/PrintLayout/index.ts +19 -0
- package/src/components/PrintLayout/types.ts +37 -0
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +120 -0
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +90 -0
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +112 -0
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +279 -0
- package/src/components/PrintPageBreak/index.ts +23 -0
- package/src/components/PrintPageBreak/types.ts +94 -0
- package/src/components/PrintSection/PrintColumn.tsx +104 -0
- package/src/components/PrintSection/PrintDivider.tsx +101 -0
- package/src/components/PrintSection/PrintSection.tsx +129 -0
- package/src/components/PrintSection/PrintSectionContent.tsx +75 -0
- package/src/components/PrintSection/PrintSectionHeader.tsx +97 -0
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +258 -0
- package/src/components/PrintSection/index.ts +33 -0
- package/src/components/PrintSection/types.ts +155 -0
- package/src/components/PrintText/PrintText.tsx +116 -0
- package/src/components/PrintText/index.ts +16 -0
- package/src/components/PrintText/types.ts +24 -0
- package/src/components/Progress/Progress.tsx +116 -0
- package/src/components/Progress/index.ts +3 -0
- package/src/components/PublicLayout/EventLogo.tsx +287 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +279 -0
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +208 -0
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +130 -0
- package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
- package/src/components/PublicLayout/PublicPageFooter.tsx +124 -0
- package/src/components/PublicLayout/PublicPageHeader.tsx +178 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +232 -0
- package/src/components/PublicLayout/PublicPageProvider.tsx +137 -0
- package/src/components/PublicLayout/index.ts +51 -0
- package/src/components/Select/Select.test.tsx +948 -0
- package/src/components/Select/Select.tsx +660 -0
- package/src/components/Select/index.ts +1 -0
- package/src/components/SuperAdminGuard.tsx +116 -0
- package/src/components/Table/Table.tsx +222 -0
- package/src/components/Table/index.ts +11 -0
- package/src/components/Toast/Toast.test.tsx +586 -0
- package/src/components/Toast/Toast.tsx +339 -0
- package/src/components/Toast/index.ts +14 -0
- package/src/components/Tooltip/Tooltip.test.tsx +852 -0
- package/src/components/Tooltip/Tooltip.tsx +167 -0
- package/src/components/Tooltip/index.ts +7 -0
- package/src/components/UserMenu/UserMenu.test.tsx +702 -0
- package/src/components/UserMenu/UserMenu.tsx +243 -0
- package/src/components/UserMenu/index.ts +3 -0
- package/src/components/examples/PermissionExample.tsx +150 -0
- package/src/components/index.ts +434 -0
- package/src/components.ts +19 -0
- package/src/constants/performance.ts +14 -0
- package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
- package/src/examples/PublicEventPage.tsx +274 -0
- package/src/examples/PublicPageApp.tsx +308 -0
- package/src/examples/PublicPageUsageExample.tsx +216 -0
- package/src/hooks/index.ts +56 -0
- package/src/hooks/public/index.ts +34 -0
- package/src/hooks/public/usePublicEvent.ts +261 -0
- package/src/hooks/public/usePublicEventLogo.ts +285 -0
- package/src/hooks/public/usePublicRouteParams.ts +259 -0
- package/src/hooks/useAppConfig.ts +94 -0
- package/src/hooks/useComponentPerformance.ts +39 -0
- package/src/hooks/useCounter.test.ts +135 -0
- package/src/hooks/useDataTablePerformance.ts +387 -0
- package/src/hooks/useDataTableState.ts +110 -0
- package/src/hooks/useDebounce.test.ts +375 -0
- package/src/hooks/useDebounce.ts +18 -0
- package/src/hooks/useFocusManagement.ts +161 -0
- package/src/hooks/useFocusTrap.ts +155 -0
- package/src/hooks/useInactivityTracker.ts +372 -0
- package/src/hooks/useIsMobile.ts +42 -0
- package/src/hooks/useKeyboardShortcuts.ts +237 -0
- package/src/hooks/useOrganisationPermissions.test.ts +528 -0
- package/src/hooks/useOrganisationPermissions.ts +208 -0
- package/src/hooks/useOrganisationSecurity.test.ts +734 -0
- package/src/hooks/useOrganisationSecurity.ts +262 -0
- package/src/hooks/usePerformanceMonitor.ts +128 -0
- package/src/hooks/usePermissionCache.test.ts +542 -0
- package/src/hooks/usePermissionCache.ts +455 -0
- package/src/hooks/useSecureDataAccess.ts +586 -0
- package/src/hooks/useStorage.ts +274 -0
- package/src/hooks/useToast.ts +242 -0
- package/src/hooks/useZodForm.ts +28 -0
- package/src/index.ts +199 -0
- package/src/providers/AuthProvider.tsx +369 -0
- package/src/providers/EventProvider.tsx +324 -0
- package/src/providers/InactivityProvider.tsx +238 -0
- package/src/providers/OrganisationProvider.tsx +588 -0
- package/src/providers/UnifiedAuthProvider.tsx +327 -0
- package/src/providers/index.ts +17 -0
- package/src/rbac/README.md +885 -0
- package/src/rbac/__tests__/integration.test.tsx +218 -0
- package/src/rbac/adapters.tsx +726 -0
- package/src/rbac/api.test.ts +441 -0
- package/src/rbac/api.ts +339 -0
- package/src/rbac/audit-enhanced.ts +339 -0
- package/src/rbac/audit.ts +338 -0
- package/src/rbac/cache.ts +215 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +294 -0
- package/src/rbac/components/NavigationGuard.tsx +294 -0
- package/src/rbac/components/NavigationProvider.tsx +314 -0
- package/src/rbac/components/PagePermissionGuard.tsx +430 -0
- package/src/rbac/components/PagePermissionProvider.tsx +274 -0
- package/src/rbac/components/PermissionEnforcer.tsx +307 -0
- package/src/rbac/components/RoleBasedRouter.tsx +425 -0
- package/src/rbac/components/SecureDataProvider.tsx +319 -0
- package/src/rbac/components/index.ts +64 -0
- package/src/rbac/config.ts +133 -0
- package/src/rbac/docs/event-based-apps.md +285 -0
- package/src/rbac/engine.ts +1026 -0
- package/src/rbac/eslint-rules.js +285 -0
- package/src/rbac/examples/CompleteRBACExample.tsx +323 -0
- package/src/rbac/examples/EventBasedApp.tsx +238 -0
- package/src/rbac/hooks/index.ts +21 -0
- package/src/rbac/hooks/useCan.test.ts +461 -0
- package/src/rbac/hooks/usePermissions.test.ts +359 -0
- package/src/rbac/hooks/usePermissions.ts +567 -0
- package/src/rbac/hooks/useRBAC.simple.test.ts +90 -0
- package/src/rbac/hooks/useRBAC.test.ts +503 -0
- package/src/rbac/hooks/useRBAC.ts +262 -0
- package/src/rbac/index.ts +109 -0
- package/src/rbac/permissions.ts +293 -0
- package/src/rbac/providers/RBACProvider.tsx +634 -0
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +687 -0
- package/src/rbac/providers/index.ts +11 -0
- package/src/rbac/secureClient.ts +244 -0
- package/src/rbac/security.ts +346 -0
- package/src/rbac/testing/index.tsx +340 -0
- package/src/rbac/types.ts +343 -0
- package/src/rbac/utils/eventContext.ts +83 -0
- package/src/styles/core.css +364 -0
- package/src/styles/index.ts +51 -0
- package/src/theming/runtime.ts +187 -0
- package/src/types/database.ts +472 -0
- package/src/types/guards.ts +30 -0
- package/src/types/index.ts +25 -0
- package/src/types/organisation.ts +184 -0
- package/src/types/security.ts +70 -0
- package/src/types/supabase.ts +166 -0
- package/src/types/theme.ts +6 -0
- package/src/types/unified.ts +262 -0
- package/src/types/validation.ts +164 -0
- package/src/types/vitest-globals.d.ts +43 -0
- package/src/utils/__mocks__/supabaseMock.ts +75 -0
- package/src/utils/__mocks__/supabaseMock.tsx +198 -0
- package/src/utils/appConfig.ts +47 -0
- package/src/utils/appIdResolver.ts +130 -0
- package/src/utils/appNameResolver.ts +190 -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 +46 -0
- package/src/utils/deviceFingerprint.ts +215 -0
- package/src/utils/dynamicUtils.ts +105 -0
- package/src/utils/formatDate.test.ts +241 -0
- package/src/utils/formatting.ts +77 -0
- package/src/utils/index.ts +145 -0
- package/src/utils/lazyLoad.tsx +44 -0
- package/src/utils/organisationContext.ts +135 -0
- package/src/utils/performanceBenchmark.ts +64 -0
- package/src/utils/performanceBudgets.ts +111 -0
- package/src/utils/permissionTypes.ts +37 -0
- package/src/utils/permissionUtils.ts +31 -0
- package/src/utils/print/PrintDataProcessor.ts +390 -0
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +397 -0
- package/src/utils/print/index.ts +29 -0
- package/src/utils/print/types.ts +196 -0
- package/src/utils/print/usePrintOptimization.ts +272 -0
- package/src/utils/sanitization.ts +264 -0
- package/src/utils/schemaUtils.ts +37 -0
- package/src/utils/secureDataAccess.ts +361 -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 +170 -0
- package/src/utils/storage/README.md +348 -0
- package/src/utils/storage/config.ts +100 -0
- package/src/utils/storage/helpers.ts +359 -0
- package/src/utils/storage/index.ts +36 -0
- package/src/utils/storage/types.ts +90 -0
- package/src/utils/validation.ts +111 -0
- package/src/utils/validationUtils.ts +120 -0
- package/src/validation/common.ts +53 -0
- package/src/validation/csrf.ts +214 -0
- package/src/validation/index.ts +43 -0
- package/src/validation/passwordSchema.ts +125 -0
- package/src/validation/sanitization.ts +96 -0
- package/src/validation/schemaUtils.ts +42 -0
- package/src/validation/sqlInjectionProtection.ts +242 -0
- package/src/validation/user.ts +34 -0
- package/dist/chunk-4MCJAK7J.js.map +0 -1
- package/dist/chunk-4ZTIEYU2.js.map +0 -1
- package/dist/chunk-H4PZ4B3Y.js.map +0 -1
- package/dist/chunk-IOX76PSM.js.map +0 -1
- package/dist/chunk-JUUNUW3O.js.map +0 -1
- package/dist/chunk-KK6WIDK6.js +0 -63
- package/dist/chunk-KK6WIDK6.js.map +0 -1
- package/dist/chunk-U7DY5T33.js +0 -11
- package/dist/chunk-U7DY5T33.js.map +0 -1
- package/dist/chunk-WHLSWC6W.js.map +0 -1
- package/dist/chunk-XI7QFSSC.js +0 -790
- package/dist/chunk-XI7QFSSC.js.map +0 -1
- package/dist/chunk-XIJMMBDD.js +0 -73
- package/dist/chunk-XIJMMBDD.js.map +0 -1
- package/dist/chunk-YNU5QJ4S.js.map +0 -1
- package/dist/chunk-YWYCNGWH.js +0 -2070
- package/dist/chunk-YWYCNGWH.js.map +0 -1
- package/dist/chunk-ZJ3UKPIW.js +0 -952
- package/dist/chunk-ZJ3UKPIW.js.map +0 -1
- package/dist/useAppConfig-CZNJJsT_.d.ts +0 -148
- package/dist/{DataTable-2LB6HI6V.js.map → DataTable-ZQDRE46Q.js.map} +0 -0
- package/dist/{api-AIJ3IJX3.js.map → api-H5A3H4IR.js.map} +0 -0
- package/dist/{audit-PD5L5ZSC.js.map → appNameResolver-7GHF5ED2.js.map} +0 -0
- package/dist/{chunk-DC5AMYBS.js.map → audit-BUW3LMJB.js.map} +0 -0
- package/dist/{validation-D2-NNCCE.d.ts → validation-PM_iOaTI.d.ts} +6 -6
package/docs/rbac/quick-start.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# RBAC Quick Start
|
|
2
2
|
|
|
3
|
+
> **⚠️ CRITICAL**: This guide is designed to be impossible to get wrong. Follow every step exactly as written, and your RBAC system will work perfectly.
|
|
4
|
+
|
|
3
5
|
Build your first RBAC-enabled application in under 10 minutes.
|
|
4
6
|
|
|
5
7
|
## 🎯 What We'll Build
|
|
@@ -7,8 +9,16 @@ Build your first RBAC-enabled application in under 10 minutes.
|
|
|
7
9
|
A simple user management app that demonstrates:
|
|
8
10
|
- Permission-based UI rendering
|
|
9
11
|
- Component-level access control
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
+
- Proper app ID resolution (fixes "Access Denied" errors)
|
|
13
|
+
- Correct provider setup (fixes 400/406 database errors)
|
|
14
|
+
- App context requirements (organisation vs event-based)
|
|
15
|
+
|
|
16
|
+
## 🚨 Critical Rules (Follow These or It Won't Work)
|
|
17
|
+
|
|
18
|
+
1. **Never make direct database queries** to `rbac_apps`, `rbac_global_roles`, or other RBAC tables
|
|
19
|
+
2. **Always use `PagePermissionGuard`** for page-level permissions (not manual permission checks)
|
|
20
|
+
3. **Always set up providers correctly** in the exact order shown
|
|
21
|
+
4. **Use the exact app name** from your environment variable (must match database exactly)
|
|
12
22
|
|
|
13
23
|
## 🚀 Step-by-Step Implementation
|
|
14
24
|
|
|
@@ -23,31 +33,40 @@ cd user-manager
|
|
|
23
33
|
|
|
24
34
|
### 2. Install Dependencies
|
|
25
35
|
|
|
36
|
+
**CRITICAL**: Use the exact versions specified to avoid compatibility issues.
|
|
37
|
+
|
|
26
38
|
```bash
|
|
27
|
-
npm install @jmruthers/pace-core @supabase/supabase-js
|
|
39
|
+
npm install @jmruthers/pace-core@^0.4.1 @supabase/supabase-js@^2.38.0
|
|
28
40
|
npm install -D tailwindcss @types/react @types/react-dom
|
|
29
41
|
```
|
|
30
42
|
|
|
31
|
-
### 3. Configure Tailwind CSS
|
|
43
|
+
### 3. Configure Tailwind CSS v4
|
|
32
44
|
|
|
45
|
+
Install Tailwind CSS v4:
|
|
33
46
|
```bash
|
|
34
|
-
|
|
47
|
+
npm install -D @tailwindcss/vite tailwindcss@^4.0.0
|
|
35
48
|
```
|
|
36
49
|
|
|
37
|
-
Update `
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
Update `vite.config.ts`:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// vite.config.ts
|
|
54
|
+
import { defineConfig } from 'vite'
|
|
55
|
+
import react from '@vitejs/plugin-react'
|
|
56
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
57
|
+
|
|
58
|
+
export default defineConfig({
|
|
59
|
+
plugins: [
|
|
60
|
+
react(),
|
|
61
|
+
tailwindcss({
|
|
62
|
+
// CRITICAL: Include pace-core components for scanning
|
|
63
|
+
content: [
|
|
64
|
+
'./src/**/*.{js,ts,jsx,tsx}',
|
|
65
|
+
'./node_modules/@jmruthers/pace-core/**/*.{js,ts,jsx,tsx}'
|
|
66
|
+
]
|
|
67
|
+
})
|
|
45
68
|
],
|
|
46
|
-
|
|
47
|
-
extend: {},
|
|
48
|
-
},
|
|
49
|
-
plugins: [],
|
|
50
|
-
}
|
|
69
|
+
})
|
|
51
70
|
```
|
|
52
71
|
|
|
53
72
|
### 4. Set Up Environment Variables
|
|
@@ -55,389 +74,546 @@ module.exports = {
|
|
|
55
74
|
Create `.env.local`:
|
|
56
75
|
|
|
57
76
|
```bash
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
# .env.local
|
|
78
|
+
VITE_SUPABASE_URL=https://your-project.supabase.co
|
|
79
|
+
VITE_SUPABASE_ANON_KEY=your-anon-key-here
|
|
80
|
+
VITE_APP_NAME=user-manager
|
|
60
81
|
```
|
|
61
82
|
|
|
62
|
-
|
|
83
|
+
**CRITICAL**:
|
|
84
|
+
- Use `VITE_` prefix for Vite projects
|
|
85
|
+
- Use `NEXT_PUBLIC_` prefix for Next.js projects
|
|
86
|
+
- The `VITE_APP_NAME` must match exactly what you have in the `rbac_apps` table
|
|
87
|
+
|
|
88
|
+
### 5. Database Setup (CRITICAL)
|
|
89
|
+
|
|
90
|
+
**CRITICAL**: Your app must be registered in the database before you can use RBAC.
|
|
91
|
+
|
|
92
|
+
#### 5.1 Register Your App
|
|
93
|
+
|
|
94
|
+
Run this SQL in your Supabase SQL editor:
|
|
95
|
+
|
|
96
|
+
```sql
|
|
97
|
+
-- Replace 'user-manager' with your actual app name (must match VITE_APP_NAME)
|
|
98
|
+
INSERT INTO rbac_apps (name, display_name, requires_event, is_active)
|
|
99
|
+
VALUES ('user-manager', 'User Manager', false, true)
|
|
100
|
+
ON CONFLICT (name) DO UPDATE SET
|
|
101
|
+
display_name = EXCLUDED.display_name,
|
|
102
|
+
requires_event = EXCLUDED.requires_event,
|
|
103
|
+
is_active = EXCLUDED.is_active;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### 5.2 Create App Pages
|
|
107
|
+
|
|
108
|
+
```sql
|
|
109
|
+
-- Create pages for your app (replace 'user-manager' with your actual app name)
|
|
110
|
+
INSERT INTO rbac_app_pages (app_id, page_name, page_description)
|
|
111
|
+
SELECT
|
|
112
|
+
a.id,
|
|
113
|
+
unnest(ARRAY['dashboard', 'users', 'settings', 'admin']),
|
|
114
|
+
unnest(ARRAY['Main Dashboard', 'User Management', 'App Settings', 'Admin Panel'])
|
|
115
|
+
FROM rbac_apps a
|
|
116
|
+
WHERE a.name = 'user-manager';
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### 5.3 Set Up Page Permissions
|
|
120
|
+
|
|
121
|
+
```sql
|
|
122
|
+
-- Set up basic permissions for each page (replace 'user-manager' with your actual app name)
|
|
123
|
+
WITH app_pages AS (
|
|
124
|
+
SELECT ap.id as page_id, ap.page_name, a.id as app_id
|
|
125
|
+
FROM rbac_app_pages ap
|
|
126
|
+
JOIN rbac_apps a ON ap.app_id = a.id
|
|
127
|
+
WHERE a.name = 'user-manager'
|
|
128
|
+
)
|
|
129
|
+
INSERT INTO rbac_page_permissions (app_page_id, operation, role_name, allowed, organisation_id)
|
|
130
|
+
SELECT
|
|
131
|
+
ap.page_id,
|
|
132
|
+
op.operation,
|
|
133
|
+
role.role_name,
|
|
134
|
+
CASE
|
|
135
|
+
WHEN role.role_name = 'org_admin' THEN true
|
|
136
|
+
WHEN role.role_name = 'leader' AND ap.page_name != 'admin' THEN true
|
|
137
|
+
WHEN role.role_name = 'member' AND ap.page_name IN ('dashboard', 'users') THEN true
|
|
138
|
+
ELSE false
|
|
139
|
+
END,
|
|
140
|
+
'00000000-0000-0000-0000-000000000000'::uuid -- Replace with your organisation ID
|
|
141
|
+
FROM app_pages ap
|
|
142
|
+
CROSS JOIN (SELECT unnest(ARRAY['read', 'create', 'update', 'delete']) as operation) op
|
|
143
|
+
CROSS JOIN (SELECT unnest(ARRAY['org_admin', 'leader', 'member']) as role_name) role;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### 5.4 Assign User Roles
|
|
147
|
+
|
|
148
|
+
```sql
|
|
149
|
+
-- Grant a user the org_admin role (replace with actual user and organisation IDs)
|
|
150
|
+
INSERT INTO rbac_organisation_roles (user_id, organisation_id, role, status, granted_at)
|
|
151
|
+
VALUES (
|
|
152
|
+
'your-user-id'::uuid,
|
|
153
|
+
'your-organisation-id'::uuid,
|
|
154
|
+
'org_admin',
|
|
155
|
+
'active',
|
|
156
|
+
NOW()
|
|
157
|
+
)
|
|
158
|
+
ON CONFLICT (user_id, organisation_id) DO UPDATE SET
|
|
159
|
+
role = EXCLUDED.role,
|
|
160
|
+
status = EXCLUDED.status,
|
|
161
|
+
updated_at = NOW();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 6. App Context Requirements
|
|
165
|
+
|
|
166
|
+
The RBAC system supports two types of apps based on their context requirements:
|
|
167
|
+
|
|
168
|
+
#### Organisation-Based App (Default)
|
|
169
|
+
- **Context**: Requires `organisationId` in scope
|
|
170
|
+
- **Use case**: User management, organisation settings, general dashboards
|
|
171
|
+
- **Database**: `rbac_apps.requires_event = false`
|
|
172
|
+
|
|
173
|
+
#### Event-Based App
|
|
174
|
+
- **Context**: Requires `eventId` in scope, automatically resolves `organisationId` from event
|
|
175
|
+
- **Use case**: Event registration, event management, event-specific reporting
|
|
176
|
+
- **Database**: `rbac_apps.requires_event = true`
|
|
177
|
+
|
|
178
|
+
### 7. Create Supabase Client
|
|
179
|
+
|
|
180
|
+
Create `src/lib/supabase.ts`:
|
|
63
181
|
|
|
64
182
|
```typescript
|
|
65
183
|
// src/lib/supabase.ts
|
|
66
184
|
import { createClient } from '@supabase/supabase-js'
|
|
67
185
|
|
|
68
|
-
const supabaseUrl =
|
|
69
|
-
const supabaseAnonKey =
|
|
186
|
+
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
|
|
187
|
+
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
|
|
188
|
+
|
|
189
|
+
if (!supabaseUrl || !supabaseAnonKey) {
|
|
190
|
+
throw new Error('Missing Supabase environment variables')
|
|
191
|
+
}
|
|
70
192
|
|
|
71
193
|
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
|
|
72
194
|
```
|
|
73
195
|
|
|
74
|
-
###
|
|
196
|
+
### 8. App Setup (CRITICAL)
|
|
75
197
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
import
|
|
198
|
+
Create `src/App.tsx` with this EXACT structure:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// src/App.tsx
|
|
202
|
+
import React from 'react'
|
|
203
|
+
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
|
|
81
204
|
import {
|
|
82
205
|
UnifiedAuthProvider,
|
|
83
|
-
OrganisationProvider
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
import {
|
|
87
|
-
import {
|
|
88
|
-
import {
|
|
89
|
-
import {
|
|
206
|
+
OrganisationProvider,
|
|
207
|
+
EventProvider
|
|
208
|
+
} from '@jmruthers/pace-core/providers'
|
|
209
|
+
import { setRBACAppName } from '@jmruthers/pace-core/utils'
|
|
210
|
+
import { supabase } from './lib/supabase'
|
|
211
|
+
import { Dashboard } from './pages/Dashboard'
|
|
212
|
+
import { Users } from './pages/Users'
|
|
213
|
+
import { Login } from './pages/Login'
|
|
214
|
+
|
|
215
|
+
// CRITICAL: Set the app name for RBAC resolution
|
|
216
|
+
const APP_NAME = import.meta.env.VITE_APP_NAME
|
|
217
|
+
|
|
218
|
+
if (!APP_NAME) {
|
|
219
|
+
throw new Error('VITE_APP_NAME environment variable is required')
|
|
220
|
+
}
|
|
90
221
|
|
|
91
|
-
//
|
|
92
|
-
|
|
222
|
+
// CRITICAL: Set the app name globally for RBAC
|
|
223
|
+
setRBACAppName(APP_NAME)
|
|
93
224
|
|
|
94
225
|
function App() {
|
|
95
226
|
return (
|
|
96
227
|
<UnifiedAuthProvider
|
|
97
|
-
supabaseClient={supabase}
|
|
98
|
-
appName="user-manager"
|
|
228
|
+
supabaseClient={supabase}
|
|
99
229
|
>
|
|
100
230
|
<OrganisationProvider>
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
<Route
|
|
106
|
-
<Route path="users" element={<
|
|
107
|
-
</
|
|
108
|
-
</
|
|
109
|
-
</
|
|
231
|
+
<EventProvider>
|
|
232
|
+
<Router>
|
|
233
|
+
<Routes>
|
|
234
|
+
<Route path="/login" element={<Login />} />
|
|
235
|
+
<Route path="/" element={<Dashboard />} />
|
|
236
|
+
<Route path="/users" element={<Users />} />
|
|
237
|
+
</Routes>
|
|
238
|
+
</Router>
|
|
239
|
+
</EventProvider>
|
|
110
240
|
</OrganisationProvider>
|
|
111
241
|
</UnifiedAuthProvider>
|
|
112
|
-
)
|
|
242
|
+
)
|
|
113
243
|
}
|
|
114
244
|
|
|
115
|
-
export default App
|
|
245
|
+
export default App
|
|
116
246
|
```
|
|
117
247
|
|
|
118
|
-
###
|
|
119
|
-
|
|
120
|
-
```tsx
|
|
121
|
-
// src/components/LoginPage.tsx
|
|
122
|
-
import { LoginForm } from '@jmruthers/pace-core';
|
|
123
|
-
import { useNavigate } from 'react-router-dom';
|
|
248
|
+
### 9. Create Login Page
|
|
124
249
|
|
|
125
|
-
|
|
126
|
-
const navigate = useNavigate();
|
|
250
|
+
Create `src/pages/Login.tsx`:
|
|
127
251
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
252
|
+
```typescript
|
|
253
|
+
// src/pages/Login.tsx
|
|
254
|
+
import React, { useState } from 'react'
|
|
255
|
+
import { useNavigate } from 'react-router-dom'
|
|
256
|
+
import { supabase } from '../lib/supabase'
|
|
257
|
+
|
|
258
|
+
export function Login() {
|
|
259
|
+
const [email, setEmail] = useState('')
|
|
260
|
+
const [password, setPassword] = useState('')
|
|
261
|
+
const [loading, setLoading] = useState(false)
|
|
262
|
+
const navigate = useNavigate()
|
|
263
|
+
|
|
264
|
+
const handleLogin = async (e: React.FormEvent) => {
|
|
265
|
+
e.preventDefault()
|
|
266
|
+
setLoading(true)
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const { data, error } = await supabase.auth.signInWithPassword({
|
|
270
|
+
email,
|
|
271
|
+
password
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
if (error) {
|
|
275
|
+
alert('Login failed: ' + error.message)
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (data.user) {
|
|
280
|
+
navigate('/')
|
|
281
|
+
}
|
|
282
|
+
} catch (error) {
|
|
283
|
+
alert('Login failed: ' + (error as Error).message)
|
|
284
|
+
} finally {
|
|
285
|
+
setLoading(false)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
131
288
|
|
|
132
289
|
return (
|
|
133
290
|
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
134
291
|
<div className="max-w-md w-full space-y-8">
|
|
135
292
|
<div>
|
|
136
293
|
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
|
|
137
|
-
Sign in to
|
|
294
|
+
Sign in to your account
|
|
138
295
|
</h2>
|
|
139
296
|
</div>
|
|
140
|
-
<
|
|
297
|
+
<form className="mt-8 space-y-6" onSubmit={handleLogin}>
|
|
298
|
+
<div>
|
|
299
|
+
<label htmlFor="email" className="sr-only">
|
|
300
|
+
Email address
|
|
301
|
+
</label>
|
|
302
|
+
<input
|
|
303
|
+
id="email"
|
|
304
|
+
name="email"
|
|
305
|
+
type="email"
|
|
306
|
+
required
|
|
307
|
+
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
|
|
308
|
+
placeholder="Email address"
|
|
309
|
+
value={email}
|
|
310
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
311
|
+
/>
|
|
312
|
+
</div>
|
|
313
|
+
<div>
|
|
314
|
+
<label htmlFor="password" className="sr-only">
|
|
315
|
+
Password
|
|
316
|
+
</label>
|
|
317
|
+
<input
|
|
318
|
+
id="password"
|
|
319
|
+
name="password"
|
|
320
|
+
type="password"
|
|
321
|
+
required
|
|
322
|
+
className="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
|
|
323
|
+
placeholder="Password"
|
|
324
|
+
value={password}
|
|
325
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
326
|
+
/>
|
|
327
|
+
</div>
|
|
328
|
+
<div>
|
|
329
|
+
<button
|
|
330
|
+
type="submit"
|
|
331
|
+
disabled={loading}
|
|
332
|
+
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
|
|
333
|
+
>
|
|
334
|
+
{loading ? 'Signing in...' : 'Sign in'}
|
|
335
|
+
</button>
|
|
336
|
+
</div>
|
|
337
|
+
</form>
|
|
141
338
|
</div>
|
|
142
339
|
</div>
|
|
143
|
-
)
|
|
340
|
+
)
|
|
144
341
|
}
|
|
145
342
|
```
|
|
146
343
|
|
|
147
|
-
###
|
|
344
|
+
### 10. Create Dashboard Page
|
|
148
345
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
{
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
346
|
+
Create `src/pages/Dashboard.tsx`:
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// src/pages/Dashboard.tsx
|
|
350
|
+
import React from 'react'
|
|
351
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers'
|
|
352
|
+
import { PagePermissionGuard } from '@jmruthers/pace-core/rbac'
|
|
353
|
+
import { Link } from 'react-router-dom'
|
|
354
|
+
import { supabase } from '../lib/supabase'
|
|
355
|
+
|
|
356
|
+
export function Dashboard() {
|
|
357
|
+
const { user, selectedOrganisationId } = useUnifiedAuth()
|
|
358
|
+
|
|
359
|
+
if (!user) {
|
|
360
|
+
return <div>Please log in</div>
|
|
164
361
|
}
|
|
165
|
-
];
|
|
166
362
|
|
|
167
|
-
export function AppLayout() {
|
|
168
363
|
return (
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
364
|
+
<div className="min-h-screen bg-gray-50">
|
|
365
|
+
<nav className="bg-white shadow">
|
|
366
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
367
|
+
<div className="flex justify-between h-16">
|
|
368
|
+
<div className="flex items-center">
|
|
369
|
+
<h1 className="text-xl font-semibold">Dashboard</h1>
|
|
370
|
+
</div>
|
|
371
|
+
<div className="flex items-center space-x-4">
|
|
372
|
+
<span className="text-sm text-gray-700">
|
|
373
|
+
{user.email} | Org: {selectedOrganisationId}
|
|
374
|
+
</span>
|
|
375
|
+
<button
|
|
376
|
+
onClick={() => supabase.auth.signOut()}
|
|
377
|
+
className="text-sm text-gray-500 hover:text-gray-700"
|
|
378
|
+
>
|
|
379
|
+
Sign out
|
|
380
|
+
</button>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
</nav>
|
|
385
|
+
|
|
386
|
+
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
|
387
|
+
<div className="px-4 py-6 sm:px-0">
|
|
388
|
+
<div className="border-4 border-dashed border-gray-200 rounded-lg h-96 p-8">
|
|
389
|
+
<h2 className="text-2xl font-bold mb-4">Welcome to your Dashboard</h2>
|
|
390
|
+
|
|
391
|
+
{/* CRITICAL: Use PagePermissionGuard for page-level permissions */}
|
|
392
|
+
<PagePermissionGuard
|
|
393
|
+
pageName="dashboard"
|
|
394
|
+
operation="read"
|
|
395
|
+
fallback={<div>You don't have permission to view the dashboard</div>}
|
|
396
|
+
>
|
|
397
|
+
<div className="space-y-4">
|
|
398
|
+
<p>You have access to the dashboard!</p>
|
|
399
|
+
|
|
400
|
+
<div className="space-x-4">
|
|
401
|
+
<Link
|
|
402
|
+
to="/users"
|
|
403
|
+
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
|
|
404
|
+
>
|
|
405
|
+
View Users
|
|
406
|
+
</Link>
|
|
407
|
+
</div>
|
|
408
|
+
</div>
|
|
409
|
+
</PagePermissionGuard>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
</main>
|
|
413
|
+
</div>
|
|
414
|
+
)
|
|
176
415
|
}
|
|
177
416
|
```
|
|
178
417
|
|
|
179
|
-
###
|
|
418
|
+
### 11. Create Users Page
|
|
419
|
+
|
|
420
|
+
Create `src/pages/Users.tsx`:
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
// src/pages/Users.tsx
|
|
424
|
+
import React from 'react'
|
|
425
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core/providers'
|
|
426
|
+
import { PagePermissionGuard } from '@jmruthers/pace-core/rbac'
|
|
427
|
+
import { Link } from 'react-router-dom'
|
|
428
|
+
import { supabase } from '../lib/supabase'
|
|
429
|
+
|
|
430
|
+
export function Users() {
|
|
431
|
+
const { user, selectedOrganisationId } = useUnifiedAuth()
|
|
432
|
+
|
|
433
|
+
if (!user) {
|
|
434
|
+
return <div>Please log in</div>
|
|
435
|
+
}
|
|
180
436
|
|
|
181
|
-
```tsx
|
|
182
|
-
// src/components/DashboardPage.tsx
|
|
183
|
-
import { Card, CardHeader, CardTitle, CardContent } from '@jmruthers/pace-core';
|
|
184
|
-
import { PermissionEnforcer, useCan } from '@jmruthers/pace-core/rbac';
|
|
185
|
-
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
186
|
-
|
|
187
|
-
export function DashboardPage() {
|
|
188
|
-
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
189
|
-
|
|
190
|
-
// Pattern 1: PermissionEnforcer for automatic scope resolution
|
|
191
437
|
return (
|
|
192
|
-
<div className="
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
438
|
+
<div className="min-h-screen bg-gray-50">
|
|
439
|
+
<nav className="bg-white shadow">
|
|
440
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
441
|
+
<div className="flex justify-between h-16">
|
|
442
|
+
<div className="flex items-center">
|
|
443
|
+
<Link to="/" className="text-blue-600 hover:text-blue-800 mr-4">
|
|
444
|
+
← Back to Dashboard
|
|
445
|
+
</Link>
|
|
446
|
+
<h1 className="text-xl font-semibold">Users</h1>
|
|
447
|
+
</div>
|
|
448
|
+
<div className="flex items-center space-x-4">
|
|
449
|
+
<span className="text-sm text-gray-700">
|
|
450
|
+
{user.email} | Org: {selectedOrganisationId}
|
|
451
|
+
</span>
|
|
452
|
+
<button
|
|
453
|
+
onClick={() => supabase.auth.signOut()}
|
|
454
|
+
className="text-sm text-gray-500 hover:text-gray-700"
|
|
455
|
+
>
|
|
456
|
+
Sign out
|
|
457
|
+
</button>
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
</nav>
|
|
462
|
+
|
|
463
|
+
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
|
464
|
+
<div className="px-4 py-6 sm:px-0">
|
|
465
|
+
<div className="border-4 border-dashed border-gray-200 rounded-lg h-96 p-8">
|
|
466
|
+
<h2 className="text-2xl font-bold mb-4">User Management</h2>
|
|
467
|
+
|
|
468
|
+
{/* CRITICAL: Use PagePermissionGuard for page-level permissions */}
|
|
469
|
+
<PagePermissionGuard
|
|
470
|
+
pageName="users"
|
|
471
|
+
operation="read"
|
|
472
|
+
fallback={<div>You don't have permission to view users</div>}
|
|
473
|
+
>
|
|
474
|
+
<div className="space-y-4">
|
|
475
|
+
<p>You have access to the users page!</p>
|
|
476
|
+
<p>This means your RBAC system is working correctly.</p>
|
|
477
|
+
|
|
478
|
+
<div className="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
|
|
479
|
+
<strong>Success!</strong> Your RBAC setup is working correctly.
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
</PagePermissionGuard>
|
|
483
|
+
</div>
|
|
233
484
|
</div>
|
|
234
|
-
</
|
|
485
|
+
</main>
|
|
235
486
|
</div>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Pattern 2: useCan for specific permission checks
|
|
240
|
-
function PermissionStatus({ userId, organisationId }) {
|
|
241
|
-
const { can, isLoading, error } = useCan(
|
|
242
|
-
userId || '',
|
|
243
|
-
{ organisationId: organisationId || '' },
|
|
244
|
-
'manage:users'
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
if (isLoading) return <p className="text-3xl font-bold">Loading...</p>;
|
|
248
|
-
if (error) return <p className="text-3xl font-bold text-red-500">Error</p>;
|
|
249
|
-
|
|
250
|
-
return (
|
|
251
|
-
<p className="text-3xl font-bold">
|
|
252
|
-
{can ? 'Admin' : 'User'}
|
|
253
|
-
</p>
|
|
254
|
-
);
|
|
487
|
+
)
|
|
255
488
|
}
|
|
256
489
|
```
|
|
257
490
|
|
|
258
|
-
|
|
491
|
+
## 🧪 Test Your Setup
|
|
259
492
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
import { useUnifiedAuth } from '@jmruthers/pace-core/providers';
|
|
265
|
-
import { useState } from 'react';
|
|
266
|
-
|
|
267
|
-
interface User {
|
|
268
|
-
id: string;
|
|
269
|
-
name: string;
|
|
270
|
-
email: string;
|
|
271
|
-
role: string;
|
|
272
|
-
lastLogin: string;
|
|
273
|
-
}
|
|
493
|
+
1. **Start your development server**:
|
|
494
|
+
```bash
|
|
495
|
+
npm run dev
|
|
496
|
+
```
|
|
274
497
|
|
|
275
|
-
|
|
276
|
-
{ id: '1', name: 'John Doe', email: 'john@example.com', role: 'admin', lastLogin: '2024-01-15' },
|
|
277
|
-
{ id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'user', lastLogin: '2024-01-14' },
|
|
278
|
-
{ id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'user', lastLogin: '2024-01-13' },
|
|
279
|
-
];
|
|
280
|
-
|
|
281
|
-
const columns = [
|
|
282
|
-
{
|
|
283
|
-
accessorKey: 'name',
|
|
284
|
-
header: 'Name',
|
|
285
|
-
},
|
|
286
|
-
{
|
|
287
|
-
accessorKey: 'email',
|
|
288
|
-
header: 'Email',
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
accessorKey: 'role',
|
|
292
|
-
header: 'Role',
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
accessorKey: 'lastLogin',
|
|
296
|
-
header: 'Last Login',
|
|
297
|
-
},
|
|
298
|
-
];
|
|
299
|
-
|
|
300
|
-
export function UsersPage() {
|
|
301
|
-
const [users] = useState<User[]>(sampleUsers);
|
|
302
|
-
const { user, selectedOrganisationId } = useUnifiedAuth();
|
|
498
|
+
2. **Navigate to your app** (usually `http://localhost:3000` or `http://localhost:5173`)
|
|
303
499
|
|
|
304
|
-
|
|
305
|
-
<div className="p-6">
|
|
306
|
-
<div className="flex justify-between items-center mb-6">
|
|
307
|
-
<h1 className="text-2xl font-bold">Users</h1>
|
|
308
|
-
|
|
309
|
-
{/* Pattern 1: PermissionEnforcer - automatic scope resolution */}
|
|
310
|
-
<PermissionEnforcer
|
|
311
|
-
permissions={['create:users']}
|
|
312
|
-
operation="user-creation"
|
|
313
|
-
fallback={null} // Don't show anything if no permission
|
|
314
|
-
>
|
|
315
|
-
<button className="bg-blue-500 text-white px-4 py-2 rounded">
|
|
316
|
-
Add User
|
|
317
|
-
</button>
|
|
318
|
-
</PermissionEnforcer>
|
|
319
|
-
</div>
|
|
320
|
-
|
|
321
|
-
<DataTable
|
|
322
|
-
data={users}
|
|
323
|
-
columns={columns}
|
|
324
|
-
features={{
|
|
325
|
-
search: true,
|
|
326
|
-
pagination: true,
|
|
327
|
-
}}
|
|
328
|
-
/>
|
|
329
|
-
|
|
330
|
-
{/* Pattern 2: useCan for specific permission checks */}
|
|
331
|
-
<DeleteActions userId={user?.id} organisationId={selectedOrganisationId} />
|
|
332
|
-
</div>
|
|
333
|
-
);
|
|
334
|
-
}
|
|
500
|
+
3. **You should see the login page**
|
|
335
501
|
|
|
336
|
-
|
|
337
|
-
function DeleteActions({ userId, organisationId }) {
|
|
338
|
-
const { can, isLoading, error } = useCan(
|
|
339
|
-
userId || '',
|
|
340
|
-
{ organisationId: organisationId || '' },
|
|
341
|
-
'delete:users'
|
|
342
|
-
);
|
|
502
|
+
4. **Log in with a user that has the `org_admin` role**
|
|
343
503
|
|
|
344
|
-
|
|
345
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
346
|
-
if (!can) return null;
|
|
504
|
+
5. **You should be redirected to the dashboard**
|
|
347
505
|
|
|
348
|
-
|
|
349
|
-
<div className="mt-4">
|
|
350
|
-
<h3 className="text-lg font-semibold mb-2">Admin Actions</h3>
|
|
351
|
-
<button className="bg-red-500 text-white px-4 py-2 rounded">
|
|
352
|
-
Delete Selected Users
|
|
353
|
-
</button>
|
|
354
|
-
</div>
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
```
|
|
506
|
+
6. **Click "View Users" - you should see the users page**
|
|
358
507
|
|
|
359
|
-
|
|
508
|
+
7. **If you see "You don't have permission" messages, check the troubleshooting section below**
|
|
360
509
|
|
|
361
|
-
|
|
362
|
-
- ✅ User authentication with Supabase
|
|
363
|
-
- ✅ Role-based access control
|
|
364
|
-
- ✅ Permission-based UI rendering
|
|
365
|
-
- ✅ Component-level access control
|
|
366
|
-
- ✅ Modern UI components
|
|
510
|
+
## 🎉 Success Checklist
|
|
367
511
|
|
|
368
|
-
|
|
512
|
+
Your RBAC setup is working correctly if:
|
|
369
513
|
|
|
370
|
-
-
|
|
371
|
-
-
|
|
372
|
-
-
|
|
373
|
-
-
|
|
514
|
+
- [ ] You can log in successfully
|
|
515
|
+
- [ ] You see the dashboard without "Access Denied" messages
|
|
516
|
+
- [ ] You can navigate to the users page
|
|
517
|
+
- [ ] You see "Success! Your RBAC setup is working correctly" on the users page
|
|
518
|
+
- [ ] No 400/406 errors in the browser console
|
|
519
|
+
- [ ] No "App not found" errors in the console
|
|
374
520
|
|
|
375
|
-
##
|
|
521
|
+
## 🚨 Troubleshooting
|
|
376
522
|
|
|
377
|
-
###
|
|
523
|
+
### Issue: "Access Denied" on all pages
|
|
378
524
|
|
|
379
|
-
|
|
380
|
-
// Check multiple permissions
|
|
381
|
-
const { hasPermission } = useCan();
|
|
525
|
+
**Check these in order:**
|
|
382
526
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
hasPermission('delete:users', { userId, scope }),
|
|
388
|
-
]);
|
|
527
|
+
1. **Verify your app is registered**:
|
|
528
|
+
```sql
|
|
529
|
+
SELECT * FROM rbac_apps WHERE name = 'user-manager';
|
|
530
|
+
```
|
|
389
531
|
|
|
390
|
-
|
|
391
|
-
```
|
|
532
|
+
2. **Check your environment variable**:
|
|
533
|
+
```typescript
|
|
534
|
+
console.log('App name:', import.meta.env.VITE_APP_NAME);
|
|
535
|
+
```
|
|
392
536
|
|
|
393
|
-
|
|
537
|
+
3. **Verify user has roles**:
|
|
538
|
+
```sql
|
|
539
|
+
SELECT * FROM rbac_organisation_roles WHERE user_id = 'your-user-id';
|
|
540
|
+
```
|
|
394
541
|
|
|
395
|
-
|
|
396
|
-
|
|
542
|
+
4. **Check page permissions exist**:
|
|
543
|
+
```sql
|
|
544
|
+
SELECT pp.*, ap.page_name, a.name as app_name
|
|
545
|
+
FROM rbac_page_permissions pp
|
|
546
|
+
JOIN rbac_app_pages ap ON pp.app_page_id = ap.id
|
|
547
|
+
JOIN rbac_apps a ON ap.app_id = a.id
|
|
548
|
+
WHERE a.name = 'user-manager';
|
|
549
|
+
```
|
|
397
550
|
|
|
398
|
-
|
|
399
|
-
const { accessLevel, isLoading } = useAccessLevel({
|
|
400
|
-
userId: 'current-user-id',
|
|
401
|
-
scope: { organisationId: 'current-org-id' }
|
|
402
|
-
});
|
|
551
|
+
### Issue: 400/406 Database Errors
|
|
403
552
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
553
|
+
**This means your app is making incorrect database queries. Check:**
|
|
554
|
+
|
|
555
|
+
1. **You're using the providers correctly** (Step 8)
|
|
556
|
+
2. **You're not making direct database queries to RBAC tables**
|
|
557
|
+
3. **You're using the PagePermissionGuard component** (not manual permission checks)
|
|
558
|
+
|
|
559
|
+
### Issue: "App not found or inactive"
|
|
560
|
+
|
|
561
|
+
**Check:**
|
|
562
|
+
|
|
563
|
+
1. **Your app name in the database matches your environment variable exactly**
|
|
564
|
+
2. **Your app is marked as active** (`is_active = true`)
|
|
565
|
+
3. **You're using the correct app name in the UnifiedAuthProvider**
|
|
566
|
+
|
|
567
|
+
### Issue: Components not rendering
|
|
568
|
+
|
|
569
|
+
**Check:**
|
|
570
|
+
|
|
571
|
+
1. **You're using PagePermissionGuard (not manual permission checks)**
|
|
572
|
+
2. **You're providing the correct pageName and operation**
|
|
573
|
+
3. **The page exists in rbac_app_pages table**
|
|
574
|
+
|
|
575
|
+
## 🏢 App Context Requirements
|
|
576
|
+
|
|
577
|
+
Your app is configured as an **organisation-based app** by default. This means:
|
|
578
|
+
|
|
579
|
+
- ✅ Requires `organisationId` in permission scope
|
|
580
|
+
- ✅ Works with organisation-level permissions
|
|
581
|
+
- ✅ Automatically enforces organisation context
|
|
415
582
|
|
|
416
|
-
###
|
|
583
|
+
### Switching to Event-Based App
|
|
417
584
|
|
|
585
|
+
If you want to create an event-based app instead:
|
|
586
|
+
|
|
587
|
+
1. **Update Database**:
|
|
588
|
+
```sql
|
|
589
|
+
UPDATE rbac_apps
|
|
590
|
+
SET requires_event = true
|
|
591
|
+
WHERE name = 'user-manager';
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
2. **Update Frontend**:
|
|
418
595
|
```tsx
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
export async function POST(request: Request) {
|
|
423
|
-
const { userId, organisationId } = await request.json();
|
|
424
|
-
|
|
425
|
-
const canCreate = await isPermitted({
|
|
426
|
-
userId,
|
|
427
|
-
scope: { organisationId },
|
|
428
|
-
permission: 'create:users'
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
if (!canCreate) {
|
|
432
|
-
return Response.json({ error: 'Insufficient permissions' }, { status: 403 });
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Proceed with user creation
|
|
436
|
-
}
|
|
596
|
+
// Event-based app requires eventId instead of organisationId
|
|
597
|
+
const scope = { eventId: selectedEventId, appId: 'user-manager' };
|
|
437
598
|
```
|
|
438
599
|
|
|
439
|
-
|
|
600
|
+
3. **Automatic Organisation Resolution**:
|
|
601
|
+
The system will automatically resolve the organisation ID from the event context.
|
|
602
|
+
|
|
603
|
+
## 🚀 Next Steps
|
|
604
|
+
|
|
605
|
+
- **[API Reference](./api-reference.md)** - Complete API documentation
|
|
606
|
+
- **[Examples](./examples.md)** - More complex usage patterns
|
|
607
|
+
- **[Troubleshooting](./troubleshooting.md)** - Advanced troubleshooting
|
|
608
|
+
- **[Migration Guide](../migration/rbac-migration.md)** - If migrating from legacy RBAC
|
|
609
|
+
|
|
610
|
+
## 🆘 Still Having Issues?
|
|
611
|
+
|
|
612
|
+
If you're still having problems after following this guide exactly:
|
|
613
|
+
|
|
614
|
+
1. **Check the browser console** for specific error messages
|
|
615
|
+
2. **Verify your database setup** matches the SQL commands exactly
|
|
616
|
+
3. **Make sure your environment variables** are set correctly
|
|
617
|
+
4. **Check that your user has the correct roles** in the database
|
|
440
618
|
|
|
441
|
-
-
|
|
442
|
-
- Review [Examples](./examples.md) for usage patterns
|
|
443
|
-
- See [API Reference](./api-reference.md) for detailed documentation
|
|
619
|
+
This guide is designed to be foolproof - if you follow it exactly, your RBAC system will work correctly.
|