diff --git a/package.json b/package.json
index 8af2be3..20c186a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "webfam",
"private": true,
- "version": "1.3.0",
+ "version": "1.5.0",
"type": "module",
"scripts": {
"dev": "vite --open",
diff --git a/src/App.jsx b/src/App.jsx
index e58b955..b4af1a5 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -3,23 +3,25 @@ import { Route, Routes } from 'react-router-dom'
import { useContext } from 'react'
//* Components
+import { AllUsers } from './components/AllUsers/AllUsers'
+import { Citas } from './components/Citas/Citas'
+import { CitaDetalle } from './components/Citas/CitaDetalle/CitaDetalle'
+import { CitasUser } from './components/Citas/CitasUser/CitasUser'
+import { EditUser } from './components/EditUser/EditUser'
+import { InfoUserEdit } from './components/AllUsers/EditUser/InfoUserEdit'
+import { FAQ } from './components/FAQ/FAQ'
+import { AddFAQ } from './components/FAQ/AddFAQ/AddFAQ'
+import { EditFAQ } from './components/FAQ/EditFAQ/EditFAQ'
import { Home } from './components/Home/Home'
import { Login } from './components/Login/Login'
+import { RecoverPassword } from './components/Login/RecoverPassword/RecoverPassword'
+import { ResetPassword } from './components/Login/ResetPassword/ResetPassword'
+import { NotFound } from './components/Not-found/NotFound'
import { Register } from './components/Register/Register'
import { Services } from './components/Services/Services'
import { Service } from './components/Services/Service/Service'
-import { Citas } from './components/Citas/Citas'
-import { CitaDetalle } from './components/Citas/CitaDetalle/CitaDetalle'
-import { EditUser } from './components/EditUser/EditUser'
-import { RecoverPassword } from './components/Login/RecoverPassword/RecoverPassword'
import { AddService } from './components/Services/AddService/AddService'
-import { AllUsers } from './components/AllUsers/AllUsers'
-import { InfoUserEdit } from './components/AllUsers/EditUser/InfoUserEdit'
import { EditService } from './components/Services/EditService/EditService'
-import { CitasUser } from './components/Citas/CitasUser/CitasUser'
-
-import { NotFound } from './components/Not-found/NotFound'
-import { ResetPassword } from './components/Login/ResetPassword/ResetPassword'
import { ProtectedRoute } from './components/Utils'
@@ -52,11 +54,15 @@ export const App = () => {
} />
} />
} />
- } />
+ {session && } />}
} />
} />
+ } />
+ } />
+ {session && } />}
+
} />
} />
diff --git a/src/components/FAQ/AddFAQ/AddFAQ.jsx b/src/components/FAQ/AddFAQ/AddFAQ.jsx
new file mode 100644
index 0000000..2c6d381
--- /dev/null
+++ b/src/components/FAQ/AddFAQ/AddFAQ.jsx
@@ -0,0 +1,161 @@
+import axios from 'axios'
+import Cookies from 'js-cookie'
+import jwtDecode from 'jwt-decode'
+import { useContext, useEffect, useRef, useState } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { toast, ToastContainer, Zoom } from 'react-toastify'
+import { SessionContext } from '../../../context/SessionContext'
+import { ToastifyContext } from '../../../context/ToastifyContext'
+import { Footer } from '../../Home/Footer/Footer'
+import { API_URL, Button2, Input, Navbar, ResponsiveNav, TextArea } from '../../Utils'
+
+const AddFAQ = () => {
+ const { session } = useContext(SessionContext)
+ const { setToastify } = useContext(ToastifyContext)
+
+ const tituloInputEl = useRef(null)
+ const respuestaInputEl = useRef(null)
+
+ const [button, setButton] = useState(null)
+ const [idRol, setIdRol] = useState(null)
+ const [disabled, setDisabled] = useState(false)
+
+ const navigate = useNavigate()
+
+ useEffect(() => {
+ !session ? setButton(1) : setButton(2)
+
+ window.scrollTo(0, 0)
+
+ document.title = 'FADEMET Montajes | Crear Pregunta'
+ }, [])
+
+ useEffect(() => {
+ const token = Cookies.get('token')
+
+ if (!token) return
+
+ new Promise((resolve, reject) => {
+ const decoded = jwtDecode(token)
+ resolve(decoded.data)
+ reject(new Error('Error al decodificar el token'))
+ }).then((decoded) => {
+ setIdRol(decoded[0].id_rol)
+ })
+ }, [])
+
+ const focusInput = (input) => input.current.focus()
+
+ const postFAQ = (e) => {
+ e.preventDefault()
+
+ const titulo = e.target[0].value
+ const respuesta = e.target[1].value
+
+ if (titulo.length === 0 || /^\s+$/.test(titulo)) {
+ toast.error('¡El título no puede estar vacío!', {
+ theme: 'colored'
+ })
+ focusInput(tituloInputEl)
+ setDisabled(false)
+ return
+ } else if (respuesta.length === 0 || /^\s+$/.test(respuesta)) {
+ toast.error('¡La respuesta no puede estar vacía!', {
+ theme: 'colored'
+ })
+ focusInput(respuestaInputEl)
+ setDisabled(false)
+ return
+ }
+ setDisabled(true)
+ axios
+ .post(API_URL('createFaq'), {
+ titulo,
+ respuesta
+ })
+ .then(() => {
+ toast.success('¡Pregunta frecuente creada con éxito!', {
+ theme: 'colored'
+ })
+ setDisabled(false)
+ setToastify('faqCreado')
+ navigate('/frequent-questions')
+ })
+ .catch(() => {
+ setDisabled(false)
+ toast.error('¡Hubo un error al crear la pregunta frecuente!', {
+ theme: 'colored'
+ })
+ })
+ }
+
+ return (
+ <>
+
+
+
+
+
+
Llena este formulario para crear una nueva pregunta frecuente
+
+
+
+
+
+ >
+ )
+}
+
+export { AddFAQ }
diff --git a/src/components/FAQ/EditFAQ/EditFAQ.jsx b/src/components/FAQ/EditFAQ/EditFAQ.jsx
new file mode 100644
index 0000000..62f7172
--- /dev/null
+++ b/src/components/FAQ/EditFAQ/EditFAQ.jsx
@@ -0,0 +1,187 @@
+import axios from 'axios'
+import Cookies from 'js-cookie'
+import jwtDecode from 'jwt-decode'
+import { useContext, useEffect, useRef, useState } from 'react'
+import { useNavigate, useParams } from 'react-router-dom'
+import { toast, ToastContainer, Zoom } from 'react-toastify'
+import { SessionContext } from '../../../context/SessionContext'
+import { ToastifyContext } from '../../../context/ToastifyContext'
+import { Footer } from '../../Home/Footer/Footer'
+import { API_URL, Button2, Input, Navbar, ResponsiveNav, TextArea } from '../../Utils'
+
+const EditFAQ = () => {
+ const { faqId } = useParams()
+ const { session } = useContext(SessionContext)
+ const { setToastify } = useContext(ToastifyContext)
+
+ const tituloInputEl = useRef(null)
+ const respuestaInputEl = useRef(null)
+
+ const [button, setButton] = useState(null)
+ const [textFaq, setTextFaq] = useState(null)
+ const [idRol, setIdRol] = useState(null)
+ const [disabled, setDisabled] = useState(false)
+
+ const navigate = useNavigate()
+
+ useEffect(() => {
+ !session ? setButton(1) : setButton(2)
+
+ window.scrollTo(0, 0)
+
+ document.title = `FADEMET Montajes | Editar Pregunta Frecuente ${faqId}`
+ }, [])
+
+ // * Validate if user is admin
+ useEffect(() => {
+ const token = Cookies.get('token')
+ if (!token) {
+ navigate('/login')
+ return
+ }
+ const decode = jwtDecode(token)
+ const { id_rol } = decode.data[0]
+ setIdRol(id_rol)
+ if (id_rol === 2) navigate('/', { replace: true })
+ }, [])
+
+ const focusInput = (input) => input.current.focus()
+
+ const validarInputs = (e) => {
+ e.preventDefault()
+
+ const titulo = e.target[0].value
+ const respuesta = e.target[1].value
+
+ setDisabled(true)
+
+ if (disabled) return
+
+ if (titulo.length === 0 || /^\s+$/.test(titulo)) {
+ toast.error('¡El título no puede estar vacío!', {
+ theme: 'colored'
+ })
+ focusInput(tituloInputEl)
+ setDisabled(false)
+ return
+ } else if (respuesta.length === 0 || /^\s+$/.test(respuesta)) {
+ toast.error('¡La respuesta no puede estar vacía!', {
+ theme: 'colored'
+ })
+ focusInput(respuestaInputEl)
+ setDisabled(false)
+ return
+ }
+ axios
+ .patch(API_URL(`editFaq/${faqId}`), {
+ titulo,
+ respuesta
+ })
+ .then(() => {
+ toast.success('¡Pregunta frecuente editada con éxito!', {
+ theme: 'colored'
+ })
+ setDisabled(false)
+ setToastify('faqEditado')
+ navigate('/frequent-questions')
+ })
+ .catch(() => {
+ setDisabled(false)
+ toast.error('¡Hubo un error al editar la pregunta frecuente!', {
+ theme: 'colored'
+ })
+ })
+ }
+
+ const getFaqData = () => {
+ axios
+ .get(API_URL('faq/1'))
+ .then(({ data }) => {
+ const [faq] = data.faq
+ setTextFaq({
+ titulo: faq.titulo,
+ respuesta: faq.respuesta
+ })
+ })
+ .catch((err) => {
+ console.log(err)
+ throw new Error('Error al obtener los datos del servicio')
+ })
+ }
+
+ useEffect(() => {
+ faqId && getFaqData()
+ }, [])
+
+ return (
+ <>
+
+
+
+
+
+
Edita este formulario para modificar {textFaq && textFaq.titulo.toLowerCase()}
+
+
+
+
+ >
+ )
+}
+
+export { EditFAQ }
diff --git a/src/components/FAQ/FAQ.css b/src/components/FAQ/FAQ.css
new file mode 100644
index 0000000..5c4eda4
--- /dev/null
+++ b/src/components/FAQ/FAQ.css
@@ -0,0 +1,106 @@
+.faq {
+ margin: auto;
+ max-width: 1300px;
+ padding: 2rem;
+}
+.faq__title {
+ font-family: syne;
+ font-size: 2.5rem;
+ margin-bottom: 1rem;
+ position: relative;
+ text-align: center;
+ width: fit-content;
+}
+.faq__title::after {
+ background: var(--blue-gradient);
+ background-color: #fff;
+ bottom: 1px;
+ content: '';
+ height: 2px;
+ left: 0;
+ position: absolute;
+ width: 100%;
+}
+.faq__text {
+ font-size: 15px;
+ margin-bottom: 1rem;
+ max-width: 60ch;
+}
+.faq__button {
+ margin-bottom: 1.5rem;
+}
+
+/* TODO */
+.faq__cards {
+ display: grid;
+ gap: 1rem;
+ grid-auto-flow: row dense;
+ place-items: center;
+}
+
+.get-in-touch {
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+ padding: 2rem 4rem;
+}
+.get-in-touch__info {
+ align-items: center;
+ display: flex;
+ gap: 1rem;
+}
+.get-in-touch__info img {
+ border-radius: 50%;
+}
+.info-text__title {
+ margin: 0.4rem 0;
+}
+.info-text__text {
+ font-size: 15px;
+ line-height: 1.5;
+ max-width: 65ch;
+ padding-right: 5rem;
+}
+
+@media (max-width: 768px) {
+ .faq {
+ padding: 0.8rem;
+ }
+ .get-in-touch {
+ flex-direction: column;
+ gap: 1.8rem;
+ padding: 1.5rem 0.5rem;
+ }
+ .get-in-touch__info img {
+ height: 48px;
+ width: 48px;
+ }
+ .info-text__text {
+ padding-right: 0;
+ }
+}
+@media (max-width: 500px) {
+ .MuiPaper-root {
+ width: 100% !important;
+ }
+}
+
+/* * Cards Responsive */
+@media all and (min-width: 320px) {
+ .faq__cards {
+ grid-template-columns: repeat(1, 1fr);
+ }
+}
+
+@media all and (min-width: 768px) {
+ .faq__cards {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+@media all and (min-width: 1060px) {
+ .faq__cards {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+/* * Fin Cards Responsive */
diff --git a/src/components/FAQ/FAQ.jsx b/src/components/FAQ/FAQ.jsx
new file mode 100644
index 0000000..0c97f0e
--- /dev/null
+++ b/src/components/FAQ/FAQ.jsx
@@ -0,0 +1,182 @@
+import { Card, CardContent, Typography } from '@mui/material'
+import axios from 'axios'
+import Cookies from 'js-cookie'
+import jwtDecode from 'jwt-decode'
+import { useContext, useEffect, useState } from 'react'
+import { LazyLoadImage } from 'react-lazy-load-image-component'
+import { Link } from 'react-router-dom'
+import { toast, ToastContainer, Zoom } from 'react-toastify'
+import { SessionContext } from '../../context/SessionContext'
+import { ToastifyContext } from '../../context/ToastifyContext'
+import { Footer } from '../Home/Footer/Footer'
+import { API_URL, Button, Button2, Navbar, ResponsiveNav } from '../Utils'
+import './FAQ.css'
+import { style } from './FAQStyle'
+
+const FAQ = () => {
+ const { session } = useContext(SessionContext)
+ const { toastify, setToastify } = useContext(ToastifyContext)
+
+ const [button, setButton] = useState(null)
+ const [idRol, setIdRol] = useState(null)
+ const [userPhoto, setUserPhoto] = useState(null)
+ const [faqs, setFaqs] = useState(null)
+
+ useEffect(() => {
+ if (toastify === 'faqCreado') {
+ toast.success('Pregunta frecuente creada con éxito', {
+ theme: 'colored'
+ })
+ setToastify(false)
+ }
+ if (toastify === 'faqEditado') {
+ toast.success('Pregunta frecuente editada con éxito', {
+ theme: 'colored'
+ })
+ setToastify(false)
+ }
+
+ !session ? setButton(1) : setButton(2)
+
+ window.scrollTo(0, 0)
+
+ document.title = 'FADEMET Montajes | Preguntas Frecuentes'
+ }, [])
+
+ useEffect(() => {
+ const token = Cookies.get('token')
+
+ if (!token) return
+
+ new Promise((resolve, reject) => {
+ const decoded = jwtDecode(token)
+ resolve(decoded.data)
+ reject(new Error('Error al decodificar el token'))
+ }).then((decoded) => {
+ setIdRol(decoded[0].id_rol)
+ setUserPhoto(decoded[0].foto_perfil)
+ })
+ }, [])
+
+ const getFaqs = async () => {
+ const { data } = await axios.get(API_URL('faq'))
+ const { faq } = data
+ setFaqs(faq)
+ }
+
+ useEffect(() => {
+ getFaqs()
+ }, [])
+
+ return (
+ <>
+
+
+
+
+ Preguntas Frecuentes
+
+ Todo lo que necesitas saber sobre nuestros servicios. ¿No encontraste lo que buscabas? Por
+ favor, contacta con nuestro equipo.
+
+ {idRol && idRol !== 2 && (
+
+
+
+ )}
+
+ {!faqs &&
Cargando...
}
+ {idRol && idRol !== 2
+ ? faqs &&
+ faqs.map(({ id_faq, titulo, respuesta }) => (
+
+
+
+
+ {titulo}
+
+
+ {respuesta}
+
+
+
+
+ ))
+ : faqs &&
+ faqs.map(({ id_faq, titulo, respuesta }) => (
+
+
+
+ {titulo}
+
+
+ {respuesta}
+
+
+
+ ))}
+
+
+
+
+
+
¿Todavía tienes dudas?
+
+ Por favor, contacta con nuestro equipo y te responderemos lo más pronto posible.
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export { FAQ }
diff --git a/src/components/FAQ/FAQStyle.js b/src/components/FAQ/FAQStyle.js
new file mode 100644
index 0000000..c1d138d
--- /dev/null
+++ b/src/components/FAQ/FAQStyle.js
@@ -0,0 +1,30 @@
+export const style = {
+ card: {
+ boxShadow: '0 0 10px 0 rgba(0, 0, 0, 0.1)',
+ display: 'flex',
+ flexDirection: 'column',
+ width: '320px',
+ minHeight: '210px'
+ },
+ linkCard: {
+ boxShadow: '0 0 10px 0 rgba(0, 0, 0, 0.1)',
+ display: 'flex',
+ flexDirection: 'column',
+ width: '320px',
+ minHeight: '210px',
+ hover: {
+ background: 'red'
+ }
+ },
+ cardTitle: {
+ fontFamily: 'system-ui, sans-serif',
+ fontSize: '22px',
+ fontWeight: '700',
+ marginBottom: '1rem'
+ },
+ cardText: {
+ color: '#333',
+ fontSize: '15px',
+ marginTop: '0.5rem'
+ }
+}
diff --git a/src/components/Home/FrequentQuestions/FrequentQuestions.css b/src/components/Home/FrequentQuestions/FrequentQuestions.css
index 413ceb6..cf40abf 100644
--- a/src/components/Home/FrequentQuestions/FrequentQuestions.css
+++ b/src/components/Home/FrequentQuestions/FrequentQuestions.css
@@ -1,5 +1,5 @@
.frequent-questions {
- align-items: start;
+ align-items: center;
display: grid;
gap: 3rem;
grid-template-columns: 40% 1fr;
@@ -9,7 +9,6 @@
}
.frequent-questions section {
flex-direction: column;
- padding-top: 2rem;
}
.frequent-questions aside {
display: grid;
@@ -18,6 +17,7 @@
grid-template-rows: 1fr 1fr;
min-height: 470px;
padding: 2rem;
+ place-items: center;
}
.frequent-questions__title {
font-family: 'Syne', sans-serif;
@@ -30,6 +30,13 @@
max-width: 65ch;
text-align: center;
}
+.frequent-questions__buttons {
+ align-items: center;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.6rem;
+ justify-content: center;
+}
@media (max-width: 920px) {
.frequent-questions aside {
grid-template-columns: 1fr;
diff --git a/src/components/Home/FrequentQuestions/FrequentQuestions.jsx b/src/components/Home/FrequentQuestions/FrequentQuestions.jsx
index 1368929..2300cb5 100644
--- a/src/components/Home/FrequentQuestions/FrequentQuestions.jsx
+++ b/src/components/Home/FrequentQuestions/FrequentQuestions.jsx
@@ -3,6 +3,8 @@ import './FrequentQuestions.css'
// ? Components
import { Button2 } from '../../Utils/Button2/Button2'
import { Accordion } from '../../Utils/Accordion/Accordion'
+import { Link } from 'react-router-dom'
+import { Button } from '../../Utils'
const FrequentQuestions = () => {
return (
@@ -13,13 +15,18 @@ const FrequentQuestions = () => {
Si algo no te ha quedado claro, no dudes en contactarnos y te responderemos lo más pronto
posible.
-
-
-
+