Desishub Lessons
Next JS and TypeScript
Data Table

Data Tables

Install Dependencies

You will needto Add the <Table /> component to your project:

 npx shadcn-ui@latest add table

and Also Add tanstack/react-table dependency:

 npm install @tanstack/react-table

Folder Structure For the Route

Lets take an example i have this route called dashboard/inventory/categories

    • columns.tsx
    • page.tsx
  • So go ahead and Create the route and add those files

    Folder Structure For the Components

  • So go ahead and Create these Components

    • columns.tsx (client component) will contain our column definitions.
    • data-table.tsx (client component) will contain our <DataTable /> component.
    • page.tsx (server component) is where we'll fetch data and render our table.

    Create the Page.tsx in the categories

    in the Page we fetch the Data and also we need to send the Coumns and the Data to the DataTable

    import React from "react";
    import { columns } from "./columns";
    import { Category } from "@prisma/client";
    import DataTable from "@/components/DataTableComponents/DataTable";
    import TableHeader from "../../../../components/dashboard/Tables/TableHeader";
    import { getAllCategories } from "@/actions/categories";
     
    export default async function page() {
      const categories: Category[] = (await getAllCategories()) || [];
      return (
        <div className="p-8">
          <TableHeader
            title="Categories"
            linkTitle="Add Category"
            href="/dashboard/categories/new"
            data={categories}
            model="category"
          />
          <div className="py-8">
            <DataTable data={categories} columns={columns} />
          </div>
        </div>
      );
    }

    Create the Columns.tsx

    Make sure to Install all the missing shad components

    "use client";
     
    import Image from "next/image";
    import { ArrowUpDown, MoreHorizontal } from "lucide-react";
    import { Button } from "@/components/ui/button";
    import { Checkbox } from "@/components/ui/checkbox";
     
    import DateColumn from "@/components/DataTableColumns/DateColumn";
    import ImageColumn from "@/components/DataTableColumns/ImageColumn";
    import SortableColumn from "@/components/DataTableColumns/SortableColumn";
    import { ColumnDef } from "@tanstack/react-table";
    import ActionColumn from "@/components/DataTableColumns/ActionColumn";
    import { Category } from "@prisma/client";
    export const columns: ColumnDef<Category>[] = [
      {
        id: "select",
        header: ({ table }) => (
          <Checkbox
            checked={
              table.getIsAllPageRowsSelected() ||
              (table.getIsSomePageRowsSelected() && "indeterminate")
            }
            onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
            aria-label="Select all"
          />
        ),
        cell: ({ row }) => (
          <Checkbox
            checked={row.getIsSelected()}
            onCheckedChange={(value) => row.toggleSelected(!!value)}
            aria-label="Select row"
          />
        ),
        enableSorting: false,
        enableHiding: false,
      },
      {
        accessorKey: "title",
        header: ({ column }) => <SortableColumn column={column} title="Title" />,
      },
      {
        accessorKey: "imageUrl",
        header: "Category Image",
        cell: ({ row }) => <ImageColumn row={row} accessorKey="imageUrl" />,
      },
     
      {
        accessorKey: "createdAt",
        header: "Date Created",
        cell: ({ row }) => <DateColumn row={row} accessorKey="createdAt" />,
      },
      {
        id: "actions",
        cell: ({ row }) => {
          const category = row.original;
          return (
            <ActionColumn
              row={row}
              model="category"
              editEndpoint={`categories/update/${category.id}`}
              id={category.id}
            />
          );
        },
      },
    ];

    Create the Components in the DataTableComponents Folder

    ActionColumn.tsx

    "use client";
    import React from "react";
    import { Button } from "@/components/ui/button";
    import {
      DropdownMenu,
      DropdownMenuContent,
      DropdownMenuItem,
      DropdownMenuLabel,
      DropdownMenuSeparator,
      DropdownMenuTrigger,
    } from "@/components/ui/dropdown-menu";
    import {
      AlertDialog,
      AlertDialogAction,
      AlertDialogCancel,
      AlertDialogContent,
      AlertDialogDescription,
      AlertDialogFooter,
      AlertDialogHeader,
      AlertDialogTitle,
      AlertDialogTrigger,
    } from "@/components/ui/alert-dialog";
    import { MoreHorizontal, Pencil, Trash } from "lucide-react";
    import { deleteCategory } from "@/actions/categories";
    import toast from "react-hot-toast";
    import Link from "next/link";
     
    type ActionColumnProps = {
      row: any;
      model: any;
      editEndpoint: string;
      id: string | undefined;
      // revPath: string;
    };
    export default function ActionColumn({
      row,
      model,
      editEndpoint,
      id = "",
    }: ActionColumnProps) {
      const isActive = row.isActive;
      async function handleDelete() {
        try {
          if (model === "category") {
            const res = await deleteCategory(id);
            if (res?.ok) {
              window.location.reload();
            }
            toast.success(`${model} Deleted Successfully`);
          }
        } catch (error) {
          console.log(error);
          toast.error("Category Couldn't be deleted");
        }
      }
      return (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="ghost" className="h-8 w-8 p-0">
              <span className="sr-only">Open menu</span>
              <MoreHorizontal className="h-4 w-4" />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuLabel>Actions</DropdownMenuLabel>
            <DropdownMenuSeparator />
            <AlertDialog>
              <AlertDialogTrigger asChild>
                {/* <DropdownMenuItem className="text-red-600 hover:text-red-700 transition-all duration-500 cursor-pointer">
                  
                </DropdownMenuItem> */}
                <Button
                  variant={"ghost"}
                  size={"sm"}
                  className="text-red-600 hover:text-red-700 transition-all duration-500 cursor-pointer "
                >
                  <Trash className="w-4 h-4  mr-2 flex-shrink-0" />
                  <span>Delete</span>
                </Button>
              </AlertDialogTrigger>
              <AlertDialogContent>
                <AlertDialogHeader>
                  <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
                  <AlertDialogDescription>
                    This action cannot be undone. This will permanently delete this{" "}
                    {model}.
                  </AlertDialogDescription>
                </AlertDialogHeader>
                <AlertDialogFooter>
                  <AlertDialogCancel>Cancel</AlertDialogCancel>
                  <Button variant={"destructive"} onClick={() => handleDelete()}>
                    Permanently Delete
                  </Button>
                </AlertDialogFooter>
              </AlertDialogContent>
            </AlertDialog>
            {/* <DropdownMenuItem
              className="text-red-600 hover:text-red-700 transition-all duration-500 cursor-pointer"
              onClick={() => handleDelete()}
            >
              <Trash className="w-4 h-4  mr-2 flex-shrink-0" />
              <span>Delete</span>
            </DropdownMenuItem> */}
            <DropdownMenuItem>
              <Link href={editEndpoint} className="flex item gap-2">
                <Pencil className="w-4 h-4 " />
                <span>Edit</span>
              </Link>
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      );
    }

    DateColumn.tsx

    import { getNormalDate } from "@/lib/getNormalDate";
    import React from "react";
     
    export default function DateColumn({
      row,
      accessorKey,
    }: {
      row: any;
      accessorKey: any;
    }) {
      const createdAt = row.getValue(`${accessorKey}`);
      const date = getNormalDate(createdAt);
      const originalDate = new Date(createdAt);
     
      const day = originalDate.getDate();
      const month = originalDate.toLocaleString("default", { month: "short" });
      const year = originalDate.getFullYear();
     
      const formatted = `${day}th ${month} ${year}`;
      // console.log(imageUrl);
      return <div className="">{date}</div>;
    }

    ImageColumn.tsx

    import Image from "next/image";
    import React from "react";
     
    export default function ImageColumn({
      row,
      accessorKey,
    }: {
      row: any;
      accessorKey: any;
    }) {
      const imageUrl = row.getValue(`${accessorKey}`);
      // const thum = row.getValue(`${accessorKey}`);
      // console.log(imageUrl);
      return (
        <div className="shrink-0">
          <Image
            alt={`${accessorKey}`}
            className="aspect-square rounded-md object-cover"
            height="50"
            src={imageUrl ?? ""}
            width="50"
          />
        </div>
      );
    }

    SortableColumn.tsx

    import React from "react";
    import { Button } from "@/components/ui/button";
    import { ArrowUpDown } from "lucide-react";
    export default function SortableColumn({
      column,
      title,
    }: {
      column: any;
      title: string;
    }) {
      return (
        <Button
          variant="ghost"
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
        >
          {title}
          <ArrowUpDown className="ml-2 h-4 w-4" />
        </Button>
      );
    }

    Create the DataTable components

    DataTable.tsx

    "use client";
     
    import * as React from "react";
    import {
      ColumnDef,
      ColumnFiltersState,
      SortingState,
      VisibilityState,
      flexRender,
      getCoreRowModel,
      getFacetedRowModel,
      getFacetedUniqueValues,
      getFilteredRowModel,
      getPaginationRowModel,
      getSortedRowModel,
      useReactTable,
    } from "@tanstack/react-table";
     
    import {
      Table,
      TableBody,
      TableCell,
      TableHead,
      TableHeader,
      TableRow,
    } from "@/components/ui/table";
    import {
      DropdownMenu,
      DropdownMenuCheckboxItem,
      DropdownMenuContent,
      DropdownMenuLabel,
      DropdownMenuSeparator,
      DropdownMenuTrigger,
    } from "@/components/ui/dropdown-menu";
    import { useState } from "react";
     
    import SearchBar from "./SearchBar";
    import { DataTableViewOptions } from "./DataTableViewOptions";
    import { Button } from "../ui/button";
    import { ListFilter } from "lucide-react";
    import DateFilters from "./DateFilters";
    import DateRangeFilter from "./DateRangeFilter";
    import { DataTablePagination } from "./DataTablePagination";
     
    interface DataTableProps<TData, TValue> {
      columns: ColumnDef<TData, TValue>[];
      data: TData[];
    }
    export default function DataTable<TData, TValue>({
      columns,
      data,
    }: DataTableProps<TData, TValue>) {
      const [rowSelection, setRowSelection] = useState({});
      const [columnVisibility, setColumnVisibility] = useState({});
      const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
        []
      );
      const [searchResults, setSearchResults] = useState(data);
      const [filteredData, setFilteredData] = useState(data);
      const [sorting, setSorting] = React.useState<SortingState>([]);
      const [isSearch, setIsSearch] = useState(true);
      // console.log(isSearch);
      const table = useReactTable({
        data: isSearch ? searchResults : filteredData,
        columns,
        state: {
          sorting,
          columnVisibility,
          rowSelection,
          columnFilters,
        },
        enableRowSelection: true,
        onRowSelectionChange: setRowSelection,
        onSortingChange: setSorting,
        onColumnFiltersChange: setColumnFilters,
        onColumnVisibilityChange: setColumnVisibility,
        getCoreRowModel: getCoreRowModel(),
        getFilteredRowModel: getFilteredRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        getSortedRowModel: getSortedRowModel(),
        getFacetedRowModel: getFacetedRowModel(),
        getFacetedUniqueValues: getFacetedUniqueValues(),
      });
      // console.log(searchResults);
      return (
        <div className="space-y-4">
          <div className="flex justify-between items-center gap-8">
            <div className="flex-1 w-full">
              <SearchBar
                data={data}
                onSearch={setSearchResults}
                setIsSearch={setIsSearch}
              />
            </div>
            <div className="flex items-center gap-2 ">
              <DateRangeFilter
                data={data}
                onFilter={setFilteredData}
                setIsSearch={setIsSearch}
              />
              <DateFilters
                data={data}
                onFilter={setFilteredData}
                setIsSearch={setIsSearch}
              />
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button variant="outline" size="sm" className="h-8 gap-1">
                    <ListFilter className="h-3.5 w-3.5" />
                    <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
                      Filter
                    </span>
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent align="end">
                  <DropdownMenuLabel>Filter by</DropdownMenuLabel>
                  <DropdownMenuSeparator />
                  <DropdownMenuCheckboxItem checked>
                    Active
                  </DropdownMenuCheckboxItem>
                  <DropdownMenuCheckboxItem>Draft</DropdownMenuCheckboxItem>
                  <DropdownMenuCheckboxItem>Archived</DropdownMenuCheckboxItem>
                </DropdownMenuContent>
              </DropdownMenu>
              <DataTableViewOptions table={table} />
            </div>
          </div>
     
          <div className="rounded-md border">
            <Table>
              <TableHeader>
                {table.getHeaderGroups().map((headerGroup) => (
                  <TableRow key={headerGroup.id}>
                    {headerGroup.headers.map((header) => {
                      return (
                        <TableHead key={header.id} colSpan={header.colSpan}>
                          {header.isPlaceholder
                            ? null
                            : flexRender(
                                header.column.columnDef.header,
                                header.getContext()
                              )}
                        </TableHead>
                      );
                    })}
                  </TableRow>
                ))}
              </TableHeader>
              <TableBody>
                {table.getRowModel().rows?.length ? (
                  table.getRowModel().rows.map((row) => (
                    <TableRow
                      key={row.id}
                      data-state={row.getIsSelected() && "selected"}
                    >
                      {row.getVisibleCells().map((cell) => (
                        <TableCell key={cell.id}>
                          {flexRender(
                            cell.column.columnDef.cell,
                            cell.getContext()
                          )}
                        </TableCell>
                      ))}
                    </TableRow>
                  ))
                ) : (
                  <TableRow>
                    <TableCell
                      colSpan={columns.length}
                      className="h-24 text-center"
                    >
                      No results.
                    </TableCell>
                  </TableRow>
                )}
              </TableBody>
            </Table>
          </div>
          <DataTablePagination table={table} />
        </div>
      );
    }

    DataTablePagination.tsx

    "use client";
    import {
      ChevronLeftIcon,
      ChevronRightIcon,
      DoubleArrowLeftIcon,
      DoubleArrowRightIcon,
    } from "@radix-ui/react-icons";
    import { Table } from "@tanstack/react-table";
    import {
      Select,
      SelectContent,
      SelectItem,
      SelectTrigger,
      SelectValue,
    } from "@/components/ui/select";
     
    interface DataTablePaginationProps<TData> {
      table: Table<TData>;
    }
    import { Button } from "@/components/ui/button";
     
    export function DataTablePagination<TData>({
      table,
    }: DataTablePaginationProps<TData>) {
      return (
        <div className="flex items-center justify-between px-2">
          <div className="flex-1 text-sm text-muted-foreground">
            {table.getFilteredSelectedRowModel().rows.length} of{" "}
            {table.getFilteredRowModel().rows.length} row(s) selected.
          </div>
          <div className="flex items-center space-x-6 lg:space-x-8">
            <div className="flex items-center space-x-2">
              <p className="text-sm font-medium">Rows per page</p>
              <Select
                value={`${table.getState().pagination.pageSize}`}
                onValueChange={(value) => {
                  table.setPageSize(Number(value));
                }}
              >
                <SelectTrigger className="h-8 w-[70px]">
                  <SelectValue placeholder={table.getState().pagination.pageSize} />
                </SelectTrigger>
                <SelectContent side="top">
                  {[10, 20, 30, 40, 50].map((pageSize) => (
                    <SelectItem key={pageSize} value={`${pageSize}`}>
                      {pageSize}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            <div className="flex w-[100px] items-center justify-center text-sm font-medium">
              Page {table.getState().pagination.pageIndex + 1} of{" "}
              {table.getPageCount()}
            </div>
            <div className="flex items-center space-x-2">
              <Button
                variant="outline"
                className="hidden h-8 w-8 p-0 lg:flex"
                onClick={() => table.setPageIndex(0)}
                disabled={!table.getCanPreviousPage()}
              >
                <span className="sr-only">Go to first page</span>
                <DoubleArrowLeftIcon className="h-4 w-4" />
              </Button>
              <Button
                variant="outline"
                className="h-8 w-8 p-0"
                onClick={() => table.previousPage()}
                disabled={!table.getCanPreviousPage()}
              >
                <span className="sr-only">Go to previous page</span>
                <ChevronLeftIcon className="h-4 w-4" />
              </Button>
              <Button
                variant="outline"
                className="h-8 w-8 p-0"
                onClick={() => table.nextPage()}
                disabled={!table.getCanNextPage()}
              >
                <span className="sr-only">Go to next page</span>
                <ChevronRightIcon className="h-4 w-4" />
              </Button>
              <Button
                variant="outline"
                className="hidden h-8 w-8 p-0 lg:flex"
                onClick={() => table.setPageIndex(table.getPageCount() - 1)}
                disabled={!table.getCanNextPage()}
              >
                <span className="sr-only">Go to last page</span>
                <DoubleArrowRightIcon className="h-4 w-4" />
              </Button>
            </div>
          </div>
        </div>
      );
    }

    DataTableToolbar.tsx

    "use client";
     
    import { Cross2Icon } from "@radix-ui/react-icons";
    import { Table } from "@tanstack/react-table";
    import { Button } from "@/components/ui/button";
    import { Input } from "@/components/ui/input";
     
    export function DataTableToolbar({ table }: { table: any }) {
      return <div className="flex items-center justify-between"></div>;
    }

    DataTableViewOptions.tsx

    "use client";
     
    import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
    import { MixerHorizontalIcon } from "@radix-ui/react-icons";
    import { Table } from "@tanstack/react-table";
     
    import { Button } from "@/components/ui/button";
     
    import {
      DropdownMenu,
      DropdownMenuCheckboxItem,
      DropdownMenuContent,
      DropdownMenuLabel,
      DropdownMenuSeparator,
    } from "@/components/ui/dropdown-menu";
    interface DataTablePaginationProps<TData> {
      table: Table<TData>;
    }
    export function DataTableViewOptions<TData>({
      table,
    }: DataTablePaginationProps<TData>) {
      return (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button
              variant="outline"
              size="sm"
              className="ml-auto hidden h-8 lg:flex"
            >
              <MixerHorizontalIcon className="mr-2 h-4 w-4" />
              View
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end" className="w-[150px]">
            <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
            <DropdownMenuSeparator />
            {table
              .getAllColumns()
              .filter(
                (column) =>
                  typeof column.accessorFn !== "undefined" && column.getCanHide()
              )
              .map((column) => {
                return (
                  <DropdownMenuCheckboxItem
                    key={column.id}
                    className="capitalize"
                    checked={column.getIsVisible()}
                    onCheckedChange={(value) => column.toggleVisibility(!!value)}
                  >
                    {column.id}
                  </DropdownMenuCheckboxItem>
                );
              })}
          </DropdownMenuContent>
        </DropdownMenu>
      );
    }

    DateFilters.tsx

    "use client";
    import {
      filterByLast7Days,
      filterByThisMonth,
      filterByThisYear,
      filterByToday,
      filterByYesterday,
    } from "@/lib/dateFilters";
    import React, { useState } from "react";
    import Select from "react-tailwindcss-select";
    import { SelectValue } from "react-tailwindcss-select/dist/components/type";
     
    export default function DateFilters({
      data,
      onFilter,
      setIsSearch,
    }: {
      data: any[];
      onFilter: any;
      setIsSearch: any;
    }) {
      const options = [
        { value: "life", label: "Life time" },
        { value: "today", label: "Today" },
        { value: "last-7-days", label: "Last 7 days" },
        { value: "month", label: "This Month" },
        { value: "year", label: "This year" },
      ];
      const [selectedFilter, setSelectedFilter] = useState<SelectValue>(options[0]);
      const handleChange = (item: any) => {
        const valueString = item!.value;
        setSelectedFilter(item);
        setIsSearch(false);
        if (valueString === "life") {
          onFilter(data);
        } else if (valueString === "today") {
          const filteredData = filterByToday(data);
          onFilter(filteredData);
        } else if (valueString === "yesterday") {
          const filteredData = filterByYesterday(data);
          onFilter(filteredData);
        } else if (valueString === "last-7-days") {
          const filteredData = filterByLast7Days(data);
          onFilter(filteredData);
        } else if (valueString === "month") {
          const filteredData = filterByThisMonth(data);
          onFilter(filteredData);
        } else if (valueString === "year") {
          const filteredData = filterByThisYear(data);
          onFilter(filteredData);
        }
        console.log("value:", valueString);
        // setAnimal(value);
        // onFilter(filteredData);
      };
      return (
        <div>
          <Select
            value={selectedFilter}
            onChange={handleChange}
            options={options}
            primaryColor={"indigo"}
          />
        </div>
      );
    }

    DateRangeFilter.tsx

    "use client";
    import {
      filterByDateRange,
      filterByLast7Days,
      filterByThisMonth,
      filterByThisYear,
      filterByToday,
      filterByYesterday,
    } from "@/lib/dateFilters";
    import React, { useState } from "react";
    import Select from "react-tailwindcss-select";
    import { SelectValue } from "react-tailwindcss-select/dist/components/type";
    import { addDays, format } from "date-fns";
    import { Calendar as CalendarIcon } from "lucide-react";
    import { DateRange } from "react-day-picker";
    import { cn } from "@/lib/utils";
    import { Button } from "@/components/ui/button";
    import { Calendar } from "@/components/ui/calendar";
    import {
      Popover,
      PopoverContent,
      PopoverTrigger,
    } from "@/components/ui/popover";
    export default function DateRangeFilter({
      data,
      onFilter,
      setIsSearch,
      className,
    }: {
      data: any[];
      onFilter: any;
      setIsSearch: any;
      className?: string;
    }) {
      const [date, setDate] = React.useState<DateRange | undefined>({
        from: new Date(2024, 0, 20),
        to: addDays(new Date(2024, 0, 20), 20),
      });
      // console.log(date);
      const handleChange = (selectedDate: any) => {
        console.log(selectedDate);
        setDate(selectedDate);
        setIsSearch(false);
        const startDate = selectedDate.from;
        const endDate = selectedDate.to;
        const filteredData = filterByDateRange(data, startDate, endDate);
        onFilter(filteredData);
      };
      return (
        <div className={cn("grid gap-2", className)}>
          <Popover>
            <PopoverTrigger asChild>
              <Button
                id="date"
                variant={"outline"}
                className={cn(
                  "w-[300px] justify-start text-left font-normal",
                  !date && "text-muted-foreground"
                )}
              >
                <CalendarIcon className="mr-2 h-4 w-4" />
                {date?.from ? (
                  date.to ? (
                    <>
                      {format(date.from, "LLL dd, y")} -{" "}
                      {format(date.to, "LLL dd, y")}
                    </>
                  ) : (
                    format(date.from, "LLL dd, y")
                  )
                ) : (
                  <span>Pick a date</span>
                )}
              </Button>
            </PopoverTrigger>
            <PopoverContent className="w-auto p-0" align="start">
              <Calendar
                initialFocus
                mode="range"
                defaultMonth={date?.from}
                selected={date}
                onSelect={(value) => handleChange(value)}
                numberOfMonths={2}
              />
            </PopoverContent>
          </Popover>
        </div>
      );
    }

    SearchBar.tsx

    import { Search } from "lucide-react";
    import React, { useState } from "react";
     
    export default function SearchBar({
      data,
      onSearch,
      setIsSearch,
    }: {
      data: any[];
      onSearch: any;
      setIsSearch: any;
    }) {
      const [searchTerm, setSearchTerm] = useState("");
     
      const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
        setSearchTerm(e.target.value);
        const filteredData = data.filter((item: any) =>
          Object.values(item).some(
            (value: any) =>
              value &&
              value.toString().toLowerCase().includes(e.target.value.toLowerCase())
          )
        );
        setIsSearch(true);
        onSearch(filteredData);
      };
      return (
        <div className="flex justify-between items-center gap-8 w-full">
          <div className="mt-2 relativeclea">
            <div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
              <Search className="text-slate-300 w-4 h-4" />
            </div>
            <input
              id="search"
              name="search"
              type="text"
              autoComplete="search"
              value={searchTerm}
              onChange={handleSearch}
              className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 pl-8"
            />
          </div>
        </div>
      );
    }

    TableHeader.tsx

    "use client";
    import { Button } from "@/components/ui/button";
    import * as XLSX from "xlsx";
     
    import { ScrollArea } from "@/components/ui/scroll-area";
    import { ExcelCategoryProps, ProductProps, SelectOption } from "@/types/types";
    import {
      Check,
      CloudUpload,
      Delete,
      File,
      ListFilter,
      Loader2,
      PlusCircle,
      Search,
      X,
    } from "lucide-react";
    import Link from "next/link";
    import React, { useState } from "react";
    import { RiFileExcel2Line } from "react-icons/ri";
    import { SiMicrosoftexcel } from "react-icons/si";
    import Select from "react-tailwindcss-select";
    import {
      Options,
      SelectValue,
    } from "react-tailwindcss-select/dist/components/type";
    import {
      Dialog,
      DialogClose,
      DialogContent,
      DialogDescription,
      DialogFooter,
      DialogHeader,
      DialogTitle,
      DialogTrigger,
    } from "@/components/ui/dialog";
    import { DialogDemo } from "./Button";
    import { formatBytes } from "@/lib/formatBytes";
    import { generateSlug } from "@/lib/generateSlug";
    import { createBulkCategories } from "@/actions/category";
    import toast from "react-hot-toast";
    import exportDataToExcel from "@/lib/exportDataToExcel";
    import { createBulkBrands } from "@/actions/brand";
    import { createBulkWarehouses } from "@/actions/warehouse";
    import { createBulkSuppliers } from "@/actions/supplier";
    import { createBulkUnits } from "@/actions/unit";
    import { createBulkProducts } from "@/actions/products";
    type TableHeaderProps = {
      title: string;
      href: string;
      linkTitle: string;
      data: any;
      model: string;
      showImport?: boolean;
    };
    export default function TableHeader({
      title,
      href,
      linkTitle,
      data,
      model,
      showImport = true,
    }: TableHeaderProps) {
      const [status, setStatus] = useState<SelectValue>(null);
      const [date, setDate] = useState<SelectValue>(null);
      const [excelFile, setExcelFile] = useState<File | null>(null);
      const [jsonData, setJsonData] = useState("");
      const [preview, setPreview] = useState(false);
      const [loading, setLoading] = useState(false);
      const [uploadSuccess, setUploadSuccess] = useState(false);
      let excelDownload = "#";
      if (model === "category") {
        excelDownload = "/Categories.xlsx";
      } else if (model === "brand") {
        excelDownload = "/Brands.xlsx";
      } else if (model === "warehouse") {
        excelDownload = "/Warehouses.xlsx";
      } else if (model === "supplier") {
        excelDownload = "/Suppliers.xlsx";
      } else if (model === "unit") {
        excelDownload = "/Units.xlsx";
      } else if (model === "product") {
        excelDownload = "/Products.xlsx";
      }
      console.log(excelFile);
      const options: Options = [
        { value: "true", label: "Active" },
        { value: "false", label: "Disabled" },
      ];
      const dateOptions: Options = [
        { value: "lastMonth", label: "Last Month" },
        { value: "thisMonth", label: "This Month" },
      ];
      const handleStatusChange = (item: SelectValue) => {
        console.log("value:", item);
        setStatus(item);
      };
      const handleDateChange = (item: SelectValue) => {
        console.log("value:", item);
        setDate(item);
      };
     
      function previewData() {
        setPreview(true);
        if (excelFile) {
          const reader = new FileReader();
          reader.onload = (e) => {
            const data = e.target?.result;
            if (data) {
              const workbook = XLSX.read(data, { type: "binary" });
              // SheetName
              const sheetName = workbook.SheetNames[0];
              // Worksheet
              const workSheet = workbook.Sheets[sheetName];
              // Json
              const json = XLSX.utils.sheet_to_json(workSheet);
              setJsonData(JSON.stringify(json, null, 2));
            }
          };
          reader.readAsBinaryString(excelFile);
        }
      }
      function saveData() {
        setPreview(false);
        if (excelFile) {
          const reader = new FileReader();
          reader.onload = async (e) => {
            const data = e.target?.result;
            if (data) {
              const workbook = XLSX.read(data, { type: "binary" });
              // SheetName
              const sheetName = workbook.SheetNames[0];
              // Worksheet
              const workSheet = workbook.Sheets[sheetName];
              // Json
              const json = XLSX.utils.sheet_to_json(workSheet);
              setJsonData(JSON.stringify(json, null, 2));
     
              try {
                setLoading(true);
                if (model === "category") {
                  const categories = json.map((item: any) => {
                    return {
                      title: item.Title,
                      slug: generateSlug(item.Title),
                      description: item.Description,
                      imageUrl: item.Image,
                      mainCategoryId: item.mainCategoryId,
                      status: true,
                    };
                  });
                  await createBulkCategories(categories);
                } else if (model === "brand") {
                  const brands = json.map((item: any) => {
                    return {
                      title: item.Title,
                      slug: generateSlug(item.Title),
                      logo: item.Logo,
                      status: true,
                    };
                  });
                  await createBulkBrands(brands);
                  // console.log(brands);
                } else if (model === "warehouse") {
                  const warehouses = json.map((item: any) => {
                    return {
                      name: item.name,
                      slug: generateSlug(item.name),
                      logo: item.logo,
                      country: item.country,
                      city: item.city,
                      phone: item.phone,
                      email: item.email,
                      zipCode: item.zipCode,
                      contactPerson: item.contactPerson,
                      status: true,
                    };
                  });
                  await createBulkWarehouses(warehouses);
                  // console.log(warehouses);
                } else if (model === "supplier") {
                  const suppliers = json.map((item: any) => {
                    return {
                      name: item.name,
                      imageUrl: item.imageUrl,
                      country: item.country,
                      city: item.city,
                      phone: String(item.phone),
                      email: item.email,
                      state: item.state,
                      companyName: item.companyName,
                      vatNumber: String(item.vatNumber),
                      address: item.address,
                      postalCode: String(item.postalCode),
                      status: true,
                    };
                  });
                  await createBulkSuppliers(suppliers);
                  // console.log(suppliers);
                } else if (model === "unit") {
                  const units = json.map((item: any) => {
                    return {
                      title: item.title,
                      abbreviation: item.abbreviation,
                    };
                  });
                  await createBulkUnits(units);
                  // console.log(brands);
                } else if (model === "product") {
                  const products = json.map((item: any) => {
                    return {
                      name: item.name,
                      slug: generateSlug(item.name),
                      productCode: item.productCode,
                      stockQty: item.stockQty,
                      warehouseId: item.warehouseId,
                      brandId: item.brandId,
                      supplierId: item.supplierId,
                      categoryId: item.categoryId,
                      unitId: item.unitId,
                      productCost: item.productCost,
                      productPrice: item.productPrice,
                      alertQty: item.alertQty,
                      productTax: item.productTax,
                      productThumbnail: item.productThumbnail,
                      taxMethod: item.taxMethod,
                      status: true,
                      productImages: [...item.productThumbnail],
                      productDetails: item.productDetails,
                    };
                  });
                  await createBulkProducts(products as any);
                  // console.log(brands);
                }
                setLoading(false);
                setUploadSuccess(true);
                // window.location.reload();
                // toast.success("All Data Synced Successfully with No errors 👍");
              } catch (error) {
                setUploadSuccess(false);
                setLoading(false);
                toast.error("Something went wrong, Please Try again 😢");
                console.log(error);
              }
            }
          };
          reader.readAsBinaryString(excelFile);
        }
      }
      function handleExportData() {
        console.log("data exported");
        const today = new Date();
        const filename = `Exported ${title} ${today.toDateString()}`;
        // console.log(filename);
        exportDataToExcel(data, filename);
      }
      return (
        <div className=" mb-3">
          <div className="flex justify-between items-center border-b border-gray-200 dark:border-gray-600 py-3">
            <h2 className="scroll-m-20  text-2xl font-semibold tracking-tight first:mt-0">
              {title}
            </h2>
            <div className="ml-auto flex items-center gap-2">
              <Button
                onClick={handleExportData}
                size="sm"
                variant="outline"
                className="h-8 gap-1"
              >
                <SiMicrosoftexcel className="h-3.5 w-3.5" />
                <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
                  Export
                </span>
              </Button>
     
              {showImport && (
                <Dialog>
                  <DialogTrigger asChild>
                    <Button
                      onClick={() => setUploadSuccess(false)}
                      size="sm"
                      variant="outline"
                      className="h-8 gap-1"
                    >
                      <RiFileExcel2Line className="h-3.5 w-3.5" />
                      <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
                        Import
                      </span>
                    </Button>
                  </DialogTrigger>
                  {loading ? (
                    <DialogContent className="sm:max-w-[425px]">
                      <DialogHeader>
                        <DialogTitle>Excel Upload</DialogTitle>
                        <DialogDescription className="text-xs">
                          You can Bring all your Data from excel, Please Download
                          the Sample file First to Make Sure you have Data Columns
                          Named Correctly
                        </DialogDescription>
                      </DialogHeader>
                      <div className="h-60 w-full rounded-md border flex items-center justify-center">
                        <Button disabled className="items-center">
                          <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                          Syncing Data Please wait ...
                        </Button>
                      </div>
                      {!loading && (
                        <DialogFooter className="justify-between ">
                          {preview ? (
                            <Button
                              onClick={() => setPreview(false)}
                              variant={"outline"}
                              type="button"
                            >
                              Stop Preview
                            </Button>
                          ) : (
                            <Button
                              onClick={previewData}
                              variant={"outline"}
                              type="button"
                            >
                              Preview
                            </Button>
                          )}
                          <Button onClick={saveData} type="button">
                            Save Data
                          </Button>
                        </DialogFooter>
                      )}
                    </DialogContent>
                  ) : (
                    <>
                      {uploadSuccess ? (
                        <DialogContent className="sm:max-w-[425px]">
                          <DialogHeader>
                            <DialogTitle>Excel Upload</DialogTitle>
                            <DialogDescription className="text-xs">
                              You can Bring all your Data from excel, Please
                              Download the Sample file First to Make Sure you have
                              Data Columns Required
                            </DialogDescription>
                          </DialogHeader>
                          <div className="h-72 w-full rounded-md border flex items-center justify-center flex-col">
                            <div className="flex items-center justify-center w-24 h-24 bg-green-100 rounded-full">
                              <Check />
                            </div>
                            <h2 className="text-xs pt-2 px-8 text-center">
                              Data Synced Successfully. You can close the Window
                            </h2>
                          </div>
     
                          <DialogFooter className="justify-between ">
                            <DialogClose asChild>
                              <Button
                                onClick={() => window.location.reload()}
                                type="button"
                                variant="secondary"
                              >
                                Close
                              </Button>
                            </DialogClose>
                          </DialogFooter>
                        </DialogContent>
                      ) : (
                        <DialogContent className="sm:max-w-[425px]">
                          <DialogHeader>
                            <DialogTitle>Excel Upload</DialogTitle>
                            <DialogDescription className="text-xs">
                              You can Bring all your Data from excel, Please
                              Download the Sample file First to Make Sure you have
                              Data Columns Required
                            </DialogDescription>
                          </DialogHeader>
                          {preview && jsonData ? (
                            <ScrollArea className="h-72 w-full rounded-md border">
                              <div className="p-4">
                                <pre>{jsonData}</pre>
                              </div>
                            </ScrollArea>
                          ) : (
                            <div className="grid gap-4 py-4">
                              <Button asChild variant="outline">
                                <Link href={excelDownload} download>
                                  Download {model} Sample Data
                                </Link>
                              </Button>
     
                              <div className="flex items-center justify-center w-full">
                                <label
                                  htmlFor="dropzone-file"
                                  className="flex lg:flex-col flex-row  items-center justify-center w-full h-16 lg:h-36 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
                                >
                                  <div className="flex flex-row lg:flex-col items-center justify-center pt-5 pb-6 gap-4 lg:gap-0">
                                    <CloudUpload className="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400" />
     
                                    <p className="lg:mb-2 text-sm text-gray-500 dark:text-gray-400">
                                      <span className="font-semibold">
                                        Click to upload
                                      </span>{" "}
                                      <span className="hidden lg:inline">
                                        {" "}
                                        or drag and drop
                                      </span>
                                    </p>
                                    <p className="text-xs text-gray-500 dark:text-gray-400">
                                      Only Excel Files (.xlsx)
                                    </p>
                                  </div>
                                  <input
                                    id="dropzone-file"
                                    accept=".xls,.xlsx"
                                    type="file"
                                    className="hidden"
                                    onChange={(e) =>
                                      setExcelFile(
                                        e.target.files ? e.target.files[0] : null
                                      )
                                    }
                                  />
                                </label>
                              </div>
                              {excelFile && (
                                <div className="flex items-center shadow-lg rounded-md lg:py-3 py-2 px-6 bg-slate-100 dark:bg-slate-800 justify-between">
                                  <div className="flex items-center gap-3">
                                    <div className="w-8 h-8 lg:w-14 lg:h-14 p-2 lg:p-4 bg-slate-300 dark:bg-slate-500 rounded flex items-center justify-center flex-shrink-0">
                                      <RiFileExcel2Line className="h-4 w-4" />
                                    </div>
                                    <div className="">
                                      <p className="text-sm font-semibold">
                                        {excelFile.name}
                                      </p>
                                      <span className="text-xs">
                                        {formatBytes(excelFile.size)}
                                      </span>
                                    </div>
                                  </div>
                                  <button onClick={() => setExcelFile(null)}>
                                    <X className="text-slate-600 w-5 h-5" />
                                  </button>
                                </div>
                              )}
                            </div>
                          )}
     
                          <DialogFooter className="justify-between ">
                            {preview ? (
                              <Button
                                onClick={() => setPreview(false)}
                                variant={"outline"}
                                type="button"
                              >
                                Stop Preview
                              </Button>
                            ) : (
                              <Button
                                onClick={previewData}
                                variant={"outline"}
                                type="button"
                              >
                                Preview
                              </Button>
                            )}
                            <Button onClick={saveData} type="button">
                              Save Data
                            </Button>
                          </DialogFooter>
                        </DialogContent>
                      )}
                    </>
                  )}
                </Dialog>
              )}
              <Button size="sm" asChild className="h-8 gap-1">
                <Link href={href}>
                  <PlusCircle className="h-3.5 w-3.5" />
                  <span className="sr-only sm:not-sr-only sm:whitespace-nowrap">
                    {linkTitle}
                  </span>
                </Link>
              </Button>
            </div>
          </div>
        </div>
      );
    }