39735-vm/POSFuku/Pelanggan.html
Flatlogic Bot 8c2a5d487c POSFuku_v2
2026-04-19 12:46:29 +00:00

1483 lines
70 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Menu Pelanggan - POS</title>
<script src="/local-preview-bridge.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;800;900&display=swap" rel="stylesheet">
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<style>
/* Optimasi Scroll & Performa untuk HP Low-end */
html, body {
margin:0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; /* Gunakan font sistem agar lebih ringan */
background:#f4f7f6;
padding-bottom: 20px;
-webkit-font-smoothing: antialiased;
color: #333;
overflow-x: hidden;
overflow-y: auto;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
/* Hapus shadow berat dan ganti dengan border tipis */
.header {
background:#111;
color:white;
padding:15px;
text-align:center;
position:sticky;
top:0;
z-index:100;
border-bottom: 1px solid #333;
}
.sync-btn {
position: absolute;
top: 15px;
right: 15px;
background: #333;
color: #fff;
border: 1px solid #444;
padding: 4px 8px;
border-radius: 6px;
font-size: 10px;
font-weight: 800;
cursor: pointer;
}
.sync-btn:active { opacity: 0.7; }
.meja-info { font-weight:900; color:#e11d48; font-size:18px; margin-top: 2px; }
.main-layout { display: flex; flex-direction: column; padding: 12px; gap: 15px; max-width: 1400px; margin: 0 auto; }
@media (min-width: 992px) {
.main-layout { flex-direction: row; align-items: flex-start; }
.menu-section { flex: 1.8; }
.cart-sidebar { flex: 1; position: sticky; top: 100px; max-height: calc(100vh - 120px); overflow-y: auto; }
}
.menu-section { width: 100%; }
.cart-sidebar {
width: 100%;
background: white;
border-radius: 16px;
border: 1px solid #ddd;
padding: 20px;
box-sizing: border-box;
}
.title-sm { font-weight: 900; font-size: 14px; color: #111; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
.menu-grid {
display:grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap:12px;
margin-top: 10px;
}
@media (max-width: 480px) { .menu-grid { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); } }
.menu-card {
background:white;
border-radius:14px;
overflow:hidden;
position:relative;
border: 1px solid #eee;
cursor: pointer;
transition: transform 0.1s;
}
.menu-card:active { transform: scale(0.96); }
.menu-card.menu-disabled { cursor: default; background:#f8fafc; border:1px dashed #cbd5e1; }
.menu-card.menu-disabled:active { transform: none; }
.qty-badge { position:absolute; top:6px; left:6px; background:#e11d48; color:white; font-weight:900; font-size:11px; padding:3px 8px; border-radius:8px; z-index: 10; border: 1.5px solid white; display: none; }
.menu-card.selected { border: 2px solid #e11d48; background: #fff1f2; }
.menu-card img { width:100%; aspect-ratio:1/1; object-fit:cover; display: block; background: #f8f8f8; }
.menu-info { padding:10px; }
.menu-name { font-size:11px; font-weight:700; height:2.8em; overflow:hidden; line-height: 1.4; color: #111; }
.menu-price { font-size:12px; font-weight:800; color:#e11d48; margin-top: 5px; }
.menu-stok { font-size:9px; color:#888; margin-top:3px; }
.stok-habis { opacity: 0.5; filter: grayscale(1); pointer-events: none; }
.badge-habis { position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); background:rgba(0,0,0,0.8); color:white; padding:5px 10px; border-radius:8px; font-weight:900; font-size:10px; z-index: 15; }
.cat-bar {
display:flex;
gap:8px;
overflow-x:auto;
padding:10px 0;
position:sticky;
top:60px; /* Sesuaikan dengan tinggi header */
background:#f4f7f6;
z-index:90;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
border-bottom: 1px solid #ddd;
margin-bottom: 15px;
}
.cat-bar::-webkit-scrollbar { display: none; }
.tag { padding:10px 18px; border-radius:14px; background:white; border:1px solid #ddd; white-space:nowrap; font-weight:700; font-size:12px; color: #555; cursor: pointer; transition: background 0.1s; }
.tag.active { background:#e11d48; color:white; border-color:#e11d48; }
.sidebar-title { font-weight: 900; color: #111; font-size: 16px; margin-bottom: 20px; border-bottom: 2px solid #f4f7f6; padding-bottom: 10px; display: flex; justify-content: space-between; align-items: center; }
.cart-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding: 12px; border: 1px solid #f0f0f0; border-radius: 12px; background: #fff; }
.cart-item-info { flex: 1; padding-right: 10px; }
.cart-item-name { font-weight: 700; font-size: 13px; color: #111; line-height: 1.3; }
.cart-item-price { font-size: 11px; color: #666; margin-top: 3px; font-weight: 600; }
.cart-qty-ctrl { display: flex; align-items: center; gap: 8px; background: #f3f4f6; padding: 4px; border-radius: 10px; }
.qty-btn { width: 30px; height: 30px; border-radius: 8px; background: #111; color: white; display: flex; align-items: center; justify-content: center; font-weight: 900; cursor: pointer; border: none; transition: opacity 0.1s; }
.qty-btn:active { opacity: 0.7; }
.qty-val { font-weight: 800; font-size: 14px; min-width: 22px; text-align: center; color: #111; }
.cart-summary { background: #f8fafc; border-radius: 12px; padding: 15px; margin-top: 20px; border: 1px solid #edf2f7; }
.summary-row { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 13px; font-weight: 600; color: #4a5568; }
.summary-total { display: flex; justify-content: space-between; font-weight: 900; font-size: 18px; color: #e11d48; margin-top: 12px; padding-top: 12px; border-top: 2px dashed #cbd5e0; }
.btn-main { width: 100%; padding: 15px; border-radius: 12px; font-weight: 900; font-size: 14px; cursor: pointer; margin-top: 15px; border: none; background: #e11d48; color: white; transition: all 0.2s; }
.btn-main:active { transform: scale(0.98); opacity: 0.8; }
.btn-main:disabled { background: #cbd5e0; color: #718096; cursor: not-allowed; }
.btn-outline { width: 100%; padding: 12px; border-radius: 12px; font-weight: 800; font-size: 13px; cursor: pointer; margin-top: 10px; border: 2px solid #111; background: white; color: #111; transition: all 0.2s; }
.btn-outline:active { background: #f3f4f6; transform: scale(0.98); }
#floating-cart-bar { position: fixed; left: 15px; right: 15px; bottom: 20px; background: #111; color: white; border-radius: 16px; padding: 15px 20px; display: none; justify-content: space-between; align-items: center; z-index: 1000; border: 1px solid #333; cursor: pointer; }
@media (min-width: 992px) { #floating-cart-bar { display: none !important; } }
.f-cart-info { display: flex; align-items: center; gap: 12px; }
.f-cart-count { background: #e11d48; color: white; padding: 2px 10px; border-radius: 8px; font-weight: 900; font-size: 14px; }
.f-cart-text { font-weight: 800; font-size: 14px; }
.f-cart-price { font-weight: 900; color: #fff; font-size: 15px; }
.f-cart-btn { background: #e11d48; color: white; border: none; padding: 8px 15px; border-radius: 10px; font-weight: 900; font-size: 12px; }
#loading { position:fixed; inset:0; background:#fff; display:flex; flex-direction:column; justify-content:center; align-items:center; z-index:2000; }
.spinner { border:4px solid #f3f3f3; border-top:4px solid #e11d48; border-radius:50%; width:40px; height:40px; animation:spin 0.8s linear infinite; margin-bottom: 15px; }
@keyframes spin { 0% { transform:rotate(0deg); } 100% { transform:rotate(360deg); } }
.input-group { margin-bottom: 15px; }
.input-group label { display: block; font-weight: 800; font-size: 11px; color: #4a5568; margin-bottom: 6px; text-transform: uppercase; }
.input-group input { width: 100%; padding: 12px; border-radius: 10px; border: 1px solid #ddd; font-size: 13px; font-family: inherit; font-weight: 600; box-sizing: border-box; outline: none; }
.input-group input:focus { border-color: #e11d48; }
.wifi-card { background: #111; color: white; padding: 18px; border-radius: 16px; display: flex; align-items: center; gap: 15px; margin-bottom: 20px; border-left: 5px solid #e11d48; position: relative; overflow: hidden; }
.wifi-icon { font-size: 28px; }
.wifi-ssid { font-weight: 900; font-size: 15px; margin-bottom: 2px; }
.wifi-pass { font-size: 13px; color: #aaa; font-family: monospace; }
/* Fitur Lock WiFi yang Ringan */
.wifi-locked .wifi-ssid, .wifi-locked .wifi-pass {
color: transparent;
text-shadow: 0 0 8px rgba(255,255,255,0.5); /* Efek teks samar tanpa blur berat */
user-select: none;
pointer-events: none;
}
.wifi-unlock-overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0.8); /* Background solid lebih ringan dari backdrop-filter */
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
z-index: 20;
padding: 10px;
}
.wifi-locked .wifi-unlock-overlay { display: flex; }
.wifi-unlock-text { color: white; font-weight: 900; font-size: 11px; margin-bottom: 5px; text-transform: uppercase; letter-spacing: 0.5px; }
.wifi-unlock-subtext { color: #ccc; font-size: 10px; font-weight: 600; }
.btn-wifi { background: #333; color: white; border: none; padding: 8px 14px; border-radius: 10px; font-weight: 800; font-size: 11px; cursor: pointer; transition: background 0.2s; }
.btn-wifi:hover { background: #444; }
#active-order-section {
padding: 15px;
background: #fff;
border-radius: 16px;
display: none;
border: 2px solid #edf2f7;
margin-bottom: 20px;
}
/* Print styles */
#print-area { display: none; }
@media print {
body * { visibility: hidden; }
#print-area, #print-area * { visibility: visible; }
#print-area { position: absolute; left: 0; top: 0; width: 100%; display: block !important; }
}
</style>
</head>
<body>
<div id="loading"><div class="spinner"></div><p style="font-weight:700; color:#111;">Memuat Menu...</p></div>
<!-- Floating Cart Bar (Mobile) -->
<div id="floating-cart-bar" onclick="scrollToCart()">
<div class="f-cart-info">
<span class="f-cart-count" id="f-cart-count">0</span>
<div>
<div class="f-cart-text">PESANAN</div>
<div class="f-cart-price" id="f-cart-price">Rp 0</div>
</div>
</div>
<button class="f-cart-btn">LIHAT KERANJANG ➔</button>
</div>
<!-- Area Cetak Tersembunyi -->
<div id="print-area"></div>
<!-- Modal Gambar Struk (iOS) -->
<div id="image-modal" style="position:fixed; inset:0; background:rgba(0,0,0,0.9); z-index:3000; display:none; flex-direction:column; justify-content:center; align-items:center; padding:20px;" onclick="this.style.display='none'">
<div style="background:#fff; border-radius:12px; padding:15px; text-align:center; max-width:100%;">
<div style="font-weight:800; margin-bottom:10px;">Tekan lama gambar untuk simpan/cetak</div>
<img id="struk-img" style="max-width:100%; max-height:70vh; border:1px solid #eee; display:block; margin:0 auto;">
<button onclick="this.parentElement.parentElement.style.display='none'" style="margin-top:15px; width:100%; padding:12px; border:none; border-radius:10px; background:#111; color:white; font-weight:800;">TUTUP</button>
</div>
</div>
<!-- Modal QR WiFi -->
<div id="wifi-qr-modal" style="position:fixed; inset:0; background:rgba(0,0,0,0.8); z-index:3500; display:none; flex-direction:column; justify-content:center; align-items:center; padding:20px;" onclick="if(event.target.id==='wifi-qr-modal') this.style.display='none'">
<div style="background:#fff; border-radius:12px; padding:20px; text-align:center; max-width:100%; box-sizing:border-box;">
<div style="font-weight:800; color:#111; margin-bottom:10px;">Scan QR WiFi</div>
<div style="border:1px solid #eee; margin:15px 0; border-radius:8px; background:#f9fafb; padding:10px;">
<img id="wifi-qr-img" src="" style="max-width:100%; height:auto; border-radius:8px; display:block; margin:0 auto;">
</div>
<button onclick="document.getElementById('wifi-qr-modal').style.display='none'" style="width:100%; padding:12px; border:none; border-radius:10px; background:#e11d48; color:white; font-weight:800; font-size:14px;">TUTUP</button>
</div>
</div>
<div id="qris-modal" style="position:fixed; inset:0; background:rgba(0,0,0,0.85); z-index:3600; display:none; flex-direction:column; justify-content:center; align-items:center; padding:20px;" onclick="if(event.target.id==='qris-modal') this.style.display='none'">
<div style="background:#fff; border-radius:12px; padding:20px; text-align:center; max-width:100%; width:420px; box-sizing:border-box;">
<div style="font-weight:900; color:#111; margin-bottom:10px;">QRIS Pembayaran</div>
<div style="border:1px solid #eee; margin:15px 0; border-radius:10px; background:#f9fafb; padding:10px;">
<img id="qris-img" src="" style="max-width:100%; height:auto; border-radius:10px; display:block; margin:0 auto;">
</div>
<div style="display:flex; gap:10px;">
<button onclick="downloadQris()" style="flex:1; padding:12px; border:none; border-radius:10px; background:#16a34a; color:white; font-weight:900; font-size:14px;">DOWNLOAD</button>
<button onclick="document.getElementById('qris-modal').style.display='none'" style="flex:1; padding:12px; border:none; border-radius:10px; background:#111; color:white; font-weight:900; font-size:14px;">TUTUP</button>
</div>
<div class="muted" style="margin-top:10px;">Jika tombol download tidak jalan, tekan lama pada gambar lalu pilih Simpan.</div>
</div>
</div>
<div class="header">
<div class="sync-btn" onclick="syncData(false)">SYNC</div>
<div id="store-name" style="font-weight:900; font-size:14px; letter-spacing:1px;">POS</div>
<div id="store-address" style="margin-top:6px; font-size:10px; font-weight:600; color:#bbb;"></div>
<div class="meja-info">MEJA <?= meja ?></div>
<div style="margin-top:10px; font-size:10px; font-weight:600; color:#ccc;">Follow & Review us on:</div>
<div style="display:flex; justify-content:center; gap:15px; margin-top:8px; align-items: center;">
<a id="social-ig" href="#" target="_blank"><img src="https://cdn-icons-png.flaticon.com/512/174/174855.png" width="18"></a>
<a id="social-tt" href="#" target="_blank"><img src="https://cdn-icons-png.flaticon.com/512/3046/3046121.png" width="18"></a>
<a id="social-maps" href="#" target="_blank"><img src="https://cdn-icons-png.flaticon.com/512/2991/2991231.png" width="18"></a>
<a id="social-link" href="#" target="_blank"><img src="https://cdn-icons-png.flaticon.com/512/9102/9102575.png" width="18"></a>
<div style="width:1px; height:15px; background:#444; margin:0 5px;"></div>
<a id="store-wa-link" href="#" target="_blank" style="color:#25d366; text-decoration:none; font-size:11px; font-weight:800; display:flex; align-items:center; gap:4px;">
<img src="https://cdn-icons-png.flaticon.com/512/733/733585.png" width="16">
<span id="store-wa-text">-</span>
</a>
</div>
<div style="margin-top:10px; display:flex; justify-content:center;">
<button onclick="openQris()" style="padding:10px 14px; border:none; border-radius:12px; background:#16a34a; color:white; font-weight:900; font-size:12px; cursor:pointer;">LIHAT QRIS</button>
</div>
</div>
<div class="main-layout">
<!-- Sisi Kiri: Menu -->
<div class="menu-section">
<div class="wifi-card" id="wifi-card">
<div class="wifi-unlock-overlay" onclick="scrollToNameWA()">
<div class="wifi-unlock-text">INFO WIFI TERKUNCI (ISI NAMA & WA)</div>
<div class="wifi-unlock-subtext">Isi data di keranjang belanja untuk membuka password</div>
</div>
<div class="wifi-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#e11d48" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 12.55a11 11 0 0 1 14.08 0"></path>
<path d="M1.42 9a16 16 0 0 1 21.16 0"></path>
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"></path>
<line x1="12" y1="20" x2="12.01" y2="20"></line>
</svg>
</div>
<div class="wifi-details">
<div class="wifi-ssid">WiFi: <span id="wifi-ssid-text">FukuShabuGrill</span></div>
<div class="wifi-pass">Pass: <span id="wifi-pass-text">Fusagi17</span></div>
<div style="margin-top: 10px; display: flex; gap: 8px;">
<button class="btn-wifi" id="btn-wifi-copy" onclick="copyWifiPassword()">SALIN PASSWORD</button>
<button class="btn-wifi" id="btn-wifi-qr" style="background:#16a34a;" onclick="openWifiQR()">LIHAT QR CODE</button>
</div>
</div>
</div>
<!-- Promo Follow & Review Card -->
<div style="background: linear-gradient(135deg, #e11d48 0%, #be123c 100%); color: white; padding: 18px; border-radius: 16px; margin-bottom: 20px; border-left: 5px solid #fff; position: relative; overflow: hidden;">
<div style="display: flex; align-items: center; gap: 15px; margin-bottom: 15px;">
<div style="font-size: 32px; min-width: 50px;">🎁</div>
<div style="flex: 1;">
<div style="font-weight: 900; font-size: 15px; margin-bottom: 6px;">FREE MINUMAN 1 PCS! 🎉</div>
<div style="font-size: 12px; line-height: 1.5; color: #fff;">
Sudah follow dan review kami di <strong>TikTok / Instagram / Google Maps</strong>? <br>
<strong>Tunjukkan bukti screenshot review Anda</strong> untuk mendapatkan <strong>FREE MINUMAN 1 untuk kedatangan berikutnya!</strong>
</div>
</div>
</div>
<!-- Upload Foto Section -->
<div style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 12px; border: 2px dashed rgba(255,255,255,0.4);">
<div style="font-size: 11px; font-weight: 800; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.5px;">Upload Screenshot Review</div>
<div style="margin-bottom: 10px;">
<input type="file" id="promo-photo" accept="image/*" style="display: none;">
<button onclick="openReviewPicker()" style="width: 100%; padding: 10px; background: #fff; color: #e11d48; border: none; border-radius: 8px; font-weight: 800; font-size: 12px; cursor: pointer; transition: all 0.2s;">
📸 PILIH FOTO (Max 500KB)
</button>
</div>
<div style="font-size: 10px; opacity: 0.9; color: rgba(255,255,255,0.85); margin-bottom: 10px; line-height: 1.4;">
Upload screenshot review saja sudah cukup. Jika belum pesan, kami akan menyimpan bukti review sebagai entri terpisah.
</div>
<div id="review-form" style="display:none; margin-top:10px; background:rgba(255,255,255,0.12); padding:10px; border-radius:10px;">
<div style="font-size:10px; font-weight:900; margin-bottom:8px; text-transform:uppercase; letter-spacing:0.5px;">Data untuk Review</div>
<div style="display:flex; gap:8px;">
<input id="review-nama" type="text" placeholder="Nama" style="flex:1; padding:10px; border-radius:8px; border:none; outline:none; font-weight:800; font-size:12px;">
<input id="review-wa" type="tel" placeholder="WhatsApp" style="flex:1; padding:10px; border-radius:8px; border:none; outline:none; font-weight:800; font-size:12px;">
</div>
<div style="font-size:10px; opacity:0.9; margin-top:6px;">Jika belum pesan menu, isi Nama & WA di sini untuk menyimpan bukti review.</div>
</div>
<div id="photo-preview" style="display: none; margin-top: 10px;">
<img id="preview-img" style="width: 100%; max-height: 150px; border-radius: 8px; object-fit: cover; border: 2px solid #fff; margin-bottom: 8px;">
<button onclick="clearPromoPhoto()" style="width: 100%; padding: 8px; background: rgba(255,255,255,0.2); color: #fff; border: 1px solid #fff; border-radius: 6px; font-weight: 700; font-size: 11px; cursor: pointer;">Hapus Foto</button>
</div>
<div id="photo-actions" style="display: none; margin-top: 10px;">
<button id="submit-review-btn" onclick="submitReviewProof()" style="width: 100%; padding: 10px; background: #fff; color: #e11d48; border: none; border-radius: 8px; font-weight: 800; font-size: 12px; cursor: pointer;">KIRIM REVIEW</button>
</div>
<div id="photo-info" style="font-size: 10px; color: rgba(255,255,255,0.8); margin-top: 8px;"></div>
</div>
</div>
<div id="active-order-section">
<div style="display:flex; justify-content:space-between; align-items:center; border-bottom: 2px solid #f4f7f6; padding-bottom: 8px; margin-bottom: 12px;">
<div style="font-weight: 900; color: #111; font-size: 15px;">PESANAN ANDA SAAT INI</div>
<div style="display:flex; gap:8px; align-items:center;">
<button onclick="cetakStrukPelanggan()" style="background:#111; color:#fff; border:none; padding:6px 12px; border-radius:8px; font-size:11px; font-weight:800; cursor:pointer;">CETAK STRUK</button>
</div>
</div>
<div id="active-status-badge" style="display:inline-block; font-size:11px; padding:5px 12px; border-radius:20px; font-weight:900; margin-bottom:12px; text-transform:uppercase;"></div>
<div id="active-order-list" style="font-size: 13px;"></div>
<div id="active-order-total" style="margin-top: 12px; font-weight: 900; text-align: right; border-top: 2px solid #f4f7f6; padding-top: 10px; color: #e11d48; font-size: 15px;"></div>
</div>
<div class="title-sm">PILIH MENU</div>
<div id="cat-container" class="cat-bar"></div>
<div id="menu-list" class="menu-grid"></div>
</div>
<!-- Sisi Kanan: Keranjang Sidebar -->
<div class="cart-sidebar">
<div class="sidebar-title">KERANJANG BELANJA <span id="cart-count-badge" style="background:#e11d48; color:white; font-size:11px; padding:2px 8px; border-radius:10px;">0</span></div>
<div id="sidebar-cart-list">
<div class="muted" style="text-align:center; padding:40px 20px; color:#a0aec0; font-size:13px; font-weight:600;">
<div style="font-size:30px; margin-bottom:10px;">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin:0 auto; display:block; opacity:0.5;">
<circle cx="9" cy="21" r="1"></circle>
<circle cx="20" cy="21" r="1"></circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
</svg>
</div>
Keranjang masih kosong.<br>Silakan pilih menu di sebelah kiri.
</div>
</div>
<div id="sidebar-cart-controls" style="display:none;">
<div style="margin-top:20px; padding-top:20px; border-top: 2px solid #f4f7f6;">
<div class="input-group">
<label>Nama Pemesan</label>
<input type="text" id="cart-nama" placeholder="Masukkan nama Anda">
</div>
<div class="input-group">
<label>Nomor WhatsApp</label>
<input type="tel" id="cart-wa" placeholder="Contoh: 081234567890">
</div>
<div class="input-group">
<label>Catatan Pesanan (Opsional)</label>
<textarea id="cart-catatan" placeholder="Contoh: Minta cabe rawit, minta tambahan es batu, dll" style="width:100%; height:70px; border-radius:10px; border:1px solid #ddd; padding:12px; font-size:13px; font-family:inherit; font-weight:600; outline:none; resize:none; box-sizing:border-box; background:#fff; color:#111;"></textarea>
</div>
</div>
<div class="cart-summary">
<div class="summary-row"><span>Subtotal</span><span id="sidebar-subtotal">Rp 0</span></div>
<div class="summary-total"><span>TOTAL</span><span id="sidebar-total">Rp 0</span></div>
</div>
<button class="btn-main" id="btn-pesan-sidebar" onclick="prosesPesan()">PESAN SEKARANG</button>
<button class="btn-outline" onclick="scrollToActiveOrder()">LIHAT PESANAN SAYA</button>
</div>
</div>
</div>
<script>
var wifiLockEnabled = true; // Set ke false untuk mematikan fitur kunci WiFi
var menu = [];
var transaksi = [];
var paketKustom = [];
var keranjang = [];
var kategoriAktif = 'Semua';
var mejaNo = '<?= meja ?>';
var storeName = 'POS';
var storeAddress = '';
var storeWhatsapp = '';
var socialInstagramUrl = '';
var socialTiktokUrl = '';
var socialGmapsUrl = '';
var socialLinkUrl = '';
var wifiSSID = '';
var wifiPassword = 'Fusagi17';
var wifiQRUrl = 'https://drive.google.com/file/d/1UY1QSUN5wDakP_GCq1FDbw3-KFAjVhvL/view?usp=drive_link';
var qrisImageUrl = 'https://lh3.googleusercontent.com/d/1hGPL3AlVGRMeZTzpwOHp-l-JzOG5tS09';
var promoPhotoData = null; // Simpan data foto promo
window.onload = function() {
if (String(mejaNo).indexOf('<') > -1) mejaNo = '';
try {
var params = new URLSearchParams(window.location.search);
var qMeja = params.get('meja') || params.get('table') || params.get('m');
if (qMeja) mejaNo = qMeja;
} catch(e) {}
syncData(true);
setInterval(function() { syncData(false); }, 20000);
// Listener untuk fitur kunci WiFi
document.getElementById('cart-nama').addEventListener('input', checkWifiLock);
document.getElementById('cart-wa').addEventListener('input', checkWifiLock);
checkWifiLock(); // Initial check
// Listener untuk upload foto promo
document.getElementById('promo-photo').addEventListener('change', handlePromoPhotoUpload);
};
function checkWifiLock() {
if (!wifiLockEnabled) {
document.getElementById('wifi-card').classList.remove('wifi-locked');
return;
}
var nama = document.getElementById('cart-nama').value.trim();
var wa = document.getElementById('cart-wa').value.trim();
var card = document.getElementById('wifi-card');
if (nama && wa.length >= 10) {
card.classList.remove('wifi-locked');
} else {
card.classList.add('wifi-locked');
}
}
function getActiveTransaction() {
return transaksi.find(function(t) {
return String(t.meja) === String(mejaNo) && (t.status === 'Pending' || t.status === 'Ready' || t.status === 'Selesai');
});
}
function uploadReviewScreenshotForTransaction(id) {
if (!promoPhotoData || !promoPhotoData.base64) return;
document.getElementById('photo-info').innerHTML = 'Mengunggah screenshot...';
document.getElementById('photo-info').style.color = '#fbbf24';
google.script.run.withSuccessHandler(function(uploadRes) {
if (!uploadRes || uploadRes.error || !uploadRes.url) {
alert('Gagal mengunggah screenshot review: ' + (uploadRes ? uploadRes.error : 'Tidak ada respons.'));
document.getElementById('photo-info').innerHTML = 'Gagal mengunggah screenshot.';
document.getElementById('photo-info').style.color = '#f87171';
return;
}
var active = getActiveTransaction();
if (active && active.id) {
google.script.run.withSuccessHandler(function(res) {
if (res && res.transaksi) {
transaksi = res.transaksi;
renderActiveOrder();
renderSidebarCart();
updateMenuBadges();
}
promoPhotoData.uploaded = true;
document.getElementById('photo-info').innerHTML = '✓ Screenshot tersimpan ke transaksi aktif.';
document.getElementById('photo-info').style.color = '#a3e635';
}).updateTransactionReviewPhoto(active.id, uploadRes.url);
} else {
var reviewPayload = {
id: 'REV-' + Date.now(),
meja: mejaNo || '-',
status: 'Review',
nama: document.getElementById('cart-nama').value.trim() || 'Review',
wa: document.getElementById('cart-wa').value.trim(),
items: [],
catatan: 'Upload screenshot review tanpa transaksi',
tgl: new Date().toISOString(),
jam: '',
diskon: 0,
poinDipakai: 0,
pajakPersen: 0,
servicePersen: 0,
adminPersen: 0,
pajak: 0,
service: 0,
adminFee: 0,
total: 0,
dp: 0,
metodeDp: '',
tglDp: '',
metodeBayar: '',
bayar: 0,
kembali: 0,
buktiReview: uploadRes.url
};
google.script.run.withSuccessHandler(function(res) {
if (res && res.transaksi) {
transaksi = res.transaksi;
renderActiveOrder();
renderSidebarCart();
updateMenuBadges();
}
promoPhotoData.uploaded = true;
document.getElementById('photo-info').innerHTML = '✓ Screenshot review tersimpan sebagai entri terpisah.';
document.getElementById('photo-info').style.color = '#a3e635';
}).saveTransaction(reviewPayload);
}
}).uploadReviewProof({ id: id || ('REV-' + Date.now()), base64: promoPhotoData.base64, mimeType: promoPhotoData.mimeType });
}
function scrollToNameWA() {
var el = document.getElementById('cart-nama');
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
el.focus();
}
}
function openReviewPicker() {
var rf = document.getElementById('review-form');
if (rf) rf.style.display = 'block';
try {
var rn = document.getElementById('review-nama');
var rw = document.getElementById('review-wa');
var cn = document.getElementById('cart-nama');
var cw = document.getElementById('cart-wa');
if (rn && cn && !rn.value) rn.value = cn.value || '';
if (rw && cw && !rw.value) rw.value = cw.value || '';
} catch(e) {}
var input = document.getElementById('promo-photo');
if (input) input.click();
}
function syncData(initialLoad) {
if (initialLoad === undefined) initialLoad = true;
var syncBtn = document.querySelector('.sync-btn');
if (syncBtn) {
syncBtn.innerText = '...';
syncBtn.style.opacity = '0.5';
}
var isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
var hasGAS = typeof google !== 'undefined' && google.script && google.script.run;
if (!hasGAS && isLocalhost) {
if (syncBtn) {
syncBtn.innerText = 'SYNC';
syncBtn.style.opacity = '1';
}
document.getElementById('loading').style.display = 'none';
if (initialLoad) {
alert('Mode localhost: Pelanggan.html tidak bisa menarik data dari server.\nGunakan URL WebApp untuk mode pelanggan, atau jalankan melalui Index.html localhost dan gunakan DATA → IMPORT.');
}
return;
}
google.script.run.withSuccessHandler(function(res) {
if (syncBtn) {
syncBtn.innerText = 'SYNC';
syncBtn.style.opacity = '1';
}
document.getElementById('loading').style.display = 'none';
menu = res.menu || [];
transaksi = res.transaksi || [];
paketKustom = (res.paketKustom || []).filter(function(p) { return p && p.aktif; });
// Update Pengaturan dari Server
if (res.settings) {
wifiLockEnabled = (res.settings.wifi_lock === 'true');
if (res.settings.qris_image_url) qrisImageUrl = String(res.settings.qris_image_url);
if (res.settings.wifi_ssid) wifiSSID = String(res.settings.wifi_ssid);
if (res.settings.wifi_password) wifiPassword = String(res.settings.wifi_password);
if (res.settings.store_name) storeName = String(res.settings.store_name);
if (res.settings.store_address) storeAddress = String(res.settings.store_address);
if (res.settings.store_whatsapp) storeWhatsapp = String(res.settings.store_whatsapp);
if (res.settings.social_instagram_url) socialInstagramUrl = String(res.settings.social_instagram_url);
if (res.settings.social_tiktok_url) socialTiktokUrl = String(res.settings.social_tiktok_url);
if (res.settings.social_gmaps_url) socialGmapsUrl = String(res.settings.social_gmaps_url);
if (res.settings.social_linktree_url) socialLinkUrl = String(res.settings.social_linktree_url);
var elName = document.getElementById('store-name');
if (elName) elName.innerText = storeName;
var elAddr = document.getElementById('store-address');
if (elAddr) elAddr.innerText = storeAddress;
try { document.title = 'Menu Pelanggan - ' + storeName; } catch(e) {}
var elIg = document.getElementById('social-ig');
if (elIg) elIg.href = socialInstagramUrl;
var elTt = document.getElementById('social-tt');
if (elTt) elTt.href = socialTiktokUrl;
var elMaps = document.getElementById('social-maps');
if (elMaps) elMaps.href = socialGmapsUrl;
var elLink = document.getElementById('social-link');
if (elLink) elLink.href = socialLinkUrl;
var elWaLink = document.getElementById('store-wa-link');
if (elWaLink) elWaLink.href = 'https://wa.me/' + String(storeWhatsapp || '').replace(/\\D/g, '');
var elWaText = document.getElementById('store-wa-text');
if (elWaText) {
var waDigits = String(storeWhatsapp || '').replace(/\\D/g, '');
if (waDigits.indexOf('62') === 0) elWaText.innerText = '0' + waDigits.slice(2);
else elWaText.innerText = waDigits || '-';
}
var wifiSsidEl = document.getElementById('wifi-ssid-text');
if (wifiSsidEl) wifiSsidEl.innerText = wifiSSID;
var wifiPassEl = document.getElementById('wifi-pass-text');
if (wifiPassEl) wifiPassEl.innerText = wifiPassword;
checkWifiLock();
}
if (initialLoad) {
renderCategories();
renderMenu();
}
renderActiveOrder();
}).withFailureHandler(function(err) {
if (syncBtn) {
syncBtn.innerText = 'SYNC';
syncBtn.style.opacity = '1';
}
document.getElementById('loading').style.display = 'none';
if (initialLoad) {
alert('Gagal memuat data. Pastikan koneksi internet stabil.\n' + (err && err.message ? err.message : err));
}
}).getInitialData();
}
function normalizeImageUrlClient(url) {
var raw = String(url || '').trim();
if (!raw) return '';
var m = raw.match(/\/d\/([^\/\?\s]+)/);
if (m && m[1]) return 'https://drive.google.com/thumbnail?id=' + m[1] + '&sz=w800';
return raw;
}
function openQris() {
var modal = document.getElementById('qris-modal');
var img = document.getElementById('qris-img');
var url = normalizeImageUrlClient(qrisImageUrl);
if (!url) { alert('QRIS belum diset. Hubungi kasir.'); return; }
if (img) img.src = url;
if (modal) modal.style.display = 'flex';
}
function downloadQris() {
var url = normalizeImageUrlClient(qrisImageUrl);
if (!url) return;
try {
fetch(url, { mode: 'cors' }).then(function(r) { return r.blob(); }).then(function(blob) {
var a = document.createElement('a');
var obj = URL.createObjectURL(blob);
a.href = obj;
a.download = 'QRIS-Fuku.png';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(function() { URL.revokeObjectURL(obj); }, 500);
}).catch(function() {
window.open(url, '_blank');
});
} catch(e) {
window.open(url, '_blank');
}
}
function renderCategories() {
var cats = ['Semua'];
menu.forEach(function(m) { if (cats.indexOf(m.kategori) === -1) cats.push(m.kategori); });
if (paketKustom && paketKustom.length > 0) cats.push('Paket Kustom');
if (cats.indexOf(kategoriAktif) === -1) kategoriAktif = 'Semua';
var box = document.getElementById('cat-container');
box.innerHTML = cats.map(function(c) {
return '<div class="tag ' + (c === kategoriAktif ? 'active' : '') + '" onclick="setKat(\'' + c + '\')">' + c + '</div>';
}).join('');
}
function setKat(c) {
kategoriAktif = c;
renderCategories();
renderMenu();
}
function renderMenu() {
var box = document.getElementById('menu-list');
if (!box) return;
box.innerHTML = '';
var orderable = [];
var infoOnly = [];
menu.forEach(function(m) {
var tampilDapur = String(m.tampilDapur || '').toLowerCase().trim();
var kategori = kategoriAktif === 'Semua' || m.kategori === kategoriAktif;
if (!kategori) return;
if (tampilDapur === 'tampil') orderable.push(m);
else infoOnly.push(m);
});
var paketList = [];
if (paketKustom && paketKustom.length > 0 && (kategoriAktif === 'Semua' || kategoriAktif === 'Paket Kustom')) {
paketList = paketKustom;
}
if (orderable.length === 0 && infoOnly.length === 0 && paketList.length === 0) {
box.innerHTML = '<div style="text-align:center; grid-column:1/-1; padding:40px; color:#a0aec0; font-weight:600;">Menu tidak tersedia untuk kategori ini.</div>';
return;
}
var fragment = document.createDocumentFragment();
orderable.forEach(function(m) {
var isHabis = Number(m.stok) <= 0;
var div = document.createElement('div');
div.className = 'menu-card' + (isHabis ? ' stok-habis' : '');
div.setAttribute('data-nama', m.nama);
div.onclick = function() { if(!isHabis) tambahKeKeranjang(m, 1); };
var imgUrl = m.gambar || 'https://via.placeholder.com/150';
var habisBadge = isHabis ? '<div class="badge-habis">HABIS</div>' : '';
div.innerHTML = '<div class="qty-badge">0x</div>' +
'<img src="' + imgUrl + '" loading="lazy">' +
habisBadge +
'<div class="menu-info">' +
'<div class="menu-name">' + m.nama + '</div>' +
'<div class="menu-price">Rp ' + Number(m.harga).toLocaleString('id-ID') + '</div>' +
'<div class="menu-stok">Sisa: ' + m.stok + '</div>' +
'</div>';
fragment.appendChild(div);
});
if (infoOnly.length > 0) {
var sep = document.createElement('div');
sep.style.cssText = 'grid-column:1/-1; margin-top:6px; padding:8px 10px; border-radius:12px; background:#fff; border:1px solid #eee; color:#64748b; font-weight:800; font-size:11px;';
sep.innerText = 'Menu Informasi (tidak bisa dipesan)';
fragment.appendChild(sep);
infoOnly.forEach(function(m) {
var div = document.createElement('div');
div.className = 'menu-card menu-disabled';
div.innerHTML = '<div class="menu-info">' +
'<div class="menu-name">' + m.nama + '</div>' +
'<div class="menu-price">Rp ' + Number(m.harga).toLocaleString('id-ID') + '</div>' +
'</div>';
fragment.appendChild(div);
});
}
if (paketList.length > 0) {
var sepP = document.createElement('div');
sepP.style.cssText = 'grid-column:1/-1; margin-top:10px; padding:8px 10px; border-radius:12px; background:#fff; border:1px solid #fee2e2; color:#e11d48; font-weight:900; font-size:11px;';
sepP.innerText = 'Paket Kustom (informasi, tidak bisa dipesan)';
fragment.appendChild(sepP);
paketList.forEach(function(p) {
var div = document.createElement('div');
div.className = 'menu-card menu-disabled';
var imgUrl = p.gambar || 'https://via.placeholder.com/150';
var itemsText = (p.items || []).map(function(it) { return it.qty + 'x ' + it.nama; }).join(', ');
div.innerHTML = '<img src="' + imgUrl + '" loading="lazy">' +
'<div class="menu-info">' +
'<div class="menu-name">' + p.nama + '</div>' +
'<div class="menu-price">Rp ' + Number(p.total).toLocaleString('id-ID') + '</div>' +
'<div class="menu-stok" style="color:#64748b;">Isi: ' + itemsText + '</div>' +
'</div>';
fragment.appendChild(div);
});
}
box.appendChild(fragment);
updateMenuBadges();
}
function updateMenuBadges() {
// Optimasi: Gunakan selector yang lebih spesifik atau cached references jika perlu
var cards = document.querySelectorAll('.menu-card[data-nama]');
cards.forEach(function(card) {
var nama = card.getAttribute('data-nama');
var item = keranjang.find(function(i) { return i.nama === nama; });
var badge = card.querySelector('.qty-badge');
if (item && item.qty > 0) {
if (badge) {
badge.innerText = item.qty + 'x';
badge.style.display = 'block';
}
card.classList.add('selected');
} else {
if (badge) badge.style.display = 'none';
card.classList.remove('selected');
}
});
}
function tambahKeKeranjang(item, delta) {
if (delta === undefined) delta = 1;
var existing = keranjang.find(function(i) { return i.nama === item.nama; });
if (existing) {
existing.qty += delta;
if (existing.qty <= 0) {
keranjang = keranjang.filter(function(i) { return i !== existing; });
}
} else if (delta > 0) {
keranjang.push({ nama: item.nama, harga: item.harga, qty: delta, kat: item.kategori });
}
// Surgical Update Badge
var card = document.querySelector('.menu-card[data-nama="' + item.nama + '"]');
if (card) {
var badge = card.querySelector('.qty-badge');
var qty = existing ? existing.qty : (delta > 0 ? delta : 0);
if (qty > 0) {
if (badge) {
badge.innerText = qty + 'x';
badge.style.display = 'block';
}
card.classList.add('selected');
} else {
if (badge) badge.style.display = 'none';
card.classList.remove('selected');
}
}
renderSidebarCart();
}
function updateQty(idx, delta) {
if (!keranjang[idx]) return;
var itemNama = keranjang[idx].nama;
keranjang[idx].qty += delta;
var newQty = keranjang[idx].qty;
if (keranjang[idx].qty <= 0) {
keranjang.splice(idx, 1);
newQty = 0;
}
// Surgical Update Badge
var card = document.querySelector('.menu-card[data-nama="' + itemNama + '"]');
if (card) {
var badge = card.querySelector('.qty-badge');
if (newQty > 0) {
if (badge) {
badge.innerText = newQty + 'x';
badge.style.display = 'block';
}
card.classList.add('selected');
} else {
if (badge) badge.style.display = 'none';
card.classList.remove('selected');
}
}
renderSidebarCart();
}
function renderSidebarCart() {
var list = document.getElementById('sidebar-cart-list');
var controls = document.getElementById('sidebar-cart-controls');
var countBadge = document.getElementById('cart-count-badge');
var floatingBar = document.getElementById('floating-cart-bar');
var fCount = document.getElementById('f-cart-count');
var fPrice = document.getElementById('f-cart-price');
var totalQty = keranjang.reduce(function(a,c) { return a + c.qty; }, 0);
countBadge.innerText = totalQty;
if (keranjang.length === 0) {
list.innerHTML = '<div class="muted" style="text-align:center; padding:40px 20px; color:#a0aec0; font-size:13px; font-weight:600;"><div style="font-size:30px; margin-bottom:10px;"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin:0 auto; display:block; opacity:0.5;"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg></div>Keranjang masih kosong.<br>Silakan pilih menu di sebelah kiri.</div>';
controls.style.display = 'none';
if (floatingBar) floatingBar.style.display = 'none';
return;
}
controls.style.display = 'block';
if (floatingBar) floatingBar.style.display = 'flex';
list.innerHTML = '';
var subtotal = 0;
keranjang.forEach(function(it, idx) {
var itemTotal = Number(it.qty) * Number(it.harga);
subtotal += itemTotal;
var div = document.createElement('div');
div.className = 'cart-item';
div.innerHTML = '<div class="cart-item-info">' +
'<div class="cart-item-name">' + it.nama + '</div>' +
'<div class="cart-item-price">' + it.qty + ' x ' + Number(it.harga).toLocaleString('id-ID') + ' = Rp ' + itemTotal.toLocaleString('id-ID') + '</div>' +
'</div>' +
'<div class="cart-qty-ctrl">' +
'<button class="qty-btn" onclick="updateQty(' + idx + ', -1)">-</button>' +
'<span class="qty-val">' + it.qty + '</span>' +
'<button class="qty-btn" onclick="updateQty(' + idx + ', 1)">+</button>' +
'</div>';
list.appendChild(div);
});
document.getElementById('sidebar-subtotal').innerText = 'Rp ' + subtotal.toLocaleString('id-ID');
document.getElementById('sidebar-total').innerText = 'Rp ' + subtotal.toLocaleString('id-ID');
if (fCount) fCount.innerText = totalQty;
if (fPrice) fPrice.innerText = 'Rp ' + subtotal.toLocaleString('id-ID');
}
function scrollToCart() {
var el = document.querySelector('.cart-sidebar');
if (el) {
el.scrollIntoView({ behavior: 'smooth' });
}
}
function scrollToActiveOrder() {
var el = document.getElementById('active-order-section');
if (el) {
el.scrollIntoView({ behavior: 'smooth' });
}
}
function prosesPesan() {
var active = transaksi.find(function(t) {
return String(t.meja) === String(mejaNo) && (t.status === 'Pending' || t.status === 'Ready');
});
var namaInput = document.getElementById('cart-nama').value.trim();
var waInput = document.getElementById('cart-wa').value.trim();
var nama = active ? (active.nama || '') : '';
var wa = active ? (active.wa || '') : '';
nama = nama || namaInput;
wa = normalizeWAClient(wa || waInput);
if (!nama) { alert('Mohon isi Nama Pemesan.'); return; }
if (!wa) { alert('Mohon isi nomor WhatsApp.'); return; }
if (keranjang.length === 0) { alert('Keranjang masih kosong.'); return; }
try { saveCustomerInfoSilent(); } catch(e) {}
var catatanBaru = document.getElementById('cart-catatan').value.trim();
var catatanLama = active ? (active.catatan || '') : '';
var catatanFinal = catatanLama;
if (catatanBaru) catatanFinal = catatanLama ? (catatanLama + " | " + catatanBaru) : catatanBaru;
var payload = {
id: active ? active.id : ('F-' + Date.now()),
meja: mejaNo,
status: 'Pending',
nama: nama,
wa: wa,
items: [],
catatan: catatanFinal,
tgl: active ? active.tgl : new Date().toISOString()
};
var mergedItems = active ? (active.items || []).slice() : [];
keranjang.forEach(function(newItem) {
var existing = mergedItems.find(function(ex) { return ex.nama === newItem.nama; });
if (existing) {
existing.qty = (Number(existing.qty) || 0) + (Number(newItem.qty) || 0);
} else {
mergedItems.push(newItem);
}
});
payload.items = mergedItems;
document.getElementById('loading').style.display = 'flex';
document.getElementById('loading').querySelector('p').innerText = 'Mengirim Pesanan...';
google.script.run.withSuccessHandler(function(res) {
alert('Pesanan berhasil dikirim! Silakan tunggu.');
keranjang = [];
if (res && res.transaksi) {
transaksi = res.transaksi;
renderActiveOrder();
}
updateMenuBadges();
renderSidebarCart();
document.getElementById('cart-catatan').value = '';
document.getElementById('loading').style.display = 'none';
if (promoPhotoData && !promoPhotoData.uploaded) {
var active = getActiveTransaction();
if (active && active.id) {
uploadReviewScreenshotForTransaction(active.id);
}
}
}).saveTransaction(payload);
}
function normalizeWAClient(wa) {
var clean = String(wa || '').replace(/\D/g, '');
if (!clean) return '';
if (clean.indexOf('0') === 0) clean = '62' + clean.slice(1);
else if (clean.indexOf('8') === 0) clean = '62' + clean;
return clean;
}
function getWhatsappDisplay(wa62) {
var wa = String(wa62 || '').replace(/\D/g, '');
if (!wa) return '-';
if (wa.indexOf('62') === 0) return '0' + wa.slice(2);
return wa;
}
function saveCustomerInfoSilent() {
var nama = document.getElementById('cart-nama').value.trim();
var wa = normalizeWAClient(document.getElementById('cart-wa').value.trim());
if (!nama || !wa) return;
google.script.run.saveCustomerInfo({ nama: nama, wa: wa, meja: mejaNo || '' });
}
function renderActiveOrder() {
var active = transaksi.find(function(t) {
return String(t.meja) === String(mejaNo) && (t.status === 'Pending' || t.status === 'Ready');
});
var section = document.getElementById('active-order-section');
var list = document.getElementById('active-order-list');
var totalEl = document.getElementById('active-order-total');
var badge = document.getElementById('active-status-badge');
if (!active || !active.items || active.items.length === 0) {
section.style.display = 'none';
return;
}
section.style.display = 'block';
if (active.status === 'Ready') {
badge.innerText = 'Siap Saji / Diantar';
badge.style.background = '#dcfce7';
badge.style.color = '#166534';
section.style.borderColor = '#22c55e';
} else {
badge.innerText = 'Sedang Dimasak';
badge.style.background = '#fff1f2';
badge.style.color = '#e11d48';
section.style.borderColor = '#e11d48';
}
list.innerHTML = '';
var total = 0;
active.items.forEach(function(it) {
var itemPrice = Number(it.harga) * Number(it.qty);
total += itemPrice;
var div = document.createElement('div');
div.style.cssText = 'display:flex; justify-content:space-between; margin-bottom:6px; font-weight:600;';
div.innerHTML = '<span>' + it.qty + 'x ' + it.nama + '</span>' +
'<span>Rp ' + itemPrice.toLocaleString('id-ID') + '</span>';
list.appendChild(div);
});
totalEl.innerHTML = 'TOTAL PESANAN: Rp ' + total.toLocaleString('id-ID');
// Hanya update jika input kosong agar tidak mengganggu pengetikan
var nameInput = document.getElementById('cart-nama');
var waInput = document.getElementById('cart-wa');
if (active.nama && !nameInput.value) nameInput.value = active.nama;
if (active.wa && !waInput.value) waInput.value = active.wa;
checkWifiLock(); // Update status kunci WiFi jika sudah ada pesanan aktif
}
function fmtRp(num) { return 'Rp ' + Number(num).toLocaleString('id-ID'); }
function generateReceiptText(p, isCheck) {
var width = 28;
var divider = new Array(width + 1).join('-') + '\n';
var pad = function(n) { return new Array(Math.max(0, n) + 1).join(' '); };
var center = function(txt) {
var space = Math.floor((width - txt.length) / 2);
if (space < 0) space = 0;
return pad(space) + txt + '\n';
};
var justify = function(l, r) {
var sl = String(l || '');
var sr = String(r || '');
var space = width - (sl.length + sr.length);
if (space < 1) return sl + '\n' + pad(width - sr.length) + sr + '\n';
return sl + pad(space) + sr + '\n';
};
var txt = '\n\n';
txt += divider;
txt += center(String(storeName || 'POS').toUpperCase());
var addrLines = String(storeAddress || '').split(',').map(function(s) { return String(s || '').trim(); }).filter(function(s) { return s; });
if (!addrLines.length && storeAddress) addrLines = [String(storeAddress)];
for (var ai = 0; ai < Math.min(2, addrLines.length); ai++) txt += center(addrLines[ai]);
txt += center(getWhatsappDisplay(storeWhatsapp));
if (isCheck) {
txt += divider;
txt += center("** PENGECEKAN **");
}
txt += divider;
var idFmt = 'F1' + String(p.id).replace(/\D/g, '').slice(-6).padStart(6, '0');
txt += justify("ID:" + idFmt, "Meja:" + p.meja);
if (p.nama && p.nama !== '-') txt += "Pelanggan: " + p.nama + '\n';
if (p.wa && p.wa !== '-') txt += "WA: " + p.wa + '\n';
txt += "Waktu: " + new Date().toLocaleString('id-ID', {day:'2-digit', month:'2-digit', hour:'2-digit', minute:'2-digit'}) + '\n';
txt += divider;
(p.items || []).forEach(function(i) {
var name = i.nama;
if (name.length > width) name = name.slice(0, width-3) + '...';
txt += name + '\n';
var detail = i.qty + 'x' + fmtRp(i.harga);
var sub = fmtRp(Number(i.qty) * Number(i.harga));
txt += justify(' ' + detail, sub);
});
txt += divider;
var subtotal = p.items.reduce(function(acc, i) { return acc + (Number(i.qty)*Number(i.harga)); }, 0);
txt += justify("TOTAL", fmtRp(subtotal));
if (!isCheck) {
txt += divider;
txt += center("TERIMA KASIH");
if (socialLinkUrl) txt += center(socialLinkUrl);
else if (socialInstagramUrl) txt += center(socialInstagramUrl);
if (socialInstagramUrl) txt += center(socialInstagramUrl);
} else {
txt += divider;
txt += center("** BELUM LUNAS **");
}
return txt + '\n\n\n.';
}
function cetakStrukPelanggan() {
var active = transaksi.find(function(t) {
return String(t.meja) === String(mejaNo) && (t.status === 'Pending' || t.status === 'Ready');
});
if (!active) return;
var isAndroid = /Android/i.test(navigator.userAgent);
var isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
var isMobile = isMobileDevice();
var text = generateReceiptText(active, true);
if (isMobile) {
if (isAndroid) {
try {
var escSmallFont = '\x1B\x21\x01';
var fullText = escSmallFont + text;
var base64Text = btoa(unescape(encodeURIComponent(fullText)));
window.location.href = 'rawbt:base64,' + base64Text;
} catch(e) { alert('Gagal memproses teks cetak untuk Android.'); }
} else if (isIOS) {
var printArea = document.getElementById('print-area');
printArea.innerHTML = '<pre style="font-family: \'Courier New\', monospace; font-size: 10pt; line-height: 1.1; background:#fff; color:#000; padding:20px; margin:0; width:300px; white-space: pre-wrap;">' + text + '</pre>';
printArea.style.display = 'block';
document.getElementById('loading').style.display = 'flex';
document.getElementById('loading').querySelector('p').innerText = 'MENYIAPKAN GAMBAR...';
setTimeout(function() {
html2canvas(printArea, { scale: 2 }).then(function(canvas) {
var imgData = canvas.toDataURL('image/png');
document.getElementById('struk-img').src = imgData;
document.getElementById('image-modal').style.display = 'flex';
printArea.style.display = 'none';
document.getElementById('loading').style.display = 'none';
document.getElementById('loading').querySelector('p').innerText = 'Memuat Menu...';
});
}, 500);
}
return;
}
var w = window.open('', '_blank', 'width=300,height=500');
w.document.write('<html><head><style>body{font-family:monospace;white-space:pre;font-size:10pt;}</style></head><bo' + 'dy>' + text + '</bo' + 'dy></ht' + 'ml>');
w.document.close();
w.print();
w.close();
}
function openWifiQR() {
var modal = document.getElementById('wifi-qr-modal');
var img = document.getElementById('wifi-qr-img');
var finalUrl = wifiQRUrl || 'https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=WIFI:S:FukuShabuGrill;T:WPA;P:Fusagi17;;';
if (finalUrl.indexOf('drive.google.com') > -1) {
var m = finalUrl.match(/\/d\/([^\/\?\s]+)/) || finalUrl.match(/id=([^\&\s]+)/);
if (m && m[1]) finalUrl = 'https://drive.google.com/thumbnail?id=' + m[1] + '&sz=w600';
}
img.src = finalUrl;
modal.style.display = 'flex';
}
function copyWifiPassword() {
var dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = wifiPassword;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
alert('Password WiFi disalin: ' + wifiPassword);
}
function isMobileDevice() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
// Fungsi untuk menangani upload foto promo
function handlePromoPhotoUpload(e) {
var file = e.target.files[0];
if (!file) return;
var maxSize = 500 * 1024; // 500KB dalam bytes
var minSize = 200 * 1024; // 200KB dalam bytes
var fileSize = file.size;
var photoInfo = document.getElementById('photo-info');
var photoActions = document.getElementById('photo-actions');
if (photoInfo) {
photoInfo.style.color = '#fbbf24';
photoInfo.innerHTML = 'Memproses foto...';
}
if (photoActions) photoActions.style.display = 'none';
// Baca file dan tampilkan preview
var reader = new FileReader();
reader.onload = function(event) {
var inputDataUrl = event.target.result;
var estimateBase64Bytes = function(b64) {
var padding = 0;
if (b64.slice(-2) === '==') padding = 2;
else if (b64.slice(-1) === '=') padding = 1;
return Math.floor((b64.length * 3) / 4) - padding;
};
var compressToRange = function(dataUrl, targetMin, targetMax, done) {
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var maxDim = 1280;
var baseScale = 1;
var maxSide = Math.max(img.width || 1, img.height || 1);
if (maxSide > maxDim) baseScale = maxDim / maxSide;
var attempt = function(scale) {
canvas.width = Math.max(1, Math.round((img.width || 1) * scale));
canvas.height = Math.max(1, Math.round((img.height || 1) * scale));
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.imageSmoothingEnabled = true;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var q = 0.9;
var bestDataUrl = null;
var bestBytes = Infinity;
for (var i = 0; i < 14; i++) {
var out = canvas.toDataURL('image/jpeg', q);
var outB64 = (out.split(',')[1] || '');
var bytes = estimateBase64Bytes(outB64);
if (bytes <= targetMax && bytes >= targetMin) {
bestDataUrl = out;
bestBytes = bytes;
break;
}
if (bytes <= targetMax && bytes < bestBytes) {
bestDataUrl = out;
bestBytes = bytes;
}
if (bytes > targetMax) q = q - 0.08;
else q = q + 0.05;
if (q < 0.35) q = 0.35;
if (q > 0.97) q = 0.97;
}
if (!bestDataUrl) bestDataUrl = canvas.toDataURL('image/jpeg', 0.7);
var bestB64 = (bestDataUrl.split(',')[1] || '');
var finalBytes = estimateBase64Bytes(bestB64);
if (finalBytes > targetMax && scale > 0.35) {
attempt(scale * 0.85);
return;
}
done(bestDataUrl);
};
attempt(baseScale);
};
img.onerror = function() {
done(dataUrl);
};
img.src = dataUrl;
};
var finalize = function(dataUrl) {
var matches = String(dataUrl || '').match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.*)$/);
var mimeType = 'image/jpeg';
var base64 = '';
if (matches) {
mimeType = matches[1] || 'image/jpeg';
base64 = matches[2] || '';
}
var bytes = estimateBase64Bytes(base64);
promoPhotoData = {
base64: base64,
mimeType: mimeType,
fileName: file.name,
uploaded: false
};
document.getElementById('preview-img').src = dataUrl;
document.getElementById('photo-preview').style.display = 'block';
if (photoActions) photoActions.style.display = 'block';
var rf = document.getElementById('review-form');
if (rf) rf.style.display = 'block';
if (photoInfo) {
photoInfo.innerHTML = '✓ ' + file.name + ' (' + (bytes / 1024).toFixed(2) + 'KB)';
photoInfo.style.color = (bytes > maxSize) ? '#f87171' : (bytes < minSize ? '#fbbf24' : '#a3e635');
}
};
if (fileSize > maxSize || fileSize < minSize || (file.type && file.type.indexOf('image/') === 0 && file.type !== 'image/jpeg')) {
compressToRange(inputDataUrl, minSize, maxSize, function(outDataUrl) {
finalize(outDataUrl);
});
} else {
finalize(inputDataUrl);
}
};
reader.readAsDataURL(file);
}
function submitReviewProof() {
if (!promoPhotoData || !promoPhotoData.base64) {
alert('Silakan pilih foto review terlebih dahulu.');
return;
}
if (promoPhotoData.uploaded) {
alert('Screenshot review sudah diunggah.');
return;
}
var submitBtn = document.getElementById('submit-review-btn');
var photoInfo = document.getElementById('photo-info');
submitBtn.disabled = true;
submitBtn.innerText = 'Mengunggah...';
photoInfo.style.color = '#fbbf24';
photoInfo.innerHTML = 'Mengunggah screenshot review...';
var active = getActiveTransaction();
var targetId = active && active.id ? active.id : ('REV-' + Date.now());
var namaReviewEl = document.getElementById('review-nama');
var waReviewEl = document.getElementById('review-wa');
var nama = (namaReviewEl ? namaReviewEl.value : '').trim() || document.getElementById('cart-nama').value.trim();
var waRaw = (waReviewEl ? waReviewEl.value : '').trim() || document.getElementById('cart-wa').value.trim();
var wa = normalizeWAClient(waRaw);
if (!active || !active.id) {
if (!nama) { alert('Mohon isi Nama untuk review.'); submitBtn.disabled = false; submitBtn.innerText = 'KIRIM REVIEW'; return; }
if (!wa) { alert('Mohon isi nomor WhatsApp untuk review.'); submitBtn.disabled = false; submitBtn.innerText = 'KIRIM REVIEW'; return; }
}
google.script.run.withSuccessHandler(function(uploadRes) {
if (!uploadRes || uploadRes.error || !uploadRes.url) {
alert('Gagal mengunggah screenshot review: ' + (uploadRes ? uploadRes.error : 'Tidak ada respons.'));
photoInfo.innerHTML = 'Gagal mengunggah screenshot review.';
photoInfo.style.color = '#f87171';
submitBtn.disabled = false;
submitBtn.innerText = 'KIRIM REVIEW';
return;
}
if (active && active.id) {
google.script.run.withSuccessHandler(function(res) {
promoPhotoData.uploaded = true;
submitBtn.innerText = 'Terkirim';
photoInfo.innerHTML = '✓ Screenshot tersimpan ke transaksi aktif.';
photoInfo.style.color = '#a3e635';
if (res && res.transaksi) {
transaksi = res.transaksi;
renderActiveOrder();
renderSidebarCart();
updateMenuBadges();
}
}).updateTransactionReviewPhoto(active.id, uploadRes.url);
} else {
var reviewPayload = {
id: targetId,
meja: mejaNo || '-',
status: 'Review',
nama: nama || 'Review',
wa: wa,
items: [],
catatan: 'Upload screenshot review tanpa transaksi',
tgl: new Date().toISOString(),
jam: '',
diskon: 0,
poinDipakai: 0,
pajakPersen: 0,
servicePersen: 0,
adminPersen: 0,
pajak: 0,
service: 0,
adminFee: 0,
total: 0,
dp: 0,
metodeDp: '',
tglDp: '',
metodeBayar: '',
bayar: 0,
kembali: 0,
buktiReview: uploadRes.url
};
google.script.run.withSuccessHandler(function(res) {
promoPhotoData.uploaded = true;
submitBtn.innerText = 'Terkirim';
photoInfo.innerHTML = '✓ Screenshot review tersimpan sebagai entri terpisah.';
photoInfo.style.color = '#a3e635';
if (res && res.transaksi) {
transaksi = res.transaksi;
renderActiveOrder();
renderSidebarCart();
updateMenuBadges();
}
try { google.script.run.saveCustomerInfo({ nama: nama || '', wa: wa || '', meja: mejaNo || '' }); } catch(e) {}
}).saveTransaction(reviewPayload);
}
}).uploadReviewProof({ id: targetId, base64: promoPhotoData.base64, mimeType: promoPhotoData.mimeType });
}
function clearPromoPhoto() {
document.getElementById('promo-photo').value = '';
promoPhotoData = null;
document.getElementById('photo-preview').style.display = 'none';
document.getElementById('photo-actions').style.display = 'none';
document.getElementById('review-form').style.display = 'none';
document.getElementById('photo-info').innerHTML = '';
}
</script>
</body>
</html>