parfum/app/Livewire/Member/RedeemPoint.php

135 lines
4.0 KiB
PHP

<?php
namespace App\Livewire\Member;
use App\Enums\PointRecordType;
use App\Enums\RedemptionStatus;
use App\Models\Redemption;
use App\Models\Reward;
use App\Traits\Components\WithConfirmation;
use App\Traits\Components\WithToast;
use Flux\Flux;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
#[Title('Hadiah')]
#[Layout('components.layouts.member')]
class RedeemPoint extends Component
{
use WithConfirmation, WithToast;
public array $rewards = [];
public array $userRedemptions = [];
public function mount(): void
{
$this->rewards = Reward::show()
->active()
->orderBy('points')
->get()
->map(function (Reward $reward) {
$reward->thumbnail = $reward->getFirstMediaUrl('thumbnail');
$reward->category_label = $reward->category->label();
return $reward;
})
->toArray();
$this->userRedemptions = Redemption::with('reward')
->where('user_id', auth()->id())
->latest()
->get()
->map(function (Redemption $redemption) {
$redemption->status_label = $redemption->status->label();
$redemption->status_color = $redemption->status->color();
return $redemption;
})
->toArray();
}
public function redeem(Reward $reward): void
{
$user = auth()->user();
$membership = $user->membership;
if ($membership->reward_points < $reward->points) {
$this->toast('Poin Anda tidak cukup untuk menukarkan hadiah ini.', 'Gagal', 'danger');
Flux::modals()->close();
return;
}
if ($reward->stock !== null && $reward->stock <= 0) {
$this->toast('Stok hadiah ini sudah habis.', 'Gagal', 'danger');
Flux::modals()->close();
return;
}
$redemption = DB::transaction(function () use ($user, $membership, $reward) {
// Deduct points from membership
$membership->decrement('reward_points', $reward->points);
// Create point record
$user->pointRecords()->create([
'change' => $reward->points,
'type' => PointRecordType::REWARD,
'is_addition' => false,
'description' => Str::limit("Redeem: {$reward->name}", 50),
]);
// Create redemption
$redemption = Redemption::create([
'user_id' => $user->id,
'reward_id' => $reward->id,
'code' => 'RD-'.Str::upper(Str::random(8)),
'points_spent' => $reward->points,
'status' => RedemptionStatus::WAITING_PICKUP,
'expires_at' => now()->addWeek(),
]);
// Decrement stock if applicable
if ($reward->stock !== null) {
$reward->decrement('stock');
}
return $redemption;
});
// Update rewards list in real-time
$this->rewards = collect($this->rewards)->map(function ($item) use ($reward) {
if ($item['id'] === $reward->getRouteKey() && $item['stock'] !== null) {
$item['stock']--;
}
return $item;
})->toArray();
// Update user redemptions history in real-time
$redemption->load('reward');
array_unshift($this->userRedemptions, [
...$redemption->toArray(),
'status_label' => $redemption->status->label(),
'status_color' => $redemption->status->color(),
]);
$this->toast('Hadiah berhasil ditukarkan! Silakan cek tab Riwayat untuk melihat kode penukaran Anda.');
Flux::modals()->close();
}
public function render(): View
{
return view('livewire.member.rewards', [
'pageTitle' => 'Penukaran Poin',
]);
}
}