Complete training workflow and acceptance hardening
This commit is contained in:
parent
4c4b91064f
commit
7237265833
243 changed files with 17014 additions and 1500 deletions
|
|
@ -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)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue