# Премиум Водитель — 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 (сделать до/параллельно соответствующим экранам).