# 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)
