Skip to content

Commit

Permalink
Merge pull request #200 from pinkary-project/feat/edit-links
Browse files Browse the repository at this point in the history
Feat: edit links
  • Loading branch information
nunomaduro authored Jul 17, 2024
2 parents 562022d + c28e22d commit 7fb9d8b
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 0 deletions.
86 changes: 86 additions & 0 deletions app/Livewire/Links/Edit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Links;

use App\Models\Link;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Livewire\Attributes\Locked;
use Livewire\Attributes\On;
use Livewire\Component;

final class Edit extends Component
{
/**
* The component's link ID.
*/
#[Locked]
public ?int $linkId = null;

/**
* The component's description.
*/
public string $description = '';

/**
* The component's URL.
*/
public string $url = '';

/**
* Store a new link.
*
* @throws AuthorizationException
*/
public function update(Request $request): void
{
if (! Str::startsWith($this->url, ['http://', 'https://'])) {
$this->url = "https://{$this->url}";
}

$validated = $this->validate([
'description' => 'required|max:100',
'url' => ['required', 'max:100', 'url', 'starts_with:https'],
]);

$link = Link::findOrFail($this->linkId);

$this->authorize('update', $link);

if ($link->url !== $validated['url']) {
$validated['click_count'] = 0;
}

$link->update($validated);

$this->dispatch('link.updated');
$this->dispatch('close-modal', 'link-edit-modal');
$this->dispatch('notification.created', message: 'Link updated.');
}

/**
* Initialize the edit link form component.
*/
#[On('link.edit')]
public function edit(Link $link): void
{
$this->linkId = $link->id;
$this->description = $link->description;
$this->url = $link->url;
$this->dispatch('open-modal', 'link-edit-modal');
}

/**
* Render the component.
*/
public function render(Request $request): View
{
return view('livewire.links.edit', [
'user' => $request->user(),
]);
}
}
1 change: 1 addition & 0 deletions app/Livewire/Links/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public function unfollow(int $targetId): void
* Refresh the component.
*/
#[On('link.created')]
#[On('link.updated')]
#[On('link-settings.updated')]
public function refresh(): void
{
Expand Down
8 changes: 8 additions & 0 deletions app/Policies/LinkPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,12 @@ public function delete(User $user, Link $link): bool
{
return $user->id === $link->user_id;
}

/**
* Determine whether the user can update the link.
*/
public function update(User $user, Link $link): bool
{
return $user->id === $link->user_id;
}
}
34 changes: 34 additions & 0 deletions resources/views/livewire/links/edit.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<form wire:submit="update">
<div class="space-y-3">
<div>
<x-input-label for="description" :value="__('Description')" />
<x-text-input id="description" type="text" wire:model="description" class="mt-1 block w-full" required autofocus />
@error('description')
<x-input-error :messages="$message" class="mt-2" />
@enderror
</div>
<div>
<x-input-label for="url" :value="__('URL')" />
<x-text-input id="url" type="text" class="mt-1 block w-full" wire:model="url" required />
@error('url')
<x-input-error :messages="$message" class="mt-2" />
@enderror

<p wire:dirty wire:target="url" class="mt-2 text-sm text-slate-600 text-amber-100">
Editing the URL will reset the click counter to zero
</p>
</div>
<div class="flex items-center gap-4">
<x-primary-colorless-button class="text-{{ $user->left_color }} border-{{ $user->left_color }}" type="submit">
{{ __('Save') }}
</x-primary-colorless-button>
<button
x-on:click.prevent="$dispatch('close-modal', 'link-edit-modal')"
type="button"
class="text-slate-600 hover:text-slate-800 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Cancel
</button>
</div>
</div>
</form>
21 changes: 21 additions & 0 deletions resources/views/livewire/links/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,18 @@ class="hidden min-w-fit cursor-help items-center gap-1 text-xs group-hover:flex"
{{ Number::abbreviate($link->click_count) }}
{{ str('click')->plural($link->click_count) }}
</div>

<button
wire:click="$dispatchTo('links.edit', 'link.edit', { link: {{ $link->id }} })"
type="button"
class="flex w-10 justify-center text-slate-300 opacity-50 hover:opacity-100 focus:outline-none"
>
<x-heroicon-o-pencil
class="size-5 opacity-100 group-hover:opacity-100 sm:opacity-0"
x-bind:class="{ 'invisible': isDragging }"
/>
</button>

<form wire:submit="destroy({{ $link->id }})">
<button
onclick="if (!confirm('Are you sure you want to delete this link?')) { return false; }"
Expand All @@ -270,6 +282,15 @@ class="size-5 opacity-100 group-hover:opacity-100 sm:opacity-0"
</li>
@endforeach
</ul>

<x-modal
name="link-edit-modal"
maxWidth="2xl"
>
<div class="p-10">
<livewire:links.edit />
</div>
</x-modal>
@else
<div class="space-y-3">
@foreach ($links as $link)
Expand Down
104 changes: 104 additions & 0 deletions tests/Unit/Livewire/Links/EditTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

declare(strict_types=1);

use App\Livewire\Links\Edit;
use App\Models\Link;
use App\Models\User;
use Livewire\Livewire;

test('renders the edit link form with property values', function () {
$user = User::factory()->create();

$link = Link::factory()->create([
'user_id' => $user->id,
]);

$component = Livewire::actingAs($user)->test(Edit::class);

$component->call('edit', $link->id);
$component->assertSet('linkId', $link->id);
$component->assertSet('url', $link->url);
$component->assertSet('description', $link->description);
});

test('updates link', function () {
$user = User::factory()->create();

$link = Link::factory()->create([
'user_id' => $user->id,
'url' => 'https://example.org',
'description' => 'Example Org',
]);

$component = Livewire::actingAs($user)->test(Edit::class);

$component->call('edit', $link->id);
$component->set('url', 'https://example.com');
$component->set('description', 'Example');

$component->call('update');
$component->assertDispatched('link.updated');
$component->assertDispatched('notification.created', message: 'Link updated.');

$link->refresh();

expect($user->links->count())->toBe(1);

$link = $user->links->first();

expect($link->url)
->toBe('https://example.com')
->and($link->description)
->toBe('Example');
});

test('link click count reset on url update', function () {
$user = User::factory()->create();

$link = Link::factory()->create([
'user_id' => $user->id,
'url' => 'https://example.org',
'click_count' => 10,
]);

$component = Livewire::actingAs($user)->test(Edit::class);

$component->call('edit', $link->id);
$component->set('url', 'https://example.com');
$component->call('update');

$link->refresh();

expect($link->click_count)
->toBe(0)
->and($link->url)
->toBe('https://example.com');

});

test('link click count does not reset on only description update', function () {
$user = User::factory()->create();

$link = Link::factory()->create([
'user_id' => $user->id,
'url' => 'https://example.org',
'description' => 'Example Org',
'click_count' => 10,
]);

$component = Livewire::actingAs($user)->test(Edit::class);

$component->call('edit', $link->id);
$component->set('description', 'Example');
$component->call('update');

$link->refresh();

expect($link->click_count)
->toBe(10)
->and($link->description)
->toBe('Example')
->and($link->url)
->toBe('https://example.org');
});

0 comments on commit 7fb9d8b

Please sign in to comment.