Files
cabutan-bertuah-koipb/app/Http/Controllers/DrawController.php
2026-06-24 18:30:00 +08:00

174 lines
5.7 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\AttendanceRecord;
use App\Models\DrawResult;
use App\Models\DrawSession;
use App\Models\Member;
use App\Models\Prize;
use App\Services\DrawService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;use RuntimeException;
class DrawController extends Controller
{
public function __construct(private readonly DrawService $service)
{
}
public function index(): View
{
$session = DrawSession::active();
// Setiap item hadiah diberi Nombor Cabutan 1..N ikut susunan (ascending draw_order).
// Cabutan berjalan MENURUN: mula dari nombor terakhir, hadiah utama (#1) dicabut terakhir.
$prizes = Prize::query()->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(),
];
}
}