refactor(tier): merubah cara menampilkan data nya menjadi di foreach bukan tabel lagi dan menyesuaikan validasinya

This commit is contained in:
Yoga Pangestu 2025-10-15 22:54:28 +07:00
parent 2a5c3c344f
commit 0b9e118ce0
4 changed files with 113 additions and 71 deletions

View File

@ -1,57 +0,0 @@
<?php
namespace App\Livewire\Datatable\Studio\Loyalty;
use App\Models\Tier;
use App\Traits\Datatable\WithAppendColumn;
use App\Traits\Datatable\WithConfiguration;
use App\Traits\Datatable\WithPrependColumn;
use Illuminate\Database\Eloquent\Builder;
use Rappasoft\LaravelLivewireTables\DataTableComponent;
use Rappasoft\LaravelLivewireTables\Views\Column;
class TiersTable extends DataTableComponent
{
use WithAppendColumn, WithConfiguration, WithPrependColumn;
protected $model = Tier::class;
public function columns(): array
{
return [
Column::make('Nama', 'name')->searchable(),
Column::make('Min. Poin', 'min_points')->searchable()->sortable(),
Column::make('Maks. Poin', 'max_points')->searchable()->sortable(),
Column::make('Aksi')
->label(function ($row) {
$actions = '';
if (auth()->user()->can('update tier')) {
$actions .= view('components.datatables.edit-modal', [
'id' => $row->hash,
'method' => 'update',
'modalTitle' => 'Ubah Tier',
])->render();
}
if (auth()->user()->can('delete tier')) {
$actions .= view('components.datatables.delete', [
'id' => $row->hash,
'deleteRoute' => route('studio.loyalty.tier.delete', $row->hash),
])->render();
}
return $actions;
})
->html(),
];
}
public function builder(): Builder
{
return Tier::select('id', 'name', 'min_points', 'max_points');
}
}

View File

@ -3,6 +3,7 @@
namespace App\Livewire\Forms\Studio\Loyalty;
use App\Models\Tier;
use App\Rules\UnsignedInteger;
use Livewire\Form;
class TierForm extends Form
@ -19,8 +20,8 @@ public function rules(): array
{
return [
'name' => ['required', 'string', 'max:50'],
'min_points' => ['required', 'numeric'],
'max_points' => ['nullable', 'numeric'],
'min_points' => ['required', new UnsignedInteger],
'max_points' => ['nullable', new UnsignedInteger],
];
}
@ -46,13 +47,24 @@ public function store()
{
$this->validate();
Tier::create($this->pull());
return Tier::create($this->prepareSavedData());
}
public function update()
{
$this->validate();
$this->tier->update($this->pull());
$this->tier->update($this->prepareSavedData());
return $this->tier;
}
private function prepareSavedData()
{
return [
'name' => $this->name,
'min_points' => replaceCurrency($this->min_points),
'max_points' => replaceCurrency($this->max_points),
];
}
}

View File

@ -19,12 +19,27 @@ class Tier extends Component
{
use WithAuthorization, WithCloseModal, WithConfirmation, WithToast, WithUpdatedData;
public array $tiers = [];
public TierForm $form;
public string $method = 'create';
public string $modalTitle = '';
public function mount()
{
$this->tiers = TierModel::orderBy('min_points', 'asc')
->get()
->map(fn($tier) => [
'hash' => $tier->hash,
'name' => $tier->name,
'min_points' => currency($tier->min_points, ''),
'max_points' => currency($tier->max_points, ''),
])
->toArray();
}
#[On('modal:open')]
public function openModal(string $method, string $modalTitle, ?string $id = null)
{
@ -43,9 +58,19 @@ public function create()
{
$this->canOrAbort('create tier');
$this->form->store();
$tier = $this->form->store();
$this->dispatch('refreshDatatable');
$tiers = $this->tiers;
$tiers[] = [
'hash' => $tier->hash,
'name' => $tier->name,
'min_points' => currency($tier->min_points, ''),
'max_points' => currency($tier->max_points, ''),
];
usort($tiers, fn($a, $b) => replaceCurrency($a['min_points']) <=> replaceCurrency($b['min_points']));
$this->tiers = $tiers;
$this->toast('Tier berhasil ditambahkan.');
@ -56,9 +81,22 @@ public function update()
{
$this->canOrAbort('update tier');
$this->form->update();
$tier = $this->form->update();
$this->dispatch('refreshDatatable');
// update array tiers
$tiers = $this->tiers;
foreach ($tiers as $index => $item) {
if ($item['hash'] === $tier->hash) {
$tiers[$index] = [
'hash' => $tier->hash,
'name' => $tier->name,
'min_points' => currency($tier->min_points, ''),
'max_points' => currency($tier->max_points, ''),
];
break;
}
}
$this->tiers = $tiers;
$this->toast('Tier berhasil diperbarui.');
@ -69,7 +107,9 @@ public function delete(TierModel $tier)
{
$tier->delete();
$this->dispatch('refreshDatatable');
// update array tiers
$tiers = array_values(array_filter($this->tiers, fn($item) => $item['hash'] !== $tier->hash));
$this->tiers = $tiers;
$this->toast('Tier berhasil dihapus.');

View File

@ -3,7 +3,7 @@
<div>
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
</div>
@if (auth()->user()->can('create tier'))
@can('create tier')
<div>
<flux:modal.trigger name="form-modal">
<flux:button variant="primary" class="text-sm"
@ -11,11 +11,58 @@
</flux:button>
</flux:modal.trigger>
</div>
@endif
@endcan
</div>
<div class="mt-6">
<livewire:datatable.studio.loyalty.tiers-table />
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
@foreach ($tiers as $tier)
<flux:card
class="hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<div class="space-y-4">
<div class="flex items-start justify-between">
<flux:heading size="lg">{{ $tier['name'] }}
</flux:heading>
</div>
<div class="space-y-1">
<div class="flex justify-between text-sm">
<span class="text-gray-500 dark:text-gray-400">Min. Poin:</span>
<span class="font-medium">{{ $tier['min_points'] }}</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-gray-500 dark:text-gray-400">Maks. Poin:</span>
<span class="font-medium">{{ $tier['max_points'] }}</span>
</div>
</div>
<div class="flex gap-2 pt-2 border-t border-gray-200 dark:border-gray-700">
@can('update tier')
<flux:modal.trigger name="form-modal">
<flux:tooltip content="Ubah">
<flux:button variant="primary" color="yellow" icon="pencil-square" size="sm"
wire:click="$dispatch('modal:open', {method: 'update', 'modalTitle': 'Ubah Tier', 'id': '{{ $tier['hash'] }}'})">
Ubah
</flux:button>
</flux:tooltip>
</flux:modal.trigger>
@endcan
@can('delete tier')
<flux:modal.trigger name="delete">
<flux:tooltip content="Hapus">
<flux:button variant="danger" icon="trash" size="sm"
wire:click="$dispatch('fn:confirmDelete', {id: '{{ $tier['hash'] }}'})">
Hapus
</flux:button>
</flux:tooltip>
</flux:modal.trigger>
@endcan
</div>
</div>
</flux:card>
@endforeach
</div>
</div>
@include('components.confirmation.delete')
@ -33,14 +80,14 @@
<flux:field>
<flux:label>Min. Poin <span class="text-red-500 ms-1">*</span></flux:label>
<flux:input placeholder="Masukkan minimal poin" wire:model.live.debounce.500ms="form.min_points"
autocomplete="off" />
autocomplete="off" x-mask:dynamic="$money($input, ',')" />
<flux:error name="form.min_points" />
</flux:field>
<flux:field>
<flux:label>Maks. Poin </flux:label>
<flux:input placeholder="Masukkan maksimal poin" wire:model.live.debounce.500ms="form.max_points"
autocomplete="off" />
autocomplete="off" x-mask:dynamic="$money($input, ',')" />
<flux:error name="form.max_points" />
</flux:field>