260705_1
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/* =============================================================================
|
||||
* b_page_scaffold.ts
|
||||
* B 그룹 페이지 공통 스캐폴드 — "헤더 + 준비 중 안내" 및 워크플로우 셸 래퍼
|
||||
*
|
||||
* B03~B06 등 본문 미구현 페이지에서 헤더/푸터(공통 셸)를 제외한 콘텐츠 영역을
|
||||
* 일관된 형태로 채우기 위한 헬퍼. 실제 본문은 0_old 참고하여 추후 구체화.
|
||||
*
|
||||
* 제약 준수 (frontend.md):
|
||||
* - 문구는 호출측에서 locale 참조 후 문자열로 전달 (§3).
|
||||
* - 공통 워크플로우 셸(createWorkflowShell) 재사용 (§2 3단 레이아웃).
|
||||
* - 색상/간격은 theme.css 변수만 사용 (§1).
|
||||
* ========================================================================== */
|
||||
|
||||
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import { createWorkflowShell } from "@ui/ui_template_elements";
|
||||
|
||||
/** locale 헬퍼 */
|
||||
function L(key: keyof typeof ui_locales): string {
|
||||
return ui_locales[key][currentLanguageIndex];
|
||||
}
|
||||
|
||||
const SCAFFOLD_STYLE_ID = "b-page-scaffold-style";
|
||||
|
||||
/** "준비 중" 안내 블록 생성 (B_Content_Pending 문구 표시) */
|
||||
function buildPendingBlock(): HTMLDivElement {
|
||||
const block = document.createElement("div");
|
||||
block.className = "b-pending";
|
||||
const icon = document.createElement("div");
|
||||
icon.className = "b-pending__icon";
|
||||
icon.textContent = "🚧";
|
||||
const msg = document.createElement("p");
|
||||
msg.className = "b-pending__msg";
|
||||
msg.textContent = L("B_Content_Pending");
|
||||
block.append(icon, msg);
|
||||
return block;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 1. 단순 헤더 + 준비 중 안내 (비(非)워크플로우 페이지: B03 등)
|
||||
* -------------------------------------------------------------------------- */
|
||||
export interface PendingContentOptions {
|
||||
/** 페이지 루트 클래스 (예: "b03-file") */
|
||||
pageClass: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
}
|
||||
|
||||
export function renderPendingContent(root: HTMLElement, opts: PendingContentOptions): void {
|
||||
injectScaffoldStyles();
|
||||
const page = document.createElement("div");
|
||||
page.className = `b-scaffold ${opts.pageClass}`;
|
||||
|
||||
const header = document.createElement("div");
|
||||
header.className = "b-scaffold__header";
|
||||
const title = document.createElement("h1");
|
||||
title.className = "b-scaffold__title";
|
||||
title.textContent = opts.title;
|
||||
const subtitle = document.createElement("p");
|
||||
subtitle.className = "b-scaffold__subtitle";
|
||||
subtitle.textContent = opts.subtitle;
|
||||
header.append(title, subtitle);
|
||||
|
||||
page.append(header, buildPendingBlock());
|
||||
root.append(page);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 2. 워크플로우 셸(3단 레이아웃) + 준비 중 안내 (B04~B06 등)
|
||||
* 좌측 패널·우측 뷰어 모두 준비 중 상태로 채운다.
|
||||
* -------------------------------------------------------------------------- */
|
||||
export interface PendingWorkflowOptions {
|
||||
/** 상단 페이지 타이틀 (i18n 결과) */
|
||||
title: string;
|
||||
/** 진행 단계 라벨 배열 (i18n 결과) */
|
||||
steps: string[];
|
||||
/** 현재 활성 단계 인덱스 */
|
||||
activeStep: number;
|
||||
}
|
||||
|
||||
export function renderPendingWorkflow(root: HTMLElement, opts: PendingWorkflowOptions): void {
|
||||
injectScaffoldStyles();
|
||||
const shell = createWorkflowShell({
|
||||
title: opts.title,
|
||||
steps: opts.steps,
|
||||
activeStep: opts.activeStep,
|
||||
});
|
||||
shell.root.classList.add("b-scaffold-wf");
|
||||
|
||||
// 좌측/우측 모두 준비 중 안내로 채움 (본문은 추후 구체화)
|
||||
shell.leftPanel.append(buildPendingBlock());
|
||||
shell.rightArea.append(buildPendingBlock());
|
||||
|
||||
root.append(shell.root);
|
||||
}
|
||||
|
||||
/** 워크플로우 스텝 라벨 6종 (locale 결과) — B04~B09 공용.
|
||||
* 기존 WF_Step_* 키를 재사용 (중복 등록 방지, frontend.md §3). */
|
||||
export function workflowSteps(): string[] {
|
||||
return [
|
||||
L("WF_Step_Surface"),
|
||||
L("WF_Step_Route"),
|
||||
L("WF_Step_ProfileCross"),
|
||||
L("WF_Step_DesignDetail"),
|
||||
L("WF_Step_Quantity"),
|
||||
L("WF_Step_Estimation"),
|
||||
];
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 스타일 주입 (theme.css 변수만 사용)
|
||||
* -------------------------------------------------------------------------- */
|
||||
const SCAFFOLD_CSS = `
|
||||
.b-scaffold {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-40) var(--spacing-24);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-24);
|
||||
}
|
||||
.b-scaffold__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-8);
|
||||
}
|
||||
.b-scaffold__title {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-heading);
|
||||
color: var(--color-plum-velvet);
|
||||
}
|
||||
.b-scaffold__subtitle {
|
||||
font-size: var(--text-body-sm);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
.b-scaffold-wf { height: calc(100vh - 64px - 56px); }
|
||||
|
||||
.b-pending {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-16);
|
||||
min-height: 240px;
|
||||
padding: var(--spacing-40);
|
||||
border: 1px dashed var(--color-border);
|
||||
border-radius: var(--radius-cards);
|
||||
background-color: var(--color-surface);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
.b-pending__icon { font-size: 40px; line-height: 1; }
|
||||
.b-pending__msg { font-size: var(--text-body-sm); text-align: center; }
|
||||
`;
|
||||
|
||||
function injectScaffoldStyles(): void {
|
||||
if (document.getElementById(SCAFFOLD_STYLE_ID)) return;
|
||||
const style = document.createElement("style");
|
||||
style.id = SCAFFOLD_STYLE_ID;
|
||||
style.textContent = SCAFFOLD_CSS;
|
||||
document.head.append(style);
|
||||
}
|
||||
@@ -35,6 +35,29 @@ const routeTable: Partial<Record<RoutePath, () => Promise<PageRenderer>>> = {
|
||||
(await import("../A07_Register/A07_Register_UI_Page")).renderA07Register,
|
||||
[ROUTES.A08_SUPPORT]: async () =>
|
||||
(await import("../A08_Support/A08_Support_UI_Page")).renderA08Support,
|
||||
// 로그인 후 (B 그룹)
|
||||
[ROUTES.B01_ACCOUNT]: async () =>
|
||||
(await import("../B01_AccountDetail/B01_AccountDetail_UI_Page")).renderB01AccountDetail,
|
||||
[ROUTES.B02_PROJ_REGISTER]: async () =>
|
||||
(await import("../B02_ProjRegister/B02_ProjRegister_UI_Page")).renderB02ProjRegister,
|
||||
[ROUTES.B03_FILE_INPUT]: async () =>
|
||||
(await import("../B03_FileInput/B03_FileInput_UI_Page")).renderB03FileInput,
|
||||
[ROUTES.B04_WF1_SURFACE]: async () =>
|
||||
(await import("../B04_wf1_Surface/B04_wf1_Surface_UI_Page")).renderB04Surface,
|
||||
[ROUTES.B05_WF2_ROUTE]: async () =>
|
||||
(await import("../B05_wf2_Route/B05_wf2_Route_UI_Page")).renderB05Route,
|
||||
[ROUTES.B06_WF3_PROFILE_CROSS]: async () =>
|
||||
(await import("../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Page")).renderB06ProfileCross,
|
||||
[ROUTES.B07_WF4_DESIGN_DETAIL]: async () =>
|
||||
(await import("../B07_wf4_DesignDetail/B07_wf4_DesignDetail_UI_Page")).renderB07DesignDetail,
|
||||
[ROUTES.B08_WF5_QUANTITY]: async () =>
|
||||
(await import("../B08_wf5_Quantity/B08_wf5_Quantity_UI_Page")).renderB08Quantity,
|
||||
[ROUTES.B09_WF6_ESTIMATION]: async () =>
|
||||
(await import("../B09_wf6_Estimation/B09_wf6_Estimation_UI_Page")).renderB09Estimation,
|
||||
[ROUTES.B10_PAYMENT]: async () =>
|
||||
(await import("../B10_Payment/B10_Payment_UI_Page")).renderB10Payment,
|
||||
[ROUTES.B11_STATUS]: async () =>
|
||||
(await import("../B11_Status/B11_Status_UI_Page")).renderB11Status,
|
||||
};
|
||||
|
||||
/** 로그인 여부 (토큰 존재 확인) */
|
||||
|
||||
Reference in New Issue
Block a user