diff --git a/.gitignore b/backend/.gitignore similarity index 99% rename from .gitignore rename to backend/.gitignore index 4e07be5..ea8430a 100644 --- a/.gitignore +++ b/backend/.gitignore @@ -3,7 +3,7 @@ __pycache__/ *.py[codz] *$py.class -backend/data +data # C extensions *.so diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts new file mode 100644 index 0000000..423d67a --- /dev/null +++ b/frontend/src/lib/api.ts @@ -0,0 +1,59 @@ +const API_BASE_URL = ''; // Relative path because of proxy + +interface RequestOptions extends RequestInit { + headers?: Record; +} + +async function request(url: string, options: RequestOptions = {}): Promise { + const token = localStorage.getItem('token'); + + const defaultHeaders = { + 'Content-Type': 'application/json', + ...(token ? { 'Authorization': `Bearer ${token}` } : {}), + }; + + const config: RequestInit = { + ...options, + headers: { + ...defaultHeaders, + ...options.headers, + }, + }; + + try { + const response = await fetch(`${API_BASE_URL}${url}`, config); + + if (!response.ok) { + if (response.status === 401) { + // Handle unauthorized (e.g., redirect to login or clear store) + localStorage.removeItem('token'); + localStorage.removeItem('user'); + // You might want to trigger a custom event or use window.location here + } + + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.detail || errorData.message || `API Error: ${response.statusText}`); + } + + // Handle empty responses (e.g. 204 No Content) + if (response.status === 204) { + return {} as T; + } + + return await response.json(); + } catch (error) { + console.error('API Request Failed:', error); + throw error; + } +} + +export const api = { + get: (url: string, options?: RequestOptions) => request(url, { ...options, method: 'GET' }), + post: (url: string, data: any, options?: RequestOptions) => + request(url, { ...options, method: 'POST', body: JSON.stringify(data) }), + put: (url: string, data: any, options?: RequestOptions) => + request(url, { ...options, method: 'PUT', body: JSON.stringify(data) }), + delete: (url: string, options?: RequestOptions) => request(url, { ...options, method: 'DELETE' }), + patch: (url: string, data: any, options?: RequestOptions) => + request(url, { ...options, method: 'PATCH', body: JSON.stringify(data) }), +}; diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/frontend/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +}