File

apps/recallassess/recallassess-api/src/api/admin/report/reports/shared/participant-scope-filters.service.ts

Description

Company → course → learning group → participant valuelists scoped by enrollments — shared by multiple reports (each report exposes its own HTTP routes; implementations stay here).

Index

Methods

Constructor

constructor(prisma: BNestPrismaService)
Parameters :
Name Type Optional
prisma BNestPrismaService No

Methods

Async listCompanyFilterOptions
listCompanyFilterOptions(listQuery: ReportValuelistListQuery)
Parameters :
Name Type Optional
listQuery ReportValuelistListQuery No
Returns : unknown
Async listCourseFilterOptions
listCourseFilterOptions(options: unknown)

Courses this company has enrollments for (via learning groups / LGP). No rows if companyId is missing.

Parameters :
Name Type Optional
options unknown No
Returns : unknown
Async listLearningGroupFilterOptions
listLearningGroupFilterOptions(options: unknown)

Learning groups for this company and course (LearningGroup rows match both).

Parameters :
Name Type Optional
options unknown No
Returns : unknown
Async listParticipantFilterOptions
listParticipantFilterOptions(options: unknown)
Parameters :
Name Type Optional
options unknown No
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);
  }
}

results matching ""

    No results matching ""