This commit is contained in:
2026-07-17 12:51:47 +09:00
parent 38af2aab9f
commit 4ac681bd10
30 changed files with 1340 additions and 803 deletions
+30 -20
View File
@@ -1,4 +1,5 @@
import "./ui_template_workflow_layout.css";
import { t } from "./ui_template_locale";
export interface WorkflowStage {
stage_no: number;
@@ -13,7 +14,8 @@ export interface WorkflowLayoutOptions {
leftPanel: HTMLElement;
mainContent: HTMLElement;
stages?: WorkflowStage[];
routes?: string[];
currentStage?: number;
routes?: readonly string[];
onStepClick?: (stepIndex: number, route: string) => void;
onMenuToggle?: (isOpen: boolean) => void;
onHeaderToggle?: (isVisible: boolean) => void;
@@ -25,17 +27,22 @@ export interface WorkflowLayoutHandle {
setHeaderVisible: (isVisible: boolean) => void;
}
function createStepBar(
export interface StepBarOptions {
stages?: WorkflowStage[];
currentStage?: number;
routes?: readonly string[];
compact?: boolean;
onStepClick?: (stepIndex: number, route: string) => void;
}
export function createStepBar(
steps: readonly string[],
activeStep: number,
options?: {
stages?: WorkflowStage[];
routes?: string[];
onStepClick?: (stepIndex: number, route: string) => void;
},
options?: StepBarOptions,
): HTMLElement {
const bar = document.createElement("div");
bar.className = "ui-workflow-layout__steps";
if (options?.compact) bar.classList.add("is-compact");
steps.forEach((step, index) => {
const button = document.createElement("button");
button.type = "button";
@@ -43,30 +50,32 @@ function createStepBar(
if (index === activeStep) button.classList.add("is-active");
button.textContent = step;
// 스텝바는 항상 자유롭게 이동 가능(게이팅하지 않음).
// 단계 완료/무효화 판정은 사용자가 각 페이지의 액션 버튼(업로드·분석 실행 등)을
// 눌렀을 때 백엔드가 계산·DB 갱신하며, 여기서는 그 결과를 색상/툴팁으로 표시만 한다.
button.classList.add("is-enabled");
const stage =
options?.stages?.find((item) => item.stage_no === index) ?? options?.stages?.[index];
const hasStages = Boolean(options?.stages?.length);
const isEnabled =
!hasStages || stage?.state !== "NOT_STARTED" || stage?.stage_no === options?.currentStage;
button.classList.toggle("is-enabled", isEnabled);
button.disabled = !isEnabled;
// state 기반 스타일링 (표시 전용)
if (options?.stages && options.stages[index]) {
const state = options.stages[index].state;
if (stage) {
const state = stage.state;
button.classList.add(`state-${state.toLowerCase()}`);
if (state === "STALE") {
button.title = "Stale (하위 단계 변경으로 무효화됨)";
button.title = t("WF_State_Stale");
} else if (state === "FAILED") {
button.title = "Failed (실패)";
button.title = t("WF_State_Failed");
} else if (state === "COMPLETE") {
button.title = "Complete (완료)";
button.title = t("WF_State_Complete");
} else if (state === "IN_PROGRESS") {
button.title = "In Progress (진행 중)";
button.title = t("WF_State_InProgress");
} else {
button.title = "Not Started (미실행)";
button.title = t("WF_State_NotStarted");
}
}
// 클릭 이벤트 — 모든 스텝에서 이동 허용
if (options?.routes && options?.routes[index]) {
if (isEnabled && options?.routes?.[index]) {
button.addEventListener("click", () => {
options.onStepClick?.(index, options.routes![index]);
});
@@ -99,6 +108,7 @@ export function createWorkflowLayout(options: WorkflowLayoutOptions): WorkflowLa
title,
createStepBar(options.steps, options.activeStep, {
stages: options.stages,
currentStage: options.currentStage,
routes: options.routes,
onStepClick: options.onStepClick,
}),