/* ZEN SUSHI — Painel de blog funcional (/admin). Login simples → gerenciar artigos (criar, editar, excluir) com upload de imagem. Posts salvos no navegador (localStorage) via window.KZ_BLOG. */ const { Button: AB, Badge: ABd, Card: ACd, Eyebrow: AEy, KatanaDivider: AKD } = window.KAZUKIDesignSystem_63706c; const adminSeo = window.KZ_SEO; const BLOG = window.KZ_BLOG; const CATEGORIAS = ["Novidades", "Cozinha", "Vinhos", "Confeitaria", "Experiências", "Promoções"]; const AUTH_KEY = "zenSushiAdminAuth"; /* Redimensiona a imagem enviada para no máx. 1280px e exporta JPEG (q .82), mantendo o localStorage leve. Retorna data URL (base64). */ function fileToOptimizedDataURL(file, maxW) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const img = new Image(); img.onload = () => { const scale = Math.min(1, maxW / img.width); const w = Math.round(img.width * scale); const h = Math.round(img.height * scale); const c = document.createElement("canvas"); c.width = w; c.height = h; c.getContext("2d").drawImage(img, 0, 0, w, h); try { resolve(c.toDataURL("image/jpeg", 0.82)); } catch (e) { resolve(reader.result); } }; img.onerror = reject; img.src = reader.result; }; reader.onerror = reject; reader.readAsDataURL(file); }); } function emptyDraft() { return { titulo: "", resumo: "", autor: "Equipe ZEN SUSHI", categoria: "Novidades", data: new Date().toISOString().slice(0, 10), img: "", imgAlt: "", topicos: [{ h2: "", p: "" }], dicas: { h3: "", p: "" }, cta: "Reserve sua mesa no ZEN SUSHI", slug: "", }; } function AdminPage() { const [authed, setAuthed] = React.useState(() => sessionStorage.getItem(AUTH_KEY) === "1"); const [user, setUser] = React.useState(""); const [pass, setPass] = React.useState(""); const [err, setErr] = React.useState(false); const [mode, setMode] = React.useState("list"); // list | edit const [draft, setDraft] = React.useState(emptyDraft()); const [originalSlug, setOriginalSlug] = React.useState(null); const [refresh, setRefresh] = React.useState(0); const [toast, setToast] = React.useState(""); const [busy, setBusy] = React.useState(false); React.useEffect(() => { adminSeo.track("admin_access", { autenticado: authed }); }, []); React.useEffect(() => { if (!toast) return; const t = setTimeout(() => setToast(""), 2600); return () => clearTimeout(t); }, [toast]); const login = (e) => { e.preventDefault(); if (user.trim() && pass.trim()) { setAuthed(true); setErr(false); sessionStorage.setItem(AUTH_KEY, "1"); adminSeo.track("admin_access", { autenticado: true, usuario: user }); } else { setErr(true); } }; const logout = () => { setAuthed(false); sessionStorage.removeItem(AUTH_KEY); }; /* ---------- LOGIN ---------- */ if (!authed) { return (
🔒

Área Restrita

Painel de blog do ZEN SUSHI. Esta página é noindex, nofollow.

setUser(e.target.value)} placeholder="admin" autoComplete="username" />
setPass(e.target.value)} placeholder="••••••••" autoComplete="current-password" />
{err && Preencha usuário e senha (demo: qualquer valor).} Entrar no painel

Acesso somente para a equipe.

); } /* ---------- helpers de edição ---------- */ const startNew = () => { setDraft(emptyDraft()); setOriginalSlug(null); setMode("edit"); }; const startEdit = (post) => { setDraft({ titulo: post.titulo || "", resumo: post.resumo || "", autor: post.autor || "Equipe ZEN SUSHI", categoria: post.categoria || "Novidades", data: post.data || new Date().toISOString().slice(0, 10), img: post.img || "", imgAlt: post.imgAlt || "", topicos: (post.topicos && post.topicos.length ? post.topicos.map((t) => ({ h2: t.h2 || "", p: t.p || "" })) : [{ h2: "", p: "" }]), dicas: post.dicas ? { h3: post.dicas.h3 || "", p: post.dicas.p || "" } : { h3: "", p: "" }, cta: post.cta || "Reserve sua mesa no ZEN SUSHI", slug: post.slug || "", }); setOriginalSlug(post.slug); setMode("edit"); }; const set = (k, v) => setDraft((d) => ({ ...d, [k]: v })); const setTopico = (i, k, v) => setDraft((d) => { const t = d.topicos.slice(); t[i] = { ...t[i], [k]: v }; return { ...d, topicos: t }; }); const addTopico = () => setDraft((d) => ({ ...d, topicos: d.topicos.concat([{ h2: "", p: "" }]) })); const removeTopico = (i) => setDraft((d) => ({ ...d, topicos: d.topicos.filter((_, j) => j !== i) })); const onImage = async (file) => { if (!file) return; setBusy(true); try { const url = await fileToOptimizedDataURL(file, 1280); set("img", url); if (!draft.imgAlt) set("imgAlt", (draft.titulo || "Imagem") + " — ZEN SUSHI, restaurante em Cotia"); } catch (e) { setToast("Falha ao carregar a imagem."); } setBusy(false); }; const canSave = draft.titulo.trim() && draft.resumo.trim(); const save = (publishView) => { if (!canSave) { setToast("Preencha pelo menos o título e o resumo."); return; } let slug; try { slug = BLOG.save(draft, originalSlug); } catch (e) { setToast("Não foi possível salvar (armazenamento cheio?)."); return; } adminSeo.track("blog_post_save", { slug, novo: !originalSlug }); setRefresh((n) => n + 1); if (publishView) { location.hash = "#/blog/" + slug; } else { setMode("list"); setToast("Artigo salvo com sucesso."); } }; const del = (post) => { if (!window.confirm('Excluir o artigo "' + post.titulo + '"? Esta ação não pode ser desfeita.')) return; BLOG.remove(post.slug); adminSeo.track("blog_post_delete", { slug: post.slug }); setRefresh((n) => n + 1); setToast("Artigo excluído."); }; const userPosts = BLOG.listUser().slice().sort((a, b) => (b.data || "").localeCompare(a.data || "")); const staticPosts = (window.KZ_DATA.BLOG || []); /* ---------- EDITOR ---------- */ if (mode === "edit") { return (
{toast &&
{toast}
}
{originalSlug ? "Editando artigo" : "Novo artigo"}

{originalSlug ? "Editar" : "Escrever"}

setMode("list")}>← Voltar à lista
set("titulo", e.target.value)} placeholder="Ex.: 5 segredos do sushi perfeito" />