mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 08: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:
@@ -13,7 +13,6 @@
|
||||
"@fontsource-variable/ibm-plex-sans": "^5.2.8",
|
||||
"@fontsource-variable/noto-sans": "^5.2.10",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"axios": "^1.13.6",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.562.0",
|
||||
|
||||
94
frontend/pnpm-lock.yaml
generated
94
frontend/pnpm-lock.yaml
generated
@@ -20,9 +20,6 @@ importers:
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.21.3
|
||||
version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
axios:
|
||||
specifier: ^1.13.6
|
||||
version: 1.13.6
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
@@ -928,12 +925,6 @@ packages:
|
||||
resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
axios@1.13.6:
|
||||
resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==}
|
||||
|
||||
balanced-match@4.0.4:
|
||||
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
@@ -1020,10 +1011,6 @@ packages:
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
commander@11.1.0:
|
||||
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -1164,10 +1151,6 @@ packages:
|
||||
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -1234,10 +1217,6 @@ packages:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
esbuild@0.27.4:
|
||||
resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1330,19 +1309,6 @@ packages:
|
||||
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
|
||||
follow-redirects@1.15.11:
|
||||
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
form-data@4.0.5:
|
||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
@@ -1421,10 +1387,6 @@ packages:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1723,18 +1685,10 @@ packages:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
mime-db@1.52.0:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-db@1.54.0:
|
||||
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@3.0.2:
|
||||
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1913,9 +1867,6 @@ packages:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
qs@6.15.0:
|
||||
resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==}
|
||||
engines: {node: '>=0.6'}
|
||||
@@ -3094,16 +3045,6 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
axios@1.13.6:
|
||||
dependencies:
|
||||
follow-redirects: 1.15.11
|
||||
form-data: 4.0.5
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
balanced-match@4.0.4: {}
|
||||
|
||||
baseline-browser-mapping@2.10.10: {}
|
||||
@@ -3188,10 +3129,6 @@ snapshots:
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
commander@11.1.0: {}
|
||||
|
||||
commander@14.0.3: {}
|
||||
@@ -3291,8 +3228,6 @@ snapshots:
|
||||
|
||||
define-lazy-prop@3.0.0: {}
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
@@ -3348,13 +3283,6 @@ snapshots:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.3.0
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.2
|
||||
|
||||
esbuild@0.27.4:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.4
|
||||
@@ -3511,16 +3439,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
follow-redirects@1.15.11: {}
|
||||
|
||||
form-data@4.0.5:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
es-set-tostringtag: 2.1.0
|
||||
hasown: 2.0.2
|
||||
mime-types: 2.1.35
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
@@ -3587,10 +3505,6 @@ snapshots:
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
dependencies:
|
||||
has-symbols: 1.1.0
|
||||
|
||||
hasown@2.0.2:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
@@ -3804,14 +3718,8 @@ snapshots:
|
||||
braces: 3.0.3
|
||||
picomatch: 2.3.1
|
||||
|
||||
mime-db@1.52.0: {}
|
||||
|
||||
mime-db@1.54.0: {}
|
||||
|
||||
mime-types@2.1.35:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
mime-types@3.0.2:
|
||||
dependencies:
|
||||
mime-db: 1.54.0
|
||||
@@ -3989,8 +3897,6 @@ snapshots:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
qs@6.15.0:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
||||
@@ -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