Initial commit
This commit is contained in:
10
frontend/src/App.jsx
Normal file
10
frontend/src/App.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
131
frontend/src/css/index.css
Normal file
131
frontend/src/css/index.css
Normal file
@@ -0,0 +1,131 @@
|
||||
/* ================================
|
||||
FUENTES PERSONALIZADAS
|
||||
================================== */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
src: url('/fonts/OpenSans.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Product Sans";
|
||||
src: url('/fonts/ProductSansRegular.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Product Sans Italic";
|
||||
src: url('/fonts/ProductSansItalic.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Product Sans Italic Bold";
|
||||
src: url('/fonts/ProductSansBoldItalic.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Product Sans Bold";
|
||||
src: url('/fonts/ProductSansBold.ttf');
|
||||
}
|
||||
|
||||
/* ================================
|
||||
PALETA DE COLORES
|
||||
================================== */
|
||||
|
||||
:root {
|
||||
/* Colores base */
|
||||
--bondi-blue-light: #5bc0da;
|
||||
--bondi-blue: #359ebc;
|
||||
--bondi-blue-dark: #0c7fa3;
|
||||
--bondi-blue-darker: #09698a;
|
||||
|
||||
--gun-powder-lighter: #686983;
|
||||
--gun-powder-light: #4e5067;
|
||||
--gun-powder: #3d3c52;
|
||||
--gun-powder-dark: #303145;
|
||||
|
||||
/* 🎨 Fondos */
|
||||
--background-main: var(--gun-powder-dark);
|
||||
--background-alt: var(--gun-powder);
|
||||
--background-card: var(--gun-powder-light);
|
||||
--background-hover: var(--bondi-blue-dark);
|
||||
--background-accent: var(--bondi-blue);
|
||||
|
||||
/* ✍️ Texto */
|
||||
--text-main: #f0f4f8;
|
||||
--text-muted: #c2c6d1;
|
||||
--text-inverted: #0d1117;
|
||||
--text-accent: var(--bondi-blue-light);
|
||||
--text-link: var(--bondi-blue);
|
||||
|
||||
/* 🪞 Bordes */
|
||||
--border-color: #2c2d3e;
|
||||
--border-hover: var(--bondi-blue-dark);
|
||||
--border-accent: var(--bondi-blue);
|
||||
|
||||
/* 💡 Sombras */
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25);
|
||||
--shadow-md: 0 2px 6px rgba(0, 0, 0, 0.35);
|
||||
--shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.45);
|
||||
--shadow-accent: 0 0 12px rgba(53, 158, 188, 0.4);
|
||||
|
||||
/* 🔘 Botones */
|
||||
--button-bg: var(--bondi-blue);
|
||||
--button-bg-hover: var(--bondi-blue-dark);
|
||||
--button-text: #ffffff;
|
||||
--button-disabled: #5f7080;
|
||||
|
||||
/* 🧭 Inputs */
|
||||
--input-bg: var(--gun-powder);
|
||||
--input-border: var(--gun-powder-lighter);
|
||||
--input-border-focus: var(--bondi-blue);
|
||||
--input-text: var(--text-main);
|
||||
--input-placeholder: #a0a4b2;
|
||||
|
||||
/* 🚨 Estados */
|
||||
--success: #4cc9b0;
|
||||
--warning: #f3c969;
|
||||
--error: #e85d75;
|
||||
--info: var(--bondi-blue-light);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
ESTILOS BASE / RESET SUAVE
|
||||
================================== */
|
||||
html {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
color: var(--text-main);
|
||||
background-color: var(--background-main);
|
||||
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
color: var(--text-main);
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
main {
|
||||
color: var(--text-main);
|
||||
background-color: var(--background-main);
|
||||
}
|
||||
|
||||
/* Tipografía global */
|
||||
div,
|
||||
label,
|
||||
input,
|
||||
p,
|
||||
span,
|
||||
a,
|
||||
button {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Product Sans", sans-serif;
|
||||
color: var(--text-main);
|
||||
}
|
||||
130
frontend/src/hooks/useData.js
Normal file
130
frontend/src/hooks/useData.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
export const useData = (config) => {
|
||||
const [data, setData] = useState(null);
|
||||
const [dataLoading, setLoading] = useState(true);
|
||||
const [dataError, setError] = useState(null);
|
||||
const configRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (config?.baseUrl) {
|
||||
configRef.current = config;
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
const getAuthHeaders = () => ({
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${localStorage.getItem("token")}`,
|
||||
});
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
const current = configRef.current;
|
||||
if (!current?.baseUrl) return;
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await axios.get(current.baseUrl, {
|
||||
headers: getAuthHeaders(),
|
||||
params: current.params,
|
||||
});
|
||||
setData(response.data.data);
|
||||
} catch (err) {
|
||||
setError(err.response?.data?.message || err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (config?.baseUrl) {
|
||||
fetchData();
|
||||
}
|
||||
}, [config, fetchData]);
|
||||
|
||||
const getData = async (url, params = {}) => {
|
||||
try {
|
||||
const response = await axios.get(url, {
|
||||
headers: getAuthHeaders(),
|
||||
params,
|
||||
});
|
||||
return { data: response.data.data, error: null };
|
||||
} catch (err) {
|
||||
return {
|
||||
data: null,
|
||||
error: err.response?.data?.message || err.message,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const postData = async (endpoint, payload) => {
|
||||
const headers = {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
...(payload instanceof FormData ? {} : { "Content-Type": "application/json" }),
|
||||
};
|
||||
const response = await axios.post(endpoint, payload, { headers });
|
||||
await fetchData();
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
const postDataValidated = async (endpoint, payload) => {
|
||||
try {
|
||||
const headers = {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
...(payload instanceof FormData ? {} : { "Content-Type": "application/json" }),
|
||||
};
|
||||
const response = await axios.post(endpoint, payload, { headers });
|
||||
return { data: response.data.data, errors: null };
|
||||
} catch (err) {
|
||||
const raw = err.response?.data?.message;
|
||||
let parsed = {};
|
||||
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch {
|
||||
return { data: null, errors: { general: raw || err.message } };
|
||||
}
|
||||
|
||||
return { data: null, errors: parsed };
|
||||
}
|
||||
};
|
||||
|
||||
const putData = async (endpoint, payload) => {
|
||||
const response = await axios.put(endpoint, payload, {
|
||||
headers: getAuthHeaders(),
|
||||
});
|
||||
await fetchData();
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
const deleteData = async (endpoint) => {
|
||||
const response = await axios.delete(endpoint, {
|
||||
headers: getAuthHeaders(),
|
||||
});
|
||||
await fetchData();
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
const deleteDataWithBody = async (endpoint, payload) => {
|
||||
const response = await axios.delete(endpoint, {
|
||||
headers: getAuthHeaders(),
|
||||
data: payload,
|
||||
});
|
||||
await fetchData();
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
return {
|
||||
data,
|
||||
dataLoading,
|
||||
dataError,
|
||||
getData,
|
||||
postData,
|
||||
postDataValidated,
|
||||
putData,
|
||||
deleteData,
|
||||
deleteDataWithBody,
|
||||
};
|
||||
};
|
||||
13
frontend/src/main.jsx
Normal file
13
frontend/src/main.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import '@/css/index.css'
|
||||
import App from '@/App.jsx'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>
|
||||
)
|
||||
Reference in New Issue
Block a user