Complete training workflow and acceptance hardening

This commit is contained in:
andreysk0304 2026-09-26 18:12:27 +03:00 • committed by gglamer
commit 7237265833
243 changed files with 17014 additions and 1500 deletions

View file

@ -1,10 +1,22 @@
// Экран входа. Ставится перед всеми остальными: номер занятия перестал быть
// пропуском, по ссылке пускает роль, а не знание адреса (lct-23).
import { useState, type MouseEvent, type ReactNode } from "react";
import { useEffect, useState, type MouseEvent, type ReactNode } from "react";
import { DEMO_MODE, SCREENS, useDemoLogin, useLogin, useLogout, useMe } from "./useAuth";
const preloadScreenModule: Record<string, () => Promise<unknown>> = {
"/trainee": () => import("@/pages/trainee/Call"),
"/instructor": () => import("@/pages/instructor/Instructor"),
"/wall": () => import("@/pages/wall/Wall"),
"/profile": () => import("@/pages/profile/Profile"),
"/groups": () => import("@/pages/groups/Groups"),
"/dds": () => import("@/pages/dds/Dds"),
"/phone": () => import("@/pages/phone/Phone"),
"/materials": () => import("@/pages/materials/Materials"),
"/admin": () => import("@/pages/admin/Admin"),
};
function DemoLinks() {
const openScreen = (event: MouseEvent<HTMLAnchorElement>, path: string) => {
event.preventDefault();
@ -33,6 +45,12 @@ export function RequireAuth({ screen, children }: { screen: string; children: Re
const me = useMe();
const logout = useLogout();
// Fetch only static route code while the signed cookie is being checked;
// user data and the screen itself remain gated by the role check below.
useEffect(() => {
void preloadScreenModule[screen]?.().catch(() => undefined);
}, [screen]);
if (me.isLoading) {
return <main className="page"><p>Проверяем доступ…</p></main>;
}
@ -76,7 +94,7 @@ function Login() {
<tr>
<th>Логин</th>
<td>
<input value={name} autoFocus onChange={(event) => setName(event.target.value)} />
<input aria-label="Логин" value={name} autoFocus onChange={(event) => setName(event.target.value)} />
</td>
</tr>
<tr>
@ -84,6 +102,7 @@ function Login() {
<td>
<input
type="password"
aria-label="Пароль"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>

View file

@ -73,7 +73,7 @@ export function useLogout() {
/** Какие экраны открыты роли. Повторяет domain/roles.py: список короткий
* и меняется редко, а держать его на сервере ради двух строк — лишний запрос. */
export const SCREENS: Record<Role, string[]> = {
admin: ["/admin", "/profile", "/groups", "/materials"],
admin: ["/admin", "/wall", "/profile", "/groups", "/materials"],
instructor: ["/instructor", "/wall", "/profile", "/dds", "/phone", "/groups", "/materials"],
trainee: ["/trainee", "/dds", "/phone", "/profile", "/materials"],
};