feat: add paid, userEmail, userName, and rating filters to insights page (#23299)
* feat: add paid, userEmail, and userName filters to insights page - Add paid filter as MULTI_SELECT with 'Paid'/'Free' options - Add userEmail filter as TEXT for searching user emails - Add userName filter as TEXT for searching user names - Update DummyTableRow type to include new filter columns - Add backend filtering logic in buildColumnFilterCondition method - Use existing columns from BookingTimeStatusDenormalized view - Follow established patterns from /bookings page implementation Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * fix: change paid filter from MULTI_SELECT to SINGLE_SELECT - Updated useInsightsBookings.ts to use ColumnFilterType.SINGLE_SELECT for paid filter - Modified buildColumnFilterCondition to handle single select with isSingleSelectFilterValue - Simplified backend logic to handle single boolean value instead of array Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * fix paid filter * feat: add rating filter to insights page - Add rating field to DummyTableRow type - Add rating column with SINGLE_SELECT filter type - Add rating options 1-5 in faceted unique values - Add rating filtering logic in backend service - Rating filter allows filtering bookings by 1-5 star ratings Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * feat: change rating filter to NUMBER type - Update rating filter from SINGLE_SELECT to NUMBER in useInsightsBookings.ts - Remove rating options from useInsightsBookingFacetedUniqueValues.ts since NUMBER filters don't need predefined options - Add isNumberFilterValue import and implement number filter logic in InsightsBookingBaseService.ts - Support eq, gt, gte, lt, lte operators for rating number filter Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: use makeSqlCondition utility for filter logic - Import makeSqlCondition from @calcom/features/data-table/lib/server - Replace manual SQL condition building with makeSqlCondition utility for userEmail, userName, and rating filters - Maintain existing paid filter logic for boolean conversion - Follow established pattern from InsightsRoutingBaseService - Cleaner, more maintainable code with consistent filter handling Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * add missing texts --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
This commit is contained in:
4 files changed
+100
-2
No files matched your search
@@ -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 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
@@ -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<FacetedValue, number>();
|
||||
},
|
||||
[users, eventTypes]
|
||||
[users, eventTypes, t]
|
||||
);
|
||||
};
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user