import { useState } from 'react' import { Badge, Button, DataTable, EmptyState, Field, Notice, PageHeader, Toolbar } from '@sims/ui' import { createEmployee, deactivateEmployee, getEmployees, patchEmployee, reactivateEmployee, resetEmployeePassword, EMPLOYEE_ROLES, type Employee, type EmployeeRole, } from '../api' import { useData } from './Clients' /** * Employee management (owner-only page; the nav item is hidden for others and the * server 403s every mutation anyway). Standard table per spec §6a: name, email, * role, active, actions — plus add / edit / reset-password forms. Email is the * login id and is immutable once created. */ export function Employees() { const list = useData(getEmployees, []) const [adding, setAdding] = useState(false) const [editing, setEditing] = useState() const [resetting, setResetting] = useState() const [error, setError] = useState() const act = (p: Promise) => { setError(undefined) p.then(() => list.reload()).catch((e: Error) => setError(e.message)) } const employees = list.data?.employees return (
{ setAdding((v) => !v); setEditing(undefined); setResetting(undefined) }}> + Add employee )} /> {adding && ( { setAdding(false); list.reload() }} onCancel={() => setAdding(false)} /> )} {editing !== undefined && ( { setEditing(undefined); list.reload() }} onCancel={() => setEditing(undefined)} /> )} {resetting !== undefined && ( { setResetting(undefined); list.reload() }} onCancel={() => setResetting(undefined)} /> )} {error !== undefined && {error}} {list.error !== undefined && {list.error}} {/* Branch on the fetch error first — `employees` stays undefined on failure, so falling through would show "Loading…" forever under the error notice. */} {list.error !== undefined ? null : employees === undefined ? Loading… : employees.length === 0 ? No employees yet. : ( <> ({ name: e.displayName, email: e.email, role: {e.role}, active: e.active ? active : inactive, actions: ( {e.active ? : } ), }))} /> {list.data!.total} employee{list.data!.total === 1 ? '' : 's'} Guards: the last active owner cannot be demoted or deactivated; you cannot deactivate yourself. )}
) } function AddEmployeeForm(props: { onDone: () => void; onCancel: () => void }) { const [f, setF] = useState({ email: '', displayName: '', role: 'staff' as EmployeeRole, password: '' }) const [error, setError] = useState() const set = (k: keyof typeof f) => (e: { target: { value: string } }) => setF((p) => ({ ...p, [k]: e.target.value })) const save = () => { if (f.email.trim() === '' || f.displayName.trim() === '') { setError('Email and name are required'); return } if (f.password.length < 8) { setError('Initial password must be at least 8 characters'); return } setError(undefined) createEmployee({ email: f.email.trim(), displayName: f.displayName.trim(), role: f.role, password: f.password }) .then(props.onDone) .catch((e: Error) => setError(e.message)) } return (
{error !== undefined && {error}}
) } function EditEmployeeForm(props: { employee: Employee; onDone: () => void; onCancel: () => void }) { const [f, setF] = useState({ displayName: props.employee.displayName, role: props.employee.role }) const [error, setError] = useState() const save = () => { if (f.displayName.trim() === '') { setError('Name is required'); return } setError(undefined) patchEmployee(props.employee.id, { displayName: f.displayName.trim(), role: f.role }) .then(props.onDone) .catch((e: Error) => setError(e.message)) } return (
{/* Email is the login id — immutable after creation. */} setF((p) => ({ ...p, displayName: e.target.value }))} />
{error !== undefined && {error}}
) } function ResetPasswordForm(props: { employee: Employee; onDone: () => void; onCancel: () => void }) { const [password, setPassword] = useState('') const [error, setError] = useState() const save = () => { if (password.length < 8) { setError('New password must be at least 8 characters'); return } setError(undefined) resetEmployeePassword(props.employee.id, password) .then(props.onDone) .catch((e: Error) => setError(e.message)) } return (
setPassword(e.target.value)} />
{error !== undefined && {error}}
) }