diff --git a/apps/api/src/assistance-applications/assistance-applications.controller.ts b/apps/api/src/assistance-applications/assistance-applications.controller.ts index c08a1a0c..e1c1a88d 100644 --- a/apps/api/src/assistance-applications/assistance-applications.controller.ts +++ b/apps/api/src/assistance-applications/assistance-applications.controller.ts @@ -71,7 +71,7 @@ export class AssistanceApplicationsController { @Get(':id/document') findOneDocument(@Param('id') id: string) { - return this.assistanceApplicationsService.findOne(id, true); + return this.assistanceApplicationsService.findOneDocument(id); } @Patch(':id') diff --git a/apps/api/src/assistance-applications/assistance-applications.service.ts b/apps/api/src/assistance-applications/assistance-applications.service.ts index baa376da..860e71a2 100644 --- a/apps/api/src/assistance-applications/assistance-applications.service.ts +++ b/apps/api/src/assistance-applications/assistance-applications.service.ts @@ -69,7 +69,7 @@ export class AssistanceApplicationsService { return results; } - async findOne(id: string, withDocument: boolean = false) { + async findOne(id: string) { const application = await this.assistanceApplicationRepository.findOne({ where: { id }, relations: { @@ -77,7 +77,7 @@ export class AssistanceApplicationsService { professor: true, }, student: true, - document: withDocument, + document: true, }, }); if (!application) { @@ -88,6 +88,37 @@ export class AssistanceApplicationsService { return application; } + async findOneDocument(id: string) { + const application = await this.findOne(id); + const graduatedAssistanceId = application.graduatedAssistance.id; + + const applications = await this.assistanceApplicationRepository + .createQueryBuilder('app') + .select(['app.id']) + .where('app.graduatedAssistanceId = :graduatedAssistanceId', { + graduatedAssistanceId, + }) + .orderBy('app.createdAt', 'ASC') + .addOrderBy('app.id', 'ASC') + .getMany(); + + const applicationIds = applications.map((app) => app.id); + const currentIndex = applicationIds.indexOf(application.id); + + const previousId = + currentIndex > 0 ? applicationIds[currentIndex - 1] : null; + const nextId = + currentIndex < applicationIds.length - 1 + ? applicationIds[currentIndex + 1] + : null; + + return { + application, + previousId, + nextId, + }; + } + async update( id: string, updateAssistanceApplicationDto: UpdateAssistanceApplicationDto, diff --git a/apps/api/src/assistance-applications/entities/assistance-application.entity.ts b/apps/api/src/assistance-applications/entities/assistance-application.entity.ts index 4dd91518..8ce6b026 100644 --- a/apps/api/src/assistance-applications/entities/assistance-application.entity.ts +++ b/apps/api/src/assistance-applications/entities/assistance-application.entity.ts @@ -17,6 +17,9 @@ export class AssistanceApplication extends Base { @ManyToOne(() => Student, (student) => student.assistanceApplications) student: Student; + @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) + createdAt: Date; + @ManyToOne( () => GraduatedAssistance, (graduatedAssistance) => graduatedAssistance.assistanceApplications, diff --git a/apps/api/src/users/users.service.ts b/apps/api/src/users/users.service.ts index 08e49583..51aeded8 100644 --- a/apps/api/src/users/users.service.ts +++ b/apps/api/src/users/users.service.ts @@ -64,13 +64,11 @@ export class UsersService { getUserRoles(user: User) { const roles: string[] = []; - if (user.administrator && user.administrator.isActive) - roles.push('administrador'); - if (user.coordinator && user.coordinator.isActive) - roles.push('coordinador'); - if (user.professor && user.professor.isActive) roles.push('profesor'); - if (user.student && user.student.isActive) { - if (user.administrator && user.administrator.isActive) { + if (user.administrator?.isActive) roles.push('administrador'); + if (user.coordinator?.isActive) roles.push('coordinador'); + if (user.professor?.isActive) roles.push('profesor'); + if (user.student?.isActive) { + if (user.administrator?.isActive) { roles.push('estudiante'); roles.push('estudiante_maestria'); } else if (user.student.isUndergraduate) { diff --git a/apps/web/src/app/inicio/administrador/incidencia/page.tsx b/apps/web/src/app/inicio/administrador/incidencia/page.tsx index 3dc0a2d6..70680a4a 100644 --- a/apps/web/src/app/inicio/administrador/incidencia/page.tsx +++ b/apps/web/src/app/inicio/administrador/incidencia/page.tsx @@ -48,7 +48,8 @@ const columns: ColumnDef[] = [ sortingFn: (rowA, rowB, columnId) => { const a = rowA.getValue(columnId); const b = rowB.getValue(columnId); - return a === b ? 0 : a ? 1 : -1; + if (a === b) return 0; + return a ? 1 : -1; } }, { @@ -107,9 +108,10 @@ function ActionCell({ incidence }: { readonly incidence: Incidence }) { const completed = incidence.isClosed? true: isClosing; const handleClose = async () => { + if (!incidence.id) return; setIsClosing(true); try { - await closeIncidence(incidence.id!); + await closeIncidence(incidence.id); await queryClient.invalidateQueries({ queryKey: ['incidences'] }); } catch (error) { console.error("Error al cerrar la incidencia:", error); diff --git a/apps/web/src/app/inicio/administrador/lider/page.tsx b/apps/web/src/app/inicio/administrador/lider/page.tsx index f153bc64..b1b60046 100644 --- a/apps/web/src/app/inicio/administrador/lider/page.tsx +++ b/apps/web/src/app/inicio/administrador/lider/page.tsx @@ -2,7 +2,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; import { DataTable } from "@/components/data-table"; -import { ColumnDef } from "@tanstack/react-table"; +import { ColumnDef, Row } from "@tanstack/react-table"; import SpinnerPage from "@/components/shared/spinner-page"; import ErrorPage from "@/components/shared/error-page"; import { Button } from "@/components/ui/button"; @@ -20,59 +20,38 @@ import { import { LeaderPerCourse } from "@/app/types/leader-per-course.type"; import { User } from "@/app/types/entities/user.type"; +const columns: ColumnDef[] = [ + { + accessorKey: "code", + header: "Código", + }, + { + accessorKey: "courseName", + header: "Nombre curso", + }, + { + accessorKey: "professorName", + header: "Nombre profesor", + }, + { + accessorKey: "email", + header: "Email", + }, + { + id: "assign", + header: "Asignar", + enableSorting: false, + cell: ({ row }: { row: Row }) => + }, +]; + export default function CourseList() { - const queryClient = useQueryClient(); const { data, isFetching, isError } = useQuery({ queryKey: ["courses-leaders"], queryFn: () => findAllWithMainProfessor(), }); - const columns: ColumnDef[] = [ - { - accessorKey: "code", - header: "Código", - }, - { - accessorKey: "courseName", - header: "Nombre curso", - }, - { - accessorKey: "professorName", - header: "Nombre profesor", - }, - { - accessorKey: "email", - header: "Email", - }, - { - id: "assign", - header: "Asignar", - enableSorting: false, - cell: ({ row }) => ( - { - queryClient.setQueryData( - ["courses-leaders"], - (oldData) => - oldData?.map((course) => - course.courseId === row.original.courseId - ? { - ...course, - professorId: newProfessor.id, - professorName: newProfessor.name, - email: newProfessor.email, - } - : course - ) ?? [] - ); - }} - /> - ), - }, - ]; - if (isFetching) return ; if (isError) return ; return ( @@ -87,6 +66,31 @@ export default function CourseList() { ); } +function PopoverPerCourse({ row }: { readonly row: Row }) { + const queryClient = useQueryClient(); + return ( + { + queryClient.setQueryData( + ["courses-leaders"], + (oldData) => + oldData?.map((course) => + course.courseId === row.original.courseId + ? { + ...course, + professorId: newProfessor.id, + professorName: newProfessor.name, + email: newProfessor.email, + } + : course + ) ?? [] + ); + }} + /> + ) +} + function ProfessorListPopover({ courseId, onAssigned, @@ -96,7 +100,6 @@ function ProfessorListPopover({ }) { const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); - const [selected, setSelected] = useState(null); const { data } = useQuery({ queryKey: ["professors-by-course", courseId], @@ -111,7 +114,6 @@ function ProfessorListPopover({ const handleAssign = async (professor: User) => { try { await assignProfessorAsCourseLeader(professor.id, courseId); - setSelected(professor); setOpen(false); onAssigned?.(professor); } catch (error) { @@ -123,7 +125,7 @@ function ProfessorListPopover({ @@ -155,4 +157,4 @@ function ProfessorListPopover({ ); -} +} \ No newline at end of file diff --git a/apps/web/src/app/inicio/coordinador/cartelera/page.tsx b/apps/web/src/app/inicio/coordinador/cartelera/page.tsx index e00c6667..375a0367 100644 --- a/apps/web/src/app/inicio/coordinador/cartelera/page.tsx +++ b/apps/web/src/app/inicio/coordinador/cartelera/page.tsx @@ -69,16 +69,49 @@ export default function UploadBillboard() { handleUploadXlsm(file); } }} - dialogText={dialogText} + dialogText={dialogText} > - - + + ); } +const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Clase", + }, + { + accessorKey: "code", + header: "Código", + }, + { + accessorFn: (row) => row.sections.length, + header: "Secciones", + }, + { + accessorKey: "credits", + header: "Créditos", + }, + { + id: "professors", + header: "Profesores", + cell: ({ row }) => { + const professors = row.original.sections.flatMap((section) => + section.professors.map((p) => p.user.name) + ); + return ( + + {professors.length > 0 ? professors.join(", ") : "No asignados"} + + ); + }, + }, +]; + function UploadedBillboard({ classesData, loadError, @@ -86,43 +119,7 @@ function UploadedBillboard({ readonly classesData: Course[]; readonly loadError: boolean; }) { - const columns: ColumnDef[] = [ - { - accessorKey: "name", - header: "Clase", - }, - { - accessorKey: "code", - header: "Código", - }, - { - accessorFn: (row) => row.sections.length, - header: "Secciones", - }, - { - accessorKey: "credits", - header: "Créditos", - }, - { - id: "professors", - header: "Profesores", - cell: ({ row }) => { - const professors = row.original.sections.flatMap((section) => - section.professors.map((p) => p.user.name) - ); - return ( - - {professors.length > 0 ? professors.join(", ") : "No asignados"} - - ); - }, - }, - ]; - if (loadError) return ; - - - return (
diff --git a/apps/web/src/app/inicio/coordinador/programas/lista/ProgramColumns.tsx b/apps/web/src/app/inicio/coordinador/programas/lista/ProgramColumns.tsx index 330a6dd3..94e02a84 100644 --- a/apps/web/src/app/inicio/coordinador/programas/lista/ProgramColumns.tsx +++ b/apps/web/src/app/inicio/coordinador/programas/lista/ProgramColumns.tsx @@ -34,7 +34,7 @@ export const getProgramColumns = (handleDetail: (id: string) => void): ColumnDef header: "Profesor", cell: ({ row }) => ( - {row.original.mainProfessor?.user?.name || "No professor assigned"} + {row.original.mainProfessor?.user?.name || "Profesor no asignado"} ), }, diff --git a/apps/web/src/app/inicio/coordinador/programas/lista/[id]/page.tsx b/apps/web/src/app/inicio/coordinador/programas/lista/[id]/page.tsx index a824262d..0fa7a4a1 100644 --- a/apps/web/src/app/inicio/coordinador/programas/lista/[id]/page.tsx +++ b/apps/web/src/app/inicio/coordinador/programas/lista/[id]/page.tsx @@ -37,7 +37,7 @@ export default function ApplicationDetail() { const [pdfUrl, setPdfUrl] = useState(null); // Blob URL for the PDF document // Fetch course data using React Query - const { data: course, isFetching, error } = useQuery({ + const { data: course, isFetching, isError } = useQuery({ queryKey: ["course-program", id], queryFn: () => getCourseWithDocument(String(id)), }); @@ -59,6 +59,7 @@ export default function ApplicationDetail() { // Show loading spinner while data is being fetched if (isFetching) return ; + if (isError) return

Error al cargar el documento

return (
@@ -66,19 +67,18 @@ export default function ApplicationDetail() { {/* Left section: PDF document or error/loading message */}
- {error ? ( -

Error al cargar el documento

- ) : pdfUrl ? ( -