fix: including Prisma.sql for Prisma v6 upgrade (#23775)
This commit is contained in:
@@ -12,8 +12,8 @@ import {
|
||||
isNumberFilterValue,
|
||||
} from "@calcom/features/data-table/lib/utils";
|
||||
import type { DateRange } from "@calcom/features/insights/server/insightsDateUtils";
|
||||
import { Prisma } from "@calcom/prisma/client";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
import { Prisma } from "@calcom/prisma/client";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { MembershipRepository } from "../repository/membership";
|
||||
@@ -168,12 +168,7 @@ export class InsightsBookingBaseService {
|
||||
async getBookingsByHourStats({ timeZone }: { timeZone: string }) {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
const results = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
hour: string;
|
||||
count: number;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
SELECT
|
||||
EXTRACT(HOUR FROM ("startTime" AT TIME ZONE 'UTC' AT TIME ZONE ${timeZone}))::int as "hour",
|
||||
COUNT(*)::int as "count"
|
||||
@@ -184,6 +179,13 @@ export class InsightsBookingBaseService {
|
||||
ORDER BY 1
|
||||
`;
|
||||
|
||||
const results = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
hour: string;
|
||||
count: number;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
// Create a map of results by hour for easy lookup
|
||||
const resultsMap = new Map(results.map((row) => [Number(row.hour), row.count]));
|
||||
|
||||
@@ -216,11 +218,13 @@ export class InsightsBookingBaseService {
|
||||
}
|
||||
}
|
||||
|
||||
return await this.prisma.$queryRaw<Array<SelectedFields<TSelect>>>`
|
||||
const query = Prisma.sql`
|
||||
SELECT ${selectFields}
|
||||
FROM "BookingTimeStatusDenormalized"
|
||||
WHERE ${baseConditions}
|
||||
`;
|
||||
|
||||
return await this.prisma.$queryRaw<Array<SelectedFields<TSelect>>>(query);
|
||||
}
|
||||
|
||||
async getBaseConditions(): Promise<Prisma.Sql> {
|
||||
@@ -449,33 +453,17 @@ export class InsightsBookingBaseService {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
// Get total count first
|
||||
const totalCountResult = await this.prisma.$queryRaw<[{ count: number }]>`
|
||||
const totalCountQuery = Prisma.sql`
|
||||
SELECT COUNT(*)::int as count
|
||||
FROM "BookingTimeStatusDenormalized"
|
||||
WHERE ${baseConditions}
|
||||
`;
|
||||
|
||||
const totalCountResult = await this.prisma.$queryRaw<[{ count: number }]>(totalCountQuery);
|
||||
const totalCount = totalCountResult[0]?.count || 0;
|
||||
|
||||
// 1. Get booking data from BookingTimeStatusDenormalized
|
||||
const csvData = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
id: number;
|
||||
uid: string | null;
|
||||
title: string;
|
||||
createdAt: Date;
|
||||
timeStatus: string;
|
||||
eventTypeId: number | null;
|
||||
eventLength: number;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
paid: boolean;
|
||||
userEmail: string;
|
||||
userUsername: string;
|
||||
rating: number | null;
|
||||
ratingFeedback: string | null;
|
||||
noShowHost: boolean;
|
||||
}>
|
||||
>`
|
||||
const csvDataQuery = Prisma.sql`
|
||||
SELECT
|
||||
"id",
|
||||
"uid",
|
||||
@@ -499,6 +487,26 @@ export class InsightsBookingBaseService {
|
||||
OFFSET ${offset}
|
||||
`;
|
||||
|
||||
const csvData = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
id: number;
|
||||
uid: string | null;
|
||||
title: string;
|
||||
createdAt: Date;
|
||||
timeStatus: string;
|
||||
eventTypeId: number | null;
|
||||
eventLength: number;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
paid: boolean;
|
||||
userEmail: string;
|
||||
userUsername: string;
|
||||
rating: number | null;
|
||||
ratingFeedback: string | null;
|
||||
noShowHost: boolean;
|
||||
}>
|
||||
>(csvDataQuery);
|
||||
|
||||
if (csvData.length === 0) {
|
||||
return { data: csvData, total: totalCount };
|
||||
}
|
||||
@@ -641,15 +649,7 @@ export class InsightsBookingBaseService {
|
||||
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
const data = await this.prisma.$queryRaw<
|
||||
{
|
||||
date: Date;
|
||||
bookingsCount: number;
|
||||
timeStatus: string;
|
||||
noShowHost: boolean;
|
||||
noShowGuests: number;
|
||||
}[]
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
WITH booking_stats AS (
|
||||
SELECT
|
||||
DATE("createdAt" AT TIME ZONE ${timeZone}) as "date",
|
||||
@@ -688,6 +688,16 @@ export class InsightsBookingBaseService {
|
||||
ORDER BY bs."date"
|
||||
`;
|
||||
|
||||
const data = await this.prisma.$queryRaw<
|
||||
{
|
||||
date: Date;
|
||||
bookingsCount: number;
|
||||
timeStatus: string;
|
||||
noShowHost: boolean;
|
||||
noShowGuests: number;
|
||||
}[]
|
||||
>(query);
|
||||
|
||||
// Initialize aggregate object with zero counts for all date ranges
|
||||
const aggregate: {
|
||||
[date: string]: {
|
||||
@@ -777,12 +787,7 @@ export class InsightsBookingBaseService {
|
||||
async getPopularEventsStats() {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
const bookingsFromSelected = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
eventTypeId: number;
|
||||
count: number;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
SELECT
|
||||
"eventTypeId",
|
||||
COUNT(id)::int as count
|
||||
@@ -793,6 +798,13 @@ export class InsightsBookingBaseService {
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
const bookingsFromSelected = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
eventTypeId: number;
|
||||
count: number;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
const eventTypeIds = bookingsFromSelected.map((booking) => booking.eventTypeId);
|
||||
|
||||
if (eventTypeIds.length === 0) {
|
||||
@@ -866,12 +878,7 @@ export class InsightsBookingBaseService {
|
||||
additionalCondition = Prisma.sql`AND status = 'accepted'`;
|
||||
}
|
||||
|
||||
const bookingsFromTeam = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number;
|
||||
count: number;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
SELECT
|
||||
"userId",
|
||||
COUNT(id)::int as count
|
||||
@@ -882,6 +889,13 @@ export class InsightsBookingBaseService {
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
const bookingsFromTeam = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number;
|
||||
count: number;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
if (bookingsFromTeam.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -927,12 +941,7 @@ export class InsightsBookingBaseService {
|
||||
async getMembersRatingStats(sortOrder: "ASC" | "DESC" = "DESC"): Promise<UserStatsData> {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
const bookingsFromTeam = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number;
|
||||
count: number;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
SELECT
|
||||
"userId",
|
||||
AVG("rating")::float as "count"
|
||||
@@ -943,6 +952,13 @@ export class InsightsBookingBaseService {
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
const bookingsFromTeam = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number;
|
||||
count: number;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
if (bookingsFromTeam.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -988,13 +1004,7 @@ export class InsightsBookingBaseService {
|
||||
async getRecentRatingsStats() {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
const bookingsFromTeam = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number | null;
|
||||
rating: number | null;
|
||||
ratingFeedback: string | null;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
SELECT
|
||||
"userId",
|
||||
"rating",
|
||||
@@ -1005,6 +1015,14 @@ export class InsightsBookingBaseService {
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
const bookingsFromTeam = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number | null;
|
||||
rating: number | null;
|
||||
ratingFeedback: string | null;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
if (bookingsFromTeam.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -1062,19 +1080,7 @@ export class InsightsBookingBaseService {
|
||||
async getBookingStats() {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
const stats = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
total_bookings: bigint;
|
||||
completed_bookings: bigint;
|
||||
rescheduled_bookings: bigint;
|
||||
cancelled_bookings: bigint;
|
||||
no_show_host_bookings: bigint;
|
||||
avg_rating: number | null;
|
||||
total_ratings: bigint;
|
||||
ratings_above_3: bigint;
|
||||
no_show_guests: bigint;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
WITH booking_stats AS (
|
||||
SELECT
|
||||
COUNT(*) as total_bookings,
|
||||
@@ -1107,6 +1113,20 @@ export class InsightsBookingBaseService {
|
||||
FROM booking_stats bs, guest_stats gs
|
||||
`;
|
||||
|
||||
const stats = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
total_bookings: bigint;
|
||||
completed_bookings: bigint;
|
||||
rescheduled_bookings: bigint;
|
||||
cancelled_bookings: bigint;
|
||||
no_show_host_bookings: bigint;
|
||||
avg_rating: number | null;
|
||||
total_ratings: bigint;
|
||||
ratings_above_3: bigint;
|
||||
no_show_guests: bigint;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
const rawStats = stats[0];
|
||||
return rawStats
|
||||
? {
|
||||
@@ -1136,15 +1156,7 @@ export class InsightsBookingBaseService {
|
||||
async getRecentNoShowGuests() {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
const recentNoShowBookings = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
bookingId: number;
|
||||
startTime: Date;
|
||||
eventTypeName: string;
|
||||
guestName: string;
|
||||
guestEmail: string;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
WITH booking_attendee_stats AS (
|
||||
SELECT
|
||||
b.id as booking_id,
|
||||
@@ -1182,6 +1194,16 @@ export class InsightsBookingBaseService {
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
const recentNoShowBookings = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
bookingId: number;
|
||||
startTime: Date;
|
||||
eventTypeName: string;
|
||||
guestName: string;
|
||||
guestEmail: string;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
return recentNoShowBookings;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,14 +183,7 @@ export class InsightsRoutingBaseService {
|
||||
.reduce((acc, curr) => Prisma.sql`${acc} ${curr}`);
|
||||
|
||||
// Single query to get all data grouped by date ranges using CTE
|
||||
const results = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
dateRange: string | null;
|
||||
totalSubmissions: bigint;
|
||||
successfulRoutings: bigint;
|
||||
acceptedBookings: bigint;
|
||||
}>
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
WITH date_ranged_data AS (
|
||||
SELECT
|
||||
CASE ${caseStatements}
|
||||
@@ -212,6 +205,15 @@ export class InsightsRoutingBaseService {
|
||||
ORDER BY "dateRange"
|
||||
`;
|
||||
|
||||
const results = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
dateRange: string | null;
|
||||
totalSubmissions: bigint;
|
||||
successfulRoutings: bigint;
|
||||
acceptedBookings: bigint;
|
||||
}>
|
||||
>(query);
|
||||
|
||||
// Create a map of results by dateRange for easy lookup
|
||||
const resultsMap = new Map(
|
||||
results
|
||||
@@ -265,16 +267,18 @@ export class InsightsRoutingBaseService {
|
||||
const orderByClause = this.buildOrderByClause(sorting);
|
||||
|
||||
// Get total count
|
||||
const totalCountResult = await this.prisma.$queryRaw<Array<{ count: bigint }>>`
|
||||
const totalCountQuery = Prisma.sql`
|
||||
SELECT COUNT(*) as count
|
||||
FROM "RoutingFormResponseDenormalized" rfrd
|
||||
WHERE ${baseConditions}
|
||||
`;
|
||||
|
||||
const totalCountResult = await this.prisma.$queryRaw<Array<{ count: bigint }>>(totalCountQuery);
|
||||
|
||||
const totalCount = Number(totalCountResult[0]?.count || 0);
|
||||
|
||||
// Get paginated data with JSON aggregation for attendees and fields
|
||||
const data = await this.prisma.$queryRaw<Array<InsightsRoutingTableItem>>`
|
||||
const dataQuery = Prisma.sql`
|
||||
SELECT
|
||||
rfrd."id",
|
||||
rfrd."uuid",
|
||||
@@ -341,6 +345,8 @@ export class InsightsRoutingBaseService {
|
||||
OFFSET ${offset}
|
||||
`;
|
||||
|
||||
const data = await this.prisma.$queryRaw<Array<InsightsRoutingTableItem>>(dataQuery);
|
||||
|
||||
return {
|
||||
total: totalCount,
|
||||
data,
|
||||
@@ -359,19 +365,25 @@ export class InsightsRoutingBaseService {
|
||||
}
|
||||
|
||||
// Get total count
|
||||
const totalResult = await this.prisma.$queryRaw<Array<{ count: bigint }>>`
|
||||
const totalQuery = Prisma.sql`
|
||||
SELECT COUNT(*) as count
|
||||
FROM "RoutingFormResponseDenormalized" rfrd
|
||||
WHERE ${baseConditions}
|
||||
`;
|
||||
|
||||
const totalResult = await this.prisma.$queryRaw<Array<{ count: bigint }>>(totalQuery);
|
||||
|
||||
// Get total without booking count
|
||||
const totalWithoutBookingResult = await this.prisma.$queryRaw<Array<{ count: bigint }>>`
|
||||
const totalWithoutBookingQuery = Prisma.sql`
|
||||
SELECT COUNT(*) as count
|
||||
FROM "RoutingFormResponseDenormalized" rfrd
|
||||
WHERE (${baseConditions}) AND ("bookingUid" IS NULL)
|
||||
`;
|
||||
|
||||
const totalWithoutBookingResult = await this.prisma.$queryRaw<Array<{ count: bigint }>>(
|
||||
totalWithoutBookingQuery
|
||||
);
|
||||
|
||||
const total = Number(totalResult[0]?.count || 0);
|
||||
const totalWithoutBooking = Number(totalWithoutBookingResult[0]?.count || 0);
|
||||
|
||||
@@ -437,14 +449,7 @@ export class InsightsRoutingBaseService {
|
||||
: Prisma.sql`1 = 1`;
|
||||
|
||||
// Get users who have been routed to during the period
|
||||
const usersQuery = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
id: number;
|
||||
name: string | null;
|
||||
email: string | null;
|
||||
avatarUrl: string | null;
|
||||
}>
|
||||
>`
|
||||
const usersQuerySql = Prisma.sql`
|
||||
SELECT DISTINCT ON ("bookingUserId")
|
||||
"bookingUserId" as id,
|
||||
"bookingUserName" as name,
|
||||
@@ -461,6 +466,15 @@ export class InsightsRoutingBaseService {
|
||||
${limit ? Prisma.sql`LIMIT ${limit}` : Prisma.empty}
|
||||
`;
|
||||
|
||||
const usersQuery = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
id: number;
|
||||
name: string | null;
|
||||
email: string | null;
|
||||
avatarUrl: string | null;
|
||||
}>
|
||||
>(usersQuerySql);
|
||||
|
||||
const users = usersQuery;
|
||||
|
||||
// Return early if no users found
|
||||
@@ -478,13 +492,7 @@ export class InsightsRoutingBaseService {
|
||||
}
|
||||
|
||||
// Get periods with pagination
|
||||
const periodStats = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number;
|
||||
period_start: Date;
|
||||
total: number;
|
||||
}>
|
||||
>`
|
||||
const periodStatsQuery = Prisma.sql`
|
||||
WITH RECURSIVE date_range AS (
|
||||
SELECT date_trunc(${dayjsPeriod}, ${startDate}::timestamp) as date
|
||||
UNION ALL
|
||||
@@ -545,13 +553,16 @@ export class InsightsRoutingBaseService {
|
||||
ORDER BY c.period_start ASC, c."userId" ASC
|
||||
`;
|
||||
|
||||
// Get statistics for the entire period for comparison
|
||||
const statsQuery = await this.prisma.$queryRaw<
|
||||
const periodStats = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number;
|
||||
total_bookings: number;
|
||||
period_start: Date;
|
||||
total: number;
|
||||
}>
|
||||
>`
|
||||
>(periodStatsQuery);
|
||||
|
||||
// Get statistics for the entire period for comparison
|
||||
const statsQuerySql = Prisma.sql`
|
||||
SELECT
|
||||
"bookingUserId" as "userId",
|
||||
COUNT(DISTINCT "bookingId")::integer as total_bookings
|
||||
@@ -564,6 +575,13 @@ export class InsightsRoutingBaseService {
|
||||
ORDER BY total_bookings ASC
|
||||
`;
|
||||
|
||||
const statsQuery = await this.prisma.$queryRaw<
|
||||
Array<{
|
||||
userId: number;
|
||||
total_bookings: number;
|
||||
}>
|
||||
>(statsQuerySql);
|
||||
|
||||
// Calculate average and median
|
||||
const average =
|
||||
statsQuery.reduce((sum, stat) => sum + Number(stat.total_bookings), 0) / statsQuery.length || 0;
|
||||
@@ -625,7 +643,10 @@ export class InsightsRoutingBaseService {
|
||||
if (!userStatsMap.has(userId)) {
|
||||
userStatsMap.set(userId, {});
|
||||
}
|
||||
userStatsMap.get(userId)![stat.period_start.toISOString()] = stat.total;
|
||||
const userStat = userStatsMap.get(userId);
|
||||
if (userStat) {
|
||||
userStat[stat.period_start.toISOString()] = stat.total;
|
||||
}
|
||||
});
|
||||
|
||||
return data.users.data.map((user) => {
|
||||
@@ -940,17 +961,7 @@ export class InsightsRoutingBaseService {
|
||||
const baseConditions = await this.getBaseConditions();
|
||||
|
||||
// Get failed bookings (responses without a successful booking) grouped by form, field, and option
|
||||
const result = await this.prisma.$queryRaw<
|
||||
{
|
||||
formId: string;
|
||||
formName: string;
|
||||
fieldId: string;
|
||||
fieldLabel: string;
|
||||
optionId: string;
|
||||
optionLabel: string;
|
||||
count: number;
|
||||
}[]
|
||||
>`
|
||||
const query = Prisma.sql`
|
||||
WITH form_fields AS (
|
||||
SELECT DISTINCT
|
||||
rfrd."formId" as form_id,
|
||||
@@ -997,6 +1008,18 @@ export class InsightsRoutingBaseService {
|
||||
ORDER BY count DESC
|
||||
`;
|
||||
|
||||
const result = await this.prisma.$queryRaw<
|
||||
{
|
||||
formId: string;
|
||||
formName: string;
|
||||
fieldId: string;
|
||||
fieldLabel: string;
|
||||
optionId: string;
|
||||
optionLabel: string;
|
||||
count: number;
|
||||
}[]
|
||||
>(query);
|
||||
|
||||
// First group by form and field
|
||||
const groupedByFormAndField = result.reduce((acc, curr) => {
|
||||
const formKey = curr.formName;
|
||||
|
||||
Reference in New Issue
Block a user