ERP-node/frontend/lib/registry/pop-components/pop-card-list-v2/cell-renderers.tsx

734 lines
26 KiB
TypeScript
Raw Normal View History

feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
"use client";
/**
* pop-card-list-v2
*
* CardV2Grid에서 type별 dispatch로 .
* pop-card-list의 pop-string-list의 CardModeView .
*/
import React, { useMemo, useState } from "react";
import {
ShoppingCart, X, Package, Truck, Box, Archive, Heart, Star,
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
Loader2, CheckCircle2, CircleDot, Clock,
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
type LucideIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription,
} from "@/components/ui/dialog";
import { cn } from "@/lib/utils";
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
import type { CardCellDefinitionV2, PackageEntry, TimelineProcessStep, ActionButtonDef } from "../types";
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
import { DEFAULT_CARD_IMAGE, VIRTUAL_SUB_STATUS, VIRTUAL_SUB_SEMANTIC } from "../types";
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
import type { ButtonVariant } from "../pop-button";
type RowData = Record<string, unknown>;
// ===== 공통 유틸 =====
const LUCIDE_ICON_MAP: Record<string, LucideIcon> = {
ShoppingCart, Package, Truck, Box, Archive, Heart, Star,
};
function DynamicLucideIcon({ name, size = 20 }: { name?: string; size?: number }) {
if (!name) return <ShoppingCart size={size} />;
const IconComp = LUCIDE_ICON_MAP[name];
if (!IconComp) return <ShoppingCart size={size} />;
return <IconComp size={size} />;
}
function formatValue(value: unknown): string {
if (value === null || value === undefined) return "-";
if (typeof value === "number") return value.toLocaleString();
if (typeof value === "boolean") return value ? "예" : "아니오";
if (value instanceof Date) return value.toLocaleDateString();
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}/.test(value)) {
const date = new Date(value);
if (!isNaN(date.getTime())) {
return `${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
}
}
return String(value);
}
const FONT_SIZE_MAP = { xs: "10px", sm: "11px", md: "12px", lg: "14px" } as const;
const FONT_WEIGHT_MAP = { normal: 400, medium: 500, bold: 700 } as const;
// ===== 셀 렌더러 Props =====
export interface CellRendererProps {
cell: CardCellDefinitionV2;
row: RowData;
inputValue?: number;
isCarted?: boolean;
isButtonLoading?: boolean;
onInputClick?: (e: React.MouseEvent) => void;
onCartAdd?: () => void;
onCartCancel?: () => void;
onButtonClick?: (cell: CardCellDefinitionV2, row: RowData) => void;
onActionButtonClick?: (taskPreset: string, row: RowData, buttonConfig?: Record<string, unknown>) => void;
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
onEnterSelectMode?: (whenStatus: string, buttonConfig: Record<string, unknown>) => void;
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
packageEntries?: PackageEntry[];
inputUnit?: string;
}
// ===== 메인 디스패치 =====
export function renderCellV2(props: CellRendererProps): React.ReactNode {
switch (props.cell.type) {
case "text":
return <TextCell {...props} />;
case "field":
return <FieldCell {...props} />;
case "image":
return <ImageCell {...props} />;
case "badge":
return <BadgeCell {...props} />;
case "button":
return <ButtonCell {...props} />;
case "number-input":
return <NumberInputCell {...props} />;
case "cart-button":
return <CartButtonCell {...props} />;
case "package-summary":
return <PackageSummaryCell {...props} />;
case "status-badge":
return <StatusBadgeCell {...props} />;
case "timeline":
return <TimelineCell {...props} />;
case "action-buttons":
return <ActionButtonsCell {...props} />;
case "footer-status":
return <FooterStatusCell {...props} />;
default:
return <span className="text-[10px] text-muted-foreground"> </span>;
}
}
// ===== 1. text =====
function TextCell({ cell, row }: CellRendererProps) {
const value = cell.columnName ? row[cell.columnName] : "";
const fs = FONT_SIZE_MAP[cell.fontSize || "md"];
const fw = FONT_WEIGHT_MAP[cell.fontWeight || "normal"];
return (
<span
className="truncate"
style={{ fontSize: fs, fontWeight: fw, color: cell.textColor || undefined }}
>
{formatValue(value)}
</span>
);
}
// ===== 2. field (라벨+값) =====
function FieldCell({ cell, row, inputValue }: CellRendererProps) {
const valueType = cell.valueType || "column";
const fs = FONT_SIZE_MAP[cell.fontSize || "md"];
const displayValue = useMemo(() => {
if (valueType !== "formula") {
const raw = cell.columnName ? row[cell.columnName] : undefined;
const formatted = formatValue(raw);
return cell.unit ? `${formatted} ${cell.unit}` : formatted;
}
if (cell.formulaLeft && cell.formulaOperator) {
const rightVal =
(cell.formulaRightType || "input") === "input"
? (inputValue ?? 0)
: Number(row[cell.formulaRight || ""] ?? 0);
const leftVal = Number(row[cell.formulaLeft] ?? 0);
let result: number | null = null;
switch (cell.formulaOperator) {
case "+": result = leftVal + rightVal; break;
case "-": result = leftVal - rightVal; break;
case "*": result = leftVal * rightVal; break;
case "/": result = rightVal !== 0 ? leftVal / rightVal : null; break;
}
if (result !== null && isFinite(result)) {
const formatted = (Math.round(result * 100) / 100).toLocaleString();
return cell.unit ? `${formatted} ${cell.unit}` : formatted;
}
return "-";
}
return "-";
}, [valueType, cell, row, inputValue]);
const isFormula = valueType === "formula";
const isLabelLeft = cell.labelPosition === "left";
return (
<div
className={isLabelLeft ? "flex items-baseline gap-1" : "flex flex-col"}
style={{ fontSize: fs }}
>
{cell.label && (
<span className="shrink-0 text-[10px] text-muted-foreground">
{cell.label}{isLabelLeft ? ":" : ""}
</span>
)}
<span
className="truncate font-medium"
style={{ color: cell.textColor || (isFormula ? "#ea580c" : undefined) }}
>
{displayValue}
</span>
</div>
);
}
// ===== 3. image =====
function ImageCell({ cell, row }: CellRendererProps) {
const value = cell.columnName ? row[cell.columnName] : "";
const imageUrl = value ? String(value) : (cell.defaultImage || DEFAULT_CARD_IMAGE);
return (
<div className="flex h-full w-full items-center justify-center overflow-hidden rounded-md border bg-muted/30">
<img
src={imageUrl}
alt={cell.label || ""}
className="h-full w-full object-contain p-1"
onError={(e) => {
const target = e.target as HTMLImageElement;
if (target.src !== DEFAULT_CARD_IMAGE) target.src = DEFAULT_CARD_IMAGE;
}}
/>
</div>
);
}
// ===== 4. badge =====
function BadgeCell({ cell, row }: CellRendererProps) {
const value = cell.columnName ? row[cell.columnName] : "";
return (
<span className="inline-flex items-center rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-medium text-primary">
{formatValue(value)}
</span>
);
}
// ===== 5. button =====
function ButtonCell({ cell, row, isButtonLoading, onButtonClick }: CellRendererProps) {
return (
<Button
variant={cell.buttonVariant || "outline"}
size="sm"
className="h-7 text-[10px]"
disabled={isButtonLoading}
onClick={(e) => {
e.stopPropagation();
onButtonClick?.(cell, row);
}}
>
{isButtonLoading ? (
<Loader2 className="mr-1 h-3 w-3 animate-spin" />
) : null}
{cell.label || formatValue(cell.columnName ? row[cell.columnName] : "")}
</Button>
);
}
// ===== 6. number-input =====
function NumberInputCell({ cell, row, inputValue, onInputClick }: CellRendererProps) {
const unit = cell.inputUnit || "EA";
return (
<button
type="button"
onClick={(e) => {
e.stopPropagation();
onInputClick?.(e);
}}
className="w-full rounded-lg border-2 border-input bg-background px-2 py-1.5 text-center hover:border-primary active:bg-muted"
>
<span className="block text-lg font-bold leading-tight">
{(inputValue ?? 0).toLocaleString()}
</span>
<span className="block text-[12px] text-muted-foreground">{unit}</span>
</button>
);
}
// ===== 7. cart-button =====
function CartButtonCell({ cell, row, isCarted, onCartAdd, onCartCancel }: CellRendererProps) {
const iconSize = 18;
const label = cell.cartLabel || "담기";
const cancelLabel = cell.cartCancelLabel || "취소";
if (isCarted) {
return (
<button
type="button"
onClick={(e) => { e.stopPropagation(); onCartCancel?.(); }}
className="flex w-full flex-col items-center justify-center gap-0.5 rounded-xl bg-destructive px-2 py-1.5 text-destructive-foreground transition-colors duration-150 hover:bg-destructive/90 active:bg-destructive/80"
>
<X size={iconSize} />
<span className="text-[10px] font-semibold leading-tight">{cancelLabel}</span>
</button>
);
}
return (
<button
type="button"
onClick={(e) => { e.stopPropagation(); onCartAdd?.(); }}
className="flex w-full flex-col items-center justify-center gap-0.5 rounded-xl bg-linear-to-br from-amber-400 to-orange-500 px-2 py-1.5 text-white transition-colors duration-150 hover:from-amber-500 hover:to-orange-600 active:from-amber-600 active:to-orange-700"
>
{cell.cartIconType === "emoji" && cell.cartIconValue ? (
<span style={{ fontSize: `${iconSize}px` }}>{cell.cartIconValue}</span>
) : (
<DynamicLucideIcon name={cell.cartIconValue} size={iconSize} />
)}
<span className="text-[10px] font-semibold leading-tight">{label}</span>
</button>
);
}
// ===== 8. package-summary =====
function PackageSummaryCell({ cell, packageEntries, inputUnit }: CellRendererProps) {
if (!packageEntries || packageEntries.length === 0) return null;
return (
<div className="w-full border-t bg-emerald-50">
{packageEntries.map((entry, idx) => (
<div key={idx} className="flex items-center justify-between px-3 py-1.5">
<div className="flex items-center gap-2">
<span className="rounded-full bg-emerald-500 px-2 py-0.5 text-[10px] font-bold text-white">
</span>
<Package className="h-4 w-4 text-emerald-600" />
<span className="text-xs font-medium text-emerald-700">
{entry.packageCount}{entry.unitLabel} x {entry.quantityPerUnit}
</span>
</div>
<span className="text-xs font-bold text-emerald-700">
= {entry.totalQuantity.toLocaleString()}{inputUnit || "EA"}
</span>
</div>
))}
</div>
);
}
// ===== 9. status-badge =====
const STATUS_COLORS: Record<string, { bg: string; text: string }> = {
waiting: { bg: "#94a3b820", text: "#64748b" },
accepted: { bg: "#3b82f620", text: "#2563eb" },
in_progress: { bg: "#f59e0b20", text: "#d97706" },
completed: { bg: "#10b98120", text: "#059669" },
};
function StatusBadgeCell({ cell, row }: CellRendererProps) {
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
const hasSubStatus = row[VIRTUAL_SUB_STATUS] !== undefined;
const effectiveValue = hasSubStatus
? row[VIRTUAL_SUB_STATUS]
: (cell.statusColumn ? row[cell.statusColumn] : (cell.columnName ? row[cell.columnName] : ""));
const strValue = String(effectiveValue || "");
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
const mapped = cell.statusMap?.find((m) => m.value === strValue);
if (mapped) {
return (
<span
className="inline-flex items-center rounded-full px-2.5 py-0.5 text-[10px] font-semibold"
style={{ backgroundColor: `${mapped.color}20`, color: mapped.color }}
>
{mapped.label}
</span>
);
}
const defaultColors = STATUS_COLORS[strValue];
if (defaultColors) {
const labelMap: Record<string, string> = {
waiting: "대기", accepted: "접수", in_progress: "진행", completed: "완료",
};
return (
<span
className="inline-flex items-center rounded-full px-2.5 py-0.5 text-[10px] font-semibold"
style={{ backgroundColor: defaultColors.bg, color: defaultColors.text }}
>
{labelMap[strValue] || strValue}
</span>
);
}
return (
<span className="inline-flex items-center rounded-full bg-muted px-2.5 py-0.5 text-[10px] font-medium text-muted-foreground">
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
{formatValue(effectiveValue)}
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
</span>
);
}
// ===== 10. timeline =====
type TimelineStyle = { chipBg: string; chipText: string; icon: React.ReactNode };
const TIMELINE_SEMANTIC_STYLES: Record<string, TimelineStyle> = {
done: { chipBg: "#10b981", chipText: "#ffffff", icon: <CheckCircle2 className="h-2.5 w-2.5" /> },
active: { chipBg: "#3b82f6", chipText: "#ffffff", icon: <CircleDot className="h-2.5 w-2.5 animate-pulse" /> },
pending: { chipBg: "#e2e8f0", chipText: "#64748b", icon: <Clock className="h-2.5 w-2.5" /> },
};
// 레거시 status 값 → semantic 매핑 (기존 데이터 호환)
const LEGACY_STATUS_TO_SEMANTIC: Record<string, string> = {
completed: "done", in_progress: "active", accepted: "active", waiting: "pending",
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
};
function getTimelineStyle(step: TimelineProcessStep): TimelineStyle {
if (step.semantic) return TIMELINE_SEMANTIC_STYLES[step.semantic] || TIMELINE_SEMANTIC_STYLES.pending;
const fallback = LEGACY_STATUS_TO_SEMANTIC[step.status];
return TIMELINE_SEMANTIC_STYLES[fallback || "pending"];
}
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
function TimelineCell({ cell, row }: CellRendererProps) {
const processFlow = row.__processFlow__ as TimelineProcessStep[] | undefined;
if (!processFlow || processFlow.length === 0) {
const fallback = cell.processColumn ? row[cell.processColumn] : "";
return (
<span className="text-[10px] text-muted-foreground">
{formatValue(fallback)}
</span>
);
}
const maxVisible = cell.visibleCount || 5;
const currentIdx = processFlow.findIndex((s) => s.isCurrent);
type DisplayItem =
| { kind: "step"; step: TimelineProcessStep }
| { kind: "count"; count: number; side: "before" | "after" };
// 현재 항목 기준으로 앞뒤 배분하여 축약
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
const displayItems = useMemo((): DisplayItem[] => {
if (processFlow.length <= maxVisible) {
return processFlow.map((s) => ({ kind: "step" as const, step: s }));
}
const effectiveIdx = Math.max(0, currentIdx);
const priority = cell.timelinePriority || "before";
// 숫자칩 2개를 제외한 나머지를 앞뒤로 배분 (priority에 따라 여분 슬롯 방향 결정)
const slotForSteps = maxVisible - 2;
const half = Math.floor(slotForSteps / 2);
const extra = slotForSteps - half - 1;
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
const beforeSlots = priority === "before" ? Math.max(half, extra) : Math.min(half, extra);
const afterSlots = slotForSteps - beforeSlots - 1;
let startIdx = effectiveIdx - beforeSlots;
let endIdx = effectiveIdx + afterSlots;
// 경계 보정
if (startIdx < 0) {
endIdx = Math.min(processFlow.length - 1, endIdx + Math.abs(startIdx));
startIdx = 0;
}
if (endIdx >= processFlow.length) {
startIdx = Math.max(0, startIdx - (endIdx - processFlow.length + 1));
endIdx = processFlow.length - 1;
}
const items: DisplayItem[] = [];
const beforeCount = startIdx;
const afterCount = processFlow.length - 1 - endIdx;
if (beforeCount > 0) {
items.push({ kind: "count", count: beforeCount, side: "before" });
}
for (let i = startIdx; i <= endIdx; i++) {
items.push({ kind: "step", step: processFlow[i] });
}
if (afterCount > 0) {
items.push({ kind: "count", count: afterCount, side: "after" });
}
return items;
}, [processFlow, maxVisible, currentIdx]);
const [modalOpen, setModalOpen] = useState(false);
const completedCount = processFlow.filter((s) => (s.semantic || LEGACY_STATUS_TO_SEMANTIC[s.status]) === "done").length;
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
const totalCount = processFlow.length;
return (
<>
<div
className={cn(
"flex w-full items-center gap-0.5 overflow-hidden px-0.5",
cell.showDetailModal !== false && "cursor-pointer",
cell.align === "center" ? "justify-center" : cell.align === "right" ? "justify-end" : "justify-start",
)}
onClick={cell.showDetailModal !== false ? (e) => { e.stopPropagation(); setModalOpen(true); } : undefined}
title={cell.showDetailModal !== false ? "클릭하여 전체 현황 보기" : undefined}
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
>
{displayItems.map((item, idx) => {
const isLast = idx === displayItems.length - 1;
if (item.kind === "count") {
return (
<React.Fragment key={`cnt-${item.side}`}>
<div
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-muted text-[8px] font-bold text-muted-foreground"
title={item.side === "before" ? `이전 ${item.count}` : `이후 ${item.count}`}
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
>
{item.count}
</div>
{!isLast && <div className="h-px w-1.5 shrink-0 border-t border-dashed border-muted-foreground/40" />}
</React.Fragment>
);
}
const styles = getTimelineStyle(item.step);
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
return (
<React.Fragment key={item.step.seqNo}>
<div
className="flex shrink-0 items-center gap-0.5 rounded-full px-1.5 py-0.5"
style={{
backgroundColor: styles.chipBg,
color: styles.chipText,
outline: item.step.isCurrent ? "2px solid #2563eb" : "none",
outlineOffset: "1px",
}}
title={`${item.step.seqNo}. ${item.step.processName} (${item.step.status})`}
>
{styles.icon}
<span className="max-w-[48px] truncate text-[9px] font-medium leading-tight">
{item.step.processName}
</span>
</div>
{!isLast && <div className="h-px w-1.5 shrink-0 border-t border-dashed border-muted-foreground/40" />}
</React.Fragment>
);
})}
</div>
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
<DialogContent className="max-w-[95vw] sm:max-w-[500px]">
<DialogHeader>
<DialogTitle className="text-base sm:text-lg"> </DialogTitle>
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
<DialogDescription className="text-xs sm:text-sm">
{totalCount} {completedCount}
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
</DialogDescription>
</DialogHeader>
<div className="space-y-0">
{processFlow.map((step, idx) => {
const styles = getTimelineStyle(step);
const sem = step.semantic || LEGACY_STATUS_TO_SEMANTIC[step.status] || "pending";
const statusLabel = sem === "done" ? "완료" : sem === "active" ? "진행" : "대기";
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
return (
<div key={step.seqNo} className="flex items-center">
{/* 세로 연결선 + 아이콘 */}
<div className="flex w-8 shrink-0 flex-col items-center">
{idx > 0 && <div className="h-3 w-px bg-border" />}
<div
className="flex h-6 w-6 items-center justify-center rounded-full"
style={{ backgroundColor: styles.chipBg, color: styles.chipText }}
>
{styles.icon}
</div>
{idx < processFlow.length - 1 && <div className="h-3 w-px bg-border" />}
</div>
{/* 항목 정보 */}
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
<div className={cn(
"ml-2 flex flex-1 items-center justify-between rounded-md px-3 py-2",
step.isCurrent && "bg-primary/5 ring-1 ring-primary/30",
)}>
<div className="flex items-center gap-2">
<span className="text-xs text-muted-foreground">{step.seqNo}</span>
<span className={cn(
"text-sm",
step.isCurrent ? "font-semibold" : "font-medium",
)}>
{step.processName}
</span>
{step.isCurrent && (
<Star className="h-3 w-3 fill-primary text-primary" />
)}
</div>
<span
className="rounded-full px-2 py-0.5 text-[10px] font-medium"
style={{ backgroundColor: styles.chipBg, color: styles.chipText }}
>
{statusLabel}
</span>
</div>
</div>
);
})}
</div>
{/* 하단 진행률 바 */}
<div className="space-y-1 pt-2">
<div className="flex justify-between text-xs text-muted-foreground">
<span></span>
<span>{totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0}%</span>
</div>
<div className="h-2 w-full overflow-hidden rounded-full bg-secondary">
<div
className="h-full rounded-full bg-primary transition-all duration-300"
style={{ width: `${totalCount > 0 ? (completedCount / totalCount) * 100 : 0}%` }}
/>
</div>
</div>
</DialogContent>
</Dialog>
</>
);
}
// ===== 11. action-buttons =====
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
function evaluateShowCondition(btn: ActionButtonDef, row: RowData): "visible" | "disabled" | "hidden" {
const cond = btn.showCondition;
if (!cond || cond.type === "always") return "visible";
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
let matched = false;
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
if (cond.type === "timeline-status") {
const subStatus = row[VIRTUAL_SUB_STATUS];
matched = subStatus !== undefined && String(subStatus) === cond.value;
} else if (cond.type === "column-value" && cond.column) {
matched = String(row[cond.column] ?? "") === (cond.value ?? "");
} else {
return "visible";
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
}
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
if (matched) return "visible";
return cond.unmatchBehavior === "disabled" ? "disabled" : "hidden";
}
function ActionButtonsCell({ cell, row, onActionButtonClick, onEnterSelectMode }: CellRendererProps) {
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
const processFlow = row.__processFlow__ as { isCurrent: boolean; processId?: string | number }[] | undefined;
const currentProcess = processFlow?.find((s) => s.isCurrent);
const currentProcessId = currentProcess?.processId;
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
if (cell.actionButtons && cell.actionButtons.length > 0) {
const evaluated = cell.actionButtons.map((btn) => ({
btn,
state: evaluateShowCondition(btn, row),
}));
const activeBtn = evaluated.find((e) => e.state === "visible");
const disabledBtn = activeBtn ? null : evaluated.find((e) => e.state === "disabled");
const pick = activeBtn || disabledBtn;
if (!pick) return null;
const { btn, state } = pick;
return (
<div className="flex items-center gap-1">
<Button
variant={btn.variant || "outline"}
size="sm"
disabled={state === "disabled"}
className="h-7 text-[10px]"
onClick={(e) => {
e.stopPropagation();
const actions = (btn.clickActions && btn.clickActions.length > 0) ? btn.clickActions : [btn.clickAction];
const firstAction = actions[0];
const config: Record<string, unknown> = {
...firstAction,
__allActions: actions,
selectModeConfig: firstAction.selectModeButtons
? { filterStatus: btn.showCondition?.value || "", buttons: firstAction.selectModeButtons }
: undefined,
};
if (currentProcessId !== undefined) config.__processId = currentProcessId;
if (firstAction.type === "select-mode" && onEnterSelectMode) {
onEnterSelectMode(btn.showCondition?.value || "", config);
return;
}
onActionButtonClick?.(btn.label, row, config);
}}
>
{btn.label}
</Button>
</div>
);
}
// 기존 구조 (actionRules) 폴백
const hasSubStatus = row[VIRTUAL_SUB_STATUS] !== undefined;
const statusValue = hasSubStatus
? String(row[VIRTUAL_SUB_STATUS] || "")
: (cell.statusColumn ? String(row[cell.statusColumn] || "") : (cell.columnName ? String(row[cell.columnName] || "") : ""));
const rules = cell.actionRules || [];
const matchedRule = rules.find((r) => r.whenStatus === statusValue);
if (!matchedRule) return null;
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
return (
<div className="flex items-center gap-1">
{matchedRule.buttons.map((btn, idx) => (
<Button
key={idx}
variant={btn.variant || "outline"}
size="sm"
className="h-7 text-[10px]"
onClick={(e) => {
e.stopPropagation();
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
const config = { ...(btn as Record<string, unknown>) };
feat(pop): 다중 액션 체이닝 + 외부 테이블 선택 + 카드 클릭 모달 + 필터 전 비표시 버튼 규칙 하나에 여러 액션을 순차 실행하는 다중 액션 체이닝, DB 직접 선택으로 외부 테이블에 값을 변경하는 기능, 카드 클릭 시 조건부 모달 열기, 필터 전 데이터 비표시 옵션을 추가한다. [다중 액션 체이닝] - types.ts: ActionButtonDef.clickActions 배열 추가 (하위호환 유지) - PopCardListV2Config: 액션 목록 UI (추가/삭제/순서) - cell-renderers: __allActions 배열로 config 전달 - PopCardListV2Component: actionsToRun 순차 실행, 실패 시 스킵 [외부 테이블 선택] - ActionButtonClickAction.joinConfig (sourceColumn, targetColumn) 추가 - ImmediateActionEditor: "DB에서 직접 선택..." 옵션 + 조인키 설정 UI - DbTableCombobox: 테이블명(영어)+설명(한글) 검색 가능 - Component: joinConfig 기반 lookupValue/lookupColumn 처리 [카드 클릭 모달] - types.ts: V2CardClickAction에 "modal-open", V2CardClickModalConfig 추가 - PopCardListV2Config: 동작 탭에 모달 설정 (화면 ID, 조건, 제목) - PopCardListV2Component: handleCardSelect 조건 체크 후 openPopModal [필터 전 데이터 비표시] - PopCardListV2Config.hideUntilFiltered Switch - Component: externalFilters 없을 때 안내 메시지 [버그 수정] - availableTableOptions: dataSource.table -> dataSource.tableName 수정 - popActionRoutes: INSERT 시 created_date/updated_date/writer 자동 추가, UPDATE 시 updated_date 자동 갱신 [액션 버튼 구조 개선] - evaluateShowCondition: 버튼별 조건 평가 (visible/disabled/hidden) - ActionButtonsEditor: 아코디언 UI + sessionStorage 상태 유지 - 1셀 1버튼 렌더링: 조건 맞는 버튼 1개만 표시
2026-03-11 18:20:02 +09:00
if (currentProcessId !== undefined) config.__processId = currentProcessId;
if (btn.clickMode === "select-mode" && onEnterSelectMode) {
onEnterSelectMode(matchedRule.whenStatus, config);
return;
feat(pop): 공정 상태 자동 계산 + 하위 필터 연동 + 타임라인 연동 상태배지 공정 필터 선택 시 상태 뱃지/카운트/버튼이 공정 상태 기준으로 동작하도록 파생 상태 자동 계산, 하위 필터 __subStatus__ 주입, 접수 버튼 공정 행 특정 로직을 구현한다. [파생 상태 자동 계산] - types.ts: StatusValueMapping.isDerived 필드 추가 isDerived=true면 DB에 없는 상태로, 이전 공정 완료 시 자동 변환 - PopCardListV2Component: injectProcessFlow에 derivedRules 기반 변환 로직 같은 semantic의 원본 상태를 자동 추론 (waiting → acceptable) - TimelineProcessStep에 processId, rawData 필드 추가 [하위 필터 __subStatus__ 주입] - PopCardListV2Component: filteredRows를 2단계로 분리 1단계: 하위 테이블(work_order_process) 필터 → 매칭 공정의 상태를 VIRTUAL_SUB_STATUS/SEMANTIC/PROCESS/SEQ 가상 컬럼으로 주입 2단계: 메인 필터에서 status 컬럼을 __subStatus__로 자동 대체 - cell-renderers: StatusBadgeCell/ActionButtonsCell이 __subStatus__ 우선 참조 하드코딩된 접수가능 판별 로직(isAcceptable) 제거 → 설정 기반으로 전환 - all_rows 발행: { rows, subStatusColumn } envelope 구조로 메타 포함 [타임라인 강조(isCurrent) 개선] - "기준" 상태(isDerived) 기반 강조 + 공정 필터 시 매칭 공정 강조 - 폴백: active → pending 순서로 자동 결정 [접수 버튼 공정 행 특정] - cell-renderers: ActionButtonsCell에서 현재 공정의 processId를 __processId로 전달 - PopCardListV2Component: onActionButtonClick에서 __processId로 공정 행 UPDATE [상태배지 타임라인 연동] - PopCardListV2Config: StatusMappingEditor에 "타임라인 연동" 버튼 추가 같은 카드의 타임라인 statusMappings에서 값/라벨/색상/컬럼 자동 가져옴 [타임라인 설정 UI] - PopCardListV2Config: StatusMappingsEditor에 "기준" 라디오 버튼 추가 하나만 선택 가능, 재클릭 시 해제 [연결 탭 하위 테이블 필터 설정] - ConnectionEditor: isSubTable 체크박스 + targetColumn/filterMode 설정 UI - pop-layout.ts: filterConfig.isSubTable 필드 추가 [status-chip 하위 필터 자동 전환] - PopSearchComponent: 카드가 전달한 subStatusColumn 자동 감지 useSubCount 활성 시 집계/필터 컬럼 자동 전환 - PopSearchConfig: useSubCount 체크박스 설정 UI - types.ts: StatusChipConfig.useSubCount 필드 추가 [디자이너 라벨] - ComponentEditorPanel: comp.label || comp.id 패턴으로 통일
2026-03-11 12:07:11 +09:00
}
onActionButtonClick?.(btn.taskPreset, row, config);
feat(pop): pop-card-list-v2 슬롯 기반 카드 컴포넌트 신규 + 타임라인 범용화 + 액션 인라인 설정 CSS Grid 기반 슬롯 구조의 pop-card-list-v2 컴포넌트를 추가한다. 기존 pop-card-list의 데이터 로딩/필터링/장바구니 로직을 재활용하되, 카드 내부는 12종 셀 타입(text/field/image/badge/button/number-input/ cart-button/package-summary/status-badge/timeline/action-buttons/ footer-status)의 조합으로 자유롭게 구성할 수 있다. [신규 컴포넌트: pop-card-list-v2] - PopCardListV2Component: 런타임 렌더링 (데이터 조회 + CSS Grid 카드) - PopCardListV2Config: 3탭 설정 패널 (데이터/카드 디자인/동작) - PopCardListV2Preview: 디자이너 미리보기 - cell-renderers: 셀 타입별 독립 렌더러 12종 - migrate: v1 -> v2 설정 마이그레이션 함수 - index: PopComponentRegistry 자동 등록 [타임라인 데이터 소스 범용화] - TimelineDataSource 인터페이스로 공정 테이블/FK/컬럼/상태값 매핑 설정 - 하드코딩(work_orders+work_order_process) 제거 -> 설정 기반 동적 조회 - injectProcessFlow: 설정 기반 공정 데이터 조회 + __processFlow__ 가상 컬럼 주입 - 상태값 정규화(DB값 -> waiting/accepted/in_progress/completed) [액션 버튼 인라인 설정] - actionRules 내 updates 배열로 동작 정의 (별도 DB 테이블 불필요) - execute-action API 재활용 (targetTable/column/valueType) - 백엔드 __CURRENT_USER__/__CURRENT_TIME__ 특수값 치환 [디자이너 통합] - PopComponentType에 "pop-card-list-v2" 추가 - ComponentEditorPanel/ComponentPalette/PopRenderer 등록 - PopDesigner loadLayout: components 존재 확인 null 체크 추가 [기타] - .gitignore: .gradle/ 추가
2026-03-10 16:56:14 +09:00
}}
>
{btn.label}
</Button>
))}
</div>
);
}
// ===== 12. footer-status =====
function FooterStatusCell({ cell, row }: CellRendererProps) {
const value = cell.footerStatusColumn ? row[cell.footerStatusColumn] : "";
const strValue = String(value || "");
const mapped = cell.footerStatusMap?.find((m) => m.value === strValue);
if (!strValue && !cell.footerLabel) return null;
return (
<div
className="flex w-full items-center justify-between px-2 py-1"
style={{ borderTop: cell.showTopBorder !== false ? "1px solid hsl(var(--border))" : "none" }}
>
{cell.footerLabel && (
<span className="text-[10px] text-muted-foreground">{cell.footerLabel}</span>
)}
{mapped ? (
<span
className="inline-flex items-center rounded-full px-2 py-0.5 text-[9px] font-semibold"
style={{ backgroundColor: `${mapped.color}20`, color: mapped.color }}
>
{mapped.label}
</span>
) : strValue ? (
<span className="text-[10px] font-medium text-muted-foreground">
{strValue}
</span>
) : null}
</div>
);
}