diff --git a/packages/domain/src/auth/capabilities.ts b/packages/domain/src/auth/capabilities.ts new file mode 100644 index 000000000..bb8d7b680 --- /dev/null +++ b/packages/domain/src/auth/capabilities.ts @@ -0,0 +1,88 @@ +/** + * Capabilities — minimal RBAC for Pilot 001-010 + * + * Not pages — capabilities (what user CAN do). + * Roles are combinations of capabilities. + */ + +export type Capability = + // Mission + | 'mission.create' + | 'mission.edit' + | 'mission.assign' + | 'mission.view' + // Artifact + | 'artifact.view' + | 'artifact.download' + | 'artifact.delete' + // Decision + | 'decision.review' + | 'decision.approve' + // Model (Developer Mode) + | 'model.train' + | 'model.deploy' + // Economy + | 'economy.view' + | 'economy.edit' + // Admin + | 'admin.users' + | 'admin.settings' + | 'admin.developerMode'; + +export interface Role { + readonly id: string; + readonly name: string; + readonly capabilities: Capability[]; +} + +export const Roles: Record = { + superAdmin: { + id: 'super_admin', + name: 'Super Admin', + capabilities: [ + 'mission.create', 'mission.edit', 'mission.assign', 'mission.view', + 'artifact.view', 'artifact.download', 'artifact.delete', + 'decision.review', 'decision.approve', + 'model.train', 'model.deploy', + 'economy.view', 'economy.edit', + 'admin.users', 'admin.settings', 'admin.developerMode', + ], + }, + operator: { + id: 'operator', + name: 'Operator', + capabilities: [ + 'mission.create', 'mission.edit', 'mission.assign', 'mission.view', + 'artifact.view', 'artifact.download', + 'decision.review', + 'model.train', 'model.deploy', + 'economy.view', + 'admin.developerMode', + ], + }, + reviewer: { + id: 'reviewer', + name: 'Reviewer', + capabilities: [ + 'mission.view', + 'artifact.view', 'artifact.download', + 'decision.review', 'decision.approve', + ], + }, + pilotUser: { + id: 'pilot_user', + name: 'Pilot User', + capabilities: [ + 'mission.create', 'mission.view', + 'artifact.view', + ], + }, +}; + +export function hasCapability(role: Role, capability: Capability): boolean { + return role.capabilities.includes(capability); +} + +export function hasAnyCapability(role: Role, capabilities: Capability[]): boolean { + return capabilities.some(c => hasCapability(role, c)); +} diff --git a/packages/domain/src/auth/feature-flags.ts b/packages/domain/src/auth/feature-flags.ts new file mode 100644 index 000000000..981c771f8 --- /dev/null +++ b/packages/domain/src/auth/feature-flags.ts @@ -0,0 +1,50 @@ +/** + * Feature Flags — enable features without new releases + * + * Pilot 001-010: Control which modules are visible. + */ + +export type FeatureFlag = + | 'ENABLE_REPLAY' + | 'ENABLE_DATASET_EXPLORER' + | 'ENABLE_MODEL_TRAINING' + | 'ENABLE_HOTSPOTS' + | 'ENABLE_ECONOMIC_ENGINE' + | 'ENABLE_DEVELOPER_MODE'; + +export interface FeatureFlags { + readonly flags: Record; +} + +export const DefaultFeatureFlags: FeatureFlags = { + flags: { + ENABLE_REPLAY: false, + ENABLE_DATASET_EXPLORER: true, + ENABLE_MODEL_TRAINING: false, + ENABLE_HOTSPOTS: false, + ENABLE_ECONOMIC_ENGINE: false, + ENABLE_DEVELOPER_MODE: false, + }, +}; + +export function isEnabled(flags: FeatureFlags, flag: FeatureFlag): boolean { + return flags.flags[flag] ?? false; +} + +export function enable(flags: FeatureFlags, flag: FeatureFlag): FeatureFlags { + return { + flags: { + ...flags.flags, + [flag]: true, + }, + }; +} + +export function disable(flags: FeatureFlags, flag: FeatureFlag): FeatureFlags { + return { + flags: { + ...flags.flags, + [flag]: false, + }, + }; +} diff --git a/packages/domain/src/index.ts b/packages/domain/src/index.ts index 399b3975c..8f80940d3 100644 --- a/packages/domain/src/index.ts +++ b/packages/domain/src/index.ts @@ -88,6 +88,24 @@ export { } from './mission/knowledge-gap'; export * from './common/enums'; + +// Auth +export { + Capability, + Role, + Roles, + hasCapability, + hasAnyCapability, +} from './auth/capabilities'; + +export { + FeatureFlag, + FeatureFlags, + DefaultFeatureFlags, + isEnabled, + enable, + disable, +} from './auth/feature-flags'; export * from './common/errors'; // Artifacts diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index e878c3a92..fc26edc85 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -1,4 +1,5 @@ import { Routes, Route, Link } from 'react-router-dom'; +import { useState } from 'react'; import DatasetExplorer from './pages/DatasetExplorer'; import ArtifactViewer from './pages/ArtifactViewer'; import MissionUpload from './pages/MissionUpload'; @@ -8,10 +9,26 @@ import PilotChecklist from './pages/PilotChecklist'; import ReadinessDashboard from './pages/ReadinessDashboard'; function App() { + const [developerMode, setDeveloperMode] = useState(false); + return (
-

LandveX Intelligence Lab

+
+

LandveX {developerMode ? '(Developer Mode)' : ''}

+ +