plantopia-fastapi / index.html
sulthonkaf's picture
Update index.html
f836881 verified
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Plantopia</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;500;700&display=swap" rel="stylesheet" />
<style>
* {
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
background-color: #f9fafb;
display: flex;
justify-content: center;
padding: 2rem;
}
.container {
max-width: 720px;
width: 100%;
background: #fff;
border-radius: 20px;
padding: 2rem;
text-align: center;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.05);
}
h1 {
font-size: 1.8rem;
font-weight: 700;
color: #1a202c;
}
p.subtitle {
color: #4a5568;
margin-bottom: 2rem;
}
#dropArea {
border: 2px dashed #38a169;
border-radius: 12px;
padding: 2.5rem 1rem;
margin-bottom: 1.5rem;
cursor: pointer;
transition: background 0.3s ease;
}
#dropArea.dragover {
background-color: #e6fffa;
}
#dropArea img {
width: 48px;
margin-bottom: 1rem;
}
#dropArea p {
margin: 0;
color: #2d3748;
}
input[type="file"] {
display: none;
}
.buttons {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 1rem;
}
.btn-upload {
background-color: #2f855a;
color: white;
}
.btn-predict {
background-color: #edf2f7;
color: #2d3748;
}
.buttons button {
padding: 0.6rem 1.5rem;
border: none;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.buttons button:hover {
opacity: 0.85;
}
#preview {
margin-top: 1rem;
border-radius: 12px;
max-width: 100%;
display: none;
}
.result {
margin-top: 1rem;
font-weight: bold;
}
.steps {
margin-top: 2rem;
display: flex;
justify-content: space-between;
gap: 1rem;
background: #f7fafc;
border-radius: 16px;
padding: 1rem;
flex-wrap: wrap;
}
.step {
flex: 1;
min-width: 180px;
text-align: center;
}
.step img {
height: 40px;
margin-bottom: 0.5rem;
}
.step p {
font-size: 0.9rem;
color: #4a5568;
}
</style>
</head>
<body>
<div class="container">
<h1>“Deteksi Sekarang, Selamatkan Tanamanmu!”</h1>
<p class="subtitle">
Unggah foto atau gunakan kamera untuk mendeteksi penyakit tanamanmu secara instan. Cepat, akurat, dan mudah digunakan.
</p>
<input type="file" id="fileInput" accept="image/*" />
<div id="dropArea">
<img src="https://img.icons8.com/ios-filled/100/upload.png" alt="upload" />
<p>atau upload gambar <strong>lewat sini</strong></p>
</div>
<div class="buttons">
<button class="btn-upload" onclick="fileInput.click()">Unggah</button>
<button class="btn-predict" onclick="submitImage()">Prediksi</button>
</div>
<div class="result" id="result"></div>
<img id="preview" />
<div class="steps">
<div class="step">
<img src="https://img.icons8.com/ios/100/leaf.png" alt="leaf" />
<p>Ambil foto daun atau bagian tanaman yang menunjukkan gejala.</p>
</div>
<div class="step">
<img src="https://img.icons8.com/ios/100/artificial-intelligence.png" alt="ai" />
<p>Sistem kami akan menganalisis gambar secara otomatis.</p>
</div>
<div class="step">
<img src="https://img.icons8.com/ios/100/stethoscope.png" alt="treatment" />
<p>Lihat jenis penyakit dan cara mengatasinya.</p>
</div>
</div>
</div>
<script>
const dropArea = document.getElementById("dropArea");
const fileInput = document.getElementById("fileInput");
const preview = document.getElementById("preview");
const resultDiv = document.getElementById("result");
let selectedFile = null; // ✅ Global file tracker
dropArea.addEventListener("click", () => fileInput.click());
dropArea.addEventListener("dragover", (e) => {
e.preventDefault();
dropArea.classList.add("dragover");
});
dropArea.addEventListener("dragleave", () => {
dropArea.classList.remove("dragover");
});
dropArea.addEventListener("drop", (e) => {
e.preventDefault();
dropArea.classList.remove("dragover");
const file = e.dataTransfer.files[0];
handleFile(file);
});
fileInput.addEventListener("change", () => {
const file = fileInput.files[0];
handleFile(file);
});
function handleFile(file) {
if (!file || !file.type.startsWith("image/")) {
alert("Silakan pilih gambar yang valid.");
return;
}
selectedFile = file; // ✅ Track file global
const reader = new FileReader();
reader.onload = () => {
preview.src = reader.result;
preview.style.display = "block";
resultDiv.innerHTML = "✅ Gambar berhasil dimuat. Siap untuk diproses.";
};
reader.readAsDataURL(file);
}
function submitImage() {
if (!selectedFile) {
alert("Silakan unggah gambar terlebih dahulu.");
return;
}
resultDiv.innerHTML = "⏳ Mengirim gambar untuk prediksi...";
const formData = new FormData();
formData.append("file", selectedFile);
fetch("http://localhost:8000/predict", {
method: "POST",
body: formData
})
.then((res) => res.json())
.then((data) => {
resultDiv.innerHTML = `
<p><strong>${data.predicted_label}</strong> (${data.confidence.toFixed(2)}%)</p>
<img src="${preview.src}" style="max-width:100%; border-radius:10px; margin-top:1rem;" />
`;
})
.catch((err) => {
resultDiv.innerHTML = "❌ Gagal memproses gambar.";
console.error(err);
});
}
</script>
</body>
</html>