# Notes Drawer — How to Add One to Any Page

A right-side slide-out drawer triggered by a fixed vertical "NOTES" tab on the right edge of the viewport. Used across all Cydekick connector/admin pages to surface internal operational notes without cluttering the UI.

**Live example:** `plugins/webkul/allmakes-psp/resources/views/filament/pages/allmakes-product-data.blade.php`

---

## What It Looks Like

- A purple vertical pill button fixed to the right edge, vertically centred, that reads **NOTES** (text rotated 90°)
- Clicking it slides a 660px white drawer in from the right, starting below Filament's topnav bar
- A dark semi-transparent backdrop covers the rest of the page
- The drawer has a header (title + × button), a scrollable body, and a footer (close button)
- Works in both light and dark mode

---

## Critical Rules — Read Before Copying

### 1. `x-teleport="body"` requires a single root element
The `<template x-teleport="body">` block **must have exactly one root element** inside it. Wrap the backdrop + drawer in a single outer `<div>`.

### 2. Alpine `:style` — always use object form for merging
`:style` as a **string** (e.g. `'opacity:1'`) replaces the entire `style` attribute, wiping all static styles.  
`:style` as an **object** (e.g. `{ opacity: open ? 1 : 0 }`) merges with the static `style` attribute.  
Always use the object form.

```html
{{-- WRONG — string replaces all static styles --}}
<div :style="'opacity:' + (open ? '1' : '0')" style="position:fixed;..."></div>

{{-- CORRECT — object merges with static styles --}}
<div :style="{ opacity: open ? 1 : 0 }" style="position:fixed;..."></div>
```

### 3. Drawer is full viewport height — it overlays Filament's topnav
The drawer uses `top:0;height:100vh`. Its z-index (9998) is higher than Filament's topnav (~z-30), so the drawer panel sits on top of the entire viewport. Do NOT use `top:57px` — that creates a gap at the top where the Filament topnav shows through.

### 4. Scroll body needs TWO nested divs
An `overflow-y:auto` container with `display:flex;flex-direction:column` causes flex-shrink to compress cards to fit — no overflow, no scrollbar. Fix: **outer div = scroll container only** (fixed height, overflow-y:auto), **inner div = flex column** (no height, grows to content size).

```html
{{-- WRONG — flex container shrinks its own children to fit --}}
<div style="height:...;overflow-y:auto;display:flex;flex-direction:column;gap:1rem;">
    cards here
</div>

{{-- CORRECT — outer constrains height, inner grows freely --}}
<div style="height:...;overflow-y:auto;">
<div style="display:flex;flex-direction:column;gap:1rem;">
    cards here
</div>
</div>
```

### 5. Scroll body height formula
```
height: calc(100vh - 117px)
                     header(60) + footer(57)^
```

### 6. Page scroll lock — use `@wheel.stop` not body overflow
`document.body.style.overflow = 'hidden'` blocks scroll **inside** fixed-position elements too.  
Instead, put `@wheel.stop` on the outer fixed wrapper. This stops bubbling (page never scrolls) while leaving the default scroll action intact (drawer body scrolls naturally via `overflow-y:auto`).

### 7. `wire:ignore` on the x-data wrapper
Alpine's `open` state will be reset on every Livewire render unless the wrapper has `wire:ignore`.

---

## Dark Mode CSS Classes

The notes panel uses specific CSS classes so dark mode overrides can target them without fighting inline styles. These classes have no visual effect in light mode — they only carry dark mode overrides via `.dark .am-notes-*` rules in the page `<style>` block.

| Class | Element |
|---|---|
| `am-notes-panel` | The white drawer panel div |
| `am-notes-header` | The header bar (title + × button) |
| `am-notes-title` | The `<div>` containing the panel title text |
| `am-notes-footer` | The footer bar (Close button) |

Add these dark mode rules to the page `<style>` block alongside the other `.dark` overrides:

```css
.dark .am-notes-panel  { background:#1f2937 !important; box-shadow:-8px 0 40px rgba(0,0,0,.5) !important; }
.dark .am-notes-header { border-bottom-color:#374151 !important; }
.dark .am-notes-title  { color:#f9fafb !important; }
.dark .am-notes-footer { border-top-color:#374151 !important; }
.dark .am-notes-footer span   { color:#6b7280 !important; }
.dark .am-notes-footer button { background:#374151 !important; border-color:#4b5563 !important; color:#d1d5db !important; }
```

---

## Full Template

Paste this block immediately before `</x-filament-panels::page>`. Replace:
- `«PAGE TITLE»` — e.g. `Stock & Price notes`
- `«CARD 1»`, `«CARD 2»` etc. — content cards (see Card Patterns below)

```html
{{-- Notes drawer — fixed vertical tab on right edge --}}
<div x-data="{ open: false }" wire:ignore>

    <button type="button" @click="open = true"
        style="position:fixed;right:0;top:50%;transform:translateY(-50%);z-index:9990;background:#6366f1;color:#fff;border:none;border-radius:.5rem 0 0 .5rem;padding:.875rem .4375rem;cursor:pointer;writing-mode:vertical-lr;font-size:.6875rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;box-shadow:-3px 0 14px rgba(0,0,0,.2);">Notes</button>

    <template x-teleport="body">
        {{-- @wheel.stop stops bubbling so page never receives the event; body div scrolls by default --}}
        <div x-cloak :style="{ pointerEvents: open ? 'auto' : 'none' }" style="position:fixed;inset:0;z-index:9998;" @wheel.stop @touchmove.prevent>

            {{-- Backdrop --}}
            <div :style="{ opacity: open ? 1 : 0 }"
                 style="position:fixed;inset:0;background:rgba(0,0,0,.3);transition:opacity .25s ease;"
                 @click="open = false"></div>

            {{-- Drawer: full height, overlays Filament topnav via z-index --}}
            <div :style="{ transform: open ? 'translateX(0)' : 'translateX(100%)' }"
                 class="am-notes-panel"
                 style="position:fixed;top:0;right:0;width:660px;max-width:calc(100vw - 3rem);height:100vh;background:#fff;box-shadow:-8px 0 40px rgba(0,0,0,.14);overflow:hidden;transition:transform .3s cubic-bezier(.4,0,.2,1);"
                 @click.stop>

                {{-- Header --}}
                <div class="am-notes-header" style="height:60px;display:flex;align-items:center;justify-content:space-between;padding:0 1.5rem;border-bottom:1px solid #e5e7eb;">
                    <div class="am-notes-title" style="font-size:.9375rem;font-weight:700;color:#111827;">«PAGE TITLE»</div>
                    <button @click="open = false" style="background:none;border:none;cursor:pointer;color:#9ca3af;padding:4px;line-height:1;font-size:1.375rem;display:flex;align-items:center;" title="Close">×</button>
                </div>

                {{-- Scroll container: outer constrains height, inner grows to content --}}
                <div style="height:calc(100vh - 117px);box-sizing:border-box;overflow-y:auto;overscroll-behavior:contain;padding:1.25rem 1.5rem;">
                <div style="display:flex;flex-direction:column;gap:1rem;">

                    «CARD 1»
                    «CARD 2»
                    ...

                </div>{{-- /inner flex wrapper --}}
                </div>{{-- /scroll container --}}

                {{-- Footer --}}
                <div class="am-notes-footer" style="height:57px;display:flex;align-items:center;justify-content:space-between;padding:0 1.5rem;border-top:1px solid #f3f4f6;">
                    <span style="font-size:.75rem;color:#9ca3af;">Internal notes · only visible to your team</span>
                    <button @click="open = false"
                        style="padding:.375rem .875rem;border:1px solid #e5e7eb;border-radius:.5rem;background:#f9fafb;color:#374151;font-size:.8rem;font-weight:600;cursor:pointer;">
                        Close
                    </button>
                </div>
            </div>
        </div>
    </template>
</div>
```

---

## Card Patterns

Each card is a bordered rounded box with a header row and a content body.

### Plain text card (use for explanations, bullet lists)

Change the icon colours/SVG to suit the topic. Available icon background colour options:
- Blue info: `background:#eff6ff` / icon `color:#2563eb`
- Green clipboard: `background:#f0fdf4` / icon `color:#16a34a`
- Orange refresh: `background:#fff7ed` / icon `color:#ea580c`
- Purple info circle: `background:#ede9fe` / icon `color:#7c3aed`
- Red warning: `background:#fef2f2` / icon `color:#dc2626`

```html
<div style="border:1px solid #e5e7eb;border-radius:.625rem;overflow:hidden;">
    <div style="display:flex;align-items:center;gap:.625rem;padding:.875rem 1rem;border-bottom:1px solid #f3f4f6;background:#f9fafb;">
        <div style="width:28px;height:28px;border-radius:.375rem;background:#eff6ff;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
            {{-- Heroicon outline — swap SVG path as needed --}}
            <svg xmlns="http://www.w3.org/2000/svg" style="width:14px;height:14px;color:#2563eb;" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                <circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/>
            </svg>
        </div>
        <span style="font-size:.8125rem;font-weight:700;color:#111827;">Card title</span>
    </div>
    <div style="padding:.875rem 1rem;font-size:.8rem;color:#374151;line-height:1.6;display:flex;flex-direction:column;gap:.375rem;">
        <div>First paragraph or item.</div>
        <div>Second paragraph or item.</div>
    </div>
</div>
```

To add a coloured badge/label inline (like the Fetch Images / Fetch Product Data style):
```html
<span style="background:#e0e7ff;color:#4338ca;border-radius:.25rem;padding:1px 7px;font-size:.7rem;font-weight:700;margin-right:6px;">Label</span>Explanation text here.
```

Badge colour options:
- Indigo: `background:#e0e7ff;color:#4338ca`
- Amber: `background:#fef3c7;color:#92400e`
- Green: `background:#d1fae5;color:#065f46`
- Red: `background:#fee2e2;color:#991b1b`
- Purple: `background:#ede9fe;color:#5b21b6`

---

### Table card (use for field/column reference lists)

```html
<div style="border:1px solid #e5e7eb;border-radius:.625rem;overflow:hidden;">
    <div style="display:flex;align-items:center;gap:.625rem;padding:.875rem 1rem;border-bottom:1px solid #f3f4f6;background:#f9fafb;">
        <div style="width:28px;height:28px;border-radius:.375rem;background:#f0fdf4;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
            <svg xmlns="http://www.w3.org/2000/svg" style="width:14px;height:14px;color:#16a34a;" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                <path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="1"/><path d="M9 12h6M9 16h4"/>
            </svg>
        </div>
        <span style="font-size:.8125rem;font-weight:700;color:#111827;">Table card title</span>
    </div>
    <table style="width:100%;border-collapse:collapse;font-size:.78rem;">
        <thead>
            <tr style="border-bottom:1px solid #e5e7eb;">
                <th style="text-align:left;padding:6px 12px;font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#9ca3af;white-space:nowrap;">Column A</th>
                <th style="text-align:left;padding:6px 12px;font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#9ca3af;">Column B</th>
                <th style="text-align:left;padding:6px 12px;font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#9ca3af;">Notes</th>
            </tr>
        </thead>
        <tbody>
            @foreach([
                ['Row A value', 'db_column_name', 'Explanation of this field'],
                ['Row B value', 'another_column', 'Another explanation'],
            ] as [$col1, $col2, $note])
                <tr style="border-bottom:1px solid #f3f4f6;">
                    <td style="padding:6px 12px;font-weight:600;color:#111827;white-space:nowrap;">{{ $col1 }}</td>
                    <td style="padding:6px 12px;font-family:ui-monospace,monospace;font-size:.72rem;color:#4338ca;white-space:nowrap;">{{ $col2 }}</td>
                    <td style="padding:6px 12px;color:#6b7280;">{{ $note }}</td>
                </tr>
            @endforeach
        </tbody>
    </table>
</div>
```

Use `color:#4338ca` (indigo monospace) for database column names, `color:#6b7280` (gray) for notes/descriptions.

---

### Key–value grid card (use for code lookups, status codes, enum values)

```html
<div style="border:1px solid #e5e7eb;border-radius:.625rem;overflow:hidden;">
    <div style="display:flex;align-items:center;gap:.625rem;padding:.875rem 1rem;border-bottom:1px solid #f3f4f6;background:#f9fafb;">
        <div style="width:28px;height:28px;border-radius:.375rem;background:#ede9fe;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
            <svg xmlns="http://www.w3.org/2000/svg" style="width:14px;height:14px;color:#7c3aed;" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                <circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/>
            </svg>
        </div>
        <span style="font-size:.8125rem;font-weight:700;color:#111827;">Key–value card title</span>
    </div>
    <div style="padding:.875rem 1rem;font-size:.8rem;color:#374151;line-height:1.7;display:grid;grid-template-columns:auto 1fr;gap:.25rem .875rem;">
        <span style="font-family:ui-monospace,monospace;font-weight:700;color:#4338ca;">KEY1</span><span>Description of what KEY1 means</span>
        <span style="font-family:ui-monospace,monospace;font-weight:700;color:#4338ca;">KEY2</span><span>Description of what KEY2 means</span>
        <span style="font-family:ui-monospace,monospace;font-weight:700;color:#4338ca;">KEY3</span><span>Description of what KEY3 means</span>
    </div>
</div>
```

---

## Dark Mode — Page CSS Block

Every page that uses the notes drawer must include this CSS block. The page CSS must also include the full set of dark mode overrides for the `.am-*` table/card classes. Copy the full `<style>` block from `allmakes-product-data.blade.php` as a starting point, then customise the page-specific sections.

The minimum required dark mode rules for the notes panel itself (add to the page `<style>` block):

```css
/* Notes drawer dark mode */
.dark .am-notes-panel  { background:#1f2937 !important; box-shadow:-8px 0 40px rgba(0,0,0,.5) !important; }
.dark .am-notes-header { border-bottom-color:#374151 !important; }
.dark .am-notes-title  { color:#f9fafb !important; }
.dark .am-notes-footer { border-top-color:#374151 !important; }
.dark .am-notes-footer span   { color:#6b7280 !important; }
.dark .am-notes-footer button { background:#374151 !important; border-color:#4b5563 !important; color:#d1d5db !important; }
```

Note: the content **inside** the cards uses hardcoded inline styles (light colours only). If dark mode support is needed inside cards, add `.dark` rule overrides for those specific inline colour values via additional CSS classes on those elements.

---

## Checklist When Adding to a New Page

- [ ] Pasted the full Notes drawer block immediately before `</x-filament-panels::page>`
- [ ] Updated the title text (`«PAGE TITLE»`)
- [ ] Replaced placeholder cards with page-specific content
- [ ] Added `wire:ignore` on the outer `x-data` wrapper
- [ ] Added `am-notes-panel`, `am-notes-header`, `am-notes-title`, `am-notes-footer` classes to the correct elements
- [ ] Added dark mode CSS rules (both `am-notes-*` rules AND the full `am-tbl` / `stat-card` / etc. rules if the page uses those classes)
- [ ] Verified drawer does NOT start at `top:0` (must be `top:57px`)
- [ ] Verified scroll body height uses `calc(100vh - 57px - 117px)` formula
- [ ] Verified inner flex wrapper is a **separate** child div from the scroll container div
