Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: edit links #200

Merged
merged 28 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8abac77
Add link action methods to Index component
digitlimit Apr 13, 2024
2c76fe1
Add update policy to LinkPolicy
digitlimit Apr 13, 2024
14c68b4
Add link edit livewire component
digitlimit Apr 14, 2024
01f3d06
Add link edit livewire component blade
digitlimit Apr 14, 2024
d39cdc3
Add link editor to Index.php
digitlimit Apr 14, 2024
d6a9fdb
Add livewire attributes to Edit component
digitlimit Apr 20, 2024
b815b35
Add livewire attributes to Index component
digitlimit Apr 20, 2024
52f89fc
Add show warning for click counter reset for URL update
digitlimit Apr 22, 2024
af0555a
Add edit form cancel event
digitlimit Apr 22, 2024
e25497c
remove DownloadUserAvatar event on link updated
digitlimit Apr 28, 2024
34456a3
remove link model property
digitlimit Apr 28, 2024
4f25f6f
inject model instance in edit link method
digitlimit Apr 28, 2024
f8a885c
add event to links edit form cancel button
digitlimit Apr 30, 2024
fd3cd06
add link edit dispatch in index
digitlimit Apr 30, 2024
3d7b785
handle link edit dispatch
digitlimit Apr 30, 2024
2d42c28
fix conflict in index
digitlimit Apr 30, 2024
78414e8
add doc block to edit method
digitlimit May 3, 2024
d2aa929
add refresh links/index
digitlimit May 3, 2024
607e389
add link edit tests
digitlimit May 8, 2024
4fff42a
fix modal link edit form notification z-index
digitlimit May 11, 2024
24c720f
feat: Add dispatch to open and close modal in link edit component
MrPunyapal Jul 17, 2024
b893182
Refactor link edit button in links index view
MrPunyapal Jul 17, 2024
e513696
Refactor: removed unwanted code from index
MrPunyapal Jul 17, 2024
d596f03
Refactor link edit button in links index view
MrPunyapal Jul 17, 2024
1f9c10c
removed edit icon SVG component
MrPunyapal Jul 17, 2024
f9a9a9a
chore: Update flash message wrapper z-index in show.blade.php
MrPunyapal Jul 17, 2024
edccc7e
Refactor link edit button in links index view
MrPunyapal Jul 17, 2024
c28e22d
Refactor link edit button in links index view
MrPunyapal Jul 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
*/
MrPunyapal marked this conversation as resolved.
Show resolved Hide resolved
#[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')]
MrPunyapal marked this conversation as resolved.
Show resolved Hide resolved
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">
MrPunyapal marked this conversation as resolved.
Show resolved Hide resolved
{{ __('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.');
MrPunyapal marked this conversation as resolved.
Show resolved Hide resolved

$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');
});