Files
calendar/packages/features/data-table/components/DataTableToolbar.tsx
T
a7e7561efa feat: add text & numeric filters to insights routing responses (#18016)
* feat: improve text filters (WIP)

* move function to bottom

* apply some styles

* fix selection of TextFilterOptions

* rename value to operand

* remove unused file

* merge filters/filters into filters/utils

* fix regression of not putting url params correctly

* move makeWhereClause to filters/utils

* fix negative, empty, and not empty operators

* fix initial filtering from search state (url)

* fix type errors

* do not send an empty array to query

* update yarn.lock

* i18n for text filter operators

* extract logic as useColumnFilters()

* add missing import

* fix type error

* revert yarn.lock

* use i18n

* insensitive text match

* move data-table to @calcom/features

* fix type errors

* fix type errors

* fix type errors

* feat: support DataTable filters for Insights Routing WIP

* remove unused filters

* remove additionalFilters and fix types

* clean up filter components

* support icons for ActiveFilters

* support filters on json

* fix filter ui

* fix type error and clean up

* revert changes

* revert change

* clean up

* revert change

* fix compatibility with insights booking page

* remove unused params

* fix type errors

* update yarn.lock

* fix field filter and adjust ui

* chore: update yarn.lock

* fix text filter

* add Clear Filters button

* add more test data

* feat: support numeric filter WIP

* feat: support numeric filter

* feat: move People to an external filter

* fixing query states

* rename state to activeFilters

* add useDataTable

* fix ResponseCellValue bug and enable resizing for RoutingFormResponsesTable

* fix response values

* truncate attribute text

* seed booking attendees

* fix accessor (no actual change though)

* add a gap

* rename

* use pathname as default identifier for DataTable

* fix type error

* support text filter for assignment reason

* Apply suggestions from code review

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* rename fields to headers

* use safeParse

* fix type error

* push yarn.lock with faker

* fix MemberList

---------

Co-authored-by: Udit Takkar <udit222001@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: sean-brydon <sean@cal.com>
2024-12-12 16:06:30 +00:00

129 lines
3.5 KiB
TypeScript

"use client";
import type { Table } from "@tanstack/react-table";
import { useEffect, useState, forwardRef } from "react";
import type { ComponentPropsWithoutRef } from "react";
import { classNames } from "@calcom/lib";
import { useDebounce } from "@calcom/lib/hooks/useDebounce";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { ButtonProps } from "@calcom/ui";
import { Button, Input } from "@calcom/ui";
import { useColumnFilters } from "../lib/utils";
interface DataTableToolbarProps extends ComponentPropsWithoutRef<"div"> {
children: React.ReactNode;
}
const Root = forwardRef<HTMLDivElement, DataTableToolbarProps>(function DataTableToolbar(
{ children, className },
ref
) {
return (
<div
ref={ref}
className={classNames("grid w-full items-center gap-2 py-4", className)}
style={{ gridArea: "header" }}>
{children}
</div>
);
});
interface SearchBarProps<TData> {
table: Table<TData>;
searchKey?: string;
onSearch?: (value: string) => void;
className?: string;
}
function SearchBarComponent<TData>(
{ table, searchKey, onSearch, className }: SearchBarProps<TData>,
ref: React.Ref<HTMLInputElement>
) {
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
onSearch?.(debouncedSearchTerm);
}, [debouncedSearchTerm, onSearch]);
if (onSearch) {
return (
<Input
ref={ref}
className={`max-w-64 mb-0 mr-auto rounded-md ${className ?? ""}`}
placeholder="Search"
value={searchTerm}
onChange={(event) => setSearchTerm(event.target.value.trim())}
/>
);
}
if (!searchKey) {
console.error("searchKey is required if onSearch is not provided");
return null;
}
return (
<Input
ref={ref}
className="max-w-64 mb-0 mr-auto rounded-md"
placeholder="Search"
value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ""}
onChange={(event) => {
return table.getColumn(searchKey)?.setFilterValue(event.target.value.trim());
}}
/>
);
}
const SearchBar = forwardRef(SearchBarComponent) as <TData>(
props: SearchBarProps<TData> & { ref?: React.Ref<HTMLInputElement> }
) => ReturnType<typeof SearchBarComponent>;
interface ClearFiltersButtonProps<TData> {
table: Table<TData>;
}
function ClearFiltersButtonComponent<TData>(
{ table }: ClearFiltersButtonProps<TData>,
ref: React.Ref<HTMLButtonElement>
) {
const { t } = useLocale();
const columnFilters = useColumnFilters();
const isFiltered = columnFilters.length > 0;
if (!isFiltered) return null;
return (
<Button
ref={ref}
color="minimal"
EndIcon="x"
onClick={() => table.resetColumnFilters()}
className="h-8 px-2 lg:px-3">
{t("clear")}
</Button>
);
}
const ClearFiltersButton = forwardRef(ClearFiltersButtonComponent) as <TData>(
props: ClearFiltersButtonProps<TData> & { ref?: React.Ref<HTMLButtonElement> }
) => ReturnType<typeof ClearFiltersButtonComponent>;
function CTAComponent(
{ children, onClick, color = "primary", ...rest }: ButtonProps,
ref: React.Ref<HTMLButtonElement>
) {
return (
<Button ref={ref} color={color} onClick={onClick} {...rest}>
{children}
</Button>
);
}
const CTA = forwardRef(CTAComponent) as (
props: ButtonProps & { ref?: React.Ref<HTMLButtonElement> }
) => ReturnType<typeof CTAComponent>;
export const DataTableToolbar = { Root, SearchBar, ClearFiltersButton, CTA };