863 lines
44 KiB
PHP
863 lines
44 KiB
PHP
{{-- resources/views/orangtua/child-visualization.blade.php --}}
|
|
@extends('layouts.app')
|
|
|
|
@section('title', 'Perkembangan ' . ($child->name ?? 'Anak') . ' - LearnMood')
|
|
|
|
@section('content')
|
|
<div class="space-y-6 pb-20 md:pb-0">
|
|
|
|
<!-- Breadcrumb -->
|
|
<div class="flex items-center text-xs md:text-sm text-gray-500 overflow-x-auto pb-1">
|
|
<a href="{{ route('dashboard') }}" class="hover:text-blue-600 flex items-center gap-1 whitespace-nowrap">
|
|
<svg class="w-3 h-3 md:w-4 md:h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
|
</svg>
|
|
<span>Dashboard</span>
|
|
</a>
|
|
<span class="mx-1 md:mx-2 text-gray-300">/</span>
|
|
<a href="{{ route('orangtua.children') }}" class="hover:text-blue-600 whitespace-nowrap">
|
|
Anak-anak
|
|
</a>
|
|
<span class="mx-1 md:mx-2 text-gray-300">/</span>
|
|
<span class="text-gray-700 font-medium truncate max-w-[120px] md:max-w-none">{{ $child->name }}</span>
|
|
</div>
|
|
|
|
<!-- Header -->
|
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-12 h-12 md:w-14 md:h-14 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-full flex items-center justify-center text-white font-bold text-xl md:text-2xl shadow-md">
|
|
{{ substr($child->name, 0, 1) }}
|
|
</div>
|
|
<div>
|
|
<h2 class="text-xl md:text-2xl font-bold text-gray-800">{{ $child->name }}</h2>
|
|
<p class="text-xs md:text-sm text-gray-500">Pantau aktivitas belajar anak Anda</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter Periode -->
|
|
<div class="flex bg-white rounded-lg border border-gray-200 p-1 shadow-sm w-full sm:w-auto">
|
|
<a href="{{ route('orangtua.child.visualization', ['childId' => $child->id, 'period' => 7]) }}"
|
|
class="flex-1 sm:flex-none px-3 md:px-4 py-1.5 md:py-2 text-xs md:text-sm rounded-md transition {{ $period == 7 ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100' }}">
|
|
7H
|
|
</a>
|
|
<a href="{{ route('orangtua.child.visualization', ['childId' => $child->id, 'period' => 30]) }}"
|
|
class="flex-1 sm:flex-none px-3 md:px-4 py-1.5 md:py-2 text-xs md:text-sm rounded-md transition {{ $period == 30 ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100' }}">
|
|
30H
|
|
</a>
|
|
<a href="{{ route('orangtua.child.visualization', ['childId' => $child->id, 'period' => 90]) }}"
|
|
class="flex-1 sm:flex-none px-3 md:px-4 py-1.5 md:py-2 text-xs md:text-sm rounded-md transition {{ $period == 90 ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100' }}">
|
|
90H
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
@if(count($dates) > 0)
|
|
<!-- Info Anak dengan Konsistensi yang Lebih Baik -->
|
|
<div class="bg-gradient-to-r from-blue-600 to-indigo-600 p-4 md:p-5 rounded-xl text-white shadow-md">
|
|
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 md:w-12 md:h-12 bg-white/20 rounded-full flex items-center justify-center text-xl md:text-2xl shrink-0 backdrop-blur-sm">
|
|
👤
|
|
</div>
|
|
<div>
|
|
<h3 class="text-base md:text-lg font-semibold">{{ $child->name }}</h3>
|
|
<p class="text-blue-100 text-xs md:text-sm">{{ $child->email }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Konsistensi dengan Progress Bar -->
|
|
<div class="flex items-center gap-3 bg-white/10 px-3 py-2 rounded-lg backdrop-blur-sm">
|
|
<div class="text-right">
|
|
<p class="text-xs text-blue-100">Konsistensi</p>
|
|
<p class="text-xl font-bold">{{ $stats['konsistensi'] ?? 0 }}%</p>
|
|
</div>
|
|
<div class="w-16 sm:w-24 bg-white/20 rounded-full h-2">
|
|
<div class="bg-white h-2 rounded-full" style="width: {{ min(100, $stats['konsistensi'] ?? 0) }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistik Cards dengan Desain Lebih Menarik -->
|
|
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3 md:gap-4">
|
|
<!-- Total Hari -->
|
|
<div class="bg-white p-4 rounded-xl border border-gray-100 shadow-sm hover:shadow-md transition">
|
|
<div class="flex items-start justify-between">
|
|
<div>
|
|
<p class="text-xs text-gray-500 mb-1">Total Hari Input</p>
|
|
<p class="text-2xl md:text-3xl font-bold text-gray-800">{{ $stats['total_hari'] }}</p>
|
|
<p class="text-xs text-gray-400 mt-1">dari {{ $period }} hari</p>
|
|
</div>
|
|
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rata-rata Durasi -->
|
|
<div class="bg-white p-4 rounded-xl border border-gray-100 shadow-sm hover:shadow-md transition">
|
|
<div class="flex items-start justify-between">
|
|
<div>
|
|
<p class="text-xs text-gray-500 mb-1">Rata-rata Durasi</p>
|
|
<p class="text-2xl md:text-3xl font-bold text-gray-800">{{ $stats['rata_durasi'] }}</p>
|
|
<p class="text-xs text-gray-400 mt-1">menit per hari</p>
|
|
</div>
|
|
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
|
|
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mood Terbanyak -->
|
|
<div class="bg-white p-4 rounded-xl border border-gray-100 shadow-sm hover:shadow-md transition">
|
|
<div class="flex items-start justify-between">
|
|
<div>
|
|
<p class="text-xs text-gray-500 mb-1">Mood Terbanyak</p>
|
|
<p class="text-xl md:text-2xl font-bold
|
|
@if($stats['mood_terbanyak'] == 'Bagus') text-green-600
|
|
@elseif($stats['mood_terbanyak'] == 'Lumayan') text-blue-600
|
|
@elseif($stats['mood_terbanyak'] == 'Biasa Saja') text-gray-600
|
|
@elseif($stats['mood_terbanyak'] == 'Cukup Jenuh') text-yellow-600
|
|
@elseif($stats['mood_terbanyak'] == 'Jenuh') text-red-600
|
|
@else text-gray-800 @endif">
|
|
{{ $stats['mood_terbanyak'] }}
|
|
</p>
|
|
<p class="text-xs text-gray-400 mt-1">paling sering muncul</p>
|
|
</div>
|
|
<div class="w-10 h-10 bg-yellow-100 rounded-lg flex items-center justify-center">
|
|
<svg class="w-5 h-5 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grafik Durasi Belajar -->
|
|
<div class="bg-white p-4 md:p-6 rounded-xl shadow-sm border border-gray-100">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<div class="bg-blue-100 p-1.5 md:p-2 rounded-lg">
|
|
<svg class="w-4 h-4 md:w-5 md:h-5 text-blue-600" 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>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">📈 Grafik Durasi Belajar</h3>
|
|
</div>
|
|
<div style="height: 250px;" class="relative">
|
|
<canvas id="durationChart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Setelah Grafik Durasi Belajar -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
|
|
<!-- 1. HEATMAP AKTIVITAS (Pola Harian) -->
|
|
<div class="bg-white p-4 md:p-6 rounded-xl shadow-sm border border-gray-100">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<div class="bg-red-100 p-1.5 md:p-2 rounded-lg">
|
|
<svg class="w-4 h-4 md:w-5 md:h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">🔥 Heatmap Aktivitas 30 Hari</h3>
|
|
</div>
|
|
|
|
@php
|
|
// Generate data untuk 30 hari terakhir
|
|
$heatmapData = [];
|
|
$maxDuration = max($durations) ?: 1;
|
|
|
|
for ($i = 29; $i >= 0; $i--) {
|
|
$date = Carbon\Carbon::today()->subDays($i);
|
|
$dateStr = $date->format('Y-m-d');
|
|
$dayName = $date->format('D');
|
|
|
|
// Cari aktivitas di tanggal ini
|
|
$found = false;
|
|
$duration = 0;
|
|
foreach ($activities as $activity) {
|
|
if ($activity->activity_date->format('Y-m-d') == $dateStr) {
|
|
$found = true;
|
|
$duration = $activity->duration_minutes;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$heatmapData[] = [
|
|
'date' => $dateStr,
|
|
'day' => $dayName,
|
|
'has_activity' => $found,
|
|
'duration' => $duration,
|
|
'intensity' => $found ? round(($duration / $maxDuration) * 100) : 0
|
|
];
|
|
}
|
|
@endphp
|
|
|
|
<div class="grid grid-cols-7 gap-1 mb-2">
|
|
@foreach(['Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab', 'Min'] as $day)
|
|
<div class="text-center text-[10px] font-medium text-gray-500">{{ $day }}</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
<div class="grid grid-cols-7 gap-1">
|
|
@foreach($heatmapData as $data)
|
|
@php
|
|
$intensity = $data['intensity'];
|
|
if ($intensity == 0) {
|
|
$bgColor = 'bg-gray-100';
|
|
} elseif ($intensity < 25) {
|
|
$bgColor = 'bg-green-200';
|
|
} elseif ($intensity < 50) {
|
|
$bgColor = 'bg-green-300';
|
|
} elseif ($intensity < 75) {
|
|
$bgColor = 'bg-green-400';
|
|
} else {
|
|
$bgColor = 'bg-green-500';
|
|
}
|
|
@endphp
|
|
<div class="aspect-square {{ $bgColor }} rounded-sm hover:scale-110 transition-transform cursor-pointer relative group"
|
|
title="{{ Carbon\Carbon::parse($data['date'])->format('d M Y') }}: {{ $data['duration'] }} menit">
|
|
<div class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 hidden group-hover:block z-10">
|
|
<div class="bg-gray-800 text-white text-xs rounded py-1 px-2 whitespace-nowrap">
|
|
{{ Carbon\Carbon::parse($data['date'])->format('d M') }}: {{ $data['duration'] }} menit
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between mt-3 text-xs text-gray-500">
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 bg-gray-100 rounded-sm"></span>
|
|
<span>Tidak belajar</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 bg-green-200 rounded-sm"></span>
|
|
<span>Ringan</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 bg-green-300 rounded-sm"></span>
|
|
<span>Sedang</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 bg-green-400 rounded-sm"></span>
|
|
<span>Intensif</span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 bg-green-500 rounded-sm"></span>
|
|
<span>Sangat intensif</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. INSIGHT & ANALISIS (Ringkasan Cerdas) -->
|
|
<div class="bg-white p-4 md:p-6 rounded-xl shadow-sm border border-gray-100">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<div class="bg-purple-100 p-1.5 md:p-2 rounded-lg">
|
|
<svg class="w-4 h-4 md:w-5 md:h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">💡 Insight & Analisis</h3>
|
|
</div>
|
|
|
|
@php
|
|
// Hitung berbagai insight
|
|
$totalDays = count($activities);
|
|
$totalDuration = array_sum($durations);
|
|
$avgDuration = $totalDays > 0 ? round($totalDuration / $totalDays) : 0;
|
|
|
|
// Hari paling produktif
|
|
$dayProductivity = [];
|
|
foreach ($activities as $activity) {
|
|
$day = $activity->activity_date->format('l');
|
|
$dayProductivity[$day] = ($dayProductivity[$day] ?? 0) + $activity->duration_minutes;
|
|
}
|
|
arsort($dayProductivity);
|
|
$mostProductiveDay = array_key_first($dayProductivity) ?? '-';
|
|
|
|
// Mood yang paling produktif
|
|
$moodProductivity = [];
|
|
foreach ($activities as $activity) {
|
|
$moodProductivity[$activity->mood] = ($moodProductivity[$activity->mood] ?? 0) + $activity->duration_minutes;
|
|
}
|
|
arsort($moodProductivity);
|
|
$mostProductiveMood = array_key_first($moodProductivity) ?? '-';
|
|
|
|
// Tren (meningkat/menurun)
|
|
$recentAvg = 0;
|
|
$previousAvg = 0;
|
|
if (count($activities) >= 4) {
|
|
$recent = array_slice($durations, -2);
|
|
$previous = array_slice($durations, -4, 2);
|
|
$recentAvg = count($recent) > 0 ? array_sum($recent) / count($recent) : 0;
|
|
$previousAvg = count($previous) > 0 ? array_sum($previous) / count($previous) : 0;
|
|
}
|
|
$trend = $recentAvg > $previousAvg ? 'meningkat' : ($recentAvg < $previousAvg ? 'menurun' : 'stabil');
|
|
$trendColor = $trend == 'meningkat' ? 'text-green-600' : ($trend == 'menurun' ? 'text-red-600' : 'text-yellow-600');
|
|
$trendIcon = $trend == 'meningkat' ? '📈' : ($trend == 'menurun' ? '📉' : '📊');
|
|
@endphp
|
|
|
|
<div class="space-y-4">
|
|
<!-- Statistik Utama -->
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<div class="bg-gradient-to-br from-blue-50 to-blue-100 p-3 rounded-lg">
|
|
<p class="text-xs text-blue-600 mb-1">Total Belajar</p>
|
|
<p class="text-xl font-bold text-blue-800">{{ $totalDuration }} <span class="text-sm font-normal">menit</span></p>
|
|
</div>
|
|
<div class="bg-gradient-to-br from-green-50 to-green-100 p-3 rounded-lg">
|
|
<p class="text-xs text-green-600 mb-1">Rata-rata</p>
|
|
<p class="text-xl font-bold text-green-800">{{ $avgDuration }} <span class="text-sm font-normal">mnt/hari</span></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Insight Cards -->
|
|
<div class="space-y-2">
|
|
<div class="flex items-center gap-3 p-3 bg-yellow-50 rounded-lg">
|
|
<div class="text-2xl">⭐</div>
|
|
<div>
|
|
<p class="text-xs text-yellow-700">Hari paling produktif</p>
|
|
<p class="text-sm font-semibold text-gray-800">{{ $mostProductiveDay }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3 p-3 bg-purple-50 rounded-lg">
|
|
<div class="text-2xl">😊</div>
|
|
<div>
|
|
<p class="text-xs text-purple-700">Mood paling produktif</p>
|
|
<p class="text-sm font-semibold text-gray-800">{{ $mostProductiveMood }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3 p-3 bg-blue-50 rounded-lg">
|
|
<div class="text-2xl">{{ $trendIcon }}</div>
|
|
<div>
|
|
<p class="text-xs text-blue-700">Tren 2 hari terakhir</p>
|
|
<p class="text-sm font-semibold {{ $trendColor }}">{{ ucfirst($trend) }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rekomendasi Personal -->
|
|
<div class="mt-4 p-3 bg-gradient-to-r from-indigo-50 to-purple-50 rounded-lg border border-indigo-100">
|
|
<p class="text-xs font-semibold text-indigo-700 mb-2">📝 Catatan untuk Orang Tua</p>
|
|
@if($totalDays == 0)
|
|
<p class="text-sm text-gray-600">Ajak {{ $child->name }} untuk mulai mencatat aktivitas belajar agar bisa dipantau perkembangannya.</p>
|
|
@elseif($avgDuration < 30)
|
|
<p class="text-sm text-gray-600">{{ $child->name }} belajar dengan durasi pendek. Coba motivasi untuk belajar lebih konsisten.</p>
|
|
@elseif($avgDuration < 60)
|
|
<p class="text-sm text-gray-600">Durasi belajar {{ $child->name }} cukup baik. Pertahankan konsistensi!</p>
|
|
@elseif($avgDuration < 120)
|
|
<p class="text-sm text-gray-600">Luar biasa! {{ $child->name }} sangat rajin belajar. Pastikan tetap ada waktu istirahat.</p>
|
|
@else
|
|
<p class="text-sm text-gray-600">{{ $child->name }} belajar sangat intensif. Perhatikan keseimbangan dengan istirahat.</p>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3. TREN MOOD (Setelah grid di atas) -->
|
|
<div class="bg-white p-4 md:p-6 rounded-xl shadow-sm border border-gray-100">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<div class="bg-pink-100 p-1.5 md:p-2 rounded-lg">
|
|
<svg class="w-4 h-4 md:w-5 md:h-5 text-pink-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">📊 Tren Mood Mingguan</h3>
|
|
</div>
|
|
|
|
@php
|
|
// Kelompokkan mood per minggu
|
|
$weeklyMood = [];
|
|
foreach ($activities as $activity) {
|
|
$week = $activity->activity_date->format('W');
|
|
$year = $activity->activity_date->format('Y');
|
|
$weekKey = $year . '-W' . $week;
|
|
|
|
if (!isset($weeklyMood[$weekKey])) {
|
|
$weeklyMood[$weekKey] = [
|
|
'week' => $weekKey,
|
|
'moods' => [],
|
|
'count' => 0,
|
|
'avg_score' => 0
|
|
];
|
|
}
|
|
|
|
// Konversi mood ke skor
|
|
$score = match($activity->mood) {
|
|
'Bagus' => 5,
|
|
'Lumayan' => 4,
|
|
'Biasa Saja' => 3,
|
|
'Cukup Jenuh' => 2,
|
|
'Jenuh' => 1,
|
|
default => 3
|
|
};
|
|
|
|
$weeklyMood[$weekKey]['moods'][] = $activity->mood;
|
|
$weeklyMood[$weekKey]['count']++;
|
|
$weeklyMood[$weekKey]['avg_score'] =
|
|
(($weeklyMood[$weekKey]['avg_score'] * ($weeklyMood[$weekKey]['count'] - 1)) + $score)
|
|
/ $weeklyMood[$weekKey]['count'];
|
|
}
|
|
|
|
// Ambil 5 minggu terakhir
|
|
$weeklyMood = array_slice($weeklyMood, -5);
|
|
@endphp
|
|
|
|
@if(count($weeklyMood) > 0)
|
|
<div class="space-y-3">
|
|
@foreach($weeklyMood as $week)
|
|
@php
|
|
$avgScore = round($week['avg_score'], 1);
|
|
$moodLabel = match(true) {
|
|
$avgScore >= 4.5 => 'Sangat Baik',
|
|
$avgScore >= 3.5 => 'Baik',
|
|
$avgScore >= 2.5 => 'Cukup',
|
|
$avgScore >= 1.5 => 'Kurang',
|
|
default => 'Buruk'
|
|
};
|
|
|
|
$barColor = match(true) {
|
|
$avgScore >= 4.5 => 'bg-green-500',
|
|
$avgScore >= 3.5 => 'bg-green-400',
|
|
$avgScore >= 2.5 => 'bg-yellow-400',
|
|
$avgScore >= 1.5 => 'bg-orange-400',
|
|
default => 'bg-red-400'
|
|
};
|
|
@endphp
|
|
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-xs font-medium text-gray-600 w-16">Minggu {{ substr($week['week'], -2) }}</span>
|
|
<div class="flex-1">
|
|
<div class="w-full bg-gray-200 rounded-full h-2.5">
|
|
<div class="{{ $barColor }} h-2.5 rounded-full" style="width: {{ ($avgScore / 5) * 100 }}%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-xs font-semibold text-gray-800">{{ $avgScore }}</span>
|
|
<span class="text-xs text-gray-500">{{ $moodLabel }}</span>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@else
|
|
<p class="text-center text-gray-400 py-4">Belum cukup data untuk tren mood</p>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- 4. STATISTIK KOMPARATIF (Setelah tabel riwayat) -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- Perbandingan dengan target ideal -->
|
|
<div class="bg-white p-4 md:p-6 rounded-xl shadow-sm border border-gray-100">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<div class="bg-indigo-100 p-1.5 md:p-2 rounded-lg">
|
|
<svg class="w-4 h-4 md:w-5 md:h-5 text-indigo-600" 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>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">🎯 Perbandingan Target</h3>
|
|
</div>
|
|
|
|
@php
|
|
// Target ideal: 60 menit per hari
|
|
$targetPerDay = 60;
|
|
$actualPerDay = $avgDuration;
|
|
$percentToTarget = round(($actualPerDay / $targetPerDay) * 100);
|
|
|
|
// Target konsistensi: 80%
|
|
$targetConsistency = 80;
|
|
$actualConsistency = $stats['konsistensi'] ?? 0;
|
|
@endphp
|
|
|
|
<div class="space-y-4">
|
|
<div>
|
|
<div class="flex justify-between text-sm mb-1">
|
|
<span class="text-gray-600">Durasi vs Target (60 mnt/hari)</span>
|
|
<span class="font-medium {{ $percentToTarget >= 100 ? 'text-green-600' : 'text-yellow-600' }}">
|
|
{{ $actualPerDay }} / 60 mnt
|
|
</span>
|
|
</div>
|
|
<div class="w-full bg-gray-200 rounded-full h-3">
|
|
<div class="{{ $percentToTarget >= 100 ? 'bg-green-500' : 'bg-yellow-500' }} h-3 rounded-full"
|
|
style="width: {{ min(100, $percentToTarget) }}%"></div>
|
|
</div>
|
|
<p class="text-xs text-gray-500 mt-1">{{ $percentToTarget }}% dari target</p>
|
|
</div>
|
|
|
|
<div>
|
|
<div class="flex justify-between text-sm mb-1">
|
|
<span class="text-gray-600">Konsistensi vs Target (80%)</span>
|
|
<span class="font-medium {{ $actualConsistency >= 80 ? 'text-green-600' : 'text-yellow-600' }}">
|
|
{{ $actualConsistency }}%
|
|
</span>
|
|
</div>
|
|
<div class="w-full bg-gray-200 rounded-full h-3">
|
|
<div class="{{ $actualConsistency >= 80 ? 'bg-green-500' : 'bg-yellow-500' }} h-3 rounded-full"
|
|
style="width: {{ min(100, $actualConsistency) }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rekomendasi Waktu Belajar -->
|
|
<div class="bg-white p-4 md:p-6 rounded-xl shadow-sm border border-gray-100">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<div class="bg-teal-100 p-1.5 md:p-2 rounded-lg">
|
|
<svg class="w-4 h-4 md:w-5 md:h-5 text-teal-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">⏰ Rekomendasi Waktu Belajar</h3>
|
|
</div>
|
|
|
|
@php
|
|
// Analisis waktu belajar berdasarkan data
|
|
$bestTimes = [];
|
|
foreach ($activities as $activity) {
|
|
if (isset($activity->start_time)) {
|
|
$hour = (int) substr($activity->start_time, 0, 2);
|
|
$range = floor($hour / 4) * 4;
|
|
$timeRange = $range . ':00 - ' . ($range + 4) . ':00';
|
|
$bestTimes[$timeRange] = ($bestTimes[$timeRange] ?? 0) + $activity->duration_minutes;
|
|
}
|
|
}
|
|
arsort($bestTimes);
|
|
$bestTimeRange = array_key_first($bestTimes) ?? 'Belum ada data';
|
|
@endphp
|
|
|
|
<div class="space-y-3">
|
|
<div class="bg-blue-50 p-3 rounded-lg">
|
|
<p class="text-xs text-blue-700 mb-1">Waktu paling produktif</p>
|
|
<p class="text-lg font-bold text-blue-800">{{ $bestTimeRange }}</p>
|
|
</div>
|
|
|
|
<div class="bg-green-50 p-3 rounded-lg">
|
|
<p class="text-xs text-green-700 mb-1">Rekomendasi untuk minggu depan</p>
|
|
<p class="text-sm text-gray-700">
|
|
@if($avgDuration < 30)
|
|
Coba tingkatkan durasi secara bertahap. Mulai dengan target 45 menit per hari.
|
|
@elseif($avgDuration < 60)
|
|
Pertahankan durasi saat ini. Fokus pada konsistensi.
|
|
@else
|
|
Durasi sudah baik. Pastikan ada jeda istirahat setiap 45 menit.
|
|
@endif
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rata-rata Durasi per Mood -->
|
|
<div class="bg-white p-4 md:p-6 rounded-xl shadow-sm border border-gray-100">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<div class="bg-orange-100 p-1.5 md:p-2 rounded-lg">
|
|
<svg class="w-4 h-4 md:w-5 md:h-5 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4h16v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4z"></path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">😊 Rata-rata Durasi per Mood</h3>
|
|
</div>
|
|
|
|
@php
|
|
$moodList = ['Bagus', 'Lumayan', 'Biasa Saja', 'Cukup Jenuh', 'Jenuh'];
|
|
$colors = ['green', 'blue', 'gray', 'yellow', 'red'];
|
|
$bgColors = ['bg-green-50', 'bg-blue-50', 'bg-gray-50', 'bg-yellow-50', 'bg-red-50'];
|
|
$textColors = ['text-green-700', 'text-blue-700', 'text-gray-700', 'text-yellow-700', 'text-red-700'];
|
|
$emojis = ['😊', '🙂', '😐', '😕', '😫'];
|
|
@endphp
|
|
|
|
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-2 md:gap-3">
|
|
@foreach($moodList as $idx => $mood)
|
|
@php
|
|
$total = 0;
|
|
$count = 0;
|
|
foreach($moods as $i => $m) {
|
|
if($m == $mood) {
|
|
$total += $durations[$i];
|
|
$count++;
|
|
}
|
|
}
|
|
$avg = $count > 0 ? round($total / $count) : 0;
|
|
@endphp
|
|
<div class="text-center p-2 md:p-3 rounded-xl {{ $bgColors[$idx] }} border border-{{ $colors[$idx] }}-200 hover:shadow-md transition">
|
|
<div class="text-xl md:text-2xl mb-1">{{ $emojis[$idx] }}</div>
|
|
<p class="text-[10px] md:text-xs font-medium text-gray-600 truncate">{{ $mood }}</p>
|
|
<p class="text-base md:text-lg font-bold {{ $textColors[$idx] }}">{{ $avg }}</p>
|
|
<p class="text-[8px] md:text-xs text-gray-500">menit</p>
|
|
@if($count > 0)
|
|
<p class="text-[8px] md:text-xs text-gray-400 mt-0.5">{{ $count }}x</p>
|
|
@endif
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tabel Riwayat - Mobile Friendly dengan Desain Lebih Baik -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
|
<div class="px-4 md:px-6 py-3 md:py-4 border-b border-gray-100 bg-gradient-to-r from-blue-50 to-indigo-50">
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-6 h-6 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
<svg class="w-3 h-3 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-sm md:text-base font-semibold text-gray-800">📋 Riwayat Aktivitas</h3>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Desktop Table View --}}
|
|
<div class="hidden md:block overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Tanggal</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Durasi</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Mood</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Tidur</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Rekomendasi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-100">
|
|
@forelse(array_reverse($dates) as $index => $date)
|
|
@php
|
|
$i = count($dates) - 1 - $index;
|
|
$category = $categories[$i] ?? null;
|
|
@endphp
|
|
<tr class="hover:bg-gray-50 transition-colors">
|
|
<td class="px-6 py-3 text-sm text-gray-800 whitespace-nowrap">{{ \Carbon\Carbon::parse($date)->format('d M Y') }}</td>
|
|
<td class="px-6 py-3 text-sm text-gray-800">{{ $durations[$i] }} menit</td>
|
|
<td class="px-6 py-3">
|
|
<span class="px-2 py-1 text-xs rounded-full
|
|
@if($moods[$i] == 'Bagus') bg-green-100 text-green-700
|
|
@elseif($moods[$i] == 'Lumayan') bg-blue-100 text-blue-700
|
|
@elseif($moods[$i] == 'Biasa Saja') bg-gray-100 text-gray-700
|
|
@elseif($moods[$i] == 'Cukup Jenuh') bg-yellow-100 text-yellow-700
|
|
@else bg-red-100 text-red-700 @endif">
|
|
{{ $moods[$i] }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-3 text-sm text-gray-800">{{ $sleep_hours[$i] ?? '7' }} jam</td>
|
|
<td class="px-6 py-3">
|
|
@if($category)
|
|
@php
|
|
$parentDisplay = match($category) {
|
|
'Ringan' => 'Belajar Ringan',
|
|
'Sedang' => 'Belajar Cukup',
|
|
'Intensif' => 'Belajar Lama',
|
|
default => $category
|
|
};
|
|
@endphp
|
|
<span class="px-2 py-1 text-xs rounded-full
|
|
@if($category == 'Ringan') bg-yellow-100 text-yellow-700
|
|
@elseif($category == 'Sedang') bg-green-100 text-green-700
|
|
@else bg-blue-100 text-blue-700 @endif">
|
|
{{ $parentDisplay }}
|
|
</span>
|
|
@else
|
|
<span class="text-xs text-gray-400">-</span>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="5" class="px-6 py-8 text-center text-gray-500">
|
|
Belum ada data aktivitas
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{{-- Mobile Card View dengan Desain Lebih Baik --}}
|
|
<div class="md:hidden divide-y divide-gray-100">
|
|
@forelse(array_reverse($dates) as $index => $date)
|
|
@php
|
|
$i = count($dates) - 1 - $index;
|
|
$category = $categories[$i] ?? null;
|
|
@endphp
|
|
<div class="p-4 hover:bg-gray-50 transition-colors">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<div class="flex items-center gap-2">
|
|
<span class="font-medium text-gray-800">{{ \Carbon\Carbon::parse($date)->format('d M Y') }}</span>
|
|
@if($index == 0)
|
|
<span class="text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full">Terbaru</span>
|
|
@endif
|
|
</div>
|
|
<span class="text-sm font-semibold text-blue-600">{{ $durations[$i] }} mnt</span>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-3 gap-2">
|
|
<!-- Mood -->
|
|
<div class="bg-gray-50 p-2 rounded-lg">
|
|
<p class="text-[10px] text-gray-500 mb-1">Mood</p>
|
|
<div class="flex items-center gap-1">
|
|
<span class="text-sm">
|
|
@if($moods[$i] == 'Bagus') 😊
|
|
@elseif($moods[$i] == 'Lumayan') 🙂
|
|
@elseif($moods[$i] == 'Biasa Saja') 😐
|
|
@elseif($moods[$i] == 'Cukup Jenuh') 😕
|
|
@else 😫 @endif
|
|
</span>
|
|
<span class="text-xs font-medium
|
|
@if($moods[$i] == 'Bagus') text-green-600
|
|
@elseif($moods[$i] == 'Lumayan') text-blue-600
|
|
@elseif($moods[$i] == 'Biasa Saja') text-gray-600
|
|
@elseif($moods[$i] == 'Cukup Jenuh') text-yellow-600
|
|
@else text-red-600 @endif">
|
|
{{ $moods[$i] }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tidur -->
|
|
<div class="bg-gray-50 p-2 rounded-lg">
|
|
<p class="text-[10px] text-gray-500 mb-1">Tidur</p>
|
|
<p class="text-sm font-medium text-gray-800">
|
|
{{ $sleep_hours[$i] ?? '7' }} <span class="text-xs text-gray-500">jam</span>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Rekomendasi -->
|
|
<div class="bg-gray-50 p-2 rounded-lg">
|
|
<p class="text-[10px] text-gray-500 mb-1">Rekom</p>
|
|
@if($category)
|
|
@php
|
|
$parentDisplay = match($category) {
|
|
'Ringan' => 'Belajar Ringan',
|
|
'Sedang' => 'Belajar Cukup',
|
|
'Intensif' => 'Belajar Lama',
|
|
default => $category
|
|
};
|
|
@endphp
|
|
<span class="text-xs font-medium px-2 py-0.5 rounded-full inline-block
|
|
@if($category == 'Ringan') bg-yellow-100 text-yellow-700
|
|
@elseif($category == 'Sedang') bg-green-100 text-green-700
|
|
@else bg-blue-100 text-blue-700 @endif">
|
|
{{ $parentDisplay }}
|
|
</span>
|
|
@else
|
|
<span class="text-sm text-gray-400">-</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="p-8 text-center text-gray-500">
|
|
<svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
</svg>
|
|
<p>Belum ada data aktivitas</p>
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
@else
|
|
<!-- Empty State dengan Desain Lebih Menarik -->
|
|
<div class="bg-gradient-to-br from-gray-50 to-white p-8 md:p-12 rounded-xl border border-gray-200 text-center shadow-sm">
|
|
<div class="w-20 h-20 md:w-24 md:h-24 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<svg class="w-10 h-10 md:w-12 md:h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
</svg>
|
|
</div>
|
|
<h3 class="text-lg md:text-xl font-semibold text-gray-800 mb-2">Belum Ada Data</h3>
|
|
<p class="text-sm md:text-base text-gray-500 mb-4">{{ $child->name }} belum menginput aktivitas belajar</p>
|
|
<div class="bg-blue-50 p-3 rounded-lg inline-block">
|
|
<p class="text-xs text-blue-700">💡 Ingatkan anak untuk mulai mencatat aktivitas</p>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Tombol Kembali -->
|
|
<div class="flex justify-center">
|
|
<a href="{{ route('orangtua.children') }}"
|
|
class="flex items-center gap-2 px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition text-sm">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
|
|
</svg>
|
|
Kembali ke Daftar Anak
|
|
</a>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
@if(count($dates) > 0)
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Format dates untuk tampilan lebih pendek
|
|
const dates = {!! json_encode($dates) !!};
|
|
const durations = {!! json_encode($durations) !!};
|
|
|
|
const formattedDates = dates.map(date => {
|
|
const d = new Date(date);
|
|
return d.toLocaleDateString('id-ID', { day: 'numeric', month: 'short' });
|
|
});
|
|
|
|
// Grafik Durasi
|
|
const ctx = document.getElementById('durationChart').getContext('2d');
|
|
new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: formattedDates,
|
|
datasets: [{
|
|
label: 'Durasi Belajar (menit)',
|
|
data: durations,
|
|
borderColor: '#3b82f6',
|
|
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
|
borderWidth: 3,
|
|
tension: 0.3,
|
|
fill: true,
|
|
pointBackgroundColor: '#3b82f6',
|
|
pointBorderColor: 'white',
|
|
pointBorderWidth: 2,
|
|
pointRadius: 4,
|
|
pointHoverRadius: 6
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: { display: false },
|
|
tooltip: {
|
|
backgroundColor: '#1f2937',
|
|
callbacks: {
|
|
label: function(context) {
|
|
return context.raw + ' menit';
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
grid: { color: '#e5e7eb' },
|
|
ticks: {
|
|
callback: function(value) {
|
|
return value + ' mnt';
|
|
}
|
|
}
|
|
},
|
|
x: {
|
|
grid: { display: false },
|
|
ticks: {
|
|
maxRotation: 45,
|
|
minRotation: 45,
|
|
font: { size: 10 }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
@endif
|
|
@endpush |