WIP - switchin to query filtering instead of state
This commit is contained in:
parent
c5733c0c9d
commit
7214d14b41
|
@ -5,7 +5,7 @@ import { Fragment } from "react";
|
|||
import { z } from "zod";
|
||||
|
||||
import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components";
|
||||
import BookingLayout, { useFilterStore } from "@calcom/features/bookings/layout/BookingLayout";
|
||||
import BookingLayout from "@calcom/features/bookings/layout/BookingLayout";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { RouterInputs, RouterOutputs, trpc } from "@calcom/trpc/react";
|
||||
import { Alert, Button, EmptyScreen, Icon } from "@calcom/ui";
|
||||
|
@ -42,7 +42,6 @@ const querySchema = z.object({
|
|||
});
|
||||
|
||||
export default function Bookings() {
|
||||
// const selectedUsers = useFilterStore((state) => state.selectedUsers);
|
||||
const router = useRouter();
|
||||
const { status } = router.isReady ? querySchema.parse(router.query) : { status: "upcoming" as const };
|
||||
const { t } = useLocale();
|
||||
|
|
|
@ -1,27 +1,15 @@
|
|||
import { Fragment } from "react";
|
||||
import shallow from "zustand/shallow";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { AnimatedPopover, Avatar } from "@calcom/ui";
|
||||
|
||||
import { useFilterStore } from "../layout/BookingLayout";
|
||||
|
||||
export type ITeamMemberFilters = RouterOutputs["viewer"]["teams"]["listTeamsandMembers"];
|
||||
export type ITeamMemberFilter = ITeamMemberFilters[0];
|
||||
import { useFilterQuery } from "../lib/useFilterQuery";
|
||||
|
||||
export const TeamsMemberFilter = () => {
|
||||
const { selectedUsers, pushSelectedUser, removeSelectedUser } = useFilterStore(
|
||||
(state) => ({
|
||||
selectedUsers: state.selectedUsers,
|
||||
pushSelectedUser: state.pushSelectedUser,
|
||||
removeSelectedUser: state.removeSelectedUser,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const { t } = useLocale();
|
||||
const { data: query, pop, push } = useFilterQuery();
|
||||
const { data } = trpc.viewer.teams.listTeamsandMembers.useQuery();
|
||||
// Will be handled up the tree to redirect
|
||||
|
||||
if (data?.length === 0) return null;
|
||||
|
||||
|
@ -48,17 +36,15 @@ export const TeamsMemberFilter = () => {
|
|||
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={
|
||||
selectedUsers.filter(
|
||||
(selected) => selected.id === team.id && selected.user.id === member.user.id
|
||||
).length > 0
|
||||
}
|
||||
name={`${member.team.id}-${member.user.id}`}
|
||||
checked={query.userIds?.includes(member.user.id) && query.teamIds?.includes(team.id)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
pushSelectedUser({ id: team.id, teamName: team.name, user: member.user });
|
||||
push("userIds", member.user.id);
|
||||
push("teamIds", team.id);
|
||||
} else if (!e.target.checked) {
|
||||
removeSelectedUser(member.user.id);
|
||||
pop("userIds", member.user.id);
|
||||
pop("teamIds", team.id);
|
||||
}
|
||||
}}
|
||||
className="text-primary-600 focus:ring-primary-500 inline-flex h-4 w-4 place-self-center justify-self-end rounded border-gray-300 "
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import React, { ComponentProps } from "react";
|
||||
import create from "zustand";
|
||||
|
||||
import { HorizontalTabs, Shell } from "@calcom/ui";
|
||||
import { VerticalTabItemProps, HorizontalTabItemProps } from "@calcom/ui/v2";
|
||||
|
||||
import { EventTypeFilter, IEventTypeFilter } from "../components/EventTypeFilter";
|
||||
import { TeamsMemberFilter, ITeamMemberFilter } from "../components/TeamsMemberFilter";
|
||||
import { EventTypeFilter} from "../components/EventTypeFilter";
|
||||
import { TeamsMemberFilter } from "../components/TeamsMemberFilter";
|
||||
|
||||
const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
|
||||
{
|
||||
|
@ -30,51 +29,6 @@ const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
|
|||
},
|
||||
];
|
||||
|
||||
interface BookingLayoutStoreState {
|
||||
selectedEventTypes: IEventTypeFilter[];
|
||||
selectedUsers: {
|
||||
id: number;
|
||||
teamName: string;
|
||||
user: ITeamMemberFilter["members"][0]["user"];
|
||||
}[];
|
||||
}
|
||||
interface BookingLayoutStoreActions {
|
||||
setSelectedEventTypes: (eventTypes: IEventTypeFilter[]) => void;
|
||||
setSelectedUsers: (users: BookingLayoutStoreState["selectedUsers"]) => void;
|
||||
pushSelectedEventType: (eventType: IEventTypeFilter) => void;
|
||||
pushSelectedUser: (user: BookingLayoutStoreState["selectedUsers"][0]) => void;
|
||||
removeSelectedEventType: (eventType: IEventTypeFilter) => void;
|
||||
removeSelectedUser: (userId: number) => void;
|
||||
clearState: () => void;
|
||||
}
|
||||
|
||||
// Since this is a layout i thought i'd be best to implement some "Global" component state
|
||||
export const useFilterStore = create<BookingLayoutStoreState & BookingLayoutStoreActions>((set) => ({
|
||||
selectedEventTypes: [],
|
||||
selectedUsers: [],
|
||||
setSelectedEventTypes: (eventTypes) => set({ selectedEventTypes: eventTypes }),
|
||||
setSelectedUsers: (users) => set({ selectedUsers: users }),
|
||||
pushSelectedEventType: (eventType) => {
|
||||
set((state) => ({ selectedEventTypes: [...state.selectedEventTypes, eventType] }));
|
||||
},
|
||||
pushSelectedUser: (user) => {
|
||||
set((state) => ({ selectedUsers: [...state.selectedUsers, user] }));
|
||||
},
|
||||
removeSelectedEventType: (eventType) => {
|
||||
set((state) => ({
|
||||
selectedEventTypes: state.selectedEventTypes.filter((type) => type.id !== eventType.id),
|
||||
}));
|
||||
},
|
||||
removeSelectedUser: (userId) => {
|
||||
set((state) => {
|
||||
return {
|
||||
selectedUsers: state.selectedUsers.filter((selected) => selected.user.id !== userId),
|
||||
};
|
||||
});
|
||||
},
|
||||
clearState: () => set({ selectedEventTypes: [], selectedUsers: [] }),
|
||||
}));
|
||||
|
||||
export default function BookingLayout({
|
||||
children,
|
||||
...rest
|
||||
|
@ -85,8 +39,8 @@ export default function BookingLayout({
|
|||
<div className="flex flex-col lg:flex-row">
|
||||
<HorizontalTabs tabs={tabs} />
|
||||
<div className="flex space-x-2">
|
||||
{/* <TeamsMemberFilter />
|
||||
<EventTypeFilter /> */}
|
||||
<TeamsMemberFilter />
|
||||
<EventTypeFilter />
|
||||
</div>
|
||||
</div>
|
||||
<main className="w-full max-w-6xl">{children}</main>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import { useRouter } from "next/router";
|
||||
import z from "zod";
|
||||
|
||||
// Take string and return number
|
||||
export const queryNumberSchema = z.preprocess(
|
||||
(a) => parseInt(z.string().parse(a), 10),
|
||||
z.number().positive().array()
|
||||
);
|
||||
// Take array as a string and return zod array
|
||||
export const queryNumberArray = z.preprocess(
|
||||
(a) =>
|
||||
z
|
||||
.string()
|
||||
.parse(a)
|
||||
.split(",")
|
||||
.map((a) => parseInt(a, 10)),
|
||||
z.number().positive().array()
|
||||
);
|
||||
// Take string and return return zod string array - comma separated
|
||||
export const queryStringArray = z.preprocess((a) => z.string().parse(a).split(","), z.string().array());
|
||||
|
||||
const filterQuerySchema = z.object({
|
||||
teamIds: queryNumberArray.optional(),
|
||||
status: queryStringArray.optional(), // Not used right now but could be implemented when/if we move status to a filter
|
||||
userIds: queryNumberArray.optional(),
|
||||
eventTypeIds: queryNumberArray.optional(),
|
||||
});
|
||||
|
||||
type FilterQuerySchema = z.infer<typeof filterQuerySchema>;
|
||||
type Keys = keyof FilterQuerySchema;
|
||||
|
||||
export function useFilterQuery() {
|
||||
const router = useRouter();
|
||||
const data = filterQuerySchema.parse(router.query);
|
||||
|
||||
// push item to key of filterQuerySchema and set new query params in url
|
||||
|
||||
const push = (key: Required<Keys>, item: string | number) => {
|
||||
const newData = { ...data };
|
||||
const keyData = newData[key] ?? [];
|
||||
// @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
newData[key] = [...keyData, item];
|
||||
router.push({ query: newData });
|
||||
};
|
||||
|
||||
const pop = (key: Required<Keys>, item: string | number) => {
|
||||
const newData = { ...data };
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore idk how to fix this ?
|
||||
newData[key] = newData[key].filter((a) => a !== item);
|
||||
router.push({ query: newData });
|
||||
};
|
||||
|
||||
return { data, push, pop };
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
"main": "index.ts",
|
||||
"dependencies": {
|
||||
"@lexical/react": "^0.5.0",
|
||||
"lexical": "^0.5.0",
|
||||
"zustand": "^4.1.5"
|
||||
"lexical": "^0.5.0"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user