// АРМ администратора: учётные записи, состояние стенда, журнал аудита, копии. // // Того, чего администратору нельзя по ТЗ, здесь нет вовсе — ни правки оценок, // ни вмешательства в занятие. Запрет выражен отсутствием кнопки, а не // disabled-атрибутом (lct-24). import { useState } from "react"; import { useLogout, useMe } from "@/features/auth/useAuth"; import type { Role } from "@/shared/types/generated"; import { useAudit, useBackups, useCreateBackup, useCreateUser, usePatchUser, useStatus, useUsers, } from "./api"; const ROLE_LABELS: Record = { admin: "Администратор", instructor: "Преподаватель", trainee: "Обучающийся", }; export function Admin() { const me = useMe(); const logout = useLogout(); return (

АРМ администратора

{me.data?.full_name}{" "}

); } function Status() { const status = useStatus(); return ( <>

Состояние стенда

{(status.data ?? []).map((item) => ( ))}
{item.name} {item.detail}
); } function Users() { const users = useUsers(); const create = useCreateUser(); const patch = usePatchUser(); const [form, setForm] = useState({ login: "", full_name: "", password: "", role: "trainee" as Role, service: "" }); return ( <>

Учётные записи

{(users.data ?? []).map((user) => ( ))}
ЛогинФИОРольСлужбаСостояние
{user.login} {user.full_name} {ROLE_LABELS[user.role]} {user.service ?? "—"}

Завести запись

{ event.preventDefault(); create.mutate( { ...form, service: form.service || null }, { onSuccess: () => setForm({ ...form, login: "", full_name: "", password: "" }) }, ); }} > {/* Служба осмысленна только у обучающегося: по ней в его ленту попадают профильные события (docs/spec/DATASET.md). */} {form.role === "trainee" && ( )}
Логин setForm({ ...form, login: e.target.value })} />
ФИО setForm({ ...form, full_name: e.target.value })} />
Пароль setForm({ ...form, password: e.target.value })} />{" "} не короче восьми знаков
Роль
Служба ДДС setForm({ ...form, service: e.target.value })} />

{create.isError && Логин уже занят}

); } function Backups() { const backups = useBackups(); const create = useCreateBackup(); return ( <>

Резервные копии

{create.isError && {String(create.error)}}

{(backups.data ?? []).map((item) => ( ))}
{item.name} {Math.round(item.size_bytes / 1024)} КБ {new Date(item.at).toLocaleString("ru-RU")}
{(backups.data ?? []).length === 0 &&

Копий пока нет.

} ); } function Audit() { const [action, setAction] = useState(""); const audit = useAudit(action); return ( <>

Журнал действий

Фильтр по действию:{" "} setAction(e.target.value)} />

{(audit.data ?? []).map((row, index) => ( ))}
КогдаКтоДействиеНад чемПодробности
{new Date(row.at).toLocaleString("ru-RU")} {row.actor} {row.action} {row.object_id ?? "—"} {row.detail}
); }