refactor: changed the findTasksByTaskPhase to include task info like remark, etc

This commit is contained in:
2026-07-14 21:28:09 +03:30
parent f35df6492c
commit 4263d7118e
@@ -49,26 +49,21 @@ export class TaskRepository extends Repository<TMTask> {
.getOne(); .getOne();
} }
async findTasksByTaskPhase( async findTasksByTaskPhase(taskPhaseId: string, pagination: CursorPaginationDto): Promise<{ items: TMTask[]; hasMore: boolean }> {
taskPhaseId: string,
pagination: CursorPaginationDto,
): Promise<{ items: TMTask[]; hasMore: boolean }> {
const limit = pagination.limit || 10; const limit = pagination.limit || 10;
const isBackward = pagination.direction === CursorDirection.PREV; const isBackward = pagination.direction === CursorDirection.PREV;
// truncated to milliseconds because a JS Date (and therefore the encoded cursor) only
// has millisecond precision, while Postgres timestamptz stores microseconds - comparing
// the raw column against the cursor would otherwise re-match the boundary row itself.
const createdAtExpr = `date_trunc('milliseconds', task.createdAt)`; const createdAtExpr = `date_trunc('milliseconds', task.createdAt)`;
const queryBuilder = this.createQueryBuilder("task") const seekQuery = this.createQueryBuilder("task")
.select(["task.id", "task.priority", "task.createdAt"])
.where("task.taskPhaseId = :taskPhaseId", { taskPhaseId }) .where("task.taskPhaseId = :taskPhaseId", { taskPhaseId })
.limit(limit + 1); // fetch one extra to check whether there's a further page .limit(limit + 1); // fetch one extra to check whether there's a further page
if (isBackward) { if (isBackward) {
queryBuilder.orderBy("task.priority", "ASC").addOrderBy(createdAtExpr, "DESC").addOrderBy("task.id", "DESC"); seekQuery.orderBy("task.priority", "ASC").addOrderBy(createdAtExpr, "DESC").addOrderBy("task.id", "DESC");
} else { } else {
queryBuilder.orderBy("task.priority", "DESC").addOrderBy(createdAtExpr, "ASC").addOrderBy("task.id", "ASC"); seekQuery.orderBy("task.priority", "DESC").addOrderBy(createdAtExpr, "ASC").addOrderBy("task.id", "ASC");
} }
if (pagination.cursor) { if (pagination.cursor) {
@@ -76,7 +71,7 @@ export class TaskRepository extends Repository<TMTask> {
const priorityOp = isBackward ? ">" : "<"; const priorityOp = isBackward ? ">" : "<";
const seekOp = isBackward ? "<" : ">"; const seekOp = isBackward ? "<" : ">";
queryBuilder.andWhere( seekQuery.andWhere(
`(task.priority ${priorityOp} :cursorPriority `(task.priority ${priorityOp} :cursorPriority
OR (task.priority = :cursorPriority AND ${createdAtExpr} ${seekOp} :cursorCreatedAt) OR (task.priority = :cursorPriority AND ${createdAtExpr} ${seekOp} :cursorCreatedAt)
OR (task.priority = :cursorPriority AND ${createdAtExpr} = :cursorCreatedAt AND task.id ${seekOp} :cursorId))`, OR (task.priority = :cursorPriority AND ${createdAtExpr} = :cursorCreatedAt AND task.id ${seekOp} :cursorId))`,
@@ -84,13 +79,32 @@ export class TaskRepository extends Repository<TMTask> {
); );
} }
const rows = await queryBuilder.getMany(); const seekRows = await seekQuery.getMany();
const hasMore = rows.length > limit; const hasMore = seekRows.length > limit;
if (hasMore) rows.pop(); if (hasMore) seekRows.pop();
// backward queries fetch in reverse order to seek from the cursor, so flip back to // backward queries fetch in reverse order to seek from the cursor, so flip back to
// the normal display order before returning // the normal display order before returning
const items = isBackward ? rows.reverse() : rows; const orderedRows = isBackward ? seekRows.reverse() : seekRows;
const ids = orderedRows.map((row) => row.id);
if (!ids.length) return { items: [], hasMore };
// Detail query: hydrates exactly the ids picked above with the relations/counts the
// response needs. IN (:...ids) doesn't preserve order, so re-order to match seekQuery.
const detailRows = await this.createQueryBuilder("task")
.leftJoinAndSelect("task.remarks", "remark")
.loadRelationCountAndMap("task.attachmentCount", "task.attachments")
.loadRelationCountAndMap("task.checkListItemCount", "task.checkListItems")
.loadRelationCountAndMap("task.completedCheckListItemCount", "task.checkListItems", "completedCheckListItems", (qb) =>
qb.where("completedCheckListItems.isDone = :isDone", { isDone: true }),
)
.select(["task", "remark.id", "remark.title", "remark.color"])
.where("task.id IN (:...ids)", { ids })
.getMany();
const detailById = new Map(detailRows.map((task) => [task.id, task]));
const items = ids.map((id) => detailById.get(id)).filter((task): task is TMTask => task !== undefined);
return { items, hasMore }; return { items, hasMore };
} }