From cee5b1976bcd34cf5ac891848c9a39e3f446d36c Mon Sep 17 00:00:00 2001 From: Doar Mihai Date: Sat, 18 Jul 2026 15:05:55 +0300 Subject: [PATCH] feat: add Social Accounts Livewire CRUD page Co-Authored-By: Claude Sonnet 5 --- app/Livewire/SocialAccounts/Index.php | 91 ++++++++++++++++++ .../livewire/social-accounts/index.blade.php | 96 +++++++++++++++++++ routes/web.php | 5 +- tests/Feature/Livewire/SocialAccountsTest.php | 67 +++++++++++++ 4 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 app/Livewire/SocialAccounts/Index.php create mode 100644 resources/views/livewire/social-accounts/index.blade.php create mode 100644 tests/Feature/Livewire/SocialAccountsTest.php diff --git a/app/Livewire/SocialAccounts/Index.php b/app/Livewire/SocialAccounts/Index.php new file mode 100644 index 0000000..a58b1ff --- /dev/null +++ b/app/Livewire/SocialAccounts/Index.php @@ -0,0 +1,91 @@ + SocialAccount::orderBy('username')->get(), + 'networks' => SocialAccount::NETWORKS, + 'types' => SocialAccount::TYPES, + ]); + } + + public function create(): void + { + $this->resetForm(); + $this->showForm = true; + } + + public function edit(int $id): void + { + $account = SocialAccount::findOrFail($id); + $this->editingId = $account->id; + $this->network = $account->network; + $this->username = $account->username; + $this->email = $account->email; + $this->password = $account->password; + $this->type = $account->type; + $this->showForm = true; + } + + public function save(): void + { + $validated = $this->validate([ + 'network' => ['required', 'string', 'in:' . implode(',', SocialAccount::NETWORKS)], + 'username' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'max:255'], + 'password' => ['required', 'string', 'max:255'], + 'type' => ['nullable', 'string', 'in:' . implode(',', SocialAccount::TYPES)], + ]); + + SocialAccount::updateOrCreate(['id' => $this->editingId], $validated); + + $this->resetForm(); + $this->showForm = false; + } + + public function delete(int $id): void + { + SocialAccount::findOrFail($id)->delete(); + } + + public function toggleReveal(int $id): void + { + $this->revealedId = $this->revealedId === $id ? null : $id; + } + + public function resetForm(): void + { + $this->editingId = null; + $this->network = ''; + $this->username = ''; + $this->email = ''; + $this->password = ''; + $this->type = null; + $this->resetValidation(); + } +} diff --git a/resources/views/livewire/social-accounts/index.blade.php b/resources/views/livewire/social-accounts/index.blade.php new file mode 100644 index 0000000..11d8e01 --- /dev/null +++ b/resources/views/livewire/social-accounts/index.blade.php @@ -0,0 +1,96 @@ +
+
+

Social Accounts

+ +
+ + + + + + + + + + + + + + @foreach ($accounts as $account) + + + + + + + + + @endforeach + +
NetworkUsernameEmailPasswordType
{{ ucfirst($account->network) }}{{ $account->username }}{{ $account->email }} + @if ($revealedId === $account->id) + {{ $account->password }} + @else + •••••••• + @endif + + {{ $account->type ? ucfirst($account->type) : '—' }} + + +
+ + @if ($showForm) +
+
+

{{ $editingId ? 'Edit Account' : 'Add Account' }}

+ +
+ + + @error('network') {{ $message }} @enderror +
+ +
+ + + @error('username') {{ $message }} @enderror +
+ +
+ + + @error('email') {{ $message }} @enderror +
+ +
+ + + @error('password') {{ $message }} @enderror +
+ +
+ + + @error('type') {{ $message }} @enderror +
+ +
+ + +
+
+
+ @endif +
diff --git a/routes/web.php b/routes/web.php index 4520504..e8729ff 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ name('sites.index'); - -// placeholder, replaced in Task 6 -Route::get('/social-accounts', SitesIndex::class)->name('social-accounts.index'); +Route::get('/social-accounts', SocialAccountsIndex::class)->name('social-accounts.index'); diff --git a/tests/Feature/Livewire/SocialAccountsTest.php b/tests/Feature/Livewire/SocialAccountsTest.php new file mode 100644 index 0000000..d0451fc --- /dev/null +++ b/tests/Feature/Livewire/SocialAccountsTest.php @@ -0,0 +1,67 @@ +create(['username' => 'example_user']); + + $this->get(route('social-accounts.index')) + ->assertOk() + ->assertSee('example_user'); +}); + +test('a new social account can be created', function () { + Livewire::test(Index::class) + ->set('network', 'twitter') + ->set('username', 'new_user') + ->set('email', 'new_user@example.com') + ->set('password', 'secret123') + ->set('type', 'post') + ->call('save'); + + expect(SocialAccount::where('username', 'new_user')->exists())->toBeTrue(); +}); + +test('network, username, email, and password are required', function () { + Livewire::test(Index::class) + ->set('network', '') + ->set('username', '') + ->set('email', '') + ->set('password', '') + ->call('save') + ->assertHasErrors(['network', 'username', 'email', 'password']); +}); + +test('type can be left null', function () { + Livewire::test(Index::class) + ->set('network', 'reddit') + ->set('username', 'no_type_user') + ->set('email', 'no_type@example.com') + ->set('password', 'secret123') + ->set('type', null) + ->call('save'); + + $account = SocialAccount::where('username', 'no_type_user')->first(); + expect($account->type)->toBeNull(); +}); + +test('an existing account can be edited', function () { + $account = SocialAccount::factory()->create(['username' => 'old_name']); + + Livewire::test(Index::class) + ->call('edit', $account->id) + ->set('username', 'new_name') + ->call('save'); + + expect($account->fresh()->username)->toBe('new_name'); +}); + +test('an account can be deleted', function () { + $account = SocialAccount::factory()->create(); + + Livewire::test(Index::class)->call('delete', $account->id); + + expect(SocialAccount::find($account->id))->toBeNull(); +});