Files
obsidian/Инфраструктура/pulse-web.md

264 lines
12 KiB
Markdown
Raw 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.
# pulse-web — Архитектура
**Репозиторий:** `https://git.digital-home.site/daniil/pulse-web`
**URL:** `https://pulse.digital-home.site`
**Dev:** `http://192.168.31.60:5174`
**Storybook:** `http://192.168.31.60:6006`
## Общая архитектура
React SPA (Single Page Application):
```
src/
api/ ← API-функции (axios, по доменам)
store/ ← State management (Zustand)
contexts/ ← React Context (тема)
pages/ ← Страницы (маршруты)
components/ ← Переиспользуемые компоненты
main.jsx ← Точка входа (BrowserRouter + ThemeContext)
App.jsx ← Роутер (Routes/Route)
index.css ← Глобальные стили (Tailwind)
```
**Стек:**
- Framework: React 18
- Bundler: Vite 5
- Роутинг: React Router DOM 6
- State: Zustand 4
- HTTP: Axios (с interceptors для JWT refresh)
- UI: Tailwind CSS 3
- Анимации: Framer Motion 11
- Иконки: Lucide React
- Графики: Recharts 2
- Утилиты дат: date-fns 3
- Компонент-либа: Storybook 8
- Тесты: Vitest + Testing Library
## Структура папок
```
pulse-web/
├── src/
│ ├── api/
│ │ ├── client.js # Axios instance + JWT interceptor
│ │ ├── auth.js # (в store/auth.js)
│ │ ├── tasks.js # tasksApi: list, today, create, complete...
│ │ ├── habits.js # habitsApi: list, create, log, stats...
│ │ ├── finance.js # financeApi: categories, transactions, summary
│ │ ├── savings.js # savingsApi: categories, transactions, stats
│ │ └── profile.js # profileApi: get, update
│ ├── store/
│ │ └── auth.js # useAuthStore (Zustand): user, login, logout
│ ├── contexts/
│ │ └── ThemeContext.jsx # ThemeProvider: light/dark, localStorage
│ ├── pages/
│ │ ├── Home.jsx # Главная страница (дашборд)
│ │ ├── Tracker.jsx # Трекер (таб: привычки/задачи/статистика)
│ │ ├── Habits.jsx # Страница привычек (встраивается в Tracker)
│ │ ├── Tasks.jsx # Страница задач (встраивается в Tracker)
│ │ ├── Stats.jsx # Статистика (встраивается в Tracker)
│ │ ├── Finance.jsx # Финансы (таб: обзор/транзакции/аналитика/категории)
│ │ ├── Savings.jsx # Накопления
│ │ ├── Settings.jsx # Настройки пользователя
│ │ ├── Login.jsx # Вход
│ │ ├── Register.jsx # Регистрация
│ │ ├── ForgotPassword.jsx # Сброс пароля
│ │ ├── ResetPassword.jsx # Новый пароль (по токену из email)
│ │ └── VerifyEmail.jsx # Подтверждение email
│ ├── components/
│ │ ├── Navigation.jsx # Нижняя навигация (fixed bottom)
│ │ ├── CreateTaskModal.jsx
│ │ ├── EditTaskModal.jsx
│ │ ├── CreateHabitModal.jsx
│ │ ├── EditHabitModal.jsx
│ │ ├── LogHabitModal.jsx
│ │ └── finance/
│ │ ├── FinanceDashboard.jsx # Обзор месяца
│ │ ├── TransactionList.jsx # Список транзакций
│ │ ├── FinanceAnalytics.jsx # Графики трендов
│ │ ├── CategoriesManager.jsx # Управление категориями
│ │ └── AddTransactionModal.jsx
│ ├── App.jsx # Routes + ProtectedRoute/PublicRoute
│ ├── main.jsx # ReactDOM.render + Providers
│ └── index.css # Tailwind + кастомные стили
├── public/
├── package.json
├── vite.config.js
├── tailwind.config.js
├── nginx.conf
└── Dockerfile
```
## Страницы / Роуты
| Путь | Компонент | Защита | Описание |
|------|-----------|--------|----------|
| `/login` | `Login` | Public only | Форма входа |
| `/register` | `Register` | Public only | Форма регистрации |
| `/forgot-password` | `ForgotPassword` | Public only | Запрос сброса пароля |
| `/verify-email` | `VerifyEmail` | Нет | Подтверждение email по токену |
| `/reset-password` | `ResetPassword` | Нет | Установка нового пароля |
| `/` | `Home` | Protected | Главная: дашборд |
| `/tracker` | `Tracker` | Protected | Трекер привычек/задач/статистики |
| `/habits` | → `/tracker` | Protected | Редирект на трекер |
| `/tasks` | → `/tracker` | Protected | Редирект на трекер |
| `/stats` | → `/tracker` | Protected | Редирект на трекер |
| `/savings` | `Savings` | Protected | Накопления |
| `/settings` | `Settings` | Protected | Настройки аккаунта |
| `*` | → `/` | — | Любой неизвестный → главная |
### Tracker (вкладки)
`/tracker` содержит 3 вкладки:
1. **Привычки**`Habits` компонент
2. **Задачи**`Tasks` компонент
3. **Статистика**`Stats` компонент
### Finance (вкладки)
`/finance` (доступна через роутер если добавить) содержит 4 вкладки:
1. **Обзор**`FinanceDashboard`
2. **Транзакции**`TransactionList`
3. **Аналитика**`FinanceAnalytics`
4. **Категории**`CategoriesManager`
> ⚠️ Страница Finance рендерится, но **нет роута** в App.jsx. Доступ только если добавить `<Route path="/finance">`.
## Основные компоненты
| Компонент | Файл | Назначение |
|-----------|------|------------|
| `Navigation` | components/Navigation.jsx | Нижняя навбар (Главная, Трекер, Накопления, Настройки). Показывает Finance только для `user.id === 1` (owner) |
| `ProtectedRoute` | App.jsx | Редиректит на `/login` если не авторизован |
| `PublicRoute` | App.jsx | Редиректит на `/` если уже авторизован |
| `FinanceDashboard` | finance/ | Обзор месяца: баланс, доходы/расходы, по категориям, дневной график |
| `TransactionList` | finance/ | Список транзакций с фильтрацией |
| `FinanceAnalytics` | finance/ | Recharts: тренды по месяцам |
| `CategoriesManager` | finance/ | CRUD категорий |
| `AddTransactionModal` | finance/ | Модал добавления транзакции |
| `CreateTaskModal` | components/ | Создание задачи |
| `EditTaskModal` | components/ | Редактирование задачи |
| `CreateHabitModal` | components/ | Создание привычки |
| `LogHabitModal` | components/ | Отметка привычки |
## API вызовы
### Axios Client (`src/api/client.js`)
```js
const api = axios.create({ baseURL: VITE_API_URL })
// Request interceptor: добавляет Bearer токен из localStorage
api.interceptors.request Authorization: Bearer <access_token>
// Response interceptor: при 401 делает refresh и повторяет запрос
api.interceptors.response POST /auth/refresh обновляет tokens в localStorage
```
### API модули
```js
// tasks.js
tasksApi.list(completed?) GET /tasks
tasksApi.today() GET /tasks/today
tasksApi.create(data) POST /tasks
tasksApi.complete(id) POST /tasks/{id}/complete
tasksApi.uncomplete(id) POST /tasks/{id}/uncomplete
tasksApi.update(id, data) PUT /tasks/{id}
tasksApi.delete(id) DELETE /tasks/{id}
// habits.js
habitsApi.list() GET /habits
habitsApi.create(data) POST /habits
habitsApi.log(id, data) POST /habits/{id}/log
habitsApi.stats() GET /habits/stats
habitsApi.habitStats(id) GET /habits/{id}/stats
// finance.js
financeApi.getCategories() GET /finance/categories
financeApi.createCategory(data) POST /finance/categories
financeApi.getTransactions(m, y) GET /finance/transactions?month=&year=
financeApi.createTransaction(data) POST /finance/transactions
financeApi.getSummary(m, y) GET /finance/summary
financeApi.getAnalytics() GET /finance/analytics
// savings.js
savingsApi.getCategories() GET /savings/categories
savingsApi.createCategory(data) POST /savings/categories
savingsApi.getTransactions() GET /savings/transactions
savingsApi.getStats() GET /savings/stats
// profile.js
profileApi.get() GET /profile
profileApi.update(data) PUT /profile
```
## State Management
**Zustand** — единственный store: `src/store/auth.js`
```js
useAuthStore = {
user: null | User,
isLoading: boolean,
isAuthenticated: boolean,
initialize() // Вызывается в App.jsx useEffect: GET /auth/me
login(email, password) // POST /auth/login, сохраняет tokens
register(email, username, password)
logout() // Чистит localStorage + state
}
```
**React Context:**
- `ThemeContext` (`contexts/ThemeContext.jsx`) — dark/light mode, сохраняется в localStorage
**Нет Redux / React Query** — данные загружаются локально в компонентах через `useState + useEffect`.
## Основные зависимости
| Пакет | Версия | Назначение |
|-------|--------|------------|
| react | 18.2 | UI framework |
| react-router-dom | 6.22 | Роутинг |
| zustand | 4.5 | State management |
| axios | 1.6 | HTTP клиент |
| tailwindcss | 3.4 | CSS utility framework |
| framer-motion | 11 | Анимации |
| lucide-react | 0.312 | Иконки |
| recharts | 2.12 | Графики |
| date-fns | 3.3 | Утилиты дат |
| clsx | 2.1 | Условные классы |
| storybook | 8.5 | UI компонент-браузер (dev) |
| vitest | 4 | Тесты |
## Конфигурация
| Env переменная | Описание | Default |
|---------------|----------|---------|
| `VITE_API_URL` | URL backend API | `https://api.digital-home.site` |
## Где искать что
| Задача | Файл |
|--------|------|
| **Новая страница** | `src/pages/NewPage.jsx` + роут в `App.jsx` + ссылка в `Navigation.jsx` |
| **Новый компонент** | `src/components/NewComponent.jsx` |
| **Новый API вызов** | `src/api/<domain>.js` (добавить метод) |
| **Финансы: UI** | `src/pages/Finance.jsx`, `src/components/finance/` |
| **Финансы: API** | `src/api/finance.js` |
| **Привычки: UI** | `src/pages/Habits.jsx`, `CreateHabitModal`, `LogHabitModal` |
| **Привычки: API** | `src/api/habits.js` |
| **Задачи: UI** | `src/pages/Tasks.jsx`, `CreateTaskModal`, `EditTaskModal` |
| **Задачи: API** | `src/api/tasks.js` |
| **Авторизация** | `src/store/auth.js`, `src/pages/Login.jsx` |
| **Глобальные стили** | `src/index.css` + `tailwind.config.js` |
| **Темная тема** | `src/contexts/ThemeContext.jsx` |
| **Нижняя навигация** | `src/components/Navigation.jsx` |
## Заметки
- `Finance` страница не добавлена в роутер и навигацию для обычных пользователей — только для owner (id=1)
- Legacy роуты `/habits`, `/tasks`, `/stats` → редиректят на `/tracker`
- Токены хранятся в `localStorage`: `access_token`, `refresh_token`
- Auto-refresh токена при 401 ответе (в axios interceptor)