Skip to content

Commit 3e153d5

Browse files
SOIVclaude
andcommitted
feat(web): Phase 1.5.5 설정 저장 UX 구현
- 변경 감지(dirty state): 표시 이름·언어 변경 시 저장 버튼 활성화 (테마는 변경 즉시 localStorage 저장이므로 dirty 체크 제외) - 저장 피드백: 클릭 시 로딩 상태(500ms) → 모달 닫힘 → 상단 notice 표시 - 미저장 경고: 변경 후 닫기 시 브라우저 기본 confirm 창으로 처리 - notice 메시지 한국어로 통일 ("설정이 저장되었습니다.") Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent dfc03e2 commit 3e153d5

3 files changed

Lines changed: 49 additions & 11 deletions

File tree

apps/web/src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ function App({ installMode }: { installMode: InstallMode }) {
377377
return next;
378378
});
379379
}}
380-
onSaved={() => setNotice("Settings saved (mock).")}
380+
onSaved={() => setNotice("설정이 저장되었습니다.")}
381381
/>
382382
)}
383383
</AppShell>

apps/web/src/views/SettingsView.tsx

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,64 @@ interface SettingsViewProps {
1515
onSaved: () => void;
1616
}
1717

18-
export function SettingsView({ isAdmin, theme, onThemeChange, onToggleAdmin, onClose, onSaved }: SettingsViewProps) {
19-
const [displayName, setDisplayName] = useState("");
20-
const [language, setLanguage] = useState("ko");
18+
const INIT_DISPLAY_NAME = "";
19+
const INIT_LANGUAGE = "ko";
2120

22-
const handleSave = () => {
23-
onSaved();
21+
export function SettingsView({
22+
isAdmin,
23+
theme,
24+
onThemeChange,
25+
onToggleAdmin,
26+
onClose,
27+
onSaved,
28+
}: SettingsViewProps) {
29+
const [displayName, setDisplayName] = useState(INIT_DISPLAY_NAME);
30+
const [language, setLanguage] = useState(INIT_LANGUAGE);
31+
const [isSaving, setIsSaving] = useState(false);
32+
33+
// 테마는 변경 즉시 localStorage에 저장되므로 dirty 체크 제외
34+
const isDirty = displayName !== INIT_DISPLAY_NAME || language !== INIT_LANGUAGE;
35+
36+
const handleClose = () => {
37+
if (isDirty && !window.confirm("저장하지 않은 변경사항이 있습니다. 닫으시겠습니까?")) return;
2438
onClose();
2539
};
2640

41+
const handleSave = () => {
42+
setIsSaving(true);
43+
setTimeout(() => {
44+
setIsSaving(false);
45+
onSaved();
46+
onClose();
47+
}, 500);
48+
};
49+
2750
return (
2851
<Modal
2952
open
30-
onClose={onClose}
53+
onClose={handleClose}
3154
title="General Settings"
3255
size="md"
3356
footer={
3457
<>
35-
<Button type="button" onClick={onClose}>취소</Button>
36-
<Button variant="primary" type="button" onClick={handleSave}>저장</Button>
58+
<Button type="button" onClick={handleClose} disabled={isSaving}>
59+
취소
60+
</Button>
61+
<Button
62+
variant="primary"
63+
type="button"
64+
onClick={handleSave}
65+
disabled={!isDirty || isSaving}
66+
loading={isSaving}
67+
>
68+
저장
69+
</Button>
3770
</>
3871
}
3972
>
40-
<p className="settings-dialog-subtitle">프로필, 환경설정, 권한 테스트 상태를 관리합니다.</p>
73+
<p className="settings-dialog-subtitle">
74+
프로필, 환경설정, 권한 테스트 상태를 관리합니다.
75+
</p>
4176

4277
<div className="settings-dialog-tabs">
4378
<span className="settings-tab">Profile</span>

docs/v2_FINANCIAL-LEDGER/roadmap/01-development-plan.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ Control 전체 목록과 상태 관리는 별도 문서에서 관리:
209209
- [x] Protected Route 정책 구현 (권한 부족 시 리다이렉트) - shell
210210
- [ ] 관리자 PIN 관리 UI 구현 (최초 설정/변경/오류 처리)
211211
- [ ] 관리자 세션 만료 UX 구현 (30분 만료 시 재인증 모달)
212-
- [ ] 일반 설정 저장 UX 보강 (저장 성공/실패, 미저장 변경 경고)
212+
- [x] 일반 설정 저장 UX 보강 (저장 성공/실패, 미저장 변경 경고)
213+
214+
> 변경 감지(dirty state) 구현 — 변경 없으면 저장 버튼 비활성화. 저장 클릭 시 로딩(500ms) 후 모달 닫힘 + notice 표시. 미저장 상태에서 닫기 시 브라우저 기본 confirm 창으로 경고.
213215
- [ ] 관리자 활동/감사 로그 화면 진입점 정의 (PIN 실패/주요 설정 변경 확인)
214216

215217
#### 1.5.6 UX 품질 기준
@@ -322,6 +324,7 @@ Control 전체 목록과 상태 관리는 별도 문서에서 관리:
322324
| 2026-04-12 | 1차 UI/UX 전면 개편. 다크 모드 디자인 토큰 시스템 구축 및 고정 220px 좌측 사이드바 레이아웃으로 재설계. AppShell A/B/C/D 변형 폐기 후 단일 Shell로 통합. 로그인/홈/설정/관리자 CSS 전체를 다크 토큰 기반으로 전환 |
323325
| 2026-04-12 | 임시 비밀번호 첫 로그인 강제 변경 화면(ChangePasswordView) 구현. 관리자 역할(isAdmin)과 PIN 인증(isPinVerified) 상태 분리 — 역할 보유자도 Admin 페이지 진입 시 PIN 재인증 필요. 비관리자 Admin 진입점 사이드바에서 숨김. Marketplace 사이드바 진입점 추가(Phase 3 플레이스홀더). @fieldstack/core ESM 빌드 전환 |
324326
| 2026-04-13 | P0/P0.5 Control 전 항목 `packages/controls`에 React 컴포넌트 구현 완료 (`ready: true`). `controls.css` 작성(fs- 접두사). `global.css` 토큰을 라이트 기본값 + 다크 오버라이드(`[data-theme]`/`prefers-color-scheme`) 구조로 재설계. Settings 테마 셀렉터 실제 동작 연결 (localStorage + data-theme 적용). |
327+
| 2026-04-15 | Phase 1.5.5 일반 설정 저장 UX 완료. SettingsView dirty state 감지, 저장 로딩 피드백, 미저장 변경 시 브라우저 confirm 경고 구현. |
325328
| 2026-04-14 | Phase 1.5.3 로그인 UX 개선 완료. 로그인 실패/잠금/세션 만료 UX 구현 (인라인 에러 텍스트, 5회 잠금 Alert, 30분 잠금). `ForgotPasswordView` 전면 재구성 — 경로 선택(이메일/관리자 토큰) → 각 경로별 단계 흐름 구현. 관리자 토큰 경로에 이메일+토큰 쌍 검증 구조 추가. Mock 계정 시스템 도입 (admin@/user@ 세트, 로그인 시 역할 자동 적용). `@fieldstack/core/browser` 브라우저 전용 엔트리포인트 분리 — Vite 번들링 시 Node.js 전용 패키지(jsonwebtoken 등) 포함 문제 해결. |
326329

327330
---

0 commit comments

Comments
 (0)