This commit is contained in:
2026-07-05 18:10:16 +09:00
parent 0057983671
commit 23d907265a
22 changed files with 2207 additions and 315 deletions
@@ -0,0 +1,152 @@
/* =============================================================================
* B02_ProjRegister_UI_Page.ts
* 로그인 후 02: 프로젝트 등록 (신규 임도 설계 프로젝트 기본 정보 입력)
*
* 제약 준수 (frontend.md):
* - 모든 문구는 ui_template_locale.ts 참조 (하드코딩 금지, §3).
* - 색상/간격은 theme.css 변수 + 공통 컴포넌트만 사용 (§1, §2).
* - 이벤트 핸들러는 onB02_[기능]_[액션] 명명 (§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 { navigateTo } from "../A00_Common/router";
import { ROUTES } from "@config/config_frontend";
import "./B02_ProjRegister_UI_Style.css";
/** locale 헬퍼 */
function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
}
/** select 필드 핸들 (공통 .ui-field/.ui-input 스타일 상속) */
interface SelectFieldHandle {
root: HTMLDivElement;
select: HTMLSelectElement;
}
/** 드롭다운 필드 생성 (공통 컴포넌트에 없어 로컬 정의, 공통 스타일 상속) */
function createSelectField(
label: string,
options: { value: string; text: string }[],
): SelectFieldHandle {
const root = document.createElement("div");
root.className = "ui-field";
const labelEl = document.createElement("label");
labelEl.className = "ui-field__label";
labelEl.textContent = label;
const select = document.createElement("select");
select.className = "ui-input b02-proj__select";
for (const opt of options) {
const optionEl = document.createElement("option");
optionEl.value = opt.value;
optionEl.textContent = opt.text;
select.append(optionEl);
}
root.append(labelEl, select);
return { root, select };
}
/* -----------------------------------------------------------------------------
* 페이지 진입점
* -------------------------------------------------------------------------- */
export function renderB02ProjRegister(root: HTMLElement): void {
const page = document.createElement("div");
page.className = "b02-proj";
const header = document.createElement("div");
header.className = "b02-proj__header";
const title = document.createElement("h1");
title.className = "b02-proj__title";
title.textContent = L("B02_Proj_Title");
const subtitle = document.createElement("p");
subtitle.className = "b02-proj__subtitle";
subtitle.textContent = L("B02_Proj_Subtitle");
header.append(title, subtitle);
// 입력 필드
const nameField = createInputField({
label: L("B02_Proj_Field_Name"),
placeholder: L("B02_Proj_Field_Name_Placeholder"),
required: true,
});
const regionField = createInputField({
label: L("B02_Proj_Field_Region"),
placeholder: L("B02_Proj_Field_Region_Placeholder"),
required: true,
});
const roadTypeField = createSelectField(L("B02_Proj_Field_RoadType"), [
{ value: "main", text: L("B02_Proj_RoadType_Main") },
{ value: "branch", text: L("B02_Proj_RoadType_Branch") },
{ value: "fire", text: L("B02_Proj_RoadType_Fire") },
{ value: "stream", text: L("B02_Proj_RoadType_Stream") },
]);
const currentYear = new Date().getFullYear();
const yearField = createInputField({
label: L("B02_Proj_Field_Year"),
type: "number",
value: String(currentYear),
min: 2000,
max: currentYear + 5,
});
const lengthField = createInputField({
label: L("B02_Proj_Field_Length"),
placeholder: L("B02_Proj_Field_Length_Placeholder"),
type: "number",
min: 0,
});
const memoField = createInputField({
label: L("B02_Proj_Field_Memo"),
placeholder: L("B02_Proj_Field_Memo_Placeholder"),
type: "text",
});
const submitBtn = createButton({
label: L("B02_Proj_Submit"),
variant: "filled",
onClick: onB02_Proj_Submit_Click,
});
submitBtn.classList.add("b02-proj__submit");
function onB02_Proj_Submit_Click(): void {
nameField.setError();
regionField.setError();
// 1차 유효성: 필수값 검사
let hasError = false;
if (isBlank(nameField.input.value)) {
nameField.setError(L("B02_Proj_Error_Required"));
hasError = true;
}
if (isBlank(regionField.input.value)) {
regionField.setError(L("B02_Proj_Error_Required"));
hasError = true;
}
if (hasError) return;
// TODO: POST /api/b02/project 연동 (로딩 오버레이 + 성공/실패 처리).
// 성공 시 반환된 프로젝트 ID로 storage 폴더 생성 후 파일 입력으로 이동.
showToast(L("B02_Proj_Success"), "success");
navigateTo(ROUTES.B03_FILE_INPUT);
}
const grid = document.createElement("div");
grid.className = "b02-proj__grid";
grid.append(
nameField.root,
regionField.root,
roadTypeField.root,
yearField.root,
lengthField.root,
memoField.root,
);
const card = createCard({ body: [grid, submitBtn], raised: true });
page.append(header, card);
root.append(page);
}