mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 10:28:48 +02:00
Replace axios with native fetch API wrapper
Drop axios dependency in favor of a lightweight fetch-based client
that preserves the same { data: T } interface, keeping all 25
consumer files unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,25 +1,78 @@
|
||||
import axios from 'axios';
|
||||
const BASE_URL = import.meta.env.VITE_API_URL || '/api/v1';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || '/api/v1',
|
||||
});
|
||||
class ApiError extends Error {
|
||||
response: { status: number; data: unknown };
|
||||
constructor(status: number, data: unknown) {
|
||||
super(`Request failed with status ${status}`);
|
||||
this.response = { status, data };
|
||||
}
|
||||
}
|
||||
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||
return config;
|
||||
});
|
||||
interface RequestConfig {
|
||||
params?: Record<string, string | number | boolean | undefined>;
|
||||
}
|
||||
|
||||
api.interceptors.response.use(
|
||||
(res) => res,
|
||||
(err) => {
|
||||
if (err.response?.status === 401) {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
async function request<T>(method: string, url: string, body?: unknown, config?: RequestConfig): Promise<{ data: T }> {
|
||||
let fullUrl = `${BASE_URL}${url}`;
|
||||
|
||||
if (config?.params) {
|
||||
const search = new URLSearchParams();
|
||||
for (const [k, v] of Object.entries(config.params)) {
|
||||
if (v !== undefined) search.set(k, String(v));
|
||||
}
|
||||
return Promise.reject(err);
|
||||
const qs = search.toString();
|
||||
if (qs) fullUrl += `?${qs}`;
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {};
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`;
|
||||
|
||||
let fetchBody: BodyInit | undefined;
|
||||
if (body instanceof FormData || body instanceof URLSearchParams) {
|
||||
fetchBody = body;
|
||||
} else if (body !== undefined) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
fetchBody = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const res = await fetch(fullUrl, { method, headers, body: fetchBody });
|
||||
|
||||
if (res.status === 401) {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
throw new ApiError(401, null);
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
let data: unknown = null;
|
||||
try { data = await res.json(); } catch {}
|
||||
throw new ApiError(res.status, data);
|
||||
}
|
||||
|
||||
if (res.status === 204) return { data: null as T };
|
||||
|
||||
const data = await res.json();
|
||||
return { data };
|
||||
}
|
||||
|
||||
const api = {
|
||||
get<T = any>(url: string, config?: RequestConfig) {
|
||||
return request<T>('GET', url, undefined, config);
|
||||
},
|
||||
);
|
||||
post<T = any>(url: string, body?: unknown, config?: RequestConfig) {
|
||||
return request<T>('POST', url, body, config);
|
||||
},
|
||||
patch<T = any>(url: string, body?: unknown, config?: RequestConfig) {
|
||||
return request<T>('PATCH', url, body, config);
|
||||
},
|
||||
put<T = any>(url: string, body?: unknown, config?: RequestConfig) {
|
||||
return request<T>('PUT', url, body, config);
|
||||
},
|
||||
delete<T = any>(url: string, config?: RequestConfig) {
|
||||
return request<T>('DELETE', url, undefined, config);
|
||||
},
|
||||
};
|
||||
|
||||
export default api;
|
||||
|
||||
@@ -101,6 +154,7 @@ export interface Transaction {
|
||||
notes: string | null;
|
||||
category_id: number | null;
|
||||
category: Category | null;
|
||||
deferred_to_next_cycle: boolean;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
@@ -213,6 +267,7 @@ export interface MonthlyDetail {
|
||||
uncovered_actual: number;
|
||||
gran_total_egresos: number;
|
||||
net_balance: number;
|
||||
cc_by_category: { category_name: string; amount: number }[];
|
||||
}
|
||||
|
||||
// Budget API functions
|
||||
|
||||
Reference in New Issue
Block a user