feat(proj): init

This commit is contained in:
vl.arkhangelskii
2026-09-21 04:06:43 +03:00
commit c956b94983
1076 changed files with 50876 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
# 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
```