import { Inject, Injectable, Logger } from '@nestjs/common'; import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken, } from '@smatch-corp/nestjs-pothos'; import { Builder } from '../Graphql/graphql.builder'; import { PrismaService } from '../Prisma/prisma.service'; import { MinioService } from '../Minio/minio.service'; import { CenterStatus, Role } from '@prisma/client'; import { MailService } from '../Mail/mail.service'; @Injectable() export class CenterSchema extends PothosSchema { constructor( @Inject(SchemaBuilderToken) private readonly builder: Builder, private readonly prisma: PrismaService, private readonly minioService: MinioService, private readonly mailService: MailService, ) { super(); } @PothosRef() center() { return this.builder.prismaObject('Center', { description: 'A center in the system.', fields: (t) => ({ id: t.exposeID('id', { description: 'The unique identifier of the center.', }), centerOwnerId: t.exposeID('centerOwnerId', { description: 'The ID of the center owner.', }), bank: t.exposeString('bank', { description: 'The bank of the center.', }), bankAccountNumber: t.exposeString('bankAccountNumber', { description: 'The bank account number of the center.', }), name: t.exposeString('name', { description: 'The name of the center.', }), description: t.exposeString('description', { description: 'The description of the center.', }), adminNote: t.relation('adminNote', { description: 'The admin note of the center.', }), logoUrl: t.exposeString('logoUrl', { description: 'The URL of the center logo.', }), logoFile: t.relation('logoFile', { description: 'The file associated with the center logo.', }), location: t.exposeString('location', { description: 'The location of the center.', }), individual: t.exposeBoolean('individual', { nullable: true, description: 'Whether the center is an individual center.', }), createdAt: t.expose('createdAt', { type: 'DateTime' }), updatedAt: t.expose('updatedAt', { type: 'DateTime' }), services: t.relation('services', { description: 'The services provided by the center.', }), centerOwner: t.relation('centerOwner', { description: 'The owner of the center.', }), chatRoom: t.relation('chatRoom', { description: 'The chat room associated with the center.', }), centerMentor: t.relation('centerMentors', { description: 'The mentors of the center.', }), resume: t.relation('resume', { description: 'The resume of the center.', }), centerStatus: t.expose('centerStatus', { type: CenterStatus, description: 'The status of the center.', }), uploadedFileId: t.exposeID('uploadedFileId', { description: 'The ID of the uploaded file.', }), }), }); } @Pothos() init(): void { this.builder.queryFields((t) => ({ centers: t.prismaField({ description: 'Retrieve a list of centers with optional filtering, ordering, and pagination.', type: [this.center()], args: this.builder.generator.findManyArgs('Center'), resolve: async (query, root, args) => { return await this.prisma.center.findMany({ ...query, skip: args.skip ?? undefined, take: args.take ?? undefined, orderBy: args.orderBy ?? undefined, where: args.filter ?? undefined, }); }, }), center: t.prismaField({ type: this.center(), description: 'Retrieve a single center by its unique identifier.', args: this.builder.generator.findUniqueArgs('Center'), resolve: async (query, root, args) => { return await this.prisma.center.findUnique({ ...query, where: args.where, }); }, }), // get current center of centermentor by providing userId centerByCenterMentor: t.prismaField({ type: this.center(), description: 'Retrieve a single center by its unique identifier.', args: { userId: t.arg({ type: 'String', required: true }), }, resolve: async (query, root, args) => { return await this.prisma.center.findFirst({ where: { centerMentors: { some: { mentorId: args.userId, }, }, }, }); }, }), })); // mutation section this.builder.mutationFields((t) => ({ createCenter: t.prismaField({ description: 'Create a new center.', type: this.center(), args: { input: t.arg({ type: this.builder.generator.getCreateInput('Center'), required: true, }), }, resolve: async (query, root, args) => { return await this.prisma.center.create({ ...query, data: args.input, }); }, }), updateCenter: t.prismaField({ type: this.center(), description: 'Update an existing center.', args: { input: t.arg({ type: this.builder.generator.getUpdateInput('Center'), required: true, }), where: t.arg({ type: this.builder.generator.getWhereUnique('Center'), required: true, }), }, resolve: async (query, root, args) => { return await this.prisma.center.update({ ...query, where: args.where, data: args.input, }); }, }), deleteCenter: t.prismaField({ type: this.center(), description: 'Delete an existing center.', args: { where: t.arg({ type: this.builder.generator.getWhereUnique('Center'), required: true, }), }, resolve: async (query, root, args) => { return await this.prisma.center.delete({ ...query, where: args.where, }); }, }), approveOrRejectCenter: t.prismaField({ type: this.center(), description: 'Approve a center and promote centermentor to mentor', args: { centerId: t.arg({ type: 'String', required: true, }), approve: t.arg({ type: 'Boolean', required: true, }), adminNote: t.arg({ type: 'String', required: false, }), }, resolve: async (query, root, args) => { return await this.prisma.$transaction(async (prisma) => { const center = await prisma.center.findUnique({ ...query, where: { id: args.centerId, }, }); if (!center) { throw new Error('Center not found'); } // check if center is already approved or rejected if (center.centerStatus !== CenterStatus.PENDING) { throw new Error('Center is already approved or rejected'); } // find user and promote to center owner const centerOwnerId = center.centerOwnerId; if (!centerOwnerId) { throw new Error('User not found'); } const centerOwner = await prisma.user.findUnique({ where: { id: centerOwnerId, }, }); if (!centerOwner) { throw new Error('User not found'); } await prisma.user.update({ where: { id: centerOwnerId, }, data: { role: Role.CENTER_OWNER, }, }); // update center status const updatedCenter = await prisma.center.update({ ...query, where: { id: args.centerId, }, data: { centerStatus: args.approve ? CenterStatus.APPROVED : CenterStatus.REJECTED, }, }); // mail to center owner if approved if (args.approve) { try { await this.mailService.sendTemplateEmail( [centerOwner.email], 'Thông báo phê duyệt đăng ký trung tâm', 'CenterApproved', { CENTER_NAME: center.name, }, ); } catch (error) { Logger.error(error, 'CenterSchema'); } } if (!args.approve) { // mail to center owner if rejected try { await this.mailService.sendTemplateEmail( [centerOwner.email], 'Thông báo từ chối đăng ký trung tâm', 'CenterRejected', { CENTER_NAME: center.name, ADMIN_NOTE: args.adminNote, }, ); } catch (error) { Logger.error(error, 'CenterSchema'); } } return updatedCenter; }); }, }), })); } }