579 lines
27 KiB
PHP
579 lines
27 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', 'Dashboard')
|
|
@section('page-title', 'Dashboard')
|
|
|
|
@section('content')
|
|
{{-- Sapaan --}}
|
|
<div class="mb-6">
|
|
<h2 class="text-2xl font-bold" style="font-family:'Playfair Display',serif;color:#1a3a2a;">
|
|
Halo, {{ auth()->user()->name }}! 👋
|
|
</h2>
|
|
<p class="text-sm mt-1" style="color:#5a7a67;">
|
|
Selamat datang kembali. Berikut ringkasan aktivitas diagnosa kamu hari ini.
|
|
</p>
|
|
</div>
|
|
|
|
<div id="tour-stat-cards" class="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
|
<!-- Card 1: Total Diagnosa -->
|
|
<div class="bg-white p-4 rounded-2xl border border-[#ede8df] cursor-pointer transition hover:shadow-lg"
|
|
style="box-shadow:0 4px 16px rgba(26,58,42,.08);"
|
|
onclick="openTotalModal()"
|
|
title="Klik untuk lihat statistik diagnosa">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="flex items-center gap-1" style="color:#5a7a67;text-transform:uppercase;letter-spacing:.06em;font-size:.7rem;">
|
|
Total Diagnosa
|
|
<i class="fas fa-search text-xs" style="color:#2d6a4f;"></i>
|
|
</p>
|
|
<h3 class="text-2xl font-bold mt-1" style="font-family:'Playfair Display',serif;color:#1a3a2a;">{{ $totalDiagnosis }}</h3>
|
|
</div>
|
|
<div class="p-3 rounded-xl" style="background:#d8f3dc;">
|
|
<i class="fas fa-clipboard-list text-xl" style="color:#2d6a4f;"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card 2: Bulan Ini -->
|
|
<div class="bg-white p-4 rounded-2xl border border-[#ede8df] cursor-pointer transition hover:shadow-lg"
|
|
style="box-shadow:0 4px 16px rgba(26,58,42,.08);"
|
|
onclick="openCalendar()"
|
|
title="Klik untuk lihat kalender diagnosa">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="flex items-center gap-1" style="color:#5a7a67;text-transform:uppercase;letter-spacing:.06em;font-size:.7rem;">
|
|
Bulan Ini
|
|
<i class="fas fa-search text-xs" style="color:#2563eb;"></i>
|
|
</p>
|
|
<h3 class="text-2xl font-bold mt-1" style="font-family:'Playfair Display',serif;color:#1a3a2a;">
|
|
{{ $monthlyData->where('month', (int) date('n'))->first()->count ?? 0 }}
|
|
</h3>
|
|
</div>
|
|
<div class="p-3 rounded-xl" style="background:#e0f0ff;">
|
|
<i class="fas fa-calendar text-xl" style="color:#2563eb;"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card 3: Akurasi Rata-rata -->
|
|
<div class="bg-white p-4 rounded-2xl border border-[#ede8df] cursor-pointer transition hover:shadow-lg"
|
|
style="box-shadow:0 4px 16px rgba(26,58,42,.08);"
|
|
onclick="openAccuracyModal()"
|
|
title="Klik untuk lihat grafik akurasi">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="flex items-center gap-1" style="color:#5a7a67;text-transform:uppercase;letter-spacing:.06em;font-size:.7rem;">
|
|
Akurasi Rata-rata
|
|
<i class="fas fa-search text-xs" style="color:#7c3aed;"></i>
|
|
</p>
|
|
<h3 class="text-2xl font-bold mt-1" style="font-family:'Playfair Display',serif;color:#1a3a2a;">{{ $avgAccuracy }}%</h3>
|
|
</div>
|
|
<div class="p-3 rounded-xl" style="background:#f0e8ff;">
|
|
<i class="fas fa-chart-line text-xl" style="color:#7c3aed;"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chart -->
|
|
<div id="tour-chart" class="bg-white p-4 rounded-2xl mb-6 border border-[#ede8df]" style="box-shadow:0 4px 16px rgba(26,58,42,.08);">
|
|
<h3 class="text-lg font-bold mb-4" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Statistik Diagnosa</h3>
|
|
<div class="relative" style="height:200px;">
|
|
<canvas id="diagnosisChart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Diagnosis -->
|
|
<div class="bg-white p-4 rounded-2xl border border-[#ede8df]" style="box-shadow:0 4px 16px rgba(26,58,42,.08);">
|
|
<h3 class="text-lg font-bold mb-4" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Diagnosa Terbaru</h3>
|
|
|
|
{{-- Tampilan mobile: card --}}
|
|
<div class="block sm:hidden space-y-3">
|
|
@forelse($recentDiagnosis as $diagnosis)
|
|
<div class="p-3 rounded-xl border border-[#ede8df]">
|
|
<div class="flex justify-between items-start mb-1">
|
|
<span class="text-xs" style="color:#5a7a67;">{{ $diagnosis->created_at->format('d/m/Y') }}</span>
|
|
<span class="px-2 py-0.5 rounded-full text-xs font-semibold" style="background:#d8f3dc;color:#2d6a4f;">
|
|
{{ $diagnosis->confidence }}%
|
|
</span>
|
|
</div>
|
|
<p class="font-medium text-sm" style="color:#1a3a2a;">{{ $diagnosis->plant_name }}</p>
|
|
<p class="text-sm" style="color:#5a7a67;">{{ $diagnosis->disease_name }}</p>
|
|
</div>
|
|
@empty
|
|
<p class="text-center py-4 text-sm" style="color:#8fa89a;">Belum ada diagnosa</p>
|
|
@endforelse
|
|
</div>
|
|
|
|
{{-- Tampilan desktop: tabel --}}
|
|
<div class="hidden sm:block overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead>
|
|
<tr style="border-bottom:2px solid #ede8df;">
|
|
<th class="text-left py-3 px-4" style="color:#5a7a67;font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;font-weight:600;">Tanggal</th>
|
|
<th class="text-left py-3 px-4" style="color:#5a7a67;font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;font-weight:600;">Nama Tanaman</th>
|
|
<th class="text-left py-3 px-4" style="color:#5a7a67;font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;font-weight:600;">Penyakit</th>
|
|
<th class="text-left py-3 px-4" style="color:#5a7a67;font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;font-weight:600;">Akurasi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($recentDiagnosis as $diagnosis)
|
|
<tr style="border-bottom:1px solid rgba(237,232,223,.6);" class="hover:bg-[#d8f3dc]/20 transition">
|
|
<td class="py-3 px-4 text-sm" style="color:#5a7a67;">{{ $diagnosis->created_at->format('d/m/Y') }}</td>
|
|
<td class="py-3 px-4 text-sm font-medium" style="color:#1a3a2a;">{{ $diagnosis->plant_name }}</td>
|
|
<td class="py-3 px-4 text-sm" style="color:#1a2e22;">{{ $diagnosis->disease_name }}</td>
|
|
<td class="py-3 px-4">
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold" style="background:#d8f3dc;color:#2d6a4f;">
|
|
{{ $diagnosis->confidence }}%
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="4" class="text-center py-8" style="color:#8fa89a;">Belum ada diagnosa</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ─── Modal Total Diagnosa ─── -->
|
|
<div id="totalModal" class="fixed inset-0 z-50 hidden flex items-center justify-center"
|
|
style="background:rgba(0,0,0,0.4);">
|
|
<div class="bg-white rounded-2xl p-6 w-full max-w-lg mx-4 shadow-2xl"
|
|
style="animation: popIn .25s cubic-bezier(0.22,1,0.36,1);">
|
|
<div class="flex items-center justify-between mb-5">
|
|
<div>
|
|
<h3 class="text-lg font-bold" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Statistik Total Diagnosa</h3>
|
|
<p class="text-sm" style="color:#5a7a67;">Per bulan tahun {{ date('Y') }}</p>
|
|
</div>
|
|
<button onclick="closeTotalModal()"
|
|
class="w-8 h-8 rounded-full flex items-center justify-center transition"
|
|
style="background:#f0fdf4;color:#2d6a4f;"
|
|
onmouseover="this.style.background='#d8f3dc'" onmouseout="this.style.background='#f0fdf4'">
|
|
<i class="fas fa-times text-sm"></i>
|
|
</button>
|
|
</div>
|
|
<canvas id="totalModalChart" height="160"></canvas>
|
|
<div class="mt-4 p-3 rounded-xl text-sm text-center font-semibold" style="background:#d8f3dc;color:#2d6a4f;">
|
|
Total keseluruhan: {{ $totalDiagnosis }} diagnosa
|
|
</div>
|
|
<button onclick="closeTotalModal()"
|
|
class="w-full mt-4 py-2.5 rounded-xl text-sm font-semibold"
|
|
style="background:#f0fdf4;color:#2d6a4f;border:1.5px solid #b7ddc4;">
|
|
Tutup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ─── Modal Kalender ─── -->
|
|
<div id="calendarModal" class="fixed inset-0 z-50 hidden flex items-center justify-center"
|
|
style="background:rgba(0,0,0,0.4);">
|
|
<div class="bg-white rounded-2xl p-6 w-full max-w-sm mx-4 shadow-2xl"
|
|
style="animation: popIn .25s cubic-bezier(0.22,1,0.36,1);">
|
|
<div class="flex items-center justify-between mb-5">
|
|
<div>
|
|
<h3 class="text-lg font-bold" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Kalender Diagnosa</h3>
|
|
<p class="text-sm" style="color:#5a7a67;" id="calendarMonthYear"></p>
|
|
</div>
|
|
<button onclick="closeCalendar()"
|
|
class="w-8 h-8 rounded-full flex items-center justify-center transition"
|
|
style="background:#f0fdf4;color:#2d6a4f;"
|
|
onmouseover="this.style.background='#d8f3dc'" onmouseout="this.style.background='#f0fdf4'">
|
|
<i class="fas fa-times text-sm"></i>
|
|
</button>
|
|
</div>
|
|
<div class="flex items-center gap-4 mb-4">
|
|
<div class="flex items-center gap-1.5">
|
|
<div class="w-4 h-4 rounded-full" style="background:#2d6a4f;"></div>
|
|
<span class="text-xs" style="color:#5a7a67;">Ada diagnosa</span>
|
|
</div>
|
|
<div class="flex items-center gap-1.5">
|
|
<div class="w-4 h-4 rounded-full" style="background:#f0f0f0;"></div>
|
|
<span class="text-xs" style="color:#5a7a67;">Tidak ada diagnosa</span>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-7 gap-1 mb-2">
|
|
@foreach(['Min','Sen','Sel','Rab','Kam','Jum','Sab'] as $day)
|
|
<div class="text-center text-xs font-semibold py-1" style="color:#8fa89a;">{{ $day }}</div>
|
|
@endforeach
|
|
</div>
|
|
<div class="grid grid-cols-7 gap-1" id="calendarGrid"></div>
|
|
<div id="selectedDayInfo" class="mt-4 hidden p-3 rounded-xl text-sm" style="background:#f0fdf4;color:#2d6a4f;border:1px solid #b7ddc4;"></div>
|
|
<button onclick="closeCalendar()"
|
|
class="w-full mt-4 py-2.5 rounded-xl text-sm font-semibold"
|
|
style="background:#f0fdf4;color:#2d6a4f;border:1.5px solid #b7ddc4;">
|
|
Tutup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ─── Modal Akurasi ─── -->
|
|
<div id="accuracyModal" class="fixed inset-0 z-50 hidden flex items-center justify-center"
|
|
style="background:rgba(0,0,0,0.4);">
|
|
<div class="bg-white rounded-2xl p-6 w-full max-w-lg mx-4 shadow-2xl"
|
|
style="animation: popIn .25s cubic-bezier(0.22,1,0.36,1);">
|
|
<div class="flex items-center justify-between mb-5">
|
|
<div>
|
|
<h3 class="text-lg font-bold" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Grafik Akurasi</h3>
|
|
<p class="text-sm" style="color:#5a7a67;">Rata-rata akurasi per bulan tahun {{ date('Y') }}</p>
|
|
</div>
|
|
<button onclick="closeAccuracyModal()"
|
|
class="w-8 h-8 rounded-full flex items-center justify-center transition"
|
|
style="background:#f0e8ff;color:#7c3aed;"
|
|
onmouseover="this.style.background='#e9d5ff'" onmouseout="this.style.background='#f0e8ff'">
|
|
<i class="fas fa-times text-sm"></i>
|
|
</button>
|
|
</div>
|
|
<canvas id="accuracyModalChart" height="160"></canvas>
|
|
<div class="mt-4 p-3 rounded-xl text-sm text-center font-semibold" style="background:#f0e8ff;color:#7c3aed;">
|
|
Akurasi rata-rata keseluruhan: {{ $avgAccuracy }}%
|
|
</div>
|
|
<button onclick="closeAccuracyModal()"
|
|
class="w-full mt-4 py-2.5 rounded-xl text-sm font-semibold"
|
|
style="background:#f0e8ff;color:#7c3aed;border:1.5px solid #c4b5fd;">
|
|
Tutup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
const monthlyRaw = [
|
|
@foreach(range(1, 12) as $month)
|
|
{{ $monthlyData->where('month', $month)->first()?->count ?? 0 }},
|
|
@endforeach
|
|
];
|
|
const monthlyAccuracy = @json($monthlyAccuracy);
|
|
const monthLabels = ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Agu','Sep','Okt','Nov','Des'];
|
|
|
|
const ctx = document.getElementById('diagnosisChart').getContext('2d');
|
|
new Chart(ctx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: monthLabels,
|
|
datasets: [{
|
|
label: 'Jumlah Diagnosa',
|
|
data: monthlyRaw,
|
|
backgroundColor: 'rgba(64,145,108,0.25)',
|
|
borderColor: 'rgba(45,106,79,1)',
|
|
borderWidth: 2,
|
|
borderRadius: 6,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: { beginAtZero: true, ticks: { stepSize: 1, color: '#5a7a67' }, grid: { color: 'rgba(237,232,223,.6)' } },
|
|
x: { ticks: { color: '#5a7a67', font: { size: 10 } }, grid: { display: false } }
|
|
},
|
|
plugins: { legend: { labels: { color: '#1a3a2a', font: { size: 11 } } } }
|
|
}
|
|
});
|
|
|
|
let totalChartInstance = null;
|
|
function openTotalModal() {
|
|
document.getElementById('totalModal').classList.remove('hidden');
|
|
if (totalChartInstance) totalChartInstance.destroy();
|
|
const ctx2 = document.getElementById('totalModalChart').getContext('2d');
|
|
totalChartInstance = new Chart(ctx2, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: monthLabels,
|
|
datasets: [{
|
|
label: 'Jumlah Diagnosa',
|
|
data: monthlyRaw,
|
|
backgroundColor: 'rgba(64,145,108,0.25)',
|
|
borderColor: 'rgba(45,106,79,1)',
|
|
borderWidth: 2,
|
|
borderRadius: 6,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
scales: {
|
|
y: { beginAtZero: true, ticks: { stepSize: 1, color: '#5a7a67' }, grid: { color: 'rgba(237,232,223,.6)' } },
|
|
x: { ticks: { color: '#5a7a67' }, grid: { display: false } }
|
|
},
|
|
plugins: { legend: { labels: { color: '#1a3a2a' } } }
|
|
}
|
|
});
|
|
}
|
|
function closeTotalModal() { document.getElementById('totalModal').classList.add('hidden'); }
|
|
document.getElementById('totalModal').addEventListener('click', function(e) { if (e.target === this) closeTotalModal(); });
|
|
|
|
const diagnosaDates = @json($diagnosaDates);
|
|
const today = {{ date('j') }};
|
|
const currentMonth = {{ date('n') }};
|
|
const currentYear = {{ date('Y') }};
|
|
const bulanNama = ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'];
|
|
|
|
function openCalendar() {
|
|
document.getElementById('calendarMonthYear').textContent = bulanNama[currentMonth - 1] + ' ' + currentYear;
|
|
buildCalendar();
|
|
document.getElementById('calendarModal').classList.remove('hidden');
|
|
document.getElementById('selectedDayInfo').classList.add('hidden');
|
|
}
|
|
function closeCalendar() { document.getElementById('calendarModal').classList.add('hidden'); }
|
|
document.getElementById('calendarModal').addEventListener('click', function(e) { if (e.target === this) closeCalendar(); });
|
|
|
|
function buildCalendar() {
|
|
const grid = document.getElementById('calendarGrid');
|
|
grid.innerHTML = '';
|
|
const firstDay = new Date(currentYear, currentMonth - 1, 1).getDay();
|
|
const daysInMonth = new Date(currentYear, currentMonth, 0).getDate();
|
|
for (let i = 0; i < firstDay; i++) grid.innerHTML += `<div></div>`;
|
|
for (let d = 1; d <= daysInMonth; d++) {
|
|
const hasDiagnosa = diagnosaDates[d] !== undefined;
|
|
const isToday = d === today;
|
|
const count = diagnosaDates[d] || 0;
|
|
let bg = 'transparent', color = '#1a3a2a', fw = '400', ring = '';
|
|
if (hasDiagnosa) { bg = '#2d6a4f'; color = '#fff'; fw = '700'; }
|
|
if (isToday && !hasDiagnosa) { ring = 'border:2px solid #2563eb;'; color = '#2563eb'; fw = '600'; }
|
|
if (isToday && hasDiagnosa) { ring = 'border:2px solid #1a3a2a;'; }
|
|
grid.innerHTML += `
|
|
<div onclick="showDayInfo(${d}, ${count})"
|
|
class="relative flex items-center justify-center rounded-full text-xs transition cursor-pointer hover:opacity-80"
|
|
style="width:32px;height:32px;background:${bg};color:${color};font-weight:${fw};${ring}margin:auto;">
|
|
${d}
|
|
${hasDiagnosa ? `<span class="absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full" style="background:#74c69d;border:1px solid white;"></span>` : ''}
|
|
</div>`;
|
|
}
|
|
}
|
|
|
|
function showDayInfo(day, count) {
|
|
const info = document.getElementById('selectedDayInfo');
|
|
if (count > 0) {
|
|
info.innerHTML = `<i class="fas fa-check-circle mr-2"></i><strong>${day} ${bulanNama[currentMonth-1]} ${currentYear}</strong>: ${count} diagnosa dilakukan`;
|
|
info.style.cssText = 'background:#f0fdf4;color:#2d6a4f;border:1px solid #b7ddc4;padding:12px;border-radius:12px;';
|
|
} else {
|
|
info.innerHTML = `<i class="fas fa-info-circle mr-2"></i><strong>${day} ${bulanNama[currentMonth-1]} ${currentYear}</strong>: Tidak ada diagnosa`;
|
|
info.style.cssText = 'background:#f8f4ee;color:#8fa89a;border:1px solid #ede8df;padding:12px;border-radius:12px;';
|
|
}
|
|
info.classList.remove('hidden');
|
|
}
|
|
|
|
let accuracyChartInstance = null;
|
|
function openAccuracyModal() {
|
|
document.getElementById('accuracyModal').classList.remove('hidden');
|
|
if (accuracyChartInstance) accuracyChartInstance.destroy();
|
|
const ctx3 = document.getElementById('accuracyModalChart').getContext('2d');
|
|
accuracyChartInstance = new Chart(ctx3, {
|
|
type: 'line',
|
|
data: {
|
|
labels: monthLabels,
|
|
datasets: [{
|
|
label: 'Akurasi (%)',
|
|
data: monthlyAccuracy,
|
|
borderColor: 'rgba(124,58,237,1)',
|
|
backgroundColor: 'rgba(124,58,237,0.1)',
|
|
borderWidth: 2,
|
|
pointBackgroundColor: 'rgba(124,58,237,1)',
|
|
pointRadius: 4,
|
|
tension: 0.4,
|
|
fill: true,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
scales: {
|
|
y: { beginAtZero: false, min: 0, max: 100, ticks: { color: '#5a7a67', callback: v => v + '%' }, grid: { color: 'rgba(237,232,223,.6)' } },
|
|
x: { ticks: { color: '#5a7a67' }, grid: { display: false } }
|
|
},
|
|
plugins: { legend: { labels: { color: '#1a3a2a' } } }
|
|
}
|
|
});
|
|
}
|
|
function closeAccuracyModal() { document.getElementById('accuracyModal').classList.add('hidden'); }
|
|
document.getElementById('accuracyModal').addEventListener('click', function(e) { if (e.target === this) closeAccuracyModal(); });
|
|
|
|
/* ============================================================
|
|
GUIDED TOUR — SiPakarTebu
|
|
Otomatis muncul untuk pengguna baru (belum pernah lihat tour)
|
|
============================================================ */
|
|
const tourSteps = [
|
|
{
|
|
title: 'Selamat datang di SiPakarTebu! 🌱',
|
|
desc: 'Ini panduan singkat supaya kamu langsung paham cara pakai aplikasi ini. Klik <b>Lanjut</b> untuk mulai!',
|
|
target: null,
|
|
},
|
|
{
|
|
title: 'Menu Navigasi',
|
|
desc: 'Di sini ada semua menu: <b>Dashboard</b>, <b>Diagnosa</b>, <b>Riwayat</b>, <b>Kamus</b>, dan <b>Profil</b>. Klik menu untuk berpindah halaman.',
|
|
targetSel: 'nav#sidebar, aside, [class*="sidebar"], nav',
|
|
pos: 'right',
|
|
},
|
|
{
|
|
title: 'Kartu Statistik',
|
|
desc: 'Di sini kamu bisa lihat <b>total diagnosa</b>, <b>diagnosa bulan ini</b>, dan <b>akurasi rata-rata</b>. Klik tiap kartu untuk detail lebih lanjut!',
|
|
targetSel: '#tour-stat-cards',
|
|
pos: 'bottom',
|
|
},
|
|
{
|
|
title: 'Grafik Diagnosa',
|
|
desc: 'Grafik batang ini menampilkan jumlah diagnosa yang kamu lakukan setiap bulan sepanjang tahun.',
|
|
targetSel: '#tour-chart',
|
|
pos: 'top',
|
|
},
|
|
{
|
|
title: 'Mulai Diagnosa Baru',
|
|
desc: 'Klik menu <b>Diagnosa</b> di sidebar untuk mengunggah foto tanaman tebu dan mendapatkan hasil diagnosa penyakitnya!',
|
|
targetSel: 'a[href*="diagnosa"], a[href*="diagnosis"]',
|
|
pos: 'right',
|
|
},
|
|
];
|
|
|
|
let _tourStep = 0;
|
|
|
|
function _tourEl(id) { return document.getElementById(id); }
|
|
|
|
function _tourFindTarget(step) {
|
|
if (!step.targetSel) return null;
|
|
const sels = step.targetSel.split(',');
|
|
for (const s of sels) {
|
|
const el = document.querySelector(s.trim());
|
|
if (el) return el;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function _tourStart() {
|
|
_tourStep = 0;
|
|
// Buat elemen tour kalau belum ada
|
|
if (!_tourEl('_tour_hl')) { const d = document.createElement('div'); d.id = '_tour_hl'; document.body.appendChild(d); }
|
|
if (!_tourEl('_tour_tip')) { const d = document.createElement('div'); d.id = '_tour_tip'; document.body.appendChild(d); }
|
|
if (!_tourEl('_tour_dim')) { const d = document.createElement('div'); d.id = '_tour_dim'; document.body.appendChild(d); }
|
|
_tourRender();
|
|
}
|
|
|
|
function _tourRender() {
|
|
const step = tourSteps[_tourStep];
|
|
const total = tourSteps.length;
|
|
const hl = _tourEl('_tour_hl');
|
|
const tip = _tourEl('_tour_tip');
|
|
const dim = _tourEl('_tour_dim');
|
|
const target = _tourFindTarget(step);
|
|
|
|
// === Dim overlay ===
|
|
dim.style.cssText = `
|
|
position:fixed;inset:0;z-index:99990;
|
|
background:rgba(0,0,0,0.52);pointer-events:auto;`;
|
|
|
|
// === Highlight box ===
|
|
if (target) {
|
|
const r = target.getBoundingClientRect();
|
|
const pad = 8;
|
|
hl.style.cssText = `
|
|
position:fixed;z-index:99992;pointer-events:none;
|
|
top:${r.top - pad}px;left:${r.left - pad}px;
|
|
width:${r.width + pad*2}px;height:${r.height + pad*2}px;
|
|
border:2.5px solid #2d6a4f;border-radius:12px;
|
|
box-shadow:0 0 0 9999px rgba(0,0,0,0.52);
|
|
transition:all .35s ease;`;
|
|
dim.style.background = 'transparent';
|
|
} else {
|
|
hl.style.cssText = 'display:none;';
|
|
}
|
|
|
|
// === Dots ===
|
|
const dots = Array.from({length: total}, (_, i) =>
|
|
`<div style="width:${i===_tourStep?16:6}px;height:6px;border-radius:3px;
|
|
background:${i===_tourStep?'#2d6a4f':'#d1d5db'};
|
|
transition:all .3s;display:inline-block;margin-right:4px;"></div>`
|
|
).join('');
|
|
|
|
// === Tooltip posisi ===
|
|
let tipPos = 'top:50%;left:50%;transform:translate(-50%,-50%);';
|
|
if (target) {
|
|
const r = target.getBoundingClientRect();
|
|
const vw = window.innerWidth;
|
|
const vh = window.innerHeight;
|
|
if (step.pos === 'right' && r.right + 310 < vw)
|
|
tipPos = `top:${Math.min(r.top, vh-260)}px;left:${r.right+16}px;transform:none;`;
|
|
else if (step.pos === 'bottom' && r.bottom + 220 < vh)
|
|
tipPos = `top:${r.bottom+16}px;left:${Math.max(16,Math.min(r.left, vw-310))}px;transform:none;`;
|
|
else if (step.pos === 'top' && r.top - 220 > 0)
|
|
tipPos = `top:${r.top-220}px;left:${Math.max(16,Math.min(r.left, vw-310))}px;transform:none;`;
|
|
else
|
|
tipPos = `top:50%;left:50%;transform:translate(-50%,-50%);`;
|
|
}
|
|
|
|
tip.style.cssText = `
|
|
position:fixed;${tipPos}
|
|
z-index:99999;background:#fff;border-radius:16px;
|
|
padding:22px 24px;width:290px;
|
|
box-shadow:0 12px 40px rgba(0,0,0,0.18);
|
|
font-family:inherit;pointer-events:auto;`;
|
|
|
|
tip.innerHTML = `
|
|
<div style="font-size:11px;color:#2d6a4f;font-weight:700;text-transform:uppercase;
|
|
letter-spacing:.8px;margin-bottom:6px;">
|
|
Langkah ${_tourStep+1} dari ${total}
|
|
</div>
|
|
<div style="font-size:15px;font-weight:700;color:#1a3a2a;margin-bottom:8px;
|
|
font-family:'Playfair Display',serif;">
|
|
${step.title}
|
|
</div>
|
|
<div style="font-size:13px;color:#5a7a67;line-height:1.65;margin-bottom:18px;">
|
|
${step.desc}
|
|
</div>
|
|
<div style="display:flex;align-items:center;justify-content:space-between;">
|
|
<div>${dots}</div>
|
|
<div style="display:flex;gap:10px;align-items:center;">
|
|
<button onclick="_tourEnd()"
|
|
style="background:none;border:none;font-size:12px;color:#9ca3af;cursor:pointer;padding:4px;">
|
|
Lewati
|
|
</button>
|
|
<button onclick="_tourNext()"
|
|
style="background:#2d6a4f;color:#fff;border:none;
|
|
padding:9px 18px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;">
|
|
${_tourStep === total-1 ? 'Selesai ✓' : 'Lanjut →'}
|
|
</button>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
function _tourNext() {
|
|
_tourStep++;
|
|
if (_tourStep >= tourSteps.length) { _tourEnd(); return; }
|
|
_tourRender();
|
|
}
|
|
|
|
// Key khusus per user
|
|
const TOUR_KEY = 'sipakartebu_tour_done_{{ auth()->id() }}';
|
|
|
|
function _tourEnd() {
|
|
['_tour_hl','_tour_tip','_tour_dim'].forEach(id => {
|
|
const el = _tourEl(id);
|
|
if (el) el.remove();
|
|
});
|
|
|
|
localStorage.setItem(TOUR_KEY, '1');
|
|
}
|
|
|
|
// Auto-mulai untuk pengguna baru
|
|
window.addEventListener('load', function () {
|
|
if (!localStorage.getItem(TOUR_KEY)) {
|
|
setTimeout(_tourStart, 900);
|
|
}
|
|
});
|
|
|
|
// Fungsi ulangi tour
|
|
function ulangiTour() {
|
|
localStorage.removeItem(TOUR_KEY);
|
|
_tourStart();
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
@keyframes popIn {
|
|
from { transform: scale(0.85); opacity: 0; }
|
|
to { transform: scale(1); opacity: 1; }
|
|
}
|
|
</style>
|
|
@endpush |