This commit is contained in:
Saufi
2026-06-03 08:51:22 +08:00
commit a14d43fe34
347 changed files with 38197 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
@extends('layouts.app')
@section('title', 'Dashboard Kehadiran')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<h1 class="h3 mb-1">Dashboard Kehadiran</h1>
<p class="text-secondary mb-0">Ringkasan kehadiran mengikut Pusat Mengundi.</p>
</div>
<div class="content-panel p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<x-sort-th column="name" label="Pusat Mengundi" />
<th>Staf</th>
<th>Hadir</th>
<th>Tidak Hadir</th>
<th>Belum Rekod</th>
<th>Status Modul</th>
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($pusats as $pusat)
@php($summary = $summaryService->totalsForPusat($pusat))
<tr>
<td>
<div class="fw-semibold">{{ $pusat->code }} - {{ $pusat->name }}</div>
<div class="small text-secondary">{{ $pusat->election?->name }}</div>
</td>
<td>{{ $summary['total_staff'] }}</td>
<td><span class="badge text-bg-success">{{ $summary['present'] }}</span></td>
<td><span class="badge text-bg-danger">{{ $summary['absent'] }}</span></td>
<td><span class="badge text-bg-warning">{{ $summary['not_recorded'] }}</span></td>
<td><span class="badge {{ $pusat->election?->settings?->is_attendance_active ? 'text-bg-success' : 'text-bg-secondary' }}">{{ $pusat->election?->settings?->is_attendance_active ? 'Aktif' : 'Belum aktif' }}</span></td>
<td class="text-end">
<a class="btn btn-sm btn-outline-primary" href="{{ route('admin.attendance.show', $pusat) }}">Detail</a>
</td>
</tr>
@empty
<tr><td colspan="7" class="text-center text-secondary py-4">Tiada Pusat Mengundi.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="mt-3">{{ $pusats->links() }}</div>
@endsection

View File

@@ -0,0 +1,86 @@
@extends('layouts.app')
@section('title', 'Detail Kehadiran')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Detail Kehadiran</h1>
<p class="text-secondary mb-0">{{ $pusat->code }} - {{ $pusat->name }}</p>
</div>
<div class="d-flex gap-2">
<a class="btn btn-outline-secondary" href="{{ route('admin.attendance.index') }}">Kembali</a>
<a class="btn btn-primary" href="{{ route('admin.attendance.export', $pusat) }}"><i class="bi bi-download me-1"></i>Export</a>
</div>
</div>
</div>
<div class="row g-3 mb-4">
@foreach ([
['label' => 'Staf', 'value' => $summary['total_staff'], 'tone' => 'primary'],
['label' => 'Hadir', 'value' => $summary['present'], 'tone' => 'success'],
['label' => 'Tidak hadir', 'value' => $summary['absent'], 'tone' => 'danger'],
['label' => 'Belum rekod', 'value' => $summary['not_recorded'], 'tone' => 'warning'],
] as $card)
<div class="col-6 col-xl-3">
<div class="stat-card h-100">
<div class="text-secondary small">{{ $card['label'] }}</div>
<div class="fs-3 fw-bold mt-2 text-{{ $card['tone'] }}">{{ $card['value'] }}</div>
</div>
</div>
@endforeach
</div>
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Ringkasan Mengikut Jawatan</h2>
<div class="row g-2">
@foreach ($roleSummaries as $roleSummary)
<div class="col-12 col-md-6 col-xl-3">
<div class="border rounded p-3 h-100">
<div class="fw-semibold">{{ $roleSummary['role'] }}</div>
<div class="small text-secondary">Staf {{ $roleSummary['total_staff'] }} · Hadir {{ $roleSummary['present'] }} · Tidak hadir {{ $roleSummary['absent'] }} · Belum {{ $roleSummary['not_recorded'] }}</div>
</div>
</div>
@endforeach
</div>
</div>
<div class="content-panel p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Staf</th>
<th>Jawatan</th>
<th>Saluran</th>
<th>Status</th>
<th>Masa Check-in</th>
<th>Catatan</th>
<th>Direkod Oleh</th>
</tr>
</thead>
<tbody>
@forelse ($assignments as $assignment)
@php($attendance = $assignment->attendance)
<tr>
<td>
<div class="fw-semibold">{{ $assignment->application?->name ?: $assignment->user?->name ?: 'Tanpa nama' }}</div>
<div class="small text-secondary">{{ $assignment->application?->ic_number ?: $assignment->user?->email ?: '-' }}</div>
</td>
<td>{{ $assignment->position?->code }}</td>
<td>{{ $assignment->saluranMengundi?->number ?: '-' }}</td>
<td><span class="badge text-bg-secondary">{{ str_replace('_', ' ', $attendance?->status ?? 'not_recorded') }}</span></td>
<td>{{ $attendance?->check_in_time?->format('d/m/Y H:i') ?: '-' }}</td>
<td>{{ $attendance?->note ?: '-' }}</td>
<td>{{ $attendance?->recordedBy?->name ?: '-' }}</td>
</tr>
@empty
<tr><td colspan="7" class="text-center text-secondary py-4">Tiada staf aktif.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection

View File

@@ -0,0 +1,22 @@
<div class="content-panel mb-4 p-2">
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-sm {{ request()->routeIs('admin.management.index') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.management.index') }}">
<i class="bi bi-grid me-1"></i>Ringkasan
</a>
<a class="btn btn-sm {{ request()->routeIs('admin.management.applications.*') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.management.applications.index') }}">
<i class="bi bi-file-earmark-person me-1"></i>Permohonan
</a>
<a class="btn btn-sm {{ request()->routeIs('admin.management.assignments.*') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.management.assignments.index') }}">
<i class="bi bi-person-lines-fill me-1"></i>Tugasan
</a>
<a class="btn btn-sm {{ request()->routeIs('admin.management.penempatan.*') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.dashboard') }}">
<i class="bi bi-people-fill me-1"></i>Penempatan
</a>
<a class="btn btn-sm {{ request()->routeIs('admin.management.representatives.*') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.management.representatives.index') }}">
<i class="bi bi-building-check me-1"></i>Wakil Agensi
</a>
<a class="btn btn-sm {{ request()->routeIs('admin.management.wheelchairs.*') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.management.wheelchairs.index') }}">
<i class="bi bi-universal-access me-1"></i>Kerusi Roda
</a>
</div>
</div>

View File

@@ -0,0 +1,48 @@
<div class="row g-3">
<div class="col-md-6">
<label class="form-label" for="assign_position_id">Jawatan</label>
<select class="form-select" id="assign_position_id" name="position_id" required>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected(old('position_id', $application->approved_position_id ?: $application->requested_position_id) == $position->id)>{{ $position->code }} - {{ $position->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="assign_pusat_mengundi_id">Pusat Mengundi</label>
<select class="form-select" id="assign_pusat_mengundi_id" name="pusat_mengundi_id" required>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}" @selected(old('pusat_mengundi_id', $application->pusat_mengundi_id) == $pusat->id)>{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="assign_saluran_mengundi_id">Saluran</label>
<select class="form-select" id="assign_saluran_mengundi_id" name="saluran_mengundi_id">
<option value="">Pusat level / ikut KTM</option>
@foreach ($salurans as $saluran)
<option value="{{ $saluran->id }}" @selected(old('saluran_mengundi_id', $application->staffAssignments->first()?->saluran_mengundi_id) == $saluran->id)>{{ $saluran->pusatMengundi?->code }} - Saluran {{ $saluran->number }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="reports_to_assignment_id">KTM Ketua</label>
<select class="form-select" id="reports_to_assignment_id" name="reports_to_assignment_id">
<option value="">Tiada</option>
@foreach ($ktmAssignments as $ktm)
<option value="{{ $ktm->id }}" @selected(old('reports_to_assignment_id', $application->selected_ktm_assignment_id) == $ktm->id)>{{ $ktm->user?->name ?? 'KTM' }} - {{ $ktm->pusatMengundi?->code }} / Saluran {{ $ktm->saluranMengundi?->number }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="assign_status">Status Tugasan</label>
<select class="form-select" id="assign_status" name="status">
@foreach (['active', 'inactive', 'replaced', 'cancelled'] as $status)
<option value="{{ $status }}">{{ $status }}</option>
@endforeach
</select>
</div>
<div class="col-12">
<label class="form-label" for="assign_note">Catatan</label>
<textarea class="form-control" id="assign_note" name="note" rows="2" placeholder="Wajib selepas tempoh pendaftaran ditutup">{{ old('note') }}</textarea>
</div>
</div>

View File

@@ -0,0 +1,86 @@
@php($isEdit = $application->exists)
<div class="row g-3">
@unless($isEdit)
<div class="col-md-6">
<label class="form-label" for="election_id">Pilihanraya</label>
<select class="form-select" id="election_id" name="election_id" required>
@foreach ($elections as $election)
<option value="{{ $election->id }}" @selected(old('election_id', $application->election_id) == $election->id)>{{ $election->name }}</option>
@endforeach
</select>
</div>
@endunless
<div class="col-md-6">
<label class="form-label" for="pusat_mengundi_id">Pusat Mengundi</label>
<select class="form-select" id="pusat_mengundi_id" name="pusat_mengundi_id" required>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}" @selected(old('pusat_mengundi_id', $application->pusat_mengundi_id) == $pusat->id)>{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="requested_position_id">Jawatan Dipohon</label>
<select class="form-select" id="requested_position_id" name="requested_position_id" required>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected(old('requested_position_id', $application->requested_position_id) == $position->id)>{{ $position->code }} - {{ $position->name }}</option>
@endforeach
</select>
</div>
@if($isEdit)
<div class="col-md-6">
<label class="form-label" for="approved_position_id">Jawatan Diluluskan</label>
<select class="form-select" id="approved_position_id" name="approved_position_id">
<option value="">Belum diluluskan</option>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected(old('approved_position_id', $application->approved_position_id) == $position->id)>{{ $position->code }} - {{ $position->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="status">Status</label>
<select class="form-select" id="status" name="status" required>
@foreach (['draft', 'submitted', 'under_ppm_review', 'approved', 'rejected', 'assigned', 'cancelled'] as $status)
<option value="{{ $status }}" @selected(old('status', $application->status) === $status)>{{ str_replace('_', ' ', $status) }}</option>
@endforeach
</select>
</div>
@endif
<div class="col-md-6">
<label class="form-label" for="name">Nama</label>
<input class="form-control" id="name" name="name" value="{{ old('name', $application->name) }}" required>
</div>
<div class="col-md-6">
<label class="form-label" for="ic_number">No. IC</label>
<input class="form-control" id="ic_number" name="ic_number" value="{{ old('ic_number', $application->ic_number) }}" maxlength="12" required>
</div>
<div class="col-md-6">
<label class="form-label" for="phone_number">Telefon</label>
<input class="form-control" id="phone_number" name="phone_number" value="{{ old('phone_number', $application->phone_number) }}" required>
</div>
<div class="col-md-6">
<label class="form-label" for="email">Email</label>
<input class="form-control" id="email" name="email" type="email" value="{{ old('email', $application->email) }}">
</div>
<div class="col-12">
<label class="form-label" for="address">Alamat</label>
<textarea class="form-control" id="address" name="address" rows="2">{{ old('address', $application->address) }}</textarea>
</div>
<div class="col-md-6">
<label class="form-label" for="bank_name">Bank</label>
<input class="form-control" id="bank_name" name="bank_name" value="{{ old('bank_name', $application->bank_name) }}">
</div>
<div class="col-md-6">
<label class="form-label" for="bank_account_number">No. Akaun Bank</label>
<input class="form-control" id="bank_account_number" name="bank_account_number" value="{{ old('bank_account_number', $application->bank_account_number) }}">
</div>
<div class="col-12">
<label class="form-label" for="note">Catatan</label>
<textarea class="form-control" id="note" name="note" rows="2" placeholder="Wajib selepas tempoh pendaftaran ditutup">{{ old('note') }}</textarea>
</div>
</div>

View File

@@ -0,0 +1,24 @@
@extends('layouts.app')
@section('title', 'Manual Entry')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<h1 class="h3 mb-1">Manual Entry Permohonan</h1>
<p class="text-secondary mb-0">Rekod yang dimasukkan Admin tidak menghantar email permintaan butiran bank.</p>
</div>
@include('admin.management._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.management.applications.store') }}">
@csrf
@include('admin.management.applications._form')
<div class="d-flex justify-content-end gap-2 mt-4">
<a class="btn btn-outline-secondary" href="{{ route('admin.management.applications.index') }}">Batal</a>
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Simpan</button>
</div>
</form>
</div>
@endsection

View File

@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('title', 'Edit Permohonan')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Edit Permohonan</h1>
<p class="text-secondary mb-0">{{ $application->name }} · {{ $application->public_uuid }}</p>
</div>
</div>
</div>
@include('admin.management._nav')
<div class="row g-3">
<div class="col-12 col-xl-7">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Maklumat Pemohon</h2>
<form method="POST" action="{{ route('admin.management.applications.update', $application->public_uuid) }}">
@csrf
@method('PATCH')
@include('admin.management.applications._form')
<div class="d-flex justify-content-end gap-2 mt-4">
<a class="btn btn-outline-secondary" href="{{ route('admin.management.applications.index') }}">Kembali</a>
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Kemas Kini</button>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Direct Assignment</h2>
<form method="POST" action="{{ route('admin.management.applications.assign', $application->public_uuid) }}">
@csrf
@include('admin.management.applications._assignment_form')
<div class="d-flex justify-content-end mt-4">
<button class="btn btn-primary" type="submit"><i class="bi bi-person-check me-1"></i>Assign</button>
</div>
</form>
</div>
<div class="content-panel p-3 p-md-4 mt-3">
<h2 class="h6 mb-3">Sejarah Status</h2>
<div class="list-group list-group-flush">
@forelse ($application->statusHistories->sortByDesc('created_at') as $history)
<div class="list-group-item px-0">
<div class="fw-semibold">{{ $history->from_status ?: '-' }} {{ $history->to_status }}</div>
<div class="text-secondary small">{{ $history->created_at?->format('d/m/Y H:i') }} · {{ $history->note ?: '-' }}</div>
</div>
@empty
<div class="text-secondary">Tiada sejarah.</div>
@endforelse
</div>
</div>
<div class="content-panel p-3 p-md-4 mt-3">
<h2 class="h6 mb-3">Dokumen</h2>
@forelse ($application->documents as $document)
<div class="d-flex justify-content-between align-items-center border-bottom py-2">
<div>
<div class="fw-semibold">{{ str_replace('_', ' ', $document->document_type) }}</div>
<div class="small text-secondary">{{ $document->original_name }}</div>
</div>
<a class="btn btn-sm btn-outline-primary" href="{{ route('admin.documents.download', $document) }}">
<i class="bi bi-download me-1"></i>Muat turun
</a>
</div>
@empty
<div class="text-secondary">Tiada dokumen dimuat naik.</div>
@endforelse
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,87 @@
@extends('layouts.app')
@section('title', 'Senarai Permohonan')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Senarai Permohonan</h1>
<p class="text-secondary mb-0">Paparan global untuk semakan, edit, dan direct assignment.</p>
</div>
<div><a class="btn btn-primary" href="{{ route('admin.management.applications.create') }}"><i class="bi bi-plus-circle me-1"></i>Manual Entry</a></div>
</div>
</div>
@include('admin.management._nav')
<div class="content-panel p-3 mb-3">
<form class="row g-2" method="GET">
<div class="col-12 col-md-3"><input class="form-control" name="q" value="{{ $filters['q'] ?? '' }}" placeholder="Cari nama / IC / email"></div>
<div class="col-6 col-md-3">
<select class="form-select" name="status">
<option value="">Semua status</option>
@foreach (['submitted', 'under_ppm_review', 'approved', 'assigned', 'rejected', 'cancelled'] as $status)
<option value="{{ $status }}" @selected(($filters['status'] ?? '') === $status)>{{ str_replace('_', ' ', $status) }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-3">
<select class="form-select" name="pusat_mengundi_id">
<option value="">Semua pusat</option>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}" @selected(($filters['pusat_mengundi_id'] ?? '') == $pusat->id)>{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-2">
<select class="form-select" name="position_id">
<option value="">Semua jawatan</option>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected(($filters['position_id'] ?? '') == $position->id)>{{ $position->code }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-1"><button class="btn btn-outline-primary w-100" type="submit"><i class="bi bi-search"></i></button></div>
</form>
</div>
<div class="content-panel p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<x-sort-th column="name" label="Nama" />
<th>Pusat</th>
<th>Jawatan</th>
<x-sort-th column="status" label="Status" />
<th>Sumber</th>
<x-sort-th column="created_at" label="Tarikh" />
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($applications as $application)
<tr>
<td>
<div class="fw-semibold">{{ $application->name }}</div>
<div class="text-secondary small">{{ $application->ic_number }} · {{ $application->email ?: '-' }}</div>
</td>
<td>{{ $application->pusatMengundi?->code }}<div class="small text-secondary">{{ $application->pusatMengundi?->name }}</div></td>
<td>{{ $application->approvedPosition?->code ?: $application->requestedPosition?->code }}</td>
<td><span class="badge text-bg-secondary">{{ str_replace('_', ' ', $application->status) }}</span></td>
<td>{{ $application->source }}</td>
<td class="small text-secondary">{{ $application->created_at?->format('d/m/Y') }}</td>
<td class="text-end"><a class="btn btn-sm btn-outline-primary" href="{{ route('admin.management.applications.edit', $application->public_uuid) }}">Edit</a></td>
</tr>
@empty
<tr><td colspan="7" class="text-center text-secondary py-4">Tiada permohonan dijumpai.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="mt-3">{{ $applications->links() }}</div>
@endsection

View File

@@ -0,0 +1,89 @@
@extends('layouts.app')
@section('title', 'Edit Tugasan')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<h1 class="h3 mb-1">Edit Tugasan</h1>
<p class="text-secondary mb-0">{{ $assignment->application?->name ?: $assignment->user?->name ?: 'Tugasan staf' }}</p>
</div>
@include('admin.management._nav')
<div class="row g-3">
<div class="col-12 col-xl-7">
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.management.assignments.update', $assignment) }}">
@csrf
@method('PATCH')
<div class="row g-3">
<div class="col-md-6">
<label class="form-label" for="position_id">Jawatan</label>
<select class="form-select" id="position_id" name="position_id" required>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected(old('position_id', $assignment->position_id) == $position->id)>{{ $position->code }} - {{ $position->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="pusat_mengundi_id">Pusat Mengundi</label>
<select class="form-select" id="pusat_mengundi_id" name="pusat_mengundi_id" required>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}" @selected(old('pusat_mengundi_id', $assignment->pusat_mengundi_id) == $pusat->id)>{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="saluran_mengundi_id">Saluran</label>
<select class="form-select" id="saluran_mengundi_id" name="saluran_mengundi_id">
<option value="">Pusat level / ikut KTM</option>
@foreach ($salurans as $saluran)
<option value="{{ $saluran->id }}" @selected(old('saluran_mengundi_id', $assignment->saluran_mengundi_id) == $saluran->id)>{{ $saluran->pusatMengundi?->code }} - Saluran {{ $saluran->number }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="reports_to_assignment_id">KTM Ketua</label>
<select class="form-select" id="reports_to_assignment_id" name="reports_to_assignment_id">
<option value="">Tiada</option>
@foreach ($ktmAssignments as $ktm)
<option value="{{ $ktm->id }}" @selected(old('reports_to_assignment_id', $assignment->reports_to_assignment_id) == $ktm->id)>{{ $ktm->user?->name ?? 'KTM' }} - {{ $ktm->pusatMengundi?->code }} / Saluran {{ $ktm->saluranMengundi?->number }}</option>
@endforeach
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="status">Status</label>
<select class="form-select" id="status" name="status" required>
@foreach (['active', 'inactive', 'replaced', 'cancelled'] as $status)
<option value="{{ $status }}" @selected(old('status', $assignment->status) === $status)>{{ $status }}</option>
@endforeach
</select>
</div>
<div class="col-12">
<label class="form-label" for="note">Catatan</label>
<textarea class="form-control" id="note" name="note" rows="2" placeholder="Wajib selepas tempoh pendaftaran ditutup">{{ old('note') }}</textarea>
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a class="btn btn-outline-secondary" href="{{ route('admin.management.assignments.index') }}">Kembali</a>
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Kemas Kini</button>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="content-panel p-3 p-md-4">
<h2 class="h6 mb-3">Sejarah Tugasan</h2>
@forelse ($assignment->histories->sortByDesc('created_at') as $history)
<div class="border-bottom py-2">
<div class="small text-secondary">{{ $history->created_at?->format('d/m/Y H:i') }}</div>
<div>{{ $history->note ?: '-' }}</div>
</div>
@empty
<div class="text-secondary">Tiada sejarah.</div>
@endforelse
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,79 @@
@extends('layouts.app')
@section('title', 'Senarai Tugasan')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<h1 class="h3 mb-1">Senarai Tugasan Staf</h1>
<p class="text-secondary mb-0">Semak dan ubah penempatan staf secara langsung.</p>
</div>
@include('admin.management._nav')
<div class="content-panel p-3 mb-3">
<form class="row g-2" method="GET">
<div class="col-12 col-md-4">
<select class="form-select" name="pusat_mengundi_id">
<option value="">Semua pusat</option>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}" @selected(($filters['pusat_mengundi_id'] ?? '') == $pusat->id)>{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-3">
<select class="form-select" name="position_id">
<option value="">Semua jawatan</option>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected(($filters['position_id'] ?? '') == $position->id)>{{ $position->code }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-3">
<select class="form-select" name="status">
<option value="">Semua status</option>
@foreach (['active', 'inactive', 'replaced', 'cancelled'] as $status)
<option value="{{ $status }}" @selected(($filters['status'] ?? '') === $status)>{{ $status }}</option>
@endforeach
</select>
</div>
<div class="col-12 col-md-2"><button class="btn btn-outline-primary w-100" type="submit"><i class="bi bi-funnel me-1"></i>Tapis</button></div>
</form>
</div>
<div class="content-panel p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Staf</th>
<th>Jawatan</th>
<th>Pusat / Saluran</th>
<th>KTM Ketua</th>
<x-sort-th column="status" label="Status" />
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($assignments as $assignment)
<tr>
<td>
<div class="fw-semibold">{{ $assignment->application?->name ?: $assignment->user?->name ?: 'Tanpa nama' }}</div>
<div class="small text-secondary">{{ $assignment->application?->ic_number ?: $assignment->user?->email ?: '-' }}</div>
</td>
<td>{{ $assignment->position?->code }}</td>
<td>{{ $assignment->pusatMengundi?->code }}<div class="small text-secondary">{{ $assignment->saluranMengundi ? 'Saluran '.$assignment->saluranMengundi->number : 'Pusat level' }}</div></td>
<td>{{ $assignment->reportsTo?->user?->name ?: '-' }}</td>
<td><span class="badge text-bg-secondary">{{ $assignment->status }}</span></td>
<td class="text-end"><a class="btn btn-sm btn-outline-primary" href="{{ route('admin.management.assignments.edit', $assignment) }}">Edit</a></td>
</tr>
@empty
<tr><td colspan="6" class="text-center text-secondary py-4">Tiada tugasan dijumpai.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="mt-3">{{ $assignments->links() }}</div>
@endsection

View File

@@ -0,0 +1,53 @@
@extends('layouts.app')
@section('title', 'Pengurusan Admin')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Pengurusan Admin</h1>
<p class="text-secondary mb-0">Senarai global, manual entry, direct assignment, wakil agensi, dan catatan perubahan selepas pendaftaran tutup.</p>
</div>
</div>
</div>
@include('admin.management._nav')
<div class="row g-3">
@foreach ([
['label' => 'Permohonan', 'value' => $counts['applications'], 'icon' => 'bi-file-earmark-person', 'tone' => 'primary'],
['label' => 'Assigned', 'value' => $counts['assigned'], 'icon' => 'bi-person-check', 'tone' => 'success'],
['label' => 'Tugasan aktif', 'value' => $counts['staffAssignments'], 'icon' => 'bi-person-lines-fill', 'tone' => 'info'],
['label' => 'Wakil agensi', 'value' => $counts['representatives'], 'icon' => 'bi-building-check', 'tone' => 'secondary'],
['label' => 'Dokumen tidak lengkap', 'value' => $counts['missingDocuments'], 'icon' => 'bi-file-earmark-x', 'tone' => 'danger'],
['label' => 'Bank tidak lengkap', 'value' => $counts['incompleteBank'], 'icon' => 'bi-bank', 'tone' => 'warning'],
['label' => 'Kerusi roda', 'value' => $counts['wheelchairAllocated'], 'icon' => 'bi-universal-access', 'tone' => 'primary'],
['label' => 'Belum dipulang', 'value' => $counts['wheelchairOutstanding'], 'icon' => 'bi-arrow-return-left', 'tone' => 'warning'],
] as $card)
<div class="col-12 col-sm-6 col-xl-3">
<div class="stat-card h-100">
<div class="d-flex justify-content-between gap-3">
<div>
<div class="text-secondary small">{{ $card['label'] }}</div>
<div class="fs-3 fw-bold mt-2">{{ $card['value'] }}</div>
</div>
<span class="stat-icon text-bg-{{ $card['tone'] }}"><i class="bi {{ $card['icon'] }}"></i></span>
</div>
</div>
</div>
@endforeach
</div>
<div class="content-panel mt-4 p-3 p-md-4">
<h2 class="h5 mb-3">Tindakan</h2>
<div class="row g-2">
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.management.applications.create') }}"><i class="bi bi-plus-circle me-2"></i>Manual Entry</a></div>
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.management.applications.index') }}"><i class="bi bi-search me-2"></i>Senarai Permohonan</a></div>
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.management.assignments.index') }}"><i class="bi bi-person-lines-fill me-2"></i>Senarai Tugasan</a></div>
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.management.representatives.index') }}"><i class="bi bi-building-check me-2"></i>Wakil Agensi</a></div>
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.management.wheelchairs.index') }}"><i class="bi bi-universal-access me-2"></i>Kerusi Roda</a></div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,78 @@
@extends('layouts.app')
@section('title', 'Pindah Petugas')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin · <a href="{{ route('admin.management.penempatan.show', $pusat) }}">{{ $pusat->name }}</a></div>
<h1 class="h3 mb-0">Pindah Petugas</h1>
</div>
@include('admin.management._nav')
<div class="row g-3">
<div class="col-12 col-xl-5">
{{-- Maklumat petugas semasa --}}
<div class="content-panel p-3 p-md-4 mb-3">
<h2 class="h6 fw-semibold text-secondary mb-3">Maklumat Petugas Semasa</h2>
<dl class="row mb-0">
<dt class="col-5 text-secondary small">Nama</dt>
<dd class="col-7">{{ $assignment->application?->name ?? $assignment->user?->name ?? '-' }}</dd>
<dt class="col-5 text-secondary small">No. KP</dt>
<dd class="col-7 small">{{ $assignment->application?->ic_number ?? '-' }}</dd>
<dt class="col-5 text-secondary small">Jawatan</dt>
<dd class="col-7"><span class="badge text-bg-primary">{{ $assignment->position?->code }}</span></dd>
<dt class="col-5 text-secondary small">Pusat Semasa</dt>
<dd class="col-7">{{ $pusat->code }} {{ $pusat->name }}</dd>
<dt class="col-5 text-secondary small">Saluran Semasa</dt>
<dd class="col-7">{{ $assignment->saluranMengundi ? 'Saluran '.$assignment->saluranMengundi->number : 'Peringkat Pusat' }}</dd>
</dl>
</div>
{{-- Form pindah --}}
<div class="content-panel p-3 p-md-4">
<h2 class="h6 fw-semibold mb-3">Pindah ke Lokasi Baru</h2>
<form method="POST" action="{{ route('admin.management.penempatan.pindah', [$pusat, $assignment]) }}">
@csrf
<div class="mb-3">
<label class="form-label fw-semibold" for="pusat_mengundi_id">Pusat Mengundi Baru</label>
<select id="pusat_mengundi_id" name="pusat_mengundi_id"
class="form-select @error('pusat_mengundi_id') is-invalid @enderror"
required data-tom-select>
@foreach ($pusats as $p)
<option value="{{ $p->id }}" @selected($p->id === $pusat->id)>
{{ $p->code }} {{ $p->name }}
@if ($p->election) ({{ $p->election->name }}) @endif
</option>
@endforeach
</select>
@error('pusat_mengundi_id') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="mb-4">
<label class="form-label fw-semibold" for="saluran_mengundi_id">Saluran (pilihan)</label>
<select id="saluran_mengundi_id" name="saluran_mengundi_id"
class="form-select @error('saluran_mengundi_id') is-invalid @enderror">
<option value="">Peringkat Pusat (Saluran 0)</option>
@foreach ($salurans as $saluran)
<option value="{{ $saluran->id }}" @selected($saluran->id === $assignment->saluran_mengundi_id)>
Saluran {{ $saluran->number }}
@if ($saluran->name) {{ $saluran->name }} @endif
</option>
@endforeach
</select>
<div class="form-text">Senarai saluran akan dikemaskini apabila pusat baru dipilih.</div>
@error('saluran_mengundi_id') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="d-flex justify-content-end gap-2">
<a href="{{ route('admin.management.penempatan.show', $pusat) }}" class="btn btn-outline-secondary">Batal</a>
<button type="submit" class="btn btn-primary">
<i class="bi bi-arrow-left-right me-1"></i>Sahkan Pindah
</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,146 @@
@extends('layouts.app')
@section('title', 'Penempatan — ' . $pusat->name)
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin · <a href="{{ route('admin.dashboard') }}">Dashboard</a></div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Penempatan Petugas</h1>
<p class="text-secondary mb-0">
{{ $pusat->code }} {{ $pusat->name }}
@if ($pusat->dun) · DUN {{ $pusat->dun->code }} @endif
@if ($ppm) · PPM: {{ $ppm->application?->name }} @endif
</p>
</div>
<div class="d-flex gap-2 align-items-start">
<a href="{{ route('admin.dashboard') }}" class="btn btn-outline-secondary">Kembali</a>
<a href="{{ route('admin.management.applications.index', ['pusat_mengundi_id' => $pusat->id]) }}"
class="btn btn-outline-primary">
<i class="bi bi-file-earmark-person me-1"></i>Permohonan Pusat Ini
</a>
</div>
</div>
</div>
@include('admin.management._nav')
@forelse ($sections as $section)
<div class="content-panel p-0 overflow-hidden mb-4">
{{-- Section header --}}
<div class="p-3 border-bottom d-flex justify-content-between align-items-center">
<h2 class="h6 mb-0 fw-semibold">
@if ($section['saluran'])
<i class="bi bi-signpost me-2 text-primary"></i>{{ $section['label'] }}
<span class="text-secondary fw-normal small ms-2">{{ $section['saluran']->voter_count ?? 0 }} pengundi</span>
@else
<i class="bi bi-building me-2 text-secondary"></i>{{ $section['label'] }}
@endif
</h2>
@php
$sectionFilled = $section['entries']->sum(fn($e) => $e['assignments']->count());
$sectionQuota = $section['entries']->sum(fn($e) => $e['quota']->quota);
$sectionVacant = $section['entries']->sum(fn($e) => $e['vacant']);
@endphp
<span class="badge {{ $sectionVacant > 0 ? 'text-bg-warning' : 'text-bg-success' }}">
{{ $sectionFilled }} / {{ $sectionQuota }} diisi
</span>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>Jawatan</th>
<th>Nama Petugas</th>
<th>No. KP</th>
<th>Telefon</th>
<th>Bank</th>
<th>No. Akaun</th>
<th class="text-end">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($section['entries'] as $entry)
{{-- Filled slots --}}
@foreach ($entry['assignments'] as $assignment)
<tr>
@if ($loop->first)
<td rowspan="{{ $entry['assignments']->count() + $entry['vacant'] }}"
class="fw-semibold align-top pt-3">
{{ $entry['position']?->code }}
<div class="small text-secondary fw-normal">{{ $entry['position']?->name }}</div>
<div class="small text-secondary">Kuota: {{ $entry['quota']->quota }}</div>
</td>
@endif
@php $app = $assignment->application; @endphp
<td>
<div class="fw-semibold">{{ $app?->name ?? $assignment->user?->name ?? 'Tanpa nama' }}</div>
<span class="badge text-bg-success small">Aktif</span>
</td>
<td class="small text-secondary">{{ $app?->ic_number ?? '-' }}</td>
<td class="small text-secondary">{{ $app?->phone_number ?? '-' }}</td>
<td class="small">{{ $app?->bank_name ?? '-' }}</td>
<td class="small text-secondary">
{{ $app?->bank_account_number ? \App\Support\SensitiveData::maskAccount($app->bank_account_number) : '-' }}
</td>
<td class="text-end">
<a href="{{ route('admin.management.penempatan.pindah.form', [$pusat, $assignment]) }}"
class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-left-right"></i> Pindah
</a>
<form method="POST"
action="{{ route('admin.management.penempatan.lepas', [$pusat, $assignment]) }}"
class="d-inline"
onsubmit="return confirm('Lepaskan {{ addslashes($app?->name ?? 'petugas') }} dari tugasan ini?')">
@csrf
<button class="btn btn-sm btn-outline-danger">
<i class="bi bi-person-dash"></i> Lepas
</button>
</form>
</td>
</tr>
@endforeach
{{-- Vacant slots --}}
@for ($i = 0; $i < $entry['vacant']; $i++)
<tr class="table-warning">
@if ($entry['assignments']->isEmpty() && $i === 0)
<td rowspan="{{ $entry['vacant'] }}"
class="fw-semibold align-top pt-3">
{{ $entry['position']?->code }}
<div class="small text-secondary fw-normal">{{ $entry['position']?->name }}</div>
<div class="small text-secondary">Kuota: {{ $entry['quota']->quota }}</div>
</td>
@endif
<td colspan="5" class="text-secondary fst-italic small"> Slot kosong </td>
<td class="text-end">
<a href="{{ route('admin.management.applications.index', [
'pusat_mengundi_id' => $pusat->id,
'position_id' => $entry['position']?->id,
'status' => 'approved',
]) }}"
class="btn btn-sm btn-success">
<i class="bi bi-person-plus me-1"></i>Assign
</a>
</td>
</tr>
@endfor
@empty
<tr>
<td colspan="7" class="text-center text-secondary py-3">Tiada kuota ditetapkan untuk bahagian ini.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@empty
<div class="content-panel p-4 text-center text-secondary">
<i class="bi bi-info-circle fs-3 d-block mb-2"></i>
Tiada kuota ditetapkan untuk pusat ini. <a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">Tetapkan kuota</a>.
</div>
@endforelse
@endsection

View File

@@ -0,0 +1,96 @@
@extends('layouts.app')
@section('title', 'Wakil Agensi')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<h1 class="h3 mb-1">Wakil Polis, KKM dan JKM</h1>
<p class="text-secondary mb-0">Kemasukan manual wakil agensi untuk Pusat Mengundi dan Saluran.</p>
</div>
@include('admin.management._nav')
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Tambah Wakil</h2>
<form method="POST" action="{{ route('admin.management.representatives.store') }}">
@csrf
<div class="row g-3">
<div class="col-md-3">
<label class="form-label" for="type">Jenis</label>
<select class="form-select" id="type" name="type" required>
<option value="police">Polis Pengiring</option>
<option value="kkm">Wakil KKM</option>
<option value="jkm">Wakil JKM</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="election_id">Pilihanraya</label>
<select class="form-select" id="election_id" name="election_id" required>
@foreach ($elections as $election)
<option value="{{ $election->id }}">{{ $election->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="pusat_mengundi_id">Pusat</label>
<select class="form-select" id="pusat_mengundi_id" name="pusat_mengundi_id" required>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}">{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="saluran_mengundi_id">Saluran Polis</label>
<select class="form-select" id="saluran_mengundi_id" name="saluran_mengundi_id">
<option value="">Tiada / Pusat level</option>
@foreach ($salurans as $saluran)
<option value="{{ $saluran->id }}">{{ $saluran->pusatMengundi?->code }} - Saluran {{ $saluran->number }}</option>
@endforeach
</select>
</div>
<div class="col-md-4"><label class="form-label" for="name">Nama</label><input class="form-control" id="name" name="name" required></div>
<div class="col-md-4"><label class="form-label" for="ic_number">No. IC</label><input class="form-control" id="ic_number" name="ic_number" maxlength="12"></div>
<div class="col-md-4"><label class="form-label" for="phone_number">Telefon</label><input class="form-control" id="phone_number" name="phone_number"></div>
<div class="col-md-4"><label class="form-label" for="rank">Pangkat Polis</label><input class="form-control" id="rank" name="rank"></div>
<div class="col-md-4"><label class="form-label" for="station">Balai Polis</label><input class="form-control" id="station" name="station"></div>
<div class="col-md-4"><label class="form-label" for="agency">Agensi</label><input class="form-control" id="agency" name="agency"></div>
<div class="col-md-6"><label class="form-label" for="notes">Nota Operasi</label><textarea class="form-control" id="notes" name="notes" rows="2"></textarea></div>
<div class="col-md-6"><label class="form-label" for="note">Catatan Admin</label><textarea class="form-control" id="note" name="note" rows="2" placeholder="Wajib selepas tempoh pendaftaran ditutup"></textarea></div>
</div>
<div class="d-flex justify-content-end mt-3"><button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Simpan</button></div>
</form>
</div>
@foreach ([['title' => 'Polis Pengiring', 'items' => $policeEscorts, 'type' => 'police'], ['title' => 'Wakil KKM', 'items' => $kkmRepresentatives, 'type' => 'kkm'], ['title' => 'Wakil JKM', 'items' => $jkmRepresentatives, 'type' => 'jkm']] as $section)
<div class="content-panel p-0 overflow-hidden mb-4">
<div class="p-3 border-bottom fw-semibold">{{ $section['title'] }}</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead><tr><th>Nama</th><th>Pusat</th><th>Telefon</th><th>Nota</th><th class="text-end no-sort">Tindakan</th></tr></thead>
<tbody>
@forelse ($section['items'] as $item)
<tr>
<td>{{ $item->name }}<div class="small text-secondary">{{ $item->ic_number ?: '-' }}</div></td>
<td>{{ $item->pusatMengundi?->code }} @if($section['type'] === 'police' && $item->saluranMengundi) · Saluran {{ $item->saluranMengundi->number }} @endif</td>
<td>{{ $item->phone_number ?: '-' }}</td>
<td>{{ $item->notes ?: '-' }}</td>
<td class="text-end">
<form method="POST" action="{{ route('admin.management.representatives.destroy', [$section['type'], $item->id]) }}" onsubmit="return confirm('Padam rekod ini?')">
@csrf
@method('DELETE')
<input type="hidden" name="note" value="Dipadam oleh Admin.">
<button class="btn btn-sm btn-outline-danger" type="submit"><i class="bi bi-trash"></i></button>
</form>
</td>
</tr>
@empty
<tr><td colspan="5" class="text-center text-secondary py-4">Tiada rekod.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="p-3">{{ $section['items']->links() }}</div>
</div>
@endforeach
@endsection

View File

@@ -0,0 +1,91 @@
@extends('layouts.app')
@section('title', 'Kerusi Roda')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Pengurusan Kerusi Roda</h1>
<p class="text-secondary mb-0">Peruntukan, rekod ambil, rekod pulang, keadaan, dan catatan kerusi roda.</p>
</div>
</div>
</div>
@include('admin.management._nav')
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Tetapkan Peruntukan</h2>
<form method="POST" action="{{ route('admin.management.wheelchairs.store') }}">
@csrf
<div class="row g-3">
<div class="col-md-3">
<label class="form-label" for="election_id">Pilihanraya</label>
<select class="form-select" id="election_id" name="election_id" required>
@foreach ($elections as $election)
<option value="{{ $election->id }}">{{ $election->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-5">
<label class="form-label" for="pusat_mengundi_id">Pusat Mengundi</label>
<select class="form-select" id="pusat_mengundi_id" name="pusat_mengundi_id" required>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}">{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<label class="form-label" for="allocated_quantity">Jumlah</label>
<input class="form-control" id="allocated_quantity" name="allocated_quantity" type="number" min="0" value="0" required>
</div>
<div class="col-md-2 d-flex align-items-end">
<button class="btn btn-primary w-100" type="submit"><i class="bi bi-save me-1"></i>Simpan</button>
</div>
<div class="col-12">
<label class="form-label" for="notes">Nota</label>
<textarea class="form-control" id="notes" name="notes" rows="2"></textarea>
</div>
</div>
</form>
</div>
<div class="content-panel p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Pusat Mengundi</th>
<th>Peruntukan</th>
<th>Diambil</th>
<th>Dipulang</th>
<th>Belum Dipulang</th>
<th>Baki Boleh Diambil</th>
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($allocations as $allocation)
<tr>
<td>
<div class="fw-semibold">{{ $allocation->pusatMengundi?->code }} - {{ $allocation->pusatMengundi?->name }}</div>
<div class="small text-secondary">{{ $allocation->election?->name }}</div>
</td>
<td>{{ $allocation->allocated_quantity }}</td>
<td>{{ $service->takenQuantity($allocation) }}</td>
<td>{{ $service->returnedQuantity($allocation) }}</td>
<td><span class="badge text-bg-warning">{{ $service->outstandingQuantity($allocation) }}</span></td>
<td><span class="badge text-bg-success">{{ $service->availableQuantity($allocation) }}</span></td>
<td class="text-end"><a class="btn btn-sm btn-outline-primary" href="{{ route('admin.management.wheelchairs.show', $allocation) }}">Urus</a></td>
</tr>
@empty
<tr><td colspan="7" class="text-center text-secondary py-4">Tiada peruntukan kerusi roda.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="mt-3">{{ $allocations->links() }}</div>
@endsection

View File

@@ -0,0 +1,232 @@
@extends('layouts.app')
@section('title', 'Rekod Kerusi Roda')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Rekod Kerusi Roda</h1>
<p class="text-secondary mb-0">{{ $allocation->pusatMengundi?->code }} {{ $allocation->pusatMengundi?->name }}</p>
</div>
<div><a class="btn btn-outline-secondary" href="{{ route('admin.management.wheelchairs.index') }}">Kembali</a></div>
</div>
</div>
@include('admin.management._nav')
@php
$taken = $service->takenQuantity($allocation);
$returned = $service->returnedQuantity($allocation);
$outstanding = $service->outstandingQuantity($allocation);
$available = $service->availableQuantity($allocation);
@endphp
<div class="row g-3 mb-4">
@foreach ([
['label' => 'Peruntukan', 'value' => $allocation->allocated_quantity, 'tone' => 'primary'],
['label' => 'Diambil', 'value' => $taken, 'tone' => 'info'],
['label' => 'Dipulang', 'value' => $returned, 'tone' => 'success'],
['label' => 'Belum dipulang', 'value' => $outstanding, 'tone' => 'warning'],
['label' => 'Baki boleh diambil','value' => $available, 'tone' => 'secondary'],
] as $card)
<div class="col-12 col-sm-6 col-xl">
<div class="stat-card h-100">
<div class="text-secondary small">{{ $card['label'] }}</div>
<div class="fs-3 fw-bold mt-2 text-{{ $card['tone'] }}">{{ $card['value'] }}</div>
</div>
</div>
@endforeach
</div>
<div class="row g-3">
{{-- Kiri: 3 panel borang --}}
<div class="col-12 col-xl-5">
{{-- Kemaskini Peruntukan --}}
<div class="content-panel p-3 p-md-4 mb-3">
<h2 class="h5 mb-3">Kemaskini Peruntukan</h2>
<form method="POST" action="{{ route('admin.management.wheelchairs.update', $allocation) }}">
@csrf @method('PATCH')
<input type="hidden" name="election_id" value="{{ $allocation->election_id }}">
<input type="hidden" name="pusat_mengundi_id" value="{{ $allocation->pusat_mengundi_id }}">
<div class="mb-3">
<label class="form-label" for="allocated_quantity">Jumlah Peruntukan</label>
<input class="form-control @error('allocated_quantity') is-invalid @enderror"
id="allocated_quantity" name="allocated_quantity" type="number" min="{{ $outstanding }}"
value="{{ old('allocated_quantity', $allocation->allocated_quantity) }}" required>
<div class="form-text">Minimum {{ $outstanding }} (kerusi belum dipulang).</div>
@error('allocated_quantity') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label class="form-label" for="notes">Nota</label>
<textarea class="form-control" id="notes" name="notes" rows="2">{{ old('notes', $allocation->notes) }}</textarea>
</div>
<div class="d-flex justify-content-end">
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Simpan</button>
</div>
</form>
</div>
{{-- Rekod Ambil --}}
<div class="content-panel p-3 p-md-4 mb-3 border-start border-4 border-info">
<h2 class="h5 mb-1">Rekod Ambil</h2>
<p class="text-secondary small mb-3">Baki boleh diambil: <strong class="text-info">{{ $available }}</strong></p>
@if ($available === 0)
<div class="alert alert-secondary py-2 mb-0">Tiada baki kerusi roda untuk diambil.</div>
@else
<form method="POST" action="{{ route('admin.management.wheelchairs.ambil', $allocation) }}">
@csrf
<div class="row g-3">
<div class="col-md-6">
<label class="form-label" for="taken_quantity">Jumlah Ambil</label>
<input class="form-control @error('quantity') is-invalid @enderror"
id="taken_quantity" name="quantity" type="number" min="1" max="{{ $available }}"
value="{{ old('quantity') }}" required>
<div class="form-text">Maks: {{ $available }}</div>
@error('quantity') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="col-md-6">
<label class="form-label" for="taken_at">Masa Ambil</label>
<input class="form-control @error('taken_at') is-invalid @enderror"
id="taken_at" name="taken_at" type="datetime-local"
value="{{ old('taken_at', now()->format('Y-m-d\TH:i')) }}">
@error('taken_at') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="col-12">
<label class="form-label" for="taken_by_name">Diambil Oleh <span class="text-danger">*</span></label>
<input class="form-control @error('taken_by_name') is-invalid @enderror"
id="taken_by_name" name="taken_by_name"
value="{{ old('taken_by_name') }}" required>
@error('taken_by_name') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="col-12">
<label class="form-label" for="taken_notes">Nota</label>
<textarea class="form-control" id="taken_notes" name="notes" rows="2">{{ old('notes') }}</textarea>
</div>
</div>
<div class="d-flex justify-content-end mt-3">
<button class="btn btn-info text-white" type="submit"><i class="bi bi-box-arrow-up-right me-1"></i>Rekod Ambil</button>
</div>
</form>
@endif
</div>
{{-- Rekod Pulang --}}
<div class="content-panel p-3 p-md-4 border-start border-4 border-success">
<h2 class="h5 mb-1">Rekod Pulang</h2>
<p class="text-secondary small mb-3">Belum dipulang: <strong class="text-warning">{{ $outstanding }}</strong></p>
@if ($outstanding === 0)
<div class="alert alert-secondary py-2 mb-0">Tiada kerusi roda yang perlu dipulangkan.</div>
@else
<form method="POST" action="{{ route('admin.management.wheelchairs.pulang', $allocation) }}">
@csrf
<div class="row g-3">
<div class="col-md-6">
<label class="form-label" for="return_quantity">Jumlah Pulang</label>
<input class="form-control @error('quantity') is-invalid @enderror"
id="return_quantity" name="quantity" type="number" min="1" max="{{ $outstanding }}"
value="{{ old('quantity') }}" required>
<div class="form-text">Maks: {{ $outstanding }}</div>
@error('quantity') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="col-md-6">
<label class="form-label" for="returned_at">Masa Pulang</label>
<input class="form-control @error('returned_at') is-invalid @enderror"
id="returned_at" name="returned_at" type="datetime-local"
value="{{ old('returned_at', now()->format('Y-m-d\TH:i')) }}">
@error('returned_at') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="col-12">
<label class="form-label" for="returned_by_name">Dipulang Oleh <span class="text-danger">*</span></label>
<input class="form-control @error('returned_by_name') is-invalid @enderror"
id="returned_by_name" name="returned_by_name"
value="{{ old('returned_by_name') }}" required>
@error('returned_by_name') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="col-md-12">
<label class="form-label" for="return_condition">Keadaan <span class="text-danger">*</span></label>
<select class="form-select @error('return_condition') is-invalid @enderror"
id="return_condition" name="return_condition" required>
<option value="">Pilih keadaan</option>
<option value="baik" @selected(old('return_condition') === 'baik')>Baik</option>
<option value="rosak" @selected(old('return_condition') === 'rosak')>Rosak</option>
</select>
@error('return_condition') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="col-12">
<label class="form-label" for="return_notes">Nota</label>
<textarea class="form-control" id="return_notes" name="notes" rows="2">{{ old('notes') }}</textarea>
</div>
</div>
<div class="d-flex justify-content-end mt-3">
<button class="btn btn-success" type="submit"><i class="bi bi-box-arrow-in-down-left me-1"></i>Rekod Pulang</button>
</div>
</form>
@endif
</div>
</div>
{{-- Kanan: Sejarah transaksi --}}
<div class="col-12 col-xl-7">
<div class="content-panel p-0 overflow-hidden">
<div class="p-3 border-bottom fw-semibold">Sejarah Transaksi</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Jenis</th>
<th>Jml</th>
<th>Masa</th>
<th>Nama</th>
<th>Keadaan</th>
<th>Direkod Oleh</th>
<th>Nota</th>
</tr>
</thead>
<tbody>
@forelse ($allocation->transactions->sortByDesc('created_at') as $tx)
<tr>
<td>
@if ($tx->transaction_type === 'taken')
<span class="badge text-bg-info">Ambil</span>
@else
<span class="badge text-bg-success">Pulang</span>
@endif
</td>
<td class="fw-semibold">{{ $tx->quantity }}</td>
<td class="small">
{{ ($tx->taken_at ?? $tx->returned_at)?->format('d/m/Y H:i') ?? '-' }}
</td>
<td class="small">
@if ($tx->transaction_type === 'taken')
{{ $tx->taken_by_name ?: '-' }}
@else
{{ $tx->returned_by_name ?: '-' }}
@endif
</td>
<td>
@if ($tx->transaction_type === 'returned' && $tx->return_condition)
<span class="badge {{ $tx->return_condition === 'baik' ? 'text-bg-success' : 'text-bg-danger' }}">
{{ ucfirst($tx->return_condition) }}
</span>
@else
<span class="text-secondary"></span>
@endif
</td>
<td class="small text-secondary">{{ $tx->recordedBy?->name ?: '-' }}</td>
<td class="small text-secondary">{{ $tx->notes ?: '-' }}</td>
</tr>
@empty
<tr><td colspan="7" class="text-center text-secondary py-4">Tiada transaksi direkodkan.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,18 @@
<div class="content-panel p-2 mb-4">
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-sm {{ request()->routeIs('admin.setup.index') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.setup.index') }}">
<i class="bi bi-grid me-1"></i>Overview
</a>
<a class="btn btn-sm {{ request()->routeIs('admin.setup.positions.*') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.setup.positions.index') }}">Jawatan</a>
<a class="btn btn-sm {{ request()->routeIs('admin.setup.pusat.*') ? 'btn-primary' : 'btn-outline-primary' }}" href="{{ route('admin.setup.pusat.index') }}">Pusat</a>
<a class="btn btn-sm {{ request()->routeIs('admin.setup.import.*') ? 'btn-success' : 'btn-outline-success' }}" href="{{ route('admin.setup.import.create') }}">
<i class="bi bi-upload me-1"></i>Import
</a>
@php $activeElection = \App\Models\Election::query()->where('status', 'active')->first(); @endphp
@if ($activeElection)
<a class="btn btn-sm {{ request()->routeIs('admin.setup.settings.*') ? 'btn-warning' : 'btn-outline-warning' }}" href="{{ route('admin.setup.settings.edit', $activeElection) }}">
<i class="bi bi-sliders me-1"></i>Tetapan
</a>
@endif
</div>
</div>

View File

@@ -0,0 +1,25 @@
@csrf
<div class="row g-3">
<div class="col-12 col-lg-4">
<label class="form-label" for="election_id">Election</label>
<select id="election_id" name="election_id" class="form-select" required>
@foreach ($elections as $election)
<option value="{{ $election->id }}" @selected(old('election_id', $bahagian->election_id) == $election->id)>{{ $election->name }}</option>
@endforeach
</select>
</div>
<div class="col-12 col-lg-3">
<label class="form-label" for="code">Kod</label>
<input id="code" name="code" class="form-control" value="{{ old('code', $bahagian->code) }}" required>
</div>
<div class="col-12 col-lg-5">
<label class="form-label" for="name">Nama Bahagian</label>
<input id="name" name="name" class="form-control" value="{{ old('name', $bahagian->name) }}" required>
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="{{ route('admin.setup.bahagian.index') }}" class="btn btn-outline-secondary">Batal</a>
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Simpan</button>
</div>

View File

@@ -0,0 +1,13 @@
@extends('layouts.app')
@section('title', 'Tambah Bahagian')
@section('content')
<div class="page-heading"><div class="eyebrow">Setup</div><h1 class="h3 mb-0">Tambah Bahagian Pilihanraya</h1></div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.bahagian.store') }}">
@include('admin.setup.bahagian._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,14 @@
@extends('layouts.app')
@section('title', 'Edit Bahagian')
@section('content')
<div class="page-heading"><div class="eyebrow">Setup</div><h1 class="h3 mb-0">Edit Bahagian Pilihanraya</h1></div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.bahagian.update', $bahagian) }}">
@method('PUT')
@include('admin.setup.bahagian._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,45 @@
@extends('layouts.app')
@section('title', 'Bahagian Pilihanraya')
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Bahagian Pilihanraya</h1>
<p class="text-secondary mb-0">Senarai bahagian pilihanraya untuk election aktif.</p>
</div>
<a href="{{ route('admin.setup.bahagian.create') }}" class="btn btn-primary"><i class="bi bi-plus-circle me-1"></i>Tambah</a>
</div>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead><tr><x-sort-th column="code" label="Kod" /><x-sort-th column="name" label="Nama" /><th>Election</th><th class="text-end no-sort">Tindakan</th></tr></thead>
<tbody>
@forelse ($bahagians as $bahagian)
<tr>
<td class="fw-semibold">{{ $bahagian->code }}</td>
<td>{{ $bahagian->name }}</td>
<td>{{ $bahagian->election?->name }}</td>
<td class="text-end">
<a href="{{ route('admin.setup.bahagian.edit', $bahagian) }}" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="{{ route('admin.setup.bahagian.destroy', $bahagian) }}" class="d-inline" data-confirm>
@csrf @method('DELETE')
<button class="btn btn-sm btn-outline-danger" type="submit">Padam</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="4" class="text-center text-secondary py-4">Tiada rekod.</td></tr>
@endforelse
</tbody>
</table>
</div>
{{ $bahagians->links() }}
</div>
@endsection

View File

@@ -0,0 +1,33 @@
@csrf
<div class="row g-3">
<div class="col-12 col-lg-4">
<label class="form-label" for="election_id">Election</label>
<select id="election_id" name="election_id" class="form-select" required>
@foreach ($elections as $election)
<option value="{{ $election->id }}" @selected(old('election_id', $daerah->election_id) == $election->id)>{{ $election->name }}</option>
@endforeach
</select>
</div>
<div class="col-12 col-lg-4">
<label class="form-label" for="bahagian_pilihanraya_id">Bahagian</label>
<select id="bahagian_pilihanraya_id" name="bahagian_pilihanraya_id" class="form-select" required data-tom-select>
@foreach ($bahagians as $bahagian)
<option value="{{ $bahagian->id }}" @selected(old('bahagian_pilihanraya_id', $daerah->bahagian_pilihanraya_id) == $bahagian->id)>{{ $bahagian->code }} - {{ $bahagian->name }}</option>
@endforeach
</select>
</div>
<div class="col-12 col-lg-2">
<label class="form-label" for="code">Kod</label>
<input id="code" name="code" class="form-control" value="{{ old('code', $daerah->code) }}" required>
</div>
<div class="col-12 col-lg-2">
<label class="form-label" for="name">Nama</label>
<input id="name" name="name" class="form-control" value="{{ old('name', $daerah->name) }}" required>
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="{{ route('admin.setup.daerah.index') }}" class="btn btn-outline-secondary">Batal</a>
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Simpan</button>
</div>

View File

@@ -0,0 +1,7 @@
@extends('layouts.app')
@section('title', 'Tambah Daerah')
@section('content')
<div class="page-heading"><div class="eyebrow">Setup</div><h1 class="h3 mb-0">Tambah Daerah Mengundi</h1></div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4"><form method="POST" action="{{ route('admin.setup.daerah.store') }}">@include('admin.setup.daerah._form')</form></div>
@endsection

View File

@@ -0,0 +1,7 @@
@extends('layouts.app')
@section('title', 'Edit Daerah')
@section('content')
<div class="page-heading"><div class="eyebrow">Setup</div><h1 class="h3 mb-0">Edit Daerah Mengundi</h1></div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4"><form method="POST" action="{{ route('admin.setup.daerah.update', $daerah) }}">@method('PUT') @include('admin.setup.daerah._form')</form></div>
@endsection

View File

@@ -0,0 +1,41 @@
@extends('layouts.app')
@section('title', 'Daerah Mengundi')
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div><h1 class="h3 mb-1">Daerah Mengundi</h1><p class="text-secondary mb-0">Daerah mengundi di bawah bahagian pilihanraya.</p></div>
<a href="{{ route('admin.setup.daerah.create') }}" class="btn btn-primary"><i class="bi bi-plus-circle me-1"></i>Tambah</a>
</div>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead><tr><x-sort-th column="code" label="Kod" /><x-sort-th column="name" label="Nama" /><th>Bahagian</th><th>Election</th><th class="text-end no-sort">Tindakan</th></tr></thead>
<tbody>
@forelse ($daerahs as $daerah)
<tr>
<td class="fw-semibold">{{ $daerah->code }}</td>
<td>{{ $daerah->name }}</td>
<td>{{ $daerah->bahagianPilihanraya?->name }}</td>
<td>{{ $daerah->election?->name }}</td>
<td class="text-end">
<a href="{{ route('admin.setup.daerah.edit', $daerah) }}" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="{{ route('admin.setup.daerah.destroy', $daerah) }}" class="d-inline" data-confirm>
@csrf @method('DELETE')
<button class="btn btn-sm btn-outline-danger" type="submit">Padam</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="5" class="text-center text-secondary py-4">Tiada rekod.</td></tr>
@endforelse
</tbody>
</table>
</div>
{{ $daerahs->links() }}
</div>
@endsection

View File

@@ -0,0 +1,128 @@
@extends('layouts.app')
@section('title', 'Import Pusat Mengundi')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin · Setup</div>
<h1 class="h3 mb-1">Import Pusat Mengundi</h1>
<p class="text-secondary mb-0">Upload fail XLSX untuk mencipta DUN, Pusat Mengundi, Saluran, dan Kuota secara automatik.</p>
</div>
@include('admin.setup._nav')
@if (session('import_errors'))
<div class="alert alert-warning" role="alert">
<div class="fw-semibold mb-1">Beberapa baris tidak dapat diimport:</div>
<ul class="mb-0 small">
@foreach (session('import_errors') as $err)
<li>{{ $err }}</li>
@endforeach
</ul>
</div>
@endif
<div class="row g-3">
<div class="col-12 col-xl-7">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Borang Import</h2>
<form method="POST" action="{{ route('admin.setup.import.store') }}" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label for="election_id" class="form-label fw-semibold">Pilihan Raya</label>
<select id="election_id" name="election_id" class="form-select @error('election_id') is-invalid @enderror" required>
<option value="">Pilih pilihan raya</option>
@foreach ($elections as $election)
<option value="{{ $election->id }}" @selected(old('election_id') == $election->id)>
{{ $election->name }}
</option>
@endforeach
</select>
@error('election_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="row g-3 mb-3">
<div class="col-md-4">
<label for="dun_code" class="form-label fw-semibold">Kod DUN</label>
<input id="dun_code" type="text" name="dun_code" value="{{ old('dun_code', 'N49') }}"
class="form-control @error('dun_code') is-invalid @enderror" maxlength="20"
placeholder="cth: N49" required>
@error('dun_code')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-8">
<label for="dun_name" class="form-label fw-semibold">Nama DUN</label>
<input id="dun_name" type="text" name="dun_name" value="{{ old('dun_name', 'Kota Iskandar') }}"
class="form-control @error('dun_name') is-invalid @enderror" maxlength="255"
placeholder="cth: Kota Iskandar" required>
@error('dun_name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="mb-4">
<label for="file" class="form-label fw-semibold">Fail XLSX Senarai Pusat Mengundi</label>
<input id="file" type="file" name="file" accept=".xlsx"
class="form-control @error('file') is-invalid @enderror" required>
<div class="form-text">Format: .xlsx sahaja, saiz maksimum 10MB.</div>
@error('file')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="alert alert-warning py-2 small" role="alert">
<i class="bi bi-exclamation-triangle me-1"></i>
<strong>Amaran:</strong> Import akan <strong>menggantikan</strong> semua data Pusat Mengundi, Saluran, dan Kuota sedia ada untuk DUN yang sama. Pastikan fail betul sebelum teruskan.
</div>
<div class="d-flex justify-content-end gap-2 mt-3">
<a href="{{ route('admin.setup.index') }}" class="btn btn-outline-secondary">Batal</a>
<button type="submit" class="btn btn-success">
<i class="bi bi-upload me-1"></i>Import Data
</button>
</div>
</form>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="content-panel p-3 p-md-4 h-100">
<h2 class="h5 mb-3">Format Fail XLSX</h2>
<p class="text-secondary small mb-2">Fail mesti mempunyai lajur mengikut susunan berikut (baris pertama = tajuk):</p>
<div class="table-responsive">
<table class="table table-sm table-bordered small">
<thead class="table-light">
<tr>
<th>Lajur</th>
<th>Nama</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody>
<tr><td>A</td><td>SEKOLAH</td><td>Nama Pusat Mengundi</td></tr>
<tr><td>B</td><td>NAMA PPM</td><td>Nama PPM (simpan sebagai metadata)</td></tr>
<tr><td>C</td><td>SALURAN</td><td>Bilangan saluran (nombor)</td></tr>
<tr><td>DF</td><td>KTM, POLIS, KP</td><td>Dikira automatik dari bilangan saluran</td></tr>
<tr><td>G</td><td>PPM</td><td>Kuota PPM per pusat (0 atau 1)</td></tr>
<tr><td>H</td><td>KPDP</td><td>Dikira automatik</td></tr>
<tr><td>I</td><td>PAPM</td><td>Kuota PAPM per pusat (nombor)</td></tr>
<tr><td>J</td><td>JKM</td><td>Kuota JKM per pusat</td></tr>
<tr><td>K</td><td>KKM</td><td>Kuota KKM per pusat</td></tr>
<tr><td>L</td><td>TAMBAHAN</td><td>Dikira automatik</td></tr>
<tr><td>M</td><td>NO TELEFON</td><td>Telefon PPM (simpan sebagai metadata)</td></tr>
</tbody>
</table>
</div>
<p class="text-secondary small mt-2 mb-0">
Lajur dengan formula Excel (KTM, POLIS, KP, KPDP, TAMBAHAN) dikira semula secara automatik berdasarkan bilangan saluran nilai asal dalam fail diabaikan.
</p>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,55 @@
@extends('layouts.app')
@section('title', 'Setup Pilihanraya')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Setup Pilihanraya</h1>
<p class="text-secondary mb-0">Konfigurasi hierarchy, jawatan, kuota, PPM, dan QR Pusat Mengundi.</p>
</div>
</div>
</div>
@include('admin.setup._nav')
<div class="row g-3">
@foreach ([
['label' => 'Election', 'value' => $counts['elections'], 'icon' => 'bi-calendar-event', 'tone' => 'primary'],
['label' => 'DUN', 'value' => $counts['duns'], 'icon' => 'bi-map', 'tone' => 'info'],
['label' => 'Pusat', 'value' => $counts['pusat'], 'icon' => 'bi-building', 'tone' => 'success'],
['label' => 'Saluran', 'value' => $counts['saluran'], 'icon' => 'bi-diagram-3', 'tone' => 'success'],
['label' => 'Jawatan', 'value' => $counts['positions'], 'icon' => 'bi-person-vcard', 'tone' => 'secondary'],
['label' => 'Kuota', 'value' => $counts['quotas'], 'icon' => 'bi-list-ol', 'tone' => 'warning'],
['label' => 'PPM aktif', 'value' => $counts['ppmAssignments'], 'icon' => 'bi-person-check', 'tone' => 'primary'],
] as $card)
<div class="col-12 col-sm-6 col-xl-3">
<div class="stat-card h-100">
<div class="d-flex justify-content-between gap-3">
<div>
<div class="text-secondary small">{{ $card['label'] }}</div>
<div class="fs-3 fw-bold mt-2">{{ $card['value'] }}</div>
</div>
<span class="stat-icon text-bg-{{ $card['tone'] }}"><i class="bi {{ $card['icon'] }}"></i></span>
</div>
</div>
</div>
@endforeach
</div>
<div class="content-panel mt-4 p-3 p-md-4">
<h2 class="h5 mb-3">Tindakan Setup</h2>
<div class="row g-2">
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-success w-100 text-start" href="{{ route('admin.setup.import.create') }}"><i class="bi bi-upload me-2"></i>Import Data Pusat</a></div>
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.setup.pusat.index') }}"><i class="bi bi-building me-2"></i>Senarai Pusat</a></div>
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.setup.positions.index') }}"><i class="bi bi-person-vcard me-2"></i>Setup Jawatan</a></div>
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-primary w-100 text-start" href="{{ route('admin.setup.pusat.index') }}"><i class="bi bi-list-ol me-2"></i>Urus Kuota</a></div>
@php $activeElection = \App\Models\Election::query()->where('status', 'active')->first(); @endphp
@if ($activeElection)
<div class="col-12 col-md-6 col-xl-3"><a class="btn btn-outline-warning w-100 text-start" href="{{ route('admin.setup.settings.edit', $activeElection) }}"><i class="bi bi-sliders me-2"></i>Tetapan Pilihanraya</a></div>
@endif
</div>
</div>
@endsection

View File

@@ -0,0 +1,45 @@
@csrf
<div class="row g-3">
<div class="col-12 col-lg-3">
<label class="form-label" for="code">Kod</label>
<input id="code" name="code" class="form-control" value="{{ old('code', $position->code) }}" required>
</div>
<div class="col-12 col-lg-5">
<label class="form-label" for="name">Nama Jawatan</label>
<input id="name" name="name" class="form-control" value="{{ old('name', $position->name) }}" required>
</div>
<div class="col-12 col-lg-2">
<label class="form-label" for="scope">Skop</label>
<select id="scope" name="scope" class="form-select" required>
@foreach (['pusat' => 'Pusat', 'saluran' => 'Saluran', 'external' => 'External'] as $value => $label)
<option value="{{ $value }}" @selected(old('scope', $position->scope ?: 'pusat') === $value)>{{ $label }}</option>
@endforeach
</select>
</div>
<div class="col-12 col-lg-2">
<label class="form-label" for="sort_order">Susunan</label>
<input id="sort_order" name="sort_order" type="number" min="0" class="form-control" value="{{ old('sort_order', $position->sort_order ?? 0) }}">
</div>
<div class="col-12">
<div class="d-flex flex-wrap gap-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="1" id="is_public_applyable" name="is_public_applyable" @checked(old('is_public_applyable', $position->is_public_applyable))>
<label class="form-check-label" for="is_public_applyable">Boleh dipohon awam</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="1" id="is_assignable" name="is_assignable" @checked(old('is_assignable', $position->exists ? $position->is_assignable : true))>
<label class="form-check-label" for="is_assignable">Boleh assign</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="1" id="allows_dual_role" name="allows_dual_role" @checked(old('allows_dual_role', $position->allows_dual_role))>
<label class="form-check-label" for="allows_dual_role">Benarkan dual-role</label>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="{{ route('admin.setup.positions.index') }}" class="btn btn-outline-secondary">Batal</a>
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Simpan</button>
</div>

View File

@@ -0,0 +1,7 @@
@extends('layouts.app')
@section('title', 'Tambah Jawatan')
@section('content')
<div class="page-heading"><div class="eyebrow">Setup</div><h1 class="h3 mb-0">Tambah Jawatan</h1></div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4"><form method="POST" action="{{ route('admin.setup.positions.store') }}">@include('admin.setup.positions._form')</form></div>
@endsection

View File

@@ -0,0 +1,7 @@
@extends('layouts.app')
@section('title', 'Edit Jawatan')
@section('content')
<div class="page-heading"><div class="eyebrow">Setup</div><h1 class="h3 mb-0">Edit Jawatan</h1></div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4"><form method="POST" action="{{ route('admin.setup.positions.update', $position) }}">@method('PUT') @include('admin.setup.positions._form')</form></div>
@endsection

View File

@@ -0,0 +1,42 @@
@extends('layouts.app')
@section('title', 'Jawatan')
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div><h1 class="h3 mb-1">Jawatan</h1><p class="text-secondary mb-0">Tetapan jawatan dan ciri assignment.</p></div>
<a href="{{ route('admin.setup.positions.create') }}" class="btn btn-primary"><i class="bi bi-plus-circle me-1"></i>Tambah</a>
</div>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead><tr><x-sort-th column="code" label="Kod" /><x-sort-th column="name" label="Nama" /><x-sort-th column="scope" label="Skop" /><th>Awam</th><th>Dual-role</th><th class="text-end no-sort">Tindakan</th></tr></thead>
<tbody>
@forelse ($positions as $position)
<tr>
<td class="fw-semibold">{{ $position->code }}</td>
<td>{{ $position->name }}</td>
<td><span class="badge text-bg-light border">{{ $position->scope }}</span></td>
<td>{{ $position->is_public_applyable ? 'Ya' : 'Tidak' }}</td>
<td>{{ $position->allows_dual_role ? 'Ya' : 'Tidak' }}</td>
<td class="text-end">
<a href="{{ route('admin.setup.positions.edit', $position) }}" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="{{ route('admin.setup.positions.destroy', $position) }}" class="d-inline" data-confirm>
@csrf @method('DELETE')
<button class="btn btn-sm btn-outline-danger" type="submit">Padam</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="6" class="text-center text-secondary py-4">Tiada rekod.</td></tr>
@endforelse
</tbody>
</table>
</div>
{{ $positions->links() }}
</div>
@endsection

View File

@@ -0,0 +1,105 @@
@csrf
<div class="row g-3">
<div class="col-md-6">
<label for="election_id" class="form-label">Pilihan Raya</label>
<select id="election_id" name="election_id" class="form-select @error('election_id') is-invalid @enderror" required>
<option value="">Pilih pilihan raya</option>
@foreach ($elections as $election)
<option value="{{ $election->id }}" @selected((string) old('election_id', $pusat->election_id) === (string) $election->id)>
{{ $election->name }}
</option>
@endforeach
</select>
@error('election_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="dun_id" class="form-label">Dewan Undangan Negeri (DUN)</label>
<select id="dun_id" name="dun_id" class="form-select @error('dun_id') is-invalid @enderror">
<option value="">Pilih DUN (pilihan)</option>
@foreach ($duns as $dun)
<option value="{{ $dun->id }}" @selected((string) old('dun_id', $pusat->dun_id) === (string) $dun->id)>
{{ $dun->code }} - {{ $dun->name }}
</option>
@endforeach
</select>
@error('dun_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-4">
<label for="code" class="form-label">Kod Pusat</label>
<input id="code" type="text" name="code" value="{{ old('code', $pusat->code) }}" class="form-control @error('code') is-invalid @enderror" maxlength="50" required>
@error('code')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-8">
<label for="name" class="form-label">Nama Pusat Mengundi</label>
<input id="name" type="text" name="name" value="{{ old('name', $pusat->name) }}" class="form-control @error('name') is-invalid @enderror" maxlength="255" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-12">
<label for="address" class="form-label">Alamat</label>
<textarea id="address" name="address" rows="3" class="form-control @error('address') is-invalid @enderror">{{ old('address', $pusat->address) }}</textarea>
@error('address')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
@unless ($pusat->exists)
@php
$operationalFields = [
'saluran_count' => ['label' => 'Bilangan Saluran', 'default' => 1, 'min' => 1],
'ktm_count' => ['label' => 'KTM setiap Saluran', 'default' => 1],
'polis_iring_count' => ['label' => 'Polis Iring setiap Saluran', 'default' => 1],
'kp_count' => ['label' => 'KP setiap Saluran', 'default' => 4],
'ppm_count' => ['label' => 'PPM setiap Pusat', 'default' => 1],
'kpdp_count' => ['label' => 'KPDP setiap Saluran', 'default' => 1],
'papm_count' => ['label' => 'PAPM setiap Pusat', 'default' => 0],
'jkm_count' => ['label' => 'JKM setiap Pusat', 'default' => 1],
'kkm_count' => ['label' => 'KKM setiap Pusat', 'default' => 1],
'calon_tambahan_count' => ['label' => 'Calon Tambahan setiap Saluran', 'default' => 1],
];
@endphp
<div class="col-12 mt-4">
<h2 class="h6 mb-1">Bilangan Operasi</h2>
<p class="text-body-secondary small mb-0">Saluran akan dijana mengikut nombor secara automatik.</p>
</div>
@foreach ($operationalFields as $field => $configuration)
<div class="col-6 col-md-4 col-xl-3">
<label for="{{ $field }}" class="form-label">{{ $configuration['label'] }}</label>
<input
id="{{ $field }}"
type="number"
name="{{ $field }}"
value="{{ old($field, $configuration['default']) }}"
min="{{ $configuration['min'] ?? 0 }}"
max="100"
class="form-control @error($field) is-invalid @enderror"
required
>
@error($field)
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
@endforeach
@endunless
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="{{ $pusat->exists ? route('admin.setup.pusat.show', $pusat) : route('admin.setup.pusat.index') }}" class="btn btn-outline-secondary">Batal</a>
<button class="btn btn-primary" type="submit">
<i class="bi bi-save me-1"></i>{{ $pusat->exists ? 'Kemas Kini Pusat' : 'Simpan Pusat' }}
</button>
</div>

View File

@@ -0,0 +1,18 @@
@extends('layouts.app')
@section('title', 'Tambah Pusat')
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup</div>
<h1 class="h3 mb-0">Tambah Pusat Mengundi</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.store') }}">
@include('admin.setup.pusat._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,19 @@
@extends('layouts.app')
@section('title', 'Edit Pusat')
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup</div>
<h1 class="h3 mb-0">Edit Pusat Mengundi</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.update', $pusat) }}">
@method('PUT')
@include('admin.setup.pusat._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,47 @@
@extends('layouts.app')
@section('title', 'Pusat Mengundi')
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div><h1 class="h3 mb-1">Pusat Mengundi</h1><p class="text-secondary mb-0">Pusat Mengundi, QR pendaftaran, dan assignment PPM.</p></div>
<a href="{{ route('admin.setup.pusat.create') }}" class="btn btn-primary"><i class="bi bi-plus-circle me-1"></i>Tambah</a>
</div>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead><tr><x-sort-th column="code" label="Kod" /><x-sort-th column="name" label="Nama" /><th>DUN</th><th>PPM</th><th>Saluran</th><th>QR</th><th class="text-end no-sort">Tindakan</th></tr></thead>
<tbody>
@forelse ($pusats as $pusat)
<tr>
<td class="fw-semibold">{{ $pusat->code }}</td>
<td>{{ $pusat->name }}</td>
<td>{{ $pusat->dun?->code }}</td>
<td class="small text-secondary">{{ $pusat->ppm_name ?: '-' }}</td>
<td>{{ $pusat->saluran_mengundis_count ?? $pusat->saluranMengundis->count() }}</td>
<td><span class="badge {{ $pusat->qr_code_path ? 'text-bg-success' : 'text-bg-secondary' }}">{{ $pusat->qr_code_path ? 'Ada' : 'Belum' }}</span></td>
<td class="text-end">
<a href="{{ route('admin.setup.pusat.show', $pusat) }}" class="btn btn-sm btn-outline-primary">Buka</a>
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}" class="btn btn-sm btn-outline-info">Saluran</a>
<a href="{{ route('admin.setup.pusat.edit', $pusat) }}" class="btn btn-sm btn-outline-secondary">Edit</a>
<form action="{{ route('admin.setup.pusat.destroy', $pusat) }}" method="POST" class="d-inline"
onsubmit="return confirm('Padam {{ addslashes($pusat->name) }}? Tindakan ini tidak boleh diundur.')">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-outline-danger">Padam</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="7" class="text-center text-secondary py-4">Tiada rekod. <a href="{{ route('admin.setup.import.create') }}">Import data pusat</a>.</td></tr>
@endforelse
</tbody>
</table>
</div>
{{ $pusats->links() }}
</div>
@endsection

View File

@@ -0,0 +1,87 @@
@extends('layouts.app')
@section('title', $pusat->name)
@section('content')
<div class="page-heading">
<div class="eyebrow">Pusat Mengundi</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">{{ $pusat->name }}</h1>
<p class="text-secondary mb-0">{{ $pusat->code }} - {{ $pusat->daerahMengundi?->name }}</p>
</div>
<div class="d-flex gap-2">
<a href="{{ route('admin.setup.pusat.edit', $pusat) }}" class="btn btn-outline-primary"><i class="bi bi-pencil me-1"></i>Edit</a>
<form method="POST" action="{{ route('admin.setup.pusat.generate-qr', $pusat) }}">
@csrf
<button class="btn btn-primary" type="submit"><i class="bi bi-qr-code me-1"></i>Jana QR</button>
</form>
</div>
</div>
</div>
@include('admin.setup._nav')
<div class="row g-4">
<div class="col-12 col-xl-7">
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Maklumat Pusat</h2>
<dl class="row mb-0">
<dt class="col-sm-4">Election</dt><dd class="col-sm-8">{{ $pusat->election?->name }}</dd>
<dt class="col-sm-4">Alamat</dt><dd class="col-sm-8">{{ $pusat->address ?: '-' }}</dd>
<dt class="col-sm-4">Public UUID</dt><dd class="col-sm-8"><code>{{ $pusat->public_uuid }}</code></dd>
<dt class="col-sm-4">Link QR</dt>
<dd class="col-sm-8">
@if ($pusat->registration_url)
<a href="{{ $pusat->registration_url }}" target="_blank">{{ $pusat->registration_url }}</a>
@else
<span class="text-secondary">Belum dijana.</span>
@endif
</dd>
</dl>
</div>
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Saluran</h2>
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead><tr><th>No.</th><th>Nama</th><th>Pengundi</th></tr></thead>
<tbody>
@forelse ($pusat->saluranMengundis as $saluran)
<tr><td>{{ $saluran->number }}</td><td>{{ $saluran->name }}</td><td>{{ $saluran->voter_count ?? '-' }}</td></tr>
@empty
<tr><td colspan="3" class="text-secondary text-center py-3">Tiada saluran.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">QR Pendaftaran</h2>
@if ($pusat->qr_code_path)
<img class="img-fluid border rounded bg-white p-2 mb-3" style="max-width: 220px" src="{{ route('admin.setup.pusat.qr-svg', $pusat) }}" alt="QR Pusat Mengundi">
@else
<p class="text-secondary">QR belum dijana.</p>
@endif
<div class="small text-secondary">QR menunjuk ke pautan awam Pusat Mengundi ini.</div>
</div>
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Tetapan PPM</h2>
<p class="mb-3">PPM semasa: <span class="fw-semibold">{{ $ppmAssignment?->user?->name ?? 'Belum ditetapkan' }}</span></p>
<form method="POST" action="{{ route('admin.setup.pusat.assign-ppm', $pusat) }}">
@csrf
<label for="user_id" class="form-label">Pilih pengguna</label>
<select id="user_id" name="user_id" class="form-select mb-3" required data-tom-select>
@foreach ($users as $user)
<option value="{{ $user->id }}" @selected($ppmAssignment?->user_id === $user->id)>{{ $user->name }} - {{ $user->email }}</option>
@endforeach
</select>
<button class="btn btn-primary" type="submit"><i class="bi bi-person-check me-1"></i>Simpan PPM</button>
</form>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,35 @@
@csrf
<div class="row g-3">
<div class="col-12 col-lg-8">
<label class="form-label fw-semibold" for="position_id">Jawatan</label>
<select id="position_id" name="position_id" class="form-select @error('position_id') is-invalid @enderror" required data-tom-select>
<option value="">Pilih jawatan</option>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected((int) old('position_id', $quota->position_id) === $position->id)>
{{ $position->code }} {{ $position->name }}
</option>
@endforeach
</select>
@error('position_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-12 col-lg-4">
<label class="form-label fw-semibold" for="quota">Bilangan Kekosongan</label>
<input id="quota" name="quota" type="number" min="0" max="9999"
class="form-control @error('quota') is-invalid @enderror"
value="{{ old('quota', $quota->quota ?? 0) }}" required>
@error('quota')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="{{ route('admin.setup.pusat.saluran.kuota.index', [$pusat, $saluran]) }}" class="btn btn-outline-secondary">Batal</a>
<button class="btn btn-primary" type="submit">
<i class="bi bi-save me-1"></i>{{ $quota->exists ? 'Kemas Kini' : 'Simpan' }}
</button>
</div>

View File

@@ -0,0 +1,41 @@
@csrf
<div class="row g-3">
<div class="col-12 col-lg-8">
<label class="form-label fw-semibold" for="position_id">Jawatan</label>
@if ($quota->exists)
{{-- Edit: papar nama sahaja, tidak boleh tukar jawatan (guna updateOrCreate) --}}
<input type="hidden" name="position_id" value="{{ $quota->position_id }}">
<input type="text" class="form-control" value="{{ $quota->position?->code }} — {{ $quota->position?->name }}" disabled>
@else
<select id="position_id" name="position_id" class="form-select @error('position_id') is-invalid @enderror" required data-tom-select>
<option value="">Pilih jawatan</option>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected((int) old('position_id', $quota->position_id) === $position->id)>
{{ $position->code }} {{ $position->name }}
</option>
@endforeach
</select>
@error('position_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
@endif
</div>
<div class="col-12 col-lg-4">
<label class="form-label fw-semibold" for="quota">Bilangan Kekosongan</label>
<input id="quota" name="quota" type="number" min="0" max="9999"
class="form-control @error('quota') is-invalid @enderror"
value="{{ old('quota', $quota->quota ?? 0) }}" required>
@error('quota')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="{{ route('admin.setup.pusat.kuota.index', $pusat) }}" class="btn btn-outline-secondary">Batal</a>
<button class="btn btn-primary" type="submit">
<i class="bi bi-save me-1"></i>{{ $quota->exists ? 'Kemas Kini' : 'Simpan' }}
</button>
</div>

View File

@@ -0,0 +1,23 @@
@extends('layouts.app')
@section('title', 'Tambah Kuota — Saluran ' . $saluran->number)
@section('content')
<div class="page-heading">
<div class="eyebrow">
Setup ·
<a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> ·
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a> ·
<a href="{{ route('admin.setup.pusat.saluran.kuota.index', [$pusat, $saluran]) }}">Saluran {{ $saluran->number }}</a>
</div>
<h1 class="h3 mb-0">Tambah Kuota Jawatan</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.saluran.kuota.store', [$pusat, $saluran]) }}">
@include('admin.setup.quotas._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,24 @@
@extends('layouts.app')
@section('title', 'Edit Kuota — ' . $quota->position?->code)
@section('content')
<div class="page-heading">
<div class="eyebrow">
Setup ·
<a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> ·
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a> ·
<a href="{{ route('admin.setup.pusat.saluran.kuota.index', [$pusat, $saluran]) }}">Saluran {{ $saluran->number }}</a>
</div>
<h1 class="h3 mb-0">Edit Kuota Jawatan</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.saluran.kuota.update', [$pusat, $saluran, $quota]) }}">
@method('PUT')
@include('admin.setup.quotas._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,70 @@
@extends('layouts.app')
@section('title', 'Kuota — Saluran ' . $saluran->number)
@section('content')
<div class="page-heading">
<div class="eyebrow">
Setup ·
<a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> ·
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a>
</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Kuota Jawatan</h1>
<p class="text-secondary mb-0">{{ $pusat->code }} {{ $pusat->name }} · Saluran {{ $saluran->number }}</p>
</div>
<a href="{{ route('admin.setup.pusat.saluran.kuota.create', [$pusat, $saluran]) }}" class="btn btn-primary">
<i class="bi bi-plus-circle me-1"></i>Tambah Kuota
</a>
</div>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Jawatan</th>
<x-sort-th column="quota" label="Kuota" class="text-end" />
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($quotas as $quota)
<tr>
<td>
<span class="fw-semibold">{{ $quota->position?->code }}</span>
<div class="small text-secondary">{{ $quota->position?->name }}</div>
</td>
<td class="text-end fw-semibold">{{ $quota->quota }}</td>
<td class="text-end">
<a href="{{ route('admin.setup.pusat.saluran.kuota.edit', [$pusat, $saluran, $quota]) }}" class="btn btn-sm btn-outline-secondary">Edit</a>
<form method="POST" action="{{ route('admin.setup.pusat.saluran.kuota.destroy', [$pusat, $saluran, $quota]) }}" class="d-inline"
onsubmit="return confirm('Padam kuota {{ addslashes($quota->position?->code) }}?')">
@csrf @method('DELETE')
<button class="btn btn-sm btn-outline-danger" type="submit">Padam</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="3" class="text-center text-secondary py-4">
Tiada kuota. <a href="{{ route('admin.setup.pusat.saluran.kuota.create', [$pusat, $saluran]) }}">Tambah kuota</a>.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $quotas->links() }}
</div>
<div class="mt-3">
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Kembali ke Saluran
</a>
</div>
@endsection

View File

@@ -0,0 +1,23 @@
@extends('layouts.app')
@section('title', 'Tambah Kuota Pusat — ' . $pusat->name)
@section('content')
<div class="page-heading">
<div class="eyebrow">
Setup ·
<a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> ·
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a> ·
<a href="{{ route('admin.setup.pusat.kuota.index', $pusat) }}">Saluran 0 (Pusat)</a>
</div>
<h1 class="h3 mb-0">Tambah Kuota Peringkat Pusat</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.kuota.store', $pusat) }}">
@include('admin.setup.quotas._pusat-form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,24 @@
@extends('layouts.app')
@section('title', 'Edit Kuota Pusat — ' . $quota->position?->code)
@section('content')
<div class="page-heading">
<div class="eyebrow">
Setup ·
<a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> ·
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a> ·
<a href="{{ route('admin.setup.pusat.kuota.index', $pusat) }}">Saluran 0 (Pusat)</a>
</div>
<h1 class="h3 mb-0">Edit Kuota Peringkat Pusat</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.kuota.update', [$pusat, $quota]) }}">
@method('PUT')
@include('admin.setup.quotas._pusat-form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,70 @@
@extends('layouts.app')
@section('title', 'Kuota Pusat — ' . $pusat->name)
@section('content')
<div class="page-heading">
<div class="eyebrow">
Setup ·
<a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> ·
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a>
</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Kuota Jawatan Peringkat Pusat</h1>
<p class="text-secondary mb-0">{{ $pusat->code }} {{ $pusat->name }} · Saluran 0 (Pusat)</p>
</div>
<a href="{{ route('admin.setup.pusat.kuota.create', $pusat) }}" class="btn btn-primary">
<i class="bi bi-plus-circle me-1"></i>Tambah Kuota
</a>
</div>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Jawatan</th>
<x-sort-th column="quota" label="Kuota" class="text-end" />
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($quotas as $quota)
<tr>
<td>
<span class="fw-semibold">{{ $quota->position?->code }}</span>
<div class="small text-secondary">{{ $quota->position?->name }}</div>
</td>
<td class="text-end fw-semibold">{{ $quota->quota }}</td>
<td class="text-end">
<a href="{{ route('admin.setup.pusat.kuota.edit', [$pusat, $quota]) }}" class="btn btn-sm btn-outline-secondary">Edit</a>
<form method="POST" action="{{ route('admin.setup.pusat.kuota.destroy', [$pusat, $quota]) }}" class="d-inline"
onsubmit="return confirm('Padam kuota {{ addslashes($quota->position?->code) }}?')">
@csrf @method('DELETE')
<button class="btn btn-sm btn-outline-danger" type="submit">Padam</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="3" class="text-center text-secondary py-4">
Tiada kuota peringkat pusat. <a href="{{ route('admin.setup.pusat.kuota.create', $pusat) }}">Tambah kuota</a>.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $quotas->links() }}
</div>
<div class="mt-3">
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Kembali ke Saluran
</a>
</div>
@endsection

View File

@@ -0,0 +1,28 @@
@csrf
<div class="row g-3">
<div class="col-md-6">
<label for="number" class="form-label">Nombor Saluran</label>
<input id="number" type="text" name="number" value="{{ old('number', $saluran->number) }}"
class="form-control @error('number') is-invalid @enderror" maxlength="20" required>
@error('number')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="voter_count" class="form-label">Bilangan Pengundi</label>
<input id="voter_count" type="number" name="voter_count" value="{{ old('voter_count', $saluran->voter_count) }}"
class="form-control @error('voter_count') is-invalid @enderror" min="0">
@error('voter_count')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-4">
<a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}" class="btn btn-outline-secondary">Batal</a>
<button class="btn btn-primary" type="submit">
<i class="bi bi-save me-1"></i>{{ $saluran->exists ? 'Kemas Kini Saluran' : 'Simpan Saluran' }}
</button>
</div>

View File

@@ -0,0 +1,18 @@
@extends('layouts.app')
@section('title', 'Tambah Saluran — ' . $pusat->name)
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup · <a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> · <a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a></div>
<h1 class="h3 mb-0">Tambah Saluran Mengundi</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.saluran.store', $pusat) }}">
@include('admin.setup.saluran._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,19 @@
@extends('layouts.app')
@section('title', 'Edit Saluran — ' . $pusat->name)
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup · <a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a> · <a href="{{ route('admin.setup.pusat.saluran.index', $pusat) }}">{{ $pusat->name }}</a></div>
<h1 class="h3 mb-0">Edit Saluran Mengundi</h1>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('admin.setup.pusat.saluran.update', [$pusat, $saluran]) }}">
@method('PUT')
@include('admin.setup.saluran._form')
</form>
</div>
@endsection

View File

@@ -0,0 +1,69 @@
@extends('layouts.app')
@section('title', 'Saluran — ' . $pusat->name)
@section('content')
<div class="page-heading">
<div class="eyebrow">Setup · <a href="{{ route('admin.setup.pusat.index') }}">Pusat Mengundi</a></div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Saluran Mengundi</h1>
<p class="text-secondary mb-0">{{ $pusat->code }} {{ $pusat->name }}</p>
</div>
<a href="{{ route('admin.setup.pusat.saluran.create', $pusat) }}" class="btn btn-primary">
<i class="bi bi-plus-circle me-1"></i>Tambah Saluran
</a>
</div>
</div>
@include('admin.setup._nav')
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr><x-sort-th column="number" label="No." /><th>Nama</th><x-sort-th column="voter_count" label="Bilangan Pengundi" /><th class="text-end no-sort">Tindakan</th></tr>
</thead>
<tbody>
{{-- Row tetap: Saluran 0 = Kuota peringkat Pusat --}}
<tr class="table-light">
<td class="fw-semibold text-secondary">0</td>
<td>
<span class="fw-semibold">Pusat</span>
<div class="small text-secondary">Kuota jawatan peringkat pusat (PPM, PAPM, JKM, KKM, dll.)</div>
</td>
<td class="text-secondary"></td>
<td class="text-end">
<a href="{{ route('admin.setup.pusat.kuota.index', $pusat) }}" class="btn btn-sm btn-outline-info">Kuota</a>
</td>
</tr>
@forelse ($salurans as $saluran)
<tr>
<td class="fw-semibold">{{ $saluran->number }}</td>
<td>{{ $saluran->name ?? '-' }}</td>
<td>{{ $saluran->voter_count ?? '-' }}</td>
<td class="text-end">
<a href="{{ route('admin.setup.pusat.saluran.kuota.index', [$pusat, $saluran]) }}" class="btn btn-sm btn-outline-info">Kuota</a>
<a href="{{ route('admin.setup.pusat.saluran.edit', [$pusat, $saluran]) }}" class="btn btn-sm btn-outline-secondary">Edit</a>
<form method="POST" action="{{ route('admin.setup.pusat.saluran.destroy', [$pusat, $saluran]) }}" class="d-inline"
onsubmit="return confirm('Padam saluran {{ $saluran->number }}?')">
@csrf @method('DELETE')
<button class="btn btn-sm btn-outline-danger" type="submit">Padam</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="4" class="text-center text-secondary py-4">Tiada saluran. <a href="{{ route('admin.setup.pusat.saluran.create', $pusat) }}">Tambah saluran</a>.</td></tr>
@endforelse
</tbody>
</table>
</div>
{{ $salurans->links() }}
</div>
<div class="mt-3">
<a href="{{ route('admin.setup.pusat.show', $pusat) }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Kembali ke {{ $pusat->name }}
</a>
</div>
@endsection

View File

@@ -0,0 +1,213 @@
@extends('layouts.app')
@section('title', 'Tetapan Pilihanraya')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin · Setup</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Tetapan Pilihanraya</h1>
<p class="text-secondary mb-0">{{ $election->name }} · Kawalan tarikh, mod kehadiran, dan pendaftaran.</p>
</div>
</div>
</div>
@include('admin.setup._nav')
<div class="row g-3">
{{-- Status semasa --}}
<div class="col-12 col-xl-4">
<div class="content-panel p-3 p-md-4 h-100">
<h2 class="h5 mb-3">Status Semasa</h2>
@if ($election->settings)
<dl class="mb-0">
<dt class="text-secondary small">Pilihanraya</dt>
<dd class="mb-3">{{ $election->name }}</dd>
<dt class="text-secondary small">Tarikh Daftar Buka</dt>
<dd class="mb-3">{{ $election->settings->registration_start_date?->format('d/m/Y') ?? '-' }}</dd>
<dt class="text-secondary small">Tarikh Daftar Tutup</dt>
<dd class="mb-3">{{ $election->settings->registration_end_date?->format('d/m/Y') ?? '-' }}</dd>
<dt class="text-secondary small">Tarikh Mengundi</dt>
<dd class="mb-3">{{ $election->settings->polling_date?->format('d/m/Y') ?? '-' }}</dd>
<dt class="text-secondary small">Status Pendaftaran</dt>
<dd class="mb-3">
@if ($isRegistrationOpen)
<span class="badge text-bg-success"><i class="bi bi-door-open me-1"></i>Dibuka</span>
@else
<span class="badge text-bg-secondary"><i class="bi bi-door-closed me-1"></i>Ditutup</span>
@endif
@if ($election->settings->is_registration_open_override !== null)
<span class="badge text-bg-warning ms-1"><i class="bi bi-exclamation-triangle me-1"></i>Override aktif</span>
@endif
</dd>
<dt class="text-secondary small">Mod Kehadiran</dt>
<dd class="mb-0">
@if ($election->settings->is_attendance_active)
<span class="badge text-bg-success"><i class="bi bi-check-circle me-1"></i>Aktif</span>
@else
<span class="badge text-bg-secondary"><i class="bi bi-dash-circle me-1"></i>Tidak aktif</span>
@endif
</dd>
</dl>
@else
<p class="text-secondary small mb-0">Tiada tetapan lagi. Isikan borang untuk mencipta tetapan baru.</p>
@endif
</div>
</div>
{{-- Borang tetapan --}}
<div class="col-12 col-xl-8">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">{{ $election->settings ? 'Kemaskini Tetapan' : 'Tetapan Baru' }}</h2>
@php
$isNew = $election->settings === null;
$formAction = $isNew
? route('admin.setup.settings.store', $election)
: route('admin.setup.settings.update', $election);
@endphp
<form method="POST" action="{{ $formAction }}">
@csrf
@unless ($isNew) @method('PATCH') @endunless
{{-- Tarikh --}}
<div class="row g-3 mb-4">
<div class="col-12 col-md-4">
<label for="registration_start_date" class="form-label fw-semibold">Tarikh Daftar Buka</label>
<input type="date" class="form-control @error('registration_start_date') is-invalid @enderror"
id="registration_start_date" name="registration_start_date"
value="{{ old('registration_start_date', $election->settings?->registration_start_date?->format('Y-m-d')) }}">
@error('registration_start_date')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-12 col-md-4">
<label for="registration_end_date" class="form-label fw-semibold">Tarikh Daftar Tutup</label>
<input type="date" class="form-control @error('registration_end_date') is-invalid @enderror"
id="registration_end_date" name="registration_end_date"
value="{{ old('registration_end_date', $election->settings?->registration_end_date?->format('Y-m-d')) }}">
@error('registration_end_date')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-12 col-md-4">
<label for="polling_date" class="form-label fw-semibold">Tarikh Mengundi</label>
<input type="date" class="form-control @error('polling_date') is-invalid @enderror"
id="polling_date" name="polling_date"
value="{{ old('polling_date', $election->settings?->polling_date?->format('Y-m-d')) }}">
@error('polling_date')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<hr>
{{-- Mod Kehadiran --}}
<div class="mb-4">
<label class="form-label fw-semibold">Mod Kehadiran</label>
<p class="text-secondary small mb-2">
Apabila diaktifkan, PPM boleh merekod kehadiran staf pada skrin kehadiran.
Aktifkan hanya pada hari mengundi.
</p>
<div class="d-flex gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="is_attendance_active"
id="attendance_off" value="0"
{{ old('is_attendance_active', $election->settings?->is_attendance_active ? '1' : '0') === '0' ? 'checked' : '' }}>
<label class="form-check-label" for="attendance_off">
<span class="badge text-bg-secondary"><i class="bi bi-dash-circle me-1"></i>Tidak aktif</span>
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="is_attendance_active"
id="attendance_on" value="1"
{{ old('is_attendance_active', $election->settings?->is_attendance_active ? '1' : '0') === '1' ? 'checked' : '' }}>
<label class="form-check-label" for="attendance_on">
<span class="badge text-bg-success"><i class="bi bi-check-circle me-1"></i>Aktif</span>
</label>
</div>
</div>
@error('is_attendance_active')
<div class="text-danger small mt-1">{{ $message }}</div>
@enderror
</div>
<hr>
{{-- Override Pendaftaran --}}
<div class="mb-4">
<label class="form-label fw-semibold">Override Tempoh Pendaftaran</label>
<p class="text-secondary small mb-2">
Secara lalai, sistem mengikut tarikh buka dan tutup pendaftaran yang ditetapkan.
Gunakan override hanya apabila perlu buka atau tutup pendaftaran di luar tarikh tersebut.
</p>
@php
$currentOverride = old('is_registration_open_override',
$election->settings?->is_registration_open_override === null
? ''
: ($election->settings->is_registration_open_override ? '1' : '0')
);
@endphp
<div class="d-flex flex-wrap gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="is_registration_open_override"
id="override_auto" value=""
{{ $currentOverride === '' ? 'checked' : '' }}>
<label class="form-check-label" for="override_auto">
<span class="fw-semibold">Auto</span>
<div class="text-secondary small">Ikut tarikh buka/tutup pendaftaran</div>
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="is_registration_open_override"
id="override_open" value="1"
{{ $currentOverride === '1' ? 'checked' : '' }}>
<label class="form-check-label" for="override_open">
<span class="fw-semibold text-success">Buka Paksa</span>
<div class="text-secondary small">Buka walaupun di luar tempoh tarikh</div>
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="is_registration_open_override"
id="override_closed" value="0"
{{ $currentOverride === '0' ? 'checked' : '' }}>
<label class="form-check-label" for="override_closed">
<span class="fw-semibold text-danger">Tutup Paksa</span>
<div class="text-secondary small">Tutup walaupun dalam tempoh tarikh</div>
</label>
</div>
</div>
@error('is_registration_open_override')
<div class="text-danger small mt-1">{{ $message }}</div>
@enderror
</div>
@if ($election->settings?->is_registration_open_override !== null)
<div class="alert alert-warning py-2" role="alert">
<i class="bi bi-exclamation-triangle me-2"></i>
Override sedang aktif. Tetapkan semula ke <strong>Auto</strong> untuk kembali kepada kawalan tarikh biasa.
</div>
@endif
<div class="d-flex justify-content-end gap-2 mt-3">
<a class="btn btn-outline-secondary" href="{{ route('admin.setup.index') }}">Kembali</a>
<button class="btn btn-primary" type="submit">
<i class="bi bi-save me-1"></i>{{ $isNew ? 'Cipta Tetapan' : 'Simpan Tetapan' }}
</button>
</div>
</form>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,25 @@
@extends('layouts.guest')
@section('title', 'Sahkan Kata Laluan')
@section('content')
<div class="mb-4">
<div class="eyebrow">Pengesahan</div>
<h1 class="h4 mt-2 mb-1">Sahkan kata laluan</h1>
<p class="text-secondary mb-0">Masukkan kata laluan semasa untuk meneruskan tindakan ini.</p>
</div>
<form method="POST" action="{{ route('password.confirm') }}">
@csrf
<div class="mb-3">
<label for="password" class="form-label">Kata laluan</label>
<input id="password" class="form-control @error('password') is-invalid @enderror" type="password" name="password" required autocomplete="current-password">
@error('password')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary w-100">Sahkan</button>
</form>
@endsection

View File

@@ -0,0 +1,28 @@
@extends('layouts.guest')
@section('title', 'Lupa Kata Laluan')
@section('content')
<div class="mb-4">
<div class="eyebrow">Reset akses</div>
<h1 class="h4 mt-2 mb-1">Lupa kata laluan</h1>
<p class="text-secondary mb-0">Masukkan emel akaun untuk menerima pautan reset.</p>
</div>
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="mb-3">
<label for="email" class="form-label">Emel</label>
<input id="email" class="form-control @error('email') is-invalid @enderror" type="email" name="email" value="{{ old('email') }}" required autofocus>
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="d-flex justify-content-between align-items-center">
<a href="{{ route('login') }}" class="small">Kembali ke log masuk</a>
<button type="submit" class="btn btn-primary">Hantar pautan</button>
</div>
</form>
@endsection

View File

@@ -0,0 +1,46 @@
@extends('layouts.guest')
@section('title', 'Log Masuk')
@section('content')
<div class="mb-4">
<div class="eyebrow">Akses Pengguna</div>
<h1 class="h4 mt-2 mb-1">Log masuk</h1>
<p class="text-secondary mb-0">Masukkan emel dan kata laluan akaun sistem.</p>
</div>
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="mb-3">
<label for="email" class="form-label">Emel</label>
<input id="email" class="form-control @error('email') is-invalid @enderror" type="email" name="email" value="{{ old('email') }}" required autofocus autocomplete="username">
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="password" class="form-label">Kata laluan</label>
<input id="password" class="form-control @error('password') is-invalid @enderror" type="password" name="password" required autocomplete="current-password">
@error('password')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-check mb-3">
<input id="remember_me" type="checkbox" class="form-check-input" name="remember">
<label for="remember_me" class="form-check-label">Ingat sesi ini</label>
</div>
<div class="d-flex justify-content-between align-items-center gap-3">
@if (Route::has('password.request'))
<a class="small" href="{{ route('password.request') }}">Lupa kata laluan?</a>
@endif
<button type="submit" class="btn btn-primary">
<i class="bi bi-box-arrow-in-right me-1"></i>Log masuk
</button>
</div>
</form>
@endsection

View File

@@ -0,0 +1,12 @@
@extends('layouts.guest')
@section('title', 'Pendaftaran Akaun')
@section('content')
<div class="text-center">
<div class="eyebrow">Pendaftaran akaun</div>
<h1 class="h4 mt-2 mb-2">Pendaftaran umum ditutup</h1>
<p class="text-secondary">Akaun dalaman diwujudkan oleh Admin. Permohonan awam akan menggunakan pautan QR Pusat Mengundi dalam fasa permohonan.</p>
<a href="{{ route('login') }}" class="btn btn-primary">Log masuk</a>
</div>
@endsection

View File

@@ -0,0 +1,42 @@
@extends('layouts.guest')
@section('title', 'Reset Kata Laluan')
@section('content')
<div class="mb-4">
<div class="eyebrow">Reset akses</div>
<h1 class="h4 mt-2 mb-1">Tetapkan kata laluan baru</h1>
</div>
<form method="POST" action="{{ route('password.store') }}">
@csrf
<input type="hidden" name="token" value="{{ $request->route('token') }}">
<div class="mb-3">
<label for="email" class="form-label">Emel</label>
<input id="email" class="form-control @error('email') is-invalid @enderror" type="email" name="email" value="{{ old('email', $request->email) }}" required autofocus autocomplete="username">
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="password" class="form-label">Kata laluan baru</label>
<input id="password" class="form-control @error('password') is-invalid @enderror" type="password" name="password" required autocomplete="new-password">
@error('password')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="password_confirmation" class="form-label">Sahkan kata laluan</label>
<input id="password_confirmation" class="form-control @error('password_confirmation') is-invalid @enderror" type="password" name="password_confirmation" required autocomplete="new-password">
@error('password_confirmation')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary w-100">Reset kata laluan</button>
</form>
@endsection

View File

@@ -0,0 +1,29 @@
@extends('layouts.guest')
@section('title', 'Sahkan Emel')
@section('content')
<div class="mb-4">
<div class="eyebrow">Pengesahan emel</div>
<h1 class="h4 mt-2 mb-1">Sahkan emel</h1>
<p class="text-secondary mb-0">Pautan pengesahan telah dihantar ke emel akaun anda.</p>
</div>
@if (session('status') === 'verification-link-sent')
<div class="alert alert-success" role="alert">
Pautan pengesahan baru telah dihantar.
</div>
@endif
<div class="d-flex justify-content-between align-items-center gap-3">
<form method="POST" action="{{ route('verification.send') }}">
@csrf
<button type="submit" class="btn btn-primary">Hantar semula</button>
</form>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="btn btn-link p-0">Log keluar</button>
</form>
</div>
@endsection

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" {{ $attributes }}>
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,7 @@
@props(['status'])
@if ($status)
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
{{ $status }}
</div>
@endif

View File

@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>

View File

@@ -0,0 +1 @@
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>

View File

@@ -0,0 +1,35 @@
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white'])
@php
$alignmentClasses = match ($align) {
'left' => 'ltr:origin-top-left rtl:origin-top-right start-0',
'top' => 'origin-top',
default => 'ltr:origin-top-right rtl:origin-top-left end-0',
};
$width = match ($width) {
'48' => 'w-48',
default => $width,
};
@endphp
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
<div @click="open = ! open">
{{ $trigger }}
</div>
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
style="display: none;"
@click="open = false">
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
{{ $content }}
</div>
</div>
</div>

View File

@@ -0,0 +1,9 @@
@props(['messages'])
@if ($messages)
<ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}>
@foreach ((array) $messages as $message)
<li>{{ $message }}</li>
@endforeach
</ul>
@endif

View File

@@ -0,0 +1,5 @@
@props(['value'])
<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700']) }}>
{{ $value ?? $slot }}
</label>

View File

@@ -0,0 +1,78 @@
@props([
'name',
'show' => false,
'maxWidth' => '2xl'
])
@php
$maxWidth = [
'sm' => 'sm:max-w-sm',
'md' => 'sm:max-w-md',
'lg' => 'sm:max-w-lg',
'xl' => 'sm:max-w-xl',
'2xl' => 'sm:max-w-2xl',
][$maxWidth];
@endphp
<div
x-data="{
show: @js($show),
focusables() {
// All focusable element types...
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
return [...$el.querySelectorAll(selector)]
// All non-disabled elements...
.filter(el => ! el.hasAttribute('disabled'))
},
firstFocusable() { return this.focusables()[0] },
lastFocusable() { return this.focusables().slice(-1)[0] },
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
}"
x-init="$watch('show', value => {
if (value) {
document.body.classList.add('overflow-y-hidden');
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
} else {
document.body.classList.remove('overflow-y-hidden');
}
})"
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null"
x-on:close.stop="show = false"
x-on:keydown.escape.window="show = false"
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
x-show="show"
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
style="display: {{ $show ? 'block' : 'none' }};"
>
<div
x-show="show"
class="fixed inset-0 transform transition-all"
x-on:click="show = false"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
>
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<div
x-show="show"
class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
{{ $slot }}
</div>
</div>

View File

@@ -0,0 +1,11 @@
@props(['active'])
@php
$classes = ($active ?? false)
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>

View File

@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>

View File

@@ -0,0 +1,11 @@
@props(['active'])
@php
$classes = ($active ?? false)
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>

View File

@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>

View File

@@ -0,0 +1,16 @@
@props(['column', 'label'])
@php
$currentSort = request('sort');
$currentDir = request('direction', 'asc');
$active = $currentSort === $column;
$nextDir = ($active && $currentDir === 'asc') ? 'desc' : 'asc';
$icon = $active
? ($currentDir === 'asc' ? 'bi-sort-down' : 'bi-sort-up')
: 'bi-arrow-down-up';
@endphp
<th {{ $attributes }}>
<a href="{{ request()->fullUrlWithQuery(['sort' => $column, 'direction' => $nextDir, 'page' => null]) }}"
class="text-decoration-none d-inline-flex align-items-center gap-1 {{ $active ? 'fw-semibold text-primary' : 'link-secondary' }}">
{{ $label }}<i class="bi {{ $icon }} small"></i>
</a>
</th>

View File

@@ -0,0 +1,3 @@
@props(['disabled' => false])
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>

View File

@@ -0,0 +1,10 @@
@include('dashboards.index', [
'title' => 'Dashboard',
'eyebrow' => 'Akaun pengguna',
'description' => 'Paparan asas sistem.',
'cards' => [
['label' => 'Peranan aktif', 'value' => '0', 'icon' => 'bi-shield-lock', 'tone' => 'secondary'],
['label' => 'Modul aktif', 'value' => '0', 'icon' => 'bi-grid', 'tone' => 'secondary'],
],
'actions' => [],
])

View File

@@ -0,0 +1,137 @@
@extends('layouts.app')
@section('title', 'Dashboard Admin')
@section('content')
<div class="page-heading">
<div class="eyebrow">Kawalan operasi pilihanraya</div>
<div class="d-flex flex-column flex-lg-row justify-content-between gap-3 mt-1">
<div>
<h1 class="h3 mb-1">Dashboard Admin</h1>
<p class="text-secondary mb-0">Ringkasan setup, kekosongan petugas, dan modul pengurusan.</p>
</div>
<div class="d-flex gap-2 align-items-start">
<a href="{{ route('admin.setup.index') }}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-gear me-1"></i>Setup
</a>
<a href="{{ route('admin.management.index') }}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-people me-1"></i>Pengurusan
</a>
</div>
</div>
</div>
{{-- Stats --}}
<div class="row g-3 mb-4">
@foreach ($cards as $card)
<div class="col-12 col-sm-6 col-xl-3">
<div class="stat-card h-100">
<div class="d-flex justify-content-between align-items-start gap-3">
<div>
<div class="text-secondary small">{{ $card['label'] }}</div>
<div class="fs-3 fw-bold mt-2">{{ $card['value'] }}</div>
</div>
<span class="stat-icon text-bg-{{ $card['tone'] }}">
<i class="bi {{ $card['icon'] }}"></i>
</span>
</div>
</div>
</div>
@endforeach
</div>
{{-- Vacancy table --}}
<div class="content-panel p-0 overflow-hidden mb-4">
<div class="p-3 border-bottom d-flex justify-content-between align-items-center">
<div>
<h2 class="h5 mb-0">Pusat dengan Kekosongan Petugas</h2>
<p class="text-secondary small mb-0 mt-1">Berdasarkan kuota jawatan berbanding tugasan aktif.</p>
</div>
@if ($pusatsWithVacancy->isEmpty())
<span class="badge text-bg-success">Semua penuh</span>
@else
<span class="badge text-bg-warning">{{ $pusatsWithVacancy->count() }} pusat ada kekosongan</span>
@endif
</div>
@if ($pusatsWithVacancy->isEmpty())
<div class="p-4 text-center text-secondary">
<i class="bi bi-check-circle fs-3 d-block mb-2 text-success"></i>
Semua pusat mengundi telah penuh petugas mengikut kuota.
</div>
@else
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Pusat Mengundi</th>
<th>DUN</th>
<th>PPM</th>
<th class="text-center">Saluran</th>
<th class="text-center">Diisi / Kuota</th>
<th class="text-center">Kosong</th>
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@foreach ($pusatsWithVacancy as $pusat)
<tr>
<td>
<div class="fw-semibold">{{ $pusat->code }}</div>
<div class="small text-secondary">{{ $pusat->name }}</div>
</td>
<td class="small">{{ $pusat->dun?->code ?? '-' }}</td>
<td class="small text-secondary">{{ $pusat->ppm_name }}</td>
<td class="text-center">{{ $pusat->saluran_mengundis_count }}</td>
<td class="text-center">
<span class="fw-semibold">{{ $pusat->filled }}</span>
<span class="text-secondary"> / {{ $pusat->total_quota }}</span>
@if ($pusat->total_quota > 0)
<div class="progress mt-1" style="height:4px">
<div class="progress-bar bg-success" style="width:{{ round($pusat->filled / $pusat->total_quota * 100) }}%"></div>
</div>
@endif
</td>
<td class="text-center">
<span class="badge text-bg-warning fw-bold">{{ $pusat->vacant }}</span>
</td>
<td class="text-end">
<a href="{{ route('admin.management.penempatan.show', $pusat) }}" class="btn btn-sm btn-primary">
<i class="bi bi-people me-1"></i>Urus Penempatan
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
{{-- Quick links --}}
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Modul Utama</h2>
<div class="row g-2">
<div class="col-12 col-md-6 col-xl-3">
<a href="{{ route('admin.setup.index') }}" class="btn btn-outline-primary w-100 text-start">
<i class="bi bi-gear me-2"></i>Election Setup
</a>
</div>
<div class="col-12 col-md-6 col-xl-3">
<a href="{{ route('admin.management.applications.index') }}" class="btn btn-outline-primary w-100 text-start">
<i class="bi bi-file-earmark-person me-2"></i>Senarai Permohonan
</a>
</div>
<div class="col-12 col-md-6 col-xl-3">
<a href="{{ route('admin.management.assignments.index') }}" class="btn btn-outline-primary w-100 text-start">
<i class="bi bi-person-lines-fill me-2"></i>Senarai Tugasan
</a>
</div>
<div class="col-12 col-md-6 col-xl-3">
<a href="{{ route('admin.attendance.index') }}" class="btn btn-outline-primary w-100 text-start">
<i class="bi bi-check2-circle me-2"></i>Kehadiran
</a>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,62 @@
@extends('layouts.app')
@section('title', $title)
@section('content')
<div class="page-heading">
<div class="eyebrow">{{ $eyebrow }}</div>
<div class="d-flex flex-column flex-lg-row justify-content-between gap-3 mt-1">
<div>
<h1 class="h3 mb-1">{{ $title }}</h1>
<p class="text-secondary mb-0">{{ $description }}</p>
</div>
<div class="d-flex align-items-start">
<span class="badge text-bg-light border">
<i class="bi bi-calendar-event me-1"></i> Phase 1
</span>
</div>
</div>
</div>
<div class="row g-3">
@foreach ($cards as $card)
<div class="col-12 col-sm-6 col-xl-3">
<div class="stat-card h-100">
<div class="d-flex justify-content-between align-items-start gap-3">
<div>
<div class="text-secondary small">{{ $card['label'] }}</div>
<div class="fs-3 fw-bold mt-2">{{ $card['value'] }}</div>
</div>
<span class="stat-icon text-bg-{{ $card['tone'] }}">
<i class="bi {{ $card['icon'] }}"></i>
</span>
</div>
</div>
</div>
@endforeach
</div>
@if (! empty($actions))
<div class="content-panel mt-4 p-3 p-md-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="h5 mb-0">Modul Utama</h2>
<span class="badge text-bg-secondary">Base dashboard</span>
</div>
<div class="row g-2">
@foreach ($actions as $action)
<div class="col-12 col-md-6 col-xl-3">
@if (is_array($action))
<a href="{{ route($action['route']) }}" class="btn btn-outline-primary w-100 text-start">
<i class="bi bi-folder2-open me-2"></i>{{ $action['label'] }}
</a>
@else
<button type="button" class="btn btn-outline-primary w-100 text-start" disabled>
<i class="bi bi-folder2-open me-2"></i>{{ $action }}
</button>
@endif
</div>
@endforeach
</div>
</div>
@endif
@endsection

View File

@@ -0,0 +1,15 @@
<x-mail::message>
# Makluman Pendaftaran KP
Nama: {{ $application->name }}
Jawatan: KP - Kerani Pengundian
Pusat Mengundi: {{ $application->pusatMengundi?->name ?? '-' }}
KTM: {{ $ktmAssignment->user?->name ?? '-' }}
Rekod anda telah didaftarkan oleh KTM untuk semakan lanjut. Sila hubungi KTM atau Admin jika terdapat maklumat yang tidak tepat.
Terima kasih.
</x-mail::message>

View File

@@ -0,0 +1,34 @@
<x-mail::message>
# Akaun Login Telah Dicipta
Assalamualaikum / Salam Sejahtera **{{ $user->name }}**,
Anda telah ditetapkan sebagai **{{ $roleCode }}** dalam Sistem Pengurusan Kakitangan Pilihanraya.
Akaun login telah disediakan untuk anda. Sila gunakan maklumat berikut untuk log masuk:
<x-mail::panel>
**URL Login:** {{ url('/login') }}
**E-mel:** {{ $user->email }}
**Kata Laluan Sementara:** `{{ $tempPassword }}`
</x-mail::panel>
> **Penting:** Sila tukar kata laluan anda selepas log masuk pertama melalui menu **Profil**.
Setelah log masuk, anda boleh mengakses fungsi berikut mengikut peranan anda:
@if ($roleCode === 'PPM')
- Semak dan luluskan permohonan kakitangan
- Rekod kehadiran staf pada hari mengundi
@elseif ($roleCode === 'KTM')
- Daftar dan urus ahli pasukan KP
- Semak permohonan KP
@endif
Jika anda mempunyai sebarang masalah log masuk, sila hubungi pentadbir sistem.
Terima kasih,<br>
{{ config('app.name') }}
</x-mail::message>

View File

@@ -0,0 +1,111 @@
@extends('layouts.app')
@section('title', 'Semakan Bank')
@section('content')
<div class="page-heading">
<div class="eyebrow">Admin Kewangan</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Semakan Akaun Bank</h1>
<p class="text-secondary mb-0">Semak maklumat bank staf yang telah diluluskan tanpa mengubah penempatan.</p>
</div>
<div>
<a class="btn btn-primary" href="{{ route('kewangan.bank.export', request()->query()) }}">
<i class="bi bi-download me-1"></i>Export
</a>
</div>
</div>
</div>
<div class="content-panel p-3 mb-3">
<form class="row g-2" method="GET">
<div class="col-12 col-md-3">
<input class="form-control" name="q" value="{{ $filters['q'] ?? '' }}" placeholder="Cari nama / IC / email">
</div>
<div class="col-6 col-md-2">
<select class="form-select" name="status">
<option value="">Semua status</option>
@foreach (['pending', 'verified', 'rejected', 'requires_correction'] as $status)
<option value="{{ $status }}" @selected(($filters['status'] ?? '') === $status)>{{ str_replace('_', ' ', $status) }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-3">
<select class="form-select" name="pusat_mengundi_id">
<option value="">Semua pusat</option>
@foreach ($pusats as $pusat)
<option value="{{ $pusat->id }}" @selected(($filters['pusat_mengundi_id'] ?? '') == $pusat->id)>{{ $pusat->code }} - {{ $pusat->name }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-2">
<select class="form-select" name="role_id">
<option value="">Semua jawatan</option>
@foreach ($positions as $position)
<option value="{{ $position->id }}" @selected(($filters['role_id'] ?? '') == $position->id)>{{ $position->code }}</option>
@endforeach
</select>
</div>
<div class="col-6 col-md-2">
<button class="btn btn-outline-primary w-100" type="submit"><i class="bi bi-funnel me-1"></i>Tapis</button>
</div>
<div class="col-12">
<div class="d-flex flex-wrap gap-3 pt-2">
<label class="form-check">
<input class="form-check-input" type="checkbox" name="missing_bank_statement" value="1" @checked($filters['missing_bank_statement'] ?? false)>
<span class="form-check-label">Tiada penyata bank</span>
</label>
<label class="form-check">
<input class="form-check-input" type="checkbox" name="missing_account_number" value="1" @checked($filters['missing_account_number'] ?? false)>
<span class="form-check-label">Tiada no. akaun</span>
</label>
</div>
</div>
</form>
</div>
<div class="content-panel p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Staf</th>
<th>Pusat / Jawatan</th>
<th>Bank</th>
<th>Dokumen</th>
<x-sort-th column="status" label="Status" />
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($verifications as $verification)
@php($application = $verification->application)
@php($position = $application?->approvedPosition ?: $application?->requestedPosition)
@php($hasBankStatement = $application?->documents->contains('document_type', 'bank_statement') === true)
<tr>
<td>
<div class="fw-semibold">{{ $application?->name }}</div>
<div class="small text-secondary">{{ \App\Support\SensitiveData::maskIc($application?->ic_number) }} · {{ \App\Support\SensitiveData::maskPhone($application?->phone_number) }}</div>
</td>
<td>{{ $application?->pusatMengundi?->code }}<div class="small text-secondary">{{ $position?->code ?: '-' }}</div></td>
<td>
<div>{{ $application?->bank_name ?: '-' }}</div>
<div class="small text-secondary">{{ \App\Support\SensitiveData::maskAccount($application?->bank_account_number) }}</div>
</td>
<td>
<span class="badge {{ $hasBankStatement ? 'text-bg-success' : 'text-bg-danger' }}">{{ $hasBankStatement ? 'Penyata ada' : 'Penyata tiada' }}</span>
</td>
<td><span class="badge text-bg-secondary">{{ str_replace('_', ' ', $verification->status) }}</span></td>
<td class="text-end"><a class="btn btn-sm btn-outline-primary" href="{{ route('kewangan.bank.show', $verification) }}">Semak</a></td>
</tr>
@empty
<tr><td colspan="6" class="text-center text-secondary py-4">Tiada rekod kewangan dijumpai.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="mt-3">{{ $verifications->links() }}</div>
@endsection

View File

@@ -0,0 +1,104 @@
@extends('layouts.app')
@section('title', 'Semakan Rekod Bank')
@section('content')
@php($application = $verification->application)
@php($position = $application?->approvedPosition ?: $application?->requestedPosition)
@php($hasBankStatement = $application?->documents->contains('document_type', 'bank_statement') === true)
<div class="page-heading">
<div class="eyebrow">Admin Kewangan</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Semakan Rekod Bank</h1>
<p class="text-secondary mb-0">{{ $application?->name }} · {{ $application?->pusatMengundi?->name }}</p>
</div>
<div><a class="btn btn-outline-secondary" href="{{ route('kewangan.bank.index') }}">Kembali</a></div>
</div>
</div>
<div class="row g-3">
<div class="col-12 col-xl-7">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Maklumat Bank</h2>
<div class="row g-3">
<div class="col-md-6">
<div class="text-secondary small">Nama</div>
<div class="fw-semibold">{{ $application?->name }}</div>
</div>
<div class="col-md-6">
<div class="text-secondary small">No. IC</div>
<div>{{ \App\Support\SensitiveData::maskIc($application?->ic_number) }}</div>
</div>
<div class="col-md-6">
<div class="text-secondary small">Pusat Mengundi</div>
<div>{{ $application?->pusatMengundi?->code }} - {{ $application?->pusatMengundi?->name }}</div>
</div>
<div class="col-md-6">
<div class="text-secondary small">Jawatan</div>
<div>{{ $position?->code }} - {{ $position?->name }}</div>
</div>
<div class="col-md-6">
<div class="text-secondary small">Bank</div>
<div>{{ $application?->bank_name ?: '-' }}</div>
</div>
<div class="col-md-6">
<div class="text-secondary small">No. Akaun</div>
<div>{{ \App\Support\SensitiveData::maskAccount($application?->bank_account_number) }}</div>
</div>
<div class="col-md-6">
<div class="text-secondary small">Penyata Bank</div>
<span class="badge {{ $hasBankStatement ? 'text-bg-success' : 'text-bg-danger' }}">{{ $hasBankStatement ? 'Ada' : 'Tiada' }}</span>
@if ($hasBankStatement)
@php($bankStatement = $application?->documents->firstWhere('document_type', 'bank_statement'))
@if ($bankStatement)
<div class="mt-2">
<a class="btn btn-sm btn-outline-primary" href="{{ route('kewangan.documents.download', $bankStatement) }}">
<i class="bi bi-download me-1"></i>Muat turun
</a>
</div>
@endif
@endif
</div>
<div class="col-md-6">
<div class="text-secondary small">Status Semasa</div>
<span class="badge text-bg-secondary">{{ str_replace('_', ' ', $verification->status) }}</span>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Keputusan Semakan</h2>
<form method="POST" action="{{ route('kewangan.bank.update', $verification) }}">
@csrf
@method('PATCH')
<div class="mb-3">
<label class="form-label" for="status">Status</label>
<select class="form-select" id="status" name="status" required>
@foreach (['pending', 'verified', 'rejected', 'requires_correction'] as $status)
<option value="{{ $status }}" @selected(old('status', $verification->status) === $status)>{{ str_replace('_', ' ', $status) }}</option>
@endforeach
</select>
</div>
<div class="mb-3">
<label class="form-label" for="finance_note">Nota Kewangan</label>
<textarea class="form-control" id="finance_note" name="finance_note" rows="4">{{ old('finance_note', $verification->finance_note) }}</textarea>
</div>
<div class="d-flex justify-content-end">
<button class="btn btn-primary" type="submit"><i class="bi bi-save me-1"></i>Simpan</button>
</div>
</form>
</div>
<div class="content-panel p-3 p-md-4 mt-3">
<h2 class="h6 mb-3">Maklumat Audit Ringkas</h2>
<div class="small text-secondary">Disemak oleh</div>
<div>{{ $verification->verifiedBy?->name ?: '-' }}</div>
<div class="small text-secondary mt-2">Masa semakan</div>
<div>{{ $verification->verified_at?->format('d/m/Y H:i') ?: '-' }}</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,65 @@
@extends('layouts.app')
@section('title', 'Daftar KP')
@section('content')
<div class="page-heading">
<div class="eyebrow">KTM</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Daftar KP</h1>
<p class="text-secondary mb-0">KTM hanya boleh mendaftarkan Kerani Pengundian untuk saluran sendiri.</p>
</div>
<a href="{{ route('ktm.applications.index') }}" class="btn btn-outline-secondary align-self-start">
<i class="bi bi-arrow-left me-1"></i>Kembali
</a>
</div>
</div>
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('ktm.applications.store') }}">
@csrf
<div class="row g-3">
<div class="col-12">
<label for="ktm_assignment_id" class="form-label">Saluran KTM</label>
<select id="ktm_assignment_id" name="ktm_assignment_id" class="form-select" required>
<option value="">Pilih saluran</option>
@foreach ($assignments as $assignment)
<option value="{{ $assignment->id }}" @selected((int) old('ktm_assignment_id') === $assignment->id) @disabled(! ($registrationOpenByAssignment[$assignment->id] ?? false))>
{{ $assignment->pusatMengundi?->name }} - Saluran {{ $assignment->saluranMengundi?->number }}
{{ ($registrationOpenByAssignment[$assignment->id] ?? false) ? '' : '(pendaftaran ditutup)' }}
</option>
@endforeach
</select>
</div>
<div class="col-12 col-md-6">
<label for="name" class="form-label">Nama</label>
<input id="name" name="name" class="form-control" value="{{ old('name') }}" required>
</div>
<div class="col-12 col-md-6">
<label for="ic_number" class="form-label">No. Kad Pengenalan</label>
<input id="ic_number" name="ic_number" class="form-control" inputmode="numeric" value="{{ old('ic_number') }}" required>
</div>
<div class="col-12 col-md-6">
<label for="phone_number" class="form-label">No. Telefon</label>
<input id="phone_number" name="phone_number" class="form-control" value="{{ old('phone_number') }}" required>
</div>
<div class="col-12 col-md-6">
<label for="email" class="form-label">Emel</label>
<input id="email" name="email" type="email" class="form-control" value="{{ old('email') }}" required>
</div>
<div class="col-12">
<label for="address" class="form-label">Alamat</label>
<textarea id="address" name="address" class="form-control" rows="3">{{ old('address') }}</textarea>
</div>
</div>
<div class="d-flex justify-content-end mt-4">
<button type="submit" class="btn btn-primary">
<i class="bi bi-save me-1"></i>Simpan dan Hantar Emel
</button>
</div>
</form>
</div>
@endsection

View File

@@ -0,0 +1,88 @@
@extends('layouts.app')
@section('title', 'Team KP')
@section('content')
<div class="page-heading">
<div class="eyebrow">KTM</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Team KP</h1>
<p class="text-secondary mb-0">Saluran dan permohonan KP di bawah tugasan KTM anda.</p>
</div>
<a href="{{ route('ktm.applications.create') }}" class="btn btn-primary align-self-start">
<i class="bi bi-person-plus me-1"></i>Daftar KP
</a>
</div>
</div>
<div class="row g-3 mb-4">
@forelse ($assignments as $assignment)
<div class="col-12 col-xl-4">
<div class="content-panel p-3 h-100">
<div class="d-flex justify-content-between gap-3">
<div>
<div class="text-secondary small">{{ $assignment->pusatMengundi?->name }}</div>
<h2 class="h5 mb-2">Saluran {{ $assignment->saluranMengundi?->number }}</h2>
</div>
<span class="stat-icon text-bg-primary"><i class="bi bi-signpost"></i></span>
</div>
<div class="small text-secondary mb-2">Kekosongan KP</div>
<div class="fs-4 fw-bold">{{ $vacancyService->remainingForAssignment($assignment, 'KP') }}</div>
<hr>
<div class="fw-semibold mb-2">KP Aktif</div>
@forelse ($assignment->teamMembers->where('status', 'active') as $member)
<div class="small">{{ $member->application?->name ?? 'KP tanpa nama' }}</div>
@empty
<div class="small text-secondary">Tiada KP aktif.</div>
@endforelse
</div>
</div>
@empty
<div class="col-12">
<div class="content-panel p-4 text-center text-secondary">Tiada tugasan KTM aktif.</div>
</div>
@endforelse
</div>
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Permohonan KP</h2>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<x-sort-th column="name" label="Nama" />
<th>Pusat / Saluran</th>
<th>Sumber</th>
<x-sort-th column="status" label="Status" />
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($applications as $application)
<tr>
<td>
<div class="fw-semibold">{{ $application->name }}</div>
<div class="small text-secondary">{{ Illuminate\Support\Str::mask($application->ic_number, '*', 6, 4) }}</div>
</td>
<td>
{{ $application->pusatMengundi?->name }}
<div class="small text-secondary">Saluran {{ $application->selectedKtmAssignment?->saluranMengundi?->number ?? '-' }}</div>
</td>
<td><span class="badge text-bg-light border">{{ $application->source }}</span></td>
<td><span class="badge text-bg-light border">{{ $application->status }}</span></td>
<td class="text-end">
<a href="{{ route('ktm.applications.show', $application->public_uuid) }}" class="btn btn-sm btn-outline-primary">Buka</a>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-secondary py-4">Tiada permohonan KP.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $applications->links() }}
</div>
@endsection

View File

@@ -0,0 +1,81 @@
@extends('layouts.app')
@section('title', 'Permohonan KP')
@section('content')
<div class="page-heading">
<div class="eyebrow">KTM</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">{{ $application->name }}</h1>
<p class="text-secondary mb-0">{{ $application->pusatMengundi?->name }} - Saluran {{ $application->selectedKtmAssignment?->saluranMengundi?->number ?? '-' }}</p>
</div>
<a href="{{ route('ktm.applications.index') }}" class="btn btn-outline-secondary align-self-start">
<i class="bi bi-arrow-left me-1"></i>Kembali
</a>
</div>
</div>
<div class="row g-4">
<div class="col-12 col-xl-7">
<div class="content-panel p-3 p-md-4">
<div class="d-flex justify-content-between align-items-start gap-3 mb-3">
<h2 class="h5 mb-0">Maklumat KP</h2>
<span class="badge text-bg-light border">{{ $application->status }}</span>
</div>
<dl class="row mb-0">
<dt class="col-sm-4">No. Kad Pengenalan</dt>
<dd class="col-sm-8">{{ $application->ic_number }}</dd>
<dt class="col-sm-4">Telefon</dt>
<dd class="col-sm-8">{{ $application->phone_number }}</dd>
<dt class="col-sm-4">Emel</dt>
<dd class="col-sm-8">{{ $application->email ?: '-' }}</dd>
<dt class="col-sm-4">Alamat</dt>
<dd class="col-sm-8">{{ $application->address ?: '-' }}</dd>
<dt class="col-sm-4">Sumber</dt>
<dd class="col-sm-8">{{ $application->source }}</dd>
<dt class="col-sm-4">Jawatan</dt>
<dd class="col-sm-8">{{ $application->requestedPosition?->code }} - {{ $application->requestedPosition?->name }}</dd>
</dl>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Tindakan</h2>
@if (in_array($application->status, ['submitted', 'under_ppm_review', 'approved'], true))
<form method="POST" action="{{ route('ktm.applications.approve', $application->public_uuid) }}" class="mb-3">
@csrf
<button class="btn btn-primary w-100" type="submit">
<i class="bi bi-person-check me-1"></i>Lulus dan Assign KP
</button>
</form>
@endif
@if ($application->source === 'created_by_ktm' && $application->status !== 'assigned')
<form method="POST" action="{{ route('ktm.applications.destroy', $application->public_uuid) }}" data-confirm>
@csrf
@method('DELETE')
<button class="btn btn-outline-danger w-100" type="submit">
<i class="bi bi-trash me-1"></i>Padam Rekod KTM-created
</button>
</form>
@endif
</div>
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Sejarah Status</h2>
@forelse ($application->statusHistories->sortByDesc('created_at') as $history)
<div class="border-bottom pb-2 mb-2">
<div class="fw-semibold">{{ $history->from_status ?: '-' }} ke {{ $history->to_status }}</div>
<div class="small text-secondary">{{ $history->created_at?->format('d/m/Y H:i') }} oleh {{ $history->changedBy?->name ?? 'Sistem' }}</div>
@if ($history->note)
<div class="small">{{ $history->note }}</div>
@endif
</div>
@empty
<p class="text-secondary mb-0">Tiada sejarah status.</p>
@endforelse
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,45 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', $title ?? 'Dashboard') - {{ config('app.name') }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
@include('layouts.navigation')
<main class="page-shell">
<div class="container-fluid px-3 px-md-4">
@if (session('status'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('status') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Tutup"></button>
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger" role="alert">
<div class="fw-semibold mb-1">Sila semak maklumat borang.</div>
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@isset($slot)
{{ $slot }}
@else
@yield('content')
@endisset
</div>
</main>
@stack('scripts')
</body>
</html>

View File

@@ -0,0 +1,34 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', $title ?? 'Log Masuk') - {{ config('app.name') }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="auth-body">
<main class="auth-shell">
<div class="text-center text-white mb-4">
<a href="{{ route('home') }}" class="d-inline-flex align-items-center gap-2 text-white text-decoration-none">
<span class="brand-mark"><i class="bi bi-check2-square"></i></span>
<span class="fw-semibold">{{ config('app.name') }}</span>
</a>
</div>
<div class="auth-card">
@if (session('status'))
<div class="alert alert-success" role="alert">{{ session('status') }}</div>
@endif
@isset($slot)
{{ $slot }}
@else
@yield('content')
@endisset
</div>
</main>
</body>
</html>

View File

@@ -0,0 +1,72 @@
@php($user = auth()->user())
<nav class="navbar navbar-expand-lg navbar-dark app-navbar">
<div class="container-fluid px-3 px-md-4">
<a class="navbar-brand d-flex align-items-center gap-2" href="{{ route('dashboard') }}">
<span class="brand-mark"><i class="bi bi-check2-square"></i></span>
<span>{{ config('app.name') }}</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavigation" aria-controls="mainNavigation" aria-expanded="false" aria-label="Buka menu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNavigation">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('dashboard') ? 'active' : '' }}" href="{{ route('dashboard') }}">
<i class="bi bi-speedometer2 me-1"></i> Dashboard
</a>
</li>
@if ($user?->hasRole('Admin'))
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('admin.*') ? 'active' : '' }}" href="{{ route('admin.dashboard') }}">
<i class="bi bi-shield-check me-1"></i> Admin
</a>
</li>
@endif
@if ($user?->hasRole('Admin Kewangan'))
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('kewangan.*') ? 'active' : '' }}" href="{{ route('kewangan.dashboard') }}">
<i class="bi bi-bank me-1"></i> Kewangan
</a>
</li>
@endif
@if ($user?->hasRole('PPM'))
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('ppm.*') ? 'active' : '' }}" href="{{ route('ppm.dashboard') }}">
<i class="bi bi-geo-alt me-1"></i> PPM
</a>
</li>
@endif
@if ($user?->hasRole('KTM'))
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('ktm.*') ? 'active' : '' }}" href="{{ route('ktm.dashboard') }}">
<i class="bi bi-person-badge me-1"></i> KTM
</a>
</li>
@endif
</ul>
<div class="dropdown">
<button class="btn btn-outline-light btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-1"></i> {{ $user?->name }}
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{{ route('profile.edit') }}">Profil</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="dropdown-item">Log keluar</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</nav>

View File

@@ -0,0 +1,49 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', $title ?? 'Pendaftaran') - {{ config('app.name') }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
<nav class="navbar navbar-dark app-navbar">
<div class="container-fluid px-3 px-md-4">
<a class="navbar-brand d-flex align-items-center gap-2" href="{{ route('home') }}">
<span class="brand-mark"><i class="bi bi-check2-square"></i></span>
<span>{{ config('app.name') }}</span>
</a>
<a href="{{ route('login') }}" class="btn btn-outline-light btn-sm">Log masuk</a>
</div>
</nav>
<main class="page-shell">
<div class="container px-3 px-md-4">
@if (session('status'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('status') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Tutup"></button>
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger" role="alert">
<div class="fw-semibold mb-1">Sila semak maklumat borang.</div>
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@yield('content')
</div>
</main>
@stack('scripts')
</body>
</html>

View File

@@ -0,0 +1,74 @@
@extends('layouts.app')
@section('title', 'Semakan Permohonan PPM')
@section('content')
<div class="page-heading">
<div class="eyebrow">PPM</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Semakan Permohonan</h1>
<p class="text-secondary mb-0">Permohonan untuk Pusat Mengundi di bawah jagaan anda sahaja.</p>
</div>
</div>
</div>
<div class="content-panel p-3 p-md-4 mb-4">
<form method="GET" class="row g-2 align-items-end">
<div class="col-12 col-md-4">
<label for="status" class="form-label">Status</label>
<select id="status" name="status" class="form-select">
<option value="">Semua status</option>
@foreach (['submitted', 'under_ppm_review', 'approved', 'assigned', 'rejected'] as $option)
<option value="{{ $option }}" @selected($status === $option)>{{ $option }}</option>
@endforeach
</select>
</div>
<div class="col-12 col-md-auto">
<button type="submit" class="btn btn-primary"><i class="bi bi-funnel me-1"></i>Tapis</button>
</div>
</form>
</div>
<div class="content-panel p-3 p-md-4">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<x-sort-th column="name" label="Nama" />
<th>Pusat</th>
<th>Jawatan</th>
<x-sort-th column="status" label="Status" />
<x-sort-th column="created_at" label="Tarikh" />
<th class="text-end no-sort">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($applications as $application)
<tr>
<td>
<div class="fw-semibold">{{ $application->name }}</div>
<div class="small text-secondary">{{ Illuminate\Support\Str::mask($application->ic_number, '*', 6, 4) }}</div>
</td>
<td>{{ $application->pusatMengundi?->name }}</td>
<td>
{{ $application->approvedPosition?->code ?? $application->requestedPosition?->code }}
<div class="small text-secondary">{{ $application->requestedPosition?->name }}</div>
</td>
<td><span class="badge text-bg-light border">{{ $application->status }}</span></td>
<td>{{ $application->created_at?->format('d/m/Y H:i') }}</td>
<td class="text-end">
<a href="{{ route('ppm.applications.show', $application->public_uuid) }}" class="btn btn-sm btn-outline-primary">Semak</a>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center text-secondary py-4">Tiada permohonan untuk semakan.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $applications->links() }}
</div>
@endsection

View File

@@ -0,0 +1,265 @@
@extends('layouts.app')
@section('title', 'Semak Permohonan')
@section('content')
<div class="page-heading">
<div class="eyebrow">PPM</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">{{ $application->name }}</h1>
<p class="text-secondary mb-0">{{ $application->pusatMengundi?->code }} - {{ $application->pusatMengundi?->name }}</p>
</div>
<a href="{{ route('ppm.applications.index') }}" class="btn btn-outline-secondary align-self-start">
<i class="bi bi-arrow-left me-1"></i>Kembali
</a>
</div>
</div>
<div class="row g-4">
<div class="col-12 col-xl-7">
<div class="content-panel p-3 p-md-4 mb-4">
<div class="d-flex justify-content-between align-items-start gap-3 mb-3">
<h2 class="h5 mb-0">Maklumat Pemohon</h2>
<span class="badge text-bg-light border">{{ $application->status }}</span>
</div>
<dl class="row mb-0">
<dt class="col-sm-4">No. Kad Pengenalan</dt>
<dd class="col-sm-8">{{ $application->ic_number }}</dd>
<dt class="col-sm-4">Telefon</dt>
<dd class="col-sm-8">{{ $application->phone_number }}</dd>
<dt class="col-sm-4">Emel</dt>
<dd class="col-sm-8">{{ $application->email ?: '-' }}</dd>
<dt class="col-sm-4">Alamat</dt>
<dd class="col-sm-8">{{ $application->address ?: '-' }}</dd>
<dt class="col-sm-4">Jawatan Dipohon</dt>
<dd class="col-sm-8">{{ $application->requestedPosition?->code }} - {{ $application->requestedPosition?->name }}</dd>
<dt class="col-sm-4">Jawatan Diluluskan</dt>
<dd class="col-sm-8">
@if ($application->approvedPosition)
{{ $application->approvedPosition->code }} - {{ $application->approvedPosition->name }}
@else
<span class="text-secondary">Belum diluluskan</span>
@endif
</dd>
<dt class="col-sm-4">Bank</dt>
<dd class="col-sm-8">{{ $application->bank_name }} / {{ Illuminate\Support\Str::mask((string) $application->bank_account_number, '*', 4, 4) }}</dd>
<dt class="col-sm-4">KTM Pilihan Pemohon</dt>
<dd class="col-sm-8">
@if ($application->selectedKtmAssignment)
{{ $application->selectedKtmAssignment->user?->name ?? '-' }}
<span class="text-secondary">Saluran {{ $application->selectedKtmAssignment->saluranMengundi?->number ?? '-' }}</span>
@else
-
@endif
</dd>
</dl>
</div>
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Dokumen</h2>
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead>
<tr>
<th>Jenis</th>
<th>Fail</th>
<th class="text-end">Tindakan</th>
</tr>
</thead>
<tbody>
@forelse ($application->documents as $document)
<tr>
<td>{{ $document->document_type }}</td>
<td>{{ $document->original_name }}</td>
<td class="text-end">
<a href="{{ route('ppm.applications.documents.download', [$application->public_uuid, $document]) }}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-download me-1"></i>Muat turun
</a>
</td>
</tr>
@empty
<tr>
<td colspan="3" class="text-center text-secondary py-3">Tiada dokumen.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Wakil Agensi</h2>
<div class="row g-3">
<div class="col-12 col-lg-4">
<div class="border rounded p-3 h-100">
<div class="fw-semibold mb-2">Polis Pengiring</div>
@forelse ($application->pusatMengundi?->policeEscorts ?? [] as $police)
<div class="small">{{ $police->name }} <span class="text-secondary">{{ $police->saluranMengundi?->number ? 'Saluran '.$police->saluranMengundi->number : '' }}</span></div>
@empty
<div class="small text-secondary">Tiada rekod.</div>
@endforelse
</div>
</div>
<div class="col-12 col-lg-4">
<div class="border rounded p-3 h-100">
<div class="fw-semibold mb-2">Wakil KKM</div>
@forelse ($application->pusatMengundi?->kkmRepresentatives ?? [] as $kkm)
<div class="small">{{ $kkm->name }}</div>
@empty
<div class="small text-secondary">Tiada rekod.</div>
@endforelse
</div>
</div>
<div class="col-12 col-lg-4">
<div class="border rounded p-3 h-100">
<div class="fw-semibold mb-2">Wakil JKM</div>
@forelse ($application->pusatMengundi?->jkmRepresentatives ?? [] as $jkm)
<div class="small">{{ $jkm->name }}</div>
@empty
<div class="small text-secondary">Tiada rekod.</div>
@endforelse
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Lulus dan Assign</h2>
@if (in_array($application->status, ['submitted', 'under_ppm_review', 'approved'], true))
<form method="POST" action="{{ route('ppm.applications.approve', $application->public_uuid) }}">
@csrf
<div class="mb-3">
<label for="approved_position_code" class="form-label">Jawatan Diluluskan</label>
<select id="approved_position_code" name="approved_position_code" class="form-select" required>
@foreach ($roles as $role)
<option value="{{ $role->code }}" @selected(old('approved_position_code', $application->approvedPosition?->code ?? $application->requestedPosition?->code) === $role->code)>
{{ $role->code }} - {{ $role->name }}
</option>
@endforeach
</select>
</div>
<div class="mb-3 d-none" id="saluran-section">
<label for="saluran_mengundi_id" class="form-label">Saluran Mengundi untuk KTM</label>
<select id="saluran_mengundi_id" name="saluran_mengundi_id" class="form-select">
<option value="">Pilih Saluran</option>
@foreach ($availableKtmSalurans as $saluran)
<option value="{{ $saluran->id }}" @selected((int) old('saluran_mengundi_id') === $saluran->id)>
Saluran {{ $saluran->number }}{{ $saluran->name ? ' - '.$saluran->name : '' }}
</option>
@endforeach
</select>
</div>
<div class="mb-3 d-none" id="ktm-section">
<label for="ktm_assignment_id" class="form-label">KTM untuk KP/KPDP</label>
<select id="ktm_assignment_id" name="ktm_assignment_id" class="form-select" data-old-value="{{ old('ktm_assignment_id', $application->selected_ktm_assignment_id) }}">
<option value="">Pilih KTM</option>
</select>
</div>
<div class="mb-3">
<label for="note" class="form-label">Catatan</label>
<textarea id="note" name="note" class="form-control" rows="3">{{ old('note') }}</textarea>
</div>
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-person-check me-1"></i>Lulus dan Assign
</button>
</form>
@else
<p class="text-secondary mb-0">Permohonan ini tidak lagi boleh diluluskan.</p>
@endif
</div>
<div class="content-panel p-3 p-md-4 mb-4">
<h2 class="h5 mb-3">Tolak Permohonan</h2>
@if (in_array($application->status, ['submitted', 'under_ppm_review', 'approved'], true))
<form method="POST" action="{{ route('ppm.applications.reject', $application->public_uuid) }}" data-confirm>
@csrf
<div class="mb-3">
<label for="reason" class="form-label">Sebab Penolakan</label>
<textarea id="reason" name="reason" class="form-control" rows="3" required>{{ old('reason') }}</textarea>
</div>
<button type="submit" class="btn btn-outline-danger w-100">
<i class="bi bi-x-circle me-1"></i>Tolak
</button>
</form>
@else
<p class="text-secondary mb-0">Permohonan ini tidak lagi boleh ditolak.</p>
@endif
</div>
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Sejarah Status</h2>
@forelse ($application->statusHistories->sortByDesc('created_at') as $history)
<div class="border-bottom pb-2 mb-2">
<div class="fw-semibold">{{ $history->from_status ?: '-' }} ke {{ $history->to_status }}</div>
<div class="small text-secondary">{{ $history->created_at?->format('d/m/Y H:i') }} oleh {{ $history->changedBy?->name ?? 'Sistem' }}</div>
@if ($history->note)
<div class="small">{{ $history->note }}</div>
@endif
</div>
@empty
<p class="text-secondary mb-0">Tiada sejarah status.</p>
@endforelse
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
const ktmOptionsByRole = @json($ktmOptionsByRole);
const roleSelect = document.getElementById('approved_position_code');
const saluranSection = document.getElementById('saluran-section');
const saluranSelect = document.getElementById('saluran_mengundi_id');
const ktmSection = document.getElementById('ktm-section');
const ktmSelect = document.getElementById('ktm_assignment_id');
const oldKtmValue = ktmSelect.dataset.oldValue;
function refreshAssignmentInputs() {
const roleCode = roleSelect.value;
const isKtm = roleCode === 'KTM';
const requiresKtm = ['KP', 'KPDP'].includes(roleCode);
saluranSection.classList.toggle('d-none', !isKtm);
saluranSelect.required = isKtm;
saluranSelect.disabled = !isKtm;
ktmSection.classList.toggle('d-none', !requiresKtm);
ktmSelect.required = requiresKtm;
ktmSelect.disabled = !requiresKtm;
ktmSelect.innerHTML = '';
if (!requiresKtm) {
ktmSelect.append(new Option('Pilih KTM', ''));
return;
}
const options = ktmOptionsByRole[roleCode] || [];
if (options.length === 0) {
ktmSelect.append(new Option('Tiada KTM dengan kekosongan', ''));
return;
}
ktmSelect.append(new Option('Pilih KTM', ''));
options.forEach((item) => {
const option = new Option(`${item.label} (${item.remaining} kosong)`, item.id);
if (oldKtmValue && oldKtmValue === String(item.id)) {
option.selected = true;
}
ktmSelect.append(option);
});
}
roleSelect.addEventListener('change', refreshAssignmentInputs);
refreshAssignmentInputs();
</script>
@endpush

View File

@@ -0,0 +1,45 @@
@extends('layouts.app')
@section('title', 'Kehadiran PPM')
@section('content')
<div class="page-heading">
<div class="eyebrow">PPM</div>
<h1 class="h3 mb-1">Rekod Kehadiran</h1>
<p class="text-secondary mb-0">Pilih Pusat Mengundi jagaan untuk merekod kehadiran hari mengundi.</p>
</div>
<div class="row g-3">
@forelse ($pusats as $pusat)
@php($summary = $summaryService->totalsForPusat($pusat))
<div class="col-12 col-lg-6">
<div class="content-panel p-3 p-md-4 h-100">
<div class="d-flex justify-content-between gap-3">
<div>
<h2 class="h5 mb-1">{{ $pusat->code }} - {{ $pusat->name }}</h2>
<div class="text-secondary small">{{ $pusat->election?->name }}</div>
</div>
<span class="badge {{ $pusat->election?->settings?->is_attendance_active ? 'text-bg-success' : 'text-bg-secondary' }}">
{{ $pusat->election?->settings?->is_attendance_active ? 'Aktif' : 'Belum aktif' }}
</span>
</div>
<div class="row g-2 mt-3">
<div class="col-6 col-md-3"><div class="small text-secondary">Staf</div><div class="fw-bold">{{ $summary['total_staff'] }}</div></div>
<div class="col-6 col-md-3"><div class="small text-secondary">Hadir</div><div class="fw-bold text-success">{{ $summary['present'] }}</div></div>
<div class="col-6 col-md-3"><div class="small text-secondary">Tidak hadir</div><div class="fw-bold text-danger">{{ $summary['absent'] }}</div></div>
<div class="col-6 col-md-3"><div class="small text-secondary">Belum rekod</div><div class="fw-bold text-warning">{{ $summary['not_recorded'] }}</div></div>
</div>
<div class="mt-3">
<a class="btn btn-primary" href="{{ route('ppm.attendance.show', $pusat) }}">
<i class="bi bi-clipboard-check me-1"></i>Rekod Kehadiran
</a>
</div>
</div>
</div>
@empty
<div class="col-12">
<div class="content-panel p-4 text-center text-secondary">Tiada Pusat Mengundi ditetapkan kepada akaun PPM ini.</div>
</div>
@endforelse
</div>
@endsection

View File

@@ -0,0 +1,92 @@
@extends('layouts.app')
@section('title', 'Rekod Kehadiran')
@section('content')
<div class="page-heading">
<div class="eyebrow">PPM</div>
<div class="d-flex justify-content-between flex-column flex-lg-row gap-3">
<div>
<h1 class="h3 mb-1">Rekod Kehadiran</h1>
<p class="text-secondary mb-0">{{ $pusat->code }} - {{ $pusat->name }}</p>
</div>
<div><a class="btn btn-outline-secondary" href="{{ route('ppm.attendance.index') }}">Kembali</a></div>
</div>
</div>
<div class="row g-3 mb-4">
@foreach ([
['label' => 'Staf', 'value' => $summary['total_staff'], 'tone' => 'primary'],
['label' => 'Hadir', 'value' => $summary['present'], 'tone' => 'success'],
['label' => 'Tidak hadir', 'value' => $summary['absent'], 'tone' => 'danger'],
['label' => 'Belum rekod', 'value' => $summary['not_recorded'], 'tone' => 'warning'],
] as $card)
<div class="col-6 col-xl-3">
<div class="stat-card h-100">
<div class="text-secondary small">{{ $card['label'] }}</div>
<div class="fs-3 fw-bold mt-2 text-{{ $card['tone'] }}">{{ $card['value'] }}</div>
</div>
</div>
@endforeach
</div>
@if (! $pusat->election?->settings?->is_attendance_active)
<div class="alert alert-warning">
Modul kehadiran belum diaktifkan untuk pilihanraya ini.
</div>
@endif
<form method="POST" action="{{ route('ppm.attendance.store', $pusat) }}">
@csrf
<div class="content-panel p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th>Staf</th>
<th>Jawatan</th>
<th>Saluran</th>
<th>Status</th>
<th>Masa Check-in</th>
<th>Catatan</th>
</tr>
</thead>
<tbody>
@forelse ($assignments as $assignment)
@php($attendance = $assignment->attendance)
<tr>
<td>
<div class="fw-semibold">{{ $assignment->application?->name ?: $assignment->user?->name ?: 'Tanpa nama' }}</div>
<div class="small text-secondary">{{ $assignment->application?->ic_number ?: $assignment->user?->email ?: '-' }}</div>
</td>
<td>{{ $assignment->position?->code }}</td>
<td>{{ $assignment->saluranMengundi?->number ?: '-' }}</td>
<td style="min-width: 160px;">
<select class="form-select form-select-sm" name="attendance[{{ $assignment->id }}][status]">
@foreach (['not_recorded' => 'Belum rekod', 'present' => 'Hadir', 'absent' => 'Tidak hadir'] as $value => $label)
<option value="{{ $value }}" @selected(old("attendance.{$assignment->id}.status", $attendance?->status ?? 'not_recorded') === $value)>{{ $label }}</option>
@endforeach
</select>
</td>
<td style="min-width: 190px;">
<input class="form-control form-control-sm" type="datetime-local" name="attendance[{{ $assignment->id }}][check_in_time]" value="{{ old("attendance.{$assignment->id}.check_in_time", $attendance?->check_in_time?->format('Y-m-d\TH:i')) }}">
</td>
<td style="min-width: 220px;">
<input class="form-control form-control-sm" name="attendance[{{ $assignment->id }}][note]" value="{{ old("attendance.{$assignment->id}.note", $attendance?->note) }}">
</td>
</tr>
@empty
<tr><td colspan="6" class="text-center text-secondary py-4">Tiada staf aktif untuk Pusat Mengundi ini.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="d-flex justify-content-end mt-3">
<button class="btn btn-primary" type="submit" @disabled(! $pusat->election?->settings?->is_attendance_active)>
<i class="bi bi-save me-1"></i>Simpan Kehadiran
</button>
</div>
</form>
@endsection

View File

@@ -0,0 +1,81 @@
@extends('layouts.app')
@section('title', 'Profil')
@section('content')
<div class="page-heading">
<div class="eyebrow">Akaun Pengguna</div>
<h1 class="h3 mb-1">Profil</h1>
<p class="text-secondary mb-0">Maklumat asas akaun sistem.</p>
</div>
<div class="row g-4">
<div class="col-12 col-xl-6">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Maklumat profil</h2>
<form method="POST" action="{{ route('profile.update') }}">
@csrf
@method('PATCH')
<div class="mb-3">
<label for="name" class="form-label">Nama</label>
<input id="name" name="name" type="text" class="form-control @error('name') is-invalid @enderror" value="{{ old('name', $user->name) }}" required autofocus autocomplete="name">
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="email" class="form-label">Emel</label>
<input id="email" name="email" type="email" class="form-control @error('email') is-invalid @enderror" value="{{ old('email', $user->email) }}" required autocomplete="username">
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">
<i class="bi bi-save me-1"></i>Simpan
</button>
</form>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Kata laluan</h2>
<form method="POST" action="{{ route('password.update') }}">
@csrf
@method('PUT')
<div class="mb-3">
<label for="update_password_current_password" class="form-label">Kata laluan semasa</label>
<input id="update_password_current_password" name="current_password" type="password" class="form-control @error('current_password', 'updatePassword') is-invalid @enderror" autocomplete="current-password">
@error('current_password', 'updatePassword')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="update_password_password" class="form-label">Kata laluan baru</label>
<input id="update_password_password" name="password" type="password" class="form-control @error('password', 'updatePassword') is-invalid @enderror" autocomplete="new-password">
@error('password', 'updatePassword')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="update_password_password_confirmation" class="form-label">Sahkan kata laluan</label>
<input id="update_password_password_confirmation" name="password_confirmation" type="password" class="form-control @error('password_confirmation', 'updatePassword') is-invalid @enderror" autocomplete="new-password">
@error('password_confirmation', 'updatePassword')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">
<i class="bi bi-key me-1"></i>Kemas kini
</button>
</form>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,55 @@
<section class="space-y-6">
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Delete Account') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }}
</p>
</header>
<x-danger-button
x-data=""
x-on:click.prevent="$dispatch('open-modal', 'confirm-user-deletion')"
>{{ __('Delete Account') }}</x-danger-button>
<x-modal name="confirm-user-deletion" :show="$errors->userDeletion->isNotEmpty()" focusable>
<form method="post" action="{{ route('profile.destroy') }}" class="p-6">
@csrf
@method('delete')
<h2 class="text-lg font-medium text-gray-900">
{{ __('Are you sure you want to delete your account?') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }}
</p>
<div class="mt-6">
<x-input-label for="password" value="{{ __('Password') }}" class="sr-only" />
<x-text-input
id="password"
name="password"
type="password"
class="mt-1 block w-3/4"
placeholder="{{ __('Password') }}"
/>
<x-input-error :messages="$errors->userDeletion->get('password')" class="mt-2" />
</div>
<div class="mt-6 flex justify-end">
<x-secondary-button x-on:click="$dispatch('close')">
{{ __('Cancel') }}
</x-secondary-button>
<x-danger-button class="ms-3">
{{ __('Delete Account') }}
</x-danger-button>
</div>
</form>
</x-modal>
</section>

View File

@@ -0,0 +1,48 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Update Password') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Ensure your account is using a long, random password to stay secure.') }}
</p>
</header>
<form method="post" action="{{ route('password.update') }}" class="mt-6 space-y-6">
@csrf
@method('put')
<div>
<x-input-label for="update_password_current_password" :value="__('Current Password')" />
<x-text-input id="update_password_current_password" name="current_password" type="password" class="mt-1 block w-full" autocomplete="current-password" />
<x-input-error :messages="$errors->updatePassword->get('current_password')" class="mt-2" />
</div>
<div>
<x-input-label for="update_password_password" :value="__('New Password')" />
<x-text-input id="update_password_password" name="password" type="password" class="mt-1 block w-full" autocomplete="new-password" />
<x-input-error :messages="$errors->updatePassword->get('password')" class="mt-2" />
</div>
<div>
<x-input-label for="update_password_password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="update_password_password_confirmation" name="password_confirmation" type="password" class="mt-1 block w-full" autocomplete="new-password" />
<x-input-error :messages="$errors->updatePassword->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'password-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>

View File

@@ -0,0 +1,64 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Profile Information') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __("Update your account's profile information and email address.") }}
</p>
</header>
<form id="send-verification" method="post" action="{{ route('verification.send') }}">
@csrf
</form>
<form method="post" action="{{ route('profile.update') }}" class="mt-6 space-y-6">
@csrf
@method('patch')
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name', $user->name)" required autofocus autocomplete="name" />
<x-input-error class="mt-2" :messages="$errors->get('name')" />
</div>
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" :value="old('email', $user->email)" required autocomplete="username" />
<x-input-error class="mt-2" :messages="$errors->get('email')" />
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())
<div>
<p class="text-sm mt-2 text-gray-800">
{{ __('Your email address is unverified.') }}
<button form="send-verification" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
{{ __('Click here to re-send the verification email.') }}
</button>
</p>
@if (session('status') === 'verification-link-sent')
<p class="mt-2 font-medium text-sm text-green-600">
{{ __('A new verification link has been sent to your email address.') }}
</p>
@endif
</div>
@endif
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'profile-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>

View File

@@ -0,0 +1,34 @@
@extends('layouts.public')
@section('title', 'Permohonan Petugas')
@section('content')
<div class="page-heading">
<div class="eyebrow">Pendaftaran Awam</div>
<h1 class="h3 mb-1">Permohonan Petugas Pilihanraya</h1>
<p class="text-secondary mb-0">{{ $pusatMengundi->name }}</p>
</div>
<div class="content-panel p-3 p-md-4">
<div class="d-flex align-items-start gap-3">
<div class="stat-icon text-bg-info">
<i class="bi bi-qr-code-scan"></i>
</div>
<div>
<h2 class="h5 mb-2">Borang permohonan akan dibina dalam Phase 4</h2>
<p class="text-secondary mb-3">
Pautan QR ini telah disediakan untuk Pusat Mengundi yang betul. Modul permohonan awam seterusnya
akan menggunakan UUID ini tanpa mendedahkan ID dalaman sistem.
</p>
<dl class="row mb-0">
<dt class="col-sm-3">Kod Pusat</dt>
<dd class="col-sm-9">{{ $pusatMengundi->code }}</dd>
<dt class="col-sm-3">Daerah</dt>
<dd class="col-sm-9">{{ $pusatMengundi->daerahMengundi?->name ?? '-' }}</dd>
<dt class="col-sm-3">Alamat</dt>
<dd class="col-sm-9">{{ $pusatMengundi->address ?: '-' }}</dd>
</dl>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,23 @@
@extends('layouts.public')
@section('title', 'Pendaftaran Ditutup')
@section('content')
<div class="page-heading">
<div class="eyebrow">Pendaftaran Awam</div>
<h1 class="h3 mb-1">Pendaftaran Ditutup</h1>
<p class="text-secondary mb-0">{{ $pusatMengundi->code }} - {{ $pusatMengundi->name }}</p>
</div>
<div class="content-panel p-3 p-md-4">
<div class="d-flex gap-3 align-items-start">
<div class="stat-icon text-bg-warning">
<i class="bi bi-calendar-x"></i>
</div>
<div>
<h2 class="h5 mb-2">Permohonan tidak boleh dihantar</h2>
<p class="text-secondary mb-0">{{ $message }}</p>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,164 @@
@extends('layouts.public')
@section('title', 'Permohonan Petugas')
@section('content')
<div class="page-heading">
<div class="eyebrow">Pendaftaran Awam</div>
<h1 class="h3 mb-1">Permohonan Petugas Pilihanraya</h1>
<p class="text-secondary mb-0">{{ $pusatMengundi->code }} - {{ $pusatMengundi->name }}</p>
</div>
<div class="row g-4">
<div class="col-12 col-xl-4">
<div class="content-panel p-3 p-md-4">
<h2 class="h5 mb-3">Pusat Mengundi</h2>
<dl class="mb-0">
<dt class="text-secondary small">Daerah</dt>
<dd class="mb-3">{{ $pusatMengundi->daerahMengundi?->name ?? '-' }}</dd>
<dt class="text-secondary small">Alamat</dt>
<dd class="mb-0">{{ $pusatMengundi->address ?: '-' }}</dd>
</dl>
</div>
</div>
<div class="col-12 col-xl-8">
<div class="content-panel p-3 p-md-4">
<form method="POST" action="{{ route('public.applications.store', $pusatMengundi->public_uuid) }}" enctype="multipart/form-data">
@csrf
<div class="row g-3">
<div class="col-12">
<h2 class="h5 mb-0">Maklumat Pemohon</h2>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="name">Nama</label>
<input id="name" name="name" class="form-control" value="{{ old('name') }}" required>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="ic_number">No. Kad Pengenalan</label>
<input id="ic_number" name="ic_number" class="form-control" inputmode="numeric" value="{{ old('ic_number') }}" required>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="phone_number">No. Telefon</label>
<input id="phone_number" name="phone_number" class="form-control" value="{{ old('phone_number') }}" required>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="email">Emel</label>
<input id="email" name="email" type="email" class="form-control" value="{{ old('email') }}">
</div>
<div class="col-12">
<label class="form-label" for="address">Alamat</label>
<textarea id="address" name="address" class="form-control" rows="3" required>{{ old('address') }}</textarea>
</div>
<div class="col-12">
<hr>
<h2 class="h5 mb-0">Jawatan dan Bank</h2>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="requested_position_code">Jawatan Dipohon</label>
<select id="requested_position_code" name="requested_position_code" class="form-select" required>
<option value="">Pilih jawatan</option>
@foreach ($roles as $role)
<option value="{{ $role->code }}" @selected(old('requested_position_code') === $role->code)>
{{ $role->code }} - {{ $role->name }}
</option>
@endforeach
</select>
</div>
<div class="col-12 col-md-6 d-none" id="ktm-section">
<label class="form-label" for="selected_ktm_assignment_id">KTM</label>
<select id="selected_ktm_assignment_id" name="selected_ktm_assignment_id" class="form-select" data-old-value="{{ old('selected_ktm_assignment_id') }}">
<option value="">Pilih KTM</option>
</select>
<div class="form-text">KTM hanya dipaparkan jika kekosongan masih ada.</div>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="bank_name">Nama Bank</label>
<input id="bank_name" name="bank_name" class="form-control" value="{{ old('bank_name') }}" required>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="bank_account_number">No. Akaun Bank</label>
<input id="bank_account_number" name="bank_account_number" class="form-control" inputmode="numeric" value="{{ old('bank_account_number') }}" required>
</div>
<div class="col-12">
<hr>
<h2 class="h5 mb-0">Dokumen</h2>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="ic_document">Dokumen Kad Pengenalan</label>
<input id="ic_document" name="ic_document" type="file" class="form-control" accept=".pdf,.jpg,.jpeg,.png" required>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="bank_statement">Penyata Bank</label>
<input id="bank_statement" name="bank_statement" type="file" class="form-control" accept=".pdf,.jpg,.jpeg,.png" required>
</div>
</div>
<div class="d-flex justify-content-end mt-4">
<button type="submit" class="btn btn-primary">
<i class="bi bi-send me-1"></i>Hantar Permohonan
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
const ktmOptionsByRole = @json($ktmOptionsByRole);
const roleSelect = document.getElementById('requested_position_code');
const ktmSection = document.getElementById('ktm-section');
const ktmSelect = document.getElementById('selected_ktm_assignment_id');
const oldKtmValue = ktmSelect.dataset.oldValue;
function refreshKtmOptions() {
const roleCode = roleSelect.value;
const requiresKtm = ['KP', 'KPDP'].includes(roleCode);
const options = ktmOptionsByRole[roleCode] || [];
ktmSection.classList.toggle('d-none', !requiresKtm);
ktmSelect.required = requiresKtm;
ktmSelect.disabled = !requiresKtm;
ktmSelect.innerHTML = '';
if (!requiresKtm) {
ktmSelect.append(new Option('Pilih KTM', ''));
return;
}
if (options.length === 0) {
ktmSelect.append(new Option('Tiada KTM dengan kekosongan', ''));
return;
}
ktmSelect.append(new Option('Pilih KTM', ''));
options.forEach((item) => {
const option = new Option(`${item.label} (${item.remaining} kosong)`, item.id);
if (oldKtmValue && oldKtmValue === String(item.id)) {
option.selected = true;
}
ktmSelect.append(option);
});
}
roleSelect.addEventListener('change', refreshKtmOptions);
refreshKtmOptions();
</script>
@endpush

View File

@@ -0,0 +1,91 @@
@extends('layouts.public')
@section('title', 'Status Permohonan')
@section('content')
<div class="page-heading">
<div class="eyebrow">Semak Status Permohonan</div>
<h1 class="h3 mb-1">Status Permohonan</h1>
<p class="text-secondary mb-0">Paparan status terhad. Dokumen dan maklumat sensitif tidak dipaparkan.</p>
</div>
@if ($application === null)
{{-- Generic response for unknown / internal-only UUIDs --}}
<div class="content-panel p-3 p-md-4">
<div class="d-flex gap-3 align-items-start">
<div class="stat-icon text-bg-secondary">
<i class="bi bi-question-circle"></i>
</div>
<div>
<h2 class="h5 mb-2">Status tidak dapat dipaparkan</h2>
<p class="text-secondary mb-0">
Tiada rekod permohonan yang aktif ditemui untuk pautan ini.
Sila pastikan URL yang digunakan adalah betul.
</p>
</div>
</div>
</div>
@else
@php
$statusConfig = match ($application->status) {
'submitted' => ['label' => 'Menunggu Semakan', 'tone' => 'info', 'icon' => 'bi-hourglass-split'],
'under_ppm_review' => ['label' => 'Sedang Disemak', 'tone' => 'warning', 'icon' => 'bi-eye'],
'approved' => ['label' => 'Diluluskan', 'tone' => 'success', 'icon' => 'bi-check-circle'],
'assigned' => ['label' => 'Diluluskan & Ditempatkan', 'tone' => 'success', 'icon' => 'bi-person-check'],
'rejected' => ['label' => 'Ditolak', 'tone' => 'danger', 'icon' => 'bi-x-circle'],
'cancelled' => ['label' => 'Dibatalkan', 'tone' => 'secondary', 'icon' => 'bi-slash-circle'],
default => ['label' => ucfirst($application->status), 'tone' => 'secondary', 'icon' => 'bi-circle'],
};
@endphp
<div class="content-panel p-3 p-md-4">
<div class="d-flex gap-3 align-items-start mb-4">
<div class="stat-icon text-bg-{{ $statusConfig['tone'] }}">
<i class="bi {{ $statusConfig['icon'] }}"></i>
</div>
<div>
<h2 class="h5 mb-1">{{ $statusConfig['label'] }}</h2>
<p class="text-secondary mb-0 small">
Dikemaskini: {{ $application->updated_at?->format('d/m/Y H:i') }}
</p>
</div>
</div>
<dl class="row mb-0">
<dt class="col-sm-4 text-secondary small">Nama (disamarkan)</dt>
<dd class="col-sm-8 fw-semibold">{{ $maskedName }}</dd>
<dt class="col-sm-4 text-secondary small">Jawatan Dipohon</dt>
<dd class="col-sm-8">
{{ $application->requestedPosition?->name ?? '-' }}
</dd>
@if ($application->approvedPosition && $application->approvedPosition->id !== $application->requestedPosition?->id)
<dt class="col-sm-4 text-secondary small">Jawatan Diluluskan</dt>
<dd class="col-sm-8">{{ $application->approvedPosition->name }}</dd>
@endif
<dt class="col-sm-4 text-secondary small">Pusat Mengundi</dt>
<dd class="col-sm-8">{{ $application->pusatMengundi?->name ?? '-' }}</dd>
<dt class="col-sm-4 text-secondary small">Status</dt>
<dd class="col-sm-8">
<span class="badge text-bg-{{ $statusConfig['tone'] }}">
{{ $statusConfig['label'] }}
</span>
</dd>
@if ($application->status === 'rejected' && $application->rejection_reason)
<dt class="col-sm-4 text-secondary small">Sebab Penolakan</dt>
<dd class="col-sm-8 text-danger">{{ $application->rejection_reason }}</dd>
@endif
</dl>
</div>
<p class="text-secondary small mt-3">
<i class="bi bi-shield-lock me-1"></i>
Untuk pertanyaan lanjut, hubungi pentadbir pilihanraya dengan menyertakan nombor rujukan anda:
<code>{{ $application->public_uuid }}</code>
</p>
@endif
@endsection

View File

@@ -0,0 +1,49 @@
@extends('layouts.public')
@section('title', 'Permohonan Diterima')
@section('content')
<div class="page-heading">
<div class="eyebrow">Pendaftaran Awam</div>
<h1 class="h3 mb-1">Permohonan Diterima</h1>
<p class="text-secondary mb-0">Rujukan: <code>{{ $application->public_uuid }}</code></p>
</div>
<div class="content-panel p-3 p-md-4">
<div class="d-flex gap-3 align-items-start">
<div class="stat-icon text-bg-success">
<i class="bi bi-check2-circle"></i>
</div>
<div>
<h2 class="h5 mb-2">Rekod permohonan telah disimpan</h2>
<dl class="row mb-0">
<dt class="col-sm-4">Pusat Mengundi</dt>
<dd class="col-sm-8">{{ $application->pusatMengundi?->name }}</dd>
<dt class="col-sm-4">Jawatan Dipohon</dt>
<dd class="col-sm-8">{{ $application->requestedPosition?->code }} - {{ $application->requestedPosition?->name }}</dd>
<dt class="col-sm-4">Status</dt>
<dd class="col-sm-8"><span class="badge text-bg-info">Menunggu Semakan</span></dd>
</dl>
</div>
</div>
</div>
<div class="content-panel p-3 p-md-4 mt-3">
<h2 class="h6 mb-2"><i class="bi bi-bookmark me-1"></i>Simpan pautan status anda</h2>
<p class="text-secondary small mb-2">
Gunakan pautan berikut untuk semak status permohonan anda pada bila-bila masa tanpa perlu log masuk.
</p>
<div class="input-group input-group-sm">
<input type="text" class="form-control font-monospace"
value="{{ route('public.applications.status', $application->public_uuid) }}"
id="statusUrl" readonly>
<button class="btn btn-outline-secondary" type="button"
onclick="navigator.clipboard.writeText(document.getElementById('statusUrl').value)">
<i class="bi bi-clipboard"></i> Salin
</button>
</div>
<p class="text-secondary small mt-2 mb-0">
Nombor rujukan: <code>{{ $application->public_uuid }}</code>
</p>
</div>
@endsection

View File

@@ -0,0 +1,23 @@
@extends('layouts.guest')
@section('title', 'Akses Sistem')
@section('content')
<div class="text-center mb-4">
<div class="eyebrow">Pilihanraya Negeri</div>
<h1 class="h4 mt-2 mb-2">Sistem Pengurusan Pusat Mengundi</h1>
<p class="text-secondary mb-0">Akses dalaman untuk Admin, Admin Kewangan, PPM, dan KTM.</p>
</div>
<div class="d-grid gap-2">
@auth
<a href="{{ route('dashboard') }}" class="btn btn-primary">
<i class="bi bi-speedometer2 me-2"></i>Dashboard
</a>
@else
<a href="{{ route('login') }}" class="btn btn-primary">
<i class="bi bi-box-arrow-in-right me-2"></i>Log masuk
</a>
@endauth
</div>
@endsection