feat: filter bookings by event type (#9011)

* filter bookings by event type

* fix: hide event-types sections not having atleast 1 event-type

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
mohammed hussam 2023-05-26 13:19:13 +05:30 committed by GitHub
parent e4a16a86a4
commit 781e0e361d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 34 deletions

View File

@ -1,12 +1,14 @@
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { Fragment, useState, useEffect } from "react"; import { Fragment, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react"; import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react";
import { AnimatedPopover } from "@calcom/ui"; import { AnimatedPopover } from "@calcom/ui";
import { Checkbox } from "@calcom/ui";
import { groupBy } from "../groupBy"; import { groupBy } from "../groupBy";
import { useFilterQuery } from "../lib/useFilterQuery";
export type IEventTypesFilters = RouterOutputs["viewer"]["eventTypes"]["listWithTeam"]; export type IEventTypesFilters = RouterOutputs["viewer"]["eventTypes"]["listWithTeam"];
export type IEventTypeFilter = IEventTypesFilters[0]; export type IEventTypeFilter = IEventTypesFilters[0];
@ -27,24 +29,32 @@ type GroupedEventTypeState = Record<
export const EventTypeFilter = () => { export const EventTypeFilter = () => {
const { t } = useLocale(); const { t } = useLocale();
const { data: user } = useSession(); const { data: user } = useSession();
const eventTypes = trpc.viewer.eventTypes.listWithTeam.useQuery(); const { data: query, pushItemToKey, removeItemByKeyAndValue } = useFilterQuery();
const [groupedEventTypes, setGroupedEventTypes] = useState<GroupedEventTypeState>(); const [groupedEventTypes, setGroupedEventTypes] = useState<GroupedEventTypeState>();
// Will be handled up the tree to redirect
useEffect(() => {
if (!eventTypes.data) return;
// Group event types by team
const grouped = groupBy<IEventTypeFilter>(
eventTypes.data.filter((el) => el.team),
(item) => item?.team?.name || ""
); // Add the team name
const individualEvents = eventTypes.data.filter((el) => !el.team);
// push indivdual events to the start of grouped array
setGroupedEventTypes({ user_own_event_types: individualEvents, ...grouped });
}, [eventTypes.data, user]);
if (!user) return null; const eventTypes = trpc.viewer.eventTypes.listWithTeam.useQuery(undefined, {
onSuccess: (data) => {
// 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 indivdual events to the start of grouped array
setGroupedEventTypes(
individualEvents.length > 0 ? { user_own_event_types: individualEvents, ...grouped } : grouped
);
},
enabled: !!user,
});
return ( if (!eventTypes.data) return null;
const isNotEmpty = eventTypes.data.length > 0;
return eventTypes.status === "success" && isNotEmpty ? (
<AnimatedPopover text={t("event_type")}> <AnimatedPopover text={t("event_type")}>
<div className=""> <div className="">
{groupedEventTypes && {groupedEventTypes &&
@ -54,25 +64,23 @@ export const EventTypeFilter = () => {
{teamName === "user_own_event_types" ? t("individual") : teamName} {teamName === "user_own_event_types" ? t("individual") : teamName}
</div> </div>
{groupedEventTypes[teamName].map((eventType) => ( {groupedEventTypes[teamName].map((eventType) => (
<Fragment key={eventType.id}> <div key={eventType.id} className="item-center flex px-4 py-1.5">
<div className="item-center flex px-4 py-[6px]"> <Checkbox
<p className="text-default block self-center truncate text-sm font-medium"> checked={query.eventTypeIds?.includes(eventType.id)}
{eventType.title} onChange={(e) => {
</p> if (e.target.checked) {
<div className="ml-auto"> pushItemToKey("eventTypeIds", eventType.id);
<input } else if (!e.target.checked) {
type="checkbox" removeItemByKeyAndValue("eventTypeIds", eventType.id);
name="" }
id="" }}
className="text-primary-600 focus:ring-primary-500 border-default h-4 w-4 rounded ltr:mr-2 rtl:ml-2 " description={eventType.title}
/> />
</div> </div>
</div>
</Fragment>
))} ))}
</Fragment> </Fragment>
))} ))}
</div> </div>
</AnimatedPopover> </AnimatedPopover>
); ) : null;
}; };

View File

@ -1,9 +1,11 @@
import { Fragment, ReactNode } from "react"; import type { ReactNode } from "react";
import { Fragment } from "react";
import { EventTypeFilter } from "./EventTypeFilter";
import { PeopleFilter } from "./PeopleFilter"; import { PeopleFilter } from "./PeopleFilter";
import { TeamsMemberFilter } from "./TeamFilter"; import { TeamsMemberFilter } from "./TeamFilter";
type FilterTypes = "teams" | "people"; type FilterTypes = "teams" | "people" | "eventType";
type Filter = { type Filter = {
name: FilterTypes; name: FilterTypes;
@ -25,6 +27,12 @@ const filters: Filter[] = [
controllingQueryParams: ["usersId"], controllingQueryParams: ["usersId"],
showByDefault: true, showByDefault: true,
}, },
{
name: "eventType",
component: <EventTypeFilter />,
controllingQueryParams: ["eventTypeId"],
showByDefault: true,
},
]; ];
export function FiltersContainer() { export function FiltersContainer() {

View File

@ -102,6 +102,15 @@ export const getHandler = async ({ ctx, input }: GetOptions) => {
}, },
], ],
}, },
eventTypeIds: {
AND: [
{
eventTypeId: {
in: input.filters?.eventTypeIds,
},
},
],
},
}; };
const filtersCombined: Prisma.BookingWhereInput[] = const filtersCombined: Prisma.BookingWhereInput[] =