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
+274 -51
View File
@@ -634,6 +634,104 @@ button.row-card:hover:not(:disabled) {
/* ---------------------------------------------------------------- A1 entry */
.signin {
max-width: 640px;
}
/* Standalone sign-in page: no sidebar, one centred card */
.signin-page {
min-height: 100vh;
min-height: 100dvh;
display: grid;
place-items: center;
padding: var(--s-8) var(--s-4);
background: var(--c-page-desk);
}
.signin-page .signin {
width: 100%;
max-width: 480px;
display: grid;
gap: var(--s-5);
}
.signin__brand {
display: flex;
align-items: center;
justify-content: center;
gap: var(--s-3);
}
.signin__name {
display: block;
font-size: var(--fs-18);
font-weight: 700;
color: var(--c-text);
}
.signin__tag {
display: block;
font-size: var(--fs-14);
color: var(--c-text-2);
}
.signin__card {
display: grid;
gap: var(--s-5);
padding: var(--s-8) var(--s-6);
border-radius: 24px;
background: var(--c-surface);
box-shadow: 0 12px 40px rgba(15, 39, 71, 0.08);
}
.signin__card .entry__title {
flex: 1;
}
.signin__perks {
display: grid;
gap: var(--s-2);
font-size: var(--fs-14);
color: var(--c-text-2);
}
.signin__perks li {
display: flex;
align-items: flex-start;
gap: var(--s-2);
}
.signin__perks svg {
flex: none;
margin-top: 1px;
color: var(--c-teal);
}
.signin__foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--s-3);
padding: var(--s-3) var(--s-4);
border-radius: var(--r-tile);
background: var(--c-red-soft);
}
.signin__sos {
display: inline-flex;
align-items: center;
gap: var(--s-2);
font-weight: 700;
color: var(--c-red-strong);
}
.signin__tel {
font-size: var(--fs-20);
font-weight: 700;
color: var(--c-red-strong);
text-decoration: none;
}
.entry-grid {
display: grid;
gap: var(--s-4);
@@ -1251,49 +1349,6 @@ button.row-card:hover:not(:disabled) {
justify-self: center;
}
.how {
display: grid;
gap: var(--s-3);
}
.how__list {
display: grid;
gap: var(--s-3);
}
.how__item {
display: flex;
gap: var(--s-3);
align-items: flex-start;
padding: var(--s-4);
border-radius: var(--r-tile);
background: var(--c-tile);
}
.how__num {
flex: none;
width: 32px;
height: 32px;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--c-navy);
color: #fff;
font-weight: 700;
font-size: var(--fs-14);
}
.how__title {
display: block;
font-weight: 700;
}
.how__text {
display: block;
font-size: var(--fs-13);
color: var(--c-text-2);
}
/* ---------------------------------------------------------------- meds */
.rx {
@@ -1972,10 +2027,6 @@ button.row-card:hover:not(:disabled) {
grid-column: 1 / -1;
}
.how__list {
grid-template-columns: repeat(3, 1fr);
}
.rail {
position: sticky;
top: var(--s-8);
@@ -2113,10 +2164,6 @@ button.row-card:hover:not(:disabled) {
border-top: 1px solid var(--c-border);
}
.survey__saved {
margin: 0;
}
.btn--sm {
width: auto;
min-height: 40px;
@@ -2433,3 +2480,179 @@ button.row-card:hover:not(:disabled) {
transition: none !important;
}
}
/* ---------------------------------------------------------------- bare: questionnaires, centred, no sidebar */
.app--bare {
background: var(--c-page-desk);
}
@media (min-width: 1024px) {
.app--bare {
display: block;
}
.app--bare .app__main {
grid-template-columns: minmax(0, 1fr);
max-width: 760px;
min-height: 100dvh;
margin: 0 auto;
padding: var(--s-10) var(--s-4);
align-content: center;
}
.app--bare .screen > .sosblock {
display: grid;
}
.app--bare .split {
grid-template-columns: minmax(0, 1fr);
gap: var(--s-6);
}
.app--bare .split__lead {
position: static;
}
}
/* ---------------------------------------------------------------- results and questionnaire log */
.result {
display: grid;
gap: var(--s-3);
padding: var(--s-5);
border-radius: var(--r-tile);
background: var(--c-teal-soft);
}
.result__title {
font-size: var(--fs-20);
font-weight: 700;
}
.result__chips {
display: flex;
flex-wrap: wrap;
gap: var(--s-2);
}
.result .chip {
background: var(--c-surface);
}
.log {
display: grid;
border: 1px solid var(--c-border);
border-radius: var(--r-tile);
background: var(--c-surface);
overflow: hidden;
}
.log > li + li {
border-top: 1px solid var(--c-border);
}
.log__row {
width: 100%;
display: flex;
align-items: center;
gap: var(--s-4);
padding: var(--s-3) var(--s-4);
text-align: left;
color: var(--c-text);
}
.log__row:disabled {
cursor: default;
}
.log__row svg {
flex: none;
color: var(--c-text-3);
}
.log__date {
flex: none;
width: 64px;
font-size: var(--fs-13);
color: var(--c-text-2);
}
.log__text {
flex: 1;
min-width: 0;
display: grid;
}
.log__kind {
font-weight: 600;
}
.log__sub {
font-size: var(--fs-13);
color: var(--c-text-2);
}
.log__body {
display: grid;
gap: var(--s-2);
padding: 0 var(--s-4) var(--s-4) calc(64px + var(--s-8));
font-size: var(--fs-14);
}
/* ---------------------------------------------------------------- toasts (top right) */
.toasts {
position: fixed;
top: calc(var(--s-4) + var(--safe-top));
right: max(var(--s-4), var(--safe-right));
left: max(var(--s-4), var(--safe-left));
z-index: 60;
display: grid;
gap: var(--s-2);
justify-items: end;
pointer-events: none;
}
.toast {
width: min(100%, 380px);
display: flex;
align-items: flex-start;
gap: var(--s-3);
padding: var(--s-3) var(--s-3) var(--s-3) var(--s-4);
border-radius: var(--r-tile);
background: var(--c-navy);
color: #fff;
box-shadow: 0 12px 32px rgb(15 39 71 / 0.25);
pointer-events: auto;
animation: toast-in 200ms var(--ease);
}
.toast__icon {
flex: none;
display: grid;
place-items: center;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--c-teal);
}
.toast__text {
flex: 1;
margin: 3px 0 0;
font-size: var(--fs-14);
line-height: 1.4;
}
.toast__close {
flex: none;
color: rgb(255 255 255 / 0.7);
}
@keyframes toast-in {
from {
opacity: 0;
transform: translateY(-8px);
}
}