output:
Chunk ID: 04a02d
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 8242
Output:
specs/07-SEEDERS-AND-TEST-DATA.md:20:| 6 | StoreSettingsSeeder | Stores | Inserts per-store JSON settings |
specs/07-SEEDERS-AND-TEST-DATA.md:21:| 7 | TaxSettingsSeeder | Stores | Configures tax mode and rates |
specs/07-SEEDERS-AND-TEST-DATA.md:28:| 14 | ThemeSeeder | Stores | Creates themes with settings |
specs/07-SEEDERS-AND-TEST-DATA.md:29:| 15 | PageSeeder | Stores | Creates content pages |
specs/07-SEEDERS-AND-TEST-DATA.md:30:| 16 | NavigationSeeder | Stores, Collections, Pages | Creates menus linking to collections and pages |
specs/07-SEEDERS-AND-TEST-DATA.md:32:| 18 | SearchSettingsSeeder | Stores | Configures search synonyms and stop words |
specs/07-SEEDERS-AND-TEST-DATA.md:40:    B --> F[StoreSettingsSeeder]
specs/07-SEEDERS-AND-TEST-DATA.md:41:    B --> G[TaxSettingsSeeder]
specs/07-SEEDERS-AND-TEST-DATA.md:53:    B --> N[ThemeSeeder]
specs/07-SEEDERS-AND-TEST-DATA.md:55:    I --> P[NavigationSeeder]
specs/07-SEEDERS-AND-TEST-DATA.md:59:    B --> R[SearchSettingsSeeder]
specs/07-SEEDERS-AND-TEST-DATA.md:631:### 2.22 ThemeFactory
specs/07-SEEDERS-AND-TEST-DATA.md:633:**Model:** Theme
specs/07-SEEDERS-AND-TEST-DATA.md:640:| name | "Default Theme" |
specs/07-SEEDERS-AND-TEST-DATA.md:677:### 2.24 NavigationMenuFactory
specs/07-SEEDERS-AND-TEST-DATA.md:679:**Model:** NavigationMenu
specs/07-SEEDERS-AND-TEST-DATA.md:693:### 2.25 NavigationItemFactory
specs/07-SEEDERS-AND-TEST-DATA.md:695:**Model:** NavigationItem
specs/07-SEEDERS-AND-TEST-DATA.md:701:| menu_id | Creates a new NavigationMenu via its factory |
specs/07-SEEDERS-AND-TEST-DATA.md:831:### 3.6 StoreSettingsSeeder
specs/07-SEEDERS-AND-TEST-DATA.md:833:**Acme Fashion - settings_json contents:**
specs/07-SEEDERS-AND-TEST-DATA.md:842:**Acme Electronics - settings_json contents:**
specs/07-SEEDERS-AND-TEST-DATA.md:853:### 3.7 TaxSettingsSeeder
specs/07-SEEDERS-AND-TEST-DATA.md:1885:### 3.14 ThemeSeeder
specs/07-SEEDERS-AND-TEST-DATA.md:1891:| name | Default Theme |
specs/07-SEEDERS-AND-TEST-DATA.md:1896:**Theme settings (theme_settings row):**
specs/07-SEEDERS-AND-TEST-DATA.md:1919:| name | Default Theme |
specs/07-SEEDERS-AND-TEST-DATA.md:1923:Theme settings:
specs/07-SEEDERS-AND-TEST-DATA.md:1941:**Acme Fashion - 5 content pages:**
specs/07-SEEDERS-AND-TEST-DATA.md:1943:All pages: status = "published", published_at = 3 months ago.
specs/07-SEEDERS-AND-TEST-DATA.md:1947:- body_html content: Three sections with h2 headings - "Our Story" (mission and philosophy), "Our Values" (ethical sourcing, sustainability, fair labor), "Our Team" (Berlin-based designers). Multiple paragraphs of realistic store copy.
specs/07-SEEDERS-AND-TEST-DATA.md:1951:- body_html content: h2 "Frequently Asked Questions" followed by four Q&A pairs using h3/p structure:
specs/07-SEEDERS-AND-TEST-DATA.md:1959:- body_html content: Shipping rates table (Germany standard 4.99/express 9.99, EU 8.99, International 14.99) using h3 and ul/li elements, plus returns policy paragraph (30 days, customer pays return shipping unless defective).
specs/07-SEEDERS-AND-TEST-DATA.md:1963:- body_html content: Standard privacy policy with sections: Information We Collect, How We Use Your Information, Cookies, Contact (privacy@acme-fashion.test). Uses h2/h3/p structure.
specs/07-SEEDERS-AND-TEST-DATA.md:1967:- body_html content: Standard ToS with sections: Orders and Payments (EUR, tax-inclusive), Product Descriptions (color variance disclaimer), Limitation of Liability, Governing Law (Federal Republic of Germany). Uses h2/h3/p structure.
specs/07-SEEDERS-AND-TEST-DATA.md:1971:### 3.16 NavigationSeeder
specs/07-SEEDERS-AND-TEST-DATA.md:2075:### 3.18 SearchSettingsSeeder
specs/03-ADMIN-UI.md:13:The admin layout is a persistent shell that wraps every admin page. It consists of a left sidebar, a top bar, and a main content area.
specs/03-ADMIN-UI.md:19:### 1.2 Sidebar Navigation
specs/03-ADMIN-UI.md:66:|  [bars-3]       Navigation    |
specs/03-ADMIN-UI.md:67:|  [paint-brush]  Themes        |
specs/03-ADMIN-UI.md:71:|  [cog-6-tooth]  Settings      |
specs/03-ADMIN-UI.md:77:#### Navigation Structure
specs/03-ADMIN-UI.md:92:  Pages                          -> admin.pages.index
specs/03-ADMIN-UI.md:93:  Navigation                     -> admin.navigation.index
specs/03-ADMIN-UI.md:94:  Themes                         -> admin.themes.index
specs/03-ADMIN-UI.md:96:Settings                         -> admin.settings.index
specs/03-ADMIN-UI.md:101:Each nav item displays an icon (via Flux icon component) and a text label. The active item receives a highlighted background and bold text. All navigation links use Livewire SPA-style page transitions. Groups are visually separated with Flux separator components.
specs/03-ADMIN-UI.md:108:| Navigation icons | `icon` |
specs/03-ADMIN-UI.md:113:- **Desktop (lg and above):** Sidebar is always visible, fixed left, 256px wide. Main content has a left margin of 256px.
specs/03-ADMIN-UI.md:148:    - Settings (links to /admin/settings)
specs/03-ADMIN-UI.md:173:The main content area sits to the right of the sidebar and below the top bar. Every page renders within this area.
specs/03-ADMIN-UI.md:175:**Breadcrumbs:** Every page includes a Flux breadcrumbs component at the top of the content area. The first item is always "Home" linking to `/admin`. Subsequent items reflect the page hierarchy. Example for product edit: Home > Products > "Blue T-Shirt".
specs/03-ADMIN-UI.md:177:**Layout wrapper:** The content area has consistent padding (larger on desktop, smaller on mobile) and a max-width container centered horizontally.
specs/03-ADMIN-UI.md:198:Dark mode preference is persisted in `localStorage` under the key `theme` with values `light` or `dark`. On page load, the stored preference is applied before first paint to avoid flash of incorrect theme. If no preference is stored, the system preference (`prefers-color-scheme`) is used as default.
specs/03-ADMIN-UI.md:1124:| Text content | `text` |
specs/03-ADMIN-UI.md:1378:## 11. Settings Pages
specs/03-ADMIN-UI.md:1380:### 11.1 General Settings
specs/03-ADMIN-UI.md:1382:**Route:** `GET /admin/settings`
specs/03-ADMIN-UI.md:1384:**Livewire component:** `App\Livewire\Admin\Settings\General`
specs/03-ADMIN-UI.md:1386:**Blade view:** `resources/views/livewire/admin/settings/general.blade.php`
specs/03-ADMIN-UI.md:1400:| `save()` | Validates and saves store settings |
specs/03-ADMIN-UI.md:1413:- Left: Heading "Defaults" + description "Currency, language, and timezone settings."
specs/03-ADMIN-UI.md:1421:### 11.2 Domains Settings
specs/03-ADMIN-UI.md:1423:Domains is a tab within the Settings page (`/admin/settings`), not a separate route. The Settings page has tabs: General, Domains, Shipping, Taxes, Checkout, Notifications.
specs/03-ADMIN-UI.md:1425:**Livewire component:** `App\Livewire\Admin\Settings\Domains`
specs/03-ADMIN-UI.md:1427:**Blade view:** `resources/views/livewire/admin/settings/domains.blade.php`
specs/03-ADMIN-UI.md:1471:### 11.3 Shipping Settings
specs/03-ADMIN-UI.md:1473:**Route:** `GET /admin/settings/shipping`
specs/03-ADMIN-UI.md:1475:**Livewire component:** `App\Livewire\Admin\Settings\Shipping`
specs/03-ADMIN-UI.md:1477:**Blade view:** `resources/views/livewire/admin/settings/shipping.blade.php`
specs/03-ADMIN-UI.md:1588:### 11.4 Tax Settings
specs/03-ADMIN-UI.md:1590:**Route:** `GET /admin/settings/taxes`
specs/03-ADMIN-UI.md:1592:**Livewire component:** `App\Livewire\Admin\Settings\Taxes`
specs/03-ADMIN-UI.md:1594:**Blade view:** `resources/views/livewire/admin/settings/taxes.blade.php`
specs/03-ADMIN-UI.md:1608:| `save()` | Saves tax settings |
specs/03-ADMIN-UI.md:1655:## 12. Themes Management
specs/03-ADMIN-UI.md:1657:### 12.1 Themes List
specs/03-ADMIN-UI.md:1659:**Route:** `GET /admin/themes`
specs/03-ADMIN-UI.md:1661:**Livewire component:** `App\Livewire\Admin\Themes\Index`
specs/03-ADMIN-UI.md:1663:**Blade view:** `resources/views/livewire/admin/themes/index.blade.php`
specs/03-ADMIN-UI.md:1669:| `themes` | `Collection` | All themes for the store |
specs/03-ADMIN-UI.md:1673:| `publishTheme(int $themeId)` | Publishes a theme (sets as active) |
specs/03-ADMIN-UI.md:1674:| `duplicateTheme(int $themeId)` | Creates a copy of the theme |
specs/03-ADMIN-UI.md:1675:| `deleteTheme(int $themeId)` | Deletes a theme |
specs/03-ADMIN-UI.md:1681:| Themes                                                            |
specs/03-ADMIN-UI.md:1687:| | My Theme   v1.0  | | Dark Theme v1.2  | | Minimal    v2.0  |   |
specs/03-ADMIN-UI.md:1696:**Theme grid:** Responsive 3-column grid (1 on mobile, 2 on md, 3 on lg).
specs/03-ADMIN-UI.md:1698:Each theme card contains:
specs/03-ADMIN-UI.md:1699:- **Thumbnail area:** Aspect-video container with theme preview image or placeholder
specs/03-ADMIN-UI.md:1701:  - Theme name (heading, size md) and version text
specs/03-ADMIN-UI.md:1704:  - "Customize" primary button (links to theme editor)
specs/03-ADMIN-UI.md:1712:The published theme card has a blue ring border highlight.
specs/03-ADMIN-UI.md:1714:### 12.2 Theme Editor
specs/03-ADMIN-UI.md:1716:**Route:** `GET /admin/themes/{theme}/editor`
specs/03-ADMIN-UI.md:1718:**Livewire component:** `App\Livewire\Admin\Themes\Editor`
specs/03-ADMIN-UI.md:1720:**Blade view:** `resources/views/livewire/admin/themes/editor.blade.php`
specs/03-ADMIN-UI.md:1726:| `theme` | `Theme` | The theme being edited |
specs/03-ADMIN-UI.md:1727:| `sections` | `array` | List of theme sections from settings schema |
specs/03-ADMIN-UI.md:1729:| `sectionSettings` | `array` | Settings values for the selected section |
specs/03-ADMIN-UI.md:1734:| `mount(Theme $theme)` | Loads theme and its sections |
specs/03-ADMIN-UI.md:1735:| `selectSection(string $sectionKey)` | Changes the selected section and loads its settings |
specs/03-ADMIN-UI.md:1737:| `save()` | Saves all section settings to theme_settings |
specs/03-ADMIN-UI.md:1738:| `publish()` | Publishes the theme |
specs/03-ADMIN-UI.md:1745:| [<- Back to themes]                     [Save]  [Save & publish] |
specs/03-ADMIN-UI.md:1750:| Header     |  +------------------------+ |  Header Settings     |
specs/03-ADMIN-UI.md:1771:- "Back to themes" button (ghost, with left arrow icon) linking to themes index
specs/03-ADMIN-UI.md:1785:**Right panel (320px):** Settings form
specs/03-ADMIN-UI.md:1786:- Shows settings for the selected section
specs/03-ADMIN-UI.md:1799:- When no section is selected: centered text "Select a section to edit its settings."
specs/03-ADMIN-UI.md:1807:**Route:** `GET /admin/pages`
specs/03-ADMIN-UI.md:1811:**Blade view:** `resources/views/livewire/admin/pages/index.blade.php`
specs/03-ADMIN-UI.md:1821:| `pages` | Paginated pages for the current store |
specs/03-ADMIN-UI.md:1832:**Route:** `GET /admin/pages/create` and `GET /admin/pages/{page}/edit`
specs/03-ADMIN-UI.md:1836:**Blade view:** `resources/views/livewire/admin/pages/form.blade.php`
specs/03-ADMIN-UI.md:1845:| `bodyHtml` | `string` | Page body content (HTML) |
specs/03-ADMIN-UI.md:1869:## 14. Navigation Management
specs/03-ADMIN-UI.md:1871:**Route:** `GET /admin/navigation`
specs/03-ADMIN-UI.md:1873:**Livewire component:** `App\Livewire\Admin\Navigation\Index`
specs/03-ADMIN-UI.md:1875:**Blade view:** `resources/views/livewire/admin/navigation/index.blade.php`
specs/03-ADMIN-UI.md:1881:| `menus` | `Collection` | Navigation menus (Main Menu, Footer Menu) |
specs/03-ADMIN-UI.md:1882:| `editingMenu` | `?NavigationMenu` | Menu being edited |
specs/03-ADMIN-UI.md:1905:| Navigation                                                        |
specs/03-ADMIN-UI.md:2209:## 18. Search Settings
specs/03-ADMIN-UI.md:2211:**Route:** `GET /admin/search/settings`
specs/03-ADMIN-UI.md:2213:**Livewire component:** `App\Livewire\Admin\Search\Settings`
specs/03-ADMIN-UI.md:2215:**Blade view:** `resources/views/livewire/admin/search/settings.blade.php`
specs/03-ADMIN-UI.md:2229:| `save()` | Saves synonym and stop word settings |
specs/03-ADMIN-UI.md:2239:| Search Settings                                                   |
specs/03-ADMIN-UI.md:2296:All list pages follow a consistent pattern:
specs/03-ADMIN-UI.md:2307:All form pages follow a consistent pattern:
specs/03-ADMIN-UI.md:2310:3. Left column: primary content fields
specs/03-ADMIN-UI.md:2367:- Semantic HTML: headings hierarchy, landmark regions, lists for navigation
specs/03-ADMIN-UI.md:2389:| Settings saved | "Settings saved" | success |
specs/03-ADMIN-UI.md:2390:| Theme published | "Theme published" | success |
specs/03-ADMIN-UI.md:2392:| Navigation saved | "Navigation saved" | success |
specs/03-ADMIN-UI.md:2402:| `App\Livewire\Admin\Layout\Sidebar` | `livewire.admin.layout.sidebar` | Layout (all pages) |
specs/03-ADMIN-UI.md:2403:| `App\Livewire\Admin\Layout\TopBar` | `livewire.admin.layout.top-bar` | Layout (all pages) |
specs/03-ADMIN-UI.md:2416:| `App\Livewire\Admin\Settings\General` | `livewire.admin.settings.general` | `GET /admin/settings` |
specs/03-ADMIN-UI.md:2417:| `App\Livewire\Admin\Settings\Domains` | `livewire.admin.settings.domains` | Tab within `GET /admin/settings` |
specs/03-ADMIN-UI.md:2418:| `App\Livewire\Admin\Settings\Shipping` | `livewire.admin.settings.shipping` | `GET /admin/settings/shipping` |
specs/03-ADMIN-UI.md:2419:| `App\Livewire\Admin\Settings\Taxes` | `livewire.admin.settings.taxes` | `GET /admin/settings/taxes` |
specs/03-ADMIN-UI.md:2420:| `App\Livewire\Admin\Themes\Index` | `livewire.admin.themes.index` | `GET /admin/themes` |
specs/03-ADMIN-UI.md:2421:| `App\Livewire\Admin\Themes\Editor` | `livewire.admin.themes.editor` | `GET /admin/themes/{theme}/editor` |
specs/03-ADMIN-UI.md:2422:| `App\Livewire\Admin\Pages\Index` | `livewire.admin.pages.index` | `GET /admin/pages` |
specs/03-ADMIN-UI.md:2423:| `App\Livewire\Admin\Pages\Form` | `livewire.admin.pages.form` | `GET /admin/pages/create`, `GET /admin/pages/{page}/edit` |
specs/03-ADMIN-UI.md:2424:| `App\Livewire\Admin\Navigation\Index` | `livewire.admin.navigation.index` | `GET /admin/navigation` |
specs/03-ADMIN-UI.md:2428:| `App\Livewire\Admin\Search\Settings` | `livewire.admin.search.settings` | `GET /admin/search/settings` |
specs/01-DATABASE-SCHEMA.md:139:### store_settings
specs/01-DATABASE-SCHEMA.md:141:Single-row-per-store JSON settings bag.
specs/01-DATABASE-SCHEMA.md:146:| settings_json | TEXT | No | '{}' | - | JSON settings object |
specs/01-DATABASE-SCHEMA.md:367:## Epic 3 - Themes, Storefront, and Localization
specs/01-DATABASE-SCHEMA.md:369:### themes
specs/01-DATABASE-SCHEMA.md:371:Storefront theme definitions per store.
specs/01-DATABASE-SCHEMA.md:377:| name | TEXT | No | - | - | Theme display name |
specs/01-DATABASE-SCHEMA.md:386:- `idx_themes_store_id` on (`store_id`) - FK lookup, listing themes per store
specs/01-DATABASE-SCHEMA.md:387:- `idx_themes_store_status` on (`store_id`, `status`) - Finding published/draft themes per store
specs/01-DATABASE-SCHEMA.md:391:### theme_files
specs/01-DATABASE-SCHEMA.md:393:Individual files belonging to a theme (templates, assets, snippets).
specs/01-DATABASE-SCHEMA.md:398:| theme_id | INTEGER | No | - | FK -> themes(id) ON DELETE CASCADE | Parent theme |
specs/01-DATABASE-SCHEMA.md:399:| path | TEXT | No | - | UNIQUE per (theme_id, path) | File path within theme |
specs/01-DATABASE-SCHEMA.md:406:- `idx_theme_files_theme_path` UNIQUE on (`theme_id`, `path`) - Enforces unique file paths per theme
specs/01-DATABASE-SCHEMA.md:407:- `idx_theme_files_theme_id` on (`theme_id`) - FK lookup, listing files per theme
specs/01-DATABASE-SCHEMA.md:411:### theme_settings
specs/01-DATABASE-SCHEMA.md:413:Single-row JSON configuration per theme.
specs/01-DATABASE-SCHEMA.md:417:| theme_id | INTEGER | No | - | PRIMARY KEY, FK -> themes(id) ON DELETE CASCADE | One-to-one with themes |
specs/01-DATABASE-SCHEMA.md:418:| settings_json | TEXT | No | '{}' | - | JSON settings object |
specs/01-DATABASE-SCHEMA.md:423:### pages
specs/01-DATABASE-SCHEMA.md:425:Static CMS pages (About Us, Contact, etc.).
specs/01-DATABASE-SCHEMA.md:433:| body_html | TEXT | Yes | NULL | - | Rich-text content |
specs/01-DATABASE-SCHEMA.md:441:- `idx_pages_store_handle` UNIQUE on (`store_id`, `handle`) - Unique handle per store for URL routing
specs/01-DATABASE-SCHEMA.md:442:- `idx_pages_store_id` on (`store_id`) - FK lookup, listing pages per store
specs/01-DATABASE-SCHEMA.md:443:- `idx_pages_store_status` on (`store_id`, `status`) - Filtering pages by status
specs/01-DATABASE-SCHEMA.md:447:### navigation_menus
specs/01-DATABASE-SCHEMA.md:462:- `idx_navigation_menus_store_handle` UNIQUE on (`store_id`, `handle`) - Unique handle per store
specs/01-DATABASE-SCHEMA.md:463:- `idx_navigation_menus_store_id` on (`store_id`) - FK lookup, listing menus per store
specs/01-DATABASE-SCHEMA.md:467:### navigation_items
specs/01-DATABASE-SCHEMA.md:469:Individual links within a navigation menu (supports nesting via position).
specs/01-DATABASE-SCHEMA.md:474:| menu_id | INTEGER | No | - | FK -> navigation_menus(id) ON DELETE CASCADE | Parent menu |
specs/01-DATABASE-SCHEMA.md:478:| resource_id | INTEGER | Yes | NULL | - | FK to pages/collections/products (polymorphic) |
specs/01-DATABASE-SCHEMA.md:483:- `idx_navigation_items_menu_id` on (`menu_id`) - FK lookup, listing items per menu
specs/01-DATABASE-SCHEMA.md:484:- `idx_navigation_items_menu_position` on (`menu_id`, `position`) - Ordered item listing
specs/01-DATABASE-SCHEMA.md:492:### search_settings
specs/01-DATABASE-SCHEMA.md:658:### tax_settings
specs/01-DATABASE-SCHEMA.md:1212:    stores ||--|| store_settings : "has"
specs/01-DATABASE-SCHEMA.md:1245:    store_settings {
specs/01-DATABASE-SCHEMA.md:1247:        TEXT settings_json
specs/01-DATABASE-SCHEMA.md:1318:### Epic 3 - Themes, Storefront, and Localization
specs/01-DATABASE-SCHEMA.md:1322:    stores ||--o{ themes : "has"
specs/01-DATABASE-SCHEMA.md:1323:    themes ||--o{ theme_files : "contains"
specs/01-DATABASE-SCHEMA.md:1324:    themes ||--|| theme_settings : "configured by"
specs/01-DATABASE-SCHEMA.md:1325:    stores ||--o{ pages : "has"
specs/01-DATABASE-SCHEMA.md:1326:    stores ||--o{ navigation_menus : "has"
specs/01-DATABASE-SCHEMA.md:1327:    navigation_menus ||--o{ navigation_items : "contains"
specs/01-DATABASE-SCHEMA.md:1329:    themes {
specs/01-DATABASE-SCHEMA.md:1335:    theme_files {
specs/01-DATABASE-SCHEMA.md:1337:        INTEGER theme_id FK
specs/01-DATABASE-SCHEMA.md:1341:    theme_settings {
specs/01-DATABASE-SCHEMA.md:1342:        INTEGER theme_id PK
specs/01-DATABASE-SCHEMA.md:1343:        TEXT settings_json
specs/01-DATABASE-SCHEMA.md:1345:    pages {
specs/01-DATABASE-SCHEMA.md:1352:    navigation_menus {
specs/01-DATABASE-SCHEMA.md:1358:    navigation_items {
specs/01-DATABASE-SCHEMA.md:1484:The application requires specific SQLite PRAGMA settings configured in the Laravel database configuration file (`config/database.php`). These settings must be applied for correct and performant operation.
specs/01-DATABASE-SCHEMA.md:1486:### Required PRAGMA Settings
specs/01-DATABASE-SCHEMA.md:1500:- The `busy_timeout` must be set to `5000` (milliseconds) to handle write contention gracefully.
specs/01-DATABASE-SCHEMA.md:1526:        store_settings
specs/01-DATABASE-SCHEMA.md:1528:        themes
specs/01-DATABASE-SCHEMA.md:1529:        pages
specs/01-DATABASE-SCHEMA.md:1530:        navigation_menus
specs/01-DATABASE-SCHEMA.md:1531:        search_settings
specs/01-DATABASE-SCHEMA.md:1533:        tax_settings
specs/01-DATABASE-SCHEMA.md:1548:        navigation_items
specs/01-DATABASE-SCHEMA.md:1549:        theme_files
specs/01-DATABASE-SCHEMA.md:1550:        theme_settings
specs/01-DATABASE-SCHEMA.md:1586:    stores --> store_settings
specs/01-DATABASE-SCHEMA.md:1588:    stores --> themes
specs/01-DATABASE-SCHEMA.md:1589:    stores --> pages
specs/01-DATABASE-SCHEMA.md:1590:    stores --> navigation_menus
specs/01-DATABASE-SCHEMA.md:1591:    stores --> search_settings
specs/01-DATABASE-SCHEMA.md:1593:    stores --> tax_settings
specs/01-DATABASE-SCHEMA.md:1606:    navigation_menus --> navigation_items
specs/01-DATABASE-SCHEMA.md:1607:    themes --> theme_files
specs/01-DATABASE-SCHEMA.md:1608:    themes --> theme_settings
specs/01-DATABASE-SCHEMA.md:1645:- **Batch 3** (depends on Batch 2): `store_domains`, `store_users`, `store_settings`, `customers`, `themes`, `pages`, `navigation_menus`, `search_settings`, `shipping_zones`, `tax_settings`, `discounts`, `products`, `collections`, `app_installations`, `oauth_clients`
specs/01-DATABASE-SCHEMA.md:1646:- **Batch 4** (depends on Batch 3): `product_options`, `product_variants`, `product_media`, `collection_products`, `customer_addresses`, `carts`, `navigation_items`, `theme_files`, `theme_settings`, `search_queries`, `shipping_rates`, `oauth_tokens`, `webhook_subscriptions`
specs/01-DATABASE-SCHEMA.md:1673:| 1 - Foundation and Multi-Store Tenancy | organizations, stores, store_domains, users, store_users, store_settings | 6 |
specs/01-DATABASE-SCHEMA.md:1675:| 3 - Themes, Storefront, Localization | themes, theme_files, theme_settings, pages, navigation_menus, navigation_items | 6 |
specs/01-DATABASE-SCHEMA.md:1676:| 4 - Search | search_settings, search_queries | 2 |
specs/01-DATABASE-SCHEMA.md:1677:| 5 - Cart, Checkout, Discounts, Shipping, Taxes | carts, cart_lines, checkouts, shipping_zones, shipping_rates, tax_settings, discounts | 7 |
specs/09-IMPLEMENTATION-ROADMAP.md:43:    P3[Phase 3: Themes and Storefront Layout]
specs/09-IMPLEMENTATION-ROADMAP.md:46:    P6[Phase 6: Customer Accounts]
specs/09-IMPLEMENTATION-ROADMAP.md:109:6. `create_store_settings_table` - PK: `store_id`
specs/09-IMPLEMENTATION-ROADMAP.md:123:| Model (with migration, factory, seeder) | `StoreSettings` | Per-store configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:130:| `Store` | `belongsTo(Organization::class)`, `hasMany(StoreDomain::class)`, `belongsToMany(User::class)` through `store_users`, `hasOne(StoreSettings::class)` |
specs/09-IMPLEMENTATION-ROADMAP.md:134:| `StoreSettings` | `belongsTo(Store::class)` |
specs/09-IMPLEMENTATION-ROADMAP.md:176:Applied on: products, collections, customers, orders, carts, checkouts, discounts, shipping_zones, themes, pages, navigation_menus, analytics_events, analytics_daily, webhook_subscriptions, inventory_items, search_queries.
specs/09-IMPLEMENTATION-ROADMAP.md:217:| Policy | `ThemePolicy` | Theme authorization based on store role |
specs/09-IMPLEMENTATION-ROADMAP.md:227:| Manage store settings | Y | Y | N | N |
specs/09-IMPLEMENTATION-ROADMAP.md:236:| Manage themes | Y | Y | N | N |
specs/09-IMPLEMENTATION-ROADMAP.md:349:### Phase 3: Themes, Pages, Navigation, Storefront Layout
specs/09-IMPLEMENTATION-ROADMAP.md:355:1. `create_themes_table` - FK: `store_id`
specs/09-IMPLEMENTATION-ROADMAP.md:356:2. `create_theme_files_table` - FK: `theme_id`
specs/09-IMPLEMENTATION-ROADMAP.md:357:3. `create_theme_settings_table` - PK: `theme_id`
specs/09-IMPLEMENTATION-ROADMAP.md:358:4. `create_pages_table` - FK: `store_id`
specs/09-IMPLEMENTATION-ROADMAP.md:359:5. `create_navigation_menus_table` - FK: `store_id`
specs/09-IMPLEMENTATION-ROADMAP.md:360:6. `create_navigation_items_table` - FK: `menu_id`
specs/09-IMPLEMENTATION-ROADMAP.md:368:| Model (with migration, factory, seeder) | `Theme` | Store theme entity |
specs/09-IMPLEMENTATION-ROADMAP.md:369:| Model (with migration, factory, seeder) | `ThemeFile` | Individual theme file |
specs/09-IMPLEMENTATION-ROADMAP.md:370:| Model (with migration, factory, seeder) | `ThemeSettings` | Theme configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:372:| Model (with migration, factory, seeder) | `NavigationMenu` | Navigation menu entity |
specs/09-IMPLEMENTATION-ROADMAP.md:373:| Model (with migration, factory, seeder) | `NavigationItem` | Individual navigation item |
specs/09-IMPLEMENTATION-ROADMAP.md:379:| `Theme` | `belongsTo(Store)`, `hasMany(ThemeFile)`, `hasOne(ThemeSettings)` |
specs/09-IMPLEMENTATION-ROADMAP.md:380:| `ThemeFile` | `belongsTo(Theme)` |
specs/09-IMPLEMENTATION-ROADMAP.md:381:| `ThemeSettings` | `belongsTo(Theme)` |
specs/09-IMPLEMENTATION-ROADMAP.md:383:| `NavigationMenu` | `belongsTo(Store)`, `hasMany(NavigationItem)` |
specs/09-IMPLEMENTATION-ROADMAP.md:384:| `NavigationItem` | `belongsTo(NavigationMenu)` |
specs/09-IMPLEMENTATION-ROADMAP.md:390:| `ThemeStatus` | `Draft`, `Published` |
specs/09-IMPLEMENTATION-ROADMAP.md:392:| `NavigationItemType` | `Link`, `Page`, `Collection`, `Product` |
specs/09-IMPLEMENTATION-ROADMAP.md:401:| `pages/` | `home.blade.php`, `collection.blade.php`, `product.blade.php`, `cart.blade.php`, `search.blade.php`, `page.blade.php` |
specs/09-IMPLEMENTATION-ROADMAP.md:409:Base layout (`app.blade.php`): header with nav, announcement bar, main content, footer, cart drawer. All with dark mode support via `dark:` prefix.
specs/09-IMPLEMENTATION-ROADMAP.md:411:**ThemeSettings Service:** Singleton registered in `AppServiceProvider`, loads and caches active theme settings for the current store.
specs/09-IMPLEMENTATION-ROADMAP.md:419:| `Storefront\Home` | Dynamic sections based on theme settings |
specs/09-IMPLEMENTATION-ROADMAP.md:429:#### Step 3.5: Navigation Service
specs/09-IMPLEMENTATION-ROADMAP.md:431:**File to create:** `App\Services\NavigationService`
specs/09-IMPLEMENTATION-ROADMAP.md:435:| `buildTree(NavigationMenu $menu): array` | Builds navigation trees from navigation_menus / navigation_items |
specs/09-IMPLEMENTATION-ROADMAP.md:436:| `resolveUrl(NavigationItem $item): string` | Resolves URLs for page, collection, product type items |
specs/09-IMPLEMENTATION-ROADMAP.md:453:6. `create_tax_settings_table` - PK: `store_id`
specs/09-IMPLEMENTATION-ROADMAP.md:467:| Model (with migration, factory, seeder) | `TaxSettings` | Per-store tax configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:479:| `TaxSettings` | `belongsTo(Store)` |
specs/09-IMPLEMENTATION-ROADMAP.md:537:| `calculate(int $amount, TaxSettings $settings, array $address): TaxResult` | Calculates tax per manual rate or provider |
specs/09-IMPLEMENTATION-ROADMAP.md:746:### Phase 6: Customer Accounts
specs/09-IMPLEMENTATION-ROADMAP.md:785:| `Admin\Layout\Sidebar` | Fixed left sidebar (256px desktop, overlay mobile), navigation groups, Flux `brand`, `icon`, `separator` |
specs/09-IMPLEMENTATION-ROADMAP.md:840:| `Admin\Settings\Index` | Tabbed settings: General, Domains, Shipping, Taxes, Checkout, Notifications |
specs/09-IMPLEMENTATION-ROADMAP.md:841:| `Admin\Settings\Shipping` | Shipping zones CRUD, rates CRUD per zone |
specs/09-IMPLEMENTATION-ROADMAP.md:842:| `Admin\Settings\Taxes` | Tax mode toggle, manual rate configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:843:| `Admin\Themes\Index` | Theme cards with Publish/Duplicate/Delete actions |
specs/09-IMPLEMENTATION-ROADMAP.md:844:| `Admin\Themes\Editor` | Left: sections. Center: preview. Right: settings. |
specs/09-IMPLEMENTATION-ROADMAP.md:845:| `Admin\Pages\Index` | Content pages list |
specs/09-IMPLEMENTATION-ROADMAP.md:847:| `Admin\Navigation\Index` | Menu management with drag-and-drop item ordering |
specs/09-IMPLEMENTATION-ROADMAP.md:849:| `Admin\Search\Settings` | Synonyms, stop words, reindex button |
specs/09-IMPLEMENTATION-ROADMAP.md:862:1. `create_search_settings_table` - PK: `store_id`
specs/09-IMPLEMENTATION-ROADMAP.md:864:3. FTS5 virtual table migration (raw SQL) - create `products_fts` virtual table using FTS5 with columns: title, description, vendor, product_type, tags; content synced from `products` table
specs/09-IMPLEMENTATION-ROADMAP.md:957:1. Accessibility audit: skip links on all pages, ARIA labels on interactive elements, focus management in modals
specs/09-IMPLEMENTATION-ROADMAP.md:958:2. Responsive testing: all pages render correctly at sm/md/lg/xl breakpoints
specs/09-IMPLEMENTATION-ROADMAP.md:960:4. Error pages: styled 404 and 503 pages matching the storefront theme
specs/09-IMPLEMENTATION-ROADMAP.md:998:| `tests/Feature/Admin/` | `DashboardTest.php`, `ProductManagementTest.php`, `OrderManagementTest.php`, `DiscountManagementTest.php`, `SettingsTest.php` |
specs/09-IMPLEMENTATION-ROADMAP.md:1348:| handles prices-include-tax correctly | tax settings: prices_include_tax=true, rate=1900; item price 11900 (gross) | tax extracted = 1900, subtotal = 10000 | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md:1383:| applies zero tax when no tax settings exist | no tax_settings row for store | tax = 0 | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md:1553:#### tests/Feature/Admin/SettingsTest.php
specs/09-IMPLEMENTATION-ROADMAP.md:1557:| renders the settings page | GET /admin/settings | 200 response, sees settings tabs | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1558:| updates general store settings | update store name | store_settings updated | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1560:| configures tax settings | set mode=manual, rate=1900, prices_include_tax=true | tax_settings updated | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1561:| restricts settings to owner and admin roles | staff user GET /admin/settings | 403 response | authorization |
specs/09-IMPLEMENTATION-ROADMAP.md:1701:| `StoreSettings` | Per-store configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:1709:| `Theme` | Store theme entity |
specs/09-IMPLEMENTATION-ROADMAP.md:1710:| `ThemeFile` | Individual theme file |
specs/09-IMPLEMENTATION-ROADMAP.md:1711:| `ThemeSettings` | Theme configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:1713:| `NavigationMenu` | Navigation menu entity |
specs/09-IMPLEMENTATION-ROADMAP.md:1714:| `NavigationItem` | Individual navigation item |
specs/09-IMPLEMENTATION-ROADMAP.md:1720:| `TaxSettings` | Per-store tax configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:1738:| `SearchSettings` | Per-store search configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:1747:| `Admin/Layout/Sidebar` | Admin sidebar navigation |
specs/09-IMPLEMENTATION-ROADMAP.md:1759:| `Admin/Settings/Index` | Settings tabs |
specs/09-IMPLEMENTATION-ROADMAP.md:1760:| `Admin/Settings/Shipping` | Shipping zone/rate management |
specs/09-IMPLEMENTATION-ROADMAP.md:1761:| `Admin/Settings/Taxes` | Tax settings |
specs/09-IMPLEMENTATION-ROADMAP.md:1763:| `Admin/Themes/Index` | Theme list |
specs/09-IMPLEMENTATION-ROADMAP.md:1764:| `Admin/Themes/Editor` | Theme editor |
specs/09-IMPLEMENTATION-ROADMAP.md:1767:| `Admin/Navigation/Index` | Navigation menu management |
specs/09-IMPLEMENTATION-ROADMAP.md:1769:| `Admin/Search/Settings` | Search settings |
specs/09-IMPLEMENTATION-ROADMAP.md:1815:| `ThemePolicy` | Theme |
specs/09-IMPLEMENTATION-ROADMAP.md:1838:| `UpdateTaxSettingsRequest` | Validate tax settings update |
specs/09-IMPLEMENTATION-ROADMAP.md:1914:| `Admin/SettingsTest` | Admin settings feature tests |
specs/09-IMPLEMENTATION-ROADMAP.md:1979:| `NavigationService` | Menu tree building, URL resolution, caching |
specs/09-IMPLEMENTATION-ROADMAP.md:2051:6. `StoreSettingsSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md:2052:7. `TaxSettingsSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md:2059:14. `ThemeSeeder` - creates themes with files and settings
specs/09-IMPLEMENTATION-ROADMAP.md:2061:16. `NavigationSeeder` - creates navigation menus and items
specs/09-IMPLEMENTATION-ROADMAP.md:2063:18. `SearchSettingsSeeder`
specs/05-BUSINESS-LOGIC.md:41:3. If the store is suspended: return HTTP 403 Forbidden. Admin users can still view settings but cannot perform storefront-affecting mutations.
specs/05-BUSINESS-LOGIC.md:80:products, collections, customers, orders, carts, checkouts, discounts, shipping_zones, themes, pages, navigation_menus, analytics_events, analytics_daily, webhook_subscriptions, inventory_items, search_queries.
specs/05-BUSINESS-LOGIC.md:90:| Manage store settings | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md:95:| Manage themes | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md:195:- Same logic applies to collections and pages.
specs/05-BUSINESS-LOGIC.md:341:- Finds carts where status = `active` AND updated_at < (now - 14 days). The 14-day threshold is configurable via `store_settings`.
specs/05-BUSINESS-LOGIC.md:737:- The TaxCalculationRequest contains: line items (with discounted amounts), shipping amount, shipping address, store tax settings.
specs/05-BUSINESS-LOGIC.md:750:2. Use the tax rate defined in `tax_settings.config_json` for the matched zone, or the global default rate.
specs/05-BUSINESS-LOGIC.md:763:Controlled by `tax_settings.prices_include_tax` (boolean).
specs/05-BUSINESS-LOGIC.md:802:- Fallback behavior (configurable per store in `tax_settings.config_json`):
specs/05-BUSINESS-LOGIC.md:868:- Cost = fixed amount regardless of cart contents.
specs/05-BUSINESS-LOGIC.md:1047:- Configurable timeout: `store_settings.settings_json` key `bank_transfer_cancel_days`, default = 7.
specs/05-BUSINESS-LOGIC.md:1082:- Format: configurable prefix (default: `#`) stored in `store_settings.settings_json` key `order_number_prefix`.
specs/05-BUSINESS-LOGIC.md:1702:| TaxCalculationRequest | lineItems (array), shippingAmount (int), address (Address), taxSettings (object) | Input for tax providers |


server: laravel-boost

tool: search-docs

packages: laravel/framework livewire/livewire livewire/flux pestphp/pest

queries: livewire full page form validation pagination

queries: flux textarea select input checkbox table button

queries: laravel validation array json date unique

type: text

text:
# Search Results

## Testing See also
Testing | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/testing

## See also

- **[Actions](/docs/4.x/actions)** — Test component actions and interactions
- **[Forms](/docs/4.x/forms)** — Test form submissions and validation
- **[Events](/docs/4.x/events)** — Test event dispatching and listening
- **[Components](/docs/4.x/components)** — Create testable component structure

---

## Components Checkbox > Checkbox
Components Checkbox | livewire/flux@2.x | Source: https://fluxui.dev/components/checkbox#livewire/flux

    Checkbox

Select one or multiple options from a set.

<flux:field variant="inline">    <flux:checkbox wire:model="terms" />    <flux:label>I agree to the terms and conditions</flux:label>    <flux:error name="terms" /></flux:field>

---

## Redirecting Redirecting to full-page components
Redirecting | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/redirecting

## Redirecting to full-page components

Because Livewire uses Laravel's built-in redirection feature, you can use all of the redirection methods available to you in a typical Laravel application.

For example, if you are using a Livewire component as a full-page component for a route like so:

```php
Route::livewire('/posts', 'pages::show-posts');
```

You can redirect to it simply by using the route path:

```php
public function save()
{
    // ...

    $this->redirect('/posts');
}
```

---

## Quickstart Test it out
Quickstart | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/quickstart

## Test it out

With everything in place, let's test the component!

Start your Laravel development server if it's not already running:

```shell
php artisan serve
```

Visit `http://localhost:8000/post/create` in your browser (or `http://yourapp.test/post/create` if using Valet, Herd, or a similar tool).

You should see a simple form with two fields and a submit button.

**Try the following:**

1. **Test validation:** Click "Save Post" without filling in any fields. You'll see red error messages appear instantly below each field—no page reload required.

2. **Test submission:** Fill in both fields and click "Save Post". You should see a debug screen showing the values you entered.

This demonstrates Livewire's core power: reactive data binding, real-time validation, and form handling—all written in PHP without touching JavaScript.

---

## Http Tests Validation Assertions assertInvalid
Http Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md

#### assertInvalid

Assert that the response has validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session:

```php
$response->assertInvalid(['name', 'email']);
```

You may also assert that a given key has a particular validation error message. When doing so, you may provide the entire message or only a small portion of the message:

```php
$response->assertInvalid([
    'name' => 'The name field is required.',
    'email' => 'valid email address',
]);
```

---

## Lazy Full-page lazy loading
Lazy | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/lazy

## Full-page lazy loading

You can lazy load or defer full-page Livewire components using route methods.

### Lazy loading full pages

Use `->lazy()` to load the component when it enters the viewport:

```php
Route::livewire('/dashboard', 'pages::dashboard')->lazy();
```

### Deferring full pages

Use `->defer()` to load the component immediately after the page loads:

```php
Route::livewire('/dashboard', 'pages::dashboard')->defer();
```

### Disabling lazy/defer loading

If a component is lazy or deferred by default (via the `#[Lazy]` or `#[Defer]` attribute), you can opt-out using `enabled: false`:

```php
Route::livewire('/dashboard', 'pages::dashboard')->lazy(enabled: false);
Route::livewire('/dashboard', 'pages::dashboard')->defer(enabled: false);
```

---

## Components Checkbox > Buttons
Components Checkbox | livewire/flux@2.x | Source: https://fluxui.dev/components/checkbox#livewire/flux

Buttons

Button-style checkbox options that look like a toolbar. Perfect for toggle controls and feature selections where you want a more prominent visual style.

<flux:checkbox.group wire:model="features" label="Features" variant="buttons">    <flux:checkbox value="notifications" icon="bell" label="Notifications" />    <flux:checkbox value="analytics" icon="chart-bar" label="Analytics" />    <flux:checkbox value="backups" icon="cloud-arrow-up" label="Backups" /></flux:checkbox.group>

---

## Testing Creating your first test
Testing | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/testing

## Creating your first test

You can generate a test file alongside a component by appending the `--test` flag to the `make:livewire` command:

```shell
php artisan make:livewire post.create --test
```

For multi-file components, this will create a test file at `resources/views/components/post/create.test.php`:

```php
<?php

use Livewire\Livewire;

it('renders successfully', function () {
    Livewire::test('post.create')
        ->assertStatus(200);
});
```

For class-based components, this creates a PHPUnit test file at `tests/Feature/Livewire/Post/CreateTest.php`. You can convert it to Pest syntax or keep using PHPUnit—both work great with Livewire.

### Testing a page contains a component

The simplest Livewire test you can write is asserting that a given endpoint includes and successfully renders a Livewire component.

```php
it('component exists on the page', function () {
    $this->get('/posts/create')
        ->assertSeeLivewire('post.create');
});
```

> [!tip] Smoke tests provide huge value
> Tests like these are called "smoke tests"—they ensure there are no catastrophic problems in your application. Although simple, these tests provide enormous value as they require very little maintenance and give you a base level of confidence that your pages render successfully.

---

## Validation See also
Validation | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/validation

## See also

- **[Forms](/docs/4.x/forms)** — Validate form inputs with real-time feedback
- **[Properties](/docs/4.x/properties)** — Validate property values before persisting
- **[Validate Attribute](/docs/4.x/attribute-validate)** — Use #[Validate] for property validation
- **[Actions](/docs/4.x/actions)** — Validate data in action methods

---

## Attribute Title Only for full-page components
Attribute Title | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/attribute-title

## Only for full-page components

> [!info] Full-page components only
> The `#[Title]` attribute only works for full-page components accessed via routes. Regular components rendered within other views don't use titles—they inherit the parent page's title.

---

## Validation Available Validation Rules in_array_keys:_value_.*
Validation | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/validation.md

#### in_array_keys:_value_.*

The field under validation must be an array having at least one of the given _values_ as a key within the array:

```php
'config' => 'array|in_array_keys:timezone'
```

---

## Components Textarea > flux:textarea
Components Textarea | livewire/flux@2.x | Source: https://fluxui.dev/components/textarea#livewire/flux

flux:textarea

<ui-table-scroll-area>

Prop

Description

    wire:model

Binds the textarea to a Livewire property. See the wire:model documentation for more information.

    placeholder

Placeholder text displayed when the textarea is empty.

    label

Label text displayed above the textarea. When provided, wraps the textarea in a flux:field component with an adjacent flux:label component. See the field component.

    description

Help text displayed below the textarea. When provided alongside label, appears between the label and textarea within the flux:field wrapper. See the field component.

    description:trailing

The description provided will be displayed below the textarea instead of above it.

    badge

Badge text displayed at the end of the flux:label component when the label prop is provided.

    rows

Number of visible text lines. Use "auto" for automatic height adjustment. Default: 4.

    resize

Control how the textarea can be resized. Options: vertical (default), horizontal, both, none.

    invalid

If true, applies error styling to the textarea.

</ui-table-scroll-area>

<ui-table-scroll-area>

Attribute

Description

    data-flux-textarea

Applied to the textarea element for styling and identification.

</ui-table-scroll-area>

---

## Test Impact Analysis
Test Impact Analysis | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/test-impact-analysis.md

---
title: Test Impact Analysis (TIA)
description: Test Impact Analysis is a great way to drastically reduce the time it takes to run your test suite by re-running only the tests affected by your latest changes.
---

# Test Impact Analysis (TIA)

Test Impact Analysis is a great way to drastically reduce the time it takes to run your test suite by re-running only the tests affected by your latest changes. The first time you run with `--tia`, Pest records a graph of which tests depend on which files. Every run after that, Pest looks at what you changed, runs only the tests that touched those files, and replays cached results for everything else.

A typical Laravel suite that takes 15 seconds replays in under a second. Edits to a single Blade template re-run a handful of feature tests. Comment-only edits, formatter passes, and README touches re-run nothing at all.

To get started, just add the `--tia` flag to any Pest invocation:

```bash
./vendor/bin/pest --parallel --tia
```

The first run is the **baseline** — Pest enables a coverage driver (PCOV or Xdebug) and records the dependency graph as your tests execute. Expect a small overhead on this run only.

Every subsequent run is a **replay**. Pest compares your working tree against the baseline and re-runs only the tests affected by your changes:

```
Tests:    774 passed (2658 assertions, 7 affected, 767 replayed)
Duration: 0.74s
```

`affected` is the set of tests Pest actually executed. `replayed` is the set whose results were served from cache.

## How Pest Decides What To Run

For each file you've changed, Pest looks for tests that depend on it:

- **PHP source files** — your `app/` classes, controllers, models, helpers — are tracked through the coverage driver. A change to `app/Models/User.php` re-runs only the tests that touched `User`.
- **Migrations** are intersected with the tables each test queried during the baseline. A column rename in `create_users_table.php` re-runs only the tests that queried the `users` table.
- **Inertia pages** under `resources/js/Pages` re-run only the tests that server-side-rendered them.
- **Shared JS components** under `resources/js/Components`, `Layouts`, and friends re-run only the tests whose pages import them — Pest walks Vite's module graph to figure this out.
- **Blade templates** re-run only the tests that rendered them, including renders triggered by browser tests.
- **Anything else** — config files, route files, `.env`, files outside the recorded graph — falls through to a broader pattern. Editing `config/app.php` re-runs every test, because Pest can't statically prove which tests depend on it.

## Cosmetic Edits Don't Run Anything

Pest normalises file content before comparing, so cosmetic changes don't trigger any tests. PHP files have whitespace, line comments, and docblocks stripped before hashing. Blade strips `{{-- … --}}` comments. JS, TS, Vue, and Svelte files have line and block comments removed too.

The result: a comment-only edit, a Prettier reformat, a Pint pass, or a README tweak produces an identical hash, and the file never enters the changed set. Zero tests run.

## Modes

Pest supports a few flags alongside `--tia`:

| Flag | Behaviour |
|---|---|
| `--tia` | Replay if a baseline graph exists, otherwise record. |
| `--tia --fresh` | Discard any existing graph and re-record from scratch. Use this after large refactors or when the graph feels stale. |
| `--tia --refetch` | Force a CI baseline fetch even within the 24-hour cooldown after a previous failed fetch.

## Sharing The Baseline From CI

Recording the baseline locally takes minutes on large suites. Instead, you can have CI record it once per merge to `main`, and every developer downloads the result.

When Pest detects no local graph (or the local graph is out of date), it uses GitHub's CLI to download the latest successful run of a `tia-baseline.yml` workflow's `pest-tia-baseline` artifact. Pest validates the fetched graph against your project state — if it matches, it's adopted. Otherwise it's discarded and a local rebuild proceeds.

Here's a starter workflow you can drop into `.github/workflows/tia-baseline.yml`:

```yaml
name: TIA Baseline
on:
  push: { branches: [main] }
  schedule: [{ cron: '0 3 * * *' }]
  workflow_dispatch:
jobs:
  baseline:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.5', coverage: xdebug }
      - run: composer install --no-interaction --prefer-dist
      - run: ./vendor/bin/pest --parallel --tia --coverage
      - name: Stage baseline for upload
        shell: bash
        run: |
          mkdir -p .pest-tia-baseline
          cp -R "$HOME/.pest/tia"/*/. .pest-tia-baseline/
      - uses: actions/upload-artifact@v4
        with:
          name: pest-tia-baseline
          path: .pest-tia-baseline/
          retention-days: 30
```

After CI runs, every developer who runs `pest --tia` for the first time on the repo will download this baseline and start replaying immediately, paying no record cost.

## Storage

Pest stores its state at `~/.pest/tia/<project-key>/`, where the project key is derived from your normalised git remote URL — so `git@github.com:foo/bar.git` and `https://github.com/foo/bar` produce the same key. A non-git project falls back to a hash of the project's absolute path.

Sharing state per remote URL means multiple worktrees of the same repository share one cache, while unrelated projects on the same machine stay isolated.

## Custom Watch Patterns

If your project has a directory layout that doesn't match the framework defaults, you can register custom watch patterns in `tests/Pest.php`:

```php
pest()->tia()->watch([
    'config/billing/**/*.php' => ['tests/Feature/Billing'],

    //
]);
```

Each glob maps to a list of test directories; whenever a matching file changes, every test under those directories is invalidated. Use this when you have specific test directories that genuinely depend on a file Pest can't see otherwise.

---

Now that you've learned how to use Test Impact Analysis to speed up your test suite, let's discuss how to integrate Pest with your continuous integration workflow: [Continuous Integration](/docs/continuous-integration)

---

## Components Button > Full width
Components Button | livewire/flux@2.x | Source: https://fluxui.dev/components/button#livewire/flux

Full width

A button that spans the full width of the container.

<flux:button variant="primary" class="w-full">Send invite</flux:button>

---

## Validation Deprecated `[#Rule]` attribute
Validation | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/validation

## Deprecated `[#Rule]` attribute

When Livewire v3 first launched, it used the term "Rule" instead of "Validate" for it's validation attributes (`#[Rule]`).

Because of naming conflicts with Laravel rule objects, this has since been changed to `#[Validate]`. Both are supported in Livewire v3, however it is recommended that you change all occurrences of `#[Rule]` with `#[Validate]` to stay current.

---

## Installation Next Steps Laravel the Full Stack Framework
Installation | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/installation.md

### Laravel the Full Stack Framework

Laravel may serve as a full stack framework. By "full stack" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or a single-page application hybrid technology like [Inertia](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel.

If this is how you plan to use Laravel, you may want to check out our documentation on [frontend development](/docs/{{version}}/frontend), [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://livewire.laravel.com) and [Inertia](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications.

If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Vite](/docs/{{version}}/vite).

> [!NOTE]
> If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits).

---

## Components Textarea > Reference
Components Textarea | livewire/flux@2.x | Source: https://fluxui.dev/components/textarea#livewire/flux

    Reference

---

## Testing All available testing methods
Testing | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/testing

## All available testing methods

Below is a comprehensive reference of every Livewire testing method available to you:

### Setup methods

| Method                                                  | Description                                                                                |
|---------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| `Livewire::test('post.create')`                      | Test the `post.create` component |
| `Livewire::test(UpdatePost::class, ['post' => $post])`                      | Test the `UpdatePost` component with parameters passed to `mount()` |
| `Livewire::actingAs($user)`                      | Set the authenticated user for the test |
| `Livewire::withQueryParams(['search' => '...'])`                      | Set URL query parameters (ex. `?search=...`) |
| `Livewire::withCookie('name', 'value')`                      | Set a cookie for the test |
| `Livewire::withCookies(['color' => 'blue', 'name' => 'Taylor'])`                      | Set multiple cookies |
| `Livewire::withHeaders(['X-Header' => 'value'])`                      | Set custom headers |
| `Livewire::withoutLazyLoading()`                      | Disable lazy loading for all components in this test |

### Interacting with components

| Method                                                  | Description                                                                                |
|---------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| `set('title', '...')`                      | Set the `title` property to the provided value |
| `set(['title' => '...', 'content' => '...'])`                      | Set multiple properties using an array |
| `toggle('sortAsc')`                      | Toggle a boolean property between `true` and `false`  |
| `call('save')`                      | Call the `save` action/method |
| `call('remove', $postId)`                      | Call a method with parameters |
| `refresh()`                      | Trigger a component re-render |
| `dispatch('post-created')`                      | Dispatch an event from the component  |
| `dispatch('post-created', postId: $post->id)`                      | Dispatch an event with parameters |

### Assertions

| Method                                                | Description                                                                                |
|-------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `assertSet('title', '...')`                           | Assert that a property equals the provided value                                                                                |
| `assertNotSet('title', '...')`                        | Assert that a property does not equal the provided value                                                                                |
| `assertCount('posts', 3)`                             | Assert that a property contains 3 items                                                                                |
| `assertSee('...')`                             | Assert that the rendered HTML contains the provided text                                                                                |
| `assertDontSee('...')`                         | Assert that the rendered HTML does not contain the provided text                                                                                |
| `assertSeeHtml('<div>...</div>')`                     | Assert that raw HTML is present in the rendered output |
| `assertDontSeeHtml('<div>...</div>')`                 | Assert that raw HTML is not present in the rendered output                                                                                |
| `assertSeeInOrder(['first', 'second'])`                    | Assert that strings appear in order in the rendered output                                                                                |
| `assertDispatched('post-created')`                    | Assert that an event was dispatched                                                                                |
| `assertNotDispatched('post-created')`                 | Assert that an event was not dispatched                                                                                |
| `assertHasErrors('title')`                            | Assert that validation failed for a property                                                                                |
| `assertHasErrors(['title' => ['required', 'min:6']])`   | Assert that specific validation rules failed                                                                                |
| `assertHasNoErrors('title')`                          | Assert that there are no validation errors for a property                                                                                |
| `assertRedirect()`                                    | Assert that a redirect was triggered                                                                                |
| `assertRedirect('/posts')`                            | Assert a redirect to a specific URL                                                                                |
| `assertRedirectToRoute('posts.index')`       | Assert a redirect to a named route                                                                                |
| `assertNoRedirect()`                                  | Assert that no redirect was triggered                                                                                |
| `assertViewHas('posts')`                              | Assert that data was passed to the view                                                                                |
| `assertViewHas('postCount', 3)`                       | Assert that view data has a specific value                                                                                |
| `assertViewHas('posts', function ($posts) { ... })`   | Assert view data passes custom validation                                                                         |
| `assertViewIs('livewire.show-posts')`                 | Assert that a specific view was rendered                                                                                |
| `assertJs("alert('...')")`                            | Assert that a JS expression was evaluated                                                                                |
| `assertNoJs()`                                        | Assert that no JS was evaluated                                                                                |
| `assertFileDownloaded()`                              | Assert that a file download was triggered                                                                                |
| `assertFileDownloaded($filename)`                     | Assert that a specific file was downloaded                                                                                |
| `assertUnauthorized()`                                | Assert that an authorization exception was thrown (401)                                                                                |
| `assertForbidden()`                                   | Assert that access was forbidden (403)                                                                                |
| `assertStatus(500)`                                   | Assert that a specific status code was returned                                                                                |

_[Truncated]_


output:
Wall time: 4.6471 seconds
Output:
[{"type":"text","text":"# Search Results\n\n## Testing See also\nTesting | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/testing\n\n## See also\n\n- **[Actions](/docs/4.x/actions)** — Test component actions and interactions\n- **[Forms](/docs/4.x/forms)** — Test form submissions and validation\n- **[Events](/docs/4.x/events)** — Test event dispatching and listening\n- **[Components](/docs/4.x/components)** — Create testable component structure\n\n---\n\n## Components Checkbox > Checkbox\nComponents Checkbox | livewire/flux@2.x | Source: https://fluxui.dev/components/checkbox#livewire/flux\n\n

\n    Checkbox\n

Select one or multiple options from a set.

<flux:field variant=\"inline\">    <flux:checkbox wire:model=\"terms\" />    <flux:label>I agree to the terms and conditions</flux:label>    <flux:error name=\"terms\" /></flux:field>

\n\n---\n\n## Redirecting Redirecting to full-page components\nRedirecting | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/redirecting\n\n## Redirecting to full-page components\n\nBecause Livewire uses Laravel's built-in redirection feature, you can use all of the redirection methods available to you in a typical Laravel application.\n\nFor example, if you are using a Livewire component as a full-page component for a route like so:\n\n```php\nRoute::livewire('/posts', 'pages::show-posts');\n```\n\nYou can redirect to it simply by using the route path:\n\n```php\npublic function save()\n{\n    // ...\n\n    $this->redirect('/posts');\n}\n```\n\n---\n\n## Quickstart Test it out\nQuickstart | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/quickstart\n\n## Test it out\n\nWith everything in place, let's test the component!\n\nStart your Laravel development server if it's not already running:\n\n```shell\nphp artisan serve\n```\n\nVisit `http://localhost:8000/post/create` in your browser (or `http://yourapp.test/post/create` if using Valet, Herd, or a similar tool).\n\nYou should see a simple form with two fields and a submit button.\n\n**Try the following:**\n\n1. **Test validation:** Click \"Save Post\" without filling in any fields. You'll see red error messages appear instantly below each field—no page reload required.\n\n2. **Test submission:** Fill in both fields and click \"Save Post\". You should see a debug screen showing the values you entered.\n\nThis demonstrates Livewire's core power: reactive data binding, real-time validation, and form handling—all written in PHP without touching JavaScript.\n\n---\n\n## Http Tests Validation Assertions assertInvalid\nHttp Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md\n\n\n#### assertInvalid\n\nAssert that the response has validation errors for the given keys. This method may be used for asserting against responses where the validation errors are returned as a JSON structure or where the validation errors have been flashed to the session:\n\n```php\n$response->assertInvalid(['name', 'email']);\n```\n\nYou may also assert that a given key has a particular validation error message. When doing so, you may provide the entire message or only a small portion of the message:\n\n```php\n$response->assertInvalid([\n    'name' => 'The name field is required.',\n    'email' => 'valid email address',\n]);\n```\n\n---\n\n## Lazy Full-page lazy loading\nLazy | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/lazy\n\n## Full-page lazy loading\n\nYou can lazy load or defer full-page Livewire components using route methods.\n\n### Lazy loading full pages\n\nUse `->lazy()` to load the component when it enters the viewport:\n\n```php\nRoute::livewire('/dashboard', 'pages::dashboard')->lazy();\n```\n\n### Deferring full pages\n\nUse `->defer()` to load the component immediately after the page loads:\n\n```php\nRoute::livewire('/dashboard', 'pages::dashboard')->defer();\n```\n\n### Disabling lazy/defer loading\n\nIf a component is lazy or deferred by default (via the `#[Lazy]` or `#[Defer]` attribute), you can opt-out using `enabled: false`:\n\n```php\nRoute::livewire('/dashboard', 'pages::dashboard')->lazy(enabled: false);\nRoute::livewire('/dashboard', 'pages::dashboard')->defer(enabled: false);\n```\n\n---\n\n## Components Checkbox > Buttons\nComponents Checkbox | livewire/flux@2.x | Source: https://fluxui.dev/components/checkbox#livewire/flux\n\n
Buttons

Button-style checkbox options that look like a toolbar. Perfect for toggle controls and feature selections where you want a more prominent visual style.

<flux:checkbox.group wire:model=\"features\" label=\"Features\" variant=\"buttons\">    <flux:checkbox value=\"notifications\" icon=\"bell\" label=\"Notifications\" />    <flux:checkbox value=\"analytics\" icon=\"chart-bar\" label=\"Analytics\" />    <flux:checkbox value=\"backups\" icon=\"cloud-arrow-up\" label=\"Backups\" /></flux:checkbox.group>

\n\n---\n\n## Testing Creating your first test\nTesting | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/testing\n\n## Creating your first test\n\nYou can generate a test file alongside a component by appending the `--test` flag to the `make:livewire` command:\n\n```shell\nphp artisan make:livewire post.create --test\n```\n\nFor multi-file components, this will create a test file at `resources/views/components/post/create.test.php`:\n\n```php\n<?php\n\nuse Livewire\\Livewire;\n\nit('renders successfully', function () {\n    Livewire::test('post.create')\n        ->assertStatus(200);\n});\n```\n\nFor class-based components, this creates a PHPUnit test file at `tests/Feature/Livewire/Post/CreateTest.php`. You can convert it to Pest syntax or keep using PHPUnit—both work great with Livewire.\n\n### Testing a page contains a component\n\nThe simplest Livewire test you can write is asserting that a given endpoint includes and successfully renders a Livewire component.\n\n```php\nit('component exists on the page', function () {\n    $this->get('/posts/create')\n        ->assertSeeLivewire('post.create');\n});\n```\n\n> [!tip] Smoke tests provide huge value\n> Tests like these are called \"smoke tests\"—they ensure there are no catastrophic problems in your application. Although simple, these tests provide enormous value as they require very little maintenance and give you a base level of confidence that your pages render successfully.\n\n---\n\n## Validation See also\nValidation | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/validation\n\n## See also\n\n- **[Forms](/docs/4.x/forms)** — Validate form inputs with real-time feedback\n- **[Properties](/docs/4.x/properties)** — Validate property values before persisting\n- **[Validate Attribute](/docs/4.x/attribute-validate)** — Use #[Validate] for property validation\n- **[Actions](/docs/4.x/actions)** — Validate data in action methods\n\n---\n\n## Attribute Title Only for full-page components\nAttribute Title | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/attribute-title\n\n## Only for full-page components\n\n> [!info] Full-page components only\n> The `#[Title]` attribute only works for full-page components accessed via routes. Regular components rendered within other views don't use titles—they inherit the parent page's title.\n\n---\n\n## Validation Available Validation Rules in_array_keys:_value_.*\nValidation | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/validation.md\n\n\n#### in_array_keys:_value_.*\n\nThe field under validation must be an array having at least one of the given _values_ as a key within the array:\n\n```php\n'config' => 'array|in_array_keys:timezone'\n```\n\n---\n\n## Components Textarea > flux:textarea\nComponents Textarea | livewire/flux@2.x | Source: https://fluxui.dev/components/textarea#livewire/flux\n\n
flux:textarea

<ui-table-scroll-area>

Prop

Description

\n    wire:model\n

Binds the textarea to a Livewire property. See the wire:model documentation for more information.

\n    placeholder\n

Placeholder text displayed when the textarea is empty.

\n    label\n

Label text displayed above the textarea. When provided, wraps the textarea in a flux:field component with an adjacent flux:label component. See the field component.

\n    description\n

Help text displayed below the textarea. When provided alongside label, appears between the label and textarea within the flux:field wrapper. See the field component.

\n    description:trailing\n

The description provided will be displayed below the textarea instead of above it.

\n    badge\n

Badge text displayed at the end of the flux:label component when the label prop is provided.

\n    rows\n

Number of visible text lines. Use \"auto\" for automatic height adjustment. Default: 4.

\n    resize\n

Control how the textarea can be resized. Options: vertical (default), horizontal, both, none.

\n    invalid\n

If true, applies error styling to the textarea.

</ui-table-scroll-area>

<ui-table-scroll-area>

Attribute

Description

\n    data-flux-textarea\n

Applied to the textarea element for styling and identification.

</ui-table-scroll-area>

\n\n---\n\n## Test Impact Analysis\nTest Impact Analysis | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/test-impact-analysis.md\n\n---\ntitle: Test Impact Analysis (TIA)\ndescription: Test Impact Analysis is a great way to drastically reduce the time it takes to run your test suite by re-running only the tests affected by your latest changes.\n---\n\n# Test Impact Analysis (TIA)\n\nTest Impact Analysis is a great way to drastically reduce the time it takes to run your test suite by re-running only the tests affected by your latest changes. The first time you run with `--tia`, Pest records a graph of which tests depend on which files. Every run after that, Pest looks at what you changed, runs only the tests that touched those files, and replays cached results for everything else.\n\nA typical Laravel suite that takes 15 seconds replays in under a second. Edits to a single Blade template re-run a handful of feature tests. Comment-only edits, formatter passes, and README touches re-run nothing at all.\n\nTo get started, just add the `--tia` flag to any Pest invocation:\n\n```bash\n./vendor/bin/pest --parallel --tia\n```\n\nThe first run is the **baseline** — Pest enables a coverage driver (PCOV or Xdebug) and records the dependency graph as your tests execute. Expect a small overhead on this run only.\n\nEvery subsequent run is a **replay**. Pest compares your working tree against the baseline and re-runs only the tests affected by your changes:\n\n```\nTests:    774 passed (2658 assertions, 7 affected, 767 replayed)\nDuration: 0.74s\n```\n\n`affected` is the set of tests Pest actually executed. `replayed` is the set whose results were served from cache.\n\n## How Pest Decides What To Run\n\nFor each file you've changed, Pest looks for tests that depend on it:\n\n- **PHP source files** — your `app/` classes, controllers, models, helpers — are tracked through the coverage driver. A change to `app/Models/User.php` re-runs only the tests that touched `User`.\n- **Migrations** are intersected with the tables each test queried during the baseline. A column rename in `create_users_table.php` re-runs only the tests that queried the `users` table.\n- **Inertia pages** under `resources/js/Pages` re-run only the tests that server-side-rendered them.\n- **Shared JS components** under `resources/js/Components`, `Layouts`, and friends re-run only the tests whose pages import them — Pest walks Vite's module graph to figure this out.\n- **Blade templates** re-run only the tests that rendered them, including renders triggered by browser tests.\n- **Anything else** — config files, route files, `.env`, files outside the recorded graph — falls through to a broader pattern. Editing `config/app.php` re-runs every test, because Pest can't statically prove which tests depend on it.\n\n## Cosmetic Edits Don't Run Anything\n\nPest normalises file content before comparing, so cosmetic changes don't trigger any tests. PHP files have whitespace, line comments, and docblocks stripped before hashing. Blade strips `{{-- … --}}` comments. JS, TS, Vue, and Svelte files have line and block comments removed too.\n\nThe result: a comment-only edit, a Prettier reformat, a Pint pass, or a README tweak produces an identical hash, and the file never enters the changed set. Zero tests run.\n\n## Modes\n\nPest supports a few flags alongside `--tia`:\n\n| Flag | Behaviour |\n|---|---|\n| `--tia` | Replay if a baseline graph exists, otherwise record. |\n| `--tia --fresh` | Discard any existing graph and re-record from scratch. Use this after large refactors or when the graph feels stale. |\n| `--tia --refetch` | Force a CI baseline fetch even within the 24-hour cooldown after a previous failed fetch.\n\n## Sharing The Baseline From CI\n\nRecording the baseline locally takes minutes on large suites. Instead, you can have CI record it once per merge to `main`, and every developer downloads the result.\n\nWhen Pest detects no local graph (or the local graph is out of date), it uses GitHub's CLI to download the latest successful run of a `tia-baseline.yml` workflow's `pest-tia-baseline` artifact. Pest validates the fetched graph against your project state — if it matches, it's adopted. Otherwise it's discarded and a local rebuild proceeds.\n\nHere's a starter workflow you can drop into `.github/workflows/tia-baseline.yml`:\n\n```yaml\nname: TIA Baseline\non:\n  push: { branches: [main] }\n  schedule: [{ cron: '0 3 * * *' }]\n  workflow_dispatch:\njobs:\n  baseline:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with: { fetch-depth: 0 }\n      - uses: shivammathur/setup-php@v2\n        with: { php-version: '8.5', coverage: xdebug }\n      - run: composer install --no-interaction --prefer-dist\n      - run: ./vendor/bin/pest --parallel --tia --coverage\n      - name: Stage baseline for upload\n        shell: bash\n        run: |\n          mkdir -p .pest-tia-baseline\n          cp -R \"$HOME/.pest/tia\"/*/. .pest-tia-baseline/\n      - uses: actions/upload-artifact@v4\n        with:\n          name: pest-tia-baseline\n          path: .pest-tia-baseline/\n          retention-days: 30\n```\n\nAfter CI runs, every developer who runs `pest --tia` for the first time on the repo will download this baseline and start replaying immediately, paying no record cost.\n\n## Storage\n\nPest stores its state at `~/.pest/tia/<project-key>/`, where the project key is derived from your normalised git remote URL — so `git@github.com:foo/bar.git` and `https://github.com/foo/bar` produce the same key. A non-git project falls back to a hash of the project's absolute path.\n\nSharing state per remote URL means multiple worktrees of the same repository share one cache, while unrelated projects on the same machine stay isolated.\n\n## Custom Watch Patterns\n\nIf your project has a directory layout that doesn't match the framework defaults, you can register custom watch patterns in `tests/Pest.php`:\n\n```php\npest()->tia()->watch([\n    'config/billing/**/*.php' => ['tests/Feature/Billing'],\n\n    //\n]);\n```\n\nEach glob maps to a list of test directories; whenever a matching file changes, every test under those directories is invalidated. Use this when you have specific test directories that genuinely depend on a file Pest can't see otherwise.\n\n---\n\nNow that you've learned how to use Test Impact Analysis to speed up your test suite, let's discuss how to integrate Pest with your continuous integration workflow: [Continuous Integration](/docs/continuous-integration)\n\n\n---\n\n## Components Button > Full width\nComponents Button | livewire/flux@2.x | Source: https://fluxui.dev/components/button#livewire/flux\n\n
Full width

A button that spans the full width of the container.

<flux:button variant=\"primary\" class=\"w-full\">Send invite</flux:button>

\n\n---\n\n## Validation Deprecated `[#Rule]` attribute\nValidation | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/validation\n\n## Deprecated `[#Rule]` attribute\n\nWhen Livewire v3 first launched, it used the term \"Rule\" instead of \"Validate\" for it's validation attributes (`#[Rule]`).\n\nBecause of naming conflicts with Laravel rule objects, this has since been changed to `#[Validate]`. Both are supported in Livewire v3, however it is recommended that you change all occurrences of `#[Rule]` with `#[Validate]` to stay current.\n\n---\n\n## Installation Next Steps Laravel the Full Stack Framework\nInstallation | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/installation.md\n\n\n### Laravel the Full Stack Framework\n\nLaravel may serve as a full stack framework. By \"full stack\" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or a single-page application hybrid technology like [Inertia](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel.\n\nIf this is how you plan to use Laravel, you may want to check out our documentation on [frontend development](/docs/{{version}}/frontend), [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://livewire.laravel.com) and [Inertia](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications.\n\nIf you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Vite](/docs/{{version}}/vite).\n\n> [!NOTE]\n> If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits).\n\n---\n\n## Components Textarea > Reference\nComponents Textarea | livewire/flux@2.x | Source: https://fluxui.dev/components/textarea#livewire/flux\n\n
\n    Reference\n

\n\n---\n\n## Testing All available testing methods\nTesting | livewire/livewire@4.x | Source: https://livewire.laravel.com/docs/4.x/testing\n\n## All available testing methods\n\nBelow is a comprehensive reference of every Livewire testing method available to you:\n\n### Setup methods\n\n| Method                                                  | Description                                                                                |\n|---------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|\n| `Livewire::test('post.create')`                      | Test the `post.create` component |\n| `Livewire::test(UpdatePost::class, ['post' => $post])`                      | Test the `UpdatePost` component with parameters passed to `mount()` |\n| `Livewire::actingAs($user)`                      | Set the authenticated user for the test |\n| `Livewire::withQueryParams(['search' => '...'])`                      | Set URL query parameters (ex. `?search=...`) |\n| `Livewire::withCookie('name', 'value')`                      | Set a cookie for the test |\n| `Livewire::withCookies(['color' => 'blue', 'name' => 'Taylor'])`                      | Set multiple cookies |\n| `Livewire::withHeaders(['X-Header' => 'value'])`                      | Set custom headers |\n| `Livewire::withoutLazyLoading()`                      | Disable lazy loading for all components in this test |\n\n### Interacting with components\n\n| Method                                                  | Description                                                                                |\n|---------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|\n| `set('title', '...')`                      | Set the `title` property to the provided value |\n| `set(['title' => '...', 'content' => '...'])`                      | Set multiple properties using an array |\n| `toggle('sortAsc')`                      | Toggle a boolean property between `true` and `false`  |\n| `call('save')`                      | Call the `save` action/method |\n| `call('remove', $postId)`                      | Call a method with parameters |\n| `refresh()`                      | Trigger a component re-render |\n| `dispatch('post-created')`                      | Dispatch an event from the component  |\n| `dispatch('post-created', postId: $post->id)`                      | Dispatch an event with parameters |\n\n### Assertions\n\n| Method                                                | Description                                                                                |\n|-------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `assertSet('title', '...')`                           | Assert that a property equals the provided value                                                                                |\n| `assertNotSet('title', '...')`                        | Assert that a property does not equal the provided value                                                                                |\n| `assertCount('posts', 3)`                             | Assert that a property contains 3 items                                                                                |\n| `assertSee('...')`                             | Assert that the rendered HTML contains the provided text                                                                                |\n| `assertDontSee('...')`                         | Assert that the rendered HTML does not contain the provided text                                                                                |\n| `assertSeeHtml('<div>...</div>')`                     | Assert that raw HTML is present in the rendered output |\n| `assertDontSeeHtml('<div>...</div>')`                 | Assert that raw HTML is not present in the rendered output                                                                                |\n| `assertSeeInOrder(['first', 'second'])`                    | Assert that strings appear in order in the rendered output                                                                                |\n| `assertDispatched('post-created')`                    | Assert that an event was dispatched                                                                                |\n| `assertNotDispatched('post-created')`                 | Assert that an event was not dispatched                                                                                |\n| `assertHasErrors('title')`                            | Assert that validation failed for a property                                                                                |\n| `assertHasErrors(['title' => ['required', 'min:6']])`   | Assert that specific validation rules failed                                                                                |\n| `assertHasNoErrors('title')`                          | Assert that there are no validation errors for a property                                                                                |\n| `assertRedirect()`                                    | Assert that a redirect was triggered                                                                                |\n| `assertRedirect('/posts')`                            | Assert a redirect to a specific URL                                                                                |\n| `assertRedirectToRoute('posts.index')`       | Assert a redirect to a named route                                                                                |\n| `assertNoRedirect()`                                  | Assert that no redirect was triggered                                                                                |\n| `assertViewHas('posts')`                              | Assert that data was passed to the view                                                                                |\n| `assertViewHas('postCount', 3)`                       | Assert that view data has a specific value                                                                                |\n| `assertViewHas('posts', function ($posts) { ... })`   | Assert view data passes custom validation                                                                         |\n| `assertViewIs('livewire.show-posts')`                 | Assert that a specific view was rendered                                                                                |\n| `assertJs(\"alert('...')\")`                            | Assert that a JS expression was evaluated                                                                                |\n| `assertNoJs()`                                        | Assert that no JS was evaluated                                                                                |\n| `assertFileDownloaded()`                              | Assert that a file download was triggered                                                                                |\n| `assertFileDownloaded($filename)`                     | Assert that a specific file was downloaded                                                                                |\n| `assertUnauthorized()`                                | Assert that an authorization exception was thrown (401)                                                                                |\n| `assertForbidden()`                                   | Assert that access was forbidden (403)                                                                                |\n| `assertStatus(500)`                                   | Assert that a specific status code was returned                                                                                |\n\n_[Truncated]_"}]


tool: database-schema

engine: sqlite

key: varchar

value: text

expiration: integer

owner: varchar

cart_id: integer

variant_id: integer

quantity: integer

unit_price_amount: integer

line_subtotal_amount: integer

line_discount_amount: integer

line_total_amount: integer

store_id: integer

customer_id: integer

currency: varchar

cart_version: integer

status: varchar

created_at: datetime

updated_at: datetime

payment_method: varchar

email: varchar

shipping_address_json: text

billing_address_json: text

shipping_method_id: integer

discount_code: varchar

tax_provider_snapshot_json: text

totals_json: text

expires_at: datetime

collection_id: integer

product_id: integer

position: integer

title: varchar

handle: varchar

description_html: text

type: varchar

label: varchar

address_json: text

token: varchar

varchar

password_hash: varchar

code: varchar

value_type: varchar

value_amount: integer

starts_at: datetime

ends_at: datetime

usage_limit: integer

usage_count: integer

rules_json: text

connection: text

queue: text

payload: text

exception: text

failed_at: datetime

fulfillment_id: integer

order_line_id: integer

order_id: integer

tracking_company: varchar

tracking_number: varchar

tracking_url: varchar

shipped_at: datetime

delivered_at: datetime

quantity_on_hand: integer

quantity_reserved: integer

policy: varchar

total_jobs: integer

pending_jobs: integer

failed_jobs: integer

failed_job_ids: text

title: varchar

title: varchar

title: varchar


message: The specs point at a fairly large admin area: settings, themes/editor, pages, and navigation. I’m narrowing this slice to a coherent first pass that matches the existing data layer and can be fully tested and smoked in one commit.

phase: commentary


type: message

role: assistant

text: The specs point at a fairly large admin area: settings, themes/editor, pages, and navigation. I’m narrowing this slice to a coherent first pass that matches the existing data layer and can be fully tested and smoked in one commit.


cmd: sed -n '1378,1915p' specs/03-ADMIN-UI.md


cmd: sed -n '830,850p' specs/09-IMPLEMENTATION-ROADMAP.md


cmd: sed -n '1550,1565p' specs/09-IMPLEMENTATION-ROADMAP.md


cmd: rg --files app/Models app/Policies app/Services database/factories database/seeders | rg "StoreSettings|Theme|Page|Navigation|TaxSettings|Shipping"


command: sed -n '830,850p' specs/09-IMPLEMENTATION-ROADMAP.md

type: read

aggregated_output:
**Files to create:**

| Component | Description |
|-----------|-------------|
| `Admin\Collections\Index` | Collection list |
| `Admin\Collections\Form` | Shared form component for create/edit collection with product picker, reorder products |
| `Admin\Customers\Index` | Customer list with search |
| `Admin\Customers\Show` | Customer detail: info, orders, addresses |
| `Admin\Discounts\Index` | Discount list with status/type filters |
| `Admin\Discounts\Form` | Shared form component for create/edit discount: code, type, value, dates, rules |
| `Admin\Settings\Index` | Tabbed settings: General, Domains, Shipping, Taxes, Checkout, Notifications |
| `Admin\Settings\Shipping` | Shipping zones CRUD, rates CRUD per zone |
| `Admin\Settings\Taxes` | Tax mode toggle, manual rate configuration |
| `Admin\Themes\Index` | Theme cards with Publish/Duplicate/Delete actions |
| `Admin\Themes\Editor` | Left: sections. Center: preview. Right: settings. |
| `Admin\Pages\Index` | Content pages list |
| `Admin\Pages\Form` | Shared form component for create/edit page with rich text editor |
| `Admin\Navigation\Index` | Menu management with drag-and-drop item ordering |
| `Admin\Analytics\Index` | Sales chart, traffic, funnel visualization, date range |
| `Admin\Search\Settings` | Synonyms, stop words, reindex button |
| `Admin\Apps\Index` | App directory |

status: completed


command: sed -n '1378,1915p' specs/03-ADMIN-UI.md

aggregated_output:
## 11. Settings Pages

### 11.1 General Settings

**Route:** `GET /admin/settings`

**Livewire component:** `App\Livewire\Admin\Settings\General`

**Blade view:** `resources/views/livewire/admin/settings/general.blade.php`

#### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `storeName` | `string` | Store display name |
| `storeHandle` | `string` | Store handle (read-only or with warning) |
| `defaultCurrency` | `string` | Currency code (e.g., "EUR") |
| `defaultLocale` | `string` | Locale code (e.g., "en") |
| `timezone` | `string` | Timezone string |

| Action | Behavior |
|--------|----------|
| `save()` | Validates and saves store settings |

#### Layout

Two-column layout per section: left side has section label and description, right side has form fields.

**Store details section:**
- Left: Heading "Store details" + description "Basic information about your store."
- Right: Store name input, Store handle input (disabled, with description: "The store handle cannot be changed after creation.")

Separator between sections.

**Defaults section:**
- Left: Heading "Defaults" + description "Currency, language, and timezone settings."
- Right:
  - Default currency: Select (EUR/USD/GBP and more)
  - Default locale: Select (English/German/French and more)
  - Timezone: Select (populated from PHP timezone list)

Save button at the bottom (primary variant, right-aligned).

### 11.2 Domains Settings

Domains is a tab within the Settings page (`/admin/settings`), not a separate route. The Settings page has tabs: General, Domains, Shipping, Taxes, Checkout, Notifications.

**Livewire component:** `App\Livewire\Admin\Settings\Domains`

**Blade view:** `resources/views/livewire/admin/settings/domains.blade.php`

#### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `domains` | `Collection` | Store domains |
| `newHostname` | `string` | Hostname for new domain |
| `newType` | `string` | Type for new domain: `storefront`, `admin`, `api` |

| Action | Behavior |
|--------|----------|
| `addDomain()` | Creates a new domain record |
| `removeDomain(int $domainId)` | Removes a domain |
| `setPrimary(int $domainId)` | Sets domain as primary |

#### Layout

- Heading "Domains"
- Table of existing domains:

| Column | Content |
|--------|---------|
| Hostname | Domain hostname |
| Type | Flux badge: storefront/admin/api |
| Primary | Flux badge (green) "Primary" if `is_primary` |
| TLS | Flux badge showing TLS mode |
| Actions | "Set Primary" button, "Delete" button |

- "Add domain" button opens a modal

#### Add Domain Modal

| Property | Value |
|----------|-------|
| Modal name | `add-domain` |
| Max width | md |
| Heading | "Add domain" |

**Form fields:**
- Hostname input (placeholder: "shop.example.com")
- Type select: Storefront, Admin, API
- Cancel button (ghost) and "Add domain" button (primary)

### 11.3 Shipping Settings

**Route:** `GET /admin/settings/shipping`

**Livewire component:** `App\Livewire\Admin\Settings\Shipping`

**Blade view:** `resources/views/livewire/admin/settings/shipping.blade.php`

#### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `zones` | `Collection` | Shipping zones with rates |
| `editingZone` | `?ShippingZone` | Zone being edited |
| `zoneName` | `string` | Name for zone form |
| `zoneCountries` | `array` | Selected countries for zone |
| `editingRate` | `?ShippingRate` | Rate being edited |
| `rateName` | `string` | Rate name |
| `rateType` | `string` | `flat`, `weight`, `price`, `carrier` |
| `rateConfig` | `array` | Rate configuration (depends on type) |
| `rateActive` | `bool` | Whether rate is active |
| `testAddress` | `array` | Address fields for test tool |
| `testResult` | `?array` | Result of test address lookup |

| Action | Behavior |
|--------|----------|
| `openZoneModal(?ShippingZone $zone)` | Opens zone create/edit modal |
| `saveZone()` | Saves shipping zone |
| `deleteZone(int $zoneId)` | Deletes a shipping zone |
| `openRateModal(int $zoneId, ?ShippingRate $rate)` | Opens rate create/edit modal |
| `saveRate()` | Saves shipping rate |
| `deleteRate(int $rateId)` | Deletes a rate |
| `testShippingAddress()` | Tests which zone/rate matches a given address |

#### Layout Wireframe

```
+==================================================================+
| Shipping                                         [+ Add zone]    |
+==================================================================+
|                                                                   |
| +--------------------------------------------------------------+ |
| | ZONE: Domestic                            [Edit] [Delete]    | |
| | Countries: US, CA                                             | |
| |                                                               | |
| | Name      | Type    | Config           | Active | Actions    | |
| |-----------|---------|------------------|--------|-------…11351 chars truncated…ighted)
```

**Full-width layout** (no standard admin shell padding). Three-panel layout:

**Top toolbar:** A horizontal bar with:
- "Back to themes" button (ghost, with left arrow icon) linking to themes index
- "Save" ghost button and "Save and publish" primary button

**Left panel (264px):** Section list
- Heading "Sections"
- Vertical list of section buttons
- Active section gets highlighted background and bold text
- Clicking a section calls `selectSection()`

**Center panel (flexible):** Live preview
- Background color behind the iframe
- Full-height iframe loading the storefront preview URL
- Iframe has rounded border and shadow

**Right panel (320px):** Settings form
- Shows settings for the selected section
- Heading with section label
- Separator
- Dynamic form fields based on section field definitions:

| Field Type | UI Element |
|-----------|------------|
| `text` | Flux input (live-debounced at 500ms) |
| `textarea` | Flux textarea (live-debounced at 500ms, 3 rows) |
| `color` | Native color picker input |
| `select` | Flux select with options from field definition |
| `checkbox` | Flux checkbox |

- When no section is selected: centered text "Select a section to edit its settings."

---

## 13. Pages Management

### 13.1 Pages List

**Route:** `GET /admin/pages`

**Livewire component:** `App\Livewire\Admin\Pages\Index`

**Blade view:** `resources/views/livewire/admin/pages/index.blade.php`

#### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `search` | `string` | Search by page title |

| Computed Property | Purpose |
|-------------------|---------|
| `pages` | Paginated pages for the current store |

#### Layout

- Heading "Pages" with "Add page" primary button
- Search input
- Table columns: Title (link to edit), Handle, Status (Flux badge), Updated date
- Pagination

### 13.2 Page Form

**Route:** `GET /admin/pages/create` and `GET /admin/pages/{page}/edit`

**Livewire component:** `App\Livewire\Admin\Pages\Form`

**Blade view:** `resources/views/livewire/admin/pages/form.blade.php`

#### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `page` | `?Page` | Page being edited |
| `title` | `string` | Page title |
| `handle` | `string` | URL handle |
| `bodyHtml` | `string` | Page body content (HTML) |
| `status` | `string` | `draft` or `active` |
| `publishedAt` | `?string` | Publish date |

| Action | Behavior |
|--------|----------|
| `save()` | Validates and saves the page |
| `deletePage()` | Deletes the page |

#### Layout (Two-Column)

**Left column (2/3):**
- Title: Flux input
- Handle: Flux input
- Body: Flux textarea (16 rows)

**Right column (1/3):**
- Status: Flux select with Draft/Active
- Published at: Datetime-local input

Sticky save bar (same pattern as other forms).

---

## 14. Navigation Management

**Route:** `GET /admin/navigation`

**Livewire component:** `App\Livewire\Admin\Navigation\Index`

**Blade view:** `resources/views/livewire/admin/navigation/index.blade.php`

### Component Requirements

| Property | Type | Purpose |
|----------|------|---------|
| `menus` | `Collection` | Navigation menus (Main Menu, Footer Menu) |
| `editingMenu` | `?NavigationMenu` | Menu being edited |
| `menuItems` | `array` | Nested array of menu items for the editing menu |
| `editingItem` | `?array` | Item currently being edited in the modal |
| `editingItemIndex` | `?int` | Index of item being edited |
| `itemLabel` | `string` | Item label |
| `itemType` | `string` | `link`, `page`, `collection`, `product` |
| `itemUrl` | `string` | URL if type is link |
| `itemResourceId` | `?int` | Resource ID if type is page/collection/product |

| Action | Behavior |
|--------|----------|
| `selectMenu(int $menuId)` | Loads a menu for editing |
| `addItem()` | Opens the item form modal in create mode |
| `editItem(int $index)` | Opens the item form modal in edit mode |
| `saveItem()` | Saves the item (add or update) |
| `removeItem(int $index)` | Removes an item |
| `reorderItems(array $order)` | Updates item positions (supports nesting) |
| `saveMenu()` | Persists all menu items |

### Layout Wireframe

```
+==================================================================+
| Navigation                                                        |
+==================================================================+
|                                                                   |
| MENUS                                                             |
| +----------------------------+  +----------------------------+   |
| | Main Menu        [Edit]    |  | Footer Menu       [Edit]   |   |
| +----------------------------+  +----------------------------+   |
|                                                                   |
| MENU EDITOR (shown when a menu is selected)                      |
| +--------------------------------------------------------------+ |
| | Main Menu                                    [+ Add item]    | |