96 lines
3.4 KiB
JavaScript
96 lines
3.4 KiB
JavaScript
// Feature request form
|
||
(() => {
|
||
const $ = (id) => document.getElementById(id);
|
||
const sections = {
|
||
form: $("s-form"),
|
||
processing: $("s-processing"),
|
||
thanks: $("s-thanks"),
|
||
};
|
||
const show = (name) => {
|
||
for (const [k, el] of Object.entries(sections)) el.classList.toggle("hidden", k !== name);
|
||
};
|
||
|
||
// Persist name + email for next submission
|
||
for (const k of ["name", "email"]) {
|
||
const v = localStorage.getItem("req_" + k);
|
||
if (v) $(k).value = v;
|
||
$(k).addEventListener("input", () => localStorage.setItem("req_" + k, $(k).value));
|
||
}
|
||
|
||
// ── File handling ──
|
||
const fileInput = $("file-input");
|
||
const dropZone = $("file-drop");
|
||
const dropText = $("file-drop-text");
|
||
let attachedFile = null;
|
||
|
||
function setFile(f) {
|
||
if (!f) return;
|
||
attachedFile = f;
|
||
const size = f.size > 1024 * 1024
|
||
? `${(f.size / 1024 / 1024).toFixed(1)} MB`
|
||
: `${(f.size / 1024).toFixed(0)} kB`;
|
||
dropZone.classList.add("has-file");
|
||
dropText.innerHTML = `
|
||
<span class="file-pill">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||
<polyline points="14 2 14 8 20 8"/>
|
||
</svg>
|
||
${escapeHtml(f.name)} <span style="color:var(--text-tertiary)">(${size})</span>
|
||
<button type="button" class="file-pill-clear" id="file-clear">×</button>
|
||
</span>
|
||
`;
|
||
$("file-clear").addEventListener("click", clearFile);
|
||
}
|
||
function clearFile() {
|
||
attachedFile = null;
|
||
fileInput.value = "";
|
||
dropZone.classList.remove("has-file");
|
||
dropText.innerHTML = `Přetáhněte soubor sem nebo
|
||
<button type="button" class="btn-link" id="file-pick-btn">vyberte z disku</button>`;
|
||
$("file-pick-btn").addEventListener("click", () => fileInput.click());
|
||
}
|
||
|
||
$("file-pick-btn").addEventListener("click", () => fileInput.click());
|
||
fileInput.addEventListener("change", (e) => setFile(e.target.files[0]));
|
||
|
||
["dragenter", "dragover"].forEach((ev) =>
|
||
dropZone.addEventListener(ev, (e) => { e.preventDefault(); dropZone.classList.add("drag-over"); }));
|
||
["dragleave", "drop"].forEach((ev) =>
|
||
dropZone.addEventListener(ev, (e) => { e.preventDefault(); dropZone.classList.remove("drag-over"); }));
|
||
dropZone.addEventListener("drop", (e) => { e.preventDefault(); setFile(e.dataTransfer.files[0]); });
|
||
|
||
// ── Submit ──
|
||
$("request-form").addEventListener("submit", async (e) => {
|
||
e.preventDefault();
|
||
show("processing");
|
||
try {
|
||
const fd = new FormData();
|
||
fd.append("title", $("title").value);
|
||
fd.append("description", $("description").value);
|
||
fd.append("name", $("name").value);
|
||
fd.append("email", $("email").value);
|
||
if (attachedFile) fd.append("file", attachedFile);
|
||
|
||
const r = await fetch("api/submit", { method: "POST", body: fd });
|
||
if (!r.ok) throw new Error((await r.json()).detail || r.statusText);
|
||
show("thanks");
|
||
} catch (err) {
|
||
alert("Chyba: " + err.message);
|
||
show("form");
|
||
}
|
||
});
|
||
|
||
$("another-btn").addEventListener("click", () => {
|
||
$("title").value = "";
|
||
$("description").value = "";
|
||
clearFile();
|
||
show("form");
|
||
});
|
||
|
||
function escapeHtml(s) {
|
||
return String(s).replace(/[&<>"']/g, (c) =>
|
||
({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
||
}
|
||
})();
|