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' => formatCurrencyNumber($tier->min_points), 'max_points' => formatCurrencyNumber($tier->max_points), 'total_members' => formatCurrencyNumber($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' => formatCurrencyNumber($topMembership->tier_points), 'total_orders' => formatCurrencyNumber($totalOrders), 'items' => [ 'total_perfume' => formatCurrencyNumber($totalPerfume), 'total_product' => formatCurrencyNumber($totalProduct), 'total_bottle' => formatCurrencyNumber($totalBottle), ], 'total' => [ 'subtotal' => formatCurrencyNumber($subtotal, 'Rp'), 'discount' => formatCurrencyNumber($totalDiscount, 'Rp'), 'grand_total' => formatCurrencyNumber($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 = parseRupiahToInt($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; $id && $this->form->setTier(TierModel::byHashOrFail($id)); } public function render(): View { return view('livewire.studio.loyalty.tier.index', [ 'pageTitle' => 'Tier', ]); } }