diff --git a/app/Http/Controllers/Admin/StatisticsController.php b/app/Http/Controllers/Admin/StatisticsController.php index cb666be..8df7fa1 100644 --- a/app/Http/Controllers/Admin/StatisticsController.php +++ b/app/Http/Controllers/Admin/StatisticsController.php @@ -3,9 +3,129 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Models\Program; +use App\Models\Attendance; +use App\Models\Certificate; +use App\Models\QuestionnaireAnswer; +use App\Models\QuestionnaireQuestion; +use App\Models\QuestionnaireResponse; use Illuminate\Http\Request; +use Illuminate\Http\Response; +use Illuminate\View\View; class StatisticsController extends Controller { - // + public function show(Program $program): View + { + $program->load(['attendances.participant', 'questionnaire.questionnaireSet.questions']); + + // Attendance by session + $bySession = $program->attendances() + ->selectRaw('attendance_session, COUNT(*) as total') + ->groupBy('attendance_session') + ->pluck('total', 'attendance_session') + ->toArray(); + + // Attendance by source + $bySource = $program->attendances() + ->selectRaw('source, COUNT(*) as total') + ->groupBy('source') + ->pluck('total', 'source') + ->toArray(); + + // Certificate status breakdown + $certStats = $program->certificates() + ->selectRaw('status, COUNT(*) as total') + ->groupBy('status') + ->pluck('total', 'status') + ->toArray(); + + // Response rate + $pq = $program->questionnaire; + $responseRate = null; + $questionStats = []; + + if ($pq && $pq->is_confirmed) { + $totalAttended = $program->attendances()->count(); + $totalResponses = QuestionnaireResponse::where('program_id', $program->id)->count(); + $responseRate = $totalAttended > 0 ? round($totalResponses / $totalAttended * 100) : 0; + + // Rating question averages + $questions = $pq->questionnaireSet->questions ?? collect(); + foreach ($questions as $q) { + if ($q->question_type === 'rating') { + $answers = QuestionnaireAnswer::where('questionnaire_question_id', $q->id) + ->pluck('answer_value'); + $values = $answers->map(fn($v) => is_array($v) ? (int)($v[0] ?? 0) : (int)$v); + $questionStats[] = [ + 'id' => $q->id, + 'text' => $q->question_text, + 'type' => 'rating', + 'average' => $values->count() > 0 ? round($values->avg(), 2) : null, + 'count' => $values->count(), + ]; + } elseif (in_array($q->question_type, ['single_choice', 'multiple_choice'])) { + $answers = QuestionnaireAnswer::where('questionnaire_question_id', $q->id) + ->pluck('answer_value'); + $counts = []; + foreach ($answers as $val) { + $items = is_array($val) ? $val : [$val]; + foreach ($items as $item) { + $counts[$item] = ($counts[$item] ?? 0) + 1; + } + } + $questionStats[] = [ + 'id' => $q->id, + 'text' => $q->question_text, + 'type' => $q->question_type, + 'options' => $q->options_json ?? [], + 'counts' => $counts, + 'total' => $answers->count(), + ]; + } + } + } + + $summary = [ + 'total_attendances' => $program->attendances()->count(), + 'pre_registered' => $program->attendances()->where('source', 'pre_registered_staff')->count(), + 'walk_in' => $program->attendances()->where('source', 'walk_in_external')->count(), + 'total_certificates' => $program->certificates()->count(), + 'generated_certs' => $program->certificates()->whereIn('status', ['generated', 'emailed', 'downloaded'])->count(), + 'downloaded_certs' => $program->certificates()->where('status', 'downloaded')->count(), + 'total_responses' => QuestionnaireResponse::where('program_id', $program->id)->count(), + ]; + + return view('admin.programs.statistics.show', compact( + 'program', 'summary', 'bySession', 'bySource', + 'certStats', 'responseRate', 'questionStats' + )); + } + + public function export(Program $program): Response + { + $rows = $program->attendances() + ->with('participant') + ->get() + ->map(fn($a) => [ + $a->participant->name, + $a->participant->agency ?: '', + $a->attendance_session, + $a->source, + $a->checked_in_at->format('d/m/Y H:i'), + ]); + + $csv = "\xEF\xBB\xBF"; + $csv .= implode(',', ['Nama', 'Agensi', 'Sesi', 'Sumber', 'Masa Check-In']) . "\n"; + foreach ($rows as $row) { + $csv .= implode(',', array_map(fn($v) => '"' . str_replace('"', '""', $v) . '"', $row)) . "\n"; + } + + $filename = 'statistik-' . str($program->title)->slug() . '-' . now()->format('Ymd') . '.csv'; + + return response($csv, 200, [ + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Content-Disposition' => 'attachment; filename="' . $filename . '"', + ]); + } } diff --git a/resources/views/admin/programs/statistics/show.blade.php b/resources/views/admin/programs/statistics/show.blade.php new file mode 100644 index 0000000..01d8e47 --- /dev/null +++ b/resources/views/admin/programs/statistics/show.blade.php @@ -0,0 +1,218 @@ +@extends('layouts.admin') + +@section('title', 'Statistik — ' . $program->title) +@section('header', 'Statistik Program') + +@section('breadcrumb') +
| {{ $labels[$status] ?? $status }} | +{{ $count }} | +
+
+
+
+ |
+
| {{ $opt }} | +{{ $c }} | +
+
+
+
+ |
+ {{ $pct }}% | +