172 lines
5.4 KiB
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,
|
|
]);
|
|
}
|
|
}
|