.checkSlug() triggered Livewire re-render on every keystroke,
causing the editor to scroll to top. #[Renderless] skips re-render
while still returning the bool result to Alpine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Edit view: debounced $wire.checkSlug() call on input (350ms)
- Shows red error text + red border immediately when slug taken
- Hides help text when error shown
- Edit component: checkSlug() queries DB excluding current page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migration: add UNIQUE(workspace_id, slug) to bio_pages
Old (subdomain_host, slug) unique allowed MySQL NULL bypass —
two free-plan bios with NULL subdomain_host + same slug were not caught
- Edit::save(): validate slug uniqueness before update, dispatch toast on conflict
- Edit::save(): validate slug format (alphanumeric + dash/underscore, max 64)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- All create modals now validate→create→dispatch(kebab-case event)→toast→close
- All edit/delete modals dispatch kebab-case events to Index classes
- Index components listen via #[On] and call resetPage() to refresh
- QR Code save() validates url field conditionally per type
- Tests updated to assert kebab-case event names
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Links Livewire page (Index component + blade view) with live search,
status filter, paginated table, and delete action using DeleteLink for
cache invalidation. Register workspace-scoped route group under
w/{workspace} with ResolveWorkspace middleware. Add stub Livewire pages
for QrCodes, Bio, Analytics, Domains, Settings, and Billing with a shared
coming-soon view.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>