docs: 다양한 문서 및 가이드 업데이트

- 여러 문서의 내용을 업데이트하여 최신 정보를 반영하였습니다.
- 컴포넌트 개발 가이드와 관련된 문서의 목차를 재구성하고, V2 및 Zod 레이아웃 시스템에 대한 내용을 추가하였습니다.
- 화면 컴포넌트 개발 가이드를 개선하여 핵심 원칙과 패턴을 명확히 설명하였습니다.
- 불필요한 문서 및 가이드를 삭제하고, 통합된 가이드를 통해 개발자들이 쉽게 참고할 수 있도록 하였습니다.
This commit is contained in:
kjs
2026-01-28 17:36:19 +09:00
parent e0ee375f01
commit 95bef976a5
276 changed files with 2544 additions and 2495 deletions
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -11,7 +11,7 @@ import {
ActiveStatus,
TimestampFields,
BaseApiResponse,
} from "./unified-core";
} from "./v2-core";
// ===== 버튼 제어 관련 =====
+10 -10
View File
@@ -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;
+1 -1
View File
@@ -5,7 +5,7 @@
* 화면 관리에서 선택 가능한 세부 타입들을 정의합니다.
*/
import { WebType } from "./unified-core";
import { WebType } from "./v2-core";
/**
* 핵심 입력 타입
+1 -1
View File
@@ -16,7 +16,7 @@ import {
CompanyCode,
ActiveStatus,
isWebType,
} from "./unified-core";
} from "./v2-core";
import { ColumnSpanPreset } from "@/lib/constants/columnSpans";
import { ResponsiveComponentConfig } from "./responsive";
+3 -3
View File
@@ -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,
+30 -30
View File
@@ -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;
};
-512
View File
@@ -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",
};
+532
View File
@@ -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,5 +1,5 @@
/**
* UnifiedRepeater
* V2Repeater
*
* :
* - inline: 현재 (simple-repeater-table)
@@ -105,6 +105,7 @@ export interface RepeaterFeatureOptions {
// 데이터 소스 설정
export interface RepeaterDataSource {
// inline 모드: 현재 테이블 설정은 필요 없음 (컬럼만 선택)
tableName?: string; // 데이터 테이블명 (레거시 호환)
// modal 모드: 소스 테이블 설정
sourceTable?: 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: [],