Аудит интерфейса Хорики
Интерфейс собран вокруг шагов конвейера, а не вокруг работы менеджера. Поэтому в нём трудно ориентироваться, а главное, ради чего система существует, разобранное меню заведения и покрытие его номенклатурой, на карточке заведения не показано вовсе.
Резюме
Фронтенд технически здоров: React, shadcn, TanStack Query, светлая и тёмная темы, скелетоны, тосты, редактор предложения с автосохранением. Проблема не в качестве кода, а в модели: экраны повторяют устройство бэкенда. Меню «Работа» перечисляет десять пунктов подряд, среди них песочница «Матчинг меню» с захардкоженным примером, «Парсинг сайта» с собственным списком тех же заведений и два справочника. Карточка заведения показывает адрес, координаты, «venue_score» и фото, но не показывает ни блюд, ни их состава, ни того, что из этого поставщик может закрыть.
Пять выводов:
- Навигация построена по глаголам и внутренностям конвейера. Нужна навигация по объектам: Заведения, Предложения, Номенклатура, Настройки. Три отдельных входа в одно и то же (Организации, Парсинг сайта, Матчинг меню) надо свести к одному.
- Раскладка блюд есть в базе и в API, но не на карточке. Таблица
dish_decompositionи функцияmatch_menuотдают состав каждого блюда с пометкой, что закрыто, а что нет. Показать это можно без новой математики: нужен один эндпоинт и одна вкладка. - Три системы статусов не совпадают друг с другом. «ожидает / new» у заведения с разобранным меню и четырьмя черновиками КП. Статус должен выводиться из фактов, а не храниться от старого пайплайна.
- Язык смешанный. Resolve, Score, Stage, fetch_site, classify, extract, E.164, Redis, «термов», «строг./мягк.». Словарь проекта уже написан в CONTEXT.md, интерфейс его не использует.
- Визуальная система не расставляет акценты. Один оранжевый на активный пункт меню, ссылки, кнопки и иконки; контраст оранжевого текста на белом 3,1:1 при норме 4,5:1; всё в одинаковых карточках; текст 10–11 px в 21 месте.
Рекомендация: не «перекрасить», а перестроить каркас за четыре этапа (раздел План внедрения). Первый этап, словарь, статусы и навигация, укладывается в один-два дня и уже снимает большую часть жалоб на ориентацию. Второй этап, карточка заведения с вкладкой «Меню и раскладка», закрывает главную содержательную дыру.
Как проверяли
- Прочитан весь фронтенд: 9 129 строк, 19 экранов, компоненты, тема, флаг первой версии. Прочитаны роутеры API и модели данных, чтобы отличить «данных нет» от «данные есть, но не показаны».
- Стенд поднят локально на демо-наборе (3 заведения, 15 товаров, 4 черновика КП). Сняты скриншоты каждого экрана при ширине 1440, 1280 и 390 px, в тёмной теме и со свёрнутым меню.
- Посчитан контраст ключевых пар цветов по формуле WCAG 2.2.
- Проверены консоль браузера, поведение при ошибке API, подсчитаны опросы сервера, размеры шрифтов, нелокализованные подписи.
- Изучены 20 источников по навигации, страницам записей, таблицам, пустым состояниям, дашбордам, прозрачности ИИ и инструментам продаж для дистрибьюторов продуктов. Список в конце.
Ограничение: на стенде нет живых распарсенных сайтов, поэтому экран «Парсинг сайта» с трейсом LLM оценивался по коду и по устройству ответа API.
Для кого интерфейс
Пользователь первой версии, по docs/PRODUCT.md и docs/MVP_FLOW.html, это менеджер по продажам поставщика продуктов. Его задача звучит так: «У меня есть сайт заведения. Хочу за десять минут понять, что оно готовит, что из этого мы возим и по какой цене, и отправить письмо с подбором продуктов». Администратор заполняет реквизиты и шаблон. Оператор очереди и прокси в первой версии скрыт.
Объекты, которыми думает менеджер, перечислены в CONTEXT.md: Заведение, его Меню из блюд, Раскладка блюда на ингредиенты, Покрытие номенклатурой, Пробелы, Колонка прайса, Предложение из письма и подбора. Это и есть карта объектов, вокруг которой по методу OOUX строится навигация и каждая страница.
Интерфейс же построен вокруг других слов: ассеты, fetch_site, classify, extract, resolve, stage, score, матчинг. Это слова разработчика конвейера. Каждое из них требует от менеджера перевода, а перевод и есть та тяжесть в ориентации, о которой идёт речь.
Есть в API, нет в интерфейсе
- Загрузка меню файлом, фото, PDF, архивом или текстом:
POST /organizations/{id}/menu/ingest. MVP_FLOW называет это запасным путём, кнопки нет. - Покрытие меню и сводка по заведению: поля
menu_scoreиmenu_summaryуже считаются и хранятся, в списке заведений не показаны. - Раскладка блюда: таблица
dish_decompositionс источником seed / rule / llm / manual. Ручная правка предусмотрена моделью, интерфейса нет. - Обратный поиск «ингредиент → заведения»: эндпоинт и индекс есть, экран спрятан флагом первой версии.
- Заметки и кухня заведения:
PATCH /organizations/{id}принимаетnotesиcuisine_types, полей на карточке нет. - Стоимость LLM по заведению:
llm_cost_usdотдаётся, нигде не показана.
Главные находки
Находки пронумерованы по приоритету: первая мешает сильнее всего. Серьёзность: критично мешает выполнить задачу, серьёзно замедляет и путает, средне снижает доверие и качество, мелочь шлифовка.
Навигация повторяет конвейер, а не работу менеджера
Боковое меню: «Обзор» с одним пунктом, «Работа» с десятью, «Админ» с одним. Группа «Данные» скрыта флагом. Внутри «Работы» вперемешку рабочие экраны (Организации, КП), справочники (Номенклатура, Ингредиенты, Кухни, Контрагенты 1С), инструмент ввода (Парсинг сайта) и песочница (Матчинг меню). Порядок не отражает ни частоту, ни последовательность работы.
Что видно
- Три входа в одни и те же заведения. «Организации» показывает всё. «Парсинг сайта» показывает только заведения с отпечатком
site:и собственную таблицу «лидов» со своими статусами. «Матчинг меню» просит вставить блюда текстом и не связан ни с одним заведением. Демо-заведения видны в первом списке и невидимы во втором. - Названия групп общие: «Обзор», «Работа», «Данные», «Админ». По NN/g такие ярлыки не помогают предсказать содержимое, и пользователь перебирает пункты.
- Названия пунктов это действия и техника: «Матчинг меню», «Парсинг сайта». По Pencil & Paper и OOUX навигация по существительным предсказуемее.
- Бренд называется «AI, внутренний инструмент» в меню, «catering-ai» на входе и «Хорика» в документации. Вкладка браузера всегда называется «AI», история и закладки бесполезны.
- Нет глобального поиска. Чтобы найти заведение, надо зайти в список и набрать название в фильтре; товары ищутся на другом экране, предложения на третьем.
- Свёрнутое меню оставляет десять иконок без подписей. NN/g: «в навигации слово стоит тысячи картинок».
- Хлебные крошки есть только на карточке и на меню заведения. В редакторе предложения вместо них собственная кнопка «К списку»; на карточке заведения крошки дублируются ссылкой «назад к списку».
- Ссылка «Смотреть очередь» на дашборде ведёт на маршрут, скрытый флагом, и молча возвращает на дашборд.
Что сделать
- Три группы по существительным: Работа (Рабочий стол, Заведения, Предложения), Справочники (Номенклатура с вкладками «Товары» и «Ингредиенты», Кухни, Контрагенты 1С), Настройки (Предложение, Система). Семь пунктов вместо десяти, и у каждой группы своё имя.
- «Парсинг сайта» превратить в действие «Добавить заведение» на экране Заведений: ссылка или список ссылок, файл, текст меню. Ход и результат разбора показывать на карточке заведения, а не в отдельной таблице.
- «Матчинг меню» перенести на карточку заведения как вкладку «Меню и раскладка». Песочнику для проверки произвольного текста оставить внутри «Добавить заведение → вставить меню текстом».
- Одно имя продукта во всех местах, заголовок вкладки браузера вида «Траттория Da Vinci · Хорика».
- Глобальный поиск по заведениям, товарам и предложениям с клавиши
Ctrl+K. Для продукта с шестью типами объектов это стандарт (Pencil & Paper, Linear). - Крошки на каждом вложенном экране по правилам NN/g: текущий уровень последним и не ссылкой, дублирующие «назад» убрать.
Карточка заведения не показывает меню, раскладку и покрытие
Карточка это шесть карточек друг под другом: «Контакты и метаданные» (пять полей из шести пустые), «Скоринг и типы площадки» (venue_score «—» и поле ввода типов через запятую), «Предложения», «Что можем поставлять (по кухне) — без меню», «Контрагент 1С», «Фото (0)». Блюд нет. Ингредиентов нет. Того, что закрыто номенклатурой, нет. Кнопка «Меню» уводит на отдельную страницу, где первым блоком идут «Ассеты (0)», а таблица позиций содержит колонки «Описание», «Цена», «Вес» с прочерками и ни одной колонки про состав.
Где данные на самом деле есть
- Экран «Матчинг меню» умеет показывать раскладку каждого блюда: чипы ингредиентов с тремя состояниями (закрыт точно, закрыт родственным товаром, нет в номенклатуре), долю покрытия, список «уже можем поставлять» и «предложить в меню». Но он требует вставить блюда руками и не знает о заведении.
- API
GET /sites/parse/{org_id}уже вызываетmatch_menuпо позициям заведения и отдаёт те же данные. Сборка предложения делает то же самое и подбирает до трёх товаров под ингредиент с ценой выбранной колонки. - Иными словами, вся математика для вкладки «Меню и раскладка» написана. Не написан один эндпоинт карточки и одна вкладка.
Что сделать
- Карточка заведения с вкладками: Обзор, Меню и раскладка, Подбор товаров, Предложения, Источники, Контрагент 1С. Каркас в разделе «Целевая модель».
- Шапка карточки отвечает на три вопроса менеджера одной строкой: сколько блюд разобрано, какое покрытие, есть ли черновик КП. Рядом главное действие «Создать КП» или «Открыть черновик».
- Вкладка «Меню и раскладка»: таблица по разделам меню, у каждого блюда ингредиенты чипами с состоянием, полоска покрытия, число товаров. Раскрытие строки показывает товары под каждый ингредиент по колонке прайса заведения. Внизу сводка: «Поставляем сейчас», «Чего нет в ассортименте» с числом блюд, «Расширение меню».
- Кнопка «Поправить состав» у блюда: модель
dish_decompositionуже знает источникmanual. Это тот самый контроль человека над выводом LLM, который рекомендуют для извлечённых данных: подтвердить или исправить, а сомнительные строки подсветить. - Блок «по кухне без меню» показывать только когда меню действительно нет.
- Добавить кнопки «Загрузить меню файлом» и «Вставить меню текстом», эндпоинт есть.
Три системы статусов и смешанный язык
У заведения два хранимых поля статуса от старого конвейера автопоиска: visit_status (new, in_progress, done, failed, skipped) и pipeline_stage (pending, resolved, collected, classified, done, failed). Поток первой версии «сайт по ссылке» их не обновляет. Экран «Парсинг сайта» считает третий статус на лету (В очереди, Обход, Распознавание, Готово, Ошибка, Меню не найдено). В итоге заведение с разобранным меню и четырьмя черновиками показано как «ожидает / new», а на дашборде не входит в «Готовы к КП».
Что видно
- На карточке два бейджа рядом: «готово» и «done»; «ожидает» и «new». Второй выводится сырым значением перечисления.
- В таблице заведений колонки «Stage» и «Статус» на английском и русском, значения «готово / Готово».
- На дашборде воронка со ступенью «Resolved» по-английски среди русских.
- Нелокализованные подписи: Resolve, Score, Stage, fetch_site, classify, extract, aggregators, Retry, Org, E.164, «Ждут в Redis», ARQ, классы фото menu / dish / interior / garbage / unknown, «термов», «строг./мягк.», «Мягкое 18 %».
- Тосты сообщают «fetch_site поставлен», «classify поставлен». Пустые состояния советуют «Запустите fetch_site» и даже команду
python -m app.cli import-nomenclature. - Заголовок списка говорит о «найденных кейтерингах», список типов площадок содержит «event-агентство» и «площадка для мероприятий»: наследие этапа, когда клиентом считался кейтеринг. PRODUCT.md прямо просит этого избегать.
Что сделать
- Один статус заведения, выводимый из фактов на сервере: Новое (нет задач и меню), Ищем меню (есть активная задача), Меню не найдено, Меню разобрано (есть позиции), Есть черновик КП, КП отправлено (экспорт), Ошибка. Технические стадии оставить на вкладке «Источники».
- Словарь интерфейса по CONTEXT.md: Заведение, а не Организация; Товар и Номенклатура; Ингредиент; Раскладка; Покрытие; Пробел; Колонка прайса; Предложение, «КП» допустимо в кнопках. Таблица замен в разделе «Целевая модель».
- Действия по-русски и по смыслу: «Найти меню на сайте», «Распознать позиции», «Разметить фото», «Обновить карточку», «Повторить».
- Убрать из первой версии наследие автопоиска: типы площадок кейтеринга, «мин. score», кнопку «Resolve» на карточке (в списке она уже скрыта флагом, на карточке осталась).
Дашборд рассказывает о краулере, а не о продажах
Что видно
- Три показателя: всего заведений, новые, «Готовы к КП» по статусу done. Из-за находки 3 «готовые» посчитаны неверно: 2 вместо 3.
- «Воронка pipeline» это распределение шести последних заведений по стадиям, а не метрика. Проценты от шести создают ложную точность.
- Карточка «Очередь» показывает воркеры и Redis, то есть данные оператора, а ссылка «Смотреть очередь» мертва в первой версии.
- Нет ответа на вопрос «что мне делать сейчас»: у каких заведений меню разобрано, а КП ещё нет; какие черновики не экспортированы; где идёт разбор и сколько ждать; где ошибка.
- Нет свежести данных: дашборд опрашивает сервер пять раз каждые 8–15 секунд, но не показывает, когда обновлён.
Что сделать
- Заменить дашборд «Рабочим столом» из очередей действий, каждая ведёт в отфильтрованный список: «Меню разобрано, КП ещё нет», «Черновики без экспорта», «Идёт поиск меню», «Меню не найдено», «Ошибки». Это операционный дашборд по Стивену Фью: 5–9 показателей, каждый с действием.
- Лента последних событий: заведение добавлено, меню разобрано, КП собрано, КП экспортировано. Источники:
crawl_jobsиproposals. - Метрики воркеров и Redis оставить на скрытом экране «Очередь».
- Убрать приветствие по логину и эмодзи; заголовок должен называть экран.
Списки не помогают выбирать
Что видно
- Таблица заведений: восемь колонок, из них «Типы» и «Score» пусты у каждой строки в потоке первой версии, «Stage» и «Статус» дублируют друг друга. Нет кухни, нет числа блюд, нет покрытия (хотя
menu_scoreхранится), нет статуса КП, нет даты последнего действия. Менеджеру нечем отличить перспективное заведение от пустого. - Семь элементов фильтра в ряд, включая числовое поле «мин. score» и сортировку без подписи. Активные фильтры не выделены; кнопка «Сбросить» появляется только при наличии фильтра.
- Поиск в списке заведений срабатывает на каждую букву и дёргает сервер, в номенклатуре, ингредиентах и предложениях нужна кнопка «Найти». Два разных поведения для одного действия.
- Кликабельна только ссылка в названии, остальная строка нет. В номенклатуре и ингредиентах, наоборот, кликабельна строка. Опять два поведения.
- Пагинация «Назад / Вперёд» без выбора страницы и без размера страницы. Экран «Парсинг сайта» ограничен 200 строками без пагинации вовсе.
- «Кухни» это стена из 33 карточек, почти все с красной полоской 0–17 %: демонстрирует, чего у поставщика нет, а не что есть. Сортировки, поиска и сводки нет.
- В номенклатуре первая и самая яркая категория «без категории 8».
Что сделать
- Колонки списка заведений: Заведение (имя и домен), Кухня, Меню (число блюд), Покрытие (процент и полоска), КП (статус и номер), Обновлено. Числа справа, табличные цифры, строка кликабельна целиком.
- Фильтры чипами с видимым активным состоянием и сохранённые виды: «Готовы к КП», «В работе», «Без меню». Единое поведение поиска: мгновенно с задержкой 300 мс везде.
- Сортировка по покрытию по умолчанию для вида «Готовы к КП»: Pencil & Paper советуют сортировать по тому, что требует действия.
- Кухни: таблица с сортировкой по покрытию, поиском и вкладками «закрыто / пробелы», как справочник внутри Номенклатуры.
- Пагинация с числом страниц и размером страницы, липкая шапка, плотность строк 40–44 px.
Визуальная система не расставляет акценты
Тема это shadcn по умолчанию с тёплыми нейтралями и оранжевым amber-600. Основа хорошая, но акцент не экономят: он на активном пункте меню, на каждой ссылке в таблице, на главной кнопке, на иконках KPI, на фокусе. Когда всё оранжевое, ничто не главное. Linear в своём редизайне сформулировал это так: навигация должна отступать, а содержание вести.
| Пара цветов | Контраст | Норма AA | Где |
|---|---|---|---|
| Оранжевый текст на белом | 3,1:1 | 4,5:1 | ссылки в таблицах, «Все организации →», активный пункт меню |
| Белый текст на оранжевой кнопке | 3,1:1 | 4,5:1 | «Создать КП», «Сматчить», «Войти» |
| Оранжевый на оранжевой подложке 10 % | 2,7:1 | 4,5:1 | активный пункт бокового меню |
| Заголовки групп меню, серый 70 % | 2,6:1 | 4,5:1 | «ОБЗОР», «РАБОТА», «АДМИН» |
| Серый вторичный текст на белом | 4,75:1 | 4,5:1 | описания, подписи: норма |
| Тёмная тема: оранжевый на карточке | 7,8:1 | 4,5:1 | норма |
Что видно
- Текст 10–11 px используется в 21 месте: бейджи типов, подписи «строг./мягк.», группы меню, подсказки. Для таблиц, которые читают часами, минимум 12 px, а базовый 13–14.
- Каждый блок это белая карточка с рамкой, тенью и одинаковым радиусом на бежевом фоне. Иерархия между «карточкой с реквизитами» и «карточкой с фото (0)» не читается.
- На каждом экране заголовок и абзац-описание длиной в предложение из документации: «Канонические продукт-типы из номенклатуры (синонимы и сорта сведены). Позвоночник матчинга…». Это объяснение системы, а не подпись экрана.
- Отступы страниц различаются: список предложений, редактор и настройки предложения добавляют собственные поля поверх общих, заголовок на них сдвинут на 25 px правее остальных экранов.
- Компонент Select при указании ширины ставит стрелку в правый край родителя, а не поля: на списке предложений стрелка «Все статусы» висит у правого края экрана.
- Шрифт Inter грузится с внешнего сайта rsms.me. При блокировке страница уходит на системный шрифт молча.
- Иконка бренда «искры» и имя «AI»: общий знак искусственного интеллекта, не поставщика продуктов.
Что сделать
- Оставить тёплые нейтрали, но акцент тратить в одном месте: главная кнопка экрана и ссылки. Активный пункт меню решать весом и фоном нейтрали, как в Linear и в блоках shadcn sidebar.
- Для текста взять акцент темнее (порядка amber-700/orange-700, контраст выше 4,5:1) либо синий, оранжевый оставить кнопкам с тёмным текстом. Семантические цвета покрытия (зелёный, янтарный, красный) не считать акцентом.
- Шкала текста: 12 / 13 / 14 / 16 / 20 / 24 px, три цвета текста и два веса, по Refactoring UI. Табличные цифры и правое выравнивание чисел.
- Не всё карточка: разделять блоки заголовками и отступами, рамку и тень оставить тому, что действительно отделено (диалог, всплывающая панель).
- Описания экранов убрать из заголовков в подсказки «?» и в пустые состояния.
- Шрифт в сборку, единые отступы страниц через Layout, починить Select.
Мобильная версия работает, но не для работы
Что видно
- Выезжающее меню и верхняя панель работают. Но в панели нет названия экрана, и после закрытия меню пользователь не знает, где он.
- Таблицы не перестраиваются: восемь колонок в горизонтальной прокрутке внутри карточки, адрес обрезан.
- Редактор предложения на 390 px это холст A4 в горизонтальной прокрутке. Здесь достаточно предупредить: правка на телефоне не поддерживается, скачать PDF можно.
- Свёрнутое боковое меню на десктопе показывает только иконки без всплывающих подписей, кроме системного title.
Что сделать
- Списки на узких экранах превращать в карточки-строки: имя, кухня, покрытие, статус, одна строка на заведение.
- Название экрана в верхней панели, фильтры в выдвижной панели с кнопкой «Фильтры (2)».
- Карточка заведения на телефоне: шапка со статусом и одним действием, затем вкладки.
- Свёрнутое меню с подсказками при наведении, а лучше не сворачивать до иконок вовсе: NN/g не рекомендует прятать подписи.
Ошибки молчат, обратная связь техническая
Что видно
- Нет глобальной обработки ошибок: при ошибке запроса экраны просто ничего не рисуют. Проверка авторизации показывает голую строку «Загрузка...» и «Нет доступа» красным текстом без оформления.
- Диалоги написаны вручную, без ловушки фокуса и возврата фокуса; клик по подложке закрывает форму «Новое предложение» с потерей введённого.
- Пустые состояния хорошо оформлены, но говорят техническим языком: «Запустите fetch_site», «python -m app.cli import-nomenclature», «Они появятся после fetch_site или обогащения через агрегаторы».
- Ссылка «Заполнить в настройках» в диалоге предложения это обычный
<a href>, перезагружающий приложение. На скрытых экранах удаление подтверждается системнымconfirm(). - Карточка и меню заведения опрашивают сервер каждые 5 секунд бессрочно, список каждые 8; экран «Парсинг сайта» делает правильно и останавливает опрос, когда задач нет.
- Ход разбора виден только на экране «Парсинг сайта»: список решений LLM по страницам. На карточке заведения о том, что идёт разбор, не сказано ничего.
Что сделать
- Единое состояние ошибки для экранов и границу ошибок приложения: что случилось, что сделать, кнопка «Повторить».
- Диалоги на Radix Dialog (уже в зависимостях): фокус, Escape, подтверждение закрытия при несохранённых полях.
- Ход разбора на карточке как контрольный список шагов: «Обход сайта → Распознавание → Раскладка → Матчинг», с временем и текущим шагом; подробный трейс решений LLM свёрнут под «Как искали меню». Это паттерны «динамический чек-лист» и «переключатель рассуждений» из Smashing Magazine.
- Опрос сервера только пока есть активная задача.
- Тексты пустых состояний по трём правилам NN/g: что происходит, чему научить, куда нажать.
Предложения: сильный редактор, слабая обвязка
Что уже хорошо
- Холст шириной A4 с теми же стилями, что в печати; две вкладки документа; автосохранение с индикатором и обработкой конфликта правок; предупреждение об устаревшем меню или прайсе.
- Форма сборки с расчётом до нажатия «Собрать»: сколько блюд, ингредиентов, товаров с ценой по колонке.
- Настройки профиля разбиты на блоки со своим сохранением, как и задумано.
Что видно
- Пересборка каждый раз создаёт новое предложение. За три минуты у одного заведения четыре черновика КП-0011…0014 без пометки, какой актуален. В списке и на карточке они равноправны.
- Колонка «Файлы» в списке пуста у черновиков; для экспортированных две кнопки «письмо» и «подбор» без иконок файла.
- «Скачать PDF» сразу скачивает два файла без предпросмотра. Кнопка удаления это иконка корзины рядом с главной кнопкой.
- Диалог «Новое предложение» из девяти полей; поля менеджера и условий повторяют профиль, хотя меняют их редко.
- В настройках предложения шаблон правится в визуальном редакторе с сырыми плейсхолдерами Jinja; панель из сорока плейсхолдеров без поиска.
Что сделать
- На карточке заведения показывать актуальный черновик отдельно от истории; при пересборке предлагать заархивировать предыдущий.
- В списке предложений группировать по заведению, показывать менеджера и «актуально / устарело».
- Диалог сборки: основные поля сверху, «Менеджер и условия» свёрнуты со значениями из профиля.
- Удаление в меню «…» с подтверждением; предпросмотр PDF во встроенном просмотрщике до скачивания.
- Панель плейсхолдеров с поиском и вставкой по клику остаётся; сырой Jinja прятать за подписями «если заполнено имя» можно позже.
Настройки и справочники говорят языком базы
- Таблица настроек показывает JSON, редактирование в однострочном поле. Для первой версии достаточно формы из трёх групп: Автоматизация, LLM (модель, температура, бюджет), Промпты (многострочные поля).
- «Профиль и шаблон КП» это отдельный экран для администратора, но в навигации его нет: только кнопка на экране настроек.
- Пользователей заводят через Swagger; экрана пользователей нет, хотя роли есть.
- Номенклатура: карточка товара с ценами хорошая. Не хватает фильтра «есть цена по колонке» и ссылки на ингредиент из карточки.
- Ингредиенты: клик по строке уводит в номенклатуру с фильтром, а обратно только крестик в бейдже. Логичнее вкладка внутри Номенклатуры.
Что говорят практики
Десять правил, которые прямо применимы к Хорике. У каждого источник и следствие для нас.
- Навигация по существительным, не по глаголам. Объектная навигация предсказуемее, а её артефакт, карта объектов, совпадает с моделью данных. OOUX (Sophia Prater), Pencil & Paper «Navigation UX for SaaS». Для нас: Заведения, Предложения, Номенклатура, а не Парсинг и Матчинг.
- Названия категорий описательные и непересекающиеся. Общие слова вроде «Работа» и «Данные» не предсказывают содержимое; каждая категория должна иметь своё лицо. NN/g «5 Tips for Avoiding Confusing Category Names». Для нас: Работа / Справочники / Настройки, проверка предсказуемости на двух менеджерах.
- Вертикальное меню слева с текстовыми подписями. Вертикальный список сканируется быстрее, подписи важнее иконок, менее важное вниз. NN/g «Left-Side Vertical Navigation». Для нас: не сворачивать до иконок, справочники ниже рабочих экранов.
- Страница записи: шапка, вкладки, боковая панель. Слева ключевые свойства и действия, в центре вкладки с обзором и деталями, справа связанные записи. HubSpot «Understand the default record layout». Для нас: карточка заведения именно так, с покрытием и КП в шапке.
- Навигация отступает, содержание ведёт. Приглушённый сайдбар, меньше иконок, теплее серые, мягче границы, меньше разделителей. Linear «A calmer interface for a product in motion». Для нас: убрать оранжевый из меню, оставить его действиям.
- Три цвета текста, два веса, де-акцент вместо акцента. Иерархия строится приглушением второстепенного, а не выделением главного. Refactoring UI (Wathan, Schoger). Для нас: шкала 12–24 px, минимум 12 px в таблицах.
- Таблицы: числа справа, табличные цифры, плотность 40–56 px, действия по наведению, сортировка по тому, что требует действия. Pencil & Paper «Data Table Design UX Patterns», Setproduct «Data table UI design». Для нас: список заведений с покрытием и статусом КП, липкая шапка, строка кликабельна.
- Пустое состояние: сообщить статус, научить, дать путь. Подсказка в контексте запоминается лучше обучения на старте. NN/g «Designing Empty States in Complex Applications». Для нас: «Меню не найдено. Загрузите файл или вставьте текст».
- Прозрачность работы ИИ слоями: итог, затем чек-лист шагов, затем сырой лог по клику. Частичный успех показывать отдельно от провала. Smashing Magazine «Practical Interface Patterns for AI Transparency», LandingAI «Human-in-the-loop review workflows». Для нас: ход разбора на карточке, сомнительные строки раскладки подсвечены, «Поправить состав».
- Что видит продавец дистрибьютора о заведении. Кухня, позиции меню, ценовой сегмент, «уже подают ли», под какой продукт есть место, контакт ЛПР; из меню собирают образец заказа. Brizo by Datassential, First Bite, Pepper DSR Connect, Tastewise. Для нас: шапка карточки с кухней, числом блюд и покрытием; вкладка «Подбор товаров» как «образец заказа».
Целевая модель
Навигация и рабочий стол
КП ещё нет →
без экспорта →
~6 мин →
11:48 · ilpatio.ru · меню разобрано, 242 блюда, покрытие 64 %
11:20 · whiterabbitmoscow.ru · идёт поиск меню, 14 страниц
Семь пунктов в трёх группах с говорящими именами. Скрытые экраны оператора (Очередь, Регионы, Прокси) остаются под флагом в группе «Система». Кнопка «Добавить заведение» живёт в шапке списка заведений и принимает ссылку, список ссылок, файл или текст меню.
Словарь и статусы
| Сейчас | Предлагается | Почему |
|---|---|---|
| Организации | Заведения | термин CONTEXT.md; «организация» техническое имя таблицы |
| Парсинг сайта | Добавить заведение (по ссылке, файлом, текстом) | это ввод, а не раздел |
| Матчинг меню | вкладка «Меню и раскладка» на карточке | результат принадлежит заведению |
| КП | Предложения; «Создать КП» в кнопках | CONTEXT.md допускает «КП» как сокращение |
| Stage + Статус, ожидает / resolved / собрано / классиф. / готово; new / in_progress / done | один статус: Новое · Ищем меню · Меню не найдено · Меню разобрано · Есть черновик КП · КП отправлено · Ошибка | выводится из фактов, одинаков на дашборде, в списке и на карточке |
| fetch_site / classify / extract / aggregators / Resolve | Найти меню на сайте · Разметить фото · Распознать позиции · Поискать на агрегаторах · Обновить карточку | действия по смыслу |
| Ассеты; menu / dish / interior / garbage / unknown | Источники меню; Меню · Блюдо · Интерьер · Не меню · Не размечено | язык менеджера |
| Мягкое 18 % / Строгое 8 %; строг./мягк. | Покрытие: точные товары 8 %, с родственными 18 % | подпись объясняет, а не сокращает |
| Score, venue_score, мин. score | убрать из первой версии | не считается в потоке по ссылке |
| термов; Ждут в Redis; E.164; Org | ингредиентов; В очереди; Телефон; Заведение | техника наружу не выходит |
Карточка заведения
| Блюдо | Ингредиенты | Покрытие | Товаров | |
|---|---|---|---|---|
| Паста · 3 блюда | ||||
| Карбонара | макароныбеконсыр пармезаняйцосливки | 6 | ▾ | |
|
сыр пармезан · Сыр Пармезан 6 мес. 32 % · 05.04.30-042 · 1 029,00 ₽/кг · Италия макароны · Паста Спагетти №5 · Barilla · 220,50 ₽/кг бекон: нет в номенклатуре · встречается в 2 блюдах Поправить состав | ||||
| Лазанья | макароныговядинатоматсыр моцарелласоус | 9 | ▸ | |
| Паста с креветками проверить | пастакреветки | 2 | ▸ | |
18 ингредиентов
Точные товары 41 %
С родственными 64 %
сыр пармезан · 4
томат · 2
креветки · 1
соус · 2 блюда
рис · 1 блюдо
Шапка отвечает на три вопроса менеджера до прокрутки. Таблица раскладки читается как меню заведения, а раскрытая строка показывает, чем именно поставщик закроет ингредиент и по какой цене. Строки с раскладкой из лексического правила помечены «проверить»: источник уже хранится в dish_decomposition.source. Вкладка «Источники» получает всё техническое: фото и PDF с разметкой, ход задач чек-листом, трейс LLM под спойлером, кнопки повторного запуска.
Что нужно от API
GET /organizations/{id}/analysis: вынести_compute_matchиз sites.py в ядро и отдавать раскладку по блюдам, сводку, пробелы, расширение. Кэшировать вmenu_summary.GET /organizations/{id}/selection?price_type_id=: переиспользоватьselect.pyиз сборки предложения для вкладки «Подбор товаров».PATCH /dishes/{dish_key}: ручная правка раскладки, источникmanual.- Поле
statusвOrgOut, вычисляемое из позиций, задач и предложений; поляmenu_items_count,menu_score,latest_proposalв списке. - Кнопки для существующего
POST /menu/ingestи дляnotes,cuisine_typesвPATCH.
Список заведений
| Заведение | Кухня | Блюд | Покрытие | КП | Статус | Обновлено |
|---|---|---|---|---|---|---|
| iL Патио ilpatio.ru | итальянская | 242 | 64 % | КП-2026-0009 · экспорт | КП отправлено | вчера |
| Траттория Da Vinci davinci-kursk.ru | итальянская | 10 | 64 % | черновик 0014 | Меню разобрано | 2 ч назад |
| White Rabbit whiterabbitmoscow.ru | — | — | — | — | Ищем меню · 6 мин | сейчас |
| Кафе «Лето» | — | 0 | — | — | Меню не найдено | 3 дня назад |
Предложения
- Список сгруппирован по заведению, у актуального черновика пометка, у остальных «архив». Колонки: номер, заведение, статус, колонка прайса, менеджер, создано, экспорт, файлы с иконками.
- В редакторе: крошки «Заведения › Траттория Da Vinci › КП-2026-0014», удаление в меню «…», предпросмотр PDF перед скачиванием, панель таблиц показывать только внутри таблицы.
- Диалог сборки: «Кому», «Контактное лицо», «Колонка прайса», расчёт; «Менеджер и условия» свёрнуты.
Справочники и настройки
- Номенклатура с вкладками «Товары» и «Ингредиенты», фильтр «есть цена по колонке», из карточки товара ссылка на ингредиент и на заведения, где он используется (эндпоинт есть).
- Кухни как таблица с сортировкой по покрытию; карточка кухни с двумя списками: закрыто и пробелы.
- Контрагенты 1С без изменений по сути; связь с заведением показывать и на карточке заведения.
- Настройки: «Предложение» (профиль, логотип, шаблоны) и «Система» (автоматизация, LLM, промпты) как формы; JSON-таблица только для администратора под спойлером.
Визуальная система
- Цвет. Нейтрали остаются тёплыми. Акцент один и только для главного действия и ссылок; для текста оттенок с контрастом не ниже 4,5:1. Активный пункт меню: нейтральный фон и полужирный. Семантика покрытия: зелёный, янтарный, красный, одинаковые везде, где есть чипы ингредиентов и полоски.
- Текст. Шкала 12 / 13 / 14 / 16 / 20 / 24 px, три цвета текста, веса 400 и 600. Таблицы 13–14 px, подписи не меньше 12. Табличные цифры, числа справа, «540 ₽» вместо «540.00 RUB», даты относительные в списках и полные в карточках.
- Поверхности. Карточка только для отдельных объектов; секции страницы делятся заголовком и отступом. Один радиус, одна тень, у диалогов и всплывающих панелей.
- Плотность. Строки таблиц 40–44 px, липкая шапка, действия строки по наведению, переключатель плотности можно отложить.
- Шрифт. Inter из сборки, а не с внешнего сайта; либо Golos Text, если хочется своего лица с полной кириллицей.
- Бренд. Одно имя «Хорика» в меню, на входе и во вкладке; знак не про ИИ, а про поставку: например, ящик или лист накладной.
Отзывчивость и доступность
- Три точки: до 760 px карточки-строки и выдвижные фильтры; 760–1100 px таблицы с приоритетными колонками; выше полный вид.
- Название экрана в верхней панели на всех ширинах; боковое меню не сворачивается до иконок.
- Диалоги на Radix: фокус, Escape, подтверждение закрытия. Видимый фокус у всех интерактивных элементов, минимальная цель касания 44 px.
- Редактор предложения на телефоне: только просмотр и скачивание с явным сообщением.
План внедрения
Четыре этапа. Каждый заканчивается тем, что можно показать владельцу и проверить на трёх реальных заведениях. Оценки для одного разработчика с готовыми компонентами shadcn.
Этап A · Каркас и язык 1–2 дня
- Навигация: три группы, семь пунктов, одно имя продукта, заголовок вкладки браузера, крошки по правилам, глобальный поиск по заведениям.
- Единый статус заведения на сервере и словарь замен по таблице выше; пустые состояния и тосты по-русски.
- Токены: акцент для текста с контрастом 4,5:1, нейтральный активный пункт меню, минимальный текст 12 px, единые отступы страниц, починенный Select, шрифт в сборке.
- Граница ошибок и состояние ошибки экранов; диалоги на Radix.
Готово, когда: менеджер без подсказки находит заведение, его КП и настройки; ни одного английского слова в интерфейсе первой версии; контраст всех текстов не ниже 4,5:1.
Этап B · Карточка заведения 3–5 дней
- Эндпоинт анализа меню и вкладка «Меню и раскладка» с раскрытием строк и сводкой.
- Вкладка «Подбор товаров» на данных
select.pyпо колонке прайса заведения. - Шапка карточки с одним статусом, покрытием и актуальным черновиком; вкладки «Предложения», «Источники» с чек-листом хода задач и трейсом под спойлером, «Контрагент 1С».
- «Добавить заведение»: ссылка, список, файл, текст; «Обновить меню» на карточке.
- «Поправить состав» блюда с сохранением источника manual; подсветка строк с раскладкой по правилу.
Готово, когда: для ilpatio.ru на одной странице видны блюда, их состав, что закрыто и чем, что нет; от ссылки до черновика КП путь не выходит за карточку.
Этап C · Списки и рабочий стол 2–3 дня
- Список заведений с новыми колонками, чипами-фильтрами, сохранёнными видами, пагинацией и мобильными карточками-строками.
- Рабочий стол из очередей действий и ленты событий; метрики очереди под флаг.
- Список предложений с группировкой по заведению и пометкой актуального черновика.
Готово, когда: утро менеджера начинается с рабочего стола, и каждая цифра на нём ведёт к списку, где можно действовать.
Этап D · Справочники, настройки, шлифовка 2–3 дня
- Номенклатура с вкладкой «Ингредиенты», кухни таблицей, настройки формами, экран пользователей для администратора.
- Тёмная тема на новых экранах, доступность с клавиатуры, предпросмотр PDF.
- Проверка на трёх реальных заведениях с владельцем; замеры времени «ссылка → КП».
Готово, когда: все экраны, включая настройки, выглядят как один продукт, и ни один экран не объясняет себя абзацем из документации.
Приложение: по экранам
| Экран | Серьёзность | Главное |
|---|---|---|
| Вход | мелочь | Имя «catering-ai», подпись «оператор», плейсхолдер you@company.com; форма и фон в порядке. |
| Дашборд | серьёзно | Метрики краулера, воронка по шести записям, мёртвая ссылка на очередь, приветствие по логину. Заменить рабочим столом. |
| Организации | серьёзно | Пустые колонки, два статуса, нет покрытия и КП, семь фильтров, поиск на каждую букву, строка не кликабельна. |
| Карточка заведения | критично | Нет меню, раскладки, покрытия; шесть карточек столбиком; блок «по кухне» при наличии меню; «Resolve»; два бейджа статуса. |
| Меню заведения | критично | Ассеты первыми, технические кнопки, таблица без состава, «540.00 RUB», нет загрузки файлом. |
| Номенклатура | мелочь | Хорошая карточка цен. «без категории» первым чипом, нет фильтра по цене колонки. |
| Ингредиенты | средне | «термов», клик уводит в номенклатуру, обратной навигации нет; логичнее вкладка. |
| Кухни | средне | Стена из 33 красных карточек, нет сортировки и поиска. |
| Кухня | средне | Пустой экран при ошибке 404; блюда «0/0» без объяснения. |
| Матчинг меню | серьёзно | Песочница с примером в коде, не связана с заведением; единственное место с раскладкой; подписи «строг./мягк.». |
| Парсинг сайта | серьёзно | Второй список заведений со своими статусами; пример URL в поле; трейс LLM полезен, но живёт не там. |
| Предложения | средне | Сдвинутые отступы, оторванная стрелка селекта, четыре равноправных черновика, пустые «Файлы». |
| Редактор предложения | хорошо | Холст A4, автосохранение, конфликты. Доработать крошки, удаление, панель таблиц, предпросмотр. |
| Настройки | средне | JSON-таблица и облако ключей; профиль КП спрятан за кнопкой. |
| Настройки предложения | хорошо | Блоки со своим сохранением. Рваная сетка карточек, сырой Jinja в шаблоне, сорок плейсхолдеров без поиска. |
| Контрагенты 1С | мелочь | Понятный экран; иконка «отвязать» без подписи. |
| Очередь, Регионы, Прокси (скрыты) | мелочь | Системный confirm() на удаление, «Retry», короткие ID заведений вместо имён. |
Источники
- Pencil & Paper, Navigation UX Best Practices for SaaS Products. pencilandpaper.io
- Nielsen Norman Group, Left-Side Vertical Navigation on Desktop. nngroup.com
- Nielsen Norman Group, 5 Tips for Avoiding Confusing Category Names. nngroup.com
- Nielsen Norman Group, Breadcrumbs: 11 Design Guidelines. nngroup.com
- Nielsen Norman Group, Designing Empty States in Complex Applications. nngroup.com
- Pencil & Paper, Data Table Design UX Patterns & Best Practices. pencilandpaper.io
- Setproduct, Data table UI design reference guide. setproduct.com
- OOUX, What is Object-Oriented UX. ooux.com
- HubSpot, Understand the default record layout. knowledge.hubspot.com
- Linear, A calmer interface for a product in motion. linear.app
- Adam Wathan, Steve Schoger, Refactoring UI; конспект. gist.github.com
- UXPin, Dashboard Design Principles. uxpin.com
- Smashing Magazine, Practical Interface Patterns for AI Transparency. smashingmagazine.com
- LandingAI, Building Human-in-the-Loop Review Workflows for Document AI. landing.ai
- Brizo by Datassential, How F&B Distributors Can Leverage Menu Data. brizodata.com
- First Bite, Foodservice Sales Platform. firstbite.io
- Pepper, DSR Connect: Sales Engagement for Food Distributors. usepepper.com
- Tastewise, Foodservice Sales Enablement. tastewise.io
- shadcn/ui, Sidebar blocks. ui.shadcn.com
- W3C, WCAG 2.2 Understanding Contrast (Minimum). w3.org