ordered()->get()->values()->map(function (Prize $p, int $i) { return [ 'id' => $p->id, 'seq' => $i + 1, 'nama_hadiah' => $p->nama_hadiah, 'draw_order' => $p->draw_order, 'kategori' => $p->kategori, 'status' => $p->status, ]; }); $totalPrizes = $prizes->count(); $attendance = AttendanceRecord::count(); // Nombor terakhir (nombor mula cabutan) = jumlah hadiah, // tetapi jika kehadiran < jumlah hadiah, hadkan kepada jumlah kehadiran. $startNumber = min($totalPrizes, $attendance); $eligibleCount = $this->service->eligibleMembers($session)->count(); // Cabutan pending semasa (jika ada) untuk sambung semula $pending = DrawResult::with(['member', 'prize']) ->where('status', DrawResult::STATUS_PENDING) ->latest('spun_at') ->first(); return view('draw.index', compact( 'session', 'prizes', 'totalPrizes', 'attendance', 'startNumber', 'eligibleCount', 'pending' )); } /** Senarai pool semasa untuk render roda */ public function pool(): JsonResponse { $session = DrawSession::active(); return response()->json([ 'pool' => $this->poolPayload($session), ]); } public function spin(Request $request, DrawService $service): JsonResponse { $data = $request->validate([ 'prize_id' => ['required', 'integer', 'exists:prizes,id'], ]); $session = DrawSession::active(); $prize = Prize::findOrFail($data['prize_id']); try { $result = $service->spin($prize, $session, $request->user()); } catch (RuntimeException $e) { return response()->json(['message' => $e->getMessage()], 422); } // Pool selepas spin (winner mesti ada dalamnya) $pool = $this->poolPayload($session); $winnerIndex = collect($pool)->search(fn ($p) => $p['id'] === $result->member_id); return response()->json([ 'result_id' => $result->id, 'winner_index' => $winnerIndex === false ? 0 : $winnerIndex, 'pool' => $pool, 'winner' => $this->memberPayload($result->member), 'prize' => $this->prizePayload($result->prize), 'attempt' => $result->attempt_number, ]); } public function confirm(Request $request, DrawService $service): JsonResponse { $data = $request->validate(['result_id' => ['required', 'integer', 'exists:draw_results,id']]); $result = DrawResult::findOrFail($data['result_id']); try { $result = $service->confirm($result, $request->user()); } catch (RuntimeException $e) { return response()->json(['message' => $e->getMessage()], 422); } return response()->json([ 'status' => 'confirmed', 'message' => 'TAHNIAH! Pemenang telah disahkan.', 'winner' => $this->memberPayload($result->member), 'prize' => $this->prizePayload($result->prize), ]); } public function cancel(Request $request, DrawService $service): JsonResponse { $data = $request->validate([ 'result_id' => ['required', 'integer', 'exists:draw_results,id'], 'sebab' => ['nullable', 'string', 'max:255'], ]); $result = DrawResult::findOrFail($data['result_id']); try { $result = $service->cancel($result, $request->user(), $data['sebab'] ?? null); } catch (RuntimeException $e) { return response()->json(['message' => $e->getMessage()], 422); } return response()->json([ 'status' => 'cancelled', 'message' => 'Cabutan dibatalkan. Hadiah boleh dicabut semula.', 'prize' => $this->prizePayload($result->prize), ]); } private function poolPayload(DrawSession $session): array { return $this->service->eligibleMembers($session) ->map(fn (Member $m) => [ 'id' => $m->id, 'label' => $m->no_pekerja ?: $m->no_anggota ?: $m->nama, 'nama' => $m->nama, ]) ->values() ->all(); } private function memberPayload(Member $m): array { return [ 'id' => $m->id, 'nama' => $m->nama, 'no_pekerja' => $m->no_pekerja, 'no_anggota' => $m->no_anggota, 'jabatan' => $m->jabatan, 'bahagian' => $m->bahagian, 'masked_kp' => $m->maskedKp(), ]; } private function prizePayload(Prize $p): array { return [ 'id' => $p->id, 'nama_hadiah' => $p->nama_hadiah, 'draw_order' => $p->draw_order, 'kategori' => $p->kategori, 'status' => $p->status, 'status_label' => $p->statusLabel(), ]; } }