chore: remove not used code related to filters on /bookings (#19450)
* chore: remove filter-related code that is no longer in use * remove groupBy.ts
This commit is contained in:
@@ -1,113 +0,0 @@
|
||||
import { useSession } from "next-auth/react";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
|
||||
import {
|
||||
FilterCheckboxField,
|
||||
FilterCheckboxFieldsContainer,
|
||||
} from "@calcom/features/filters/components/TeamsFilter";
|
||||
import type { IEventTypeFilter } from "@calcom/features/filters/types/filter";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { FilterSearchField } from "@calcom/ui";
|
||||
import { AnimatedPopover, Divider, Icon } from "@calcom/ui";
|
||||
|
||||
import { groupBy } from "../groupBy";
|
||||
import { useFilterQuery } from "../lib/useFilterQuery";
|
||||
|
||||
type GroupedEventTypeState = Record<
|
||||
string,
|
||||
{
|
||||
team: {
|
||||
id: number;
|
||||
name: string;
|
||||
} | null;
|
||||
id: number;
|
||||
title: string;
|
||||
slug: string;
|
||||
}[]
|
||||
>;
|
||||
|
||||
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,
|
||||
});
|
||||
const groupedEventTypes: GroupedEventTypeState | null = useMemo(() => {
|
||||
const data = eventTypes.data;
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
// Will be handled up the tree to redirect
|
||||
// Group event types by team
|
||||
const grouped = groupBy<IEventTypeFilter>(
|
||||
data.filter((el) => el.team),
|
||||
(item) => item?.team?.name || ""
|
||||
); // Add the team name
|
||||
const individualEvents = data.filter((el) => !el.team);
|
||||
// push individual events to the start of grouped array
|
||||
return individualEvents.length > 0 ? { user_own_event_types: individualEvents, ...grouped } : grouped;
|
||||
}, [eventTypes.data]);
|
||||
|
||||
if (!eventTypes.data) return null;
|
||||
const isEmpty = eventTypes.data.length === 0;
|
||||
|
||||
const getTextForPopover = () => {
|
||||
const eventTypeIds = query.eventTypeIds;
|
||||
if (eventTypeIds) {
|
||||
return `${t("number_selected", { count: eventTypeIds.length })}`;
|
||||
}
|
||||
return `${t("all")}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<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" />}
|
||||
checked={!query.eventTypeIds?.length}
|
||||
onChange={removeAllQueryParams}
|
||||
label={t("all_event_types_filter_label")}
|
||||
/>
|
||||
<Divider />
|
||||
{groupedEventTypes &&
|
||||
Object.keys(groupedEventTypes).map((teamName) => (
|
||||
<Fragment key={teamName}>
|
||||
<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]
|
||||
.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>
|
||||
) : (
|
||||
<h2 className="text-default px-4 py-2 text-sm font-medium">{t("no_options_available")}</h2>
|
||||
)}
|
||||
</AnimatedPopover>
|
||||
);
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
|
||||
import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Badge, Button, Tooltip } from "@calcom/ui";
|
||||
|
||||
export interface FilterToggleProps {
|
||||
setIsFiltersVisible: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export function FilterToggle({ setIsFiltersVisible }: FilterToggleProps) {
|
||||
const {
|
||||
data: { teamIds, userIds, eventTypeIds },
|
||||
} = useFilterQuery();
|
||||
const { t } = useLocale();
|
||||
|
||||
function toggleFiltersVisibility() {
|
||||
setIsFiltersVisible((prev) => !prev);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button color="secondary" onClick={toggleFiltersVisibility} className="mb-4" StartIcon="filter">
|
||||
<div className="flex items-center">
|
||||
<Tooltip content={t("filters")}>
|
||||
<div className="mx-2">{t("filters")}</div>
|
||||
</Tooltip>
|
||||
{(teamIds || userIds || eventTypeIds) && (
|
||||
<Badge variant="gray" rounded>
|
||||
{(teamIds ? 1 : 0) + (userIds ? 1 : 0) + (eventTypeIds ? 1 : 0)}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
import { PeopleFilter } from "@calcom/features/bookings/components/PeopleFilter";
|
||||
import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import { StartTimeFilters } from "@calcom/features/filters/components/StartTimeFilters";
|
||||
import { TeamsFilter } from "@calcom/features/filters/components/TeamsFilter";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Tooltip, Button } from "@calcom/ui";
|
||||
|
||||
import { EventTypeFilter } from "./EventTypeFilter";
|
||||
|
||||
export interface FiltersContainerProps {
|
||||
isFiltersVisible: boolean;
|
||||
}
|
||||
|
||||
export function FiltersContainer({ isFiltersVisible }: FiltersContainerProps) {
|
||||
const [animationParentRef] = useAutoAnimate<HTMLDivElement>();
|
||||
const { removeAllQueryParams } = useFilterQuery();
|
||||
const { t } = useLocale();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const validFilterKeys = ["userIds", "eventTypeIds", "upIds", "teamIds", "afterStartDate", "beforeEndDate"];
|
||||
const hasValidQueryParams = Array.from(searchParams?.keys() ?? []).some((key) =>
|
||||
validFilterKeys.includes(key)
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={animationParentRef}>
|
||||
{isFiltersVisible ? (
|
||||
<div className="no-scrollbar mb-2 flex w-full space-x-2 overflow-x-scroll rtl:space-x-reverse">
|
||||
<PeopleFilter />
|
||||
<EventTypeFilter />
|
||||
<TeamsFilter />
|
||||
<StartTimeFilters />
|
||||
<Tooltip content={t("remove_filters")}>
|
||||
<Button
|
||||
disabled={!hasValidQueryParams}
|
||||
color="secondary"
|
||||
type="button"
|
||||
onClick={removeAllQueryParams}>
|
||||
{t("remove_filters")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import {
|
||||
FilterCheckboxField,
|
||||
FilterCheckboxFieldsContainer,
|
||||
} from "@calcom/features/filters/components/TeamsFilter";
|
||||
import { useDebounce } from "@calcom/lib/hooks/useDebounce";
|
||||
import { useInViewObserver } from "@calcom/lib/hooks/useInViewObserver";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { AnimatedPopover, Avatar, Divider, FilterSearchField, Icon, Button } from "@calcom/ui";
|
||||
|
||||
export const PeopleFilter = () => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery();
|
||||
const isAdmin = currentOrg?.user.role === "ADMIN" || currentOrg?.user.role === "OWNER";
|
||||
const hasPermToView = !currentOrg?.isPrivate || isAdmin;
|
||||
|
||||
const { data: query, pushItemToKey, removeItemByKeyAndValue, removeAllQueryParams } = useFilterQuery();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
const debouncedSearch = useDebounce(searchText, 500);
|
||||
|
||||
const queryMembers = trpc.viewer.teams.legacyListMembers.useInfiniteQuery(
|
||||
{ limit: 10, searchText: debouncedSearch, includeEmail: true },
|
||||
{
|
||||
enabled: true,
|
||||
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
||||
}
|
||||
);
|
||||
|
||||
const { ref: observerRef } = useInViewObserver(() => {
|
||||
if (queryMembers.hasNextPage && !queryMembers.isFetching) {
|
||||
queryMembers.fetchNextPage();
|
||||
}
|
||||
}, document.querySelector('[role="dialog"]'));
|
||||
|
||||
const filteredMembers = queryMembers?.data?.pages.flatMap((page) => page.members);
|
||||
|
||||
const getTextForPopover = () => {
|
||||
const userIds = query.userIds;
|
||||
if (userIds) {
|
||||
return `${t("number_selected", { count: userIds.length })}`;
|
||||
}
|
||||
return `${t("all")}`;
|
||||
};
|
||||
|
||||
if (!hasPermToView) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatedPopover text={getTextForPopover()} prefix={`${t("people")}: `}>
|
||||
<FilterCheckboxFieldsContainer>
|
||||
<FilterSearchField onChange={(e) => setSearchText(e.target.value)} placeholder={t("search")} />
|
||||
|
||||
<FilterCheckboxField
|
||||
id="all"
|
||||
icon={<Icon name="user" className="h-4 w-4" />}
|
||||
checked={!query.userIds?.length}
|
||||
onChange={removeAllQueryParams}
|
||||
label={t("all_users_filter_label")}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{filteredMembers?.map((member) => (
|
||||
<FilterCheckboxField
|
||||
key={member.id}
|
||||
id={member.id.toString()}
|
||||
label={member?.name ?? member.username ?? t("no_name")}
|
||||
checked={!!query.userIds?.includes(member.id)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
pushItemToKey("userIds", member.id);
|
||||
} else if (!e.target.checked) {
|
||||
removeItemByKeyAndValue("userIds", member.id);
|
||||
}
|
||||
}}
|
||||
icon={<Avatar alt={`${member?.id} avatar`} imageSrc={member.avatarUrl} size="xs" />}
|
||||
/>
|
||||
))}
|
||||
<div className="text-default text-center" ref={observerRef} data-testid="people-filter">
|
||||
<Button
|
||||
color="minimal"
|
||||
loading={queryMembers.isFetchingNextPage}
|
||||
disabled={!queryMembers.hasNextPage}
|
||||
onClick={() => queryMembers.fetchNextPage()}
|
||||
data-testid="people-filter">
|
||||
{queryMembers.hasNextPage ? t("load_more_results") : t("no_more_results")}
|
||||
</Button>
|
||||
</div>
|
||||
</FilterCheckboxFieldsContainer>
|
||||
</AnimatedPopover>
|
||||
);
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
type KeySelector<T> = (item: T) => string;
|
||||
|
||||
export function groupBy<T>(array: Iterable<T>, keySelector: KeySelector<T>): Record<string, T[]> {
|
||||
return Array.from(array).reduce(
|
||||
(acc: Record<string, T[]>, item: T) => {
|
||||
const key = keySelector(item);
|
||||
if (key in acc) {
|
||||
// found key, push new item into existing array
|
||||
acc[key].push(item);
|
||||
} else {
|
||||
// did not find key, create new array
|
||||
acc[key] = [item];
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} // start with empty object
|
||||
);
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import type { Dayjs } from "@calcom/dayjs";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import { DateRangePicker } from "@calcom/ui";
|
||||
|
||||
export const StartTimeFilters = () => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const { data: query } = useFilterQuery();
|
||||
|
||||
const getQueryDate = (param: string) =>
|
||||
searchParams?.get(param) ? dayjs(searchParams?.get(param)) : undefined;
|
||||
|
||||
const [afterStartDate, setAfterStartDate] = useState<Dayjs | undefined>(() =>
|
||||
getQueryDate("afterStartDate")
|
||||
);
|
||||
const [beforeEndDate, setBeforeEndDate] = useState<Dayjs | undefined>(() => getQueryDate("beforeEndDate"));
|
||||
|
||||
const startValue = afterStartDate?.toDate();
|
||||
const endValue = beforeEndDate?.toDate();
|
||||
|
||||
const updateUrlParams = (newStartDate: Dayjs, newEndDate: Dayjs) => {
|
||||
const search = new URLSearchParams(searchParams?.toString());
|
||||
|
||||
Object.entries(query).forEach(([key, value]) => {
|
||||
if (key !== "afterStartDate" && key !== "beforeEndDate") {
|
||||
search.set(key, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
if (newStartDate) {
|
||||
search.set("afterStartDate", newStartDate.startOf("day").format("YYYY-MM-DDTHH:mm:ss"));
|
||||
}
|
||||
if (newEndDate) {
|
||||
search.set("beforeEndDate", newEndDate.endOf("day").format("YYYY-MM-DDTHH:mm:ss"));
|
||||
}
|
||||
|
||||
router.replace(`${pathname}?${search.toString()}`);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
//if params has date range
|
||||
if (searchParams?.has("afterStartDate") && searchParams?.has("beforeEndDate")) {
|
||||
setAfterStartDate(getQueryDate("afterStartDate"));
|
||||
setBeforeEndDate(getQueryDate("beforeEndDate"));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(query).length === 1 && "status" in query && afterStartDate && beforeEndDate) {
|
||||
setAfterStartDate(undefined);
|
||||
setBeforeEndDate(undefined);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<DateRangePicker
|
||||
minDate={null}
|
||||
dates={{ startDate: startValue, endDate: endValue }}
|
||||
onDatesChange={(values) => {
|
||||
const newAfterStartDate = values.startDate ? dayjs(values.startDate) : undefined;
|
||||
const newBeforeEndDate = values.endDate ? dayjs(values.endDate) : undefined;
|
||||
setAfterStartDate(newAfterStartDate);
|
||||
setBeforeEndDate(newBeforeEndDate);
|
||||
|
||||
if (newAfterStartDate && newBeforeEndDate) {
|
||||
updateUrlParams(newAfterStartDate, newBeforeEndDate);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -64,10 +64,6 @@ export const legacyListMembers = async ({ ctx, input }: ListMembersOptions) => {
|
||||
{ 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: {
|
||||
|
||||
@@ -7,9 +7,7 @@ export const ZListMembersInputSchema = z.object({
|
||||
cursor: z.number().nullish(),
|
||||
});
|
||||
|
||||
export const ZLegacyListMembersInputSchema = ZListMembersInputSchema.extend({
|
||||
includeEmail: z.boolean().optional(),
|
||||
});
|
||||
export const ZLegacyListMembersInputSchema = ZListMembersInputSchema.extend({});
|
||||
|
||||
export type TListMembersInputSchema = z.infer<typeof ZListMembersInputSchema>;
|
||||
export type TLegacyListMembersInputSchema = z.infer<typeof ZLegacyListMembersInputSchema>;
|
||||
|
||||
Reference in New Issue
Block a user