import { Inject, Injectable, Logger } from '@nestjs/common' import { OrderStatus, Role, ScheduleStatus } from '@prisma/client' import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos' import { Builder } from '../Graphql/graphql.builder' import { PrismaService } from '../Prisma/prisma.service' @Injectable() export class ServiceFeedbackSchema extends PothosSchema { constructor( @Inject(SchemaBuilderToken) private readonly builder: Builder, private readonly prisma: PrismaService, ) { super() } @PothosRef() serviceFeedback() { return this.builder.prismaObject('ServiceFeedback', { description: 'A feedback for a service.', fields: (t) => ({ id: t.exposeID('id', { description: 'The ID of the service feedback.', }), userId: t.exposeID('userId', { description: 'The ID of the user who provided the feedback.', }), serviceId: t.exposeID('serviceId', { description: 'The ID of the service that was provided.', }), rating: t.exposeFloat('rating', { description: 'The rating of the service.', }), comments: t.exposeString('comments', { description: 'The comments of the service feedback.', }), createdAt: t.expose('createdAt', { type: 'DateTime', nullable: true, description: 'The date and time the service feedback was created.', }), updatedAt: t.expose('updatedAt', { type: 'DateTime', nullable: true, description: 'The date and time the service feedback was updated.', }), user: t.relation('user', { description: 'The user who provided the feedback.', }), service: t.relation('service', { description: 'The service that was provided.', }), }), }) } @Pothos() init(): void { this.builder.queryFields((t) => ({ serviceFeedbacks: t.prismaField({ type: [this.serviceFeedback()], args: this.builder.generator.findManyArgs('ServiceFeedback'), description: 'Retrieve a list of service feedbacks with optional filtering, ordering, and pagination.', resolve: async (query, _root, args, _ctx, _info) => { return await this.prisma.serviceFeedback.findMany({ ...query, skip: args.skip ?? undefined, take: args.take ?? undefined, orderBy: args.orderBy ?? undefined, where: args.filter ?? undefined, }) }, }), })) this.builder.mutationFields((t) => ({ createServiceFeedback: t.prismaField({ type: this.serviceFeedback(), args: { orderId: t.arg({ type: 'String', required: true, }), rating: t.arg({ type: 'Float', required: true, }), comments: t.arg({ type: 'String', required: false, }), }, description: 'Create a new service feedback.', resolve: async (_, _root, args, ctx, _info) => { if (ctx.isSubscription) { throw new Error('Not allowed') } if (!ctx.http?.me) { throw new Error('Unauthorized') } // allow only when user is CUSTOMER and order is completed if (ctx.http?.me?.role !== Role.CUSTOMER) { throw new Error('Unauthorized') } Logger.log(`args: ${JSON.stringify(args)}`) const order = await this.prisma.order.findFirst({ where: { id: args.orderId, userId: ctx.http?.me?.id, status: OrderStatus.PAID, schedule: { dates: { every: { status: ScheduleStatus.COMPLETED, }, }, }, }, }) if (!order) { throw new Error('Order not found') } if (order.userId !== ctx.http?.me?.id) { throw new Error('Unauthorized') } if (order.status !== OrderStatus.PAID) { throw new Error('Order not completed') } // validate rating if (args.rating < 0 || args.rating > 5) { throw new Error('Invalid rating') } // validate comments if (args.comments && args.comments.length > 1024) { throw new Error('Comments too long') } return await this.prisma.serviceFeedback.create({ data: { userId: ctx.http?.me?.id, serviceId: order.serviceId, rating: args.rating, comments: args.comments, }, }) }, }), })) } }