feat(web): patient web app with ЕМИАС (demo) and anonymous sign-in
A second channel to the same dialog engine as the Telegram bot: consent, onboarding, scales and check-ins come from app/dialog; the bot is unchanged. - API: /auth/emias (doctor's one-time connect code via EmiasMock), /auth/emias/demo (synthetic personas), /auth/anonymous (random token, nothing stored in the identity DB), /crisis/resources from crisis.yaml - web/: React + TS + Vite, mobile-first, safe areas, hash routing, tokens and texts isolated for a Capacitor mobile build Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { api } from "../api/client";
|
||||
import { AppShell, BackLink, Notice } from "../components/AppShell";
|
||||
import { Icon } from "../components/Icon";
|
||||
import { ru } from "../content/ru";
|
||||
import { useSession } from "../session/SessionContext";
|
||||
|
||||
export function AnonymousSignIn() {
|
||||
const t = ru.anonymous;
|
||||
const navigate = useNavigate();
|
||||
const { signIn } = useSession();
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const start = async () => {
|
||||
setBusy(true);
|
||||
setError(false);
|
||||
try {
|
||||
signIn(await api.signInAnonymous());
|
||||
navigate("/chat", { replace: true });
|
||||
} catch {
|
||||
setError(true);
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AppShell>
|
||||
<main className="page" id="main">
|
||||
<div className="narrow">
|
||||
<BackLink to="/">{t.back}</BackLink>
|
||||
<section className="panel" aria-labelledby="anon-title">
|
||||
<h1 id="anon-title" className="h2">
|
||||
{t.title}
|
||||
</h1>
|
||||
<p className="lead lead--sm">{t.lead}</p>
|
||||
<ul className="facts facts--spaced">
|
||||
{t.points.map((p) => (
|
||||
<li key={p} className="fact">
|
||||
<Icon name="check" size={18} />
|
||||
<span>{p}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Notice tone="warning">{t.warning}</Notice>
|
||||
{error && (
|
||||
<p className="field__error" role="alert">
|
||||
{ru.emias.networkError}
|
||||
</p>
|
||||
)}
|
||||
<button type="button" className="btn btn--primary btn--block" onClick={start} disabled={busy}>
|
||||
{busy ? t.submitting : t.submit}
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user