parfum/app/Livewire/Home/Voucher.php

172 lines
5.4 KiB
PHP

<?php
namespace App\Livewire\Home;
use App\Models\Outlet;
use App\Models\Voucher as VoucherModel;
use App\Traits\Components\WithToast;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Layout;
use Livewire\Component;
use Livewire\WithPagination;
#[Layout('components.layouts.home', [
'title' => 'Voucher',
])]
class Voucher extends Component
{
use WithPagination, WithToast;
public $outlets;
public string $search = '';
public ?string $outlet_id = null;
public ?string $sort = null;
public ?VoucherModel $selectedVoucher = null;
public bool $showDetailModal = false;
public function mount(): void
{
$this->outlets = Outlet::query()->operational()->pluck('name', 'id');
}
public function updatedSearch(): void
{
$this->resetPage();
}
public function updatedOutletId(): void
{
$this->resetPage();
}
public function updatedSort(): void
{
$this->resetPage();
}
public function showDetail(string $hashId): void
{
$this->selectedVoucher = VoucherModel::byHash($hashId);
$this->selectedVoucher->load('outlets');
$this->selectedVoucher->min_purchase_formatted = formatCurrencyNumber($this->selectedVoucher->min_purchase, 'Rp');
$this->selectedVoucher->end_date_formatted = formatDateLocalized($this->selectedVoucher->end_date);
$this->showDetailModal = true;
}
public function closeDetail(): void
{
$this->showDetailModal = false;
$this->selectedVoucher = null;
}
public function claim(string $hashId): void
{
if (! auth()->check()) {
$this->redirect(route('login'), navigate: true);
return;
}
$voucher = VoucherModel::byHash($hashId);
$user = auth()->user();
// Basic checks
if (! $voucher) {
$this->toast('Voucher tidak ditemukan.', 'Gagal', 'danger');
return;
}
if ($voucher->quota !== null && $voucher->available_count >= $voucher->quota) {
$this->toast('Klaim gagal! Kuota voucher sudah habis.', 'Gagal', 'danger');
return;
}
// Check if user already reached limit
$claimedCount = $user->vouchers()->where('voucher_id', $voucher->id)->exists();
if ($voucher->limit_per_user !== null) {
$claimedCount = $user->vouchers()->where('voucher_id', $voucher->id)->count();
if ($claimedCount >= $voucher->limit_per_user) {
$this->toast('Anda sudah mencapai batas klaim untuk voucher ini.', 'Peringatan', 'warning');
return;
}
} elseif ($claimedCount) {
// If limit_per_user is null, assume it can only be claimed once?
// Or can it be claimed multiple times?
// Usually vouchers are one-time claim per user unless specified.
// Based on common logic, if limit_per_user is null, maybe it's unlimited?
// But usually it's 1. Let's stick to the limit_per_user check.
}
// Check if voucher is for specific tier
$userTierId = $user->membership?->tier_id;
if ($voucher->tiers()->exists() && ! $voucher->tiers()->where('tiers.id', $userTierId)->exists()) {
$this->toast('Voucher ini tidak tersedia untuk tier Anda.', 'Gagal', 'danger');
return;
}
try {
// Claim with transaction
DB::transaction(function () use ($user, $voucher) {
// Re-check quota inside transaction
$v = VoucherModel::where('id', $voucher->id)->lockForUpdate()->first();
if ($v->quota !== null && $v->available_count >= $v->quota) {
throw new \Exception('Kuota voucher sudah habis.');
}
$user->vouchers()->attach($v->id);
$v->increment('available_count');
});
$this->toast('Voucher berhasil diklaim! Silakan cek di menu Voucher Saya.', 'Berhasil', 'success');
} catch (\Throwable $e) {
$this->toast($e->getMessage(), 'Gagal', 'danger');
}
}
public function render(): View
{
$vouchers = VoucherModel::where(function ($query) {
$query->where('name', 'like', '%'.$this->search.'%')
->orWhere('code', 'like', '%'.$this->search.'%');
})
->when($this->outlet_id, function ($query) {
$query->whereHas('outlets', function ($q) {
$q->where('outlets.id', $this->outlet_id);
});
})
->when($this->sort, function ($query) {
match ($this->sort) {
'latest' => $query->orderBy('created_at', 'desc'),
'biggest' => $query->orderBy('discount_amount', 'desc'),
'ending-soon' => $query->orderByRaw('end_date IS NULL')
->orderBy('end_date', 'asc'),
};
})
->active()
->latest()
->paginate(3)
->through(function (VoucherModel $voucher) {
$voucher->min_purchase_formatted = formatCurrencyNumber($voucher->min_purchase, 'Rp');
$voucher->end_date = formatDateLocalized($voucher->end_date);
return $voucher;
});
return view('livewire.home.vouchers', [
'vouchers' => $vouchers,
]);
}
}