import { Inject, Injectable } 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 { MailService } from '../Mail/mail.service'; import { JwtUtils } from '../common/utils/jwt.utils'; @Injectable() export class CenterMentorSchema extends PothosSchema { constructor( @Inject(SchemaBuilderToken) private readonly builder: Builder, private readonly prisma: PrismaService, private readonly mailService: MailService, private readonly jwtUtils: JwtUtils, ) { super(); } @PothosRef() centerMentor() { return this.builder.prismaObject('CenterMentor', { description: 'A mentor of a center.', fields: (t) => ({ mentorId: t.exposeID('mentorId', { description: 'The ID of the mentor.', }), centerId: t.exposeID('centerId', { description: 'The ID of the center.', }), isCenterOwner: t.exposeBoolean('isCenterOwner', { description: 'Whether the mentor is the center owner.', }), mentor: t.relation('mentor', { description: 'The mentor.', }), center: t.relation('center', { description: 'The center.', }), createdWorkshop: t.relation('createdWorkshop', { description: 'The workshops created by the center mentor.', }), managedService: t.relation('managedService', { description: 'The managed services of the center mentor.', }), adminNote: t.relation('adminNote', { description: 'The admin note of the center mentor.', }), }), }); } @Pothos() init(): void { this.builder.queryFields((t) => ({ centerMentor: t.prismaField({ description: 'Retrieve a list of center mentors with optional filtering, ordering, and pagination.', type: [this.centerMentor()], args: this.builder.generator.findManyArgs('CenterMentor'), resolve: async (query, root, args) => { return await this.prisma.centerMentor.findMany({ ...query, skip: args.skip ?? undefined, take: args.take ?? undefined, orderBy: args.orderBy ?? undefined, where: args.filter ?? undefined, }); }, }), })); // mutations this.builder.mutationFields((t) => ({ createCenterMentor: t.prismaField({ type: this.centerMentor(), description: 'Create a new center mentor.', args: { data: t.arg({ type: this.builder.generator.getCreateInput('CenterMentor'), required: true, }), }, resolve: async (query, root, args) => { return await this.prisma.centerMentor.create({ ...query, data: args.data, }); }, }), updateCenterMentor: t.prismaField({ type: this.centerMentor(), description: 'Update an existing center mentor.', args: { where: t.arg({ type: this.builder.generator.getWhereUnique('CenterMentor'), required: true, }), data: t.arg({ type: this.builder.generator.getUpdateInput('CenterMentor'), required: true, }), }, resolve: async (query, root, args) => { return await this.prisma.centerMentor.update({ ...query, where: args.where, data: args.data, }); }, }), deleteCenterMentor: t.prismaField({ type: this.centerMentor(), description: 'Delete an existing center mentor.', args: { where: t.arg({ type: this.builder.generator.getWhereUnique('CenterMentor'), required: true, }), }, resolve: async (query, root, args) => { return await this.prisma.centerMentor.delete({ ...query, where: args.where, }); }, }), inviteCenterMentor: t.prismaField({ type: this.centerMentor(), description: 'Invite a new center mentor.', args: { email: t.arg({ type: 'String', required: true }), }, resolve: async (query, root, args, ctx) => { return this.prisma.$transaction(async (prisma) => { // get centerId by user id from context const userId = ctx.me.id; if (!userId) { throw new Error('User ID is required'); } // get centerId by user id const center = await prisma.center.findUnique({ where: { centerOwnerId: userId }, }); if (!center) { throw new Error('Center not found'); } // build signature const token = this.jwtUtils.signTokenRS256( { centerId: center.id, email: args.email }, '1d', ); // build invite url const inviteUrl = `${process.env.CENTER_BASE_URL}/invite?token=${token}`; // mail to user with params centerId, email await this.mailService.sendTemplateEmail( args.email, 'Invite to center', 'MentorInvitation', { center_name: center.name, invite_url: inviteUrl, }, ); return null; }); }, }), testInviteCenterMentor: t.prismaField({ type: this.centerMentor(), args: { email: t.arg({ type: 'String', required: true }), centerId: t.arg({ type: 'String', required: true }), }, description: 'Test invite center mentor.', resolve: async (query, root, args) => { return this.prisma.$transaction(async () => { // sign token const token = this.jwtUtils.signTokenRS256( { centerId: args.centerId, email: args.email }, '1d', ); // build invite url const inviteUrl = `${process.env.CENTER_BASE_URL}/invite?token=${token}`; // mail to user with params centerId, email await this.mailService.sendTemplateEmail( args.email, 'Invite to center', 'MentorInvitation', { center_name: args.centerId, invite_url: inviteUrl, }, ); return null; }); }, }), approveOrRejectCenterMentor: t.prismaField({ type: this.centerMentor(), description: 'Approve or reject a center mentor.', args: { where: t.arg({ type: this.builder.generator.getWhereUnique('User'), required: true, }), approved: t.arg({ type: 'Boolean', required: true }), adminNote: t.arg({ type: 'String', required: false }), }, resolve: async (query, root, args, ctx, info) => { return this.prisma.$transaction(async (prisma) => { // validate input if (args.approved && !args.adminNote) { throw new Error('Admin note is required'); } // get mentor info const mentor = await prisma.user.findUnique({ where: args.where, }); if (!mentor) { throw new Error('Mentor not found'); } // get centerMentor const centerMentor = await prisma.centerMentor.findUnique({ where: { mentorId: mentor.id }, }); if (!centerMentor) { throw new Error('Center mentor not found'); } // get center const center = await prisma.center.findUnique({ where: { id: centerMentor.centerId }, }); if (!center) { throw new Error('Center not found'); } // get email const email = await prisma.user.findUnique({ where: args.where, select: { email: true }, }); if (!email) { throw new Error('Email is required'); } // if approved, update role to mentor if (args.approved) { // send mail to user await this.mailService.sendTemplateEmail( email.email, 'Thông báo về việc được chấp nhận làm mentor', 'MentorApproved', { CENTER_NAME: center.name, USER_NAME: mentor.name, }, ); // create adminNote const adminNote = await prisma.adminNote.create({ data: { content: args.adminNote ?? '', mentorId: mentor.id, notedByUserId: ctx.me.id, }, }); const updatedUser = await prisma.user.update({ where: args.where, data: { role: 'CENTER_MENTOR', updatedAt: new Date(), }, }); // update centerMentor const updatedCenterMentor = await prisma.centerMentor.update({ where: { mentorId_centerId: { mentorId: mentor.id, centerId: centerMentor.centerId, }, }, data: { adminNote: { connect: { id: adminNote.id } }, }, }); return updatedCenterMentor; } // if rejected, update adminNote await this.mailService.sendTemplateEmail( email.email, 'Thông báo về việc không được chấp nhận làm mentor', 'MentorRejected', { CENTER_NAME: center.name, USER_NAME: mentor.name, }, ); return await prisma.centerMentor.update({ where: { mentorId_centerId: { mentorId: mentor.id, centerId: centerMentor.centerId, }, }, data: { adminNote: { create: { content: args.adminNote ?? '', notedByUserId: ctx.me.id, updatedAt: new Date(), }, }, }, }); }); }, }), })); } }