# ProductSwitcher

> App launcher grid that shows each installed product's official mark, plus a sidebar products group and "Switch to…" palette commands.

Source: https://docs.nasaqui.com/components/product-switcher

## Install

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

Moves the user between the products they have installed (Mahaam, Zekra, Nasaq, CircleXO…). It has
three pieces that share one `Product[]` list:

- **`ProductSwitcher`**: a grid icon button that opens a launcher popover (like Google's apps grid).
- **`SidebarProducts`**: the pinned products as a sidebar group.
- **`useProductCommands`**: registers "Switch to <product>" in the CommandPalette. `ProductSwitcher`
  already calls it.

Nasaq does not fetch the list. The host passes it in, whether that is a static array, CircleXO's
installed-apps API or a feature flag. Each product appears with its **official mark, never recoloured**.
Its brand accent appears only as the small "current" marker.

## When to use

- The user has access to more than one product and needs to jump between them.
- You want pinned products in the sidebar (this replaces the old "Favourites" group).

## When not to use

- Switching organisations or tenants inside one product: use [`WorkspaceSwitcher`](https://docs.nasaqui.com/components/workspace-switcher).
- Navigating pages inside one product: use `SidebarItem`s in [`AppShell`](https://docs.nasaqui.com/components/app-shell).
- Showing a single brand logo: use [`ProductMark` / `ProductLogo`](https://docs.nasaqui.com/components/product-mark).

## Import

```tsx
import { ProductSwitcher, SidebarProducts, type Product } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { AppHeader, ProductSwitcher, type Product } from "@fadymondy/nasaq/web";

const products: Product[] = [
  { id: "mahaam", brand: "mahaam", name: "Mahaam", href: "https://mahaam.app", pinned: true },
  { id: "zekra", brand: "zekra", name: "Zekra", href: "https://zekra.dev", pinned: true },
  { id: "nasaq", brand: "nasaq", name: "Nasaq", href: "https://nasaqui.com" },
];

export function Header() {
  return (
    <AppHeader>
      <div className="ms-auto">
        <ProductSwitcher products={products} current="mahaam" allHref="/apps" />
      </div>
    </AppHeader>
  );
}
```

Products with `href` render as links. Without `href`, handle `onSelect`.

## Anatomy

```
ProductSwitcher
├─ trigger                    data-slot="product-switcher-trigger"  (grid icon, or your `children`)
└─ popover                    data-slot="product-switcher"
   ├─ title                   (labels.heading, default "Apps" / "التطبيقات")
   ├─ grid (role="group")
   │  └─ tile × n             data-slot="product-tile", aria-current="page" on the current one
   │     ├─ mark tile         ProductIcon (official mark) + optional badge
   │     ├─ name
   │     └─ accent marker     2px bar in the product's accent, current tile only
   └─ "All apps" link         only with allHref or onViewAll

SidebarProducts
└─ SidebarGroup (collapsible)
   └─ SidebarItem × n         ProductIcon 16px, badge as trailing, active = current
```

## API

### `Product`

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | Stable id. Compared with `current`. |
| `name` | `string` | Display name, already localised by the host. |
| `brand?` | `string` | A Nasaq brand key (`mahaam`, `zekra`, `nasaq`, `moharrik`, `seatfor`, `health-debug`, `circlexo`, `hosbah`, `orchestra`, `fadymondy`). Draws the official mark and supplies the accent. |
| `logo?` | `ReactNode` | For products without a Nasaq brand: the product's own official logo (e.g. an `<img>` of the supplied file). Never a generic icon. |
| `accent?` | `string` | Accent colour when `brand` is not a Nasaq brand. Used only for the current marker. |
| `description?` | `string` | Shown as the tile's tooltip (`title`). |
| `href?` | `string` | Renders the tile and sidebar item as a link. |
| `pinned?` | `boolean` | Shown in `SidebarProducts`; ranked first in the palette. |
| `badge?` | `ReactNode` | Small count, e.g. unread items. |
| `keywords?` | `string[]` | Extra palette search terms. |

With no `brand` and no `logo`, the product's initial is shown. Nasaq never substitutes a pictogram.

### `ProductSwitcher`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `products` | `Product[]` | required | Products to show, in order. |
| `current?` | `string` | none | The id of the product the user is in. |
| `onSelect?` | `(product: Product) => void` | none | Called on click and from the palette. |
| `allHref?` | `string` | none | Adds an "All apps" footer link (e.g. an app store). |
| `onViewAll?` | `() => void` | none | "All apps" as a button instead of a link. |
| `registerCommands?` | `boolean` | `true` | Registers "Switch to…" commands. Set `false` on a second instance so commands are not duplicated. |
| `labels?` | `{ trigger?; heading?; all? }` | EN/AR built in | Trigger aria-label and tooltip, popover title, footer link. |
| `children?` | `ReactNode` | `<LayoutGrid />` | Custom trigger content. |
| `className?` | `string` | none | Classes for the trigger. |

### `SidebarProducts`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `products` | `Product[]` | required | Same list as the switcher. |
| `current?` | `string` | none | Marks the active item. |
| `onSelect?` | `(product: Product) => void` | none | Called on click. |
| `label?` | `string` | "Apps" / "التطبيقات" | Group heading. |
| `action?` | `ReactNode` | none | Group header action, e.g. a `ProductSwitcher`. |
| `pinnedOnly?` | `boolean` | `true` | Only pinned products; shows all if none are pinned. |
| `order?` | `string[]` | none | The user's products, in their order, by id (e.g. `useSidebarLayout(…).visible`). Overrides `pinned`. |
| `onMove?` | `(activeId, overId) => void` | none | Makes the items draggable like the rest of the sidebar (`layout.move`). |

It renders nothing when there are no products to show.

### `ProductIcon`

`({ product: Product; size?: number = 20 }) => JSX.Element`: the official mark (via `ProductMark`),
the host's `logo`, or the product's initial.

### `useProductCommands`

```ts
useProductCommands(products: Product[], onSelect: (p: Product) => void, current?: string): void
```

Registers one command per product (except `current`) in the `products` section of the command registry.
Use it when you render `SidebarProducts` without a `ProductSwitcher`. It needs a
`CommandProvider`, which `AppShell` already includes.

## Examples

### Sidebar group with the full grid as its action

```tsx
<SidebarProducts
  products={products}
  current="mahaam"
  onSelect={open}
  action={<ProductSwitcher products={products} current="mahaam" onSelect={open} registerCommands={false} className="size-6" />}
/>
```

### User-pinned, reorderable, and editable in Customize sidebar

This is how the AppShell dashboard demo replaces the old Favourites group. Every product is listed in
`SidebarCustomize`, the unpinned ones start switched off, and the user drags or toggles them.

```tsx
const layout = useSidebarLayout("app-nav-products", products.map((p) => p.id), {
  defaultHidden: products.filter((p) => !p.pinned).map((p) => p.id),
});

<SidebarProducts products={products} current="mahaam" order={layout.visible} onMove={layout.move} action={…} />
// and in SidebarCustomize sections:
{ id: "apps", label: "Apps", items: products.map((p) => ({ id: p.id, label: p.name, icon: <ProductIcon product={p} size={16} /> })), layout }
```

Put a `ProductSwitcher` in the header as well (next to notifications), so the full grid is reachable
while the sidebar is collapsed or closed on mobile. Only one of the two should register commands.

### Data from CircleXO (or any source)

```tsx
const { data: installed = [] } = useInstalledApps(); // host code
const products = installed.map((app) => ({
  id: app.slug,
  name: locale === "ar" ? app.nameAr : app.name,
  brand: app.nasaqBrand,                  // when the app is a Nasaq brand
  logo: app.nasaqBrand ? undefined : <img src={app.logoUrl} alt="" />,
  accent: app.accent,
  href: app.url,
  pinned: app.pinned,
  badge: app.unread || undefined,
}));
```

### Handling selection without links

```tsx
<ProductSwitcher products={products} current={currentId} onSelect={(p) => router.push(`/apps/${p.id}`)} />
```

## Accessibility

| Key | Action |
| --- | --- |
| `Enter` / `Space` on trigger | Opens the launcher; focus moves into the grid. |
| `←` `→` | Previous/next tile (reversed in RTL, so it always follows reading order visually). |
| `↑` `↓` | Up/down one row (3 columns). |
| `Home` / `End` | First / last tile. |
| `Enter` | Opens the product and closes the popover. |
| `Esc` | Closes and returns focus to the trigger. |

- The trigger has an `aria-label` and a tooltip (`labels.trigger`).
- The current tile has `aria-current="page"`. Its accent marker is decorative, so current state is
  never shown by colour alone.
- Marks render with `title=""` (decorative) because the name is visible beside them.

## RTL & i18n

- The grid follows `dir`; arrow keys are RTL-aware. The "All apps" arrow mirrors.
- Marks are **never mirrored** (SVG geometry ignores `dir`).
- Built-in strings: "Apps"/"التطبيقات", "All apps"/"كل التطبيقات", palette hint "App"/"تطبيق".
- Product names are the host's responsibility. Pass them localised.

## Styling & tokens

- Surfaces: `bg-popover`, `shadow-floating`, `rounded-floating`; tiles `rounded-control`.
- States: hover `bg-nq-hover`, current `bg-nq-selected` + `--product-accent` bar, open trigger
  `data-popup-open`.
- The badge uses `bg-nq-danger-solid` / `text-nq-on-danger`.
- Target parts with `[data-slot=product-tile]`, `[data-slot=product-switcher]`.

## Do / Don't

- **Do** pass `brand` for Nasaq products so the official mark and accent are used.
- **Do** keep the list short and relevant; pin the 3–5 the user opens most.
- **Don't** recolour, mirror, redraw or replace a product's logo, or tint it with the accent.
- **Don't** put business logic in Nasaq: selection is the host's `onSelect` / `href`.
- **Don't** render two switchers with `registerCommands` both on.

## Related

- [CommandPalette](https://docs.nasaqui.com/components/command-palette) · [commands registry](https://docs.nasaqui.com/components/commands)
- [WorkspaceSwitcher](https://docs.nasaqui.com/components/workspace-switcher) · [AppShell](https://docs.nasaqui.com/components/app-shell)
- [ProductMark](https://docs.nasaqui.com/components/product-mark)

## Lab

https://docs.nasaqui.com/?path=/docs/components-navigation-product-switcher--docs

## Code

### React

```tsx
import { AppHeader, ProductSwitcher, type Product } from "@fadymondy/nasaq/web";

const products: Product[] = [
  { id: "mahaam", brand: "mahaam", name: "Mahaam", href: "https://mahaam.app", pinned: true },
  { id: "zekra", brand: "zekra", name: "Zekra", href: "https://zekra.dev", pinned: true },
  { id: "nasaq", brand: "nasaq", name: "Nasaq", href: "https://nasaqui.com" },
];

export function Header() {
  return (
    <AppHeader>
      <div className="ms-auto">
        <ProductSwitcher products={products} current="mahaam" allHref="/apps" />
      </div>
    </AppHeader>
  );
}
```

### shadcn

```tsx
import { AppHeader } from "@/components/ui/app-shell";
import { ProductSwitcher, type Product } from "@/components/ui/product-switcher";

const products: Product[] = [
  { id: "mahaam", brand: "mahaam", name: "Mahaam", href: "https://mahaam.app", pinned: true },
  { id: "zekra", brand: "zekra", name: "Zekra", href: "https://zekra.dev", pinned: true },
  { id: "nasaq", brand: "nasaq", name: "Nasaq", href: "https://nasaqui.com" },
];

export function Header() {
  return (
    <AppHeader>
      <div className="ms-auto">
        <ProductSwitcher products={products} current="mahaam" allHref="/apps" />
      </div>
    </AppHeader>
  );
}
```

### Vue

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

const products: Product[] = [
  { id: "mahaam", brand: "mahaam", name: "Mahaam", href: "https://mahaam.app", pinned: true },
  { id: "zekra", brand: "zekra", name: "Zekra", href: "https://zekra.dev", pinned: true },
  { id: "nasaq", brand: "nasaq", name: "Nasaq", href: "https://nasaq.fadymondy.com" },
];
</script>

<template>
  <NqAppHeader>
    <div class="ms-auto">
      <NqProductSwitcher :products="products" current="mahaam" all-href="/apps" />
    </div>
  </NqAppHeader>
</template>
```

### Blade

```blade
<div x-data="{ rail: false, collapsed: false }" class="flex w-64 flex-col gap-4">
    <div class="flex justify-end">
        <x-nq::product-switcher current="mahaam" all-href="/apps" :products="[
            ['id' => 'mahaam', 'brand' => 'mahaam', 'name' => 'Mahaam', 'href' => 'https://mahaam.app', 'pinned' => true, 'badge' => 3],
            ['id' => 'zekra', 'brand' => 'zekra', 'name' => 'Zekra', 'pinned' => true],
            ['id' => 'custom', 'name' => 'Custom app', 'description' => 'A host app'],
        ]" />
    </div>
    <x-nq::product-switcher.sidebar-products current="mahaam" :products="[
        ['id' => 'mahaam', 'brand' => 'mahaam', 'name' => 'Mahaam', 'href' => 'https://mahaam.app', 'pinned' => true, 'badge' => 3],
        ['id' => 'zekra', 'brand' => 'zekra', 'name' => 'Zekra', 'pinned' => true],
        ['id' => 'custom', 'name' => 'Custom app'],
    ]" />
</div>
```

### HTML + Alpine

```html
<div x-data="{ rail: false, collapsed: false }" class="flex w-64 flex-col gap-4">
    <div class="flex justify-end">
        <div data-slot="popover" x-data="nqPopover(false)" x-modelable="open" x-id="['nq-popover']" class="contents">
    <button type="button" data-slot="product-switcher-trigger" x-ref="trigger" aria-haspopup="dialog" x-on:click="toggle()" x-bind:aria-expanded="open" x-bind:data-popup-open="open ? '' : null"
        aria-label="Apps" title="Apps"
        class="inline-flex size-control-sm items-center justify-center rounded-control text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:bg-nq-hover hover:text-foreground data-popup-open:bg-nq-selected data-popup-open:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus [&_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>
    <template x-teleport="body">
    <div data-slot="product-switcher" x-bind="popup" x-init="popupEl = $el" x-nq-presence="open" x-anchor.bottom-end.offset.6="$refs.trigger"
        class="z-50 max-w-[var(--available-width)] rounded-floating border border-border bg-popover text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0 w-[19rem] p-1.5 text-body"><div class="px-2 pt-1.5 pb-2 text-caption font-medium text-muted-foreground">Apps</div>
        <div role="group" x-data="nqProductGrid" x-on:keydown="onKey($event)" class="grid grid-cols-3 gap-1">
                                            <a data-slot="product-tile" data-id="mahaam"
                     href="https://mahaam.app"                      aria-current="page"                                         style="--product-accent: #C9A227" x-on:click="$refs.trigger.dispatchEvent(new CustomEvent(`nq-select`, { bubbles: true, detail: { id: $el.dataset.id } })); close()"
                    class="group relative flex flex-col items-center gap-1.5 rounded-control px-1 pt-3 pb-2 text-center outline-none transition-colors duration-150 ease-nq hover:bg-nq-hover focus-visible:bg-nq-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus aria-[current]:bg-nq-selected">
                    <span class="relative inline-flex size-10 items-center justify-center rounded-control border border-border bg-card">
                        <svg data-slot="product-mark" viewBox="0 0 100 100" width="24" height="24"  aria-hidden="true"  shape-rendering="crispEdges"  class="shrink-0">
                        <rect x="25" y="25" width="25" height="25" fill="#8C3FB5" />
                    <rect x="75" y="25" width="25" height="25" fill="#C9A227" />
                    <rect x="0" y="50" width="25" height="25" fill="#8C3FB5" />
                    <rect x="50" y="50" width="25" height="25" fill="#8C3FB5" />
            </svg>
                                                    <span class="absolute -end-1.5 -top-1.5 min-w-4 rounded-full bg-nq-danger-solid px-1 text-center text-[10px] leading-4 font-medium text-nq-on-danger tabular-nums">3</span>
                                            </span>
                    <span class="w-full truncate text-caption text-foreground">Mahaam</span>
                    <span aria-hidden="true" class="absolute inset-x-5 bottom-0.5 h-0.5 rounded-full bg-(--product-accent) opacity-0 group-aria-[current]:opacity-100"></span>
                </a>
                                            <button data-slot="product-tile" data-id="zekra"
                     type="button"                                                             style="--product-accent: #C9A227" x-on:click="$refs.trigger.dispatchEvent(new CustomEvent(`nq-select`, { bubbles: true, detail: { id: $el.dataset.id } })); close()"
                    class="group relative flex flex-col items-center gap-1.5 rounded-control px-1 pt-3 pb-2 text-center outline-none transition-colors duration-150 ease-nq hover:bg-nq-hover focus-visible:bg-nq-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus aria-[current]:bg-nq-selected">
                    <span class="relative inline-flex size-10 items-center justify-center rounded-control border border-border bg-card">
                        <svg data-slot="product-mark" viewBox="0 0 100 100" width="24" height="24"  aria-hidden="true"  shape-rendering="crispEdges"  class="shrink-0">
                        <rect x="40" y="10" width="20" height="20" fill="#C9A227" />
                    <rect x="20" y="30" width="20" height="20" fill="#6D4DE6" />
                    <rect x="60" y="30" width="20" height="20" fill="#6D4DE6" />
                    <rect x="0" y="50" width="20" height="20" fill="#6D4DE6" />
                    <rect x="40" y="50" width="20" height="20" fill="#6D4DE6" />
                    <rect x="80" y="50" width="20" height="20" fill="#6D4DE6" />
                    <rect x="20" y="70" width="20" height="20" fill="#6D4DE6" />
                    <rect x="60" y="70" width="20" height="20" fill="#6D4DE6" />
            </svg>
                                            </span>
                    <span class="w-full truncate text-caption text-foreground">Zekra</span>
                    <span aria-hidden="true" class="absolute inset-x-5 bottom-0.5 h-0.5 rounded-full bg-(--product-accent) opacity-0 group-aria-[current]:opacity-100"></span>
                </button>
                                            <button data-slot="product-tile" data-id="custom"
                     type="button"                                          title="A host app"                     style="--product-accent: var(--nq-accent)" x-on:click="$refs.trigger.dispatchEvent(new CustomEvent(`nq-select`, { bubbles: true, detail: { id: $el.dataset.id } })); close()"
                    class="group relative flex flex-col items-center gap-1.5 rounded-control px-1 pt-3 pb-2 text-center outline-none transition-colors duration-150 ease-nq hover:bg-nq-hover focus-visible:bg-nq-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus aria-[current]:bg-nq-selected">
                    <span class="relative inline-flex size-10 items-center justify-center rounded-control border border-border bg-card">
                        <span aria-hidden="true" class="inline-flex shrink-0 items-center justify-center rounded-[4px] bg-secondary font-medium text-muted-foreground" style="width: 24px; height: 24px; font-size: 11px">C</span>
                                            </span>
                    <span class="w-full truncate text-caption text-foreground">Custom app</span>
                    <span aria-hidden="true" class="absolute inset-x-5 bottom-0.5 h-0.5 rounded-full bg-(--product-accent) opacity-0 group-aria-[current]:opacity-100"></span>
                </button>
                    </div>
                    <a  href="/apps"  data-slot="product-all" x-on:click="$refs.trigger.dispatchEvent(new CustomEvent(`nq-view-all`, { bubbles: true })); close()"
                class="mt-1.5 flex h-9 w-full items-center justify-center gap-1.5 rounded-control border-t border-border text-body-sm text-muted-foreground outline-none hover:bg-nq-hover hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus">
                All apps
                <svg aria-hidden="true" class="size-3.5 rtl:-scale-x-100" 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="M7 7h10v10"/>
  <path d="M7 17 17 7"/>
</svg>            </a></div>
</template>
</div>
    </div>
    <div data-slot="sidebar-group" role="group"  x-bind:aria-label="(rail && collapsed) ? 'Apps' : null"  >
    <div data-slot="collapsible" x-data="nqCollapsible(true)" x-modelable="open" x-id="['nq-collapsible']"
    :data-open="open ? '' : undefined" :data-closed="open ? undefined : ''"
     data-open     class="group/group flex flex-col">
    <div class="group/label flex h-[calc(var(--spacing-nav-row)-4px)] items-center gap-1 ps-2 pe-1 group-data-collapsed/sidebar:hidden">
                <button type="button" x-on:click="toggle()" x-bind:aria-expanded="open"
                    class="flex min-w-0 flex-1 items-center gap-1 rounded-[3px] text-start text-caption font-medium text-muted-foreground outline-none hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus [&_svg]:size-3">
                    <span class="truncate">Apps</span>
                    <svg aria-hidden="true" class="opacity-0 transition-[rotate,opacity] duration-200 ease-nq group-hover/label:opacity-100 group-focus-within/label:opacity-100 group-data-open/group:rotate-90 rtl:-scale-x-100 rtl:group-data-open/group:-rotate-90" 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="m9 18 6-6-6-6"/>
</svg>                </button>
                            </div>
                <div data-slot="collapsible-panel" x-ref="panel" x-nq-presence="open" :id="$id('nq-collapsible', 'panel')"
     data-open     class="h-(--collapsible-panel-height) overflow-hidden transition-[height,opacity] duration-200 ease-nq motion-reduce:transition-none data-starting-style:h-0 data-starting-style:opacity-0 data-ending-style:h-0 data-ending-style:opacity-0 group-data-collapsed/sidebar:block! group-data-collapsed/sidebar:h-auto! group-data-collapsed/sidebar:opacity-100!"><div class="flex flex-col gap-0.5"><div data-slot="tooltip" x-data="nqTooltip(600, false)" x-modelable="open" x-id="['nq-tooltip']" class="contents">
    <span data-slot="tooltip-trigger" class="contents"><a data-slot="sidebar-item"  aria-current="page"          x-bind:aria-label="(rail && collapsed) ? 'Mahaam' : null"         href="https://mahaam.app" data-id="mahaam" data-href="https://mahaam.app" x-on:click="if (! $el.dataset.href) $event.preventDefault(); $dispatch(`nq-select`, { id: $el.dataset.id })" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] w-full items-center gap-2 rounded-control px-2 text-start text-body-sm transition-colors duration-150 ease-nq outline-none hover:bg-nq-hover hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus [&_svg]:size-4 [&_svg]:shrink-0 group-data-collapsed/sidebar:size-control group-data-collapsed/sidebar:justify-center group-data-collapsed/sidebar:px-0 bg-nq-selected font-medium text-foreground">
        <span data-slot="sidebar-icon" class="contents"><svg data-slot="product-mark" viewBox="0 0 100 100" width="16" height="16"  aria-hidden="true"  shape-rendering="crispEdges"  class="shrink-0">
                        <rect x="25" y="25" width="25" height="25" fill="#8C3FB5" />
                    <rect x="75" y="25" width="25" height="25" fill="#8C3FB5" />
                    <rect x="0" y="50" width="25" height="25" fill="#8C3FB5" />
                    <rect x="50" y="50" width="25" height="25" fill="#8C3FB5" />
            </svg></span>        <span class="min-w-0 flex-1 truncate group-data-collapsed/sidebar:hidden">Mahaam</span>
        <span class="ms-auto text-caption text-muted-foreground tabular-nums group-data-collapsed/sidebar:hidden">3</span>    </a></span>
    <template x-teleport="body">
        <div data-slot="tooltip-content" x-bind="popup" x-nq-presence="open" x-anchor.right.offset.6="triggerEl"
            x-bind:class="rail &amp;&amp; collapsed ? &#039;&#039; : &#039;hidden&#039;" class="z-50 max-w-64 rounded-control bg-foreground px-2 py-1 text-caption text-background transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">Mahaam</div>
    </template>
</div>
                                            <div data-slot="tooltip" x-data="nqTooltip(600, false)" x-modelable="open" x-id="['nq-tooltip']" class="contents">
    <span data-slot="tooltip-trigger" class="contents"><a data-slot="sidebar-item"          x-bind:aria-label="(rail && collapsed) ? 'Zekra' : null"         href="#" data-id="zekra" data-href="" x-on:click="if (! $el.dataset.href) $event.preventDefault(); $dispatch(`nq-select`, { id: $el.dataset.id })" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] w-full items-center gap-2 rounded-control px-2 text-start text-body-sm text-sidebar-foreground transition-colors duration-150 ease-nq outline-none hover:bg-nq-hover hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus [&_svg]:size-4 [&_svg]:shrink-0 group-data-collapsed/sidebar:size-control group-data-collapsed/sidebar:justify-center group-data-collapsed/sidebar:px-0">
        <span data-slot="sidebar-icon" class="contents"><svg data-slot="product-mark" viewBox="0 0 100 100" width="16" height="16"  aria-hidden="true"  shape-rendering="crispEdges"  class="shrink-0">
                        <rect x="40" y="10" width="20" height="20" fill="#6D4DE6" />
                    <rect x="20" y="30" width="20" height="20" fill="#6D4DE6" />
                    <rect x="60" y="30" width="20" height="20" fill="#6D4DE6" />
                    <rect x="0" y="50" width="20" height="20" fill="#6D4DE6" />
                    <rect x="40" y="50" width="20" height="20" fill="#6D4DE6" />
                    <rect x="80" y="50" width="20" height="20" fill="#6D4DE6" />
                    <rect x="20" y="70" width="20" height="20" fill="#6D4DE6" />
                    <rect x="60" y="70" width="20" height="20" fill="#6D4DE6" />
            </svg></span>        <span class="min-w-0 flex-1 truncate group-data-collapsed/sidebar:hidden">Zekra</span>
            </a></span>
    <template x-teleport="body">
        <div data-slot="tooltip-content" x-bind="popup" x-nq-presence="open" x-anchor.right.offset.6="triggerEl"
            x-bind:class="rail &amp;&amp; collapsed ? &#039;&#039; : &#039;hidden&#039;" class="z-50 max-w-64 rounded-control bg-foreground px-2 py-1 text-caption text-background transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">Zekra</div>
    </template>
</div></div></div>
</div>
    </div>
</div>
```
