diff --git a/FrontEnd/src/Components/Clase/Clase.jsx b/FrontEnd/src/Components/Clase/Clase.jsx index 66fc96cf..15965b4b 100644 --- a/FrontEnd/src/Components/Clase/Clase.jsx +++ b/FrontEnd/src/Components/Clase/Clase.jsx @@ -1,66 +1,76 @@ -import { Typography, Button, CardContent} from '@mui/material' -import React from 'react' -import Box from '@mui/material/Box' -import Card from '@mui/material/Card' -import CardActions from '@mui/material/CardActions' -import Divider from '@mui/material/Divider'; +import { Typography, Button, CardContent } from '@mui/material'; +import React from 'react'; +import Box from '@mui/material/Box'; +import Card from '@mui/material/Card'; +import CardActions from '@mui/material/CardActions'; +import { nivelDict } from '../../utils/constants'; - -function Clase({handleClick, handleMoreInfo, clase}) { - - const nivelDict = { - '1' : 'Desde cero', - '2' : 'Con bases', - '3' : 'Intermedio', - '4' : 'Avanzado' - } - - return ( - - - {clase.clave}. {clase.nombre_curso} -
- { - clase.status === "Inscrito" ? - Ya estas inscrito - : - null - } - { - clase.status === "ListaEspera" ? - Estas en lista de espera - : - null - } - - Periodo: {clase.clavePeriodo} - Modalidad: {clase.modalidad} - Nivel: {nivelDict[clase.nivel]} - = 1 ? "red" : "black" }} variant='body1'> - { Number(clase.cupo_actual) / Number(clase.cupo_maximo) >=1 ? Lleno - : ¡Curso {(Number(clase.cupo_actual) / Number(clase.cupo_maximo) * 100).toFixed()}% lleno! - } - - -
- - - { Number(clase.cupo_actual) < Number(clase.cupo_maximo) ? - : - } - -
- ) +function Clase({ handleClick, handleMoreInfo, clase }) { + return ( + + + + {`${clase.clave}.${clase.nombre_curso}`} + +
+ { + clase.status === 'Inscrito' + ? Ya estas inscrito + : null + } + { + clase.status === 'ListaEspera' + ? Estas en lista de espera + : null + } + + + Periodo: + {` ${clase.clavePeriodo}`} + + + Modalidad: + {` ${clase.modalidad}`} + + + Nivel: + {` ${nivelDict[clase.nivel]}`} + + = 1 ? 'red' : 'black' }} variant="body1"> + { Number(clase.cupo_actual) / Number(clase.cupo_maximo) >= 1 ? Lleno + : ( + + ¡Curso + {(Number(clase.cupo_actual) / Number(clase.cupo_maximo) * 100).toFixed()} + % lleno! + + )} + + +
+ + + { Number(clase.cupo_actual) < Number(clase.cupo_maximo) + ? ( + + ) : ( + + )} + +
+ ); } -export default Clase +export default Clase; diff --git a/FrontEnd/src/Components/Clase/ClaseModal.jsx b/FrontEnd/src/Components/Clase/ClaseModal.jsx index 335cb552..614f027b 100644 --- a/FrontEnd/src/Components/Clase/ClaseModal.jsx +++ b/FrontEnd/src/Components/Clase/ClaseModal.jsx @@ -1,72 +1,79 @@ -import React from 'react' -import Box from '@mui/material/Box' -import Typography from '@mui/material/Typography' +import React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; import TableCell from '@mui/material/TableCell'; import TableContainer from '@mui/material/TableContainer'; -import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; import Paper from '@mui/material/Paper'; +import { nivelDict } from '../../utils/constants'; +function ClaseModal({ clase }) { + const horario = [ + { dia: 'Lunes', hora: clase.lunes }, + { dia: 'Martes', hora: clase.martes }, + { dia: 'Miércoles', hora: clase.miercoles }, + { dia: 'Jueves', hora: clase.jueves }, + { dia: 'Viernes', hora: clase.viernes }, + { dia: 'Sábado', hora: clase.sabado }, + ]; -const ClaseModal = ({clase}) => { - - const nivelDict = { - '1' : 'Desde cero', - '2' : 'Con bases', - '3' : 'Intermedio', - '4' : 'Avanzado' - } - - return ( - <> - - {clase.clave}. {clase.nombre_curso} - Sobre el curso - Periodo: {clase.clavePeriodo} - Area: {clase.area} - Nivel: {nivelDict[clase.nivel]} - Modalidad: {clase.modalidad} - Horario: - - - - - Lunes - {clase.lunes ? clase.lunes : '-'} - - - Martes - {clase.martes ? clase.martes : '-'} - - - Miércoles - {clase.miercoles ? clase.miercoles : '-'} - - - Jueves - {clase.jueves ? clase.jueves : '-'} - - - Viernes - {clase.viernes ? clase.viernes : '-'} - - - Sábado - {clase.sabado ? clase.sabado : '-'} - - -
-
- Cupos: ¡Curso {(Number(clase.cupo_actual) / Number(clase.cupo_maximo) * 100).toFixed()}% lleno! -
- - ) + return ( + + + {`${clase.clave}.${clase.nombre_curso}`} + + Sobre el curso + + Periodo: + {` ${clase.clavePeriodo}`} + + + Area: + {` ${clase.area}`} + + + Nivel: + {` ${nivelDict[clase.nivel]}`} + + + Modalidad: + {` ${clase.modalidad}`} + + + Horario: + {` `} + + + + + {horario.map((h, i) => ( + + {h.dia} + {h.hora ? h.hora : '-'} + + ))} + +
+
+ + Cupos: + {` ¡Curso ${(Number(clase.cupo_actual) / Number(clase.cupo_maximo) * 100).toFixed()} % lleno!`} + +
+ ); } -export default ClaseModal \ No newline at end of file +export default ClaseModal; \ No newline at end of file diff --git a/FrontEnd/src/Components/Clase/EditarInscripcion.jsx b/FrontEnd/src/Components/Clase/EditarInscripcion.jsx new file mode 100644 index 00000000..f8da5fde --- /dev/null +++ b/FrontEnd/src/Components/Clase/EditarInscripcion.jsx @@ -0,0 +1,131 @@ +import React from 'react'; +import { + TextField, + Typography, +} from '@mui/material'; +import MenuItem from '@mui/material/MenuItem'; +import { + classAtributes, dayAtributes, niveloptions, +} from '../../utils/constants'; + +function EditarInscripcion({ + clase, currentProfesor, handleChange, profesorList, handleChangeProfesor, +}) { + const textFieldStyle = { + paddingBottom: '15px', + fontFamily: 'arial', + marginRight: 10, + width: '40%', + }; + + const containerStyle = { + width: '100%', + borderTop: '1px solid gray', + paddingTop: '5px', + }; + + const labelStyle = { + textAlign: 'center', + marginTop: '10px', + }; + + return ( +
+ {classAtributes.map((atribute) => ( + handleChange(e)} + name={atribute.key} + key={atribute.key} + value={clase[atribute.key]} + autoFocus + /> + ))} + handleChange(e)} + select + > + {['presencial', 'online'].map((e) => ( + + {e} + + ))} + + handleChange(e)} + select + > + {niveloptions.map((e) => ( + + {e} + + ))} + +
+ Horarios + {dayAtributes.map((atribute) => ( + handleChange(e)} + name={atribute.key} + key={atribute.key} + value={clase[atribute.key]} + /> + ))} +
+
+ Datos del profesor +
+ handleChangeProfesor(e)} + select + > + {profesorList.map((e) => ( + + {`${e.nombre} ${e.apellidos}`} + + ))} + + + +
+
+ ); +} + +export default EditarInscripcion; diff --git a/FrontEnd/src/Components/Clase/HeaderInscripcionClase.jsx b/FrontEnd/src/Components/Clase/HeaderInscripcionClase.jsx index aa07859e..35742381 100644 --- a/FrontEnd/src/Components/Clase/HeaderInscripcionClase.jsx +++ b/FrontEnd/src/Components/Clase/HeaderInscripcionClase.jsx @@ -9,87 +9,37 @@ import { InsertDriveFile } from '@mui/icons-material'; import Select from 'react-select'; import { CSVLink } from 'react-csv'; import { subirClases, subirProfes } from '../../api/csv'; +import { parseCSV } from '../../utils/utilFunctions'; function HeaderInscripcionClase({ - data, setOpenModal, dataPeriodo, handleSelectChange, resetClases + data, setOpenModal, dataPeriodo, handleSelectChange, resetClases, }) { const importFile = () => { const input = document.createElement('input'); input.type = 'file'; - input.click(); - input.onchange = (e) => { + input.onchange = async (e) => { const { target } = e; if (!target.files) { return; } - let file; - file = target.files[0]; - + const file = target.files[0]; const reader = new FileReader(); reader.readAsText(file); - reader.onload = (e) => { - if (file.name.includes('.csv')) { - const result = e.target?.result?.toString(); - result !== undefined ? sendCSV(result) : alert('error'); - } else { - alert('error: el archivo necesita ser tipo markdown o txt'); - } + reader.onload = async (e) => { + const result = e.target?.result?.toString(); + const isCsvFile = file.name.includes('.csv'); + isCsvFile ? sendCSV(result) : alert('error: el archivo necesita ser tipo markdown o txt'); }; }; }; const sendCSV = async (csv) => { - const csvArray = csv.split('\n').slice(1); - const clasesJson = []; - const profesoresJson = []; - const profesorHash = {}; - - csvArray.forEach((iterator, i) => { - const iteratorArray = iterator.split(','); - const clavePeriodo = iteratorArray[5]; - - clasesJson[i] = { - clave: iteratorArray[0], - nombre_curso: iteratorArray[1], - nivel: iteratorArray[2], - area: iteratorArray[3], - modalidad: iteratorArray[4], - clavePeriodo, - cupo_maximo: iteratorArray[6], - edad_minima: iteratorArray[7], - edad_maxima: iteratorArray[8], - lunes: iteratorArray[9], - martes: iteratorArray[10], - miercoles: iteratorArray[11], - jueves: iteratorArray[12], - viernes: iteratorArray[13], - sabado: iteratorArray[14], - matriculaProfesor: iteratorArray[17], - cupo_actual: '0', - nombreProfesor: iteratorArray[15].trim(), - apellidosProfesor: iteratorArray[16].trim(), - }; - - const profesorMatricula = iteratorArray[17]; - if (!profesorHash[profesorMatricula]) { - profesorHash[profesorMatricula] = true; - profesoresJson.push({ - nombre: iteratorArray[15].trim(), - apellidos: iteratorArray[16].trim(), - matricula: profesorMatricula, - correo: iteratorArray[18], - fecha_de_nacimiento: '', - num_telefono: '', - num_cursos_impartidos: '0', - idUser: '', - }); - } - }); - - await subirProfes({ profesoresJson: JSON.stringify(profesoresJson) }); - await subirClases({ clasesJson: JSON.stringify(clasesJson) }); + const { claseList, profesorList } = parseCSV(csv); + + await subirProfes({ profesoresJson: JSON.stringify(profesorList) }); + await subirClases({ clasesJson: JSON.stringify(claseList) }); resetClases(); }; diff --git a/FrontEnd/src/Components/Clase/InscripcionClase.jsx b/FrontEnd/src/Components/Clase/InscripcionClase.jsx new file mode 100644 index 00000000..93b493ba --- /dev/null +++ b/FrontEnd/src/Components/Clase/InscripcionClase.jsx @@ -0,0 +1,84 @@ +import React, { useState } from 'react'; +import { + classTemplate, +} from '../../utils/constants'; +import HeaderInscripcionClase from './HeaderInscripcionClase'; +import BodyInscripcionClase from './BodyInscripcionClase'; +import ModalInscripcionClase from './ModalInscripcionClase'; +import { getWaitList } from '../../api/waitList'; +import { getStudents } from '../../api/students'; + +function InscripcionClase({ + data, abrirCerrarModal, resetClases, dataPeriodo, handleSelectChange, + profesorList, seleccionarClase, clase, currentProfesor, + handleChange, handleChangeProfesor, currentOperation, openModal, +}) { + const [currentClase, setCurrentClase] = useState(null); + const [currentWaitList, setCurrentWaitList] = useState(null); + + const getClassWaitList = async (clase) => { + try { + const studentsList = await getStudents(); + const students = await studentsList.json(); + const studentsById = students.reduce((obj, student) => { + obj[student._id] = student; + return obj; + }, {}); + + const responseWaitList = await getWaitList(); + const waitList = await responseWaitList.json(); + const result = []; + + waitList.forEach((inWaitList) => { + if (inWaitList.idClase === clase._id && studentsById[inWaitList.idAlumno]) { + const student = studentsById[inWaitList.idAlumno]; + result.push({ + _id: inWaitList._id, + studentName: `${student.nombre} ${student.apellido_paterno} ${student.apellido_materno}`, + time_stamp: inWaitList.time_stamp, + }); + } + }); + + result.sort((a, b) => (a > b ? 1 : a < b ? -1 : 0)); + setCurrentWaitList(result); + setCurrentClase(clase); + seleccionarClase(clase, 'AbrirWaitList'); + } catch (error) { + console.error(error); + } + }; + + return ( +
+ { seleccionarClase(classTemplate, 'Crear'); }} + resetClases={resetClases} + dataPeriodo={dataPeriodo} + handleSelectChange={handleSelectChange} + /> + + +
+ ); +} + +export default InscripcionClase; diff --git a/FrontEnd/src/Components/Clase/ModalInscripcionClase.jsx b/FrontEnd/src/Components/Clase/ModalInscripcionClase.jsx index 8e5b5e2a..64ae14b9 100644 --- a/FrontEnd/src/Components/Clase/ModalInscripcionClase.jsx +++ b/FrontEnd/src/Components/Clase/ModalInscripcionClase.jsx @@ -2,203 +2,63 @@ import React from 'react'; import { Button, Modal, - TextField, Typography, } from '@mui/material'; -import MenuItem from '@mui/material/MenuItem'; import WaitList from './WaitList'; +import EditarInscripcion from './EditarInscripcion'; +import { mapNiveles } from '../../utils/utilFunctions'; import { - classAtributes, dayAtributes, niveloptions, -} from '../../utils/constants'; + createClass, deleteClasses, updateClass, +} from '../../api/classes.js'; function ModalInscripcionClase({ - clase, currentProfesor, handleChange, profesorList, handleChangeProfesor, - modalSubmit, currentOperation, openModal, setOpenModal, currentClase, currentWaitList, + resetClases, clase, currentProfesor, handleChange, profesorList, handleChangeProfesor, + currentOperation, openModal, setOpenModal, currentClase, currentWaitList, }) { - const bodyEditar = ( -
- {classAtributes.map((atribute) => ( - { - handleChange(e); - }} - name={atribute.key} - key={atribute.key} - value={clase[atribute.key]} - autoFocus - /> - ))} - { - handleChange(e); - }} - select - > - {['presencial', 'online'].map((e) => ( - - {e} - - ))} - - { - handleChange(e); - }} - select - > - {niveloptions.map((e) => ( - - {e} - - ))} - -
- - Horarios - - {dayAtributes.map((atribute) => ( - { - handleChange(e); - }} - name={atribute.key} - key={atribute.key} - value={clase[atribute.key]} - autoFocus - /> - ))} -
-
- - Datos del profesor - -
- { - handleChangeProfesor(e); - }} - select - > - {profesorList.map((e) => ( - - {`${e.nombre} ${e.apellidos}`} - - ))} - - - -
-
- ); + const modalSubmit = async (e) => { + e.preventDefault(); + try { + if (currentOperation === 'Eliminar') { + await deleteClasses({ + _id: clase._id, + }); + } else if (currentOperation === 'Crear') { + const claseACrear = mapNiveles(classTemplate, currentProfesor); + await createClass(claseACrear); + } else if (currentOperation === 'Editar') { + const claseAModificar = mapNiveles(clase, currentProfesor); + await updateClass(claseAModificar); + } + } catch (error) { + console.log(error); + } + setOpenModal(); + resetClases(); + }; const generateActionModalBody = () => (
- {currentOperation === 'ELiminar' ? - 'Eliminar una clase' : - (currentOperation === 'Editar' ? - 'Actualizar una clase' : 'Añadir nueva clase')} + {currentOperation === 'ELiminar' + ? 'Eliminar una clase' + : (currentOperation === 'Editar' + ? 'Actualizar una clase' : 'Añadir nueva clase')} - { - currentOperation === 'Eliminar' && + { + currentOperation === 'Eliminar' + && ( {`Esta clase ${clase.clave} y toda su información relacionada a ella va a ser eliminada`} - + + ) } { - (currentOperation === 'Editar' || currentOperation === 'Crear') && bodyEditar + (currentOperation === 'Editar' || currentOperation === 'Crear') && }

-
- ) + ); return ( diff --git a/FrontEnd/src/Components/Dialog/ConfirmationDialog.jsx b/FrontEnd/src/Components/Dialog/ConfirmationDialog.jsx index d3c60edd..f894fceb 100644 --- a/FrontEnd/src/Components/Dialog/ConfirmationDialog.jsx +++ b/FrontEnd/src/Components/Dialog/ConfirmationDialog.jsx @@ -1,16 +1,103 @@ -import React, {useState} from 'react'; +import React from 'react'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; +import { findTerm } from '../../api/term'; +import { + getClassStudent, + deleteClassStudent, +} from '../../api/classStudent'; +import { + createClassStudent, +} from '../../api/classStudent'; +import { + createWaitList, + getWaitList, + deleteWaitList, +} from '../../api/waitList'; +import { dialogTitle, dialogContent } from '../../utils/utilFunctions'; export default function ConfirmationDialog({ - handleClaseRegistrada, handleCancelarClaseRegistrada, - handleSalirListaEspera, handleClose, open, clase, handleListaEspera, action + setError, setErrorMsg, handleClose, open, clase, action }) { + const handleListaEspera = async (clase) => { + const waitListResponse = await getWaitList(); + const lista = (await waitListResponse.json()).filter( + (lista) => lista.idAlumno === currentStudent._id, + ); + + await createWaitList({ + idAlumno: currentStudent._id, + idClase: clase._id, + time_stamp: new Date().toISOString(), + status: 'Espera', + }); + clase.status = 'ListaEspera'; + handleCloseDialog(); + }; + + const handleSalirListaEspera = async (clase) => { + const periodoResponse = await findTerm({ clave: clase.clavePeriodo }); + const periodo = await periodoResponse.json(); + + const myWaitListResponse = await getWaitList(); + const myWaitList = (await myWaitListResponse.json()).filter( + (aWList) => aWList.idClase === clase._id + && aWList.idAlumno === currentStudent._id + && aWList.idPeriodo === periodo[0]._id, + ); + + await deleteWaitList({ _id: myWaitList[0]._id }); + clase.status = ''; + handleCloseDialog(); + }; + + const handleClaseRegistrada = async (clase) => { + try { + const periodoResponse = await findTerm({ clave: clase.clavePeriodo }); + const periodo = await periodoResponse.json(); + + const response = await createClassStudent({ + idClase: clase._id, + idAlumno: currentStudent._id, + idPeriodo: periodo[0]._id, + }); + + const data = await response.json(); + + if (data.msg.includes('Un documento fue insertado con el ID')) { + clase.status = 'Inscrito'; + handleCloseDialog(); + } else { + handleCloseDialog(); + setErrorMsg(data.msg); + setError(true); + } + } catch (error) { + alert(error); + } + }; + + const handleCancelarClaseRegistrada = async (clase) => { + const periodoResponse = await findTerm({ clave: clase.clavePeriodo }); + const periodo = await periodoResponse.json(); + + const myClassStudentResponse = await getClassStudent(); + const myClassStudent = (await myClassStudentResponse.json()).filter( + (aClass) => aClass.idClase === clase._id + && aClass.idAlumno === currentStudent._id + && aClass.idPeriodo === periodo[0]._id, + ); + + await deleteClassStudent({ _id: myClassStudent[0]._id }); + clase.status = ''; + handleClose(); + }; + const handleClick = () =>{ switch (action) { case 'ListaEspera': @@ -28,33 +115,6 @@ export default function ConfirmationDialog({ } } - const dialogContent = (clase) => { - switch (action) { - case 'ListaEspera': - return `Estas seguro que quieres entrar a la lista de espera de la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}.` - case 'Registrar': - return `Estas seguro que quieres inscribir la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}, - recuerda que hay inscripciones limitadas.` - case 'CancelarInscripcion': - return `Estas seguro que quieres cancelar tu inscripción de la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}.` - case 'SalirLista': - return `Estas seguro que quieres salir de la lista de espera de la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}.` - } - } - - const dialogTitle = () => { - switch (action) { - case 'ListaEspera': - return '¿Entrar a lista de espera?' - case 'Registrar': - return '¿Inscribir esta clase? ' - case 'CancelarInscripcion': - return '¿Cancelar inscripción?' - case 'SalirLista': - return '¿Salir de lista de espera?' - } - } - return (
{ - dialogTitle() + dialogTitle(action) } { - dialogContent(clase) + dialogContent(clase, action) } diff --git a/FrontEnd/src/Components/Registro/ButtonActionsInscripcion.jsx b/FrontEnd/src/Components/Registro/ButtonActionsInscripcion.jsx new file mode 100644 index 00000000..26d50e81 --- /dev/null +++ b/FrontEnd/src/Components/Registro/ButtonActionsInscripcion.jsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { + Button, +} from '@mui/material'; + +function ButtonActionsInscripcion({ params }) { + return (Number(params.row.cupo_actual) < Number(params.row.cupo_maximo) ? ( + + ) : ( + + )) +} + +export default ButtonActionsInscripcion; \ No newline at end of file diff --git a/FrontEnd/src/Components/Registro/LoadingRegisterData.jsx b/FrontEnd/src/Components/Registro/LoadingRegisterData.jsx new file mode 100644 index 00000000..281c336f --- /dev/null +++ b/FrontEnd/src/Components/Registro/LoadingRegisterData.jsx @@ -0,0 +1,58 @@ +import React from 'react'; +import CircularProgress from '@mui/material/CircularProgress'; +import { + Link, + Typography, +} from '@mui/material'; +import Box from '@mui/material/Box'; + +function LoadingRegisterData({ changeContent, students, clases }) { + if (!students || !clases) { + return ( + + + + ); + } + + if (students !== null && students.length === 0) { + return ( + + + Registro clases (Inscripción) + + + No tienes alumnos registrados, ve a + changeContent('Profile')} + variant="h3" + sx={{ mx: 2 }} + > + Perfil + + para agregar alumnos. + + + ); + } + + return null; +} + +export default LoadingRegisterData; diff --git a/FrontEnd/src/Components/Registro/RegistroClasesBody.jsx b/FrontEnd/src/Components/Registro/RegistroClasesBody.jsx new file mode 100644 index 00000000..056a271e --- /dev/null +++ b/FrontEnd/src/Components/Registro/RegistroClasesBody.jsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { + getNivel, getHorario, getProfesor, getCupo, +} from '../../utils/utilFunctions'; +import ButtonActionsInscripcion from './ButtonActionsInscripcion'; +import RegistroClasesTable from './RegistroClasesTable'; + +function RegistroClasesBody({ + handleClick, filteredClasses, classNames, +}) { + const columns = [ + { + field: 'clavePeriodo', + headerName: 'Periodo', + width: 110, + editable: false, + }, + { + field: 'clave', + headerName: 'Clave', + width: 90, + }, + { + field: 'nombre_curso', + headerName: 'Curso', + width: 90, + editable: false, + }, + { + field: 'nivel', + headerName: 'Nivel', + width: 100, + editable: false, + valueGetter: getNivel, + }, + { + field: 'area', + headerName: 'Area', + width: 110, + editable: false, + }, + { + field: 'modalidad', + headerName: 'Modalidad', + width: 110, + editable: false, + }, + { + field: 'horario', + headerName: 'Horario', + width: 150, + editable: false, + valueGetter: getHorario, + }, + { + field: 'profesor', + headerName: 'Profesor', + width: 140, + editable: 'false', + valueGetter: getProfesor, + }, + { + field: 'cupos', + headerName: '% curso lleno', + width: 100, + editable: 'false', + valueGetter: getCupo, + }, + { + field: 'actions', + headerName: 'Inscripción', + type: 'actions', + width: 115, + renderCell: (params) => , + }, + ]; + + return ( + + ); +} + +export default RegistroClasesBody; diff --git a/FrontEnd/src/Components/Registro/RegistroClasesError.jsx b/FrontEnd/src/Components/Registro/RegistroClasesError.jsx new file mode 100644 index 00000000..c939e706 --- /dev/null +++ b/FrontEnd/src/Components/Registro/RegistroClasesError.jsx @@ -0,0 +1,66 @@ +import Box from '@mui/material/Box'; +import React from 'react'; +import { + Alert, Button, AlertTitle, +} from '@mui/material'; +import Snackbar from '@mui/material/Snackbar'; +import Modal from '@mui/material/Modal'; + +function RegistroClasesError({ + selectAlertOpen, setSelectAlertOpen, error, setError, errorMsg, +}) { + return ( + <> + setSelectAlertOpen(false)} + > + + Selecciona un alumno para inscribir clases o entrar a la lista de + espera + + + setError(!error)} + sx={{ overflow: 'scroll' }} + > + + + Error + {errorMsg} +
+ +
+
+
+ + ); +} + +export default RegistroClasesError; diff --git a/FrontEnd/src/Components/Registro/RegistroClasesHeader.jsx b/FrontEnd/src/Components/Registro/RegistroClasesHeader.jsx new file mode 100644 index 00000000..fcc5b33d --- /dev/null +++ b/FrontEnd/src/Components/Registro/RegistroClasesHeader.jsx @@ -0,0 +1,116 @@ +import Box from '@mui/material/Box'; +import React from 'react'; +import { + Typography, + TextField, + MenuItem, +} from '@mui/material'; +import Autocomplete from '@mui/material/Autocomplete'; +import InputLabel from '@mui/material/InputLabel'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; +import { + calculateAge, +} from '../../utils/utilFunctions'; +import { getWaitList } from '../../api/waitList'; +import { + getClassStudent, +} from '../../api/classStudent'; + +function RegistroClasesHeader({ + classNames, clases, currentStudent, students, setFilteredClasses, setCurrentStudent, +}) { + const filterClasses = async (student) => { + const age = calculateAge(student.fecha_de_nacimiento); + let waitList = []; + let myClasses = []; + + const filter = clases.filter( + (clase) => Number(clase.edad_minima) < age + && age < (clase.edad_maxima ? Number(clase.edad_maxima) : 99), + ).map((aClass) => ({ ...aClass, status: '' })); + + const waitListResponse = await getWaitList(); + waitList = waitListResponse.json().filter((lista) => lista.idAlumno === student._id); + + waitList.forEach((inWaitList) => { + const classIndex = filter.findIndex((aClass) => aClass._id === inWaitList.idClase); + if (classIndex !== -1) { + filter[classIndex].status = 'ListaEspera'; + } + }); + + const myClassesResponse = await getClassStudent(); + myClasses = myClassesResponse.json().filter((clase) => clase.idAlumno === student._id); + + myClasses.forEach((myClass) => { + const classIndex = filter.findIndex((aClass) => aClass._id === myClass.idClase); + if (classIndex !== -1) { + filter[classIndex].status = 'Inscrito'; + } + }); + + setFilteredClasses(filter); + }; + + const handleNameFilter = (value) => { + const filteredClasses = clases.filter( + (clase) => clase.nombre_curso.toLowerCase().includes(value.trim().toLowerCase()), + ); + setFilteredClasses(filteredClasses); + }; + + const handleChange = (e) => { + if (e.target.value === '') { + setFilteredClasses(clases); + setCurrentStudent(null); + return; + } + setCurrentStudent(e.target.value); + filterClasses(e.target.value); + }; + + return ( + <> + + Registro clases (Inscripción) + + + + Estudiantes + + + handleNameFilter(newvalue)} + onInputChange={(_e, newvalue) => handleNameFilter(newvalue)} + sx={{ display: { xs: 'flex', md: 'none' }, mt: 1 }} + fullWidth + renderInput={(params) => ( + + )} + /> + + + ); +} + +export default RegistroClasesHeader; diff --git a/FrontEnd/src/Components/Registro/RegistroClasesSearchbar.jsx b/FrontEnd/src/Components/Registro/RegistroClasesSearchbar.jsx new file mode 100644 index 00000000..e1932992 --- /dev/null +++ b/FrontEnd/src/Components/Registro/RegistroClasesSearchbar.jsx @@ -0,0 +1,124 @@ +import Box from '@mui/material/Box'; +import React from 'react'; +import { + Card, + CardContent, + TextField, + MenuItem, +} from '@mui/material'; +import Autocomplete from '@mui/material/Autocomplete'; +import SearchIcon from '@mui/icons-material/Search'; +import MiRegistro from './MiRegistro'; + +function RegistroClasesSearchbar({ setItems, classNames }) { + return ( + + + + + { + setItems([ + { + columnField: 'nombre_curso', + operatorValue: 'contains', + value: newvalue, + }, + ]); + }} + onInputChange={(_e, newvalue) => { + setItems([ + { + columnField: 'nombre_curso', + operatorValue: 'contains', + value: newvalue, + }, + ]); + }} + renderInput={(params) => ( + + )} + /> + { + setItems([ + { + columnField: 'nivel', + operatorValue: 'contains', + value: e.target.value, + }, + ]); + }} + select + > + {[ + '', + 'Desde cero', + 'Con bases', + 'Intermedio', + 'Avanzado', + ].map((e) => ( + + {e} + + ))} + + { + setItems([ + { + columnField: 'clavePeriodo', + operatorValue: 'contains', + value: e.target.value, + }, + ]); + }} + /> + { + setItems([ + { + columnField: 'modalidad', + operatorValue: 'contains', + value: e.target.value, + }, + ]); + }} + /> + + + + + ); +} + +export default RegistroClasesSearchbar; diff --git a/FrontEnd/src/Components/Registro/RegistroClasesTable.jsx b/FrontEnd/src/Components/Registro/RegistroClasesTable.jsx new file mode 100644 index 00000000..48fcc71a --- /dev/null +++ b/FrontEnd/src/Components/Registro/RegistroClasesTable.jsx @@ -0,0 +1,113 @@ +import Box from '@mui/material/Box'; +import React, { useState } from 'react'; +import { + Typography, +} from '@mui/material'; +import { DataGrid } from '@mui/x-data-grid'; +import Modal from '@mui/material/Modal'; +import ClaseModal from '../Clase/ClaseModal'; +import Clase from '../Clase/Clase'; +import RegistroClasesSearchbar from './RegistroClasesSearchbar'; + +function RegistroClasesTable({ + handleClick, filteredClasses, classNames, columns +}) { + const [items, setItems] = useState([]); + const [currentClase, setCurrentClase] = useState(); + const [openMoreInfo, setOpenMoreInfo] = useState(false); + + const handleMoreInfo = (clase) => { + setCurrentClase(clase); + setOpenMoreInfo(!openMoreInfo); + }; + + return ( + <> + + {filteredClasses.length !== 0 ? ( + filteredClasses.map((e) => ( + + )) + ) : ( + + + No hay clases disponibles por el momento. + + + )} + + + + + row._id} + getRowHeight={() => 'auto'} + filterModel={{ + items, + }} + getRowClassName={(params) => `theme--${params.row.status}`} + /> + + setOpenMoreInfo(!openMoreInfo)} + sx={{ overflowY: 'scroll' }} + > + + + + + ); +} + +export default RegistroClasesTable; diff --git a/FrontEnd/src/Pages/AdministratorClassRegister/ShowClass.jsx b/FrontEnd/src/Pages/AdministratorClassRegister/ShowClass.jsx index e06643f8..339bfef9 100644 --- a/FrontEnd/src/Pages/AdministratorClassRegister/ShowClass.jsx +++ b/FrontEnd/src/Pages/AdministratorClassRegister/ShowClass.jsx @@ -1,18 +1,12 @@ import React, { useState, useEffect } from 'react'; -import { getWaitList } from '../../api/waitList'; -import { getStudents } from '../../api/students'; import { getPeriodos } from '../../api/Periodos'; import { getProfesors } from '../../api/profesors.js'; +import { getClasses } from '../../api/classes.js'; import { - createClass, deleteClasses, getClasses, updateClass, -} from '../../api/classes.js'; -import { - classTemplate, nivelesMapa, claseActualDefault, profesorVacioInscripcion, + classTemplate, claseActualDefault, profesorVacioInscripcion, } from '../../utils/constants'; -import HeaderInscripcionClase from '../../Components/Clase/HeaderInscripcionClase'; -import { mapNiveles } from '../../utils/utilFunctions'; -import BodyInscripcionClase from '../../Components/Clase/BodyInscripcionClase'; -import ModalInscripcionClase from '../../Components/Clase/ModalInscripcionClase'; +import { mapClaseToData } from '../../utils/utilFunctions'; +import InscripcionClase from '../../Components/Clase/InscripcionClase'; export default function ShowClass() { const [dataPeriodo, setDataPeriodo] = useState([]); @@ -20,8 +14,6 @@ export default function ShowClass() { const [profesorList, setProfesorList] = useState([profesorVacioInscripcion]); const [currentProfesor, setCurrentProfesor] = useState(profesorVacioInscripcion); const [claseActual, setClaseActual] = useState(claseActualDefault); - const [currentClase, setCurrentClase] = useState(null); - const [currentWaitList, setCurrentWaitList] = useState(null); const [clase, setClase] = useState(claseActual); const [openModal, setOpenModal] = useState(false); const [currentOperation, setCurrentOperation] = useState(''); @@ -34,7 +26,6 @@ export default function ShowClass() { const handleSelectChange = (event) => { const filteredData = [data.filter((data) => data.clavePeriodo === event.label)].flat(); - filteredData.length > 0 ? setData(filteredData) : resetClases(); }; @@ -48,58 +39,13 @@ export default function ShowClass() { setProfesorList(newProfList); }); }; - + const resetClases = async () => { try { const response = await getClasses(); const result = await response.json(); - - const dataList = result.map((clase) => { - let fechas = ''; - let edades = ''; - let niveles = ''; - - if (clase.lunes !== '') fechas += 'lunes, '; - if (clase.martes !== '') fechas += 'martes, '; - if (clase.miercoles !== '') fechas += 'miercoles, '; - if (clase.jueves !== '') fechas += 'jueves, '; - if (clase.viernes !== '') fechas += 'viernes, '; - if (clase.sabado !== '') fechas += 'sabado, '; - - edades = clase.edad_maxima === '' - ? `${clase.edad_minima} en Adelante` - : `${clase.edad_minima}-${clase.edad_maxima}`; - - niveles = nivelesMapa[clase.nivel] || ''; - - return { - _id: clase._id, - clave: clase.clave, - nombre_curso: clase.nombre_curso, - nivel: clase.nivel, - matriculaProfesor: clase.matriculaProfesor, - edades, - edad_minima: clase.edad_minima, - edad_maxima: clase.edad_maxima, - cupo_maximo: clase.cupo_maximo, - modalidad: clase.modalidad, - fechas, - lunes: clase.lunes, - martes: clase.martes, - miercoles: clase.miercoles, - jueves: clase.jueves, - viernes: clase.viernes, - sabado: clase.sabado, - clavePeriodo: clase.clavePeriodo, - area: clase.area, - cupo_actual: clase.cupo_actual, - niveles, - nombreProfesor: clase.nombreProfesor, - apellidosProfesor: clase.apellidosProfesor, - nombreCompleto: `${clase.nombreProfesor} ${clase.apellidosProfesor}`, - }; - }); - + const dataList = result.map(mapClaseToData); + setData(dataList); getOptions(); } catch (error) { @@ -129,27 +75,6 @@ export default function ShowClass() { setClase({ ...clase, [name]: value }); }; - const modalSubmit = async (e) => { - e.preventDefault(); - try { - if (currentOperation === 'Eliminar') { - await deleteClasses({ - _id: clase._id, - }); - } else if (currentOperation === 'Crear') { - const claseACrear = mapNiveles(classTemplate, currentProfesor); - await createClass(claseACrear); - } else if (currentOperation === 'Editar') { - const claseAModificar = mapNiveles(clase, currentProfesor); - await updateClass(claseAModificar); - } - } catch (error) { - console.log(error); - } - abrirCerrarModal(); - resetClases(); - }; - const seleccionarClase = (consola, caso) => { if (caso === 'Crear') { setCurrentProfesor(profesorVacioInscripcion); @@ -173,69 +98,22 @@ export default function ShowClass() { } setOpenModal(!openModal); }; - - const getClassWaitList = async (clase) => { - try { - const studentsList = await getStudents(); - const students = await studentsList.json(); - const studentsById = students.reduce((obj, student) => { - obj[student._id] = student; - return obj; - }, {}); - - const responseWaitList = await getWaitList(); - const waitList = await responseWaitList.json(); - const result = []; - - waitList.forEach((inWaitList) => { - if (inWaitList.idClase === clase._id && studentsById[inWaitList.idAlumno]) { - const student = studentsById[inWaitList.idAlumno]; - result.push({ - _id: inWaitList._id, - studentName: `${student.nombre} ${student.apellido_paterno} ${student.apellido_materno}`, - time_stamp: inWaitList.time_stamp, - }); - } - }); - - result.sort((a, b) => (a > b ? 1 : a < b ? -1 : 0)); - setCurrentWaitList(result); - setCurrentClase(clase); - seleccionarClase(clase, 'AbrirWaitList'); - } catch (error) { - console.error(error); - } - }; return ( -
- { seleccionarClase(classTemplate, 'Crear'); }} - resetClases={resetClases} - dataPeriodo={dataPeriodo} - handleSelectChange={handleSelectChange} - /> - - -
+ ); } diff --git a/FrontEnd/src/Pages/RegistroClasesAlumno/RegistroClasesAlumno.jsx b/FrontEnd/src/Pages/RegistroClasesAlumno/RegistroClasesAlumno.jsx index 2bd2f402..1d8cf068 100644 --- a/FrontEnd/src/Pages/RegistroClasesAlumno/RegistroClasesAlumno.jsx +++ b/FrontEnd/src/Pages/RegistroClasesAlumno/RegistroClasesAlumno.jsx @@ -1,61 +1,32 @@ -import Box from "@mui/material/Box"; -import React, { useState, useEffect, useContext } from "react"; -import Clase from "../../Components/Clase/Clase"; -import CircularProgress from "@mui/material/CircularProgress"; -import { Alert, Button, Link, AlertTitle } from "@mui/material"; -import Snackbar from "@mui/material/Snackbar"; -import Autocomplete from "@mui/material/Autocomplete"; +import React, { useState, useEffect } from 'react'; +import Box from '@mui/material/Box'; +import { useAuth0 } from '@auth0/auth0-react'; +import moment from 'moment-timezone'; +import { getStudents } from '../../api/students'; +import { getClasses } from '../../api/classes'; +import { findTerm, getPeriodos } from '../../api/term'; +import ConfirmationDialog from '../../Components/Dialog/ConfirmationDialog'; +import { startDateDict, endDateDict } from '../../utils/constants'; import { - Card, - CardContent, - Typography, - TextField, - MenuItem, -} from "@mui/material"; -import { DataGrid } from "@mui/x-data-grid"; -import Modal from "@mui/material/Modal"; -import InputLabel from "@mui/material/InputLabel"; -import FormControl from "@mui/material/FormControl"; -import Select from "@mui/material/Select"; -import SearchIcon from "@mui/icons-material/Search"; -import { getStudents } from "../../api/students"; -import { getClasses } from "../../api/classes"; -import { useAuth0 } from "@auth0/auth0-react"; -import { - createClassStudent, - getClassStudent, - deleteClassStudent, -} from "../../api/classStudent"; -import { - createWaitList, - getWaitList, - deleteWaitList, -} from "../../api/waitList"; -import { findTerm, getPeriodos } from "../../api/term"; -import ConfirmationDialog from "../../Components/Dialog/ConfirmationDialog"; -import ClaseModal from "../../Components/Clase/ClaseModal"; -import MiRegistro from "../../Components/Registro/MiRegistro"; -import moment from "moment-timezone"; + compararFecha, +} from '../../utils/utilFunctions'; +import RegistroClasesHeader from '../../Components/Registro/RegistroClasesHeader'; +import RegistroClasesBody from '../../Components/Registro/RegistroClasesBody'; +import RegistroClasesError from '../../Components/Registro/RegistroClasesError'; +import LoadingRegisterData from '../../Components/Registro/LoadingRegisterData'; function RegistroClasesAlumnos({ changeContent }) { - const [items, setItems] = useState([]); const [students, setStudents] = useState(null); const [currentStudent, setCurrentStudent] = useState(null); const [error, setError] = useState(false); const [clases, setClases] = useState(null); const [classNames, setClassNames] = useState([]); - const [claseRegistrada, setClaseRegistrada] = useState([]); // esto se obtendria de la base de datos + const [claseRegistrada, setClaseRegistrada] = useState([]); const [openConfirmationDialog, setOpenConfirmationDialog] = useState(false); - const [openMoreInfo, setOpenMoreInfo] = useState(false); - const [currentClase, setCurrentClase] = useState(); const [selectAlertOpen, setSelectAlertOpen] = useState(false); - const [nameFilter, setNameFilter] = useState(""); const [filteredClasses, setFilteredClasses] = useState(null); - const [dialogAction, setDialogAction] = useState(""); - const [errorMsg, setErrorMsg] = useState(""); - const [inRegistrationTime, setInRegistrationTime] = useState(false); - const [periodos, setPeriodos] = useState([]); - const [currentTerm, setCurrentTerm] = useState(null); + const [dialogAction, setDialogAction] = useState(''); + const [errorMsg, setErrorMsg] = useState(''); const { user } = useAuth0(); @@ -64,231 +35,39 @@ function RegistroClasesAlumnos({ changeContent }) { getStudents() .then((response) => response.json()) .then((data) => { - const students = data.filter( - (student) => student.idUser === user.sub + const tempStudents = data.filter( + (student) => student.idUser === user.sub, ); - setStudents(students); - //console.log(students) + setStudents(tempStudents); }); }; - getUserStudents(); - }, []); - - const startDateDict = { - talleres: "fecha_inicio_insc_talleres", - idiomas: "fecha_inicio_insc_idiomas", - asesorias: "fecha_inicio_insc_asesorias", - }; - - const endDateDict = { - talleres: "fecha_fin_insc_talleres", - idiomas: "fecha_fin_insc_idiomas", - asesorias: "fecha_fin_insc_asesorias", - }; - useEffect(() => { - const getStudentClasses = () => { - let allClassNames = []; - let allTermClases = []; - let activeTermClases = []; - let activeTerm = []; - getPeriodos() - .then((response) => response.json()) - .then((data) => { - const periodo = compararFecha(data); - setPeriodos(data); - findTerm({ clave: periodo }) - .then((response) => response.json()) - .then((data) => { - activeTerm = data; - getClasses() - .then((response) => response.json()) - .then((result) => { - for (let i = 0; i < result.length; i++) { - if (activeTerm[0].clave === result[i].clavePeriodo) { - allTermClases.push(result[i]); - allClassNames.push(result[i].nombre_curso); - setClassNames([...new Set(allClassNames)]); - } - } - const currentDate = moment() - .tz("America/Mexico_City") - .format(); - for (let j = 0; j < allTermClases.length; j++) { - if ( - activeTerm[0][startDateDict[allTermClases[j].area]] < - currentDate && - activeTerm[0][endDateDict[allTermClases[j].area]] > - currentDate - ) { - activeTermClases.push(allTermClases[j]); - } - } - setClases(activeTermClases); - setFilteredClasses(activeTermClases); - }); - }); - }); - }; - getStudentClasses(); - //console.log(clases) - }, []); + const getStudentClasses = async () => { + const data = await getPeriodos().then((res) => res.json()); + const periodo = compararFecha(data); - function traducirDate(raw) { - const date = raw.split("T", 2); - return date[0]; - } + const activeTerm = await findTerm({ clave: periodo }).then((res) => res.json()); + const result = await getClasses().then((res) => res.json()); - function compararFecha(data) { - let periodos = []; - for (const element of data) { - let fecha = traducirDate(element.fecha_inicio); - let separado = fecha.split("-", 3); - let valorA = Number(separado[0]); - let valorM = Number(separado[1]) / 100; - let valorD = Number(separado[2]) / 10000; - let valorT = valorA + valorM + valorD; - var obj = { - id: element.clave, - fecha: valorT, - }; - periodos.push(obj); - } + const allTermClases = result.filter( + (item) => item.clavePeriodo === activeTerm[0].clave, + ); + const allClassNames = allTermClases.map((item) => item.nombre_curso); + setClassNames([...new Set(allClassNames)]); - periodos.sort((a, b) => b.fecha - a.fecha); - let clave = String(periodos[0].id); - return clave; - } + const currentDate = moment().tz('America/Mexico_City').format(); + const activeTermClases = allTermClases.filter( + (item) => activeTerm[0][startDateDict[item.area]] < currentDate + && activeTerm[0][endDateDict[item.area]] > currentDate, + ); - // Funcion para calcular edad - const calculate_age = (dateString) => { - var birthday = +new Date(dateString); - // The magic number: 31557600000 is 24 * 3600 * 365.25 * 1000, which is the length of a year - const magic_number = 31557600000; - return ~~((Date.now() - birthday) / magic_number); - }; - - const nivelDict = { - 1: "Desde cero", - 2: "Con bases", - 3: "Intermedio", - 4: "Avanzado", - }; - - const getNivel = (params) => { - return nivelDict[params.row.nivel]; - }; - - const getHorario = (params) => { - return `${params.row.lunes ? `Lun: ${params.row.lunes}` : ""} - ${params.row.martes ? `Mar: ${params.row.martes}` : ""} - ${params.row.miercoles ? `Mierc: ${params.row.miercoles}` : ""} - ${params.row.jueves ? `Juev: ${params.row.jueves}` : ""} - ${params.row.viernes ? `Vier: ${params.row.viernes}` : ""} - ${params.row.sabado ? `Sab: ${params.row.sabado}` : ""}`; - }; - - const getProfesor = (params) => { - return `${params.row.nombreProfesor} ${params.row.apellidosProfesor}`; - }; - - const getCupo = (params) => { - return `${( - (Number(params.row.cupo_actual) / Number(params.row.cupo_maximo)) * - 100 - ).toFixed()}%`; - }; + setClases(activeTermClases); + setFilteredClasses(activeTermClases); + }; - const columns = [ - { - field: "clavePeriodo", - headerName: "Periodo", - width: 110, - editable: false, - }, - { - field: "clave", - headerName: "Clave", - width: 90, - }, - { - field: "nombre_curso", - headerName: "Curso", - width: 90, - editable: false, - }, - { - field: "nivel", - headerName: "Nivel", - width: 100, - editable: false, - valueGetter: getNivel, - }, - { - field: "area", - headerName: "Area", - width: 110, - editable: false, - }, - { - field: "modalidad", - headerName: "Modalidad", - width: 110, - editable: false, - }, - { - field: "horario", - headerName: "Horario", - width: 150, - editable: false, - valueGetter: getHorario, - }, - { - field: "profesor", - headerName: "Profesor", - width: 140, - editable: "false", - valueGetter: getProfesor, - }, - , - { - field: "cupos", - headerName: "% curso lleno", - width: 100, - editable: "false", - valueGetter: getCupo, - }, - { - field: "actions", - headerName: "Inscripción", - type: "actions", - width: 115, - renderCell: (params) => - Number(params.row.cupo_actual) < Number(params.row.cupo_maximo) ? ( - - ) : ( - - ), - }, - ]; + getUserStudents(); + getStudentClasses(); + }, []); const handleClick = (clase) => { if (currentStudent == null) { @@ -296,185 +75,23 @@ function RegistroClasesAlumnos({ changeContent }) { return; } switch (clase.status) { - case "Inscrito": - setDialogAction("CancelarInscripcion"); + case 'Inscrito': + setDialogAction('CancelarInscripcion'); handleOpenDialog(clase); break; - case "ListaEspera": - setDialogAction("SalirLista"); + case 'ListaEspera': + setDialogAction('SalirLista'); handleOpenDialog(clase); break; - case "": - Number(clase.cupo_actual) < Number(clase.cupo_maximo) - ? setDialogAction("Registrar") - : setDialogAction("ListaEspera"); + case '': + setDialogAction( + Number(clase.cupo_actual) < Number(clase.cupo_maximo) + ? 'Registrar' : 'ListaEspera', + ); handleOpenDialog(clase); } }; - const handleMoreInfo = (clase) => { - setCurrentClase(clase); - setOpenMoreInfo(!openMoreInfo); - }; - - const filterClasses = (student) => { - const age = calculate_age(student.fecha_de_nacimiento); - let waitList = []; - let myClasses = []; - const filter = clases.filter( - (clase) => - Number(clase.edad_minima) < age && - age < (clase.edad_maxima ? Number(clase.edad_maxima) : 99) - ); - filter.map((aClass) => { - aClass.status = ""; - }); - getWaitList() - .then((response) => response.json()) - .then((data) => { - waitList = data.filter((lista) => lista.idAlumno === student._id); - }) - .then(() => { - waitList.map((inWaitList) => { - for (let i = 0; i < filter.length; i++) { - if (inWaitList.idClase === filter[i]._id) { - filter[i].status = "ListaEspera"; - } - } - }); - }); - getClassStudent() - .then((response) => response.json()) - .then((data) => { - myClasses = data.filter((clase) => clase.idAlumno === student._id); - }) - .then(() => { - myClasses.map((myClass) => { - for (let i = 0; i < filter.length; i++) { - if (myClass.idClase === filter[i]._id) { - filter[i].status = "Inscrito"; - } - } - }); - setFilteredClasses(filter); - }); - }; - - const handleChange = (e) => { - if (e.target.value === "") { - setFilteredClasses(clases); - setCurrentStudent(null); - return; - } - setCurrentStudent(e.target.value); - filterClasses(e.target.value); - }; - - const handleListaEspera = (clase) => { - let lista = []; - getWaitList() - .then((response) => response.json()) - .then((data) => { - lista = data.filter((lista) => lista.idAlumno === currentStudent._id); - }) - .then(() => { - createWaitList({ - idAlumno: currentStudent._id, - idClase: clase._id, - time_stamp: new Date().toISOString(), - status: "Espera", - }); - clase.status = "ListaEspera"; - handleCloseDialog(); - }); - }; - - const handleSalirListaEspera = (clase) => { - let periodo = []; - let myWaitList = []; - findTerm({ clave: clase.clavePeriodo }) - .then((response) => response.json()) - .then((data) => { - periodo = data; - }) - .then(() => { - getWaitList() - .then((response) => response.json()) - .then((data) => { - myWaitList = data.filter( - (aWList) => - aWList.idClase === clase._id && - aWList.idAlumno === currentStudent._id && - aWList.idPeriodo === periodo[0]._id - ); - }) - .then(() => { - deleteWaitList({ _id: myWaitList[0]._id }); - clase.status = ""; - handleCloseDialog(); - }); - }); - }; - - const handleClaseRegistrada = (clase) => { - // Hacer validación de numero de clases disponibles por inscribir - let periodo = []; - findTerm({ clave: clase.clavePeriodo }) - .then((response) => response.json()) - .then((data) => { - periodo = data; - }) - .then(() => { - createClassStudent({ - idClase: clase._id, - idAlumno: currentStudent._id, - idPeriodo: periodo[0]._id, - }) - .then((response) => response.json()) - .then((data) => { - if (data.msg.includes("Un documento fue insertado con el ID")) { - clase.status = "Inscrito"; - handleCloseDialog(); - } else { - handleCloseDialog(); - setErrorMsg(data.msg); - setError(true); - } - }) - .catch((error) => { - //console.log(error); - alert(error); - }); - }); - }; - - const handleCancelarClaseRegistrada = (clase) => { - let periodo = []; - let myClassStudent = []; - findTerm({ clave: clase.clavePeriodo }) - .then((response) => response.json()) - .then((result) => { - periodo = result; - }) - .then(() => { - getClassStudent() - .then((response) => response.json()) - .then((result) => { - myClassStudent = result.filter( - (aClass) => - aClass.idClase === clase._id && - aClass.idAlumno === currentStudent._id && - aClass.idPeriodo === periodo[0]._id - ); - }) - .then(() => { - deleteClassStudent({ _id: myClassStudent[0]._id }); - clase.status = ""; - handleCloseDialog(); - }); - }); - }; - const handleOpenDialog = (clase) => { setClaseRegistrada(clase); setOpenConfirmationDialog(true); @@ -484,350 +101,48 @@ function RegistroClasesAlumnos({ changeContent }) { setOpenConfirmationDialog(false); }; - const handleNameFilter = (value) => { - //setNameFilter(value); - const filteredClasses = clases.filter((clase) => - clase.nombre_curso.toLowerCase().includes(value.trim().toLowerCase()) - ); - setFilteredClasses(filteredClasses); - }; - - if (!students || !clases) { - return ( - - - - ); - } - if (students.length === 0 && students !== null) { - return ( - - - Registro clases (Inscripción) - - - No tienes alumnos registrados, ve a - changeContent("Profile")} - variant='h3' - sx={{ mx: 2 }} - > - Perfil - - para agregar alumnos. - - - ); - } return ( <> - - - Registro clases (Inscripción) - - - - Estudiantes - - - - handleNameFilter(newvalue)} - onInputChange={(e, newvalue) => handleNameFilter(newvalue)} - sx={{ display: { xs: "flex", md: "none" }, mt: 1 }} - fullWidth - renderInput={(params) => ( - - )} + {LoadingRegisterData({ changeContent, students, clases })? ( + + ) : ( + + + + + - - {filteredClasses.length !== 0 ? ( - filteredClasses.map((e) => ( - - )) - ) : ( - - - No hay clases disponibles por el momento. - - - )} - - - - - - - { - setItems([ - { - columnField: "nombre_curso", - operatorValue: "contains", - value: newvalue, - }, - ]); - }} - onInputChange={(e, newvalue) => { - setItems([ - { - columnField: "nombre_curso", - operatorValue: "contains", - value: newvalue, - }, - ]); - }} - renderInput={(params) => ( - - )} - /> - { - setItems([ - { - columnField: "nivel", - operatorValue: "contains", - value: e.target.value, - }, - ]); - }} - select - > - {[ - "", - "Desde cero", - "Con bases", - "Intermedio", - "Avanzado", - ].map((e) => ( - - {e} - - ))} - - { - setItems([ - { - columnField: "clavePeriodo", - operatorValue: "contains", - value: e.target.value, - }, - ]); - }} - > - { - setItems([ - { - columnField: "modalidad", - operatorValue: "contains", - value: e.target.value, - }, - ]); - }} - > - - - - - - row._id} - getRowHeight={() => "auto"} - filterModel={{ - items: items, - }} - getRowClassName={(params) => `theme--${params.row.status}`} - /> - - setOpenMoreInfo(!openMoreInfo)} - sx={{ overflowY: "scroll" }} - > - <> - - - - - - setSelectAlertOpen(false)} - > - - Selecciona un alumno para inscribir clases o entrar a la lista de - espera - - - - setError(!error)} - sx={{ overflow: "scroll" }} - > - <> - - - Error - {errorMsg} -
- -
-
- -
-
+ )} - ); + ); } export default RegistroClasesAlumnos; diff --git a/FrontEnd/src/utils/constants.js b/FrontEnd/src/utils/constants.js index e85efb3b..86c5ffe0 100644 --- a/FrontEnd/src/utils/constants.js +++ b/FrontEnd/src/utils/constants.js @@ -183,4 +183,23 @@ export const profesorVacioInscripcion = { apellidoProfesor: '', nombreCompleto: '', correo: '', -}; \ No newline at end of file +}; + +export const nivelDict = { + 1: 'Desde cero', + 2: 'Con bases', + 3: 'Intermedio', + 4: 'Avanzado', +}; + +export const startDateDict = { + talleres: "fecha_inicio_insc_talleres", + idiomas: "fecha_inicio_insc_idiomas", + asesorias: "fecha_inicio_insc_asesorias", +}; + +export const endDateDict = { + talleres: "fecha_fin_insc_talleres", + idiomas: "fecha_fin_insc_idiomas", + asesorias: "fecha_fin_insc_asesorias", +}; diff --git a/FrontEnd/src/utils/utilFunctions.js b/FrontEnd/src/utils/utilFunctions.js index cee608fe..fc683a4f 100644 --- a/FrontEnd/src/utils/utilFunctions.js +++ b/FrontEnd/src/utils/utilFunctions.js @@ -1,3 +1,5 @@ +import { nivelesMapa } from "./constants"; + // Funcion para calcular edad, si es menor de 18 se pide // un nombre de Tutor al estudiante export const calculateAge = (dateString) => { @@ -139,3 +141,164 @@ export const mapNiveles = (clase, currentProfesor) => { delete claseModificada.niveles; return claseModificada; }; + +export const parseCSV = (csv) => { + return csv + .split('\n') + .slice(1) + .map((row) => { + const [ + clave, + nombre_curso, + nivel, + area, + modalidad, + clavePeriodo, + cupo_maximo, + edad_minima, + edad_maxima, + lunes, + martes, + miercoles, + jueves, + viernes, + sabado, + nombreProfesor, + apellidosProfesor, + matriculaProfesor, + correoProfesor, + ] = row.split(','); + + const clase = { + clave, + nombre_curso, + nivel, + area, + modalidad, + clavePeriodo, + cupo_maximo, + edad_minima, + edad_maxima, + lunes, + martes, + miercoles, + jueves, + viernes, + sabado, + cupo_actual: '0', + nombreProfesor: nombreProfesor.trim(), + apellidosProfesor: apellidosProfesor.trim(), + matriculaProfesor, + }; + + const profesor = { + nombre: nombreProfesor.trim(), + apellidos: apellidosProfesor.trim(), + matricula: matriculaProfesor, + correo: correoProfesor, + fecha_de_nacimiento: '', + num_telefono: '', + num_cursos_impartidos: '0', + idUser: '', + }; + + return { clase, profesor }; + }); +}; + +export const mapClaseToData = (clase) => { + let fechas = ''; + let edades = ''; + let niveles = ''; + + if (clase.lunes !== '') fechas += 'lunes, '; + if (clase.martes !== '') fechas += 'martes, '; + if (clase.miercoles !== '') fechas += 'miercoles, '; + if (clase.jueves !== '') fechas += 'jueves, '; + if (clase.viernes !== '') fechas += 'viernes, '; + if (clase.sabado !== '') fechas += 'sabado, '; + + edades = clase.edad_maxima === '' + ? `${clase.edad_minima} en Adelante` + : `${clase.edad_minima}-${clase.edad_maxima}`; + + niveles = nivelesMapa[clase.nivel] || ''; + + return { + _id: clase._id, + clave: clase.clave, + nombre_curso: clase.nombre_curso, + nivel: clase.nivel, + matriculaProfesor: clase.matriculaProfesor, + edades, + edad_minima: clase.edad_minima, + edad_maxima: clase.edad_maxima, + cupo_maximo: clase.cupo_maximo, + modalidad: clase.modalidad, + fechas, + lunes: clase.lunes, + martes: clase.martes, + miercoles: clase.miercoles, + jueves: clase.jueves, + viernes: clase.viernes, + sabado: clase.sabado, + clavePeriodo: clase.clavePeriodo, + area: clase.area, + cupo_actual: clase.cupo_actual, + niveles, + nombreProfesor: clase.nombreProfesor, + apellidosProfesor: clase.apellidosProfesor, + nombreCompleto: `${clase.nombreProfesor} ${clase.apellidosProfesor}`, + }; +}; + +export const getNivel = (params) => { + return nivelDict[params.row.nivel]; +}; + +export const getHorario = (params) => { + return `${params.row.lunes ? `Lun: ${params.row.lunes}` : ""} + ${params.row.martes ? `Mar: ${params.row.martes}` : ""} + ${params.row.miercoles ? `Mierc: ${params.row.miercoles}` : ""} + ${params.row.jueves ? `Juev: ${params.row.jueves}` : ""} + ${params.row.viernes ? `Vier: ${params.row.viernes}` : ""} + ${params.row.sabado ? `Sab: ${params.row.sabado}` : ""}`; +}; + +export const getProfesor = (params) => { + return `${params.row.nombreProfesor} ${params.row.apellidosProfesor}`; +}; + +export const getCupo = (params) => { + return `${( + (Number(params.row.cupo_actual) / Number(params.row.cupo_maximo)) * + 100 + ).toFixed()}%`; +}; + +export const dialogTitle = (action) => { + switch (action) { + case 'ListaEspera': + return '¿Entrar a lista de espera?' + case 'Registrar': + return '¿Inscribir esta clase? ' + case 'CancelarInscripcion': + return '¿Cancelar inscripción?' + case 'SalirLista': + return '¿Salir de lista de espera?' + } +} + +export const dialogContent = (clase, action) => { + switch (action) { + case 'ListaEspera': + return `Estas seguro que quieres entrar a la lista de espera de la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}.` + case 'Registrar': + return `Estas seguro que quieres inscribir la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}, + recuerda que hay inscripciones limitadas.` + case 'CancelarInscripcion': + return `Estas seguro que quieres cancelar tu inscripción de la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}.` + case 'SalirLista': + return `Estas seguro que quieres salir de la lista de espera de la clase ${clase.clave} ${clase.nombre_curso} ${clase.nivel}.` + } +} \ No newline at end of file