diff --git a/public/admin.css b/public/admin.css index 4f0b7d3..bddfece 100644 --- a/public/admin.css +++ b/public/admin.css @@ -126,6 +126,85 @@ label { display: grid; gap: 6px; color: var(--muted); font-size: 13px; } .message .by { font-size: 12px; color: var(--muted); margin-bottom: 4px; } .message p { margin: 0; white-space: pre-wrap; } .message a { color: var(--brand); display: inline-block; margin-top: 6px; } +.attachments { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 8px; +} +.photo-preview { + width: 116px; + display: grid; + gap: 5px; + padding: 0; + border: 1px solid var(--line); + border-radius: 8px; + background: white; + color: var(--ink); + overflow: hidden; + text-align: left; +} +.photo-preview img { + width: 100%; + aspect-ratio: 4 / 3; + object-fit: cover; + display: block; + background: #edf3ef; +} +.photo-preview span { + padding: 0 7px 7px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--muted); + font-size: 12px; +} +.photo-modal { + position: fixed; + inset: 0; + z-index: 20; + display: grid; + place-items: center; + padding: 24px; +} +.photo-backdrop { + position: absolute; + inset: 0; + border: 0; + border-radius: 0; + background: rgb(0 0 0 / 72%); +} +.photo-modal figure { + position: relative; + z-index: 1; + margin: 0; + max-width: min(1100px, 94vw); + max-height: 90vh; + display: grid; + gap: 8px; +} +.photo-modal img { + max-width: 100%; + max-height: 82vh; + object-fit: contain; + border-radius: 8px; + background: white; +} +.photo-modal figcaption { + color: white; + text-align: center; + font-size: 13px; +} +.photo-close { + position: absolute; + top: 10px; + right: 10px; + width: 34px; + height: 34px; + padding: 0; + border-radius: 50%; + background: rgb(0 0 0 / 54%); +} .typing { padding: 0 20px 10px; color: var(--brand); font-size: 13px; } .reply { display: grid; diff --git a/public/admin.html b/public/admin.html index 0bc3381..569aac6 100644 --- a/public/admin.html +++ b/public/admin.html @@ -4,6 +4,7 @@ MaalFlows Admin + diff --git a/public/admin.js b/public/admin.js index 7c10a04..3f29cf9 100644 --- a/public/admin.js +++ b/public/admin.js @@ -87,6 +87,12 @@ $("#statusSelect").addEventListener("change", async () => { await loadConversations(); }); +$("#messages").addEventListener("click", (event) => { + const preview = event.target.closest("[data-photo-url]"); + if (!preview) return; + openPhotoPreview(preview.dataset.photoUrl, preview.dataset.photoName); +}); + async function showApp() { $("#loginView").classList.add("hidden"); $("#appView").classList.remove("hidden"); @@ -188,12 +194,57 @@ function renderActive() {
${escapeHtml(message.senderName || message.senderType)} · ${escapeHtml(message.createdAt)}

${escapeHtml(message.body)}

- ${message.attachments.map((a) => `${escapeHtml(a.name)}`).join("")} + ${renderAttachments(message.attachments)}
`).join(""); $("#messages").scrollTop = $("#messages").scrollHeight; } +function renderAttachments(attachments) { + if (!attachments.length) return ""; + return `
${attachments.map((attachment) => { + if (!attachment.type?.startsWith("image/")) { + return `${escapeHtml(attachment.name)}`; + } + return ` + + `; + }).join("")}
`; +} + +function openPhotoPreview(url, name) { + let modal = $("#photoModal"); + if (!modal) { + modal = document.createElement("div"); + modal.id = "photoModal"; + modal.className = "photo-modal hidden"; + modal.innerHTML = ` + +
+ + +
+
+ `; + document.body.append(modal); + modal.addEventListener("click", (event) => { + if (event.target.closest(".photo-close") || event.target.classList.contains("photo-backdrop")) { + modal.classList.add("hidden"); + } + }); + document.addEventListener("keydown", (event) => { + if (event.key === "Escape") modal.classList.add("hidden"); + }); + } + modal.querySelector("img").src = url; + modal.querySelector("img").alt = name || "Fotka"; + modal.querySelector("figcaption").textContent = name || ""; + modal.classList.remove("hidden"); +} + async function saveSite(patch) { const data = await api("/api/admin/site", { method: "PATCH", body: patch }); state.site = data.site; diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..59552ce --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/preview.html b/public/preview.html index fc657a2..b8800ef 100644 --- a/public/preview.html +++ b/public/preview.html @@ -4,6 +4,7 @@ MaalFlows Preview +