diff --git a/.jules/palette.md b/.jules/palette.md index a1633627..820f33d1 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -1,3 +1,6 @@ ## 2024-05-18 - Disabled and Loading States for Async Actions **Learning:** Adding explicit loading and disabled states to asynchronous action buttons (like "Refresh") provides immediate feedback, reducing user confusion and preventing double-submissions. **Action:** Always ensure that buttons triggering network requests visually indicate the loading state and are disabled until the request completes. +## 2026-07-09 - 버튼 내 로딩 텍스트 +**Learning:** 비동기 작업 시 버튼 HTML을 임시로 로딩 텍스트로 교체하여 진행 상태를 시각적으로 보여주는 것은 사용자 경험(UX)을 크게 향상시킵니다. +**Action:** 비동기 작업을 시작하기 전에 버튼의 원래 innerHTML을 저장하고, 진행 상태를 나타내도록 HTML을 업데이트한 뒤, finally 블록에서 원래 HTML로 복원합니다. diff --git a/src/main/resources/static/assets/viewer/demo.js b/src/main/resources/static/assets/viewer/demo.js index 84eb8f3a..d6c7d6c8 100644 --- a/src/main/resources/static/assets/viewer/demo.js +++ b/src/main/resources/static/assets/viewer/demo.js @@ -271,6 +271,8 @@ async function retryActiveJob() { const jobId = activeJobDetail.jobId; setStatus("Requesting operator retry..."); el.retryJobBtn.disabled = true; + const originalRetryHtml = el.retryJobBtn.innerHTML; + el.retryJobBtn.innerHTML = "Requesting retry..."; try { const res = await fetch(`/api/v1/convert/jobs/${encodeURIComponent(jobId)}/retry`, { method: "POST", @@ -308,6 +310,7 @@ async function retryActiveJob() { setError("Network error while requesting retry. Retry when the service is reachable."); } finally { el.retryJobBtn.disabled = false; + el.retryJobBtn.innerHTML = originalRetryHtml; } } @@ -437,6 +440,8 @@ async function submitDocument(event) { el.submitBtn.disabled = true; setStatus("Submitting document..."); + const originalBtnHtml = el.submitBtn.innerHTML; + el.submitBtn.innerHTML = "Submitting..."; try { const body = new FormData(); @@ -479,6 +484,7 @@ async function submitDocument(event) { setError("Network error while submitting. Retry when the service is reachable."); } finally { el.submitBtn.disabled = false; + el.submitBtn.innerHTML = originalBtnHtml; } } diff --git a/src/test/java/com/clearfolio/viewer/controller/ViewerUiControllerTest.java b/src/test/java/com/clearfolio/viewer/controller/ViewerUiControllerTest.java index dadb8949..c244b173 100644 --- a/src/test/java/com/clearfolio/viewer/controller/ViewerUiControllerTest.java +++ b/src/test/java/com/clearfolio/viewer/controller/ViewerUiControllerTest.java @@ -116,6 +116,7 @@ void demoScriptUsesExistingApiAndSessionHistory() throws Exception { assertTrue(script.contains("lastInspectedAt")); assertTrue(script.contains("openJobDetail")); assertTrue(script.contains("retryActiveJob")); + assertTrue(script.contains("innerHTML")); assertTrue(script.contains("/retry")); assertTrue(script.contains("X-Clearfolio-Operator-Id")); assertTrue(script.contains("X-Clearfolio-Tenant-Id"));