diff --git a/app/Livewire/Sites/Index.php b/app/Livewire/Sites/Index.php
new file mode 100644
index 0000000..9b9906f
--- /dev/null
+++ b/app/Livewire/Sites/Index.php
@@ -0,0 +1,72 @@
+ Site::orderBy('name')->get(),
+ ]);
+ }
+
+ public function create(): void
+ {
+ $this->resetForm();
+ $this->showForm = true;
+ }
+
+ public function edit(int $id): void
+ {
+ $site = Site::findOrFail($id);
+ $this->editingId = $site->id;
+ $this->name = $site->name;
+ $this->url = $site->url;
+ $this->description = $site->description;
+ $this->showForm = true;
+ }
+
+ public function save(): void
+ {
+ $validated = $this->validate([
+ 'name' => ['required', 'string', 'max:255'],
+ 'url' => ['required', 'string', 'max:255'],
+ 'description' => ['nullable', 'string'],
+ ]);
+
+ Site::updateOrCreate(['id' => $this->editingId], $validated);
+
+ $this->resetForm();
+ $this->showForm = false;
+ }
+
+ public function delete(int $id): void
+ {
+ Site::findOrFail($id)->delete();
+ }
+
+ public function resetForm(): void
+ {
+ $this->editingId = null;
+ $this->name = '';
+ $this->url = '';
+ $this->description = null;
+ $this->resetValidation();
+ }
+}
diff --git a/resources/views/livewire/sites/index.blade.php b/resources/views/livewire/sites/index.blade.php
new file mode 100644
index 0000000..b0223dd
--- /dev/null
+++ b/resources/views/livewire/sites/index.blade.php
@@ -0,0 +1,60 @@
+
+
+
Sites
+
+
+
+
+
+
+ | Name |
+ URL |
+ Description |
+ |
+
+
+
+ @foreach ($sites as $site)
+
+ | {{ $site->name }} |
+ {{ $site->url }} |
+ {{ $site->description }} |
+
+
+
+ |
+
+ @endforeach
+
+
+
+ @if ($showForm)
+
+
+
{{ $editingId ? 'Edit Site' : 'Add Site' }}
+
+
+
+
+ @error('name') {{ $message }} @enderror
+
+
+
+
+
+ @error('url') {{ $message }} @enderror
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @endif
+
diff --git a/routes/web.php b/routes/web.php
index 86a06c5..4520504 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,7 +1,13 @@
name('sites.index');
+
+// placeholder, replaced in Task 6
+Route::get('/social-accounts', SitesIndex::class)->name('social-accounts.index');
diff --git a/tests/Feature/Livewire/SitesTest.php b/tests/Feature/Livewire/SitesTest.php
new file mode 100644
index 0000000..8055b5b
--- /dev/null
+++ b/tests/Feature/Livewire/SitesTest.php
@@ -0,0 +1,50 @@
+create(['name' => 'Example Site']);
+
+ $this->get(route('sites.index'))
+ ->assertOk()
+ ->assertSee('Example Site');
+});
+
+test('a new site can be created', function () {
+ Livewire::test(Index::class)
+ ->set('name', 'New Site')
+ ->set('url', 'https://newsite.test')
+ ->set('description', 'Some description')
+ ->call('save');
+
+ expect(Site::where('name', 'New Site')->exists())->toBeTrue();
+});
+
+test('name and url are required to create a site', function () {
+ Livewire::test(Index::class)
+ ->set('name', '')
+ ->set('url', '')
+ ->call('save')
+ ->assertHasErrors(['name', 'url']);
+});
+
+test('an existing site can be edited', function () {
+ $site = Site::factory()->create(['name' => 'Old Name']);
+
+ Livewire::test(Index::class)
+ ->call('edit', $site->id)
+ ->set('name', 'Updated Name')
+ ->call('save');
+
+ expect($site->fresh()->name)->toBe('Updated Name');
+});
+
+test('a site can be deleted', function () {
+ $site = Site::factory()->create();
+
+ Livewire::test(Index::class)->call('delete', $site->id);
+
+ expect(Site::find($site->id))->toBeNull();
+});