first
This commit is contained in:
173
app/Http/Controllers/DrawController.php
Normal file
173
app/Http/Controllers/DrawController.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user