Внутренний инструмент поставщика HoReCa · ветка feature/wave1-foundations

Аудит интерфейса Хорики

Интерфейс собран вокруг шагов конвейера, а не вокруг работы менеджера. Поэтому в нём трудно ориентироваться, а главное, ради чего система существует, разобранное меню заведения и покрытие его номенклатурой, на карточке заведения не показано вовсе.

Дата 7 сентября 2026 Экранов 19, из них 16 видимы в первой версии Стенд локальный, демо-данные Скриншотов 40 Источников 20

Резюме

Фронтенд технически здоров: React, shadcn, TanStack Query, светлая и тёмная темы, скелетоны, тосты, редактор предложения с автосохранением. Проблема не в качестве кода, а в модели: экраны повторяют устройство бэкенда. Меню «Работа» перечисляет десять пунктов подряд, среди них песочница «Матчинг меню» с захардкоженным примером, «Парсинг сайта» с собственным списком тех же заведений и два справочника. Карточка заведения показывает адрес, координаты, «venue_score» и фото, но не показывает ни блюд, ни их состава, ни того, что из этого поставщик может закрыть.

Пять выводов:

  1. Навигация построена по глаголам и внутренностям конвейера. Нужна навигация по объектам: Заведения, Предложения, Номенклатура, Настройки. Три отдельных входа в одно и то же (Организации, Парсинг сайта, Матчинг меню) надо свести к одному.
  2. Раскладка блюд есть в базе и в API, но не на карточке. Таблица dish_decomposition и функция match_menu отдают состав каждого блюда с пометкой, что закрыто, а что нет. Показать это можно без новой математики: нужен один эндпоинт и одна вкладка.
  3. Три системы статусов не совпадают друг с другом. «ожидает / new» у заведения с разобранным меню и четырьмя черновиками КП. Статус должен выводиться из фактов, а не храниться от старого пайплайна.
  4. Язык смешанный. Resolve, Score, Stage, fetch_site, classify, extract, E.164, Redis, «термов», «строг./мягк.». Словарь проекта уже написан в CONTEXT.md, интерфейс его не использует.
  5. Визуальная система не расставляет акценты. Один оранжевый на активный пункт меню, ссылки, кнопки и иконки; контраст оранжевого текста на белом 3,1:1 при норме 4,5:1; всё в одинаковых карточках; текст 10–11 px в 21 месте.
10пунктов подряд в группе «Работа», из них 3 ведут к одним и тем же заведениям
0экранов, где состав блюда виден рядом с заведением
3несовпадающие системы статусов заведения
3,1:1контраст ссылок и активного пункта меню при норме 4,5:1
6функций, которые есть в API, но не имеют интерфейса

Рекомендация: не «перекрасить», а перестроить каркас за четыре этапа (раздел План внедрения). Первый этап, словарь, статусы и навигация, укладывается в один-два дня и уже снимает большую часть жалоб на ориентацию. Второй этап, карточка заведения с вкладкой «Меню и раскладка», закрывает главную содержательную дыру.

Как проверяли

Ограничение: на стенде нет живых распарсенных сайтов, поэтому экран «Парсинг сайта» с трейсом 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 критично

Навигация повторяет конвейер, а не работу менеджера

Боковое меню: «Обзор» с одним пунктом, «Работа» с десятью, «Админ» с одним. Группа «Данные» скрыта флагом. Внутри «Работы» вперемешку рабочие экраны (Организации, КП), справочники (Номенклатура, Ингредиенты, Кухни, Контрагенты 1С), инструмент ввода (Парсинг сайта) и песочница (Матчинг меню). Порядок не отражает ни частоту, ни последовательность работы.

Список организаций с боковым меню из десяти пунктов
Список организаций. Десять пунктов подряд без иерархии. В таблице пустые колонки «Типы» и «Score», две колонки статуса «Stage» и «Статус» с почти одинаковыми значениями «готово / Готово».

Что видно

  • Три входа в одни и те же заведения. «Организации» показывает всё. «Парсинг сайта» показывает только заведения с отпечатком site: и собственную таблицу «лидов» со своими статусами. «Матчинг меню» просит вставить блюда текстом и не связан ни с одним заведением. Демо-заведения видны в первом списке и невидимы во втором.
  • Названия групп общие: «Обзор», «Работа», «Данные», «Админ». По NN/g такие ярлыки не помогают предсказать содержимое, и пользователь перебирает пункты.
  • Названия пунктов это действия и техника: «Матчинг меню», «Парсинг сайта». По Pencil & Paper и OOUX навигация по существительным предсказуемее.
  • Бренд называется «AI, внутренний инструмент» в меню, «catering-ai» на входе и «Хорика» в документации. Вкладка браузера всегда называется «AI», история и закладки бесполезны.
  • Нет глобального поиска. Чтобы найти заведение, надо зайти в список и набрать название в фильтре; товары ищутся на другом экране, предложения на третьем.
  • Свёрнутое меню оставляет десять иконок без подписей. NN/g: «в навигации слово стоит тысячи картинок».
  • Хлебные крошки есть только на карточке и на меню заведения. В редакторе предложения вместо них собственная кнопка «К списку»; на карточке заведения крошки дублируются ссылкой «назад к списку».
  • Ссылка «Смотреть очередь» на дашборде ведёт на маршрут, скрытый флагом, и молча возвращает на дашборд.

Что сделать

  • Три группы по существительным: Работа (Рабочий стол, Заведения, Предложения), Справочники (Номенклатура с вкладками «Товары» и «Ингредиенты», Кухни, Контрагенты 1С), Настройки (Предложение, Система). Семь пунктов вместо десяти, и у каждой группы своё имя.
  • «Парсинг сайта» превратить в действие «Добавить заведение» на экране Заведений: ссылка или список ссылок, файл, текст меню. Ход и результат разбора показывать на карточке заведения, а не в отдельной таблице.
  • «Матчинг меню» перенести на карточку заведения как вкладку «Меню и раскладка». Песочнику для проверки произвольного текста оставить внутри «Добавить заведение → вставить меню текстом».
  • Одно имя продукта во всех местах, заголовок вкладки браузера вида «Траттория Da Vinci · Хорика».
  • Глобальный поиск по заведениям, товарам и предложениям с клавиши Ctrl+K. Для продукта с шестью типами объектов это стандарт (Pencil & Paper, Linear).
  • Крошки на каждом вложенном экране по правилам NN/g: текущий уровень последним и не ссылкой, дублирующие «назад» убрать.
НАХОДКА 2 критично

Карточка заведения не показывает меню, раскладку и покрытие

Карточка это шесть карточек друг под другом: «Контакты и метаданные» (пять полей из шести пустые), «Скоринг и типы площадки» (venue_score «—» и поле ввода типов через запятую), «Предложения», «Что можем поставлять (по кухне) — без меню», «Контрагент 1С», «Фото (0)». Блюд нет. Ингредиентов нет. Того, что закрыто номенклатурой, нет. Кнопка «Меню» уводит на отдельную страницу, где первым блоком идут «Ассеты (0)», а таблица позиций содержит колонки «Описание», «Цена», «Вес» с прочерками и ни одной колонки про состав.

Карточка заведения Траттория Da Vinci во всю высоту
Карточка заведения. Заведение с десятью блюдами и четырьмя черновиками КП имеет статус «ожидает / new». Блок «Что можем поставлять» считается по кухне, «без меню», хотя меню разобрано.
Страница меню заведения: ассеты и таблица позиций
Страница «Меню». Сначала пустые ассеты и три технические кнопки, потом таблица без состава блюд. Цена «540.00 RUB» вместо «540 ₽».

Где данные на самом деле есть

  • Экран «Матчинг меню» умеет показывать раскладку каждого блюда: чипы ингредиентов с тремя состояниями (закрыт точно, закрыт родственным товаром, нет в номенклатуре), долю покрытия, список «уже можем поставлять» и «предложить в меню». Но он требует вставить блюда руками и не знает о заведении.
  • API GET /sites/parse/{org_id} уже вызывает match_menu по позициям заведения и отдаёт те же данные. Сборка предложения делает то же самое и подбирает до трёх товаров под ингредиент с ценой выбранной колонки.
  • Иными словами, вся математика для вкладки «Меню и раскладка» написана. Не написан один эндпоинт карточки и одна вкладка.
Экран Матчинг меню с раскладкой блюд на ингредиенты
Экран «Матчинг меню». Единственное место, где виден состав блюда. Подписи «17 % / 33 % строг./мягк.» не объяснены; пример «Том ям с креветками» зашит в код и показывается каждому.

Что сделать

  • Карточка заведения с вкладками: Обзор, Меню и раскладка, Подбор товаров, Предложения, Источники, Контрагент 1С. Каркас в разделе «Целевая модель».
  • Шапка карточки отвечает на три вопроса менеджера одной строкой: сколько блюд разобрано, какое покрытие, есть ли черновик КП. Рядом главное действие «Создать КП» или «Открыть черновик».
  • Вкладка «Меню и раскладка»: таблица по разделам меню, у каждого блюда ингредиенты чипами с состоянием, полоска покрытия, число товаров. Раскрытие строки показывает товары под каждый ингредиент по колонке прайса заведения. Внизу сводка: «Поставляем сейчас», «Чего нет в ассортименте» с числом блюд, «Расширение меню».
  • Кнопка «Поправить состав» у блюда: модель dish_decomposition уже знает источник manual. Это тот самый контроль человека над выводом LLM, который рекомендуют для извлечённых данных: подтвердить или исправить, а сомнительные строки подсветить.
  • Блок «по кухне без меню» показывать только когда меню действительно нет.
  • Добавить кнопки «Загрузить меню файлом» и «Вставить меню текстом», эндпоинт есть.
НАХОДКА 3 критично

Три системы статусов и смешанный язык

У заведения два хранимых поля статуса от старого конвейера автопоиска: 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» на карточке (в списке она уже скрыта флагом, на карточке осталась).
НАХОДКА 4 серьёзно

Дашборд рассказывает о краулере, а не о продажах

Дашборд: приветствие, три KPI, воронка pipeline, очередь
Дашборд. «Привет, audit 👋», счётчики по базе, «Воронка pipeline», «Очередь» с «Ждут в Redis». Воронка считается по шести последним заведениям, комментарий в коде это признаёт.

Что видно

  • Три показателя: всего заведений, новые, «Готовы к КП» по статусу done. Из-за находки 3 «готовые» посчитаны неверно: 2 вместо 3.
  • «Воронка pipeline» это распределение шести последних заведений по стадиям, а не метрика. Проценты от шести создают ложную точность.
  • Карточка «Очередь» показывает воркеры и Redis, то есть данные оператора, а ссылка «Смотреть очередь» мертва в первой версии.
  • Нет ответа на вопрос «что мне делать сейчас»: у каких заведений меню разобрано, а КП ещё нет; какие черновики не экспортированы; где идёт разбор и сколько ждать; где ошибка.
  • Нет свежести данных: дашборд опрашивает сервер пять раз каждые 8–15 секунд, но не показывает, когда обновлён.

Что сделать

  • Заменить дашборд «Рабочим столом» из очередей действий, каждая ведёт в отфильтрованный список: «Меню разобрано, КП ещё нет», «Черновики без экспорта», «Идёт поиск меню», «Меню не найдено», «Ошибки». Это операционный дашборд по Стивену Фью: 5–9 показателей, каждый с действием.
  • Лента последних событий: заведение добавлено, меню разобрано, КП собрано, КП экспортировано. Источники: crawl_jobs и proposals.
  • Метрики воркеров и Redis оставить на скрытом экране «Очередь».
  • Убрать приветствие по логину и эмодзи; заголовок должен называть экран.
НАХОДКА 5 серьёзно

Списки не помогают выбирать

Что видно

  • Таблица заведений: восемь колонок, из них «Типы» и «Score» пусты у каждой строки в потоке первой версии, «Stage» и «Статус» дублируют друг друга. Нет кухни, нет числа блюд, нет покрытия (хотя menu_score хранится), нет статуса КП, нет даты последнего действия. Менеджеру нечем отличить перспективное заведение от пустого.
  • Семь элементов фильтра в ряд, включая числовое поле «мин. score» и сортировку без подписи. Активные фильтры не выделены; кнопка «Сбросить» появляется только при наличии фильтра.
  • Поиск в списке заведений срабатывает на каждую букву и дёргает сервер, в номенклатуре, ингредиентах и предложениях нужна кнопка «Найти». Два разных поведения для одного действия.
  • Кликабельна только ссылка в названии, остальная строка нет. В номенклатуре и ингредиентах, наоборот, кликабельна строка. Опять два поведения.
  • Пагинация «Назад / Вперёд» без выбора страницы и без размера страницы. Экран «Парсинг сайта» ограничен 200 строками без пагинации вовсе.
  • «Кухни» это стена из 33 карточек, почти все с красной полоской 0–17 %: демонстрирует, чего у поставщика нет, а не что есть. Сортировки, поиска и сводки нет.
  • В номенклатуре первая и самая яркая категория «без категории 8».
Экран Кухни: сетка из карточек с красными полосками покрытия
Кухни. Тридцать три карточки с зачёркнутыми ингредиентами. Как справочник полезен, как первый экран пугает.

Что сделать

  • Колонки списка заведений: Заведение (имя и домен), Кухня, Меню (число блюд), Покрытие (процент и полоска), КП (статус и номер), Обновлено. Числа справа, табличные цифры, строка кликабельна целиком.
  • Фильтры чипами с видимым активным состоянием и сохранённые виды: «Готовы к КП», «В работе», «Без меню». Единое поведение поиска: мгновенно с задержкой 300 мс везде.
  • Сортировка по покрытию по умолчанию для вида «Готовы к КП»: Pencil & Paper советуют сортировать по тому, что требует действия.
  • Кухни: таблица с сортировкой по покрытию, поиском и вкладками «закрыто / пробелы», как справочник внутри Номенклатуры.
  • Пагинация с числом страниц и размером страницы, липкая шапка, плотность строк 40–44 px.
НАХОДКА 6 серьёзно

Визуальная система не расставляет акценты

Тема это shadcn по умолчанию с тёплыми нейтралями и оранжевым amber-600. Основа хорошая, но акцент не экономят: он на активном пункте меню, на каждой ссылке в таблице, на главной кнопке, на иконках KPI, на фокусе. Когда всё оранжевое, ничто не главное. Linear в своём редизайне сформулировал это так: навигация должна отступать, а содержание вести.

Пара цветовКонтрастНорма AAГде
Оранжевый текст на белом3,1:14,5:1ссылки в таблицах, «Все организации →», активный пункт меню
Белый текст на оранжевой кнопке3,1:14,5:1«Создать КП», «Сматчить», «Войти»
Оранжевый на оранжевой подложке 10 %2,7:14,5:1активный пункт бокового меню
Заголовки групп меню, серый 70 %2,6:14,5:1«ОБЗОР», «РАБОТА», «АДМИН»
Серый вторичный текст на белом4,75:14,5:1описания, подписи: норма
Тёмная тема: оранжевый на карточке7,8:14,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.
НАХОДКА 7 средне

Мобильная версия работает, но не для работы

Список заведений на ширине 390 px
Список на телефоне. Семь строк фильтров, таблица из восьми колонок со скроллом внутри карточки.
Карточка заведения на ширине 390 px
Карточка на телефоне. Столбик из шести карточек высотой около 2000 px, главное действие на третьем месте.

Что видно

  • Выезжающее меню и верхняя панель работают. Но в панели нет названия экрана, и после закрытия меню пользователь не знает, где он.
  • Таблицы не перестраиваются: восемь колонок в горизонтальной прокрутке внутри карточки, адрес обрезан.
  • Редактор предложения на 390 px это холст A4 в горизонтальной прокрутке. Здесь достаточно предупредить: правка на телефоне не поддерживается, скачать PDF можно.
  • Свёрнутое боковое меню на десктопе показывает только иконки без всплывающих подписей, кроме системного title.

Что сделать

  • Списки на узких экранах превращать в карточки-строки: имя, кухня, покрытие, статус, одна строка на заведение.
  • Название экрана в верхней панели, фильтры в выдвижной панели с кнопкой «Фильтры (2)».
  • Карточка заведения на телефоне: шапка со статусом и одним действием, затем вкладки.
  • Свёрнутое меню с подсказками при наведении, а лучше не сворачивать до иконок вовсе: NN/g не рекомендует прятать подписи.
НАХОДКА 8 средне

Ошибки молчат, обратная связь техническая

Пустой экран при ошибке 404 на странице кухни
Ответ API 404. Страница кухни остаётся пустой: только кнопка «Все кухни». Ни сообщения, ни причины, ни действия.

Что видно

  • Нет глобальной обработки ошибок: при ошибке запроса экраны просто ничего не рисуют. Проверка авторизации показывает голую строку «Загрузка...» и «Нет доступа» красным текстом без оформления.
  • Диалоги написаны вручную, без ловушки фокуса и возврата фокуса; клик по подложке закрывает форму «Новое предложение» с потерей введённого.
  • Пустые состояния хорошо оформлены, но говорят техническим языком: «Запустите fetch_site», «python -m app.cli import-nomenclature», «Они появятся после fetch_site или обогащения через агрегаторы».
  • Ссылка «Заполнить в настройках» в диалоге предложения это обычный <a href>, перезагружающий приложение. На скрытых экранах удаление подтверждается системным confirm().
  • Карточка и меню заведения опрашивают сервер каждые 5 секунд бессрочно, список каждые 8; экран «Парсинг сайта» делает правильно и останавливает опрос, когда задач нет.
  • Ход разбора виден только на экране «Парсинг сайта»: список решений LLM по страницам. На карточке заведения о том, что идёт разбор, не сказано ничего.

Что сделать

  • Единое состояние ошибки для экранов и границу ошибок приложения: что случилось, что сделать, кнопка «Повторить».
  • Диалоги на Radix Dialog (уже в зависимостях): фокус, Escape, подтверждение закрытия при несохранённых полях.
  • Ход разбора на карточке как контрольный список шагов: «Обход сайта → Распознавание → Раскладка → Матчинг», с временем и текущим шагом; подробный трейс решений LLM свёрнут под «Как искали меню». Это паттерны «динамический чек-лист» и «переключатель рассуждений» из Smashing Magazine.
  • Опрос сервера только пока есть активная задача.
  • Тексты пустых состояний по трём правилам NN/g: что происходит, чему научить, куда нажать.
НАХОДКА 9 средне

Предложения: сильный редактор, слабая обвязка

Что уже хорошо

  • Холст шириной A4 с теми же стилями, что в печати; две вкладки документа; автосохранение с индикатором и обработкой конфликта правок; предупреждение об устаревшем меню или прайсе.
  • Форма сборки с расчётом до нажатия «Собрать»: сколько блюд, ингредиентов, товаров с ценой по колонке.
  • Настройки профиля разбиты на блоки со своим сохранением, как и задумано.
Редактор предложения: письмо на холсте A4
Редактор. Письмо читается как документ. Панель инструментов на вкладке подбора держит восемь неактивных кнопок таблиц; удаление это иконка без подписи рядом с «Скачать PDF».

Что видно

  • Пересборка каждый раз создаёт новое предложение. За три минуты у одного заведения четыре черновика КП-0011…0014 без пометки, какой актуален. В списке и на карточке они равноправны.
  • Колонка «Файлы» в списке пуста у черновиков; для экспортированных две кнопки «письмо» и «подбор» без иконок файла.
  • «Скачать PDF» сразу скачивает два файла без предпросмотра. Кнопка удаления это иконка корзины рядом с главной кнопкой.
  • Диалог «Новое предложение» из девяти полей; поля менеджера и условий повторяют профиль, хотя меняют их редко.
  • В настройках предложения шаблон правится в визуальном редакторе с сырыми плейсхолдерами Jinja; панель из сорока плейсхолдеров без поиска.

Что сделать

  • На карточке заведения показывать актуальный черновик отдельно от истории; при пересборке предлагать заархивировать предыдущий.
  • В списке предложений группировать по заведению, показывать менеджера и «актуально / устарело».
  • Диалог сборки: основные поля сверху, «Менеджер и условия» свёрнуты со значениями из профиля.
  • Удаление в меню «…» с подтверждением; предпросмотр PDF во встроенном просмотрщике до скачивания.
  • Панель плейсхолдеров с поиском и вставкой по клику остаётся; сырой Jinja прятать за подписями «если заполнено имя» можно позже.
НАХОДКА 10 мелочь

Настройки и справочники говорят языком базы

Экран настроек: чекбоксы, известные ключи и таблица JSON
Настройки. Два чекбокса автоматизации, облако «известных ключей» и таблица ключ-значение в JSON. Настройки предложения спрятаны за кнопкой и отсутствуют в меню.
  • Таблица настроек показывает JSON, редактирование в однострочном поле. Для первой версии достаточно формы из трёх групп: Автоматизация, LLM (модель, температура, бюджет), Промпты (многострочные поля).
  • «Профиль и шаблон КП» это отдельный экран для администратора, но в навигации его нет: только кнопка на экране настроек.
  • Пользователей заводят через Swagger; экрана пользователей нет, хотя роли есть.
  • Номенклатура: карточка товара с ценами хорошая. Не хватает фильтра «есть цена по колонке» и ссылки на ингредиент из карточки.
  • Ингредиенты: клик по строке уводит в номенклатуру с фильтром, а обратно только крестик в бейдже. Логичнее вкладка внутри Номенклатуры.

Что говорят практики

Десять правил, которые прямо применимы к Хорике. У каждого источник и следствие для нас.

  1. Навигация по существительным, не по глаголам. Объектная навигация предсказуемее, а её артефакт, карта объектов, совпадает с моделью данных. OOUX (Sophia Prater), Pencil & Paper «Navigation UX for SaaS». Для нас: Заведения, Предложения, Номенклатура, а не Парсинг и Матчинг.
  2. Названия категорий описательные и непересекающиеся. Общие слова вроде «Работа» и «Данные» не предсказывают содержимое; каждая категория должна иметь своё лицо. NN/g «5 Tips for Avoiding Confusing Category Names». Для нас: Работа / Справочники / Настройки, проверка предсказуемости на двух менеджерах.
  3. Вертикальное меню слева с текстовыми подписями. Вертикальный список сканируется быстрее, подписи важнее иконок, менее важное вниз. NN/g «Left-Side Vertical Navigation». Для нас: не сворачивать до иконок, справочники ниже рабочих экранов.
  4. Страница записи: шапка, вкладки, боковая панель. Слева ключевые свойства и действия, в центре вкладки с обзором и деталями, справа связанные записи. HubSpot «Understand the default record layout». Для нас: карточка заведения именно так, с покрытием и КП в шапке.
  5. Навигация отступает, содержание ведёт. Приглушённый сайдбар, меньше иконок, теплее серые, мягче границы, меньше разделителей. Linear «A calmer interface for a product in motion». Для нас: убрать оранжевый из меню, оставить его действиям.
  6. Три цвета текста, два веса, де-акцент вместо акцента. Иерархия строится приглушением второстепенного, а не выделением главного. Refactoring UI (Wathan, Schoger). Для нас: шкала 12–24 px, минимум 12 px в таблицах.
  7. Таблицы: числа справа, табличные цифры, плотность 40–56 px, действия по наведению, сортировка по тому, что требует действия. Pencil & Paper «Data Table Design UX Patterns», Setproduct «Data table UI design». Для нас: список заведений с покрытием и статусом КП, липкая шапка, строка кликабельна.
  8. Пустое состояние: сообщить статус, научить, дать путь. Подсказка в контексте запоминается лучше обучения на старте. NN/g «Designing Empty States in Complex Applications». Для нас: «Меню не найдено. Загрузите файл или вставьте текст».
  9. Прозрачность работы ИИ слоями: итог, затем чек-лист шагов, затем сырой лог по клику. Частичный успех показывать отдельно от провала. Smashing Magazine «Practical Interface Patterns for AI Transparency», LandingAI «Human-in-the-loop review workflows». Для нас: ход разбора на карточке, сомнительные строки раскладки подсвечены, «Поправить состав».
  10. Что видит продавец дистрибьютора о заведении. Кухня, позиции меню, ценовой сегмент, «уже подают ли», под какой продукт есть место, контакт ЛПР; из меню собирают образец заказа. Brizo by Datassential, First Bite, Pepper DSR Connect, Tastewise. Для нас: шапка карточки с кухней, числом блюд и покрытием; вкладка «Подбор товаров» как «образец заказа».

Целевая модель

Навигация и рабочий стол

Хорика
Ctrl+K · поиск
Работа
Рабочий стол
Заведения
Предложения
Справочники
Номенклатура
Кухни
Контрагенты 1С
Настройки
Предложение
Система
Рабочий стол
Очереди действий · обновлено 12:04
7
меню разобрано,
КП ещё нет →
3
черновики
без экспорта →
2
идёт поиск меню
~6 мин →
4
меню не найдено →
1
ошибка →
Последние события
12:01 · Траттория Da Vinci · собрано КП-2026-0014
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ингредиентов; В очереди; Телефон; Заведениетехника наружу не выходит

Карточка заведения

Заведения › Траттория Da Vinci
Траттория Da Vinci
Итальянская · Курск · davinci-kursk.ru · ООО «Да Винчи», ИНН 4632111111
Меню разобрано 10 блюд покрытие 64 % 3 пробела черновик КП-2026-0014
Открыть черновик КПОбновить меню ▾…
ОбзорМеню и раскладкаПодбор товаровПредложения (4)ИсточникиКонтрагент 1С
БлюдоИнгредиентыПокрытиеТоваров
Паста · 3 блюда
Карбонара макароныбеконсыр пармезаняйцосливки
6▾
сыр пармезан · Сыр Пармезан 6 мес. 32 % · 05.04.30-042 · 1 029,00 ₽/кг · Италия
макароны · Паста Спагетти №5 · Barilla · 220,50 ₽/кг
бекон: нет в номенклатуре · встречается в 2 блюдах   Поправить состав
Лазанья макароныговядинатоматсыр моцарелласоус
9▸
Паста с креветками проверить пастакреветки
2▸
Сводка
10 блюд · 1 напиток исключён
18 ингредиентов
Точные товары 41 %
С родственными 64 %
Поставляем сейчас
сыр моцарелла · 4
сыр пармезан · 4
томат · 2
креветки · 1
Чего нет в ассортименте
бекон · 2 блюда
соус · 2 блюда
рис · 1 блюдо
Колонка прайса
01. Цена КП · сменить

Шапка отвечает на три вопроса менеджера до прокрутки. Таблица раскладки читается как меню заведения, а раскрытая строка показывает, чем именно поставщик закроет ингредиент и по какой цене. Строки с раскладкой из лексического правила помечены «проверить»: источник уже хранится в 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.

Список заведений

ВсеГотовы к КП · 7В работе · 2Без меню · 4   Кухня: итальянская ×
Добавить заведение
ЗаведениеКухняБлюдПокрытиеКПСтатусОбновлено
iL Патио
ilpatio.ru
итальянская242
64 %
КП-2026-0009 · экспортКП отправленовчера
Траттория Da Vinci
davinci-kursk.ru
итальянская10
64 %
черновик 0014Меню разобрано2 ч назад
White Rabbit
whiterabbitmoscow.ru
————Ищем меню · 6 минсейчас
Кафе «Лето»—0——Меню не найдено3 дня назад

Предложения

Справочники и настройки

Визуальная система

Отзывчивость и доступность

План внедрения

Четыре этапа. Каждый заканчивается тем, что можно показать владельцу и проверить на трёх реальных заведениях. Оценки для одного разработчика с готовыми компонентами 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 заведений вместо имён.

Источники

  1. Pencil & Paper, Navigation UX Best Practices for SaaS Products. pencilandpaper.io
  2. Nielsen Norman Group, Left-Side Vertical Navigation on Desktop. nngroup.com
  3. Nielsen Norman Group, 5 Tips for Avoiding Confusing Category Names. nngroup.com
  4. Nielsen Norman Group, Breadcrumbs: 11 Design Guidelines. nngroup.com
  5. Nielsen Norman Group, Designing Empty States in Complex Applications. nngroup.com
  6. Pencil & Paper, Data Table Design UX Patterns & Best Practices. pencilandpaper.io
  7. Setproduct, Data table UI design reference guide. setproduct.com
  8. OOUX, What is Object-Oriented UX. ooux.com
  9. HubSpot, Understand the default record layout. knowledge.hubspot.com
  10. Linear, A calmer interface for a product in motion. linear.app
  11. Adam Wathan, Steve Schoger, Refactoring UI; конспект. gist.github.com
  12. UXPin, Dashboard Design Principles. uxpin.com
  13. Smashing Magazine, Practical Interface Patterns for AI Transparency. smashingmagazine.com
  14. LandingAI, Building Human-in-the-Loop Review Workflows for Document AI. landing.ai
  15. Brizo by Datassential, How F&B Distributors Can Leverage Menu Data. brizodata.com
  16. First Bite, Foodservice Sales Platform. firstbite.io
  17. Pepper, DSR Connect: Sales Engagement for Food Distributors. usepepper.com
  18. Tastewise, Foodservice Sales Enablement. tastewise.io
  19. shadcn/ui, Sidebar blocks. ui.shadcn.com
  20. W3C, WCAG 2.2 Understanding Contrast (Minimum). w3.org