From 997bac5d1fce1aff411a955f21e01ee8ad96fe1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=97=D0=B0=D0=B8=D0=B4=20=D0=9E=D0=BC=D0=B0=D1=80=20?= =?UTF-8?q?=D0=9C=D0=B5=D0=B4=D1=85=D0=B0=D1=82?= Date: Sat, 13 Dec 2025 15:59:21 +0500 Subject: [PATCH] chore: frontend schema --- FRONTEND_FUNCTIONS.md | 241 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 FRONTEND_FUNCTIONS.md diff --git a/FRONTEND_FUNCTIONS.md b/FRONTEND_FUNCTIONS.md new file mode 100644 index 0000000..b125be5 --- /dev/null +++ b/FRONTEND_FUNCTIONS.md @@ -0,0 +1,241 @@ +# Recommended Frontend Functions (API Client) + +This document lists recommended frontend functions to implement against the backend REST API. + +Base URL: + +- Development: `http://localhost:3000/api/v1` + +Notes: + +- Authentication uses **HTTP-only cookies**. The frontend should use `fetch`/Axios with `credentials: 'include'`. +- All endpoints below are assumed to require auth unless explicitly public (e.g. register/login). +- Suggested return types are illustrative; align with the backend DTO/response shapes. + +## Shared HTTP Helpers + +- **`apiFetch(path: string, options?: RequestInit): Promise`** + - Sets `baseUrl`, JSON headers, `credentials: 'include'`, error normalization. + +- **`parseApiError(e: unknown): { messageRu: string; status?: number; details?: any }`** + - Normalizes backend error responses. + +- **`toQueryString(params: Record): string`** + - For query endpoints (transactions search, analytics ranges, etc.). + +## Auth (Session/Cookies) + +- **`authRegister(payload: { email: string; password: string; firstName?: string; lastName?: string; phone?: string }): Promise`** + - `POST /auth/register` + +- **`authLogin(payload: { email: string; password: string }): Promise`** + - `POST /auth/login` + +- **`authRefresh(): Promise`** + - `POST /auth/refresh` + +- **`authLogout(): Promise`** + - `POST /auth/logout` + +- **`authLogoutAll(): Promise`** + - `POST /auth/logout-all` + +- **`authGetProfile(): Promise`** + - `GET /auth/profile` + +- **`authUpdateProfile(payload: { firstName?: string; lastName?: string; phone?: string }): Promise`** + - `PUT /auth/profile` + +- **`authChangePassword(payload: { currentPassword: string; newPassword: string; confirmPassword: string }): Promise`** + - `POST /auth/change-password` + +Suggested UI helpers: + +- **`useAuth()` hook** + - `user`, `isAuthenticated`, `login()`, `logout()`, `refresh()`, `loadProfile()` + +## Categories + +- **`categoriesList(params?: { type?: 'INCOME' | 'EXPENSE' }): Promise`** + - `GET /categories?type=...` + +- **`categoriesGetGrouped(): Promise<{ ESSENTIAL: Category[]; PERSONAL: Category[]; SAVINGS: Category[]; UNCATEGORIZED: Category[] }>`** + - `GET /categories/grouped` + +- **`categoriesCreate(payload: { nameRu: string; nameEn?: string; type: 'INCOME' | 'EXPENSE'; groupType?: 'ESSENTIAL' | 'PERSONAL' | 'SAVINGS'; icon?: string; color?: string; parentId?: string }): Promise`** + - `POST /categories` + +- **`categoriesUpdate(id: string, payload: Partial<{ nameRu: string; nameEn?: string; type: 'INCOME' | 'EXPENSE'; groupType?: 'ESSENTIAL' | 'PERSONAL' | 'SAVINGS'; icon?: string; color?: string; parentId?: string }>): Promise`** + - `PUT /categories/:id` + +- **`categoriesDelete(id: string): Promise`** + - `DELETE /categories/:id` + +Suggested UI helpers: + +- **`getCategoryDisplayName(cat: Category, locale: 'ru' | 'en'): string`** +- **`getCategoryBadgeStyle(cat: Category): { color: string; icon: string }`** + +## Transactions + +Core CRUD: + +- **`transactionsList(params?: { page?: number; limit?: number; type?: 'INCOME' | 'EXPENSE'; startDate?: string; endDate?: string; categoryId?: string; search?: string; minAmount?: number; maxAmount?: number; paymentMethod?: 'CASH' | 'CARD' | 'BANK_TRANSFER' }): Promise<{ data: Transaction[]; meta: PaginationMeta }>`** + - `GET /transactions?...` + +- **`transactionsGet(id: string): Promise`** + - `GET /transactions/:id` + +- **`transactionsCreate(payload: { amount: number; currency?: string; type: 'INCOME' | 'EXPENSE'; categoryId?: string; description?: string; transactionDate: string; paymentMethod?: 'CASH' | 'CARD' | 'BANK_TRANSFER'; receiptUrl?: string; isRecurring?: boolean; recurringPattern?: any; isPlanned?: boolean }): Promise`** + - `POST /transactions` + +- **`transactionsUpdate(id: string, payload: Partial<{ amount: number; categoryId?: string; description?: string; transactionDate: string; paymentMethod?: 'CASH' | 'CARD' | 'BANK_TRANSFER'; receiptUrl?: string; isRecurring?: boolean; recurringPattern?: any; isPlanned?: boolean }>): Promise`** + - `PUT /transactions/:id` + +- **`transactionsDelete(id: string): Promise`** + - `DELETE /transactions/:id` + +Bulk + reports: + +- **`transactionsBulkImport(payload: { items: Array }): Promise<{ created: number; skipped: number; errors: any[] }>`** + - `POST /transactions/bulk-import` + +- **`transactionsSummary(params: { startDate: string; endDate: string }): Promise<{ totalIncome: number; totalExpense: number; net: number }>`** + - `GET /transactions/summary?startDate=...&endDate=...` + +- **`transactionsSpendingByCategory(params: { startDate: string; endDate: string }): Promise>`** + - `GET /transactions/spending-by-category?startDate=...&endDate=...` + +Suggested UI helpers: + +- **`formatMoneyRu(amount: number, currency: string = 'RUB'): string`** +- **`groupTransactionsByDay(transactions: Transaction[]): Record`** + +## Budgets (50/30/20) + +- **`budgetsList(): Promise`** + - `GET /budgets` + +- **`budgetsGetCurrent(): Promise`** + - `GET /budgets/current` + +- **`budgetsGetByMonth(month: string /* YYYY-MM-01 */): Promise`** + - `GET /budgets/month?month=...` + +- **`budgetsCreate(payload: { month: string; totalIncome: number; essentialsPercent?: number; personalPercent?: number; savingsPercent?: number }): Promise`** + - `POST /budgets` + +- **`budgetsUpdate(month: string, payload: Partial<{ totalIncome: number; essentialsPercent?: number; personalPercent?: number; savingsPercent?: number }>): Promise`** + - `PUT /budgets?month=...` + +- **`budgetsDelete(month: string): Promise`** + - `DELETE /budgets?month=...` + +- **`budgetsProgress(month: string): Promise<{ essentials: Progress; personal: Progress; savings: Progress; total: TotalProgress }>`** + - `GET /budgets/progress?month=...` + +Suggested UI helpers: + +- **`budgetProgressColor(pct: number): 'green' | 'yellow' | 'red'`** + +## Goals + +- **`goalsList(params?: { status?: 'ACTIVE' | 'COMPLETED' | 'PAUSED' | 'CANCELLED' }): Promise`** + - `GET /goals?status=...` + +- **`goalsGet(id: string): Promise`** + - `GET /goals/:id` + +- **`goalsCreate(payload: { titleRu: string; descriptionRu?: string; targetAmount: number; currentAmount?: number; targetDate?: string; priority?: 'LOW' | 'MEDIUM' | 'HIGH'; icon?: string; color?: string; autoSaveEnabled?: boolean; autoSaveAmount?: number; autoSaveFrequency?: 'DAILY' | 'WEEKLY' | 'MONTHLY' }): Promise`** + - `POST /goals` + +- **`goalsUpdate(id: string, payload: Partial<...same as create...>): Promise`** + - `PUT /goals/:id` + +- **`goalsDelete(id: string): Promise`** + - `DELETE /goals/:id` + +- **`goalsAddFunds(id: string, payload: { amount: number; note?: string }): Promise`** + - `POST /goals/:id/add-funds` + +- **`goalsWithdraw(id: string, payload: { amount: number; note?: string }): Promise`** + - `POST /goals/:id/withdraw` + +- **`goalsSummary(): Promise<{ totalGoals: number; activeGoals: number; completedGoals: number; totalTargetAmount: number; totalCurrentAmount: number; overallProgress: number }>`** + - `GET /goals/summary` + +- **`goalsUpcomingDeadlines(days?: number): Promise`** + - `GET /goals/upcoming?days=...` + +Suggested UI helpers: + +- **`goalProgress(goal: Goal): { percent: number; remaining: number }`** +- **`goalStatusLabelRu(status: string): string`** + +## Recommendations + +- **`recommendationsList(params?: { type?: 'SAVING' | 'SPENDING' | 'INVESTMENT' | 'TAX' | 'DEBT' | 'BUDGET' | 'GOAL' }): Promise`** + - `GET /recommendations?type=...` + +- **`recommendationsGet(id: string): Promise`** + - `GET /recommendations/:id` + +- **`recommendationsGenerate(): Promise`** + - `POST /recommendations/generate` + +- **`recommendationsMarkViewed(id: string): Promise`** + - `POST /recommendations/:id/view` + +- **`recommendationsApply(id: string): Promise`** + - `POST /recommendations/:id/apply` + +- **`recommendationsDismiss(id: string): Promise`** + - `POST /recommendations/:id/dismiss` + +- **`recommendationsStats(): Promise<{ total: number; new: number; viewed: number; applied: number; dismissed: number; potentialSavings: number }>`** + - `GET /recommendations/stats` + +Suggested UI helpers: + +- **`recommendationBadge(type: string): { labelRu: string; color: string }`** + +## Analytics + +- **`analyticsMonthlyOverview(month?: string /* YYYY-MM */): Promise`** + - `GET /analytics/overview?month=...` + +- **`analyticsTrends(months?: number): Promise`** + - `GET /analytics/trends?months=...` + +- **`analyticsCategoryBreakdown(params: { startDate: string; endDate: string; type?: 'INCOME' | 'EXPENSE' }): Promise`** + - `GET /analytics/categories?startDate=...&endDate=...&type=...` + +- **`analyticsIncomeVsExpenses(months?: number): Promise>`** + - `GET /analytics/income-vs-expenses?months=...` + +- **`analyticsFinancialHealth(): Promise`** + - `GET /analytics/health` + +- **`analyticsYearly(year?: number): Promise`** + - `GET /analytics/yearly?year=...` + +Suggested UI helpers: + +- **`financialHealthBadge(grade: 'A'|'B'|'C'|'D'|'F'): { labelRu: string; color: string }`** + +## Suggested Frontend Architecture + +- `src/api/`: + - `client.ts` (`apiFetch`, interceptors, base URL) + - `auth.ts`, `categories.ts`, `transactions.ts`, `budgets.ts`, `goals.ts`, `recommendations.ts`, `analytics.ts` +- `src/types/`: + - shared types mirrored from backend DTOs/entities +- `src/hooks/`: + - `useAuth`, `useTransactions`, `useBudgets`, `useGoals`, `useRecommendations`, `useAnalytics` + +## Minimal Types (Suggested) + +- `User`, `Category`, `Transaction`, `Budget`, `Goal`, `Recommendation` +- `PaginationMeta` + +(Define based on Swagger at `/api/docs` once backend is running.)