Files
Aislo/A06_Login/A06_Login_UI_Page.ts
T

107 lines
3.7 KiB
TypeScript

/* =============================================================================
* A06_Login_UI_Page.ts
* 로그인 전 06: 로그인 (이메일 + 비밀번호 폼)
*
* 제약 준수:
* - 모든 문구는 ui_template_locale.ts 참조 (하드코딩 금지).
* - 색상/간격은 A06_Login_UI_Style.css + theme.css 변수만 사용.
* - 공통 컴포넌트(createButton/createInputField/createCard) 우선 사용.
* - 이벤트 핸들러는 on[페이지]_[기능]_[액션] 명명.
* - 백엔드 전송 전 1차 유효성 검사 (frontend.md §4).
* ========================================================================== */
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
import { createButton, createInputField } from "@ui/ui_template_elements";
import { isBlank, isValidEmail } from "@util/common_util_validate";
import { navigateTo } from "../A00_Common/router";
import { ROUTES, AUTH_TOKEN_KEY } from "@config/config_frontend";
import "./A06_Login_UI_Style.css";
/** locale 헬퍼 */
function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
}
/* -----------------------------------------------------------------------------
* 페이지 진입점
* -------------------------------------------------------------------------- */
export function renderA06Login(root: HTMLElement): void {
const page = document.createElement("div");
page.className = "a06-login";
const card = document.createElement("div");
card.className = "a06-login__card";
const title = document.createElement("h1");
title.className = "a06-login__title";
title.textContent = L("A06_Login_Title");
const subtitle = document.createElement("p");
subtitle.className = "a06-login__subtitle";
subtitle.textContent = L("A06_Login_Subtitle");
// 입력 필드
const emailField = createInputField({
label: L("A06_Login_Field_Email"),
placeholder: L("A06_Login_Field_Email_Placeholder"),
type: "email",
required: true,
});
const passwordField = createInputField({
label: L("A06_Login_Field_Password"),
placeholder: L("A06_Login_Field_Password_Placeholder"),
type: "password",
required: true,
});
const form = document.createElement("form");
form.className = "a06-login__form";
form.noValidate = true;
const submitBtn = createButton({
label: L("A06_Login_Submit"),
variant: "filled",
type: "submit",
});
submitBtn.classList.add("a06-login__submit");
/* 이벤트 핸들러: 제출 시 1차 유효성 검사 */
function onA06_Login_Submit_Click(event: Event): void {
event.preventDefault();
emailField.setError();
passwordField.setError();
const email = emailField.input.value;
const password = passwordField.input.value;
if (isBlank(email) || isBlank(password)) {
if (isBlank(email)) emailField.setError(L("A06_Login_Error_Required"));
if (isBlank(password)) passwordField.setError(L("A06_Login_Error_Required"));
return;
}
if (!isValidEmail(email)) {
emailField.setError(L("A06_Login_Error_Email"));
return;
}
// TODO: POST /api/a06/login 연동. 성공 시 토큰 저장 후 계정 페이지로.
localStorage.setItem(AUTH_TOKEN_KEY, "dev-placeholder-token");
navigateTo(ROUTES.B01_ACCOUNT);
}
form.addEventListener("submit", onA06_Login_Submit_Click);
form.append(emailField.root, passwordField.root, submitBtn);
// 회원가입 링크
const toRegister = createButton({
label: L("A06_Login_ToRegister"),
variant: "ghost",
onClick: () => navigateTo(ROUTES.A07_REGISTER),
});
toRegister.classList.add("a06-login__link");
card.append(title, subtitle, form, toRegister);
page.append(card);
root.append(page);
}