/* ============================================================================= * B01_AccountDetail_UI_Page.ts * 로그인 후 01: 계정 상세 (기본 정보 수정 + 비밀번호 변경) * * 제약 준수 (frontend.md): * - 모든 문구는 ui_template_locale.ts 참조 (하드코딩 금지, §3). * - 색상/간격은 theme.css 변수 + 공통 컴포넌트만 사용 (§1, §2). * - 이벤트 핸들러는 onB01_[기능]_[액션] 명명 (§4). * - 백엔드 전송 전 1차 유효성 검사 (§4). * ========================================================================== */ import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale"; import { createButton, createInputField, createCard, showToast } from "@ui/ui_template_elements"; import { isBlank } from "@util/common_util_validate"; import "./B01_AccountDetail_UI_Style.css"; /** locale 헬퍼 */ function L(key: keyof typeof ui_locales): string { return ui_locales[key][currentLanguageIndex]; } /** 비밀번호 최소 길이 (A07 회원가입과 동일 기준) */ const PASSWORD_MIN_LENGTH = 8; /* ----------------------------------------------------------------------------- * 페이지 진입점 * -------------------------------------------------------------------------- */ export function renderB01AccountDetail(root: HTMLElement): void { const page = document.createElement("div"); page.className = "b01-account"; const header = document.createElement("div"); header.className = "b01-account__header"; const title = document.createElement("h1"); title.className = "b01-account__title"; title.textContent = L("B01_Account_Title"); const subtitle = document.createElement("p"); subtitle.className = "b01-account__subtitle"; subtitle.textContent = L("B01_Account_Subtitle"); header.append(title, subtitle); page.append(header, buildProfileCard(), buildSecurityCard()); root.append(page); } /* ----------------------------------------------------------------------------- * 기본 정보 카드 (회사/이름/이메일/연락처) * -------------------------------------------------------------------------- */ function buildProfileCard(): HTMLDivElement { // 회사명·이메일은 읽기 전용(가입 시 확정), 이름·연락처만 수정 가능 const companyField = createInputField({ label: L("B01_Account_Field_Company"), value: "", }); companyField.input.readOnly = true; companyField.input.classList.add("b01-account__readonly"); const emailField = createInputField({ label: L("B01_Account_Field_Email"), type: "email", value: "", }); emailField.input.readOnly = true; emailField.input.classList.add("b01-account__readonly"); const nameField = createInputField({ label: L("B01_Account_Field_Name"), required: true, }); const phoneField = createInputField({ label: L("B01_Account_Field_Phone"), placeholder: L("B01_Account_Field_Phone_Placeholder"), type: "text", }); const saveBtn = createButton({ label: L("B01_Account_Save_Profile"), variant: "filled", onClick: onB01_Profile_Save_Click, }); saveBtn.classList.add("b01-account__submit"); function onB01_Profile_Save_Click(): void { nameField.setError(); if (isBlank(nameField.input.value)) { nameField.setError(L("B01_Account_Error_Required")); return; } // TODO: PATCH /api/b01/profile 연동 (로딩 오버레이 + 성공/실패 처리) showToast(L("B01_Account_Success_Profile"), "success"); } const grid = document.createElement("div"); grid.className = "b01-account__grid"; grid.append(companyField.root, emailField.root, nameField.root, phoneField.root); return createCard({ title: L("B01_Account_Section_Profile"), body: [grid, saveBtn], raised: true, }); } /* ----------------------------------------------------------------------------- * 보안 카드 (비밀번호 변경) * -------------------------------------------------------------------------- */ function buildSecurityCard(): HTMLDivElement { const currentPwField = createInputField({ label: L("B01_Account_Field_CurrentPw"), type: "password", required: true, }); const newPwField = createInputField({ label: L("B01_Account_Field_NewPw"), type: "password", required: true, }); const confirmPwField = createInputField({ label: L("B01_Account_Field_ConfirmPw"), type: "password", required: true, }); const saveBtn = createButton({ label: L("B01_Account_Save_Password"), variant: "ghost", onClick: onB01_Password_Save_Click, }); saveBtn.classList.add("b01-account__submit"); function onB01_Password_Save_Click(): void { currentPwField.setError(); newPwField.setError(); confirmPwField.setError(); const current = currentPwField.input.value; const next = newPwField.input.value; const confirm = confirmPwField.input.value; // 1차 유효성: 빈 값 검사 let hasError = false; if (isBlank(current)) { currentPwField.setError(L("B01_Account_Error_Required")); hasError = true; } if (isBlank(next)) { newPwField.setError(L("B01_Account_Error_Required")); hasError = true; } if (isBlank(confirm)) { confirmPwField.setError(L("B01_Account_Error_Required")); hasError = true; } if (hasError) return; // 길이 검사 if (next.length < PASSWORD_MIN_LENGTH) { newPwField.setError(L("B01_Account_Error_PwLength")); return; } // 일치 검사 if (next !== confirm) { confirmPwField.setError(L("B01_Account_Error_PwMismatch")); return; } // TODO: PATCH /api/b01/password 연동 (로딩 오버레이 + 성공/실패 처리) currentPwField.input.value = ""; newPwField.input.value = ""; confirmPwField.input.value = ""; showToast(L("B01_Account_Success_Password"), "success"); } const grid = document.createElement("div"); grid.className = "b01-account__grid"; grid.append(currentPwField.root, newPwField.root, confirmPwField.root); return createCard({ title: L("B01_Account_Section_Security"), body: [grid, saveBtn], raised: true, }); }