# ContactList

> A list of contacts as a table or cards with avatar, company, stage, tags, owner and last activity, plus search, stage and tag and owner filters, bulk select, loading skeletons and an empty state.

Source: https://docs.nasaqui.com/components/contact-list

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/contact-list.json
```

The contacts screen of a CRM: people with their company, lifecycle stage, tags, owner and last activity. It is
`EntityList` with the columns, cards and filters already set up, so you pass contacts and get both views.

## When to use

- A CRM or address book of people.
- Any list of people with a stage, tags and an owner.

## When not to use

- Companies: use [`CompanyList`](https://docs.nasaqui.com/components/company-list).
- A different kind of record: build on [`EntityList`](https://docs.nasaqui.com/components/entity-list).

## Import

```tsx
import { ContactList } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { ContactList, type Contact } from "@fadymondy/nasaq/web";

const contacts: Contact[] = [
  {
    id: "1",
    name: "Sara Ali",
    email: "sara@example.com",
    company: "Nasaq",
    stage: "customer",
    tags: [{ label: "VIP", hue: "violet" }],
    owner: { name: "Omar Nasser" },
    lastActivity: new Date(),
  },
];

export function Contacts() {
  return <ContactList contacts={contacts} onRowClick={(c) => console.log(c.id)} />;
}
```

## Anatomy

`ContactList` renders `EntityList` (see its anatomy). Columns: name (avatar, name, email), company, phone (hidden by
default), stage, tags, owner, last activity. Facets: stage, tags, owner, built from the data.

## API

`ContactListProps` extends the `EntityList` props (`view`, `defaultView`, `pageSize`, `selectable`, `toolbar`,
`bulkActions`, `rowActions`, `onRowClick`, `loading`, `error`, `onRetry`, `empty`, `defaultSort`, `className`).

| Prop | Type | Description |
| --- | --- | --- |
| `contacts` | `Contact[]` | `{ id, name, email?, phone?, avatar?, company?, jobTitle?, stage?, tags?, owner?, lastActivity? }`. |
| `label` | `string` | Accessible name. Default "Contacts" / "جهات الاتصال". |
| `labels` | `Partial<ContactListLabels>` | Override any string, including `stages`. |

`ContactStage` is `"lead" | "prospect" | "customer" | "churned"`.

## Examples

**Loading, then an error**

```tsx
<ContactList contacts={[]} loading />
<ContactList contacts={[]} error="Could not load contacts" onRetry={reload} />
```

**Bulk actions and export**

```tsx
<ContactList
  contacts={contacts}
  bulkActions={({ selectedRows }) => <Button onClick={() => assign(selectedRows)}>Assign owner</Button>}
  toolbar={({ filteredRows, selectedRows }) => (
    <ExportButton columns={cols} scopes={{ selected: selectedRows, filtered: filteredRows, all: contacts }} filename="contacts" />
  )}
/>
```

## Accessibility

Inherited from `EntityList`: a table or a card grid with one tab stop, named checkboxes ("Select Sara Ali"), a named
row menu, a live result count.

## RTL & i18n

- English and Arabic strings ship, including stage names. Names are your data; emails and phone numbers are set left-to-right.
- Relative times use the locale.

## Styling & tokens

- Stage uses `Status` tones; tags use `Badge variant="tag"` hues. Extend with `className`.

## Do / Don't

- Do give every contact a stable `id`.
- Do pass `loading` while fetching, so users see skeletons and not "no contacts".
- Don't sort or filter yourself; the list does it.

## Related

- [`EntityList`](https://docs.nasaqui.com/components/entity-list)
- [`CompanyList`](https://docs.nasaqui.com/components/company-list)
- [`ExportButton`](https://docs.nasaqui.com/components/export-action)

## Lab

https://docs.nasaqui.com/?path=/docs/components-crm-contact-list--docs

## Code

### React

```tsx
import { ContactList, type Contact } from "@fadymondy/nasaq/web";

const contacts: Contact[] = [
  {
    id: "1",
    name: "Sara Ali",
    email: "sara@example.com",
    company: "Nasaq",
    stage: "customer",
    tags: [{ label: "VIP", hue: "violet" }],
    owner: { name: "Omar Nasser" },
    lastActivity: new Date(),
  },
];

export function Contacts() {
  return <ContactList contacts={contacts} onRowClick={(c) => console.log(c.id)} />;
}
```

### shadcn

```tsx
import { ContactList, type Contact } from "@/components/ui/contact-list";

const contacts: Contact[] = [
  {
    id: "1",
    name: "Sara Ali",
    email: "sara@example.com",
    company: "Nasaq",
    stage: "customer",
    tags: [{ label: "VIP", hue: "violet" }],
    owner: { name: "Omar Nasser" },
    lastActivity: new Date(),
  },
];

export function Contacts() {
  return <ContactList contacts={contacts} onRowClick={(c) => console.log(c.id)} />;
}
```

### Vue

```vue
<script setup lang="ts">
import { NqContactList, type Contact } from "@fadymondy/nasaq/vue";

const contacts: Contact[] = [
  {
    id: "1",
    name: "Sara Ali",
    email: "sara@example.com",
    company: "Nasaq",
    stage: "customer",
    tags: [{ label: "VIP", hue: "violet" }],
    owner: { name: "Omar Nasser" },
    lastActivity: "2026-09-28T09:00:00Z",
  },
];
</script>

<template>
  <NqContactList :contacts="contacts" :on-row-click="(c: Contact) => console.log(c.id)" />
</template>
```

### Blade

```blade
<x-nq::contact-list :contacts="[
    [
        'id' => '1',
        'name' => 'Sara Ali',
        'email' => 'sara@example.com',
        'company' => 'Nasaq',
        'stage' => 'customer',
        'tags' => [['label' => 'VIP', 'hue' => 'violet']],
        'owner' => ['name' => 'Omar Nasser'],
        'lastActivity' => '2026-09-28T09:00:00Z',
    ],
]" />
{{-- <x-nq::contact-list :contacts="$contacts" /> Bubbling events: nq-entity-list-row-click { row } (row.id is the contact id). See the component header for the contacts shape and labels. --}}
```

### HTML + Alpine

```html
<div data-slot="contact-list" class="min-w-0">
    <div data-slot="entity-list" x-data="nqEntityList(JSON.parse('[{\u0022nameCell\u0022:\u0022\\u003Cdiv data-slot=\\u0022entity-identity\\u0022 class=\\u0022flex min-w-0 items-center gap-3\\u0022\\u003E\\n    \\u003Cspan data-slot=\\u0022avatar\\u0022 x-data=\\u0022nqAvatar(0)\\u0022\\n    class=\\u0022inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-8 text-caption rounded-full\\u0022\\u003E\\n                    \\u003Cspan data-slot=\\u0022avatar-fallback\\u0022 class=\\u0022flex size-full items-center justify-center\\u0022 x-bind=\\u0022fallback\\u0022\\n             role=\\u0022img\\u0022 aria-label=\\u0022Sara Ali\\u0022 \\u003ESA\\u003C\\\/span\\u003E\\n    \\u003C\\\/span\\u003E\\n    \\u003Cdiv class=\\u0022flex min-w-0 flex-col\\u0022\\u003E\\n        \\u003Cspan class=\\u0022truncate text-label text-foreground\\u0022\\u003ESara Ali\\u003C\\\/span\\u003E\\n        \\u003Cspan class=\\u0022truncate text-body-sm text-muted-foreground\\u0022\\u003E\\u003Cbdi dir=\\u0022ltr\\u0022\\u003Esara@example.com\\u003C\\\/bdi\\u003E\\u003C\\\/span\\u003E    \\u003C\\\/div\\u003E\\n\\u003C\\\/div\\u003E\u0022,\u0022companyCell\u0022:\u0022\\u003Cspan class=\\u0022flex min-w-0 flex-col\\u0022\\u003E\\n    \\u003Cspan class=\\u0022truncate\\u0022\\u003ENasaq\\u003C\\\/span\\u003E\\n    \\u003C\\\/span\\u003E\u0022,\u0022stageCell\u0022:\u0022\\u003Cspan data-slot=\\u0022status\\u0022 data-tone=\\u0022success\\u0022 class=\\u0022inline-flex min-w-0 items-center gap-1.5 text-body-sm text-foreground\\u0022\\u003E\\n            \\u003Csvg aria-hidden=\\u0022true\\u0022 class=\\u0022size-3.5 shrink-0 text-nq-success-text\\u0022 xmlns=\\u0022http:\\\/\\\/www.w3.org\\\/2000\\\/svg\\u0022 viewBox=\\u00220 0 24 24\\u0022 fill=\\u0022none\\u0022 stroke=\\u0022currentColor\\u0022 stroke-width=\\u00222\\u0022 stroke-linecap=\\u0022round\\u0022 stroke-linejoin=\\u0022round\\u0022\\u003E\\n  \\u003Ccircle cx=\\u002212\\u0022 cy=\\u002212\\u0022 r=\\u002210\\u0022\\\/\\u003E\\n  \\u003Cpath d=\\u0022m9 12 2 2 4-4\\u0022\\\/\\u003E\\n\\u003C\\\/svg\\u003E        \\u003Cspan class=\\u0022truncate\\u0022  title=\\u0022Customer\\u0022 \\u003ECustomer\\u003C\\\/span\\u003E\\n\\u003C\\\/span\\u003E\u0022,\u0022tagsCell\u0022:\u0022\\u003Cspan data-slot=\\u0022tag-list\\u0022 class=\\u0022inline-flex flex-wrap items-center gap-1\\u0022\\u003E\\n        \\u003Cspan data-slot=\\u0022badge\\u0022  style=\\u0022--tag-solid: var(--nq-tag-violet); --tag-soft: var(--nq-tag-violet-soft)\\u0022     class=\\u0022inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [\\u0026_svg]:size-3 border-transparent bg-[var(--tag-soft)] text-[var(--tag-solid)]\\u0022\\u003EVIP\\u003C\\\/span\\u003E\\n            \\u003C\\\/span\\u003E\u0022,\u0022ownerCell\u0022:\u0022\\u003Cspan data-slot=\\u0022person-cell\\u0022 class=\\u0022inline-flex min-w-0 items-center gap-2\\u0022\\u003E\\n        \\u003Cspan data-slot=\\u0022avatar\\u0022 x-data=\\u0022nqAvatar(0)\\u0022\\n    class=\\u0022inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-6 text-[10px] rounded-full\\u0022\\u003E\\n                    \\u003Cspan data-slot=\\u0022avatar-fallback\\u0022 class=\\u0022flex size-full items-center justify-center\\u0022 x-bind=\\u0022fallback\\u0022\\n             role=\\u0022img\\u0022 aria-label=\\u0022Omar Nasser\\u0022 \\u003EON\\u003C\\\/span\\u003E\\n    \\u003C\\\/span\\u003E\\n        \\u003Cspan class=\\u0022truncate text-body-sm\\u0022\\u003EOmar Nasser\\u003C\\\/span\\u003E\\n    \\u003C\\\/span\\u003E\u0022,\u0022activityCell\u0022:\u0022\\u003Ctime data-slot=\\u0022date-time\\u0022 datetime=\\u00222026-09-28T09:00:00+00:00\\u0022 dir=\\u0022auto\\u0022  title=\\u0022Sep 28, 2026\\u0022     class=\\u0022tabular-nums [unicode-bidi:isolate] text-body-sm text-muted-foreground\\u0022\\u003E1 day ago\\u003C\\\/time\\u003E\u0022,\u0022searchText\u0022:\u0022Sara Ali sara@example.com  Nasaq\u0022,\u0022stageRank\u0022:2,\u0022activityTs\u0022:1790586000,\u0022id\u0022:\u00221\u0022,\u0022name\u0022:\u0022Sara Ali\u0022,\u0022email\u0022:\u0022sara@example.com\u0022,\u0022company\u0022:\u0022Nasaq\u0022,\u0022stage\u0022:\u0022customer\u0022,\u0022stageLabel\u0022:\u0022Customer\u0022,\u0022tagsText\u0022:\u0022VIP\u0022,\u0022tags\u0022:[\u0022VIP\u0022],\u0022owner\u0022:\u0022Omar Nasser\u0022,\u0022lastActivityText\u0022:\u00221 day ago\u0022,\u0022card\u0022:\u0022\\u003Cdiv class=\\u0022flex min-w-0 flex-col gap-3\\u0022\\u003E\\n    \\u003Cdiv data-slot=\\u0022entity-identity\\u0022 class=\\u0022flex min-w-0 items-center gap-3 pe-(--entity-card-controls)\\u0022\\u003E\\n    \\u003Cspan data-slot=\\u0022avatar\\u0022 x-data=\\u0022nqAvatar(0)\\u0022\\n    class=\\u0022inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-10 text-label rounded-full\\u0022\\u003E\\n                    \\u003Cspan data-slot=\\u0022avatar-fallback\\u0022 class=\\u0022flex size-full items-center justify-center\\u0022 x-bind=\\u0022fallback\\u0022\\n             role=\\u0022img\\u0022 aria-label=\\u0022Sara Ali\\u0022 \\u003ESA\\u003C\\\/span\\u003E\\n    \\u003C\\\/span\\u003E\\n    \\u003Cdiv class=\\u0022flex min-w-0 flex-col\\u0022\\u003E\\n        \\u003Cspan class=\\u0022truncate text-label text-foreground\\u0022\\u003ESara Ali\\u003C\\\/span\\u003E\\n        \\u003Cspan class=\\u0022truncate text-body-sm text-muted-foreground\\u0022\\u003ENasaq\\u003C\\\/span\\u003E    \\u003C\\\/div\\u003E\\n\\u003C\\\/div\\u003E\\n    \\u003Cbdi dir=\\u0022ltr\\u0022 class=\\u0022truncate text-body-sm text-muted-foreground\\u0022\\u003Esara@example.com\\u003C\\\/bdi\\u003E\\n    \\u003Cdiv class=\\u0022flex flex-col gap-1.5\\u0022\\u003E\\n        \\u003Cdiv data-slot=\\u0022card-meta\\u0022 class=\\u0022flex items-center justify-between gap-3 text-body-sm\\u0022\\u003E\\n    \\u003Cspan class=\\u0022shrink-0 text-muted-foreground\\u0022\\u003EStage\\u003C\\\/span\\u003E\\n    \\u003Cspan class=\\u0022min-w-0 truncate text-end\\u0022\\u003E\\u003Cspan data-slot=\\u0022status\\u0022 data-tone=\\u0022success\\u0022 class=\\u0022inline-flex min-w-0 items-center gap-1.5 text-body-sm text-foreground\\u0022\\u003E\\n            \\u003Csvg aria-hidden=\\u0022true\\u0022 class=\\u0022size-3.5 shrink-0 text-nq-success-text\\u0022 xmlns=\\u0022http:\\\/\\\/www.w3.org\\\/2000\\\/svg\\u0022 viewBox=\\u00220 0 24 24\\u0022 fill=\\u0022none\\u0022 stroke=\\u0022currentColor\\u0022 stroke-width=\\u00222\\u0022 stroke-linecap=\\u0022round\\u0022 stroke-linejoin=\\u0022round\\u0022\\u003E\\n  \\u003Ccircle cx=\\u002212\\u0022 cy=\\u002212\\u0022 r=\\u002210\\u0022\\\/\\u003E\\n  \\u003Cpath d=\\u0022m9 12 2 2 4-4\\u0022\\\/\\u003E\\n\\u003C\\\/svg\\u003E        \\u003Cspan class=\\u0022truncate\\u0022  title=\\u0022Customer\\u0022 \\u003ECustomer\\u003C\\\/span\\u003E\\n\\u003C\\\/span\\u003E\\u003C\\\/span\\u003E\\n\\u003C\\\/div\\u003E\\n                \\u003Cdiv data-slot=\\u0022card-meta\\u0022 class=\\u0022flex items-center justify-between gap-3 text-body-sm\\u0022\\u003E\\n    \\u003Cspan class=\\u0022shrink-0 text-muted-foreground\\u0022\\u003EOwner\\u003C\\\/span\\u003E\\n    \\u003Cspan class=\\u0022min-w-0 truncate text-end\\u0022\\u003E\\u003Cspan data-slot=\\u0022person-cell\\u0022 class=\\u0022inline-flex min-w-0 items-center gap-2\\u0022\\u003E\\n        \\u003Cspan data-slot=\\u0022avatar\\u0022 x-data=\\u0022nqAvatar(0)\\u0022\\n    class=\\u0022inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-secondary align-middle font-medium text-secondary-foreground size-6 text-[10px] rounded-full\\u0022\\u003E\\n                    \\u003Cspan data-slot=\\u0022avatar-fallback\\u0022 class=\\u0022flex size-full items-center justify-center\\u0022 x-bind=\\u0022fallback\\u0022\\n             role=\\u0022img\\u0022 aria-label=\\u0022Omar Nasser\\u0022 \\u003EON\\u003C\\\/span\\u003E\\n    \\u003C\\\/span\\u003E\\n        \\u003Cspan class=\\u0022truncate text-body-sm\\u0022\\u003EOmar Nasser\\u003C\\\/span\\u003E\\n    \\u003C\\\/span\\u003E\\u003C\\\/span\\u003E\\n\\u003C\\\/div\\u003E\\n        \\u003Cdiv data-slot=\\u0022card-meta\\u0022 class=\\u0022flex items-center justify-between gap-3 text-body-sm\\u0022\\u003E\\n    \\u003Cspan class=\\u0022shrink-0 text-muted-foreground\\u0022\\u003ELast activity\\u003C\\\/span\\u003E\\n    \\u003Cspan class=\\u0022min-w-0 truncate text-end\\u0022\\u003E\\u003Ctime data-slot=\\u0022date-time\\u0022 datetime=\\u00222026-09-28T09:00:00+00:00\\u0022 dir=\\u0022auto\\u0022  title=\\u0022Sep 28, 2026\\u0022     class=\\u0022tabular-nums [unicode-bidi:isolate] text-body-sm text-muted-foreground\\u0022\\u003E1 day ago\\u003C\\\/time\\u003E\\u003C\\\/span\\u003E\\n\\u003C\\\/div\\u003E\\n    \\u003C\\\/div\\u003E\\n    \\u003Cspan data-slot=\\u0022tag-list\\u0022 class=\\u0022inline-flex flex-wrap items-center gap-1\\u0022\\u003E\\n        \\u003Cspan data-slot=\\u0022badge\\u0022  style=\\u0022--tag-solid: var(--nq-tag-violet); --tag-soft: var(--nq-tag-violet-soft)\\u0022     class=\\u0022inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [\\u0026_svg]:size-3 border-transparent bg-[var(--tag-soft)] text-[var(--tag-solid)]\\u0022\\u003EVIP\\u003C\\\/span\\u003E\\n            \\u003C\\\/span\\u003E\\n\\u003C\\\/div\\u003E\u0022}]'), JSON.parse('[{\u0022id\u0022:\u0022name\u0022,\u0022type\u0022:\u0022html\u0022,\u0022key\u0022:\u0022nameCell\u0022,\u0022sortKey\u0022:\u0022name\u0022,\u0022searchKey\u0022:\u0022searchText\u0022,\u0022header\u0022:\u0022Name\u0022,\u0022sortable\u0022:true,\u0022searchable\u0022:true},{\u0022id\u0022:\u0022company\u0022,\u0022type\u0022:\u0022html\u0022,\u0022key\u0022:\u0022companyCell\u0022,\u0022sortKey\u0022:\u0022company\u0022,\u0022header\u0022:\u0022Company\u0022,\u0022sortable\u0022:true},{\u0022id\u0022:\u0022stage\u0022,\u0022type\u0022:\u0022html\u0022,\u0022key\u0022:\u0022stageCell\u0022,\u0022sortKey\u0022:\u0022stageRank\u0022,\u0022header\u0022:\u0022Stage\u0022,\u0022sortable\u0022:true},{\u0022id\u0022:\u0022tags\u0022,\u0022type\u0022:\u0022html\u0022,\u0022key\u0022:\u0022tagsCell\u0022,\u0022header\u0022:\u0022Tags\u0022},{\u0022id\u0022:\u0022owner\u0022,\u0022type\u0022:\u0022html\u0022,\u0022key\u0022:\u0022ownerCell\u0022,\u0022sortKey\u0022:\u0022owner\u0022,\u0022header\u0022:\u0022Owner\u0022,\u0022sortable\u0022:true},{\u0022id\u0022:\u0022lastActivity\u0022,\u0022type\u0022:\u0022html\u0022,\u0022key\u0022:\u0022activityCell\u0022,\u0022sortKey\u0022:\u0022activityTs\u0022,\u0022header\u0022:\u0022Last activity\u0022,\u0022align\u0022:\u0022end\u0022,\u0022sortable\u0022:true}]'), JSON.parse('{\u0022view\u0022:\u0022table\u0022,\u0022views\u0022:[\u0022table\u0022,\u0022cards\u0022],\u0022facets\u0022:[{\u0022id\u0022:\u0022stage\u0022,\u0022title\u0022:\u0022Stage\u0022,\u0022key\u0022:\u0022stage\u0022,\u0022options\u0022:[{\u0022value\u0022:\u0022lead\u0022,\u0022label\u0022:\u0022Lead\u0022},{\u0022value\u0022:\u0022prospect\u0022,\u0022label\u0022:\u0022Prospect\u0022},{\u0022value\u0022:\u0022customer\u0022,\u0022label\u0022:\u0022Customer\u0022},{\u0022value\u0022:\u0022churned\u0022,\u0022label\u0022:\u0022Churned\u0022}]},{\u0022id\u0022:\u0022tags\u0022,\u0022title\u0022:\u0022Tags\u0022,\u0022key\u0022:\u0022tags\u0022,\u0022options\u0022:[{\u0022value\u0022:\u0022VIP\u0022,\u0022label\u0022:\u0022VIP\u0022}]},{\u0022id\u0022:\u0022owner\u0022,\u0022title\u0022:\u0022Owner\u0022,\u0022key\u0022:\u0022owner\u0022,\u0022options\u0022:[{\u0022value\u0022:\u0022Omar Nasser\u0022,\u0022label\u0022:\u0022Omar Nasser\u0022}]}],\u0022locale\u0022:\u0022en\u0022,\u0022labels\u0022:{\u0022selectRow\u0022:\u0022Select {name}\u0022,\u0022rowActions\u0022:\u0022Actions for {name}\u0022,\u0022selected\u0022:\u0022{n} selected\u0022,\u0022results\u0022:\u0022{n} results\u0022,\u0022resultsOne\u0022:\u00221 result\u0022}}'))" x-modelable="rows"
    x-bind:data-view="view" class="flex min-w-0 flex-col gap-3">
    <div data-slot="data-table-toolbar" class="flex flex-wrap items-center gap-2">
                    <button type="button" role="checkbox" data-slot="checkbox" aria-label="Select all on this page" x-on:click="togglePage()" x-show="view === 'cards'" style="display: none"
                x-bind:aria-checked="pageSelection() === 'all' ? 'true' : (pageSelection() === 'some' ? 'mixed' : 'false')"
                x-bind:data-checked="pageSelection() === 'all' ? '' : null" x-bind:data-indeterminate="pageSelection() === 'some' ? '' : null" aria-checked="false" class="relative inline-flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-nq-line-strong bg-card text-primary-foreground outline-none transition-colors duration-150 ease-nq data-checked:border-primary data-checked:bg-primary data-indeterminate:border-primary data-indeterminate:bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus after:absolute after:-inset-1 align-middle me-1">
                <span data-slot="checkbox-indicator" class="flex items-center justify-center [&_svg]:size-3 [&_svg]:stroke-3" x-show="pageSelection() !== 'none'" style="display: none">
                    <x-lucide-minus aria-hidden="true" x-show="pageSelection() === 'some'" style="display: none" />
                    <x-lucide-check aria-hidden="true" x-show="pageSelection() === 'all'" style="display: none" />
                </span>
            </button>
                            <div data-slot="data-table-search" class="relative w-full sm:w-64">
                <svg aria-hidden="true" class="pointer-events-none absolute start-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m21 21-4.34-4.34"/>
  <circle cx="11" cy="11" r="8"/>
</svg>                <input data-slot="input" type="search"
                    x-model="query" x-on:keydown.escape="if (query) { $event.preventDefault(); query = &#039;&#039; }" placeholder="Search contacts…" aria-label="Search contacts…" class="w-full min-w-0 rounded-control border border-input bg-card px-3 text-foreground min-h-[var(--nq-touch-min,0px)] transition-colors duration-150 ease-nq outline-none placeholder:text-muted-foreground focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-invalid:border-nq-danger aria-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px] h-control-sm ps-8 pe-8 text-body-sm [&::-webkit-search-cancel-button]:hidden">
                <button type="button" x-show="query" aria-label="Clear search" x-on:click="query = ''" style="display: none"
                    class="absolute end-1.5 top-1/2 inline-flex size-5 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground outline-none hover:bg-nq-hover hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus">
                    <svg aria-hidden="true" class="size-3.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M18 6 6 18"/>
  <path d="m6 6 12 12"/>
</svg>                </button>
            </div>
                    <div data-slot="dropdown-menu" x-data="nqDropdownMenu(false)" x-modelable="open" class="contents"><button data-slot="dropdown-menu-trigger"
     type="button"                         x-bind="trigger" x-ref="trigger" x-bind:class="facetCount(&#039;stage&#039;) ? &#039;&#039; : &#039;border-dashed text-muted-foreground&#039;" data-facet="stage" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M2 5h20"/>
  <path d="M6 12h12"/>
  <path d="M9 19h6"/>
</svg>                    Stage
                    <span data-slot="badge" x-show="facetCount('stage')" x-text="facetSummary('stage')" style="display: none"
                        class="-me-1 inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border border-border px-1.5 text-caption font-medium tabular-nums text-muted-foreground"></span></button>
                <template x-teleport="body">
    <div data-slot="dropdown-menu-content" x-bind="popup" x-init="popupEl = $el" x-nq-presence="open" x-anchor.bottom-start.offset.4="$refs.trigger"
        class="z-50 overflow-hidden rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0 max-h-80 min-w-48 overflow-y-auto"><div data-slot="dropdown-menu-group" role="group" ><div data-slot="dropdown-menu-checkbox-item" role="menuitemcheckbox" x-data="{ checked: false }" x-modelable="checked" x-bind="item"
    x-on:click="if (! $el.hasAttribute('data-disabled')) checked = ! checked"
    x-bind:aria-checked="checked ? 'true' : 'false'" x-bind:data-checked="checked ? '' : undefined" x-bind:data-unchecked="checked ? undefined : ''"
     data-keep-open         x-model="facet[&#039;stage|lead&#039;]" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground ps-8">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="checked" class="contents"  style="display:none" ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    Lead
</div>
                                                    <div data-slot="dropdown-menu-checkbox-item" role="menuitemcheckbox" x-data="{ checked: false }" x-modelable="checked" x-bind="item"
    x-on:click="if (! $el.hasAttribute('data-disabled')) checked = ! checked"
    x-bind:aria-checked="checked ? 'true' : 'false'" x-bind:data-checked="checked ? '' : undefined" x-bind:data-unchecked="checked ? undefined : ''"
     data-keep-open         x-model="facet[&#039;stage|prospect&#039;]" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground ps-8">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="checked" class="contents"  style="display:none" ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    Prospect
</div>
                                                    <div data-slot="dropdown-menu-checkbox-item" role="menuitemcheckbox" x-data="{ checked: false }" x-modelable="checked" x-bind="item"
    x-on:click="if (! $el.hasAttribute('data-disabled')) checked = ! checked"
    x-bind:aria-checked="checked ? 'true' : 'false'" x-bind:data-checked="checked ? '' : undefined" x-bind:data-unchecked="checked ? undefined : ''"
     data-keep-open         x-model="facet[&#039;stage|customer&#039;]" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground ps-8">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="checked" class="contents"  style="display:none" ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    Customer
</div>
                                                    <div data-slot="dropdown-menu-checkbox-item" role="menuitemcheckbox" x-data="{ checked: false }" x-modelable="checked" x-bind="item"
    x-on:click="if (! $el.hasAttribute('data-disabled')) checked = ! checked"
    x-bind:aria-checked="checked ? 'true' : 'false'" x-bind:data-checked="checked ? '' : undefined" x-bind:data-unchecked="checked ? undefined : ''"
     data-keep-open         x-model="facet[&#039;stage|churned&#039;]" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground ps-8">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="checked" class="contents"  style="display:none" ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    Churned
</div></div>
                    <div x-show="facetCount('stage')" style="display: none">
                        <div data-slot="dropdown-menu-separator" role="separator" aria-orientation="horizontal" class="-mx-1.5 my-1.5 h-px bg-border"></div>
                        <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="resetFacet(&#039;stage&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground">Reset</div>
                    </div></div>
</template></div>
                    <div data-slot="dropdown-menu" x-data="nqDropdownMenu(false)" x-modelable="open" class="contents"><button data-slot="dropdown-menu-trigger"
     type="button"                         x-bind="trigger" x-ref="trigger" x-bind:class="facetCount(&#039;tags&#039;) ? &#039;&#039; : &#039;border-dashed text-muted-foreground&#039;" data-facet="tags" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M2 5h20"/>
  <path d="M6 12h12"/>
  <path d="M9 19h6"/>
</svg>                    Tags
                    <span data-slot="badge" x-show="facetCount('tags')" x-text="facetSummary('tags')" style="display: none"
                        class="-me-1 inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border border-border px-1.5 text-caption font-medium tabular-nums text-muted-foreground"></span></button>
                <template x-teleport="body">
    <div data-slot="dropdown-menu-content" x-bind="popup" x-init="popupEl = $el" x-nq-presence="open" x-anchor.bottom-start.offset.4="$refs.trigger"
        class="z-50 overflow-hidden rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0 max-h-80 min-w-48 overflow-y-auto"><div data-slot="dropdown-menu-group" role="group" ><div data-slot="dropdown-menu-checkbox-item" role="menuitemcheckbox" x-data="{ checked: false }" x-modelable="checked" x-bind="item"
    x-on:click="if (! $el.hasAttribute('data-disabled')) checked = ! checked"
    x-bind:aria-checked="checked ? 'true' : 'false'" x-bind:data-checked="checked ? '' : undefined" x-bind:data-unchecked="checked ? undefined : ''"
     data-keep-open         x-model="facet[&#039;tags|VIP&#039;]" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground ps-8">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="checked" class="contents"  style="display:none" ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    VIP
</div></div>
                    <div x-show="facetCount('tags')" style="display: none">
                        <div data-slot="dropdown-menu-separator" role="separator" aria-orientation="horizontal" class="-mx-1.5 my-1.5 h-px bg-border"></div>
                        <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="resetFacet(&#039;tags&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground">Reset</div>
                    </div></div>
</template></div>
                    <div data-slot="dropdown-menu" x-data="nqDropdownMenu(false)" x-modelable="open" class="contents"><button data-slot="dropdown-menu-trigger"
     type="button"                         x-bind="trigger" x-ref="trigger" x-bind:class="facetCount(&#039;owner&#039;) ? &#039;&#039; : &#039;border-dashed text-muted-foreground&#039;" data-facet="owner" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M2 5h20"/>
  <path d="M6 12h12"/>
  <path d="M9 19h6"/>
</svg>                    Owner
                    <span data-slot="badge" x-show="facetCount('owner')" x-text="facetSummary('owner')" style="display: none"
                        class="-me-1 inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border border-border px-1.5 text-caption font-medium tabular-nums text-muted-foreground"></span></button>
                <template x-teleport="body">
    <div data-slot="dropdown-menu-content" x-bind="popup" x-init="popupEl = $el" x-nq-presence="open" x-anchor.bottom-start.offset.4="$refs.trigger"
        class="z-50 overflow-hidden rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0 max-h-80 min-w-48 overflow-y-auto"><div data-slot="dropdown-menu-group" role="group" ><div data-slot="dropdown-menu-checkbox-item" role="menuitemcheckbox" x-data="{ checked: false }" x-modelable="checked" x-bind="item"
    x-on:click="if (! $el.hasAttribute('data-disabled')) checked = ! checked"
    x-bind:aria-checked="checked ? 'true' : 'false'" x-bind:data-checked="checked ? '' : undefined" x-bind:data-unchecked="checked ? undefined : ''"
     data-keep-open         x-model="facet[&#039;owner|Omar Nasser&#039;]" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground ps-8">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="checked" class="contents"  style="display:none" ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    Omar Nasser
</div></div>
                    <div x-show="facetCount('owner')" style="display: none">
                        <div data-slot="dropdown-menu-separator" role="separator" aria-orientation="horizontal" class="-mx-1.5 my-1.5 h-px bg-border"></div>
                        <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="resetFacet(&#039;owner&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground">Reset</div>
                    </div></div>
</template></div>
        <button data-slot="entity-list-clear"
     type="button"                         x-show="isFiltered()" x-on:click="clearAll()" style="display: none" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M18 6 6 18"/>
  <path d="m6 6 12 12"/>
</svg>            Clear filters</button>
        <div class="ms-auto flex flex-wrap items-center gap-2">
                                                    <div x-show="view === 'cards'" style="display: none">
                    <div data-slot="dropdown-menu" x-data="nqDropdownMenu(false)" x-modelable="open" class="contents"><button data-slot="entity-list-sort"
     type="button"                         x-bind="trigger" x-ref="trigger" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m3 16 4 4 4-4"/>
  <path d="M7 20V4"/>
  <path d="m21 8-4-4-4 4"/>
  <path d="M17 4v16"/>
</svg>                            Sort</button>
                        <template x-teleport="body">
    <div data-slot="dropdown-menu-content" x-bind="popup" x-init="popupEl = $el" x-nq-presence="open" x-anchor.bottom-end.offset.4="$refs.trigger"
        class="z-50 overflow-hidden rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none max-h-[var(--available-height)] overflow-y-auto transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0 min-w-44"><div data-slot="dropdown-menu-group" role="group" ><div data-slot="dropdown-menu-label" role="presentation" class="px-2.5 pt-1.5 pb-1 text-caption font-medium text-muted-foreground">Sort by</div>
                                                                                                            <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="toggleSort(&#039;name&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><span class="flex-1">Name</span>
                                            <span aria-hidden="true" class="text-muted-foreground" x-text="sort?.id === 'name' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span></div>
                                                                                                                                                <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="toggleSort(&#039;company&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><span class="flex-1">Company</span>
                                            <span aria-hidden="true" class="text-muted-foreground" x-text="sort?.id === 'company' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span></div>
                                                                                                                                                <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="toggleSort(&#039;stage&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><span class="flex-1">Stage</span>
                                            <span aria-hidden="true" class="text-muted-foreground" x-text="sort?.id === 'stage' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span></div>
                                                                                                                                                                                                                    <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="toggleSort(&#039;owner&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><span class="flex-1">Owner</span>
                                            <span aria-hidden="true" class="text-muted-foreground" x-text="sort?.id === 'owner' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span></div>
                                                                                                                                                <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="toggleSort(&#039;lastActivity&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><span class="flex-1">Last activity</span>
                                            <span aria-hidden="true" class="text-muted-foreground" x-text="sort?.id === 'lastActivity' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span></div></div></div>
</template></div>
                </div>
                                        <div role="group" data-slot="toggle-group" aria-label="Layout" class="flex w-fit max-w-full gap-0.5 rounded-control bg-secondary p-0.5">
                                            <button type="button" data-slot="toggle" aria-label="Table view" x-on:click="setView('table')" x-bind:aria-pressed="view === 'table' ? 'true' : 'false'"
                            x-bind:data-pressed="view === 'table' ? '' : null"
                            class="inline-flex h-control-sm min-w-control-sm items-center justify-center rounded-[calc(var(--radius-control)-2px)] px-2 text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus data-pressed:bg-card data-pressed:text-foreground data-pressed:shadow-sm [&_svg]:size-4">
                            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M3 5h.01"/>
  <path d="M3 12h.01"/>
  <path d="M3 19h.01"/>
  <path d="M8 5h13"/>
  <path d="M8 12h13"/>
  <path d="M8 19h13"/>
</svg>                        </button>
                                            <button type="button" data-slot="toggle" aria-label="Card view" x-on:click="setView('cards')" x-bind:aria-pressed="view === 'cards' ? 'true' : 'false'"
                            x-bind:data-pressed="view === 'cards' ? '' : null"
                            class="inline-flex h-control-sm min-w-control-sm items-center justify-center rounded-[calc(var(--radius-control)-2px)] px-2 text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus data-pressed:bg-card data-pressed:text-foreground data-pressed:shadow-sm [&_svg]:size-4">
                            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect width="7" height="7" x="3" y="3" rx="1"/>
  <rect width="7" height="7" x="14" y="3" rx="1"/>
  <rect width="7" height="7" x="14" y="14" rx="1"/>
  <rect width="7" height="7" x="3" y="14" rx="1"/>
</svg>                        </button>
                                    </div>
                    </div>
    </div>
            <div data-slot="data-table-bulk-actions" role="toolbar" x-bind:aria-label="say('selected').replace('{n}', selectedCount())" x-bind:hidden="selectedCount() === 0" hidden
            class="flex flex-wrap items-center gap-2 rounded-control bg-nq-selected py-1 ps-3 pe-1">
            <span aria-live="polite" class="me-auto text-label tabular-nums text-foreground" x-text="say('selected').replace('{n}', selectedCount())"></span>
                        <button type="button" aria-label="Clear selection" x-on:click="clearSelection()" class="inline-flex size-control-sm shrink-0 items-center justify-center rounded-control text-foreground outline-none hover:bg-nq-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:size-4"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M18 6 6 18"/>
  <path d="m6 6 12 12"/>
</svg></button>
        </div>
    <span role="status" aria-live="polite" class="sr-only" x-text="status()"></span>
        <div x-show="view === 'table'" style="display: none" data-slot="table-container" role="region" tabindex="0" aria-label="Contacts"
            class="relative w-full overflow-x-auto outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus">
            <div data-slot="table" role="table" aria-label="Contacts"  style="grid-template-columns: repeat(7, auto)" class="grid w-full min-w-max text-body-sm">
                <div data-slot="table-header" role="rowgroup" class="contents">
                    <div role="row" class="col-span-full grid grid-cols-subgrid border-b border-border">
                        <div role="columnheader" class="flex h-row w-10 items-center"><span class="sr-only">Select all on this page</span></div>                                                    <div role="columnheader" data-slot="table-head"   x-bind:aria-sort="sortState('name')"                                 class="flex h-row items-center px-4 text-caption font-medium whitespace-nowrap text-muted-foreground text-start">
                                                                    <button type="button" x-on:click="toggleSort('name')" class="inline-flex items-center gap-1.5 rounded-control outline-none hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus">
                                        Name
                                        <span aria-hidden="true" x-text="sort?.id === 'name' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span>
                                    </button>
                                                            </div>
                                                    <div role="columnheader" data-slot="table-head"   x-bind:aria-sort="sortState('company')"                                 class="flex h-row items-center px-4 text-caption font-medium whitespace-nowrap text-muted-foreground text-start">
                                                                    <button type="button" x-on:click="toggleSort('company')" class="inline-flex items-center gap-1.5 rounded-control outline-none hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus">
                                        Company
                                        <span aria-hidden="true" x-text="sort?.id === 'company' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span>
                                    </button>
                                                            </div>
                                                    <div role="columnheader" data-slot="table-head"   x-bind:aria-sort="sortState('stage')"                                 class="flex h-row items-center px-4 text-caption font-medium whitespace-nowrap text-muted-foreground text-start">
                                                                    <button type="button" x-on:click="toggleSort('stage')" class="inline-flex items-center gap-1.5 rounded-control outline-none hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus">
                                        Stage
                                        <span aria-hidden="true" x-text="sort?.id === 'stage' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span>
                                    </button>
                                                            </div>
                                                    <div role="columnheader" data-slot="table-head"                                  class="flex h-row items-center px-4 text-caption font-medium whitespace-nowrap text-muted-foreground text-start">
                                                                    Tags
                                                            </div>
                                                    <div role="columnheader" data-slot="table-head"   x-bind:aria-sort="sortState('owner')"                                 class="flex h-row items-center px-4 text-caption font-medium whitespace-nowrap text-muted-foreground text-start">
                                                                    <button type="button" x-on:click="toggleSort('owner')" class="inline-flex items-center gap-1.5 rounded-control outline-none hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus">
                                        Owner
                                        <span aria-hidden="true" x-text="sort?.id === 'owner' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span>
                                    </button>
                                                            </div>
                                                    <div role="columnheader" data-slot="table-head"   x-bind:aria-sort="sortState('lastActivity')"                                 class="flex h-row items-center px-4 text-caption font-medium whitespace-nowrap text-muted-foreground justify-end text-end">
                                                                    <button type="button" x-on:click="toggleSort('lastActivity')" class="inline-flex items-center gap-1.5 rounded-control outline-none hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus">
                                        Last activity
                                        <span aria-hidden="true" x-text="sort?.id === 'lastActivity' ? (sort.dir === 'asc' ? '↑' : '↓') : ''"></span>
                                    </button>
                                                            </div>
                                                                    </div>
                </div>
                <div data-slot="table-body" role="rowgroup" class="contents">
                    <template x-for="(row, index) in pageRows" :key="idOf(row)">
                                                    <div role="row" data-row x-bind:data-state="isSelected(row) ? 'selected' : null" x-on:click="openRow(row, $event)"
                                class="group/row col-span-full grid grid-cols-subgrid border-b border-border transition-colors hover:bg-nq-hover data-[state=selected]:bg-nq-selected">
                                    <div role="cell" data-slot="table-cell" class="flex h-row w-10 items-center pe-0">
        <button type="button" role="checkbox" data-slot="checkbox" x-bind:aria-label="say('selectRow', row)" x-on:click="toggleRow(row)" aria-checked="false"
            x-bind:aria-checked="isSelected(row) ? 'true' : 'false'" x-bind:data-checked="isSelected(row) ? '' : null" x-bind:data-unchecked="isSelected(row) ? null : ''" class="relative inline-flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-nq-line-strong bg-card text-primary-foreground outline-none transition-colors duration-150 ease-nq data-checked:border-primary data-checked:bg-primary data-indeterminate:border-primary data-indeterminate:bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus after:absolute after:-inset-1 align-middle">
            <span data-slot="checkbox-indicator" class="flex items-center justify-center [&_svg]:size-3 [&_svg]:stroke-3" x-show="isSelected(row)" style="display: none"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
        </button>
    </div>
        <div role="cell" data-slot="table-cell" data-cell-col="name"  class="flex h-row items-center px-4 py-3 ">
        <span class="flex min-w-0 items-center gap-1.5 ">
                            <span class="min-w-0" x-html="row['nameCell'] ?? ''"></span>
                    </span>
    </div>
        <div role="cell" data-slot="table-cell" data-cell-col="company"  class="flex h-row items-center px-4 py-3 ">
        <span class="flex min-w-0 items-center gap-1.5 ">
                            <span class="min-w-0" x-html="row['companyCell'] ?? ''"></span>
                    </span>
    </div>
        <div role="cell" data-slot="table-cell" data-cell-col="stage"  class="flex h-row items-center px-4 py-3 ">
        <span class="flex min-w-0 items-center gap-1.5 ">
                            <span class="min-w-0" x-html="row['stageCell'] ?? ''"></span>
                    </span>
    </div>
        <div role="cell" data-slot="table-cell" data-cell-col="tags"  class="flex h-row items-center px-4 py-3 ">
        <span class="flex min-w-0 items-center gap-1.5 ">
                            <span class="min-w-0" x-html="row['tagsCell'] ?? ''"></span>
                    </span>
    </div>
        <div role="cell" data-slot="table-cell" data-cell-col="owner"  class="flex h-row items-center px-4 py-3 ">
        <span class="flex min-w-0 items-center gap-1.5 ">
                            <span class="min-w-0" x-html="row['ownerCell'] ?? ''"></span>
                    </span>
    </div>
        <div role="cell" data-slot="table-cell" data-cell-col="lastActivity"  class="flex h-row items-center px-4 py-3 justify-end text-end">
        <span class="flex min-w-0 items-center gap-1.5 justify-end">
                            <span class="min-w-0" x-html="row['activityCell'] ?? ''"></span>
                    </span>
    </div>
                            </div>
                                            </template>
                </div>
            </div>
        </div>
        <div x-show="view === 'cards'" style="display: none" role="list" aria-label="Contacts" class="grid gap-3"
            style="grid-template-columns: repeat(auto-fill, minmax(min(100%, 272px), 1fr)); --entity-card-controls: 1.75rem">
            <template x-for="(row, index) in pageRows" :key="idOf(row)">
                                    <div role="listitem" data-card x-bind:tabindex="index === Math.min(active, pageRows.length - 1) ? 0 : -1" x-bind:data-state="isSelected(row) ? 'selected' : null"
                        x-on:keydown="cardKey($event, row, index)" x-on:click="openRow(row, $event)" x-on:focus="$event.target === $event.currentTarget ? (active = index) : null" class="group/card relative flex min-w-0 flex-col rounded-card border border-border bg-card p-4 text-card-foreground outline-none transition-colors duration-150 ease-nq hover:bg-nq-hover focus-visible:outline-2 focus-visible:outline-nq-focus data-[state=selected]:border-primary data-[state=selected]:bg-nq-selected">
                        <div class="min-w-0 flex-1"><div x-html="row.card"></div></div>
    <div class="absolute end-2 top-2 flex items-center gap-1">
                    <button type="button" role="checkbox" data-slot="checkbox" x-bind:aria-label="say('selectRow', row)" x-on:click="toggleRow(row)" aria-checked="false"
                x-bind:aria-checked="isSelected(row) ? 'true' : 'false'" x-bind:data-checked="isSelected(row) ? '' : null" x-bind:data-unchecked="isSelected(row) ? null : ''"
                x-bind:tabindex="index === Math.min(active, pageRows.length - 1) ? 0 : -1" class="relative inline-flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-nq-line-strong bg-card text-primary-foreground outline-none transition-colors duration-150 ease-nq data-checked:border-primary data-checked:bg-primary data-indeterminate:border-primary data-indeterminate:bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus after:absolute after:-inset-1 align-middle">
                <span data-slot="checkbox-indicator" class="flex items-center justify-center [&_svg]:size-3 [&_svg]:stroke-3" x-show="isSelected(row)" style="display: none"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
            </button>
                    </div>
                    </div>
                            </template>
        </div>
        <div x-show="! hasRows() && noMatches()" style="display: none">
            <div data-slot="empty-state" class="flex flex-col items-center justify-center gap-3 border-dashed border-border px-6 py-12 text-center rounded-card border-0">
            <span data-slot="state-icon" class="inline-flex size-10 items-center justify-center rounded-control border border-border bg-card [&amp;_svg]:size-5 text-muted-foreground">
            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m21 21-4.34-4.34"/>
  <circle cx="11" cy="11" r="8"/>
</svg>        </span>
        <div class="flex max-w-sm flex-col gap-1">
        <p class="text-label text-foreground">No matching results</p>
                    <p class="text-body-sm text-muted-foreground">Try a different search or clear the filters.</p>
            </div>
            <div class="mt-1 flex flex-wrap items-center justify-center gap-2"><button data-slot="button"
     type="button"                         x-on:click="clearAll()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5">
        Clear filters</button></div>
    </div>
        </div>
        <div x-show="rows.length === 0" style="display: none">
            <div data-slot="empty-state" class="flex flex-col items-center justify-center gap-3 border border-dashed border-border px-6 py-12 text-center rounded-card">
            <span data-slot="state-icon" class="inline-flex size-10 items-center justify-center rounded-control border border-border bg-card [&amp;_svg]:size-5 text-muted-foreground">
            <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
  <path d="M16 3.128a4 4 0 0 1 0 7.744"/>
  <path d="M22 21v-2a4 4 0 0 0-3-3.87"/>
  <circle cx="9" cy="7" r="4"/>
</svg>        </span>
        <div class="flex max-w-sm flex-col gap-1">
        <p class="text-label text-foreground">No contacts yet</p>
                    <p class="text-body-sm text-muted-foreground">Add a contact or import a list to get started.</p>
            </div>
    </div>        </div>
    </div>
</div>
```
