Browse Source
Shared data layer + two access points for design-review docs. - src/docs.js — SCREEN_DOCS dictionary keyed by screen id with title, category, goal, tasks[], rationale[], and optional variants note; helpers getScreenDoc(screenId, ctx) resolves home variants (home:cards / home:list / home:feed) and compound routes (doctor:id, article:id, chat:id, etc.); getAllDocs groups by category; resolveRouteForDoc maps doc key back to a concrete navigable route - Toggle "Описания" in Tweaks + plashka above the phone in single layout: card with category, full-width title, and full goal text (line-clamp removed so whole sentence is readable); tap opens a full modal with tasks, rationale, and variants - Live sync: PhoneApp reports top-of-stack via onCurrentChange prop, App tracks innerScreen state so the plashka follows the real nav inside the phone (clicking "Записаться" on home now updates the plashka to the booking screen) - DocsScreen route "docs" in Tweaks screen selector — categorized list of all ~30 screens with collapsible inline descriptions and an "Открыть экран" CTA per row - Convention: SPRINTS.md "Правила разработки" + memory note — when editing any src/screens/* file, update the matching entry in src/docs.js to keep in-prototype documentation in sync Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>main
5 changed files with 890 additions and 9 deletions
@ -0,0 +1,630 @@
|
||||
// Описания экранов прототипа — для коллаборативного ревью.
|
||||
// Читается двумя UI-слоями:
|
||||
// 1. Плашка над телефоном в Tweaks-режиме «Описания»
|
||||
// 2. Экран `docs` (список всех экранов с разворачивающимися описаниями)
|
||||
|
||||
export const SCREEN_DOCS = { |
||||
'home:cards': { |
||||
title: 'Главная 1 · Карточки', |
||||
category: 'Главная', |
||||
goal: 'Быстрый старт: записаться или увидеть ближайший приём. Приветствие + один доминирующий CTA + фасетная сетка входов.', |
||||
tasks: [ |
||||
'Нажать «Записаться» и попасть в 4-шаговый флоу', |
||||
'Увидеть ближайший приём и открыть его детали', |
||||
'Выбрать специализацию (browse) или быстрое действие (телемед, тест слуха)', |
||||
'Прочитать статью врача', |
||||
], |
||||
rationale: [ |
||||
'Бежевый градиент шапки — тёплый контакт вместо стерильного белого', |
||||
'Единственная доминирующая CTA тёмный teal + тень — видна сразу', |
||||
'Сетка 3×2 специализаций — browse-режим для пользователя, не знающего врача', |
||||
'Статьи горизонтально — низкий приоритет, не давят вертикально', |
||||
], |
||||
variants: 'В Tweaks «Главный экран»: Карточки / Лента / Таймлайн — три подхода к приоритизации контента', |
||||
}, |
||||
'home:list': { |
||||
title: 'Главная 1 · Лента', |
||||
category: 'Главная', |
||||
goal: 'Утилитарная навигация: одна компактная карточка со всеми разделами как в iOS Settings. Для частых пользователей, которым не нужно browse.', |
||||
tasks: [ |
||||
'Быстро открыть Медкарту / Анализы / Чат с одного тапа', |
||||
'Увидеть ближайший приём (компактно сверху)', |
||||
'Попасть на запись через первый выделенный пункт', |
||||
], |
||||
rationale: [ |
||||
'Одна плотная карточка-список — минимум визуального шума', |
||||
'CTA «Записаться» выделена фоном primary-50 среди остальных', |
||||
'Бейджи справа для счётчиков (непрочитанные в чате)', |
||||
'Без градиентов и крупных CTA — подчёркнуто утилитарно', |
||||
], |
||||
}, |
||||
'home:feed': { |
||||
title: 'Главная 1 · Таймлайн', |
||||
category: 'Главная', |
||||
goal: 'Пациент-центричная лента: восстановление, лекарства, ближайший приём — всё, что влияет на самочувствие сегодня. Для послеоперационных.', |
||||
tasks: [ |
||||
'Увидеть прогресс восстановления с процентом и «что сегодня»', |
||||
'Отметить приём лекарства (one-tap)', |
||||
'Перейти к ближайшему приёму', |
||||
'Прочитать релевантные статьи', |
||||
], |
||||
rationale: [ |
||||
'Первый блок — прогресс операции, а не CTA: для послеоперационных это важнее', |
||||
'Вопрос «Как Ваше самочувствие?» вместо нейтрального приветствия — эмпатия', |
||||
'Лекарство с кнопкой «Принял» — one-tap action', |
||||
'Accent-CTA (красная) для записи — выделяется на фоне тёплых карточек', |
||||
], |
||||
}, |
||||
'home-v2': { |
||||
title: 'Главная 2', |
||||
category: 'Главная', |
||||
goal: 'Search-first layout: универсальная поисковая строка вверху — врач, симптом, услуга, дата. Для пользователя, который точно знает, что ищет.', |
||||
tasks: [ |
||||
'Ввести симптом или ФИО врача → получить релевантный результат', |
||||
'Попасть в Контакты или Цены через тайлы', |
||||
'Увидеть статистику клиники (формирование доверия)', |
||||
], |
||||
rationale: [ |
||||
'Поисковая строка вверху + AI-бейдж — сигнал «умный поиск»', |
||||
'Тайлы Контакты/Цены/Анализы/Восстановление — частые разделы в один тап', |
||||
'Градиентная карточка статистики — эмоциональный контакт с клиникой', |
||||
'Бордер primary-300 на stats-карточке — демонстрация «живого» цвета в палитре Бриз', |
||||
], |
||||
}, |
||||
'doctors': { |
||||
title: 'Врачи · список', |
||||
category: 'Врачи и запись', |
||||
goal: 'Список всех врачей клиники с поиском, фильтрами и тремя вариантами представления карточек.', |
||||
tasks: [ |
||||
'Найти врача по ФИО или специализации', |
||||
'Отфильтровать по «свободно сегодня», «кандидаты наук», «детские»', |
||||
'Открыть карточку врача → записаться', |
||||
], |
||||
rationale: [ |
||||
'Пиллы-фильтры горизонтально скроллятся — помещаются все на узких экранах', |
||||
'Три варианта карточек (rich/list/photo) — под разные привычки сканирования', |
||||
'Пиллы вынесены из карточки — общий фильтр для всего списка', |
||||
], |
||||
variants: 'В Tweaks «Карточки врачей»: Карточки+ / Список / Плитка', |
||||
}, |
||||
'doctor': { |
||||
title: 'Карточка врача', |
||||
category: 'Врачи и запись', |
||||
goal: 'Подробная карточка врача: регалии, рейтинг, цены, расписание, отзывы. Всё для решения «записаться — не записаться».', |
||||
tasks: [ |
||||
'Увидеть аватар, имя, специализацию, рейтинг, стоимость', |
||||
'Прочитать образование и специализацию', |
||||
'Посмотреть расписание (таб)', |
||||
'Прочитать отзывы (таб)', |
||||
'Нажать «Записаться» внизу → сразу на выбор времени', |
||||
], |
||||
rationale: [ |
||||
'Три равных stat-карточки: опыт, рейтинг, цена — ключевая инфа сразу', |
||||
'Сегментед-контрол для вкладок — компактно, iOS-паттерн', |
||||
'Фиксированная CTA внизу с backdrop-blur — не теряется при прокрутке', |
||||
'Чип «К.м.н.» warm-цветом — выделяет научную степень', |
||||
], |
||||
}, |
||||
'booking-specs': { |
||||
title: 'Запись: специализация', |
||||
category: 'Флоу записи', |
||||
goal: 'Шаг 1 из 4: выбор направления. Для пользователей, которые не знают конкретного врача.', |
||||
tasks: [ |
||||
'Переключиться между «Взрослому / Ребёнку / Онлайн»', |
||||
'Выбрать специализацию из сетки 2×2', |
||||
'Перейти к списку врачей специализации', |
||||
], |
||||
rationale: [ |
||||
'Сегментед 3 варианта сразу — быстрый split аудитории', |
||||
'Крупные карточки специализаций с иконкой, названием, кол-вом врачей, минимальной ценой', |
||||
'«Шаг 1 из 4» в шапке — прогресс флоу виден', |
||||
], |
||||
}, |
||||
'booking-doctor': { |
||||
title: 'Запись: выбор врача', |
||||
category: 'Флоу записи', |
||||
goal: 'Шаг 2 из 4: список врачей выбранной специализации с поиском и фильтрами.', |
||||
tasks: [ |
||||
'Найти конкретного врача по ФИО', |
||||
'Отфильтровать по «свободно сегодня», «детские»', |
||||
'Выбрать врача → перейти к выбору времени', |
||||
], |
||||
rationale: [ |
||||
'Та же логика, что и в tab «Врачи», но filter по специализации уже применён', |
||||
'Rich-карточка с ближайшим временем приёма — дополнительный фильтр «когда»', |
||||
], |
||||
}, |
||||
'booking-time': { |
||||
title: 'Запись: дата и время', |
||||
category: 'Флоу записи', |
||||
goal: 'Шаг 3 из 4: выбор даты и времени. Ключевой шаг флоу — тут пользователь принимает финальное решение.', |
||||
tasks: [ |
||||
'Увидеть выбранного врача (повтор сверху)', |
||||
'Выбрать дату из горизонтального скролла (7 дней)', |
||||
'Выбрать время из трёх групп (Утро/День/Вечер)', |
||||
'Нажать «Выбрать · [дата], [время]» → подтверждение', |
||||
], |
||||
rationale: [ |
||||
'Дата как горизонтальные тайлы 54px — mobile-friendly, помещаются 5-6 на экране', |
||||
'Слоты в сетке 4 колонки — легко сканировать', |
||||
'Занятые слоты disabled с зачёркиванием — чтобы пользователь не пробовал', |
||||
'CTA закреплена снизу с текущим выбором — пользователь всегда знает, что выберет', |
||||
], |
||||
}, |
||||
'booking-confirm': { |
||||
title: 'Запись: подтверждение', |
||||
category: 'Флоу записи', |
||||
goal: 'Шаг 4 из 4: финальный обзор перед подтверждением. Выбор формата (очно/онлайн) и комментарий для врача.', |
||||
tasks: [ |
||||
'Проверить врача, дату, время, адрес', |
||||
'Выбрать очно или онлайн', |
||||
'Написать комментарий для врача (симптомы)', |
||||
'Увидеть стоимость', |
||||
'Подтвердить запись', |
||||
], |
||||
rationale: [ |
||||
'Вся инфа в одной карточке-таблице — одним взглядом', |
||||
'Формат приёма — 2 крупных варианта, выбор чёткий', |
||||
'Комментарий опциональный, но есть — врач лучше готовится', |
||||
'Warm-плашка про отмену за 3 часа — ожидание поставлено сразу', |
||||
], |
||||
}, |
||||
'booking-success': { |
||||
title: 'Запись: успех', |
||||
category: 'Флоу записи', |
||||
goal: 'Визуальное подтверждение успешной записи с ключевой информацией для встречи.', |
||||
tasks: [ |
||||
'Убедиться, что запись состоялась', |
||||
'Запомнить адрес и кабинет', |
||||
'Перейти к «Моим приёмам» или на главную', |
||||
], |
||||
rationale: [ |
||||
'Полноэкранный modal (таббар скрыт) — фокус на событии', |
||||
'Большая primary-галочка с ореолом — эмоциональный момент', |
||||
'Адрес выделен отдельно — пользователь часто возвращается смотреть', |
||||
'Два выхода: appts (для чекинга) и главная (для продолжения)', |
||||
], |
||||
}, |
||||
'appts': { |
||||
title: 'Мои приёмы', |
||||
category: 'Приёмы и результаты', |
||||
goal: 'Предстоящие и прошедшие приёмы. Основная точка контроля для активных пациентов.', |
||||
tasks: [ |
||||
'Переключиться между предстоящими и прошедшими', |
||||
'Открыть детали приёма', |
||||
'Записаться на новый приём (CTA внизу)', |
||||
], |
||||
rationale: [ |
||||
'Сегментед с счётчиком предстоящих — важно знать, сколько запланировано', |
||||
'Разный фон карточек: предстоящие — градиент primary-100, прошедшие — белый', |
||||
'Чип «Заключение» на прошедших приёмах с готовым документом', |
||||
], |
||||
}, |
||||
'appt': { |
||||
title: 'Детали приёма', |
||||
category: 'Приёмы и результаты', |
||||
goal: 'Полная карточка приёма: дата/время, врач, адрес, контакты, заключение (для прошедших).', |
||||
tasks: [ |
||||
'Увидеть дату, время, тип приёма', |
||||
'Открыть карточку врача', |
||||
'Посмотреть адрес на карте', |
||||
'Позвонить в клинику', |
||||
'Отменить или перенести (для предстоящих)', |
||||
'Открыть заключение PDF (для прошедших)', |
||||
], |
||||
rationale: [ |
||||
'Крупное время 42px monospace-narrow — главное, что пациент ищет', |
||||
'Адрес отдельной секцией с кнопкой карты — частый re-check', |
||||
'Кнопка «Отменить» приглушённым danger — чтобы случайно не нажать', |
||||
'Перенос primary — предполагаемое действие', |
||||
], |
||||
}, |
||||
'results': { |
||||
title: 'Анализы и обследования', |
||||
category: 'Приёмы и результаты', |
||||
goal: 'Список всех анализов и обследований. Часть медкарты, но с фокусом на конкретные документы.', |
||||
tasks: [ |
||||
'Увидеть все результаты с датой и врачом', |
||||
'Отфильтровать по типу', |
||||
'Открыть результат (аудио/эндоскопия/лаб)', |
||||
], |
||||
rationale: [ |
||||
'Статус «Готово» / «В работе» — чип цветом справа', |
||||
'Разные иконки для типов результата (audio/image/lab) — быстрая семантика', |
||||
'Pending-результаты приглушены opacity: .7 — нельзя открыть', |
||||
], |
||||
}, |
||||
'result-audio': { |
||||
title: 'Аудиограмма', |
||||
category: 'Приёмы и результаты', |
||||
goal: 'Экран просмотра аудиограммы: график, заключение сурдолога, рекомендация.', |
||||
tasks: [ |
||||
'Увидеть график слуха для двух ушей', |
||||
'Прочитать заключение', |
||||
'Получить рекомендацию по контролю', |
||||
'Скачать PDF', |
||||
], |
||||
rationale: [ |
||||
'SVG-график с сеткой, легендой (правое/левое), зелёной зоной «норма» — стандарт аудиологии', |
||||
'Круги + сплошная линия для правого уха, крестики + пунктир для левого — международная нотация', |
||||
'Рекомендация выделена primary-50 фоном — подсказка действия', |
||||
], |
||||
}, |
||||
'result': { |
||||
title: 'Эндоскопия носоглотки', |
||||
category: 'Приёмы и результаты', |
||||
goal: 'Просмотр результата эндоскопического обследования: снимки, диагноз, рекомендации.', |
||||
tasks: [ |
||||
'Увидеть кто и когда провёл исследование', |
||||
'Просмотреть 4 снимка (сетка 2×2)', |
||||
'Открыть любой снимок в полноэкранном режиме', |
||||
'Прочитать диагноз и заключение', |
||||
'Выполнить рекомендации (нумерованный список)', |
||||
'Скачать PDF или обсудить с врачом', |
||||
], |
||||
rationale: [ |
||||
'CSS-мокапы эндоскопических снимков: радиальный градиент + блик — имитация медицинского изображения', |
||||
'Диагноз отдельной карточкой с чипом warm — самое важное', |
||||
'Рекомендации нумерованы — чёткий порядок действий', |
||||
'Полноэкранный просмотр с точками-навигацией — удобный UX для галерей', |
||||
], |
||||
}, |
||||
'recovery': { |
||||
title: 'Восстановление', |
||||
category: 'Здоровье', |
||||
goal: 'Трекер восстановления после операции: прогресс по дням, лекарства, контрольные осмотры.', |
||||
tasks: [ |
||||
'Увидеть процент восстановления', |
||||
'Отметить приём лекарства', |
||||
'Увидеть план восстановления по дням (таймлайн)', |
||||
'Связаться с хирургом в чате (chat:doctor-syndaev)', |
||||
], |
||||
rationale: [ |
||||
'Тёмная primary-карточка вверху — «важно», вне обычной иерархии', |
||||
'Прогресс-бар на белом фоне контрастно на тёмном', |
||||
'Лекарства с счётчиком доз (2/4) — пользователь видит прогресс', |
||||
'Таймлайн с линией между шагами, галочки для сделанных, активный день с кольцом', |
||||
], |
||||
}, |
||||
'audiotest': { |
||||
title: 'Тест слуха', |
||||
category: 'Здоровье', |
||||
goal: 'Трёхминутный тест слуха в приложении. Скрининг, а не диагноз.', |
||||
tasks: [ |
||||
'Прочитать инструкции (наушники, тихое место, не торопиться)', |
||||
'Пройти тест (кнопка «Слышу» при каждом тоне)', |
||||
'Увидеть результат по каждому уху', |
||||
'Записаться к сурдологу, если нужно', |
||||
], |
||||
rationale: [ |
||||
'Три стадии: intro → test → done — чёткая структура', |
||||
'Крупная анимированная волна (pulse) — объект фокуса внимания', |
||||
'Большая кнопка «Слышу» — one-tap действие, выбор быстрый', |
||||
'В результате разные цвета по ушам (success/warning) — сразу видно где норма', |
||||
'CTA «Записаться к сурдологу» — естественное продолжение', |
||||
], |
||||
}, |
||||
'chat': { |
||||
title: 'Чаты · список', |
||||
category: 'Коммуникации', |
||||
goal: 'Центр всех коммуникаций с клиникой: AI-помощник, врач, администратор.', |
||||
tasks: [ |
||||
'Открыть чат с AI-помощником для быстрого вопроса', |
||||
'Продолжить диалог с врачом', |
||||
'Написать в регистратуру', |
||||
'Начать новый чат с другим врачом', |
||||
], |
||||
rationale: [ |
||||
'AI выделен featured-карточкой с градиентом и AI-бейджем — показать как новая возможность', |
||||
'Остальные в обычном списке с аватаром, online-точкой, бейджем непрочитанных', |
||||
'Непрочитанные выделены fg-1 текстом и accent-бейджем — сразу видно', |
||||
'Плашка снизу: чат с врачом ограничен 14 днями — ожидания пользователя правильные', |
||||
], |
||||
}, |
||||
'chat:ai': { |
||||
title: 'Чат: AI-помощник', |
||||
category: 'Коммуникации', |
||||
goal: 'Приватный диалог с AI-помощником клиники: напоминания, помощь с записью, объяснение результатов.', |
||||
tasks: [ |
||||
'Задать вопрос о расписании или операции', |
||||
'Получать напоминания о лекарствах', |
||||
'Подтверждать приём препарата', |
||||
'Использовать suggested-reply чипсы для быстрых ответов', |
||||
], |
||||
rationale: [ |
||||
'Сообщения от AI — gradient primary-50 → primary-100 + бордер primary-200 — субтильное отличие от человека', |
||||
'Suggested-replies над инпутом — снижает барьер начала диалога', |
||||
'Плейсхолдер «Спросите что-нибудь» вместо «Сообщение» — AI-контекст', |
||||
], |
||||
}, |
||||
'chat:doctor-syndaev': { |
||||
title: 'Чат: врач', |
||||
category: 'Коммуникации', |
||||
goal: 'Приватный чат с лечащим врачом. Продолжение консультации после приёма.', |
||||
tasks: [ |
||||
'Рассказать о самочувствии между приёмами', |
||||
'Уточнить режим лечения', |
||||
'Согласовать перенос приёма', |
||||
'Нажать кнопку видеозвонка для срочной консультации', |
||||
], |
||||
rationale: [ |
||||
'Статус «Онлайн · отвечает 5 мин» — ожидания пациента поставлены', |
||||
'Кнопка video в хедере только у врача — эскалация в видеозвонок', |
||||
'Обычные iOS-bubbles — привычный паттерн', |
||||
], |
||||
}, |
||||
'chat:operator': { |
||||
title: 'Чат: администратор', |
||||
category: 'Коммуникации', |
||||
goal: 'Общение с регистратурой: справки, переносы, оплата, документы.', |
||||
tasks: [ |
||||
'Заказать медицинскую справку', |
||||
'Перенести приём', |
||||
'Уточнить счёт к оплате', |
||||
'Позвонить по кнопке в хедере', |
||||
], |
||||
rationale: [ |
||||
'Часы работы в подписи — пользователь знает, когда ждать ответ', |
||||
'Кнопка phone в хедере — альтернатива чату', |
||||
'Иконка 📞 на аватаре — тип коммуникации визуально закодирован', |
||||
], |
||||
}, |
||||
'profile': { |
||||
title: 'Профиль', |
||||
category: 'Сервис', |
||||
goal: 'Профиль пациента с группировкой по темам: здоровье, оплата, клиника, настройки.', |
||||
tasks: [ |
||||
'Увидеть имя, возраст, телефон', |
||||
'Перейти к медкарте, анализам, лекарствам', |
||||
'Проверить способы оплаты и бонусы', |
||||
'Показать QR пациента', |
||||
], |
||||
rationale: [ |
||||
'Градиентная карточка профиля с аватаром и QR-чипом — личное пространство', |
||||
'Секции-группы как в iOS Settings — знакомый паттерн', |
||||
'Бейджи на некоторых пунктах (Серебро у бонусов) — достижения', |
||||
], |
||||
}, |
||||
'qr': { |
||||
title: 'QR пациента', |
||||
category: 'Сервис', |
||||
goal: 'QR-код пациента для быстрой идентификации на ресепшене. Полноэкранный modal.', |
||||
tasks: [ |
||||
'Показать QR сотруднику', |
||||
'Увидеть номер пациента', |
||||
], |
||||
rationale: [ |
||||
'Тёмный primary-gradient фон — пользователь воспринимает как важный билет', |
||||
'Большой QR на белой карточке — максимальный контраст для сканера', |
||||
'Таббар скрыт — фокус на QR', |
||||
'Плашка внизу: код обновляется каждые 60 сек — сигнал безопасности', |
||||
], |
||||
}, |
||||
'telemed': { |
||||
title: 'Видеозвонок', |
||||
category: 'Коммуникации', |
||||
goal: 'Видеозвонок с врачом. Full-screen экран «в разговоре».', |
||||
tasks: [ |
||||
'Вести разговор с врачом', |
||||
'Свернуть (кнопка вниз)', |
||||
'Завершить звонок (красная кнопка)', |
||||
'Переключить микрофон / видео / чат', |
||||
], |
||||
rationale: [ |
||||
'Тёмный фон — стандартная метафора видеозвонка', |
||||
'Self-preview окошко в углу — пользователь видит себя', |
||||
'Красный таймер с мигающей точкой — идёт запись/звонок', |
||||
'Крупные контрольные кнопки снизу — критичные действия большими хит-зонами', |
||||
], |
||||
}, |
||||
'medcard': { |
||||
title: 'Медицинская карта', |
||||
category: 'Здоровье', |
||||
goal: 'Медкарта: основное, аллергии, история диагнозов.', |
||||
tasks: [ |
||||
'Увидеть пол, возраст, рост/вес, группу крови', |
||||
'Проверить аллергии', |
||||
'Просмотреть историю диагнозов с датами', |
||||
'Добавить аллергию', |
||||
], |
||||
rationale: [ |
||||
'Основные данные в label/value списке — табличная структура', |
||||
'Аллергии как красные чипы — критическая инфа', |
||||
'История — плоская лента с датой, диагнозом, врачом', |
||||
], |
||||
}, |
||||
'notifications': { |
||||
title: 'Уведомления', |
||||
category: 'Сервис', |
||||
goal: 'Лента уведомлений: напоминания, готовые заключения, сообщения, акции.', |
||||
tasks: [ |
||||
'Просмотреть последние уведомления', |
||||
'Увидеть время каждого', |
||||
], |
||||
rationale: [ |
||||
'Разные tint (primary/warning/warm) по типу уведомления — семантика цветом', |
||||
'Первая строка с временем справа — стандарт ленты', |
||||
], |
||||
}, |
||||
'articles': { |
||||
title: 'Статьи врачей · список', |
||||
category: 'Информация', |
||||
goal: 'Список всех статей врачей. Образовательный контент, формирует доверие к клинике.', |
||||
tasks: [ |
||||
'Отфильтровать по тегу (Дети / Операции / Беременность / Слух)', |
||||
'Открыть статью', |
||||
], |
||||
rationale: [ |
||||
'Крупные hero-карточки с emoji и лидом — стимулируют нажатие', |
||||
'Теги как pill-фильтры сверху — выбор по интересу', |
||||
], |
||||
}, |
||||
'article': { |
||||
title: 'Статья врача', |
||||
category: 'Информация', |
||||
goal: 'Детальная статья с разметкой: лид, подзаголовки, списки, callout-плашки, карточка автора.', |
||||
tasks: [ |
||||
'Прочитать статью', |
||||
'Увидеть автора с контактом', |
||||
'Записаться к автору', |
||||
'Открыть связанные статьи', |
||||
], |
||||
rationale: [ |
||||
'Hero с заголовком занимает 72% ширины, emoji 82px справа — журнальный ритм', |
||||
'Плавающие back/bookmark кнопки поверх hero — не мешают картинке', |
||||
'Callout разных tone (danger/warn/info) — визуально маркированные предупреждения', |
||||
'Author card + CTA записи к автору — конверсия из чтения в действие', |
||||
], |
||||
}, |
||||
'search': { |
||||
title: 'Поиск', |
||||
category: 'Информация', |
||||
goal: 'Универсальный поиск по врачам, услугам, симптомам, статьям, приёмам.', |
||||
tasks: [ |
||||
'Ввести ФИО / симптом / услугу / дату', |
||||
'Получить релевантные результаты по типам', |
||||
'Использовать предложенные чипы-запросы или симптомы', |
||||
'Перейти на выбранный результат', |
||||
], |
||||
rationale: [ |
||||
'Autofocus на инпуте — сразу можно печатать', |
||||
'Пустое состояние с популярными запросами — помогает начать', |
||||
'Группировка результатов по типам (Врачи, Услуги, Симптомы…) — лёгкий скан', |
||||
'Date-detection (сегодня, завтра, апр) показывает приёмы — smart-поведение', |
||||
], |
||||
}, |
||||
'contacts': { |
||||
title: 'Контакты', |
||||
category: 'Информация', |
||||
goal: 'Адреса клиник, телефон, часы работы, маршруты.', |
||||
tasks: [ |
||||
'Позвонить в клинику', |
||||
'Написать в чат', |
||||
'Увидеть адреса с мокапом здания и карты', |
||||
'Построить маршрут', |
||||
], |
||||
rationale: [ |
||||
'Primary-darker карточка сверху с телефоном 26px narrow — главный контакт', |
||||
'CSS-мокапы здания и карты для каждого адреса — прототипное решение без реальных изображений', |
||||
'Кнопки «Маршрут / позвонить / видео» в каждой карточке — действия по локации', |
||||
], |
||||
}, |
||||
'prices': { |
||||
title: 'Цены', |
||||
category: 'Информация', |
||||
goal: 'Прайс-лист всех услуг с поиском, категориями, группировкой, диапазоном цен.', |
||||
tasks: [ |
||||
'Найти услугу по названию', |
||||
'Отфильтровать по категории', |
||||
'Увидеть сгруппированные цены', |
||||
'Записаться прямо с услуги', |
||||
], |
||||
rationale: [ |
||||
'Сводка найдено / от-до — сразу понятен разброс цен', |
||||
'Категории как pill-фильтры, группировка в карточках', |
||||
'Цены narrow-шрифтом — акцент на числе', |
||||
], |
||||
}, |
||||
'dev-colors': { |
||||
title: 'DEV · Палитра', |
||||
category: 'DEV', |
||||
goal: 'Служебный экран для разработчиков. Все цвета дизайн-системы с hex, ролями, применением.', |
||||
tasks: [ |
||||
'Увидеть hex любой CSS-переменной', |
||||
'Переключать палитру в Tweaks и наблюдать изменения', |
||||
'Понять, где используется конкретный цвет', |
||||
'Перейти к примерам применения', |
||||
], |
||||
rationale: [ |
||||
'Ключевая полоса из 8 цветов сверху — палитра видна сразу', |
||||
'Группировка по ролям (primary / warm / accent / status / text / surfaces)', |
||||
'Каждая строка: свотч + имя + css-var + hex + описание — полная информация', |
||||
'Подпись «динамически» у групп, меняющихся при переключении палитры', |
||||
], |
||||
}, |
||||
'dev-examples': { |
||||
title: 'DEV · Примеры', |
||||
category: 'DEV', |
||||
goal: 'Готовые компоненты дизайн-системы с указанием CSS-переменных, которые они используют.', |
||||
tasks: [ |
||||
'Посмотреть, как выглядит любая кнопка/чип/карточка', |
||||
'Прочитать, какие vars внутри', |
||||
'Сверить с экраном DEV · Палитра', |
||||
], |
||||
rationale: [ |
||||
'Каждый пример помечен VarTag-монокодами', |
||||
'Секции по типам: кнопки / чипы / поверхности / статусы / текст / аватары / формы / тени', |
||||
], |
||||
}, |
||||
'docs': { |
||||
title: 'Документация', |
||||
category: 'DEV', |
||||
goal: 'Единый дизайн-гайд прототипа. Список всех экранов с описаниями — цели, задачи, design-решения.', |
||||
tasks: [ |
||||
'Просмотреть все экраны по категориям', |
||||
'Развернуть описание экрана inline', |
||||
'Перейти к реальному экрану для интерактивного просмотра', |
||||
], |
||||
rationale: [ |
||||
'Группировка по категориям облегчает навигацию для ревью', |
||||
'Описания collapsed-by-default, чтобы список оставался обозримым', |
||||
'Кнопка «Открыть экран» на каждой карточке — быстрый переход в демо-режим', |
||||
'Работает в паре с toggle «Описания» в Tweaks (плашка над телефоном)', |
||||
], |
||||
}, |
||||
}; |
||||
|
||||
// Резолвер: по текущему screenId и ctx отдаёт правильное описание.
|
||||
// Учитывает: варианты главной (homeVariant), compound-маршруты (doctor:id,
|
||||
// appt:id, result:id, article:id, chat:id).
|
||||
export function getScreenDoc(screenId, ctx) { |
||||
if (!screenId) return null; |
||||
const parts = screenId.split(':'); |
||||
const base = parts[0]; |
||||
|
||||
// home с вариантом: home:cards / home:list / home:feed
|
||||
if (base === 'home') { |
||||
const v = ctx?.homeVariant || 'cards'; |
||||
return SCREEN_DOCS[`home:${v}`] || SCREEN_DOCS['home:cards']; |
||||
} |
||||
// chat с конкретным id (ai / doctor-syndaev / operator) — отдельные описания
|
||||
if (base === 'chat' && parts[1]) { |
||||
return SCREEN_DOCS[screenId] || SCREEN_DOCS['chat']; |
||||
} |
||||
// Generic compound: doctor:xxx → doctor, article:xxx → article и т.д.
|
||||
const compounds = ['doctor', 'appt', 'result', 'article', 'booking-doctor', 'booking-time', 'booking-confirm']; |
||||
if (compounds.includes(base)) { |
||||
return SCREEN_DOCS[base] || null; |
||||
} |
||||
return SCREEN_DOCS[screenId] || SCREEN_DOCS[base] || null; |
||||
} |
||||
|
||||
// Для экрана «Документация»: список всех уникальных описаний, сгруппированных
|
||||
// по категориям. Возвращает упорядоченный массив для рендера.
|
||||
export function getAllDocs() { |
||||
const ORDER = ['Главная', 'Врачи и запись', 'Флоу записи', 'Приёмы и результаты', 'Здоровье', 'Коммуникации', 'Информация', 'Сервис', 'DEV']; |
||||
const groups = new Map(); |
||||
for (const [key, doc] of Object.entries(SCREEN_DOCS)) { |
||||
if (!groups.has(doc.category)) groups.set(doc.category, []); |
||||
groups.get(doc.category).push({ key, ...doc }); |
||||
} |
||||
return ORDER.filter(c => groups.has(c)).map(c => ({ category: c, items: groups.get(c) })); |
||||
} |
||||
|
||||
// screenId для навигации из docs screen. Учитывает compound-маршруты.
|
||||
export function resolveRouteForDoc(key) { |
||||
// home:cards → переводим в home + homeVariant cards (но через простой nav.set
|
||||
// мы не можем поменять homeVariant — это в Tweaks). Поэтому home:* → 'home'.
|
||||
if (key.startsWith('home:')) return 'home'; |
||||
// chat:ai / chat:doctor-syndaev / chat:operator — полноценные маршруты
|
||||
if (key.startsWith('chat:')) return key; |
||||
// generic compounds: нужно подставить пример id
|
||||
if (key === 'doctor') return 'doctor:syndaev'; |
||||
if (key === 'appt') return 'appt:a1'; |
||||
if (key === 'result') return 'result:r2'; |
||||
if (key === 'article') return 'article:otitis-kids'; |
||||
if (key === 'booking-doctor') return 'booking-doctor:ent'; |
||||
if (key === 'booking-time') return 'booking-time:syndaev'; |
||||
if (key === 'booking-confirm') return 'booking-confirm:syndaev:1:16:00'; |
||||
return key; |
||||
} |
||||
@ -0,0 +1,106 @@
|
||||
import React, { useState } from 'react'; |
||||
import { I } from '../icons.jsx'; |
||||
import { ScreenHeader } from '../components.jsx'; |
||||
import { getAllDocs, resolveRouteForDoc } from '../docs.js'; |
||||
|
||||
function DocRow({ doc, route, onOpen }) { |
||||
const [open, setOpen] = useState(false); |
||||
return ( |
||||
<div style={{ padding: 0 }}> |
||||
<button onClick={() => setOpen(o => !o)} style={{ |
||||
width: '100%', padding: '14px 16px', display: 'flex', gap: 12, alignItems: 'flex-start', textAlign: 'left', |
||||
background: 'transparent', |
||||
}}> |
||||
<div style={{ |
||||
width: 34, height: 34, borderRadius: 9, background: 'var(--c-primary-100)', |
||||
color: 'var(--c-primary-darker)', display: 'flex', alignItems: 'center', justifyContent: 'center', |
||||
fontSize: 13, fontWeight: 700, flexShrink: 0, marginTop: 1, |
||||
}}>i</div> |
||||
<div style={{ flex: 1, minWidth: 0 }}> |
||||
<div style={{ fontSize: 14, fontWeight: 700, color: 'var(--c-fg-1)', marginBottom: 3 }}>{doc.title}</div> |
||||
<div className="sub" style={{ fontSize: 12, lineHeight: 1.45 }}>{doc.goal}</div> |
||||
</div> |
||||
<I.chevD size={16} style={{ |
||||
color: 'var(--c-fg-4)', flexShrink: 0, marginTop: 8, |
||||
transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .15s', |
||||
}} /> |
||||
</button> |
||||
|
||||
{open && ( |
||||
<div style={{ padding: '0 16px 14px 62px' }}> |
||||
<div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: .6, color: 'var(--c-fg-3)', marginTop: 6, marginBottom: 6 }}>Задачи пользователя</div> |
||||
<ul style={{ margin: 0, padding: 0, listStyle: 'none' }}> |
||||
{doc.tasks.map((t, i) => ( |
||||
<li key={i} style={{ display: 'flex', gap: 8, padding: '3px 0', fontSize: 13, color: 'var(--c-fg-2)', lineHeight: 1.5 }}> |
||||
<span style={{ width: 5, height: 5, borderRadius: 999, background: 'var(--c-primary-darker)', marginTop: 8, flexShrink: 0 }} /> |
||||
<span>{t}</span> |
||||
</li> |
||||
))} |
||||
</ul> |
||||
|
||||
<div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: .6, color: 'var(--c-fg-3)', marginTop: 14, marginBottom: 6 }}>Design-решения</div> |
||||
<ul style={{ margin: 0, padding: 0, listStyle: 'none' }}> |
||||
{doc.rationale.map((t, i) => ( |
||||
<li key={i} style={{ display: 'flex', gap: 8, padding: '3px 0', fontSize: 13, color: 'var(--c-fg-2)', lineHeight: 1.5 }}> |
||||
<span style={{ width: 5, height: 5, borderRadius: 999, background: 'var(--c-warm-text)', marginTop: 8, flexShrink: 0 }} /> |
||||
<span>{t}</span> |
||||
</li> |
||||
))} |
||||
</ul> |
||||
|
||||
{doc.variants && ( |
||||
<div style={{ marginTop: 10, padding: 10, background: 'var(--c-primary-50)', borderRadius: 8, fontSize: 12, color: 'var(--c-fg-2)', lineHeight: 1.5 }}> |
||||
<strong>Варианты: </strong>{doc.variants} |
||||
</div> |
||||
)} |
||||
|
||||
<button onClick={() => onOpen(route)} className="btn-s" style={{ marginTop: 12 }}> |
||||
Открыть экран <I.arrow size={14} /> |
||||
</button> |
||||
</div> |
||||
)} |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export function DocsScreen({ nav }) { |
||||
const groups = getAllDocs(); |
||||
|
||||
return ( |
||||
<div style={{ paddingBottom: 40 }}> |
||||
<ScreenHeader title="Документация" subtitle="Все экраны прототипа" onBack={() => nav.pop()} /> |
||||
|
||||
<div style={{ padding: '0 20px 14px' }}> |
||||
<div className="card" style={{ |
||||
padding: 14, background: 'var(--c-primary-50)', border: '1px solid var(--c-primary-200)', |
||||
}}> |
||||
<div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}> |
||||
<I.shield size={18} style={{ color: 'var(--c-primary-darker)', flexShrink: 0, marginTop: 1 }} /> |
||||
<div style={{ fontSize: 13, color: 'var(--c-fg-2)', lineHeight: 1.5 }}> |
||||
Список всех экранов прототипа с целями и design-решениями. Разверните любой пункт, чтобы прочитать задачи пользователя и обоснование. Кнопка «Открыть экран» ведёт в демо. |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div style={{ padding: '0 16px' }}> |
||||
{groups.map(g => ( |
||||
<div key={g.category} style={{ marginBottom: 18 }}> |
||||
<div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: .6, color: 'var(--c-fg-3)', padding: '4px 4px 10px', display: 'flex', justifyContent: 'space-between' }}> |
||||
<span>{g.category}</span> |
||||
<span>{g.items.length}</span> |
||||
</div> |
||||
<div className="card" style={{ padding: 0 }}> |
||||
{g.items.map((item, i) => ( |
||||
<React.Fragment key={item.key}> |
||||
<DocRow doc={item} route={resolveRouteForDoc(item.key)} onOpen={(r) => nav.set(r)} /> |
||||
{i < g.items.length - 1 && <div style={{ height: 1, background: 'var(--c-divider)', marginLeft: 62 }} />} |
||||
</React.Fragment> |
||||
))} |
||||
</div> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
Loading…
Reference in new issue