feat: F002 authentication & session layer
Add permission matrix, ProtectedRoute, RoleGuard, PermissionGate, DemoRoleSwitcher, OrganizationSwitcher, AccessDenied, SessionExpired, and institutional LoginScreen. Wire workspace-level route protection into App.tsx; sidebar filters tabs by allowedWorkspaces. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand'
|
||||
import { UserRole } from '../domain/enums'
|
||||
import { UserRole, WorkspaceType } from '../domain/enums'
|
||||
|
||||
export interface MockUser {
|
||||
id: string
|
||||
@@ -8,15 +8,27 @@ export interface MockUser {
|
||||
role: UserRole
|
||||
organizationId: string
|
||||
organizationName: string
|
||||
allowedWorkspaces: WorkspaceType[]
|
||||
}
|
||||
|
||||
export const SessionStatus = {
|
||||
UNAUTHENTICATED: 'unauthenticated',
|
||||
AUTHENTICATED: 'authenticated',
|
||||
EXPIRED: 'expired',
|
||||
RESTRICTED: 'restricted',
|
||||
ONBOARDING: 'onboarding',
|
||||
} as const
|
||||
export type SessionStatus = typeof SessionStatus[keyof typeof SessionStatus]
|
||||
|
||||
interface SessionState {
|
||||
currentUser: MockUser | null
|
||||
activeOrganizationId: string | null
|
||||
isAuthenticated: boolean
|
||||
sessionStatus: SessionStatus
|
||||
login: (user: MockUser) => void
|
||||
logout: () => void
|
||||
switchOrganization: (organizationId: string) => void
|
||||
setSessionStatus: (status: SessionStatus) => void
|
||||
}
|
||||
|
||||
const mockUser: MockUser = {
|
||||
@@ -26,13 +38,26 @@ const mockUser: MockUser = {
|
||||
role: UserRole.ORGANIZATION_ADMIN,
|
||||
organizationId: 'org-wincasa',
|
||||
organizationName: 'Wincasa AG',
|
||||
allowedWorkspaces: [WorkspaceType.SUPPLY, WorkspaceType.DEMAND, WorkspaceType.OPERATIONS],
|
||||
}
|
||||
|
||||
export const useSessionStore = create<SessionState>((set) => ({
|
||||
currentUser: mockUser,
|
||||
activeOrganizationId: mockUser.organizationId,
|
||||
isAuthenticated: true,
|
||||
login: (user) => set({ currentUser: user, activeOrganizationId: user.organizationId, isAuthenticated: true }),
|
||||
logout: () => set({ currentUser: null, activeOrganizationId: null, isAuthenticated: false }),
|
||||
sessionStatus: SessionStatus.AUTHENTICATED,
|
||||
login: (user) => set({
|
||||
currentUser: user,
|
||||
activeOrganizationId: user.organizationId,
|
||||
isAuthenticated: true,
|
||||
sessionStatus: SessionStatus.AUTHENTICATED,
|
||||
}),
|
||||
logout: () => set({
|
||||
currentUser: null,
|
||||
activeOrganizationId: null,
|
||||
isAuthenticated: false,
|
||||
sessionStatus: SessionStatus.UNAUTHENTICATED,
|
||||
}),
|
||||
switchOrganization: (organizationId) => set({ activeOrganizationId: organizationId }),
|
||||
setSessionStatus: (status) => set({ sessionStatus: status }),
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user