diff --git a/packages/features/insights/filters/Download/Download.tsx b/packages/features/insights/filters/Download/Download.tsx
index d44dfe2616..0f31b47c43 100644
--- a/packages/features/insights/filters/Download/Download.tsx
+++ b/packages/features/insights/filters/Download/Download.tsx
@@ -1,41 +1,46 @@
+import { useState } from "react";
+
import { downloadAsCsv } from "@calcom/lib/csvUtils";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import type { RouterOutputs } from "@calcom/trpc";
import { trpc } from "@calcom/trpc";
-import { Dropdown, DropdownItem, DropdownMenuContent, DropdownMenuTrigger } from "@calcom/ui/components/dropdown";
import { Button } from "@calcom/ui/components/button";
+import {
+ Dropdown,
+ DropdownItem,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from "@calcom/ui/components/dropdown";
+import { showToast } from "@calcom/ui/components/toast";
import { useInsightsParameters } from "../../hooks/useInsightsParameters";
const Download = () => {
const { t } = useLocale();
- const { startDate, endDate, teamId, userId, eventTypeId, memberUserId } = useInsightsParameters();
+ const { startDate, endDate, teamId, userId, eventTypeId, memberUserId, isAll } = useInsightsParameters();
+ const [isDownloading, setIsDownloading] = useState(false);
+ const utils = trpc.useUtils();
- const { data, isPending } = trpc.viewer.insights.rawData.useQuery(
- {
- startDate,
- endDate,
- teamId,
- userId,
- eventTypeId,
- memberUserId,
- },
- {
- refetchOnWindowFocus: false,
- refetchOnReconnect: false,
- retry: false,
- staleTime: Infinity,
- trpc: {
- context: { skipBatch: true },
- },
+ const handleDownloadClick = async () => {
+ try {
+ setIsDownloading(true);
+ const data = await utils.viewer.insights.rawData.fetch({
+ startDate,
+ endDate,
+ teamId,
+ userId,
+ eventTypeId,
+ memberUserId,
+ isAll,
+ });
+
+ if (!data) return;
+ const { data: csvRaw, filename } = data;
+ downloadAsCsv(csvRaw, filename);
+ } catch (error) {
+ showToast(t("unexpected_error_try_again"), "error");
+ } finally {
+ setIsDownloading(false);
}
- );
-
- type RawData = RouterOutputs["viewer"]["insights"]["rawData"] | undefined;
- const handleDownloadClick = async (data: RawData) => {
- if (!data) return;
- const { data: csvRaw, filename } = data;
- downloadAsCsv(csvRaw, filename);
};
return (
@@ -44,13 +49,13 @@ const Download = () => {
- handleDownloadClick(data)}>{t("as_csv")}
+ {t("as_csv")}
);
diff --git a/packages/features/insights/server/events.ts b/packages/features/insights/server/events.ts
index 434f4ecd62..5bd5bd55f0 100644
--- a/packages/features/insights/server/events.ts
+++ b/packages/features/insights/server/events.ts
@@ -381,7 +381,7 @@ class EventsInsights {
}
) => {
// Obtain the where conditional
- const whereConditional = await this.obtainWhereConditional(props);
+ const whereConditional = await this.obtainWhereConditionalForDownload(props);
const csvData = await prisma.bookingTimeStatus.findMany({
select: {
@@ -452,7 +452,9 @@ class EventsInsights {
};
/*
- * This is meant to be used for all functions inside insights router, ideally we should have a view that have all of this data
+ * This is meant to be used for all functions inside insights router,
+ * but it's currently used only for CSV download.
+ * Ideally we should have a view that have all of this data
* The order where will be from the most specific to the least specific
* starting from the top will be:
* - memberUserId
@@ -466,7 +468,7 @@ class EventsInsights {
* @param props
* @returns
*/
- static obtainWhereConditional = async (
+ static obtainWhereConditionalForDownload = async (
props: RawDataInput & { organizationId: number | null; isOrgAdminOrOwner: boolean | null }
) => {
const {
@@ -492,14 +494,7 @@ class EventsInsights {
}
if (eventTypeId) {
- whereConditional["OR"] = [
- {
- eventTypeId,
- },
- {
- eventParentId: eventTypeId,
- },
- ];
+ whereConditional["eventTypeId"] = eventTypeId;
}
if (memberUserId) {
whereConditional["userId"] = memberUserId;
diff --git a/packages/features/insights/server/trpc-router.ts b/packages/features/insights/server/trpc-router.ts
index 76d50b5d2b..68080c63ff 100644
--- a/packages/features/insights/server/trpc-router.ts
+++ b/packages/features/insights/server/trpc-router.ts
@@ -1080,103 +1080,19 @@ export const insightsRouter = router({
const { prisma, user } = ctx;
const { teamId, userId, isAll } = input;
- if (!teamId && !userId) {
- return [];
- }
-
- const membershipWhereConditional: Prisma.MembershipWhereInput = {};
-
- let childrenTeamIds: number[] = [];
-
- if (isAll && teamId && user.organizationId && user.isOwnerAdminOfParentTeam) {
- const childTeams = await prisma.team.findMany({
- where: {
- parentId: user.organizationId,
- },
- select: {
- id: true,
- },
- });
- if (childTeams.length > 0) {
- childrenTeamIds = childTeams.map((team) => team.id);
- }
- membershipWhereConditional["teamId"] = {
- in: [user.organizationId, ...childrenTeamIds],
- };
- }
-
- if (teamId && !isAll) {
- membershipWhereConditional["teamId"] = teamId;
- membershipWhereConditional["userId"] = user.id;
- }
- if (userId) {
- membershipWhereConditional["userId"] = userId;
- }
-
- // I'm not using unique here since when userId comes from input we should look for every
- // event type that user owns
- const membership = await prisma.membership.findFirst({
- where: membershipWhereConditional,
- });
-
- if (!membership && !user.isOwnerAdminOfParentTeam) {
- throw new Error("User is not part of a team/org");
- }
-
- const eventTypeWhereConditional: Prisma.EventTypeWhereInput = {};
- if (isAll && childrenTeamIds.length > 0 && user.organizationId && user.isOwnerAdminOfParentTeam) {
- eventTypeWhereConditional["teamId"] = {
- in: [user.organizationId, ...childrenTeamIds],
- };
- }
- if (teamId && !isAll) {
- eventTypeWhereConditional["teamId"] = teamId;
- }
- if (userId) {
- eventTypeWhereConditional["userId"] = userId;
- }
- let eventTypeResult: Prisma.EventTypeGetPayload<{
- select: {
- id: true;
- slug: true;
- teamId: true;
- title: true;
- team: {
- select: {
- name: true;
- };
- };
- };
- }>[] = [];
-
- let isMember = membership?.role === "MEMBER";
- if (user.isOwnerAdminOfParentTeam) {
- isMember = false;
- }
- if (isMember) {
- eventTypeWhereConditional["OR"] = [
- { userId: user.id },
- { users: { some: { id: user.id } } },
- // @TODO this is not working as expected
- // hosts: { some: { id: user.id } },
- ];
- }
- eventTypeResult = await prisma.eventType.findMany({
- select: {
- id: true,
- slug: true,
- title: true,
- teamId: true,
- team: {
- select: {
- name: true,
- },
- },
+ const eventTypeList = await getEventTypeList({
+ prisma,
+ teamId,
+ userId,
+ isAll,
+ user: {
+ id: user.id,
+ organizationId: user.organizationId,
+ isOwnerAdminOfParentTeam: user.isOwnerAdminOfParentTeam,
},
- where: eventTypeWhereConditional,
});
- return eventTypeResult;
+ return eventTypeList;
}),
recentRatings: userBelongsToTeamProcedure.input(rawDataInputSchema).query(async ({ ctx, input }) => {
const { teamId, startDate, endDate, eventTypeId, isAll, userId, memberUserId } = input;
@@ -1534,7 +1450,22 @@ export const insightsRouter = router({
return result;
}),
rawData: userBelongsToTeamProcedure.input(rawDataInputSchema).query(async ({ ctx, input }) => {
- const { startDate, endDate, teamId, userId, memberUserId, isAll, eventTypeId } = input;
+ const { startDate, endDate, teamId, userId, memberUserId, isAll } = input;
+
+ const eventTypeList = await getEventTypeList({
+ prisma: ctx.prisma,
+ teamId,
+ userId,
+ isAll,
+ user: {
+ id: ctx.user.id,
+ organizationId: ctx.user.organizationId,
+ isOwnerAdminOfParentTeam: ctx.user.isOwnerAdminOfParentTeam,
+ },
+ });
+
+ // use eventTypeId filter only if it's accessible by this user
+ const eventTypeId = eventTypeList.find((eventType) => eventType.id === input.eventTypeId)?.id;
const isOrgAdminOrOwner = ctx.user.isOwnerAdminOfParentTeam;
try {
@@ -1560,7 +1491,6 @@ export const insightsRouter = router({
} catch (e) {
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
}
- return { data: "", filename: "" };
}),
getRoutingFormsForFilters: userBelongsToTeamProcedure
@@ -1758,3 +1688,119 @@ export const insightsRouter = router({
}
}),
});
+
+async function getEventTypeList({
+ prisma,
+ teamId,
+ userId,
+ isAll,
+ user,
+}: {
+ prisma: typeof readonlyPrisma;
+ teamId: number | null | undefined;
+ userId: number | null | undefined;
+ isAll: boolean | undefined;
+ user: {
+ id: number;
+ organizationId: number | null;
+ isOwnerAdminOfParentTeam: boolean;
+ };
+}) {
+ if (!teamId && !userId) {
+ return [];
+ }
+
+ const membershipWhereConditional: Prisma.MembershipWhereInput = {};
+
+ let childrenTeamIds: number[] = [];
+
+ if (isAll && teamId && user.organizationId && user.isOwnerAdminOfParentTeam) {
+ const childTeams = await prisma.team.findMany({
+ where: {
+ parentId: user.organizationId,
+ },
+ select: {
+ id: true,
+ },
+ });
+ if (childTeams.length > 0) {
+ childrenTeamIds = childTeams.map((team) => team.id);
+ }
+ membershipWhereConditional["teamId"] = {
+ in: [user.organizationId, ...childrenTeamIds],
+ };
+ }
+
+ if (teamId && !isAll) {
+ membershipWhereConditional["teamId"] = teamId;
+ membershipWhereConditional["userId"] = user.id;
+ }
+ if (userId) {
+ membershipWhereConditional["userId"] = userId;
+ }
+
+ // I'm not using unique here since when userId comes from input we should look for every
+ // event type that user owns
+ const membership = await prisma.membership.findFirst({
+ where: membershipWhereConditional,
+ });
+
+ if (!membership && !user.isOwnerAdminOfParentTeam) {
+ throw new Error("User is not part of a team/org");
+ }
+
+ const eventTypeWhereConditional: Prisma.EventTypeWhereInput = {};
+ if (isAll && childrenTeamIds.length > 0 && user.organizationId && user.isOwnerAdminOfParentTeam) {
+ eventTypeWhereConditional["teamId"] = {
+ in: [user.organizationId, ...childrenTeamIds],
+ };
+ }
+ if (teamId && !isAll) {
+ eventTypeWhereConditional["teamId"] = teamId;
+ }
+ if (userId) {
+ eventTypeWhereConditional["userId"] = userId;
+ }
+ let eventTypeResult: Prisma.EventTypeGetPayload<{
+ select: {
+ id: true;
+ slug: true;
+ teamId: true;
+ title: true;
+ team: {
+ select: {
+ name: true;
+ };
+ };
+ };
+ }>[] = [];
+
+ let isMember = membership?.role === "MEMBER";
+ if (user.isOwnerAdminOfParentTeam) {
+ isMember = false;
+ }
+ if (isMember) {
+ eventTypeWhereConditional["OR"] = [
+ { userId: user.id },
+ { users: { some: { id: user.id } } },
+ // @TODO this is not working as expected
+ // hosts: { some: { id: user.id } },
+ ];
+ }
+ eventTypeResult = await prisma.eventType.findMany({
+ select: {
+ id: true,
+ slug: true,
+ title: true,
+ teamId: true,
+ team: {
+ select: {
+ name: true,
+ },
+ },
+ },
+ where: eventTypeWhereConditional,
+ });
+
+ return eventTypeResult;
+}