* 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>
84 lines
2.3 KiB
TypeScript
84 lines
2.3 KiB
TypeScript
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";
|
|
|
|
import { bookingStatusToText } from "../lib/bookingStatusToText";
|
|
|
|
const statusOrder: Record<BookingStatus, number> = {
|
|
[BookingStatus.ACCEPTED]: 1,
|
|
[BookingStatus.PENDING]: 2,
|
|
[BookingStatus.AWAITING_HOST]: 3,
|
|
[BookingStatus.CANCELLED]: 4,
|
|
[BookingStatus.REJECTED]: 5,
|
|
};
|
|
|
|
export const useInsightsBookingFacetedUniqueValues = ({
|
|
userId,
|
|
teamId,
|
|
isAll,
|
|
}: {
|
|
userId: number | undefined;
|
|
teamId: number | undefined;
|
|
isAll: boolean;
|
|
}) => {
|
|
const { t } = useLocale();
|
|
const { data: users } = trpc.viewer.insights.userList.useQuery(
|
|
{
|
|
teamId,
|
|
isAll,
|
|
},
|
|
{
|
|
refetchOnWindowFocus: false,
|
|
}
|
|
);
|
|
|
|
const { data: eventTypes } = trpc.viewer.insights.eventTypeList.useQuery(
|
|
{
|
|
teamId,
|
|
userId,
|
|
isAll,
|
|
},
|
|
{
|
|
refetchOnWindowFocus: false,
|
|
}
|
|
);
|
|
|
|
return useCallback(
|
|
(_: Table<any>, columnId: string) => (): Map<FacetedValue, number> => {
|
|
if (columnId === "status") {
|
|
return convertFacetedValuesToMap(
|
|
Object.keys(statusOrder).map((status) => ({
|
|
value: status.toLowerCase(),
|
|
label: bookingStatusToText(status as BookingStatus),
|
|
}))
|
|
);
|
|
} else if (columnId === "userId") {
|
|
return convertFacetedValuesToMap(
|
|
users?.map((user) => ({
|
|
label: user.name ?? user.email,
|
|
value: user.id,
|
|
})) ?? []
|
|
);
|
|
} else if (columnId === "eventTypeId") {
|
|
return convertFacetedValuesToMap(
|
|
eventTypes?.map((eventType) => ({
|
|
value: eventType.id,
|
|
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, t]
|
|
);
|
|
};
|