From e39cf519001a24da113b33750454d540a41e971a Mon Sep 17 00:00:00 2001 From: Ly Tuan Kiet Date: Sun, 27 Oct 2024 15:16:47 +0700 Subject: [PATCH] update approve center --- package-lock.json | 79 ++++++++++++++++---------- package.json | 5 +- src/Center/center.schema.ts | 76 ++++++++++++++++++++++++- src/Mail/mail.service.ts | 12 ++-- src/Mail/templates/StaffInvitation.pug | 71 +++++++++++++++++++++++ 5 files changed, 203 insertions(+), 40 deletions(-) create mode 100644 src/Mail/templates/StaffInvitation.pug diff --git a/package-lock.json b/package-lock.json index 8124b8c..1316a49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@pothos/plugin-scope-auth": "^4.1.0", "@pothos/plugin-simple-objects": "^4.1.0", "@pothos/plugin-smart-subscriptions": "^4.1.0", - "@prisma/client": "^5.20.0", + "@prisma/client": "^5.21.1", "@smatch-corp/nestjs-pothos": "^0.3.0", "@smatch-corp/nestjs-pothos-apollo-driver": "^0.1.0", "apollo-server-express": "^3.13.0", @@ -88,7 +88,7 @@ "globals": "^15.10.0", "jest": "^29.5.0", "prettier": "^3.0.0", - "prisma": "^5.20.0", + "prisma": "^5.21.1", "source-map-support": "^0.5.21", "supertest": "^7.0.0", "ts-jest": "^29.1.0", @@ -5239,9 +5239,9 @@ } }, "node_modules/@prisma/client": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.20.0.tgz", - "integrity": "sha512-CLv55ZuMuUawMsxoqxGtLT3bEZoa2W8L3Qnp6rDIFWy+ZBrUcOFKdoeGPSnbBqxc3SkdxJrF+D1veN/WNynZYA==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.21.1.tgz", + "integrity": "sha512-3n+GgbAZYjaS/k0M03yQsQfR1APbr411r74foknnsGpmhNKBG49VuUkxIU6jORgvJPChoD4WC4PqoHImN1FP0w==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -5263,38 +5263,52 @@ "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.20.0.tgz", - "integrity": "sha512-DtqkP+hcZvPEbj8t8dK5df2b7d3B8GNauKqaddRRqQBBlgkbdhJkxhoJTrOowlS3vaRt2iMCkU0+CSNn0KhqAQ==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.21.1.tgz", + "integrity": "sha512-hGVTldUkIkTwoV8//hmnAAiAchi4oMEKD3aW5H2RrnI50tTdwza7VQbTTAyN3OIHWlK5DVg6xV7X8N/9dtOydA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.20.0", - "@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "@prisma/fetch-engine": "5.20.0", - "@prisma/get-platform": "5.20.0" + "@prisma/debug": "5.21.1", + "@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "@prisma/fetch-engine": "5.21.1", + "@prisma/get-platform": "5.21.1" } }, "node_modules/@prisma/engines-version": { - "version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284.tgz", - "integrity": "sha512-Lg8AS5lpi0auZe2Mn4gjuCg081UZf88k3cn0RCwHgR+6cyHHpttPZBElJTHf83ZGsRNAmVCZCfUGA57WB4u4JA==", + "version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36.tgz", + "integrity": "sha512-qvnEflL0//lh44S/T9NcvTMxfyowNeUxTunPcDfKPjyJNrCNf2F1zQLcUv5UHAruECpX+zz21CzsC7V2xAeM7Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines/node_modules/@prisma/debug": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.21.1.tgz", + "integrity": "sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==", "dev": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.20.0.tgz", - "integrity": "sha512-JVcaPXC940wOGpCOwuqQRTz6I9SaBK0c1BAyC1pcz9xBi+dzFgUu3G/p9GV1FhFs9OKpfSpIhQfUJE9y00zhqw==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.21.1.tgz", + "integrity": "sha512-70S31vgpCGcp9J+mh/wHtLCkVezLUqe/fGWk3J3JWZIN7prdYSlr1C0niaWUyNK2VflLXYi8kMjAmSxUVq6WGQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.20.0", - "@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284", - "@prisma/get-platform": "5.20.0" + "@prisma/debug": "5.21.1", + "@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", + "@prisma/get-platform": "5.21.1" } }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/debug": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.21.1.tgz", + "integrity": "sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@prisma/generator-helper": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-5.20.0.tgz", @@ -5305,15 +5319,22 @@ } }, "node_modules/@prisma/get-platform": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.20.0.tgz", - "integrity": "sha512-8/+CehTZZNzJlvuryRgc77hZCWrUDYd/PmlZ7p2yNXtmf2Una4BWnTbak3us6WVdqoz5wmptk6IhsXdG2v5fmA==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.21.1.tgz", + "integrity": "sha512-sRxjL3Igst3ct+e8ya/x//cDXmpLbZQ5vfps2N4tWl4VGKQAmym77C/IG/psSMsQKszc8uFC/q1dgmKFLUgXZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.20.0" + "@prisma/debug": "5.21.1" } }, + "node_modules/@prisma/get-platform/node_modules/@prisma/debug": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.21.1.tgz", + "integrity": "sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -14914,14 +14935,14 @@ } }, "node_modules/prisma": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.20.0.tgz", - "integrity": "sha512-6obb3ucKgAnsGS9x9gLOe8qa51XxvJ3vLQtmyf52CTey1Qcez3A6W6ROH5HIz5Q5bW+0VpmZb8WBohieMFGpig==", + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.21.1.tgz", + "integrity": "sha512-PB+Iqzld/uQBPaaw2UVIk84kb0ITsLajzsxzsadxxl54eaU5Gyl2/L02ysivHxK89t7YrfQJm+Ggk37uvM70oQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "5.20.0" + "@prisma/engines": "5.21.1" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index beb8da7..682d44d 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "prisma:reset": "npx prisma migrate reset --schema=./epess-database/prisma/schema.prisma", "prisma:seed": "npx prisma db seed --schema=./epess-database/prisma/schema.prisma", "prisma:format": "npx prisma format --schema=./epess-database/prisma/schema.prisma", + "prisma:studio": "npx prisma studio --schema=./epess-database/prisma/schema.prisma", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "prettier": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "test": "jest", @@ -53,7 +54,7 @@ "@pothos/plugin-scope-auth": "^4.1.0", "@pothos/plugin-simple-objects": "^4.1.0", "@pothos/plugin-smart-subscriptions": "^4.1.0", - "@prisma/client": "^5.20.0", + "@prisma/client": "^5.21.1", "@smatch-corp/nestjs-pothos": "^0.3.0", "@smatch-corp/nestjs-pothos-apollo-driver": "^0.1.0", "apollo-server-express": "^3.13.0", @@ -107,7 +108,7 @@ "globals": "^15.10.0", "jest": "^29.5.0", "prettier": "^3.0.0", - "prisma": "^5.20.0", + "prisma": "^5.21.1", "source-map-support": "^0.5.21", "supertest": "^7.0.0", "ts-jest": "^29.1.0", diff --git a/src/Center/center.schema.ts b/src/Center/center.schema.ts index be6bb55..b9a015a 100644 --- a/src/Center/center.schema.ts +++ b/src/Center/center.schema.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { Pothos, PothosRef, @@ -9,13 +9,14 @@ import { Builder } from '../Graphql/graphql.builder'; import { PrismaService } from '../Prisma/prisma.service'; import { MinioService } from '../Minio/minio.service'; import { CenterStatus } 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(); } @@ -189,6 +190,77 @@ export class CenterSchema extends PothosSchema { }); }, }), + approveOrRejectCenter: t.prismaField({ + type: this.center(), + description: 'Approve a center and promote centerstaff to staff', + args: { + centerId: t.arg({ + type: 'String', + required: true, + }), + approve: t.arg({ + type: 'Boolean', + required: true, + }), + }, + resolve: async (query, root, args, ctx, info) => { + 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.APPROVED || + center.centerStatus === CenterStatus.REJECTED + ) { + throw new Error('Center is already approved or rejected'); + } + // find center owner and promote to staff + const centerOwnerId = center.centerOwnerId; + if (!centerOwnerId) { + throw new Error('Center owner not found'); + } + const centerOwner = await prisma.user.findUnique({ + where: { + id: centerOwnerId, + }, + }); + if (!centerOwner) { + throw new Error('Center owner not found'); + } + const updatedCenter = await prisma.center.update({ + ...query, + where: { + id: args.centerId, + }, + data: { + centerStatus: args.approve + ? CenterStatus.APPROVED + : CenterStatus.REJECTED, + }, + }); + // mail to center owner + try { + await this.mailService.sendEmail( + centerOwner.email, + args.approve + ? 'Your center has been approved' + : 'Your center has been rejected', + args.approve ? 'center-approved' : 'center-rejected', + ); + } catch (error) { + Logger.error(error, 'CenterSchema'); + } + return updatedCenter; + }); + }, + }), })); } } diff --git a/src/Mail/mail.service.ts b/src/Mail/mail.service.ts index a569b3f..df530a8 100644 --- a/src/Mail/mail.service.ts +++ b/src/Mail/mail.service.ts @@ -13,17 +13,15 @@ export class MailService { async sendEmail(to: string, subject: string, text: string) { try { - const mailContent = - await this.openaiService.generateInvitationMailContent( - to, - 'John Doe', - 'https://epess.org', - ); + const mailContent = ` +

${subject}

+

${text}

+ `; const result = await this.mailerService.sendMail({ to, subject, - text: mailContent ?? text, + text: mailContent ?? text, }); Logger.log(result, 'MailService'); } catch (error) { diff --git a/src/Mail/templates/StaffInvitation.pug b/src/Mail/templates/StaffInvitation.pug new file mode 100644 index 0000000..9bc101f --- /dev/null +++ b/src/Mail/templates/StaffInvitation.pug @@ -0,0 +1,71 @@ +doctype html +html + head + meta(charset="UTF-8") + title Thư mời làm việc từ Trung tâm [center_name] + style. + body { + font-family: Arial, sans-serif; + background-color: #f0f8ff; + color: #333; + margin: 0; + padding: 0; + } + .container { + max-width: 600px; + margin: 20px auto; + padding: 20px; + background-color: #ffffff; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + .header { + text-align: center; + background-color: #457D84; /* Medium teal */ + color: #ffffff; + padding: 15px; + border-radius: 8px 8px 0 0; + } + .header h1 { + margin: 0; + font-size: 24px; + } + .content { + padding: 20px; + color: #333; + } + .content p { + font-size: 16px; + line-height: 1.5; + } + .button { + display: inline-block; + padding: 12px 20px; + background-color: #2BD4E2; /* Bright aqua */ + color: #ffffff; + text-decoration: none; + font-size: 16px; + border-radius: 5px; + text-align: center; + margin: 20px 0; + } + .footer { + text-align: center; + font-size: 14px; + color: #555; + padding: 10px; + border-top: 1px solid #e0e0e0; + } + body + .container + .header + h1 Thư mời làm việc từ Trung tâm [center_name] + .content + p Chào bạn, + p Chúng tôi rất vui mừng thông báo rằng bạn đã được mời làm nhân viên tại trung tâm [center_name]. + p Để tiếp tục quá trình ứng tuyển, vui lòng nhấn vào nút dưới đây để truy cập vào trang nộp resume của bạn. + a.button(href="http://localhost:3000/upload-cv?centerId=[center_id]") Nộp Resume + p Nếu bạn có bất kỳ thắc mắc nào, đừng ngần ngại liên hệ với chúng tôi. + .footer + p Trân trọng, + p EPESS