* init page * init insights frontend * nit * nit * nit * merge * fixed icons * i18n, needs features * Init insights trpc * Using trpc on client * Added events timeline * Seed analytics script * connect ui with trpc * Added and fixed event time lines * WIP popular days and avg time duration event type * added new metric graphs * improved upgrade tip * always show upgrade screen * upgrade tremor.so and select inputs for page * Remove log * Move everything to components and add context * Fix select types using calcom ui one * Adding translations * Add missing translations * Add more translations * min fix * Fixes for date select * Prefer early return and mobile design fixes * Fix style for mobile * Fix data with userId filter from popular events * add user id to average time duration * fix types for select-react * Removed submodules * Delete website * Update yarn.lock * Code organization and type fixes * trpc fixes * Builds are now passing * Relocates server code * Add url state in insights * Update FiltersProvider.tsx * Cleanup * Update embed-iframe.ts * Update FilterType.tsx * Update seed-app-store.config.json * Update index.tsx * Renamed seeder * Update FiltersProvider.tsx * Fix for query params * no wrap on lg screen * Fix shadow borders from tremor components, fix title font * Add ring-gray to match filters * add cursor pointer * copy improvements * blue to black * fixed date select focus * text right for table * Adds missing translation strings * Fix url state for filter type * more layout improvements * more padding improvements * more UI fixes (padding, sizes) requested by ciaran * Apply suggestions from code review * WIP user without team metrics * yarn * Adds loading state for graphs and tables * fix average render when no data * Multiple fixes in insights * Fix most and least booked members querys * More fix for clear filters --------- Co-authored-by: Peer Richelsen <peer@cal.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: zomars <zomars@me.com>
183 lines
4.8 KiB
TypeScript
183 lines
4.8 KiB
TypeScript
import type { Dayjs } from "@calcom/dayjs";
|
|
import dayjs from "@calcom/dayjs";
|
|
import { prisma } from "@calcom/prisma";
|
|
import type { Prisma } from "@calcom/prisma/client";
|
|
|
|
interface ITimeRange {
|
|
start: Dayjs;
|
|
end: Dayjs;
|
|
}
|
|
|
|
type TimeViewType = "week" | "month" | "year" | "day";
|
|
|
|
class EventsInsights {
|
|
static getBookingsInTimeRange = async (timeRange: ITimeRange, where: Prisma.BookingWhereInput) => {
|
|
const { start, end } = timeRange;
|
|
|
|
const events = await prisma.booking.count({
|
|
where: {
|
|
...where,
|
|
createdAt: {
|
|
gte: start.toISOString(),
|
|
lte: end.toISOString(),
|
|
},
|
|
},
|
|
});
|
|
|
|
return events;
|
|
};
|
|
|
|
static getCreatedEventsInTimeRange = async (timeRange: ITimeRange, where: Prisma.BookingWhereInput) => {
|
|
const result = await this.getBookingsInTimeRange(timeRange, where);
|
|
|
|
return result;
|
|
};
|
|
|
|
static getCancelledEventsInTimeRange = async (timeRange: ITimeRange, where: Prisma.BookingWhereInput) => {
|
|
const result = await this.getBookingsInTimeRange(timeRange, {
|
|
...where,
|
|
status: "CANCELLED",
|
|
});
|
|
|
|
return result;
|
|
};
|
|
|
|
static getCompletedEventsInTimeRange = async (timeRange: ITimeRange, where: Prisma.BookingWhereInput) => {
|
|
const result = await this.getBookingsInTimeRange(timeRange, {
|
|
...where,
|
|
status: "ACCEPTED",
|
|
endTime: {
|
|
lte: dayjs().toISOString(),
|
|
},
|
|
});
|
|
|
|
return result;
|
|
};
|
|
|
|
static getRescheduledEventsInTimeRange = async (timeRange: ITimeRange, where: Prisma.BookingWhereInput) => {
|
|
const result = await this.getBookingsInTimeRange(timeRange, {
|
|
...where,
|
|
rescheduled: true,
|
|
});
|
|
|
|
return result;
|
|
};
|
|
|
|
static getBaseBookingForEventStatus = async (where: Prisma.BookingWhereInput) => {
|
|
const baseBookings = await prisma.booking.findMany({
|
|
where,
|
|
select: {
|
|
id: true,
|
|
eventType: true,
|
|
},
|
|
});
|
|
|
|
return baseBookings;
|
|
};
|
|
|
|
static getTotalRescheduledEvents = async (bookingIds: number[]) => {
|
|
return await prisma.booking.count({
|
|
where: {
|
|
id: {
|
|
in: bookingIds,
|
|
},
|
|
rescheduled: true,
|
|
},
|
|
});
|
|
};
|
|
|
|
static getTotalCancelledEvents = async (bookingIds: number[]) => {
|
|
return await prisma.booking.count({
|
|
where: {
|
|
id: {
|
|
in: bookingIds,
|
|
},
|
|
status: "CANCELLED",
|
|
},
|
|
});
|
|
};
|
|
|
|
static getTimeLine = async (timeView: TimeViewType, startDate: Dayjs, endDate: Dayjs) => {
|
|
let resultTimeLine: string[] = [];
|
|
|
|
if (timeView) {
|
|
switch (timeView) {
|
|
case "week":
|
|
resultTimeLine = this.getWeekTimeline(startDate, endDate);
|
|
break;
|
|
case "month":
|
|
resultTimeLine = this.getMonthTimeline(startDate, endDate);
|
|
break;
|
|
case "year":
|
|
resultTimeLine = this.getYearTimeline(startDate, endDate);
|
|
break;
|
|
default:
|
|
resultTimeLine = this.getWeekTimeline(startDate, endDate);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return resultTimeLine;
|
|
};
|
|
|
|
static getTimeView = (timeView: TimeViewType, startDate: Dayjs, endDate: Dayjs) => {
|
|
let resultTimeView = timeView;
|
|
|
|
if (startDate.diff(endDate, "day") > 90) {
|
|
resultTimeView = "month";
|
|
} else if (startDate.diff(endDate, "day") > 365) {
|
|
resultTimeView = "year";
|
|
}
|
|
|
|
return resultTimeView;
|
|
};
|
|
|
|
static getWeekTimeline(startDate: Dayjs, endDate: Dayjs): string[] {
|
|
const now = dayjs();
|
|
const endOfDay = now.endOf("day");
|
|
let pivotDate = dayjs(startDate);
|
|
const dates: string[] = [];
|
|
while (pivotDate.isBefore(endDate) && pivotDate.isBefore(endOfDay)) {
|
|
const weekEndDate = pivotDate.add(7, "day").isBefore(endOfDay) ? pivotDate.add(7, "day") : endOfDay;
|
|
dates.push(pivotDate.format("YYYY-MM-DD"));
|
|
pivotDate = weekEndDate.add(1, "day");
|
|
}
|
|
return dates;
|
|
}
|
|
|
|
static getMonthTimeline(startDate: Dayjs, endDate: Dayjs) {
|
|
let pivotDate = dayjs(startDate);
|
|
const dates = [];
|
|
while (pivotDate.isBefore(endDate)) {
|
|
pivotDate = pivotDate.set("month", pivotDate.get("month") + 1);
|
|
|
|
dates.push(pivotDate.format("YYYY-MM-DD"));
|
|
}
|
|
return dates;
|
|
}
|
|
|
|
static getYearTimeline(startDate: Dayjs, endDate: Dayjs) {
|
|
const pivotDate = dayjs(startDate);
|
|
const dates = [];
|
|
while (pivotDate.isBefore(endDate)) {
|
|
pivotDate.set("year", pivotDate.get("year") + 1);
|
|
dates.push(pivotDate.format("YYYY-MM-DD"));
|
|
}
|
|
return dates;
|
|
}
|
|
|
|
static getPercentage = (actualMetric: number, previousMetric: number) => {
|
|
const differenceActualVsPrevious = actualMetric - previousMetric;
|
|
if (differenceActualVsPrevious === 0) {
|
|
return 0;
|
|
}
|
|
const result = (differenceActualVsPrevious * 100) / previousMetric;
|
|
if (isNaN(result) || !isFinite(result)) {
|
|
return 0;
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
|
|
export { EventsInsights };
|