Skip to content

fix: separate Filter Segment implementation from DataTableProvider #20587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/web/modules/bookings/views/bookings-listing-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ZDateRangeFilterValue,
ZTextFilterValue,
} from "@calcom/features/data-table";
import { useSegments } from "@calcom/features/data-table/hooks/useSegments";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
Expand Down Expand Up @@ -86,7 +87,7 @@ type BookingsProps = {

export default function Bookings(props: BookingsProps) {
return (
<DataTableProvider>
<DataTableProvider useSegments={useSegments}>
<BookingsContent {...props} />
</DataTableProvider>
);
Expand Down
3 changes: 2 additions & 1 deletion apps/web/modules/insights/insights-routing-view.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { DataTableProvider } from "@calcom/features/data-table/DataTableProvider";
import { useSegments } from "@calcom/features/data-table/hooks/useSegments";
import {
RoutingFormResponsesTable,
FailedBookingsByField,
Expand All @@ -13,7 +14,7 @@ export default function InsightsRoutingFormResponsesPage() {
const { t } = useLocale();

return (
<DataTableProvider>
<DataTableProvider useSegments={useSegments}>
<InsightsOrgTeamsProvider>
<div className="mb-4 space-y-4">
<RoutingFormResponsesTable />
Expand Down
53 changes: 30 additions & 23 deletions packages/features/data-table/DataTableProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { usePathname } from "next/navigation";
import { useQueryState, parseAsArrayOf, parseAsJson, parseAsInteger, parseAsString } from "nuqs";
import { createContext, useCallback, useEffect, useRef, useMemo } from "react";

import { useSegments } from "./lib/segments";
import { useSegmentsNoop } from "./hooks/useSegmentsNoop";
import {
type FilterValue,
ZSorting,
Expand All @@ -16,6 +16,7 @@ import {
ZColumnSizing,
type FilterSegmentOutput,
type ActiveFilters,
type UseSegments,
} from "./lib/types";
import { CTA_CONTAINER_CLASS_NAME } from "./lib/utils";

Expand Down Expand Up @@ -51,6 +52,7 @@ export type DataTableContextType = {
segmentId: number | undefined;
setSegmentId: (id: number | null) => void;
canSaveSegment: boolean;
isSegmentEnabled: boolean;

searchTerm: string;
setSearchTerm: (searchTerm: string | null) => void;
Expand All @@ -65,6 +67,7 @@ const DEFAULT_COLUMN_SIZING: ColumnSizingState = {};
const DEFAULT_PAGE_SIZE = 10;

interface DataTableProviderProps {
useSegments?: UseSegments;
tableIdentifier?: string;
children: React.ReactNode;
ctaContainerClassName?: string;
Expand All @@ -74,6 +77,7 @@ interface DataTableProviderProps {
export function DataTableProvider({
tableIdentifier: _tableIdentifier,
children,
useSegments = useSegmentsNoop,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It accepts useSegments as a prop, and we use the noop implementation as the default value.

defaultPageSize = DEFAULT_PAGE_SIZE,
ctaContainerClassName = CTA_CONTAINER_CLASS_NAME,
}: DataTableProviderProps) {
Expand Down Expand Up @@ -165,36 +169,38 @@ export function DataTableProvider({
[setPageSize, setPageIndex, defaultPageSize]
);

const { segments, selectedSegment, canSaveSegment, setSegmentIdAndSaveToLocalStorage } = useSegments({
tableIdentifier,
activeFilters,
sorting,
columnVisibility,
columnSizing,
pageSize,
searchTerm,
defaultPageSize,
segmentId,
setSegmentId,
setActiveFilters,
setSorting,
setColumnVisibility,
setColumnSizing,
setPageSize,
setPageIndex,
setSearchTerm,
});
const { segments, selectedSegment, canSaveSegment, setAndPersistSegmentId, isSegmentEnabled } = useSegments(
{
tableIdentifier,
activeFilters,
sorting,
columnVisibility,
columnSizing,
pageSize,
searchTerm,
defaultPageSize,
segmentId,
setSegmentId,
setActiveFilters,
setSorting,
setColumnVisibility,
setColumnSizing,
setPageSize,
setPageIndex,
setSearchTerm,
}
);

const clearAll = useCallback(
(exclude?: string[]) => {
setSegmentIdAndSaveToLocalStorage(null);
setAndPersistSegmentId(null);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's renamed (just cleaning up)

setPageIndex(null);
setActiveFilters((prev) => {
const remainingFilters = prev.filter((filter) => exclude?.includes(filter.f));
return remainingFilters.length === 0 ? null : remainingFilters;
});
},
[setActiveFilters, setPageIndex, setSegmentIdAndSaveToLocalStorage]
[setActiveFilters, setPageIndex, setAndPersistSegmentId]
);

const ctaContainerRef = useRef<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -230,8 +236,9 @@ export function DataTableProvider({
segments,
selectedSegment,
segmentId: segmentId || undefined,
setSegmentId: setSegmentIdAndSaveToLocalStorage,
setSegmentId: setAndPersistSegmentId,
canSaveSegment,
isSegmentEnabled,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is now provided from useSegments.

useSegmentsNoop returns false for this.

searchTerm,
setSearchTerm: setDebouncedSearchTerm,
}}>
Expand Down
4 changes: 2 additions & 2 deletions packages/features/data-table/components/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from "@calcom/ui/components/table";

import { useColumnSizingVars } from "../hooks";
import { usePersistentColumnResizing } from "../lib/resizing";
import { useColumnResizing } from "../hooks/useColumnResizing";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib/resizing.ts has been renamed to hooks/useColumnResizing

Also usePersistentColumnResizing has been renamed to useColumnResizing.
Previously the "persisting" part was done by this hook, but since Filter Segment, we've moved that part to useSegments(). So this hook itself doesn't persist anything. So I'm renaming it this way.


export type DataTablePropsFromWrapper<TData> = {
table: ReactTableType<TData>;
Expand Down Expand Up @@ -96,7 +96,7 @@ export function DataTable<TData>({

const columnSizingVars = useColumnSizingVars({ table });

usePersistentColumnResizing({
useColumnResizing({
enabled: Boolean(enableColumnResizing),
table,
tableContainerRef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type SubmenuItem = {

export function FilterSegmentSelect() {
const { t } = useLocale();
const { segments, selectedSegment, segmentId, setSegmentId } = useDataTable();
const { segments, selectedSegment, segmentId, setSegmentId, isSegmentEnabled } = useDataTable();
const [segmentToRename, setSegmentToRename] = useState<FilterSegmentOutput | undefined>();
const [segmentToDuplicate, setSegmentToDuplicate] = useState<FilterSegmentOutput | undefined>();
const [segmentToDelete, setSegmentToDelete] = useState<FilterSegmentOutput | undefined>();
Expand Down Expand Up @@ -99,6 +99,14 @@ export function FilterSegmentSelect() {
];
}, [segments, t]);

if (!isSegmentEnabled) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't use these components when Segment is not enabled, but just in case of mistake, we're early returning a disabled button.

return (
<Button color="secondary" StartIcon="list-filter" EndIcon="chevron-down" disabled>
{t("segment")}
</Button>
);
}

return (
<>
<Dropdown>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function SaveFilterSegmentButton() {
columnSizing,
selectedSegment,
canSaveSegment,
isSegmentEnabled,
setSegmentId,
pageSize,
searchTerm,
Expand Down Expand Up @@ -161,6 +162,14 @@ export function SaveFilterSegmentButton() {
setIsOpen(open);
};

if (!isSegmentEnabled) {
return (
<Button StartIcon="bookmark" color="secondary" disabled>
{t("save")}
</Button>
);
}

return (
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
<DialogTrigger asChild>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type { Header, Table, ColumnSizingState } from "@tanstack/react-table";
import debounce from "lodash/debounce";
import { useCallback, useEffect, useRef, useMemo } from "react";

import { useDebouncedWidth, useDataTable } from "../hooks";
import { useDebouncedWidth, useDataTable } from ".";

type UsePersistentColumnResizingProps<TData> = {
type UseColumnResizingProps<TData> = {
enabled: boolean;
table: Table<TData>;
tableContainerRef: React.RefObject<HTMLDivElement>;
Expand Down Expand Up @@ -81,11 +81,11 @@ function getPartialColumnSizing(columnSizing: ColumnSizingState, columnsToExtrac
}, {} as ColumnSizingState);
}

export function usePersistentColumnResizing<TData>({
export function useColumnResizing<TData>({
enabled,
table,
tableContainerRef,
}: UsePersistentColumnResizingProps<TData>) {
}: UseColumnResizingProps<TData>) {
const initialized = useRef(false);
const columnSizing = useRef<ColumnSizingState>({});
const initialColumnSizing = useRef<ColumnSizingState>({});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,12 @@
import type { SortingState, VisibilityState, ColumnSizingState } from "@tanstack/react-table";
// eslint-disable-next-line no-restricted-imports
import { isEqual } from "lodash";
import { useCallback, useMemo, useEffect } from "react";

import { trpc } from "@calcom/trpc/react";

import { type ActiveFilters, ZSegmentStorage } from "./types";
import { ZSegmentStorage, type UseSegments } from "../lib/types";

type UseSegmentsProps = {
tableIdentifier: string;
activeFilters: ActiveFilters;
sorting: SortingState;
columnVisibility: VisibilityState;
columnSizing: ColumnSizingState;
pageSize: number;
searchTerm: string;
defaultPageSize: number;
segmentId: number;
setSegmentId: (segmentId: number | null) => void;
setActiveFilters: (activeFilters: ActiveFilters) => void;
setSorting: (sorting: SortingState) => void;
setColumnVisibility: (columnVisibility: VisibilityState) => void;
setColumnSizing: (columnSizing: ColumnSizingState) => void;
setPageSize: (pageSize: number) => void;
setPageIndex: (pageIndex: number) => void;
setSearchTerm: (searchTerm: string | null) => void;
};

export function useSegments({
export const useSegments: UseSegments = ({
tableIdentifier,
activeFilters,
sorting,
Expand All @@ -45,7 +24,7 @@ export function useSegments({
setPageSize,
setPageIndex,
setSearchTerm,
}: UseSegmentsProps) {
}) => {
const { data: segments, isFetching: isFetchingSegments } = trpc.viewer.filterSegments.list.useQuery({
tableIdentifier,
});
Expand Down Expand Up @@ -132,7 +111,7 @@ export function useSegments({
defaultPageSize,
]);

const setSegmentIdAndSaveToLocalStorage = useCallback(
const setAndPersistSegmentId = useCallback(
(segmentId: number | null) => {
setSegmentId(segmentId);
saveSegmentToLocalStorage({ tableIdentifier, segmentId });
Expand All @@ -144,9 +123,10 @@ export function useSegments({
segments: segments ?? [],
selectedSegment,
canSaveSegment,
setSegmentIdAndSaveToLocalStorage,
setAndPersistSegmentId,
isSegmentEnabled: true,
};
}
};

const LOCAL_STORAGE_KEY = "data-table:segments";

Expand Down
14 changes: 14 additions & 0 deletions packages/features/data-table/hooks/useSegmentsNoop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// eslint-disable-next-line no-restricted-imports
import { noop } from "lodash";

import { type UseSegments } from "../lib/types";

export const useSegmentsNoop: UseSegments = ({}) => {
return {
segments: [],
selectedSegment: undefined,
canSaveSegment: false,
setAndPersistSegmentId: noop,
isSegmentEnabled: false,
};
};
2 changes: 1 addition & 1 deletion packages/features/data-table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export * from "./components";
export * from "./lib/types";
export * from "./lib/utils";
export * from "./DataTableProvider";
export * from "./lib/resizing";
export * from "./hooks/useColumnResizing";
export * from "./hooks";
39 changes: 34 additions & 5 deletions packages/features/data-table/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { SortingState, ColumnSort, VisibilityState, ColumnSizingState } from "@tanstack/react-table";
import { z } from "zod";

import type { IconName } from "@calcom/ui/components/icon";

export type { SortingState } from "@tanstack/react-table";

export enum ColumnFilterType {
SINGLE_SELECT = "ss",
MULTI_SELECT = "ms",
Expand Down Expand Up @@ -200,11 +203,7 @@ export type TypedColumnFilter<T extends ColumnFilterType> = {
value: FilterValue<T>;
};

export type Sorting = {
id: string;
desc: boolean;
};
export type SortingState = Sorting[];
export type Sorting = ColumnSort;

export const ZSorting = z.object({
id: z.string(),
Expand Down Expand Up @@ -267,3 +266,33 @@ export const ZSegmentStorage = z.record(
segmentId: z.number(),
})
) satisfies z.ZodType<SegmentStorage>;

export type UseSegments = (props: UseSegmentsProps) => UseSegmentsReturn;

type UseSegmentsProps = {
tableIdentifier: string;
activeFilters: ActiveFilters;
sorting: SortingState;
columnVisibility: VisibilityState;
columnSizing: ColumnSizingState;
pageSize: number;
searchTerm: string;
defaultPageSize: number;
segmentId: number;
setSegmentId: (segmentId: number | null) => void;
setActiveFilters: (activeFilters: ActiveFilters) => void;
setSorting: (sorting: SortingState) => void;
setColumnVisibility: (columnVisibility: VisibilityState) => void;
setColumnSizing: (columnSizing: ColumnSizingState) => void;
setPageSize: (pageSize: number) => void;
setPageIndex: (pageIndex: number) => void;
setSearchTerm: (searchTerm: string | null) => void;
};

type UseSegmentsReturn = {
segments: FilterSegmentOutput[];
selectedSegment: FilterSegmentOutput | undefined;
canSaveSegment: boolean;
setAndPersistSegmentId: (segmentId: number | null) => void;
isSegmentEnabled: boolean;
};
Loading
Loading