This commit is contained in:
Saufi
2026-06-24 18:30:00 +08:00
commit c0c3d7c09c
122 changed files with 16321 additions and 0 deletions

View 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'),
];
}
}