<!DOCTYPE html>
<html lang="ms">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Restoran Embok Lily - Pesanan Digital</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;700;800&display=swap');
body { font-family: 'Plus Jakarta Sans', sans-serif; scroll-behavior: smooth; background-color: #fcfcfc; }
.admin-only { display: none; }
body.admin-mode .admin-only { display: block; }
body.admin-mode .customer-only { display: none; }
.modal-animate { animation: fadeIn 0.25s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
</style>
</head>
<body class="pb-32 text-gray-900" id="body-root">
<script>
const DEFAULT_CONFIG = {
nama: "Restoran Embok Lily",
tagline: "Rasa Tradisional, Pesanan Digital",
logoUrl: "https://via.placeholder.com/150?text=LOGO",
whatsappAdmin: "60123456789",
alamat: "No. 12, Jalan Utama, Bandar Baru, 80000 Johor Bahru",
duitnowQr: "",
menu: [
{
kategori: "Makanan Utama",
items: [
{ id: 1, nama: "Nasi Ayam Penyet", harga: 12.50, options: "Asing Sambal, Asing Kuah, Extra Pedas", img: "" }
]
}
]
};
let KEDAI_CONFIG = JSON.parse(localStorage.getItem('embok_lily_v2')) || DEFAULT_CONFIG;
let cart = [];
let currentEditIndices = { cat: -1, item: -1 };
let uploadedImageBase64 = "";
function persistData() {
localStorage.setItem('embok_lily_v2', JSON.stringify(KEDAI_CONFIG));
}
</script>
<!-- Header -->
<header class="bg-white/80 backdrop-blur-md border-b sticky top-0 z-50 p-4 shadow-sm text-left">
<div class="max-w-md mx-auto flex items-center justify-between">
<div class="flex items-center gap-4">
<img id="display-logo" src="" alt="Logo" class="w-11 h-11 rounded-full object-cover border-2 border-red-50">
<div>
<h1 id="display-name" class="font-bold text-gray-900 text-sm tracking-tight leading-none mb-1"></h1>
<p id="display-tagline" class="text-[9px] text-gray-400 font-bold uppercase tracking-widest"></p>
</div>
</div>
<button onclick="toggleAdminLogin(true)" id="admin-btn" class="bg-gray-100 text-gray-400 px-3 py-1.5 rounded-lg text-[9px] font-black border uppercase">Admin</button>
</div>
</header>
<!-- Admin Panel -->
<div id="admin-panel" class="admin-only max-w-md mx-auto p-4 bg-yellow-50 border-b border-yellow-200">
<div class="flex justify-between items-center mb-5">
<h2 class="font-black text-yellow-800 text-[10px] uppercase">🛠️ Kawalan Restoran</h2>
<button onclick="exitAdmin()" class="text-[9px] bg-yellow-600 px-5 py-2 rounded-xl text-white font-black uppercase shadow-sm">Simpan</button>
</div>
<div class="space-y-4 bg-white p-6 rounded-[2.5rem] shadow-sm border border-yellow-100 max-h-[70vh] overflow-y-auto text-left">
<div class="flex flex-col items-center gap-2 mb-4">
<img id="edit-logo-preview" src="" class="w-16 h-16 rounded-full object-cover border-2 border-yellow-200">
<label class="bg-yellow-50 text-yellow-700 px-4 py-1.5 rounded-xl text-[9px] font-black border border-yellow-200 cursor-pointer">
Tukar Logo
<input type="file" class="hidden" accept="image/*" onchange="handleLogoUpload(this)">
</label>
</div>
<input type="text" id="edit-name" oninput="updateShopInfo()" class="w-full border-gray-100 border bg-gray-50 rounded-xl p-3 text-sm font-bold" placeholder="Nama Kedai">
<input type="text" id="edit-wa" oninput="updateShopInfo()" class="w-full border-gray-100 border bg-gray-50 rounded-xl p-3 text-sm" placeholder="WhatsApp (601...)">
<textarea id="edit-address" oninput="updateShopInfo()" class="w-full border-gray-100 border bg-gray-50 rounded-xl p-3 text-sm" rows="2" placeholder="Alamat"></textarea>
<p class="text-[9px] font-black text-green-600 uppercase border-b pb-1 mt-4">Upload QR Bayaran</p>
<div class="flex items-center gap-4">
<img id="edit-qr-preview" src="" class="w-16 h-16 rounded-lg border">
<input type="file" class="text-[10px]" accept="image/*" onchange="handleQrUpload(this)">
</div>
</div>
</div>
<!-- Main Content -->
<main class="max-w-md mx-auto p-4">
<div class="bg-red-50 p-6 rounded-[2.8rem] mb-12 flex items-start gap-5 border border-red-100 customer-only shadow-sm">
<div class="bg-red-600 text-white p-3 rounded-2xl">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /></svg>
</div>
<div class="text-left text-xs text-red-900 font-bold leading-relaxed">
<p class="text-[10px] font-black text-red-400 uppercase tracking-widest">Lokasi Kami</p>
<p id="display-address"></p>
</div>
</div>
<div id="main-content" class="space-y-12 text-left"></div>
<button onclick="openCategoryModal()" class="admin-only w-full py-10 border-4 border-dashed border-yellow-200 rounded-[3.5rem] text-yellow-600 font-black text-xs uppercase tracking-widest mt-10">✨ Tambah Kategori</button>
</main>
<!-- Modal Urus Menu (Admin) -->
<div id="item-modal" class="fixed inset-0 bg-black/60 z-[300] hidden items-center justify-center p-6 backdrop-blur-sm">
<div class="bg-white w-full max-w-sm rounded-[3rem] p-8 shadow-2xl modal-animate overflow-y-auto max-h-[90vh]">
<h3 class="font-black text-xl mb-6 text-gray-800 text-center">Urus Menu</h3>
<div class="space-y-4">
<div class="w-32 h-32 bg-gray-50 rounded-3xl border-2 border-dashed border-gray-200 overflow-hidden mx-auto flex items-center justify-center">
<img id="modal-img-preview" src="" class="w-full h-full object-cover hidden">
</div>
<input type="file" accept="image/*" onchange="handleFileUpload(this)" class="text-xs">
<input type="text" id="modal-item-name" class="w-full border rounded-xl p-3 text-sm font-bold" placeholder="Nama Menu">
<input type="number" id="modal-item-price" class="w-full border rounded-xl p-3 text-sm font-bold" placeholder="Harga (RM)">
<div>
<label class="text-[10px] font-black text-gray-400 uppercase">Optional Pilihan (Pisahkan dengan koma):</label>
<textarea id="modal-item-options" class="w-full border rounded-xl p-3 text-xs" placeholder="Cth: Asing Sambal, Asing Kuah, Extra Pedas"></textarea>
</div>
</div>
<div class="flex gap-2 mt-8">
<button onclick="saveItem()" class="flex-1 bg-green-600 py-4 rounded-2xl font-black text-white text-xs uppercase tracking-widest">Simpan</button>
<button onclick="closeItemModal()" class="flex-1 bg-gray-100 py-4 rounded-2xl font-bold text-gray-400 text-xs uppercase tracking-widest">Batal</button>
</div>
</div>
</div>
<!-- Modal Checkout (Pelanggan) -->
<div id="checkout-modal" class="fixed inset-0 bg-black/70 z-[100] hidden items-end sm:items-center justify-center p-4 backdrop-blur-sm">
<div class="bg-white w-full max-w-md rounded-t-[3.5rem] sm:rounded-[3rem] p-8 shadow-2xl modal-animate overflow-y-auto max-h-[95vh]">
<h2 class="text-2xl font-black text-gray-900 text-center uppercase tracking-widest mb-6">Check Out</h2>
<div class="space-y-6">
<div id="modal-cart-list" class="space-y-4"></div>
<input type="text" id="cust-name" class="w-full border-2 border-slate-50 bg-slate-50 rounded-2xl p-4 font-bold outline-none focus:border-red-500" placeholder="Nama Anda">
<input type="text" id="cust-address" class="w-full border-2 border-slate-50 bg-slate-50 rounded-2xl p-4 font-bold outline-none focus:border-red-500" placeholder="No Meja / Alamat">
<div class="bg-white p-6 rounded-[3.5rem] border border-slate-100 flex flex-col items-center">
<img id="display-qr-img" src="" class="w-48 h-48 object-contain mb-4">
<p class="text-[10px] font-bold text-gray-400 uppercase">Jumlah Bayaran:</p>
<p id="modal-total-price" class="text-4xl font-black text-red-600"></p>
</div>
<button onclick="finalCheckout()" class="w-full bg-green-600 text-white font-black py-5 rounded-[2rem] shadow-xl uppercase tracking-widest">🚀 Hantar Order Ke WhatsApp</button>
</div>
<button onclick="toggleModal(false)" class="w-full text-slate-300 font-black text-[10px] mt-8 uppercase tracking-widest">Tutup</button>
</div>
</div>
<!-- Floating Bakul (Pelanggan) -->
<div id="cart-bar" class="fixed bottom-8 left-4 right-4 z-40 hidden customer-only">
<div class="max-w-md mx-auto bg-white/95 backdrop-blur-xl rounded-[2.5rem] p-4 shadow-2xl flex items-center justify-between border border-t-4 border-t-red-600">
<div class="pl-6 text-left">
<p id="total-items" class="text-[9px] text-gray-400 font-black uppercase mb-1"></p>
<p id="total-price" class="text-2xl font-black text-red-600 leading-none"></p>
</div>
<button onclick="toggleModal(true)" class="bg-red-600 text-white px-10 py-5 rounded-[2rem] font-black text-xs uppercase tracking-widest active:scale-95 transition-all shadow-lg">Check Out</button>
</div>
</div>
<!-- Modal Login & Kategori (Sama seperti sebelum) -->
<div id="admin-login-modal" class="fixed inset-0 bg-black/60 z-[200] hidden items-center justify-center p-6"><div class="bg-white w-full max-w-xs rounded-[3rem] p-10 shadow-2xl text-center"><h3 class="font-black text-xl mb-6">ADMIN LOGIN</h3><input type="password" id="admin-pass-input" class="w-full border rounded-xl p-4 mb-6 text-center" placeholder="••••••"><button onclick="checkAdminPass()" class="w-full bg-red-600 text-white py-4 rounded-xl font-black mb-2">MASUK</button><button onclick="toggleAdminLogin(false)" class="w-full text-gray-400 font-bold">BATAL</button></div></div>
<div id="category-modal" class="fixed inset-0 bg-black/60 z-[350] hidden items-center justify-center p-6"><div class="bg-white w-full max-w-xs rounded-[3rem] p-8 text-center"><h3 class="font-black text-lg mb-6 uppercase">Kategori Baru</h3><input type="text" id="modal-cat-name" class="w-full border rounded-xl p-4 mb-6 text-center font-bold" placeholder="Cth: Minuman"><button onclick="saveCategory()" class="w-full bg-yellow-500 text-white py-4 rounded-xl font-black">TAMBAH</button></div></div>
<script>
function initApp() {
document.getElementById('display-name').innerText = KEDAI_CONFIG.nama;
document.getElementById('display-tagline').innerText = KEDAI_CONFIG.tagline;
document.getElementById('display-logo').src = KEDAI_CONFIG.logoUrl;
document.getElementById('display-address').innerText = KEDAI_CONFIG.alamat;
document.getElementById('edit-name').value = KEDAI_CONFIG.nama;
document.getElementById('edit-wa').value = KEDAI_CONFIG.whatsappAdmin;
document.getElementById('edit-address').value = KEDAI_CONFIG.alamat;
document.getElementById('edit-logo-preview').src = KEDAI_CONFIG.logoUrl;
document.getElementById('edit-qr-preview').src = KEDAI_CONFIG.duitnowQr || "";
renderMenu();
}
function renderMenu() {
const container = document.getElementById('main-content');
container.innerHTML = "";
const isAdmin = document.getElementById('body-root').classList.contains('admin-mode');
KEDAI_CONFIG.menu.forEach((cat, catIdx) => {
let html = `<div class="mb-10"><div class="flex items-center justify-between mb-8 px-2"><div class="flex items-center gap-4"><div class="h-2 w-2 bg-red-600 rounded-full animate-pulse"></div><h2 class="text-xs font-black text-gray-800 uppercase tracking-[0.4em]">${cat.kategori}</h2></div>${isAdmin ? `<button onclick="deleteCategory(${catIdx})" class="text-[9px] text-red-400 font-bold">Hapus</button>` : ''}</div><div class="grid gap-6">`;
cat.items.forEach((item, itemIdx) => {
const cartItem = cart.find(c => c.name === item.nama);
const qty = cartItem ? cartItem.qty : 0;
const opts = item.options ? item.options.split(',') : [];
html += `
<div class="bg-white p-5 rounded-[2.5rem] shadow-sm border border-gray-50 flex flex-col gap-4">
<div class="flex items-center gap-5">
<div class="w-24 h-24 rounded-[2rem] overflow-hidden flex-shrink-0 bg-gray-50 border shadow-inner">
<img src="${item.img || 'https://via.placeholder.com/150'}" class="w-full h-full object-cover">
</div>
<div class="flex-grow text-left">
<h3 class="font-bold text-gray-900 text-base mb-1">${item.nama}</h3>
<p class="text-red-600 font-black text-lg leading-none">RM ${parseFloat(item.harga).toFixed(2)}</p>
<div class="customer-only flex items-center gap-3 mt-4">
${qty > 0 ? `<button onclick="removeFromCart('${item.nama}')" class="bg-gray-100 p-2 rounded-xl active:scale-90"><svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4" /></svg></button><span class="font-black">${qty}</span>` : ''}
<button onclick="addToCart('${item.nama}', ${item.harga})" class="bg-red-600 text-white p-2 px-6 rounded-2xl font-black text-[10px] uppercase flex items-center gap-1 active:scale-90 shadow-lg shadow-red-100"><svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /></svg>${qty === 0 ? 'Pilih' : ''}</button>
</div>
${isAdmin ? `<div class="flex gap-2 mt-4"><button onclick="openEditModal(${catIdx}, ${itemIdx})" class="bg-yellow-400 text-[10px] font-black px-4 py-1.5 rounded-full">Edit</button><button onclick="deleteItem(${catIdx}, ${itemIdx})" class="bg-gray-100 text-[10px] font-bold px-4 py-1.5 rounded-full">Padam</button></div>` : ''}
</div>
</div>
<!-- Optional Pilihan Pelanggan -->
${qty > 0 && opts.length > 0 ? `
<div class="customer-only border-t border-dashed pt-3 mt-1 flex flex-wrap gap-2">
${opts.map((o, oIdx) => `
<label class="flex items-center gap-2 bg-slate-50 px-3 py-1.5 rounded-xl cursor-pointer">
<input type="checkbox" onchange="toggleCartOption('${item.nama}', '${o.trim()}')" ${cartItem.selectedOpts && cartItem.selectedOpts.includes(o.trim()) ? 'checked' : ''} class="accent-red-600">
<span class="text-[10px] font-bold text-gray-500">${o.trim()}</span>
</label>
`).join('')}
</div>
` : ''}
</div>
`;
});
html += `</div></div>`;
container.innerHTML += html;
});
}
// --- CART LOGIC ---
function addToCart(name, price) {
const item = cart.find(i => i.name === name);
if (item) { item.qty++; } else { cart.push({ name, price, qty: 1, selectedOpts: [] }); }
updateCartUI(); renderMenu();
}
function removeFromCart(name) {
const idx = cart.findIndex(i => i.name === name);
if (idx > -1) { cart[idx].qty--; if (cart[idx].qty <= 0) cart.splice(idx, 1); }
updateCartUI(); renderMenu();
if (document.getElementById('checkout-modal').style.display === 'flex') updateModalCartList();
}
function toggleCartOption(name, option) {
const item = cart.find(i => i.name === name);
if (item) {
if (!item.selectedOpts) item.selectedOpts = [];
const oIdx = item.selectedOpts.indexOf(option);
if (oIdx > -1) { item.selectedOpts.splice(oIdx, 1); } else { item.selectedOpts.push(option); }
}
if (document.getElementById('checkout-modal').style.display === 'flex') updateModalCartList();
}
function updateCartUI() {
const bar = document.getElementById('cart-bar');
if (cart.length > 0) {
bar.classList.remove('hidden');
const tQty = cart.reduce((s, i) => s + i.qty, 0);
const tPrice = cart.reduce((s, i) => s + (i.price * i.qty), 0);
document.getElementById('total-items').innerText = `${tQty} Unit`;
document.getElementById('total-price').innerText = `RM ${tPrice.toFixed(2)}`;
} else { bar.classList.add('hidden'); }
}
function updateModalCartList() {
const list = document.getElementById('modal-cart-list');
const totalDisplay = document.getElementById('modal-total-price');
list.innerHTML = ""; let total = 0;
if (cart.length === 0) { list.innerHTML = "<p class='text-center text-xs py-4'>Bakul kosong.</p>"; totalDisplay.innerText = "RM 0.00"; return; }
cart.forEach((item, idx) => {
const sub = item.price * item.qty; total += sub;
list.innerHTML += `<div class="bg-white p-4 rounded-3xl border text-left"><div class="flex justify-between font-black text-xs"><span>${item.name} (x${item.qty})</span><span class="text-red-600">RM ${sub.toFixed(2)}</span></div><div class="text-[10px] text-red-400 italic mt-1">${item.selectedOpts && item.selectedOpts.length > 0 ? '✅ ' + item.selectedOpts.join(', ') : 'Tiada request'}</div></div>`;
});
totalDisplay.innerText = `RM ${total.toFixed(2)}`;
}
function toggleModal(show) {
if (show) { updateModalCartList(); document.getElementById('display-qr-img').src = KEDAI_CONFIG.duitnowQr; }
document.getElementById('checkout-modal').style.display = show ? 'flex' : 'none';
}
function finalCheckout() {
const name = document.getElementById('cust-name').value;
const addr = document.getElementById('cust-address').value;
if(!name || !addr) return alert("Isi Nama & Lokasi.");
const total = cart.reduce((s, i) => s + (i.price * i.qty), 0);
let msg = `*PESANAN - ${KEDAI_CONFIG.nama.toUpperCase()}*\n👤: ${name}\n📍: ${addr}\n━━━━━━━━━━━━━━━━━━━━\n\n`;
cart.forEach(i => {
msg += `• ${i.name} (x${i.qty}): RM ${(i.price * i.qty).toFixed(2)}\n`;
if(i.selectedOpts && i.selectedOpts.length > 0) msg += ` _Request: ${i.selectedOpts.join(', ')}_\n`;
});
msg += `\n*TOTAL: RM ${total.toFixed(2)}*\n━━━━━━━━━━━━━━━━━━━━\n\n✅ *DAH BAYAR (QR/RESIT)*`;
window.open(`https://wa.me/${KEDAI_CONFIG.whatsappAdmin}?text=${encodeURIComponent(msg)}`);
}
// --- ADMIN ACTIONS ---
function updateShopInfo() {
KEDAI_CONFIG.nama = document.getElementById('edit-name').value;
KEDAI_CONFIG.whatsappAdmin = document.getElementById('edit-wa').value;
KEDAI_CONFIG.alamat = document.getElementById('edit-address').value;
initApp(); persistData();
}
function handleLogoUpload(input) {
const file = input.files[0]; if (file) {
const reader = new FileReader(); reader.onload = (e) => { KEDAI_CONFIG.logoUrl = e.target.result; initApp(); persistData(); }; reader.readAsDataURL(file);
}
}
function handleQrUpload(input) {
const file = input.files[0]; if (file) {
const reader = new FileReader(); reader.onload = (e) => { KEDAI_CONFIG.duitnowQr = e.target.result; initApp(); persistData(); }; reader.readAsDataURL(file);
}
}
function handleFileUpload(input) {
const file = input.files[0]; if (file) {
const reader = new FileReader(); reader.onload = (e) => { uploadedImageBase64 = e.target.result; document.getElementById('modal-img-preview').src = uploadedImageBase64; document.getElementById('modal-img-preview').classList.remove('hidden'); }; reader.readAsDataURL(file);
}
}
function toggleAdminLogin(show) { document.getElementById('admin-login-modal').style.display = show ? 'flex' : 'none'; }
function checkAdminPass() { if (document.getElementById('admin-pass-input').value === "admin123") { document.getElementById('body-root').classList.add('admin-mode'); toggleAdminLogin(false); renderMenu(); } else alert("Salah!"); }
function exitAdmin() { document.getElementById('body-root').classList.remove('admin-mode'); initApp(); }
function openCategoryModal() { document.getElementById('modal-cat-name').value = ""; document.getElementById('category-modal').style.display = 'flex'; }
function closeCategoryModal() { document.getElementById('category-modal').style.display = 'none'; }
function saveCategory() { const n = document.getElementById('modal-cat-name').value; if (!n) return; KEDAI_CONFIG.menu.push({ kategori: n, items: [] }); closeCategoryModal(); renderMenu(); persistData(); }
function deleteCategory(idx) { if (confirm(`Hapus?`)) { KEDAI_CONFIG.menu.splice(idx, 1); renderMenu(); persistData(); } }
function openEditModal(catIdx, itemIdx) {
modalMode = 'edit'; currentEditIndices = { cat: catIdx, item: itemIdx };
const item = KEDAI_CONFIG.menu[catIdx].items[itemIdx];
uploadedImageBase64 = item.img || "";
document.getElementById('modal-item-name').value = item.nama;
document.getElementById('modal-item-price').value = item.harga;
document.getElementById('modal-item-options').value = item.options || "";
if (item.img) { document.getElementById('modal-img-preview').src = item.img; document.getElementById('modal-img-preview').classList.remove('hidden'); } else document.getElementById('modal-img-preview').classList.add('hidden');
document.getElementById('item-modal').style.display = 'flex';
}
function openAddModal(catIdx) {
modalMode = 'add'; currentEditIndices.cat = catIdx; uploadedImageBase64 = "";
document.getElementById('modal-img-preview').classList.add('hidden');
document.getElementById('modal-item-name').value = ""; document.getElementById('modal-item-price').value = ""; document.getElementById('modal-item-options').value = "";
document.getElementById('item-modal').style.display = 'flex';
}
function closeItemModal() { document.getElementById('item-modal').style.display = 'none'; }
function saveItem() {
const n = document.getElementById('modal-item-name').value, p = document.getElementById('modal-item-price').value, o = document.getElementById('modal-item-options').value;
if (!n || !p) return alert("Isi data!");
if (modalMode === 'add') { KEDAI_CONFIG.menu[currentEditIndices.cat].items.push({ id: Date.now(), nama: n, harga: parseFloat(p), img: uploadedImageBase64, options: o }); }
else { const i = KEDAI_CONFIG.menu[currentEditIndices.cat].items[currentEditIndices.item]; i.nama = n; i.harga = parseFloat(p); i.img = uploadedImageBase64; i.options = o; }
closeItemModal(); renderMenu(); persistData();
}
function deleteItem(catIdx, itemIdx) { if (confirm(`Hapus?`)) { KEDAI_CONFIG.menu[catIdx].items.splice(itemIdx, 1); renderMenu(); persistData(); } }
window.onload = initApp;
</script>
</body>
</html>