Files
fintrack/lib/store-db.ts

190 lines
5.0 KiB
TypeScript

"use client";
import type {
BankingData,
Account,
Transaction,
Folder,
Category,
} from "./types";
const API_BASE = "/api/banking";
export async function loadData(): Promise<BankingData> {
const response = await fetch(API_BASE);
if (!response.ok) {
throw new Error("Failed to load data");
}
return response.json();
}
export async function addAccount(
account: Omit<Account, "id">,
): Promise<Account> {
const response = await fetch(`${API_BASE}/accounts`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(account),
});
if (!response.ok) {
throw new Error("Failed to add account");
}
return response.json();
}
export async function updateAccount(account: Account): Promise<Account> {
const response = await fetch(`${API_BASE}/accounts`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(account),
});
if (!response.ok) {
throw new Error("Failed to update account");
}
return response.json();
}
export async function deleteAccount(accountId: string): Promise<void> {
const response = await fetch(`${API_BASE}/accounts?id=${accountId}`, {
method: "DELETE",
});
if (!response.ok) {
throw new Error("Failed to delete account");
}
}
export async function addTransactions(
transactions: Transaction[],
): Promise<{ count: number; transactions: Transaction[] }> {
const response = await fetch(`${API_BASE}/transactions`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(transactions),
});
if (!response.ok) {
throw new Error("Failed to add transactions");
}
return response.json();
}
export async function updateTransaction(
transaction: Transaction,
): Promise<Transaction> {
const response = await fetch(`${API_BASE}/transactions`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(transaction),
});
if (!response.ok) {
throw new Error("Failed to update transaction");
}
return response.json();
}
export async function addFolder(folder: Omit<Folder, "id">): Promise<Folder> {
const response = await fetch(`${API_BASE}/folders`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(folder),
});
if (!response.ok) {
throw new Error("Failed to add folder");
}
return response.json();
}
export async function updateFolder(folder: Folder): Promise<Folder> {
const response = await fetch(`${API_BASE}/folders`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(folder),
});
if (!response.ok) {
throw new Error("Failed to update folder");
}
return response.json();
}
export async function deleteFolder(folderId: string): Promise<void> {
const response = await fetch(`${API_BASE}/folders?id=${folderId}`, {
method: "DELETE",
});
if (!response.ok) {
throw new Error("Failed to delete folder");
}
}
export async function addCategory(
category: Omit<Category, "id">,
): Promise<Category> {
const response = await fetch(`${API_BASE}/categories`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(category),
});
if (!response.ok) {
throw new Error("Failed to add category");
}
return response.json();
}
export async function updateCategory(category: Category): Promise<Category> {
const response = await fetch(`${API_BASE}/categories`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(category),
});
if (!response.ok) {
throw new Error("Failed to update category");
}
return response.json();
}
export async function deleteCategory(categoryId: string): Promise<void> {
const response = await fetch(`${API_BASE}/categories?id=${categoryId}`, {
method: "DELETE",
});
if (!response.ok) {
throw new Error("Failed to delete category");
}
}
// Auto-categorize a transaction based on keywords
export function autoCategorize(
description: string,
categories: Category[],
): string | null {
const lowerDesc = description.toLowerCase();
for (const category of categories) {
for (const keyword of category.keywords) {
const lowerKeyword = keyword.toLowerCase();
// Pour les keywords courts (< 6 chars), matcher uniquement des mots entiers
// Évite les faux positifs comme "chat" dans "achat"
if (lowerKeyword.length < 6) {
const wordBoundary = new RegExp(`\\b${escapeRegex(lowerKeyword)}\\b`);
if (wordBoundary.test(lowerDesc)) {
return category.id;
}
} else {
// Pour les keywords plus longs, includes() suffit
if (lowerDesc.includes(lowerKeyword)) {
return category.id;
}
}
}
}
return null;
}
// Échappe les caractères spéciaux pour les regex
function escapeRegex(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
export function generateId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}