feat(ui): extend minimal doctor design across all screens
CI / ruff · pytest (pull_request) Successful in 2m16s

Refs #85
This commit is contained in:
Pavel Eliseev
2026-10-04 16:09:57 +03:00
parent afbfbad19e
commit 2a2fdb171e
15 changed files with 526 additions and 192 deletions
+27 -2
View File
@@ -3,13 +3,14 @@ import { Link, NavLink } from "react-router-dom";
import { ru } from "../content/ru";
import { useDialog } from "../dialog/DialogContext";
import { useSession } from "../session/SessionContext";
import { HotlineCard, useHelp } from "./HelpSheet";
import { Icon, LogoMark, type IconName } from "./Icon";
/*
* Layout from docs/mock (series 2).
* Shared patient workspace, following the approved doctor dashboard.
* phone: content → «Экстренная помощь» block → bottom tab bar (signed in)
* desktop: navy sidebar | content | right rail (optional `aside` + emergency panel)
* desktop: white sidebar | workspace header | content + emergency panel
* bare: one centred column on every width (questionnaires)
*/
@@ -87,6 +88,10 @@ function Sidebar({ signedIn }: { signedIn: boolean }) {
</li>
</ul>
)}
<div className="sidebar__note">
<strong>{ru.product.supportTitle}</strong>
<span>{ru.product.supportText}</span>
</div>
</nav>
);
}
@@ -122,6 +127,24 @@ function Rail({ aside }: { aside?: ReactNode }) {
);
}
function WorkspaceHeader({ signedIn, bare = false }: { signedIn: boolean; bare?: boolean }) {
const { session } = useSession();
return (
<header className={`workspace${bare ? " workspace--bare" : ""}`}>
<div className="workspace__brand">
<LogoMark size={30} />
<span>{bare ? ru.product.name : signedIn ? ru.product.workspace : ru.nav.signIn}</span>
</div>
{signedIn && !bare ? (
<Link className="workspace__profile" to="/profile">
<span className="avatar"><Icon name="user" size={18} /></span>
<span>{session?.displayName || ru.nav.profile}</span>
</Link>
) : <span className="workspace__tag">{ru.product.tagline}</span>}
</header>
);
}
export function AppShell({
children,
signedIn = true,
@@ -144,6 +167,7 @@ export function AppShell({
return (
<div className="app app--bare">
<div className="app__main">
<WorkspaceHeader signedIn={signedIn} bare />
<main className="screen" id="main">
{children}
{sos && <EmergencyBlock />}
@@ -155,6 +179,7 @@ export function AppShell({
<div className={`app${signedIn && !focus ? " app--tabs" : ""}`}>
<Sidebar signedIn={signedIn} />
<div className="app__main">
<WorkspaceHeader signedIn={signedIn} />
<main className="screen" id="main">
{children}
{sos && <EmergencyBlock />}
+2 -2
View File
@@ -163,8 +163,8 @@ export function Icon({ name, size = 22, label }: { name: IconName; size?: number
export function LogoMark({ size = 32 }: { size?: number }) {
return (
<svg width={size} height={size} viewBox="0 0 32 32" aria-hidden="true" focusable="false">
<rect width="32" height="32" rx="9" fill="var(--c-teal)" />
<path d="M13 7h6v6h6v6h-6v6h-6v-6H7v-6h6z" fill="#fff" />
<rect width="32" height="32" rx="9" fill="var(--c-primary)" />
<path d="M16 8v16M8 16h16" stroke="#fff" strokeWidth="2" strokeLinecap="round" />
</svg>
);
}
+3
View File
@@ -6,6 +6,9 @@ export const ru = {
product: {
name: "Сопровождение пациента",
tagline: "между визитами к врачу",
workspace: "Личный кабинет",
supportTitle: "Рядом между визитами",
supportText: "Наблюдение за самочувствием и поддержка назначенного лечения.",
},
nav: {
today: "Сегодня",
-4
View File
@@ -1,7 +1,3 @@
import "@fontsource/golos-text/400.css";
import "@fontsource/golos-text/500.css";
import "@fontsource/golos-text/600.css";
import "@fontsource/golos-text/700.css";
import "./theme/tokens.css";
import "./theme/global.css";
+387 -106
View File
File diff suppressed because it is too large Load Diff
+33 -33
View File
@@ -1,45 +1,45 @@
/*
* Design tokens — single source of colours, type and spacing (docs/mock, series 2).
* Shared visual language with dashboard/theme.py and the approved doctor workspace.
* Capacitor wraps this same bundle; a React Native port maps these 1:1 to a theme object.
*/
:root {
/* Ink & surfaces */
--c-navy: #0f2747; /* primary buttons, headings, desktop sidebar */
--c-navy-hover: #0a1d37;
--c-navy-soft: #e8eef6;
--c-text: #13294b;
--c-text-2: #4d5d73;
--c-text-3: #7a889b;
--c-page: #ffffff;
--c-page-desk: #f4f7fb; /* desktop canvas behind white panels */
--c-primary: #285e4e; /* primary buttons and active navigation */
--c-primary-hover: #204b3e;
--c-primary-soft: #edf3ee;
--c-text: #213b38;
--c-text-2: #526660;
--c-text-3: #63736f;
--c-page: #f6f7f3;
--c-page-desk: #f6f7f3; /* desktop canvas behind white panels */
--c-surface: #ffffff;
--c-tile: #f3f7fb; /* soft card fill */
--c-border: #e2e9f1;
--c-border-strong: #c9d4e1;
--c-tile: #f4f6f2; /* soft card fill */
--c-border: #e1e7e3;
--c-border-strong: #b4c6ba;
/* Selection & positive: teal */
--c-teal: #17a08f;
--c-teal-strong: #0e7e71;
--c-teal-soft: #e3f5f2;
/* Selection & positive */
--c-teal: #527f61;
--c-teal-strong: #38634b;
--c-teal-soft: #eef4ee;
/* Info: blue */
--c-blue: #2f7fdb;
--c-blue-soft: #eaf3fd;
/* Informational surfaces */
--c-blue: #38634b;
--c-blue-soft: #edf3ee;
/* Help & crisis: red */
--c-red: #e2383b;
--c-red-strong: #c62b2e;
--c-red-soft: #fdeded;
--c-red-line: #f7d3d3;
--c-red: #a83236;
--c-red-strong: #a83236;
--c-red-soft: #faeeee;
--c-red-line: #e7cbcb;
/* Warning */
--c-amber: #e69a12;
--c-amber-soft: #fff4dd;
--c-amber: #805714;
--c-amber-soft: #fbf3e4;
--c-focus: #2f7fdb;
--c-focus: #598d7a;
/* Type: Golos Text (OFL), bundled locally — works offline. */
--font: "Golos Text", system-ui, -apple-system, "Segoe UI", sans-serif;
/* Same offline system stack as the doctor workspace. */
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
--fs-12: 0.75rem;
--fs-13: 0.8125rem;
--fs-14: 0.875rem;
@@ -63,14 +63,14 @@
--s-12: 48px;
/* Radii: tiles > buttons > chips */
--r-tile: 16px;
--r-btn: 12px;
--r-chip: 999px;
--r-tile: 12px;
--r-btn: 8px;
--r-chip: 5px;
--tap: 52px;
--nav-h: 64px;
--sidebar-w: 232px;
--rail-w: 300px;
--sidebar-w: 228px;
--rail-w: 264px;
--safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);