Skip to content

Commit 1a5a1e7

Browse files
committed
Add dossier listing feature and fixed user pagination bug
1 parent 42c05ad commit 1a5a1e7

20 files changed

+351
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ObjectId } from 'mongodb';
2+
3+
export interface Expediente {
4+
id: ObjectId;
5+
clase: string;
6+
titulo: string;
7+
adjudicataria: string;
8+
estado: string;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { paginateItems } from '#common/helpers/index.js';
2+
import { CollectionQuery } from '#common/models/index.js';
3+
import { db } from '#dals/mock.data.js';
4+
import * as model from './expediente.model.js';
5+
6+
export const expedienteRepository = {
7+
getExpedienteList: async (page?: number, pageSize?: number): Promise<CollectionQuery<model.Expediente>> =>
8+
page !== undefined && pageSize !== undefined && page !== null && pageSize !== null
9+
? paginateItems<model.Expediente>({ items: db.expedientes, page, pageSize })
10+
: {
11+
data: db.expedientes,
12+
pagination: {
13+
totalPages: db.expedientes.length,
14+
},
15+
},
16+
};
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './expediente.model.js';

mock-server/src/dals/mock.data.ts

+144
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { ObjectId } from 'mongodb';
22
import { Lookup } from '#common/models/index.js';
33
import { Usuario } from './user/index.js';
4+
import { Expediente } from './expediente/index.js';
45

56
export interface DB {
67
users: Usuario[];
8+
expedientes: Expediente[];
79
unidadProponentes: Lookup[];
810
roles: Lookup[];
911
}
@@ -140,4 +142,146 @@ export const db: DB = {
140142
nombre: 'Usuario-Lectura',
141143
},
142144
],
145+
expedientes: [
146+
{
147+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceed7'),
148+
clase: 'Administrativo',
149+
titulo: 'Solicitud de Información',
150+
adjudicataria: 'Juan Pérez',
151+
estado: 'En Proceso',
152+
},
153+
{
154+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceed8'),
155+
clase: 'Técnico',
156+
titulo: 'Informe Técnico',
157+
adjudicataria: 'María López',
158+
estado: 'Finalizado',
159+
},
160+
{
161+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceed9'),
162+
clase: 'Financiero',
163+
titulo: 'Informe Financiero',
164+
adjudicataria: 'Carlos García',
165+
estado: 'Pendiente',
166+
},
167+
{
168+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceeda'),
169+
clase: 'Legal',
170+
titulo: 'Informe Legal',
171+
adjudicataria: 'Laura Martínez',
172+
estado: 'En Revisión',
173+
},
174+
{
175+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceedb'),
176+
clase: 'Administrativo',
177+
titulo: 'Solicitud de Modificación',
178+
adjudicataria: 'Miguel Fernández',
179+
estado: 'Aprobado',
180+
},
181+
{
182+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceedc'),
183+
clase: 'Técnico',
184+
titulo: 'Informe de Progreso',
185+
adjudicataria: 'Ana Torres',
186+
estado: 'En Proceso',
187+
},
188+
{
189+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceedd'),
190+
clase: 'Financiero',
191+
titulo: 'Informe de Gastos',
192+
adjudicataria: 'Luis Martínez',
193+
estado: 'Pendiente',
194+
},
195+
{
196+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceede'),
197+
clase: 'Legal',
198+
titulo: 'Informe de Cumplimiento',
199+
adjudicataria: 'Sofía García',
200+
estado: 'Finalizado',
201+
},
202+
{
203+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceedf'),
204+
clase: 'Administrativo',
205+
titulo: 'Solicitud de Aprobación',
206+
adjudicataria: 'Pedro González',
207+
estado: 'En Revisión',
208+
},
209+
{
210+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee0'),
211+
clase: 'Técnico',
212+
titulo: 'Informe de Evaluación',
213+
adjudicataria: 'Clara López',
214+
estado: 'Aprobado',
215+
},
216+
{
217+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee1'),
218+
clase: 'Legal',
219+
titulo: 'Solicitud de Revisión',
220+
adjudicataria: 'Isabel Ramírez',
221+
estado: 'Pendiente',
222+
},
223+
{
224+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee2'),
225+
clase: 'Financiero',
226+
titulo: 'Análisis de Presupuesto',
227+
adjudicataria: 'Javier Torres',
228+
estado: 'Finalizado',
229+
},
230+
{
231+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee3'),
232+
clase: 'Técnico',
233+
titulo: 'Inspección Técnica',
234+
adjudicataria: 'Carmen Díaz',
235+
estado: 'En Proceso',
236+
},
237+
{
238+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee4'),
239+
clase: 'Administrativo',
240+
titulo: 'Petición Administrativa',
241+
adjudicataria: 'Andrés Morales',
242+
estado: 'Aprobado',
243+
},
244+
{
245+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee5'),
246+
clase: 'Legal',
247+
titulo: 'Resolución Judicial',
248+
adjudicataria: 'Elena Navarro',
249+
estado: 'Finalizado',
250+
},
251+
{
252+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee6'),
253+
clase: 'Financiero',
254+
titulo: 'Informe de Auditoría',
255+
adjudicataria: 'Pablo Ortega',
256+
estado: 'En Revisión',
257+
},
258+
{
259+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee7'),
260+
clase: 'Técnico',
261+
titulo: 'Plan de Mantenimiento',
262+
adjudicataria: 'Sara Medina',
263+
estado: 'Pendiente',
264+
},
265+
{
266+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee8'),
267+
clase: 'Administrativo',
268+
titulo: 'Solicitud de Recursos',
269+
adjudicataria: 'Daniel Herrera',
270+
estado: 'En Proceso',
271+
},
272+
{
273+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceee9'),
274+
clase: 'Legal',
275+
titulo: 'Consulta Jurídica',
276+
adjudicataria: 'Laura Ruiz',
277+
estado: 'Aprobado',
278+
},
279+
{
280+
id: new ObjectId('67e2a2c1f3b8d4a8b57ceeea'),
281+
clase: 'Financiero',
282+
titulo: 'Informe de Deuda',
283+
adjudicataria: 'Víctor Ramos',
284+
estado: 'Pendiente',
285+
},
286+
],
143287
};

mock-server/src/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ENV } from '#core/constants/index.js';
33
import { logger } from '#core/logger/index.js';
44
import { createRestApiServer } from '#core/servers/index.js';
55
import { userApi } from '#pods/user/index.js';
6+
import { expedienteApi } from '#pods/expediente/index.js';
67
import { lookupApi } from '#pods/lookup/index.js';
78

89
const app = createRestApiServer();
@@ -12,6 +13,8 @@ app.use(logRequestMiddleware(logger));
1213
app.use('/api/user', userApi);
1314
app.use('/api/lookup', lookupApi);
1415

16+
app.use('/api/expediente', expedienteApi);
17+
1518
app.use(logErrorRequestMiddleware(logger));
1619

1720
app.listen(ENV.PORT, async () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface Expediente {
2+
id?: string;
3+
clase: string;
4+
titulo: string;
5+
adjudicataria: string;
6+
estado: string;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ObjectId } from 'mongodb';
2+
import { mapObjectIdToString, mapToCollection } from '#common/mappers/index.js';
3+
import { CollectionQuery } from '#common/models/index.js';
4+
import * as model from '#dals/expediente/expediente.model.js';
5+
import * as apiModel from './expediente.api-model.js';
6+
7+
export const mapExpedienteFromModelToApi = (expediente: model.Expediente): apiModel.Expediente => ({
8+
id: mapObjectIdToString(expediente.id),
9+
clase: expediente.clase,
10+
titulo: expediente.titulo,
11+
adjudicataria: expediente.adjudicataria,
12+
estado: expediente.estado,
13+
});
14+
15+
export const mapExpedienteListFromModelToApi = (
16+
expedienteList: CollectionQuery<model.Expediente>
17+
): CollectionQuery<apiModel.Expediente> => ({
18+
data: mapToCollection(expedienteList.data, mapExpedienteFromModelToApi),
19+
pagination: {
20+
totalPages: expedienteList.pagination.totalPages,
21+
},
22+
});
23+
24+
export const mapExpedienteFromApiToModel = (exp: apiModel.Expediente): model.Expediente => ({
25+
id: new ObjectId(exp.id),
26+
clase: exp.clase,
27+
titulo: exp.titulo,
28+
adjudicataria: exp.adjudicataria,
29+
estado: exp.estado,
30+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import express from 'express';
2+
import { expedienteRepository } from '#dals/expediente/expediente.repository.js';
3+
import { mapExpedienteListFromModelToApi } from './expediente.mappers.js';
4+
5+
export const expedienteApi = express.Router();
6+
7+
expedienteApi.get('/', async (req, res, next) => {
8+
try {
9+
const page = Number(req.query.page);
10+
const pageSize = Number(req.query.pageSize);
11+
const expedienteList = await expedienteRepository.getExpedienteList(page, pageSize);
12+
res.send(mapExpedienteListFromModelToApi(expedienteList));
13+
} catch (error) {
14+
next(error);
15+
}
16+
});
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './expediente.rest-api.js';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './list-api';
2+
export * from './list.api-model';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import axios from 'axios';
2+
import { CollectionQuery } from '#common/models';
3+
import { Expediente } from './list.api-model';
4+
5+
export const getExpedientes = async (page?: number, pageSize?: number): Promise<CollectionQuery<Expediente>> => {
6+
const response = await axios.get<CollectionQuery<Expediente>>('/api/expediente', {
7+
params: {
8+
page,
9+
pageSize,
10+
},
11+
});
12+
return response.data;
13+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface Expediente {
2+
id: string;
3+
clase: string;
4+
titulo: string;
5+
adjudicataria: string;
6+
estado: string;
7+
}

src/modules/expedientes/list/components/table.component.tsx

+4-10
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,22 @@ import * as innerClasses from './table.styles';
88
interface Props {
99
columns: ColumnDef<Expediente>[];
1010
expedienteCollection: Expediente[];
11-
totalItems: number;
11+
totalPages: number;
1212
currentPage: number;
1313
onPageChange: (event: React.ChangeEvent<unknown>, page: number) => void;
1414
}
1515

1616
export const TableComponent: React.FC<Props> = props => {
17-
const { columns, expedienteCollection: data, totalItems, currentPage, onPageChange } = props;
17+
const { columns, expedienteCollection: data, totalPages, currentPage, onPageChange } = props;
1818
const classes = useWithTheme(innerClasses);
1919
const tableInstance = useReactTable({
2020
data,
2121
columns,
2222
getCoreRowModel: getCoreRowModel(),
2323
getPaginationRowModel: getPaginationRowModel(),
24-
rowCount: totalItems,
2524
});
2625

27-
const { getRowModel, getHeaderGroups, getPageCount } = tableInstance;
26+
const { getRowModel, getHeaderGroups } = tableInstance;
2827

2928
return (
3029
<TableContainer component={Paper}>
@@ -48,12 +47,7 @@ export const TableComponent: React.FC<Props> = props => {
4847
))}
4948
</TableBody>
5049
</Table>
51-
<Pagination
52-
count={getPageCount()}
53-
page={currentPage + 1}
54-
onChange={onPageChange}
55-
className={classes.pagination}
56-
/>
50+
<Pagination count={totalPages} page={currentPage + 1} onChange={onPageChange} className={classes.pagination} />
5751
</TableContainer>
5852
);
5953
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { createEmptyCollectionQuery } from '#common/models';
2+
import { mapExpedienteListFromApiToVm } from './expedientes.mappers';
3+
4+
describe('expedientes.mappers.spec', () => {
5+
describe('mapExpedienteListFromApiToVm', () => {
6+
it('should return empty collection when passing empty collection', () => {
7+
const expedienteList = {
8+
data: [],
9+
pagination: {
10+
totalPages: 0,
11+
},
12+
};
13+
const result = mapExpedienteListFromApiToVm(expedienteList);
14+
15+
expect(result).toEqual(createEmptyCollectionQuery());
16+
});
17+
18+
it('should return collection with mapped expedientes when passing collection with expedientes', () => {
19+
const expedienteList = {
20+
data: [
21+
{
22+
id: '123',
23+
clase: 'Administrativo',
24+
titulo: 'Solicitud de Información',
25+
adjudicataria: 'Juan Pérez',
26+
estado: 'En Proceso',
27+
},
28+
],
29+
pagination: {
30+
totalPages: 1,
31+
},
32+
};
33+
34+
const result = mapExpedienteListFromApiToVm(expedienteList);
35+
36+
expect(result.data.length).toEqual(1);
37+
expect(result).toEqual({
38+
data: [
39+
{
40+
id: '123',
41+
clase: 'Administrativo',
42+
titulo: 'Solicitud de Información',
43+
adjudicataria: 'Juan Pérez',
44+
estado: 'En Proceso',
45+
},
46+
],
47+
pagination: {
48+
totalPages: 1,
49+
},
50+
});
51+
});
52+
});
53+
});

0 commit comments

Comments
 (0)