Files
mechanic-pwa/docs/superpowers/specs/2026-06-18-premium-driver-pwa-design.md
T

10 KiB
Raw Blame History

Премиум Водитель — PWA (дизайн)

Дата: 2026-06-18 Статус: дизайн согласован (брейншторм + визуальный компаньон). Код не начат. Цель: PWA-приложение водителя «Премиум Водитель» — вход по телефону (код через TG/MAX), просмотр баланса (счета из CRM-леджера), пополнение через Т-Банк (СБП/карта), история пополнений. Поверхность для уже построенного бэкенда (Фаза 1 платежей + авторизация водителя в crm2/app/modules/payments).

Размещение и стек

  • Новый PWA — сиблинг PremiumDriverApp/driver-pwa/frontend/ (рядом с mechanic-pwa/), на том же стеке: React + Vite + vite-plugin-pwa + TypeScript + Tailwind + @tanstack/react-query + ky + zod + react-router-dom + react-hook-form + zustand + sonner (тосты) + lucide-react + vitest. Структура src/{api,components,pages,hooks,lib,store} как у mechanic-pwa.
  • Хостинг: app.pptaxi.ru (статика + Caddy; деплой как у mechanic — build → dist → tar/rsync, см. mechanic deploy flow).
  • Имя приложения (везде в UI, манифест PWA, заголовок): «Премиум Водитель».

Визуальный язык — Cream Minimal

  • Палитра (без жёлтого): фон #F3EEE3 (cream), карточка/поверхность #FBF8F1, линия-разделитель #E4DCCB, чернила #1C1B19, вторичный текст #7C7361/opacity. Кнопки primary — тёмные #1C1B19 с текстом #F3EEE3. Долг (минус) #C0493A, плюс/депозит #3B6E4A.
  • Типографика: Inter (один шрифт), без эмодзи. Иконки — lucide (тонкие, монохром).
  • Скругления 12–16px, тонкие 1px-границы, мягкие тени минимально. Крупные тап-таргеты (мобайл-фёрст). App-like / edit-first: минимум лишних кнопок, экран сам реагирует.

Экраны (MVP)

  1. Вход.
    • Шаг 1: поле телефона +7 ___ ___-__-__ → «Получить код». → POST /api/driver/auth/request {phone}.
    • Если ответ code_sent → шаг 2: ввод кода (свой цифровой кейпад) → POST /api/driver/auth/verify {phone, code} → сохраняем driver JWT → на «Баланс».
    • Если ответ onboarding → экран «Подключите бота»: две кнопки Telegram / MAX (открывают deep_links.tg/.max); подсказка «нажмите Старт у бота, затем вернитесь и запросите код снова». Кнопка «Я подключил — запросить код».
  2. Баланс (компоновка B: hero + список).
    • Шапка: «Премиум Водитель» + инициалы водителя.
    • Hero-блок «Общий долг» — крупная сумма (сумма отрицательных счетов).
    • Список счетов (bucket из /api/driver/balance): название + подпись (марка/госномер для аренды, «N неоплаченных» для штрафов) + значение (минус — красный, плюс — зелёный).
    • Кнопка «Пополнить» (primary). Тап по счёту/кнопке → «Пополнение» с предвыбранным счётом.
    • Внизу/в меню: «История», «Выйти».
  3. Пополнение (компоновка C: свой кейпад).
    • Заголовок «Пополнить · <счёт>». Крупная сумма. Строка «к оплате X ₽ (комиссия Y ₽)» — пересчёт на лету по ставке (получаем из ответа /topup или показываем оценку и уточняем).
    • Свой цифровой кейпад (1–9, 0, ·, ⌫) — без системной клавиатуры; крупные клавиши. Быстрые суммы 500/1000/2000 (чипы).
    • Сегмент СБП / Картой.
    • Кнопка «Оплатить»POST /api/driver/topup {bucket, amount, method} → получаем {order_id, pay_url}.
  4. Оплата / статус (вариант A: авто-статус, без QR).
    • Иконка «СБП», сумма к оплате, кнопка «Оплатить через СБП» (или «Оплатить картой») → открывает pay_url (СБП deep-link qr.nspk.ru/... → приложение банка; карта → форма pay.tbank.ru). QR не показываем, «оплата с другого устройства» убрана — водитель платит на том же телефоне через приложение банка.
    • Под кнопкой — авто-статус «Ожидаем оплату…» (спиннер). Приложение опрашивает статус платежа; при PAID → экран «Оплачено» (зелёная галочка, «<счёт> пополнен на N ₽») → кнопка «На главную» (баланс перезапрашивается).
  5. История пополнений.
    • Список из driver_payments водителя: дата, счёт, сумма, статус (Оплачено/Ожидает/Отклонён), способ. Пусто → «Пока нет пополнений».
  6. Выход — очистка JWT, возврат на «Вход».

Поток данных / API (crm2 BFF, префикс /api)

Готово (Фаза 1 + авторизация):

  • POST /driver/auth/request {phone}{status:"code_sent",channel} | {status:"onboarding",deep_links:{tg,max}}
  • POST /driver/auth/verify {phone, code}{token, driver_id}
  • GET /driver/balance (Bearer) → {accounts:[{bucket, balance}]}
  • POST /driver/topup {bucket, amount, method} (Bearer) → {order_id, pay_url, commission, total}

Нужно добавить на бэкенде (зависимость этого PWA — отдельные backend-задачи):

  • GET /driver/payments (Bearer) → история пополнений водителя (из driver_payments: order_id, bucket, amount, commission, method, status, created_at, paid_at).
  • GET /driver/payment/{order_id} (Bearer) → {status} для авто-опроса статуса оплаты (NEW/FORM/PAID/REJECTED). (Альтернатива: опрашивать /driver/balance и сравнивать — но явный статус надёжнее и показывает «Оплачено».)
  • (Опц.) В /driver/topup вернуть и commission_rate, чтобы кейпад считал «к оплате» локально до запроса.

Сессия / клиент

  • driver JWT (typ='driver', 30 дн) хранится в localStorage; ky-инстанс добавляет Authorization: Bearer. На 401 → разлогин (на «Вход»).
  • @tanstack/react-query для balance/history (кэш + рефетч); zod валидирует ответы; zustand — сессия (token, driver_id). Тосты ошибок — sonner.
  • База API из env (VITE_API_BASE, по умолчанию https://crm.pptaxi.ru/api). CORS: бэкенд должен разрешить origin https://app.pptaxi.ru для /api/driver/* (backend-задача).

PWA

  • vite-plugin-pwa: manifest (name «Премиум Водитель», cream theme #F3EEE3, иконки 192/512, standalone, портрет), service worker (precache оболочки, network-first для API). Устанавливается на телефон без сторов (по ссылке app.pptaxi.ru → «На экран Домой»).

Обработка ошибок

  • Телефон не найден (404) → «Номер не найден. Обратитесь в парк.» Неверный код (401) → «Неверный или просроченный код» + повтор. Платёж не создан → тост + остаёмся на «Пополнении». Авто-опрос: таймаут N минут → «Не дождались оплаты» + кнопка «Проверить ещё раз»/«Вернуться».

Тестирование

  • vitest + @testing-library/react: рендер экранов, кейпад (ввод/⌫/быстрые суммы, пересчёт «к оплате»), форма телефона/кода, маппинг ответов API (zod-схемы), guard приватных роутов (нет токена → «Вход»), авто-статус (мок таймера/опроса → success). API мокается (msw или ky-mock), без сети.

Скоуп / порядок

MVP-экраны: Вход (+онбординг) · Баланс · Пополнение · Оплата/статус · История · Выход. Вне MVP: push-уведомления, вывод средств, профиль/настройки, мультиязычность. Порядок сборки (для плана): каркас приложения+PWA+API-клиент+сессия → Вход/онбординг → Баланс → Пополнение(кейпад) → Оплата/авто-статус → История → деплой app.pptaxi.ru. Бэкенд-зависимости (/driver/payments, /driver/payment/{order_id}, CORS) — отдельные мелкие задачи в crm2 (сделать до/параллельно соответствующим экранам).