Skip to content

Commit d074ab0

Browse files
authored
Develop (#88)
* enhance/solve-reliability-and-security-codesmells * enhance/change-datatable-for-assistantship-application-list * Solve reliability and security codesmells * enhance/solve-reliability-and-security-codesmells * enhance/change-datatable-for-assistantship-application-list * enhance/delete-codesmells
1 parent 0cc8c40 commit d074ab0

File tree

21 files changed

+389
-357
lines changed

21 files changed

+389
-357
lines changed

apps/api/src/assistance-applications/assistance-applications.service.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,37 @@ export class AssistanceApplicationsService {
8888
return application;
8989
}
9090

91+
async findOneDocument(id: string) {
92+
const application = await this.findOne(id, true);
93+
const graduatedAssistanceId = application.graduatedAssistance.id;
94+
95+
const applications = await this.assistanceApplicationRepository
96+
.createQueryBuilder('app')
97+
.select(['app.id'])
98+
.where('app.graduatedAssistanceId = :graduatedAssistanceId', {
99+
graduatedAssistanceId,
100+
})
101+
.orderBy('app.createdAt', 'ASC')
102+
.addOrderBy('app.id', 'ASC')
103+
.getMany();
104+
105+
const applicationIds = applications.map((app) => app.id);
106+
const currentIndex = applicationIds.indexOf(application.id);
107+
108+
const previousId =
109+
currentIndex > 0 ? applicationIds[currentIndex - 1] : null;
110+
const nextId =
111+
currentIndex < applicationIds.length - 1
112+
? applicationIds[currentIndex + 1]
113+
: null;
114+
115+
return {
116+
application,
117+
previousId,
118+
nextId,
119+
};
120+
}
121+
91122
async update(
92123
id: string,
93124
updateAssistanceApplicationDto: UpdateAssistanceApplicationDto,
@@ -107,4 +138,4 @@ export class AssistanceApplicationsService {
107138
remove(id: number) {
108139
return `This action removes a #${id} assistanceApplication`;
109140
}
110-
}
141+
}

apps/api/src/assistance-applications/entities/assistance-application.entity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ export class AssistanceApplication extends Base {
1717
@ManyToOne(() => Student, (student) => student.assistanceApplications)
1818
student: Student;
1919

20+
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
21+
createdAt: Date;
22+
2023
@ManyToOne(
2124
() => GraduatedAssistance,
2225
(graduatedAssistance) => graduatedAssistance.assistanceApplications,

apps/api/src/users/users.service.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,11 @@ export class UsersService {
6464

6565
getUserRoles(user: User) {
6666
const roles: string[] = [];
67-
if (user.administrator && user.administrator.isActive)
68-
roles.push('administrador');
69-
if (user.coordinator && user.coordinator.isActive)
70-
roles.push('coordinador');
71-
if (user.professor && user.professor.isActive) roles.push('profesor');
72-
if (user.student && user.student.isActive) {
73-
if (user.administrator && user.administrator.isActive) {
67+
if (user.administrator?.isActive) roles.push('administrador');
68+
if (user.coordinator?.isActive) roles.push('coordinador');
69+
if (user.professor?.isActive) roles.push('profesor');
70+
if (user.student?.isActive) {
71+
if (user.administrator?.isActive) {
7472
roles.push('estudiante');
7573
roles.push('estudiante_maestria');
7674
} else if (user.student.isUndergraduate) {

apps/web/src/app/inicio/administrador/incidencia/page.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ const columns: ColumnDef<Incidence>[] = [
4848
sortingFn: (rowA, rowB, columnId) => {
4949
const a = rowA.getValue<boolean>(columnId);
5050
const b = rowB.getValue<boolean>(columnId);
51-
return a === b ? 0 : a ? 1 : -1;
51+
if (a === b) return 0;
52+
return a ? 1 : -1;
5253
}
5354
},
5455
{
@@ -107,9 +108,10 @@ function ActionCell({ incidence }: { readonly incidence: Incidence }) {
107108
const completed = incidence.isClosed? true: isClosing;
108109

109110
const handleClose = async () => {
111+
if (!incidence.id) return;
110112
setIsClosing(true);
111113
try {
112-
await closeIncidence(incidence.id!);
114+
await closeIncidence(incidence.id);
113115
await queryClient.invalidateQueries({ queryKey: ['incidences'] });
114116
} catch (error) {
115117
console.error("Error al cerrar la incidencia:", error);

apps/web/src/app/inicio/administrador/lider/page.tsx

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useQuery, useQueryClient } from "@tanstack/react-query";
44
import { DataTable } from "@/components/data-table";
5-
import { ColumnDef } from "@tanstack/react-table";
5+
import { ColumnDef, Row } from "@tanstack/react-table";
66
import SpinnerPage from "@/components/shared/spinner-page";
77
import ErrorPage from "@/components/shared/error-page";
88
import { Button } from "@/components/ui/button";
@@ -20,59 +20,38 @@ import {
2020
import { LeaderPerCourse } from "@/app/types/leader-per-course.type";
2121
import { User } from "@/app/types/entities/user.type";
2222

23+
const columns: ColumnDef<LeaderPerCourse>[] = [
24+
{
25+
accessorKey: "code",
26+
header: "Código",
27+
},
28+
{
29+
accessorKey: "courseName",
30+
header: "Nombre curso",
31+
},
32+
{
33+
accessorKey: "professorName",
34+
header: "Nombre profesor",
35+
},
36+
{
37+
accessorKey: "email",
38+
header: "Email",
39+
},
40+
{
41+
id: "assign",
42+
header: "Asignar",
43+
enableSorting: false,
44+
cell: ({ row }: { row: Row<LeaderPerCourse> }) => <PopoverPerCourse row={row} />
45+
},
46+
];
47+
2348
export default function CourseList() {
24-
const queryClient = useQueryClient();
2549

2650
const { data, isFetching, isError } = useQuery({
2751
queryKey: ["courses-leaders"],
2852
queryFn: () => findAllWithMainProfessor(),
2953
});
3054

31-
const columns: ColumnDef<LeaderPerCourse>[] = [
32-
{
33-
accessorKey: "code",
34-
header: "Código",
35-
},
36-
{
37-
accessorKey: "courseName",
38-
header: "Nombre curso",
39-
},
40-
{
41-
accessorKey: "professorName",
42-
header: "Nombre profesor",
43-
},
44-
{
45-
accessorKey: "email",
46-
header: "Email",
47-
},
48-
{
49-
id: "assign",
50-
header: "Asignar",
51-
enableSorting: false,
52-
cell: ({ row }) => (
53-
<ProfessorListPopover
54-
courseId={row.original.courseId}
55-
onAssigned={(newProfessor) => {
56-
queryClient.setQueryData<LeaderPerCourse[]>(
57-
["courses-leaders"],
58-
(oldData) =>
59-
oldData?.map((course) =>
60-
course.courseId === row.original.courseId
61-
? {
62-
...course,
63-
professorId: newProfessor.id,
64-
professorName: newProfessor.name,
65-
email: newProfessor.email,
66-
}
67-
: course
68-
) ?? []
69-
);
70-
}}
71-
/>
72-
),
73-
},
74-
];
75-
7655
if (isFetching) return <SpinnerPage />;
7756
if (isError) return <ErrorPage />;
7857
return (
@@ -87,6 +66,31 @@ export default function CourseList() {
8766
);
8867
}
8968

69+
function PopoverPerCourse({ row }: { readonly row: Row<LeaderPerCourse> }) {
70+
const queryClient = useQueryClient();
71+
return (
72+
<ProfessorListPopover
73+
courseId={row.original.courseId}
74+
onAssigned={(newProfessor) => {
75+
queryClient.setQueryData<LeaderPerCourse[]>(
76+
["courses-leaders"],
77+
(oldData) =>
78+
oldData?.map((course) =>
79+
course.courseId === row.original.courseId
80+
? {
81+
...course,
82+
professorId: newProfessor.id,
83+
professorName: newProfessor.name,
84+
email: newProfessor.email,
85+
}
86+
: course
87+
) ?? []
88+
);
89+
}}
90+
/>
91+
)
92+
}
93+
9094
function ProfessorListPopover({
9195
courseId,
9296
onAssigned,
@@ -96,7 +100,6 @@ function ProfessorListPopover({
96100
}) {
97101
const [open, setOpen] = useState(false);
98102
const [search, setSearch] = useState("");
99-
const [selected, setSelected] = useState<User | null>(null);
100103

101104
const { data } = useQuery({
102105
queryKey: ["professors-by-course", courseId],
@@ -111,7 +114,6 @@ function ProfessorListPopover({
111114
const handleAssign = async (professor: User) => {
112115
try {
113116
await assignProfessorAsCourseLeader(professor.id, courseId);
114-
setSelected(professor);
115117
setOpen(false);
116118
onAssigned?.(professor);
117119
} catch (error) {
@@ -123,7 +125,7 @@ function ProfessorListPopover({
123125
<Popover open={open} onOpenChange={setOpen}>
124126
<PopoverTrigger asChild>
125127
<Button className="w-full">
126-
{selected?.name ?? "Asignar"}
128+
Asignar
127129
</Button>
128130
</PopoverTrigger>
129131
<PopoverContent className="w-[300px]">
@@ -155,4 +157,4 @@ function ProfessorListPopover({
155157
</PopoverContent>
156158
</Popover>
157159
);
158-
}
160+
}

apps/web/src/app/inicio/coordinador/cartelera/page.tsx

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -69,60 +69,57 @@ export default function UploadBillboard() {
6969
handleUploadXlsm(file);
7070
}
7171
}}
72-
dialogText={dialogText}
72+
dialogText={dialogText}
7373
>
74-
<UploadedBillboard classesData={coursesData} loadError={loadError} />
75-
</UploadFilePage >
74+
<UploadedBillboard classesData={coursesData} loadError={loadError} />
75+
</UploadFilePage >
7676

7777
<AlertDialogError open={loadError} onOpenChange={setLoadError} />
7878
</>
7979
);
8080
}
8181

82+
const columns: ColumnDef<Course>[] = [
83+
{
84+
accessorKey: "name",
85+
header: "Clase",
86+
},
87+
{
88+
accessorKey: "code",
89+
header: "Código",
90+
},
91+
{
92+
accessorFn: (row) => row.sections.length,
93+
header: "Secciones",
94+
},
95+
{
96+
accessorKey: "credits",
97+
header: "Créditos",
98+
},
99+
{
100+
id: "professors",
101+
header: "Profesores",
102+
cell: ({ row }) => {
103+
const professors = row.original.sections.flatMap((section) =>
104+
section.professors.map((p) => p.user.name)
105+
);
106+
return (
107+
<span>
108+
{professors.length > 0 ? professors.join(", ") : "No asignados"}
109+
</span>
110+
);
111+
},
112+
},
113+
];
114+
82115
function UploadedBillboard({
83116
classesData,
84117
loadError,
85118
}: {
86119
readonly classesData: Course[];
87120
readonly loadError: boolean;
88121
}) {
89-
const columns: ColumnDef<Course>[] = [
90-
{
91-
accessorKey: "name",
92-
header: "Clase",
93-
},
94-
{
95-
accessorKey: "code",
96-
header: "Código",
97-
},
98-
{
99-
accessorFn: (row) => row.sections.length,
100-
header: "Secciones",
101-
},
102-
{
103-
accessorKey: "credits",
104-
header: "Créditos",
105-
},
106-
{
107-
id: "professors",
108-
header: "Profesores",
109-
cell: ({ row }) => {
110-
const professors = row.original.sections.flatMap((section) =>
111-
section.professors.map((p) => p.user.name)
112-
);
113-
return (
114-
<span>
115-
{professors.length > 0 ? professors.join(", ") : "No asignados"}
116-
</span>
117-
);
118-
},
119-
},
120-
];
121-
122122
if (loadError) return <BillboardNotFound />;
123-
124-
125-
126123
return (
127124
<div className="min-h-full min-w-full">
128125
<div className="bg-card rounded-lg shadow-lg p-6 space-y-6">

apps/web/src/app/inicio/coordinador/programas/lista/[id]/page.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export default function ApplicationDetail() {
3737
const [pdfUrl, setPdfUrl] = useState<string | null>(null); // Blob URL for the PDF document
3838

3939
// Fetch course data using React Query
40-
const { data: course, isFetching, error } = useQuery({
40+
const { data: course, isFetching, isError } = useQuery({
4141
queryKey: ["course-program", id],
4242
queryFn: () => getCourseWithDocument(String(id)),
4343
});
@@ -59,26 +59,26 @@ export default function ApplicationDetail() {
5959

6060
// Show loading spinner while data is being fetched
6161
if (isFetching) return <SpinnerPage />;
62+
if (isError) return <p className="text-red-500 p-4">Error al cargar el documento</p>
6263

6364
return (
6465
<div className="flex flex-col h-full w-full">
6566
<main className="flex-1 grid grid-cols-1 lg:grid-cols-3 gap-6 p-6">
6667
{/* Left section: PDF document or error/loading message */}
6768
<section className="lg:col-span-2 flex flex-col">
6869
<div className="flex-1 border rounded-lg overflow-hidden">
69-
{error ? (
70-
<p className="text-red-500 p-4">Error al cargar el documento</p>
71-
) : pdfUrl ? (
72-
<iframe
73-
src={pdfUrl}
74-
width="100%"
75-
height="100%"
76-
title="Documento PDF"
77-
className="min-h-[400px] md:min-h-[600px]"
78-
/>
79-
) : (
80-
<p className="text-center py-4 text-gray-500">Cargando documento…</p>
81-
)}
70+
{
71+
pdfUrl ? (
72+
<iframe
73+
src={pdfUrl}
74+
width="100%"
75+
height="100%"
76+
title="Documento PDF"
77+
className="min-h-[400px] md:min-h-[600px]"
78+
/>
79+
) : (
80+
<p className="text-center py-4 text-gray-500">Cargando documento…</p>
81+
)}
8282
</div>
8383
</section>
8484

0 commit comments

Comments
 (0)