Replace React/Inertia with Blade + Livewire 4 #52
Labels
No labels
bug
duplicate
enhancement
good first issue
help wanted
question
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lvl0/incr#52
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The stack swap. Deliberately sequenced last: after #47–#51 the frontend is one display component plus a dialog, so this is a rewrite of ~2 components rather than a port of a codebase.
Rationale
For a page that is one number and a dialog, the React starter kit's baseline (layout system, component library, SSR, a TS type mirroring every model) is pure overhead.
wire:click="increment"is the entire feature spec as one line of markup.Mirror ffr's setup — same Livewire 4 major, same Tailwind 4 + Vite toolchain — so patterns lift directly between the two projects.
Scope
Add:
livewire/livewire— pin to the same major ffr uses (^4.0)app/Livewire/Counter.php—increment(),setCount(int),#[Validate]on the dialog inputresources/views/livewire/counter.blade.phpRemove:
inertiajs/inertia-laravel,tightenco/ziggy(composer)@inertiajs/react,react,react-dom,@types/react,@types/react-dom(npm)app/Http/Middleware/HandleInertiaRequests.phpandHandleAppearance.php, plus both registrations inbootstrap/app.php:20-21resources/js/app.tsx,resources/js/ssr.tsx,build:ssrscript,ssrentry invite.config.tsresources/js/types/,resources/js/hooks/, remainingcomponents/eslint.config.js,tsconfig.json,components.json,.prettierrcif nothing else uses themPort, don't rewrite: the black/red monospace terminal aesthetic is the asset here.
LedDisplay.tsxis only 67 lines but its Tailwind classes carry the whole look — copy the markup and classes into Blade rather than restyling from scratch.Acceptance criteria
package.jsondependencies reduced to roughly Tailwind + Vite, as in ffrAdditional scope: production Dockerfile will break
docker/production/Dockerfilecopies three files this ticket deletes:Removing them without updating the Dockerfile breaks the production image build — and it would fail at release time, not in CI, since
.forgejo/workflows/build.ymlonly runs onmainpushes andv*tags.The frontend-builder stage will also need revisiting: it exists to run the Vite/React build, which after the Livewire migration is only Tailwind. Worth checking whether the multi-stage split still earns its keep.
Add to acceptance criteria:
docker/production/Dockerfilebuilds successfully after the deletionslinux/amd64,linux/arm64) production build still worksPHPStan level 8 finding in a file this ticket deletes
From the #55 setup — trying level 8 surfaced two errors in
HandleInertiaRequests.php, which this ticket removes along with Inertia:No action needed beyond the deletion already scoped here.
Once this and #53 land, level 8 is within reach — the only remaining errors are five
Model::first()nullable-return cases in tests, each a one-line fix. Noted on #53.Done —
6ce470344 files, −7,456 lines net. React and Inertia are gone.
app/PHP filesroutes/web.phpThe whole application is now:
app/Livewire/Counter.php,app/Models/Tracker.php,resources/views/livewire/counter.blade.php,resources/views/layouts/app.blade.php, and 24 lines of CSS.Added
app/Livewire/Counter.php—mount(),initialise(),increment(),edit(),save(),cancel()resources/views/livewire/counter.blade.php— onboarding and counter in one viewresources/views/layouts/app.blade.php— mirrors ffr's->layout('layouts.app')conventionRemoved
All of
resources/js/(19 files),app.blade.php,HandleInertiaRequests,HandleAppearance,config/inertia.php,eslint.config.js,tsconfig.json,components.json,.prettierrc,.prettierignore, thedev:ssrscript,inertiajs/inertia-laravel,tightenco/ziggy, and both orphaned controllers.The aesthetic was ported, not rewritten — Tailwind classes copied verbatim from
LedDisplay.tsxandSetCountForm.tsx. Only@font-face,.font-digitaland.glow-redsurvive in CSS; the rest was shadcn theming for components deleted earlier in this milestone.Deliberate decision: the JSON API is gone
GET/POST/PATCH /tracker,POST /incrementandPATCH /countwere removed along withCounterControllerandTrackerController. All state changes go through Livewire. Nothing in-repo consumed those endpoints; anything external scripting them would break.Security hardening
$countand$needsOnboardingare#[Locked]. Livewire public properties are client-writable, and tampering withneedsOnboardingwould have exposed the onboarding form on a configured app, withinitialise()then attempting a secondTracker::create().initialise()now adopts an existing tracker (Tracker::current() ?? Tracker::create(...)) rather than creating blindly, so two tabs onboarding concurrently cannot produce duplicate rows. There is no unique constraint ontrackersto catch this at the DB level.From review
increment()had no ceiling check —save()andinitialise()validated againstmax:4294967295butincrement()did not, so at the unsigned-int ceiling the next click would overflow or throw an unhandled DB error. ExtractedCounter::MAX_COUNT, applied to both the guard and the validation rule, with a test.@livewireScriptsadded for symmetry with@livewireStyles(Livewire 4 auto-injects, but the asymmetry read as accidental).vite.config.ts→.js. Cosmetic; the file is valid TS and Vite handles it natively.Also fixed
docker/production/Dockerfilecopiedtsconfig.json,components.jsonandeslint.config.js— all deleted here. That would have failed at release time, not in CI, since the image build only runs onmainpushes andv*tags.composer.jsonname/description were still thelaravel/react-starter-kitdefaults.Gates
PHPUnit 33 tests, 81 assertions · PHPStan 0 errors (level 7) · Pint PASS 39 files ·
npm run buildPASSVerified live:
GET /returns 200 with>1005<server-rendered in the initial HTML andwire:click="increment"wired. Livewire's runtime serves from its hashed asset path..claude/PLATFORM.mdrewritten — it still described aUser-owned tracker and the React stack.