import type { ListBookingReportsFilters, SystemBookingReportsFilters, } from "@calcom/features/bookingReport/repositories/IBookingReportRepository"; import type { PrismaBookingReportRepository } from "@calcom/features/bookingReport/repositories/PrismaBookingReportRepository"; import type { UserRepository } from "@calcom/features/users/repositories/UserRepository"; import type { WatchlistRepository } from "@calcom/features/watchlist/lib/repository/WatchlistRepository"; import type { PrismaClient } from "@calcom/prisma"; import type { WatchlistType, WatchlistSource } from "@calcom/prisma/enums"; import { WatchlistErrors } from "../errors/WatchlistErrors"; export interface ListWatchlistEntriesInput { limit: number; offset: number; searchTerm?: string; filters?: { type?: WatchlistType; source?: WatchlistSource; }; } export interface GetWatchlistEntryDetailsInput { entryId: string; } export interface ListBookingReportsInput { limit: number; offset: number; searchTerm?: string; filters?: ListBookingReportsFilters; systemFilters?: SystemBookingReportsFilters; sortBy?: "createdAt" | "reportCount"; } type Deps = { watchlistRepo: WatchlistRepository; bookingReportRepo: PrismaBookingReportRepository; userRepo: UserRepository; prisma: PrismaClient; }; export class AdminWatchlistQueryService { constructor(private readonly deps: Deps) {} async listWatchlistEntries(input: ListWatchlistEntriesInput) { const { rows, meta } = await this.deps.watchlistRepo.findAllEntriesWithLatestAudit({ organizationId: null, isGlobal: true, limit: input.limit, offset: input.offset, searchTerm: input.searchTerm, filters: input.filters, }); const userIds = rows .map((entry) => entry.latestAudit?.changedByUserId) .filter((id): id is number => id !== null && id !== undefined); const uniqueUserIds = Array.from(new Set(userIds)); const users = uniqueUserIds.length > 0 ? await this.deps.userRepo.findUsersByIds(uniqueUserIds) : []; const userMap = new Map(users.map((u) => [u.id, u])); const rowsWithCreators = rows.map((entry) => { if (entry.latestAudit?.changedByUserId) { const changedByUser = userMap.get(entry.latestAudit.changedByUserId); return { ...entry, latestAudit: { ...entry.latestAudit, changedByUser, }, }; } return entry; }); return { rows: rowsWithCreators, meta, }; } async getWatchlistEntryDetails(input: GetWatchlistEntryDetailsInput) { const result = await this.deps.watchlistRepo.findEntryWithAuditAndReports(input.entryId); if (!result.entry) { throw WatchlistErrors.notFound("Blocklist entry not found"); } if (!result.entry.isGlobal || result.entry.organizationId !== null) { throw WatchlistErrors.permissionDenied("You can only view system blocklist entries"); } const userIds = result.auditHistory .map((audit) => audit.changedByUserId) .filter((id): id is number => id !== null && id !== undefined); const uniqueUserIds = Array.from(new Set(userIds)); const users = uniqueUserIds.length > 0 ? await this.deps.userRepo.findUsersByIds(uniqueUserIds) : []; const userMap = new Map(users.map((u) => [u.id, u])); const auditHistoryWithUsers = result.auditHistory.map((audit) => ({ ...audit, changedByUser: audit.changedByUserId ? userMap.get(audit.changedByUserId) : undefined, })); return { entry: result.entry, auditHistory: auditHistoryWithUsers, }; } async listBookingReports(input: ListBookingReportsInput) { const result = await this.deps.bookingReportRepo.findGroupedReportedBookings({ skip: input.offset, take: input.limit, searchTerm: input.searchTerm, filters: input.filters, systemFilters: input.systemFilters, sortBy: input.sortBy, }); return result; } async getPendingReportsCount(): Promise { return this.deps.bookingReportRepo.countSystemPendingReports(); } }