> ## Documentation Index
> Fetch the complete documentation index at: https://woltz.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Filter

> A pre-built, customizable filter UI component that integrates with `useCriteria`.

<a href="https://react-rich-domain.netlify.app/#filter" target="_blank">Live Demo</a>

## Instalation

<CodeGroup>
  ```bash npm theme={null}
  npx shadcn add "https://tarcisioandrade.github.io/rich-domain/packages/react-rich-domain/public/r/filter.json"
  ```

  ```bash pnpm theme={null}
  pnpm dlx shadcn add "https://tarcisioandrade.github.io/rich-domain/packages/react-rich-domain/public/r/filter.json"
  ```

  ```bash yarn theme={null}
  yarn dlx shadcn add "https://tarcisioandrade.github.io/rich-domain/packages/react-rich-domain/public/r/filter.json"
  ```
</CodeGroup>

### Basic Usage

```tsx theme={null}
import { useCriteria } from "@/hooks/use-criteria";
import { Filter } from "@/components/filter/filter";
import type { QueryFilter } from "@/lib/filter-utils";

interface User {
  id: string;
  name: string;
  email: string;
  status: "active" | "inactive" | "pending";
  age: number;
  createdAt: Date;
}

// Define filterable fields
const userFields: QueryFilter[] = [
  {
    field: "name",
    fieldLabel: "Name",
    type: "string",
  },
  {
    field: "email",
    fieldLabel: "Email",
    type: "string",
  },
  {
    field: "status",
    fieldLabel: "Status",
    type: "string",
    options: [
      { value: "active", label: "Active" },
      { value: "inactive", label: "Inactive" },
      { value: "pending", label: "Pending" },
    ],
  },
  {
    field: "age",
    fieldLabel: "Age",
    type: "number",
  },
  {
    field: "createdAt",
    fieldLabel: "Created At",
    type: "date",
  },
];

function UserFilters() {
  const { filters, addOrReplaceByIndex, removeFilter, clearFilters } =
    useCriteria<User>();

  return (
    <Filter
      fields={userFields}
      filters={filters}
      addOrReplaceByIndex={addOrReplaceByIndex}
      removeFilter={removeFilter}
      clearFilters={clearFilters}
    />
  );
}
```

### QueryFilter Type

```tsx theme={null}
interface QueryFilter {
  // Field path in the entity (e.g., "name", "profile.bio")
  field: string;

  // Display label for the field
  fieldLabel: string;

  // Field type determines available operators
  type: "string" | "number" | "date" | "boolean";

  // Optional: mark if field path traverses a collection (1:N or N:N relation)
  isCollection?: boolean;

  // Optional: mark if field can be null
  isNullable?: boolean;

  // Optional: predefined options for select fields
  options?: Array<{
    value: string;
    label: string;
    icon?: React.ReactNode;
  }>;
}
```

### Field Types and Operators

The Filter component automatically shows the appropriate operators based on field type:

| Type      | Operators                                                                                                            |
| --------- | -------------------------------------------------------------------------------------------------------------------- |
| `string`  | equals, notEquals, contains, startsWith, endsWith, in, notIn, isNull, isNotNull                                      |
| `number`  | equals, notEquals, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, between, in, notIn, isNull, isNotNull |
| `date`    | equals, notEquals, greaterThan, lessThan, between, isNull, isNotNull                                                 |
| `boolean` | equals, notEquals, isNull, isNotNull                                                                                 |

### With Options (Select Fields)

For fields with predefined values, use the `options` property:

```tsx theme={null}
const orderFields: QueryFilter[] = [
  {
    field: "status",
    fieldLabel: "Order Status",
    type: "string",
    options: [
      { value: "draft", label: "Draft", icon: <FileIcon /> },
      { value: "confirmed", label: "Confirmed", icon: <CheckIcon /> },
      { value: "shipped", label: "Shipped", icon: <TruckIcon /> },
      { value: "delivered", label: "Delivered", icon: <PackageIcon /> },
    ],
  },
  {
    field: "priority",
    fieldLabel: "Priority",
    type: "string",
    options: [
      { value: "low", label: "Low" },
      { value: "medium", label: "Medium" },
      { value: "high", label: "High" },
    ],
  },
];
```

### Nested Fields

Support for nested entity paths:

```tsx theme={null}
const userFields: QueryFilter[] = [
  {
    field: "profile.bio",
    fieldLabel: "Bio",
    type: "string",
  },
  {
    field: "address.city",
    fieldLabel: "City",
    type: "string",
  },
  {
    field: "address.country",
    fieldLabel: "Country",
    type: "string",
    options: [
      { value: "US", label: "United States" },
      { value: "BR", label: "Brazil" },
      { value: "UK", label: "United Kingdom" },
    ],
  },
];
```

### Collection Fields

When filtering through a 1:N or N:N relation (e.g., User has many Posts), use `isCollection: true`:

```tsx theme={null}
const userFields: QueryFilter[] = [
  {
    field: "name",
    fieldLabel: "Name",
    type: "string",
  },
  {
    // Filtering through a collection requires isCollection: true
    field: "posts.title",
    fieldLabel: "Post Title",
    type: "string",
    isCollection: true, // ✅ Required for collections
  },
  {
    field: "posts.status",
    fieldLabel: "Post Status",
    type: "string",
    isCollection: true,
    options: [
      { value: "draft", label: "Draft" },
      { value: "published", label: "Published" },
    ],
  },
];
```

When `isCollection: true`, the filter will automatically use `{ quantifier: "some" }` to generate the correct Prisma query:

```typescript theme={null}
// Without isCollection (incorrect for collections)
{ posts: { title: { contains: "hello" } } }  // ❌ Error

// With isCollection: true (correct)
{ posts: { some: { title: { contains: "hello" } } } }  // ✅ Works
```
