137 lines
3.8 KiB
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>
|