---
description: Livewire 3 + Alpine.js patterns. One Alpine scope per feature.
globs: ["app/Livewire/**", "resources/views/livewire/**", "resources/views/components/**"]
---

# Livewire & Alpine Rules

## Livewire Component Structure

### List components ALWAYS have:
- `WithPagination` trait
- `#[Url]` on search, status, sortBy, sortDir
- `updatedSearch()` → `$this->resetPage()`
- Permission check in render query: `PermissionService::applyScope()`
- Server-side pagination (NEVER load all records)

### Form components ALWAYS have:
- `$this->authorize()` in mount
- `rules()` method matching migration constraints exactly
- `messages()` with Arabic error messages
- `save()` wraps in try/catch for DomainExceptions
- Redirects with flash message on success

## Alpine.js — ONE Scope Per Feature

```html
<!-- CORRECT -->
<div x-data="{ activeTab: 'info', showModal: false }">
    <!-- everything that needs these states lives inside -->
</div>

<!-- WRONG — nested x-data shadows parent -->
<div x-data="{ activeTab: 'info' }">
    <div x-data="{ activeTab: 'sub' }"> <!-- SHADOWS! -->
```

## State Ownership

- Alpine owns: tabs, dropdowns, modals, tooltips, visual toggles
- Livewire owns: data, search, filters, CRUD, form submission

## Communication

```html
<!-- Alpine → Livewire -->
<button @click="$wire.delete(id)">

<!-- Livewire → Alpine -->
$this->dispatch('item-saved');
<!-- consumed: @item-saved.window="open = false" -->
```

## Loading States (MANDATORY)

Every submit button:
```html
<button wire:loading.attr="disabled" wire:target="save">
    <span wire:loading.remove wire:target="save">حفظ</span>
    <span wire:loading wire:target="save">جارٍ الحفظ...</span>
</button>
```

Every table:
```html
<div wire:loading.class="opacity-50 pointer-events-none">
```

## No Dead Links

- `href="#"` is FORBIDDEN
- If feature not built: don't render the link
- All links use `route('name')` — never hardcoded URLs
- Nav items wrapped in `@can('module.action')`

## Components — Required Interface

Every `<x-ui.input>` must accept: `name`, `label`, `error`, `required`, `disabled`
Every `<x-ui.button>` must accept: `type`, `variant`, `size`, `disabled`, `loading`
Every `<x-ui.modal>` must accept: `title`, `maxWidth`, `closeable`

## Debouncing

- Search: `wire:model.live.debounce.300ms`
- Expensive checks: `wire:model.blur`
- Regular form fields: `wire:model` (no live)
