refactor: standardize formatting and improve readability in AnalyticSchema
- Reformatted import statements and code structure for consistency and clarity across the AnalyticSchema. - Enhanced field descriptions to ensure uniformity and better understanding of the analytics data. - Updated error messages for improved user feedback during analytic data retrieval. - Improved organization of query fields for better maintainability and readability. These changes aim to enhance code readability, maintainability, and user experience within the analytic features.
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
import { Inject, Injectable } from '@nestjs/common'
|
||||
import { OrderStatus, Prisma, Role, ServiceStatus } from '@prisma/client'
|
||||
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
|
||||
import { CenterSchema } from 'src/Center/center.schema'
|
||||
import { Builder } from 'src/Graphql/graphql.builder'
|
||||
import { OrderSchema } from 'src/Order/order.schema'
|
||||
import { PrismaService } from 'src/Prisma/prisma.service'
|
||||
import { ServiceSchema } from 'src/Service/service.schema'
|
||||
import { DateTimeUtils } from 'src/common/utils/datetime.utils'
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { OrderStatus, Prisma, Role, ServiceStatus } from "@prisma/client";
|
||||
import {
|
||||
Pothos,
|
||||
PothosRef,
|
||||
PothosSchema,
|
||||
SchemaBuilderToken,
|
||||
} from "@smatch-corp/nestjs-pothos";
|
||||
import { CenterSchema } from "src/Center/center.schema";
|
||||
import { Builder } from "src/Graphql/graphql.builder";
|
||||
import { OrderSchema } from "src/Order/order.schema";
|
||||
import { PrismaService } from "src/Prisma/prisma.service";
|
||||
import { ServiceSchema } from "src/Service/service.schema";
|
||||
import { DateTimeUtils } from "src/common/utils/datetime.utils";
|
||||
@Injectable()
|
||||
export class AnalyticSchema extends PothosSchema {
|
||||
constructor(
|
||||
@@ -14,154 +19,154 @@ export class AnalyticSchema extends PothosSchema {
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly serviceSchema: ServiceSchema,
|
||||
private readonly centerSchema: CenterSchema,
|
||||
private readonly orderSchema: OrderSchema,
|
||||
private readonly orderSchema: OrderSchema
|
||||
) {
|
||||
super()
|
||||
super();
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
customerAnalytic() {
|
||||
return this.builder.simpleObject('CustomerAnalytic', {
|
||||
description: 'A customer analytic in the system.',
|
||||
return this.builder.simpleObject("CustomerAnalytic", {
|
||||
description: "A customer analytic in the system.",
|
||||
fields: (t) => ({
|
||||
userId: t.string({
|
||||
description: 'The ID of the user.',
|
||||
description: "The ID of the user.",
|
||||
}),
|
||||
activeServiceCount: t.int({
|
||||
description: 'The number of active services.',
|
||||
description: "The number of active services.",
|
||||
}),
|
||||
totalServiceCount: t.int({
|
||||
description: 'The total number of services.',
|
||||
description: "The total number of services.",
|
||||
}),
|
||||
totalSpent: t.float({
|
||||
description: 'The total amount spent.',
|
||||
description: "The total amount spent.",
|
||||
}),
|
||||
updatedAt: t.field({
|
||||
type: 'DateTime',
|
||||
description: 'The date the analytic was last updated.',
|
||||
type: "DateTime",
|
||||
description: "The date the analytic was last updated.",
|
||||
}),
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
mentorAnalytic() {
|
||||
return this.builder.simpleObject('MentorAnalytic', {
|
||||
description: 'A mentor analytic in the system.',
|
||||
return this.builder.simpleObject("MentorAnalytic", {
|
||||
description: "A mentor analytic in the system.",
|
||||
fields: (t) => ({
|
||||
userId: t.string({
|
||||
description: 'The ID of the mentor.',
|
||||
description: "The ID of the mentor.",
|
||||
}),
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
centerAnalytic() {
|
||||
return this.builder.simpleObject('CenterAnalytic', {
|
||||
description: 'A center analytic in the system.',
|
||||
return this.builder.simpleObject("CenterAnalytic", {
|
||||
description: "A center analytic in the system.",
|
||||
fields: (t) => ({
|
||||
centerId: t.string({
|
||||
description: 'The ID of the center.',
|
||||
description: "The ID of the center.",
|
||||
}),
|
||||
activeMentorCount: t.int({
|
||||
description: 'The number of active mentors.',
|
||||
description: "The number of active mentors.",
|
||||
}),
|
||||
activeServiceCount: t.int({
|
||||
description: 'The number of active services.',
|
||||
description: "The number of active services.",
|
||||
}),
|
||||
totalServiceCount: t.int({
|
||||
description: 'The total number of services.',
|
||||
description: "The total number of services.",
|
||||
}),
|
||||
revenue: t.int({
|
||||
description: 'The total revenue.',
|
||||
description: "The total revenue.",
|
||||
}),
|
||||
rating: t.float({
|
||||
description: 'The average rating.',
|
||||
description: "The average rating.",
|
||||
nullable: true,
|
||||
}),
|
||||
updatedAt: t.field({
|
||||
type: 'DateTime',
|
||||
description: 'The date the analytic was last updated.',
|
||||
type: "DateTime",
|
||||
description: "The date the analytic was last updated.",
|
||||
}),
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
platformAnalytic() {
|
||||
return this.builder.simpleObject('PlatformAnalytic', {
|
||||
description: 'A platform analytic in the system.',
|
||||
return this.builder.simpleObject("PlatformAnalytic", {
|
||||
description: "A platform analytic in the system.",
|
||||
fields: (t) => ({
|
||||
topServices: t.field({
|
||||
type: [this.serviceSchema.service()],
|
||||
description: 'The top services by revenue.',
|
||||
description: "The top services by revenue.",
|
||||
}),
|
||||
topCenters: t.field({
|
||||
type: [this.centerSchema.center()],
|
||||
description: 'The top centers by revenue.',
|
||||
description: "The top centers by revenue.",
|
||||
}),
|
||||
pendingRefunds: t.field({
|
||||
type: [this.orderSchema.order()],
|
||||
description: 'The pending refunds.',
|
||||
description: "The pending refunds.",
|
||||
}),
|
||||
activeCenterCount: t.int({
|
||||
description: 'The number of active centers.',
|
||||
description: "The number of active centers.",
|
||||
}),
|
||||
totalCenterCount: t.int({
|
||||
description: 'The total number of centers.',
|
||||
description: "The total number of centers.",
|
||||
}),
|
||||
totalUserCount: t.int({
|
||||
description: 'The total number of users.',
|
||||
description: "The total number of users.",
|
||||
}),
|
||||
activeMentorCount: t.int({
|
||||
description: 'The number of active mentors.',
|
||||
description: "The number of active mentors.",
|
||||
}),
|
||||
totalMentorCount: t.int({
|
||||
description: 'The total number of mentors.',
|
||||
description: "The total number of mentors.",
|
||||
}),
|
||||
totalServiceCount: t.int({
|
||||
description: 'The total number of services.',
|
||||
description: "The total number of services.",
|
||||
}),
|
||||
totalWorkshopCount: t.int({
|
||||
description: 'The total number of workshops.',
|
||||
description: "The total number of workshops.",
|
||||
}),
|
||||
revenue: t.int({
|
||||
description: 'The total revenue.',
|
||||
description: "The total revenue.",
|
||||
}),
|
||||
approvedServiceCount: t.int({
|
||||
description: 'The number of approved services.',
|
||||
description: "The number of approved services.",
|
||||
}),
|
||||
rejectedServiceCount: t.int({
|
||||
description: 'The number of rejected services.',
|
||||
description: "The number of rejected services.",
|
||||
}),
|
||||
updatedAt: t.field({
|
||||
type: 'DateTime',
|
||||
description: 'The date the analytic was last updated.',
|
||||
type: "DateTime",
|
||||
description: "The date the analytic was last updated.",
|
||||
}),
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
timeframes() {
|
||||
return this.builder.enumType('Timeframe', {
|
||||
values: ['day', 'week', 'month', 'year'],
|
||||
})
|
||||
return this.builder.enumType("Timeframe", {
|
||||
values: ["day", "week", "month", "year"],
|
||||
});
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
serviceSortBy() {
|
||||
return this.builder.enumType('ServiceSortBy', {
|
||||
values: ['order', 'rating'],
|
||||
})
|
||||
return this.builder.enumType("ServiceSortBy", {
|
||||
values: ["order", "rating"],
|
||||
});
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
centerSortBy() {
|
||||
return this.builder.enumType('CenterSortBy', {
|
||||
values: ['revenue', 'rating', 'services'],
|
||||
})
|
||||
return this.builder.enumType("CenterSortBy", {
|
||||
values: ["revenue", "rating", "services"],
|
||||
});
|
||||
}
|
||||
|
||||
@Pothos()
|
||||
@@ -169,13 +174,13 @@ export class AnalyticSchema extends PothosSchema {
|
||||
this.builder.queryFields((t) => ({
|
||||
customerAnalytic: t.field({
|
||||
type: this.customerAnalytic(),
|
||||
description: 'Retrieve a single customer analytic.',
|
||||
description: "Retrieve a single customer analytic.",
|
||||
resolve: async (_parent, _args, ctx, _info) => {
|
||||
if (!ctx.me) {
|
||||
throw new Error('Unauthorized')
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
if (ctx.me.role !== Role.CUSTOMER) {
|
||||
throw new Error('Only customers can access this data')
|
||||
throw new Error("Only customers can access this data");
|
||||
}
|
||||
// calculate analytic
|
||||
const activeServiceCount = await this.prisma.order.count({
|
||||
@@ -192,12 +197,12 @@ export class AnalyticSchema extends PothosSchema {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
const totalServiceCount = await this.prisma.order.count({
|
||||
where: {
|
||||
userId: ctx.me.id,
|
||||
},
|
||||
})
|
||||
});
|
||||
const totalSpent = await this.prisma.order.aggregate({
|
||||
where: {
|
||||
userId: ctx.me.id,
|
||||
@@ -206,50 +211,50 @@ export class AnalyticSchema extends PothosSchema {
|
||||
_sum: {
|
||||
total: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
return {
|
||||
userId: ctx.me.id,
|
||||
activeServiceCount: activeServiceCount,
|
||||
totalServiceCount: totalServiceCount,
|
||||
totalSpent: totalSpent._sum.total,
|
||||
updatedAt: DateTimeUtils.now(),
|
||||
}
|
||||
};
|
||||
},
|
||||
}),
|
||||
mentorAnalytic: t.field({
|
||||
type: this.mentorAnalytic(),
|
||||
description: 'Retrieve a single mentor analytic.',
|
||||
description: "Retrieve a single mentor analytic.",
|
||||
resolve: async (_parent, _args, ctx, _info) => {
|
||||
if (!ctx.me) {
|
||||
throw new Error('Unauthorized')
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
if (ctx.me.role !== Role.CENTER_MENTOR) {
|
||||
throw new Error('Only center mentors can access this data')
|
||||
throw new Error("Only center mentors can access this data");
|
||||
}
|
||||
// calculate analytic
|
||||
return {
|
||||
userId: ctx.me.id,
|
||||
}
|
||||
};
|
||||
},
|
||||
}),
|
||||
centerAnalytic: t.field({
|
||||
type: this.centerAnalytic(),
|
||||
description: 'Retrieve a single center analytic.',
|
||||
description: "Retrieve a single center analytic.",
|
||||
resolve: async (_parent, _args, ctx, _info) => {
|
||||
if (!ctx.me) {
|
||||
throw new Error('Unauthorized')
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
if (ctx.me.role !== Role.CENTER_OWNER) {
|
||||
throw new Error('Only center owners can access this data')
|
||||
throw new Error("Only center owners can access this data");
|
||||
}
|
||||
// get center by owner id
|
||||
const center = await this.prisma.center.findUnique({
|
||||
where: {
|
||||
centerOwnerId: ctx.me.id,
|
||||
},
|
||||
})
|
||||
});
|
||||
if (!center) {
|
||||
throw new Error('Center not found')
|
||||
throw new Error("Center not found");
|
||||
}
|
||||
// calculate analytic
|
||||
|
||||
@@ -261,39 +266,40 @@ export class AnalyticSchema extends PothosSchema {
|
||||
},
|
||||
banned: false,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const activeServiceCount = await this.prisma.service.count({
|
||||
where: {
|
||||
centerId: center.id,
|
||||
status: ServiceStatus.APPROVED,
|
||||
},
|
||||
})
|
||||
});
|
||||
const totalServiceCount = await this.prisma.service.count({
|
||||
where: {
|
||||
centerId: center.id,
|
||||
},
|
||||
})
|
||||
});
|
||||
// calculate revenue from orders of services in the center and factor in commission percentage
|
||||
// query all orders of services in the center and calculate actual revenue of each order
|
||||
// then sum up the revenue
|
||||
let revenue = 0
|
||||
let revenue = 0;
|
||||
const orders = await this.prisma.order.findMany({
|
||||
where: {
|
||||
service: { centerId: center.id },
|
||||
status: OrderStatus.PAID,
|
||||
},
|
||||
})
|
||||
});
|
||||
for (const order of orders) {
|
||||
const service = await this.prisma.service.findUnique({
|
||||
where: { id: order.serviceId },
|
||||
})
|
||||
});
|
||||
if (!service) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
const commission = service.commission
|
||||
const actualRevenue = (order.total || 0) - (order.total || 0) * commission
|
||||
revenue += actualRevenue
|
||||
const commission = service.commission;
|
||||
const actualRevenue =
|
||||
(order.total || 0) - (order.total || 0) * commission;
|
||||
revenue += actualRevenue;
|
||||
}
|
||||
return {
|
||||
centerId: center.id,
|
||||
@@ -302,40 +308,40 @@ export class AnalyticSchema extends PothosSchema {
|
||||
totalServiceCount: totalServiceCount,
|
||||
revenue: revenue,
|
||||
updatedAt: DateTimeUtils.now(),
|
||||
}
|
||||
};
|
||||
},
|
||||
}),
|
||||
platformAnalytic: t.field({
|
||||
type: this.platformAnalytic(),
|
||||
args: {
|
||||
take: t.arg({
|
||||
type: 'Int',
|
||||
description: 'The number of services to take.',
|
||||
type: "Int",
|
||||
description: "The number of services to take.",
|
||||
required: true,
|
||||
}),
|
||||
serviceSortBy: t.arg({
|
||||
type: this.serviceSortBy(),
|
||||
description: 'The field to sort by.',
|
||||
description: "The field to sort by.",
|
||||
required: true,
|
||||
}),
|
||||
centerSortBy: t.arg({
|
||||
type: this.centerSortBy(),
|
||||
description: 'The field to sort by.',
|
||||
description: "The field to sort by.",
|
||||
required: true,
|
||||
}),
|
||||
timeframes: t.arg({
|
||||
type: this.timeframes(),
|
||||
description: 'The frame of time Eg day, week, month, year.',
|
||||
description: "The frame of time Eg day, week, month, year.",
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
description: 'Retrieve a single platform analytic.',
|
||||
description: "Retrieve a single platform analytic.",
|
||||
resolve: async (_parent, args, ctx, _info) => {
|
||||
if (!ctx.me) {
|
||||
throw new Error('Unauthorized')
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
if (ctx.me.role !== Role.ADMIN && ctx.me.role !== Role.MODERATOR) {
|
||||
throw new Error('Only admins and moderators can access this data')
|
||||
throw new Error("Only admins and moderators can access this data");
|
||||
}
|
||||
// calculate analytic for services sorted by args.serviceSortBy and args.timeframes
|
||||
const topServices = await this.prisma.service.findMany({
|
||||
@@ -348,7 +354,7 @@ export class AnalyticSchema extends PothosSchema {
|
||||
},
|
||||
},
|
||||
take: args.take,
|
||||
})
|
||||
});
|
||||
// get top centers by args.centerSortBy
|
||||
const topCenters = await this.prisma.center.findMany({
|
||||
orderBy: {
|
||||
@@ -357,13 +363,13 @@ export class AnalyticSchema extends PothosSchema {
|
||||
},
|
||||
},
|
||||
take: args.take,
|
||||
})
|
||||
});
|
||||
// get pending refunds
|
||||
const pendingRefunds = await this.prisma.order.findMany({
|
||||
where: {
|
||||
status: OrderStatus.PENDING_REFUND,
|
||||
},
|
||||
})
|
||||
});
|
||||
// get active center count by center owner not banned and have schedule with dates in the future
|
||||
const activeCenterCount = await this.prisma.center.count({
|
||||
where: {
|
||||
@@ -390,47 +396,49 @@ export class AnalyticSchema extends PothosSchema {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
// get total center count
|
||||
const totalCenterCount = await this.prisma.center.count()
|
||||
const totalCenterCount = await this.prisma.center.count();
|
||||
// get total user count
|
||||
const totalUserCount = await this.prisma.user.count()
|
||||
const totalUserCount = await this.prisma.user.count();
|
||||
// get active mentor count
|
||||
const activeMentorCount = await this.prisma.user.count({
|
||||
where: {
|
||||
role: Role.CENTER_MENTOR,
|
||||
banned: false,
|
||||
},
|
||||
})
|
||||
});
|
||||
// get total mentor count
|
||||
const totalMentorCount = await this.prisma.user.count({
|
||||
where: {
|
||||
role: Role.CENTER_MENTOR,
|
||||
},
|
||||
})
|
||||
});
|
||||
// get approved service count
|
||||
const approvedServiceCount = await this.prisma.service.count({
|
||||
where: {
|
||||
status: ServiceStatus.APPROVED,
|
||||
},
|
||||
})
|
||||
});
|
||||
// get rejected service count
|
||||
const rejectedServiceCount = await this.prisma.service.count({
|
||||
where: {
|
||||
status: ServiceStatus.REJECTED,
|
||||
},
|
||||
})
|
||||
});
|
||||
// get total workshop count
|
||||
const totalWorkshopCount = await this.prisma.workshop.count()
|
||||
const totalWorkshopCount = await this.prisma.workshop.count();
|
||||
// get total order count
|
||||
const totalOrderCount = await this.prisma.order.count()
|
||||
const totalOrderCount = await this.prisma.order.count();
|
||||
// get total service count
|
||||
const totalServiceCount = await this.prisma.service.count()
|
||||
const totalServiceCount = await this.prisma.service.count();
|
||||
// get revenue
|
||||
let revenue = 0
|
||||
let revenue = 0;
|
||||
// query all orders of services in all centers in the past args.timeframes and calculate actual revenue of each order by convert commission percentage to float
|
||||
// convert args.timeframes to number of days
|
||||
const timeframes = DateTimeUtils.subtractDaysFromTimeframe(args.timeframes)
|
||||
const timeframes = DateTimeUtils.subtractDaysFromTimeframe(
|
||||
args.timeframes
|
||||
);
|
||||
const orders = await this.prisma.order.findMany({
|
||||
where: {
|
||||
status: OrderStatus.PAID,
|
||||
@@ -438,17 +446,20 @@ export class AnalyticSchema extends PothosSchema {
|
||||
gte: timeframes.toJSDate(),
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
for (const order of orders) {
|
||||
const service = await this.prisma.service.findUnique({
|
||||
where: { id: order.serviceId },
|
||||
})
|
||||
});
|
||||
if (!service) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
const orderTotal = Number(order.total ?? 0);
|
||||
const commission = Number(service.commission ?? 0);
|
||||
const actualRevenue = orderTotal * (1 - commission);
|
||||
if (!isNaN(actualRevenue)) {
|
||||
revenue += actualRevenue;
|
||||
}
|
||||
const commission = service.commission
|
||||
const actualRevenue = (order.total || 0) - (order.total || 0) * commission
|
||||
revenue += actualRevenue
|
||||
}
|
||||
// return analytic
|
||||
return {
|
||||
@@ -467,9 +478,9 @@ export class AnalyticSchema extends PothosSchema {
|
||||
totalOrderCount: totalOrderCount,
|
||||
totalServiceCount: totalServiceCount,
|
||||
updatedAt: DateTimeUtils.now(),
|
||||
}
|
||||
};
|
||||
},
|
||||
}),
|
||||
}))
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user