Skip to content

Commit

Permalink
add: Create and Edit Stripe Plans
Browse files Browse the repository at this point in the history
  • Loading branch information
Morten Bak committed Aug 24, 2024
1 parent 93f0778 commit 284dfa8
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 9 deletions.
25 changes: 16 additions & 9 deletions app/Livewire/Admin/Plans.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,32 @@
use Illuminate\View\View;
use Jantinnerezo\LivewireAlert\LivewireAlert;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Session;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;
use Spatie\Permission\Models\Permission;

class Plans extends Component
{
use LivewireAlert;
use WithPagination;

#[Session]
public int $perPage = 25;

public string $sortField = 'title';

public bool $sortAsc = true;

#[Url]
public string $search = '';

public array $searchableFields = ['title', 'slug', 'stripe_id'];

protected $listeners = [
'deletePermission' => 'destroy',
'permissionCreated' => '$refresh',
'permissionUpdated' => '$refresh',
'deletePlan' => 'destroy',
'planCreated' => '$refresh',
'planUpdated' => '$refresh',
];

public function updatingSearch(): void
Expand All @@ -52,19 +55,23 @@ public function sortBy($field): void
public function render(): View
{
return view('livewire.admin.plans', [
'plans' => Plan::query()->search($this->searchableFields, $this->search)
'plans' => Plan::query()
->when($this->search, function ($query) {
$query->search($this->searchableFields, $this->search);
})
->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc')
->paginate($this->perPage),
]);
}

public function destroy($id): void
public function deletePlan(string $id): void
{
try {
Permission::query()->find($id)->delete();
$this->alert('success', 'Permission deleted successfully!');
Plan::query()->where('id', '=', $id)->delete();
$this->alert('success', __('plans.plan_was_deleted'));
$this->dispatch('deletePlan');
} catch (Exception $e) {
$this->alert('error', 'Something went wrong!');
$this->alert('error', __('plans.something_went_wrong'));
}
}
}
8 changes: 8 additions & 0 deletions app/Livewire/Admin/Plans/CreatePlan.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

use App\Models\Plan;
use Illuminate\Contracts\View\View;
use Jantinnerezo\LivewireAlert\LivewireAlert;
use Livewire\Attributes\Rule;
use LivewireUI\Modal\ModalComponent;

class CreatePlan extends ModalComponent
{
use LivewireAlert;

#[Rule(['required', 'max:255', 'unique:plans,title'])]
public string $title = '';

Expand All @@ -29,6 +32,11 @@ public function create(): void
]);

$this->dispatch('planCreated');

$this->alert('success', 'Plan was created');

$this->closeModal();

}

public function mount(): void
Expand Down
58 changes: 58 additions & 0 deletions app/Livewire/Admin/Plans/EditPlan.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App\Livewire\Admin\Plans;

use App\Models\Plan;
use Illuminate\Contracts\View\View;
use Jantinnerezo\LivewireAlert\LivewireAlert;
use Livewire\Attributes\Rule;
use LivewireUI\Modal\ModalComponent;

class EditPlan extends ModalComponent
{
use LivewireAlert;

#[Rule(['required', 'max:255', 'unique:plans,title'])]
public string $title = '';

#[Rule(['required', 'max:255'])]
public string $slug = '';

#[Rule(['required', 'max:255'])]
public string $stripe_id = '';

public Plan $plan;

public function mount(Plan $plan)
{
abort_if(! auth()->check(), 403);
abort_unless(auth()->user()->hasPermissionTo('edit plans'), 403);

$this->plan = $plan;
$this->title = $plan->title ?? '';
$this->slug = $plan->slug ?? '';
$this->stripe_id = $plan->stripe_id ?? '';
}

public function save()
{
$this->validate();

$this->plan->update([
'title' => $this->title,
'slug' => $this->slug,
'stripe_id' => $this->stripe_id,
]);

$this->alert('success', __('plans.update_successful'));

$this->dispatch('planUpdated');

$this->closeModal();
}

public function render(): View
{
return view('livewire.admin.plans.edit-plan');
}
}
13 changes: 13 additions & 0 deletions lang/en/plans.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

return [
'edit_plan' => 'Edit plan',
'delete_plan' => 'Delete plan',
'confirm_deletion' => 'Are you sure you want to delete this plan?',
'no_plans_found' => 'No plans was found',
'plan_was_deleted' => 'The Plan was deleted',
'something_went_wrong' => 'Something went wrong.',
'update_successful' => 'Plan was updated',
'save' => 'Save',
'cancel' => 'Cancel',
];
60 changes: 60 additions & 0 deletions resources/views/livewire/admin/plans.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,64 @@ class="text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-pr
</div>
</div>

<div class="flex flex-col space-y-4">
<div class="flex items-center justify-between">
<input type="text"
class="w-1/4 rounded border border-gray-200 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
wire:model.live="search" placeholder="{{ __('Type in and search...') }}">
<div class="flex space-x-4">
<select wire:model.live="perPage"
class="rounded border border-gray-200 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500">
<option value="10">{{ __('10 per page') }}</option>
<option value="25">{{ __('25 per page') }}</option>
<option value="50">{{ __('50 per page') }}</option>
</select>
</div>
</div>

<x-table wire:loading.class="opacity-50">
<x-slot name="head">
<x-table.heading><a href="#" wire:click.prevent="sortBy('id')">{{ __('ID') }}</a></x-table.heading>
<x-table.heading><a href="#" wire:click.prevent="sortBy('title')">{{ __('Title') }}</a>
</x-table.heading>
<x-table.heading><a href="#" wire:click.prevent="sortBy('stripe_id')">{{ __('Stripe ID') }}</a>
</x-table.heading>
<x-table.heading class="text-right">{{ __('Actions') }}</x-table.heading>
</x-slot>

<x-slot:body>
@forelse ($plans as $plan)
<x-table.row>
<x-table.cell>
{{ $plan->id }}
</x-table.cell>
<x-table.cell>{{ $plan->title }}</x-table.cell>
<x-table.cell>{{ $plan->stripe_id }}</x-table.cell>
<x-table.cell class="text-right">
@can('edit plans')
<x-secondary-button wire:click.prevent="$dispatch('openModal', {component: 'admin.plans.edit-plan', arguments: { plan: {{ $plan->id }} }})">
@lang('plans.edit_plan')
</x-secondary-button>
@endcan
@can('delete plans')
<x-secondary-button wire:click.prevent="deletePlan('{{ $plan->id }}')" wire:confirm="{{ __('plans.confirm_deletion') }}">
@lang('plans.delete_plan')
</x-secondary-button>
@endcan
</x-table.cell>
</x-table.row>
@empty
<x-table.row>
<x-table.cell colspan="4">
<div class="text-center py-2">
@lang('plans.no_plans_found')
</div>
</x-table.cell>
</x-table.row>
@endforelse
</x-slot:body>
</x-table>

</div>

</div>
46 changes: 46 additions & 0 deletions resources/views/livewire/admin/plans/edit-plan.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<div class="p-4 w-full dark:bg-gray-900 flex flex-col space-y-4">

<h2 class="text-2xl font-bold dark:text-white">
{{ __('Edit Plan') }}
</h2>

<div class="form-group">
<label for="title" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
{{ __('Title') }}
</label>
<input id="title" wire:model.live="title" type="text"
class="@error('title') is-invalid @enderror bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
placeholder="Enter title" required>
@error('title') <span class="text-danger">{{ $message }}</span>@enderror
</div>

<div class="form-group">
<label for="slug" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
{{ __('Slug') }}
</label>
<input id="slug" wire:model.live="slug" type="text"
class="@error('slug') is-invalid @enderror bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
placeholder="Enter slug" required>
@error('slug') <span class="text-danger">{{ $message }}</span>@enderror
</div>

<div class="form-group">
<label for="stripe_id" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
{{ __('Stripe ID') }}
</label>
<input id="stripe_id" wire:model.live="stripe_id" type="text"
class="@error('stripe_id') is-invalid @enderror bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
placeholder="Enter Stripe ID" required>
@error('stripe_id') <span class="text-danger">{{ $message }}</span>@enderror
</div>

<div class="flex justify-between items-center">
<button wire:click.prevent="save" class="text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">
{{ __('plans.save') }}
</button>
<x-secondary-button wire:click="closeModal">
{{ __('plans.cancel') }}
</x-secondary-button>
</div>

</div>
86 changes: 86 additions & 0 deletions tests/Feature/Livewire/Admin/Plans/EditPlanTest.php
Original file line number Diff line number Diff line change
@@ -1 +1,87 @@
<?php

use App\Livewire\Admin\Plans\EditPlan;
use App\Models\Plan;
use App\Models\User;
use Livewire\Livewire;

use function Pest\Laravel\assertDatabaseMissing;

it('requires the correct access to view the component', function () {

Livewire::test(EditPlan::class)
->assertForbidden();

$user = User::factory()->create();
/** @var User $user */
$user->givePermissionTo('edit plans');

Livewire::actingAs($user)
->test(EditPlan::class)
->assertOk();
});

it('can fetch the plan from the provided plan id', function () {
Livewire::test(EditPlan::class)
->assertForbidden();

$user = User::factory()->create();
/** @var User $user */
$user->givePermissionTo('edit plans');

$plan = Plan::factory()->create();

Livewire::actingAs($user)
->test(EditPlan::class, ['plan' => $plan])
->assertOk()
->assertSet('title', $plan->title)
->assertSet('slug', $plan->slug)
->assertSet('stripe_id', $plan->stripe_id);

});

it('has wired properties and methods', function () {
$user = User::factory()->create();
/** @var User $user */
$user->givePermissionTo('edit plans');
$plan = Plan::factory()
->create();

Livewire::actingAs($user)
->test(EditPlan::class, ['plan' => $plan])
->assertOk()
->assertPropertyWired('title')
->assertPropertyWired('slug')
->assertPropertyWired('stripe_id')
->assertMethodWired('save');
});

it('can save a updated plan', function () {

$user = User::factory()->create();
/** @var User $user */
$user->givePermissionTo('edit plans');
$plan = Plan::factory()
->create();

Livewire::actingAs($user)
->test(EditPlan::class, ['plan' => $plan])
->assertOk()
->set('title', 'New Plan Title')
->set('slug', 'New-Plan-Slug')
->set('stripe_id', 'New Stripe ID')
->call('save')
->assertHasNoErrors();

assertDatabaseMissing('plans', [
'title' => $plan->title,
'slug' => $plan->slug,
'stripe_id' => $plan->stripe_id,
]);

expect($plan->refresh())
->title->toBe('New Plan Title')
->slug->toBe('New-Plan-Slug')
->stripe_id->toBe('New Stripe ID');

});

0 comments on commit 284dfa8

Please sign in to comment.