user()->membership->tier_id; $this->rewards = Reward::show() ->active() ->whereHas('tiers', function ($query) use ($userTierId) { $query->where('id', $userTierId); }) ->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; // Check if reward is available for user's tier if (! $reward->tiers()->where('tiers.id', $membership->tier_id)->exists()) { $this->toast('Hadiah ini tidak tersedia untuk tier Anda.', 'Gagal', 'danger'); Flux::modals()->close(); return; } 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', ]); } }