DINO SSW 飲食料品製造業

/* ===== SALES GATE: PIN login + lazy per-field content loading ===== */ function deviceId(){ let d = null; try{ d = localStorage.getItem("ssw_device_id"); }catch(e){} if(!d){ d = (window.crypto && crypto.randomUUID) ? crypto.randomUUID() : "dev-" + Date.now() + "-" + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2); try{ localStorage.setItem("ssw_device_id", d); }catch(e){} } return d; } function authHeaders(){ let t = null; try{ t = localStorage.getItem("ssw_token"); }catch(e){} return t ? {"Authorization": "Bearer " + t} : {}; } async function api(path, opts){ const o = opts || {}; o.headers = Object.assign({"Content-Type": "application/json"}, authHeaders(), o.headers || {}); return fetch(path, o); } /* --- gate overlay --- */ const GATE = document.createElement("div"); GATE.id = "gate"; document.body.appendChild(GATE); function showLoading(msg){ GATE.style.display = "flex"; GATE.innerHTML = `
⏳

${esc(msg || "Memuat…")}

`; } function showGate(err){ GATE.style.display = "flex"; GATE.innerHTML = `
Maskot DINO SSW

DINO SSW

Masukkan PIN akses kamu untuk mulai belajar.
PIN didapat setelah pembelian.

${err ? esc(err) : ""}

1 PIN hanya berlaku untuk 1 perangkat.
Belum punya PIN? Hubungi penjual.

`; const inp = document.getElementById("pin-inp"); inp.focus(); inp.addEventListener("keydown", e=>{ if(e.key === "Enter") doLogin(); }); } function hideGate(){ GATE.style.display = "none"; GATE.innerHTML = ""; } async function doLogin(){ const inp = document.getElementById("pin-inp"); const pin = (inp.value || "").trim().toUpperCase(); const errEl = document.getElementById("pin-err"); if(!pin){ errEl.textContent = "Isi PIN dulu ya."; return; } errEl.textContent = "Memeriksa PIN…"; try{ const r = await api("/api/login", {method: "POST", body: JSON.stringify({pin, deviceId: deviceId()})}); const data = await r.json().catch(()=>({})); if(!r.ok){ errEl.textContent = data.error || "PIN tidak valid."; return; } if(data.token){ try{ localStorage.setItem("ssw_token", data.token); }catch(e){} } }catch(e){ errEl.textContent = "Tidak ada koneksi internet. Coba lagi."; return; } showLoading("Login berhasil. Memuat data…"); const ok = await ensureField(FIELD); if(ok){ hideGate(); addLogout(); loadState(); go("beranda"); } } async function doLogout(){ try{ await api("/api/logout", {method: "POST"}); }catch(e){} try{ localStorage.removeItem("ssw_token"); }catch(e){} location.reload(); } function addLogout(){ if(document.getElementById("logoutbtn")) return; const b = document.createElement("button"); b.className = "langbtn"; b.id = "logoutbtn"; b.textContent = "🔒 Keluar"; b.onclick = ()=>askConfirm("Keluar dari perangkat ini? PIN tetap terikat ke perangkat ini.", doLogout); const flex = document.querySelector("header.top > div"); if(flex) flex.appendChild(b); } /* --- beranda welcome banner (used by renderBeranda in sales build) --- */ window.salesBannerHTML = function(){ return `
Maskot DINO SSW

DINO SSW

Belajar SSW nggak harus semangat terus — yang penting konsisten. Yuk, lanjut!
`; }; /* --- lazy field loading (overrides app1 setField) --- */ async function ensureField(id){ if(DB[id]) return true; let r; try{ r = await api("/content/" + id + ".json"); } catch(e){ showGate("Gagal memuat data. Periksa koneksi internet, lalu coba masuk lagi."); return false; } if(r.status === 401){ try{ localStorage.removeItem("ssw_token"); }catch(e){} showGate("Sesi berakhir. Masuk lagi dengan PIN kamu."); return false; } if(!r.ok){ showGate("Gagal memuat data bidang. Coba lagi nanti."); return false; } DB[id] = await r.json(); return true; } function applyField(id){ FIELD = id; loadState(); K.idx = 0; K.flipped = false; W.idx = 0; W.flipped = false; QZ = null; if(SM){ clearInterval(smTimer); SM = null; } go("beranda"); } function setField(id){ if(DB[id]){ applyField(id); return; } showLoading("Memuat data bidang…"); ensureField(id).then(ok=>{ hideGate(); if(ok) applyField(id); }); } /* --- boot --- */ (async function bootSales(){ showLoading("Memeriksa sesi…"); let logged = false; try{ const r = await api("/api/me"); logged = r.ok; }catch(e){} if(!logged){ showGate(); return; } const ok = await ensureField(FIELD); if(ok){ hideGate(); addLogout(); loadState(); go("beranda"); } })();