Migrate all 84 frontend files from the old themed/colored design to a monochrome glass-morphism design system. Pure black backgrounds, white text with opacity levels, glass-card components with backdrop-blur, and functional color reserved for status indicators only. Foundation: remap CSS variables to monochrome, simplify Tailwind config, remove theme toggle, convert brand logo/wordmark to white. Pages: all 14 pages updated. Components: all common, library, session, step-library, tree-editor, tree-preview, admin, and subscription components converted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
43 lines
1.1 KiB
TypeScript
43 lines
1.1 KiB
TypeScript
import { Navigate, useLocation } from 'react-router-dom'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { usePermissions, type EffectiveRole } from '@/hooks/usePermissions'
|
|
|
|
interface ProtectedRouteProps {
|
|
requiredRole?: EffectiveRole
|
|
children: React.ReactNode
|
|
}
|
|
|
|
export function ProtectedRoute({ requiredRole, children }: ProtectedRouteProps) {
|
|
const { isAuthenticated, isLoading } = useAuthStore()
|
|
const location = useLocation()
|
|
const { effectiveRole } = usePermissions()
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center">
|
|
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!isAuthenticated) {
|
|
return <Navigate to="/login" state={{ from: location }} replace />
|
|
}
|
|
|
|
if (requiredRole) {
|
|
const ROLE_HIERARCHY: Record<EffectiveRole, number> = {
|
|
super_admin: 4,
|
|
owner: 3,
|
|
engineer: 2,
|
|
viewer: 1,
|
|
}
|
|
if (ROLE_HIERARCHY[effectiveRole] < ROLE_HIERARCHY[requiredRole]) {
|
|
return <Navigate to="/trees" replace />
|
|
}
|
|
}
|
|
|
|
return <>{children}</>
|
|
}
|
|
|
|
export default ProtectedRoute
|