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,163 @@
<?php
namespace App\Http\Controllers;
use App\Models\Application;
use App\Models\Attendance;
use App\Models\BankVerification;
use App\Models\PusatMengundi;
use App\Models\SaluranMengundi;
use App\Models\StaffAssignment;
use App\Services\Admin\Management\PennempatanService;
use App\Services\Ktm\KtmScopeService;
use App\Services\Ppm\PpmScopeService;
use App\Services\Public\KtmVacancyService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class DashboardController extends Controller
{
public function index(Request $request): RedirectResponse|View
{
$user = $request->user();
if ($user->hasRole('Admin')) {
return redirect()->route('admin.dashboard');
}
if ($user->hasRole('Admin Kewangan')) {
return redirect()->route('kewangan.dashboard');
}
if ($user->hasRole('PPM')) {
return redirect()->route('ppm.dashboard');
}
if ($user->hasRole('KTM')) {
return redirect()->route('ktm.dashboard');
}
if ($user->hasRole('Pemohon')) {
return redirect()->route('pemohon.dashboard');
}
return view('dashboards.index', $this->payload(
'Dashboard',
'Akaun ini belum mempunyai peranan sistem.',
'Hubungi Admin untuk penetapan peranan.',
[
['label' => 'Peranan aktif', 'value' => '0', 'icon' => 'bi-shield-lock', 'tone' => 'secondary'],
['label' => 'Akses modul', 'value' => 'Belum aktif', 'icon' => 'bi-door-closed', 'tone' => 'secondary'],
],
));
}
public function admin(PennempatanService $pennempatanService): View
{
$pusatsWithVacancy = $pennempatanService->pusatsWithVacancySummary();
return view('dashboards.admin', [
'cards' => [
['label' => 'Pusat Mengundi', 'value' => (string) PusatMengundi::query()->count(), 'icon' => 'bi-building', 'tone' => 'primary'],
['label' => 'Saluran', 'value' => (string) SaluranMengundi::query()->count(), 'icon' => 'bi-diagram-3', 'tone' => 'info'],
['label' => 'Pemohon', 'value' => (string) Application::query()->count(), 'icon' => 'bi-people', 'tone' => 'success'],
['label' => 'Staf Aktif', 'value' => (string) StaffAssignment::query()->where('status', 'active')->count(), 'icon' => 'bi-person-check', 'tone' => 'success'],
],
'pusatsWithVacancy' => $pusatsWithVacancy,
]);
}
public function kewangan(): View
{
$missingBankStatement = BankVerification::query()
->whereHas('application', fn ($query) => $query->whereIn('status', ['approved', 'assigned']))
->whereHas('application', fn ($query) => $query->whereDoesntHave('documents', fn ($documentQuery) => $documentQuery->where('document_type', 'bank_statement')))
->count();
return view('dashboards.index', $this->payload(
'Dashboard Admin Kewangan',
'Semakan bank dan dokumen kewangan',
'Paparan asas untuk status verifikasi bank tanpa akses perubahan penempatan.',
[
['label' => 'Pending bank', 'value' => (string) BankVerification::query()->where('status', 'pending')->whereHas('application', fn ($query) => $query->whereIn('status', ['approved', 'assigned']))->count(), 'icon' => 'bi-bank', 'tone' => 'warning'],
['label' => 'Dokumen hilang', 'value' => (string) $missingBankStatement, 'icon' => 'bi-file-earmark-x', 'tone' => 'danger'],
['label' => 'Ditolak', 'value' => (string) BankVerification::query()->where('status', 'rejected')->count(), 'icon' => 'bi-x-circle', 'tone' => 'danger'],
['label' => 'Perlu pembetulan', 'value' => (string) BankVerification::query()->where('status', 'requires_correction')->count(), 'icon' => 'bi-pencil-square', 'tone' => 'info'],
],
[
['label' => 'Senarai staf diluluskan', 'route' => 'kewangan.bank.index'],
['label' => 'Semakan akaun bank', 'route' => 'kewangan.bank.index'],
['label' => 'Export finance list', 'route' => 'kewangan.bank.export'],
],
));
}
public function ppm(Request $request, PpmScopeService $scopeService): View
{
$pusatIds = $scopeService->assignedPusatIds($request->user());
return view('dashboards.index', $this->payload(
'Dashboard PPM',
'Pengurusan Pusat Mengundi sendiri',
'Paparan asas untuk semakan permohonan, tugasan saluran, wakil agensi, dan kehadiran.',
[
['label' => 'Pusat jagaan', 'value' => (string) $pusatIds->count(), 'icon' => 'bi-geo-alt', 'tone' => 'primary'],
['label' => 'Permohonan pending', 'value' => (string) Application::query()->whereIn('pusat_mengundi_id', $pusatIds)->whereIn('status', ['submitted', 'under_ppm_review'])->count(), 'icon' => 'bi-inbox', 'tone' => 'warning'],
['label' => 'Staf aktif', 'value' => (string) StaffAssignment::query()->whereIn('pusat_mengundi_id', $pusatIds)->where('status', 'active')->count(), 'icon' => 'bi-person-check', 'tone' => 'success'],
['label' => 'Kehadiran hadir', 'value' => (string) Attendance::query()->whereIn('pusat_mengundi_id', $pusatIds)->where('status', 'present')->count(), 'icon' => 'bi-check2-circle', 'tone' => 'info'],
],
[
['label' => 'Semak permohonan', 'route' => 'ppm.applications.index'],
['label' => 'Rekod kehadiran', 'route' => 'ppm.attendance.index'],
'Assign KTM/KP/KPDP',
],
));
}
public function ktm(Request $request, KtmScopeService $scopeService, KtmVacancyService $vacancyService): View
{
$assignments = $scopeService->assignmentsFor($request->user());
$assignmentIds = $assignments->pluck('id');
$kpVacancy = $assignments->sum(fn (StaffAssignment $assignment): int => $vacancyService->remainingForAssignment($assignment, 'KP'));
return view('dashboards.index', $this->payload(
'Dashboard KTM',
'Pengurusan pasukan KP sendiri',
'Paparan asas untuk saluran ditugaskan, kekosongan KP, dan pendaftaran KP.',
[
['label' => 'Saluran ditugaskan', 'value' => (string) $assignments->count(), 'icon' => 'bi-signpost', 'tone' => 'primary'],
['label' => 'KP aktif', 'value' => (string) StaffAssignment::query()->whereIn('reports_to_assignment_id', $assignmentIds)->where('status', 'active')->count(), 'icon' => 'bi-people', 'tone' => 'success'],
['label' => 'Kekosongan KP', 'value' => (string) $kpVacancy, 'icon' => 'bi-person-plus', 'tone' => 'warning'],
],
[
['label' => 'Senarai KP', 'route' => 'ktm.applications.index'],
['label' => 'Daftar KP', 'route' => 'ktm.applications.create'],
'Semak permohonan KP',
],
));
}
public function pemohon(): View
{
return view('dashboards.index', $this->payload(
'Dashboard Pemohon',
'Status permohonan',
'Akaun Pemohon akan digunakan jika portal status kendiri diaktifkan dalam fasa akan datang.',
[
['label' => 'Permohonan', 'value' => '0', 'icon' => 'bi-file-text', 'tone' => 'secondary'],
['label' => 'Status', 'value' => 'Belum aktif', 'icon' => 'bi-info-circle', 'tone' => 'info'],
],
));
}
/**
* @param array<int, array{label: string, value: string, icon: string, tone: string}> $cards
* @param array<int, string|array{label: string, route: string}> $actions
* @return array<string, mixed>
*/
private function payload(string $title, string $eyebrow, string $description, array $cards, array $actions = []): array
{
return compact('title', 'eyebrow', 'description', 'cards', 'actions');
}
}