Files
uis-cloud-computing/7project/frontend/src/api.ts

228 lines
6.4 KiB
TypeScript

import { BACKEND_URL } from './config';
export type LoginResponse = {
access_token: string;
token_type: string;
};
export type Category = {
id: number;
name: string;
description?: string | null;
};
export type Transaction = {
id: number;
amount: number;
description?: string | null;
category_ids: number[];
date?: string | null; // ISO date (YYYY-MM-DD)
};
function getBaseUrl() {
const base = BACKEND_URL?.replace(/\/$/, '') || '';
return base || '';
}
function getHeaders(contentType: 'json' | 'form' | 'none' = 'json'): Record<string, string> {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {};
if (contentType === 'json') {
headers['Content-Type'] = 'application/json';
} else if (contentType === 'form') {
headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
export async function login(email: string, password: string): Promise<void> {
const body = new URLSearchParams();
body.set('username', email);
body.set('password', password);
const res = await fetch(`${getBaseUrl()}/auth/jwt/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body.toString(),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Login failed');
}
const data: LoginResponse = await res.json();
localStorage.setItem('token', data.access_token);
}
export async function register(email: string, password: string, first_name?: string, last_name?: string): Promise<void> {
const res = await fetch(`${getBaseUrl()}/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, first_name, last_name }),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Registration failed');
}
}
export async function getCategories(): Promise<Category[]> {
const res = await fetch(`${getBaseUrl()}/categories/`, {
headers: getHeaders(),
});
if (!res.ok) throw new Error('Failed to load categories');
return res.json();
}
export type CreateTransactionInput = {
amount: number;
description?: string;
category_ids?: number[];
date?: string; // YYYY-MM-DD
};
export async function createTransaction(input: CreateTransactionInput): Promise<Transaction> {
const res = await fetch(`${getBaseUrl()}/transactions/create`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(input),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Failed to create transaction');
}
return res.json();
}
export async function getTransactions(start_date?: string, end_date?: string): Promise<Transaction[]> {
const params = new URLSearchParams();
if (start_date) params.set('start_date', start_date);
if (end_date) params.set('end_date', end_date);
const qs = params.toString();
const url = `${getBaseUrl()}/transactions/${qs ? `?${qs}` : ''}`;
const res = await fetch(url, {
headers: getHeaders(),
});
if (!res.ok) throw new Error('Failed to load transactions');
return res.json();
}
export type User = {
id: string;
email: string;
first_name?: string | null;
last_name?: string | null;
is_active: boolean;
is_superuser: boolean;
is_verified: boolean;
};
export async function getMe(): Promise<User> {
const res = await fetch(`${getBaseUrl()}/users/me`, {
headers: getHeaders(),
});
if (!res.ok) throw new Error('Failed to load user');
return res.json();
}
export type UpdateMeInput = Partial<Pick<User, 'first_name' | 'last_name'>> & { password?: string };
export async function updateMe(input: UpdateMeInput): Promise<User> {
const res = await fetch(`${getBaseUrl()}/users/me`, {
method: 'PATCH',
headers: getHeaders(),
body: JSON.stringify(input),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Failed to update user');
}
return res.json();
}
export async function deleteMe(): Promise<void> {
const res = await fetch(`${getBaseUrl()}/users/me`, {
method: 'DELETE',
headers: getHeaders(),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Failed to delete account');
}
}
export function logout() {
localStorage.removeItem('token');
}
// Categories
export type CreateCategoryInput = { name: string; description?: string };
export async function createCategory(input: CreateCategoryInput): Promise<Category> {
const res = await fetch(`${getBaseUrl()}/categories/create`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(input),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Failed to create category');
}
return res.json();
}
export type UpdateCategoryInput = { name?: string; description?: string };
export async function updateCategory(category_id: number, input: UpdateCategoryInput): Promise<Category> {
const res = await fetch(`${getBaseUrl()}/categories/${category_id}`, {
method: 'PATCH',
headers: getHeaders(),
body: JSON.stringify(input),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Failed to update category');
}
return res.json();
}
// Transactions update
export type UpdateTransactionInput = {
amount?: number;
description?: string;
date?: string;
category_ids?: number[];
};
export async function updateTransaction(id: number, input: UpdateTransactionInput): Promise<Transaction> {
const res = await fetch(`${getBaseUrl()}/transactions/${id}/edit`, {
method: 'PATCH',
headers: getHeaders(),
body: JSON.stringify(input),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Failed to update transaction');
}
return res.json();
}
// Balance series
export type BalancePoint = { date: string; balance: number };
export async function getBalanceSeries(start_date?: string, end_date?: string): Promise<BalancePoint[]> {
const params = new URLSearchParams();
if (start_date) params.set('start_date', start_date);
if (end_date) params.set('end_date', end_date);
const qs = params.toString();
const url = `${getBaseUrl()}/transactions/balance_series${qs ? `?${qs}` : ''}`;
const res = await fetch(url, { headers: getHeaders() });
if (!res.ok) {
const text = await res.text();
throw new Error(text || 'Failed to load balance series');
}
return res.json();
}