MIF_E31232094/resources/views/dashboard.blade.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