first
This commit is contained in:
109
app/Http/Controllers/CounterController.php
Normal file
109
app/Http/Controllers/CounterController.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\AttendanceRecord;
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\Member;
|
||||
use Illuminate\Database\UniqueConstraintViolationException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class CounterController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
return view('counter.index');
|
||||
}
|
||||
|
||||
/** Live search (AJAX) — laju untuk ~200 peserta */
|
||||
public function search(Request $request): JsonResponse
|
||||
{
|
||||
$term = trim((string) $request->query('q', ''));
|
||||
|
||||
if (mb_strlen($term) < 2) {
|
||||
return response()->json(['data' => [], 'message' => 'Taip sekurang-kurangnya 2 aksara.']);
|
||||
}
|
||||
|
||||
$members = Member::query()
|
||||
->search($term)
|
||||
->with('attendance')
|
||||
->orderBy('nama')
|
||||
->limit(25)
|
||||
->get()
|
||||
->map(fn (Member $m) => $this->present($m));
|
||||
|
||||
return response()->json([
|
||||
'data' => $members,
|
||||
'message' => $members->isEmpty()
|
||||
? 'Rekod tidak dijumpai. Individu ini bukan anggota koperasi yang berdaftar.'
|
||||
: null,
|
||||
]);
|
||||
}
|
||||
|
||||
/** Rekod kehadiran — elak double attendance */
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'member_id' => ['required', 'integer', 'exists:members,id'],
|
||||
]);
|
||||
|
||||
$member = Member::with('attendance')->findOrFail($data['member_id']);
|
||||
|
||||
if ($member->attendance) {
|
||||
return response()->json([
|
||||
'status' => 'already',
|
||||
'message' => 'Anggota ini sudah direkodkan hadir.',
|
||||
'member' => $this->present($member),
|
||||
], 200);
|
||||
}
|
||||
|
||||
try {
|
||||
$attendance = AttendanceRecord::create([
|
||||
'member_id' => $member->id,
|
||||
'attended_at' => now(),
|
||||
'recorded_by' => $request->user()->id,
|
||||
'ip_address' => $request->ip(),
|
||||
'user_agent' => substr((string) $request->userAgent(), 0, 255),
|
||||
]);
|
||||
} catch (UniqueConstraintViolationException $e) {
|
||||
// Race: dua kaunter rekod serentak
|
||||
$member->load('attendance');
|
||||
|
||||
return response()->json([
|
||||
'status' => 'already',
|
||||
'message' => 'Anggota ini sudah direkodkan hadir.',
|
||||
'member' => $this->present($member),
|
||||
], 200);
|
||||
}
|
||||
|
||||
AuditLog::record('attendance.record', "Rekod hadir: {$member->nama}", $attendance, [
|
||||
'member_id' => $member->id,
|
||||
]);
|
||||
|
||||
$member->load('attendance');
|
||||
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
'message' => "Kehadiran {$member->nama} berjaya direkodkan.",
|
||||
'member' => $this->present($member),
|
||||
]);
|
||||
}
|
||||
|
||||
private function present(Member $m): array
|
||||
{
|
||||
return [
|
||||
'id' => $m->id,
|
||||
'nama' => $m->nama,
|
||||
'no_pekerja' => $m->no_pekerja,
|
||||
'no_anggota' => $m->no_anggota,
|
||||
'masked_kp' => $m->maskedKp(),
|
||||
'jabatan' => $m->jabatan,
|
||||
'bahagian' => $m->bahagian,
|
||||
'status_aktif' => (bool) $m->status_aktif,
|
||||
'hadir' => (bool) $m->attendance,
|
||||
'attended_at' => $m->attendance?->attended_at?->format('d/m/Y H:i:s'),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user