- Focus screen renders versioned questionnaire items (iq: buttons): section chip, position (adaptive forms without "of N"), recall date, pause / help-now actions, optional skip, sleep-hours input, 0-10 scale anchors, RUS-AUDIT item 2 standard-drink table. - Paused / held-for-review questionnaires get their own screen. - Today: intake and diary tasks, review notice, therapy-day card on days 1/7/14. - Drug card /meds/:ref from data/meds/meds_reference.json (draft built from the Minzdrav guidelines: depression 2024, PTSD 2023) — expected effect, course, side effects, red flags; no doses shown. "Report a side effect" always available (med:side_effects:<ref>). - API: /instruments/catalog, /meds/reference; today gains questionnaire state, reference id, indication and therapy day; demo persona seeds the dashboard dose history. - INTAKE_DEMO=1 (demo clock only) opens the questionnaires on synthetic data without touching the clinical release gate in config/intake_runtime.yaml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
1.7 KiB
TypeScript
49 lines
1.7 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
|
|
import { api } from "../api/client";
|
|
import type { TodayPlan } from "../api/types";
|
|
import { useDialog } from "../dialog/DialogContext";
|
|
import { useSession } from "../session/SessionContext";
|
|
|
|
/** The patient's plan for today; refreshed whenever the dialog moves (a dose answered, a check-in done). */
|
|
export function useToday(): { plan: TodayPlan | null; failed: boolean } {
|
|
const { session } = useSession();
|
|
const { pending } = useDialog();
|
|
const [plan, setPlan] = useState<TodayPlan | null>(null);
|
|
const [failed, setFailed] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!session) return;
|
|
let alive = true;
|
|
api
|
|
.today(session.token)
|
|
.then((p) => {
|
|
if (!alive) return;
|
|
setPlan(p);
|
|
setFailed(false);
|
|
})
|
|
.catch(() => alive && setFailed(true));
|
|
return () => {
|
|
alive = false;
|
|
};
|
|
}, [session, pending]);
|
|
|
|
return { plan, failed };
|
|
}
|
|
|
|
const MONTHS = ["января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"];
|
|
const WEEKDAYS = ["воскресенье", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота"];
|
|
|
|
/** "12 марта, вторник" — from an ISO date, no timezone shift. */
|
|
export function humanDate(iso: string): string {
|
|
const [y, m, d] = iso.split("-").map(Number);
|
|
const wd = new Date(Date.UTC(y, m - 1, d)).getUTCDay();
|
|
return `${d} ${MONTHS[m - 1]}, ${WEEKDAYS[wd]}`;
|
|
}
|
|
|
|
/** "4 октября" — from an ISO date. */
|
|
export function dayMonth(iso: string): string {
|
|
const [, m, d] = iso.split("-").map(Number);
|
|
return `${d} ${MONTHS[m - 1]}`;
|
|
}
|