88 lines
2.8 KiB
JavaScript
88 lines
2.8 KiB
JavaScript
// Minimal vanilla-JS client for the suggestions page. Talks to the JSON API
|
|
// exposed by routes_flask.py / routes_fastapi.py. No build step, no framework.
|
|
|
|
const $ = (id) => document.getElementById(id);
|
|
|
|
async function loadIssues() {
|
|
const list = $("s-list");
|
|
if (!list) return;
|
|
try {
|
|
const res = await fetch("/api/suggestions?state=open");
|
|
const data = await res.json();
|
|
const issues = data.issues || [];
|
|
if (!issues.length) {
|
|
list.innerHTML = '<p class="muted">No open suggestions yet.</p>';
|
|
return;
|
|
}
|
|
list.innerHTML = issues.map((i) => `
|
|
<div class="card">
|
|
<div class="issue-title">#${i.number} ${escapeHtml(i.title)}</div>
|
|
<div class="issue-meta">${i.comments} comment(s) ·
|
|
<a href="${i.html_url}" target="_blank" rel="noopener">view on Gitea</a></div>
|
|
</div>`).join("");
|
|
} catch (e) {
|
|
list.innerHTML = '<p class="muted">Could not load suggestions.</p>';
|
|
}
|
|
}
|
|
|
|
function escapeHtml(s) {
|
|
return (s || "").replace(/[&<>"']/g, (c) => (
|
|
{ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]
|
|
));
|
|
}
|
|
|
|
async function uploadImage(file) {
|
|
const fd = new FormData();
|
|
fd.append("file", file);
|
|
const res = await fetch("/api/suggestions/upload", { method: "POST", body: fd });
|
|
if (!res.ok) throw new Error("upload failed");
|
|
return (await res.json()).url;
|
|
}
|
|
|
|
function wireComposer() {
|
|
const send = $("s-send");
|
|
const body = $("s-body");
|
|
if (!send || !body) return;
|
|
|
|
// Paste-to-attach: drop an image embed into the body.
|
|
body.addEventListener("paste", async (ev) => {
|
|
const item = [...(ev.clipboardData?.items || [])].find((i) => i.type.startsWith("image/"));
|
|
if (!item) return;
|
|
ev.preventDefault();
|
|
$("s-status").textContent = "Uploading image…";
|
|
try {
|
|
const url = await uploadImage(item.getAsFile());
|
|
body.value += `\n\n`;
|
|
$("s-status").textContent = "Image attached.";
|
|
} catch {
|
|
$("s-status").textContent = "Image upload failed.";
|
|
}
|
|
});
|
|
|
|
send.addEventListener("click", async () => {
|
|
const title = $("s-title").value.trim();
|
|
if (title.length < 3) { $("s-status").textContent = "Add a longer title."; return; }
|
|
send.disabled = true;
|
|
$("s-status").textContent = "Sending…";
|
|
try {
|
|
const res = await fetch("/api/suggestions", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ title, body: body.value }),
|
|
});
|
|
if (!res.ok) throw new Error();
|
|
$("s-title").value = "";
|
|
body.value = "";
|
|
$("s-status").textContent = "Thanks! Sent.";
|
|
loadIssues();
|
|
} catch {
|
|
$("s-status").textContent = "Could not send — try again.";
|
|
} finally {
|
|
send.disabled = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
wireComposer();
|
|
loadIssues();
|