NewReUI Pro is now available! Get 20% off with early bird pricing.View pricing
Overview
  • Introduction
  • Get Started
  • License Setup
  • Styling
  • MCP
  • Registry
  • Roadmap
  • Changelog
  • llms.txt
  • v1 Docs
Components
  • Alert
  • Autocomplete
  • Badge
  • Data GridVirtualization and row pinning support added
  • Date Selector
  • File Upload
  • Filters
  • Frame
  • Shadcn Icon Stack
  • Kanban
  • Number Field
  • Phone Input
  • Rating
  • Scrollspy
  • Sortable
  • Stepper
  • Timeline
  • Tree

Application

  • Authentication
  • Card
  • Chart
  • Data Grid
  • Dialog
  • Browse all

eCommerce

  • Shopping Cart
  • Category Card
  • Checkout
  • Comparison
  • Coupon
  • Browse all

Marketing

  • Blog
  • Comparison Table
  • Contact
  • Content Section
  • CTA
  • Browse all

SaaS

  • Analytics
  • Billing
  • Dashboard
  • Integrations
  • Notifications
  • Browse all

Fintech

  • Accounts
  • Transactions
  • Transfer
  • Cards
  • Investments
  • Browse all

Dev Tools

  • API Console
  • CI/CD
  • Code Editor
  • Debug Panel
  • Documentation
  • Browse all

AI & LLM

  • AI Playground
  • AI Settings
  • Chat Interface
  • Embeddings
  • Evaluation
  • Browse all

Data Visualization

  • Charts
  • Dashboards
  • Heatmaps
  • Maps
  • Metrics
  • Browse all

Resources

  • Components
  • Blocks
  • Docs
  • Help & Contact
  • Pricing
  • RoadmapSoon
  • AffiliateSoon

Legal

  • Privacy Policy
  • Terms & Conditions
  • Licensing
  • Cookies

© 2026 ReUI. All rights reserved.

ComponentsBlocksIconsTemplatesDocsPricing
X
LoginGet All-access
2.5k

Shadcn Filters

PreviousNext

Custom Shadcn Filters for React and Tailwind CSS. A comprehensive filtering system with multiple filter types, operators, and visual indicators for data organization.

Base UIRadix UI

Installation

pnpm dlx shadcn@latest add @reui/filters

More Shadcn Filters Components

Browse 9 production-ready Shadcn Filters components for dashboards, forms, and product UI. These examples use Base UI primitives from @base-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.

Browse all 9 Shadcn Filters components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the ReUI library.

File UploadFrame

On This Page

InstallationUsageExamplesValidationTrigger ButtonSmall SizeLarge SizeCustom ControlsData GridNuqsWith i18n SupportAPI ReferenceFiltersFilterFieldConfigFilterOptionFilterFilterI18nConfigHelpers

Usage

import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"
const [filters, setFilters] = useState<Filter[]>([
  createFilter("priority", "is_any_of", ["low", "medium"]),
])
 
const fields: FilterFieldConfig[] = [
  {
    key: "priority",
    label: "Priority",
    type: "multiselect",
    options: [
      { value: "low", label: "Low" },
      { value: "medium", label: "Medium" },
      { value: "high", label: "High" },
    ],
  },
]
 
return <Filters filters={filters} fields={fields} onChange={setFilters} />

Examples

Validation

Trigger Button

Small Size

Large Size

Custom Controls

Data Grid

Nuqs

With i18n Support

API Reference

Filters

The main component for displaying and managing a collection of active filters.

PropTypeDefaultDescription
filtersFilter<T>[]-Required. The array of active filters.
fieldsFilterFieldsConfig<T>-Required. The configuration for available filter fields.
onChange(filters: Filter<T>[]) => void-Required. Callback fired when filters are added, updated, or removed.
size"sm" | "default" | "lg""default"The size of the filter items and controls.
triggerReactNode-Custom element to trigger the "Add Filter" menu.
showSearchInputbooleantrueWhether to show the search input in the "Add Filter" menu.
allowMultiplebooleantrueWhether to allow multiple filters for the same field.
enableShortcutbooleanfalseWhether to enable the keyboard shortcut to open the filter menu.
shortcutKeystring"f"The key used for the shortcut (e.g., "f").
shortcutLabelstring"F"The label to display in the shortcut indicator.
i18nPartial<FilterI18nConfig>-Custom labels and operators for internationalization.
classNamestring-Additional CSS classes for the container.
menuPopupClassNamestring-Additional CSS classes for the filter dropdown menu.

FilterFieldConfig

Configuration for an individual filterable field.

PropertyTypeDescription
keystringRequired. Unique identifier for the field.
labelstringRequired. Human-readable label for the field.
type"text" | "select" | "multiselect" | "custom" | "separator"The type of filter input to use.
iconReactNodeOptional icon to display next to the field label.
optionsFilterOption<T>[]List of options for select and multiselect fields.
operatorsFilterOperator[]Custom operators for this specific field.
defaultOperatorstringThe operator to select by default when adding this field.
placeholderstringPlaceholder text for text inputs.
searchablebooleanWhether the options list should be searchable.
maxSelectionsnumberMaximum number of items allowed in multiselect.
prefixReactNode | stringPrefix element for the input field.
suffixReactNode | stringSuffix element for the input field.
patternstringRegex pattern for text input validation.
validation(value) => boolean | objectCustom validation function.
customRenderer(props) => ReactNodeCustom renderer for the filter value selector.
customValueRenderer(values, options) => ReactNodeCustom renderer for the active filter value display.
fieldsFilterFieldConfig<T>[]Nested fields if this is a group configuration.
groupstringGroup label if this is a group configuration.
classNamestringAdditional CSS classes for the field components.

FilterOption

Structure for options in select and multiselect fields.

PropertyTypeDescription
valueTRequired. The internal value of the option.
labelstringRequired. Human-readable label for the option.
iconReactNodeOptional icon to display next to the option.
classNamestringAdditional CSS classes for the option item.

Filter

The structure of an active filter object.

PropertyTypeDescription
idstringUnique identifier for the filter instance.
fieldstringThe key of the associated field configuration.
operatorstringThe selected operator (e.g., "is", "contains").
valuesT[]The current value(s) of the filter.

FilterI18nConfig

Configuration for internationalization and custom labels.

PropertyTypeDescription
addFilterstringLabel for the "Add Filter" button.
searchFieldsstringPlaceholder for the field search input.
operatorsobjectMap of operator keys to localized strings.
validationobjectMap of validation error messages.
placeholdersobjectMap of dynamic placeholder templates.

Helpers

FunctionSignatureDescription
createFilter(field, operator?, values?) => FilterCreates a new filter object with a unique ID.
createFilterGroup(id, label, fields, initialFilters?) => FilterGroupCreates a filter group configuration.
"use client"

import { useCallback, useState } from "react"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"

import { cn } from "@/lib/utils"
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { BanIcon, BellIcon, BuildingIcon, CircleAlertIcon, CircleCheckIcon, ClockIcon, FunnelXIcon, GlobeIcon, ListFilterIcon, MailIcon, PhoneIcon, StarIcon, TypeIcon, UserRoundCheckIcon, UserRoundXIcon, UsersIcon } from 'lucide-react'

// Priority icon component
const PriorityIcon = ({ priority }: { priority: string }) => {
  const colors = {
    low: "bg-green-500",
    medium: "bg-yellow-500",
    high: "bg-violet-500",
    urgent: "bg-orange-500",
    critical: "bg-red-500",
  }
  return (
    <div
      className={cn(
        "size-2.25 shrink-0 rounded-full",
        colors[priority as keyof typeof colors]
      )}
    />
  )
}

const countryFlags = [
  { code: "AF", name: "Afghanistan" },
  { code: "AL", name: "Albania" },
  { code: "DZ", name: "Algeria" },
  { code: "AS", name: "American Samoa" },
  { code: "AD", name: "Andorra" },
  { code: "AO", name: "Angola" },
  { code: "AI", name: "Anguilla" },
  { code: "AG", name: "Antigua and Barbuda" },
  { code: "AR", name: "Argentina" },
  { code: "AM", name: "Armenia" },
  { code: "AU", name: "Australia" },
  { code: "AT", name: "Austria" },
  { code: "AZ", name: "Azerbaijan" },
  { code: "BS", name: "Bahamas" },
  { code: "BH", name: "Bahrain" },
  { code: "BD", name: "Bangladesh" },
  { code: "BB", name: "Barbados" },
  { code: "BY", name: "Belarus" },
  { code: "BE", name: "Belgium" },
  { code: "BZ", name: "Belize" },
  { code: "BJ", name: "Benin" },
  { code: "BM", name: "Bermuda" },
  { code: "BT", name: "Bhutan" },
  { code: "BO", name: "Bolivia" },
  { code: "BA", name: "Bosnia and Herzegovina" },
  { code: "BW", name: "Botswana" },
  { code: "BR", name: "Brazil" },
  { code: "IO", name: "British Indian Ocean Territory" },
  { code: "BN", name: "Brunei Darussalam" },
  { code: "BG", name: "Bulgaria" },
  { code: "BF", name: "Burkina Faso" },
  { code: "BI", name: "Burundi" },
  { code: "KH", name: "Cambodia" },
  { code: "CM", name: "Cameroon" },
  { code: "CA", name: "Canada" },
  { code: "CV", name: "Cape Verde" },
  { code: "KY", name: "Cayman Islands" },
  { code: "CF", name: "Central African Republic" },
  { code: "TD", name: "Chad" },
  { code: "CL", name: "Chile" },
  { code: "CN", name: "China" },
  { code: "CO", name: "Colombia" },
  { code: "KM", name: "Comoros" },
  { code: "CG", name: "Congo" },
  { code: "CR", name: "Costa Rica" },
  { code: "CI", name: "Cote D'Ivoire" },
  { code: "HR", name: "Croatia" },
  { code: "CU", name: "Cuba" },
  { code: "CY", name: "Cyprus" },
  { code: "CZ", name: "Czech Republic" },
  { code: "DK", name: "Denmark" },
  { code: "DJ", name: "Djibouti" },
  { code: "DM", name: "Dominica" },
  { code: "DO", name: "Dominican Republic" },
  { code: "EC", name: "Ecuador" },
  { code: "EG", name: "Egypt" },
  { code: "SV", name: "El Salvador" },
  { code: "GQ", name: "Equatorial Guinea" },
  { code: "ER", name: "Eritrea" },
  { code: "EE", name: "Estonia" },
  { code: "SZ", name: "Eswatini" },
  { code: "ET", name: "Ethiopia" },
  { code: "FI", name: "Finland" },
  { code: "FR", name: "France" },
  { code: "GA", name: "Gabon" },
  { code: "GM", name: "Gambia" },
  { code: "GE", name: "Georgia" },
  { code: "DE", name: "Germany" },
  { code: "GH", name: "Ghana" },
  { code: "GR", name: "Greece" },
  { code: "GD", name: "Grenada" },
  { code: "GT", name: "Guatemala" },
  { code: "GN", name: "Guinea" },
  { code: "GW", name: "Guinea-Bissau" },
  { code: "GY", name: "Guyana" },
  { code: "HT", name: "Haiti" },
  { code: "HN", name: "Honduras" },
  { code: "HK", name: "Hong Kong" },
  { code: "HU", name: "Hungary" },
  { code: "IS", name: "Iceland" },
  { code: "IN", name: "India" },
  { code: "ID", name: "Indonesia" },
  { code: "IR", name: "Iran" },
  { code: "IQ", name: "Iraq" },
  { code: "IE", name: "Ireland" },
  { code: "IL", name: "Israel" },
  { code: "IT", name: "Italy" },
  { code: "JM", name: "Jamaica" },
  { code: "JP", name: "Japan" },
  { code: "JO", name: "Jordan" },
  { code: "KZ", name: "Kazakhstan" },
  { code: "KE", name: "Kenya" },
  { code: "KR", name: "South Korea" },
  { code: "KW", name: "Kuwait" },
  { code: "KG", name: "Kyrgyzstan" },
  { code: "LA", name: "Laos" },
  { code: "LV", name: "Latvia" },
  { code: "LB", name: "Lebanon" },
  { code: "LS", name: "Lesotho" },
  { code: "LR", name: "Liberia" },
  { code: "LY", name: "Libya" },
  { code: "LT", name: "Lithuania" },
  { code: "LU", name: "Luxembourg" },
  { code: "MO", name: "Macao" },
  { code: "MG", name: "Madagascar" },
  { code: "MW", name: "Malawi" },
  { code: "MY", name: "Malaysia" },
  { code: "MV", name: "Maldives" },
  { code: "ML", name: "Mali" },
  { code: "MT", name: "Malta" },
  { code: "MH", name: "Marshall Islands" },
  { code: "MR", name: "Mauritania" },
  { code: "MU", name: "Mauritius" },
  { code: "MX", name: "Mexico" },
  { code: "FM", name: "Micronesia" },
  { code: "MD", name: "Moldova" },
  { code: "MC", name: "Monaco" },
  { code: "MN", name: "Mongolia" },
  { code: "ME", name: "Montenegro" },
  { code: "MA", name: "Morocco" },
  { code: "MZ", name: "Mozambique" },
  { code: "MM", name: "Myanmar" },
  { code: "NA", name: "Namibia" },
  { code: "NP", name: "Nepal" },
  { code: "NL", name: "Netherlands" },
  { code: "NZ", name: "New Zealand" },
  { code: "NI", name: "Nicaragua" },
  { code: "NG", name: "Nigeria" },
  { code: "NO", name: "Norway" },
  { code: "OM", name: "Oman" },
  { code: "PK", name: "Pakistan" },
  { code: "PA", name: "Panama" },
  { code: "PG", name: "Papua New Guinea" },
  { code: "PY", name: "Paraguay" },
  { code: "PE", name: "Peru" },
  { code: "PH", name: "Philippines" },
  { code: "PL", name: "Poland" },
  { code: "PT", name: "Portugal" },
  { code: "QA", name: "Qatar" },
  { code: "RO", name: "Romania" },
  { code: "RU", name: "Russia" },
  { code: "RW", name: "Rwanda" },
  { code: "WS", name: "Samoa" },
  { code: "SM", name: "San Marino" },
  { code: "SA", name: "Saudi Arabia" },
  { code: "SN", name: "Senegal" },
  { code: "RS", name: "Serbia" },
  { code: "SG", name: "Singapore" },
  { code: "SK", name: "Slovakia" },
  { code: "SI", name: "Slovenia" },
  { code: "ZA", name: "South Africa" },
  { code: "ES", name: "Spain" },
  { code: "LK", name: "Sri Lanka" },
  { code: "SE", name: "Sweden" },
  { code: "CH", name: "Switzerland" },
  { code: "SY", name: "Syria" },
  { code: "TW", name: "Taiwan" },
  { code: "TJ", name: "Tajikistan" },
  { code: "TZ", name: "Tanzania" },
  { code: "TH", name: "Thailand" },
  { code: "TR", name: "Turkey" },
  { code: "UG", name: "Uganda" },
  { code: "UA", name: "Ukraine" },
  { code: "AE", name: "United Arab Emirates" },
  { code: "GB", name: "United Kingdom" },
  { code: "US", name: "United States" },
  { code: "UY", name: "Uruguay" },
  { code: "UZ", name: "Uzbekistan" },
  { code: "VN", name: "Vietnam" },
  { code: "ZM", name: "Zambia" },
  { code: "ZW", name: "Zimbabwe" },
]

export function Pattern() {
  // Example: All Possible Filter Field Types with Grouping
  const fields: FilterFieldConfig[] = [
    {
      group: "Basic",
      fields: [
        {
          key: "text",
          label: "Text",
          type: "text",
          icon: (
            <MailIcon />
          ),
          placeholder: "Search text...",
        },
        {
          key: "email",
          label: "Email",
          type: "text",
          icon: (
            <TypeIcon />
          ),
          placeholder: "user@example.com",
        },
        {
          key: "website",
          label: "Website",
          icon: (
            <GlobeIcon />
          ),
          type: "text",
          placeholder: "https://example.com",
        },
        {
          key: "phone",
          label: "Phone",
          icon: (
            <PhoneIcon />
          ),
          type: "text",
          placeholder: "+1 (123) 456-7890",
        },
      ],
    },
    {
      group: "Select",
      fields: [
        {
          key: "status",
          label: "Status",
          icon: (
            <BellIcon />
          ),
          type: "select",
          searchable: false,
          className: "w-[200px]",
          options: [
            {
              value: "todo",
              label: "To Do",
              icon: (
                <ClockIcon  className="stroke-violet-500" />
              ),
            },
            {
              value: "in-progress",
              label: "In Progress",
              icon: (
                <CircleAlertIcon  className="stroke-yellow-500" />
              ),
            },
            {
              value: "done",
              label: "Done",
              icon: (
                <CircleCheckIcon  className="stroke-green-500" />
              ),
            },
            {
              value: "cancelled",
              label: "Cancelled",
              icon: (
                <BanIcon  className="stroke-destructive" />
              ),
            },
          ],
        },
        {
          key: "priority",
          label: "Priority",
          icon: (
            <BanIcon />
          ),
          type: "multiselect",
          className: "w-[180px]",
          options: [
            {
              value: "low",
              label: "Low",
              icon: <PriorityIcon priority="low" />,
            },
            {
              value: "medium",
              label: "Medium",
              icon: <PriorityIcon priority="medium" />,
            },
            {
              value: "high",
              label: "High",
              icon: <PriorityIcon priority="high" />,
            },
            {
              value: "urgent",
              label: "Urgent",
              icon: <PriorityIcon priority="urgent" />,
            },
            {
              value: "critical",
              label: "Critical",
              icon: <PriorityIcon priority="critical" />,
            },
          ],
        },
        {
          key: "assignee",
          label: "Assignee",
          icon: (
            <UserRoundCheckIcon />
          ),
          type: "multiselect",
          maxSelections: 5,
          options: [
            {
              value: "john",
              label: "John Doe",
              icon: (
                <Avatar className="size-5 border">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/1.jpg"
                    alt="John Doe"
                  />
                  <AvatarFallback>JD</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "jane",
              label: "Jane Smith",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/women/2.jpg"
                    alt="Jane Smith"
                  />
                  <AvatarFallback>JS</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "bob",
              label: "Bob Johnson",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/3.jpg"
                    alt="Bob Johnson"
                  />
                  <AvatarFallback>BJ</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "alice",
              label: "Alice Brown",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/women/4.jpg"
                    alt="Alice Brown"
                  />
                  <AvatarFallback>AB</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "nick",
              label: "Nick Bold",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/4.jpg"
                    alt="Nick Bold"
                  />
                  <AvatarFallback>NB</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "sarah",
              label: "Sarah Wilson",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/women/5.jpg"
                    alt="Sarah Wilson"
                  />
                  <AvatarFallback>SW</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "michael",
              label: "Michael Scott",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/6.jpg"
                    alt="Michael Scott"
                  />
                  <AvatarFallback>MS</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "emily",
              label: "Emily Blunt",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/women/7.jpg"
                    alt="Emily Blunt"
                  />
                  <AvatarFallback>EB</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "david",
              label: "David Gandy",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/8.jpg"
                    alt="David Gandy"
                  />
                  <AvatarFallback>DG</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "laura",
              label: "Laura Palmer",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/women/9.jpg"
                    alt="Laura Palmer"
                  />
                  <AvatarFallback>LP</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "kevin",
              label: "Kevin Hart",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/10.jpg"
                    alt="Kevin Hart"
                  />
                  <AvatarFallback>KH</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "anna",
              label: "Anna Kendrick",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/women/11.jpg"
                    alt="Anna Kendrick"
                  />
                  <AvatarFallback>AK</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "tom",
              label: "Tom Cruise",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/12.jpg"
                    alt="Tom Cruise"
                  />
                  <AvatarFallback>TC</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "lisa",
              label: "Lisa Kudrow",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/women/13.jpg"
                    alt="Lisa Kudrow"
                  />
                  <AvatarFallback>LK</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "james",
              label: "James Bond",
              icon: (
                <Avatar className="size-5">
                  <AvatarImage
                    src="https://randomuser.me/api/portraits/men/14.jpg"
                    alt="James Bond"
                  />
                  <AvatarFallback>JB</AvatarFallback>
                </Avatar>
              ),
            },
            {
              value: "unassigned",
              label: "Unassigned",
              icon: (
                <Avatar className="size-5">
                  <AvatarFallback>
                    <UserRoundXIcon />
                  </AvatarFallback>
                </Avatar>
              ),
            },
          ],
        },
        {
          key: "userType",
          label: "User Type",
          icon: (
            <UsersIcon />
          ),
          type: "select",
          searchable: false,
          className: "w-[200px]",
          options: [
            {
              value: "premium",
              label: "Premium",
              icon: (
                <StarIcon  className="size-3 text-yellow-500" />
              ),
            },
            {
              value: "standard",
              label: "Standard",
              icon: (
                <BuildingIcon  className="size-3 text-blue-500" />
              ),
            },
            {
              value: "trial",
              label: "Trial",
              icon: (
                <ClockIcon  className="size-3 text-gray-500" />
              ),
            },
          ],
        },
        {
          key: "country",
          label: "Country",
          icon: (
            <GlobeIcon />
          ),
          type: "select",
          searchable: true,
          className: "w-[220px]",
          options: countryFlags.map((country) => ({
            value: country.code,
            label: country.name,
            icon: (
              <img
                src={`https://flagcdn.com/${country.code.toLowerCase()}.svg`}
                alt={country.code}
                className="size-4 rounded-full object-cover"
              />
            ),
          })),
        },
      ],
    },
  ]

  const [filters, setFilters] = useState<Filter[]>([
    createFilter("priority", "is_any_of", ["low", "medium", "critical"]),
  ])

  const handleFiltersChange = useCallback((filters: Filter[]) => {
    setFilters(filters)
  }, [])

  return (
    <div className="flex grow content-start items-start gap-2.5 self-start">
      <div className="grow space-y-5">
        {/* Filters Section */}
        <div className="flex items-start gap-2.5">
          <div className="flex-1">
            <Filters
              filters={filters}
              fields={fields}
              onChange={handleFiltersChange}
              shortcutKey="f"
              shortcutLabel="F"
              enableShortcut={true}
              trigger={
                <Button variant="outline">
                  <ListFilterIcon />
                  Add Filter
                </Button>
              }
            />
          </div>

          {filters.length > 0 && (
            <Button variant="outline" onClick={() => setFilters([])}>
              <FunnelXIcon />
              Clear
            </Button>
          )}
        </div>

        {/* Debug Block */}
        <pre className="bg-muted dark:bg-muted/60 mt-2 max-h-[400px] w-full max-w-[500px] overflow-auto overflow-x-auto rounded-md border p-3 text-xs">
          {JSON.stringify(filters, null, 2)}
        </pre>
      </div>
    </div>
  )
}
"use client"

import { useCallback, useState } from "react"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"
import { z } from "zod"

import { Button } from "@/components/ui/button"
import { AtSignIcon, CreditCardIcon, GlobeIcon, LinkIcon, ListFilterIcon, PhoneIcon, UserIcon } from 'lucide-react'

// Zod validation helper - wraps a Zod schema to return validation result with message
function zodValidator<T extends z.ZodType>(schema: T) {
  return (value: unknown): { valid: boolean; message?: string } => {
    const result = schema.safeParse(value)
    if (result.success) {
      return { valid: true }
    }
    // Get the first error message from Zod using format()
    const formatted = result.error.format()
    const message =
      formatted._errors?.[0] || result.error.message || "Invalid value"
    return { valid: false, message }
  }
}

// Define Zod schemas for different field types
const emailSchema = z
  .string()
  .min(1, { message: "Email is required" })
  .pipe(z.email({ message: "Please enter a valid email address" }))

const urlSchema = z
  .string()
  .pipe(
    z.url({ message: "Please enter a valid URL (e.g., https://example.com)" })
  )

const phoneSchema = z
  .string()
  .regex(/^\+?[1-9]\d{1,14}$/, { message: "Please enter a valid phone number" })

const usernameSchema = z
  .string()
  .min(3, { message: "Username must be at least 3 characters" })
  .max(20, { message: "Username must be at most 20 characters" })
  .regex(/^[a-zA-Z0-9_]+$/, {
    message: "Username can only contain letters, numbers, and underscores",
  })

const creditCardSchema = z.string().regex(/^\d{13,19}$/, {
  message: "Please enter a valid credit card number (13-19 digits)",
})

export function Pattern() {
  const fields: FilterFieldConfig[] = [
    {
      key: "email",
      label: "Email",
      icon: (
        <AtSignIcon  className="size-3.5" />
      ),
      type: "text",
      placeholder: "user@example.com",
      // Use Zod validator for email validation
      validation: zodValidator(emailSchema),
    },
    {
      key: "website",
      label: "Website",
      icon: (
        <GlobeIcon  className="size-3.5" />
      ),
      type: "text",
      placeholder: "https://example.com",
      // Use Zod validator for URL validation
      validation: zodValidator(urlSchema),
    },
    {
      key: "phone",
      label: "Phone",
      icon: (
        <PhoneIcon  className="size-3.5" />
      ),
      type: "text",
      placeholder: "+1234567890",
      // Use Zod validator for phone validation
      validation: zodValidator(phoneSchema),
    },
    {
      key: "username",
      label: "Username",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-44",
      placeholder: "john_doe",
      // Use Zod validator for username validation
      validation: zodValidator(usernameSchema),
    },
    {
      key: "cardNumber",
      label: "Card Number",
      icon: (
        <CreditCardIcon  className="size-3.5" />
      ),
      type: "text",
      placeholder: "4111111111111111",
      // Use Zod validator for credit card validation
      validation: zodValidator(creditCardSchema),
    },
    {
      key: "customUrl",
      label: "Custom URL",
      icon: (
        <LinkIcon  className="size-3.5" />
      ),
      type: "text",
      placeholder: "https://...",
      // Custom validation function without Zod (for comparison)
      validation: (value) => {
        const urlPattern = /^https?:\/\/.+\..+/
        if (!urlPattern.test(value as string)) {
          return {
            valid: false,
            message: "URL must start with http:// or https://",
          }
        }
        return { valid: true }
      },
    },
  ]

  const [filters, setFilters] = useState<Filter[]>([
    createFilter("email", "contains", [""]),
  ])

  const handleFiltersChange = useCallback((filters: Filter[]) => {
    setFilters(filters)
  }, [])

  return (
    <div className="flex grow content-start items-start self-start">
      <Filters
        filters={filters}
        fields={fields}
        trigger={
          <Button variant="outline" size="icon">
            <ListFilterIcon />
          </Button>
        }
        onChange={handleFiltersChange}
      />
    </div>
  )
}
"use client"

import { useCallback, useState } from "react"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { CircleAlertIcon, FunnelXIcon, GlobeIcon, ListFilterIcon, MailIcon, StarIcon, TagIcon, UserIcon, UserRoundXIcon } from 'lucide-react'

// Priority icon component
const PriorityIcon = ({ priority }: { priority: string }) => {
  const colors = {
    low: "text-green-500",
    medium: "text-yellow-500",
    high: "text-orange-500",
    urgent: "text-red-500",
  }
  return (
    <StarIcon  className="colors[priority as keyof typeof colors]" />
  )
}

export function Pattern() {
  // Basic filter fields for outline variant demo
  const fields: FilterFieldConfig[] = [
    {
      key: "text",
      label: "Text",
      icon: (
        <TagIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-36",
      placeholder: "Search text...",
    },
    {
      key: "email",
      label: "Email",
      icon: (
        <MailIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "user@example.com",
    },
    {
      key: "website",
      label: "Website",
      icon: (
        <GlobeIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "https://example.com",
    },
    {
      key: "assignee",
      label: "Assignee",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "multiselect",
      className: "w-[200px]",
      options: [
        {
          value: "john",
          label: "John Doe",
          icon: (
            <Avatar className="size-5">
              <AvatarImage
                src="https://randomuser.me/api/portraits/men/1.jpg"
                alt="John Doe"
              />
              <AvatarFallback>JD</AvatarFallback>
            </Avatar>
          ),
        },
        {
          value: "jane",
          label: "Jane Smith",
          icon: (
            <Avatar className="size-5">
              <AvatarImage
                src="https://randomuser.me/api/portraits/women/2.jpg"
                alt="Jane Smith"
              />
              <AvatarFallback>JS</AvatarFallback>
            </Avatar>
          ),
        },
        {
          value: "bob",
          label: "Bob Johnson",
          icon: (
            <Avatar className="size-5">
              <AvatarImage
                src="https://randomuser.me/api/portraits/men/3.jpg"
                alt="Bob Johnson"
              />
              <AvatarFallback>BJ</AvatarFallback>
            </Avatar>
          ),
        },
        {
          value: "alice",
          label: "Alice Brown",
          icon: (
            <Avatar className="size-5">
              <AvatarImage
                src="https://randomuser.me/api/portraits/women/4.jpg"
                alt="Alice Brown"
              />
              <AvatarFallback>AB</AvatarFallback>
            </Avatar>
          ),
        },
        {
          value: "nick",
          label: "Nick Bold",
          icon: (
            <Avatar className="size-5">
              <AvatarImage
                src="https://randomuser.me/api/portraits/men/4.jpg"
                alt="Nick Bold"
              />
              <AvatarFallback>NB</AvatarFallback>
            </Avatar>
          ),
        },
        {
          value: "unassigned",
          label: "Unassigned",
          icon: (
            <Avatar className="size-5">
              <AvatarFallback>
                <UserRoundXIcon />
              </AvatarFallback>
            </Avatar>
          ),
        },
      ],
    },
    {
      key: "priority",
      label: "Priority",
      icon: (
        <CircleAlertIcon  className="size-3.5" />
      ),
      type: "multiselect",
      className: "w-[180px]",
      options: [
        { value: "low", label: "Low", icon: <PriorityIcon priority="low" /> },
        {
          value: "medium",
          label: "Medium",
          icon: <PriorityIcon priority="medium" />,
        },
        {
          value: "high",
          label: "High",
          icon: <PriorityIcon priority="high" />,
        },
        {
          value: "urgent",
          label: "Urgent",
          icon: <PriorityIcon priority="urgent" />,
        },
      ],
    },
  ]

  const [filters, setFilters] = useState<Filter[]>([
    createFilter("assignee", "is_any_of", ["john", "nick", "alice"]),
  ])

  const handleFiltersChange = useCallback((filters: Filter[]) => {
    setFilters(filters)
  }, [])

  return (
    <div className="flex grow content-start items-start gap-2.5 self-start">
      <div className="flex-1">
        <Filters
          filters={filters}
          fields={fields}
          trigger={
            <Button variant="outline" size="icon">
              <ListFilterIcon />
            </Button>
          }
          onChange={handleFiltersChange}
        />
      </div>

      {filters.length > 0 && (
        <Button variant="outline" onClick={() => setFilters([])}>
          <FunnelXIcon />
          Clear
        </Button>
      )}
    </div>
  )
}
"use client"

import { useCallback, useState } from "react"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"

import { Button } from "@/components/ui/button"
import { BanIcon, CircleAlertIcon, CircleCheckIcon, CircleIcon, ClockIcon, GlobeIcon, ListFilterIcon, MailIcon, StarIcon, TagIcon } from 'lucide-react'

const StatusIcon = ({ status }: { status: string }) => {
  switch (status) {
    case "todo":
      return (
        <ClockIcon  className="text-primary" />
      )
    case "in-progress":
      return (
        <CircleAlertIcon  className="text-yellow-500" />
      )
    case "done":
      return (
        <CircleCheckIcon  className="text-green-500" />
      )
    case "cancelled":
      return (
        <BanIcon  className="text-destructive" />
      )
    default:
      return (
        <CircleIcon  className="text-muted-foreground" />
      )
  }
}

// Priority icon component
const PriorityIcon = ({ priority }: { priority: string }) => {
  const colors = {
    low: "text-green-500",
    medium: "text-yellow-500",
    high: "text-orange-500",
    urgent: "text-red-500",
  }
  return (
    <StarIcon  className="colors[priority as keyof typeof colors]" />
  )
}

export function Pattern() {
  // Basic filter fields for size variant demo
  const fields: FilterFieldConfig[] = [
    {
      key: "text",
      label: "Text",
      icon: (
        <TagIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-36",
      placeholder: "Search text...",
    },
    {
      key: "email",
      label: "Email",
      icon: (
        <MailIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-48",
      placeholder: "user@example.com",
    },
    {
      key: "website",
      label: "Website",
      icon: (
        <GlobeIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "https://example.com",
    },
    {
      key: "status",
      label: "Status",
      icon: (
        <ClockIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: false,
      className: "w-[200px]",
      options: [
        { value: "todo", label: "To Do", icon: <StatusIcon status="todo" /> },
        {
          value: "in-progress",
          label: "In Progress",
          icon: <StatusIcon status="in-progress" />,
        },
        { value: "done", label: "Done", icon: <StatusIcon status="done" /> },
        {
          value: "cancelled",
          label: "Cancelled",
          icon: <StatusIcon status="cancelled" />,
        },
      ],
    },
    {
      key: "priority",
      label: "Priority",
      icon: (
        <CircleAlertIcon  className="size-3.5" />
      ),
      type: "multiselect",
      className: "w-[180px]",
      options: [
        { value: "low", label: "Low", icon: <PriorityIcon priority="low" /> },
        {
          value: "medium",
          label: "Medium",
          icon: <PriorityIcon priority="medium" />,
        },
        {
          value: "high",
          label: "High",
          icon: <PriorityIcon priority="high" />,
        },
        {
          value: "urgent",
          label: "Urgent",
          icon: <PriorityIcon priority="urgent" />,
        },
      ],
    },
  ]

  const [smallFilters, setSmallFilters] = useState<Filter[]>([
    createFilter("priority", "is_any_of", ["high", "urgent"]),
  ])

  const [mediumFilters, setMediumFilters] = useState<Filter[]>([
    createFilter("status", "is", ["todo"]),
  ])

  const [largeFilters, setLargeFilters] = useState<Filter[]>([
    createFilter("email", "contains", ["example@example.com"]),
  ])

  const handleSmallFiltersChange = useCallback((filters: Filter[]) => {
    setSmallFilters(filters)
  }, [])

  const handleMediumFiltersChange = useCallback((filters: Filter[]) => {
    setMediumFilters(filters)
  }, [])

  const handleLargeFiltersChange = useCallback((filters: Filter[]) => {
    setLargeFilters(filters)
  }, [])

  return (
    <div className="flex grow flex-col content-start items-start gap-2.5 space-y-6 self-start">
      <Filters
        size="sm"
        filters={smallFilters}
        fields={fields}
        onChange={handleSmallFiltersChange}
        trigger={
          <Button variant="outline" size="icon-sm">
            <ListFilterIcon />
          </Button>
        }
      />
    </div>
  )
}
"use client"

import { useCallback, useState } from "react"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"

import { Button } from "@/components/ui/button"
import { BanIcon, CircleAlertIcon, CircleCheckIcon, CircleIcon, ClockIcon, GlobeIcon, ListFilterIcon, MailIcon, StarIcon, TagIcon } from 'lucide-react'

const StatusIcon = ({ status }: { status: string }) => {
  switch (status) {
    case "todo":
      return (
        <ClockIcon  className="text-primary" />
      )
    case "in-progress":
      return (
        <CircleAlertIcon  className="text-yellow-500" />
      )
    case "done":
      return (
        <CircleCheckIcon  className="text-green-500" />
      )
    case "cancelled":
      return (
        <BanIcon  className="text-destructive" />
      )
    default:
      return (
        <CircleIcon  className="text-muted-foreground" />
      )
  }
}

// Priority icon component
const PriorityIcon = ({ priority }: { priority: string }) => {
  const colors = {
    low: "text-green-500",
    medium: "text-yellow-500",
    high: "text-orange-500",
    urgent: "text-red-500",
  }
  return (
    <StarIcon  className="colors[priority as keyof typeof colors]" />
  )
}

export function Pattern() {
  // Basic filter fields for size variant demo
  const fields: FilterFieldConfig[] = [
    {
      key: "text",
      label: "Text",
      icon: (
        <TagIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-36",
      placeholder: "Search text...",
    },
    {
      key: "email",
      label: "Email",
      icon: (
        <MailIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-48",
      placeholder: "user@example.com",
    },
    {
      key: "website",
      label: "Website",
      icon: (
        <GlobeIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "https://example.com",
    },
    {
      key: "status",
      label: "Status",
      icon: (
        <ClockIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: false,
      className: "w-[200px]",
      options: [
        { value: "todo", label: "To Do", icon: <StatusIcon status="todo" /> },
        {
          value: "in-progress",
          label: "In Progress",
          icon: <StatusIcon status="in-progress" />,
        },
        { value: "done", label: "Done", icon: <StatusIcon status="done" /> },
        {
          value: "cancelled",
          label: "Cancelled",
          icon: <StatusIcon status="cancelled" />,
        },
      ],
    },
    {
      key: "priority",
      label: "Priority",
      icon: (
        <CircleAlertIcon  className="size-3.5" />
      ),
      type: "multiselect",
      className: "w-[180px]",
      options: [
        { value: "low", label: "Low", icon: <PriorityIcon priority="low" /> },
        {
          value: "medium",
          label: "Medium",
          icon: <PriorityIcon priority="medium" />,
        },
        {
          value: "high",
          label: "High",
          icon: <PriorityIcon priority="high" />,
        },
        {
          value: "urgent",
          label: "Urgent",
          icon: <PriorityIcon priority="urgent" />,
        },
      ],
    },
  ]

  const [smallFilters, setSmallFilters] = useState<Filter[]>([
    createFilter("priority", "is_any_of", ["high", "urgent"]),
  ])

  const [mediumFilters, setMediumFilters] = useState<Filter[]>([
    createFilter("status", "is", ["todo"]),
  ])

  const [largeFilters, setLargeFilters] = useState<Filter[]>([
    createFilter("email", "contains", ["example@example.com"]),
  ])

  const handleSmallFiltersChange = useCallback((filters: Filter[]) => {
    setSmallFilters(filters)
  }, [])

  const handleMediumFiltersChange = useCallback((filters: Filter[]) => {
    setMediumFilters(filters)
  }, [])

  const handleLargeFiltersChange = useCallback((filters: Filter[]) => {
    setLargeFilters(filters)
  }, [])

  return (
    <div className="flex grow flex-col content-start items-start gap-2.5 space-y-6 self-start">
      <Filters
        size="lg"
        filters={smallFilters}
        fields={fields}
        onChange={handleSmallFiltersChange}
        trigger={
          <Button variant="outline" size="icon-sm">
            <ListFilterIcon />
          </Button>
        }
      />
    </div>
  )
}
"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import {
  DateSelector,
  formatDateValue,
  type DateSelectorValue,
} from "@/components/reui/date-selector"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"
import {
  endOfMonth,
  endOfYear,
  format,
  isEqual,
  startOfDay,
  startOfMonth,
  startOfYear,
  subDays,
  subMonths,
  subYears,
} from "date-fns"
import { DateRange } from "react-day-picker"

import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Slider } from "@/components/ui/slider"
import { CalendarIcon, ClockIcon, FunnelXIcon, ListFilterIcon, SlidersVerticalIcon } from 'lucide-react'

// Type for custom renderer props
type CustomRendererProps = {
  values: unknown[]
  onChange: (values: unknown[]) => void
  autoFocus?: boolean
}

// Modal-based Date Selector Component
function CustomModalDateSelector({
  values,
  onChange,
  autoFocus,
}: CustomRendererProps) {
  const value = values?.[0] as DateSelectorValue | undefined
  const [open, setOpen] = useState(false)
  const [internalValue, setInternalValue] = useState<
    DateSelectorValue | undefined
  >(value)

  const formattedValue = value ? formatDateValue(value) : ""
  const displayText = formattedValue || "Select a date"

  useEffect(() => {
    if (autoFocus) {
      const timer = setTimeout(() => setOpen(true), 400)
      return () => clearTimeout(timer)
    }
  }, [autoFocus])

  useEffect(() => {
    if (open) {
      setInternalValue(value)
    }
  }, [open, value])

  const handleApply = () => {
    onChange([internalValue])
    setOpen(false)
  }

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger>{displayText}</DialogTrigger>
      <DialogContent className="sm:max-w-lg" showCloseButton={false}>
        <DialogHeader>
          <DialogTitle>Select Date</DialogTitle>
        </DialogHeader>

        <DateSelector
          value={internalValue}
          onChange={setInternalValue}
          showInput={true}
        />

        <DialogFooter>
          <DialogClose render={<Button variant="outline">Cancel</Button>} />
          <Button onClick={handleApply}>Apply</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

// Custom Date Range Input Component
function CustomDateRangeInput({
  values,
  onChange,
  autoFocus,
}: CustomRendererProps) {
  const [date, setDate] = useState<DateRange | undefined>(
    values?.[0] && typeof values[0] === "string"
      ? {
          from: new Date(values[0] as string),
          to:
            values[1] && typeof values[1] === "string"
              ? new Date(values[1] as string)
              : undefined,
        }
      : undefined
  )
  const [isOpen, setIsOpen] = useState(false)

  useEffect(() => {
    if (autoFocus) {
      const timer = setTimeout(() => setIsOpen(true), 400)
      return () => clearTimeout(timer)
    }
  }, [autoFocus])

  const handleApply = () => {
    if (date?.from) {
      const fromStr = date.from.toISOString().split("T")[0]
      const toStr = date.to ? date.to.toISOString().split("T")[0] : fromStr
      onChange([fromStr, toStr])
    }
    setIsOpen(false)
  }

  const handleCancel = () => {
    setIsOpen(false)
  }

  const handleSelect = (selected: DateRange | undefined) => {
    setDate(selected)
  }

  return (
    <Popover open={isOpen} onOpenChange={setIsOpen}>
      <PopoverTrigger>
        {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 range</span>
        )}
      </PopoverTrigger>
      <PopoverContent className="w-auto p-0" align="start" sideOffset={8}>
        <Calendar
          autoFocus
          mode="range"
          defaultMonth={date?.from}
          showOutsideDays={false}
          selected={date}
          onSelect={handleSelect}
          numberOfMonths={2}
        />
        <div className="border-border flex items-center justify-end gap-1.5 border-t p-3">
          <Button variant="outline" onClick={handleCancel}>
            Cancel
          </Button>
          <Button onClick={handleApply}>Apply</Button>
        </div>
      </PopoverContent>
    </Popover>
  )
}

// Custom Date Range with Presets Input Component
function CustomDateRangeWithPresetsInput({
  values,
  onChange,
  autoFocus,
}: CustomRendererProps) {
  const today = useMemo(() => new Date(), [])

  const presets = useMemo(
    () => [
      { label: "Today", range: { from: today, to: today } },
      {
        label: "Yesterday",
        range: { from: subDays(today, 1), to: subDays(today, 1) },
      },
      { label: "Last 7 days", range: { from: subDays(today, 6), to: today } },
      { label: "Last 30 days", range: { from: subDays(today, 29), to: today } },
      {
        label: "Month to date",
        range: { from: startOfMonth(today), to: today },
      },
      {
        label: "Last month",
        range: {
          from: startOfMonth(subMonths(today, 1)),
          to: endOfMonth(subMonths(today, 1)),
        },
      },
      { label: "Year to date", range: { from: startOfYear(today), to: today } },
      {
        label: "Last year",
        range: {
          from: startOfYear(subYears(today, 1)),
          to: endOfYear(subYears(today, 1)),
        },
      },
    ],
    [today]
  )

  const [month, setMonth] = useState(today)
  const [date, setDate] = useState<DateRange | undefined>(
    values?.[0] && typeof values[0] === "string"
      ? {
          from: new Date(values[0] as string),
          to:
            values[1] && typeof values[1] === "string"
              ? new Date(values[1] as string)
              : undefined,
        }
      : undefined
  )
  const [selectedPreset, setSelectedPreset] = useState<string | null>(null)
  const [isOpen, setIsOpen] = useState(false)

  useEffect(() => {
    if (autoFocus) {
      const timer = setTimeout(() => setIsOpen(true), 400)
      return () => clearTimeout(timer)
    }
  }, [autoFocus])

  useEffect(() => {
    const matchedPreset = presets.find(
      (preset) =>
        isEqual(
          startOfDay(preset.range.from),
          startOfDay(date?.from || new Date(0))
        ) &&
        isEqual(
          startOfDay(preset.range.to),
          startOfDay(date?.to || new Date(0))
        )
    )
    setSelectedPreset(matchedPreset?.label || null)
  }, [date, presets])

  const handleApply = () => {
    if (date?.from) {
      const fromStr = date.from.toISOString().split("T")[0]
      const toStr = date.to ? date.to.toISOString().split("T")[0] : fromStr
      onChange([fromStr, toStr])
    }
    setIsOpen(false)
  }

  const handleCancel = () => {
    setIsOpen(false)
  }

  const handleSelect = (selected: DateRange | undefined) => {
    setDate({
      from: selected?.from || undefined,
      to: selected?.to || undefined,
    })
    setSelectedPreset(null)
  }

  const handlePresetSelect = (preset: (typeof presets)[0]) => {
    setDate(preset.range)
    setMonth(preset.range.from || today)
    setSelectedPreset(preset.label)
  }

  return (
    <Popover open={isOpen} onOpenChange={setIsOpen}>
      <PopoverTrigger>
        {date?.from ? (
          format(date.from, "LLL dd, y") +
          (date.to ? ` - ${format(date.to, "LLL dd, y")}` : "")
        ) : (
          <span>Pick a date range with presets</span>
        )}
      </PopoverTrigger>
      <PopoverContent className="w-auto p-0" align="center" sideOffset={8}>
        <div className="flex max-sm:flex-col">
          <div className="border-border relative max-sm:order-1 max-sm:border-t sm:w-32">
            <div className="border-border h-full py-2 sm:border-e">
              <div className="flex flex-col gap-[2px] px-2">
                {presets.map((preset, index) => (
                  <Button
                    key={index}
                    type="button"
                    variant="ghost"
                    className={cn(
                      "h-8 w-full justify-start",
                      selectedPreset === preset.label && "bg-accent"
                    )}
                    onClick={() => handlePresetSelect(preset)}
                  >
                    {preset.label}
                  </Button>
                ))}
              </div>
            </div>
          </div>
          <Calendar
            autoFocus
            mode="range"
            month={month}
            onMonthChange={setMonth}
            showOutsideDays={false}
            selected={date}
            onSelect={handleSelect}
            numberOfMonths={2}
          />
        </div>
        <div className="border-border flex items-center justify-end gap-1.5 border-t p-3">
          <Button variant="outline" onClick={handleCancel}>
            Cancel
          </Button>
          <Button onClick={handleApply}>Apply</Button>
        </div>
      </PopoverContent>
    </Popover>
  )
}

// Custom DateTime Input Component
function CustomDateTimeInput({
  values,
  onChange,
  autoFocus,
}: CustomRendererProps) {
  const today = new Date()
  const [date, setDate] = useState<Date | undefined>(
    values?.[0] && typeof values[0] === "string"
      ? new Date(values[0] as string)
      : undefined
  )
  const [time, setTime] = useState<string | undefined>(
    values?.[0] && typeof values[0] === "string"
      ? new Date(values[0] as string).toTimeString().slice(0, 5)
      : "10:00"
  )
  const [isOpen, setIsOpen] = useState(false)

  useEffect(() => {
    if (autoFocus) {
      const timer = setTimeout(() => setIsOpen(true), 400)
      return () => clearTimeout(timer)
    }
  }, [autoFocus])

  const timeSlots = [
    { time: "09:00", available: false },
    { time: "09:30", available: false },
    { time: "10:00", available: true },
    { time: "10:30", available: true },
    { time: "11:00", available: true },
    { time: "11:30", available: true },
    { time: "12:00", available: false },
    { time: "12:30", available: true },
    { time: "13:00", available: true },
    { time: "13:30", available: true },
    { time: "14:00", available: true },
    { time: "14:30", available: false },
    { time: "15:00", available: false },
    { time: "15:30", available: true },
    { time: "16:00", available: true },
    { time: "16:30", available: true },
    { time: "17:00", available: true },
    { time: "17:30", available: true },
    { time: "18:00", available: true },
    { time: "18:30", available: true },
    { time: "19:00", available: true },
    { time: "19:30", available: true },
    { time: "20:00", available: true },
    { time: "20:30", available: true },
    { time: "21:00", available: true },
    { time: "21:30", available: true },
    { time: "22:00", available: true },
    { time: "22:30", available: true },
    { time: "23:00", available: true },
    { time: "23:30", available: true },
  ]

  const handleApply = () => {
    if (date && time) {
      const dateTime = new Date(date)
      const [hours, minutes] = time.split(":").map(Number)
      dateTime.setHours(hours, minutes, 0, 0)
      onChange([dateTime.toISOString()])
    }
    setIsOpen(false)
  }

  const handleCancel = () => {
    setIsOpen(false)
  }

  return (
    <Popover open={isOpen} onOpenChange={setIsOpen}>
      <PopoverTrigger>
        {date ? (
          format(date, "PPP") + (time ? ` - ${time}` : "")
        ) : (
          <span>Pick a date and time</span>
        )}
      </PopoverTrigger>
      <PopoverContent className="w-auto gap-0 p-0 pt-1" align="start">
        <div className="flex max-sm:flex-col">
          <Calendar
            mode="single"
            selected={date}
            onSelect={(newDate: Date | undefined) => {
              if (newDate) {
                setDate(newDate)
                setTime(undefined)
              }
            }}
            className="p-2 sm:pe-5"
            disabled={[{ before: today }]}
          />
          <div className="relative w-full max-sm:h-46 sm:w-40">
            <div className="absolute inset-0 py-4 max-sm:border-t">
              <ScrollArea className="h-full sm:border-s">
                <div className="space-y-3">
                  <div className="flex h-5 shrink-0 items-center px-5">
                    <p className="text-sm font-medium">
                      {date ? format(date, "EEEE, d") : "Pick a date"}
                    </p>
                  </div>
                  <div className="grid gap-1.5 px-5 max-sm:grid-cols-2">
                    {timeSlots.map(({ time: timeSlot, available }) => (
                      <Button
                        key={timeSlot}
                        variant={time === timeSlot ? "default" : "outline"}
                        size="sm"
                        className="w-full"
                        onClick={() => setTime(timeSlot)}
                        disabled={!available}
                      >
                        {timeSlot}
                      </Button>
                    ))}
                  </div>
                </div>
              </ScrollArea>
            </div>
          </div>
        </div>
        <div className="border-border flex items-center justify-end gap-1.5 border-t p-3">
          <Button variant="outline" onClick={handleCancel}>
            Cancel
          </Button>
          <Button onClick={handleApply}>Apply</Button>
        </div>
      </PopoverContent>
    </Popover>
  )
}

// Custom Slider Range Input Component
function CustomSliderRangeInput({
  values,
  onChange,
  autoFocus,
}: CustomRendererProps) {
  const [range, setRange] = useState<number[]>(
    values?.[0] &&
      typeof values[0] === "object" &&
      values[0] !== null &&
      "min" in values[0] &&
      "max" in values[0]
      ? [
          (values[0] as { min: number; max: number }).min,
          (values[0] as { min: number; max: number }).max,
        ]
      : [0, 100]
  )
  const [isOpen, setIsOpen] = useState(false)

  useEffect(() => {
    if (autoFocus) {
      const timer = setTimeout(() => setIsOpen(true), 400)
      return () => clearTimeout(timer)
    }
  }, [autoFocus])

  const handleApply = () => {
    onChange([{ min: range[0], max: range[1] }])
    setIsOpen(false)
  }

  const handleCancel = () => {
    setIsOpen(false)
  }

  return (
    <Popover open={isOpen} onOpenChange={setIsOpen}>
      <PopoverTrigger render={<span />}>
        {range[0]} - {range[1]}
      </PopoverTrigger>
      <PopoverContent
        className="w-auto p-4"
        align="start"
        sideOffset={8}
        alignOffset={-8}
      >
        <div className="space-y-2.5">
          <div className="space-y-4 pt-2.5">
            <Slider
              value={range}
              onValueChange={(value) => setRange(value as number[])}
              max={100}
              min={0}
              step={1}
              className="w-[200px]"
            />
            <div className="text-muted-foreground flex justify-between ps-1.5 text-xs">
              <span>0</span>
              <span>100</span>
            </div>
          </div>
          <div className="flex items-center justify-end gap-1.5">
            <Button variant="ghost" size="sm" onClick={handleCancel}>
              Cancel
            </Button>
            <Button size="sm" variant="outline" onClick={handleApply}>
              Apply
            </Button>
          </div>
        </div>
      </PopoverContent>
    </Popover>
  )
}

export function Pattern() {
  const [filters, setFilters] = useState<Filter[]>([
    createFilter("customDateRange", "between", []),
  ])
  const [lastAddedValues, setLastAddedValues] = useState<unknown[] | null>(null)

  const fields: FilterFieldConfig[] = [
    {
      key: "modalDateSelector",
      label: "Modal Date Selector",
      icon: (
        <CalendarIcon  className="size-3.5" />
      ),
      type: "custom",
      operators: [
        { value: "is", label: "is" },
        { value: "is_not", label: "is not" },
      ],
      customRenderer: ({ values, onChange }) => (
        <CustomModalDateSelector
          values={values}
          onChange={onChange}
          autoFocus={values === lastAddedValues}
        />
      ),
    },
    {
      key: "customDateRange",
      label: "Date Range",
      icon: (
        <CalendarIcon  className="size-3.5" />
      ),
      type: "custom",
      operators: [
        { value: "between", label: "between" },
        { value: "not_between", label: "not between" },
      ],
      customRenderer: ({ values, onChange }) => (
        <CustomDateRangeInput
          values={values}
          onChange={onChange}
          autoFocus={values === lastAddedValues}
        />
      ),
    },
    {
      key: "customDateRangePresets",
      label: "Date Range Presets",
      icon: (
        <CalendarIcon  className="size-3.5" />
      ),
      type: "custom",
      operators: [
        { value: "between", label: "between" },
        { value: "not_between", label: "not between" },
      ],
      customRenderer: ({ values, onChange }) => (
        <CustomDateRangeWithPresetsInput
          values={values}
          onChange={onChange}
          autoFocus={values === lastAddedValues}
        />
      ),
    },
    {
      key: "customDateTime",
      label: "Date & Time",
      icon: (
        <ClockIcon  className="size-3.5" />
      ),
      type: "custom",
      operators: [
        { value: "is", label: "is" },
        { value: "before", label: "before" },
        { value: "after", label: "after" },
      ],
      customRenderer: ({ values, onChange }) => (
        <CustomDateTimeInput
          values={values}
          onChange={onChange}
          autoFocus={values === lastAddedValues}
        />
      ),
    },
    {
      key: "customSliderRange",
      label: "Slider Range",
      icon: (
        <SlidersVerticalIcon  className="size-3.5" />
      ),
      type: "custom",
      className: "w-36",
      operators: [
        { value: "between", label: "between" },
        { value: "not_between", label: "not between" },
      ],
      customRenderer: ({ values, onChange }) => (
        <CustomSliderRangeInput
          values={values}
          onChange={onChange}
          autoFocus={values === lastAddedValues}
        />
      ),
    },
  ]

  const handleFiltersChange = useCallback(
    (newFilters: Filter[]) => {
      // Check if a filter was added by comparing IDs
      const added = newFilters.find(
        (nf) => !filters.some((f) => f.id === nf.id)
      )
      if (added) {
        setLastAddedValues(added.values)
      }
      setFilters(newFilters)
    },
    [filters]
  )

  return (
    <div className="flex grow content-start items-start gap-2.5 space-y-6 self-start">
      <div className="flex-1">
        <Filters
          filters={filters}
          fields={fields}
          onChange={handleFiltersChange}
          trigger={
            <Button variant="outline" size="icon">
              <ListFilterIcon />
            </Button>
          }
        />
      </div>

      {filters.length > 0 && (
        <Button variant="outline" onClick={() => setFilters([])}>
          <FunnelXIcon />
          Clear
        </Button>
      )}
    </div>
  )
}
"use client"

import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { Alert, AlertTitle } from "@/components/reui/alert"
import { Badge } from "@/components/reui/badge"
import {
  DataGrid,
  DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridColumnHeader } from "@/components/reui/data-grid/data-grid-column-header"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"
import {
  ColumnDef,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  PaginationState,
  SortingState,
  useReactTable,
} from "@tanstack/react-table"

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import { Skeleton } from "@/components/ui/skeleton"
import { BuildingIcon, CircleAlertIcon, FunnelXIcon, ListFilterIcon, MailIcon, MapPinIcon, UserIcon } from 'lucide-react'

interface IData {
  id: string
  name: string
  availability: "online" | "away" | "busy" | "offline"
  avatar: string
  status: "active" | "inactive"
  flag: string // Emoji flags
  email: string
  company: string
  role: string
  joined: string
  location: string
  balance: number
}

const demoData: IData[] = [
  {
    id: "1",
    name: "Alex Johnson",
    availability: "online",
    avatar:
      "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "us",
    email: "alex@apple.com",
    company: "Apple",
    role: "CEO",
    joined: "Jan, 2024",
    location: "San Francisco, USA",
    balance: 5143.03,
  },
  {
    id: "2",
    name: "Sarah Chen",
    availability: "away",
    avatar:
      "https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "gb",
    email: "sarah@openai.com",
    company: "OpenAI",
    role: "CTO",
    joined: "Mar, 2023",
    location: "London, UK",
    balance: 4321.87,
  },
  {
    id: "3",
    name: "Michael Rodriguez",
    availability: "busy",
    avatar:
      "https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "ca",
    email: "michael@meta.com",
    company: "Meta",
    role: "Designer",
    joined: "Jun, 2022",
    location: "Toronto, Canada",
    balance: 7654.98,
  },
  {
    id: "4",
    name: "Emma Wilson",
    availability: "offline",
    avatar:
      "https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "au",
    email: "emma@tesla.com",
    company: "Tesla",
    role: "Developer",
    joined: "Sep, 2024",
    location: "Sydney, Australia",
    balance: 3456.45,
  },
  {
    id: "5",
    name: "David Kim",
    availability: "online",
    avatar:
      "https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "de",
    email: "david@sap.com",
    company: "SAP",
    role: "Lawyer",
    joined: "Nov, 2023",
    location: "Berlin, Germany",
    balance: 9876.54,
  },
  {
    id: "6",
    name: "Aron Thompson",
    availability: "away",
    avatar:
      "https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "my",
    email: "aron@keenthemes.com",
    company: "Keenthemes",
    role: "Director",
    joined: "Feb, 2022",
    location: "Kuala Lumpur, MY",
    balance: 6214.22,
  },
  {
    id: "7",
    name: "James Brown",
    availability: "busy",
    avatar:
      "https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "es",
    email: "james@bbva.es",
    company: "BBVA",
    role: "Product Manager",
    joined: "Aug, 2024",
    location: "Barcelona, Spain",
    balance: 5321.77,
  },
  {
    id: "8",
    name: "Maria Garcia",
    availability: "offline",
    avatar:
      "https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "jp",
    email: "maria@sony.jp",
    company: "Sony",
    role: "Marketing Lead",
    joined: "Dec, 2023",
    location: "Tokyo, Japan",
    balance: 8452.39,
  },
  {
    id: "9",
    name: "Nick Johnson",
    availability: "online",
    avatar:
      "https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "fr",
    email: "nick@lvmh.fr",
    company: "LVMH",
    role: "Data Scientist",
    joined: "Apr, 2022",
    location: "Paris, France",
    balance: 7345.1,
  },
  {
    id: "10",
    name: "Liam Thompson",
    availability: "away",
    avatar:
      "https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "it",
    email: "liam@eni.it",
    company: "ENI",
    role: "Engineer",
    joined: "Jul, 2024",
    location: "Milan, Italy",
    balance: 5214.88,
  },
  {
    id: "11",
    name: "Alex Johnson",
    availability: "busy",
    avatar:
      "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "br",
    email: "alex@vale.br",
    company: "Vale",
    role: "Software Engineer",
    joined: "May, 2023",
    location: "Rio de Janeiro, Brazil",
    balance: 9421.5,
  },
  {
    id: "12",
    name: "Sarah Chen",
    availability: "offline",
    avatar:
      "https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "in",
    email: "sarah@tata.in",
    company: "Tata",
    role: "Sales Manager",
    joined: "Oct, 2024",
    location: "Mumbai, India",
    balance: 4521.67,
  },
]

// Availability status component
const AvailabilityStatus = ({ availability }: { availability: string }) => {
  const getStatusColor = (status: string) => {
    switch (status) {
      case "online":
        return "bg-green-500"
      case "away":
        return "bg-yellow-500"
      case "busy":
        return "bg-red-500"
      case "offline":
        return "bg-gray-400"
      default:
        return "bg-gray-400"
    }
  }

  const getStatusLabel = (status: string) => {
    switch (status) {
      case "online":
        return "Online"
      case "away":
        return "Away"
      case "busy":
        return "Busy"
      case "offline":
        return "Offline"
      default:
        return "Unknown"
    }
  }

  return (
    <div className="flex items-center gap-1.5">
      <div className={`size-2 rounded-full ${getStatusColor(availability)}`} />
      <span className="text-muted-foreground text-sm">
        {getStatusLabel(availability)}
      </span>
    </div>
  )
}

// Helper to check if a filter has meaningful values
const getActiveFilters = (filters: Filter[]) => {
  return filters.filter((filter) => {
    const { values } = filter

    // Check if filter has meaningful values
    if (!values || values.length === 0) return false

    // For text/string values, check if they're not empty strings
    if (
      values.every((value) => typeof value === "string" && value.trim() === "")
    )
      return false

    // For number values, check if they're not null/undefined
    if (values.every((value) => value === null || value === undefined))
      return false

    // For arrays, check if they're not empty
    if (values.every((value) => Array.isArray(value) && value.length === 0))
      return false

    return true
  })
}

export function Pattern() {
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: 0,
    pageSize: 5,
  })
  const [sorting, setSorting] = useState<SortingState>([
    { id: "name", desc: false },
  ])
  const [filters, setFilters] = useState<Filter[]>([
    createFilter("status", "is", ["active"]),
  ])

  // Async state management
  const [isLoading, setIsLoading] = useState(false)
  const [filteredData, setFilteredData] = useState<IData[]>(demoData)
  const isInitialLoad = useRef(true)

  // Filter field configurations
  const fields: FilterFieldConfig[] = [
    {
      key: "name",
      label: "Name",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "Search names...",
    },
    {
      key: "email",
      label: "Email",
      icon: (
        <MailIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-48",
      placeholder: "user@example.com",
    },
    {
      key: "company",
      label: "Company",
      icon: (
        <BuildingIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: true,
      className: "w-[180px]",
      options: [
        { value: "Apple", label: "Apple" },
        { value: "OpenAI", label: "OpenAI" },
        { value: "Meta", label: "Meta" },
        { value: "Tesla", label: "Tesla" },
        { value: "SAP", label: "SAP" },
        { value: "Keenthemes", label: "Keenthemes" },
        { value: "BBVA", label: "BBVA" },
        { value: "Sony", label: "Sony" },
        { value: "LVMH", label: "LVMH" },
        { value: "ENI", label: "ENI" },
        { value: "Vale", label: "Vale" },
        { value: "Tata", label: "Tata" },
      ],
    },
    {
      key: "role",
      label: "Role",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: true,
      className: "w-[160px]",
      options: [
        { value: "CEO", label: "CEO" },
        { value: "CTO", label: "CTO" },
        { value: "Designer", label: "Designer" },
        { value: "Developer", label: "Developer" },
        { value: "Lawyer", label: "Lawyer" },
        { value: "Director", label: "Director" },
        { value: "Product Manager", label: "Product Manager" },
        { value: "Marketing Lead", label: "Marketing Lead" },
        { value: "Data Scientist", label: "Data Scientist" },
        { value: "Engineer", label: "Engineer" },
        { value: "Software Engineer", label: "Software Engineer" },
        { value: "Sales Manager", label: "Sales Manager" },
      ],
    },
    {
      key: "status",
      label: "Status",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: false,
      className: "w-[140px]",
      options: [
        {
          value: "active",
          label: "Active",
          icon: <div className="size-2 rounded-full bg-green-500"></div>,
        },
        {
          value: "inactive",
          label: "Inactive",
          icon: <div className="bg-destructive size-2 rounded-full"></div>,
        },
        {
          value: "archived",
          label: "Archived",
          icon: <div className="size-2 rounded-full bg-zinc-400"></div>,
        },
      ],
    },
    {
      key: "availability",
      label: "Availability",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: false,
      className: "w-[160px]",
      options: [
        {
          value: "online",
          label: "Online",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-green-500" />
              <span>Online</span>
            </div>
          ),
        },
        {
          value: "away",
          label: "Away",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-yellow-500" />
              <span>Away</span>
            </div>
          ),
        },
        {
          value: "busy",
          label: "Busy",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-red-500" />
              <span>Busy</span>
            </div>
          ),
        },
        {
          value: "offline",
          label: "Offline",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-gray-400" />
              <span>Offline</span>
            </div>
          ),
        },
      ],
    },
    {
      key: "location",
      label: "Location",
      icon: (
        <MapPinIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "Search locations...",
    },
  ]

  // Apply filters to data (shared function)
  const applyFiltersToData = useCallback((newFilters: Filter[]) => {
    let filtered = [...demoData]

    // Filter out empty filters before applying
    const activeFilters = getActiveFilters(newFilters)

    activeFilters.forEach((filter) => {
      const { field, operator, values } = filter

      filtered = filtered.filter((item) => {
        const fieldValue = item[field as keyof IData]

        switch (operator) {
          case "is":
            return values.includes(fieldValue)
          case "is_not":
            return !values.includes(fieldValue)
          case "contains":
            return values.some((value) =>
              String(fieldValue)
                .toLowerCase()
                .includes(String(value).toLowerCase())
            )
          case "not_contains":
            return !values.some((value) =>
              String(fieldValue)
                .toLowerCase()
                .includes(String(value).toLowerCase())
            )
          case "equals":
            return fieldValue === values[0]
          case "not_equals":
            return fieldValue !== values[0]
          case "greater_than":
            return Number(fieldValue) > Number(values[0])
          case "less_than":
            return Number(fieldValue) < Number(values[0])
          case "greater_than_or_equal":
            return Number(fieldValue) >= Number(values[0])
          case "less_than_or_equal":
            return Number(fieldValue) <= Number(values[0])
          case "between":
            if (values.length >= 2) {
              const min = Number(values[0])
              const max = Number(values[1])
              return Number(fieldValue) >= min && Number(fieldValue) <= max
            }
            return true
          case "not_between":
            if (values.length >= 2) {
              const min = Number(values[0])
              const max = Number(values[1])
              return Number(fieldValue) < min || Number(fieldValue) > max
            }
            return true
          case "before":
            return new Date(String(fieldValue)) < new Date(String(values[0]))
          case "after":
            return new Date(String(fieldValue)) > new Date(String(values[0]))
          default:
            return true
        }
      })
    })

    return filtered
  }, [])

  // Simulate async data filtering
  const simulateAsyncFiltering = useCallback(
    async (newFilters: Filter[]) => {
      setIsLoading(true) // Show loading on current data

      // Simulate API call delay
      await new Promise((resolve) =>
        setTimeout(resolve, 800 + Math.random() * 1200)
      )

      // Apply filters and update data after timeout
      const filtered = applyFiltersToData(newFilters)
      setFilteredData(filtered)
      setIsLoading(false)
    },
    [applyFiltersToData]
  )

  const handleFiltersChange = useCallback(
    (newFilters: Filter[]) => {
      const oldActive = getActiveFilters(filters)
      const newActive = getActiveFilters(newFilters)

      setFilters(newFilters)

      // Compare active filters to decide if we need to trigger async search
      // Use stringify for simple deep comparison of filter objects
      if (JSON.stringify(oldActive) === JSON.stringify(newActive)) {
        return
      }

      // Reset pagination when filters change
      setPagination((prev) => ({ ...prev, pageIndex: 0 }))
      // Trigger async filtering
      simulateAsyncFiltering(newFilters)
    },
    [filters, simulateAsyncFiltering]
  )

  // Initial data load - only run once on mount
  useEffect(() => {
    if (isInitialLoad.current) {
      // Apply initial filter without loading state
      const initialFiltered = applyFiltersToData(filters)
      setFilteredData(initialFiltered)
      isInitialLoad.current = false
    }
  }, [filters, applyFiltersToData])

  const columns = useMemo<ColumnDef<IData>[]>(
    () => [
      {
        accessorKey: "name",
        id: "name",
        header: ({ column }) => (
          <DataGridColumnHeader title="Staff" column={column} />
        ),
        cell: ({ row }) => {
          return (
            <div className="flex items-center gap-3">
              <Avatar className="size-8">
                <AvatarImage
                  src={row.original.avatar}
                  alt={row.original.name}
                />
                <AvatarFallback>
                  {row.original.name
                    .split(" ")
                    .map((n) => n[0])
                    .join("")}
                </AvatarFallback>
              </Avatar>
              <div className="space-y-px">
                <div className="text-foreground font-medium">
                  {row.original.name}
                </div>
                <div className="text-muted-foreground truncate text-xs">
                  {row.original.email}
                </div>
              </div>
            </div>
          )
        },
        size: 200,
        enableSorting: true,
        enableHiding: false,
        meta: {
          skeleton: (
            <div className="flex items-center gap-3">
              <Skeleton className="size-8 rounded-full" />
              <div className="space-y-1">
                <Skeleton className="h-4 w-24" />
                <Skeleton className="h-4 w-16" />
              </div>
            </div>
          ),
        },
      },
      {
        accessorKey: "company",
        id: "company",
        header: ({ column }) => (
          <DataGridColumnHeader title="Company" column={column} />
        ),
        cell: (info) => <span>{info.getValue() as string}</span>,
        size: 150,
        enableSorting: true,
        enableHiding: false,
        meta: {
          skeleton: <Skeleton className="h-4 w-20" />,
        },
      },
      {
        accessorKey: "role",
        id: "role",
        header: ({ column }) => (
          <DataGridColumnHeader title="Occupation" column={column} />
        ),
        cell: (info) => <span>{info.getValue() as string}</span>,
        size: 125,
        enableSorting: true,
        enableHiding: false,
        meta: {
          skeleton: <Skeleton className="h-4 w-16" />,
        },
      },
      {
        accessorKey: "status",
        id: "status",
        header: "Status",
        cell: ({ row }) => {
          const status = row.original.status

          if (status == "active") {
            return <Badge variant="success-outline">Active</Badge>
          } else if (status == "inactive") {
            return <Badge variant="destructive-outline">Inactive</Badge>
          } else if (status == "archived") {
            return <Badge variant="warning-outline">Archived</Badge>
          }
        },
        size: 100,
        meta: {
          skeleton: <Skeleton className="h-4 w-16 rounded-full" />,
        },
      },
      {
        accessorKey: "availability",
        id: "availability",
        header: "Availability",
        cell: ({ row }) => (
          <AvailabilityStatus availability={row.original.availability} />
        ),
        size: 120,
        enableSorting: true,
        meta: {
          skeleton: (
            <div className="flex items-center gap-1.5">
              <Skeleton className="size-4 rounded-full" />
              <Skeleton className="h-3.5 w-12" />
            </div>
          ),
        },
      },
      {
        accessorKey: "location",
        id: "location",
        header: ({ column }) => (
          <DataGridColumnHeader title="Location" column={column} />
        ),
        cell: ({ row }) => (
          <div className="flex items-center gap-2">
            <img
              src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
              alt={row.original.flag}
              className="size-4 rounded-full object-cover"
            />
            <span>{row.original.location}</span>
          </div>
        ),
        size: 180,
        enableSorting: true,
        meta: {
          skeleton: (
            <div className="flex items-center gap-2">
              <Skeleton className="size-4 rounded" />
              <Skeleton className="h-3.5 w-24" />
            </div>
          ),
        },
      },
      {
        accessorKey: "balance",
        id: "balance",
        header: ({ column }) => (
          <DataGridColumnHeader title="Balance" column={column} />
        ),
        cell: ({ row }) => (
          <span className="font-medium">
            ${row.original.balance.toLocaleString()}
          </span>
        ),
        size: 120,
        enableSorting: true,
        meta: {
          skeleton: <Skeleton className="h-4 w-16" />,
        },
      },
    ],
    []
  )

  const [columnOrder, setColumnOrder] = useState<string[]>(
    columns.map((column) => column.id as string)
  )

  const table = useReactTable({
    columns,
    data: filteredData,
    pageCount: Math.ceil((filteredData?.length || 0) / pagination.pageSize),
    getRowId: (row: IData) => row.id,
    state: {
      pagination,
      sorting,
      columnOrder,
    },
    onColumnOrderChange: setColumnOrder,
    onPaginationChange: setPagination,
    onSortingChange: setSorting,
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    getSortedRowModel: getSortedRowModel(),
  })

  return (
    <div className="w-full self-start">
      {/* Filters Section */}
      <div className="mb-3.5 flex items-start gap-2.5">
        <div className="flex-1">
          <Filters
            filters={filters}
            fields={fields}
            onChange={handleFiltersChange}
            size="sm"
            trigger={
              <Button variant="outline" size="icon-sm">
                <ListFilterIcon />
              </Button>
            }
          />
        </div>
        {filters.length > 0 && (
          <Button
            variant="outline"
            size="sm"
            onClick={() => {
              setFilters([])
              simulateAsyncFiltering([])
            }}
            disabled={isLoading}
          >
            <FunnelXIcon />
            Clear
          </Button>
        )}
      </div>

      {/* Data Grid */}
      <DataGrid
        table={table}
        isLoading={isLoading}
        loadingMode="skeleton"
        recordCount={filteredData?.length || 0}
        tableLayout={{
          dense: true,
          columnsMovable: true,
        }}
      >
        <div className="w-full space-y-2.5">
          <DataGridContainer>
            <ScrollArea>
              <DataGridTable />
              <ScrollBar orientation="horizontal" />
            </ScrollArea>
          </DataGridContainer>
          <DataGridPagination />
        </div>
      </DataGrid>

      {/* Async Info Alert */}
      <Alert variant="success" className="mt-5">
        <CircleAlertIcon />
        <AlertTitle>
          Async Mode: Simulated API Delay of <strong>800-2000ms</strong>
        </AlertTitle>
      </Alert>
    </div>
  )
}
"use client"

import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { Alert, AlertTitle } from "@/components/reui/alert"
import { Badge } from "@/components/reui/badge"
import {
  DataGrid,
  DataGridContainer,
} from "@/components/reui/data-grid/data-grid"
import { DataGridColumnHeader } from "@/components/reui/data-grid/data-grid-column-header"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridTable } from "@/components/reui/data-grid/data-grid-table"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/reui/filters"
import {
  ColumnDef,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  PaginationState,
  SortingState,
  useReactTable,
} from "@tanstack/react-table"
import {
  createParser,
  parseAsBoolean,
  parseAsJson,
  useQueryState,
  useQueryStates,
} from "nuqs"

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import { Skeleton } from "@/components/ui/skeleton"
import { BuildingIcon, CircleAlertIcon, FunnelXIcon, ListFilterIcon, MailIcon, MapPinIcon, UserIcon } from 'lucide-react'

interface IData {
  id: string
  name: string
  availability: "online" | "away" | "busy" | "offline"
  avatar: string
  status: "active" | "inactive"
  flag: string // Emoji flags
  email: string
  company: string
  role: string
  joined: string
  location: string
  balance: number
}

const demoData: IData[] = [
  {
    id: "1",
    name: "Alex Johnson",
    availability: "online",
    avatar:
      "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "us",
    email: "alex@apple.com",
    company: "Apple",
    role: "CEO",
    joined: "Jan, 2024",
    location: "San Francisco, USA",
    balance: 5143.03,
  },
  {
    id: "2",
    name: "Sarah Chen",
    availability: "away",
    avatar:
      "https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "gb",
    email: "sarah@openai.com",
    company: "OpenAI",
    role: "CTO",
    joined: "Mar, 2023",
    location: "London, UK",
    balance: 4321.87,
  },
  {
    id: "3",
    name: "Michael Rodriguez",
    availability: "busy",
    avatar:
      "https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "ca",
    email: "michael@meta.com",
    company: "Meta",
    role: "Designer",
    joined: "Jun, 2022",
    location: "Toronto, Canada",
    balance: 7654.98,
  },
  {
    id: "4",
    name: "Emma Wilson",
    availability: "offline",
    avatar:
      "https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "au",
    email: "emma@tesla.com",
    company: "Tesla",
    role: "Developer",
    joined: "Sep, 2024",
    location: "Sydney, Australia",
    balance: 3456.45,
  },
  {
    id: "5",
    name: "David Kim",
    availability: "online",
    avatar:
      "https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "de",
    email: "david@sap.com",
    company: "SAP",
    role: "Lawyer",
    joined: "Nov, 2023",
    location: "Berlin, Germany",
    balance: 9876.54,
  },
  {
    id: "6",
    name: "Aron Thompson",
    availability: "away",
    avatar:
      "https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "my",
    email: "aron@keenthemes.com",
    company: "Keenthemes",
    role: "Director",
    joined: "Feb, 2022",
    location: "Kuala Lumpur, MY",
    balance: 6214.22,
  },
  {
    id: "7",
    name: "James Brown",
    availability: "busy",
    avatar:
      "https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "es",
    email: "james@bbva.es",
    company: "BBVA",
    role: "Product Manager",
    joined: "Aug, 2024",
    location: "Barcelona, Spain",
    balance: 5321.77,
  },
  {
    id: "8",
    name: "Maria Garcia",
    availability: "offline",
    avatar:
      "https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "jp",
    email: "maria@sony.jp",
    company: "Sony",
    role: "Marketing Lead",
    joined: "Dec, 2023",
    location: "Tokyo, Japan",
    balance: 8452.39,
  },
  {
    id: "9",
    name: "Nick Johnson",
    availability: "online",
    avatar:
      "https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "fr",
    email: "nick@lvmh.fr",
    company: "LVMH",
    role: "Data Scientist",
    joined: "Apr, 2022",
    location: "Paris, France",
    balance: 7345.1,
  },
  {
    id: "10",
    name: "Liam Thompson",
    availability: "away",
    avatar:
      "https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "it",
    email: "liam@eni.it",
    company: "ENI",
    role: "Engineer",
    joined: "Jul, 2024",
    location: "Milan, Italy",
    balance: 5214.88,
  },
  {
    id: "11",
    name: "Alex Johnson",
    availability: "busy",
    avatar:
      "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "br",
    email: "alex@vale.br",
    company: "Vale",
    role: "Software Engineer",
    joined: "May, 2023",
    location: "Rio de Janeiro, Brazil",
    balance: 9421.5,
  },
  {
    id: "12",
    name: "Sarah Chen",
    availability: "offline",
    avatar:
      "https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "in",
    email: "sarah@tata.in",
    company: "Tata",
    role: "Sales Manager",
    joined: "Oct, 2024",
    location: "Mumbai, India",
    balance: 4521.67,
  },
]

// Availability status component
const AvailabilityStatus = ({ availability }: { availability: string }) => {
  const getStatusColor = (status: string) => {
    switch (status) {
      case "online":
        return "bg-green-500"
      case "away":
        return "bg-yellow-500"
      case "busy":
        return "bg-red-500"
      case "offline":
        return "bg-gray-400"
      default:
        return "bg-gray-400"
    }
  }

  const getStatusLabel = (status: string) => {
    switch (status) {
      case "online":
        return "Online"
      case "away":
        return "Away"
      case "busy":
        return "Busy"
      case "offline":
        return "Offline"
      default:
        return "Unknown"
    }
  }

  return (
    <div className="flex items-center gap-1.5">
      <div className={`size-2 rounded-full ${getStatusColor(availability)}`} />
      <span className="text-muted-foreground text-sm">
        {getStatusLabel(availability)}
      </span>
    </div>
  )
}

// Helper to check if a filter has meaningful values
const getActiveFilters = (filters: Filter[]) => {
  return filters.filter((filter) => {
    const { values } = filter

    // Check if filter has meaningful values
    if (!values || values.length === 0) return false

    // For text/string values, check if they're not empty strings
    if (
      values.every((value) => typeof value === "string" && value.trim() === "")
    )
      return false

    // For number values, check if they're not null/undefined
    if (values.every((value) => value === null || value === undefined))
      return false

    // For arrays, check if they're not empty
    if (values.every((value) => Array.isArray(value) && value.length === 0))
      return false

    return true
  })
}

type FilterState = { operator: string; values: string[] }

// Custom parser for "operator:value1|value2" format
const parseAsFilterValue = createParser<FilterState>({
  parse: (queryValue: string) => {
    if (!queryValue) return null
    const separatorIndex = queryValue.indexOf(":")
    if (separatorIndex === -1) {
      return { operator: "is", values: queryValue.split("|").filter(Boolean) }
    }
    const operator = queryValue.slice(0, separatorIndex)
    const values = queryValue
      .slice(separatorIndex + 1)
      .split("|")
      .filter(Boolean)
    return { operator, values }
  },
  serialize: (filter: FilterState) => {
    if (!filter.values?.length) return ""
    return `${filter.operator}:${filter.values.join("|")}`
  },
})

export function Pattern() {
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: 0,
    pageSize: 5,
  })

  // Sorting state synced with URL
  const [sorting, setSorting] = useQueryState(
    "sort",
    parseAsJson<SortingState>((v) => v as SortingState).withDefault([
      { id: "name", desc: false },
    ])
  )

  // Individual filter states synced with URL
  const [filterStates, setFilterStates] = useQueryStates(
    {
      filters: parseAsBoolean.withDefault(false),
      name: parseAsFilterValue,
      email: parseAsFilterValue,
      company: parseAsFilterValue,
      role: parseAsFilterValue,
      status: parseAsFilterValue,
      availability: parseAsFilterValue,
      location: parseAsFilterValue,
    },
    { history: "replace", shallow: true }
  )

  // Derived filters array for the Filters component
  const filters = useMemo(() => {
    return Object.entries(filterStates)
      .filter(
        ([key, state]) =>
          key !== "filters" && state !== null && typeof state !== "boolean"
      )
      .map(([key, state]) => {
        const filterState = state as FilterState
        return createFilter(key, filterState.operator, filterState.values)
      })
  }, [filterStates])

  // Async state management
  const [isLoading, setIsLoading] = useState(false)
  const [filteredData, setFilteredData] = useState<IData[]>(demoData)
  const isInitialLoad = useRef(true)
  const filterTimeoutRef = useRef<NodeJS.Timeout | null>(null)

  // Filter field configurations
  const fields: FilterFieldConfig[] = [
    {
      key: "name",
      label: "Name",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "Search names...",
    },
    {
      key: "email",
      label: "Email",
      icon: (
        <MailIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-48",
      placeholder: "user@example.com",
    },
    {
      key: "company",
      label: "Company",
      icon: (
        <BuildingIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: true,
      className: "w-[180px]",
      options: [
        { value: "Apple", label: "Apple" },
        { value: "OpenAI", label: "OpenAI" },
        { value: "Meta", label: "Meta" },
        { value: "Tesla", label: "Tesla" },
        { value: "SAP", label: "SAP" },
        { value: "Keenthemes", label: "Keenthemes" },
        { value: "BBVA", label: "BBVA" },
        { value: "Sony", label: "Sony" },
        { value: "LVMH", label: "LVMH" },
        { value: "ENI", label: "ENI" },
        { value: "Vale", label: "Vale" },
        { value: "Tata", label: "Tata" },
      ],
    },
    {
      key: "role",
      label: "Role",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: true,
      className: "w-[160px]",
      options: [
        { value: "CEO", label: "CEO" },
        { value: "CTO", label: "CTO" },
        { value: "Designer", label: "Designer" },
        { value: "Developer", label: "Developer" },
        { value: "Lawyer", label: "Lawyer" },
        { value: "Director", label: "Director" },
        { value: "Product Manager", label: "Product Manager" },
        { value: "Marketing Lead", label: "Marketing Lead" },
        { value: "Data Scientist", label: "Data Scientist" },
        { value: "Engineer", label: "Engineer" },
        { value: "Software Engineer", label: "Software Engineer" },
        { value: "Sales Manager", label: "Sales Manager" },
      ],
    },
    {
      key: "status",
      label: "Status",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "multiselect",
      searchable: false,
      className: "w-[140px]",
      options: [
        {
          value: "active",
          label: "Active",
          icon: <div className="size-2 rounded-full bg-green-500"></div>,
        },
        {
          value: "inactive",
          label: "Inactive",
          icon: <div className="bg-destructive size-2 rounded-full"></div>,
        },
        {
          value: "archived",
          label: "Archived",
          icon: <div className="size-2 rounded-full bg-zinc-400"></div>,
        },
      ],
    },
    {
      key: "availability",
      label: "Availability",
      icon: (
        <UserIcon  className="size-3.5" />
      ),
      type: "select",
      searchable: false,
      className: "w-[160px]",
      options: [
        {
          value: "online",
          label: "Online",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-green-500" />
              <span>Online</span>
            </div>
          ),
        },
        {
          value: "away",
          label: "Away",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-yellow-500" />
              <span>Away</span>
            </div>
          ),
        },
        {
          value: "busy",
          label: "Busy",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-red-500" />
              <span>Busy</span>
            </div>
          ),
        },
        {
          value: "offline",
          label: "Offline",
          icon: (
            <div className="flex items-center gap-2">
              <div className="size-2 rounded-full bg-gray-400" />
              <span>Offline</span>
            </div>
          ),
        },
      ],
    },
    {
      key: "location",
      label: "Location",
      icon: (
        <MapPinIcon  className="size-3.5" />
      ),
      type: "text",
      className: "w-40",
      placeholder: "Search locations...",
    },
  ]

  // Apply filters to data (shared function)
  const applyFiltersToData = useCallback((newFilters: Filter[]) => {
    let filtered = [...demoData]

    // Filter out empty filters before applying
    const activeFilters = getActiveFilters(newFilters)

    activeFilters.forEach((filter) => {
      const { field, operator, values } = filter

      filtered = filtered.filter((item) => {
        const fieldValue = item[field as keyof IData]

        switch (operator) {
          case "is":
            return values.includes(fieldValue)
          case "is_not":
            return !values.includes(fieldValue)
          case "contains":
            return values.some((value) =>
              String(fieldValue)
                .toLowerCase()
                .includes(String(value).toLowerCase())
            )
          case "not_contains":
            return !values.some((value) =>
              String(fieldValue)
                .toLowerCase()
                .includes(String(value).toLowerCase())
            )
          case "equals":
            return fieldValue === values[0]
          case "not_equals":
            return fieldValue !== values[0]
          case "greater_than":
            return Number(fieldValue) > Number(values[0])
          case "less_than":
            return Number(fieldValue) < Number(values[0])
          case "greater_than_or_equal":
            return Number(fieldValue) >= Number(values[0])
          case "less_than_or_equal":
            return Number(fieldValue) <= Number(values[0])
          case "between":
            if (values.length >= 2) {
              const min = Number(values[0])
              const max = Number(values[1])
              return Number(fieldValue) >= min && Number(fieldValue) <= max
            }
            return true
          case "not_between":
            if (values.length >= 2) {
              const min = Number(values[0])
              const max = Number(values[1])
              return Number(fieldValue) < min || Number(fieldValue) > max
            }
            return true
          case "before":
            return new Date(String(fieldValue)) < new Date(String(values[0]))
          case "after":
            return new Date(String(fieldValue)) > new Date(String(values[0]))
          default:
            return true
        }
      })
    })

    return filtered
  }, [])

  // Simulate async data filtering
  const simulateAsyncFiltering = useCallback(
    async (newFilters: Filter[]) => {
      setIsLoading(true) // Show loading on current data

      // Simulate API call delay
      await new Promise((resolve) =>
        setTimeout(resolve, 800 + Math.random() * 1200)
      )

      // Apply filters and update data after timeout
      const filtered = applyFiltersToData(newFilters)
      setFilteredData(filtered)
      setIsLoading(false)
    },
    [applyFiltersToData]
  )

  const handleFiltersChange = useCallback(
    (newFilters: Filter[]) => {
      const oldActive = getActiveFilters(filters)
      const newActive = getActiveFilters(newFilters)

      // Convert Filter[] back to individual query states
      const nextStates: Record<string, FilterState | boolean | null> = {}

      // Reset all tracked fields first
      Object.keys(filterStates).forEach((key) => {
        nextStates[key] = null
      })

      // Set only active ones
      newFilters.forEach((f) => {
        if (f.values.length > 0) {
          nextStates[f.field] = {
            operator: f.operator,
            values: f.values as string[],
          }
        }
      })

      // Set the filters marker if any filters exist
      nextStates.filters = newFilters.length > 0 ? true : null

      setFilterStates(nextStates)

      if (JSON.stringify(oldActive) === JSON.stringify(newActive)) {
        return
      }

      setPagination((prev) => ({ ...prev, pageIndex: 0 }))

      // Clear any pending timeout
      if (filterTimeoutRef.current) {
        clearTimeout(filterTimeoutRef.current)
      }

      // Add a small debounce before starting the async simulation
      filterTimeoutRef.current = setTimeout(() => {
        simulateAsyncFiltering(newFilters)
      }, 300)
    },
    [filters, filterStates, setFilterStates, simulateAsyncFiltering]
  )

  // Cleanup timeout on unmount
  useEffect(() => {
    return () => {
      if (filterTimeoutRef.current) {
        clearTimeout(filterTimeoutRef.current)
      }
    }
  }, [])

  // Initial data load - only run once on mount
  useEffect(() => {
    if (isInitialLoad.current) {
      // Apply initial filter without loading state
      const initialFiltered = applyFiltersToData(filters || [])
      setFilteredData(initialFiltered)
      isInitialLoad.current = false
    }
  }, [filters, applyFiltersToData])

  const columns = useMemo<ColumnDef<IData>[]>(
    () => [
      {
        accessorKey: "name",
        id: "name",
        header: ({ column }) => (
          <DataGridColumnHeader title="Staff" column={column} />
        ),
        cell: ({ row }) => {
          return (
            <div className="flex items-center gap-3">
              <Avatar className="size-8">
                <AvatarImage
                  src={row.original.avatar}
                  alt={row.original.name}
                />
                <AvatarFallback>
                  {row.original.name
                    .split(" ")
                    .map((n) => n[0])
                    .join("")}
                </AvatarFallback>
              </Avatar>
              <div className="space-y-px">
                <div className="text-foreground font-medium">
                  {row.original.name}
                </div>
                <div className="text-muted-foreground truncate text-xs">
                  {row.original.email}
                </div>
              </div>
            </div>
          )
        },
        size: 200,
        enableSorting: true,
        enableHiding: false,
        meta: {
          skeleton: (
            <div className="flex items-center gap-3">
              <Skeleton className="size-8 rounded-full" />
              <div className="space-y-1">
                <Skeleton className="h-4 w-24" />
                <Skeleton className="h-4 w-16" />
              </div>
            </div>
          ),
        },
      },
      {
        accessorKey: "company",
        id: "company",
        header: ({ column }) => (
          <DataGridColumnHeader title="Company" column={column} />
        ),
        cell: (info) => <span>{info.getValue() as string}</span>,
        size: 150,
        enableSorting: true,
        enableHiding: false,
        meta: {
          skeleton: <Skeleton className="h-4 w-20" />,
        },
      },
      {
        accessorKey: "role",
        id: "role",
        header: ({ column }) => (
          <DataGridColumnHeader title="Occupation" column={column} />
        ),
        cell: (info) => <span>{info.getValue() as string}</span>,
        size: 125,
        enableSorting: true,
        enableHiding: false,
        meta: {
          skeleton: <Skeleton className="h-4 w-16" />,
        },
      },
      {
        accessorKey: "status",
        id: "status",
        header: "Status",
        cell: ({ row }) => {
          const status = row.original.status

          if (status == "active") {
            return <Badge variant="success-outline">Active</Badge>
          } else if (status == "inactive") {
            return <Badge variant="destructive-outline">Inactive</Badge>
          } else if (status == "archived") {
            return <Badge variant="warning-outline">Archived</Badge>
          }
        },
        size: 100,
        meta: {
          skeleton: <Skeleton className="h-4 w-16 rounded-full" />,
        },
      },
      {
        accessorKey: "availability",
        id: "availability",
        header: "Availability",
        cell: ({ row }) => (
          <AvailabilityStatus availability={row.original.availability} />
        ),
        size: 120,
        enableSorting: true,
        meta: {
          skeleton: (
            <div className="flex items-center gap-1.5">
              <Skeleton className="size-4 rounded-full" />
              <Skeleton className="h-3.5 w-12" />
            </div>
          ),
        },
      },
      {
        accessorKey: "location",
        id: "location",
        header: ({ column }) => (
          <DataGridColumnHeader title="Location" column={column} />
        ),
        cell: ({ row }) => (
          <div className="flex items-center gap-2">
            <img
              src={`https://flagcdn.com/${row.original.flag.toLowerCase()}.svg`}
              alt={row.original.flag}
              className="size-4 rounded-full object-cover"
            />
            <span>{row.original.location}</span>
          </div>
        ),
        size: 180,
        enableSorting: true,
        meta: {
          skeleton: (
            <div className="flex items-center gap-2">
              <Skeleton className="size-4 rounded" />
              <Skeleton className="h-3.5 w-24" />
            </div>
          ),
        },
      },
      {
        accessorKey: "balance",
        id: "balance",
        header: ({ column }) => (
          <DataGridColumnHeader title="Balance" column={column} />
        ),
        cell: ({ row }) => (
          <span className="font-medium">
            ${row.original.balance.toLocaleString()}
          </span>
        ),
        size: 120,
        enableSorting: true,
        meta: {
          skeleton: <Skeleton className="h-4 w-16" />,
        },
      },
    ],
    []
  )

  const [columnOrder, setColumnOrder] = useState<string[]>(
    columns.map((column) => column.id as string)
  )

  const table = useReactTable({
    columns,
    data: filteredData,
    pageCount: Math.ceil((filteredData?.length || 0) / pagination.pageSize),
    getRowId: (row: IData) => row.id,
    state: {
      pagination,
      sorting: sorting || [],
      columnOrder,
    },
    onColumnOrderChange: setColumnOrder,
    onPaginationChange: setPagination,
    onSortingChange: (updater) => {
      const nextSorting =
        typeof updater === "function" ? updater(sorting || []) : updater
      setSorting(nextSorting, { history: "replace", shallow: true })
    },
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    getSortedRowModel: getSortedRowModel(),
  })

  return (
    <div className="w-full self-start">
      {/* Filters Section */}
      <div className="mb-3.5 flex items-start gap-2.5">
        <div className="flex-1">
          <Filters
            filters={filters}
            fields={fields}
            onChange={handleFiltersChange}
            size="sm"
            trigger={
              <Button variant="outline" size="icon-sm">
                <ListFilterIcon />
              </Button>
            }
          />
        </div>
        {filters.length > 0 && (
          <Button
            variant="outline"
            size="sm"
            onClick={() => {
              if (filterTimeoutRef.current) {
                clearTimeout(filterTimeoutRef.current)
              }
              // Clear all filters in the URL
              const clearedStates: Record<string, null> = {}
              Object.keys(filterStates).forEach(
                (key) => (clearedStates[key] = null)
              )
              clearedStates.filters = null
              setFilterStates(clearedStates)
              simulateAsyncFiltering([])
            }}
            disabled={isLoading}
          >
            <FunnelXIcon />
            Clear
          </Button>
        )}
      </div>

      {/* Data Grid */}
      <DataGrid
        table={table}
        isLoading={isLoading}
        loadingMode="skeleton"
        recordCount={filteredData?.length || 0}
        tableLayout={{
          dense: true,
          columnsMovable: true,
        }}
      >
        <div className="w-full space-y-2.5">
          <DataGridContainer>
            <ScrollArea>
              <DataGridTable />
              <ScrollBar orientation="horizontal" />
            </ScrollArea>
          </DataGridContainer>
          <DataGridPagination />
        </div>
      </DataGrid>

      {/* Async Info Alert */}
      <Alert variant="success" className="mt-5">
        <CircleAlertIcon />
        <AlertTitle>
          Async Mode: Simulated API Delay of <strong>800-2000ms</strong>
        </AlertTitle>
      </Alert>
    </div>
  )
}
"use client"

import { useCallback, useMemo, useState } from "react"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
  type FilterI18nConfig,
} from "@/components/reui/filters"

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { BuildingIcon, ChevronDownIcon, CircleCheckIcon, ListFilterIcon, MailIcon, MapPinIcon, UserIcon } from 'lucide-react'

// Internationalization configurations
const i18nConfigs: Record<string, FilterI18nConfig> = {
  en: {
    // UI Labels
    addFilter: "Add filter",
    searchFields: "Search fields...",
    noFieldsFound: "No fields found.",
    noResultsFound: "No results found.",
    select: "Select...",
    true: "True",
    false: "False",
    min: "Min",
    max: "Max",
    to: "to",
    typeAndPressEnter: "Type and press Enter to add tag",
    selected: "selected",
    selectedCount: "selected",
    percent: "%",
    defaultCurrency: "$",
    defaultColor: "#000000",
    addFilterTitle: "Add filter",

    // Operators
    operators: {
      is: "is",
      isNot: "is not",
      isAnyOf: "is any of",
      isNotAnyOf: "is not any of",
      includesAll: "includes all",
      excludesAll: "excludes all",
      before: "before",
      after: "after",
      between: "between",
      notBetween: "not between",
      contains: "contains",
      notContains: "does not contain",
      startsWith: "starts with",
      endsWith: "ends with",
      isExactly: "is exactly",
      equals: "equals",
      notEquals: "not equals",
      greaterThan: "greater than",
      lessThan: "less than",
      overlaps: "overlaps",
      includes: "includes",
      excludes: "excludes",
      includesAllOf: "includes all of",
      includesAnyOf: "includes any of",
      empty: "is empty",
      notEmpty: "is not empty",
    },

    // Placeholders
    placeholders: {
      enterField: (fieldType: string) => `Enter ${fieldType}...`,
      selectField: "Select...",
      searchField: (fieldName: string) =>
        `Search ${fieldName.toLowerCase()}...`,
      enterKey: "Enter key...",
      enterValue: "Enter value...",
    },

    // Helper functions
    helpers: {
      formatOperator: (operator: string) => operator.replace(/_/g, " "),
    },

    // Validation
    validation: {
      invalidEmail: "Invalid email format",
      invalidUrl: "Invalid URL format",
      invalidTel: "Invalid phone format",
      invalid: "Invalid input format",
    },
  },
  es: {
    // UI Labels
    addFilter: "Agregar filtro",
    searchFields: "Buscar campos...",
    noFieldsFound: "No se encontraron campos.",
    noResultsFound: "No se encontraron resultados.",
    select: "Seleccionar...",
    true: "Verdadero",
    false: "Falso",
    min: "Mín",
    max: "Máx",
    to: "a",
    typeAndPressEnter: "Escriba y presione Enter para agregar etiqueta",
    selected: "seleccionado",
    selectedCount: "seleccionados",
    percent: "%",
    defaultCurrency: "€",
    defaultColor: "#000000",
    addFilterTitle: "Agregar filtro",

    // Operators
    operators: {
      is: "es",
      isNot: "no es",
      isAnyOf: "es cualquiera de",
      isNotAnyOf: "no es cualquiera de",
      includesAll: "incluye todos",
      excludesAll: "excluye todos",
      before: "antes de",
      after: "después de",
      between: "entre",
      notBetween: "no entre",
      contains: "contiene",
      notContains: "no contiene",
      startsWith: "comienza con",
      endsWith: "termina con",
      isExactly: "es exactamente",
      equals: "igual a",
      notEquals: "no igual a",
      greaterThan: "mayor que",
      lessThan: "menor que",
      overlaps: "se superpone",
      includes: "incluye",
      excludes: "excluye",
      includesAllOf: "incluye todos de",
      includesAnyOf: "incluye cualquiera de",
      empty: "está vacío",
      notEmpty: "no está vacío",
    },

    // Placeholders
    placeholders: {
      enterField: (fieldType: string) => `Ingrese ${fieldType}...`,
      selectField: "Seleccionar...",
      searchField: (fieldName: string) =>
        `Buscar ${fieldName.toLowerCase()}...`,
      enterKey: "Ingrese clave...",
      enterValue: "Ingrese valor...",
    },

    // Helper functions
    helpers: {
      formatOperator: (operator: string) => operator.replace(/_/g, " "),
    },

    // Validation
    validation: {
      invalidEmail: "Formato de email inválido",
      invalidUrl: "Formato de URL inválido",
      invalidTel: "Formato de teléfono inválido",
      invalid: "Formato de entrada inválido",
    },
  },
  fr: {
    // UI Labels
    addFilter: "Ajouter un filtre",
    searchFields: "Rechercher des champs...",
    noFieldsFound: "Aucun champ trouvé.",
    noResultsFound: "Aucun résultat trouvé.",
    select: "Sélectionner...",
    true: "Vrai",
    false: "Faux",
    min: "Min",
    max: "Max",
    to: "à",
    typeAndPressEnter: "Tapez et appuyez sur Entrée pour ajouter une étiquette",
    selected: "sélectionné",
    selectedCount: "sélectionnés",
    percent: "%",
    defaultCurrency: "€",
    defaultColor: "#000000",
    addFilterTitle: "Ajouter un filtre",

    // Operators
    operators: {
      is: "est",
      isNot: "n'est pas",
      isAnyOf: "est l'un de",
      isNotAnyOf: "n'est pas l'un de",
      includesAll: "inclut tous",
      excludesAll: "exclut tous",
      before: "avant",
      after: "après",
      between: "entre",
      notBetween: "pas entre",
      contains: "contient",
      notContains: "ne contient pas",
      startsWith: "commence par",
      endsWith: "se termine par",
      isExactly: "est exactement",
      equals: "égal à",
      notEquals: "pas égal à",
      greaterThan: "supérieur à",
      lessThan: "inférieur à",
      overlaps: "se chevauche",
      includes: "inclut",
      excludes: "exclut",
      includesAllOf: "inclut tous de",
      includesAnyOf: "inclut l'un de",
      empty: "est vide",
      notEmpty: "n'est pas vide",
    },

    // Placeholders
    placeholders: {
      enterField: (fieldType: string) => `Entrez ${fieldType}...`,
      selectField: "Sélectionner...",
      searchField: (fieldName: string) =>
        `Rechercher ${fieldName.toLowerCase()}...`,
      enterKey: "Entrez la clé...",
      enterValue: "Entrez la valeur...",
    },

    // Helper functions
    helpers: {
      formatOperator: (operator: string) => operator.replace(/_/g, " "),
    },

    // Validation
    validation: {
      invalidEmail: "Format d'email invalide",
      invalidUrl: "Format d'URL invalide",
      invalidTel: "Format de téléphone invalide",
      invalid: "Format de saisie invalide",
    },
  },
  de: {
    // UI Labels
    addFilter: "Filter hinzufügen",
    searchFields: "Felder suchen...",
    noFieldsFound: "Keine Felder gefunden.",
    noResultsFound: "Keine Ergebnisse gefunden.",
    select: "Auswählen...",
    true: "Wahr",
    false: "Falsch",
    min: "Min",
    max: "Max",
    to: "bis",
    typeAndPressEnter: "Tippen und Enter drücken, um Tag hinzuzufügen",
    selected: "ausgewählt",
    selectedCount: "ausgewählt",
    percent: "%",
    defaultCurrency: "€",
    defaultColor: "#000000",
    addFilterTitle: "Filter hinzufügen",

    // Operators
    operators: {
      is: "ist",
      isNot: "ist nicht",
      isAnyOf: "ist eines von",
      isNotAnyOf: "ist nicht eines von",
      includesAll: "enthält alle",
      excludesAll: "schließt alle aus",
      before: "vor",
      after: "nach",
      between: "zwischen",
      notBetween: "nicht zwischen",
      contains: "enthält",
      notContains: "enthält nicht",
      startsWith: "beginnt mit",
      endsWith: "endet mit",
      isExactly: "ist genau",
      equals: "gleich",
      notEquals: "nicht gleich",
      greaterThan: "größer als",
      lessThan: "kleiner als",
      overlaps: "überschneidet sich",
      includes: "enthält",
      excludes: "schließt aus",
      includesAllOf: "enthält alle von",
      includesAnyOf: "enthält eines von",
      empty: "ist leer",
      notEmpty: "ist nicht leer",
    },

    // Placeholders
    placeholders: {
      enterField: (fieldType: string) => `${fieldType} eingeben...`,
      selectField: "Auswählen...",
      searchField: (fieldName: string) =>
        `${fieldName.toLowerCase()} suchen...`,
      enterKey: "Schlüssel eingeben...",
      enterValue: "Wert eingeben...",
    },

    // Helper functions
    helpers: {
      formatOperator: (operator: string) => operator.replace(/_/g, " "),
    },

    // Validation
    validation: {
      invalidEmail: "Ungültiges E-Mail-Format",
      invalidUrl: "Ungültiges URL-Format",
      invalidTel: "Ungültiges Telefonformat",
      invalid: "Ungültiges Format",
    },
  },
  ja: {
    // UI Labels
    addFilter: "フィルターを追加",
    searchFields: "フィールドを検索...",
    noFieldsFound: "フィールドが見つかりません。",
    noResultsFound: "結果が見つかりません。",
    select: "選択...",
    true: "真",
    false: "偽",
    min: "最小",
    max: "最大",
    to: "から",
    typeAndPressEnter: "入力してEnterキーを押してタグを追加",
    selected: "選択済み",
    selectedCount: "選択済み",
    percent: "%",
    defaultCurrency: "¥",
    defaultColor: "#000000",
    addFilterTitle: "フィルターを追加",

    // Operators
    operators: {
      is: "は",
      isNot: "ではない",
      isAnyOf: "のいずれか",
      isNotAnyOf: "のいずれでもない",
      includesAll: "すべて含む",
      excludesAll: "すべて除外",
      before: "より前",
      after: "より後",
      between: "の間",
      notBetween: "の間ではない",
      contains: "含む",
      notContains: "含まない",
      startsWith: "で始まる",
      endsWith: "で終わる",
      isExactly: "正確に",
      equals: "等しい",
      notEquals: "等しくない",
      greaterThan: "より大きい",
      lessThan: "より小さい",
      overlaps: "重複する",
      includes: "含む",
      excludes: "除外",
      includesAllOf: "すべて含む",
      includesAnyOf: "いずれか含む",
      empty: "空",
      notEmpty: "空でない",
    },

    // Placeholders
    placeholders: {
      enterField: (fieldType: string) => `${fieldType}を入力...`,
      selectField: "選択...",
      searchField: (fieldName: string) => `${fieldName.toLowerCase()}を検索...`,
      enterKey: "キーを入力...",
      enterValue: "値を入力...",
    },

    // Helper functions
    helpers: {
      formatOperator: (operator: string) => operator.replace(/_/g, " "),
    },

    // Validation
    validation: {
      invalidEmail: "無効なメール形式",
      invalidUrl: "無効なURL形式",
      invalidTel: "無効な電話番号形式",
      invalid: "無効な形式",
    },
  },
}

// Language options for the selector
const languageOptions = [
  { value: "en", label: "English", flag: "us" },
  { value: "es", label: "Español", flag: "es" },
  { value: "fr", label: "Français", flag: "fr" },
  { value: "de", label: "Deutsch", flag: "de" },
  { value: "ja", label: "日本語", flag: "jp" },
]

export function Pattern() {
  const [currentLanguage, setCurrentLanguage] = useState<string>("es")
  const [filters, setFilters] = useState<Filter[]>([
    createFilter("status", "is", ["active"]),
  ])

  // Get current i18n configuration
  const currentI18n = useMemo(
    () => i18nConfigs[currentLanguage],
    [currentLanguage]
  )

  // Filter field configurations with localized labels
  const fields: FilterFieldConfig[] = useMemo(() => {
    const fieldLabels = {
      en: {
        name: "Name",
        email: "Email",
        company: "Company",
        role: "Role",
        status: "Status",
        location: "Location",
        joined: "Joined Date",
        balance: "Balance",
        rating: "Rating",
      },
      es: {
        name: "Nombre",
        email: "Correo electrónico",
        company: "Empresa",
        role: "Rol",
        status: "Estado",
        location: "Ubicación",
        joined: "Fecha de ingreso",
        balance: "Saldo",
        rating: "Calificación",
      },
      fr: {
        name: "Nom",
        email: "E-mail",
        company: "Entreprise",
        role: "Rôle",
        status: "Statut",
        location: "Localisation",
        joined: "Date d'adhésion",
        balance: "Solde",
        rating: "Note",
      },
      de: {
        name: "Name",
        email: "E-Mail",
        company: "Unternehmen",
        role: "Rolle",
        status: "Status",
        location: "Standort",
        joined: "Beitrittsdatum",
        balance: "Guthaben",
        rating: "Bewertung",
      },
      ja: {
        name: "名前",
        email: "メール",
        company: "会社",
        role: "役割",
        status: "ステータス",
        location: "場所",
        joined: "参加日",
        balance: "残高",
        rating: "評価",
      },
    }

    const labels =
      fieldLabels[currentLanguage as keyof typeof fieldLabels] || fieldLabels.en

    return [
      {
        key: "name",
        label: labels.name,
        icon: (
          <UserIcon  className="size-3.5" />
        ),
        type: "text",
        className: "w-40",
        placeholder:
          currentLanguage === "en"
            ? "Search names..."
            : currentLanguage === "es"
              ? "Buscar nombres..."
              : currentLanguage === "fr"
                ? "Rechercher des noms..."
                : currentLanguage === "de"
                  ? "Namen suchen..."
                  : "名前を検索...",
      },
      {
        key: "email",
        label: labels.email,
        icon: (
          <MailIcon  className="size-3.5" />
        ),
        type: "text",
        className: "w-48",
        placeholder: "user@example.com",
      },
      {
        key: "company",
        label: labels.company,
        icon: (
          <BuildingIcon  className="size-3.5" />
        ),
        type: "select",
        searchable: true,
        className: "w-[180px]",
        options: [
          { value: "TechCorp", label: "TechCorp" },
          { value: "StartupCo", label: "StartupCo" },
          { value: "BigCorp", label: "BigCorp" },
          { value: "InnovateTech", label: "InnovateTech" },
          { value: "GlobalNet", label: "GlobalNet" },
        ],
      },
      {
        key: "role",
        label: labels.role,
        icon: (
          <UserIcon  className="size-3.5" />
        ),
        type: "select",
        searchable: true,
        className: "w-[160px]",
        options: [
          { value: "Developer", label: "Developer" },
          { value: "Designer", label: "Designer" },
          { value: "Manager", label: "Manager" },
          { value: "Product Manager", label: "Product Manager" },
          { value: "Sales Rep", label: "Sales Rep" },
        ],
      },
      {
        key: "status",
        label: labels.status,
        icon: (
          <CircleCheckIcon  className="size-3.5" />
        ),
        type: "select",
        searchable: false,
        className: "w-[140px]",
        options: [
          {
            value: "active",
            label:
              currentLanguage === "en"
                ? "Active"
                : currentLanguage === "es"
                  ? "Activo"
                  : currentLanguage === "fr"
                    ? "Actif"
                    : currentLanguage === "de"
                      ? "Aktiv"
                      : "アクティブ",
          },
          {
            value: "inactive",
            label:
              currentLanguage === "en"
                ? "Inactive"
                : currentLanguage === "es"
                  ? "Inactivo"
                  : currentLanguage === "fr"
                    ? "Inactif"
                    : currentLanguage === "de"
                      ? "Inaktiv"
                      : "非アクティブ",
          },
        ],
      },
      {
        key: "location",
        label: labels.location,
        icon: (
          <MapPinIcon  className="size-3.5" />
        ),
        type: "text",
        className: "w-40",
        placeholder:
          currentLanguage === "en"
            ? "Search locations..."
            : currentLanguage === "es"
              ? "Buscar ubicaciones..."
              : currentLanguage === "fr"
                ? "Rechercher des lieux..."
                : currentLanguage === "de"
                  ? "Standorte suchen..."
                  : "場所を検索...",
      },
    ]
  }, [currentLanguage])

  const handleFiltersChange = useCallback((newFilters: Filter[]) => {
    setFilters(newFilters)
  }, [])

  return (
    <div className="flex w-full grow items-start justify-between space-y-6 self-start">
      {/* Filters Section */}
      <Filters
        filters={filters}
        fields={fields}
        onChange={handleFiltersChange}
        size="sm"
        trigger={
          <Button variant="outline" size="icon-sm">
            <ListFilterIcon />
          </Button>
        }
        i18n={currentI18n}
      />
      <div className="flex items-center gap-2">
        {/* Language selection */}
        <DropdownMenu>
          <DropdownMenuTrigger
            render={
              <Button
                variant="outline"
                size="sm"
                className="flex items-center gap-2"
              >
                {(() => {
                  const lang = languageOptions.find(
                    (l) => l.value === currentLanguage
                  )
                  return (
                    lang && (
                      <img
                        src={`https://flagcdn.com/${lang.flag.toLowerCase()}.svg`}
                        alt={lang.flag}
                        className="size-4 rounded-full object-cover"
                      />
                    )
                  )
                })()}
                <span>
                  {
                    languageOptions.find(
                      (lang) => lang.value === currentLanguage
                    )?.label
                  }
                </span>
                <ChevronDownIcon  className="size-4" />
              </Button>
            }
          />
          <DropdownMenuContent align="start">
            {languageOptions.map((lang) => (
              <DropdownMenuItem
                key={lang.value}
                onClick={() => setCurrentLanguage(lang.value)}
                className="flex items-center gap-2"
              >
                <img
                  src={`https://flagcdn.com/${lang.flag.toLowerCase()}.svg`}
                  alt={lang.flag}
                  className="size-4 rounded-full object-cover"
                />
                <span>{lang.label}</span>
              </DropdownMenuItem>
            ))}
          </DropdownMenuContent>
        </DropdownMenu>
      </div>
    </div>
  )
}