64 - Extract the counter form into a Blade component

This commit is contained in:
myrmidex 2026-08-16 12:23:28 +02:00
parent 181e0cbda5
commit e5c257d94d
4 changed files with 88 additions and 64 deletions

View file

@ -31,8 +31,10 @@ public function mount(): void
$tracker = Tracker::current();
$this->needsOnboarding = $tracker === null;
$this->count = $tracker === null ? 0 : $tracker->count;
$this->value = $this->count;
if ($tracker !== null) {
$this->syncFrom($tracker);
}
}
public function initialise(): void
@ -45,7 +47,7 @@ public function initialise(): void
'count' => $this->value,
]);
$this->count = $tracker->count;
$this->syncFrom($tracker);
$this->needsOnboarding = false;
}
@ -58,10 +60,8 @@ public function increment(): void
}
$tracker->increment('count');
$tracker->refresh();
$this->count = $tracker->count;
$this->value = $this->count;
$this->syncFrom($tracker->refresh());
}
public function edit(): void
@ -83,7 +83,7 @@ public function save(): void
$tracker->update(['count' => $this->value]);
$this->count = (int) $this->value;
$this->syncFrom($tracker);
$this->editing = false;
}
@ -94,6 +94,15 @@ public function cancel(): void
$this->editing = false;
}
/**
* The tracker is the source of truth; the form input always mirrors it.
*/
private function syncFrom(Tracker $tracker): void
{
$this->count = $tracker->count;
$this->value = $this->count;
}
public function render(): View
{
return view('livewire.counter')->layout('layouts.app');

View file

@ -0,0 +1,48 @@
@props([
'action',
'label',
'submit',
'id' => 'value',
'cancel' => false,
])
<form wire:submit="{{ $action }}" class="space-y-4">
<label for="{{ $id }}" class="text-red-400 font-mono text-xs uppercase tracking-wider">
&gt; {{ $label }}
</label>
<input
id="{{ $id }}"
type="number"
min="0"
step="1"
autofocus
wire:model="value"
class="w-full bg-black border-red-500 text-red-400 focus:border-red-300 font-mono text-sm rounded-none border-2 focus:ring-0 focus:outline-none placeholder:text-red-400/40 transition-all glow-red px-3 py-2"
>
@error('value')
<p class="text-red-400 font-mono text-xs">{{ $message }}</p>
@enderror
<div @class(['flex gap-3 pt-2' => $cancel])>
<button
type="submit"
@class([
'bg-red-500 hover:bg-red-500 text-black font-mono text-sm font-bold border-red-500 rounded-none border-2 uppercase tracking-wider transition-all glow-red px-4 py-2',
'flex-1' => $cancel,
'w-full' => ! $cancel,
])
>
{{ $submit }}
</button>
@if ($cancel)
<button
type="button"
wire:click="cancel"
class="flex-1 bg-black border-red-500 text-red-400 hover:bg-red-950 hover:text-red-300 font-mono text-sm font-bold rounded-none border-2 uppercase tracking-wider transition-all glow-red px-4 py-2"
>
[ABORT]
</button>
@endif
</div>
</form>

View file

@ -3,30 +3,12 @@
<div class="min-h-screen flex items-center justify-center p-4">
<div class="w-full max-w-md">
<div class="border-2 border-red-500 bg-black shadow-[0_0_20px_rgba(239,68,68,0.3)] p-8">
<form wire:submit="initialise" class="space-y-4">
<label for="starting-value" class="text-red-400 font-mono text-xs uppercase tracking-wider">
&gt; Starting Value
</label>
<input
id="starting-value"
type="number"
min="0"
step="1"
autofocus
wire:model="value"
class="w-full bg-black border-red-500 text-red-400 focus:border-red-300 font-mono text-sm rounded-none border-2 focus:ring-0 focus:outline-none placeholder:text-red-400/40 transition-all glow-red px-3 py-2"
>
@error('value')
<p class="text-red-400 font-mono text-xs">{{ $message }}</p>
@enderror
<button
type="submit"
class="w-full bg-red-500 hover:bg-red-500 text-black font-mono text-sm font-bold border-red-500 rounded-none border-2 uppercase tracking-wider transition-all glow-red px-4 py-2"
>
[INITIALIZE]
</button>
</form>
<x-counter-form
action="initialise"
label="Starting Value"
submit="[INITIALIZE]"
id="starting-value"
/>
</div>
</div>
</div>
@ -59,39 +41,13 @@ class="text-red-400/60 hover:text-red-400 font-mono text-xs uppercase tracking-w
@if ($editing)
<div class="bg-black p-8">
<div class="w-full border-4 border-red-500 p-6 bg-black glow-red">
<form wire:submit="save" class="space-y-4">
<label for="count" class="text-red-400 font-mono text-xs uppercase tracking-wider">
&gt; Set Value
</label>
<input
id="count"
type="number"
min="0"
step="1"
autofocus
wire:model="value"
class="w-full bg-black border-red-500 text-red-400 focus:border-red-300 font-mono text-sm rounded-none border-2 focus:ring-0 focus:outline-none placeholder:text-red-400/40 transition-all glow-red px-3 py-2"
>
@error('value')
<p class="text-red-400 font-mono text-xs">{{ $message }}</p>
@enderror
<div class="flex gap-3 pt-2">
<button
type="submit"
class="flex-1 bg-red-500 hover:bg-red-500 text-black font-mono text-sm font-bold border-red-500 rounded-none border-2 uppercase tracking-wider transition-all glow-red px-4 py-2"
>
[EXECUTE]
</button>
<button
type="button"
wire:click="cancel"
class="flex-1 bg-black border-red-500 text-red-400 hover:bg-red-950 hover:text-red-300 font-mono text-sm font-bold rounded-none border-2 uppercase tracking-wider transition-all glow-red px-4 py-2"
>
[ABORT]
</button>
</div>
</form>
<x-counter-form
action="save"
label="Set Value"
submit="[EXECUTE]"
id="count"
:cancel="true"
/>
</div>
</div>
@endif

View file

@ -29,6 +29,17 @@ public function test_the_page_renders_the_current_count(): void
->assertSee('42');
}
public function test_the_edit_form_renders_with_both_actions(): void
{
$this->tracker(42);
Livewire::test(Counter::class)
->call('edit')
->assertSee('Set Value')
->assertSee('[EXECUTE]')
->assertSee('[ABORT]');
}
public function test_increment_adds_exactly_one(): void
{
$tracker = $this->tracker(5);