refactor(tier): Restructure tier management by introducing a new Index component, enhancing validation logic, and removing the old Tier component for improved clarity and maintainability.

This commit is contained in:
Yoga Pangestu 2025-12-14 10:29:39 +07:00
parent 470cc2240f
commit 1f29e4193d
8 changed files with 398 additions and 269 deletions

View File

@ -26,6 +26,21 @@ public function rules(): array
];
}
public function withValidator($validator): void
{
$validator->after(function ($validator) {
$minPoints = replaceCurrency($this->min_points);
$maxPoints = $this->max_points ? replaceCurrency($this->max_points) : null;
$ignoreId = $this->tier?->id;
$tierErrors = Tier::validateTierPoints($minPoints, $maxPoints, $ignoreId);
foreach ($tierErrors as $error) {
$validator->errors()->add('min_points', $error);
}
});
}
public function validationAttributes(): array
{
return [

View File

@ -1,188 +0,0 @@
<?php
namespace App\Livewire\Studio\Loyalty;
use App\Livewire\Forms\Studio\Loyalty\TierForm;
use App\Models\Bottle;
use App\Models\Perfume;
use App\Models\Product;
use App\Models\Tier as TierModel;
use App\Traits\Notification\WithSubscribeNotification;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation;
use App\Traits\WithToast;
use App\Traits\WithUpdatedData;
use Flux\Flux;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Livewire\Attributes\On;
use Livewire\Attributes\Title;
use Livewire\Component;
#[Title('Tier')]
class Tier extends Component
{
use WithAuthorization, WithCloseModal, WithConfirmation, WithSubscribeNotification, WithToast, WithUpdatedData;
public Collection $tiers;
public TierForm $form;
public string $method = 'create';
public string $modalTitle = '';
public function mount(): void
{
$this->loadTiers();
}
#[On('data:tiersUpdated')]
public function loadTiers(): void
{
$this->tiers = TierModel::with(['rewards', 'memberships.user.customer.orders.items'])
->withCount(['memberships', 'rewards'])
->orderBy('min_points', 'asc')
->get()
->map(function ($tier) {
$topMembership = $tier->memberships()
->with(['user.customer.orders.items'])
->orderByDesc('tier_points')
->first();
$topMemberStats = null;
if ($topMembership && $topMembership->user && $topMembership->user->customer) {
$orders = $topMembership->user->customer->orders;
$totalOrders = $orders->count();
// Sum items
$totalPerfume = $orders->flatMap(fn ($order) => $order->items->where('orderable_type', Perfume::class))->sum('quantity');
$totalProduct = $orders->flatMap(fn ($order) => $order->items->where('orderable_type', Product::class))->sum('quantity');
$totalBottle = $orders->flatMap(fn ($order) => $order->items->where('orderable_type', Bottle::class))->sum('quantity');
// Sum total
$subtotal = $orders->sum('subtotal');
$totalDiscount = $orders->sum('discount');
$grandTotal = $orders->sum('total');
$topMemberStats = [
'name' => $topMembership->user->customer->name,
'points' => currency($topMembership->tier_points),
'total_orders' => currency($totalOrders),
'items' => [
'total_perfume' => currency($totalPerfume),
'total_product' => currency($totalProduct),
'total_bottle' => currency($totalBottle),
],
'total' => [
'subtotal' => currency($subtotal, 'Rp'),
'discount' => currency($totalDiscount, 'Rp'),
'grand_total' => currency($grandTotal, 'Rp'),
],
];
}
$tier->top_member = $topMemberStats;
$tier->total_members = $tier->memberships_count;
$tier->total_rewards = $tier->rewards_count;
return $tier;
});
}
#[On('modal:open')]
public function openModal(string $method, string $modalTitle, ?string $id = null): void
{
$this->resetValidation();
$this->resetErrorBag();
$this->method = $method;
$this->modalTitle = $modalTitle;
if ($id) {
$this->form->setTier(TierModel::byHashOrFail($id));
}
}
public function create(): void
{
$this->canOrAbort('create tier');
$minPoints = (int) $this->form->min_points;
$maxPoints = (int) $this->form->max_points;
if (TierModel::isOverlap($minPoints, $maxPoints)) {
$this->toast('Range poin yang kamu masukkan bertabrakan dengan tier yang sudah ada.', 'Info', 'warning');
return;
}
$tier = $this->form->store();
// Add computed properties to the model
$tier->total_members = 0;
$tier->total_rewards = 0;
$tier->top_member = null;
// Add new tier to collection and sort
$this->tiers = $this->tiers->push($tier)->sortBy('min_points')->values();
$this->toast('Tier berhasil ditambahkan.');
Flux::modals()->close();
}
public function update(): void
{
$this->canOrAbort('update tier');
$tierId = $this->form->tier->id;
$minPoints = (int) $this->form->min_points;
$maxPoints = (int) $this->form->max_points;
if (TierModel::isOverlap($minPoints, $maxPoints, $tierId)) {
$this->toast('Range poin yang kamu masukkan bertabrakan dengan tier yang sudah ada.', 'Info', 'warning');
return;
}
$tier = $this->form->update();
// Update the tier in collection
$this->tiers = $this->tiers->map(function ($t) use ($tier) {
if ($t->hash === $tier->hash) {
$t->name = $tier->name;
$t->min_points = $tier->min_points;
$t->max_points = $tier->max_points;
}
return $t;
});
$this->toast('Tier berhasil diperbarui.');
Flux::modals()->close();
}
public function delete(TierModel $tier): void
{
$tier->delete();
// Remove tier from collection
$this->tiers = $this->tiers->filter(fn ($t) => $t->hash !== $tier->hash)->values();
$this->toast('Tier berhasil dihapus.');
Flux::modals()->close();
}
public function render(): View
{
return view('livewire.studio.loyalty.tier.index', [
'pageTitle' => 'Tier',
]);
}
}

View File

@ -0,0 +1,192 @@
<?php
namespace App\Livewire\Studio\Loyalty\Tier;
use App\Livewire\Forms\Studio\Loyalty\TierForm;
use App\Models\Bottle;
use App\Models\Perfume;
use App\Models\Product;
use App\Models\Tier as TierModel;
use App\Traits\Notification\WithSubscribeNotification;
use App\Traits\WithAuthorization;
use App\Traits\WithCloseModal;
use App\Traits\WithConfirmation;
use App\Traits\WithToast;
use App\Traits\WithUpdatedData;
use Flux\Flux;
use Illuminate\Contracts\View\View;
use Livewire\Attributes\On;
use Livewire\Attributes\Title;
use Livewire\Component;
#[Title('Tier')]
class Index extends Component
{
use WithAuthorization, WithCloseModal, WithConfirmation, WithSubscribeNotification, WithToast, WithUpdatedData;
public array $tiers = [];
public TierForm $form;
public string $method = 'create';
public string $modalTitle = '';
public function mount(): void
{
$this->loadTiers();
}
#[On('data:tiersUpdated')]
public function loadTiers(): void
{
$this->tiers = TierModel::withCount('memberships')
->withCount('rewards')
->orderBy('min_points', 'asc')
->get()
->map(function ($tier) {
return [
'id' => $tier->id,
'hash' => $tier->hash,
'name' => $tier->name,
'min_points' => currency($tier->min_points),
'max_points' => currency($tier->max_points),
'total_members' => currency($tier->memberships_count),
'total_rewards' => $tier->rewards_count,
];
})
->toArray();
}
public function getTopMemberStats($tierId): ?array
{
$tier = TierModel::with(['memberships.user.customer.orders.items'])->find($tierId);
if (! $tier) {
return null;
}
$topMembership = $tier->memberships()
->with(['user.customer.orders.items'])
->orderByDesc('tier_points')
->first();
if (! $topMembership || ! $topMembership->user || ! $topMembership->user->customer) {
return null;
}
$orders = $topMembership->user->customer->orders;
$totalOrders = $orders->count();
// Sum items
$totalPerfume = $orders->flatMap(fn ($order) => $order->items->where('orderable_type', Perfume::class))->sum('quantity');
$totalProduct = $orders->flatMap(fn ($order) => $order->items->where('orderable_type', Product::class))->sum('quantity');
$totalBottle = $orders->flatMap(fn ($order) => $order->items->where('orderable_type', Bottle::class))->sum('quantity');
// Sum total
$subtotal = $orders->sum('subtotal');
$totalDiscount = $orders->sum('discount');
$grandTotal = $orders->sum('total');
return [
'name' => $topMembership->user->customer->name,
'points' => currency($topMembership->tier_points),
'total_orders' => currency($totalOrders),
'items' => [
'total_perfume' => currency($totalPerfume),
'total_product' => currency($totalProduct),
'total_bottle' => currency($totalBottle),
],
'total' => [
'subtotal' => currency($subtotal, 'Rp'),
'discount' => currency($totalDiscount, 'Rp'),
'grand_total' => currency($grandTotal, 'Rp'),
],
];
}
public function create(): void
{
$this->canOrAbort('create tier');
$this->form->store();
// Reorder the entire tier chain to ensure proper max_points
TierModel::reorderTierChain();
// Reload tiers to reflect the changes
$this->loadTiers();
$this->toast('Tier berhasil ditambahkan.');
Flux::modals()->close();
}
public function update(): void
{
$this->canOrAbort('update tier');
$oldMinPoints = $this->form->tier->min_points;
$newMinPoints = replaceCurrency($this->form->min_points);
$this->form->update();
// If min_points changed, reorder the entire tier chain
if ($oldMinPoints !== $newMinPoints) {
TierModel::reorderTierChain();
}
// Reload tiers to reflect any changes from reordering
$this->loadTiers();
$this->toast('Tier berhasil diperbarui.');
Flux::modals()->close();
}
public function delete(TierModel $tier): void
{
$this->canOrAbort('delete tier');
// Validate if tier can be deleted
$canDelete = TierModel::canDeleteTier($tier->id);
if (! $canDelete['can_delete']) {
$this->toast($canDelete['message'], 'Gagal', 'danger');
return;
}
$tier->delete();
// Reorder tier chain after deletion to ensure proper max_points
TierModel::reorderTierChain();
// Reload tiers to reflect the changes
$this->loadTiers();
$this->toast('Tier berhasil dihapus.');
Flux::modals()->close();
}
#[On('modal:open')]
public function openModal(string $method, string $modalTitle, ?string $id = null): void
{
$this->resetValidation();
$this->resetErrorBag();
$this->method = $method;
$this->modalTitle = $modalTitle;
if ($id) {
$this->form->setTier(TierModel::byHashOrFail($id));
}
}
public function render(): View
{
return view('livewire.studio.loyalty.tier.index', [
'pageTitle' => 'Tier',
]);
}
}

View File

@ -3,7 +3,7 @@
namespace App\Livewire\Studio\Loyalty\Tier;
use App\Livewire\Forms\Studio\Loyalty\Tier\RewardForm;
use App\Livewire\Studio\Loyalty\Tier;
use App\Livewire\Studio\Loyalty\Tier\Index as Tier;
use App\Models\TierReward;
use App\Traits\Notification\WithSubscribeNotification;
use App\Traits\WithAuthorization;
@ -12,6 +12,7 @@
use App\Traits\WithToast;
use App\Traits\WithUpdatedData;
use Flux\Flux;
use Illuminate\Contracts\View\View;
use Livewire\Attributes\On;
use Livewire\Attributes\Title;
use Livewire\Component;
@ -29,6 +30,66 @@ class Reward extends Component
public array $rewards = [];
public function create(): void
{
$this->canOrAbort('create tier reward');
$reward = $this->form->store();
$this->rewards = collect($this->rewards)
->push(
$reward->only(['tier_id', 'name', 'value']) + ['hash' => $reward->hash]
)
->toArray();
$this->dispatch('data:tiersUpdated')->to(Tier::class);
$this->toast('Hadiah berhasil ditambahkan.');
Flux::modal('reward-form-modal')->close();
}
public function update(): void
{
$this->canOrAbort('update tier reward');
$reward = $this->form->update();
$updatedReward = $reward->only(['tier_id', 'name', 'value']) + [
'hash' => $reward->hash,
];
$this->rewards = collect($this->rewards)
->map(
fn ($item) => $item['hash'] === $updatedReward['hash']
? $updatedReward
: $item
)
->toArray();
$this->toast('Hadiah berhasil diperbarui.');
Flux::modal('reward-form-modal')->close();
}
public function delete(TierReward $reward): void
{
$this->canOrAbort('delete tier reward');
$reward->delete();
$this->rewards = collect($this->rewards)
->reject(fn ($item) => $item['hash'] === $reward->hash)
->values()
->toArray();
$this->toast('Hadiah berhasil dihapus.');
$this->dispatch('data:tiersUpdated')->to(Tier::class);
Flux::modal('delete-reward-confirmation')->close();
}
#[On('modal:open:reward')]
public function openModal(string $method, string $modalTitle, ?string $tier_id = null, ?string $id = null)
{
@ -41,58 +102,17 @@ public function openModal(string $method, string $modalTitle, ?string $tier_id =
$this->form->tier_id = $tier_id;
$this->rewards = TierReward::where('tier_id', $tier_id)
->latest()
->get()
->map(fn (TierReward $reward) => [
'hash' => $reward->hash,
'tier_id' => $reward->tier_id,
'name' => $reward->name,
'value' => currency($reward->value, 'Rp'),
])
->append('hash')
->toArray();
if ($id) {
$this->form->setRewards(TierReward::byHashOrFail($id));
}
$id && $this->form->setRewards(
TierReward::byHashOrFail($id)
);
}
public function create()
{
$this->canOrAbort('create tier reward');
$this->form->store();
$this->dispatch('data:tiersUpdated')->to(Tier::class);
$this->toast('Hadiah berhasil ditambahkan.');
Flux::modal('reward-form-modal')->close();
}
public function update()
{
$this->canOrAbort('update tier reward');
$this->form->update();
$this->toast('Hadiah berhasil diperbarui.');
Flux::modal('reward-form-modal')->close();
}
public function delete(TierReward $reward)
{
$reward->delete();
$this->toast('Hadiah berhasil dihapus.');
$this->dispatch('data:tiersUpdated')->to(Tier::class);
Flux::modal('delete')->close();
$this->rewards = array_values(array_filter($this->rewards, fn ($item) => $item['hash'] !== $reward->hash));
}
public function render()
public function render(): View
{
return view('livewire.studio.loyalty.tier.reward', [
'pageTitle' => 'Hadiah',

View File

@ -47,4 +47,93 @@ public function rewards(): HasMany
{
return $this->hasMany(TierReward::class);
}
public static function getHighestTier()
{
return self::orderByDesc('min_points')->first();
}
public static function updatePreviousTierMaxPoints(int $newMinPoints): void
{
$previousTier = self::whereNull('max_points')
->orWhere('max_points', '>=', $newMinPoints)
->orderByDesc('min_points')
->first();
if ($previousTier && is_null($previousTier->max_points)) {
$previousTier->update(['max_points' => $newMinPoints - 1]);
}
}
public static function validateTierPoints(int $minPoints, ?int $maxPoints = null, ?int $ignoreId = null): array
{
$errors = [];
// Get the highest tier (excluding current if updating)
$highestTier = self::when($ignoreId, fn ($query) => $query->where('id', '!=', $ignoreId))
->orderByDesc('min_points')
->first();
// If there's a highest tier with null max_points, it should be updated first
if ($highestTier && is_null($highestTier->max_points)) {
if ($minPoints <= $highestTier->min_points) {
$errors[] = "Min. poin harus lebih besar dari {$highestTier->min_points} (tier {$highestTier->name})";
}
} elseif ($highestTier) {
$requiredMinPoints = ($highestTier->max_points ?? $highestTier->min_points) + 1;
if ($minPoints <= $highestTier->max_points) {
$errors[] = "Min. poin harus lebih besar dari {$highestTier->max_points} (tier {$highestTier->name})";
}
}
// Check for overlap with existing tiers
if (self::isOverlap($minPoints, $maxPoints, $ignoreId)) {
$errors[] = 'Rentang poin tier tumpang tindih dengan tier lain';
}
return $errors;
}
public static function reorderTierChain(): void
{
$tiers = self::orderBy('min_points')->get();
foreach ($tiers as $index => $tier) {
if ($index < $tiers->count() - 1) {
// Set max_points to min_points of next tier - 1
$nextTier = $tiers[$index + 1];
$tier->update(['max_points' => $nextTier->min_points - 1]);
} else {
// Last tier should have null max_points
$tier->update(['max_points' => null]);
}
}
}
public static function canDeleteTier(int $tierId): array
{
$tier = self::find($tierId);
if (! $tier) {
return ['can_delete' => false, 'message' => 'Tier tidak ditemukan'];
}
$tiers = self::orderBy('min_points')->get();
$tierIndex = $tiers->search(fn ($t) => $t->id === $tierId);
// Can always delete the lowest tier (first tier)
if ($tierIndex === 0) {
return ['can_delete' => true, 'message' => ''];
}
// Can always delete the highest tier (last tier with null max_points)
if ($tierIndex === $tiers->count() - 1) {
return ['can_delete' => true, 'message' => ''];
}
// Cannot delete middle tiers as it would create gaps in the tier chain
return [
'can_delete' => false,
'message' => 'Tidak dapat menghapus tier tengah karena akan mengacaukan sistem poin. Tier harus membentuk rantai yang kontinyu.',
];
}
}

View File

@ -22,7 +22,7 @@ class="hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-900
<div class="space-y-4">
@can('create tier reward')
@if ($tier->total_rewards == 0)
@if ($tier['total_rewards'] == 0)
<flux:callout icon="exclamation-circle" variant="warning" inline>
<flux:callout.heading>Belum ada hadiah untuk tier ini, silakan tambah terlebih dahulu.
</flux:callout.heading>
@ -30,7 +30,7 @@ class="hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-900
<x-slot name="actions">
<flux:modal.trigger name="reward-form-modal">
<flux:button variant="primary" color="primary" size="sm"
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': 'Tambah Hadiah', 'tier_id': '{{ $tier->id }}'})">
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': 'Tambah Hadiah', 'tier_id': '{{ $tier['id'] }}'})">
Tambah</flux:button>
</flux:modal.trigger>
</x-slot>
@ -39,41 +39,44 @@ class="hover:shadow-lg transition-shadow duration-200 bg-white dark:bg-gray-900
@endcan
<div class="flex items-start justify-between">
<flux:heading size="lg">{{ $tier->name }}
<flux:heading size="lg">{{ $tier['name'] }}
</flux:heading>
</div>
<div class="space-y-2">
<div class="flex justify-between text-sm">
<span class="text-gray-500 dark:text-gray-400">Min. Poin:</span>
<span class="font-medium">{{ currency($tier->min_points) }}</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">{{ currency($tier->max_points) }}</span>
<span class="font-medium">{{ $tier['max_points'] }}</span>
</div>
<flux:separator />
<div class="flex justify-between text-sm">
<span class="text-gray-500 dark:text-gray-400">Total Member:</span>
<span class="font-medium">{{ $tier->total_members }}</span>
<span class="font-medium">{{ $tier['total_members'] }}</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-gray-500 dark:text-gray-400">Total Hadiah:</span>
<span class="font-medium">{{ $tier->total_rewards }}</span>
<span class="font-medium">{{ $tier['total_rewards'] }}</span>
</div>
@if ($tier->top_member)
@php
$topMember = $this->getTopMemberStats($tier['id']);
@endphp
@if ($topMember)
<div class="text-sm">
<span class="text-gray-500 dark:text-gray-400 font-semibold">Member Dengan Poin
Tertinggi:</span>
<div
class="mt-1 flex items-center justify-between bg-gradient-to-r from-emerald-400 to-emerald-600 text-white rounded px-3 py-3 text-sm font-medium shadow-lg">
<span>{{ $tier->top_member['name'] }}</span>
<span>{{ $tier->top_member['points'] }} Poin</span>
<span>{{ $topMember['name'] }}</span>
<span>{{ $topMember['points'] }} Poin</span>
</div>
<div
@ -81,31 +84,31 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
<h4 class="font-semibold mb-2">Statistik Order</h4>
<div class="flex justify-between text-sm">
<span>Order:</span>
<span>{{ $tier->top_member['total_orders'] }}x</span>
<span>{{ $topMember['total_orders'] }}x</span>
</div>
<div class="flex justify-between text-sm mt-1">
<span>Parfum Yang Dibeli:</span>
<span>{{ $tier->top_member['items']['total_perfume'] }} ml</span>
<span>{{ $topMember['items']['total_perfume'] }} ml</span>
</div>
<div class="flex justify-between text-sm mt-1">
<span>Produk Yang Dibeli:</span>
<span>{{ $tier->top_member['items']['total_product'] }} pcs</span>
<span>{{ $topMember['items']['total_product'] }} pcs</span>
</div>
<div class="flex justify-between text-sm mt-1">
<span>Botol Yang Dibeli:</span>
<span>{{ $tier->top_member['items']['total_bottle'] }} pcs</span>
<span>{{ $topMember['items']['total_bottle'] }} pcs</span>
</div>
<div class="flex justify-between text-sm mt-1">
<span>Subtotal Belanja:</span>
<span>{{ $tier->top_member['total']['subtotal'] }}</span>
<span>{{ $topMember['total']['subtotal'] }}</span>
</div>
<div class="flex justify-between text-sm mt-1">
<span>Diskon:</span>
<span>{{ $tier->top_member['total']['discount'] }}</span>
<span>{{ $topMember['total']['discount'] }}</span>
</div>
<div class="flex justify-between text-sm mt-1">
<span>Total Belanja:</span>
<span>{{ $tier->top_member['total']['grand_total'] }}</span>
<span>{{ $topMember['total']['grand_total'] }}</span>
</div>
</div>
</div>
@ -117,7 +120,7 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
<flux:modal.trigger name="reward-view-modal">
<flux:tooltip content="Lihat Hadiah">
<flux:button variant="primary" color="blue" icon="eye" size="sm"
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': '{{ $tier->name }}', 'tier_id': '{{ $tier->id }}'})">
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': '{{ $tier['name'] }}', 'tier_id': '{{ $tier['id'] }}'})">
</flux:button>
</flux:tooltip>
</flux:modal.trigger>
@ -127,7 +130,7 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
<flux:modal.trigger name="reward-form-modal">
<flux:tooltip content="Tambah Hadiah">
<flux:button variant="primary" color="primary" icon="plus" size="sm"
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': 'Tambah Hadiah', 'tier_id': '{{ $tier->id }}'})">
wire:click="$dispatch('modal:open:reward', {method: 'create', 'modalTitle': 'Tambah Hadiah', 'tier_id': '{{ $tier['id'] }}'})">
</flux:button>
</flux:tooltip>
</flux:modal.trigger>
@ -137,17 +140,17 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
<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 }}'})">
wire:click="$dispatch('modal:open', {method: 'update', 'modalTitle': 'Ubah Tier', 'id': '{{ $tier['hash'] }}'})">
</flux:button>
</flux:tooltip>
</flux:modal.trigger>
@endcan
@can('delete tier')
<flux:modal.trigger name="delete-confirmation">
<flux:modal.trigger name="delete-tier-confirmation">
<flux:tooltip content="Hapus">
<flux:button variant="danger" icon="trash" size="sm"
wire:click="$dispatch('fn:confirmAction', {id: '{{ $tier->hash }}'})">
wire:click="$dispatch('fn:confirmAction', {id: '{{ $tier['hash'] }}'})">
</flux:button>
</flux:tooltip>
</flux:modal.trigger>
@ -160,7 +163,7 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
</div>
@include('components.modals.confirmation', [
'modalName' => 'delete-confirmation',
'modalName' => 'delete-tier-confirmation',
'modalTitle' => 'Apakah Anda yakin?',
'modalMessage' => 'Data yang berelasi dengan data ini juga akan ikut terhapus.',
'buttonVariant' => 'primary',
@ -174,8 +177,7 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
<flux:field>
<flux:label>Nama <span class="text-red-500 ms-1">*</span></flux:label>
<flux:input placeholder="Gold" wire:model="form.name" autofocus
autocomplete="off" />
<flux:input placeholder="Gold" wire:model="form.name" autofocus autocomplete="off" />
<flux:error name="form.name" />
</flux:field>

View File

@ -15,7 +15,7 @@
@foreach ($rewards as $reward)
<flux:table.row>
<flux:table.cell>{{ $reward['name'] }}</flux:table.cell>
<flux:table.cell>{{ $reward['value'] }}</flux:table.cell>
<flux:table.cell>{{ currency($reward['value'], 'Rp') }}</flux:table.cell>
<flux:table.cell>
<div class="flex gap-2 pt-2 border-gray-200 dark:border-gray-700">
@can('update tier reward')
@ -30,7 +30,7 @@
@endcan
@can('update tier reward')
<flux:modal.trigger name="delete-confirmation">
<flux:modal.trigger name="delete-reward-confirmation">
<flux:tooltip content="Hapus">
<flux:button variant="danger" icon="trash" size="sm"
wire:click="$dispatch('fn:confirmAction', {id: '{{ $reward['hash'] }}'})">
@ -56,8 +56,7 @@
<flux:field>
<flux:label>Nama <span class="text-red-500 ms-1">*</span></flux:label>
<flux:input placeholder="Voucher Belanja 20k" wire:model="form.name" autofocus
autocomplete="off" />
<flux:input placeholder="Voucher Belanja 20k" wire:model="form.name" autofocus autocomplete="off" />
<flux:error name="form.name" />
</flux:field>
@ -67,8 +66,8 @@
<flux:description>Nilai hanya berupa rupiah, bukan persentase.</flux:description>
<flux:input.group>
<flux:input.group.prefix>Rp</flux:input.group.prefix>
<flux:input placeholder="xxx" x-mask:dynamic="$money($input, ',')"
wire:model="form.value" autocomplete="off" />
<flux:input placeholder="xxx" x-mask:dynamic="$money($input, ',')" wire:model="form.value"
autocomplete="off" />
</flux:input.group>
<flux:error name="form.value" />
</flux:field>
@ -83,7 +82,7 @@
</flux:modal>
@include('components.modals.confirmation', [
'modalName' => 'delete-confirmation',
'modalName' => 'delete-reward-confirmation',
'modalTitle' => 'Apakah Anda yakin?',
'modalMessage' => 'Data yang berelasi dengan data ini juga akan ikut terhapus.',
'buttonVariant' => 'primary',

View File

@ -24,7 +24,7 @@
use App\Livewire\Studio\Information\Faq as FaqComponent;
use App\Livewire\Studio\Information\PriceRequest as PriceRequestComponent;
use App\Livewire\Studio\Loyalty\Customer as CustomerComponent;
use App\Livewire\Studio\Loyalty\Tier as TierComponent;
use App\Livewire\Studio\Loyalty\Tier\Index as TierComponent;
use App\Livewire\Studio\Loyalty\Voucher\Create as VoucherCreate;
use App\Livewire\Studio\Loyalty\Voucher\Edit as VoucherEdit;
use App\Livewire\Studio\Loyalty\Voucher\Index as VoucherIndex;