chore: estructura inicial del MVP
Backend (Express + MariaDB) y frontend (React + Vite, identidad Fluent/Windows) con el nucleo funcional: seguridad, organizacion, proyectos, actividades, subtareas, dependencias, dashboard, calendario, gantt, comentarios, adjuntos, notificaciones in-app y catalogos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
const API_URL = import.meta.env.VITE_API_URL || '/api';
|
||||
|
||||
let onUnauthorized = () => {};
|
||||
export function setUnauthorizedHandler(fn) {
|
||||
onUnauthorized = fn;
|
||||
}
|
||||
|
||||
function getToken() {
|
||||
return localStorage.getItem('plc_token');
|
||||
}
|
||||
|
||||
async function request(method, path, { body, isForm } = {}) {
|
||||
const headers = {};
|
||||
const token = getToken();
|
||||
if (token) headers.Authorization = `Bearer ${token}`;
|
||||
if (!isForm && body !== undefined) headers['Content-Type'] = 'application/json';
|
||||
|
||||
const res = await fetch(`${API_URL}${path}`, {
|
||||
method,
|
||||
headers,
|
||||
body: isForm ? body : body !== undefined ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
|
||||
if (res.status === 401) {
|
||||
onUnauthorized();
|
||||
}
|
||||
|
||||
if (res.status === 204) return null;
|
||||
|
||||
const isJson = (res.headers.get('content-type') || '').includes('application/json');
|
||||
const payload = isJson ? await res.json() : null;
|
||||
|
||||
if (!res.ok) {
|
||||
const message = payload?.error || `Error ${res.status}`;
|
||||
throw new Error(message);
|
||||
}
|
||||
return payload?.data;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
get: (path) => request('GET', path),
|
||||
post: (path, body) => request('POST', path, { body }),
|
||||
put: (path, body) => request('PUT', path, { body }),
|
||||
patch: (path, body) => request('PATCH', path, { body }),
|
||||
delete: (path) => request('DELETE', path),
|
||||
postForm: (path, formData) => request('POST', path, { body: formData, isForm: true }),
|
||||
setToken: (token) => {
|
||||
if (token) localStorage.setItem('plc_token', token);
|
||||
else localStorage.removeItem('plc_token');
|
||||
},
|
||||
downloadUrl: (path) => `${API_URL}${path}`,
|
||||
};
|
||||
Reference in New Issue
Block a user