chore: initial Laravel 13 project setup for eCert MBIP
- 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>
This commit is contained in:
160
resources/views/admin/dashboard.blade.php
Normal file
160
resources/views/admin/dashboard.blade.php
Normal file
@@ -0,0 +1,160 @@
|
||||
@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
|
||||
@@ -0,0 +1,9 @@
|
||||
@php
|
||||
$map = [
|
||||
'draft' => ['secondary', 'Draf'],
|
||||
'published' => ['success', 'Diterbitkan'],
|
||||
'closed' => ['dark', 'Ditutup'],
|
||||
];
|
||||
[$color, $label] = $map[$status] ?? ['secondary', $status];
|
||||
@endphp
|
||||
<span class="badge bg-{{ $color }}">{{ $label }}</span>
|
||||
Reference in New Issue
Block a user