165 lines
5.2 KiB
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,
|
|
]);
|
|
}
|
|
}
|