Commit Graph

66 Commits

Author SHA1 Message Date
Hermes 4cc91b6dc8 style(client): под штрих-кодом кода выдачи убраны цифры
Код выдачи и так написан крупно над полосами, дублировать 13-значное
значение под ними не нужно (просьба владельца). Заодно убрал значение из
aria-label — читалке экрана оно ничего не добавляет.

- PickupCodeCard передаёт showText={false}; возможность печатать подпись
  у компонента осталась (нужна, если печать понадобится другой);
- SW-кэш static-v131.
2026-09-30 19:04:46 +02:00
Hermes dbac57ba2a feat(client): штрих-код под кодом выдачи (самовывоз)
Клиент открывает карточку доставки с телефона и показывает код выдачи на
складе. Теперь под кодом рисуется штрих-код с тем же значением — код можно
отсканировать, а не набирать руками.

- Code 128 без внешних зависимостей (PWA работает офлайн), подмножество C
  для четырёх цифр: пара цифр = один символ;
- таблица символов перенесена машинно из эталонной реализации, алгоритм
  закреплён тестом на её битовых строках;
- отрисовка SVG: резко на экране и на печати, тихая зона 10 модулей;
- блок кода выдачи вынесен в общий PickupCodeCard (был продублирован в
  сохранённом выборе и на вкладке «Самовывоз»);
- SW-кэш static-v130.
2026-09-30 14:18:05 +02:00
Hermes 4cf5c0ffab feat(autocall): локация без звонков помечена «Не звоним (город)»
Заказ из города, выключенного для звонков (Настройки → Коммуникации →
Автодозвон → 📍 Локации), больше не выглядит как «звонка не было»:

- autocall_status(og) отдаёт новый статус city_disabled (порядок: ручной
  запрет → локация выключена → исключение по номеру → журнал звонков);
- бейдж в реестре: 🚫 не звоним, виден и в завершённом заказе;
- фильтр «Статус автодозвона»: «Не звоним (город)»;
- карточка «Автодозвон»: пояснение «Не звоним — <город>», кнопка
  «Позвонить сейчас» заблокирована (планировщик такие заявки пропускает);
- сводка заказа и шаг «Звонок» в цепочке: «не звоним (локация выключена)»;
- в детальную выборку группы добавлены autocall_status/exclusion_reason;
- SW-кэш static-v129.

На SMS выключение локации не влияет — уведомления продолжают уходить.
2026-09-29 17:40:58 +02:00
Hermes 20e942235a feat(delivery): видно, чем согласовали заказ — пуш / SMS / звонок / логист
Владелец: «нужно видеть, согласование прошло через пуш/смс/звонок/вручную через
менеджера» и «в колонке с именем клиента пишется просто «Согласовано» — непонятно
как и кем». Плюс в карточке «завтра/послезавтра» читалось как дата заказа.

Источник правды — order_groups.delivery_date_source: manual (логист), autocall
(робот), и теперь push/sms/messenger/client от клиентской страницы. Раньше клиентское
подтверждение канал не писало вовсе — такие записи разбираются по confirmed_at
приглашения (канал «link» → «По ссылке»).

- миграция 20260929_confirmation_channel.sql: confirm_delivery_choice_by_token
  принимает p_src и сохраняет канал в delivery_date_source + action_logs.details;
- клиентская страница читает ?src= из ссылки и прокидывает в RPC (SMS-ссылка уже
  содержит src=sms);
- orderGroupViews: resolveConfirmChannel/confirmChannelMeta — единый источник подписей
  канала, плюс invitationConfirmedAt в репозитории (и в списке, и в карточке);
- реестр доставок/самовывозов: под статусом чип «как согласовано» (📞 звонок робота,
  ✉️ SMS-ссылка, 🔔 push, 🧑💼 логист, 🔗 по ссылке);
- карточка заказа: рядом с «Согласовано» тот же чип;
- AutocallStatusCard: «робот предлагал: завтра или послезавтра» вместо голого
  «завтра/послезавтра» (читалось как дата заказа);
- Аналитика: в «Пути заказа» блок «Как согласовали дату» — полосы по каналам
  (за 30 дней: логист 135, по ссылке 20, звонок робота 7, push 0).

Тесты: orderGroupViews +4, OrderPathFunnelBlock +1 (37 зелёных в этих файлах). SW v128.
2026-09-29 11:54:04 +02:00
Hermes c3df1fc0e1 feat(analytics): «Путь заказа» — воронка по ступеням обработки
Владелец: «а путь заказа можно сдать какой-то красивой воронкой?»

В admin_comms_funnel добавлен блок order_path: этапы (групп в работе → первое SMS →
дата согласована → выдано по цепочке), честная плашка «мимо цепочки» (выдано без даты,
платное хранение, отмены), источники даты (менеджер/робот/без даты) и разбивка
самовывоз/доставка. Считается так, что водитель без даты не едет: последний этап —
это выданные С датой, поэтому воронка монотонная.

OrderPathFunnelBlock рисует воронку штатным recharts FunnelChart (градиент по этапам,
подписи «значение · %», тултип с конверсией шага), под ней — легенда теми же цифрами
(на телефоне воронку читать тяжело), чипы конверсий между ступенями, плашка «мимо
цепочки» и чипы источников/типа заказа. Блок стоит над воронкой коммуникаций.

За 30 дней: 396 → первое SMS 340 (85.9%) → дата 161 (47.4% от SMS) → выдано по цепочке
134 (83.2%); мимо цепочки 204; дата от менеджера 135, от робота 6; самовывоз 235,
доставка 161. Тест: OrderPathFunnelBlock.test.jsx (6 проверок). SW v127.
2026-09-29 11:03:24 +02:00
Hermes 48fb7ad662 style(analytics): короткие подписи колонок в таблице попыток на мобильном
На 390px последняя колонка «Не взяли» обрезалась («Н»). На мобильном подписи
короче: №, Звонков, Взяли, Согл., Не взяли. SW v126.
2026-09-29 10:30:05 +02:00
Hermes 41ac30c090 style(analytics): мобильная раскладка списков воронки — без горизонтальной прокрутки
На 390px таблицы «Не поднимают трубку» и «Открыли ссылку, но не подтвердили»
уезжали вправо (статус и телефон не видны без прокрутки). На мобильном они
раскладываются списком: телефон и число попыток в строку, город/тип/статус и
дата — ниже мелким. В таблице попыток процент ответов уходит во вторую строку,
чтобы колонка «Не взяли» влезла. Тест на мобильную раскладку (8 проверок). SW v125.
2026-09-29 10:28:35 +02:00
Hermes 188db318e3 feat(analytics): воронка коммуникаций на странице «Аналитика»
Владелец: «нужно видеть, как всё происходит: push отправили — реакции не было, потом SMS,
потом звонок; с какого раза человек ответил на звонок, кто вообще не поднимает трубку».

Один RPC admin_comms_funnel(p_days, p_date_from, p_date_to) — период считается как у
admin_automation_economics (30 = группы, созданные за 30 дней; 0 = всё время) — отдаёт:
этапы цепочки по когорте (группы → push → первое SMS → открыли ссылку → позвонили →
взяли трубку → согласовано → в ручную → выдано), разбивку звонков по попыткам
(всего/взяли/согласовали/не взяли + % ответов), каналы подтверждения (робот / менеджер /
ссылка), список «не поднимают трубку» и «открыли ссылку, но не подтвердили», и «silent» —
кого не достали ни ссылкой, ни звонком.

Блок CommsFunnelBlock на «Аналитике» под KPI-карточками, те же табы периода, mobile-first,
время в МСК. За 30 дней: 396 групп, первое SMS 340, открыли ссылку 1, звонили 26,
взяли трубку 14, согласовано 128, в ручную 22, выдано 357; 265 (66.9%) не достали никак.
С попыток: 1-я — 42 ответа из 64, 2-я — 2 из 6, 3-я — 4 из 4.

Тест: src/components/admin/analytics/CommsFunnelBlock.test.jsx (7 проверок, фикстура по
реальному ответу RPC). SW: v123 -> v124.
2026-09-29 10:23:01 +02:00
Hermes 5979b03f9f style(registry): дата и половина дня в колонке «Дата» — двумя строками
В узкой колонке «Вторая половина дня» переносилась посреди фразы («Вторая /
половина дня»). Дата — первой строкой, половина дня — второй, мелким. SW: v123.
2026-09-28 23:20:53 +02:00
Hermes 3b90b79f9f fix(registry): дата и время самовывоза в реестре групп
Владелец: «в таблице всех доставок и самовывоза, если согласована доставка, дата
и время указаны, а если самовывоз — нет; нужно и самовывоз выводить».

Причина: колонка «Дата» (LogisticsReadinessBoard) читала только delivery_date/
delivery_time, а у самовывоза дата и половина дня лежат в pickup_date/
pickup_time_slot — поэтому строка самовывоза показывала «—», хотя слот согласован.
Тот же пропуск был в фильтре по периоду и группировке по датам: getDeliveryDate()
смотрела только на delivery_date, и при выбранном периоде заказы самовывоза
пропадали из реестра (падали в дату заказа).

Введены общие хелперы в orderGroupViews: isPickupGroup, getGroupSlotDate,
getGroupSlotTime (камелкеис и snake_case). Реестр показывает дату и время слота
по типу заказа, сортировка — по этой же показанной дате, фильтр по периоду и
группировка — тоже по ней. Подпись колонки «Доставка» → «Дата» (в колонке теперь
и дата выдачи).

Попутно поправлено устаревшее ожидание в тесте (getOrderGroupDisplayStatusLabel
для черновика ждало «draft», подпись локализована в «Черновик») — тест падал и до
этой правки.

SW: v121 -> v122.
2026-09-28 23:17:39 +02:00
Hermes 6944386b3b feat(admin): дни недели в окне звонков автодозвона
Владелец: «в субботу звонки не должны происходить, всё это настраивается из
админки». В панели «Автодозвон» в блоке «Окно звонков» были только часы, а дни
(work_days) править из админки было нечем — их приходилось менять в БД, хотя
планировщик именно по ним и решает, звонить ли сегодня.

Добавлены таблетки дней Пн–Вс тем же механизмом и в том же виде, что в панели
SMS-кампаний: пустой набор не подменяется дефолтом (снял все дни — значит
звонков нет, и в панели это видно). Подсказка в панели теперь честно описывает
поведение: вне окна робот не звонит вообще, попытка уходит на ближайшее
рабочее утро.

Харнесс check_autocall_window.py считает ожидания ИЗ НАСТРОЕК (часы + дни),
поэтому проверка не устаревает при смене графика.

SW: v120 -> v121.
2026-09-28 22:53:34 +02:00
Hermes 16141fe5b4 feat(client): шаг «Звонок» в цепочке заказа
В блоке «История действий по заказу» автодозвон не был виден в цепочке шагов —
там были только «Добавлен», «SMS», «Согласовано» и далее, хотя робот звонит по
этим же заказам. Теперь шаг «Звонок» идёт сразу после SMS в обеих цепочках
(доставка и самовывоз) и имеет собственное состояние: ✓ пройден, если робот
согласовал слот; янтарный — если звонили без результата; серый — если звонков
не было. Шаг не считается пройденным только потому, что заказ уже согласован
(согласовать могли по ссылке из SMS, без звонка). Подсказка при наведении —
последний исход и число звонков.

SW: v119 -> v120.
2026-09-28 14:56:17 +02:00
Hermes d98c44c9fd feat(client): номер доставки и код выдачи + «Доставка» вместо «Счета»; в SMS — {order} и {code}
Клиентская страница согласования:
- приглашение теперь отдаёт deliveryType и pickupCode (public view) — до этого
  страница не знала, что заказ самовывозный, и говорила «Доставка заказа /
  Согласование доставки» даже для самовывоза; в шапке и hero-тексте выбор по типу
- в блоке с кодом: «Ваш номер доставки 010796» + «Ваш код выдачи 7598»
  (номер берём из состава заказа, «ТП\-010796» → «010796»)
- ссылка на заказ теперь «Доставка ТП-010796» вместо «Счет ТП\-010796»,
  экранированный дефис из обмена с 1С больше не показывается

SMS: в шаблоны добавились переменные {order}/{номер}/{номер_доставки} (номер
доставки) и {code}/{код}/{код_выдачи} (код выдачи) — работают и в шаблоне
кампании, и в тексте профиля города. Дефолтные тексты самовывоза (и значения в
БД, см. supabase/sms_pickup_code_placeholders.sql) теперь содержат номер и код;
в админке у полей появилась памятка по переменным и предпросмотр с подстановкой.

service-worker v118 → v119
2026-09-28 12:21:51 +02:00
Hermes a1d1c3f7a2 chore(sw): v118 — выкладка фронта из dev-сборки (возврат автодозвона + шаблоны SMS) 2026-09-28 11:31:08 +02:00
Hermes cf5bf31008 feat(sms): текст SMS по типу отгрузки — свои шаблоны для доставки и самовывоза
Раньше текст был зашит в кампании и всегда говорил про доставку; настраиваемый
в админке sms_text_template не читался скриптами вообще. Сначала выяснилось,
что всем 909 pickup-группам уходило «Согласуйте дату доставки по ссылке».

- sms_campaign_settings.sms_text_template_pickup (новая колонка) + значения
  sms_text_template = «…дату доставки…», pickup = «…дату самовывоза…»
  (supabase/sms_text_templates_pickup.sql = миграция с фактическими текстами)
- sms_first_campaign / sms_second_campaign / sms_paid_storage_campaign:
  build_sms_text(group, settings) — шаблон выбирается по og.delivery_type,
  {link} подставляется ссылкой; приоритет профиль города (режим «Уведомление»)
  → шаблон кампании → дефолт скрипта
- вторая SMS и платное хранение тоже получили pickup-вариант (кампании были
  выключены, поэтому в текстах жил только «доставка»)
- SmsCampaignPanel: два поля ввода (📦 Доставка / 🏪 Самовывоз) + длины и
  предпросмотр с подставленной ссылкой + пояснение приоритета
- service-worker v117 (обязательный бамп при деплое фронта)
2026-09-28 10:50:32 +02:00
Hermes f35a84084b chore(sw): bump cache to v116 2026-08-21 14:11:24 +02:00
Hermes 981ca827a2 fix(sw): stop unbounded cache growth — delete stale caches on activate, cache only static assets
- activate: delete ALL caches except current STATIC/RUNTIME (was keeping every
  previous construction-delivery-* cache forever → 3GB storage, 30-40s caches.match)
- fetch: only cache same-origin static assets (js/css/img/fonts); API/data and
  cross-origin requests go straight to network, never cached
- bump to v115
2026-08-17 16:17:36 +02:00
Hermes 56cb4b460f fix(analytics): period key '1d' (Сегодня) not in PERIOD_DAYS map — fell back to 30 days; add '1d' to both hooks 2026-08-14 16:00:36 +02:00
Hermes c4aebb08a4 chore(sw): bump to v113 (timeline SMS state from log) 2026-08-14 15:08:35 +02:00
Hermes 6fe0bfbc73 chore(sw): bump to v112 (endpoint-targeted test push) 2026-08-13 15:03:23 +02:00
Hermes 4fed962f23 chore(sw): bump to v111 (direct test push button on /client) 2026-08-13 14:34:17 +02:00
Hermes 91245169f4 chore(sw): bump to v110 (text-base fix, login link) 2026-08-13 14:26:38 +02:00
Hermes e21cea1b1c chore(sw): bump to v109 (test consent page + test push button) 2026-08-13 14:18:49 +02:00
Hermes 045c903074 chore(sw): bump to v108 (root redirect → /client) 2026-08-13 14:09:16 +02:00
Hermes 15132f56e8 feat(client): PWA install — client route, manifest start_url=/client, SW app-shell, invitation links → /client/:token 2026-08-11 13:37:29 +02:00
root 02b70cac22 fix: manifest start_url → /delivery/showcase for client PWA 2026-08-06 17:52:34 +00:00
root 7a702114eb feat: iOS push instruction banner + fix edge-functions security.ts exports 2026-08-06 17:36:31 +00:00
root 2597835352 fix(mobile): anchor notifications popup to viewport right edge 2026-08-06 16:05:43 +00:00
root de5130e31f fix(mobile): anchor install tooltip to viewport right edge 2026-08-06 15:50:44 +00:00
root 6d53625579 fix(mobile): anchor PWA install tooltip to right edge 2026-08-06 15:39:51 +00:00
root 4d47261321 feat(logistics): SMS problem as tag, red highlight for unagreed 2+ days 2026-08-06 15:28:51 +00:00
root d5849f0ab2 feat(logistics): highlight and filter orders with SMS problems 2026-08-06 15:12:23 +00:00
root c321353e8f feat(orders): show SMS delivery confirmation and 2nd-SMS skip reason 2026-08-06 13:55:42 +00:00
root fefb8065be perf(dashboard): lazy load order details only when needed; skip fetch on non-order tabs 2026-08-06 13:40:20 +00:00
root 44d3f0f16d fix(client): compact order composition trigger 2026-08-06 12:10:21 +00:00
root 82d6f4f077 feat(orders): show client push subscription and delivery log 2026-08-06 12:06:11 +00:00
root ad7b92503c fix(push): remove monochrome Android badge icon 2026-08-06 11:28:53 +00:00
root 34b01758ef fix(ui): use СуперСам and normal order composition heading 2026-08-06 11:13:40 +00:00
root 74de57f3fc fix(ui): use СуперСам and normal order composition heading 2026-08-06 11:12:47 +00:00
root 9be5f81ffe fix(ui): localize SuperSam name and normalize order headings 2026-08-06 11:11:56 +00:00
root 9e4ce20c96 fix(push): use SuperSam application icon in notifications 2026-08-06 10:15:42 +00:00
root 1922778e0f feat(push): prepare client subscription test contour 2026-08-06 08:56:12 +00:00
root 98ebc935c2 fix(pwa): force SW bump for tab visibility on mobile 2026-08-06 08:12:56 +00:00
root 8faa839056 chore(pwa): bump cache version 2026-08-06 08:03:11 +00:00
root 811e2a1254 chore(pwa): bump cache version 2026-08-06 07:51:32 +00:00
root 4459d03932 chore(pwa): bump cache version 2026-08-06 07:48:40 +00:00
root b1a09ae71a chore(pwa): bump cache version 2026-08-06 07:14:46 +00:00
root 15b69102b0 chore(pwa): bump cache version 2026-08-06 07:11:02 +00:00
root 4bf95064bd chore(pwa): bump cache version 2026-08-06 07:06:41 +00:00
root db261f96db chore(pwa): bump cache version 2026-08-06 07:01:15 +00:00