diff --git a/src/app/Http/Controllers/Admin/DashboardController.php b/src/app/Http/Controllers/Admin/DashboardController.php index b80727b..f1c83c1 100644 --- a/src/app/Http/Controllers/Admin/DashboardController.php +++ b/src/app/Http/Controllers/Admin/DashboardController.php @@ -22,8 +22,11 @@ class DashboardController extends Controller 'total_certificates' => Certificate::count(), 'generated_certs' => Certificate::whereIn('status', ['generated', 'emailed', 'downloaded'])->count(), 'downloaded_certs' => Certificate::where('status', 'downloaded')->count(), + 'total_download_count'=> (int) Certificate::sum('download_count'), 'total_responses' => QuestionnaireResponse::count(), - 'pending_emails' => EmailLog::where('status', 'pending')->count(), + 'emails_pending' => EmailLog::where('status', 'pending')->count(), + 'emails_sent' => EmailLog::where('status', 'sent')->count(), + 'emails_failed' => EmailLog::where('status', 'failed')->count(), ]; $recentPrograms = Program::with('creator') diff --git a/src/app/Http/Controllers/Admin/ParticipantController.php b/src/app/Http/Controllers/Admin/ParticipantController.php index c2f3ba5..17602d4 100644 --- a/src/app/Http/Controllers/Admin/ParticipantController.php +++ b/src/app/Http/Controllers/Admin/ParticipantController.php @@ -3,6 +3,8 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Models\Certificate; +use App\Models\EmailLog; use App\Models\Participant; use App\Models\Program; use App\Models\ProgramParticipant; @@ -40,6 +42,20 @@ class ParticipantController extends Controller $programParticipants = $query->paginate(20)->withQueryString(); + // Load certificates and latest email logs for displayed participants + $participantIds = $programParticipants->pluck('participant_id'); + $certificates = Certificate::where('program_id', $program->id) + ->whereIn('participant_id', $participantIds) + ->get() + ->keyBy('participant_id'); + + $certIds = $certificates->pluck('id'); + $emailLogs = EmailLog::whereIn('certificate_id', $certIds) + ->orderByDesc('id') + ->get() + ->groupBy('certificate_id') + ->map->first(); + $countRow = DB::table('program_participants') ->where('program_id', $program->id) ->selectRaw("COUNT(*) as total, SUM(is_pre_registered) as pre_registered, SUM(registration_source = 'walk_in') as walk_in, SUM(status = 'checked_in') as checked_in") @@ -52,7 +68,7 @@ class ParticipantController extends Controller 'checked_in' => (int) ($countRow->checked_in ?? 0), ]; - return view('admin.programs.participants.index', compact('program', 'programParticipants', 'counts')); + return view('admin.programs.participants.index', compact('program', 'programParticipants', 'counts', 'certificates', 'emailLogs')); } public function create(Program $program): View diff --git a/src/app/Http/Controllers/Public/AttendanceCheckController.php b/src/app/Http/Controllers/Public/AttendanceCheckController.php index b362976..e936a1d 100644 --- a/src/app/Http/Controllers/Public/AttendanceCheckController.php +++ b/src/app/Http/Controllers/Public/AttendanceCheckController.php @@ -82,7 +82,7 @@ class AttendanceCheckController extends Controller // Masukkan queue hantar e-sijil jika sijil sudah dijana dan belum dihantar if ($certificate && $certificate->status === 'generated' && ! $certificate->emailed_at) { - SendCertificateEmailJob::dispatch($certificate); + SendCertificateEmailJob::dispatchForCert($certificate); } return view('public.semak.result', compact('program', 'qrCode', 'participant', 'attendance', 'certificate')) diff --git a/src/app/Jobs/SendCertificateEmailJob.php b/src/app/Jobs/SendCertificateEmailJob.php index b77e99a..fde625a 100644 --- a/src/app/Jobs/SendCertificateEmailJob.php +++ b/src/app/Jobs/SendCertificateEmailJob.php @@ -30,9 +30,15 @@ class SendCertificateEmailJob implements ShouldQueue $email = $cert->participant->email; if (! $email) { + EmailLog::where('certificate_id', $cert->id)->where('status', 'pending')->delete(); return; } + $log = EmailLog::where('certificate_id', $cert->id) + ->where('status', 'pending') + ->latest() + ->first(); + try { Mail::to($email)->send(new CertificateReadyMail($cert)); @@ -41,41 +47,70 @@ class SendCertificateEmailJob implements ShouldQueue 'emailed_at' => now(), ]); - EmailLog::create([ - 'program_id' => $cert->program_id, - 'participant_id' => $cert->participant_id, - 'certificate_id' => $cert->id, - 'recipient_email'=> $email, - 'subject' => 'Sijil Digital Program — ' . $cert->program->title, - 'email_type' => 'certificate_ready', - 'status' => 'sent', - 'sent_at' => now(), - ]); + if ($log) { + $log->update(['status' => 'sent', 'sent_at' => now()]); + } else { + EmailLog::create([ + 'program_id' => $cert->program_id, + 'participant_id' => $cert->participant_id, + 'certificate_id' => $cert->id, + 'recipient_email' => $email, + 'subject' => 'Sijil Digital Program — ' . $cert->program->title, + 'email_type' => 'certificate_ready', + 'status' => 'sent', + 'sent_at' => now(), + ]); + } } catch (\Throwable $e) { - EmailLog::create([ - 'program_id' => $cert->program_id, - 'participant_id' => $cert->participant_id, - 'certificate_id' => $cert->id, - 'recipient_email'=> $email, - 'subject' => 'Sijil Digital Program — ' . $cert->program->title, - 'email_type' => 'certificate_ready', - 'status' => 'failed', - 'error_message' => $e->getMessage(), - ]); + if ($log) { + $log->update(['status' => 'failed', 'error_message' => $e->getMessage()]); + } else { + EmailLog::create([ + 'program_id' => $cert->program_id, + 'participant_id' => $cert->participant_id, + 'certificate_id' => $cert->id, + 'recipient_email' => $email, + 'subject' => 'Sijil Digital Program — ' . $cert->program->title, + 'email_type' => 'certificate_ready', + 'status' => 'failed', + 'error_message' => $e->getMessage(), + ]); + } throw $e; } } + /** + * Cipta pending EmailLog dahulu, kemudian dispatch job. + * Ini membolehkan dashboard tunjuk status "dalam antrian" sebelum job diproses. + */ + public static function dispatchForCert(Certificate $cert): void + { + $cert->loadMissing(['participant', 'program']); + + EmailLog::create([ + 'program_id' => $cert->program_id, + 'participant_id' => $cert->participant_id, + 'certificate_id' => $cert->id, + 'recipient_email' => $cert->participant->email, + 'subject' => 'Sijil Digital Program — ' . $cert->program->title, + 'email_type' => 'certificate_ready', + 'status' => 'pending', + ]); + + static::dispatch($cert); + } + public static function dispatchBatch(Program $program): void { $program->certificates() ->whereIn('status', ['generated']) ->whereNull('emailed_at') - ->with('participant') + ->with(['participant', 'program']) ->each(function (Certificate $cert) { if ($cert->participant->email) { - static::dispatch($cert); + static::dispatchForCert($cert); } }); } diff --git a/src/resources/views/admin/dashboard.blade.php b/src/resources/views/admin/dashboard.blade.php index edd54e1..6118134 100644 --- a/src/resources/views/admin/dashboard.blade.php +++ b/src/resources/views/admin/dashboard.blade.php @@ -61,8 +61,8 @@
Soalselidik Dijawab
{{ $stats['total_responses'] }}
- @if($stats['pending_emails'] > 0) -
{{ $stats['pending_emails'] }} emel tertunda
+ @if($stats['emails_pending'] > 0) +
{{ $stats['emails_pending'] }} emel tertunda
@else
Tiada emel tertunda
@endif @@ -72,6 +72,69 @@
+{{-- Email & Download Status --}} +
+
+
+
+ Status Penghantaran E-Sijil +
+
+
+
+
+
+ +
+
+
Dalam Antrian
+
{{ $stats['emails_pending'] }}
+
menunggu / sedang cuba
+
+
+
+
+
+
+ +
+
+
Berjaya Dihantar
+
{{ $stats['emails_sent'] }}
+
emel e-sijil
+
+
+
+
+
+
+ +
+
+
Gagal Dihantar
+
{{ $stats['emails_failed'] }}
+
semua percubaan gagal
+
+
+
+
+
+
+ +
+
+
Sijil Dimuat Turun
+
{{ $stats['downloaded_certs'] }}
+
{{ $stats['total_download_count'] }} kali klik pautan
+
+
+
+
+
+
+
+
+ {{-- Recent Programs --}}
diff --git a/src/resources/views/admin/programs/participants/index.blade.php b/src/resources/views/admin/programs/participants/index.blade.php index eaac6bd..0e4115e 100644 --- a/src/resources/views/admin/programs/participants/index.blade.php +++ b/src/resources/views/admin/programs/participants/index.blade.php @@ -100,12 +100,17 @@ Sesi Sumber Status + E-Sijil Tindakan @foreach($programParticipants as $i => $pp) - @php $p = $pp->participant; @endphp + @php + $p = $pp->participant; + $cert = $certificates[$pp->participant_id] ?? null; + $emailLog = $cert ? ($emailLogs[$cert->id] ?? null) : null; + @endphp {{ $programParticipants->firstItem() + $i }} @@ -143,6 +148,54 @@ Berdaftar @endif + + @if(! $cert) + + @else + {{-- Sijil --}} + @if(in_array($cert->status, ['generated','emailed','downloaded'])) + + Jana ✓ + + @elseif($cert->status === 'pending') + + Menjana... + + @elseif($cert->status === 'failed') + + Gagal Jana + + @endif + + {{-- Emel --}} + @if($emailLog) + @if($emailLog->status === 'sent') +
+ Emel Dihantar +
+ @elseif($emailLog->status === 'failed') +
+ Emel Gagal +
+ @elseif($emailLog->status === 'pending') +
+ Dalam Antrian +
+ @endif + @elseif($cert->isGenerated()) +
+ Belum Dihantar +
+ @endif + + {{-- Muat turun --}} + @if($cert->download_count > 0) +
+ {{ $cert->download_count }}× Muat Turun +
+ @endif + @endif + @if($pp->status !== 'checked_in')