feat: initial Jimi Gallery prototype
- Public site (Home/Artists/Exhibitions/News/About/Contact) with EN/KO/JA i18n - Admin panel with login, CRUD, image upload, multilingual editing - Exhibition slider/lightbox view - FastAPI + MongoDB backend, JWT auth - Docker Compose deployment, behind nginx at jimi.yakenator.io
This commit is contained in:
127
admin/settings.html
Normal file
127
admin/settings.html
Normal file
@ -0,0 +1,127 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Settings — Jimi Gallery Admin</title>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;600&family=Cormorant+Garamond:wght@400;500;600&family=Inter:wght@300;400;500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="stylesheet" href="admin.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<div id="sidebar-slot"></div>
|
||||
<div class="content">
|
||||
<div class="topbar">
|
||||
<div>
|
||||
<h1 id="pg-title"></h1>
|
||||
<div class="subtitle" id="pg-sub"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<form id="form" class="form">
|
||||
<div>
|
||||
<label id="lbl-gn"></label>
|
||||
<input id="f-galleryName" />
|
||||
</div>
|
||||
<div>
|
||||
<label id="lbl-tagline"></label>
|
||||
<div id="f-tagline-wrap"></div>
|
||||
</div>
|
||||
<div class="full">
|
||||
<label id="lbl-address"></label>
|
||||
<input id="f-address" />
|
||||
</div>
|
||||
<div>
|
||||
<label id="lbl-phone"></label>
|
||||
<input id="f-phone" />
|
||||
</div>
|
||||
<div>
|
||||
<label id="lbl-email"></label>
|
||||
<input id="f-email" />
|
||||
</div>
|
||||
<div class="full">
|
||||
<label id="lbl-hours"></label>
|
||||
<div id="f-hours-wrap"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label id="lbl-ig"></label>
|
||||
<input id="f-instagram" />
|
||||
</div>
|
||||
<div class="full">
|
||||
<label id="lbl-about"></label>
|
||||
<div id="f-about-wrap"></div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="submit" class="btn" id="btn-save"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../assets/i18n.js"></script>
|
||||
<script src="../assets/data.js"></script>
|
||||
<script src="admin.js"></script>
|
||||
<script>
|
||||
(async () => {
|
||||
requireAuth();
|
||||
try { await Store.load(); } catch (e) { return showBootError(e); }
|
||||
mountSidebar("settings");
|
||||
|
||||
document.getElementById("pg-title").textContent = t("admin.page.settings");
|
||||
document.getElementById("pg-sub").textContent = t("admin.page.settings_sub");
|
||||
document.getElementById("lbl-gn").textContent = t("admin.form.gallery_name");
|
||||
document.getElementById("lbl-tagline").textContent = t("admin.form.tagline");
|
||||
document.getElementById("lbl-address").textContent = t("admin.form.address");
|
||||
document.getElementById("lbl-phone").textContent = t("admin.form.phone");
|
||||
document.getElementById("lbl-email").textContent = t("admin.form.email");
|
||||
document.getElementById("lbl-hours").textContent = t("admin.form.hours");
|
||||
document.getElementById("lbl-ig").textContent = t("admin.form.instagram");
|
||||
document.getElementById("lbl-about").textContent = t("admin.form.about");
|
||||
document.getElementById("btn-save").textContent = t("admin.btn.save_changes");
|
||||
|
||||
const s = Store.settings();
|
||||
document.getElementById("f-galleryName").value = s.galleryName || "";
|
||||
document.getElementById("f-address").value = s.address || "";
|
||||
document.getElementById("f-phone").value = s.phone || "";
|
||||
document.getElementById("f-email").value = s.email || "";
|
||||
document.getElementById("f-instagram").value = s.instagram || "";
|
||||
|
||||
const fTagline = mountMultilingualField(document.getElementById("f-tagline-wrap"), {
|
||||
type: "input",
|
||||
value: s.tagline || ""
|
||||
});
|
||||
const fHours = mountMultilingualField(document.getElementById("f-hours-wrap"), {
|
||||
type: "input",
|
||||
value: s.hours || ""
|
||||
});
|
||||
const fAbout = mountMultilingualField(document.getElementById("f-about-wrap"), {
|
||||
type: "textarea",
|
||||
value: s.about || "",
|
||||
minHeight: "180px"
|
||||
});
|
||||
|
||||
document.getElementById("form").addEventListener("submit", async (ev) => {
|
||||
ev.preventDefault();
|
||||
try {
|
||||
await Store.updateSettings({
|
||||
galleryName: document.getElementById("f-galleryName").value.trim(),
|
||||
tagline: fTagline.get(),
|
||||
address: document.getElementById("f-address").value.trim(),
|
||||
phone: document.getElementById("f-phone").value.trim(),
|
||||
email: document.getElementById("f-email").value.trim(),
|
||||
hours: fHours.get(),
|
||||
instagram: document.getElementById("f-instagram").value.trim(),
|
||||
about: fAbout.get()
|
||||
});
|
||||
toast(t("admin.toast.settings_saved"));
|
||||
} catch (err) { alert(err.message || err); }
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user