MIF_E31232094/resources/views/diagnosis/create.blade.php

485 lines
20 KiB
PHP

@extends('layouts.app')
@section('title', 'Diagnosa Penyakit')
@section('page-title', 'Diagnosa Penyakit')
@section('content')
<div class="max-w-2xl mx-auto">
<div id="tour-form-card" class="bg-white p-6 rounded-2xl border border-[#ede8df]" style="box-shadow:0 4px 16px rgba(26,58,42,.08);">
{{-- Progress bar --}}
<div id="tour-progress" class="mb-6">
<div class="flex justify-between text-xs font-semibold mb-2" style="color:#5a7a67;">
<span>Step <span id="step-number">1</span> dari 3</span>
<span id="step-label">Pilih Gejala</span>
</div>
<div class="w-full h-2 rounded-full" style="background:#ede8df;">
<div id="progress-bar" class="h-2 rounded-full transition-all duration-300" style="background:linear-gradient(90deg,#40916c,#74c69d);width:33%;"></div>
</div>
</div>
<form method="POST" action="{{ route('diagnosis.store') }}">
@csrf
{{-- STEP 1: Pilih Gejala --}}
<div id="step1">
<h3 class="text-lg font-bold mb-1" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Pilih Gejala</h3>
<p class="text-xs mb-4" style="color:#8fa89a;">Pilih minimal 4 gejala yang sesuai dengan kondisi tanaman</p>
<input type="text" id="search" placeholder="Cari gejala..."
class="w-full mb-4 px-4 py-2.5 border rounded-xl text-sm"
style="border-color:#ede8df;outline:none;color:#1a3a2a;">
<div id="tour-symptom-list" class="space-y-2 overflow-y-auto pr-1" style="max-height:380px;">
@foreach($symptoms as $symptom)
<label class="symptom-item flex items-center gap-3 p-3 rounded-xl cursor-pointer border transition"
style="border-color:#ede8df;background:white;">
<input type="checkbox"
class="symptom-checkbox w-4 h-4 accent-green-700"
data-code="{{ $symptom->code }}"
onchange="updateCount(); updateCheckStyle(this)">
<span>
<span class="text-xs font-bold px-1.5 py-0.5 rounded mr-1" style="background:#d8f3dc;color:#2d6a4f;">{{ $symptom->code }}</span>
<span class="text-sm" style="color:#1a3a2a;">{{ $symptom->name }}</span>
</span>
</label>
@endforeach
</div>
<div class="mt-4 flex items-center justify-between">
<span class="text-sm" style="color:#5a7a67;">
Dipilih: <strong id="count" style="color:#2d6a4f;">0</strong> gejala
<span id="count-warning" class="text-xs ml-1" style="color:#dc2626;display:none;">(minimal 4)</span>
</span>
</div>
<button type="button" onclick="nextStep()" id="tour-next-btn"
class="mt-4 w-full py-3 rounded-xl text-sm font-semibold text-white transition"
style="background:#1a3a2a;"
onmouseover="this.style.background='#2d6a4f'" onmouseout="this.style.background='#1a3a2a'">
Lanjut
</button>
</div>
{{-- STEP 2: Tingkat Keyakinan --}}
<div id="step2" class="hidden">
<h3 class="text-lg font-bold mb-1" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Tingkat Keyakinan</h3>
<p class="text-xs mb-5" style="color:#8fa89a;">Seberapa yakin kamu melihat gejala ini pada tanaman?</p>
<div id="cf-container" class="space-y-4"></div>
<div class="flex gap-3 mt-6">
<button type="button" onclick="prevStep()"
class="px-5 py-3 rounded-xl text-sm font-semibold border transition"
style="border-color:#ede8df;color:#5a7a67;background:white;"
onmouseover="this.style.background='#f8f4ee'" onmouseout="this.style.background='white'">
Kembali
</button>
<button type="button" onclick="nextStep()"
class="flex-1 py-3 rounded-xl text-sm font-semibold text-white transition"
style="background:#1a3a2a;"
onmouseover="this.style.background='#2d6a4f'" onmouseout="this.style.background='#1a3a2a'">
Lanjut
</button>
</div>
</div>
{{-- STEP 3: Konfirmasi --}}
<div id="step3" class="hidden">
<h3 class="text-lg font-bold mb-1" style="font-family:'Playfair Display',serif;color:#1a3a2a;">Konfirmasi</h3>
<p class="text-xs mb-5" style="color:#8fa89a;">Periksa kembali sebelum diagnosa dimulai</p>
<div id="summary" class="space-y-2 mb-6"></div>
<div class="flex gap-3">
<button type="button" onclick="prevStep()"
class="px-5 py-3 rounded-xl text-sm font-semibold border transition"
style="border-color:#ede8df;color:#5a7a67;background:white;"
onmouseover="this.style.background='#f8f4ee'" onmouseout="this.style.background='white'">
Kembali
</button>
<button type="submit"
class="flex-1 py-3 rounded-xl text-sm font-semibold text-white transition"
style="background:#1a3a2a;"
onmouseover="this.style.background='#2d6a4f'" onmouseout="this.style.background='#1a3a2a'">
Diagnosa Sekarang
</button>
</div>
</div>
</form>
</div>
</div>
<script>
let step = 1;
const stepLabels = ['', 'Pilih Gejala', 'Tingkat Keyakinan', 'Konfirmasi'];
const progressWidth = ['', '33%', '66%', '100%'];
function updateCount() {
const checked = document.querySelectorAll('.symptom-checkbox:checked').length;
document.getElementById('count').innerText = checked;
document.getElementById('count-warning').style.display = checked < 4 ? 'inline' : 'none';
}
function updateCheckStyle(cb) {
const label = cb.closest('label');
if (cb.checked) {
label.style.background = '#f0fdf4';
label.style.borderColor = '#b7ddc4';
} else {
label.style.background = 'white';
label.style.borderColor = '#ede8df';
}
}
function nextStep() {
if (step === 1) {
const count = document.querySelectorAll('.symptom-checkbox:checked').length;
if (count < 4) {
document.getElementById('count-warning').style.display = 'inline';
return;
}
buildCF();
}
// ── VALIDASI: blokir kalau semua CF = 0.0 ──
if (step === 2) {
const checked = document.querySelectorAll('.symptom-checkbox:checked');
let semuaTidakYakin = true;
checked.forEach(cb => {
const kode = cb.getAttribute('data-code');
const val = document.getElementById('cf-val-' + kode)?.value;
if (val && parseFloat(val) > 0) semuaTidakYakin = false;
});
if (semuaTidakYakin) {
showCFWarning();
return;
}
buildSummary();
}
document.getElementById('step' + step).classList.add('hidden');
step++;
document.getElementById('step' + step).classList.remove('hidden');
document.getElementById('step-number').innerText = step;
document.getElementById('step-label').innerText = stepLabels[step];
document.getElementById('progress-bar').style.width = progressWidth[step];
}
function prevStep() {
document.getElementById('step' + step).classList.add('hidden');
step--;
document.getElementById('step' + step).classList.remove('hidden');
document.getElementById('step-number').innerText = step;
document.getElementById('step-label').innerText = stepLabels[step];
document.getElementById('progress-bar').style.width = progressWidth[step];
}
const cfOptions = [
{ val: '0.8', label: 'Sangat Yakin' },
{ val: '0.6', label: 'Yakin' },
{ val: '0.4', label: 'Cukup Yakin' },
{ val: '0.2', label: 'Kurang Yakin' },
{ val: '0.0', label: 'Tidak Yakin' },
];
function buildCF() {
const container = document.getElementById('cf-container');
container.innerHTML = '';
document.querySelectorAll('.symptom-checkbox:checked').forEach(cb => {
const label = cb.closest('label').innerText.trim();
const kode = cb.getAttribute('data-code');
const buttons = cfOptions.map(opt => `
<button type="button"
onclick="selectCF(this, '${kode}', '${opt.val}')"
data-val="${opt.val}"
class="cf-btn-${kode} py-2 px-1 rounded-xl border-2 text-xs font-semibold transition text-center"
style="border-color:#ede8df;color:#5a7a67;background:white;">
${opt.label}
</button>
`).join('');
container.innerHTML += `
<div class="p-4 rounded-xl border border-[#ede8df]" style="background:#fafaf8;">
<p class="text-sm font-semibold mb-3" style="color:#1a3a2a;">${label}</p>
<input type="hidden" name="symptoms[${kode}]" id="cf-val-${kode}" value="0.8">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;">${buttons}</div>
</div>
`;
});
// Set default "Sangat Yakin" terpilih
document.querySelectorAll('.symptom-checkbox:checked').forEach(cb => {
const kode = cb.getAttribute('data-code');
const defaultBtn = document.querySelector(`.cf-btn-${kode}[data-val="0.8"]`);
if (defaultBtn) {
setSelectedStyle(defaultBtn);
document.getElementById(`cf-val-${kode}`).value = '0.8';
}
});
}
function selectCF(btn, kode, val) {
document.querySelectorAll(`.cf-btn-${kode}`).forEach(b => {
b.style.borderColor = '#ede8df';
b.style.background = 'white';
b.style.color = '#5a7a67';
});
setSelectedStyle(btn);
document.getElementById(`cf-val-${kode}`).value = val;
}
function setSelectedStyle(btn) {
btn.style.borderColor = '#2d6a4f';
btn.style.background = '#d8f3dc';
btn.style.color = '#1a3a2a';
}
function buildSummary() {
const summary = document.getElementById('summary');
summary.innerHTML = '';
const cfMap = { '0.8':'Sangat Yakin','0.6':'Yakin','0.4':'Cukup Yakin','0.2':'Kurang Yakin','0.0':'Tidak Yakin' };
document.querySelectorAll('.symptom-checkbox:checked').forEach(cb => {
const label = cb.closest('label').innerText.trim();
const kode = cb.getAttribute('data-code');
const val = document.getElementById(`cf-val-${kode}`).value;
summary.innerHTML += `
<div class="flex items-center justify-between p-3 rounded-xl" style="background:#f0fdf4;border:1px solid #b7ddc4;">
<span class="text-sm" style="color:#1a3a2a;">${label}</span>
<span class="text-xs font-semibold px-2 py-1 rounded-full ml-2 flex-shrink-0" style="background:#d8f3dc;color:#2d6a4f;">${cfMap[val] ?? val}</span>
</div>
`;
});
}
document.getElementById('search').addEventListener('input', function () {
const keyword = this.value.toLowerCase();
document.querySelectorAll('.symptom-item').forEach(item => {
item.style.display = item.innerText.toLowerCase().includes(keyword) ? '' : 'none';
});
});
/* ── Popup warning CF semua 0.0 ── */
function showCFWarning() {
if (document.getElementById('cf-warn-overlay')) return;
const overlay = document.createElement('div');
overlay.id = 'cf-warn-overlay';
overlay.style.cssText = `
position:fixed;inset:0;z-index:99999;
background:rgba(15,30,20,.55);backdrop-filter:blur(4px);
display:flex;align-items:center;justify-content:center;padding:1rem;
`;
overlay.innerHTML = `
<div style="
background:#fff;border-radius:20px;padding:2rem 1.75rem;
max-width:360px;width:100%;text-align:center;
box-shadow:0 24px 60px rgba(10,25,15,.3);
animation:warnPop .3s cubic-bezier(.22,.97,.44,1) both;
">
<div style="font-size:2.5rem;margin-bottom:.75rem;">⚠️</div>
<h3 style="
font-family:'Playfair Display',serif;
font-size:1.1rem;color:#1a3a2a;margin-bottom:.5rem;
">Tingkat Keyakinan Terlalu Rendah</h3>
<p style="font-size:.875rem;color:#5a7a67;line-height:1.6;margin-bottom:1.5rem;">
Kamu memilih <strong>Tidak Yakin</strong> untuk semua gejala.<br>
Minimal satu gejala harus memiliki tingkat keyakinan di atas <em>Tidak Yakin</em>
agar diagnosa dapat diproses.
</p>
<button onclick="closeCFWarning()"
style="
width:100%;padding:.8rem;border-radius:12px;
background:#1a3a2a;color:white;border:none;
font-family:'DM Sans',sans-serif;font-size:.9rem;
font-weight:600;cursor:pointer;
"
onmouseover="this.style.background='#2d6a4f'"
onmouseout="this.style.background='#1a3a2a'">
Oke, Saya Ubah
</button>
</div>
<style>
@keyframes warnPop {
from { opacity:0; transform:scale(.92) translateY(16px); }
to { opacity:1; transform:none; }
}
</style>
`;
document.body.appendChild(overlay);
overlay.addEventListener('click', function(e) {
if (e.target === overlay) closeCFWarning();
});
}
function closeCFWarning() {
const el = document.getElementById('cf-warn-overlay');
if (el) el.remove();
}
/* ============================================================
GUIDED TOUR — Halaman Diagnosa
============================================================ */
const diagTourSteps = [
{
title: 'Halaman Diagnosa 🌿',
desc: 'Di sini kamu bisa mendiagnosa penyakit tanaman tebumu. Ada <b>3 langkah</b> yang perlu kamu ikuti.',
target: null,
},
{
title: 'Progress Bar',
desc: 'Bar ini menunjukkan kamu sedang di langkah berapa. Total ada <b>3 langkah</b>: Pilih Gejala → Tingkat Keyakinan → Konfirmasi.',
targetSel: '#tour-progress',
pos: 'bottom',
},
{
title: 'Daftar Gejala',
desc: '<b>Centang minimal 4 gejala</b> yang kamu temukan pada tanaman tebu. Bisa juga cari gejala pakai kolom pencarian di atas.',
targetSel: '#tour-symptom-list',
pos: 'top',
},
{
title: 'Tombol Lanjut',
desc: 'Setelah memilih minimal 4 gejala, klik tombol ini untuk ke langkah berikutnya yaitu mengisi <b>tingkat keyakinan</b> tiap gejala.',
targetSel: '#tour-next-btn',
pos: 'top',
offsetY: -120
},
];
let _dTourStep = 0;
function _dTourEl(id) { return document.getElementById(id); }
function _dTourFindTarget(step) {
if (!step.targetSel) return null;
return document.querySelector(step.targetSel);
}
function _dTourStart() {
_dTourStep = 0;
if (!_dTourEl('_dtour_hl')) { const d = document.createElement('div'); d.id = '_dtour_hl'; document.body.appendChild(d); }
if (!_dTourEl('_dtour_tip')) { const d = document.createElement('div'); d.id = '_dtour_tip'; document.body.appendChild(d); }
if (!_dTourEl('_dtour_dim')) { const d = document.createElement('div'); d.id = '_dtour_dim'; document.body.appendChild(d); }
_dTourRender();
}
function _dTourRender() {
const step = diagTourSteps[_dTourStep];
const total = diagTourSteps.length;
const hl = _dTourEl('_dtour_hl');
const tip = _dTourEl('_dtour_tip');
const dim = _dTourEl('_dtour_dim');
const target = _dTourFindTarget(step);
dim.style.cssText = `position:fixed;inset:0;z-index:99990;background:rgba(0,0,0,0.52);pointer-events:auto;`;
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;';
}
const dots = Array.from({length:total},(_,i)=>
`<div style="width:${i===_dTourStep?16:6}px;height:6px;border-radius:3px;
background:${i===_dTourStep?'#2d6a4f':'#d1d5db'};
transition:all .3s;display:inline-block;margin-right:4px;"></div>`
).join('');
let tipPos = 'top:50%;left:50%;transform:translate(-50%,-50%);';
if (target) {
const r = target.getBoundingClientRect();
const vw = window.innerWidth;
const vh = window.innerHeight;
const tipH = 340;
const tipW = 310;
if (step.pos === 'bottom' && r.bottom + tipH < vh) {
tipPos = `top:${r.bottom + 16}px;left:${Math.max(16, Math.min(r.left, vw - tipW))}px;transform:none;`;
} else if (step.pos === 'top') {
let topPos = r.top - tipH - 20 + (step.offsetY || 0);
if (topPos < 20) topPos = 20;
tipPos = `top:${topPos}px;left:${Math.max(16, Math.min(r.left, vw - tipW))}px;transform:none;`;
} else if (step.pos === 'right' && r.right + tipW < vw) {
tipPos = `top:${Math.min(r.top, vh - tipH - 20)}px;left:${r.right + 16}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 ${_dTourStep+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="_dTourEnd()"
style="background:none;border:none;font-size:12px;color:#9ca3af;cursor:pointer;padding:4px;">
Lewati
</button>
<button onclick="_dTourNext()"
style="background:#2d6a4f;color:#fff;border:none;
padding:9px 18px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;">
${_dTourStep === total-1 ? 'Siap! ✓' : 'Lanjut →'}
</button>
</div>
</div>`;
}
function _dTourNext() {
_dTourStep++;
if (_dTourStep >= diagTourSteps.length) { _dTourEnd(); return; }
_dTourRender();
}
function _dTourEnd() {
['_dtour_hl','_dtour_tip','_dtour_dim'].forEach(id => {
const el = _dTourEl(id);
if (el) el.remove();
});
localStorage.setItem('sipakartebu_diag_tour_done', '1');
}
window.addEventListener('load', function () {
if (!localStorage.getItem('sipakartebu_diag_tour_done')) {
setTimeout(_dTourStart, 800);
}
});
function ulangiTourDiagnosa() {
localStorage.removeItem('sipakartebu_diag_tour_done');
_dTourStart();
}
</script>
@endsection