10 KiB
Премиум Водитель — 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: поле телефона
+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); подсказка «нажмите Старт у бота, затем вернитесь и запросите код снова». Кнопка «Я подключил — запросить код».
- Шаг 1: поле телефона
- Баланс (компоновка B: hero + список).
- Шапка: «Премиум Водитель» + инициалы водителя.
- Hero-блок «Общий долг» — крупная сумма (сумма отрицательных счетов).
- Список счетов (bucket из
/api/driver/balance): название + подпись (марка/госномер для аренды, «N неоплаченных» для штрафов) + значение (минус — красный, плюс — зелёный). - Кнопка «Пополнить» (primary). Тап по счёту/кнопке → «Пополнение» с предвыбранным счётом.
- Внизу/в меню: «История», «Выйти».
- Пополнение (компоновка C: свой кейпад).
- Заголовок «Пополнить · <счёт>». Крупная сумма. Строка «к оплате X ₽ (комиссия Y ₽)» — пересчёт на лету по ставке (получаем из ответа
/topupили показываем оценку и уточняем). - Свой цифровой кейпад (1–9, 0, ·, ⌫) — без системной клавиатуры; крупные клавиши. Быстрые суммы 500/1000/2000 (чипы).
- Сегмент СБП / Картой.
- Кнопка «Оплатить» →
POST /api/driver/topup {bucket, amount, method}→ получаем{order_id, pay_url}.
- Заголовок «Пополнить · <счёт>». Крупная сумма. Строка «к оплате X ₽ (комиссия Y ₽)» — пересчёт на лету по ставке (получаем из ответа
- Оплата / статус (вариант A: авто-статус, без QR).
- Иконка «СБП», сумма к оплате, кнопка «Оплатить через СБП» (или «Оплатить картой») → открывает
pay_url(СБП deep-linkqr.nspk.ru/...→ приложение банка; карта → формаpay.tbank.ru). QR не показываем, «оплата с другого устройства» убрана — водитель платит на том же телефоне через приложение банка. - Под кнопкой — авто-статус «Ожидаем оплату…» (спиннер). Приложение опрашивает статус платежа; при
PAID→ экран «Оплачено» (зелёная галочка, «<счёт> пополнен на N ₽») → кнопка «На главную» (баланс перезапрашивается).
- Иконка «СБП», сумма к оплате, кнопка «Оплатить через СБП» (или «Оплатить картой») → открывает
- История пополнений.
- Список из
driver_paymentsводителя: дата, счёт, сумма, статус (Оплачено/Ожидает/Отклонён), способ. Пусто → «Пока нет пополнений».
- Список из
- Выход — очистка 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: бэкенд должен разрешить originhttps://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 (сделать до/параллельно соответствующим экранам).