import { SystemRole, PermissionKey, type PermissionOverrides } from "@nexus/shared"; import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; const ALL_PERMISSION_KEYS = Object.values(PermissionKey); const PERMISSION_LABELS: Record = { viewPlanning: "View Planning", viewCosts: "View Costs", useAssistantAdvancedTools: "Assistant Advanced Tools", exportData: "Export Data", importData: "Import Data", approveVacations: "Approve Vacations", manageBlueprints: "Manage Blueprints", viewAllResources: "View All Resources", manageResources: "Manage Resources", manageProjects: "Manage Projects", manageAllocations: "Manage Allocations", manageRoles: "Manage Roles", manageUsers: "Manage Users", viewScores: "View Scores", }; const SYSTEM_ROLE_LABELS: Record = { [SystemRole.ADMIN]: "Admin", [SystemRole.MANAGER]: "Manager", [SystemRole.CONTROLLER]: "Controller", [SystemRole.USER]: "User", [SystemRole.VIEWER]: "Viewer", }; export type EditState = { userId: string; systemRole: SystemRole; granted: Set; denied: Set; chapterIds: string; }; type UserEditModalProps = { editState: EditState; selectedUserName: string; editingName: { userId: string; name: string } | null; roleDefaultsMap: Record; isPending: boolean; updateRolePending: boolean; setPermissionsPending: boolean; resetPermissionsPending: boolean; updateNamePending: boolean; onEditStateChange: (state: EditState) => void; onSaveRole: () => void; onSavePermissions: () => void; onReset: () => void; onClose: () => void; onEditingNameChange: (state: { userId: string; name: string } | null) => void; onSaveName: (userId: string, name: string) => void; currentName: string; }; export function UserEditModal({ editState, selectedUserName, editingName, roleDefaultsMap, isPending, updateRolePending, setPermissionsPending, resetPermissionsPending, updateNamePending, onEditStateChange, onSaveRole, onSavePermissions, onReset, onClose, onEditingNameChange, onSaveName, currentName, }: UserEditModalProps) { function cyclePermission(key: string) { const roleDefaults = new Set(roleDefaultsMap[editState.systemRole] ?? []); const isRoleDefault = roleDefaults.has(key as PermissionKey); const isGranted = editState.granted.has(key); const isDenied = editState.denied.has(key); const nextGranted = new Set(editState.granted); const nextDenied = new Set(editState.denied); if (isRoleDefault) { if (isDenied) { nextDenied.delete(key); } else { nextDenied.add(key); nextGranted.delete(key); } } else { if (isGranted) { nextGranted.delete(key); } else { nextGranted.add(key); nextDenied.delete(key); } } onEditStateChange({ ...editState, granted: nextGranted, denied: nextDenied }); } return (
{/* Modal Header */}

Edit User

{selectedUserName}

{/* Modal Body */}
{/* User Name */}

Display Name

{editingName?.userId === editState.userId ? (
onEditingNameChange({ ...editingName, name: e.target.value })} className="flex-1 border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400" autoFocus onKeyDown={(e) => { if (e.key === "Enter" && editingName.name.trim()) { onSaveName(editingName.userId, editingName.name.trim()); } if (e.key === "Escape") onEditingNameChange(null); }} />
) : (
{currentName || "—"}
)}
{/* System Role */}

System Role{" "}

{/* Permissions */}

Permissions{" "}

{" "} Role default {" "} Extra grant × {" "} Denied
{ALL_PERMISSION_KEYS.map((key) => { const roleDefaults = new Set(roleDefaultsMap[editState.systemRole] ?? []); const isRoleDefault = roleDefaults.has(key as PermissionKey); const isGranted = editState.granted.has(key); const isDenied = editState.denied.has(key); let state: "default" | "granted" | "denied" | "off"; if (isDenied) state = "denied"; else if (isGranted) state = "granted"; else if (isRoleDefault) state = "default"; else state = "off"; const stateStyles = { default: "bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800", granted: "bg-blue-50 border-blue-200 dark:bg-blue-900/20 dark:border-blue-800", denied: "bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800", off: "bg-gray-50 border-gray-200 dark:bg-gray-800/50 dark:border-gray-700", }; const checkStyles = { default: "text-green-600 border-green-300 bg-green-100 dark:bg-green-900/40", granted: "text-blue-600 border-blue-300 bg-blue-100 dark:bg-blue-900/40", denied: "text-red-600 border-red-300 bg-red-100 dark:bg-red-900/40", off: "text-gray-400 border-gray-300 dark:border-gray-600", }; return ( ); })}
{/* Chapter Scope */}
onEditStateChange({ ...editState, chapterIds: e.target.value })} placeholder="e.g. chapter-1, chapter-2" className="border border-gray-300 dark:border-gray-600 rounded-lg px-3 py-2 text-sm w-full bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-brand-400" />
{/* Modal Footer */}
); }