620 lines
35 KiB
PHP
620 lines
35 KiB
PHP
<x-app-layout>
|
|
<x-slot name="header">
|
|
<div class="bg-[#1e3a8a] rounded-2xl shadow-lg p-6">
|
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between">
|
|
<div class="flex items-center space-x-4">
|
|
<div class="bg-white/10 p-3 rounded-xl">
|
|
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-white">Dashboard Analisis Sentimen S-Learn</h1>
|
|
<p class="text-white/70 text-sm mt-1">Memantau kepuasan dan masukan pengguna platform S-Learn</p>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 md:mt-0 flex items-center space-x-3">
|
|
<div class="bg-white/10 px-4 py-2 rounded-lg">
|
|
<span class="text-white text-sm font-medium">Terakhir update: {{ now()->format('d M Y H:i') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-slot>
|
|
|
|
@php
|
|
$positif = $positif ?? 0;
|
|
$negatif = $negatif ?? 0;
|
|
$totalData = $totalData ?? ($positif + $negatif);
|
|
|
|
$score_0_20 = $score_0_20 ?? 0;
|
|
$score_21_40 = $score_21_40 ?? 0;
|
|
$score_41_60 = $score_41_60 ?? 0;
|
|
$score_61_80 = $score_61_80 ?? 0;
|
|
$score_81_100 = $score_81_100 ?? 0;
|
|
|
|
$formattedMetrics = $formattedMetrics ?? [];
|
|
$metricsNegatif = $formattedMetrics['negatif'] ?? ['precision'=>0,'recall'=>0,'f1'=>0];
|
|
$metricsPositif = $formattedMetrics['positif'] ?? ['precision'=>0,'recall'=>0,'f1'=>0];
|
|
|
|
$maintenance = $maintenance ?? [];
|
|
$maintenanceDetails = $maintenanceDetails ?? [];
|
|
|
|
// Hitung persentase untuk memudahkan pemahaman
|
|
$persentasePositif = $totalData > 0 ? round(($positif/$totalData)*100, 1) : 0;
|
|
$persentaseNegatif = $totalData > 0 ? round(($negatif/$totalData)*100, 1) : 0;
|
|
@endphp
|
|
|
|
<div class="py-8 bg-gray-50 min-h-screen"
|
|
x-data="{
|
|
openModal: false,
|
|
selectedCategory: '',
|
|
selectedData: [],
|
|
activeTab: 'ringkasan'
|
|
}">
|
|
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
|
|
|
<!-- ========== TAB NAVIGASI ========== -->
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-1 flex flex-wrap">
|
|
<button @click="activeTab = 'ringkasan'"
|
|
class="flex-1 py-3 px-4 rounded-lg text-sm font-medium transition-all"
|
|
:class="activeTab === 'ringkasan' ? 'bg-white text-[#1e3a8a] shadow-md' : 'text-white/80 hover:bg-white/10 hover:text-white'">
|
|
📊 Ringkasan
|
|
</button>
|
|
<button @click="activeTab = 'sentimen'"
|
|
class="flex-1 py-3 px-4 rounded-lg text-sm font-medium transition-all"
|
|
:class="activeTab === 'sentimen' ? 'bg-white text-[#1e3a8a] shadow-md' : 'text-white/80 hover:bg-white/10 hover:text-white'">
|
|
😊 Analisis Sentimen
|
|
</button>
|
|
<button @click="activeTab = 'akurasi'"
|
|
class="flex-1 py-3 px-4 rounded-lg text-sm font-medium transition-all"
|
|
:class="activeTab === 'akurasi' ? 'bg-white text-[#1e3a8a] shadow-md' : 'text-white/80 hover:bg-white/10 hover:text-white'">
|
|
📈 Akurasi & Prediksi
|
|
</button>
|
|
<button @click="activeTab = 'masalah'"
|
|
class="flex-1 py-3 px-4 rounded-lg text-sm font-medium transition-all"
|
|
:class="activeTab === 'masalah' ? 'bg-white text-[#1e3a8a] shadow-md' : 'text-white/80 hover:bg-white/10 hover:text-white'">
|
|
⚠️ Masalah & Saran
|
|
</button>
|
|
</div>
|
|
|
|
<!-- ========== TAB RINGKASAN ========== -->
|
|
<div x-show="activeTab === 'ringkasan'" x-cloak>
|
|
<!-- Statistik Utama -->
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-5">
|
|
<p class="text-white/70 text-sm mb-1">Total Ulasan</p>
|
|
<p class="text-3xl font-bold text-white">{{ number_format($totalData) }}</p>
|
|
<p class="text-white/50 text-xs mt-1">Semua ulasan yang masuk</p>
|
|
</div>
|
|
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-5">
|
|
<p class="text-white/70 text-sm mb-1">Ulasan Positif</p>
|
|
<p class="text-3xl font-bold text-green-300">{{ number_format($positif) }}</p>
|
|
<p class="text-white/50 text-xs mt-1">{{ $persentasePositif }}% dari total</p>
|
|
</div>
|
|
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-5">
|
|
<p class="text-white/70 text-sm mb-1">Ulasan Negatif</p>
|
|
<p class="text-3xl font-bold text-red-300">{{ number_format($negatif) }}</p>
|
|
<p class="text-white/50 text-xs mt-1">{{ $persentaseNegatif }}% dari total</p>
|
|
</div>
|
|
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-5">
|
|
<p class="text-white/70 text-sm mb-1">Tingkat Kepuasan</p>
|
|
<p class="text-3xl font-bold text-yellow-300">{{ $persentasePositif }}%</p>
|
|
<p class="text-white/50 text-xs mt-1">Pengguna puas</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grafik Ringkasan -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<!-- Grafik Batang -->
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-6">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="font-semibold text-white">Perbandingan Ulasan</h3>
|
|
<span class="text-xs bg-white/20 text-white px-2 py-1 rounded-full">Grafik Batang</span>
|
|
</div>
|
|
<p class="text-white/70 text-sm mb-4">Membandingkan jumlah ulasan positif dan negatif</p>
|
|
<div style="height:250px">
|
|
<canvas id="barChart"></canvas>
|
|
</div>
|
|
<div class="flex justify-center mt-4 space-x-6">
|
|
<div class="flex items-center">
|
|
<span class="w-3 h-3 bg-green-400 rounded-full mr-2"></span>
|
|
<span class="text-sm text-white">Positif: {{ number_format($positif) }}</span>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<span class="w-3 h-3 bg-red-400 rounded-full mr-2"></span>
|
|
<span class="text-sm text-white">Negatif: {{ number_format($negatif) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grafik Lingkaran -->
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-6">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="font-semibold text-white">Proporsi Kepuasan</h3>
|
|
<span class="text-xs bg-white/20 text-white px-2 py-1 rounded-full">Grafik Lingkaran</span>
|
|
</div>
|
|
<p class="text-white/70 text-sm mb-4">Persentase perbandingan ulasan</p>
|
|
<div style="height:250px">
|
|
<canvas id="pieChart"></canvas>
|
|
</div>
|
|
<div class="text-center mt-4">
|
|
<p class="text-sm">
|
|
<span class="inline-block px-3 py-1 bg-green-500/20 text-green-300 rounded-full font-medium">{{ $persentasePositif }}% Puas</span>
|
|
<span class="mx-2 text-white/50">|</span>
|
|
<span class="inline-block px-3 py-1 bg-red-500/20 text-red-300 rounded-full font-medium">{{ $persentaseNegatif }}% Tidak Puas</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========== TAB ANALISIS SENTIMEN ========== -->
|
|
<div x-show="activeTab === 'sentimen'" x-cloak>
|
|
<!-- Distribusi Tingkat Kepuasan -->
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-6 mb-6">
|
|
<h3 class="font-semibold text-white mb-2">Tingkat Kepuasan Pengguna</h3>
|
|
<p class="text-white/70 text-sm mb-4">Skor 0-100 (Semakin tinggi angkanya, semakin puas pengguna)</p>
|
|
|
|
<!-- Kategori Skor -->
|
|
<div class="grid grid-cols-5 gap-2 mb-6">
|
|
<div class="text-center p-3 bg-red-500/20 rounded-lg">
|
|
<span class="text-xs text-red-300 font-medium block">0-20</span>
|
|
<span class="text-lg font-bold text-white">{{ $score_0_20 }}</span>
|
|
<span class="text-xs text-white/60 block">Sangat Tidak Puas</span>
|
|
</div>
|
|
<div class="text-center p-3 bg-orange-500/20 rounded-lg">
|
|
<span class="text-xs text-orange-300 font-medium block">21-40</span>
|
|
<span class="text-lg font-bold text-white">{{ $score_21_40 }}</span>
|
|
<span class="text-xs text-white/60 block">Tidak Puas</span>
|
|
</div>
|
|
<div class="text-center p-3 bg-yellow-500/20 rounded-lg">
|
|
<span class="text-xs text-yellow-300 font-medium block">41-60</span>
|
|
<span class="text-lg font-bold text-white">{{ $score_41_60 }}</span>
|
|
<span class="text-xs text-white/60 block">Netral</span>
|
|
</div>
|
|
<div class="text-center p-3 bg-lime-500/20 rounded-lg">
|
|
<span class="text-xs text-lime-300 font-medium block">61-80</span>
|
|
<span class="text-lg font-bold text-white">{{ $score_61_80 }}</span>
|
|
<span class="text-xs text-white/60 block">Puas</span>
|
|
</div>
|
|
<div class="text-center p-3 bg-green-500/20 rounded-lg">
|
|
<span class="text-xs text-green-300 font-medium block">81-100</span>
|
|
<span class="text-lg font-bold text-white">{{ $score_81_100 }}</span>
|
|
<span class="text-xs text-white/60 block">Sangat Puas</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grafik Garis -->
|
|
<div style="height:250px">
|
|
<canvas id="scoreChart"></canvas>
|
|
</div>
|
|
|
|
<!-- Info Box -->
|
|
<div class="mt-4 bg-white/10 p-4 rounded-lg">
|
|
<p class="text-sm text-white">
|
|
<span class="font-semibold">📊 Analisis:</span>
|
|
Grafik menunjukkan sebaran tingkat kepuasan.
|
|
@if($score_81_100 > $score_0_20)
|
|
Mayoritas pengguna merasa puas dengan layanan S-Learn (skor 81-100).
|
|
@else
|
|
Perlu perhatian pada area dengan skor rendah (0-40) untuk meningkatkan kepuasan.
|
|
@endif
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========== TAB AKURASI & PREDIKSI ========== -->
|
|
<div x-show="activeTab === 'akurasi'" x-cloak>
|
|
<!-- Metrik Akurasi -->
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-6 mb-6">
|
|
<h3 class="font-semibold text-white mb-2">Tingkat Akurasi Sistem</h3>
|
|
<p class="text-white/70 text-sm mb-4">Seberapa akurat sistem dalam memprediksi sentimen</p>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
<div class="bg-white/10 p-5 rounded-xl text-center">
|
|
<p class="text-sm text-blue-300 mb-1">Precision (Ketepatan)</p>
|
|
<p class="text-3xl font-bold text-white">{{ number_format($metricsPositif['precision'] * 100, 1) }}%</p>
|
|
<p class="text-xs text-white/50 mt-2">Seberapa tepat sistem memprediksi sentimen positif</p>
|
|
</div>
|
|
<div class="bg-white/10 p-5 rounded-xl text-center">
|
|
<p class="text-sm text-green-300 mb-1">Recall (Kelengkapan)</p>
|
|
<p class="text-3xl font-bold text-white">{{ number_format($metricsPositif['recall'] * 100, 1) }}%</p>
|
|
<p class="text-xs text-white/50 mt-2">Seberapa lengkap sistem menangkap sentimen positif</p>
|
|
</div>
|
|
<div class="bg-white/10 p-5 rounded-xl text-center">
|
|
<p class="text-sm text-purple-300 mb-1">F1-Score (Keseimbangan)</p>
|
|
<p class="text-3xl font-bold text-white">{{ number_format($metricsPositif['f1'] * 100, 1) }}%</p>
|
|
<p class="text-xs text-white/50 mt-2">Keseimbangan antara ketepatan dan kelengkapan</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grafik Metrik -->
|
|
<div style="height:250px" class="mb-6">
|
|
<canvas id="metricsChart"></canvas>
|
|
</div>
|
|
|
|
<!-- Confusion Matrix (DIPERTAHANKAN) -->
|
|
<div class="mt-8">
|
|
<h4 class="font-semibold text-white mb-3">📊 Matriks Prediksi (Confusion Matrix)</h4>
|
|
<p class="text-white/70 text-sm mb-4">Tabel ini menunjukkan detail hasil prediksi sistem</p>
|
|
|
|
@if(isset($cm) && count($cm) > 1)
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full max-w-2xl mx-auto border-collapse">
|
|
<!-- Header -->
|
|
<tr>
|
|
<td class="p-2"></td>
|
|
<td class="p-3 text-center font-semibold bg-white/20 text-white rounded-tl-lg" colspan="2">HASIL PREDIKSI SISTEM</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="p-2"></td>
|
|
<td class="p-3 text-center bg-red-500/20 font-medium text-red-300 rounded-bl-lg">Diprediksi NEGATIF</td>
|
|
<td class="p-3 text-center bg-green-500/20 font-medium text-green-300 rounded-br-lg">Diprediksi POSITIF</td>
|
|
</tr>
|
|
|
|
<!-- Data -->
|
|
@foreach($cm as $i=>$row)
|
|
@if($i>0 && isset($row[1]) && isset($row[2]))
|
|
<tr>
|
|
<td class="p-3 font-medium bg-white/10 text-white rounded-l-lg">
|
|
{{ $i == 1 ? 'AKTUAL NEGATIF' : 'AKTUAL POSITIF' }}
|
|
<span class="block text-xs text-white/50">(Kenyataan sebenarnya)</span>
|
|
</td>
|
|
<td class="p-4 text-center bg-red-500/10 border-2 {{ $i==1 ? 'border-red-500/30' : 'border-orange-500/30' }}">
|
|
<span class="block text-2xl font-bold {{ $i==1 ? 'text-red-300' : 'text-orange-300' }}">{{ number_format($row[1]) }}</span>
|
|
<span class="text-xs text-white/70">
|
|
@if($i==1)
|
|
✓ Benar Negatif
|
|
<span class="block text-xs text-white/50">(Sistem benar memprediksi NEGATIF)</span>
|
|
@else
|
|
✗ Salah Negatif
|
|
<span class="block text-xs text-white/50">(Sistem salah memprediksi NEGATIF, padahal POSITIF)</span>
|
|
@endif
|
|
</span>
|
|
</td>
|
|
<td class="p-4 text-center bg-green-500/10 border-2 {{ $i==2 ? 'border-green-500/30' : 'border-yellow-500/30' }}">
|
|
<span class="block text-2xl font-bold {{ $i==2 ? 'text-green-300' : 'text-yellow-300' }}">{{ number_format($row[2]) }}</span>
|
|
<span class="text-xs text-white/70">
|
|
@if($i==2)
|
|
✓ Benar Positif
|
|
<span class="block text-xs text-white/50">(Sistem benar memprediksi POSITIF)</span>
|
|
@else
|
|
✗ Salah Positif
|
|
<span class="block text-xs text-white/50">(Sistem salah memprediksi POSITIF, padahal NEGATIF)</span>
|
|
@endif
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
@endif
|
|
@endforeach
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Penjelasan Sederhana -->
|
|
<div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="bg-green-500/10 p-4 rounded-lg">
|
|
<p class="font-semibold text-green-300 mb-2">✓ Prediksi Benar</p>
|
|
<ul class="text-sm text-white/80 space-y-2">
|
|
<li>• <span class="font-medium">Benar Positif ({{ number_format($cm[2][2] ?? 0) }})</span>: Sistem benar memprediksi POSITIF</li>
|
|
<li>• <span class="font-medium">Benar Negatif ({{ number_format($cm[1][1] ?? 0) }})</span>: Sistem benar memprediksi NEGATIF</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-red-500/10 p-4 rounded-lg">
|
|
<p class="font-semibold text-red-300 mb-2">✗ Prediksi Salah</p>
|
|
<ul class="text-sm text-white/80 space-y-2">
|
|
<li>• <span class="font-medium">Salah Positif ({{ number_format($cm[1][2] ?? 0) }})</span>: Sistem salah prediksi POSITIF</li>
|
|
<li>• <span class="font-medium">Salah Negatif ({{ number_format($cm[2][1] ?? 0) }})</span>: Sistem salah prediksi NEGATIF</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ringkasan -->
|
|
<div class="mt-4 bg-white/10 p-4 rounded-lg">
|
|
<p class="text-sm text-white">
|
|
<span class="font-semibold">📈 Ringkasan:</span><br>
|
|
Dari total {{ number_format($totalData) }} ulasan, sistem berhasil memprediksi dengan benar
|
|
<span class="font-bold text-green-300">{{ number_format(($cm[1][1] ?? 0) + ($cm[2][2] ?? 0)) }} data</span>
|
|
({{ $totalData > 0 ? round((($cm[1][1] ?? 0) + ($cm[2][2] ?? 0)) / $totalData * 100, 1) : 0 }}%)
|
|
dan salah memprediksi
|
|
<span class="font-bold text-red-300">{{ number_format(($cm[1][2] ?? 0) + ($cm[2][1] ?? 0)) }} data</span>.
|
|
</p>
|
|
</div>
|
|
|
|
@else
|
|
<div class="text-center py-8 bg-white/10 rounded-lg">
|
|
<p class="text-white/70">Data confusion matrix belum tersedia</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========== TAB MASALAH & SARAN ========== -->
|
|
<div x-show="activeTab === 'masalah'" x-cloak>
|
|
<div class="bg-[#1e3a8a] rounded-xl shadow-lg p-6">
|
|
<h3 class="font-semibold text-white mb-2">Kategori Masalah & Saran Perbaikan</h3>
|
|
<p class="text-white/70 text-sm mb-6">Keluhan pengguna yang perlu mendapatkan perhatian</p>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
@forelse($maintenance as $kategori => $jumlah)
|
|
<div class="bg-white/10 rounded-lg p-5 hover:bg-white/20 transition-colors">
|
|
<div class="flex items-start justify-between mb-3">
|
|
<h4 class="font-semibold text-white">{{ $kategori }}</h4>
|
|
<span class="bg-red-500/30 text-red-300 px-2 py-1 rounded text-xs font-bold">{{ $jumlah }}</span>
|
|
</div>
|
|
|
|
<p class="text-sm text-white/70 mb-4">
|
|
{{ $jumlah }} pengguna melaporkan masalah ini
|
|
</p>
|
|
|
|
@if(!empty($maintenanceDetails[$kategori]))
|
|
<button
|
|
@click="
|
|
openModal = true;
|
|
selectedCategory = '{{ $kategori }}';
|
|
selectedData = {{ json_encode($maintenanceDetails[$kategori]) }};
|
|
"
|
|
class="w-full px-4 py-2 bg-white/20 text-white rounded-lg text-sm hover:bg-white/30 transition-colors flex items-center justify-center">
|
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
|
</svg>
|
|
Lihat Detail Keluhan
|
|
</button>
|
|
@endif
|
|
</div>
|
|
@empty
|
|
<div class="col-span-full text-center py-12">
|
|
<div class="bg-white/10 rounded-lg p-8">
|
|
<svg class="w-16 h-16 mx-auto text-green-300 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
<p class="text-white font-medium">Tidak ada masalah yang dilaporkan</p>
|
|
<p class="text-sm text-white/50 mt-1">Semua sistem berjalan dengan baik</p>
|
|
</div>
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========== MODAL DETAIL KELUHAN ========== -->
|
|
<div x-show="openModal"
|
|
x-cloak
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 z-50 overflow-y-auto"
|
|
style="display: none;">
|
|
|
|
<div class="fixed inset-0 bg-black/70" @click="openModal = false"></div>
|
|
|
|
<div class="relative min-h-screen flex items-center justify-center p-4">
|
|
<div class="relative bg-[#1e3a8a] rounded-xl shadow-xl w-full max-w-2xl max-h-[80vh] overflow-hidden">
|
|
|
|
<!-- Header Modal -->
|
|
<div class="bg-white/10 p-4">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="bg-white/20 p-2 rounded-lg">
|
|
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-lg font-semibold text-white">Detail Keluhan</h3>
|
|
<p class="text-white/70 text-sm" x-text="selectedCategory"></p>
|
|
</div>
|
|
</div>
|
|
<button @click="openModal = false" class="text-white/80 hover:text-white">
|
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Konten Modal -->
|
|
<div class="p-6 overflow-y-auto max-h-[calc(80vh-120px)]">
|
|
<div class="mb-4 flex items-center justify-between">
|
|
<span class="text-sm text-white/70">Total Keluhan:</span>
|
|
<span class="bg-white/20 text-white px-3 py-1 rounded-full text-sm font-medium"
|
|
x-text="selectedData.length + ' ulasan'"></span>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
<template x-for="(item, index) in selectedData" :key="index">
|
|
<div class="bg-white/10 p-4 rounded-lg">
|
|
<div class="flex items-start space-x-3">
|
|
<span class="flex-shrink-0 w-6 h-6 bg-white/20 text-white rounded-full flex items-center justify-center text-xs font-bold"
|
|
x-text="index + 1"></span>
|
|
<p class="text-white/90 text-sm" x-text="item"></p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer Modal -->
|
|
<div class="bg-white/10 px-6 py-3 flex justify-end">
|
|
<button @click="openModal = false"
|
|
class="px-4 py-2 bg-white/20 text-white rounded-lg hover:bg-white/30 transition-colors text-sm">
|
|
Tutup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scripts -->
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
document.addEventListener('alpine:init', () => {
|
|
setTimeout(() => {
|
|
initializeCharts();
|
|
}, 100);
|
|
});
|
|
|
|
function initializeCharts() {
|
|
// Data
|
|
const positif = {{ $positif }};
|
|
const negatif = {{ $negatif }};
|
|
const scoreData = [
|
|
{{ $score_0_20 }},
|
|
{{ $score_21_40 }},
|
|
{{ $score_41_60 }},
|
|
{{ $score_61_80 }},
|
|
{{ $score_81_100 }}
|
|
];
|
|
const metricsNegatif = [
|
|
{{ $metricsNegatif['precision'] }},
|
|
{{ $metricsNegatif['recall'] }},
|
|
{{ $metricsNegatif['f1'] }}
|
|
];
|
|
const metricsPositif = [
|
|
{{ $metricsPositif['precision'] }},
|
|
{{ $metricsPositif['recall'] }},
|
|
{{ $metricsPositif['f1'] }}
|
|
];
|
|
|
|
// Hapus chart lama
|
|
['barChart', 'pieChart', 'scoreChart', 'metricsChart'].forEach(chartId => {
|
|
const canvas = document.getElementById(chartId);
|
|
if (canvas) {
|
|
const existingChart = Chart.getChart(canvas);
|
|
if (existingChart) existingChart.destroy();
|
|
}
|
|
});
|
|
|
|
// Bar Chart
|
|
new Chart(document.getElementById('barChart'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: ['Positif', 'Negatif'],
|
|
datasets: [{
|
|
data: [positif, negatif],
|
|
backgroundColor: ['#10b981', '#ef4444'],
|
|
borderRadius: 5,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
grid: { color: 'rgba(255,255,255,0.1)' },
|
|
ticks: { color: 'white' }
|
|
},
|
|
x: { ticks: { color: 'white' } }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Pie Chart
|
|
new Chart(document.getElementById('pieChart'), {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: ['Positif', 'Negatif'],
|
|
datasets: [{
|
|
data: [positif, negatif],
|
|
backgroundColor: ['#10b981', '#ef4444'],
|
|
borderWidth: 0,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
display: false
|
|
}
|
|
},
|
|
cutout: '60%',
|
|
}
|
|
});
|
|
|
|
// Score Chart
|
|
new Chart(document.getElementById('scoreChart'), {
|
|
type: 'line',
|
|
data: {
|
|
labels: ['0-20', '21-40', '41-60', '61-80', '81-100'],
|
|
datasets: [{
|
|
data: scoreData,
|
|
borderColor: '#60a5fa',
|
|
backgroundColor: 'rgba(96, 165, 250, 0.2)',
|
|
borderWidth: 2,
|
|
fill: true,
|
|
tension: 0.4,
|
|
pointBackgroundColor: '#60a5fa',
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
grid: { color: 'rgba(255,255,255,0.1)' },
|
|
ticks: { color: 'white' }
|
|
},
|
|
x: { ticks: { color: 'white' } }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Metrics Chart
|
|
new Chart(document.getElementById('metricsChart'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: ['Precision', 'Recall', 'F1-Score'],
|
|
datasets: [
|
|
{ label: 'Negatif', data: metricsNegatif, backgroundColor: '#ef4444' },
|
|
{ label: 'Positif', data: metricsPositif, backgroundColor: '#10b981' }
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom',
|
|
labels: { color: 'white' }
|
|
}
|
|
},
|
|
scales: {
|
|
y: {
|
|
min: 0,
|
|
max: 1,
|
|
ticks: {
|
|
callback: v => (v * 100) + '%',
|
|
color: 'white'
|
|
},
|
|
grid: { color: 'rgba(255,255,255,0.1)' }
|
|
},
|
|
x: { ticks: { color: 'white' } }
|
|
}
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
[x-cloak] { display: none !important; }
|
|
</style>
|
|
|
|
</x-app-layout> |