refactor: tier
This commit is contained in:
parent
f75c5c6300
commit
0d5d47849a
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Models\Tier;
|
||||
use App\Rules\UnsignedInteger;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Form;
|
||||
|
||||
class TierForm extends Form
|
||||
@ -21,7 +22,7 @@ public function rules(): array
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:50'],
|
||||
'min_points' => ['required', new UnsignedInteger],
|
||||
'max_points' => ['nullable', new UnsignedInteger],
|
||||
'max_points' => ['nullable', new UnsignedInteger, 'gte:min_points'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -34,7 +35,7 @@ public function validationAttributes(): array
|
||||
];
|
||||
}
|
||||
|
||||
public function setTier(Tier $tier)
|
||||
public function setTier(Tier $tier): void
|
||||
{
|
||||
$this->tier = $tier;
|
||||
|
||||
@ -43,23 +44,27 @@ public function setTier(Tier $tier)
|
||||
$this->max_points = $tier->max_points;
|
||||
}
|
||||
|
||||
public function store()
|
||||
public function store(): Tier
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
return Tier::create($this->prepareSavedData());
|
||||
return DB::transaction(function () {
|
||||
return Tier::create($this->prepareSavedData());
|
||||
});
|
||||
}
|
||||
|
||||
public function update()
|
||||
public function update(): Tier
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$this->tier->update($this->prepareSavedData());
|
||||
return DB::transaction(function () {
|
||||
$this->tier->update($this->prepareSavedData());
|
||||
|
||||
return $this->tier;
|
||||
return $this->tier;
|
||||
});
|
||||
}
|
||||
|
||||
private function prepareSavedData()
|
||||
private function prepareSavedData(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->name,
|
||||
|
||||
@ -14,6 +14,8 @@
|
||||
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;
|
||||
@ -23,7 +25,7 @@ class Tier extends Component
|
||||
{
|
||||
use WithAuthorization, WithCloseModal, WithConfirmation, WithSubscribeNotification, WithToast, WithUpdatedData;
|
||||
|
||||
public array $tiers = [];
|
||||
public Collection $tiers;
|
||||
|
||||
public TierForm $form;
|
||||
|
||||
@ -31,13 +33,13 @@ class Tier extends Component
|
||||
|
||||
public string $modalTitle = '';
|
||||
|
||||
public function mount()
|
||||
public function mount(): void
|
||||
{
|
||||
$this->loadTiers();
|
||||
}
|
||||
|
||||
#[On('data:tiersUpdated')]
|
||||
public function loadTiers()
|
||||
public function loadTiers(): void
|
||||
{
|
||||
$this->tiers = TierModel::with(['rewards', 'memberships.user.customer.orders.items'])
|
||||
->withCount(['memberships', 'rewards'])
|
||||
@ -50,6 +52,7 @@ public function loadTiers()
|
||||
->first();
|
||||
|
||||
$topMemberStats = null;
|
||||
|
||||
if ($topMembership && $topMembership->user && $topMembership->user->customer) {
|
||||
$orders = $topMembership->user->customer->orders;
|
||||
|
||||
@ -82,22 +85,16 @@ public function loadTiers()
|
||||
];
|
||||
}
|
||||
|
||||
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' => currency($tier->rewards_count),
|
||||
'top_member' => $topMemberStats,
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
$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)
|
||||
public function openModal(string $method, string $modalTitle, ?string $id = null): void
|
||||
{
|
||||
$this->resetValidation();
|
||||
$this->resetErrorBag();
|
||||
@ -110,70 +107,79 @@ public function openModal(string $method, string $modalTitle, ?string $id = null
|
||||
}
|
||||
}
|
||||
|
||||
public function create()
|
||||
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();
|
||||
|
||||
$tiers = $this->tiers;
|
||||
$tiers[] = [
|
||||
'id' => $tier->id,
|
||||
'hash' => $tier->hash,
|
||||
'name' => $tier->name,
|
||||
'min_points' => currency($tier->min_points, ''),
|
||||
'max_points' => currency($tier->max_points, ''),
|
||||
'total_members' => 0,
|
||||
'top_member' => null,
|
||||
'total_rewards' => 0,
|
||||
];
|
||||
// Add computed properties to the model
|
||||
$tier->total_members = 0;
|
||||
$tier->total_rewards = 0;
|
||||
$tier->top_member = null;
|
||||
|
||||
usort($tiers, fn ($a, $b) => replaceCurrency($a['min_points']) <=> replaceCurrency($b['min_points']));
|
||||
|
||||
$this->tiers = $tiers;
|
||||
// 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()
|
||||
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 array tiers
|
||||
$tiers = $this->tiers;
|
||||
foreach ($tiers as $index => $item) {
|
||||
if ($item['hash'] === $tier->hash) {
|
||||
$tiers[$index]['name'] = $tier->name;
|
||||
$tiers[$index]['min_points'] = currency($tier->min_points, '');
|
||||
$tiers[$index]['max_points'] = currency($tier->max_points, '');
|
||||
break;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
$this->tiers = $tiers;
|
||||
|
||||
return $t;
|
||||
});
|
||||
|
||||
$this->toast('Tier berhasil diperbarui.');
|
||||
|
||||
Flux::modals()->close();
|
||||
}
|
||||
|
||||
public function delete(TierModel $tier)
|
||||
public function delete(TierModel $tier): void
|
||||
{
|
||||
$tier->delete();
|
||||
|
||||
// update array tiers
|
||||
$tiers = array_values(array_filter($this->tiers, fn ($item) => $item['hash'] !== $tier->hash));
|
||||
$this->tiers = $tiers;
|
||||
// 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()
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.studio.loyalty.tier.index', [
|
||||
'pageTitle' => 'Tier',
|
||||
|
||||
@ -25,6 +25,19 @@ protected function casts(): array
|
||||
];
|
||||
}
|
||||
|
||||
public static function isOverlap(int $minPoints, ?int $maxPoints, ?int $ignoreId = null): bool
|
||||
{
|
||||
$maxPointsCheck = $maxPoints ?? PHP_INT_MAX;
|
||||
|
||||
return self::when($ignoreId, fn ($query) => $query->where('id', '!=', $ignoreId))
|
||||
->get()
|
||||
->contains(function ($tier) use ($minPoints, $maxPointsCheck) {
|
||||
$tierMax = $tier->max_points ?? PHP_INT_MAX;
|
||||
|
||||
return ($minPoints <= $tierMax) && ($maxPointsCheck >= $tier->min_points);
|
||||
});
|
||||
}
|
||||
|
||||
public function memberships(): HasMany
|
||||
{
|
||||
return $this->hasMany(Membership::class);
|
||||
|
||||
@ -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,41 @@ 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">{{ $tier['min_points'] }}</span>
|
||||
<span class="font-medium">{{ currency($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>
|
||||
<span class="font-medium">{{ currency($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'])
|
||||
@if ($tier->top_member)
|
||||
<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>{{ $tier->top_member['name'] }}</span>
|
||||
<span>{{ $tier->top_member['points'] }} Poin</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -81,31 +81,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>{{ $tier->top_member['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>{{ $tier->top_member['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>{{ $tier->top_member['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>{{ $tier->top_member['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>{{ $tier->top_member['total']['subtotal'] }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-sm mt-1">
|
||||
<span>Diskon:</span>
|
||||
<span>{{ $tier['top_member']['total']['discount'] }}</span>
|
||||
<span>{{ $tier->top_member['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>{{ $tier->top_member['total']['grand_total'] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -117,7 +117,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 +127,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,7 +137,7 @@ 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>
|
||||
@ -147,7 +147,7 @@ class="mt-2 bg-white dark:bg-gray-800 rounded-lg shadow p-4 text-gray-700 dark:t
|
||||
<flux:modal.trigger name="delete-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,13 +160,14 @@ 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' => 'confirmation-modal',
|
||||
'modalName' => 'delete-confirmation',
|
||||
'modalTitle' => 'Apakah Anda yakin?',
|
||||
'modalMessage' => 'Data yang berelasi dengan data ini juga akan ikut terhapus.',
|
||||
'buttonVariant' => 'primary',
|
||||
'buttonColor' => 'danger',
|
||||
'buttonText' => 'Ya, Hapus',
|
||||
])
|
||||
|
||||
<flux:modal name="form-modal" class="w-[95%] max-w-sm md:max-w-xl mx-auto" @close="closeModal('form-modal')">
|
||||
<div class="p-4 space-y-6">
|
||||
<flux:heading size="lg">{{ $modalTitle }}</flux:heading>
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
use App\Models\TierReward;
|
||||
use App\Models\User;
|
||||
use Database\Seeders\RolePermissionSeeder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Livewire\Livewire;
|
||||
@ -58,9 +59,10 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
expect($component->tiers)->toBeInstanceOf(\Illuminate\Support\Collection::class);
|
||||
expect($component->tiers)->toHaveCount(2);
|
||||
expect($component->tiers[0]['name'])->toBe('Bronze');
|
||||
expect($component->tiers[1]['name'])->toBe('Silver');
|
||||
expect($component->tiers[0]->name)->toBe('Bronze');
|
||||
expect($component->tiers[1]->name)->toBe('Silver');
|
||||
});
|
||||
|
||||
it('displays all tiers in the view', function () {
|
||||
@ -79,9 +81,10 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
expect($component->tiers[0]['name'])->toBe('Bronze');
|
||||
expect($component->tiers[1]['name'])->toBe('Silver');
|
||||
expect($component->tiers[2]['name'])->toBe('Platinum');
|
||||
expect($component->tiers)->toBeInstanceOf(\Illuminate\Support\Collection::class);
|
||||
expect($component->tiers[0]->name)->toBe('Bronze');
|
||||
expect($component->tiers[1]->name)->toBe('Silver');
|
||||
expect($component->tiers[2]->name)->toBe('Platinum');
|
||||
});
|
||||
|
||||
/*
|
||||
@ -112,7 +115,9 @@ function mountTierComponent(User $user)
|
||||
->call('create')
|
||||
->assertHasNoErrors()
|
||||
->assertSet('tiers', function ($tiers) {
|
||||
return count($tiers) === 1 && $tiers[0]['name'] === 'Gold';
|
||||
return $tiers instanceof \Illuminate\Support\Collection &&
|
||||
$tiers->count() === 1 &&
|
||||
$tiers[0]->name === 'Gold';
|
||||
});
|
||||
|
||||
$this->assertDatabaseHas('tiers', [
|
||||
@ -149,9 +154,10 @@ function mountTierComponent(User $user)
|
||||
->set('form.max_points', '1000')
|
||||
->call('create');
|
||||
|
||||
expect($component->tiers[0]['name'])->toBe('Bronze');
|
||||
expect($component->tiers[1]['name'])->toBe('Silver');
|
||||
expect($component->tiers[2]['name'])->toBe('Gold');
|
||||
expect($component->tiers)->toBeInstanceOf(\Illuminate\Support\Collection::class);
|
||||
expect($component->tiers[0]->name)->toBe('Bronze');
|
||||
expect($component->tiers[1]->name)->toBe('Silver');
|
||||
expect($component->tiers[2]->name)->toBe('Gold');
|
||||
});
|
||||
|
||||
it('validates required fields when creating tier', function () {
|
||||
@ -225,9 +231,9 @@ function mountTierComponent(User $user)
|
||||
->call('update')
|
||||
->assertHasNoErrors()
|
||||
->assertSet('tiers', function ($tiers) use ($tier) {
|
||||
$updatedTier = collect($tiers)->firstWhere('hash', $tier->hash);
|
||||
$updatedTier = $tiers->firstWhere('hash', $tier->hash);
|
||||
|
||||
return $updatedTier['name'] === 'Gold';
|
||||
return $updatedTier->name === 'Gold';
|
||||
});
|
||||
|
||||
$tier->refresh();
|
||||
@ -299,7 +305,9 @@ function mountTierComponent(User $user)
|
||||
->call('delete', $tier)
|
||||
->assertHasNoErrors()
|
||||
->assertSet('tiers', function ($tiers) use ($tier) {
|
||||
return ! collect($tiers)->contains('hash', $tier->hash);
|
||||
return ! $tiers->contains(function ($t) use ($tier) {
|
||||
return $t->hash === $tier->hash;
|
||||
});
|
||||
});
|
||||
|
||||
expect(TierModel::count())->toBe(0);
|
||||
@ -342,8 +350,8 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
$loadedTier = collect($component->tiers)->firstWhere('id', $tier->id);
|
||||
expect($loadedTier['total_members'])->toBe('2');
|
||||
$loadedTier = $component->tiers->firstWhere('id', $tier->id);
|
||||
expect($loadedTier->total_members)->toBe(2);
|
||||
});
|
||||
|
||||
it('loads tiers with rewards count', function () {
|
||||
@ -354,9 +362,9 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
$loadedTier = collect($component->tiers)->firstWhere('id', $tier->id);
|
||||
$loadedTier = $component->tiers->firstWhere('id', $tier->id);
|
||||
|
||||
expect((int) $loadedTier['total_rewards'])->toBe(3);
|
||||
expect((int) $loadedTier->total_rewards)->toBe(3);
|
||||
});
|
||||
|
||||
it('loads top member stats when tier has memberships', function () {
|
||||
@ -478,14 +486,14 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
$loadedTier = collect($component->tiers)->firstWhere('id', $tier->id);
|
||||
expect($loadedTier['top_member'])->not->toBeNull();
|
||||
expect($loadedTier['top_member']['name'])->toBe('John Doe');
|
||||
expect($loadedTier['top_member']['points'])->toContain('500');
|
||||
expect($loadedTier['top_member']['total_orders'])->toContain('2');
|
||||
expect($loadedTier['top_member']['items']['total_perfume'])->toContain('3');
|
||||
expect($loadedTier['top_member']['items']['total_product'])->toContain('3');
|
||||
expect($loadedTier['top_member']['items']['total_bottle'])->toContain('1');
|
||||
$loadedTier = $component->tiers->firstWhere('id', $tier->id);
|
||||
expect($loadedTier->top_member)->not->toBeNull();
|
||||
expect($loadedTier->top_member['name'])->toBe('John Doe');
|
||||
expect($loadedTier->top_member['points'])->toContain('500');
|
||||
expect($loadedTier->top_member['total_orders'])->toContain('2');
|
||||
expect($loadedTier->top_member['items']['total_perfume'])->toContain('3');
|
||||
expect($loadedTier->top_member['items']['total_product'])->toContain('3');
|
||||
expect($loadedTier->top_member['items']['total_bottle'])->toContain('1');
|
||||
});
|
||||
|
||||
it('shows null top member when tier has no memberships', function () {
|
||||
@ -493,8 +501,8 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
$loadedTier = collect($component->tiers)->firstWhere('id', $tier->id);
|
||||
expect($loadedTier['top_member'])->toBeNull();
|
||||
$loadedTier = $component->tiers->firstWhere('id', $tier->id);
|
||||
expect($loadedTier->top_member)->toBeNull();
|
||||
});
|
||||
|
||||
it('reloads tiers when data:tiersUpdated event is dispatched', function () {
|
||||
@ -507,7 +515,7 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component->dispatch('data:tiersUpdated')
|
||||
->assertSet('tiers', function ($tiers) {
|
||||
return count($tiers) === 2;
|
||||
return $tiers->count() === 2;
|
||||
});
|
||||
});
|
||||
|
||||
@ -534,6 +542,8 @@ function mountTierComponent(User $user)
|
||||
expect($component->method)->toBe('update');
|
||||
expect($component->modalTitle)->toBe('Ubah Tier');
|
||||
expect($component->form->name)->toBe($tier->name);
|
||||
expect($component->form->min_points)->toBe((string) $tier->min_points);
|
||||
expect($component->form->max_points)->toBe($tier->max_points ? (string) $tier->max_points : null);
|
||||
});
|
||||
|
||||
it('sets form data when opening update modal', function () {
|
||||
@ -565,9 +575,9 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
$loadedTier = collect($component->tiers)->firstWhere('id', $tier->id);
|
||||
expect($loadedTier['min_points'])->toContain('1.000');
|
||||
expect($loadedTier['max_points'])->toContain('5.000');
|
||||
$loadedTier = $component->tiers->firstWhere('id', $tier->id);
|
||||
expect(currency($loadedTier->min_points))->toContain('1.000');
|
||||
expect(currency($loadedTier->max_points))->toContain('5.000');
|
||||
});
|
||||
|
||||
it('handles currency formatting in form input', function () {
|
||||
@ -599,8 +609,8 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
$loadedTier = collect($component->tiers)->firstWhere('id', $tier->id);
|
||||
expect($loadedTier['max_points'])->not->toBeNull();
|
||||
$loadedTier = $component->tiers->firstWhere('id', $tier->id);
|
||||
expect(currency($loadedTier->max_points))->not->toBeNull();
|
||||
});
|
||||
|
||||
it('handles tier with zero min_points', function () {
|
||||
@ -612,14 +622,14 @@ function mountTierComponent(User $user)
|
||||
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
$loadedTier = collect($component->tiers)->firstWhere('id', $tier->id);
|
||||
expect($loadedTier['min_points'])->toContain('0');
|
||||
$loadedTier = $component->tiers->firstWhere('id', $tier->id);
|
||||
expect(currency($loadedTier->min_points))->toContain('0');
|
||||
});
|
||||
|
||||
it('handles empty tiers list', function () {
|
||||
$component = mountTierComponent($this->user);
|
||||
|
||||
expect($component->tiers)->toBe([]);
|
||||
expect($component->tiers)->toBeInstanceOf(Collection::class);
|
||||
});
|
||||
|
||||
it('updates tier array correctly after update', function () {
|
||||
@ -636,8 +646,8 @@ function mountTierComponent(User $user)
|
||||
->set('form.max_points', '5.000')
|
||||
->call('update');
|
||||
|
||||
$updatedTier = collect($component->tiers)->firstWhere('hash', $tier->hash);
|
||||
expect($updatedTier['name'])->toBe('Gold');
|
||||
expect($updatedTier['min_points'])->toContain('1.000');
|
||||
expect($updatedTier['max_points'])->toContain('5.000');
|
||||
$updatedTier = $component->tiers->firstWhere('hash', $tier->hash);
|
||||
expect($updatedTier->name)->toBe('Gold');
|
||||
expect(currency($updatedTier->min_points))->toContain('1.000');
|
||||
expect(currency($updatedTier->max_points))->toContain('5.000');
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user