@jmruthers/pace-core 0.5.16 → 0.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataTable-RICY7YDA.js → DataTable-BTXP6MW4.js} +3 -3
- package/dist/{chunk-JZCNOXSG.js → chunk-7QEQCZDV.js} +2 -2
- package/dist/{chunk-F6IHN3DC.js → chunk-J3JWBQSC.js} +8 -8
- package/dist/chunk-J3JWBQSC.js.map +1 -0
- package/dist/{chunk-JPXJGMOO.js → chunk-NU3GLMIQ.js} +2 -2
- package/dist/{chunk-V7ZZCH3G.js → chunk-UIBYODOF.js} +32 -14
- package/dist/chunk-UIBYODOF.js.map +1 -0
- package/dist/components.js +3 -3
- package/dist/index.js +4 -4
- package/dist/rbac/index.js +2 -2
- package/dist/utils.js +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +2 -2
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +187 -0
- package/docs/troubleshooting/page-permission-guard-diagnostic.js +129 -0
- package/docs/troubleshooting/page-permission-guard-ui-debugging.md +353 -0
- package/package.json +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +41 -14
- package/src/rbac/hooks/usePermissions.ts +7 -7
- package/dist/chunk-F6IHN3DC.js.map +0 -1
- package/dist/chunk-V7ZZCH3G.js.map +0 -1
- package/src/validation/__tests__/passwordSchema.unit.test 2.ts +0 -98
- /package/dist/{DataTable-RICY7YDA.js.map → DataTable-BTXP6MW4.js.map} +0 -0
- /package/dist/{chunk-JZCNOXSG.js.map → chunk-7QEQCZDV.js.map} +0 -0
- /package/dist/{chunk-JPXJGMOO.js.map → chunk-NU3GLMIQ.js.map} +0 -0
|
@@ -32,8 +32,8 @@ import {
|
|
|
32
32
|
useDataTableContext,
|
|
33
33
|
usePluginRegistry,
|
|
34
34
|
useStateManager
|
|
35
|
-
} from "./chunk-
|
|
36
|
-
import "./chunk-
|
|
35
|
+
} from "./chunk-NU3GLMIQ.js";
|
|
36
|
+
import "./chunk-J3JWBQSC.js";
|
|
37
37
|
import "./chunk-GWSBHC4J.js";
|
|
38
38
|
import "./chunk-7BNPOCLL.js";
|
|
39
39
|
import "./chunk-7UEIZCST.js";
|
|
@@ -96,4 +96,4 @@ export {
|
|
|
96
96
|
usePluginRegistry,
|
|
97
97
|
useStateManager
|
|
98
98
|
};
|
|
99
|
-
//# sourceMappingURL=DataTable-
|
|
99
|
+
//# sourceMappingURL=DataTable-BTXP6MW4.js.map
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
SelectSeparator,
|
|
16
16
|
SelectTrigger,
|
|
17
17
|
SelectValue
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-NU3GLMIQ.js";
|
|
19
19
|
import {
|
|
20
20
|
isPermitted
|
|
21
21
|
} from "./chunk-GWSBHC4J.js";
|
|
@@ -3290,4 +3290,4 @@ export {
|
|
|
3290
3290
|
PublicPageDiagnostic,
|
|
3291
3291
|
PublicPageContextChecker
|
|
3292
3292
|
};
|
|
3293
|
-
//# sourceMappingURL=chunk-
|
|
3293
|
+
//# sourceMappingURL=chunk-7QEQCZDV.js.map
|
|
@@ -230,7 +230,7 @@ function usePermissions(userId, scope) {
|
|
|
230
230
|
setIsLoading(false);
|
|
231
231
|
isFetchingRef.current = false;
|
|
232
232
|
}
|
|
233
|
-
}, [userId, scope]);
|
|
233
|
+
}, [userId, scope.organisationId, scope.eventId, scope.appId]);
|
|
234
234
|
return {
|
|
235
235
|
permissions,
|
|
236
236
|
isLoading,
|
|
@@ -267,7 +267,7 @@ function useCan(userId, scope, permission, pageId, useCache = true) {
|
|
|
267
267
|
} finally {
|
|
268
268
|
setIsLoading(false);
|
|
269
269
|
}
|
|
270
|
-
}, [userId, scope, permission, pageId, useCache]);
|
|
270
|
+
}, [userId, scope.organisationId, scope.eventId, scope.appId, permission, pageId, useCache]);
|
|
271
271
|
useEffect2(() => {
|
|
272
272
|
checkPermission();
|
|
273
273
|
}, [checkPermission]);
|
|
@@ -299,7 +299,7 @@ function useAccessLevel(userId, scope) {
|
|
|
299
299
|
} finally {
|
|
300
300
|
setIsLoading(false);
|
|
301
301
|
}
|
|
302
|
-
}, [userId, scope]);
|
|
302
|
+
}, [userId, scope.organisationId, scope.eventId, scope.appId]);
|
|
303
303
|
useEffect2(() => {
|
|
304
304
|
fetchAccessLevel();
|
|
305
305
|
}, [fetchAccessLevel]);
|
|
@@ -335,7 +335,7 @@ function useMultiplePermissions(userId, scope, permissions, useCache = true) {
|
|
|
335
335
|
} finally {
|
|
336
336
|
setIsLoading(false);
|
|
337
337
|
}
|
|
338
|
-
}, [userId, scope, permissions, useCache]);
|
|
338
|
+
}, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
|
|
339
339
|
useEffect2(() => {
|
|
340
340
|
checkPermissions();
|
|
341
341
|
}, [checkPermissions]);
|
|
@@ -374,7 +374,7 @@ function useHasAnyPermission(userId, scope, permissions, useCache = true) {
|
|
|
374
374
|
} finally {
|
|
375
375
|
setIsLoading(false);
|
|
376
376
|
}
|
|
377
|
-
}, [userId, scope, permissions, useCache]);
|
|
377
|
+
}, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
|
|
378
378
|
useEffect2(() => {
|
|
379
379
|
checkAnyPermission();
|
|
380
380
|
}, [checkAnyPermission]);
|
|
@@ -413,7 +413,7 @@ function useHasAllPermissions(userId, scope, permissions, useCache = true) {
|
|
|
413
413
|
} finally {
|
|
414
414
|
setIsLoading(false);
|
|
415
415
|
}
|
|
416
|
-
}, [userId, scope, permissions, useCache]);
|
|
416
|
+
}, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
|
|
417
417
|
useEffect2(() => {
|
|
418
418
|
checkAllPermissions();
|
|
419
419
|
}, [checkAllPermissions]);
|
|
@@ -444,7 +444,7 @@ function useCachedPermissions(userId, scope) {
|
|
|
444
444
|
} finally {
|
|
445
445
|
setIsLoading(false);
|
|
446
446
|
}
|
|
447
|
-
}, [userId, scope]);
|
|
447
|
+
}, [userId, scope.organisationId, scope.eventId, scope.appId]);
|
|
448
448
|
const invalidateCache = useCallback2(() => {
|
|
449
449
|
fetchCachedPermissions();
|
|
450
450
|
}, [fetchCachedPermissions]);
|
|
@@ -470,4 +470,4 @@ export {
|
|
|
470
470
|
useHasAllPermissions,
|
|
471
471
|
useCachedPermissions
|
|
472
472
|
};
|
|
473
|
-
//# sourceMappingURL=chunk-
|
|
473
|
+
//# sourceMappingURL=chunk-J3JWBQSC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rbac/hooks/useRBAC.ts","../src/rbac/hooks/usePermissions.ts"],"sourcesContent":["/**\n * @file RBAC Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 0.3.0\n *\n * A React hook that provides access to the new RBAC (Role-Based Access Control) system.\n * This hook integrates with the database to provide real-time role and permission information.\n *\n * Features:\n * - Real-time role detection (global, organisation, event-app)\n * - Permission checking with database validation\n * - Hierarchical permission resolution\n * - Loading states and error handling\n * - Type-safe permission operations\n * - Automatic context detection\n *\n * @example\n * ```tsx\n * import { useRBAC } from '@jmruthers/pace-core/rbac';\n * \n * function MyComponent() {\n * const {\n * globalRole,\n * organisationRole,\n * eventAppRole,\n * hasPermission,\n * isSuperAdmin,\n * isLoading,\n * error\n * } = useRBAC();\n * \n * if (isLoading) return <div>Loading permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {isSuperAdmin && <AdminPanel />}\n * {hasPermission('read', 'dashboard') && <Dashboard />}\n * {hasPermission('create', 'events') && <CreateEventButton />}\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible permission-based UI rendering\n * - Supports screen reader friendly conditional content\n *\n * @security\n * - Database-backed permission validation\n * - Hierarchical permission resolution\n * - Organisation context enforcement\n * - Real-time permission updates\n *\n * @performance\n * - Optimized with useMemo and useCallback\n * - Permission caching\n * - Minimal re-renders\n * - Lazy loading of permissions\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - @supabase/supabase-js - Database integration\n * - RBAC types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useUnifiedAuth } from '../../providers/UnifiedAuthProvider';\nimport { useOrganisations } from '../../providers/OrganisationProvider';\nimport { useEvents } from '../../providers/EventProvider';\nimport type { \n UserRBACContext, \n GlobalRole, \n OrganisationRole, \n EventAppRole, \n Operation,\n RBACPermission \n} from '../types';\n\nexport function useRBAC(pageId?: string): UserRBACContext {\n const { user, session, supabase, appName } = useUnifiedAuth();\n const { selectedOrganisation } = useOrganisations();\n \n // Try to get events context, but don't fail if not available\n let selectedEvent = null;\n try {\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } catch (error) {\n // EventProvider not available, continue without event context\n console.debug('useRBAC: EventProvider not available, continuing without event context');\n }\n \n // State\n const [globalRole, setGlobalRole] = useState<GlobalRole | null>(null);\n const [organisationRole, setOrganisationRole] = useState<OrganisationRole | null>(null);\n const [eventAppRole, setEventAppRole] = useState<EventAppRole | null>(null);\n const [permissions, setPermissions] = useState<RBACPermission[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Load RBAC context\n const loadRBACContext = useCallback(async () => {\n if (!user || !session || !supabase || !appName) {\n console.log('[useRBAC] Missing required dependencies, clearing RBAC state');\n setGlobalRole(null);\n setOrganisationRole(null);\n setEventAppRole(null);\n setPermissions([]);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n // First resolve app name to app_id\n const { data: appData, error: appError } = await supabase\n .from('rbac_apps')\n .select('id')\n .eq('name', appName)\n .eq('is_active', true)\n .single();\n\n if (appError || !appData) {\n console.warn('App not found or inactive:', appName);\n setIsLoading(false);\n return;\n }\n\n const { data, error: rpcError } = await supabase.rpc('get_rbac_permissions', {\n p_user_id: user.id,\n p_app_id: appData.id,\n p_event_id: selectedEvent?.event_id || null,\n p_organisation_id: selectedOrganisation?.id || null,\n p_page_id: pageId || null\n });\n\n if (rpcError) {\n throw new Error(`Failed to load RBAC permissions: ${rpcError.message}`);\n }\n\n if (data) {\n // Extract roles from permissions based on permission_type\n const globalPerms = data.filter((p: RBACPermission) => p.permission_type === 'all_permissions');\n const orgPerms = data.filter((p: RBACPermission) => p.permission_type === 'organisation_access');\n const eventPerms = data.filter((p: RBACPermission) => p.permission_type === 'event_app_access');\n\n // Set roles (take the highest permission for each type)\n setGlobalRole(globalPerms.length > 0 ? 'super_admin' : null);\n setOrganisationRole(orgPerms.length > 0 ? orgPerms[0].role_name as OrganisationRole : null);\n setEventAppRole(eventPerms.length > 0 ? eventPerms[0].role_name as EventAppRole : null);\n setPermissions(data);\n } else {\n setPermissions([]);\n }\n } catch (err) {\n console.error('RBAC Context Loading Error:', err);\n setError(err instanceof Error ? err : new Error('Unknown error loading RBAC context'));\n setPermissions([]);\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, session, supabase, appName, selectedEvent?.event_id, selectedOrganisation?.id, pageId]);\n\n // Permission checking function\n const hasPermission = useCallback(async (operation: Operation, targetPageId?: string): Promise<boolean> => {\n if (!user || !session || !supabase || !appName) return false;\n\n // Super admin has all permissions\n if (globalRole === 'super_admin') {\n return true;\n }\n\n try {\n // First resolve app name to app_id\n const { data: appData, error: appError } = await supabase\n .from('rbac_apps')\n .select('id')\n .eq('name', appName)\n .eq('is_active', true)\n .single();\n\n if (appError || !appData) {\n console.warn('App not found or inactive:', appName);\n return false;\n }\n\n const { data, error } = await supabase.rpc('check_page_permission', {\n p_user_id: user.id,\n p_app_id: appData.id,\n p_page_id: targetPageId || pageId || 'default',\n p_operation: operation,\n p_event_id: selectedEvent?.event_id,\n p_organisation_id: selectedOrganisation?.id\n });\n\n if (error) {\n console.error('Permission check failed:', error);\n return false;\n }\n\n return data === true;\n } catch (err) {\n console.error('Permission check error:', err);\n return false;\n }\n }, [user, supabase, appName, selectedEvent, selectedOrganisation, pageId, globalRole]);\n\n // Simple permission check for global roles (synchronous)\n const hasGlobalPermission = useCallback((permission: string): boolean => {\n // Super admin has all permissions\n if (globalRole === 'super_admin') {\n return true;\n }\n \n // Check specific global roles\n if (permission === 'super_admin') {\n return globalRole === 'super_admin';\n }\n \n if (permission === 'org_admin') {\n return organisationRole === 'org_admin' || globalRole === 'super_admin';\n }\n \n return false;\n }, [globalRole, organisationRole]);\n\n // Computed properties\n const isSuperAdmin = useMemo(() => globalRole === 'super_admin', [globalRole]);\n const isOrgAdmin = useMemo(() => organisationRole === 'org_admin', [organisationRole]);\n const isEventAdmin = useMemo(() => eventAppRole === 'event_admin', [eventAppRole]);\n const canManageOrganisation = useMemo(() => \n isSuperAdmin || isOrgAdmin, [isSuperAdmin, isOrgAdmin]\n );\n const canManageEvent = useMemo(() => \n isSuperAdmin || isEventAdmin, [isSuperAdmin, isEventAdmin]\n );\n\n // Load context when dependencies change\n useEffect(() => {\n loadRBACContext();\n }, [loadRBACContext]);\n\n return {\n user, // Add user to the returned context\n globalRole,\n organisationRole,\n eventAppRole,\n hasPermission,\n hasGlobalPermission,\n isSuperAdmin,\n isOrgAdmin,\n isEventAdmin,\n canManageOrganisation,\n canManageEvent,\n isLoading,\n error\n };\n}\n","/**\n * @file RBAC Permission Hooks\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * This module provides React hooks for RBAC functionality.\n */\n\nimport { useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { \n UUID, \n Scope, \n Permission, \n PermissionMap\n} from '../types';\nimport { AccessLevel as AccessLevelType } from '../types';\nimport { \n getAccessLevel, \n getPermissionMap, \n isPermitted,\n isPermittedCached \n} from '../api';\n\n/**\n * Hook to get user's permissions in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error } = usePermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * {permissions['create:users'] && <CreateUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePermissions(userId: UUID, scope: Scope) {\n const [permissions, setPermissions] = useState<PermissionMap>({});\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const isFetchingRef = useRef(false);\n\n useEffect(() => {\n const fetchPermissions = async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({});\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is empty)\n // Return empty permissions and loading true for invalid scopes to prevent premature access denied\n if (!scope.organisationId || scope.organisationId.trim() === '') {\n setPermissions({});\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n };\n\n fetchPermissions();\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const hasPermission = useCallback((permission: Permission): boolean => {\n return !!permissions[permission];\n }, [permissions]);\n\n const hasAnyPermission = useCallback((permissionList: Permission[]): boolean => {\n return permissionList.some(p => !!permissions[p]);\n }, [permissions]);\n\n const hasAllPermissions = useCallback((permissionList: Permission[]): boolean => {\n return permissionList.every(p => !!permissions[p]);\n }, [permissions]);\n\n const refetch = useCallback(async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({});\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is empty)\n if (!scope.organisationId || scope.organisationId.trim() === '') {\n setPermissions({});\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n return {\n permissions,\n isLoading,\n error,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n refetch\n };\n}\n\n/**\n * Hook to check if user can perform an action\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permission - Permission to check\n * @param pageId - Optional page ID\n * @param useCache - Whether to use cached results\n * @returns Permission check state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { can, isLoading, error } = useCan(userId, scope, 'read:users');\n * \n * if (isLoading) return <div>Checking permission...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return can ? <UserList /> : <div>Access denied</div>;\n * }\n * ```\n */\nexport function useCan(\n userId: UUID, \n scope: Scope, \n permission: Permission, \n pageId?: UUID,\n useCache: boolean = true\n) {\n const [can, setCan] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkPermission = useCallback(async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is empty)\n // Return can: false and loading: true for invalid scopes to prevent premature access denied\n if (!scope.organisationId || scope.organisationId.trim() === '') {\n setCan(false);\n setIsLoading(true);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const result = useCache \n ? await isPermittedCached({ userId, scope, permission, pageId })\n : await isPermitted({ userId, scope, permission, pageId });\n \n setCan(result);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permission, pageId, useCache]);\n\n useEffect(() => {\n checkPermission();\n }, [checkPermission]);\n\n return {\n can,\n isLoading,\n error,\n refetch: checkPermission\n };\n}\n\n/**\n * Hook to get user's access level in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for access level checking\n * @returns Access level state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { accessLevel, isLoading, error } = useAccessLevel(userId, scope);\n * \n * if (isLoading) return <div>Loading access level...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * Access Level: {accessLevel}\n * {accessLevel >= AccessLevel.ADMIN && <AdminPanel />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAccessLevel(userId: UUID, scope: Scope): {\n accessLevel: AccessLevelType;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [accessLevel, setAccessLevel] = useState<AccessLevelType>('viewer');\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchAccessLevel = useCallback(async () => {\n if (!userId) {\n setAccessLevel('viewer');\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const level = await getAccessLevel({ userId, scope });\n setAccessLevel(level);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch access level'));\n setAccessLevel('viewer');\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n useEffect(() => {\n fetchAccessLevel();\n }, [fetchAccessLevel]);\n\n return {\n accessLevel,\n isLoading,\n error,\n refetch: fetchAccessLevel\n };\n}\n\n/**\n * Hook to check multiple permissions at once\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Multiple permission check results\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { results, isLoading, error } = useMultiplePermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {results['read:users'] && <UserList />}\n * {results['create:users'] && <CreateUserButton />}\n * {results['update:users'] && <EditUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMultiplePermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n results: Record<Permission, boolean>;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [results, setResults] = useState<Record<Permission, boolean>>({} as Record<Permission, boolean>);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setResults({} as Record<Permission, boolean>);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionResults: Record<Permission, boolean> = {} as Record<Permission, boolean>;\n \n // Check each permission\n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n permissionResults[permission] = result;\n }\n \n setResults(permissionResults);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setResults({} as Record<Permission, boolean>);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkPermissions();\n }, [checkPermissions]);\n\n return {\n results,\n isLoading,\n error,\n refetch: checkPermissions\n };\n}\n\n/**\n * Hook to check if user has any of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has any of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAny, isLoading, error } = useHasAnyPermission(\n * userId, \n * scope, \n * ['read:users', 'create:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAny ? <UserManagementPanel /> : <div>No user permissions</div>;\n * }\n * ```\n */\nexport function useHasAnyPermission(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAny: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAny, setHasAny] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAnyPermission = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAny(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAnyPermission = false;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (result) {\n hasAnyPermission = true;\n break;\n }\n }\n \n setHasAny(hasAnyPermission);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAny(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAnyPermission();\n }, [checkAnyPermission]);\n\n return {\n hasAny,\n isLoading,\n error,\n refetch: checkAnyPermission\n };\n}\n\n/**\n * Hook to check if user has all of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has all of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAll, isLoading, error } = useHasAllPermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAll ? <FullUserManagementPanel /> : <div>Insufficient permissions</div>;\n * }\n * ```\n */\nexport function useHasAllPermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAll: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAll, setHasAll] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAllPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAll(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAllPermissions = true;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (!result) {\n hasAllPermissions = false;\n break;\n }\n }\n \n setHasAll(hasAllPermissions);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAll(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAllPermissions();\n }, [checkAllPermissions]);\n\n return {\n hasAll,\n isLoading,\n error,\n refetch: checkAllPermissions\n };\n}\n\n/**\n * Hook to get cached permissions with TTL management\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Cached permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error, invalidateCache } = useCachedPermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading cached permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * <button onClick={invalidateCache}>Refresh Permissions</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCachedPermissions(userId: UUID, scope: Scope): {\n permissions: PermissionMap;\n isLoading: boolean;\n error: Error | null;\n invalidateCache: () => void;\n refetch: () => Promise<void>;\n} {\n const [permissions, setPermissions] = useState<PermissionMap>({});\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchCachedPermissions = useCallback(async () => {\n if (!userId) {\n setPermissions({});\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch cached permissions'));\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const invalidateCache = useCallback(() => {\n // This would typically invalidate the cache in the actual implementation\n // For now, we'll just refetch\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n useEffect(() => {\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n return {\n permissions,\n isLoading,\n error,\n invalidateCache,\n refetch: fetchCachedPermissions\n };\n}\n"],"mappings":"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","names":["error","useState","useEffect","useCallback","useState","useEffect","useCallback"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useCan
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-J3JWBQSC.js";
|
|
4
4
|
import {
|
|
5
5
|
useDataTablePerformance
|
|
6
6
|
} from "./chunk-SS3E6QLB.js";
|
|
@@ -5465,4 +5465,4 @@ export {
|
|
|
5465
5465
|
GroupHeader,
|
|
5466
5466
|
EditableRow
|
|
5467
5467
|
};
|
|
5468
|
-
//# sourceMappingURL=chunk-
|
|
5468
|
+
//# sourceMappingURL=chunk-NU3GLMIQ.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useAccessLevel,
|
|
3
3
|
useCan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-J3JWBQSC.js";
|
|
5
5
|
import {
|
|
6
6
|
OrganisationContextRequiredError,
|
|
7
7
|
RBACCache,
|
|
@@ -283,12 +283,23 @@ function PagePermissionGuard({
|
|
|
283
283
|
onDenied,
|
|
284
284
|
loading = /* @__PURE__ */ jsx2(DefaultLoading, {})
|
|
285
285
|
}) {
|
|
286
|
+
const instanceId = useMemo2(() => Math.random().toString(36).substr(2, 9), []);
|
|
286
287
|
const { user, selectedOrganisationId, selectedEventId, supabase } = useUnifiedAuth();
|
|
287
288
|
const [hasChecked, setHasChecked] = useState2(false);
|
|
288
289
|
const [checkError, setCheckError] = useState2(null);
|
|
289
290
|
const [resolvedScope, setResolvedScope] = useState2(null);
|
|
290
291
|
const supabaseRef = useRef(supabase);
|
|
291
292
|
supabaseRef.current = supabase;
|
|
293
|
+
const stableScope = useMemo2(() => {
|
|
294
|
+
if (!resolvedScope || !resolvedScope.organisationId) {
|
|
295
|
+
return { organisationId: "", appId: "", eventId: void 0 };
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
organisationId: resolvedScope.organisationId,
|
|
299
|
+
appId: resolvedScope.appId,
|
|
300
|
+
eventId: resolvedScope.eventId
|
|
301
|
+
};
|
|
302
|
+
}, [resolvedScope?.organisationId, resolvedScope?.eventId, resolvedScope?.appId]);
|
|
292
303
|
useEffect2(() => {
|
|
293
304
|
const resolveScope = async () => {
|
|
294
305
|
if (scope) {
|
|
@@ -413,10 +424,10 @@ function PagePermissionGuard({
|
|
|
413
424
|
const permission = useMemo2(() => {
|
|
414
425
|
return `${operation}:page.${pageName}`;
|
|
415
426
|
}, [operation, pageName]);
|
|
416
|
-
console.log(
|
|
417
|
-
console.log(
|
|
418
|
-
console.log(
|
|
419
|
-
console.log(
|
|
427
|
+
console.log(`[PagePermissionGuard] Calling useCan with scope: (instance: ${instanceId})`, resolvedScope);
|
|
428
|
+
console.log(`[PagePermissionGuard] resolvedScope: (instance: ${instanceId})`, resolvedScope);
|
|
429
|
+
console.log(`[PagePermissionGuard] selectedEventId: (instance: ${instanceId})`, selectedEventId);
|
|
430
|
+
console.log(`[PagePermissionGuard] About to call useCan with: (instance: ${instanceId})`, {
|
|
420
431
|
userId: user?.id || "",
|
|
421
432
|
scope: resolvedScope,
|
|
422
433
|
permission,
|
|
@@ -424,12 +435,9 @@ function PagePermissionGuard({
|
|
|
424
435
|
useCache: true
|
|
425
436
|
});
|
|
426
437
|
const shouldCheckPermissions = resolvedScope && resolvedScope.organisationId;
|
|
427
|
-
const scopeForUseCan = useMemo2(() => {
|
|
428
|
-
return shouldCheckPermissions ? resolvedScope : { organisationId: "", appId: "", eventId: void 0 };
|
|
429
|
-
}, [shouldCheckPermissions, resolvedScope]);
|
|
430
438
|
const { can, isLoading: canIsLoading, error: canError } = useCan(
|
|
431
439
|
user?.id || "",
|
|
432
|
-
|
|
440
|
+
stableScope,
|
|
433
441
|
permission,
|
|
434
442
|
effectivePageId,
|
|
435
443
|
true
|
|
@@ -481,6 +489,8 @@ function PagePermissionGuard({
|
|
|
481
489
|
});
|
|
482
490
|
}
|
|
483
491
|
}, [strictMode, hasChecked, isLoading, can, pageName, operation, user?.id, resolvedScope]);
|
|
492
|
+
const shouldShowAccessDenied = !isLoading && !!resolvedScope && !checkError && !can;
|
|
493
|
+
const shouldShowContent = !isLoading && !!resolvedScope && !checkError && can;
|
|
484
494
|
console.log("[PagePermissionGuard] Final render state:", {
|
|
485
495
|
isLoading,
|
|
486
496
|
resolvedScope: !!resolvedScope,
|
|
@@ -490,23 +500,31 @@ function PagePermissionGuard({
|
|
|
490
500
|
shouldCheckPermissions,
|
|
491
501
|
willShowLoading: isLoading || !resolvedScope,
|
|
492
502
|
willShowError: !!checkError,
|
|
493
|
-
willShowAccessDenied:
|
|
494
|
-
willShowContent:
|
|
503
|
+
willShowAccessDenied: shouldShowAccessDenied,
|
|
504
|
+
willShowContent: shouldShowContent,
|
|
495
505
|
scopeKey: resolvedScope ? `${resolvedScope.organisationId}-${resolvedScope.eventId}-${resolvedScope.appId}` : "no-scope"
|
|
496
506
|
});
|
|
497
507
|
const scopeKey = resolvedScope ? `${resolvedScope.organisationId}-${resolvedScope.eventId}-${resolvedScope.appId}` : "no-scope";
|
|
498
508
|
const permissionKey = `${scopeKey}-${can}-${isLoading}-${!!checkError}`;
|
|
499
509
|
if (isLoading || !resolvedScope) {
|
|
510
|
+
console.log(`[PagePermissionGuard] RENDERING LOADING STATE for ${pageName} (instance: ${instanceId})`);
|
|
500
511
|
return /* @__PURE__ */ jsx2("div", { children: loading }, `loading-${permissionKey}`);
|
|
501
512
|
}
|
|
502
513
|
if (checkError) {
|
|
503
514
|
console.error(`[PagePermissionGuard] Permission check failed for page ${pageName}:`, checkError);
|
|
515
|
+
console.log(`[PagePermissionGuard] RENDERING ERROR STATE for ${pageName} (instance: ${instanceId})`);
|
|
504
516
|
return /* @__PURE__ */ jsx2("div", { children: fallback }, `error-${permissionKey}`);
|
|
505
517
|
}
|
|
506
|
-
if (
|
|
518
|
+
if (shouldShowAccessDenied) {
|
|
519
|
+
console.log(`[PagePermissionGuard] RENDERING ACCESS DENIED STATE for ${pageName} (instance: ${instanceId})`);
|
|
507
520
|
return /* @__PURE__ */ jsx2("div", { children: fallback }, `denied-${permissionKey}`);
|
|
508
521
|
}
|
|
509
|
-
|
|
522
|
+
if (shouldShowContent) {
|
|
523
|
+
console.log(`[PagePermissionGuard] RENDERING CONTENT STATE for ${pageName} (instance: ${instanceId})`);
|
|
524
|
+
return /* @__PURE__ */ jsx2("div", { children }, `content-${permissionKey}`);
|
|
525
|
+
}
|
|
526
|
+
console.log(`[PagePermissionGuard] RENDERING FALLBACK STATE for ${pageName} (instance: ${instanceId})`);
|
|
527
|
+
return /* @__PURE__ */ jsx2("div", { children: loading }, `fallback-${permissionKey}`);
|
|
510
528
|
}
|
|
511
529
|
function DefaultAccessDenied() {
|
|
512
530
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center min-h-[200px] p-8 text-center", children: [
|
|
@@ -1947,4 +1965,4 @@ export {
|
|
|
1947
1965
|
getPermissionsForRole,
|
|
1948
1966
|
ALL_PERMISSIONS
|
|
1949
1967
|
};
|
|
1950
|
-
//# sourceMappingURL=chunk-
|
|
1968
|
+
//# sourceMappingURL=chunk-UIBYODOF.js.map
|