- Laravel 13.9 + PHP 8.5 + MySQL - Bootstrap 5.3 + jQuery 3.7 + Chart.js (replacing Alpine/Tailwind) - Packages: intervention/image, dompdf, simple-qrcode, league/csv, laravel/breeze, laravel/boost - 17 database migrations: users, programs, qr_codes, participants, attendances, certificates, questionnaires, email_logs, audit_logs - 13 Eloquent models with full relationships - Admin layout (Bootstrap 5 sidebar) + public layout (mobile-first) - Rate limiters: checkin (60/min), certificate (30/min) - Admin seeder: admin@mbip.gov.my - Storage directories + symlink configured Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
161 lines
7.1 KiB
PHP
161 lines
7.1 KiB
PHP
@extends('layouts.admin')
|
|
|
|
@section('title', 'Dashboard')
|
|
@section('header', 'Dashboard')
|
|
|
|
@section('content')
|
|
|
|
{{-- Stats Row 1 --}}
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="card stat-card h-100">
|
|
<div class="card-body d-flex align-items-center gap-3">
|
|
<div class="stat-icon bg-primary bg-opacity-10">
|
|
<i class="bi bi-calendar-event-fill text-primary"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small">Jumlah Program</div>
|
|
<div class="fs-3 fw-bold">{{ $stats['total_programs'] }}</div>
|
|
<div class="text-success small"><i class="bi bi-circle-fill me-1" style="font-size:.5rem;"></i>{{ $stats['active_programs'] }} aktif</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="card stat-card h-100">
|
|
<div class="card-body d-flex align-items-center gap-3">
|
|
<div class="stat-icon bg-success bg-opacity-10">
|
|
<i class="bi bi-people-fill text-success"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small">Jumlah Peserta</div>
|
|
<div class="fs-3 fw-bold">{{ $stats['total_participants'] }}</div>
|
|
<div class="text-muted small">{{ $stats['total_attendances'] }} kehadiran direkod</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="card stat-card h-100">
|
|
<div class="card-body d-flex align-items-center gap-3">
|
|
<div class="stat-icon bg-warning bg-opacity-10">
|
|
<i class="bi bi-award-fill text-warning"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small">Sijil Dijana</div>
|
|
<div class="fs-3 fw-bold">{{ $stats['generated_certs'] }}</div>
|
|
<div class="text-muted small">{{ $stats['downloaded_certs'] }} dimuat turun</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="card stat-card h-100">
|
|
<div class="card-body d-flex align-items-center gap-3">
|
|
<div class="stat-icon bg-info bg-opacity-10">
|
|
<i class="bi bi-clipboard2-check-fill text-info"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small">Soalselidik Dijawab</div>
|
|
<div class="fs-3 fw-bold">{{ $stats['total_responses'] }}</div>
|
|
@if($stats['pending_emails'] > 0)
|
|
<div class="text-warning small"><i class="bi bi-envelope-fill me-1"></i>{{ $stats['pending_emails'] }} emel tertunda</div>
|
|
@else
|
|
<div class="text-muted small">Tiada emel tertunda</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Recent Programs --}}
|
|
<div class="row g-3">
|
|
<div class="col-lg-8">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white border-bottom d-flex justify-content-between align-items-center py-3">
|
|
<span class="fw-semibold">Program Terkini</span>
|
|
<a href="{{ route('admin.programs.create') }}" class="btn btn-sm btn-primary">
|
|
<i class="bi bi-plus-lg me-1"></i> Program Baru
|
|
</a>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
@if($recentPrograms->isEmpty())
|
|
<div class="text-center py-5 text-muted">
|
|
<i class="bi bi-calendar-x fs-1 d-block mb-2 opacity-25"></i>
|
|
Belum ada program. <a href="{{ route('admin.programs.create') }}">Tambah sekarang</a>
|
|
</div>
|
|
@else
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Nama Program</th>
|
|
<th>Tarikh</th>
|
|
<th>Status</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($recentPrograms as $program)
|
|
<tr>
|
|
<td>
|
|
<div class="fw-medium">{{ $program->title }}</div>
|
|
<small class="text-muted">{{ $program->organizer }}</small>
|
|
</td>
|
|
<td>
|
|
<small>{{ $program->start_date->format('d M Y') }}</small>
|
|
</td>
|
|
<td>
|
|
@include('admin.partials.program-status-badge', ['status' => $program->status])
|
|
</td>
|
|
<td class="text-end">
|
|
<a href="{{ route('admin.programs.show', $program) }}" class="btn btn-sm btn-outline-primary">
|
|
<i class="bi bi-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@if($recentPrograms->isNotEmpty())
|
|
<div class="card-footer bg-white text-center py-2">
|
|
<a href="{{ route('admin.programs.index') }}" class="text-decoration-none small">
|
|
Lihat semua program <i class="bi bi-arrow-right ms-1"></i>
|
|
</a>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-header bg-white border-bottom py-3">
|
|
<span class="fw-semibold">Tindakan Pantas</span>
|
|
</div>
|
|
<div class="card-body d-flex flex-column gap-2">
|
|
<a href="{{ route('admin.programs.create') }}" class="btn btn-outline-primary text-start">
|
|
<i class="bi bi-plus-circle me-2"></i> Tambah Program Baru
|
|
</a>
|
|
<a href="{{ route('admin.questionnaires.create') }}" class="btn btn-outline-secondary text-start">
|
|
<i class="bi bi-clipboard2-plus me-2"></i> Cipta Set Soalselidik
|
|
</a>
|
|
<a href="{{ route('admin.programs.index') }}" class="btn btn-outline-secondary text-start">
|
|
<i class="bi bi-list-ul me-2"></i> Semua Program
|
|
</a>
|
|
<a href="{{ route('admin.questionnaires.index') }}" class="btn btn-outline-secondary text-start">
|
|
<i class="bi bi-clipboard2-check me-2"></i> Semua Soalselidik
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@endsection
|