Landing page at /landing with full marketing content: hero, features, pricing, testimonials, and beta email signup form. Beta signups email beta@resolutionflow.com via new public endpoint. Unauthenticated users redirect to landing instead of login. Also raises KB Accelerator node limit from 50 to 100 to accommodate dense troubleshooting articles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
import { Navigate, useLocation } from 'react-router-dom'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { usePermissions, type EffectiveRole } from '@/hooks/usePermissions'
|
|
import { Spinner } from '@/components/common/Spinner'
|
|
|
|
interface ProtectedRouteProps {
|
|
requiredRole?: EffectiveRole
|
|
children: React.ReactNode
|
|
}
|
|
|
|
export function ProtectedRoute({ requiredRole, children }: ProtectedRouteProps) {
|
|
const { isAuthenticated, isLoading, user } = useAuthStore()
|
|
const location = useLocation()
|
|
const { effectiveRole } = usePermissions()
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center">
|
|
<Spinner className="border-t-foreground" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!isAuthenticated) {
|
|
return <Navigate to="/landing" state={{ from: location }} replace />
|
|
}
|
|
|
|
// Enforce must_change_password — redirect unless already on /change-password
|
|
if (user?.must_change_password && location.pathname !== '/change-password') {
|
|
return <Navigate to="/change-password" 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
|