import { BadRequestException, Injectable, NotFoundException, } from '@nestjs/common'; import { toListPage } from '../../../common/http/response'; import type { PaginationResponse } from '../../../common/http/response'; import { REPORT_BOOKMARK_TYPE, type ReportBookmarkType, } from '../shared/constants/bookmark-type'; import { findReportConfig } from '../shared/configs'; import { ReportBookmarksRepository, type ReportBookmark, } from './report-bookmarks.repository'; export type ListReportBookmarksQuery = { groupName?: string; uniqueName?: string; type?: ReportBookmarkType; label?: string; page?: number; limit?: number; offset?: number; }; export type CreateReportBookmarkInput = { groupName: string; uniqueName: string; label: string; type: ReportBookmarkType; applied?: boolean; configuration: unknown; }; @Injectable() export class ReportBookmarksService { constructor(private readonly repository: ReportBookmarksRepository) {} async list( ownerId: string, query: ListReportBookmarksQuery, ): Promise>> { const page = toListPage(query); const { data, total } = await this.repository.list(ownerId, { groupName: query.groupName, uniqueName: query.uniqueName, type: query.type, label: query.label, limit: page.limit, offset: page.offset, }); return { data: data.map((item) => this.toDto(item)), total }; } async labelHistory(ownerId: string, label?: string): Promise { return this.repository.labelHistory(ownerId, label); } async findApplied( ownerId: string, groupName?: string, uniqueName?: string, type?: ReportBookmarkType, ) { if (!groupName || !uniqueName || !type) { throw new BadRequestException( 'groupName, uniqueName, and type are required', ); } const bookmark = await this.repository.findApplied( ownerId, groupName, uniqueName, type, ); return bookmark ? this.toDto(bookmark) : null; } async create(ownerId: string, input: CreateReportBookmarkInput) { this.assertValidBookmarkInput(input); const bookmark = await this.repository.create(ownerId, { groupName: input.groupName, uniqueName: input.uniqueName, label: input.label, type: input.type, applied: input.applied ?? false, configuration: input.configuration, }); return this.toDto(bookmark); } async apply(ownerId: string, id: string) { const bookmark = await this.repository.apply(ownerId, id); return this.toDto(bookmark); } async unapply(ownerId: string, id: string) { const bookmark = await this.repository.unapply(ownerId, id); return this.toDto(bookmark); } async delete(ownerId: string, id: string): Promise { await this.repository.delete(ownerId, id); } private assertValidBookmarkInput(input: CreateReportBookmarkInput) { if (!findReportConfig(input.groupName, input.uniqueName)) { throw new NotFoundException('Report not found'); } if ( input.type !== REPORT_BOOKMARK_TYPE.FILTER_TABLE && input.type !== REPORT_BOOKMARK_TYPE.TABLE_CONFIG ) { throw new BadRequestException('Invalid bookmark type'); } } private toDto(bookmark: ReportBookmark) { return { id: bookmark.id, groupName: bookmark.groupName, uniqueName: bookmark.uniqueName, label: bookmark.label, type: bookmark.type, applied: bookmark.applied, configuration: bookmark.configuration, status: bookmark.status, createdAt: bookmark.createdAt, updatedAt: bookmark.updatedAt, }; } }