Commit Graph
57 Commits
Author SHA1 Message Date
tremble7681andClaude Fable 5 b3e5d05849 feat(tyres): доступ к выдаче резины по праву склада, не только админам
Плитка «Выдача резины» и маршруты /wh/tyres* открываются по признаку
warehouse:tyres из /me (или is_admin) — новый гейт RequireTyres вместо
RequireAdmin. Приёмка/инвентаризация остаются админскими. Бэкенд: право
warehouse.tyres в crm2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 16:11:17 +10:00
tremble7681andClaude Opus 5 8deddf2630 feat(tyres): на машине с комплектом — выбор «доложить» или «заменить»
Раньше экран требовал закрыть старый комплект (вернули / списали / не найден),
даже когда старые колёса остаются на машине и добавляется одно новое. Теперь
развилка: доложить — комплект тот же, колёса прибавляются; заменить — прежний
порядок с закрытием.

В шапке комплекта видно, сколько и когда доложили. Подпись кнопки следует
выбору: «Доложить 1 покрышку» вместо «Выдать комплект».

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:48:53 +10:00
tremble7681andClaude Opus 5 771558a551 feat(tyres): резину можно выдавать поштучно — 1, 2, 3 или 4 покрышки
Задача 4684. Выбор был 2/4/5 и не покрывал самый частый несезонный случай:
порезали боковину, поймали грыжу — в замену уходит одно-два колеса, а не
комплект. Теперь 1-4.

Подпись кнопки перестала врать: «Выдать комплект» осталась только для четырёх,
на меньшем количестве — «Выдать 1 покрышку» / «Выдать 2 покрышки».

Вариант «5» (комплект с запаской) убран: задача перечисляет 1-4, а выдач за
время жизни раздела не было ни одной — защищать нечего. Вернуть — одна строка.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:24:44 +10:00
tremble7681 2a58c5ca94 feat(tyres): по умолчанию только подходящая резина, «Показать все» разворачивает
Кладовщик у подъёмника выбирал размер из девятнадцати позиций глазами. Теперь
экран показывает только подходящее выбранной машине (по подтверждённой
применимости), с счётчиком и кнопкой-переключателем на полный список.

Если по модели применимость не отмечена, фильтровать нечем — показываются все
размеры и объясняется, почему: прятать правильную резину по незнанию хуже, чем
показать лишнюю.

Плашка на строке различает «подходит» (применимость) и «штатный» (справочник
размеров), и предвыбор позиции теперь учитывает оба признака.
2026-08-13 20:10:39 +10:00
tremble7681 064b02e735 feat(tyres): экран сезонной выдачи резины, справочник размеров и журнал
Осенью через кладовщика проходит весь парк, поэтому экран построен вокруг
скорости: выбрал машину — приложение подсказало штатный размер модели, показало
остаток и контейнер, три кнопки состояния, выдал. Контекст приходит одним
запросом: телефон у подъёмника работает по слабой связи, и три round-trip'а на
каждую машину заметны на четырёхстах машинах.

Пока не сказано, что случилось с предыдущим комплектом (вернули / списали / не
найден), кнопка выдачи заблокирована. Сервер это тоже проверяет — здесь просто
не даём дойти до отказа.

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

Справочник размеров подсказывает те размеры, что реально лежат на складе:
набирать «225/65/17» с телефона долго и легко ошибиться. Модели отсортированы по
числу машин — самые ходовые заполняются первыми.

Разделы админские, как приёмка и инвентаризация.
2026-08-13 08:41:28 +10:00
tremble7681andClaude Opus 5 39176d2694 feat(инвентаризация): находку можно поправить и убрать до проведения
Механик ошибся в количестве при пересчёте — раньше единственным выходом было
бросить инвентаризацию и начать заново. Теперь строку находки можно исправить
или удалить, пока пересчёт в черновике.

Правка идёт через тот же фасад склада, что и остальные складские действия
приложения, — отдельного пути в обход правил остатков не появилось.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 06:53:29 +10:00
tremble7681andClaude Opus 5 58c9a2ca1e feat(repairs): пробег видно прямо в списке ремонтов
Задача CRM 4386: «набрал номер авто, смотрю историю ремонтов по машине,
а пробеги не вижу сразу, нужно проваливаться в ремонт».

Пробег бэкенд отдавал в строке ленты и раньше — лента его просто не забирала.
Теперь он стоит справа на строке механика: так пробеги выстраиваются в колонку
и историю одной машины можно читать сверху вниз, не открывая каждый ремонт.

Показываем ровно тот пробег, что зафиксирован в самом ремонте. Он и есть
старлайновский: форма создания подставляет его из последнего снимка StarLine,
механик только подтверждает (если сервис недоступен — вводит руками).
Подставлять в историю ТЕКУЩИЙ пробег из StarLine нельзя: у ремонта полугодовой
давности он показал бы сегодняшнее число.

Когда пробега нет — не пишем ничего. У ремонтов из импортированного архива он
есть в 2% случаев, и подпись «пробег не записан» в каждой второй строке была бы
шумом. У ремонтов, заведённых в приложении, пробег заполнен в 72% случаев.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 23:31:50 +10:00
tremble7681andClaude Opus 5 3cc9ffb3e6 feat(warehouse): итоги проведённой инвентаризации вместо тупика
Владелец: «чтобы по клику по инвентаризации я видел не просто информацию о том,
что я не могу её поменять, а итоги. Какие позиции найдены, какие списаны».

Клик по проведённой сессии открывал тот же экран обхода со списком,
отфильтрованным на «только непосчитанные». У проведённой посчитано всё, поэтому
список оказывался пуст, а сверху висел баннер «правки закрыты»: экран показывал
ровно то, чего не просили, и прятал всё остальное.

Теперь отдельный экран итогов: шапка (зона, кто и когда провёл, счётчики,
недостача и излишек в штуках и рублях) и список группами — не нашли, не хватает,
излишек, находки; «сошлось» и «не дошли» свёрнуты числом, чтобы не листать мимо
совпавших строк. Только просмотр.

Числа на этом экране решают, поэтому отдельно закрыто:

- «Всё сошлось — расхождений не было» показывалось при любом числе
  НЕПОСЧИТАННЫХ позиций. Механик обошёл одну из ста, провёл — и сверху документа
  зелёным «зона чистая». Теперь это отдельное предупреждение: расхождений среди
  посчитанного нет, но обойдено не всё.
- Шапка считается по тем же строкам, из которых собираются секции, а не берётся
  готовым агрегатом с бэкенда — сходимость по построению, а не по совпадению
  определений. «Недостача» и «Излишек и находки» названы так, чтобы не совпадать
  с именем секции ниже: одноимённость заставляла сверять и не сходиться в разы.
- Позиции без цены партии больше не выпадают из денег молча — помечены в строке,
  и в шапке сказано, что реальная сумма больше.
- «Обошли X из Y» больше не включает находки: раньше они завышали долю
  обойдённого и противоречили чипу «не дошли» в той же шапке.

Идущая инвентаризация не тронута: ввод количества, «Сходится», «Нет», находки и
проведение работают как раньше.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 23:13:14 +10:00
tremble7681andClaude Opus 5 5a65f8f1ca feat(repairs): поле поиска прямо в ленте ремонтов
Владелец: «не хочу ещё один раздел, хочу чтобы в списке ремонтов просто
появилось поле поиска». Поле встроено в шапку существующего экрана /repairs,
нового раздела и роута нет.

Поиск СЕРВЕРНЫЙ, и это принципиально. Лента грузится курсором по 20 строк:
фильтрация «по уже загруженному» на старте видела бы последние 20 ремонтов
парка и честно отвечала «ничего не найдено» на машину, которая была в ремонте
позавчера. Отсутствие поиска хотя бы не врёт. Вторая ловушка того же варианта:
маркер бесконечной прокрутки стоит сразу под списком, и схлопнутый фильтром
список подтянул бы его в кадр — лента начала бы молча выкачивать всю таблицу
по 20 строк за запрос.

- q уходит на КАЖДОЙ странице и входит в ключ кэша: смена запроса создаёт свой
  список со своим курсором, поэтому курсор нефильтрованной ленты физически не
  может подмешать чужие ремонты во вторую страницу найденного.
- Во время набора на экране остаётся прошлая выдача (приглушённой), а не
  пульсирующие скелетоны на каждое нажатие клавиши.
- «Обновить» перезапрашивает одну страницу вместо всех загруженных: у
  бесконечного запроса refetch тянет их последовательно, и на слабой сети это
  десять round-trip'ов подряд с десятью шансами словить ошибку.
  При этом во время обновления догрузка молчит — иначе схлопывание ленты
  подтягивает маркер в кадр, fetchNextPage отменяет летящий запрос обновления,
  и новые ремонты сверху не появляются, сколько ни жми.
- «Ничего не нашли по этому запросу» отличается от пустой ленты, а сбой сети —
  от ответа сервера с ошибкой: совет «проверьте сеть» на 500-ку отправляет
  механика перезагружать вайфай, когда чинить у него нечего.

Строка ремонта, дневные разделители, месячный итог и поведение ленты без
поиска не менялись.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 22:04:02 +10:00
tremble7681andClaude Opus 5 b7302c23d6 feat(inspection): кнопка «Продолжить осмотр» на автозакрытом
Бэкенд раз в час закрывает осмотры, которые несколько часов никто не трогает,
— иначе снятые фото не попадают в отчёты. Редактирование в приложении завязано
на статус, поэтому механик, вернувшийся с перерыва, упирался бы в
заблокированную карточку и заводил осмотр заново.

На автозакрытом (meta.auto_completed) показываем плашку с объяснением и кнопку:
статус возвращается в in_progress, редактор открывается снова.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 12:51:35 +10:00
tremble7681andClaude Opus 4.8 bbaef9bc7d fix(repairs): количество запчасти можно поменять — «залипшая единица» (4123)
Поле количества приводилось к минимуму на КАЖДЫЙ onChange
(`Math.max(1, Number(v))`), поэтому стирание единицы мгновенно возвращало
её обратно: набрать «4 свечи» было нельзя — цифра дописывалась и выходило
«14». Ровно этот же баг однажды чинили в цене работы (3b7b2ae), там ""
уже разрешено как промежуточное состояние стейта.

- общий QtyStepper: "" остаётся в стейте, пока печатают; приведение — на
  blur и ещё раз на отправке (пустое = 1 шт: строка есть, значит деталь
  ставили);
- тап по полю выделяет число — набор поверх заменяет, а не дописывает;
- кнопки −/+: 4 свечи набираются одним пальцем в перчатках, мобильная
  цифровая клавиатура для этого слишком долгая;
- то же в форме правки ремонта — там я повторил ту же ошибку при переводе
  запчастей на складской каталог;
- регрессионные тесты на стирание/blur/степпер: баг повторился второй раз
  на том же паттерне, пусть дальше ловится сам.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 16:16:43 +10:00
tremble7681andClaude Opus 4.8 4c1df109ca wh: карточка проведённого прихода в разделе «Приёмка» (чтение)
Строка в «Последних приходах» стала кликабельной, после проведения
накладной открывается сразу она — видно, что именно получилось.

Показываем снимки на момент приёмки (имя, артикул), а не текущие значения
каталога: накладная — исторический документ, переименование номенклатуры
не должно её переписывать. Единственное «сегодняшнее» поле — сколько от
партии ещё лежит на складе: лежит целиком / осталось N / разошлось.

Правки нет намеренно: проведённый приход уже стал партиями и движениями,
и «поправить» его значило бы разъехаться с журналом — сторно делается в
CRM, где видны последствия. В карточке это сказано текстом, чтобы не
выглядело как недоделка.

Заодно после проведения инвалидируются списки черновиков и приходов —
иначе при возврате в раздел они показывались из кэша устаревшими.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 15:29:39 +10:00
tremble7681andClaude Opus 4.8 26c0261784 wh: разделы «Приёмка» и «Инвентаризация» в PWA (админам, мобильная вёрстка)
Приёмка. Главное действие — снять накладную/чек камерой; несколько листов
уходят одним документом. Кадр ужимается до 2200 px на клиенте: сервер всё
равно приводит к этому размеру, а на мобильном интернете в гараже разница
в секундах загрузки заметная. Распознанное проверяется рядом с фото
документа — строки правятся на месте, поставщик выбирается или заводится
прямо с накладной. «Провести» приходует товар в выбранную зону.

Инвентаризация. Запуск спрашивает зону: весь склад одной сессией — это
сотни строк, которые никто не обойдёт за раз. На обходе по умолчанию видны
только непосчитанные, посчитанная сразу уходит из списка. Пустое поле —
«ещё не считали», ноль вводится кнопкой «Нет» (это уже недостача). Находки
подбираются из складского каталога общим пикером. Внизу постоянно висят
недостача и излишек в штуках и рублях.

Доступ: плитки в меню и маршруты закрыты RequireAdmin (тот же гейт на
сервере — клиентская проверка нужна, чтобы прямая ссылка не открывала
экран с ошибкой вместо «вам сюда нельзя»).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 15:23:06 +10:00
tremble7681andClaude Opus 4.8 2c02d15a10 repairs(pwa): восстановлены детали/правка/лента с деньгами + слияние со складом
Прод mechanic-pwa собирался с ветки feat/driver-pwa, а main отставал на 18
коммитов (24.05→10.06). Сборка от 29.07 ушла в прод с main — из приложения
пропали разделы. Вернул содержимое ветки и заново наложил работу 29.07.

Возвращено:
- лента: сумма за месяц, разделители по дням с итогом дня, цена в строке,
  бейдж «с улицы»;
- карточка ремонта /repairs/:id (ссылка из ленты вела в никуда);
- правка ремонта /repairs/:id/edit (окно 1 час);
- ремонт «с улицы» /repairs/new-external;
- блок «Механики» — делёж оплаты 1/N;
- vitest-харнесс и вынесенные в lib/ тестируемые хелперы.

Поверх наложено (работа 29.07): несколько замен масла за заезд, запчасти из
складского каталога вместо свободного текста.

Дополнительно:
- пикер деталей вынесен в общий PartsPickerModal (создание + правка). Без
  этого правка ремонта отправляла детали без part_id и сверка склада
  возвращала всё списанное на остаток;
- форма правки знает про 1:N масла — раньше схлопнула бы вторую запись;
- карточка ремонта показывает артикул детали и все замены масла.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 11:53:02 +10:00
tremble7681andClaude Opus 4.8 226b1ca6c1 repairs: детали из складского каталога вместо свободного текста
Блок «Запчасти» — модалка-пикер по складскому каталогу (searchParts):
поиск по названию/артикулу, остаток и пометка «подходит», тумблер
«Показать все» (снимает фильтр применимости). Выбор шлёт part_id —
бэкенд списывает деталь со склада. Свободный ввод убран.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 19:16:26 +10:00
tremble7681andClaude Opus 4.8 ce4b50109a repairs: несколько замен масла за заезд (двигатель + коробка)
Блок «Замена масла» теперь мульти: чекбоксы Двигатель / Коробка (можно оба),
общий одометр (одна замена — один пробег). Отправляем список oil_changes
(1:N), стикер прикрепляем к первой выбранной. Тип 'transmission' объединяет
АКПП/вариатор/робот — интервал по модели в отчёте СТО.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 13:37:38 +10:00
tremble7681andClaude Opus 4.8 3c27b794dd repairs: тип масла в блоке замены (двигатель/вариатор/АКПП)
Для отчётов СТО по замене масла (задача 4078) нужно различать двигатель и
трансмиссию. В блоке «Замена масла» добавлен выбор типа oil_type
(engine|cvt|akpp) и он уходит в payload create. Для вариатора/АКПП поле
«следующая через (км)» скрыто — интервал задаётся по модели в отчёте СТО.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 13:12:08 +10:00
tremble7681andclaude-flow a5bd7308b5 feat(sto): activate Repair section in PWA (PR-4..PR-7)
Активация раздела «Ремонт» в Premium Мехник PWA: лента + создание ремонта,
фоновая сверка прав (У35), различающие сообщения при отказе входа (У36).

Backend для всех новых API в taxi-dashboard (отдельный commit eccaebe).
Тулится на `ENABLE_STO_MODULE=true` в backend env.

PR-4: Login + useMeSync.
- src/api/me.ts: тип Me расширен (is_admin, can_login_pwa, departments,
  support — для У35/У36).
- src/hooks/useMeSync.ts: фоновая сверка `/me` раз в 5 мин (У35). При
  403 NO_PWA_ACCESS — сброс токена, revocation info в sessionStorage.
  IndexedDB-черновики не трогаются.
- src/pages/Login.tsx: после успешного login делает getMe() и при 403
  показывает баннер «У вас нет доступа» с кликабельным TG/телефонным
  контактом (У36). Баннер также рендерится при revocation после
  фоновой сверки.
- src/App.tsx: useMeSync() подключён в корне.

PR-5: лента ремонтов.
- src/pages/ActionMenu.tsx: снят `comingSoon` с тайла «Ремонт».
- src/pages/repairs/RepairsFeedPage.tsx: лента с infinite scroll (через
  IntersectionObserver), плейсхолдер «В парке пока нет ремонтов» (У41),
  ellipsis-имена работ + плюрализация «+N работ» (У40).
- src/hooks/useRepairsFeed.ts: useInfiniteQuery + stale-while-revalidate
  + visibility refresh (У38).
- src/lib/timeFormat.ts: формат «Сегодня / Вчера / 23 мая» (У39).
- src/api/repairsFeed.ts: GET /api/v1/mechanic/repairs клиент.

PR-7: создание ремонта (online-only).
- src/api/repairsCreate.ts: searchCars, getCarContext, searchWorks,
  suggestParts, presignPhoto + uploadPhotoToTmp (PUT с правильным
  Content-Type), createRepair (POST с Idempotency-Key).
- src/pages/repairs/CarSelectPage.tsx: поиск машины с debounce 200мс.
- src/pages/repairs/CreateRepairPage.tsx: карточка создания ремонта с 5
  inline-блоками (Шапка / Пробег / Фото / Работы / Запчасти / Замена
  масла). UUID v4 как Idempotency-Key. Карандашик-разблокировка цены
  с обязательным комментарием (У19). Мягкая подписка пробега масла
  на шапочный (У24). Дефолт next_in_km=10000 (У25). Полноэкранная
  модалка поиска работ.

Routes: /repairs, /repairs/new, /repairs/new/:carId.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-24 00:16:44 +10:00
tremble7681andclaude-flow 9b4aa504ec feat(inspection): show general photo slots on приёмка (return) too
Previously slotsForType excluded all general slots for return-type
inspections and left only Домкрат, on the assumption that the car is
already familiar and приёмка should focus on damages/equipment. Feedback
from mechanics: при приёмке всё равно нужно фиксировать общее состояние
машины, иначе споры с водителем теряют доказательную базу. Now все типы
осмотров показывают одинаковый набор слотов.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 12:50:05 +10:00
tremble7681andclaude-flow 9b991a244b feat(mechanic): update search placeholder to госномер only
Backend search now matches only on plate, so reflect that in the input
placeholder instead of misleading 'Поиск по номеру или VIN'.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 12:10:30 +10:00
tremble7681 ed16b89353 inspection-editor: блок «Заметки механика» поднят под схему
Раньше textarea для заметок жил в самом низу страницы, после фото-сетки
и списка меток — на плановом осмотре приходилось много скроллить чтобы
добраться до основного поля ввода.

Перенёс Card 'Заметки механика' сразу под Card 'Схема'. На плановом осмотре
текстовое поле теперь второе на странице — не надо листать.
2026-05-20 15:39:01 +10:00
tremble7681 4c03f17448 inspection-editor: блок «Заметки механика» в форме нового/in-progress осмотра
Раньше textarea для заметок жил только в InspectionReview (завершённый
осмотр). При создании нового планового осмотра (через '/inspections/<id>/edit')
поля ввода не было — приходилось завершить осмотр, потом открыть карточку
и только там вводить.

Теперь блок есть в обеих страницах:
  - InspectionEditor (in_progress): textarea + кнопки Сохранить/Отменить
  - InspectionEditor (completed):  read-only вывод текста
  - InspectionReview: оставлен прежний редактор как был

Бонус: при нажатии «Завершить осмотр» текущий draft заметки коммитится
тем же PATCH вместе со status=completed — не теряется если механик
ввёл текст и сразу нажал завершение, не сохранив отдельно.
2026-05-20 15:35:05 +10:00
tremble7681 98806d1e92 vehicle list: цветной чип статуса (На линии / ДТП / Ремонт / Бронь)
VehicleSummary.status (cars_v2.element_status) рендерится как цветной
pill справа от гос.номера. Палитра по семантике:
  emerald — На линии (норма)
  amber   — Ремонтируется / Ждет ремонта
  red     — ДТП
  blue    — Бронь / Выкуп / На продаже / Подготовка на продажу / Ждет
            доставки до офиса (admin-операции)
  muted   — Простой и неизвестные статусы

Чип появляется в Home (списке машин) и в шапке VehicleCard. Backend
сторона — PremiumCRM 92d4317.
2026-05-20 15:17:15 +10:00
tremble7681 577ec37a2f vehicle-card: показ водителя под vendor states (Element history)
TtStateSummary.driver_id + driver_name добавлены в TS-тип. В строке
каждого Element-state'а под датой/пробегом теперь выводится '👤 ФИО'
если backend подтянул водителя. Логика идентична нашим осмотрам — общий
helper drivers_at_dates на backend (см. PremiumCRM aa55431).
2026-05-20 14:53:11 +10:00
tremble7681 71783f3724 vehicle-card: показ водителя под каждым прошлым осмотром
Под датой/пробегом каждого осмотра добавлена строка «👤 ФИО» если backend
вернул driver_name (рассчитан как rental, активный на момент started_at —
см. PremiumCRM 4700314). Раньше механик/менеджер не мог быстро понять, у
кого мы принимали машину — приходилось открывать VDS-карточку.

InspectionSummary.driver_id + driver_name добавлены в TS-тип.
2026-05-20 13:56:09 +10:00
tremble7681 d208838bb3 inspection-review: карточка машины + редактируемые заметки
1. Сверху на /inspections/<id> добавлена кликабельная карточка машины:
   гос.номер крупно + марка/модель/год + VIN. Использует новое поле
   ins.vehicle, которое backend теперь возвращает в InspectionDetail
   (см. PremiumCRM коммит a9c94d0). Клик ведёт на /vehicles/<id>.

2. Блок «Заметки механика» — textarea с локальным draft'ом и кнопкой
   «Сохранить» (видна только если draft отличается от ins.notes). Под
   капотом — PATCH /api/v1/mechanic/inspections/<id> с {notes}.
   TG-импортированные осмотры из «Подготовка авто» уже приезжают с
   заполненными notes (caption сообщения становится телом заметки), и
   теперь механик может дополнить/исправить их прямо в карточке.

3. Бонус: добавлен показ пробега (mileage) в первой info-карте.
2026-05-20 13:34:10 +10:00
tremble7681 c7c989d886 inspection-review: TT-фото только в секции «Метки», не дублировать в галерее
Counter «Фото (N)» по-прежнему учитывает TT carry-over (поэтому на 84-м
осмотре всё ещё 16), но карточки в гриде остаются только для наших MinIO-
фото + PWA-маркерных фото. TT-thumbnail рисуется один раз — inline под
своим маркером в списке.
2026-05-20 13:16:21 +10:00
tremble7681 b4e0dd6d65 inspection-review: TT-фото повреждений, счётчик и lightbox для всех маркеров
1. Photo counter теперь учитывает уникальные tt_image_id из markers, не только
   ins.photos + photo_id. На осмотрах, где почти все маркеры пришли из Element
   carry-over, счётчик показывал 1 вместо 15+ — теперь корректно.

2. В галерее «Фото» добавлена секция карточек для TT-image carry-over (label
   «из Element»). Картинки тянутся через ttImageUrl(hash), кликабельны.

3. Lightbox state поменялся с `number | null` на дискриминированный union
   { kind: 'photo', id } | { kind: 'tt', ttId }. Полноразмерное превью
   выбирает между AuthImg (наши MinIO-фото) и обычным <img> (Element).

4. Превью маркеров в секции «Метки» теперь кликабельны (раньше только грид
   с основными фото открывал lightbox). Работает и для photo_id, и для
   tt_image_id маркеров.

Backend-зависимость: MarkerOut.tt_image_id (Pydantic) и
MechanicDamageMarker.tt_image_id (ORM) добавлены в PremiumCRM коммитом
d405e17 на crm.pptaxi.ru — без них фронт получает tt_image_id=undefined.
2026-05-20 13:11:04 +10:00
tremble7681 7e2cf0c9c8 wip(mechanic): inflight damage-flow + vendor scheme + PhotoAnnotate + TT views
Маркер «вот где остановилась mechanic-сессия» — не разбито по логическим коммитам,
зафиксировано целиком чтобы не потерять при отвлечении/чекаутах.

Modified:
- pages/InspectionEditor.tsx — расширение damage-flow внутри редактора
- pages/VehicleCard.tsx — vendor data + расширение карточки
- pages/InspectionReview.tsx — обновлённый review
- api/{auth,inspections,photos,vehicles}.ts — новые endpoints для TT + photo flow
- components/damage-flow/DamageClassifyDialog.tsx — multi-step UI
- components/vehicle-scheme/VendorVehicleScheme.tsx — SVG zone interaction
- data/damageVocabulary.ts — обновлённый словарь повреждений
- frontend index.html/manifest/icons — PWA-метаданные

New (untracked):
- components/damage-flow/PhotoAnnotateStep.tsx — новый шаг flow'а
- components/tt/ — vendor TT-state UI
- pages/TtStateDetail.tsx — экран осмотра вендора
- recon/findings/damage_model.md + damage_vocabulary.json + scheme/ — recon docs

Cleanup: удалены bash-garbage 'Last' и 'dict[str' из frontend/.
2026-05-20 11:16:21 +10:00
tremble7681 e0083b894f fix(damage-flow): render points as SVG circles inside rotated <g> for exact tap alignment (eliminate HTML overlay letterboxing offset) 2026-05-18 00:53:06 +10:00
tremble7681 abdf0a2dea fix(damage-flow): rotation round-trip (store original-frame coords, display in rotated view) 2026-05-18 00:43:29 +10:00
tremble7681 56f1a2774b feat(damage-flow): rotate zoomed detail view to natural orientation (90/-90/180 per zone) 2026-05-18 00:25:59 +10:00
tremble7681 924dd7966a fix(damage-flow): precomputed zone bboxes (avoid getBBox timing); inline SVG viewBox in HTML so first paint already zoomed 2026-05-18 00:00:31 +10:00
tremble7681 102a2525db feat(mechanic-pwa): multi-step damage flow (confirm -> zone detail -> classify -> photo) 2026-05-17 23:45:34 +10:00
tremble7681 8893710254 feat(mechanic-pwa): action menu + vehicle list at /vehicles + mileage dialog + photo icon 2026-05-17 22:13:04 +10:00
tremble7681 ab9ddf7289 feat(mechanic-pwa): use vendor SVG scheme with zone-based markers (1:1 vendor coords for future import)
- Add public/scheme/exterior.svg (131 KB, viewBox 0 0 827 1209, 57 zones class="0"..class="56")
- Add public/scheme/salon.svg (53 KB, Phase 2 reserve)
- New VendorVehicleScheme: fetches SVG as separate asset (?url), strips vendor <script>,
  wires click/hover via event delegation, tints zones red/gray/blue by damage state
- AddMarkerDialog: side required, x/y optional (zone-level markers store NULL x/y)
- InspectionEditor/InspectionReview: swap CompositeVehicleScheme -> VendorVehicleScheme,
  pendingTap now {side} only, humanizeMarkerSide handles zone-{N} and legacy named zones
- Delete CompositeVehicleScheme (PNG grid + bumper suns)
- No backend changes; side=zone-{N} accepted by existing Optional[str] field
2026-05-17 20:18:46 +10:00
tremble7681 bd7665fb1d docs: update repo reference taxi-dashboard -> premium-crm 2026-05-17 19:48:48 +10:00
tremble7681andclaude-flow df709c58e5 feat(mechanic-pwa): composite unfolded scheme + tire wear dialog (replaces 5-tab view)
Single-screen grid layout showing all 5 PNG views + 4 bumper corner sun-zones + decorative
wheels at once. Tap any view for precise AddMarkerDialog; tap bumper corners for center-point
marker; global Tire Wear button opens new TireWearDialog (4-level radio cards).
Deletes MultiViewVehicleScheme. No backend changes. humanizeMarkerSide covers bumper-* and tires.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 19:41:13 +10:00
tremble7681andclaude-flow 3c6dacb29b feat(mechanic-pwa): multi-view vendor scheme, exact tap coords, per-marker photo upload
- Replace SVG sedan zone-click model with 5-view PNG scheme (top/front/back/left/right)
- New MultiViewVehicleScheme: tab selector, tap-to-exact-coord marker placement,
  touchAction:manipulation prevents iOS double-tap zoom, marker dots at normalized x/y
- New AddMarkerDialog: bottom-sheet modal with damage_type (16 opts), severity (4 opts),
  description, optional CameraCapture → uploadPhoto(side="free") → photo_id on submit
- InspectionEditor: removes addZoneMarker mutation and zone-count machinery; wires
  pendingTap state → dialog open/close → query invalidation
- InspectionReview: adds read-only MultiViewVehicleScheme; both pages show photo_id
  thumbnails via AuthImg in the marker list
- humanizeMarkerSide updated for new view names; keeps zone-* legacy mapping
- Deletes VehicleScheme.tsx and sedan-top.svg (no remaining imports)
- No backend changes, no new dependencies

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 12:21:06 +10:00
tremble7681andclaude-flow 7775e74108 feat(mechanic-pwa): 2FA flow with trusted-device cookie support
Wire frontend to backend 2FA contract: two-step state machine in Login.tsx
(credentials → totp), new login2fa() call to POST /api/auth/2fa, credentials:include
on both fetches so the 30-day trusted_device cookie travels correctly.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 11:58:42 +10:00
tremble7681 da0436cee8 docs(state): Phase 1 MVP LIVE — backend+frontend deployed, smoke checks green 2026-05-17 11:34:19 +10:00
tremble7681 db938c818d docs(state): Phase 1 MVP code-complete (B-I), deployment runbook ready for Phase J 2026-05-17 10:58:11 +10:00
tremble7681andclaude-flow a428395071 feat(mechanic-pwa): VehicleScheme + InspectionEditor + InspectionReview pages (Phase I4-I6)
Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 10:54:00 +10:00
tremble7681andclaude-flow fc4060d4d9 feat(mechanic-pwa): photo upload infra (CameraCapture + PhotoSlot + AuthImg + presigned PUT)
Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 10:48:51 +10:00
tremble7681andclaude-flow 537548d716 feat(mechanic-pwa): Login + Home + VehicleCard pages with TanStack Query + OAuth2 login
- api/auth.ts: OAuth2 password flow via form-urlencoded POST /api/auth/login (not mechanic prefix)
- api/me.ts: GET /api/v1/mechanic/me with Me interface
- api/vehicles.ts: listVehicles (unwraps {items:[]}), getVehicle with VehicleDetail + InspectionSummary
- api/inspections.ts: createInspection returning full InspectionDetail; 9-value InspectionType union
- Login.tsx: full form with Логин/Пароль labels, useMutation, toast feedback, navigate on success
- Home.tsx: vehicle list with search, /me header with user name, logout, TanStack Query
- VehicleCard.tsx: vehicle info card, all 9 inspection type buttons (ru labels), history list

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 10:44:39 +10:00
tremble7681andclaude-flow e815955d46 feat(mechanic-pwa): scaffold Vite+React+TS+Tailwind+shadcn+PWA+routing (Phase G G1-G5)
- Vite 8 + React 19 + TypeScript 6 project via create-vite react-ts template
- Tailwind v3.4 with shadcn-compatible cream palette (CSS vars)
- vite-plugin-pwa v1.3 with autoUpdate SW, manifest.webmanifest, 192+512 icons
- shadcn/ui components written manually (default style): button, card, input, label, sonner
- Zustand persist auth store, ky API client with Bearer token hooks
- React Router v7 routing with RequireAuth guard
- TanStack Query v5 + Toaster wired in main.tsx
- 5 stub pages: Login, Home, VehicleCard, InspectionEditor, InspectionReview
- Build: 293 KB JS / 9.4 KB CSS, clean zero-warning tsc+vite build

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 10:39:27 +10:00
tremble7681 3660b80357 docs(state): Phase E complete (E1-E5), next is Phase F markers 2026-05-17 10:04:34 +10:00
tremble7681 173032c12e docs(state): Phase D complete (D1-D3 + test-fakes refactor), Phase E scoping 2026-05-17 01:08:04 +10:00
tremble7681 cbe9ae5d9c docs(state): Phase D decisions resolved + vendor recon findings, B3 contract widened 2026-05-17 00:36:13 +10:00
tremble7681 3a7bd749cc docs(state): Phase C complete (C1-C3), Phase D scoping notes 2026-05-17 00:15:17 +10:00