parfum/app/Livewire/Studio/Loyalty/Tier.php

165 lines
5.2 KiB
PHP

<?php
namespace App\Livewire\Studio\Loyalty;
use App\Livewire\Forms\Studio\Loyalty\TierForm;
use App\Models\Bottle;
use App\Models\Membership;
use App\Models\Perfume;
use App\Models\Product;
use App\Models\Tier as TierModel;
use App\Traits\Authorization\WithAuthorization;
use App\Traits\Components\WithCloseModal;
use App\Traits\Components\WithConfirmation;
use App\Traits\Components\WithToast;
use App\Traits\Notification\WithSubscribeNotification;
use App\Traits\Utilities\WithUpdatedData;
use Flux\Flux;
use Illuminate\Contracts\View\View;
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 TierForm $form;
public string $method = 'create';
public string $modalTitle = '';
public function getTopMemberStats($tierId): ?array
{
$topMembership = Membership::where('tier_id', $tierId)
->with(['user.customer.orders.items'])
->orderByDesc('total_spending')
->first();
if (! $topMembership || ! $topMembership->user || ! $topMembership->user->customer) {
return null;
}
$customer = $topMembership->user->customer;
$orders = $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(fn ($o) => ($o->discount ?? 0) + ($o->voucher_discount ?? 0));
$grandTotal = $orders->sum('total');
return [
'name' => $customer->name,
'spending' => formatCurrencyNumber($topMembership->total_spending, 'Rp'),
'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_spending
TierModel::reorderTierChain();
$this->toast('Tier berhasil ditambahkan.');
Flux::modals()->close();
}
public function update(): void
{
$this->canOrAbort('update tier');
$oldMinSpending = $this->form->tier->min_spending;
$newMinSpending = parseRupiahToInt($this->form->min_spending);
$this->form->update();
// If min_spending changed, reorder the entire tier chain
if ($oldMinSpending !== $newMinSpending) {
TierModel::reorderTierChain();
}
$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_spending
TierModel::reorderTierChain();
$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
{
$tiers = TierModel::withCount('memberships')
->orderBy('min_spending', 'asc')
->get()
->map(fn (TierModel $tier) => [
'id' => $tier->id,
'hash' => $tier->hash,
'name' => $tier->name,
'min_spending' => formatCurrencyNumber($tier->min_spending, 'Rp'),
'max_spending' => $tier->max_spending ? formatCurrencyNumber($tier->max_spending, 'Rp') : 'Tak terbatas',
'total_members' => formatCurrencyNumber($tier->memberships_count),
'top_member' => $this->getTopMemberStats($tier->id),
])
->toArray();
return view('livewire.studio.loyalty.tiers', [
'pageTitle' => 'Tier',
'tiers' => $tiers,
]);
}
}