191 lines
5.7 KiB
PHP
191 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Studio\Loyalty\Tier;
|
|
|
|
use App\Livewire\Forms\Studio\Loyalty\TierForm;
|
|
use App\Models\Bottle;
|
|
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 Index extends Component
|
|
{
|
|
use WithAuthorization, WithCloseModal, WithConfirmation, WithSubscribeNotification, WithToast, WithUpdatedData;
|
|
|
|
public array $tiers = [];
|
|
|
|
public TierForm $form;
|
|
|
|
public string $method = 'create';
|
|
|
|
public string $modalTitle = '';
|
|
|
|
public function mount(): void
|
|
{
|
|
$this->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',
|
|
]);
|
|
}
|
|
}
|