Files
CRM_Montzar/apps/api/src/faq/faq.service.ts
T

120 lines
3.6 KiB
TypeScript

import { Injectable, NotFoundException } from '@nestjs/common';
import type {
CreateFaqArticleRequest,
FaqArticleDto,
UpdateFaqArticleRequest,
} from '@mento/shared';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { buildSearchWhere, ci } from '../common/search';
@Injectable()
export class FaqService {
constructor(private readonly prisma: PrismaService) {}
async list(
organizationId: string,
opts: { q?: string; category?: string; publishedOnly?: boolean },
): Promise<FaqArticleDto[]> {
const where: Prisma.FaqArticleWhereInput = {
organizationId,
...(opts.publishedOnly ? { isPublished: true } : {}),
...(opts.category?.trim()
? { category: opts.category.trim() }
: {}),
};
const search = buildSearchWhere<Prisma.FaqArticleWhereInput>(opts.q, (t) => [
{ question: ci(t) },
{ answer: ci(t) },
]);
if (search) where.AND = [search];
const rows = await this.prisma.faqArticle.findMany({
where,
orderBy: [{ position: 'asc' }, { createdAt: 'asc' }],
});
return rows.map(toDto);
}
async get(organizationId: string, id: string): Promise<FaqArticleDto> {
const row = await this.prisma.faqArticle.findFirst({
where: { id, organizationId },
});
if (!row) throw new NotFoundException('مقاله FAQ پیدا نشد');
return toDto(row);
}
async create(
organizationId: string,
dto: CreateFaqArticleRequest,
): Promise<FaqArticleDto> {
const row = await this.prisma.faqArticle.create({
data: {
organizationId,
question: dto.question.trim(),
answer: dto.answer.trim(),
category: emptyToNull(dto.category ?? null),
isPublished: dto.isPublished ?? true,
position: dto.position ?? 0,
},
});
return toDto(row);
}
async update(
organizationId: string,
id: string,
dto: UpdateFaqArticleRequest,
): Promise<FaqArticleDto> {
await this.ensureExists(organizationId, id);
const data: Prisma.FaqArticleUpdateInput = {};
if (dto.question !== undefined) data.question = dto.question.trim();
if (dto.answer !== undefined) data.answer = dto.answer.trim();
if ('category' in dto) data.category = emptyToNull(dto.category ?? null);
if (dto.isPublished !== undefined) data.isPublished = dto.isPublished;
if (dto.position !== undefined) data.position = dto.position;
const row = await this.prisma.faqArticle.update({ where: { id }, data });
return toDto(row);
}
async remove(organizationId: string, id: string): Promise<void> {
await this.ensureExists(organizationId, id);
await this.prisma.faqArticle.delete({ where: { id } });
}
private async ensureExists(organizationId: string, id: string) {
const found = await this.prisma.faqArticle.findFirst({
where: { id, organizationId },
select: { id: true },
});
if (!found) throw new NotFoundException('مقاله FAQ پیدا نشد');
}
}
function toDto(row: {
id: string;
question: string;
answer: string;
category: string | null;
isPublished: boolean;
position: number;
createdAt: Date;
updatedAt: Date;
}): FaqArticleDto {
return {
id: row.id,
question: row.question,
answer: row.answer,
category: row.category,
isPublished: row.isPublished,
position: row.position,
createdAt: row.createdAt.toISOString(),
updatedAt: row.updatedAt.toISOString(),
};
}
function emptyToNull(value?: string | null): string | null {
if (value == null) return null;
const trimmed = value.trim();
return trimmed.length ? trimmed : null;
}