feat(table,runs,sessions): add server-side sorting and pagination
- add sortable column support to Table with asc/desc/unsorted icons - active sort icon uses --color-primary; no header background change - Table supports server-side mode via total/page/onPageChange props - wire server-side sort+pagination in ScenariosPage, AllRunsPage, RunsPage, SessionsPage - remove steps count column from run tables - fix server: RunsQueryDto now extends PaginationQueryDto with orderBy/orderDir - fix findAllRuns to use QueryBuilder; sort by scenario.name via JOIN - add per-resource typed query DTOs with @IsIn allowlist on orderBy - prevents SQL injection and returns 400 for unknown orderBy values
This commit is contained in:
@@ -287,12 +287,13 @@ export class ScenarioService {
|
||||
await this.findOne(scenarioId); // 404 guard
|
||||
const page = query.page ?? 1;
|
||||
const limit = query.limit ?? 20;
|
||||
const orderBy = query.orderBy ?? "createdAt";
|
||||
const orderDir = query.orderDir ?? "DESC";
|
||||
const where: Record<string, unknown> = { scenarioId };
|
||||
if (query.status) where["status"] = query.status;
|
||||
const [data, total] = await this.runRepo.findAndCount({
|
||||
where,
|
||||
relations: ["stepRuns"],
|
||||
order: { createdAt: "DESC" },
|
||||
order: { [orderBy]: orderDir },
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
});
|
||||
@@ -306,15 +307,19 @@ export class ScenarioService {
|
||||
> {
|
||||
const page = query.page ?? 1;
|
||||
const limit = query.limit ?? 20;
|
||||
const where: Record<string, unknown> = {};
|
||||
if (query.status) where["status"] = query.status;
|
||||
const [data, total] = await this.runRepo.findAndCount({
|
||||
where,
|
||||
relations: ["stepRuns", "scenario"],
|
||||
order: { createdAt: "DESC" },
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
});
|
||||
const orderBy = query.orderBy ?? "createdAt";
|
||||
const orderDir = query.orderDir ?? "DESC";
|
||||
const qb = this.runRepo
|
||||
.createQueryBuilder("run")
|
||||
.leftJoinAndSelect("run.scenario", "scenario");
|
||||
if (query.status) qb.where("run.status = :status", { status: query.status });
|
||||
if (orderBy === "scenario.name") {
|
||||
qb.orderBy("scenario.name", orderDir);
|
||||
} else {
|
||||
qb.orderBy(`run.${orderBy}`, orderDir);
|
||||
}
|
||||
qb.skip((page - 1) * limit).take(limit);
|
||||
const [data, total] = await qb.getManyAndCount();
|
||||
return { data, total, page, limit } as PaginatedResult<
|
||||
ScenarioRunEntity & { scenario: ScenarioEntity }
|
||||
>;
|
||||
|
||||
Reference in New Issue
Block a user