import { ROUTES } from "@config/config_frontend"; import { hasMinLength, isBlank, isValidEmail } from "@util/common_util_validate"; import { createButton, createInputField, hideLoadingOverlay, showLoadingOverlay, showToast, } from "@ui/ui_template_elements"; import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { navigateTo } from "../A00_Common/router"; import { requestRegistration, verifyRegistration } from "./A07_Register_Api_Fetch"; import "./A07_Register_UI_Style.css"; const L = (key: keyof typeof ui_locales): string => ui_locales[key][currentLanguageIndex]; interface AgreementField { root: HTMLElement; input: HTMLInputElement; } function checkboxWithAccordion(label: string, contentText: string): AgreementField { const container = document.createElement("div"); container.className = "a07-register__agreement"; const summary = document.createElement("div"); summary.className = "a07-register__agreement-summary"; const input = document.createElement("input"); input.type = "checkbox"; const labelSpan = document.createElement("span"); labelSpan.className = "a07-register__agreement-label"; labelSpan.textContent = label; summary.append(input, labelSpan); const details = document.createElement("details"); details.className = "a07-register__agreement-details"; const detailsContent = document.createElement("div"); detailsContent.className = "a07-register__agreement-content"; detailsContent.textContent = contentText; details.appendChild(detailsContent); summary.addEventListener("click", (e) => { if (e.target !== input) { details.open = !details.open; } }); container.append(summary, details); return { root: container, input }; } async function loadAgreementText(filename: string): Promise { const filePath = `resources/legal/${filename}`; const response = await fetch(filePath); if (!response.ok) { console.warn(`Failed to load ${filename}`); return `[${filename}을 불러올 수 없습니다]`; } return await response.text(); } export function renderA07Register(root: HTMLElement): void { const page = document.createElement("div"); page.className = "a07-register"; const card = document.createElement("div"); card.className = "a07-register__card"; const title = document.createElement("h1"); title.className = "a07-register__title"; title.textContent = L("A07_Register_Title"); const name = createInputField({ label: L("A07_Register_Field_Name"), type: "text" }); const position = createInputField({ label: L("A07_Register_Field_Position"), type: "text", }); const email = createInputField({ label: L("A07_Register_Field_Email"), type: "email" }); const phone = createInputField({ label: L("A07_Register_Field_Phone"), type: "tel" }); const password = createInputField({ label: L("A07_Register_Field_Password"), type: "password" }); const passwordConfirm = createInputField({ label: L("A07_Register_Field_PasswordConfirm"), type: "password", }); const terms: AgreementField = { root: document.createElement("div"), input: document.createElement("input"), }; const privacy: AgreementField = { root: document.createElement("div"), input: document.createElement("input"), }; const marketing: AgreementField = { root: document.createElement("div"), input: document.createElement("input"), }; const otp = createInputField({ label: L("A07_Register_Field_Otp"), type: "text" }); otp.root.hidden = true; const submit = createButton({ label: L("A07_Register_Submit"), type: "submit" }); const form = document.createElement("form"); form.className = "a07-register__form"; let verifyStep = false; let termsText = ""; let privacyText = ""; let marketingText = ""; async function loadAgreementTexts(): Promise { termsText = await loadAgreementText("terms_of_service.txt"); privacyText = await loadAgreementText("privacy_policy.txt"); marketingText = await loadAgreementText("marketing_consent.txt"); const termsField = checkboxWithAccordion(L("A07_Register_Terms"), termsText); terms.root = termsField.root; terms.input = termsField.input; const privacyField = checkboxWithAccordion(L("A07_Register_Privacy"), privacyText); privacy.root = privacyField.root; privacy.input = privacyField.input; const marketingField = checkboxWithAccordion(L("A07_Register_Marketing"), marketingText); marketing.root = marketingField.root; marketing.input = marketingField.input; form.innerHTML = ""; form.append( name.root, position.root, email.root, phone.root, password.root, passwordConfirm.root, terms.root, privacy.root, marketing.root, otp.root, submit, ); } async function onA07_Register_Submit_Click(event: Event): Promise { event.preventDefault(); showLoadingOverlay(); try { if (verifyStep) { await verifyRegistration(email.input.value.trim(), otp.input.value); showToast(L("A07_Register_Success"), "success"); navigateTo(ROUTES.A06_LOGIN); return; } if ( [name, email, password, passwordConfirm].some((field) => isBlank(field.input.value)) || !isValidEmail(email.input.value) ) { throw new Error(L("A07_Register_Error_Required")); } if (!hasMinLength(password.input.value, 8)) throw new Error(L("A07_Register_Error_Password")); if (password.input.value !== passwordConfirm.input.value) throw new Error(L("A07_Register_Error_PasswordMismatch")); if (!terms.input.checked || !privacy.input.checked) throw new Error(L("A07_Register_Error_Terms")); await requestRegistration({ email: email.input.value.trim(), password: password.input.value, password_confirm: passwordConfirm.input.value, name: name.input.value.trim(), position: position.input.value.trim() || null, phone: phone.input.value.trim() || null, terms_version: "1.0", terms_agreed: true, privacy_agreed: true, marketing_agreed: marketing.input.checked, }); verifyStep = true; Array.from(form.children).forEach((child) => { if (child !== otp.root && child !== submit) { (child as HTMLElement).hidden = true; } }); otp.root.hidden = false; submit.hidden = false; submit.querySelector(".ui-btn__label")!.textContent = L("A07_Register_Verify"); showToast(L("A07_Register_OtpSent"), "info"); } catch (error) { showToast(error instanceof Error ? error.message : L("A07_Register_Error_Request"), "error"); } finally { hideLoadingOverlay(); } } form.addEventListener("submit", onA07_Register_Submit_Click); loadAgreementTexts().then(() => { const login = createButton({ label: L("A07_Register_ToLogin"), variant: "ghost", onClick: () => navigateTo(ROUTES.A06_LOGIN), }); card.append(title, form, login); page.append(card); root.append(page); }); }