fix: separate Filter Segment implementation from DataTableProvider (#20587)
* fix: separate Filter Segment implementation from DataTableProvider * disable components * early return * fix type error * add missing export
This commit is contained in:
@@ -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";
|
||||
@@ -86,7 +87,7 @@ type BookingsProps = {
|
||||
|
||||
export default function Bookings(props: BookingsProps) {
|
||||
return (
|
||||
<DataTableProvider>
|
||||
<DataTableProvider useSegments={useSegments}>
|
||||
<BookingsContent {...props} />
|
||||
</DataTableProvider>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
@@ -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 />
|
||||
|
||||
@@ -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,
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
ZColumnSizing,
|
||||
type FilterSegmentOutput,
|
||||
type ActiveFilters,
|
||||
type UseSegments,
|
||||
} from "./lib/types";
|
||||
import { CTA_CONTAINER_CLASS_NAME } from "./lib/utils";
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -74,6 +77,7 @@ interface DataTableProviderProps {
|
||||
export function DataTableProvider({
|
||||
tableIdentifier: _tableIdentifier,
|
||||
children,
|
||||
useSegments = useSegmentsNoop,
|
||||
defaultPageSize = DEFAULT_PAGE_SIZE,
|
||||
ctaContainerClassName = CTA_CONTAINER_CLASS_NAME,
|
||||
}: DataTableProviderProps) {
|
||||
@@ -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);
|
||||
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);
|
||||
@@ -230,8 +236,9 @@ export function DataTableProvider({
|
||||
segments,
|
||||
selectedSegment,
|
||||
segmentId: segmentId || undefined,
|
||||
setSegmentId: setSegmentIdAndSaveToLocalStorage,
|
||||
setSegmentId: setAndPersistSegmentId,
|
||||
canSaveSegment,
|
||||
isSegmentEnabled,
|
||||
searchTerm,
|
||||
setSearchTerm: setDebouncedSearchTerm,
|
||||
}}>
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
} from "@calcom/ui/components/table";
|
||||
|
||||
import { useColumnSizingVars } from "../hooks";
|
||||
import { usePersistentColumnResizing } from "../lib/resizing";
|
||||
import { useColumnResizing } from "../hooks/useColumnResizing";
|
||||
|
||||
export type DataTablePropsFromWrapper<TData> = {
|
||||
table: ReactTableType<TData>;
|
||||
@@ -96,7 +96,7 @@ export function DataTable<TData>({
|
||||
|
||||
const columnSizingVars = useColumnSizingVars({ table });
|
||||
|
||||
usePersistentColumnResizing({
|
||||
useColumnResizing({
|
||||
enabled: Boolean(enableColumnResizing),
|
||||
table,
|
||||
tableContainerRef,
|
||||
|
||||
@@ -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>();
|
||||
@@ -99,6 +99,14 @@ export function FilterSegmentSelect() {
|
||||
];
|
||||
}, [segments, t]);
|
||||
|
||||
if (!isSegmentEnabled) {
|
||||
return (
|
||||
<Button color="secondary" StartIcon="list-filter" EndIcon="chevron-down" disabled>
|
||||
{t("segment")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown>
|
||||
|
||||
@@ -50,6 +50,7 @@ export function SaveFilterSegmentButton() {
|
||||
columnSizing,
|
||||
selectedSegment,
|
||||
canSaveSegment,
|
||||
isSegmentEnabled,
|
||||
setSegmentId,
|
||||
pageSize,
|
||||
searchTerm,
|
||||
@@ -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>
|
||||
|
||||
+4
-4
@@ -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>;
|
||||
@@ -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>({});
|
||||
+7
-27
@@ -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,
|
||||
@@ -45,7 +24,7 @@ export function useSegments({
|
||||
setPageSize,
|
||||
setPageIndex,
|
||||
setSearchTerm,
|
||||
}: UseSegmentsProps) {
|
||||
}) => {
|
||||
const { data: segments, isFetching: isFetchingSegments } = trpc.viewer.filterSegments.list.useQuery({
|
||||
tableIdentifier,
|
||||
});
|
||||
@@ -132,7 +111,7 @@ export function useSegments({
|
||||
defaultPageSize,
|
||||
]);
|
||||
|
||||
const setSegmentIdAndSaveToLocalStorage = useCallback(
|
||||
const setAndPersistSegmentId = useCallback(
|
||||
(segmentId: number | null) => {
|
||||
setSegmentId(segmentId);
|
||||
saveSegmentToLocalStorage({ tableIdentifier, segmentId });
|
||||
@@ -144,9 +123,10 @@ export function useSegments({
|
||||
segments: segments ?? [],
|
||||
selectedSegment,
|
||||
canSaveSegment,
|
||||
setSegmentIdAndSaveToLocalStorage,
|
||||
setAndPersistSegmentId,
|
||||
isSegmentEnabled: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const LOCAL_STORAGE_KEY = "data-table:segments";
|
||||
|
||||
@@ -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,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";
|
||||
|
||||
@@ -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",
|
||||
@@ -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(),
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
DataTableFilters,
|
||||
DataTableSegment,
|
||||
} from "@calcom/features/data-table";
|
||||
import { useSegments } from "@calcom/features/data-table/hooks/useSegments";
|
||||
import ServerTrans from "@calcom/lib/components/ServerTrans";
|
||||
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
|
||||
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
|
||||
@@ -63,7 +64,7 @@ interface OutOfOfficeEntry {
|
||||
|
||||
export default function OutOfOfficeEntriesList() {
|
||||
return (
|
||||
<DataTableProvider>
|
||||
<DataTableProvider useSegments={useSegments}>
|
||||
<OutOfOfficeEntriesListContent />
|
||||
</DataTableProvider>
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
useColumnFilters,
|
||||
useDataTable,
|
||||
} from "@calcom/features/data-table";
|
||||
import { useSegments } from "@calcom/features/data-table/hooks/useSegments";
|
||||
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc";
|
||||
@@ -58,7 +59,10 @@ type PlatformManagedUsersTableProps = {
|
||||
|
||||
export function PlatformManagedUsersTable(props: PlatformManagedUsersTableProps) {
|
||||
return (
|
||||
<DataTableProvider defaultPageSize={25} tableIdentifier={`platform-managed-users-${props.oAuthClientId}`}>
|
||||
<DataTableProvider
|
||||
useSegments={useSegments}
|
||||
defaultPageSize={25}
|
||||
tableIdentifier={`platform-managed-users-${props.oAuthClientId}`}>
|
||||
<UserListTableContent {...props} />
|
||||
</DataTableProvider>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
convertFacetedValuesToMap,
|
||||
useDataTable,
|
||||
} from "@calcom/features/data-table";
|
||||
import { useSegments } from "@calcom/features/data-table/hooks/useSegments";
|
||||
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import {
|
||||
@@ -104,7 +105,7 @@ function reducer(state: UserTableState, action: UserTableAction): UserTableState
|
||||
|
||||
export function UserListTable() {
|
||||
return (
|
||||
<DataTableProvider defaultPageSize={25}>
|
||||
<DataTableProvider useSegments={useSegments} defaultPageSize={25}>
|
||||
<UserListTableContent />
|
||||
</DataTableProvider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user