diff --git a/src/front/components/Admin/Leads.jsx b/src/front/components/Admin/Leads.jsx new file mode 100644 index 0000000000..0651b54e85 --- /dev/null +++ b/src/front/components/Admin/Leads.jsx @@ -0,0 +1,87 @@ + +import { useEffect, useReducer } from "react"; +import storeReducer, { initialStore } from "../../store"; + +const fetchAllLeads = async (dispatch) => { + const apiUrl = import.meta.env.VITE_BACKEND_URL; + dispatch({ type: 'GET_ALL_LEADS_START' }) + try { + const response = await fetch(`${apiUrl}/api/leads`) + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Error al obtener los leads"); + } + const leadsData = await response.json(); + dispatch({ type: 'GET_ALL_LEADS_SUCCESS', payload: leadsData }) + } catch (error) { + console.error("Error fetching leads:", error); + dispatch({ type: 'GET_ALL_LEADS_FAILURE', payload: error.message }) + } +} + +export const Leads = () => { + const [store, dispatch] = useReducer(storeReducer, initialStore()); + + useEffect(() => { + fetchAllLeads(dispatch); + }, [dispatch]) + + const { leads, leadsFetchStatus } = store; + + if (leadsFetchStatus.status === 'loading') { + return ( +
+
+ Cargando leads... +
+
+ ) + } + + if (leadsFetchStatus.status === 'error') { + return ( +
+ Error al cargar los leads: {leadsFetchStatus.error} +
+ ) + } + + return ( +
+
+
+ {leads.length === 0 ? ( +

No se encontraron leads

+ ) : ( + + + + + + + + + + + + + {leads.map((lead, index) => ( + + + + + + + + + ))} + +
#NombreEmailTeléfonoProyectoMensaje
{lead.id || index + 1}{lead.name}{lead.email}{lead.phone}{lead.company}{lead.message}
+ ) + } +
+
+
+ ) +} \ No newline at end of file diff --git a/src/front/components/HeaderAbout.jsx b/src/front/components/HeaderAbout.jsx index 6334bd9bdd..93006d8ea0 100644 --- a/src/front/components/HeaderAbout.jsx +++ b/src/front/components/HeaderAbout.jsx @@ -6,29 +6,29 @@ export const HeaderAbout = () => { const { t } = useTranslation(); return ( - +
- CloudTech background image -
-
-
-
-

- {t('about.sectionTitle')} -

- -

- {t('about.sectionDescription')} -

- -
- Proyectos - Contáctanos -
-
+ CloudTech background image +
+
+
+
+

+ {t('about.sectionTitle')} +

+ +

+ {t('about.sectionDescription')} +

+ +
+ {t('headerAbout.portfolioButton')} + {t('headerAbout.contactButton')}
-
+ + + ) diff --git a/src/front/components/ProtectedRoute.jsx b/src/front/components/ProtectedRoute.jsx new file mode 100644 index 0000000000..abcd4327df --- /dev/null +++ b/src/front/components/ProtectedRoute.jsx @@ -0,0 +1,8 @@ +import React, { Children } from 'react'; +import { Navigate } from 'react-router-dom'; + +export const ProtectedRoute = ({ children }) => { + const token = localStorage.getItem('accessToken'); + + return token ? children : +}; \ No newline at end of file diff --git a/src/front/index.css b/src/front/index.css index ceb313abdf..6fd4a93a70 100644 --- a/src/front/index.css +++ b/src/front/index.css @@ -362,3 +362,15 @@ h6 { .text-stat-symbol { color: #fbff06; } + +/* Admin Styles */ + +.adminOption { + color: white; + transition: color 0.3s ease; + text-decoration: none; +} + +.adminOption:hover { + color: #fbff06; +} diff --git a/src/front/pages/Admin.jsx b/src/front/pages/Admin.jsx new file mode 100644 index 0000000000..dbd9543d3f --- /dev/null +++ b/src/front/pages/Admin.jsx @@ -0,0 +1,63 @@ +import { useState, useEffect, useContext } from "react" +import { Link } from "react-router-dom" +import { Leads } from "../components/Admin/Leads" +import { AppContext } from "./Layout" + +export const Admin = () => { + const { setShowNavbar, setShowFooter } = useContext(AppContext) + const [activeContent, setActiveContent] = useState(""); + + useEffect(() => { + if (setShowNavbar || setShowFooter) { + setShowNavbar(false); + setShowFooter(false); + } + return () => { + if (setShowNavbar || setShowFooter) { + setShowNavbar(true); + setShowFooter(true); + } + } + }, [setShowNavbar, setShowFooter]) + + const handleActiveContent = (contentName) => { + setActiveContent(contentName); + } + + const renderContent = () => { + switch (activeContent) { + case 'leads': + return + default: + return

Selecciona una opción del panel.

+ } + } + + return ( +
+
+
+

Panel de Administrador

+

¡Bienvenido!

+
    +
  • +
  • + { + e.preventDefault(); + handleActiveContent('leads'); + }} + > + Ver Leads + +
  • +
+
+
+ {renderContent()} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/front/pages/Layout.jsx b/src/front/pages/Layout.jsx index 63373a0068..91beca4358 100644 --- a/src/front/pages/Layout.jsx +++ b/src/front/pages/Layout.jsx @@ -1,4 +1,4 @@ -import React, { useReducer, createContext} from "react" +import React, { useReducer, createContext, useState } from "react" import { Outlet, useLocation } from "react-router-dom/dist"; import storeReducer, { initialStore } from "../store.js"; import ScrollToTop from "../components/ScrollToTop" @@ -13,17 +13,19 @@ export const AppContext = createContext(null); export const Layout = () => { const [store, dispatch] = useReducer(storeReducer, initialStore()); + const [showNavbar, setShowNavbar] = useState(true) + const [showFooter, setShowFooter] = useState(true) const location = useLocation(); return ( - + <> - - - - -