Improve guest upload flow

This commit is contained in:
DESKTOP-KSVGT20\shkim
2026-05-10 21:23:17 +09:00
parent 328065db18
commit 59bdfbc76f
3 changed files with 308 additions and 19 deletions
+91 -5
View File
@@ -22,21 +22,107 @@
<form class="upload-panel" method="post" action="/r/{{slug}}" enctype="multipart/form-data">
<p class="upload-panel__today">오늘의 순간</p>
<label class="upload-button file-picker" aria-label="업로드">
<span>사진 올리</span>
<label class="select-button file-picker">
<span>사진 선택하</span>
<input type="file" name="files" accept="{{accept}}" multiple required>
</label>
<button class="submit-fallback" type="submit" aria-label="업로드">사진 올리기</button>
<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"]');
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");
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;
};
input?.addEventListener("change", () => {
if (input.files && input.files.length > 0) submit?.click();
const files = Array.from(input.files || []);
list.replaceChildren();
if (files.length === 0) {
summary.hidden = true;
submit.disabled = true;
setStatus("");
return;
}
files.forEach((file) => {
const item = document.createElement("li");
const name = document.createElement("span");
const size = document.createElement("span");
name.textContent = file.name;
size.textContent = formatBytes(file.size);
item.append(name, size);
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").textContent =
`${result.count || input.files.length}개 파일을 잘 받았어요. 소중한 오늘을 함께 남겨주셔서 고마워요.`;
} catch (error) {
submit.disabled = false;
form.dataset.uploading = "false";
setStatus(error.message || "업로드 중 오류가 났어요. 다시 시도해 주세요.", "error");
}
});
});
</script>