# 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. Доступ только если добавить ``. ## Основные компоненты | Компонент | Файл | Назначение | |-----------|------|------------| | `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 // 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/.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)