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', ]); } }