Files
photo-req/src/public/upload.html
T
2026-05-11 00:01:48 +09:00

161 lines
5.7 KiB
HTML

<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{title}}</title>
<link rel="stylesheet" href="{{cssPath}}">
</head>
<body>
<main class="upload-page">
<section class="wedding-card" aria-labelledby="archive-title">
<div class="poster">
<img class="poster__art" src="/assets/wedding-illustration.png?v=single-illustration-1" alt="">
<div class="poster__plate">
<h1 id="archive-title">{{title}}</h1>
<p class="poster__date">2026.05.24 SUN</p>
<p class="poster__subtitle">OUR WEDDING ARCHIVE</p>
</div>
</div>
<form class="upload-panel" method="post" action="/r/{{slug}}" enctype="multipart/form-data">
<p class="upload-panel__today">오늘의 순간</p>
<label class="select-button file-picker">
<span>사진 선택하기</span>
<input type="file" name="files" accept="{{accept}}" multiple required>
</label>
<div class="file-summary" hidden>
<p class="file-summary__title">선택한 파일</p>
<ul class="file-list"></ul>
</div>
<button class="upload-button" type="submit" aria-label="업로드">
<span>업로드하기</span>
</button>
<p class="upload-status" aria-live="polite"></p>
</form>
<section class="thank-you" hidden>
<p class="thank-you__eyebrow">THANK YOU</p>
<h2>감사합니다</h2>
<p>소중한 오늘을 함께 남겨주셔서 감사드립니다.</p>
<a class="upload-again" href="/r/{{slug}}">사진 더 올리기</a>
</section>
</section>
</main>
<script>
document.querySelectorAll(".upload-panel").forEach((form) => {
const input = form.querySelector('input[type="file"][name="files"]');
const submit = form.querySelector('button[type="submit"][aria-label="업로드"]');
const summary = form.querySelector(".file-summary");
const list = form.querySelector(".file-list");
const status = form.querySelector(".upload-status");
const thanks = document.querySelector(".thank-you");
let previewUrls = [];
submit.disabled = true;
const formatBytes = (bytes) => {
if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
if (bytes >= 1024) return `${Math.round(bytes / 1024)} KB`;
return `${bytes} B`;
};
const setStatus = (message, type = "") => {
status.textContent = message;
status.dataset.type = type;
};
const clearPreviews = () => {
previewUrls.forEach((url) => URL.revokeObjectURL(url));
previewUrls = [];
};
input?.addEventListener("change", () => {
const files = Array.from(input.files || []);
clearPreviews();
list.replaceChildren();
if (files.length === 0) {
summary.hidden = true;
submit.disabled = true;
setStatus("");
return;
}
files.forEach((file) => {
const item = document.createElement("li");
const meta = document.createElement("div");
const name = document.createElement("span");
const size = document.createElement("span");
const preview = document.createElement("div");
const previewUrl = URL.createObjectURL(file);
previewUrls.push(previewUrl);
meta.className = "file-list__meta";
preview.className = "file-list__preview";
name.textContent = file.name;
size.textContent = formatBytes(file.size);
if (file.type.startsWith("video/")) {
const video = document.createElement("video");
video.src = previewUrl;
video.muted = true;
video.playsInline = true;
video.preload = "metadata";
preview.append(video);
} else if (file.type.startsWith("image/")) {
const image = document.createElement("img");
image.src = previewUrl;
image.alt = "";
preview.append(image);
} else {
preview.textContent = "FILE";
}
meta.append(name, size);
item.append(meta, preview);
list.append(item);
});
summary.hidden = false;
submit.disabled = false;
setStatus(`${files.length}개 파일을 선택했어요.`);
});
form.addEventListener("submit", async (event) => {
event.preventDefault();
if (!input.files || input.files.length === 0) {
setStatus("먼저 사진을 선택해 주세요.", "error");
return;
}
submit.disabled = true;
form.dataset.uploading = "true";
setStatus("업로드 중이에요. 화면을 닫지 말아 주세요.");
try {
const response = await fetch(form.action, {
method: "POST",
body: new FormData(form),
headers: { "Accept": "application/json" }
});
const result = await response.json().catch(() => ({}));
if (!response.ok || !result.ok) throw new Error(result.message || "업로드 중 오류가 났어요.");
form.hidden = true;
thanks.hidden = false;
thanks.querySelector("p:last-of-type").innerHTML =
`${result.count || input.files.length}개의 파일 잘 받았습니다.<br>소중한 오늘을 함께 남겨주셔서 감사드립니다.`;
} catch (error) {
submit.disabled = false;
form.dataset.uploading = "false";
setStatus(error.message || "업로드 중 오류가 났어요. 다시 시도해 주세요.", "error");
}
});
});
</script>
</body>
</html>