Skip to content

Commit 7a0b8a9

Browse files
authored
refactor: extract logic as bookingDetailsSheetStore (#25129)
* refactor: extract logic as bookingDetailsSheetStore * clean up * revert something * provide store from context
1 parent 8043505 commit 7a0b8a9

File tree

7 files changed

+217
-109
lines changed

7 files changed

+217
-109
lines changed

apps/web/modules/bookings/components/BookingDetailsSheet.tsx

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,54 +29,88 @@ import {
2929
import { BookingActionsDropdown } from "../../../components/booking/actions/BookingActionsDropdown";
3030
import { BookingActionsStoreProvider } from "../../../components/booking/actions/BookingActionsStoreProvider";
3131
import type { BookingListingStatus } from "../../../components/booking/types";
32+
import {
33+
useBookingDetailsSheetStore,
34+
useBookingDetailsSheetStoreApi,
35+
} from "../store/bookingDetailsSheetStore";
3236
import type { BookingOutput } from "../types";
3337
import { JoinMeetingButton } from "./JoinMeetingButton";
3438

3539
type BookingMetaData = z.infer<typeof bookingMetadataSchema>;
3640

3741
interface BookingDetailsSheetProps {
38-
booking: BookingOutput | null;
39-
isOpen: boolean;
40-
onClose: () => void;
4142
userTimeZone?: string;
4243
userTimeFormat?: number;
4344
userId?: number;
4445
userEmail?: string;
45-
onPrevious?: () => void;
46-
hasPrevious?: boolean;
47-
onNext?: () => void;
48-
hasNext?: boolean;
4946
}
5047

51-
interface BookingDetailsSheetInnerProps extends Omit<BookingDetailsSheetProps, "booking"> {
52-
booking: BookingOutput;
53-
}
48+
export function BookingDetailsSheet({
49+
userTimeZone,
50+
userTimeFormat,
51+
userId,
52+
userEmail,
53+
}: BookingDetailsSheetProps) {
54+
const booking = useBookingDetailsSheetStore((state) => state.getSelectedBooking());
5455

55-
export function BookingDetailsSheet(props: BookingDetailsSheetProps) {
56-
if (!props.booking) return null;
56+
// Return null if no booking is selected (sheet is closed)
57+
if (!booking) return null;
5758

5859
return (
5960
<BookingActionsStoreProvider>
60-
<BookingDetailsSheetInner {...props} booking={props.booking} />
61+
<BookingDetailsSheetInner
62+
booking={booking}
63+
userTimeZone={userTimeZone}
64+
userTimeFormat={userTimeFormat}
65+
userId={userId}
66+
userEmail={userEmail}
67+
/>
6168
</BookingActionsStoreProvider>
6269
);
6370
}
6471

72+
interface BookingDetailsSheetInnerProps {
73+
booking: BookingOutput;
74+
userTimeZone?: string;
75+
userTimeFormat?: number;
76+
userId?: number;
77+
userEmail?: string;
78+
}
79+
6580
function BookingDetailsSheetInner({
6681
booking,
67-
isOpen,
68-
onClose,
6982
userTimeZone,
7083
userTimeFormat,
7184
userId,
7285
userEmail,
73-
onPrevious,
74-
hasPrevious = false,
75-
onNext,
76-
hasNext = false,
7786
}: BookingDetailsSheetInnerProps) {
7887
const { t } = useLocale();
7988

89+
// Get navigation state directly from the store
90+
const hasNext = useBookingDetailsSheetStore((state) => state.hasNext());
91+
const hasPrevious = useBookingDetailsSheetStore((state) => state.hasPrevious());
92+
const setSelectedBookingId = useBookingDetailsSheetStore((state) => state.setSelectedBookingId);
93+
94+
const handleClose = () => {
95+
setSelectedBookingId(null);
96+
};
97+
98+
const storeApi = useBookingDetailsSheetStoreApi();
99+
100+
const handleNext = () => {
101+
const nextId = storeApi.getState().getNextBookingId();
102+
if (nextId !== null) {
103+
setSelectedBookingId(nextId);
104+
}
105+
};
106+
107+
const handlePrevious = () => {
108+
const prevId = storeApi.getState().getPreviousBookingId();
109+
if (prevId !== null) {
110+
setSelectedBookingId(prevId);
111+
}
112+
};
113+
80114
const startTime = dayjs(booking.startTime).tz(userTimeZone);
81115
const endTime = dayjs(booking.endTime).tz(userTimeZone);
82116

@@ -115,7 +149,7 @@ function BookingDetailsSheetInner({
115149
: [];
116150

117151
return (
118-
<Sheet open={isOpen} onOpenChange={onClose}>
152+
<Sheet open={true} onOpenChange={handleClose}>
119153
<SheetContent className="overflow-y-auto">
120154
<SheetHeader
121155
showCloseButton={false}
@@ -128,7 +162,7 @@ function BookingDetailsSheetInner({
128162
disabled={!hasPrevious}
129163
onClick={(e) => {
130164
e.preventDefault();
131-
onPrevious?.();
165+
handlePrevious();
132166
}}
133167
/>
134168
<Button
@@ -138,7 +172,7 @@ function BookingDetailsSheetInner({
138172
disabled={!hasNext}
139173
onClick={(e) => {
140174
e.preventDefault();
141-
onNext?.();
175+
handleNext();
142176
}}
143177
/>
144178
</div>
@@ -190,7 +224,7 @@ function BookingDetailsSheetInner({
190224

191225
<SheetFooter className="bg-muted border-subtle -mx-4 -mb-4 border-t pt-0 sm:-mx-6 sm:-my-6">
192226
<div className="flex w-full flex-row items-center justify-between gap-2 px-4 pb-4 pt-4">
193-
<Button color="secondary" StartIcon="x" onClick={onClose}>
227+
<Button color="secondary" StartIcon="x" onClick={handleClose}>
194228
{t("close")}
195229
</Button>
196230

apps/web/modules/bookings/components/BookingsCalendar.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ type BookingsCalendarProps = {
1919
status: BookingListingStatus;
2020
table: ReactTable<RowData>;
2121
isPending?: boolean;
22-
onOpenDetails: (bookingId: number) => void;
2322
currentWeekStart: dayjs.Dayjs;
2423
setCurrentWeekStart: (
2524
value: dayjs.Dayjs | ((old: dayjs.Dayjs) => dayjs.Dayjs | null) | null
@@ -32,7 +31,6 @@ const COLUMN_IDS_TO_HIDE = ["dateRange"];
3231
export function BookingsCalendar({
3332
table,
3433
isPending = false,
35-
onOpenDetails,
3634
currentWeekStart,
3735
setCurrentWeekStart,
3836
bookings,
@@ -77,7 +75,6 @@ export function BookingsCalendar({
7775
currentWeekStart={currentWeekStart}
7876
onWeekStartChange={handleWeekStartChange}
7977
isPending={isPending}
80-
onOpenDetails={onOpenDetails}
8178
/>
8279
</>
8380
);

apps/web/modules/bookings/components/BookingsCalendarContainer.tsx

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useReactTable, getCoreRowModel, getSortedRowModel } from "@tanstack/react-table";
44
import { createParser, useQueryState } from "nuqs";
5-
import { useCallback, useMemo } from "react";
5+
import { useMemo } from "react";
66

77
import dayjs from "@calcom/dayjs";
88
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -11,8 +11,7 @@ import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
1111
import { useFacetedUniqueValues } from "~/bookings/hooks/useFacetedUniqueValues";
1212

1313
import { buildFilterColumns, getFilterColumnVisibility } from "../columns/filterColumns";
14-
import { useBookingCursor } from "../hooks/useBookingCursor";
15-
import { useSelectedBookingId } from "../hooks/useSelectedBookingId";
14+
import { BookingDetailsSheetStoreProvider } from "../store/bookingDetailsSheetStore";
1615
import type { RowData, BookingListingStatus } from "../types";
1716
import { BookingDetailsSheet } from "./BookingDetailsSheet";
1817
import { BookingsCalendar } from "./BookingsCalendar";
@@ -43,19 +42,11 @@ export function BookingsCalendarContainer({
4342
const { t } = useLocale();
4443
const user = useMeQuery().data;
4544

46-
const [selectedBookingId, setSelectedBookingId] = useSelectedBookingId();
4745
const [currentWeekStart, setCurrentWeekStart] = useQueryState(
4846
"weekStart",
4947
weekStartParser.withDefault(dayjs().startOf("week"))
5048
);
5149

52-
const onOpenDetails = useCallback(
53-
(bookingId: number) => {
54-
setSelectedBookingId(bookingId);
55-
},
56-
[setSelectedBookingId]
57-
);
58-
5950
const columns = useMemo(() => {
6051
return buildFilterColumns({ t, permissions, status });
6152
}, [t, permissions, status]);
@@ -90,42 +81,23 @@ export function BookingsCalendarContainer({
9081
});
9182
}, [data, currentWeekStart]);
9283

93-
const selectedBooking = useMemo(() => {
94-
if (!selectedBookingId) return null;
95-
return bookings.find((booking) => booking.id === selectedBookingId) ?? null;
96-
}, [selectedBookingId, bookings]);
97-
98-
const bookingNavigation = useBookingCursor({
99-
bookings,
100-
selectedBookingId,
101-
setSelectedBookingId,
102-
});
103-
10484
return (
105-
<>
85+
<BookingDetailsSheetStoreProvider bookings={bookings}>
10686
<BookingsCalendar
10787
status={status}
10888
table={table}
10989
isPending={isPending}
110-
onOpenDetails={onOpenDetails}
11190
currentWeekStart={currentWeekStart}
11291
setCurrentWeekStart={setCurrentWeekStart}
11392
bookings={bookings}
11493
/>
11594

11695
<BookingDetailsSheet
117-
booking={selectedBooking}
118-
isOpen={!!selectedBooking}
119-
onClose={() => setSelectedBookingId(null)}
12096
userTimeZone={user?.timeZone}
12197
userTimeFormat={user?.timeFormat === null ? undefined : user?.timeFormat}
12298
userId={user?.id}
12399
userEmail={user?.email}
124-
onPrevious={bookingNavigation.onPrevious}
125-
hasPrevious={bookingNavigation.hasPrevious}
126-
onNext={bookingNavigation.onNext}
127-
hasNext={bookingNavigation.hasNext}
128100
/>
129-
</>
101+
</BookingDetailsSheetStoreProvider>
130102
);
131103
}

apps/web/modules/bookings/components/BookingsCalendarView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ import { Button } from "@calcom/ui/components/button";
1212
import { ButtonGroup } from "@calcom/ui/components/buttonGroup";
1313
import { Icon } from "@calcom/ui/components/icon";
1414

15+
import { useBookingDetailsSheetStore } from "../store/bookingDetailsSheetStore";
1516
import type { BookingOutput } from "../types";
1617

1718
type BookingsCalendarViewProps = {
1819
bookings: BookingOutput[];
1920
currentWeekStart: dayjs.Dayjs;
2021
onWeekStartChange: (weekStart: dayjs.Dayjs) => void;
2122
isPending?: boolean;
22-
onOpenDetails: (bookingId: number) => void;
2323
};
2424

2525
export function BookingsCalendarView({
2626
bookings,
2727
currentWeekStart,
2828
onWeekStartChange,
2929
isPending = false,
30-
onOpenDetails,
3130
}: BookingsCalendarViewProps) {
31+
const setSelectedBookingId = useBookingDetailsSheetStore((state) => state.setSelectedBookingId);
3232
const { t } = useLocale();
3333
const { timezone } = useTimePreferences();
3434
const { resolvedTheme, forcedTheme } = useGetTheme();
@@ -152,7 +152,7 @@ export function BookingsCalendarView({
152152
onEventClick={(event) => {
153153
const bookingId = event.options?.bookingId;
154154
if (bookingId) {
155-
onOpenDetails(bookingId);
155+
setSelectedBookingId(bookingId);
156156
}
157157
}}
158158
hideHeader

apps/web/modules/bookings/components/BookingsList.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { EmptyScreen } from "@calcom/ui/components/empty-screen";
1010

1111
import SkeletonLoader from "@components/booking/SkeletonLoader";
1212

13+
import { useBookingDetailsSheetStore } from "../store/bookingDetailsSheetStore";
1314
import type { RowData, BookingListingStatus } from "../types";
1415

1516
const descriptionByStatus: Record<BookingListingStatus, string> = {
@@ -25,25 +26,19 @@ type BookingsListViewProps = {
2526
table: ReactTable<RowData>;
2627
isPending: boolean;
2728
totalRowCount?: number;
28-
onOpenDetails: (bookingId: number) => void;
2929
};
3030

31-
export function BookingsList({
32-
status,
33-
table,
34-
isPending,
35-
totalRowCount,
36-
onOpenDetails,
37-
}: BookingsListViewProps) {
31+
export function BookingsList({ status, table, isPending, totalRowCount }: BookingsListViewProps) {
3832
const { t } = useLocale();
33+
const setSelectedBookingId = useBookingDetailsSheetStore((state) => state.setSelectedBookingId);
3934

4035
const handleRowClick = useCallback(
4136
(row: Row<RowData>) => {
4237
if (!isSeparatorRow(row.original)) {
43-
onOpenDetails(row.original.booking.id);
38+
setSelectedBookingId(row.original.booking.id);
4439
}
4540
},
46-
[onOpenDetails]
41+
[setSelectedBookingId]
4742
);
4843

4944
return (

0 commit comments

Comments
 (0)