From 1f73c5e04f3cf2ddd35893c945e161a7cf9691f4 Mon Sep 17 00:00:00 2001 From: avicenan Date: Wed, 25 Mar 2026 09:44:41 +0700 Subject: [PATCH] fix(api)!: paginate schedule calendar by resource --- src/controllers/schedule.ts | 10 +--------- src/model/schedule.ts | 37 ++++++++++++++++--------------------- src/swagger/builder.js | 4 +--- 3 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/controllers/schedule.ts b/src/controllers/schedule.ts index 23c6722..80ac303 100644 --- a/src/controllers/schedule.ts +++ b/src/controllers/schedule.ts @@ -125,8 +125,6 @@ export class ScheduleController { #swagger.parameters['filter'] = { in: 'query', required: false, type: 'string' } #swagger.parameters['limit'] = { in: 'query', required: false, type: 'number' } #swagger.parameters['page'] = { in: 'query', required: false, type: 'number' } - #swagger.parameters['order_field'] = { in: 'query', required: false, type: 'string' } - #swagger.parameters['order_direction'] = { in: 'query', required: false, schema: { '@enum': ['ASC', 'DESC'] } } #swagger.responses[200] = { description: 'Schedule calendar resources and events', schema: { @@ -152,7 +150,7 @@ export class ScheduleController { isException: false, status: 'confirmed' }], - meta: { total_count: 24, page: 1, limit: 20, order_field: 'startAt', order_direction: 'ASC' } + meta: { total_count: 24, page: 1, limit: 20 } } } } @@ -164,8 +162,6 @@ export class ScheduleController { filter: Joi.string().allow("").optional().label("Filter"), limit: Joi.number().min(1).optional().label("Limit"), page: Joi.number().min(1).optional().label("Page"), - order_field: Joi.string().allow("startAt", "endAt", "title", "seriesId", "status").optional().label("Order Field"), - order_direction: Joi.string().allow("asc", "desc").optional().label("Order Direction"), }); const param: { @@ -174,15 +170,11 @@ export class ScheduleController { filter?: string; limit?: number; page?: number; - order_field?: "startAt" | "endAt" | "title" | "seriesId" | "status"; - order_direction?: "asc" | "desc"; } = await schema.validateAsync(req.query); const filterObj = param.filter && param.filter !== "" ? JSON.parse(param.filter) : {}; const result = await ScheduleModel.getScheduleCalendarRange(param.start_date, param.end_date, filterObj, { limit: param.limit, page: param.page, - order_field: param.order_field, - order_direction: param.order_direction, }); return ReturnHelper.successResponseAny(res, 200, Language.lang.success_view, result); diff --git a/src/model/schedule.ts b/src/model/schedule.ts index d574f6f..e9ece82 100644 --- a/src/model/schedule.ts +++ b/src/model/schedule.ts @@ -34,8 +34,6 @@ type ScheduleExceptionPayload = { type CalendarQueryOptions = { limit?: number; page?: number; - order_field?: "startAt" | "endAt" | "title" | "seriesId" | "status"; - order_direction?: "asc" | "desc"; }; export class ScheduleModel { @@ -148,39 +146,36 @@ export class ScheduleModel { } } - const orderField = options.order_field || "startAt"; - const orderDirection = (options.order_direction || "asc").toUpperCase() as "ASC" | "DESC"; - const sortFactor = orderDirection === "ASC" ? 1 : -1; - - allEvents.sort((a, b) => { - const av = a[orderField]; - const bv = b[orderField]; - if (av === bv) return 0; - return av > bv ? sortFactor : -sortFactor; - }); - - const totalCount = allEvents.length; + const allResourcesMap = new Map(); + for (const event of allEvents) { + allResourcesMap.set(event._resource.id, event._resource); + } + const allResources = [...allResourcesMap.values()].sort((a, b) => a.id.localeCompare(b.id)); + const totalCount = allResources.length; const page = options.page ?? 1; const limit = options.limit ?? totalCount; const offset = (page - 1) * limit; - const pagedEventsRaw = allEvents.slice(offset, offset + limit); + const pagedResources = allResources.slice(offset, offset + limit); + const allowedResourceIds = new Set(pagedResources.map((resource) => resource.id)); - const resourceMap = new Map(); - const pagedEvents = pagedEventsRaw.map((event) => { - resourceMap.set(event._resource.id, event._resource); + const pagedEvents = allEvents + .filter((event) => allowedResourceIds.has(event.resourceId)) + .sort((a, b) => { + if (a.startAt === b.startAt) return a.id.localeCompare(b.id); + return a.startAt.localeCompare(b.startAt); + }) + .map((event) => { const { _resource, ...cleanEvent } = event; return cleanEvent; }); return { - resources: [...resourceMap.values()], + resources: pagedResources, events: pagedEvents, meta: { total_count: totalCount, page, limit, - order_field: orderField, - order_direction: orderDirection, }, }; } diff --git a/src/swagger/builder.js b/src/swagger/builder.js index 6a35f95..619493d 100644 --- a/src/swagger/builder.js +++ b/src/swagger/builder.js @@ -413,9 +413,7 @@ const doc = { scheduleCalendarMeta: { $total_count: 24, $page: 1, - $limit: 20, - $order_field: "startAt", - $order_direction: "ASC" + $limit: 20 }, }, parameters: {