Files
2026-09-21 04:06:43 +03:00

94 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Please Pay Me — Flutter mobile
Мобильный кабинет для бюджета «от зарплаты до зарплаты». UI построен на
iOS-ките (Cupertino, Apple HIG), данные — REST API из `src/PleasePayMe.Api`.
## Запуск
```powershell
cd mobile
copy .env.example .env # один раз
# отредактируй PPM_API_BASE_URL в .env
.\run.ps1
.\run.ps1 -Device chrome
.\run.ps1 -Target widgetbook
```
`run.ps1` читает `mobile/.env` и передаёт его во Flutter как
`--dart-define-from-file`. Если файла нет — копирует `.env.example`.
```bash
export PATH="$HOME/flutter/bin:$PATH"
cd mobile
flutter pub get
flutter run -d windows --dart-define-from-file=.env
```
### Конфигурация (`mobile/.env`)
| Переменная | Назначение |
| --- | --- |
| `PPM_API_BASE_URL` | адрес API; если пусто — `https://please-pay-me.ru` |
| `PPM_WEB_URL` | веб-кабинет; если пусто — тот же origin, что API |
| `PPM_DEMO` | `true` — принудительный in-memory backend |
### Авторизация
На iOS и Android:
- **Telegram** — кабинет в WebView, JWT забирается из
`localStorage.ppm_session_jwt` через канал `PpmAuth`.
- **Яндекс** — WebView на `oauth.yandex.ru`. Код возвращается на
`{кабинет}/` (как Callback URL в кабинете Яндекса), приложение шлёт
его в `POST /api/auth/yandex`. Client secret живёт только на API.
Приложение проверяет JWT через `GET /api/me` и кладёт сессию в
`SharedPreferences`.
На Windows / в браузере WebView нет: остаётся ручной ввод токена. Альтернатива
на любой платформе — «Демо-режим» (`DemoBackend` без сети).
В кабинете Яндекса должен быть Redirect URI `https://<домен>/`
(для продакшена — `https://please-pay-me.ru/`). BotFather → `/setdomain`
для Telegram — тот же домен.
## Экраны
| Вкладка | Что делает |
| --- | --- |
| Обзор | остаток бюджета, дневной лимит, трата в один тап, ближайшая выплата |
| Журнал | операции по дням, фильтр «текущий / все бюджеты», подгрузка страниц |
| Бюджеты | выбор активного конверта, создание, редактирование, архив, удаление |
| Работа | оклад, дни выплат, правило выходных, график ближайших зарплат |
| Профиль | пользователь, режим подключения, выход |
## Структура
```
mobile/
├── lib/
│ ├── app/ # CupertinoApp, session gate, таб-бар
│ ├── core/ # config, storage, AsyncValue, форматтеры
│ ├── data/
│ │ ├── api/ # ApiClient (http + маппинг ошибок)
│ │ ├── models/ # Budget/Expense/Job/AuthUser + JSON-хелперы
│ │ ├── repositories/ # интерфейсы + REST-реализации
│ │ └── demo/ # in-memory backend (превью, тесты, демо-режим)
│ ├── features/ # overview / journal / budgets / work / profile / auth
│ ├── theme/ # токены Apple HIG + CupertinoThemeData
│ └── ui/ # дизайн-система (atoms → molecules → navigation)
└── widgetbook/ # переносимый каталог компонентов и экранов
```
Слои связаны через интерфейсы репозиториев: экраны знают только
`BudgetRepository` / `ExpenseRepository` / `JobRepository`, а `SessionController`
подставляет REST- или demo-реализацию. Поэтому и Widgetbook, и виджет-тесты
гоняют настоящие экраны без сети.
## Тесты
```bash
cd mobile && flutter test # модели, форматтеры, контроллеры, сквозной сценарий
cd mobile/widgetbook && flutter test # рендер каждого use-case в light и dark
```