Merge branch 'feature/v2-renewal' of http://39.117.244.52:3000/kjs/ERP-node into feature/v2-unified-renewal
This commit is contained in:
@@ -27,7 +27,7 @@ export enum ComponentCategory {
|
||||
SYSTEM = "system", // 시스템 컴포넌트 (에러 바운더리 등)
|
||||
ADMIN = "admin", // 관리자 전용 컴포넌트
|
||||
CUSTOM = "custom", // 커스텀 컴포넌트
|
||||
UNIFIED = "unified", // 통합 컴포넌트 (새로운 Unified 컴포넌트 시스템)
|
||||
V2 = "v2", // 통합 컴포넌트 (새로운 V2 컴포넌트 시스템)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,7 +369,7 @@ export const COMPONENT_CATEGORIES_INFO = {
|
||||
description: "사용자 정의 컴포넌트",
|
||||
color: "#059669",
|
||||
},
|
||||
[ComponentCategory.UNIFIED]: {
|
||||
[ComponentCategory.V2]: {
|
||||
name: "통합",
|
||||
description: "새로운 통합 컴포넌트 시스템",
|
||||
color: "#2563eb",
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
ActiveStatus,
|
||||
TimestampFields,
|
||||
BaseApiResponse,
|
||||
} from "./unified-core";
|
||||
} from "./v2-core";
|
||||
|
||||
// ===== 버튼 제어 관련 =====
|
||||
|
||||
|
||||
+10
-10
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
// ===== 핵심 공통 타입들 =====
|
||||
export * from "./unified-core";
|
||||
export * from "./v2-core";
|
||||
|
||||
// ===== 시스템별 전용 타입들 =====
|
||||
export * from "./screen-management";
|
||||
@@ -15,7 +15,7 @@ export * from "./table-management";
|
||||
|
||||
// ===== 기존 호환성을 위한 re-export =====
|
||||
|
||||
// unified-core에서 제공하는 주요 타입들을 직접 export
|
||||
// v2-core에서 제공하는 주요 타입들을 직접 export
|
||||
export type {
|
||||
// 핵심 타입들
|
||||
WebType,
|
||||
@@ -41,7 +41,7 @@ export type {
|
||||
// 이벤트 타입들
|
||||
WebTypeEvent,
|
||||
ComponentEvent,
|
||||
} from "./unified-core";
|
||||
} from "./v2-core";
|
||||
|
||||
// screen-management에서 제공하는 주요 타입들
|
||||
export type {
|
||||
@@ -124,7 +124,7 @@ export type {
|
||||
export type {
|
||||
// 테이블 정보
|
||||
TableInfo,
|
||||
UnifiedColumnInfo,
|
||||
V2ColumnInfo,
|
||||
ColumnTypeInfo,
|
||||
ColumnSettings,
|
||||
|
||||
@@ -159,8 +159,8 @@ export type {
|
||||
|
||||
// ===== 타입 가드 함수들 통합 export =====
|
||||
|
||||
// unified-core 타입 가드들
|
||||
export { isWebType, isButtonActionType, isComponentType, ynToBoolean, booleanToYN } from "./unified-core";
|
||||
// v2-core 타입 가드들
|
||||
export { isWebType, isButtonActionType, isComponentType, ynToBoolean, booleanToYN } from "./v2-core";
|
||||
|
||||
// screen-management 타입 가드들
|
||||
export {
|
||||
@@ -195,8 +195,8 @@ export {
|
||||
isRequiredColumn,
|
||||
isSystemColumn,
|
||||
mapWebTypeStandardToDefinition,
|
||||
mapColumnTypeInfoToUnified,
|
||||
mapUnifiedToColumnTypeInfo,
|
||||
mapColumnTypeInfoToV2,
|
||||
mapV2ToColumnTypeInfo,
|
||||
} from "./table-management";
|
||||
|
||||
// ===== 상수들 통합 export =====
|
||||
@@ -207,12 +207,12 @@ export { WEB_TYPE_OPTIONS } from "./table-management";
|
||||
// ===== 타입 별칭 (기존 호환성) =====
|
||||
|
||||
/**
|
||||
* @deprecated screen.ts에서 이전하세요. unified-core.ts의 WebType을 사용하세요.
|
||||
* @deprecated screen.ts에서 이전하세요. v2-core.ts의 WebType을 사용하세요.
|
||||
*/
|
||||
export type LegacyWebType = WebType;
|
||||
|
||||
/**
|
||||
* @deprecated screen.ts에서 이전하세요. unified-core.ts의 ButtonActionType을 사용하세요.
|
||||
* @deprecated screen.ts에서 이전하세요. v2-core.ts의 ButtonActionType을 사용하세요.
|
||||
*/
|
||||
export type LegacyButtonActionType = ButtonActionType;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 화면 관리에서 선택 가능한 세부 타입들을 정의합니다.
|
||||
*/
|
||||
|
||||
import { WebType } from "./unified-core";
|
||||
import { WebType } from "./v2-core";
|
||||
|
||||
/**
|
||||
* 핵심 입력 타입
|
||||
|
||||
@@ -17,7 +17,8 @@ export type InputType =
|
||||
| "checkbox" // 체크박스
|
||||
| "radio" // 라디오버튼
|
||||
| "image" // 이미지
|
||||
| "file"; // 파일
|
||||
| "file" // 파일
|
||||
| "numbering"; // 채번 (자동번호 생성)
|
||||
|
||||
// 입력 타입 옵션 정의
|
||||
export interface InputTypeOption {
|
||||
@@ -113,6 +114,13 @@ export const INPUT_TYPE_OPTIONS: InputTypeOption[] = [
|
||||
category: "basic",
|
||||
icon: "File",
|
||||
},
|
||||
{
|
||||
value: "numbering",
|
||||
label: "채번",
|
||||
description: "자동 번호 생성 (테이블 설정 기반)",
|
||||
category: "basic",
|
||||
icon: "Hash",
|
||||
},
|
||||
];
|
||||
|
||||
// 카테고리별 입력 타입 그룹화
|
||||
@@ -180,6 +188,11 @@ export const INPUT_TYPE_DEFAULT_CONFIGS: Record<InputType, Record<string, any>>
|
||||
accept: "*/*",
|
||||
maxSize: 10485760, // 10MB
|
||||
},
|
||||
numbering: {
|
||||
placeholder: "자동 생성됩니다",
|
||||
readOnly: true,
|
||||
autoGenerate: true,
|
||||
},
|
||||
};
|
||||
|
||||
// 레거시 웹 타입 → 입력 타입 매핑
|
||||
@@ -217,6 +230,9 @@ export const WEB_TYPE_TO_INPUT_TYPE: Record<string, InputType> = {
|
||||
file: "file",
|
||||
image: "image",
|
||||
|
||||
// 채번
|
||||
numbering: "numbering",
|
||||
|
||||
// 기타 (기본값: text)
|
||||
button: "text",
|
||||
};
|
||||
@@ -234,6 +250,7 @@ export const INPUT_TYPE_TO_WEB_TYPE: Record<InputType, string> = {
|
||||
radio: "radio",
|
||||
image: "image",
|
||||
file: "file",
|
||||
numbering: "numbering",
|
||||
};
|
||||
|
||||
// 입력 타입 변환 함수
|
||||
@@ -288,4 +305,9 @@ export const INPUT_TYPE_VALIDATION_RULES: Record<InputType, Record<string, any>>
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
numbering: {
|
||||
type: "string",
|
||||
required: false,
|
||||
autoGenerate: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 코드 파트 유형 (4가지)
|
||||
* 코드 파트 유형 (5가지)
|
||||
*/
|
||||
export type CodePartType =
|
||||
| "sequence" // 순번 (자동 증가 숫자)
|
||||
| "number" // 숫자 (고정 자릿수)
|
||||
| "date" // 날짜 (다양한 날짜 형식)
|
||||
| "text"; // 문자 (텍스트)
|
||||
| "text" // 문자 (텍스트)
|
||||
| "category"; // 카테고리 (카테고리 값에 따른 형식)
|
||||
|
||||
/**
|
||||
* 생성 방식
|
||||
@@ -30,6 +31,17 @@ export type DateFormat =
|
||||
| "YYYYMMDD" // 20251104
|
||||
| "YYMMDD"; // 251104
|
||||
|
||||
/**
|
||||
* 카테고리 값별 형식 매핑
|
||||
* 예: 가스켓 → ITM, 벌브 → VLV
|
||||
*/
|
||||
export interface CategoryFormatMapping {
|
||||
categoryValueId: number; // 카테고리 값 ID
|
||||
categoryValueLabel: string; // 카테고리 값 라벨 (표시용)
|
||||
categoryValuePath?: string; // 전체 경로 (예: "원자재/벌크/가스켓")
|
||||
format: string; // 생성할 형식 (예: "ITM", "VLV")
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 규칙 파트
|
||||
*/
|
||||
@@ -57,6 +69,10 @@ export interface NumberingRulePart {
|
||||
|
||||
// 문자용
|
||||
textValue?: string; // 텍스트 값 (예: "PRJ", "CODE")
|
||||
|
||||
// 카테고리용
|
||||
categoryKey?: string; // 카테고리 키 (테이블.컬럼 형식, 예: "item_info.type")
|
||||
categoryMappings?: CategoryFormatMapping[]; // 카테고리 값별 형식 매핑
|
||||
};
|
||||
|
||||
// 직접 입력 설정
|
||||
@@ -91,6 +107,11 @@ export interface NumberingRuleConfig {
|
||||
tableName?: string; // 적용할 테이블명
|
||||
columnName?: string; // 적용할 컬럼명
|
||||
|
||||
// 카테고리 조건 (특정 카테고리 값일 때만 이 규칙 적용)
|
||||
categoryColumn?: string; // 카테고리 조건 컬럼명 (예: 'type', 'material')
|
||||
categoryValueId?: number; // 카테고리 값 ID (category_values_test.value_id)
|
||||
categoryValueLabel?: string; // 카테고리 값 라벨 (조회 시 조인)
|
||||
|
||||
// 메타 정보
|
||||
companyCode?: string;
|
||||
createdAt?: string;
|
||||
@@ -106,6 +127,7 @@ export const CODE_PART_TYPE_OPTIONS: Array<{ value: CodePartType; label: string;
|
||||
{ value: "number", label: "숫자", description: "고정 자릿수 숫자 (001, 002...)" },
|
||||
{ value: "date", label: "날짜", description: "날짜 형식 (2025-11-04)" },
|
||||
{ value: "text", label: "문자", description: "텍스트 또는 코드" },
|
||||
{ value: "category", label: "카테고리", description: "카테고리 값에 따른 형식" },
|
||||
];
|
||||
|
||||
export const DATE_FORMAT_OPTIONS: Array<{ value: DateFormat; label: string; example: string }> = [
|
||||
|
||||
@@ -43,9 +43,19 @@ export interface CalculationFormula {
|
||||
* 필드 표시 모드
|
||||
* - input: 입력 필드로 표시 (편집 가능)
|
||||
* - readonly: 읽기 전용 텍스트로 표시
|
||||
* - hidden: 숨김 (UI에 표시되지 않지만 데이터에 포함됨)
|
||||
* - (카테고리 타입은 자동으로 배지로 표시됨)
|
||||
*/
|
||||
export type RepeaterFieldDisplayMode = "input" | "readonly";
|
||||
export type RepeaterFieldDisplayMode = "input" | "readonly" | "hidden";
|
||||
|
||||
/**
|
||||
* 하위 데이터 조회 소스 설정
|
||||
* 필드 값을 하위 데이터 조회 결과에서 가져올 때 사용
|
||||
*/
|
||||
export interface SubDataSourceConfig {
|
||||
enabled: boolean; // 활성화 여부
|
||||
sourceColumn: string; // 하위 데이터 조회 테이블의 소스 컬럼 (예: lot_number)
|
||||
}
|
||||
|
||||
/**
|
||||
* 반복 그룹 내 개별 필드 정의
|
||||
@@ -60,6 +70,8 @@ export interface RepeaterFieldDefinition {
|
||||
options?: Array<{ label: string; value: string }>; // select용
|
||||
width?: string; // 필드 너비 (예: "200px", "50%")
|
||||
displayMode?: RepeaterFieldDisplayMode; // 표시 모드: input(입력), readonly(읽기전용)
|
||||
isHidden?: boolean; // 숨김 여부 (true면 테이블에 표시 안 함, 데이터는 저장)
|
||||
subDataSource?: SubDataSourceConfig; // 하위 데이터 조회에서 값 가져오기 설정
|
||||
categoryCode?: string; // category 타입일 때 사용할 카테고리 코드
|
||||
formula?: CalculationFormula; // 계산식 (type이 "calculated"일 때 사용)
|
||||
numberFormat?: {
|
||||
@@ -113,6 +125,14 @@ export type RepeaterData = RepeaterItemData[];
|
||||
// 품목 선택 시 재고/단가 등 관련 데이터를 조회하고 선택하는 기능
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* 선택 데이터 필드 매핑 설정
|
||||
*/
|
||||
export interface SubDataFieldMapping {
|
||||
sourceColumn: string; // 조회 테이블 컬럼 (예: lot_number)
|
||||
targetField: string; // 저장 테이블 컬럼 (예: lot_number) 또는 "" (선택안함)
|
||||
}
|
||||
|
||||
/**
|
||||
* 하위 데이터 조회 테이블 설정
|
||||
*/
|
||||
@@ -121,6 +141,8 @@ export interface SubDataLookupSettings {
|
||||
linkColumn: string; // 상위 데이터와 연결할 컬럼 (예: item_code)
|
||||
displayColumns: string[]; // 표시할 컬럼들 (예: ["warehouse_code", "location_code", "quantity"])
|
||||
columnLabels?: Record<string, string>; // 컬럼 라벨 (예: { warehouse_code: "창고" })
|
||||
columnOrder?: string[]; // 컬럼 표시 순서 (없으면 displayColumns 순서 사용)
|
||||
fieldMappings?: SubDataFieldMapping[]; // 선택 데이터 저장 매핑 (조회 컬럼 → 저장 컬럼)
|
||||
additionalFilters?: Record<string, any>; // 추가 필터 조건
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
CompanyCode,
|
||||
ActiveStatus,
|
||||
isWebType,
|
||||
} from "./unified-core";
|
||||
} from "./v2-core";
|
||||
import { ColumnSpanPreset } from "@/lib/constants/columnSpans";
|
||||
import { ResponsiveComponentConfig } from "./responsive";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 새로운 코드에서는 다음을 사용해주세요:
|
||||
* - import { ... } from "@/types" (통합 타입 시스템)
|
||||
* - import { ... } from "@/types/screen-management" (화면관리 전용)
|
||||
* - import { ... } from "@/types/unified-core" (핵심 공통 타입)
|
||||
* - import { ... } from "@/types/v2-core" (핵심 공통 타입)
|
||||
*/
|
||||
|
||||
// 🎯 새로운 통합 타입 시스템에서 re-export
|
||||
@@ -13,7 +13,7 @@ export * from "./index";
|
||||
|
||||
// 🔄 기존 호환성을 위한 타입 별칭들
|
||||
export type {
|
||||
// 핵심 타입들 (unified-core에서)
|
||||
// 핵심 타입들 (v2-core에서)
|
||||
WebType,
|
||||
ButtonActionType,
|
||||
ComponentType,
|
||||
@@ -63,7 +63,7 @@ export type {
|
||||
|
||||
// 테이블 정보 (table-management에서)
|
||||
TableInfo,
|
||||
UnifiedColumnInfo as ColumnInfo,
|
||||
V2ColumnInfo as ColumnInfo,
|
||||
|
||||
// API 응답들
|
||||
PaginatedResponse,
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
BaseApiResponse,
|
||||
PaginatedResponse,
|
||||
ConditionOperator,
|
||||
} from "./unified-core";
|
||||
} from "./v2-core";
|
||||
|
||||
// ===== 기본 테이블 정보 =====
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface TableInfo {
|
||||
/**
|
||||
* 통합된 컬럼 정보 (프론트엔드/백엔드 호환)
|
||||
*/
|
||||
export interface UnifiedColumnInfo {
|
||||
export interface V2ColumnInfo {
|
||||
// 기본 정보
|
||||
tableName: string;
|
||||
columnName: string;
|
||||
@@ -317,7 +317,7 @@ export interface TableListResponse extends BaseApiResponse<TableInfo[]> {}
|
||||
/**
|
||||
* 컬럼 목록 응답
|
||||
*/
|
||||
export interface ColumnListResponse extends BaseApiResponse<UnifiedColumnInfo[]> {}
|
||||
export interface ColumnListResponse extends BaseApiResponse<V2ColumnInfo[]> {}
|
||||
|
||||
/**
|
||||
* 컬럼 타입 정보 응답 (백엔드 호환)
|
||||
@@ -396,9 +396,9 @@ export const mapWebTypeStandardToDefinition = (standard: WebTypeStandard): WebTy
|
||||
});
|
||||
|
||||
/**
|
||||
* ColumnTypeInfo를 UnifiedColumnInfo로 변환
|
||||
* ColumnTypeInfo를 V2ColumnInfo로 변환
|
||||
*/
|
||||
export const mapColumnTypeInfoToUnified = (columnInfo: ColumnTypeInfo): UnifiedColumnInfo => ({
|
||||
export const mapColumnTypeInfoToV2 = (columnInfo: ColumnTypeInfo): V2ColumnInfo => ({
|
||||
tableName: columnInfo.tableName || "",
|
||||
columnName: columnInfo.columnName,
|
||||
displayName: columnInfo.displayName,
|
||||
@@ -424,31 +424,31 @@ export const mapColumnTypeInfoToUnified = (columnInfo: ColumnTypeInfo): UnifiedC
|
||||
});
|
||||
|
||||
/**
|
||||
* UnifiedColumnInfo를 ColumnTypeInfo로 변환
|
||||
* V2ColumnInfo를 ColumnTypeInfo로 변환
|
||||
*/
|
||||
export const mapUnifiedToColumnTypeInfo = (unified: UnifiedColumnInfo): ColumnTypeInfo => ({
|
||||
tableName: unified.tableName,
|
||||
columnName: unified.columnName,
|
||||
displayName: unified.displayName,
|
||||
dataType: unified.dataType,
|
||||
dbType: unified.dbType,
|
||||
webType: unified.webType,
|
||||
inputType: unified.inputType,
|
||||
detailSettings: unified.detailSettings ? JSON.stringify(unified.detailSettings) : "{}",
|
||||
description: unified.description || "",
|
||||
isNullable: unified.isNullable ? "Y" : "N",
|
||||
isPrimaryKey: unified.isPrimaryKey,
|
||||
defaultValue: unified.defaultValue,
|
||||
maxLength: unified.maxLength,
|
||||
numericPrecision: unified.numericPrecision,
|
||||
numericScale: unified.numericScale,
|
||||
isVisible: unified.isVisible,
|
||||
displayOrder: unified.displayOrder,
|
||||
codeCategory: unified.codeCategory,
|
||||
codeValue: unified.codeValue,
|
||||
referenceTable: unified.referenceTable,
|
||||
referenceColumn: unified.referenceColumn,
|
||||
displayColumn: unified.displayColumn,
|
||||
export const mapV2ToColumnTypeInfo = (v2: V2ColumnInfo): ColumnTypeInfo => ({
|
||||
tableName: v2.tableName,
|
||||
columnName: v2.columnName,
|
||||
displayName: v2.displayName,
|
||||
dataType: v2.dataType,
|
||||
dbType: v2.dbType,
|
||||
webType: v2.webType,
|
||||
inputType: v2.inputType,
|
||||
detailSettings: v2.detailSettings ? JSON.stringify(v2.detailSettings) : "{}",
|
||||
description: v2.description || "",
|
||||
isNullable: v2.isNullable ? "Y" : "N",
|
||||
isPrimaryKey: v2.isPrimaryKey,
|
||||
defaultValue: v2.defaultValue,
|
||||
maxLength: v2.maxLength,
|
||||
numericPrecision: v2.numericPrecision,
|
||||
numericScale: v2.numericScale,
|
||||
isVisible: v2.isVisible,
|
||||
displayOrder: v2.displayOrder,
|
||||
codeCategory: v2.codeCategory,
|
||||
codeValue: v2.codeValue,
|
||||
referenceTable: v2.referenceTable,
|
||||
referenceColumn: v2.referenceColumn,
|
||||
displayColumn: v2.displayColumn,
|
||||
});
|
||||
|
||||
// ===== 타입 가드 함수들 =====
|
||||
@@ -484,7 +484,7 @@ export const isSelectWebType = (webType: string): boolean => {
|
||||
/**
|
||||
* 컬럼이 필수 필드인지 확인
|
||||
*/
|
||||
export const isRequiredColumn = (column: UnifiedColumnInfo): boolean => {
|
||||
export const isRequiredColumn = (column: V2ColumnInfo): boolean => {
|
||||
return !column.isNullable || column.isPrimaryKey;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,512 +0,0 @@
|
||||
/**
|
||||
* Unified 컴포넌트 타입 정의
|
||||
*
|
||||
* 10개의 통합 컴포넌트 시스템을 위한 타입 정의
|
||||
* - UnifiedInput
|
||||
* - UnifiedSelect
|
||||
* - UnifiedDate
|
||||
* - UnifiedText
|
||||
* - UnifiedMedia
|
||||
* - UnifiedList
|
||||
* - UnifiedLayout
|
||||
* - UnifiedGroup
|
||||
* - UnifiedBiz
|
||||
* - UnifiedHierarchy
|
||||
*/
|
||||
|
||||
import { Position, Size, CommonStyle, ValidationRule } from "./unified-core";
|
||||
|
||||
// ===== 공통 타입 =====
|
||||
|
||||
/**
|
||||
* Unified 컴포넌트 타입
|
||||
*/
|
||||
export type UnifiedComponentType =
|
||||
| "UnifiedInput"
|
||||
| "UnifiedSelect"
|
||||
| "UnifiedDate"
|
||||
| "UnifiedText"
|
||||
| "UnifiedMedia"
|
||||
| "UnifiedList"
|
||||
| "UnifiedLayout"
|
||||
| "UnifiedGroup"
|
||||
| "UnifiedBiz"
|
||||
| "UnifiedHierarchy";
|
||||
|
||||
/**
|
||||
* 조건부 렌더링 설정
|
||||
*/
|
||||
export interface ConditionalConfig {
|
||||
enabled: boolean;
|
||||
field: string; // 참조 필드
|
||||
operator: "=" | "!=" | ">" | "<" | "in" | "notIn" | "isEmpty" | "isNotEmpty";
|
||||
value: unknown;
|
||||
action: "show" | "hide" | "disable" | "enable";
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 입력 설정
|
||||
*/
|
||||
export interface AutoFillConfig {
|
||||
enabled: boolean;
|
||||
sourceTable: string;
|
||||
filterColumn: string;
|
||||
userField: "companyCode" | "userId" | "deptCode";
|
||||
displayColumn: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 연쇄 관계 설정
|
||||
*/
|
||||
export interface CascadingConfig {
|
||||
parentField: string;
|
||||
filterColumn: string;
|
||||
clearOnChange?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상호 배제 설정
|
||||
*/
|
||||
export interface MutualExclusionConfig {
|
||||
enabled: boolean;
|
||||
targetField: string;
|
||||
type: "exclusive" | "inclusive";
|
||||
}
|
||||
|
||||
/**
|
||||
* 공통 Unified 컴포넌트 속성
|
||||
*/
|
||||
export interface UnifiedBaseProps {
|
||||
id: string;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
readonly?: boolean;
|
||||
disabled?: boolean;
|
||||
// 데이터 바인딩
|
||||
tableName?: string;
|
||||
columnName?: string;
|
||||
// 위치 및 크기
|
||||
position?: Position;
|
||||
size?: Size;
|
||||
// 스타일
|
||||
style?: CommonStyle;
|
||||
// 조건부 및 자동화
|
||||
conditional?: ConditionalConfig;
|
||||
autoFill?: AutoFillConfig;
|
||||
// 유효성 검사
|
||||
validation?: ValidationRule[];
|
||||
}
|
||||
|
||||
// ===== UnifiedInput =====
|
||||
|
||||
export type UnifiedInputType = "text" | "number" | "password" | "slider" | "color" | "button";
|
||||
export type UnifiedInputFormat = "none" | "email" | "tel" | "url" | "currency" | "biz_no";
|
||||
|
||||
export interface UnifiedInputConfig {
|
||||
type: UnifiedInputType;
|
||||
format?: UnifiedInputFormat;
|
||||
mask?: string;
|
||||
placeholder?: string;
|
||||
// 숫자 전용
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
// 버튼 전용
|
||||
buttonText?: string;
|
||||
buttonVariant?: "default" | "destructive" | "outline" | "secondary" | "ghost";
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export interface UnifiedInputProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedInput";
|
||||
config: UnifiedInputConfig;
|
||||
value?: string | number;
|
||||
onChange?: (value: string | number) => void;
|
||||
}
|
||||
|
||||
// ===== UnifiedSelect =====
|
||||
|
||||
export type UnifiedSelectMode = "dropdown" | "radio" | "check" | "tag" | "toggle" | "swap";
|
||||
export type UnifiedSelectSource = "static" | "code" | "db" | "api" | "entity" | "category";
|
||||
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface UnifiedSelectConfig {
|
||||
mode: UnifiedSelectMode;
|
||||
source: UnifiedSelectSource;
|
||||
// 정적 옵션 (source: static)
|
||||
options?: SelectOption[];
|
||||
// 코드 그룹 (source: code)
|
||||
codeGroup?: string;
|
||||
// DB 연결 (source: db)
|
||||
table?: string;
|
||||
valueColumn?: string;
|
||||
labelColumn?: string;
|
||||
filters?: Array<{ column: string; operator: string; value: unknown }>;
|
||||
// 엔티티 연결 (source: entity)
|
||||
entityTable?: string;
|
||||
entityValueField?: string;
|
||||
entityLabelField?: string;
|
||||
entityValueColumn?: string; // alias for entityValueField
|
||||
entityLabelColumn?: string; // alias for entityLabelField
|
||||
// API 연결 (source: api)
|
||||
apiEndpoint?: string;
|
||||
// 카테고리 연결 (source: category) - 레거시, code로 자동 변환됨
|
||||
categoryTable?: string;
|
||||
categoryColumn?: string;
|
||||
// 공통 옵션
|
||||
searchable?: boolean;
|
||||
multiple?: boolean;
|
||||
maxSelect?: number;
|
||||
allowClear?: boolean;
|
||||
// 연쇄 관계
|
||||
cascading?: CascadingConfig;
|
||||
// 상호 배제
|
||||
mutualExclusion?: MutualExclusionConfig;
|
||||
// 계층 코드 연쇄 선택 (source: code일 때 계층 구조 사용)
|
||||
hierarchical?: boolean; // 계층 구조 사용 여부
|
||||
parentField?: string; // 부모 값을 참조할 필드 (다른 컴포넌트의 columnName)
|
||||
}
|
||||
|
||||
export interface UnifiedSelectProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedSelect";
|
||||
config: UnifiedSelectConfig;
|
||||
value?: string | string[];
|
||||
onChange?: (value: string | string[]) => void;
|
||||
}
|
||||
|
||||
// ===== UnifiedDate =====
|
||||
|
||||
export type UnifiedDateType = "date" | "time" | "datetime";
|
||||
|
||||
export interface UnifiedDateConfig {
|
||||
type: UnifiedDateType;
|
||||
format?: string;
|
||||
range?: boolean;
|
||||
minDate?: string;
|
||||
maxDate?: string;
|
||||
showToday?: boolean;
|
||||
}
|
||||
|
||||
export interface UnifiedDateProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedDate";
|
||||
config: UnifiedDateConfig;
|
||||
value?: string | [string, string]; // 범위 선택 시 튜플
|
||||
onChange?: (value: string | [string, string]) => void;
|
||||
}
|
||||
|
||||
// ===== UnifiedText =====
|
||||
|
||||
export type UnifiedTextMode = "simple" | "rich" | "code" | "markdown";
|
||||
|
||||
export interface UnifiedTextConfig {
|
||||
mode: UnifiedTextMode;
|
||||
rows?: number;
|
||||
maxLength?: number;
|
||||
placeholder?: string;
|
||||
resize?: "none" | "vertical" | "horizontal" | "both";
|
||||
}
|
||||
|
||||
export interface UnifiedTextProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedText";
|
||||
config: UnifiedTextConfig;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
// ===== UnifiedMedia =====
|
||||
|
||||
export type UnifiedMediaType = "file" | "image" | "video" | "audio";
|
||||
|
||||
export interface UnifiedMediaConfig {
|
||||
type: UnifiedMediaType;
|
||||
multiple?: boolean;
|
||||
accept?: string;
|
||||
maxSize?: number;
|
||||
preview?: boolean;
|
||||
uploadEndpoint?: string;
|
||||
}
|
||||
|
||||
export interface UnifiedMediaProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedMedia";
|
||||
config: UnifiedMediaConfig;
|
||||
value?: string | string[]; // 파일 URL 또는 배열
|
||||
onChange?: (value: string | string[]) => void;
|
||||
}
|
||||
|
||||
// ===== UnifiedList =====
|
||||
|
||||
export type UnifiedListViewMode = "table" | "card" | "kanban" | "list";
|
||||
|
||||
export interface ListColumn {
|
||||
field: string;
|
||||
header: string;
|
||||
width?: number;
|
||||
sortable?: boolean;
|
||||
filterable?: boolean;
|
||||
editable?: boolean;
|
||||
format?: string;
|
||||
}
|
||||
|
||||
export interface UnifiedListCardConfig {
|
||||
titleColumn?: string;
|
||||
subtitleColumn?: string;
|
||||
descriptionColumn?: string;
|
||||
imageColumn?: string;
|
||||
cardsPerRow?: number;
|
||||
cardSpacing?: number;
|
||||
showActions?: boolean;
|
||||
}
|
||||
|
||||
export interface UnifiedListConfig {
|
||||
viewMode: UnifiedListViewMode;
|
||||
editable?: boolean;
|
||||
searchable?: boolean;
|
||||
pageable?: boolean;
|
||||
pageSize?: number;
|
||||
columns?: ListColumn[];
|
||||
modal?: boolean;
|
||||
cardConfig?: UnifiedListCardConfig;
|
||||
// 데이터 소스
|
||||
dataSource?: {
|
||||
table?: string;
|
||||
api?: string;
|
||||
filters?: Array<{ column: string; operator: string; value: unknown }>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UnifiedListProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedList";
|
||||
config: UnifiedListConfig;
|
||||
data?: Record<string, unknown>[];
|
||||
selectedRows?: Record<string, unknown>[];
|
||||
onRowSelect?: (rows: Record<string, unknown>[]) => void;
|
||||
onRowClick?: (row: Record<string, unknown>) => void;
|
||||
}
|
||||
|
||||
// ===== UnifiedLayout =====
|
||||
|
||||
export type UnifiedLayoutType = "grid" | "split" | "flex" | "divider" | "screen-embed";
|
||||
|
||||
export interface UnifiedLayoutConfig {
|
||||
type: UnifiedLayoutType;
|
||||
columns?: number; // 12컬럼 시스템에서 실제 표시할 컬럼 수 (1-12)
|
||||
gap?: string;
|
||||
splitRatio?: number[];
|
||||
direction?: "horizontal" | "vertical";
|
||||
use12Column?: boolean; // 12컬럼 시스템 사용 여부 (기본 true)
|
||||
// screen-embed 전용
|
||||
screenId?: number;
|
||||
}
|
||||
|
||||
export interface UnifiedLayoutProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedLayout";
|
||||
config: UnifiedLayoutConfig;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
// ===== UnifiedGroup =====
|
||||
|
||||
export type UnifiedGroupType = "tabs" | "accordion" | "section" | "card-section" | "modal" | "form-modal";
|
||||
|
||||
export interface TabItem {
|
||||
id: string;
|
||||
title: string;
|
||||
content?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface UnifiedGroupConfig {
|
||||
type: UnifiedGroupType;
|
||||
title?: string;
|
||||
collapsible?: boolean;
|
||||
defaultExpanded?: boolean;
|
||||
// 탭 전용
|
||||
tabs?: TabItem[];
|
||||
activeTab?: string;
|
||||
// 모달 전용
|
||||
modalSize?: "sm" | "md" | "lg" | "xl";
|
||||
}
|
||||
|
||||
export interface UnifiedGroupProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedGroup";
|
||||
config: UnifiedGroupConfig;
|
||||
children?: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
// ===== UnifiedBiz =====
|
||||
|
||||
export type UnifiedBizType = "flow" | "rack" | "map" | "numbering" | "category" | "mapping" | "related-buttons";
|
||||
|
||||
export interface UnifiedBizConfig {
|
||||
type: UnifiedBizType;
|
||||
// 각 타입별 설정은 제네릭하게 처리
|
||||
config?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface UnifiedBizProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedBiz";
|
||||
config: UnifiedBizConfig;
|
||||
}
|
||||
|
||||
// ===== UnifiedHierarchy =====
|
||||
|
||||
export type UnifiedHierarchyType = "tree" | "org" | "bom" | "cascading";
|
||||
export type UnifiedHierarchyViewMode = "tree" | "table" | "indent" | "dropdown";
|
||||
|
||||
export interface HierarchyNode {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
label: string;
|
||||
children?: HierarchyNode[];
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface UnifiedHierarchyConfig {
|
||||
type: UnifiedHierarchyType;
|
||||
viewMode: UnifiedHierarchyViewMode;
|
||||
source?: string; // 계층 그룹 코드
|
||||
editable?: boolean;
|
||||
draggable?: boolean;
|
||||
showQty?: boolean; // BOM 전용
|
||||
maxLevel?: number;
|
||||
}
|
||||
|
||||
export interface UnifiedHierarchyProps extends UnifiedBaseProps {
|
||||
unifiedType: "UnifiedHierarchy";
|
||||
config: UnifiedHierarchyConfig;
|
||||
data?: HierarchyNode[];
|
||||
selectedNode?: HierarchyNode;
|
||||
onNodeSelect?: (node: HierarchyNode) => void;
|
||||
onNodeMove?: (nodeId: string, newParentId: string) => void;
|
||||
}
|
||||
|
||||
// ===== 통합 Props 유니온 타입 =====
|
||||
|
||||
export type UnifiedComponentProps =
|
||||
| UnifiedInputProps
|
||||
| UnifiedSelectProps
|
||||
| UnifiedDateProps
|
||||
| UnifiedTextProps
|
||||
| UnifiedMediaProps
|
||||
| UnifiedListProps
|
||||
| UnifiedLayoutProps
|
||||
| UnifiedGroupProps
|
||||
| UnifiedBizProps
|
||||
| UnifiedHierarchyProps;
|
||||
|
||||
// ===== 타입 가드 =====
|
||||
|
||||
export function isUnifiedInput(props: UnifiedComponentProps): props is UnifiedInputProps {
|
||||
return props.unifiedType === "UnifiedInput";
|
||||
}
|
||||
|
||||
export function isUnifiedSelect(props: UnifiedComponentProps): props is UnifiedSelectProps {
|
||||
return props.unifiedType === "UnifiedSelect";
|
||||
}
|
||||
|
||||
export function isUnifiedDate(props: UnifiedComponentProps): props is UnifiedDateProps {
|
||||
return props.unifiedType === "UnifiedDate";
|
||||
}
|
||||
|
||||
export function isUnifiedText(props: UnifiedComponentProps): props is UnifiedTextProps {
|
||||
return props.unifiedType === "UnifiedText";
|
||||
}
|
||||
|
||||
export function isUnifiedMedia(props: UnifiedComponentProps): props is UnifiedMediaProps {
|
||||
return props.unifiedType === "UnifiedMedia";
|
||||
}
|
||||
|
||||
export function isUnifiedList(props: UnifiedComponentProps): props is UnifiedListProps {
|
||||
return props.unifiedType === "UnifiedList";
|
||||
}
|
||||
|
||||
export function isUnifiedLayout(props: UnifiedComponentProps): props is UnifiedLayoutProps {
|
||||
return props.unifiedType === "UnifiedLayout";
|
||||
}
|
||||
|
||||
export function isUnifiedGroup(props: UnifiedComponentProps): props is UnifiedGroupProps {
|
||||
return props.unifiedType === "UnifiedGroup";
|
||||
}
|
||||
|
||||
export function isUnifiedBiz(props: UnifiedComponentProps): props is UnifiedBizProps {
|
||||
return props.unifiedType === "UnifiedBiz";
|
||||
}
|
||||
|
||||
export function isUnifiedHierarchy(props: UnifiedComponentProps): props is UnifiedHierarchyProps {
|
||||
return props.unifiedType === "UnifiedHierarchy";
|
||||
}
|
||||
|
||||
// ===== JSON Schema 타입 =====
|
||||
|
||||
export interface JSONSchemaProperty {
|
||||
type: "string" | "number" | "boolean" | "array" | "object";
|
||||
title?: string;
|
||||
description?: string;
|
||||
enum?: string[];
|
||||
default?: unknown;
|
||||
items?: JSONSchemaProperty;
|
||||
properties?: Record<string, JSONSchemaProperty>;
|
||||
required?: string[];
|
||||
}
|
||||
|
||||
export interface UnifiedConfigSchema {
|
||||
type: "object";
|
||||
properties: Record<string, JSONSchemaProperty>;
|
||||
required?: string[];
|
||||
}
|
||||
|
||||
// ===== 레거시 컴포넌트 → Unified 컴포넌트 매핑 =====
|
||||
|
||||
export const LEGACY_TO_UNIFIED_MAP: Record<string, UnifiedComponentType> = {
|
||||
// Input 계열
|
||||
"text-input": "UnifiedInput",
|
||||
"number-input": "UnifiedInput",
|
||||
"password-input": "UnifiedInput",
|
||||
|
||||
// Select 계열
|
||||
"select-basic": "UnifiedSelect",
|
||||
"radio-basic": "UnifiedSelect",
|
||||
"checkbox-basic": "UnifiedSelect",
|
||||
"entity-search-input": "UnifiedSelect",
|
||||
"autocomplete-search-input": "UnifiedSelect",
|
||||
|
||||
// Date 계열
|
||||
"date-input": "UnifiedDate",
|
||||
|
||||
// Text 계열
|
||||
"textarea-basic": "UnifiedText",
|
||||
|
||||
// Media 계열
|
||||
"file-upload": "UnifiedMedia",
|
||||
"image-widget": "UnifiedMedia",
|
||||
|
||||
// List 계열
|
||||
"table-list": "UnifiedList",
|
||||
"table-search-widget": "UnifiedList",
|
||||
"modal-repeater-table": "UnifiedList",
|
||||
"repeater-field-group": "UnifiedList",
|
||||
"card-display": "UnifiedList",
|
||||
|
||||
// Layout 계열
|
||||
"split-panel-layout": "UnifiedLayout",
|
||||
"screen-split-panel": "UnifiedLayout",
|
||||
|
||||
// Group 계열
|
||||
"tabs-widget": "UnifiedGroup",
|
||||
"section-paper": "UnifiedGroup",
|
||||
"section-card": "UnifiedGroup",
|
||||
"universal-form-modal": "UnifiedGroup",
|
||||
|
||||
// Biz 계열
|
||||
"category-manager": "UnifiedBiz",
|
||||
"numbering-rule": "UnifiedBiz",
|
||||
"flow-widget": "UnifiedBiz",
|
||||
|
||||
// Button (Input의 버튼 모드)
|
||||
"button-primary": "UnifiedInput",
|
||||
};
|
||||
@@ -0,0 +1,532 @@
|
||||
/**
|
||||
* V2 컴포넌트 타입 정의
|
||||
*
|
||||
* 10개의 통합 컴포넌트 시스템을 위한 타입 정의
|
||||
* - V2Input
|
||||
* - V2Select
|
||||
* - V2Date
|
||||
* - V2Text
|
||||
* - V2Media
|
||||
* - V2List
|
||||
* - V2Layout
|
||||
* - V2Group
|
||||
* - V2Biz
|
||||
* - V2Hierarchy
|
||||
*/
|
||||
|
||||
import { Position, Size, CommonStyle, ValidationRule } from "./v2-core";
|
||||
|
||||
// ===== 공통 타입 =====
|
||||
|
||||
/**
|
||||
* V2 컴포넌트 타입
|
||||
*/
|
||||
export type V2ComponentType =
|
||||
| "V2Input"
|
||||
| "V2Select"
|
||||
| "V2Date"
|
||||
| "V2Text"
|
||||
| "V2Media"
|
||||
| "V2List"
|
||||
| "V2Layout"
|
||||
| "V2Group"
|
||||
| "V2Biz"
|
||||
| "V2Hierarchy";
|
||||
|
||||
/**
|
||||
* 조건부 렌더링 설정
|
||||
*/
|
||||
export interface ConditionalConfig {
|
||||
enabled: boolean;
|
||||
field: string; // 참조 필드
|
||||
operator: "=" | "!=" | ">" | "<" | "in" | "notIn" | "isEmpty" | "isNotEmpty";
|
||||
value: unknown;
|
||||
action: "show" | "hide" | "disable" | "enable";
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 입력 설정
|
||||
*/
|
||||
export interface AutoFillConfig {
|
||||
enabled: boolean;
|
||||
sourceTable: string;
|
||||
filterColumn: string;
|
||||
userField: "companyCode" | "userId" | "deptCode";
|
||||
displayColumn: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 연쇄 관계 설정
|
||||
*/
|
||||
export interface CascadingConfig {
|
||||
parentField: string;
|
||||
filterColumn: string;
|
||||
clearOnChange?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상호 배제 설정
|
||||
*/
|
||||
export interface MutualExclusionConfig {
|
||||
enabled: boolean;
|
||||
targetField: string;
|
||||
type: "exclusive" | "inclusive";
|
||||
}
|
||||
|
||||
/**
|
||||
* 공통 V2 컴포넌트 속성
|
||||
*/
|
||||
export interface V2BaseProps {
|
||||
id: string;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
readonly?: boolean;
|
||||
disabled?: boolean;
|
||||
// 데이터 바인딩
|
||||
tableName?: string;
|
||||
columnName?: string;
|
||||
// 위치 및 크기
|
||||
position?: Position;
|
||||
size?: Size;
|
||||
// 스타일
|
||||
style?: CommonStyle;
|
||||
// 조건부 및 자동화
|
||||
conditional?: ConditionalConfig;
|
||||
autoFill?: AutoFillConfig;
|
||||
// 유효성 검사
|
||||
validation?: ValidationRule[];
|
||||
}
|
||||
|
||||
// ===== V2Input =====
|
||||
|
||||
export type V2InputType = "text" | "number" | "password" | "slider" | "color" | "button";
|
||||
export type V2InputFormat = "none" | "email" | "tel" | "url" | "currency" | "biz_no";
|
||||
|
||||
export interface V2InputConfig {
|
||||
type: V2InputType;
|
||||
inputType?: V2InputType; // type 별칭
|
||||
format?: V2InputFormat;
|
||||
mask?: string;
|
||||
placeholder?: string;
|
||||
// 숫자 전용
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
// 버튼 전용
|
||||
buttonText?: string;
|
||||
buttonVariant?: "default" | "destructive" | "outline" | "secondary" | "ghost";
|
||||
onClick?: () => void;
|
||||
// 테이블명 (채번용)
|
||||
tableName?: string;
|
||||
}
|
||||
|
||||
export interface V2InputProps extends V2BaseProps {
|
||||
v2Type: "V2Input";
|
||||
config: V2InputConfig;
|
||||
value?: string | number;
|
||||
onChange?: (value: string | number) => void;
|
||||
}
|
||||
|
||||
// ===== V2Select =====
|
||||
|
||||
export type V2SelectMode = "dropdown" | "radio" | "check" | "tag" | "toggle" | "swap";
|
||||
export type V2SelectSource = "static" | "code" | "db" | "api" | "entity" | "category";
|
||||
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface V2SelectConfig {
|
||||
mode: V2SelectMode;
|
||||
source: V2SelectSource | "distinct" | "select"; // distinct/select 추가 (테이블 컬럼에서 자동 로드)
|
||||
// 정적 옵션 (source: static)
|
||||
options?: SelectOption[];
|
||||
// 코드 그룹 (source: code)
|
||||
codeGroup?: string;
|
||||
codeCategory?: string; // codeGroup 별칭
|
||||
// DB 연결 (source: db)
|
||||
table?: string;
|
||||
valueColumn?: string;
|
||||
labelColumn?: string;
|
||||
filters?: Array<{ column: string; operator: string; value: unknown }>;
|
||||
// 엔티티 연결 (source: entity)
|
||||
entityTable?: string;
|
||||
entityValueField?: string;
|
||||
entityLabelField?: string;
|
||||
entityValueColumn?: string; // alias for entityValueField
|
||||
entityLabelColumn?: string; // alias for entityLabelField
|
||||
// API 연결 (source: api)
|
||||
apiEndpoint?: string;
|
||||
// 카테고리 연결 (source: category) - 레거시, code로 자동 변환됨
|
||||
categoryTable?: string;
|
||||
categoryColumn?: string;
|
||||
// 공통 옵션
|
||||
searchable?: boolean;
|
||||
multiple?: boolean;
|
||||
maxSelect?: number;
|
||||
allowClear?: boolean;
|
||||
// 연쇄 관계
|
||||
cascading?: CascadingConfig;
|
||||
// 상호 배제
|
||||
mutualExclusion?: MutualExclusionConfig;
|
||||
// 계층 코드 연쇄 선택 (source: code일 때 계층 구조 사용)
|
||||
hierarchical?: boolean; // 계층 구조 사용 여부
|
||||
parentField?: string; // 부모 값을 참조할 필드 (다른 컴포넌트의 columnName)
|
||||
}
|
||||
|
||||
export interface V2SelectProps extends V2BaseProps {
|
||||
v2Type: "V2Select";
|
||||
config: V2SelectConfig;
|
||||
value?: string | string[];
|
||||
onChange?: (value: string | string[]) => void;
|
||||
}
|
||||
|
||||
// ===== V2Date =====
|
||||
|
||||
export type V2DateType = "date" | "time" | "datetime";
|
||||
|
||||
export interface V2DateConfig {
|
||||
type: V2DateType;
|
||||
format?: string;
|
||||
range?: boolean;
|
||||
minDate?: string;
|
||||
maxDate?: string;
|
||||
showToday?: boolean;
|
||||
}
|
||||
|
||||
export interface V2DateProps extends V2BaseProps {
|
||||
v2Type: "V2Date";
|
||||
config: V2DateConfig;
|
||||
value?: string | [string, string]; // 범위 선택 시 튜플
|
||||
onChange?: (value: string | [string, string]) => void;
|
||||
}
|
||||
|
||||
// ===== V2Text =====
|
||||
|
||||
export type V2TextMode = "simple" | "rich" | "code" | "markdown";
|
||||
|
||||
export interface V2TextConfig {
|
||||
mode: V2TextMode;
|
||||
rows?: number;
|
||||
maxLength?: number;
|
||||
placeholder?: string;
|
||||
resize?: "none" | "vertical" | "horizontal" | "both";
|
||||
}
|
||||
|
||||
export interface V2TextProps extends V2BaseProps {
|
||||
v2Type: "V2Text";
|
||||
config: V2TextConfig;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
// ===== V2Media =====
|
||||
|
||||
export type V2MediaType = "file" | "image" | "video" | "audio";
|
||||
|
||||
export interface V2MediaConfig {
|
||||
type: V2MediaType;
|
||||
multiple?: boolean;
|
||||
accept?: string;
|
||||
maxSize?: number;
|
||||
preview?: boolean;
|
||||
uploadEndpoint?: string;
|
||||
}
|
||||
|
||||
export interface V2MediaProps extends V2BaseProps {
|
||||
v2Type: "V2Media";
|
||||
config: V2MediaConfig;
|
||||
value?: string | string[]; // 파일 URL 또는 배열
|
||||
onChange?: (value: string | string[]) => void;
|
||||
}
|
||||
|
||||
// ===== V2List =====
|
||||
|
||||
export type V2ListViewMode = "table" | "card" | "kanban" | "list";
|
||||
|
||||
export interface ListColumn {
|
||||
field: string;
|
||||
header: string;
|
||||
width?: number;
|
||||
sortable?: boolean;
|
||||
filterable?: boolean;
|
||||
editable?: boolean;
|
||||
format?: string;
|
||||
}
|
||||
|
||||
export interface V2ListCardConfig {
|
||||
titleColumn?: string;
|
||||
subtitleColumn?: string;
|
||||
descriptionColumn?: string;
|
||||
imageColumn?: string;
|
||||
cardsPerRow?: number;
|
||||
cardSpacing?: number;
|
||||
showActions?: boolean;
|
||||
}
|
||||
|
||||
export interface V2ListConfig {
|
||||
viewMode: V2ListViewMode;
|
||||
editable?: boolean;
|
||||
searchable?: boolean;
|
||||
pageable?: boolean;
|
||||
pageSize?: number;
|
||||
sortable?: boolean;
|
||||
pagination?: boolean;
|
||||
source?: "static" | "db" | "api"; // 데이터 소스 타입
|
||||
columns?: ListColumn[];
|
||||
modal?: boolean;
|
||||
cardConfig?: V2ListCardConfig;
|
||||
// 데이터 소스
|
||||
dataSource?: {
|
||||
table?: string;
|
||||
api?: string;
|
||||
filters?: Array<{ column: string; operator: string; value: unknown }>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2ListProps extends V2BaseProps {
|
||||
v2Type: "V2List";
|
||||
config: V2ListConfig;
|
||||
data?: Record<string, unknown>[];
|
||||
selectedRows?: Record<string, unknown>[];
|
||||
onRowSelect?: (rows: Record<string, unknown>[]) => void;
|
||||
onRowClick?: (row: Record<string, unknown>) => void;
|
||||
}
|
||||
|
||||
// ===== V2Layout =====
|
||||
|
||||
export type V2LayoutType = "grid" | "split" | "flex" | "divider" | "screen-embed";
|
||||
|
||||
export interface V2LayoutConfig {
|
||||
type: V2LayoutType;
|
||||
columns?: number; // 12컬럼 시스템에서 실제 표시할 컬럼 수 (1-12)
|
||||
gap?: string;
|
||||
splitRatio?: number[];
|
||||
direction?: "horizontal" | "vertical";
|
||||
use12Column?: boolean; // 12컬럼 시스템 사용 여부 (기본 true)
|
||||
// screen-embed 전용
|
||||
screenId?: number;
|
||||
}
|
||||
|
||||
export interface V2LayoutProps extends V2BaseProps {
|
||||
v2Type: "V2Layout";
|
||||
config: V2LayoutConfig;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
// ===== V2Group =====
|
||||
|
||||
export type V2GroupType = "tabs" | "accordion" | "section" | "card-section" | "modal" | "form-modal";
|
||||
|
||||
export interface TabItem {
|
||||
id: string;
|
||||
title: string;
|
||||
content?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface V2GroupConfig {
|
||||
type: V2GroupType;
|
||||
title?: string;
|
||||
collapsible?: boolean;
|
||||
defaultExpanded?: boolean;
|
||||
defaultOpen?: boolean; // defaultExpanded 별칭
|
||||
// 탭 전용
|
||||
tabs?: TabItem[];
|
||||
activeTab?: string;
|
||||
// 모달 전용
|
||||
modalSize?: "sm" | "md" | "lg" | "xl";
|
||||
}
|
||||
|
||||
export interface V2GroupProps extends V2BaseProps {
|
||||
v2Type: "V2Group";
|
||||
config: V2GroupConfig;
|
||||
children?: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
}
|
||||
|
||||
// ===== V2Biz =====
|
||||
|
||||
export type V2BizType = "flow" | "rack" | "map" | "numbering" | "category" | "mapping" | "related-buttons";
|
||||
|
||||
export interface V2BizConfig {
|
||||
type: V2BizType;
|
||||
// 각 타입별 설정은 제네릭하게 처리
|
||||
config?: Record<string, unknown>;
|
||||
// 플로우 전용
|
||||
flowConfig?: {
|
||||
flowId?: number;
|
||||
showProgress?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2BizProps extends V2BaseProps {
|
||||
v2Type: "V2Biz";
|
||||
config: V2BizConfig;
|
||||
}
|
||||
|
||||
// ===== V2Hierarchy =====
|
||||
|
||||
export type V2HierarchyType = "tree" | "org" | "bom" | "cascading";
|
||||
export type V2HierarchyViewMode = "tree" | "table" | "indent" | "dropdown";
|
||||
|
||||
export interface HierarchyNode {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
label: string;
|
||||
children?: HierarchyNode[];
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface V2HierarchyConfig {
|
||||
type: V2HierarchyType;
|
||||
viewMode: V2HierarchyViewMode;
|
||||
source?: string; // 계층 그룹 코드
|
||||
editable?: boolean;
|
||||
draggable?: boolean;
|
||||
showQty?: boolean; // BOM 전용
|
||||
maxLevel?: number;
|
||||
// 데이터 소스
|
||||
dataSource?: {
|
||||
table?: string;
|
||||
idColumn?: string;
|
||||
parentColumn?: string;
|
||||
labelColumn?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2HierarchyProps extends V2BaseProps {
|
||||
v2Type: "V2Hierarchy";
|
||||
config: V2HierarchyConfig;
|
||||
data?: HierarchyNode[];
|
||||
selectedNode?: HierarchyNode;
|
||||
onNodeSelect?: (node: HierarchyNode) => void;
|
||||
onNodeMove?: (nodeId: string, newParentId: string) => void;
|
||||
}
|
||||
|
||||
// ===== 통합 Props 유니온 타입 =====
|
||||
|
||||
export type V2ComponentProps =
|
||||
| V2InputProps
|
||||
| V2SelectProps
|
||||
| V2DateProps
|
||||
| V2TextProps
|
||||
| V2MediaProps
|
||||
| V2ListProps
|
||||
| V2LayoutProps
|
||||
| V2GroupProps
|
||||
| V2BizProps
|
||||
| V2HierarchyProps;
|
||||
|
||||
// ===== 타입 가드 =====
|
||||
|
||||
export function isV2Input(props: V2ComponentProps): props is V2InputProps {
|
||||
return props.v2Type === "V2Input";
|
||||
}
|
||||
|
||||
export function isV2Select(props: V2ComponentProps): props is V2SelectProps {
|
||||
return props.v2Type === "V2Select";
|
||||
}
|
||||
|
||||
export function isV2Date(props: V2ComponentProps): props is V2DateProps {
|
||||
return props.v2Type === "V2Date";
|
||||
}
|
||||
|
||||
export function isV2Text(props: V2ComponentProps): props is V2TextProps {
|
||||
return props.v2Type === "V2Text";
|
||||
}
|
||||
|
||||
export function isV2Media(props: V2ComponentProps): props is V2MediaProps {
|
||||
return props.v2Type === "V2Media";
|
||||
}
|
||||
|
||||
export function isV2List(props: V2ComponentProps): props is V2ListProps {
|
||||
return props.v2Type === "V2List";
|
||||
}
|
||||
|
||||
export function isV2Layout(props: V2ComponentProps): props is V2LayoutProps {
|
||||
return props.v2Type === "V2Layout";
|
||||
}
|
||||
|
||||
export function isV2Group(props: V2ComponentProps): props is V2GroupProps {
|
||||
return props.v2Type === "V2Group";
|
||||
}
|
||||
|
||||
export function isV2Biz(props: V2ComponentProps): props is V2BizProps {
|
||||
return props.v2Type === "V2Biz";
|
||||
}
|
||||
|
||||
export function isV2Hierarchy(props: V2ComponentProps): props is V2HierarchyProps {
|
||||
return props.v2Type === "V2Hierarchy";
|
||||
}
|
||||
|
||||
// ===== JSON Schema 타입 =====
|
||||
|
||||
export interface JSONSchemaProperty {
|
||||
type: "string" | "number" | "boolean" | "array" | "object";
|
||||
title?: string;
|
||||
description?: string;
|
||||
enum?: string[];
|
||||
default?: unknown;
|
||||
items?: JSONSchemaProperty;
|
||||
properties?: Record<string, JSONSchemaProperty>;
|
||||
required?: string[];
|
||||
}
|
||||
|
||||
export interface V2ConfigSchema {
|
||||
type: "object";
|
||||
properties: Record<string, JSONSchemaProperty>;
|
||||
required?: string[];
|
||||
}
|
||||
|
||||
// ===== 레거시 컴포넌트 → V2 컴포넌트 매핑 =====
|
||||
|
||||
export const LEGACY_TO_V2_MAP: Record<string, V2ComponentType> = {
|
||||
// Input 계열
|
||||
"text-input": "V2Input",
|
||||
"number-input": "V2Input",
|
||||
"password-input": "V2Input",
|
||||
|
||||
// Select 계열
|
||||
"select-basic": "V2Select",
|
||||
"radio-basic": "V2Select",
|
||||
"checkbox-basic": "V2Select",
|
||||
"entity-search-input": "V2Select",
|
||||
"autocomplete-search-input": "V2Select",
|
||||
|
||||
// Date 계열
|
||||
"date-input": "V2Date",
|
||||
|
||||
// Text 계열
|
||||
"textarea-basic": "V2Text",
|
||||
|
||||
// Media 계열
|
||||
"file-upload": "V2Media",
|
||||
"image-widget": "V2Media",
|
||||
|
||||
// List 계열
|
||||
"table-list": "V2List",
|
||||
"table-search-widget": "V2List",
|
||||
"modal-repeater-table": "V2List",
|
||||
"repeater-field-group": "V2List",
|
||||
"card-display": "V2List",
|
||||
|
||||
// Layout 계열
|
||||
"split-panel-layout": "V2Layout",
|
||||
"screen-split-panel": "V2Layout",
|
||||
|
||||
// Group 계열
|
||||
"tabs-widget": "V2Group",
|
||||
"section-paper": "V2Group",
|
||||
"section-card": "V2Group",
|
||||
"universal-form-modal": "V2Group",
|
||||
|
||||
// Biz 계열
|
||||
"category-manager": "V2Biz",
|
||||
"numbering-rule": "V2Biz",
|
||||
"flow-widget": "V2Biz",
|
||||
|
||||
// Button (Input의 버튼 모드)
|
||||
"button-primary": "V2Input",
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* 통합 폼 시스템 타입 정의
|
||||
*
|
||||
* Unified 컴포넌트들과 레거시 컴포넌트들이 공유하는 폼 관련 타입
|
||||
* V2 컴포넌트들과 레거시 컴포넌트들이 공유하는 폼 관련 타입
|
||||
*/
|
||||
|
||||
import { ValidationRule } from "./unified-core";
|
||||
import { ValidationRule } from "./v2-core";
|
||||
|
||||
// ===== 폼 상태 타입 =====
|
||||
|
||||
@@ -201,10 +201,10 @@ export interface ScreenDataTransferConfig {
|
||||
// ===== Context 타입 =====
|
||||
|
||||
/**
|
||||
* 확장된 UnifiedFormContext 값
|
||||
* 확장된 V2FormContext 값
|
||||
*/
|
||||
export interface ExtendedFormContextValue {
|
||||
// === 기존 UnifiedFormContext 기능 ===
|
||||
// === 기존 V2FormContext 기능 ===
|
||||
formData: Record<string, unknown>;
|
||||
fieldStates: Record<string, FieldState>;
|
||||
|
||||
@@ -301,7 +301,7 @@ export interface FormCompatibilityBridge {
|
||||
errors: FieldError[];
|
||||
|
||||
// 모드
|
||||
isUnifiedMode: boolean; // UnifiedFormContext 사용 여부
|
||||
isV2Mode: boolean; // V2FormContext 사용 여부
|
||||
isLegacyMode: boolean; // 레거시 모드 여부
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* UnifiedRepeater 컴포넌트 타입 정의
|
||||
*
|
||||
* V2Repeater 컴포넌트 타입 정의
|
||||
*
|
||||
* 렌더링 모드:
|
||||
* - inline: 현재 테이블 컬럼 직접 입력 (simple-repeater-table)
|
||||
* - modal: 소스 테이블에서 검색/선택 후 복사 (modal-repeater-table)
|
||||
@@ -17,24 +17,24 @@ export type ModalSize = "sm" | "md" | "lg" | "xl" | "full";
|
||||
export type ColumnWidthOption = "auto" | "60px" | "80px" | "100px" | "120px" | "150px" | "200px" | "250px" | "300px";
|
||||
|
||||
// 자동 입력 타입
|
||||
export type AutoFillType =
|
||||
| "none" // 자동 입력 없음
|
||||
| "currentDate" // 현재 날짜
|
||||
| "currentDateTime"// 현재 날짜+시간
|
||||
| "sequence" // 순번 (1, 2, 3...)
|
||||
| "numbering" // 채번 규칙 (관리자가 등록한 규칙 선택)
|
||||
| "fromMainForm" // 메인 폼에서 값 복사
|
||||
| "fixed"; // 고정값
|
||||
export type AutoFillType =
|
||||
| "none" // 자동 입력 없음
|
||||
| "currentDate" // 현재 날짜
|
||||
| "currentDateTime" // 현재 날짜+시간
|
||||
| "sequence" // 순번 (1, 2, 3...)
|
||||
| "numbering" // 채번 규칙 (관리자가 등록한 규칙 선택)
|
||||
| "fromMainForm" // 메인 폼에서 값 복사
|
||||
| "fixed"; // 고정값
|
||||
|
||||
// 자동 입력 설정
|
||||
export interface AutoFillConfig {
|
||||
type: AutoFillType;
|
||||
// fromMainForm 타입용
|
||||
sourceField?: string; // 메인 폼에서 복사할 필드명
|
||||
sourceField?: string; // 메인 폼에서 복사할 필드명
|
||||
// fixed 타입용
|
||||
fixedValue?: string | number | boolean;
|
||||
// numbering 타입용 - 기존 채번 규칙 ID를 참조
|
||||
numberingRuleId?: string; // 채번 규칙 ID (numbering_rules 테이블)
|
||||
numberingRuleId?: string; // 채번 규칙 ID (numbering_rules 테이블)
|
||||
selectedMenuObjid?: number; // 🆕 채번 규칙 선택을 위한 대상 메뉴 OBJID
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export interface RepeaterColumnConfig {
|
||||
width: ColumnWidthOption;
|
||||
visible: boolean;
|
||||
editable?: boolean; // 편집 가능 여부 (inline 모드)
|
||||
hidden?: boolean; // 🆕 히든 처리 (화면에 안 보이지만 저장됨)
|
||||
hidden?: boolean; // 🆕 히든 처리 (화면에 안 보이지만 저장됨)
|
||||
isJoinColumn?: boolean;
|
||||
sourceTable?: string;
|
||||
// 🆕 소스 테이블 표시 컬럼 여부 (modal 모드에서 읽기 전용으로 표시)
|
||||
@@ -77,11 +77,11 @@ export interface RepeaterModalConfig {
|
||||
size: ModalSize;
|
||||
title?: string; // 모달 제목
|
||||
buttonText?: string; // 검색 버튼 텍스트
|
||||
|
||||
|
||||
// 소스 테이블 표시 설정 (modal 모드)
|
||||
sourceDisplayColumns?: ModalDisplayColumn[]; // 모달에 표시할 소스 테이블 컬럼 (라벨 포함)
|
||||
searchFields?: string[]; // 검색에 사용할 필드
|
||||
|
||||
|
||||
// 화면 기반 모달 (옵션)
|
||||
screenId?: number;
|
||||
titleTemplate?: {
|
||||
@@ -105,13 +105,14 @@ export interface RepeaterFeatureOptions {
|
||||
// 데이터 소스 설정
|
||||
export interface RepeaterDataSource {
|
||||
// inline 모드: 현재 테이블 설정은 필요 없음 (컬럼만 선택)
|
||||
|
||||
tableName?: string; // 데이터 테이블명 (레거시 호환)
|
||||
|
||||
// modal 모드: 소스 테이블 설정
|
||||
sourceTable?: string; // 검색할 테이블 (엔티티 참조 테이블)
|
||||
foreignKey?: string; // 현재 테이블의 FK 컬럼 (part_objid 등)
|
||||
referenceKey?: string; // 소스 테이블의 PK 컬럼 (id 등)
|
||||
displayColumn?: string; // 표시할 컬럼 (item_name 등)
|
||||
|
||||
|
||||
// 추가 필터
|
||||
filter?: {
|
||||
column: string;
|
||||
@@ -135,7 +136,7 @@ export interface CalculationRule {
|
||||
}
|
||||
|
||||
// 메인 설정 타입
|
||||
export interface UnifiedRepeaterConfig {
|
||||
export interface V2RepeaterConfig {
|
||||
// 렌더링 모드
|
||||
renderMode: RepeaterRenderMode;
|
||||
|
||||
@@ -173,8 +174,8 @@ export interface UnifiedRepeaterConfig {
|
||||
}
|
||||
|
||||
// 컴포넌트 Props
|
||||
export interface UnifiedRepeaterProps {
|
||||
config: UnifiedRepeaterConfig;
|
||||
export interface V2RepeaterProps {
|
||||
config: V2RepeaterConfig;
|
||||
parentId?: string | number; // 부모 레코드 ID
|
||||
data?: any[]; // 초기 데이터 (없으면 API로 로드)
|
||||
onDataChange?: (data: any[]) => void;
|
||||
@@ -183,7 +184,7 @@ export interface UnifiedRepeaterProps {
|
||||
}
|
||||
|
||||
// 기본 설정값
|
||||
export const DEFAULT_REPEATER_CONFIG: UnifiedRepeaterConfig = {
|
||||
export const DEFAULT_REPEATER_CONFIG: V2RepeaterConfig = {
|
||||
renderMode: "inline",
|
||||
dataSource: {},
|
||||
columns: [],
|
||||
Reference in New Issue
Block a user