diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index f293b268fa2d2e..3dd6231f0dafa9 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -3491,5 +3491,8 @@ "meeting_link": "Meeting link", "my_bookings": "My Bookings", "phone": "Phone", + "free": "Free", + "user_email": "User Email", + "user_name": "User Name", "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑" } diff --git a/packages/features/insights/hooks/useInsightsBookingFacetedUniqueValues.ts b/packages/features/insights/hooks/useInsightsBookingFacetedUniqueValues.ts index f399c53e849a06..c2feecf09451f4 100644 --- a/packages/features/insights/hooks/useInsightsBookingFacetedUniqueValues.ts +++ b/packages/features/insights/hooks/useInsightsBookingFacetedUniqueValues.ts @@ -2,6 +2,7 @@ import type { Table } from "@tanstack/react-table"; import { useCallback } from "react"; import { convertFacetedValuesToMap, type FacetedValue } from "@calcom/features/data-table"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { BookingStatus } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc"; @@ -24,6 +25,7 @@ export const useInsightsBookingFacetedUniqueValues = ({ teamId: number | undefined; isAll: boolean; }) => { + const { t } = useLocale(); const { data: users } = trpc.viewer.insights.userList.useQuery( { teamId, @@ -68,9 +70,14 @@ export const useInsightsBookingFacetedUniqueValues = ({ label: eventType.teamId ? `${eventType.title} (${eventType.team?.name})` : eventType.title, })) ?? [] ); + } else if (columnId === "paid") { + return convertFacetedValuesToMap([ + { value: "true", label: t("paid") }, + { value: "false", label: t("free") }, + ]); } return new Map(); }, - [users, eventTypes] + [users, eventTypes, t] ); }; diff --git a/packages/features/insights/hooks/useInsightsBookings.ts b/packages/features/insights/hooks/useInsightsBookings.ts index e105c3cd3335ed..fe4270fd4fb32a 100644 --- a/packages/features/insights/hooks/useInsightsBookings.ts +++ b/packages/features/insights/hooks/useInsightsBookings.ts @@ -12,6 +12,10 @@ type DummyTableRow = { userId: number | null; eventTypeId: number | null; status: BookingStatus; + paid: boolean; + userEmail: string | null; + userName: string | null; + rating: number | null; }; const emptyData: DummyTableRow[] = []; @@ -67,6 +71,58 @@ export const useInsightsBookings = () => { }, cell: () => null, }), + columnHelper.accessor("paid", { + id: "paid", + header: t("paid"), + size: 150, + meta: { + filter: { + type: ColumnFilterType.SINGLE_SELECT, + }, + }, + enableColumnFilter: true, + enableSorting: false, + cell: () => null, + }), + columnHelper.accessor("userEmail", { + id: "userEmail", + header: t("user_email"), + size: 200, + meta: { + filter: { + type: ColumnFilterType.TEXT, + }, + }, + enableColumnFilter: true, + enableSorting: false, + cell: () => null, + }), + columnHelper.accessor("userName", { + id: "userName", + header: t("user_name"), + size: 200, + meta: { + filter: { + type: ColumnFilterType.TEXT, + }, + }, + enableColumnFilter: true, + enableSorting: false, + cell: () => null, + }), + columnHelper.accessor("rating", { + id: "rating", + header: t("rating"), + size: 150, + meta: { + filter: { + type: ColumnFilterType.NUMBER, + }, + }, + enableColumnFilter: true, + enableSorting: false, + cell: () => null, + }), ]; }, [t]); diff --git a/packages/lib/server/service/InsightsBookingBaseService.ts b/packages/lib/server/service/InsightsBookingBaseService.ts index 7468a448866e00..ac8b65f53f52ec 100644 --- a/packages/lib/server/service/InsightsBookingBaseService.ts +++ b/packages/lib/server/service/InsightsBookingBaseService.ts @@ -3,9 +3,15 @@ import md5 from "md5"; import { z } from "zod"; import dayjs from "@calcom/dayjs"; +import { makeSqlCondition } from "@calcom/features/data-table/lib/server"; import { ZColumnFilter } from "@calcom/features/data-table/lib/types"; import type { ColumnFilter } from "@calcom/features/data-table/lib/types"; -import { isSingleSelectFilterValue, isMultiSelectFilterValue } from "@calcom/features/data-table/lib/utils"; +import { + isSingleSelectFilterValue, + isMultiSelectFilterValue, + isTextFilterValue, + isNumberFilterValue, +} from "@calcom/features/data-table/lib/utils"; import type { DateRange } from "@calcom/features/insights/server/insightsDateUtils"; import type { readonlyPrisma } from "@calcom/prisma"; import { MembershipRole } from "@calcom/prisma/enums"; @@ -319,6 +325,32 @@ export class InsightsBookingBaseService { return Prisma.sql`"status" IN (${Prisma.join(statusValues)})`; } + if (id === "paid" && isSingleSelectFilterValue(value)) { + const paidValue = value.data === "true"; + return Prisma.sql`"paid" = ${paidValue}`; + } + + if (id === "userEmail" && isTextFilterValue(value)) { + const condition = makeSqlCondition(value); + if (condition) { + return Prisma.sql`"userEmail" ${condition}`; + } + } + + if (id === "userName" && isTextFilterValue(value)) { + const condition = makeSqlCondition(value); + if (condition) { + return Prisma.sql`"userName" ${condition}`; + } + } + + if (id === "rating" && isNumberFilterValue(value)) { + const condition = makeSqlCondition(value); + if (condition) { + return Prisma.sql`"rating" ${condition}`; + } + } + return null; }