Sistem untuk admin DUN merekod waktu KTM (Ketua Tempat Mengundi) hadir mengambil peti undi. Data KTM dibaca daripada DB sppm (spr2026) melalui connection kedua — hanya penempatan berjawatan KTM yang dipaparkan. - Dashboard: senarai KTM ikut pusat mengundi (asc) & saluran (asc), status BELUM HADIR (merah) / HADIR (hijau) + tarikh & masa - Carian nama/no. KP, tapisan pusat mengundi & status - Tanda hadir / batal dengan kawalan DUN (admin terhad ke DUN sendiri) - Eksport Excel (worksheet Hadir & Belum Hadir) guna SimpleXlsx - Peranan: superadmin (urus pengguna, semua DUN) & admin DUN - Docker: nginx + php-fpm + queue + scheduler + webhook deploy, port 127.0.0.1:8009, TZ Asia/Kuala_Lumpur di semua container - Zon waktu aplikasi: Asia/Kuala_Lumpur (APP_TIMEZONE) Berasaskan scaffold spr2026_taklimat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
94 lines
3.4 KiB
PHP
94 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\KehadiranKtm;
|
|
use App\Models\Sppm\PetugasAssignment;
|
|
use App\Support\SimpleXlsx;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class EksportKehadiranController extends Controller
|
|
{
|
|
private const PENGEPALA = [
|
|
'Bil', 'DUN', 'Pusat Mengundi', 'Saluran', 'Nama', 'No. KP',
|
|
'No. Telefon', 'Jawatan', 'Status', 'Tarikh Hadir', 'Masa Hadir', 'Dicatat Oleh',
|
|
];
|
|
|
|
/**
|
|
* Eksport senarai kehadiran KTM ke Excel: worksheet "Hadir" dan
|
|
* "Belum Hadir". Admin DUN dihadkan kepada DUN sendiri; superadmin
|
|
* boleh eksport semua atau DUN terpilih (?dun_id=).
|
|
*/
|
|
public function __invoke(Request $request): Response
|
|
{
|
|
$user = $request->user();
|
|
|
|
$dunId = $user->isSuperadmin()
|
|
? ($request->filled('dun_id') ? $request->integer('dun_id') : null)
|
|
: (int) $user->dun_id;
|
|
|
|
$assignments = PetugasAssignment::ktm()
|
|
->when($dunId, fn ($q) => $q->untukDun($dunId))
|
|
->with(['permohonan', 'pusatMengundi.dun', 'saluran', 'jawatan'])
|
|
->get()
|
|
->sortBy([
|
|
fn ($a, $b) => strcmp($a->pusatMengundi?->dun?->code ?? '', $b->pusatMengundi?->dun?->code ?? ''),
|
|
fn ($a, $b) => strcmp($a->pusatMengundi?->nama ?? '', $b->pusatMengundi?->nama ?? ''),
|
|
fn ($a, $b) => ($a->saluran?->nombor_saluran ?? 0) <=> ($b->saluran?->nombor_saluran ?? 0),
|
|
])
|
|
->values();
|
|
|
|
$kehadiran = KehadiranKtm::whereIn('assignment_id', $assignments->pluck('id'))
|
|
->with('dicatatOleh')
|
|
->get()
|
|
->keyBy('assignment_id');
|
|
|
|
$hadir = [self::PENGEPALA];
|
|
$belum = [self::PENGEPALA];
|
|
|
|
foreach ($assignments as $a) {
|
|
$rekod = $kehadiran->get($a->id);
|
|
$dun = $a->pusatMengundi?->dun;
|
|
// dun.nama kebiasaannya sudah bermula dengan kod (cth. "N49 Kota Iskandar")
|
|
$dunLabel = str_starts_with($dun->nama ?? '', $dun->code ?? "\0")
|
|
? ($dun->nama ?? '')
|
|
: trim(($dun->code ?? '').' '.($dun->nama ?? ''));
|
|
|
|
$baris = [
|
|
0, // Bil diisi selepas pengasingan
|
|
$dunLabel,
|
|
$a->pusatMengundi?->nama ?? '',
|
|
$a->saluran ? 'Saluran '.$a->saluran->nombor_saluran : '',
|
|
$a->permohonan?->nama_ic ?? '',
|
|
$a->permohonan?->no_kp ?? '',
|
|
$a->permohonan?->no_telefon ?? '',
|
|
$a->jawatan?->nama ?? 'Ketua Tempat Mengundi',
|
|
$rekod ? 'HADIR' : 'BELUM HADIR',
|
|
$rekod?->hadir_at?->format('d/m/Y') ?? '',
|
|
$rekod?->hadir_at?->format('h:i A') ?? '',
|
|
$rekod?->dicatatOleh?->name ?? '',
|
|
];
|
|
|
|
if ($rekod) {
|
|
$baris[0] = count($hadir);
|
|
$hadir[] = $baris;
|
|
} else {
|
|
$baris[0] = count($belum);
|
|
$belum[] = $baris;
|
|
}
|
|
}
|
|
|
|
$xlsx = (new SimpleXlsx)
|
|
->addSheet('Hadir', $hadir)
|
|
->addSheet('Belum Hadir', $belum);
|
|
|
|
$namaFail = 'Kehadiran-KTM-'.now()->format('Ymd-His').'.xlsx';
|
|
|
|
return response($xlsx->toString(), 200, [
|
|
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
'Content-Disposition' => 'attachment; filename="'.$namaFail.'"',
|
|
]);
|
|
}
|
|
}
|