Школьники не должны придумывать пароль, чтобы пройти тест. Бэкенд уже
поддерживал это: POST /polls/{poll_id}/responses принимает user_id как
Optional, а Response.group_id/organization_id — самостоятельные nullable
снимки школы/класса, не завязанные на аккаунт. Не хватало только
фронтенда — все роуты были жёстко завёрнуты в ProtectedRoute с
обязательным JWT.
- AuthContext: параллельная "анонимная идентичность" (org+group в
localStorage, без токена и без запроса к API)
- ProtectedRoute пускает по токену ИЛИ анонимной сессии; новый
AccountRoute — строго по токену, для /my-tests, /profile,
/response/:id (у анонимной сессии нет истории — её негде хранить)
- Новая страница /anonymous — выбор школы+класса тем же
SearchableDropdown, что и в регистрации
- PollPage при отправке ответа берёт org/group из анонимной сессии,
если пользователь не залогинен
- HomePage скрывает "Мои тесты" и профиль для анонимов, добавлена
кнопка выхода из анонимного режима
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
37 lines
1.5 KiB
TypeScript
37 lines
1.5 KiB
TypeScript
import { Navigate } from "react-router-dom";
|
|
import type { ReactNode } from "react";
|
|
import { useAuth } from "../auth/AuthContext";
|
|
import { Spinner } from "./ui";
|
|
|
|
// Пускает дальше при наличии токена ИЛИ анонимной идентичности (школа+класс,
|
|
// без аккаунта). Используется для страниц, которые школьник может пройти
|
|
// анонимно: главная, список тестов, прохождение теста.
|
|
export function ProtectedRoute({ children }: { children: ReactNode }) {
|
|
const { token, anon, ready } = useAuth();
|
|
if (!ready) {
|
|
return (
|
|
<div className="spinner-center">
|
|
<Spinner />
|
|
</div>
|
|
);
|
|
}
|
|
if (!token && !anon) return <Navigate to="/login" replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
// Строже: пускает дальше только при реальном токене (не анонимной сессии).
|
|
// Для страниц, которым нужен настоящий аккаунт: история тестов, профиль,
|
|
// просмотр конкретного результата.
|
|
export function AccountRoute({ children }: { children: ReactNode }) {
|
|
const { token, ready } = useAuth();
|
|
if (!ready) {
|
|
return (
|
|
<div className="spinner-center">
|
|
<Spinner />
|
|
</div>
|
|
);
|
|
}
|
|
if (!token) return <Navigate to="/login" replace />;
|
|
return <>{children}</>;
|
|
}
|