├── .gitignore
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
└── _redirects
├── src
├── App.jsx
├── components
│ ├── Alerta.jsx
│ ├── Busqueda.jsx
│ ├── Colaborador.jsx
│ ├── FormularioColaborador.jsx
│ ├── FormularioProyecto.jsx
│ ├── Header.jsx
│ ├── ModalEliminarColaborador.jsx
│ ├── ModalEliminarTarea.jsx
│ ├── ModalFormularioTarea.jsx
│ ├── PreviewProyecto.jsx
│ ├── Sidebar.jsx
│ └── Tarea.jsx
├── config
│ └── clienteAxios.jsx
├── context
│ ├── AuthProvider.jsx
│ └── ProyectosProvider.jsx
├── favicon.svg
├── helpers
│ └── formatearFecha.jsx
├── hooks
│ ├── useAdmin.jsx
│ ├── useAuth.jsx
│ └── useProyectos.jsx
├── index.css
├── layouts
│ ├── AuthLayout.jsx
│ └── RutaProtegida.jsx
├── main.jsx
└── paginas
│ ├── ConfirmarCuenta.jsx
│ ├── EditarProyecto.jsx
│ ├── Login.jsx
│ ├── NuevoColaborador.jsx
│ ├── NuevoPassword.jsx
│ ├── NuevoProyecto.jsx
│ ├── OlvidePassword.jsx
│ ├── Proyecto.jsx
│ ├── Proyectos.jsx
│ └── Registrar.jsx
├── tailwind.config.js
└── vite.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | pnpm-debug.log*
9 | lerna-debug.log*
10 |
11 | node_modules
12 | dist
13 | dist-ssr
14 | *.local
15 |
16 | # Editor directories and files
17 | .vscode/*
18 | !.vscode/extensions.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | UpTask
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "dependencies": {
11 | "@headlessui/react": "^1.5.0",
12 | "axios": "^0.25.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-router-dom": "^6.2.1",
16 | "socket.io-client": "^4.4.1"
17 | },
18 | "devDependencies": {
19 | "@vitejs/plugin-react": "^1.0.7",
20 | "autoprefixer": "^10.4.2",
21 | "postcss": "^8.4.6",
22 | "tailwindcss": "^3.0.22",
23 | "vite": "^2.8.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Routes, Route } from 'react-router-dom'
2 |
3 | import AuthLayout from './layouts/AuthLayout'
4 | import RutaProtegida from './layouts/RutaProtegida'
5 |
6 | import Login from './paginas/Login'
7 | import Registrar from './paginas/Registrar'
8 | import OlvidePassword from './paginas/OlvidePassword'
9 | import NuevoPassword from './paginas/NuevoPassword'
10 | import ConfirmarCuenta from './paginas/ConfirmarCuenta'
11 | import Proyectos from './paginas/Proyectos'
12 | import NuevoProyecto from './paginas/NuevoProyecto'
13 | import Proyecto from './paginas/Proyecto'
14 | import EditarProyecto from './paginas/EditarProyecto'
15 | import NuevoColaborador from './paginas/NuevoColaborador'
16 |
17 | import {AuthProvider} from './context/AuthProvider'
18 | import {ProyectosProvider} from './context/ProyectosProvider'
19 |
20 |
21 |
22 | function App() {
23 |
24 |
25 | return (
26 |
27 |
28 |
29 |
30 | }>
31 | } />
32 | } />
33 | } />
34 | } />
35 | } />
36 |
37 |
38 | }>
39 | } />
40 | } />
41 | } />
42 | } />
43 | } />
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default App
53 |
--------------------------------------------------------------------------------
/src/components/Alerta.jsx:
--------------------------------------------------------------------------------
1 | const Alerta = ({alerta}) => {
2 | return (
3 |
4 | {alerta.msg}
5 |
6 | )
7 | }
8 |
9 | export default Alerta
--------------------------------------------------------------------------------
/src/components/Busqueda.jsx:
--------------------------------------------------------------------------------
1 | import { Fragment, useState } from 'react'
2 | import { Combobox, Dialog, Transition } from '@headlessui/react'
3 | import useProyectos from '../hooks/useProyectos'
4 |
5 | function classNames(...classes) {
6 | return classes.filter(Boolean).join(' ')
7 | }
8 |
9 | const Busqueda = () => {
10 | const [ busqueda, setBusqueda ] = useState('')
11 | const { buscador, handleBuscador, proyectos } = useProyectos()
12 |
13 | const proyectosFiltrados = busqueda === '' ? [] : proyectos.filter(proyecto => proyecto.nombre.toLowerCase().includes(busqueda.toLowerCase()))
14 |
15 | return (
16 | setBusqueda('') }>
17 |
68 |
69 | )
70 | }
71 |
72 | export default Busqueda
73 |
--------------------------------------------------------------------------------
/src/components/Colaborador.jsx:
--------------------------------------------------------------------------------
1 | import useProyectos from "../hooks/useProyectos"
2 |
3 | const Colaborador = ({colaborador}) => {
4 | const { handleModalEliminarColaborador } = useProyectos()
5 |
6 | const { nombre, email } = colaborador
7 |
8 | return (
9 |
10 |
11 |
{nombre}
12 |
{email}
13 |
14 |
15 |
16 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default Colaborador
--------------------------------------------------------------------------------
/src/components/FormularioColaborador.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import useProyectos from '../hooks/useProyectos'
3 | import Alerta from './Alerta'
4 |
5 | const FormularioColaborador = () => {
6 | const [email, setEmail] = useState('')
7 |
8 | const { mostrarAlerta, alerta, submitColaborador } = useProyectos()
9 |
10 | const handleSubmit = e => {
11 | e.preventDefault();
12 |
13 | if(email === '') {
14 | mostrarAlerta({
15 | msg: 'El Email es Obligatorio',
16 | error: true
17 | })
18 | return
19 | }
20 |
21 | submitColaborador(email)
22 |
23 | }
24 |
25 | const { msg } = alerta
26 |
27 | return (
28 |
57 | )
58 | }
59 |
60 | export default FormularioColaborador
--------------------------------------------------------------------------------
/src/components/FormularioProyecto.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { useParams } from 'react-router-dom'
3 | import useProyectos from '../hooks/useProyectos'
4 | import Alerta from './Alerta'
5 |
6 | const FormularioProyecto = () => {
7 | const [id, setId] = useState(null)
8 | const [nombre, setNombre] = useState('')
9 | const [descripcion, setDescripcion] = useState('')
10 | const [fechaEntrega, setFechaEntrega] = useState('')
11 | const [cliente, setCliente] = useState('')
12 |
13 | const params = useParams();
14 | const { mostrarAlerta, alerta, submitProyecto, proyecto } = useProyectos();
15 |
16 | useEffect(() => {
17 | if( params.id ) {
18 | setId(proyecto._id)
19 | setNombre(proyecto.nombre)
20 | setDescripcion(proyecto.descripcion)
21 | setFechaEntrega(proyecto.fechaEntrega?.split('T')[0])
22 | setCliente(proyecto.cliente)
23 | }
24 | }, [params])
25 |
26 |
27 | const handleSubmit = async e => {
28 | e.preventDefault();
29 |
30 | if([nombre, descripcion, fechaEntrega, cliente].includes('') ) {
31 | mostrarAlerta({
32 | msg: 'Todos los Campos son Obligatorios',
33 | error: true
34 | })
35 |
36 | return
37 | }
38 |
39 | // Pasar los datos hacia el provider
40 | await submitProyecto({ id, nombre, descripcion, fechaEntrega, cliente})
41 |
42 | setId(null)
43 | setNombre('')
44 | setDescripcion('')
45 | setFechaEntrega('')
46 | setCliente('')
47 | }
48 |
49 | const { msg } = alerta
50 |
51 | return (
52 |
126 | )
127 | }
128 |
129 | export default FormularioProyecto
--------------------------------------------------------------------------------
/src/components/Header.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom'
2 | import useProyectos from '../hooks/useProyectos'
3 | import useAuth from '../hooks/useAuth'
4 | import Busqueda from './Busqueda'
5 |
6 | const Header = () => {
7 |
8 | const { handleBuscador, cerrarSesionProyectos } = useProyectos()
9 | const { cerrarSesionAuth } = useAuth()
10 |
11 | const handleCerrarSesion = () => {
12 | cerrarSesionAuth()
13 | cerrarSesionProyectos()
14 | localStorage.removeItem('token')
15 | }
16 |
17 |
18 | return (
19 |
46 | )
47 | }
48 |
49 | export default Header
--------------------------------------------------------------------------------
/src/components/ModalEliminarColaborador.jsx:
--------------------------------------------------------------------------------
1 | import { Fragment } from 'react'
2 | import { Dialog, Transition } from '@headlessui/react'
3 | import useProyectos from '../hooks/useProyectos'
4 |
5 | const modalEliminarColaborador = () => {
6 |
7 | const { handleModalEliminarColaborador, modalEliminarColaborador, eliminarColaborador } = useProyectos()
8 |
9 | return (
10 |
11 |
95 |
96 | )
97 | }
98 |
99 | export default modalEliminarColaborador
--------------------------------------------------------------------------------
/src/components/ModalEliminarTarea.jsx:
--------------------------------------------------------------------------------
1 | import { Fragment } from 'react'
2 | import { Dialog, Transition } from '@headlessui/react'
3 | import useProyectos from '../hooks/useProyectos'
4 |
5 | const ModalEliminarTarea = () => {
6 |
7 | const { modalEliminarTarea, handleModalEliminarTarea, eliminarTarea } = useProyectos()
8 |
9 | return (
10 |
11 |
95 |
96 | )
97 | }
98 |
99 | export default ModalEliminarTarea
--------------------------------------------------------------------------------
/src/components/ModalFormularioTarea.jsx:
--------------------------------------------------------------------------------
1 | import { Fragment, useState, useEffect } from 'react'
2 | import { Dialog, Transition } from '@headlessui/react'
3 | import useProyectos from '../hooks/useProyectos'
4 | import Alerta from './Alerta'
5 | import { useParams } from 'react-router-dom'
6 |
7 | const PRIORIDAD = ['Baja', 'Media', 'Alta']
8 |
9 | const ModalFormularioTarea = () => {
10 |
11 | const [id, setId] = useState('')
12 | const [nombre, setNombre] = useState('')
13 | const [descripcion, setDescripcion] = useState('')
14 | const [fechaEntrega, setFechaEntrega] = useState('')
15 | const [prioridad, setPrioridad] = useState('')
16 |
17 | const params = useParams()
18 |
19 | const { modalFormularioTarea, handleModalTarea, mostrarAlerta, alerta, submitTarea, tarea } = useProyectos();
20 |
21 | useEffect(() => {
22 | if(tarea?._id) {
23 | setId(tarea._id)
24 | setNombre(tarea.nombre)
25 | setDescripcion(tarea.descripcion)
26 | setFechaEntrega(tarea.fechaEntrega?.split('T')[0])
27 | setPrioridad(tarea.prioridad)
28 | return
29 | }
30 | setId('')
31 | setNombre('')
32 | setDescripcion('')
33 | setFechaEntrega('')
34 | setPrioridad('')
35 |
36 | }, [tarea]);
37 |
38 |
39 | const handleSubmit = async e => {
40 | e.preventDefault();
41 |
42 | if([nombre, descripcion, fechaEntrega, prioridad].includes('') ) {
43 | mostrarAlerta({
44 | msg: 'Todos los campos son obligatorios',
45 | error: true
46 | })
47 | return
48 | }
49 |
50 | await submitTarea({ id, nombre, descripcion, fechaEntrega, prioridad, proyecto: params.id})
51 |
52 | setId('')
53 | setNombre('')
54 | setDescripcion('')
55 | setFechaEntrega('')
56 | setPrioridad('')
57 |
58 | }
59 |
60 | const { msg } = alerta
61 |
62 | return (
63 |
64 |
208 |
209 | )
210 | }
211 |
212 | export default ModalFormularioTarea
--------------------------------------------------------------------------------
/src/components/PreviewProyecto.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom'
2 | import useAuth from '../hooks/useAuth'
3 |
4 | const PreviewProyecto = ({proyecto}) => {
5 |
6 | const { auth } = useAuth()
7 |
8 | const { nombre, _id, cliente, creador} = proyecto
9 |
10 |
11 |
12 | return (
13 |
14 |
15 |
16 |
17 | {nombre}
18 |
19 |
20 | {''} {cliente}
21 |
22 |
23 |
24 | {auth._id !== creador && (
25 |
Colaborador
26 | )}
27 |
28 |
29 |
Ver Proyecto
33 |
34 | )
35 | }
36 |
37 | export default PreviewProyecto
--------------------------------------------------------------------------------
/src/components/Sidebar.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom'
2 | import useAuth from '../hooks/useAuth'
3 |
4 | const Sidebar = () => {
5 |
6 | const { auth } = useAuth()
7 |
8 | return (
9 |
17 | )
18 | }
19 |
20 | export default Sidebar
--------------------------------------------------------------------------------
/src/components/Tarea.jsx:
--------------------------------------------------------------------------------
1 | import { formatearFecha } from "../helpers/formatearFecha"
2 | import useProyectos from "../hooks/useProyectos"
3 | import useAdmin from "../hooks/useAdmin"
4 |
5 | const Tarea = ({tarea}) => {
6 |
7 | const { handleModalEditarTarea, handleModalEliminarTarea, completarTarea } = useProyectos()
8 | const admin = useAdmin()
9 |
10 | const { descripcion, nombre, prioridad, fechaEntrega, estado, _id } = tarea
11 |
12 | return (
13 |
14 |
15 |
{nombre}
16 |
{descripcion}
17 |
{ formatearFecha(fechaEntrega) }
18 |
Prioridad: {prioridad}
19 | { estado &&
Completada por: {tarea.completado.nombre}
}
20 |
21 |
22 |
23 | {admin && (
24 |
28 |
29 | )}
30 |
31 |
35 |
36 | {admin && (
37 |
41 | )}
42 |
43 |
44 | )
45 | }
46 |
47 | export default Tarea
--------------------------------------------------------------------------------
/src/config/clienteAxios.jsx:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | const clienteAxios = axios.create({
4 | baseURL: `${import.meta.env.VITE_BACKEND_URL}/api`
5 | })
6 |
7 | export default clienteAxios;
--------------------------------------------------------------------------------
/src/context/AuthProvider.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, createContext } from 'react'
2 | import { useNavigate } from 'react-router-dom'
3 | import clienteAxios from '../config/clienteAxios';
4 |
5 | const AuthContext = createContext();
6 |
7 | const AuthProvider = ({children}) => {
8 |
9 | const [auth, setAuth] = useState({})
10 | const [cargando, setCargando] = useState(true)
11 |
12 | const navigate = useNavigate()
13 |
14 | useEffect(() => {
15 | const autenticarUsuario = async () => {
16 | const token = localStorage.getItem('token')
17 | if(!token){
18 | setCargando(false)
19 | return
20 | }
21 |
22 | const config = {
23 | headers: {
24 | "Content-Type": "application/json",
25 | Authorization: `Bearer ${token}`
26 | }
27 | }
28 |
29 | try {
30 | const { data } = await clienteAxios('/usuarios/perfil', config)
31 | setAuth(data)
32 | // navigate('/proyectos')
33 |
34 | } catch (error) {
35 | setAuth({})
36 | }
37 |
38 | setCargando(false)
39 |
40 |
41 | }
42 | autenticarUsuario()
43 | }, [])
44 |
45 | const cerrarSesionAuth = () => {
46 | setAuth({})
47 | }
48 |
49 |
50 | return (
51 |
59 | {children}
60 |
61 | )
62 | }
63 |
64 | export {
65 | AuthProvider
66 | }
67 |
68 | export default AuthContext;
--------------------------------------------------------------------------------
/src/context/ProyectosProvider.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, createContext } from 'react'
2 | import clienteAxios from '../config/clienteAxios'
3 | import { useNavigate } from 'react-router-dom'
4 | import useAuth from '../hooks/useAuth'
5 | import io from 'socket.io-client'
6 |
7 | let socket;
8 |
9 | const ProyectosContext = createContext();
10 |
11 | const ProyectosProvider = ({children}) => {
12 |
13 | const [proyectos, setProyectos] = useState([]);
14 | const [alerta, setAlerta] = useState({});
15 | const [proyecto, setProyecto] = useState({});
16 | const [cargando, setCargando] = useState(false);
17 | const [ modalFormularioTarea, setModalFormularioTarea ] = useState(false)
18 | const [ tarea, setTarea] = useState({})
19 | const [ modalEliminarTarea, setModalEliminarTarea ] = useState(false)
20 | const [ colaborador, setColaborador] = useState({})
21 | const [ modalEliminarColaborador, setModalEliminarColaborador] = useState(false)
22 | const [ buscador, setBuscador] = useState(false)
23 |
24 | const navigate = useNavigate();
25 | const { auth } = useAuth()
26 |
27 | useEffect(() => {
28 | const obtenerProyectos = async () => {
29 | try {
30 | const token = localStorage.getItem('token')
31 | if(!token) return
32 |
33 |
34 | const config = {
35 | headers: {
36 | "Content-Type": "application/json",
37 | Authorization: `Bearer ${token}`
38 | }
39 | }
40 | const { data } = await clienteAxios('/proyectos', config)
41 | setProyectos(data)
42 | } catch (error) {
43 | console.log(error)
44 | }
45 | }
46 | obtenerProyectos()
47 | }, [auth])
48 |
49 | useEffect(() => {
50 | socket = io(import.meta.env.VITE_BACKEND_URL)
51 | }, [])
52 |
53 | const mostrarAlerta = alerta => {
54 | setAlerta(alerta)
55 |
56 | setTimeout(() => {
57 | setAlerta({})
58 | }, 5000);
59 | }
60 |
61 | const submitProyecto = async proyecto => {
62 | if(proyecto.id) {
63 | await editarProyecto(proyecto)
64 | } else {
65 | await nuevoProyecto(proyecto)
66 | }
67 | }
68 |
69 | const editarProyecto = async proyecto => {
70 | try {
71 | const token = localStorage.getItem('token')
72 | if(!token) return
73 |
74 | const config = {
75 | headers: {
76 | "Content-Type": "application/json",
77 | Authorization: `Bearer ${token}`
78 | }
79 | }
80 |
81 | const { data } = await clienteAxios.put(`/proyectos/${proyecto.id}`, proyecto, config)
82 |
83 | // Sincronizar el state
84 | const proyectosActualizados = proyectos.map(proyectoState => proyectoState._id === data._id ? data : proyectoState)
85 | setProyectos(proyectosActualizados)
86 |
87 | setAlerta({
88 | msg: 'Proyecto Actualizado Correctamente',
89 | error: false
90 | })
91 |
92 | setTimeout(() => {
93 | setAlerta({})
94 | navigate('/proyectos')
95 | }, 3000);
96 | } catch (error) {
97 | console.log(error)
98 | }
99 | }
100 |
101 | const nuevoProyecto = async proyecto => {
102 | try {
103 | const token = localStorage.getItem('token')
104 | if(!token) return
105 |
106 | const config = {
107 | headers: {
108 | "Content-Type": "application/json",
109 | Authorization: `Bearer ${token}`
110 | }
111 | }
112 |
113 | const { data } = await clienteAxios.post('/proyectos', proyecto, config)
114 |
115 | setProyectos([...proyectos, data])
116 |
117 | setAlerta({
118 | msg: 'Proyecto Creado Correctamente',
119 | error: false
120 | })
121 |
122 | setTimeout(() => {
123 | setAlerta({})
124 | navigate('/proyectos')
125 | }, 3000);
126 | } catch (error) {
127 | console.log(error)
128 | }
129 | }
130 |
131 | const obtenerProyecto = async id => {
132 | setCargando(true)
133 | try {
134 | const token = localStorage.getItem('token')
135 | if(!token) return
136 |
137 | const config = {
138 | headers: {
139 | "Content-Type": "application/json",
140 | Authorization: `Bearer ${token}`
141 | }
142 | }
143 |
144 | const { data } = await clienteAxios(`/proyectos/${id}`, config )
145 | setProyecto(data)
146 | setAlerta({})
147 | } catch (error) {
148 | navigate('/proyectos')
149 | setAlerta({
150 | msg: error.response.data.msg,
151 | error: true
152 | })
153 | setTimeout(() => {
154 | setAlerta({})
155 | }, 3000);
156 | } finally {
157 | setCargando(false)
158 | }
159 | }
160 |
161 | const eliminarProyecto = async id => {
162 | try {
163 | const token = localStorage.getItem('token')
164 | if(!token) return
165 |
166 | const config = {
167 | headers: {
168 | "Content-Type": "application/json",
169 | Authorization: `Bearer ${token}`
170 | }
171 | }
172 |
173 | const { data } = await clienteAxios.delete(`/proyectos/${id}`, config)
174 |
175 | // Sincronizar el state
176 | const proyectosActualizados = proyectos.filter(proyectoState => proyectoState._id !== id )
177 | setProyectos(proyectosActualizados)
178 |
179 | setAlerta({
180 | msg: data.msg,
181 | error: false
182 | })
183 |
184 | setTimeout(() => {
185 | setAlerta({})
186 | navigate('/proyectos')
187 | }, 3000);
188 | } catch (error) {
189 | console.log(error)
190 | }
191 | }
192 |
193 | const handleModalTarea = () => {
194 | setModalFormularioTarea(!modalFormularioTarea)
195 | setTarea({})
196 | }
197 |
198 | const submitTarea = async tarea => {
199 | if(tarea?.id) {
200 | await editarTarea(tarea)
201 | } else {
202 | await crearTarea(tarea)
203 | }
204 | }
205 |
206 | const crearTarea = async tarea => {
207 | try {
208 | const token = localStorage.getItem('token')
209 | if(!token) return
210 |
211 | const config = {
212 | headers: {
213 | "Content-Type": "application/json",
214 | Authorization: `Bearer ${token}`
215 | }
216 | }
217 |
218 | const { data } = await clienteAxios.post('/tareas', tarea, config)
219 |
220 | setAlerta({})
221 | setModalFormularioTarea(false)
222 |
223 | // SOCKET IO
224 | socket.emit('nueva tarea', data)
225 | } catch (error) {
226 | console.log(error)
227 | }
228 | }
229 |
230 | const editarTarea = async tarea => {
231 | try {
232 | const token = localStorage.getItem('token')
233 | if(!token) return
234 |
235 | const config = {
236 | headers: {
237 | "Content-Type": "application/json",
238 | Authorization: `Bearer ${token}`
239 | }
240 | }
241 |
242 | const { data } = await clienteAxios.put(`/tareas/${tarea.id}`, tarea, config)
243 |
244 | setAlerta({})
245 | setModalFormularioTarea(false)
246 |
247 | // SOCKET
248 | socket.emit('actualizar tarea', data)
249 | } catch (error) {
250 | console.log(error)
251 | }
252 | }
253 |
254 | const handleModalEditarTarea = tarea => {
255 | setTarea(tarea)
256 | setModalFormularioTarea(true)
257 | }
258 |
259 | const handleModalEliminarTarea = tarea => {
260 | setTarea(tarea)
261 | setModalEliminarTarea(!modalEliminarTarea)
262 | }
263 |
264 | const eliminarTarea = async () => {
265 |
266 | try {
267 | const token = localStorage.getItem('token')
268 | if(!token) return
269 |
270 | const config = {
271 | headers: {
272 | "Content-Type": "application/json",
273 | Authorization: `Bearer ${token}`
274 | }
275 | }
276 |
277 | const { data } = await clienteAxios.delete(`/tareas/${tarea._id}`, config)
278 | setAlerta({
279 | msg: data.msg,
280 | error: false
281 | })
282 |
283 | setModalEliminarTarea(false)
284 |
285 | // SOCKET
286 | socket.emit('eliminar tarea', tarea)
287 |
288 | setTarea({})
289 | setTimeout(() => {
290 | setAlerta({})
291 | }, 3000 )
292 |
293 | } catch (error) {
294 | console.log(error)
295 | }
296 | }
297 |
298 | const submitColaborador = async email => {
299 |
300 | setCargando(true)
301 | try {
302 | const token = localStorage.getItem('token')
303 | if(!token) return
304 |
305 | const config = {
306 | headers: {
307 | "Content-Type": "application/json",
308 | Authorization: `Bearer ${token}`
309 | }
310 | }
311 |
312 | const { data } = await clienteAxios.post('/proyectos/colaboradores', {email}, config)
313 |
314 | setColaborador(data)
315 | setAlerta({})
316 | } catch (error) {
317 | setAlerta({
318 | msg: error.response.data.msg,
319 | error: true
320 | })
321 | } finally {
322 | setCargando(false)
323 | }
324 | }
325 |
326 | const agregarColaborador = async email => {
327 |
328 | try {
329 | const token = localStorage.getItem('token')
330 | if(!token) return
331 |
332 | const config = {
333 | headers: {
334 | "Content-Type": "application/json",
335 | Authorization: `Bearer ${token}`
336 | }
337 | }
338 | const { data } = await clienteAxios.post(`/proyectos/colaboradores/${proyecto._id}`, email, config)
339 |
340 | setAlerta({
341 | msg: data.msg,
342 | error: false
343 | })
344 | setColaborador({})
345 |
346 | setTimeout(() => {
347 | setAlerta({})
348 | }, 3000);
349 |
350 | } catch (error) {
351 | setAlerta({
352 | msg: error.response.data.msg,
353 | error: true
354 | })
355 | }
356 | }
357 |
358 | const handleModalEliminarColaborador = (colaborador) => {
359 | setModalEliminarColaborador(!modalEliminarColaborador)
360 | setColaborador(colaborador)
361 | }
362 |
363 | const eliminarColaborador = async () => {
364 | try {
365 | const token = localStorage.getItem('token')
366 | if(!token) return
367 |
368 | const config = {
369 | headers: {
370 | "Content-Type": "application/json",
371 | Authorization: `Bearer ${token}`
372 | }
373 | }
374 | const { data } = await clienteAxios.post(`/proyectos/eliminar-colaborador/${proyecto._id}`, { id: colaborador._id }, config)
375 |
376 | const proyectoActualizado = {...proyecto}
377 |
378 | proyectoActualizado.colaboradores = proyectoActualizado.colaboradores.filter(colaboradorState => colaboradorState._id !== colaborador._id )
379 |
380 | setProyecto(proyectoActualizado)
381 | setAlerta({
382 | msg: data.msg,
383 | error: false
384 | })
385 | setColaborador({})
386 | setModalEliminarColaborador(false)
387 |
388 | setTimeout(() => {
389 | setAlerta({})
390 | }, 3000);
391 |
392 | } catch (error) {
393 | console.log(error.response)
394 | }
395 | }
396 |
397 | const completarTarea = async id => {
398 | try {
399 | const token = localStorage.getItem('token')
400 | if(!token) return
401 |
402 | const config = {
403 | headers: {
404 | "Content-Type": "application/json",
405 | Authorization: `Bearer ${token}`
406 | }
407 | }
408 | const { data } = await clienteAxios.post(`/tareas/estado/${id}`, {}, config)
409 | setTarea({})
410 | setAlerta({})
411 |
412 | // socket
413 | socket.emit('cambiar estado', data)
414 |
415 | } catch (error) {
416 | console.log(error.response)
417 | }
418 |
419 | }
420 |
421 | const handleBuscador = () => {
422 | setBuscador(!buscador)
423 | }
424 |
425 | // Socket io
426 | const submitTareasProyecto = (tarea) => {
427 | const proyectoActualizado = {...proyecto}
428 | proyectoActualizado.tareas = [...proyectoActualizado.tareas, tarea]
429 | setProyecto(proyectoActualizado)
430 | }
431 | const eliminarTareaProyecto = tarea => {
432 | console.log(tarea)
433 | const proyectoActualizado = {...proyecto}
434 | proyectoActualizado.tareas = proyectoActualizado.tareas.filter(tareaState => tareaState._id !== tarea._id )
435 | console.log(proyectoActualizado)
436 | setProyecto(proyectoActualizado)
437 | }
438 |
439 | const actualizarTareaProyecto = tarea => {
440 | const proyectoActualizado = {...proyecto}
441 | proyectoActualizado.tareas = proyectoActualizado.tareas.map( tareaState => tareaState._id === tarea._id ? tarea : tareaState )
442 | setProyecto(proyectoActualizado)
443 | }
444 | const cambiarEstadoTarea = tarea => {
445 | const proyectoActualizado = {...proyecto}
446 | proyectoActualizado.tareas = proyectoActualizado.tareas.map(tareaState => tareaState._id === tarea._id ? tarea : tareaState)
447 | setProyecto(proyectoActualizado)
448 | }
449 |
450 | const cerrarSesionProyectos = () => {
451 | setProyectos([])
452 | setProyecto({})
453 | setAlerta({})
454 |
455 | }
456 |
457 | return (
458 | {children}
492 |
493 | )
494 | }
495 | export {
496 | ProyectosProvider
497 | }
498 |
499 | export default ProyectosContext
--------------------------------------------------------------------------------
/src/favicon.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/helpers/formatearFecha.jsx:
--------------------------------------------------------------------------------
1 | export const formatearFecha = fecha => {
2 | const nuevaFecha = new Date(fecha.split('T')[0].split('-'))
3 | const opciones = {
4 | weekday: 'long',
5 | year: 'numeric',
6 | month: 'long',
7 | day: 'numeric'
8 | }
9 | return nuevaFecha.toLocaleDateString('es-ES', opciones)
10 | }
--------------------------------------------------------------------------------
/src/hooks/useAdmin.jsx:
--------------------------------------------------------------------------------
1 | import useProyectos from "./useProyectos";
2 | import useAuth from "./useAuth";
3 |
4 | const useAdmin = () => {
5 | const { proyecto } = useProyectos()
6 | const { auth } = useAuth()
7 | return proyecto.creador === auth._id
8 | }
9 |
10 | export default useAdmin
--------------------------------------------------------------------------------
/src/hooks/useAuth.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import AuthContext from '../context/AuthProvider'
3 |
4 | const useAuth = () => {
5 | return useContext(AuthContext)
6 | }
7 |
8 | export default useAuth;
--------------------------------------------------------------------------------
/src/hooks/useProyectos.jsx:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import ProyectosContext from '../context/ProyectosProvider'
3 |
4 | const useProyectos = () => {
5 | return useContext(ProyectosContext)
6 | }
7 |
8 | export default useProyectos
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/src/layouts/AuthLayout.jsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from 'react-router-dom'
2 | const AuthLayout = () => {
3 | return (
4 | <>
5 |
6 |
7 |
8 |
9 |
10 | >
11 | )
12 | }
13 |
14 | export default AuthLayout
--------------------------------------------------------------------------------
/src/layouts/RutaProtegida.jsx:
--------------------------------------------------------------------------------
1 | import { Outlet, Navigate } from 'react-router-dom'
2 | import useAuth from '../hooks/useAuth'
3 | import Header from '../components/Header'
4 | import Sidebar from '../components/Sidebar'
5 |
6 | const RutaProtegida = () => {
7 |
8 | const { auth, cargando } = useAuth();
9 | if(cargando) return 'Cargando...'
10 | return (
11 | <>
12 | {auth._id ?
13 | (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ) : }
26 | >
27 | )
28 | }
29 |
30 | export default RutaProtegida
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import './index.css'
4 | import App from './App'
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/src/paginas/ConfirmarCuenta.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { useParams, Link } from 'react-router-dom'
3 | import clienteAxios from '../config/clienteAxios'
4 | import Alerta from '../components/Alerta'
5 |
6 |
7 | const ConfirmarCuenta = () => {
8 |
9 | const [alerta, setAlerta] = useState({})
10 | const [cuentaConfirmada, setCuentaConfirmada] = useState(false)
11 |
12 | const params = useParams();
13 | const { id } = params
14 |
15 | useEffect(() => {
16 | const confirmarCuenta = async () => {
17 | try {
18 | const url = `/usuarios/confirmar/${id}`
19 | const { data } = await clienteAxios(url)
20 |
21 | setAlerta({
22 | msg: data.msg,
23 | error: false
24 | })
25 | setCuentaConfirmada(true)
26 |
27 | } catch (error) {
28 | setAlerta({
29 | msg: error.response.data.msg,
30 | error: true
31 | })
32 | }
33 | }
34 | confirmarCuenta();
35 | }, [])
36 |
37 | const { msg } = alerta
38 |
39 | return (
40 | <>
41 | Confirma tu cuenta y Comienza a crear tus {''}
42 | proyectos
43 |
44 |
45 |
46 | {msg &&
}
47 |
48 | {cuentaConfirmada && (
49 |
Inicia Sesión
53 | )}
54 |
55 | >
56 | )
57 | }
58 |
59 | export default ConfirmarCuenta
--------------------------------------------------------------------------------
/src/paginas/EditarProyecto.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useParams } from 'react-router-dom'
3 | import FormularioProyecto from '../components/FormularioProyecto'
4 | import useProyectos from "../hooks/useProyectos"
5 |
6 | const EditarProyecto = () => {
7 | const params = useParams();
8 | const { obtenerProyecto, proyecto, cargando, eliminarProyecto } = useProyectos()
9 |
10 | useEffect( () => {
11 | obtenerProyecto(params.id)
12 | }, [])
13 |
14 | const handleClick = () => {
15 | if(confirm('¿Deseas eliminar este proyecto?')) {
16 | eliminarProyecto(params.id)
17 | }
18 | }
19 |
20 | const { nombre } = proyecto
21 |
22 | if(cargando) return 'Cargando...'
23 |
24 | return (
25 | <>
26 |
27 |
28 |
Editar Proyecto: {nombre}
29 |
30 |
31 |
34 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | >
45 | )
46 | }
47 |
48 | export default EditarProyecto
--------------------------------------------------------------------------------
/src/paginas/Login.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import { Link, useNavigate } from 'react-router-dom'
3 | import Alerta from '../components/Alerta'
4 | import clienteAxios from '../config/clienteAxios'
5 | import useAuth from '../hooks/useAuth';
6 |
7 | const Login = () => {
8 | const [email, setEmail] = useState('')
9 | const [password, setPassword] = useState('')
10 | const [alerta, setAlerta] = useState({})
11 |
12 | const { setAuth } = useAuth();
13 |
14 | const navigate = useNavigate()
15 |
16 | const handleSubmit = async e => {
17 | e.preventDefault();
18 |
19 | if([email, password].includes('')) {
20 | setAlerta({
21 | msg: 'Todos los campos son obligatorios',
22 | error: true
23 | });
24 | return
25 | }
26 |
27 |
28 |
29 | try {
30 | const { data } = await clienteAxios.post('/usuarios/login', { email, password})
31 | setAlerta({})
32 | localStorage.setItem('token', data.token)
33 | setAuth(data)
34 | navigate('/proyectos')
35 | } catch (error) {
36 | setAlerta({
37 | msg: error.response.data.msg,
38 | error: true
39 | })
40 | }
41 |
42 | }
43 |
44 | const { msg } = alerta
45 |
46 | return (
47 | <>
48 | Inicia sesión y administra tus {''}
49 | proyectos
50 |
51 |
52 | {msg && }
53 |
54 |
94 |
95 |
106 |
107 | >
108 | )
109 | }
110 |
111 | export default Login
--------------------------------------------------------------------------------
/src/paginas/NuevoColaborador.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import FormularioColaborador from '../components/FormularioColaborador'
3 | import useProyectos from '../hooks/useProyectos';
4 | import { useParams } from 'react-router-dom'
5 | import Alerta from '../components/Alerta';
6 |
7 | const NuevoColaborador = () => {
8 |
9 | const { obtenerProyecto, proyecto, cargando, colaborador, agregarColaborador, alerta } = useProyectos()
10 | const params = useParams()
11 |
12 | useEffect(() => {
13 | obtenerProyecto(params.id)
14 | }, []);
15 |
16 |
17 | if(!proyecto?._id) return
18 |
19 | return (
20 | <>
21 | Añadir Colaborador(a) al Proyecto: {proyecto.nombre}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {cargando ? cargando...
: colaborador?._id && (
30 |
31 |
32 |
Resultado:
33 |
34 |
35 |
{colaborador.nombre}
36 |
37 |
44 |
45 |
46 |
47 | ) }
48 |
49 | >
50 | )
51 | }
52 |
53 | export default NuevoColaborador
--------------------------------------------------------------------------------
/src/paginas/NuevoPassword.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { Link, useParams } from 'react-router-dom'
3 | import clienteAxios from '../config/clienteAxios'
4 | import Alerta from '../components/Alerta'
5 |
6 | const NuevoPassword = () => {
7 |
8 | const [password, setPassword] = useState('')
9 | const [tokenValido, setTokenValido] = useState(false)
10 | const [alerta, setAlerta] = useState({})
11 | const [passwordModificado, setPasswordModificado] = useState(false)
12 |
13 | const params = useParams()
14 | const { token } = params
15 |
16 | useEffect(() => {
17 | const comprobarToken = async () => {
18 | try {
19 | await clienteAxios(`/usuarios/olvide-password/${token}`)
20 | setTokenValido(true)
21 | } catch (error) {
22 | setAlerta({
23 | msg: error.response.data.msg,
24 | error: true
25 | })
26 | }
27 | }
28 | comprobarToken()
29 | }, [])
30 |
31 | const handleSubmit = async e => {
32 | e.preventDefault();
33 |
34 | if(password.length < 6) {
35 | setAlerta({
36 | msg: 'El Password debe ser minimo de 6 caracteres',
37 | error: true
38 | })
39 | return
40 | }
41 |
42 | try {
43 | const url = `/usuarios/olvide-password/${token}`
44 |
45 | const { data } = await clienteAxios.post(url, { password })
46 | setAlerta({
47 | msg: data.msg,
48 | error: false
49 | })
50 | setPasswordModificado(true)
51 | } catch (error) {
52 | setAlerta({
53 | msg: error.response.data.msg,
54 | error: true
55 | })
56 | }
57 | }
58 |
59 | const { msg } = alerta
60 |
61 | return (
62 | <>
63 | Reestablece tu password y no pierdas acceso a tus {''}
64 | proyectos
65 |
66 |
67 | {msg && }
68 |
69 | { tokenValido && (
70 |
96 | )}
97 |
98 | {passwordModificado && (
99 | Inicia Sesión
103 | )}
104 | >
105 | )
106 | }
107 |
108 | export default NuevoPassword
--------------------------------------------------------------------------------
/src/paginas/NuevoProyecto.jsx:
--------------------------------------------------------------------------------
1 | import FormularioProyecto from "../components/FormularioProyecto"
2 |
3 | const NuevoProyecto = () => {
4 | return (
5 | <>
6 | Crear Proyecto
7 |
8 |
9 |
10 |
11 | >
12 | )
13 | }
14 |
15 | export default NuevoProyecto
--------------------------------------------------------------------------------
/src/paginas/OlvidePassword.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import { Link } from 'react-router-dom'
3 | import clienteAxios from '../config/clienteAxios'
4 | import Alerta from '../components/Alerta'
5 |
6 | const OlvidePassword = () => {
7 |
8 | const [email, setEmail] = useState('')
9 | const [alerta, setAlerta] = useState({})
10 |
11 | const handleSubmit = async e => {
12 | e.preventDefault();
13 |
14 | if(email === '' || email.length < 6) {
15 | setAlerta({
16 | msg: 'El Email es obligatorio',
17 | error: true
18 | });
19 | return
20 | }
21 |
22 | try {
23 | const { data } = await clienteAxios.post(`/usuarios/olvide-password`, { email })
24 |
25 | setAlerta({
26 | msg: data.msg,
27 | error: false
28 | })
29 |
30 | } catch (error) {
31 | setAlerta({
32 | msg: error.response.data.msg,
33 | error: true
34 | })
35 | }
36 |
37 |
38 | }
39 |
40 | const { msg } = alerta
41 |
42 | return (
43 | <>
44 | Recupera tu acceso y no pierdas tus {''}
45 | proyectos
46 |
47 |
48 | { msg && }
49 |
50 |
79 |
80 |
91 |
92 | >
93 | )
94 | }
95 |
96 | export default OlvidePassword
--------------------------------------------------------------------------------
/src/paginas/Proyecto.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useParams, Link } from 'react-router-dom'
3 | import useProyectos from '../hooks/useProyectos';
4 | import useAdmin from '../hooks/useAdmin';
5 | import ModalFormularioTarea from '../components/ModalFormularioTarea'
6 | import ModalEliminarTarea from '../components/ModalEliminarTarea'
7 | import ModalEliminarColaborador from '../components/ModalEliminarColaborador'
8 | import Tarea from '../components/Tarea';
9 | import Alerta from '../components/Alerta';
10 | import Colaborador from '../components/Colaborador';
11 | import io from 'socket.io-client'
12 |
13 | let socket;
14 |
15 | const Proyecto = () => {
16 | const params = useParams();
17 | const { obtenerProyecto, proyecto, cargando, handleModalTarea, alerta, submitTareasProyecto, eliminarTareaProyecto, actualizarTareaProyecto, cambiarEstadoTarea } = useProyectos()
18 |
19 | const admin = useAdmin()
20 |
21 |
22 | useEffect( () => {
23 | obtenerProyecto(params.id)
24 | }, [])
25 |
26 | useEffect(() => {
27 | socket = io(import.meta.env.VITE_BACKEND_URL)
28 | socket.emit('abrir proyecto', params.id)
29 | }, [])
30 |
31 | useEffect(() => {
32 | socket.on("tarea agregada", tareaNueva => {
33 | if(tareaNueva.proyecto === proyecto._id) {
34 | submitTareasProyecto(tareaNueva)
35 | }
36 | })
37 |
38 | socket.on('tarea eliminada', tareaEliminada => {
39 | if(tareaEliminada.proyecto === proyecto._id) {
40 | eliminarTareaProyecto(tareaEliminada)
41 | }
42 | })
43 |
44 | socket.on('tarea actualizada', tareaActualizada => {
45 | if(tareaActualizada.proyecto._id === proyecto._id) {
46 | actualizarTareaProyecto(tareaActualizada)
47 | }
48 | })
49 |
50 | socket.on('nuevo estado', nuevoEstadoTarea => {
51 | if(nuevoEstadoTarea.proyecto._id === proyecto._id) {
52 | cambiarEstadoTarea(nuevoEstadoTarea)
53 | }
54 | })
55 | })
56 |
57 | const { nombre } = proyecto
58 | if(cargando) return 'Cargando...'
59 | const { msg } = alerta
60 |
61 |
62 | return (
63 | <>
64 |
65 |
{nombre}
66 |
67 | {admin && (
68 |
69 |
72 |
Editar
76 |
77 | )}
78 |
79 |
80 | {admin && (
81 |
91 | )}
92 |
93 |
94 | Tareas del Proyecto
95 |
96 |
97 |
98 | {proyecto.tareas?.length ?
99 | proyecto.tareas?.map( tarea => (
100 |
104 | )) :
105 |
No hay tareas en este proyecto
}
106 |
107 |
108 | {admin && (
109 | <>
110 |
111 |
Colaboradores
112 |
Añadir
116 |
117 |
118 |
119 | {proyecto.colaboradores?.length ?
120 | proyecto.colaboradores?.map( colaborador => (
121 |
125 | )) :
126 |
No hay Colaboradores en este proyecto
}
127 |
128 | >
129 | )}
130 |
131 |
132 |
133 |
134 |
135 |
136 | >
137 | )
138 |
139 | }
140 |
141 | export default Proyecto
--------------------------------------------------------------------------------
/src/paginas/Proyectos.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import useProyectos from "../hooks/useProyectos"
3 | import PreviewProyecto from "../components/PreviewProyecto"
4 | import Alerta from "../components/Alerta"
5 |
6 | const Proyectos = () => {
7 | const { proyectos, alerta } = useProyectos()
8 | const { msg } = alerta
9 |
10 | return (
11 | <>
12 | Proyectos
13 |
14 | {msg && }
15 |
16 |
17 | {proyectos.length ?
18 | proyectos.map(proyecto => (
19 |
23 | ))
24 | :
No hay proyectos aún
}
25 |
26 | >
27 | )
28 | }
29 |
30 | export default Proyectos
--------------------------------------------------------------------------------
/src/paginas/Registrar.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import {Link} from 'react-router-dom'
3 | import Alerta from '../components/Alerta'
4 | import clienteAxios from '../config/clienteAxios'
5 |
6 | const Registrar = () => {
7 | const [ nombre, setNombre ] = useState('')
8 | const [ email, setEmail ] = useState('')
9 | const [ password, setPassword ] = useState('')
10 | const [ repetirPassword, setRepetirPassword ] = useState('')
11 | const [ alerta, setAlerta ] = useState({})
12 |
13 | const handleSubmit = async e => {
14 | e.preventDefault();
15 |
16 | if([nombre, email, password, repetirPassword].includes('')) {
17 | setAlerta({
18 | msg: 'Todos los campos son obligatorios',
19 | error: true
20 | })
21 | return
22 | }
23 |
24 | if(password !== repetirPassword ) {
25 | setAlerta({
26 | msg: 'Los password no son iguales',
27 | error: true
28 | })
29 | return
30 | }
31 |
32 | if(password.length < 6 ) {
33 | setAlerta({
34 | msg: 'El Password es muy corto, agrega minimo 6 caracteres',
35 | error: true
36 | })
37 | return
38 | }
39 |
40 | setAlerta({})
41 |
42 | // Crear el usuario en la API
43 | try {
44 | const { data } = await clienteAxios.post(`/usuarios`, {nombre, email, password} )
45 |
46 | setAlerta({
47 | msg: data.msg,
48 | error: false
49 | })
50 |
51 | setNombre('')
52 | setEmail('')
53 | setPassword('')
54 | setRepetirPassword('')
55 | } catch (error) {
56 | setAlerta({
57 | msg: error.response.data.msg,
58 | error: true
59 | })
60 | }
61 | }
62 |
63 | const { msg } = alerta
64 |
65 | return (
66 | <>
67 | Crea tu Cuenta y Administra tus {''}
68 | proyectos
69 |
70 |
71 | { msg && }
72 |
73 |
143 |
144 |
155 |
156 | >
157 | )
158 | }
159 |
160 | export default Registrar
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["index.html", "./src/**/*.jsx"],
3 | theme: {
4 | extend: {},
5 | },
6 | plugins: [],
7 | };
8 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()]
7 | })
8 |
--------------------------------------------------------------------------------