Commit Graph

237 Commits

Author SHA1 Message Date
wasrusgen
76fce9ec58 feat(analytics): assembler schedule parser + analytics screen
Backend:
- assembler_parser.py: parse Excel «Таблица занятости сборщиков»
  - Handles both row-order variants (2026: dates row1; 2025-: dates row2)
  - Extracts amount from end of cell text, supports compound "6030+20100"
  - aggregate(): by_assembler×month + by_month totals
  - In-memory cache with mtime invalidation
- main.py: /api/assembler_analytics — local file first, Drive fallback
  - LOCAL: /app/data/assembler_schedule.xlsx (mounted volume)
  - Config: ASSEMBLER_SCHEDULE_PATH env var override
- config.py: assembler_schedule_file_id for Drive fallback
- docker-compose.yml: /opt/zov-tech/data → /app/data:ro volume

Frontend:
- assembler_analytics.js: year filter, monthly table, assembler ranking
  with progress bars, per-order average, last-6-months breakdown
- app.js: route #/admin/assembler-analytics + "Аналитика" button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 09:44:34 +03:00
wasrusgen
12dec17ed1 fix(podbor): HTML format + sticky header + home button at report bottom
- _ai(): markdown→HTML fallback (**, *, _, `) when no HTML tags found
- podbor-header: position:sticky so back/home always visible while scrolling
- renderReport(): footer with "← Главное меню" + "+ Новый подбор" buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 09:26:07 +03:00
wasrusgen
3e7ae7764a feat(assembly): configurable assembly rates + admin panel
Backend:
- Sheet "Assembly_Rates": rules by assembler_tg_id + scope
- _resolve_rates(): priority matching (specific > wildcard)
- Default: client 10%, assembler 9% (1% margin)
- _calc_assembly_prices(): role-aware field set in detail API
- Endpoints: assembly_rates_list, assembly_rate_save, assembly_rate_delete
- Cache TTL 120s, auto-seeded default rule on first run

Frontend:
- assembly_detail.js: shows client rate %, assembler payout % (role-aware)
- admin_rates.js: list/add/edit/deactivate rules with live margin preview
- app.js: route #/admin/rates + "Ставки сборки" button in manager dashboard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 09:20:03 +03:00
wasrusgen
f473f1dc03 feat(podbor): copy report as plain text for messengers
- New button "📋 Скопировать текст" in report export panel
- _copyReportText(): formats structured text (brand, price, pros/cons, links)
- _stripHtml(): strips HTML tags for clean clipboard output
- Clipboard API with execCommand fallback for Telegram WebApp
- Button shows " Скопировано!" feedback for 2.2s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 08:32:52 +03:00
wasrusgen
f1b7f71337 fix(podbor): HTML AI output + home button on all steps
- AI prompt: use HTML tags (b, em, br, li) instead of markdown
- renderReport: _ai() helper renders AI text as innerHTML (safe, backend-controlled)
- Header: added podbor-home button (top-right) → goes to main menu from any step
- After successful submit: show "← Вернуться в главное меню" button immediately
- Fixes: no way to leave podbor after result was received

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 08:02:09 +03:00
wasrusgen
3f1531f7ca feat: add MasterTools assembler cheat-sheets (#/master/tools)
- Rail spacing calculator (gap formula from Excel)
- Shelf holder calculator (step/drop formula from Excel)
- Interactive price list (70+ items, 9 sections, transport formula)
- Entry point button in staff screen (assembler only)
- Routes: #/master/tools, /rails, /shelves, /price

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:43:23 +03:00
wasrusgen
67ca6e4b26 fix: добавить @app.post('/api/managers_list') — пропустили FastAPI-роут
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-18 23:33:33 +03:00
wasrusgen
5ed11c00fa feat: заявка на замер — поиск по клиентам + передача менеджеру
Frontend (request.js):
- Поиск клиентов из списка менеджера (autocomplete dropdown)
  по имени или цифрам телефона, макс. 6 результатов
- Режимы: search → selected (карточка + редактируемый адрес)
  или → new (ручной ввод ФИО/тел/адрес)
- «Создать нового клиента» всегда в конце dropdown
- Выпадающий список замерщиков (existing)
- Новый select «Передать менеджеру» — передаёт заявку коллеге
- Platform.initData / Platform.initDataUnsafe вместо tg?.

Backend (main.py):
- _handle_managers_list: список всех менеджеров кроме себя
- _handle_measurement_request: поддержка target_manager_tg_id
  (заявка создаётся от имени целевого менеджера + уведомление)
- Роут managers_list добавлен в handlers dict

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-18 23:32:16 +03:00
wasrusgen
bd85b30aa5 feat: SignRequest — цифровая подпись акта сборки (ФЗ-63 ПЭП)
Backend:
- _handle_sign_request_create: генерирует 6-значный OTP (72ч TTL),
  отправляет клиенту в Telegram для code-режима
- _handle_sign_request_submit: canvas (PNG→PHOTOS_DIR), code (OTP-верификация),
  proxy (представитель), absent (без подписи + причина)
- Assemblies sheet: +sign_token, sign_token_expires_at, signed_via,
  signed_by_tg_id, signed_by_phone
- assembly_detail: возвращает signed_via, client_tg_id, signed_by_phone

Frontend:
- signrequest.js: overlay-bottom-sheet, 4 таба (canvas/code/proxy/absent),
  canvas с touch-events, OTP-ввод, submit с валидацией
- assembly_detail.js: кнопка «Подписать акт» если не подписано,
  блок подписи с методом (VIA_LABELS) и перезагрузкой после подписания
- styles.css: .signreq-* компоненты

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-18 23:12:34 +03:00
wasrusgen
ef51ebcb85 feat: platform.js — адаптер Telegram WebApp для миграции на VK Max
- platform.js: глобальный Platform (initData, initDataUnsafe, startParam,
  colorScheme, ready, expand, haptic, showAlert, onThemeChange, enableClosingConfirmation)
- tg остаётся глобальным для backward-совместимости модулей
- app.js: setupTelegram() и haptic() делегируют в Platform,
  все tg?.initData/initDataUnsafe → Platform.initData/initDataUnsafe
- index.html: platform.js грузится первым (перед icons.js)
- VK Max Phase 2: заменить platform.js, остальной код не трогать

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-18 18:06:03 +03:00
wasrusgen
cb4bbfce70 docs: стратегия платформ — Telegram сейчас, VK Max отдельно после
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:40:14 +03:00
wasrusgen
6f846603a9 feat: kitchen_price в сборках + срочный замер push
- main.py: kitchen_price в assembly_list + assembly_detail
- main.py: /api/assembly_set_kitchen_price — менеджер задаёт стоимость кухни
- main.py: measurement urgent=True → bot push замерщику(ам)
- sheets.py: find_users_by_role(role) — поиск пользователей по роли
- assembly_detail.js: показывает стоимость кухни + стоимость сборки (9%)
- index.html: версии → v20260518m

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:38:44 +03:00
wasrusgen
7a47739e77 docs: мультиплатформа VK Max, ПЭП в договоре, агент Юрий в команду
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:26:07 +03:00
wasrusgen
c3e09effdb docs: SignRequest — два метода подписи: canvas (палец) + code (Telegram/SMS)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:21:31 +03:00
wasrusgen
4430340038 docs: SignRequest — платформенный модуль подписи для актов, водителей, удалённых клиентов
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:20:51 +03:00
wasrusgen
8cf60887bc docs: ROADMAP P2б — договоры, дашборд сборщика, акты, прайс доп.работ
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:51:11 +03:00
wasrusgen
4533e48167 docs: business_rules — стоимость сборки 10%, выплата сборщику 9% от кухни
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:00:21 +03:00
wasrusgen
6ba6489873 docs: ROADMAP — P2 закрыт, калькулятор отложен
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:37:28 +03:00
wasrusgen
042dc1a5d3 feat: детальная карточка сборки + цена замера везде
- assembly_detail.js: экран #/c/assembly/:id — статус, адрес, фото, подпись, gcal
- orders.js: сборки кликабельны → #/c/assembly/:id
- app.js: маршрут #/c/assembly/
- selfmeasure.js: цена 2500₽ + 40₽/км за КАД на шаге 1 и шаге 5
- cabinet.js: цена под кнопкой самозамера

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:35:21 +03:00
wasrusgen
b75f24e4d7 feat: история заказов #/c/orders — таймлайн подборов и сборок
- orders.js: единый таймлайн proposal_list + assembly_list, сортировка по дате
- cabinet.js: кнопка «📋 История заказов» → #/c/orders
- app.js: маршрут #/c/orders, версия l
- styles.css: классы orders-timeline, .block, .block-head, .assembly-card
- memory/business_rules.md: замер 2500₽ + 40₽/км за КАД СПб

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:19:20 +03:00
wasrusgen
e71ac3a5a8 fix: selfmeasure — читаемое сообщение при HTTP 4xx/5xx
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 14:49:53 +03:00
wasrusgen
78e332dd95 feat: самозамер #/c/selfmeasure — 5-шаговый мастер для клиента
- selfmeasure.js: тип кухни (SVG-карточки) → стены → коммуникации → фото → контакт+отправка
- cabinet.js: кнопка «📐 Самозамер кухни» → #/c/selfmeasure
- app.js: маршрут #/c/selfmeasure, guard _hashListenerAdded
- index.html: подключение selfmeasure.js v20260518k
- backend: /api/self_measure_submit (hot-patch на VPS)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 14:40:50 +03:00
wasrusgen
ea04e042df feat: staging-окружение (Docker Compose + Caddy + deploy-script)
docker-compose.staging.yml — backend-staging на порту 8001.
.env.staging.example — шаблон с отдельным SHEET_ID.
Caddyfile.staging.snippet — staging.api.wasrusgen1.pro.
scripts/deploy-staging.sh — один скрипт для деплоя staging.
app.js: BACKEND_URL читается из ?backend= параметра URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 14:22:01 +03:00
wasrusgen
6616d48c0a test: mock-фикстура карточки клиента в smoke-тесте
page.route() перехватывает /api/clients и возвращает тестового клиента.
Карточка клиента теперь открывается и проверяется без реальных данных.
17 чеков, 0 пропущено.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:43:09 +03:00
wasrusgen
7df9cc3901 refactor: аудит роутера app.js — устранены 3 edge case
1. Дублирование: init() теперь вызывает routeByHash() вместо копии логики.
   #/master, #/me, #/c/cabinet теперь работают при прямом переходе по ссылке.
2. Множественные hashchange listeners: guard _hashListenerAdded.
3. #/picker → #/c/proposal в cabinet.js и me.js (неверный роут).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:39:23 +03:00
wasrusgen
c773adf05e fix: Content-Type: application/json во всех fetch-хелперах
measurements, assembly, request, me, inbox, proposals —
6 модулей, 6 хелперов (_fetchWithTimeout / apiFetch / _api).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:35:20 +03:00
wasrusgen
e934576e5c feat: клиентский кабинет #/c/cabinet
cabinet.js — дашборд клиента: профиль, менеджер, подборы, сборки.
Параллельная загрузка: /api/me + /api/proposal_list + /api/assembly_list.
Backend: assembly_list теперь поддерживает роль client (client_tg_id фильтр).
me.js: кнопка «Мой кабинет» → #/c/cabinet для роли client.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:25:49 +03:00
wasrusgen
f397747a34 ci: UI Playwright smoke после деплоя Pages (workflow_run)
Тригер: Deploy MiniApp to GitHub Pages → completed + success.
Скриншот сохраняется как артефакт при провале.
workflow_dispatch с опциональным SMOKE_URL для ручного запуска.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:33:35 +03:00
wasrusgen
f7a1c3e4c0 test: smoke-тест на локальном сервере (npx serve), убран chicken-and-egg
Сервер поднимается через spawn + HTTP polling.
SMOKE_URL=... для тестирования произвольного URL.
15 чеков: +inbox, +me, +titles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:30:11 +03:00
wasrusgen
864e70f39b test: добавлены проверки #/inbox и #/me в ui_smoke.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:24:50 +03:00
wasrusgen
3741657fb6 feat: экран #/inbox — входящие задачи менеджера
InboxScreen.mount() — список замеров без решения по подбору.
Три действия: начать подбор / отложить / не нужен.
Роутинг #/inbox добавлен в routeByHash() и init().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:22:07 +03:00
wasrusgen
bdbdf4b259 feat: экран #/me — Мой профиль для всех ролей
manager: статус доступа, салон, быстрые переходы
staff: роли-чипы, кнопки на замеры/сборки
client: менеджер, кнопки подбора и сборок

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:17:52 +03:00
wasrusgen
be79f5447b docs: feature-status — #/master реализован 2026-05-18 12:08:33 +03:00
wasrusgen
b43087a4b4 feat: маршрут #/master — экран входящих задач замерщика/сборщика
- routeByHash: #/master → renderStaff(window.__zovMe)
- таймаут 15с на fetch в renderStaff и renderStaffAssemblies
- app.js v=20260518g

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:08:20 +03:00
wasrusgen
64292cef8e fix: права доступа по ролям — сборщик/клиент
- measurement_detail: любой мастер (measurer+assembler) видит фото замера
- assembly_list: клиент видит все свои сборки (по client_tg_id)
- assembly_detail: клиент видит детальную карточку сборки

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:59:16 +03:00
wasrusgen
4386779ea6 docs: структура ролей и взаимодействий (ROLES.md)
4 роли: manager / measurer / assembler / client
Цикл сделки, доступ по экранам, правило совместного подбора техники.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:42:38 +03:00
wasrusgen
542f96aaa0 docs: экономия токенов — правило краткости в CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:18:27 +03:00
wasrusgen
2d0246a5b5 feat: система идентификации агентов — бейдж в каждом ответе
Каждый агент обязан начинать ответ с бейджа роли:
🤖 КООРДИНАТОР / 🔧 DEV / ⚙️ DEVOPS / 🎨 DESIGN / 🧩 FEATURE / 🧪 TEST / 🔍 REVIEW

Правило прописано в CLAUDE.md и в каждом агент-файле.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:17:58 +03:00
wasrusgen
7b874e0195 feat: система активных агентов — ROADMAP, статусы, команда /project:review
- ROADMAP.md — единый роадмап продукта (реализовано / в работе / бэклог)
- agents/dev-status.md — состояние кода, долг, следующий шаг
- agents/devops-status.md — VPS, блокеры, инфра-бэклог
- agents/design-status.md — компоненты, нереализованные экраны
- agents/feature-status.md — таблица функционала, приоритизированный бэклог
- agents/test-status.md — покрытие, пробелы, следующий шаг
- .claude/commands/review.md — мастер-команда: сводный отчёт по всем агентам
- Все агенты обновлены: читают и пишут свой status.md в начале/конце работы

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:16:27 +03:00
wasrusgen
ff35dd769f ci: добавить pre-push hook (CSS-линтер + UI Playwright)
Блокирует push при JS-ошибках в интерфейсе или CSS-нарушениях.
API-тесты в хук не включены — зависят от VPS/Drive, запускать вручную.
Активируется через: git config core.hooksPath .githooks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 09:30:36 +03:00
wasrusgen
0fb0597b8d fix: добавить 15-секундный таймаут fetch во все модули (measurements, assembly, proposals, request)
Кнопки больше не зависают бесконечно при медленном или недоступном бэкенде.
AbortController + дружелюбное сообщение «Сервер не отвечает — попробуйте ещё раз».

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 09:27:53 +03:00
wasrusgen
40e2275949 test: добавить Playwright UI smoke-тест (10 проверок JS-ошибок по экранам)
- tests/ui_smoke.js — headless Chromium с mock Telegram.WebApp, проверяет
  загрузку, список клиентов, форму нового клиента, замеры, сборки
- package.json — playwright devDependency
- .claude/commands/test.md — добавлен Шаг 4 (UI Playwright) в агент тестировщика

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 08:53:58 +03:00
wasrusgen
6f74d05184 fix: таймаут 15с на все fetch в clients.js — кнопки больше не висят
- _fetchWithTimeout() — единый fetch с AbortController
- saveClientNote, fetchClientNote, fetchClients, fetchMeasurements
  теперь через таймаут вместо бесконечного ожидания
- При таймауте показывает 'Сервер не отвечает — попробуйте ещё раз'
- Версия clients.js: 20260518e
2026-05-18 08:35:42 +03:00
wasrusgen
a9aa704950 fix: добавить escAttr — ReferenceError в карточке клиента
escAttr использовалась в 11 местах (карта, форма редактирования)
но не была объявлена в clients.js — отсюда пустая карточка.
Версия clients.js: 20260518d
2026-05-18 08:31:27 +03:00
wasrusgen
ad6c8b9205 feat: добавить команды агентов — dev, devops, design, feature, test
/project:dev     — разработчик (Python/JS/бот)
/project:devops  — деплой и обслуживание VPS
/project:design  — CSS, темы, WCAG, open-design
/project:feature — новый функционал, флоу, роутинг
/project:test    — тестировщик (все 3 теста + сводный отчёт)
2026-05-18 08:29:43 +03:00
wasrusgen
b6bf2eaf80 docs: добавить принципы работы Claude в CLAUDE.md 2026-05-18 08:25:50 +03:00
wasrusgen
057842b7e6 debug: показывать ошибку в карточке клиента вместо пустого экрана
- Обернуть renderClientHistory в try/catch — показывает текст ошибки
- Добавить .catch() в mount() для перехвата unhandled promise rejection
- Версия clients.js: 20260518c
2026-05-18 00:21:28 +03:00
wasrusgen
860c768572 fix: пустая карточка клиента — обработка ошибок и сравнение client_tg_id
- Добавить проверку data.error после fetchClients() в renderClientHistory
- Сравнивать client_tg_id как строки (String(c.client_tg_id) === String(clientKey))
  чтобы избежать 5937498515 !== '5937498515'
- Показывать явное сообщение если клиент не найден вместо пустой страницы
- Версия clients.js: 20260518b
2026-05-18 00:11:44 +03:00
wasrusgen
a4124c6b50 fix: открывать карточку клиента вместо списка подборов
- Заголовок 'История подборов' → 'Карточка клиента'
- Убрать инлайн-монтирование Proposals.mountManager в карточке клиента
- Оставить только кнопку 'Открыть →' для перехода к подборам
- Версия clients.js: 20260518a
2026-05-18 00:03:38 +03:00
wasrusgen
20665d73f3 fix: ARRIVALS_FILE_ID = SHIPMENTS_FILE_ID (один файл ОТГРУЗКИ)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 22:23:38 +03:00