From 57983c1329e677770a9ed2846cc77e7fc86e6bd9 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 6 Jul 2026 21:11:28 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=EB=B9=84=EB=8F=99?= =?UTF-8?q?=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EB=A1=9C=EB=94=A9=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(UX/A11y=20=ED=96=A5=EC=83=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `demo.js`: `createActionButton`, `retryActiveJob`, `refreshKpiEvidence`, `submitDocument`에서 네트워크 요청 진행 시 버튼을 비활성화하고 로딩 텍스트 표기, `aria-busy` 속성 추가. - 원래의 버튼 텍스트를 변수에 저장하여 동적으로 복원하도록 처리. - `viewer.js`: `retryBtn`에 대해 일관되게 `aria-busy` 속성을 업데이트하도록 상태 반영. - 비동기 작업에 대한 명시적인 상태를 사용자에게 제공함으로써 혼란을 방지하고 이중 클릭 등의 오류 예방 (접근성 향상 포함). --- .../resources/static/assets/viewer/demo.js | 32 ++++++++++++++++++- .../resources/static/assets/viewer/viewer.js | 3 ++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/resources/static/assets/viewer/demo.js b/src/main/resources/static/assets/viewer/demo.js index 84eb8f3a..2bf70826 100644 --- a/src/main/resources/static/assets/viewer/demo.js +++ b/src/main/resources/static/assets/viewer/demo.js @@ -111,7 +111,19 @@ function createActionButton(label, onClick) { button.type = "button"; button.textContent = label; button.className = "btn btn-secondary btn-compact"; - button.addEventListener("click", onClick); + button.addEventListener("click", async (event) => { + const originalText = button.textContent; + button.disabled = true; + button.setAttribute("aria-busy", "true"); + button.textContent = "Loading..."; + try { + await Promise.resolve(onClick(event)); + } finally { + button.disabled = false; + button.removeAttribute("aria-busy"); + button.textContent = originalText; + } + }); return button; } @@ -270,7 +282,10 @@ async function retryActiveJob() { const jobId = activeJobDetail.jobId; setStatus("Requesting operator retry..."); + const originalText = el.retryJobBtn.textContent; el.retryJobBtn.disabled = true; + el.retryJobBtn.setAttribute("aria-busy", "true"); + el.retryJobBtn.textContent = "Retrying..."; try { const res = await fetch(`/api/v1/convert/jobs/${encodeURIComponent(jobId)}/retry`, { method: "POST", @@ -307,6 +322,8 @@ async function retryActiveJob() { } catch (err) { setError("Network error while requesting retry. Retry when the service is reachable."); } finally { + el.retryJobBtn.removeAttribute("aria-busy"); + el.retryJobBtn.textContent = originalText; el.retryJobBtn.disabled = false; } } @@ -380,7 +397,11 @@ async function refreshKpis() { } async function refreshKpiEvidence() { + const originalText = el.refreshEvidenceBtn.textContent; try { + el.refreshEvidenceBtn.disabled = true; + el.refreshEvidenceBtn.setAttribute("aria-busy", "true"); + el.refreshEvidenceBtn.textContent = "Refreshing..."; const { res, data } = await fetchJson(KPI_EXPORTS_ENDPOINT); if (!res.ok) { el.kpiExportStatus.textContent = "Snapshot evidence is unavailable for the current tenant claim."; @@ -390,6 +411,10 @@ async function refreshKpiEvidence() { renderKpiEvidence(data); } catch (err) { el.kpiExportStatus.textContent = "Snapshot evidence is unavailable while the service is unreachable."; + } finally { + el.refreshEvidenceBtn.removeAttribute("aria-busy"); + el.refreshEvidenceBtn.textContent = originalText; + el.refreshEvidenceBtn.disabled = false; } } @@ -435,7 +460,10 @@ async function submitDocument(event) { return; } + const originalText = el.submitBtn.textContent; el.submitBtn.disabled = true; + el.submitBtn.setAttribute("aria-busy", "true"); + el.submitBtn.textContent = "Submitting..."; setStatus("Submitting document..."); try { @@ -478,6 +506,8 @@ async function submitDocument(event) { addFailedHistory(file.name, "FAILED"); setError("Network error while submitting. Retry when the service is reachable."); } finally { + el.submitBtn.removeAttribute("aria-busy"); + el.submitBtn.textContent = originalText; el.submitBtn.disabled = false; } } diff --git a/src/main/resources/static/assets/viewer/viewer.js b/src/main/resources/static/assets/viewer/viewer.js index e4499b16..876d9d38 100644 --- a/src/main/resources/static/assets/viewer/viewer.js +++ b/src/main/resources/static/assets/viewer/viewer.js @@ -51,6 +51,7 @@ function setLoading(message) { el.liveStatus.textContent = message; el.preview.setAttribute("aria-busy", "true"); el.retryBtn.disabled = true; + el.retryBtn.setAttribute("aria-busy", "true"); el.retryBtn.textContent = "Refreshing..."; } @@ -60,6 +61,7 @@ function showError(message) { el.liveStatus.textContent = ""; el.preview.setAttribute("aria-busy", "false"); el.errorTitle.focus(); + el.retryBtn.removeAttribute("aria-busy"); el.retryBtn.disabled = false; el.retryBtn.textContent = "Refresh"; } @@ -209,6 +211,7 @@ async function poll(docId, abortSignal) { el.preview.setAttribute("aria-busy", "false"); el.liveStatus.textContent = "Ready."; + el.retryBtn.removeAttribute("aria-busy"); el.retryBtn.disabled = false; el.retryBtn.textContent = "Refresh";