fix: improve filters at /bookings (#18581)

* fix: improve filters at /bookings

* fix type
This commit is contained in:
Eunjae Lee
2025-01-10 11:10:10 +00:00
committed by GitHub
parent 8761b91f71
commit 1ffb376789
5 changed files with 47 additions and 24 deletions
@@ -1,5 +1,5 @@
import { useSession } from "next-auth/react";
import { Fragment, useMemo } from "react";
import { Fragment, useMemo, useState } from "react";
import {
FilterCheckboxField,
@@ -8,6 +8,7 @@ import {
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import { FilterSearchField } from "@calcom/ui";
import { AnimatedPopover, Divider, Icon } from "@calcom/ui";
import { groupBy } from "../groupBy";
@@ -33,6 +34,7 @@ export const EventTypeFilter = () => {
const { t } = useLocale();
const { data: user } = useSession();
const { data: query, pushItemToKey, removeItemByKeyAndValue, removeAllQueryParams } = useFilterQuery();
const [search, setSearch] = useState("");
const eventTypes = trpc.viewer.eventTypes.listWithTeam.useQuery(undefined, {
enabled: !!user,
@@ -68,6 +70,11 @@ export const EventTypeFilter = () => {
<AnimatedPopover text={getTextForPopover()} prefix={`${t("event_type")}: `}>
{!isEmpty ? (
<FilterCheckboxFieldsContainer>
<FilterSearchField
placeholder={t("search")}
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<FilterCheckboxField
id="all"
icon={<Icon name="link" className="h-4 w-4" />}
@@ -82,20 +89,22 @@ export const EventTypeFilter = () => {
<div className="text-subtle px-4 py-2 text-xs font-medium uppercase leading-none">
{teamName === "user_own_event_types" ? t("individual") : teamName}
</div>
{groupedEventTypes[teamName].map((eventType) => (
<FilterCheckboxField
key={eventType.id}
checked={query.eventTypeIds?.includes(eventType.id)}
onChange={(e) => {
if (e.target.checked) {
pushItemToKey("eventTypeIds", eventType.id);
} else if (!e.target.checked) {
removeItemByKeyAndValue("eventTypeIds", eventType.id);
}
}}
label={eventType.title}
/>
))}
{groupedEventTypes[teamName]
.filter((eventType) => eventType.title.toLowerCase().includes(search.toLowerCase()))
.map((eventType) => (
<FilterCheckboxField
key={eventType.id}
checked={query.eventTypeIds?.includes(eventType.id)}
onChange={(e) => {
if (e.target.checked) {
pushItemToKey("eventTypeIds", eventType.id);
} else if (!e.target.checked) {
removeItemByKeyAndValue("eventTypeIds", eventType.id);
}
}}
label={eventType.title}
/>
))}
</Fragment>
))}
</FilterCheckboxFieldsContainer>
@@ -24,7 +24,7 @@ export const PeopleFilter = () => {
const debouncedSearch = useDebounce(searchText, 500);
const queryMembers = trpc.viewer.teams.legacyListMembers.useInfiniteQuery(
{ limit: 10, searchText: debouncedSearch },
{ limit: 10, searchText: debouncedSearch, includeEmail: true },
{
enabled: true,
getNextPageParam: (lastPage) => lastPage.nextCursor,
@@ -15,7 +15,7 @@ import { ZGetUserConnectedAppsInputSchema } from "./getUserConnectedApps.schema"
import { ZHasEditPermissionForUserSchema } from "./hasEditPermissionForUser.schema";
import { ZInviteMemberInputSchema } from "./inviteMember/inviteMember.schema";
import { ZInviteMemberByTokenSchemaInputSchema } from "./inviteMemberByToken.schema";
import { ZListMembersInputSchema as ZLegegacyListMembers } from "./legacyListMembers.schema";
import { ZLegacyListMembersInputSchema } from "./legacyListMembers.schema";
import { ZGetListSchema } from "./list.schema";
import { ZListMembersInputSchema } from "./listMembers.schema";
import { hasTeamPlan } from "./procedures/hasTeamPlan";
@@ -121,7 +121,7 @@ export const viewerTeamsRouter = router({
const handler = await importHandler(namespaced("listMembers"), () => import("./listMembers.handler"));
return handler(opts);
}),
legacyListMembers: authedProcedure.input(ZLegegacyListMembers).query(async (opts) => {
legacyListMembers: authedProcedure.input(ZLegacyListMembersInputSchema).query(async (opts) => {
const handler = await importHandler(
namespaced("legacyListMembers"),
() => import("./legacyListMembers.handler")
@@ -1,15 +1,17 @@
import type { Prisma } from "@prisma/client";
import { UserRepository } from "@calcom/lib/server/repository/user";
import type { PrismaClient } from "@calcom/prisma";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
import type { TListMembersInputSchema } from "./legacyListMembers.schema";
import type { TLegacyListMembersInputSchema } from "./legacyListMembers.schema";
type ListMembersOptions = {
ctx: {
user: NonNullable<TrpcSessionUser>;
prisma: PrismaClient;
};
input: TListMembersInputSchema;
input: TLegacyListMembersInputSchema;
};
export const legacyListMembers = async ({ ctx, input }: ListMembersOptions) => {
@@ -57,6 +59,15 @@ export const legacyListMembers = async ({ ctx, input }: ListMembersOptions) => {
};
}
const searchTextClauses: Prisma.UserWhereInput[] = [
{ name: { contains: input.searchText, mode: "insensitive" } },
{ username: { contains: input.searchText, mode: "insensitive" } },
];
if (input.includeEmail) {
searchTextClauses.push({ email: { contains: input.searchText, mode: "insensitive" } });
}
// Fetch unique users through memberships
const memberships = await prisma.membership.findMany({
where: {
@@ -64,10 +75,7 @@ export const legacyListMembers = async ({ ctx, input }: ListMembersOptions) => {
teamId: { in: teamsToQuery },
user: input.searchText?.trim()?.length
? {
OR: [
{ name: { contains: input.searchText, mode: "insensitive" } },
{ username: { contains: input.searchText, mode: "insensitive" } },
],
OR: searchTextClauses,
}
: undefined,
},
@@ -80,6 +88,7 @@ export const legacyListMembers = async ({ ctx, input }: ListMembersOptions) => {
name: true,
username: true,
avatarUrl: true,
email: true,
},
},
},
@@ -7,4 +7,9 @@ export const ZListMembersInputSchema = z.object({
cursor: z.number().nullish(),
});
export const ZLegacyListMembersInputSchema = ZListMembersInputSchema.extend({
includeEmail: z.boolean().optional(),
});
export type TListMembersInputSchema = z.infer<typeof ZListMembersInputSchema>;
export type TLegacyListMembersInputSchema = z.infer<typeof ZLegacyListMembersInputSchema>;