feat: no show guests on insights (#17919)

* feat: return noShowGuest data from eventsByStatus & eventsTimeline procedures

* feat: add noShowGuest Data in BookingKPICards

* feat: add noShowGuest Data in BookingStatusLineChart

* feat: track noShowGuests in outer query

* chore: fix noShowGuest naming
This commit is contained in:
Vishnu
2024-12-02 18:53:32 +00:00
committed by GitHub
parent 82f64f7d6a
commit b33ca2f937
4 changed files with 69 additions and 5 deletions
@@ -40,7 +40,15 @@ export const BookingKPICards = () => {
const categories: {
title: string;
index: "created" | "completed" | "rescheduled" | "cancelled" | "no_show" | "rating" | "csat";
index:
| "created"
| "completed"
| "rescheduled"
| "cancelled"
| "no_show"
| "rating"
| "csat"
| "no_show_guest";
}[] = [
{
title: t("events_created"),
@@ -66,6 +74,10 @@ export const BookingKPICards = () => {
title: t("event_no_show"),
index: "no_show",
},
{
title: t("event_no_show_guest"),
index: "no_show_guest",
},
{
title: t("csat_score"),
index: "csat",
@@ -60,9 +60,9 @@ export const BookingStatusLineChart = () => {
<LineChart
className="linechart mt-4 h-80"
data={eventsTimeLine ?? []}
categories={["Created", "Completed", "Rescheduled", "Cancelled", "No-Show (Host)"]}
categories={["Created", "Completed", "Rescheduled", "Cancelled", "No-Show (Host)", "No-Show (Guest)"]}
index="Month"
colors={["purple", "green", "blue", "red", "slate"]}
colors={["purple", "green", "blue", "red", "slate", "orange"]}
valueFormatter={valueFormatter}
/>
</CardInsights>
+39 -2
View File
@@ -12,6 +12,7 @@ type StatusAggregate = {
rescheduled: number;
cancelled: number;
noShowHost: number;
noShowGuests: number;
_all: number;
uncompleted: number;
};
@@ -73,20 +74,30 @@ class EventsInsights {
const whereClause = buildSqlCondition(whereConditional);
const data = await prisma.$queryRaw<
{ periodStart: Date; bookingsCount: number; timeStatus: string; noShowHost: boolean }[]
{
periodStart: Date;
bookingsCount: number;
timeStatus: string;
noShowHost: boolean;
noShowGuests: number;
}[]
>`
SELECT
"periodStart",
CAST(COUNT(*) AS INTEGER) AS "bookingsCount",
CAST(COUNT(CASE WHEN "isNoShowGuest" = true THEN 1 END) AS INTEGER) AS "noShowGuests",
"timeStatus",
"noShowHost"
FROM (
SELECT
DATE_TRUNC(${timeView}, "createdAt") AS "periodStart",
"a"."noShow" AS "isNoShowGuest",
"timeStatus",
"noShowHost"
FROM
"BookingTimeStatus"
JOIN
"Attendee" "a" ON "a"."bookingId" = "BookingTimeStatus"."id"
WHERE
"createdAt" BETWEEN ${formattedStartDate}::timestamp AND ${formattedEndDate}::timestamp
AND ${Prisma.raw(whereClause)}
@@ -100,7 +111,7 @@ class EventsInsights {
`;
const aggregate: AggregateResult = {};
data.forEach(({ periodStart, bookingsCount, timeStatus, noShowHost }) => {
data.forEach(({ periodStart, bookingsCount, timeStatus, noShowHost, noShowGuests }) => {
const formattedDate = dayjs(periodStart).format("MMM D, YYYY");
if (dayjs(periodStart).isAfter(endDate)) {
@@ -116,6 +127,7 @@ class EventsInsights {
noShowHost: 0,
_all: 0,
uncompleted: 0,
noShowGuests: 0,
};
}
@@ -130,6 +142,9 @@ class EventsInsights {
if (noShowHost) {
aggregate[formattedDate]["noShowHost"] += Number(bookingsCount);
}
// Track no-show guests explicitly
aggregate[formattedDate]["noShowGuests"] += noShowGuests;
});
// Generate a complete list of expected date labels based on the timeline
@@ -160,6 +175,7 @@ class EventsInsights {
rescheduled: 0,
cancelled: 0,
noShowHost: 0,
noShowGuests: 0,
_all: 0,
uncompleted: 0,
};
@@ -169,6 +185,27 @@ class EventsInsights {
return aggregate;
};
static getTotalNoShowGuests = async (where: Prisma.BookingTimeStatusWhereInput) => {
const bookings = await prisma.bookingTimeStatus.findMany({
where,
select: {
id: true,
},
});
const { _count: totalNoShowGuests } = await prisma.attendee.aggregate({
where: {
bookingId: {
in: bookings.map((booking) => booking.id),
},
noShow: true,
},
_count: true,
});
return totalNoShowGuests;
};
static countGroupedByStatus = async (where: Prisma.BookingTimeStatusWhereInput) => {
const data = await prisma.bookingTimeStatus.groupBy({
where,
@@ -236,6 +236,10 @@ const emptyResponseEventsByStatus = {
count: 0,
deltaPrevious: 0,
},
no_show_guest: {
count: 0,
deltaPrevious: 0,
},
csat: {
count: 0,
deltaPrevious: 0,
@@ -304,16 +308,20 @@ export const insightsRouter = router({
countGroupedByStatus,
totalRatingsAggregate,
totalCSAT,
totalNoShowGuests,
lastPeriodCountGroupedByStatus,
lastPeriodTotalRatingsAggregate,
lastPeriodTotalCSAT,
lastPeriodTotalNoShowGuests,
] = await Promise.all([
EventsInsights.countGroupedByStatus(baseWhereCondition),
EventsInsights.getAverageRating(baseWhereCondition),
EventsInsights.getTotalCSAT(baseWhereCondition),
EventsInsights.getTotalNoShowGuests(baseWhereCondition),
EventsInsights.countGroupedByStatus(lastPeriodBaseCondition),
EventsInsights.getAverageRating(lastPeriodBaseCondition),
EventsInsights.getTotalCSAT(lastPeriodBaseCondition),
EventsInsights.getTotalNoShowGuests(lastPeriodBaseCondition),
]);
const baseBookingsCount = countGroupedByStatus["_all"];
@@ -360,6 +368,10 @@ export const insightsRouter = router({
count: totalNoShow,
deltaPrevious: EventsInsights.getPercentage(totalNoShow, lastPeriodTotalNoShow),
},
no_show_guest: {
count: totalNoShowGuests,
deltaPrevious: EventsInsights.getPercentage(totalNoShowGuests, lastPeriodTotalNoShowGuests),
},
rating: {
count: averageRating,
deltaPrevious: EventsInsights.getPercentage(averageRating, lastPeriodAverageRating),
@@ -379,6 +391,7 @@ export const insightsRouter = router({
result.rescheduled.count === 0 &&
result.cancelled.count === 0 &&
result.no_show.count === 0 &&
result.no_show_guest.count === 0 &&
result.rating.count === 0
) {
return emptyResponseEventsByStatus;
@@ -482,6 +495,7 @@ export const insightsRouter = router({
Rescheduled: 0,
Cancelled: 0,
"No-Show (Host)": 0,
"No-Show (Guest)": 0,
};
const countsForDateRange = countsByStatus[formattedDate];
@@ -492,6 +506,7 @@ export const insightsRouter = router({
EventData["Rescheduled"] = countsForDateRange["rescheduled"] || 0;
EventData["Cancelled"] = countsForDateRange["cancelled"] || 0;
EventData["No-Show (Host)"] = countsForDateRange["noShowHost"] || 0;
EventData["No-Show (Guest)"] = countsForDateRange["noShowGuests"] || 0;
}
return EventData;
});