apps/recallassess/recallassess-api/src/api/admin/report/reports/shared/participant-scope-filters.service.ts
Company → course → learning group → participant valuelists scoped by enrollments — shared by multiple reports (each report exposes its own HTTP routes; implementations stay here).
Methods |
|
constructor(prisma: BNestPrismaService)
|
||||||
|
Parameters :
|
| Async listCompanyFilterOptions | ||||||
listCompanyFilterOptions(listQuery: ReportValuelistListQuery)
|
||||||
|
Parameters :
Returns :
unknown
|
| Async listCourseFilterOptions | ||||||
listCourseFilterOptions(options: unknown)
|
||||||
|
Courses this company has enrollments for (via learning groups / LGP). No rows if
Parameters :
Returns :
unknown
|
| Async listLearningGroupFilterOptions | ||||||
listLearningGroupFilterOptions(options: unknown)
|
||||||
|
Learning groups for this company and course (
Parameters :
Returns :
unknown
|
| Async listParticipantFilterOptions | ||||||
listParticipantFilterOptions(options: unknown)
|
||||||
|
Parameters :
Returns :
unknown
|
import { buildListResponseData } from "@bish-nest/core";
import { BNestPrismaService } from "@bish-nest/core/services";
import { Injectable } from "@nestjs/common";
import { Prisma } from "@prisma/client";
import type { ReportValuelistListQuery } from "../../utils/report-valuelist-query.util";
/**
* Company → course → learning group → participant valuelists scoped by enrollments — shared by multiple reports
* (each report exposes its own HTTP routes; implementations stay here).
*/
@Injectable()
export class ParticipantScopeFiltersService {
constructor(private readonly prisma: BNestPrismaService) {}
async listCompanyFilterOptions(listQuery: ReportValuelistListQuery) {
const where: Prisma.CompanyWhereInput = listQuery.keyword
? { name: { contains: listQuery.keyword, mode: "insensitive" } }
: {};
const [totalCount, rows] = await Promise.all([
this.prisma.client.company.count({ where }),
this.prisma.client.company.findMany({
where,
select: { id: true, name: true },
orderBy: { name: "asc" },
take: listQuery.limit,
skip: listQuery.skip,
}),
]);
const data = rows.map((row) => ({ value: row.id, label: row.name }));
return buildListResponseData(data, listQuery.page, listQuery.limit, totalCount);
}
/** Courses this company has enrollments for (via learning groups / LGP). No rows if `companyId` is missing. */
async listCourseFilterOptions(
options: ReportValuelistListQuery & {
companyId?: number;
},
) {
const enrollmentWhere: Prisma.CourseWhereInput =
options.companyId != null
? {
learningGroupParticipants: {
some: {
cancelled: false,
learningGroup: { company_id: options.companyId },
},
},
}
: { id: { in: [] } };
const where: Prisma.CourseWhereInput =
options.keyword != null && options.keyword.length > 0
? { AND: [enrollmentWhere, { title: { contains: options.keyword, mode: "insensitive" } }] }
: enrollmentWhere;
const [totalCount, rows] = await Promise.all([
this.prisma.client.course.count({ where }),
this.prisma.client.course.findMany({
where,
select: { id: true, title: true },
orderBy: { title: "asc" },
take: options.limit,
skip: options.skip,
}),
]);
const data = rows.map((row) => ({ value: row.id, label: row.title }));
return buildListResponseData(data, options.page, options.limit, totalCount);
}
/** Learning groups for this company and course (`LearningGroup` rows match both). */
async listLearningGroupFilterOptions(
options: ReportValuelistListQuery & {
companyId?: number;
courseId?: number;
},
) {
const enrolledWhere: Prisma.LearningGroupWhereInput =
options.companyId != null && options.courseId != null
? {
company_id: options.companyId,
course_id: options.courseId,
}
: { id: { in: [] } };
const where: Prisma.LearningGroupWhereInput =
options.keyword != null && options.keyword.length > 0
? {
AND: [
enrolledWhere,
{ name: { contains: options.keyword, mode: "insensitive" } },
],
}
: enrolledWhere;
const [totalCount, rows] = await Promise.all([
this.prisma.client.learningGroup.count({ where }),
this.prisma.client.learningGroup.findMany({
where,
select: { id: true, name: true },
orderBy: { name: "asc" },
take: options.limit,
skip: options.skip,
}),
]);
const data = rows.map((row) => ({ value: row.id, label: row.name }));
return buildListResponseData(data, options.page, options.limit, totalCount);
}
async listParticipantFilterOptions(
options: ReportValuelistListQuery & {
companyId?: number;
courseId?: number;
learningGroupId?: number;
},
) {
const and: Prisma.ParticipantWhereInput[] = [];
if (options.companyId != null) {
and.push({ company_id: options.companyId });
}
if (options.courseId != null || options.learningGroupId != null) {
const lgpWhere: Prisma.LearningGroupParticipantWhereInput = {
cancelled: false,
...(options.courseId != null ? { course_id: options.courseId } : {}),
...(options.learningGroupId != null ? { learning_group_id: options.learningGroupId } : {}),
};
and.push({
learningGroupParticipants: { some: lgpWhere },
});
}
if (options.keyword != null && options.keyword.length > 0) {
const k = options.keyword;
and.push({
OR: [
{ first_name: { contains: k, mode: "insensitive" } },
{ last_name: { contains: k, mode: "insensitive" } },
{ email: { contains: k, mode: "insensitive" } },
],
});
}
const where: Prisma.ParticipantWhereInput = and.length > 0 ? { AND: and } : {};
const [totalCount, rows] = await Promise.all([
this.prisma.client.participant.count({ where }),
this.prisma.client.participant.findMany({
where,
select: { id: true, first_name: true, last_name: true, email: true },
orderBy: [{ first_name: "asc" }, { last_name: "asc" }],
distinct: ["id"],
take: options.limit,
skip: options.skip,
}),
]);
const data = rows.map((row) => ({
value: row.id,
label: `${row.first_name ?? ""} ${row.last_name ?? ""}`.trim() || row.email || String(row.id),
}));
return buildListResponseData(data, options.page, options.limit, totalCount);
}
}