Bug fixes (browser console + UX):
1. Photo /photo: "Alpine Expression Error: preview is not defined" during
Lazy hydration. Placeholder now pre-declares the same x-data scope as
the real view (file, preview, status) so Alpine bindings on children
resolve from first paint and survive the morph.
2. Tasks Start button: "Unable to resolve dependency [Parameter #0 $component]
in LivewireUI\\Modal\\Modal". Livewire 4 server-side dispatch with
->to('livewire-ui-modal') drops named payload args. Switched to
$this->js("Livewire.dispatch('openModal', {...})") which emits a
browser-side dispatch that wire-elements reliably picks up.
3. Skeleton was generic across all pages. Replaced single
components.skeleton.page placeholder with page-specific placeholders that
mirror each view's actual layout (hero header, stat-tile grid,
subject-card grid, table rows, sub-nav, etc.).
New skeleton primitives:
components/skeleton/header.blade.php
components/skeleton/stat-tile.blade.php
components/skeleton/subject-card.blade.php
components/skeleton/table-row.blade.php
components/skeleton/card.blade.php
Per-page placeholders:
livewire/{dashboard,subjects,tasks,photo-help,rewards,progress,
notifications,settings}/placeholder.blade.php
Each placeholder() now returns its feature-local view; each placeholder
has a unique data-testid (dashboard-skeleton, tasks-skeleton, etc.)
Tests:
- PerPageSkeletonTest (18 assertions): every page returns its own
placeholder view, each has unique testid, photo-help placeholder
declares preview in x-data.
- TasksStartButtonTest updated: assertScriptRan -> assertion against
Livewire effects xjs key (correct Livewire 4 API).
- LazySkeletonTest removed (superseded by PerPageSkeletonTest).
Verification:
- 156 tests passed, 0 failed, 0 skipped (390 assertions)
- All 8 routes return 302 unauth (auth middleware correct)
- All 7 modal aliases resolve correctly via livewire.finder
- Build artifacts: manifest.json, logo/favicon.svg, manifest.webmanifest
all reachable
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>