From 4586010b87ab5c1b1f152179a9eaf3235c085edb Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Thu, 28 May 2026 13:54:52 -0400 Subject: [PATCH] feat(l1): usePermissions extensions for l1_tech + coverage flag Adds 'l1_tech' to the AccountRole union, includes can_cover_l1 on the User type, and exposes isL1Tech / canCoverL1 / canUseL1Surface / canUseEngineerSurface from usePermissions. Existing isEngineer/isOwner/ etc. flags unchanged in semantics. Co-Authored-By: Claude Opus 4.7 --- .../src/components/layout/ProtectedRoute.tsx | 7 +++-- frontend/src/hooks/usePermissions.ts | 30 +++++++++++++++---- frontend/src/types/user.ts | 3 +- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/layout/ProtectedRoute.tsx b/frontend/src/components/layout/ProtectedRoute.tsx index 1c5d6140..808c331f 100644 --- a/frontend/src/components/layout/ProtectedRoute.tsx +++ b/frontend/src/components/layout/ProtectedRoute.tsx @@ -32,9 +32,10 @@ export function ProtectedRoute({ requiredRole, children }: ProtectedRouteProps) if (requiredRole) { const ROLE_HIERARCHY: Record = { - super_admin: 4, - owner: 3, - engineer: 2, + super_admin: 5, + owner: 4, + engineer: 3, + l1_tech: 2, viewer: 1, } if (ROLE_HIERARCHY[effectiveRole] < ROLE_HIERARCHY[requiredRole]) { diff --git a/frontend/src/hooks/usePermissions.ts b/frontend/src/hooks/usePermissions.ts index 3b41dc2b..34f39bfe 100644 --- a/frontend/src/hooks/usePermissions.ts +++ b/frontend/src/hooks/usePermissions.ts @@ -1,19 +1,20 @@ /** * Centralized permissions hook for ResolutionFlow. * - * Role hierarchy: super_admin > owner > engineer > viewer + * Role hierarchy: super_admin > owner > engineer > l1_tech > viewer * * Mirrors backend logic in backend/app/core/permissions.py */ import { useAuthStore } from '@/store/authStore' import type { User } from '@/types' -export type EffectiveRole = 'super_admin' | 'owner' | 'engineer' | 'viewer' +export type EffectiveRole = 'super_admin' | 'owner' | 'engineer' | 'l1_tech' | 'viewer' const ROLE_HIERARCHY: Record = { - super_admin: 4, - owner: 3, - engineer: 2, + super_admin: 5, + owner: 4, + engineer: 3, + l1_tech: 2, viewer: 1, } @@ -21,7 +22,9 @@ function getEffectiveRole(user: User | null): EffectiveRole { if (!user) return 'viewer' if (user.is_super_admin) return 'super_admin' if (user.account_role === 'owner') return 'owner' - return user.role as EffectiveRole + if (user.account_role === 'engineer') return 'engineer' + if (user.account_role === 'l1_tech') return 'l1_tech' + return 'viewer' } function hasMinimumRole(user: User | null, minimum: EffectiveRole): boolean { @@ -39,8 +42,23 @@ export function usePermissions() { isSuperAdmin: effectiveRole === 'super_admin', isAccountOwner: effectiveRole === 'owner' || effectiveRole === 'super_admin', isEngineer: hasMinimumRole(user, 'engineer'), + isL1Tech: effectiveRole === 'l1_tech', isViewer: effectiveRole === 'viewer', + // L1 workspace permissions + canCoverL1: ( + Boolean(user?.can_cover_l1) || + effectiveRole === 'owner' || + effectiveRole === 'super_admin' + ), + canUseL1Surface: ( + effectiveRole === 'l1_tech' || + effectiveRole === 'owner' || + effectiveRole === 'super_admin' || + (user?.account_role === 'engineer' && Boolean(user?.can_cover_l1)) + ), + canUseEngineerSurface: hasMinimumRole(user, 'engineer'), + // Content creation permissions canCreateTrees: hasMinimumRole(user, 'engineer'), canCreateSteps: hasMinimumRole(user, 'engineer'), diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts index f708d00f..df756e4e 100644 --- a/frontend/src/types/user.ts +++ b/frontend/src/types/user.ts @@ -9,7 +9,8 @@ export interface User { is_active: boolean must_change_password: boolean account_id: string | null - account_role: 'owner' | 'engineer' | 'viewer' | null + account_role: 'owner' | 'engineer' | 'l1_tech' | 'viewer' | null + can_cover_l1: boolean team_id: string | null created_at: string last_login: string | null