receipts/index.html

137 lines
3.8 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Upload Receipts</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
<style>
#previews img {
padding: 0.25em;
}
</style>
</head>
<body>
<div class="container">
<h1>Upload Receipts</h1>
<form id="upload-form">
<div>
<input
id="image"
type="file"
name="image[]"
accept="image/*,*.png,*.jpg,*.jpeg,*.jpx"
capture="environment"
multiple
/>
</div>
<div class="grid" id="previews">
</div>
<div><input type="reset"><button id="submit" type="submit" disabled>Submit</button></div>
</div>
</form>
<dialog id="dialog">
<article>
<h2 id="dialog-title">[upload result]</h2>
<p id="dialog-text">[result details]</p>
</article>
</dialog>
<script>
(() => {
const clearPreviews = () => {
while (previews.children.length > 0) {
previews.removeChild(previews.children[0]);
}
}
const setPreviews = () => {
for (const i of image.files) {
const el = document.createElement("div");
const img = document.createElement("img");
img.src = URL.createObjectURL(i);
el.appendChild(img);
const inpDate = document.createElement("input");
inpDate.type = "date";
inpDate.required = true;
inpDate.name = "date[]";
el.appendChild(inpDate);
const inpNotes = document.createElement("input");
inpNotes.name = "notes[]";
inpNotes.placeholder = "Notes ...";
//el.appendChild(inpNotes);
previews.appendChild(el);
}
}
const showDialog = (title, text) => {
dialog_title.innerText = title;
dialog_text.innerText = text;
dialog.show();
};
const image = document.getElementById("image");
const previews = document.getElementById("previews");
const form = document.getElementById("upload-form");
const submit = document.getElementById("submit");
const dialog = document.getElementById("dialog");
const dialog_title = document.getElementById("dialog-title");
const dialog_text = document.getElementById("dialog-text");
image.addEventListener("change", (e) => {
clearPreviews();
setPreviews();
submit.disabled = !image.files;
});
form.addEventListener("reset", () => {
submit.disabled = true;
clearPreviews();
});
form.addEventListener("submit", (e) => {
submit.setAttribute("aria-busy", "true");
const data = new FormData(form);
fetch("/", {
method: "POST",
body: data,
})
.then((r) => {
submit.removeAttribute("aria-busy");
if (r.ok) {
showDialog(
"Upload Success",
"Successfully uploaded receipts"
);
} else {
r.text().then((msg) => {
showDialog(
"Upload Failure",
`Failed to upload receipts: ${r.statusText}\n${msg}`,
);
});
}
form.reset();
})
.catch((e) => {
submit.removeAttribute("aria-busy");
showDialog("Upload Failure", e.toString());
});
e.preventDefault();
});
document.addEventListener("click", (e) => {
if (dialog.open) {
const elem = dialog.querySelector("*");
if (!elem.contains(e.target)) {
dialog.close();
}
}
});
if (image.files.length > 0) {
setPreviews();
}
})();
</script>
</body>
</html>