/* ========================================================================== M GALA Admin — shared shell (sidebar, toast, avatar fallback, modal) ========================================================================== */ (function () { "use strict"; var Store = window.MGala.Store; var NAV = [ { href: "index.html", label: "Dashboard", icon: "fa-house" }, { href: "nominations.html", label: "Nominations", icon: "fa-clipboard-list" }, { href: "nominees.html", label: "Nominees", icon: "fa-users" }, { href: "categories.html", label: "Categories", icon: "fa-tags" }, { href: "featured.html", label: "Featured Woman", icon: "fa-star" } ]; var PASSCODE = "mgala2026"; function isUnlocked() { try { return sessionStorage.getItem("mgala_admin_ok") === "1"; } catch (e) { return false; } } function unlock() { try { sessionStorage.setItem("mgala_admin_ok", "1"); } catch (e) {} } function renderLockScreen() { var overlay = document.createElement("div"); overlay.id = "admin-lock-overlay"; overlay.innerHTML = '
' + '
' + '' + '
Admin Workspace
' + '

Enter Passcode

' + '

This area is private. Enter the admin passcode to continue.

' + '
' + '' + '
Incorrect passcode — try again.
' + '' + '
' + '← Back to public site' + '
' + '
'; document.body.appendChild(overlay); var form = document.getElementById("lock-form"); var input = document.getElementById("lock-input"); var error = document.getElementById("lock-error"); form.addEventListener("submit", function (e) { e.preventDefault(); if (input.value.trim() === PASSCODE) { unlock(); location.reload(); } else { error.classList.add("show"); input.value = ""; input.focus(); } }); } window.MGala.renderAdminShell = function (active) { if (!isUnlocked()) { renderLockScreen(); return; } var shell = document.querySelector(".admin-shell"); var mainContent = document.getElementById("admin-content"); var backdrop = document.createElement("div"); backdrop.className = "sidebar-backdrop"; backdrop.id = "sb-backdrop"; var aside = document.createElement("aside"); aside.className = "sidebar"; aside.id = "sidebar"; aside.innerHTML = '
M GALAAdmin Workspace
' + '' + ''; shell.insertBefore(aside, shell.firstChild); shell.insertBefore(backdrop, shell.firstChild); var contentCol = document.createElement("div"); contentCol.style.flex = "1"; contentCol.style.minWidth = "0"; shell.insertBefore(contentCol, mainContent); var topbar = document.createElement("div"); topbar.className = "admin-topbar"; topbar.innerHTML = '' + '
M GALA
' + ''; contentCol.appendChild(topbar); contentCol.appendChild(mainContent); document.getElementById("sb-open").addEventListener("click", function () { document.getElementById("sidebar").classList.add("open"); document.getElementById("sb-backdrop").classList.add("open"); }); document.getElementById("sb-backdrop").addEventListener("click", function () { document.getElementById("sidebar").classList.remove("open"); document.getElementById("sb-backdrop").classList.remove("open"); }); }; /* ---------- avatar fallback ---------- */ function initialsAvatar(name) { var initials = (name || "?").split(" ").filter(Boolean).slice(0, 2).map(function (s) { return s[0]; }).join("").toUpperCase(); var svg = '' + '' + '' + initials + ""; return "data:image/svg+xml;base64," + btoa(svg); } window.MGala.attachImgFallback = function (img, name) { img.addEventListener("error", function onErr() { img.removeEventListener("error", onErr); img.src = initialsAvatar(name); }); }; window.MGala.wireImgFallbacks = function (root) { (root || document).querySelectorAll("img[data-name]").forEach(function (img) { window.MGala.attachImgFallback(img, img.getAttribute("data-name")); }); }; /* ---------- toast ---------- */ var toastTimer; window.MGala.toast = function (msg, icon) { var el = document.getElementById("mg-toast"); if (!el) { el = document.createElement("div"); el.id = "mg-toast"; el.className = "toast"; document.body.appendChild(el); } el.innerHTML = '' + msg + ""; el.classList.add("show"); clearTimeout(toastTimer); toastTimer = setTimeout(function () { el.classList.remove("show"); }, 3400); }; window.MGala.timeAgoStr = function (h) { return Store.timeAgo(h); }; /* ---------- photo picker: upload (compressed) + silhouette swatches ---------- */ window.MGala.readAndCompressImage = function (file, maxWidth, callback) { maxWidth = maxWidth || 700; var reader = new FileReader(); reader.onload = function (e) { var img = new Image(); img.onload = function () { var scale = Math.min(1, maxWidth / img.width); var w = Math.round(img.width * scale); var h = Math.round(img.height * scale); var canvas = document.createElement("canvas"); canvas.width = w; canvas.height = h; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, w, h); callback(canvas.toDataURL("image/jpeg", 0.82)); }; img.onerror = function () { window.MGala.toast("Could not read that image.", "fa-triangle-exclamation"); }; img.src = e.target.result; }; reader.onerror = function () { window.MGala.toast("Could not read that file.", "fa-triangle-exclamation"); }; reader.readAsDataURL(file); }; window.MGala.renderPhotoPicker = function (container, current) { // current: { photo, photoStyle } — photoStyle is one of Store.SILHOUETTE_STYLES or null (real upload) var state = { photo: current.photo, photoStyle: current.photoStyle || null }; container.innerHTML = '
' + '
' + '
' + '' + '
or choose a placeholder
' + '
' + '
' + '
'; var previewImg = container.querySelector("#pp-preview-img"); var swatchesEl = container.querySelector("#pp-swatches"); function renderSwatches() { swatchesEl.innerHTML = Store.SILHOUETTE_STYLES.map(function (styleKey) { var uri = Store.silhouetteUri(styleKey); var sel = state.photoStyle === styleKey; return ''; }).join(""); swatchesEl.querySelectorAll("[data-style]").forEach(function (btn) { btn.addEventListener("click", function () { var styleKey = btn.getAttribute("data-style"); state.photo = Store.silhouetteUri(styleKey); state.photoStyle = styleKey; previewImg.src = state.photo; renderSwatches(); }); }); } renderSwatches(); container.querySelector("#pp-file-input").addEventListener("change", function (e) { var file = e.target.files && e.target.files[0]; if (!file) return; window.MGala.readAndCompressImage(file, 700, function (dataUrl) { state.photo = dataUrl; state.photoStyle = null; previewImg.src = dataUrl; renderSwatches(); window.MGala.toast("Photo uploaded — remember to save.", "fa-image"); }); }); return { get: function () { return { photo: state.photo, photoStyle: state.photoStyle }; } }; }; })();