110 lines
3.4 KiB
PHP
110 lines
3.4 KiB
PHP
<?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'),
|
|
];
|
|
}
|
|
}
|