feat(web): two-stage questionnaires, screening result and diary by result

- Sign-in: centred ЕМИАС card without the sidebar; anonymous entry asks «Депрессия / ПТСР».
- Stage 1 is the same for everyone and follows the fixed order of the MVP questionnaire:
  PHQ-2 → trauma filter/TSQ → RUS-AUDIT-S → C-SSRS → context; stage 2 only where stage 1
  is positive. Starts without a profile.
- After stage 1 the patient sees a preliminary result (screening, not a diagnosis), stored
  with the record; it switches on the diary modules (depression / PTSD).
- Stage 2 diary: morning (sleep, sleep quality, alcohol) and evening (mood, medication,
  3 PHQ-9 items in rotation, short PCL-5); C-SSRS since last contact only under risk;
  flags on weekly means against the personal norm; scheduled full scales with change
  (remission, response, worsening).
- Questionnaire log: GET /me/{token}/results and the «Мои результаты» screen.
- Questionnaires centred without the sidebar and without scale names; «Продолжить позже»
  goes home; confirmations are toasts; free-text comments are optional.
- Demo policy config/questionnaire_demo.yaml (INTAKE_DEMO only, not a clinical approval).

ADR-012.

Refs #77

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 15:14:09 +03:00
co-authored by Claude Opus 5.5
parent 6e7c89900b
commit 290b0d6151
36 changed files with 1900 additions and 691 deletions
+39 -24
View File
@@ -37,23 +37,21 @@ export const ru = {
networkError: "Не удалось подключиться к сервису. Проверьте интернет и повторите.",
},
welcome: {
title: "Как вы хотите войти в приложение?",
lead: "Выберите удобный способ. Вы всегда можете сменить его позже в профиле.",
title: "Вход",
emias: {
title: "Через ЕМИАС",
text: "Назначения врача подгружаются сами, врач видит ответы и получает сигнал при ухудшении, история доступна с любого устройства.",
title: "Войти через ЕМИАС",
perks: [
"Назначения врача подгружаются автоматически",
"Врач видит ответы и получает сигнал при ухудшении",
"История доступна с любого устройства",
],
},
or: "или",
anonymous: {
title: "Анонимно",
text: "Не нужны имя, полис и телефон, врач ничего не видит, история только на этом устройстве.",
title: "Войти анонимно",
text: "Без имени и полиса. Врач не видит ответы, история хранится только на этом устройстве.",
},
disclaimer: "Сервис не ставит диагнозы и не назначает лечение. Решения о лечении принимает ваш врач.",
howTitle: "Как это работает",
how: [
{ title: "Врач подключает", text: "На приёме вы получаете код или входите анонимно" },
{ title: "Минута в день", text: "Напоминание о лекарстве и короткая отметка самочувствия" },
{ title: "Врач видит динамику", text: "И получает сигнал, если состоянию нужно внимание" },
],
disclaimer: "Сервис не ставит диагнозы и не назначает лечение — решения принимает врач.",
},
emias: {
title: "Введите код от врача",
@@ -70,14 +68,12 @@ export const ru = {
demoDay: (day: number) => `${day}-й день терапии`,
},
anonymous: {
title: "Анонимный вход",
lead: "Вы сможете пользоваться основными функциями приложения без входа через ЕМИАС.",
yesTitle: "Что будет доступно:",
yes: ["Ежедневные отметки", "Напоминания", "Анкеты и помощь в кризисе"],
noTitle: "Чего не будет:",
no: ["Врач не увидит ответы", "История только на этом устройстве"],
warning: "После выхода историю восстановить нельзя.",
submit: "Продолжить анонимно",
title: "Что вы хотите проверить?",
note: "Анонимно: врач не видит ответы, история хранится только на этом устройстве.",
focus: {
depression: { title: "Депрессия", text: "Подавленность, потеря интереса, усталость" },
ptsd: { title: "ПТСР", text: "Последствия травмы: флешбэки, кошмары, тревога" },
},
submitting: "Создаём профиль…",
},
today: {
@@ -105,7 +101,6 @@ export const ru = {
newMessage: "Новое сообщение",
open: "Открыть",
continue: "Продолжить",
surveyStep: (section: string, n: number) => `${section} · вопрос ${n}`,
intakeStart: "Первичный опрос",
intakeStartHint: "15–20 минут, можно прерваться и продолжить позже",
intakeResume: "Продолжить первичный опрос",
@@ -115,6 +110,9 @@ export const ru = {
intakeDone: "Первичный опрос пройден",
diary: "Дневник самочувствия",
diaryHint: "2–3 минуты: настроение, сон, лекарства",
diaryPart: { morning: "Дневник — утро", evening: "Дневник — вечер" },
diaryPartHint: { morning: "3 вопроса: сон и алкоголь", evening: "Настроение, лекарства и модули по результату опроса" },
results: "Мой результат",
diaryDone: "Дневник заполнен",
therapyDay: (day: number) => `День ${day} лечения`,
effectHint: "Когда ждать эффекта и что важно знать",
@@ -187,6 +185,20 @@ export const ru = {
wipeNote: "Для анонимного режима: история на этом устройстве будет удалена.",
logout: "Выйти",
messages: "Сообщения",
result: "Предварительный результат",
resultNone: "Появится после первичного опроса.",
history: "История опросов",
historySub: "Все опросы и дневники с датами и результатами",
},
results: {
title: "Мои результаты",
conclusion: "Предварительный результат",
conclusionDay: (d: string) => `Первичный опрос от ${d}`,
modules: { depression: "Модуль «Депрессия»", veteran: "Модуль «ПТСР»" },
history: "История опросов",
empty: "Здесь появятся пройденные опросы.",
noResult: "Пройдите первичный опрос — после него здесь будет результат.",
toIntake: "Пройти первичный опрос",
},
flow: {
question: (n: number, total: number) => `Вопрос ${n} из ${total}`,
@@ -194,6 +206,8 @@ export const ru = {
textPlaceholder: "Напишите несколько слов",
sending: "Отправляем…",
doneTitle: "Готово",
resultTitle: "Результат",
sendEmpty: "Отправить без комментария",
doneLead: "Врач увидит отметку.",
breathe: "Вдох на 4 — выдох на 6",
breatheIn: "Вдох",
@@ -214,11 +228,12 @@ export const ru = {
},
questionN: (n: number) => `Вопрос ${n}`,
recall: (period: string) => `Период: ${period}`,
asOf: (date: string) => `Считайте от ${date}`,
saved: "Ответы сохраняются после каждого вопроса",
hoursLabel: "Сколько часов",
hoursUnit: "ч",
hoursHint: "От 0 до 24, можно с половиной: 6,5",
portionsLabel: "Сколько порций",
portionsUnit: "порц.",
portionsHint: "Если не пили — 0. Можно дробное: 1,5",
drinks: "Таблица стандартных порций",
drinksLead: "Посчитайте, сколько порций выходит в обычный день, когда вы выпиваете.",
noticeTitle: "Опрос приостановлен",