feat: presentation demo mode — one-click private demo patient, screening on/off switch
CI / ruff · pytest (pull_request) Failing after 2m33s
CI / ruff · pytest (pull_request) Failing after 2m33s
- POST /auth/demo/fresh + «Демо: войти как Андрей» on the welcome screen: every sign-in gets a fresh private copy of veteran Andrey (consent, prescription, ЕМИАС record) and lands in the chat — no codes, nobody's crisis or half-done questionnaire leaks into another viewer. - Initial screening switch: SCREENING_ENABLED env default + /demo/settings + a toggle in the demo console; off → after consent straight to the chat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,8 @@ export const api = {
|
||||
signInEmias: (code: string) => post<AuthResult>("/auth/emias", { code }),
|
||||
signInEmiasDemo: (personaToken: string) => post<AuthResult>("/auth/emias/demo", { persona_token: personaToken }),
|
||||
signInAnonymous: (focus?: Focus) => post<AuthResult>("/auth/anonymous", { focus }),
|
||||
// Presentation entry: a fresh private copy of the demo patient, straight to the chat.
|
||||
signInDemoFresh: () => post<AuthResult>("/auth/demo/fresh", {}),
|
||||
patientCapabilities: () => request<{ primary_screening: boolean }>("/patient/capabilities"),
|
||||
connectDoctor: (token: string) => post<{ has_doctor: boolean }>(`/me/${encodeURIComponent(token)}/doctor/connect`),
|
||||
demoPersonas: () => request<DemoPersona[]>("/patient/demo-personas"),
|
||||
|
||||
@@ -41,6 +41,12 @@ export const ru = {
|
||||
},
|
||||
welcome: {
|
||||
title: "Вход",
|
||||
demo: {
|
||||
title: "Демо: войти как Андрей",
|
||||
text: "Ветеран СВО, 34 года, сертралин. Назначения и карта ЕМИАС уже подключены — сразу в чат.",
|
||||
busy: "Готовим демо…",
|
||||
error: "Не удалось открыть демо. Попробуйте ещё раз.",
|
||||
},
|
||||
emias: {
|
||||
title: "Войти через ЕМИАС",
|
||||
perks: [
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { api } from "../api/client";
|
||||
import { useSession } from "../session/SessionContext";
|
||||
|
||||
import { telHref, useHelp } from "../components/HelpSheet";
|
||||
import { Icon, LogoMark } from "../components/Icon";
|
||||
import { ru } from "../content/ru";
|
||||
@@ -7,10 +11,26 @@ import { ru } from "../content/ru";
|
||||
// A1 · Вход: без сайдбара, одна карточка по центру. ЕМИАС — основной путь, анонимный — по желанию
|
||||
export function Welcome() {
|
||||
const navigate = useNavigate();
|
||||
const { signIn } = useSession();
|
||||
const [demoBusy, setDemoBusy] = useState(false);
|
||||
const [demoError, setDemoError] = useState(false);
|
||||
const { resources, open } = useHelp();
|
||||
const t = ru.welcome;
|
||||
const emergency = resources[0];
|
||||
|
||||
// Presentation: one click → a fresh private demo patient → the chat.
|
||||
const demo = async () => {
|
||||
setDemoBusy(true);
|
||||
setDemoError(false);
|
||||
try {
|
||||
signIn(await api.signInDemoFresh());
|
||||
navigate("/messages", { replace: true });
|
||||
} catch {
|
||||
setDemoError(true);
|
||||
setDemoBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="signin-page">
|
||||
<main className="signin" id="main">
|
||||
@@ -25,6 +45,28 @@ export function Welcome() {
|
||||
<section className="signin__card">
|
||||
<h1 className="h1">{t.title}</h1>
|
||||
|
||||
<button type="button" className="entry entry--emias" onClick={demo} disabled={demoBusy}>
|
||||
<span className="entry__top">
|
||||
<span className="tile-icon tile-icon--teal">
|
||||
<Icon name="check" size={26} />
|
||||
</span>
|
||||
<span className="entry__title">
|
||||
{demoBusy ? t.demo.busy : t.demo.title}
|
||||
<Icon name="chevronRight" />
|
||||
</span>
|
||||
</span>
|
||||
<span className="muted small">{t.demo.text}</span>
|
||||
</button>
|
||||
{demoError && (
|
||||
<p className="muted small center" role="alert">
|
||||
{t.demo.error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="divider" role="separator">
|
||||
<span>{t.or}</span>
|
||||
</div>
|
||||
|
||||
<button type="button" className="entry entry--emias" onClick={() => navigate("/emias")}>
|
||||
<span className="entry__top">
|
||||
<span className="tile-icon tile-icon--teal">
|
||||
|
||||
Reference in New Issue
Block a user