타입 관리 개선 및 화면 비율조정 중간커밋
This commit is contained in:
514
frontend/types/control-management.ts
Normal file
514
frontend/types/control-management.ts
Normal file
@@ -0,0 +1,514 @@
|
||||
/**
|
||||
* 🎮 제어관리 시스템 전용 타입 정의
|
||||
*
|
||||
* 버튼 액션, 데이터플로우, 조건부 실행, 트랜잭션 처리 등 제어관리에서만 사용하는 타입들
|
||||
*/
|
||||
|
||||
import {
|
||||
ButtonActionType,
|
||||
ConditionOperator,
|
||||
CompanyCode,
|
||||
ActiveStatus,
|
||||
TimestampFields,
|
||||
BaseApiResponse,
|
||||
} from "./unified-core";
|
||||
|
||||
// ===== 버튼 제어 관련 =====
|
||||
|
||||
/**
|
||||
* 확장된 버튼 설정 (화면관리의 ButtonTypeConfig 확장)
|
||||
*/
|
||||
export interface ExtendedButtonTypeConfig {
|
||||
// 기본 버튼 설정
|
||||
actionType: ButtonActionType;
|
||||
text?: string;
|
||||
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
|
||||
size?: "sm" | "md" | "lg";
|
||||
icon?: string;
|
||||
|
||||
// 확인 및 검증
|
||||
confirmMessage?: string;
|
||||
requiresConfirmation?: boolean;
|
||||
|
||||
// 모달 관련 설정
|
||||
popupTitle?: string;
|
||||
popupContent?: string;
|
||||
popupScreenId?: number;
|
||||
|
||||
// 네비게이션 관련 설정
|
||||
navigateType?: "url" | "screen";
|
||||
navigateUrl?: string;
|
||||
navigateScreenId?: number;
|
||||
navigateTarget?: "_self" | "_blank";
|
||||
|
||||
// 커스텀 액션 설정
|
||||
customAction?: string;
|
||||
|
||||
// 🎯 제어관리 기능
|
||||
enableDataflowControl?: boolean;
|
||||
dataflowConfig?: ButtonDataflowConfig;
|
||||
dataflowTiming?: "before" | "after" | "replace";
|
||||
|
||||
// 스타일 설정
|
||||
backgroundColor?: string;
|
||||
textColor?: string;
|
||||
borderColor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 버튼 데이터플로우 설정
|
||||
*/
|
||||
export interface ButtonDataflowConfig {
|
||||
// 제어 방식 선택
|
||||
controlMode: "simple" | "advanced";
|
||||
|
||||
// 관계도 방식 (diagram 기반)
|
||||
selectedDiagramId?: number;
|
||||
selectedRelationshipId?: number;
|
||||
|
||||
// 직접 설정 방식
|
||||
directControl?: DirectControlConfig;
|
||||
|
||||
// 제어 데이터 소스
|
||||
controlDataSource?: ControlDataSource;
|
||||
|
||||
// 실행 옵션
|
||||
executionOptions?: ExecutionOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 제어 데이터 소스 타입
|
||||
*/
|
||||
export type ControlDataSource = "form" | "table-selection" | "both";
|
||||
|
||||
/**
|
||||
* 직접 제어 설정
|
||||
*/
|
||||
export interface DirectControlConfig {
|
||||
conditions: DataflowCondition[];
|
||||
actions: DataflowAction[];
|
||||
logic?: "AND" | "OR" | "CUSTOM";
|
||||
customLogic?: string; // "(A AND B) OR (C AND D)"
|
||||
}
|
||||
|
||||
/**
|
||||
* 실행 옵션
|
||||
*/
|
||||
export interface ExecutionOptions {
|
||||
timeout?: number; // ms
|
||||
retryCount?: number;
|
||||
parallelExecution?: boolean;
|
||||
continueOnError?: boolean;
|
||||
}
|
||||
|
||||
// ===== 데이터플로우 조건 및 액션 =====
|
||||
|
||||
/**
|
||||
* 데이터플로우 조건
|
||||
*/
|
||||
export interface DataflowCondition {
|
||||
id: string;
|
||||
type: "condition" | "group";
|
||||
|
||||
// 단일 조건
|
||||
field?: string;
|
||||
operator?: ConditionOperator;
|
||||
value?: unknown;
|
||||
dataSource?: ControlDataSource;
|
||||
|
||||
// 그룹 조건
|
||||
conditions?: DataflowCondition[];
|
||||
logic?: "AND" | "OR";
|
||||
|
||||
// 메타데이터
|
||||
name?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터플로우 액션
|
||||
*/
|
||||
export interface DataflowAction {
|
||||
id: string;
|
||||
name: string;
|
||||
type: ActionType;
|
||||
|
||||
// 데이터베이스 액션
|
||||
tableName?: string;
|
||||
operation?: DatabaseOperation;
|
||||
fields?: ActionField[];
|
||||
conditions?: DataflowCondition[];
|
||||
|
||||
// API 액션
|
||||
endpoint?: string;
|
||||
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
||||
headers?: Record<string, string>;
|
||||
body?: unknown;
|
||||
|
||||
// 알림 액션
|
||||
notificationType?: NotificationType;
|
||||
message?: string;
|
||||
recipients?: string[];
|
||||
|
||||
// 리다이렉트 액션
|
||||
redirectUrl?: string;
|
||||
redirectTarget?: "_self" | "_blank";
|
||||
|
||||
// 실행 옵션
|
||||
timeout?: number;
|
||||
retryCount?: number;
|
||||
rollbackable?: boolean;
|
||||
|
||||
// 메타데이터
|
||||
description?: string;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 액션 타입
|
||||
*/
|
||||
export type ActionType = "database" | "api" | "notification" | "redirect" | "custom";
|
||||
|
||||
/**
|
||||
* 데이터베이스 작업 타입
|
||||
*/
|
||||
export type DatabaseOperation = "INSERT" | "UPDATE" | "DELETE" | "SELECT";
|
||||
|
||||
/**
|
||||
* 액션 필드
|
||||
*/
|
||||
export interface ActionField {
|
||||
name: string;
|
||||
value: unknown;
|
||||
type?: "static" | "dynamic" | "computed";
|
||||
source?: string; // 동적 값의 소스 (form field, selected row 등)
|
||||
}
|
||||
|
||||
/**
|
||||
* 알림 타입
|
||||
*/
|
||||
export type NotificationType = "success" | "error" | "warning" | "info" | "toast" | "modal" | "email";
|
||||
|
||||
// ===== 트랜잭션 관리 =====
|
||||
|
||||
/**
|
||||
* 트랜잭션 그룹
|
||||
*/
|
||||
export interface TransactionGroup {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
actions: DataflowAction[];
|
||||
rollbackStrategy: RollbackStrategy;
|
||||
executionMode: "sequential" | "parallel";
|
||||
onFailure: FailureHandling;
|
||||
}
|
||||
|
||||
/**
|
||||
* 롤백 전략
|
||||
*/
|
||||
export type RollbackStrategy =
|
||||
| "none" // 롤백 안함
|
||||
| "partial" // 실패한 액션만 롤백
|
||||
| "complete"; // 전체 트랜잭션 롤백
|
||||
|
||||
/**
|
||||
* 실패 처리 방식
|
||||
*/
|
||||
export type FailureHandling =
|
||||
| "stop" // 실패 시 중단
|
||||
| "continue" // 실패해도 계속 진행
|
||||
| "alternative"; // 대안 액션 실행
|
||||
|
||||
/**
|
||||
* 조건부 실행 계획
|
||||
*/
|
||||
export interface ConditionalExecutionPlan {
|
||||
id: string;
|
||||
name: string;
|
||||
conditions: ExecutionCondition[];
|
||||
logic: "AND" | "OR" | "CUSTOM";
|
||||
customLogic?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 실행 조건
|
||||
*/
|
||||
export interface ExecutionCondition {
|
||||
id: string;
|
||||
type: "action_group" | "validation" | "data_check";
|
||||
|
||||
// 액션 그룹 조건
|
||||
actionGroup?: TransactionGroup;
|
||||
|
||||
// 검증 조건
|
||||
validation?: {
|
||||
field: string;
|
||||
operator: ConditionOperator;
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
// 성공/실패 조건
|
||||
expectedResult: "success" | "failure" | "any";
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건부 액션 그룹
|
||||
*/
|
||||
export interface ConditionalActionGroup {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
|
||||
// 실행 조건
|
||||
executionCondition: {
|
||||
type: "always" | "conditional" | "fallback";
|
||||
conditions?: DataflowCondition[];
|
||||
logic?: "AND" | "OR";
|
||||
};
|
||||
|
||||
// 액션들
|
||||
actions: DataflowAction[];
|
||||
|
||||
// 성공/실패 조건 정의
|
||||
successCriteria: {
|
||||
type: "all_success" | "any_success" | "custom";
|
||||
customLogic?: string;
|
||||
};
|
||||
|
||||
// 다음 단계 정의
|
||||
onSuccess?: {
|
||||
nextGroup?: string;
|
||||
completeTransaction?: boolean;
|
||||
};
|
||||
|
||||
onFailure?: {
|
||||
retryCount?: number;
|
||||
fallbackGroup?: string;
|
||||
rollbackStrategy?: RollbackStrategy;
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 실행 결과 및 상태 =====
|
||||
|
||||
/**
|
||||
* 액션 실행 결과
|
||||
*/
|
||||
export interface ActionExecutionResult {
|
||||
actionId: string;
|
||||
transactionId?: string;
|
||||
status: "pending" | "running" | "success" | "failed" | "rolled_back";
|
||||
startTime: Date;
|
||||
endTime?: Date;
|
||||
result?: unknown;
|
||||
error?: {
|
||||
code: string;
|
||||
message: string;
|
||||
details?: unknown;
|
||||
};
|
||||
rollbackData?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 트랜잭션 실행 상태
|
||||
*/
|
||||
export interface TransactionExecutionState {
|
||||
transactionId: string;
|
||||
status: "pending" | "running" | "success" | "failed" | "rolling_back" | "rolled_back";
|
||||
actions: ActionExecutionResult[];
|
||||
rollbackActions?: ActionExecutionResult[];
|
||||
startTime: Date;
|
||||
endTime?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 트랜잭션 실행 결과
|
||||
*/
|
||||
export interface TransactionExecutionResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
requiresRollback: boolean;
|
||||
results: [string, boolean][];
|
||||
transactionId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터플로우 실행 결과
|
||||
*/
|
||||
export interface DataflowExecutionResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data?: unknown;
|
||||
executedActions?: ActionExecutionResult[];
|
||||
failedActions?: ActionExecutionResult[];
|
||||
totalActions?: number;
|
||||
executionTime?: number;
|
||||
}
|
||||
|
||||
// ===== 제어 컨텍스트 =====
|
||||
|
||||
/**
|
||||
* 확장된 제어 컨텍스트
|
||||
*/
|
||||
export interface ExtendedControlContext {
|
||||
// 기존 폼 데이터
|
||||
formData: Record<string, unknown>;
|
||||
|
||||
// 테이블 선택 데이터
|
||||
selectedRows?: unknown[];
|
||||
selectedRowsData?: Record<string, unknown>[];
|
||||
|
||||
// 제어 데이터 소스 타입
|
||||
controlDataSource: ControlDataSource;
|
||||
|
||||
// 기타 컨텍스트
|
||||
buttonId: string;
|
||||
componentData?: unknown;
|
||||
timestamp: string;
|
||||
clickCount?: number;
|
||||
|
||||
// 사용자 정보
|
||||
userId?: string;
|
||||
companyCode?: CompanyCode;
|
||||
|
||||
// 화면 정보
|
||||
screenId?: number;
|
||||
screenCode?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 빠른 검증 결과
|
||||
*/
|
||||
export interface QuickValidationResult {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
canExecuteImmediately: boolean;
|
||||
actions?: DataflowAction[];
|
||||
}
|
||||
|
||||
// ===== 버튼 액션 표준 (DB 기반) =====
|
||||
|
||||
/**
|
||||
* 버튼 액션 표준 정의 (DB의 button_action_standards 테이블)
|
||||
*/
|
||||
export interface ButtonActionStandard extends TimestampFields {
|
||||
action_type: string;
|
||||
action_name: string;
|
||||
action_name_eng?: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
default_text?: string;
|
||||
default_text_eng?: string;
|
||||
default_icon?: string;
|
||||
default_color?: string;
|
||||
default_variant?: string;
|
||||
confirmation_required: boolean;
|
||||
confirmation_message?: string;
|
||||
validation_rules?: unknown;
|
||||
action_config?: unknown;
|
||||
sort_order?: number;
|
||||
is_active: ActiveStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 버튼 액션 생성/수정 요청
|
||||
*/
|
||||
export interface ButtonActionFormData {
|
||||
action_type: string;
|
||||
action_name: string;
|
||||
action_name_eng?: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
default_text?: string;
|
||||
default_text_eng?: string;
|
||||
default_icon?: string;
|
||||
default_color?: string;
|
||||
default_variant?: string;
|
||||
confirmation_required: boolean;
|
||||
confirmation_message?: string;
|
||||
validation_rules?: unknown;
|
||||
action_config?: unknown;
|
||||
sort_order?: number;
|
||||
is_active: ActiveStatus;
|
||||
}
|
||||
|
||||
// ===== API 응답 타입들 =====
|
||||
|
||||
/**
|
||||
* 버튼 액션 목록 응답
|
||||
*/
|
||||
export interface ButtonActionListResponse extends BaseApiResponse<ButtonActionStandard[]> {}
|
||||
|
||||
/**
|
||||
* 데이터플로우 실행 응답
|
||||
*/
|
||||
export interface DataflowExecutionResponse extends BaseApiResponse<DataflowExecutionResult> {}
|
||||
|
||||
/**
|
||||
* 트랜잭션 실행 응답
|
||||
*/
|
||||
export interface TransactionExecutionResponse extends BaseApiResponse<TransactionExecutionResult> {}
|
||||
|
||||
// ===== 유틸리티 타입들 =====
|
||||
|
||||
/**
|
||||
* 롤백 핸들러
|
||||
*/
|
||||
export interface RollbackHandler {
|
||||
actionId: string;
|
||||
rollbackFn: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 최적화된 실행 결과
|
||||
*/
|
||||
export interface OptimizedExecutionResult {
|
||||
jobId: string;
|
||||
immediateResult?: unknown;
|
||||
isBackground?: boolean;
|
||||
timing?: "before" | "after" | "replace";
|
||||
}
|
||||
|
||||
// ===== 타입 가드 및 유틸리티 함수들 =====
|
||||
|
||||
/**
|
||||
* DataflowCondition이 단일 조건인지 확인
|
||||
*/
|
||||
export const isSingleCondition = (condition: DataflowCondition): boolean => {
|
||||
return condition.type === "condition" && !!condition.field;
|
||||
};
|
||||
|
||||
/**
|
||||
* DataflowCondition이 그룹 조건인지 확인
|
||||
*/
|
||||
export const isGroupCondition = (condition: DataflowCondition): boolean => {
|
||||
return condition.type === "group" && !!condition.conditions?.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* DataflowAction이 데이터베이스 액션인지 확인
|
||||
*/
|
||||
export const isDatabaseAction = (action: DataflowAction): boolean => {
|
||||
return action.type === "database" && !!action.tableName;
|
||||
};
|
||||
|
||||
/**
|
||||
* DataflowAction이 API 액션인지 확인
|
||||
*/
|
||||
export const isApiAction = (action: DataflowAction): boolean => {
|
||||
return action.type === "api" && !!action.endpoint;
|
||||
};
|
||||
|
||||
/**
|
||||
* 액션 실행 결과가 성공인지 확인
|
||||
*/
|
||||
export const isActionSuccess = (result: ActionExecutionResult): boolean => {
|
||||
return result.status === "success";
|
||||
};
|
||||
|
||||
/**
|
||||
* 트랜잭션이 완료되었는지 확인 (성공 또는 실패)
|
||||
*/
|
||||
export const isTransactionCompleted = (state: TransactionExecutionState): boolean => {
|
||||
return ["success", "failed", "rolled_back"].includes(state.status);
|
||||
};
|
||||
343
frontend/types/index.ts
Normal file
343
frontend/types/index.ts
Normal file
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* 🎯 통합 타입 시스템 Index
|
||||
*
|
||||
* 모든 타입 정의를 중앙에서 관리하고 re-export합니다.
|
||||
* 이 파일을 통해 모든 타입에 일관성 있게 접근할 수 있습니다.
|
||||
*/
|
||||
|
||||
// ===== 핵심 공통 타입들 =====
|
||||
export * from "./unified-core";
|
||||
|
||||
// ===== 시스템별 전용 타입들 =====
|
||||
export * from "./screen-management";
|
||||
export * from "./control-management";
|
||||
export * from "./table-management";
|
||||
|
||||
// ===== 기존 호환성을 위한 re-export =====
|
||||
|
||||
// unified-core에서 제공하는 주요 타입들을 직접 export
|
||||
export type {
|
||||
// 핵심 타입들
|
||||
WebType,
|
||||
DynamicWebType,
|
||||
ButtonActionType,
|
||||
ComponentType,
|
||||
Position,
|
||||
Size,
|
||||
CommonStyle,
|
||||
ValidationRule,
|
||||
ConditionOperator,
|
||||
|
||||
// API 관련
|
||||
BaseApiResponse,
|
||||
PaginatedResponse,
|
||||
|
||||
// 공통 필드들
|
||||
CompanyCode,
|
||||
ActiveStatus,
|
||||
TimestampFields,
|
||||
AuditFields,
|
||||
|
||||
// 이벤트 타입들
|
||||
WebTypeEvent,
|
||||
ComponentEvent,
|
||||
} from "./unified-core";
|
||||
|
||||
// screen-management에서 제공하는 주요 타입들
|
||||
export type {
|
||||
// 컴포넌트 타입들
|
||||
ComponentData,
|
||||
BaseComponent,
|
||||
WidgetComponent,
|
||||
ContainerComponent,
|
||||
GroupComponent,
|
||||
DataTableComponent,
|
||||
FileComponent,
|
||||
|
||||
// 웹타입 설정들
|
||||
WebTypeConfig,
|
||||
DateTypeConfig,
|
||||
NumberTypeConfig,
|
||||
SelectTypeConfig,
|
||||
TextTypeConfig,
|
||||
FileTypeConfig,
|
||||
EntityTypeConfig,
|
||||
ButtonTypeConfig,
|
||||
|
||||
// 화면 관련
|
||||
ScreenDefinition,
|
||||
CreateScreenRequest,
|
||||
UpdateScreenRequest,
|
||||
LayoutData,
|
||||
GridSettings,
|
||||
ScreenTemplate,
|
||||
ScreenResolution,
|
||||
GroupState,
|
||||
|
||||
// 화면 해상도 상수
|
||||
SCREEN_RESOLUTIONS,
|
||||
|
||||
// 데이터 테이블
|
||||
DataTableColumn,
|
||||
DataTableFilter,
|
||||
|
||||
// 파일 업로드
|
||||
UploadedFile,
|
||||
} from "./screen-management";
|
||||
|
||||
// control-management에서 제공하는 주요 타입들
|
||||
export type {
|
||||
// 버튼 제어
|
||||
ExtendedButtonTypeConfig,
|
||||
ButtonDataflowConfig,
|
||||
ControlDataSource,
|
||||
|
||||
// 데이터플로우
|
||||
DataflowCondition,
|
||||
DataflowAction,
|
||||
ActionType,
|
||||
DatabaseOperation,
|
||||
NotificationType,
|
||||
|
||||
// 트랜잭션 관리
|
||||
TransactionGroup,
|
||||
RollbackStrategy,
|
||||
FailureHandling,
|
||||
ConditionalExecutionPlan,
|
||||
ExecutionCondition,
|
||||
|
||||
// 실행 결과
|
||||
ActionExecutionResult,
|
||||
TransactionExecutionState,
|
||||
DataflowExecutionResult,
|
||||
|
||||
// 컨텍스트
|
||||
ExtendedControlContext,
|
||||
QuickValidationResult,
|
||||
|
||||
// 버튼 액션 표준
|
||||
ButtonActionStandard,
|
||||
ButtonActionFormData,
|
||||
} from "./control-management";
|
||||
|
||||
// table-management에서 제공하는 주요 타입들
|
||||
export type {
|
||||
// 테이블 정보
|
||||
TableInfo,
|
||||
UnifiedColumnInfo,
|
||||
ColumnTypeInfo,
|
||||
ColumnSettings,
|
||||
|
||||
// 웹타입 표준
|
||||
WebTypeStandard,
|
||||
WebTypeDefinition,
|
||||
|
||||
// 라벨 관리
|
||||
TableLabels,
|
||||
ColumnLabels,
|
||||
|
||||
// 엔티티 조인
|
||||
EntityJoinConfig,
|
||||
EntityJoinResponse,
|
||||
BatchLookupRequest,
|
||||
BatchLookupResponse,
|
||||
|
||||
// 테이블 관계
|
||||
TableRelationship,
|
||||
DataRelationshipBridge,
|
||||
|
||||
// 컬럼 웹타입 설정
|
||||
ColumnWebTypeSetting,
|
||||
|
||||
// API 응답들
|
||||
TableListResponse,
|
||||
ColumnListResponse,
|
||||
ColumnTypeInfoResponse,
|
||||
WebTypeStandardListResponse,
|
||||
TableDataResponse,
|
||||
} from "./table-management";
|
||||
|
||||
// ===== 타입 가드 함수들 통합 export =====
|
||||
|
||||
// unified-core 타입 가드들
|
||||
export { isWebType, isButtonActionType, isComponentType, ynToBoolean, booleanToYN } from "./unified-core";
|
||||
|
||||
// screen-management 타입 가드들
|
||||
export {
|
||||
isWidgetComponent,
|
||||
isContainerComponent,
|
||||
isGroupComponent,
|
||||
isDataTableComponent,
|
||||
isFileComponent,
|
||||
asWidgetComponent,
|
||||
asContainerComponent,
|
||||
asGroupComponent,
|
||||
asDataTableComponent,
|
||||
asFileComponent,
|
||||
} from "./screen-management";
|
||||
|
||||
// control-management 타입 가드들
|
||||
export {
|
||||
isSingleCondition,
|
||||
isGroupCondition,
|
||||
isDatabaseAction,
|
||||
isApiAction,
|
||||
isActionSuccess,
|
||||
isTransactionCompleted,
|
||||
} from "./control-management";
|
||||
|
||||
// table-management 타입 가드들
|
||||
export {
|
||||
isReferenceWebType,
|
||||
isNumericWebType,
|
||||
isDateWebType,
|
||||
isSelectWebType,
|
||||
isRequiredColumn,
|
||||
isSystemColumn,
|
||||
mapWebTypeStandardToDefinition,
|
||||
mapColumnTypeInfoToUnified,
|
||||
mapUnifiedToColumnTypeInfo,
|
||||
} from "./table-management";
|
||||
|
||||
// ===== 상수들 통합 export =====
|
||||
|
||||
// table-management 상수들
|
||||
export { WEB_TYPE_OPTIONS } from "./table-management";
|
||||
|
||||
// ===== 타입 별칭 (기존 호환성) =====
|
||||
|
||||
/**
|
||||
* @deprecated screen.ts에서 이전하세요. unified-core.ts의 WebType을 사용하세요.
|
||||
*/
|
||||
export type LegacyWebType = WebType;
|
||||
|
||||
/**
|
||||
* @deprecated screen.ts에서 이전하세요. unified-core.ts의 ButtonActionType을 사용하세요.
|
||||
*/
|
||||
export type LegacyButtonActionType = ButtonActionType;
|
||||
|
||||
/**
|
||||
* @deprecated screen.ts에서 이전하세요. screen-management.ts의 ComponentData를 사용하세요.
|
||||
*/
|
||||
export type LegacyComponentData = ComponentData;
|
||||
|
||||
// ===== 유틸리티 타입들 =====
|
||||
|
||||
/**
|
||||
* 컴포넌트 업데이트를 위한 부분 타입
|
||||
*/
|
||||
export type ComponentUpdate<T extends ComponentData> = Partial<Omit<T, "id" | "type">> & {
|
||||
id: string;
|
||||
type: T["type"];
|
||||
};
|
||||
|
||||
/**
|
||||
* API 요청을 위한 기본 파라미터
|
||||
*/
|
||||
export interface BaseRequestParams {
|
||||
companyCode?: CompanyCode;
|
||||
page?: number;
|
||||
size?: number;
|
||||
sortBy?: string;
|
||||
sortDirection?: "asc" | "desc";
|
||||
searchTerm?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 폼 데이터 타입 (모든 시스템에서 공통 사용)
|
||||
*/
|
||||
export type FormData = Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* 선택된 행 데이터 타입
|
||||
*/
|
||||
export type SelectedRowData = Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* 테이블 데이터 타입
|
||||
*/
|
||||
export type TableData = Record<string, unknown>[];
|
||||
|
||||
// ===== 마이그레이션 도우미 =====
|
||||
|
||||
/**
|
||||
* 기존 screen.ts 타입을 새로운 통합 타입으로 마이그레이션하는 도우미
|
||||
*/
|
||||
export namespace Migration {
|
||||
/**
|
||||
* 기존 screen.ts의 WebType을 새로운 WebType으로 변환
|
||||
*/
|
||||
export const migrateWebType = (oldWebType: string): WebType => {
|
||||
// 기존 타입이 새로운 WebType에 포함되어 있는지 확인
|
||||
if (isWebType(oldWebType)) {
|
||||
return oldWebType as WebType;
|
||||
}
|
||||
|
||||
// 호환되지 않는 타입의 경우 기본값 반환
|
||||
console.warn(`Unknown WebType: ${oldWebType}, defaulting to 'text'`);
|
||||
return "text";
|
||||
};
|
||||
|
||||
/**
|
||||
* 기존 ButtonActionType을 새로운 ButtonActionType으로 변환
|
||||
*/
|
||||
export const migrateButtonActionType = (oldActionType: string): ButtonActionType => {
|
||||
if (isButtonActionType(oldActionType)) {
|
||||
return oldActionType as ButtonActionType;
|
||||
}
|
||||
|
||||
console.warn(`Unknown ButtonActionType: ${oldActionType}, defaulting to 'submit'`);
|
||||
return "submit";
|
||||
};
|
||||
|
||||
/**
|
||||
* Y/N 문자열을 boolean으로 변환 (DB 호환성)
|
||||
*/
|
||||
export const migrateYNToBoolean = (value: string | undefined): boolean => {
|
||||
return value === "Y";
|
||||
};
|
||||
|
||||
/**
|
||||
* boolean을 Y/N 문자열로 변환 (DB 호환성)
|
||||
*/
|
||||
export const migrateBooleanToYN = (value: boolean): string => {
|
||||
return value ? "Y" : "N";
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 타입 검증 도우미 =====
|
||||
|
||||
/**
|
||||
* 런타임에서 타입 안전성을 보장하는 검증 함수들
|
||||
*/
|
||||
export namespace TypeValidation {
|
||||
/**
|
||||
* 객체가 BaseComponent 인터페이스를 만족하는지 검증
|
||||
*/
|
||||
export const validateBaseComponent = (obj: unknown): obj is BaseComponent => {
|
||||
if (typeof obj !== "object" || obj === null) return false;
|
||||
|
||||
const component = obj as Record<string, unknown>;
|
||||
return (
|
||||
typeof component.id === "string" &&
|
||||
typeof component.type === "string" &&
|
||||
isComponentType(component.type as string) &&
|
||||
typeof component.position === "object" &&
|
||||
typeof component.size === "object"
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 객체가 WebTypeConfig를 만족하는지 검증
|
||||
*/
|
||||
export const validateWebTypeConfig = (obj: unknown): obj is WebTypeConfig => {
|
||||
return typeof obj === "object" && obj !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 문자열이 유효한 CompanyCode인지 검증
|
||||
*/
|
||||
export const validateCompanyCode = (code: unknown): code is CompanyCode => {
|
||||
return typeof code === "string" && code.length > 0;
|
||||
};
|
||||
}
|
||||
1002
frontend/types/screen-legacy-backup.ts
Normal file
1002
frontend/types/screen-legacy-backup.ts
Normal file
File diff suppressed because it is too large
Load Diff
583
frontend/types/screen-management.ts
Normal file
583
frontend/types/screen-management.ts
Normal file
@@ -0,0 +1,583 @@
|
||||
/**
|
||||
* 🖥️ 화면관리 시스템 전용 타입 정의
|
||||
*
|
||||
* 화면 설계, 컴포넌트 관리, 레이아웃 등 화면관리 시스템에서만 사용하는 타입들
|
||||
*/
|
||||
|
||||
import {
|
||||
ComponentType,
|
||||
WebType,
|
||||
DynamicWebType,
|
||||
Position,
|
||||
Size,
|
||||
CommonStyle,
|
||||
ValidationRule,
|
||||
TimestampFields,
|
||||
CompanyCode,
|
||||
ActiveStatus,
|
||||
isWebType,
|
||||
} from "./unified-core";
|
||||
|
||||
// ===== 기본 컴포넌트 인터페이스 =====
|
||||
|
||||
/**
|
||||
* 모든 컴포넌트의 기본 인터페이스
|
||||
*/
|
||||
export interface BaseComponent {
|
||||
id: string;
|
||||
type: ComponentType;
|
||||
position: Position;
|
||||
size: Size;
|
||||
parentId?: string;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
readonly?: boolean;
|
||||
style?: ComponentStyle;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면관리용 확장 스타일 (CommonStyle 기반)
|
||||
*/
|
||||
export interface ComponentStyle extends CommonStyle {
|
||||
// 화면관리 전용 스타일 확장 가능
|
||||
}
|
||||
|
||||
/**
|
||||
* 위젯 컴포넌트 (입력 요소)
|
||||
*/
|
||||
export interface WidgetComponent extends BaseComponent {
|
||||
type: "widget";
|
||||
widgetType: DynamicWebType;
|
||||
placeholder?: string;
|
||||
columnName?: string;
|
||||
webTypeConfig?: WebTypeConfig;
|
||||
validationRules?: ValidationRule[];
|
||||
|
||||
// 웹타입별 추가 설정
|
||||
dateConfig?: DateTypeConfig;
|
||||
numberConfig?: NumberTypeConfig;
|
||||
selectConfig?: SelectTypeConfig;
|
||||
textConfig?: TextTypeConfig;
|
||||
fileConfig?: FileTypeConfig;
|
||||
entityConfig?: EntityTypeConfig;
|
||||
buttonConfig?: ButtonTypeConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 컨테이너 컴포넌트 (레이아웃)
|
||||
*/
|
||||
export interface ContainerComponent extends BaseComponent {
|
||||
type: "container" | "row" | "column" | "area";
|
||||
children?: string[]; // 자식 컴포넌트 ID 배열
|
||||
layoutDirection?: "horizontal" | "vertical";
|
||||
justifyContent?: "start" | "center" | "end" | "space-between" | "space-around";
|
||||
alignItems?: "start" | "center" | "end" | "stretch";
|
||||
gap?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 그룹 컴포넌트 (논리적 그룹핑)
|
||||
*/
|
||||
export interface GroupComponent extends BaseComponent {
|
||||
type: "group";
|
||||
groupName: string;
|
||||
children: string[]; // 그룹에 속한 컴포넌트 ID 배열
|
||||
isCollapsible?: boolean;
|
||||
isCollapsed?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 테이블 컴포넌트
|
||||
*/
|
||||
export interface DataTableComponent extends BaseComponent {
|
||||
type: "datatable";
|
||||
tableName?: string;
|
||||
columns: DataTableColumn[];
|
||||
pagination?: boolean;
|
||||
pageSize?: number;
|
||||
searchable?: boolean;
|
||||
sortable?: boolean;
|
||||
filters?: DataTableFilter[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 업로드 컴포넌트
|
||||
*/
|
||||
export interface FileComponent extends BaseComponent {
|
||||
type: "file";
|
||||
fileConfig: FileTypeConfig;
|
||||
uploadedFiles?: UploadedFile[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 통합 컴포넌트 데이터 타입
|
||||
*/
|
||||
export type ComponentData = WidgetComponent | ContainerComponent | GroupComponent | DataTableComponent | FileComponent;
|
||||
|
||||
// ===== 웹타입별 설정 인터페이스 =====
|
||||
|
||||
/**
|
||||
* 기본 웹타입 설정
|
||||
*/
|
||||
export interface WebTypeConfig {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 날짜/시간 타입 설정
|
||||
*/
|
||||
export interface DateTypeConfig {
|
||||
format: "YYYY-MM-DD" | "YYYY-MM-DD HH:mm" | "YYYY-MM-DD HH:mm:ss";
|
||||
showTime: boolean;
|
||||
minDate?: string;
|
||||
maxDate?: string;
|
||||
defaultValue?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 숫자 타입 설정
|
||||
*/
|
||||
export interface NumberTypeConfig {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
format?: "integer" | "decimal" | "currency" | "percentage";
|
||||
decimalPlaces?: number;
|
||||
thousandSeparator?: boolean;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 선택박스 타입 설정
|
||||
*/
|
||||
export interface SelectTypeConfig {
|
||||
options: Array<{ label: string; value: string }>;
|
||||
multiple?: boolean;
|
||||
searchable?: boolean;
|
||||
placeholder?: string;
|
||||
allowCustomValue?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 텍스트 타입 설정
|
||||
*/
|
||||
export interface TextTypeConfig {
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
pattern?: string;
|
||||
format?: "none" | "email" | "phone" | "url" | "korean" | "english";
|
||||
placeholder?: string;
|
||||
multiline?: boolean;
|
||||
rows?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 타입 설정
|
||||
*/
|
||||
export interface FileTypeConfig {
|
||||
accept?: string;
|
||||
multiple?: boolean;
|
||||
maxSize?: number; // bytes
|
||||
maxFiles?: number;
|
||||
preview?: boolean;
|
||||
docType?: string;
|
||||
companyCode?: CompanyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 엔티티 타입 설정
|
||||
*/
|
||||
export interface EntityTypeConfig {
|
||||
referenceTable: string;
|
||||
referenceColumn: string;
|
||||
displayColumn: string;
|
||||
searchColumns?: string[];
|
||||
filters?: Record<string, unknown>;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 버튼 타입 설정
|
||||
*/
|
||||
export interface ButtonTypeConfig {
|
||||
text?: string;
|
||||
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
|
||||
size?: "sm" | "md" | "lg";
|
||||
icon?: string;
|
||||
// ButtonActionType과 관련된 설정은 control-management.ts에서 정의
|
||||
}
|
||||
|
||||
// ===== 데이터 테이블 관련 =====
|
||||
|
||||
/**
|
||||
* 데이터 테이블 컬럼
|
||||
*/
|
||||
export interface DataTableColumn {
|
||||
id: string;
|
||||
columnName: string;
|
||||
label: string;
|
||||
dataType?: string;
|
||||
widgetType?: DynamicWebType;
|
||||
width?: number;
|
||||
sortable?: boolean;
|
||||
searchable?: boolean;
|
||||
visible: boolean;
|
||||
frozen?: boolean;
|
||||
align?: "left" | "center" | "right";
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 테이블 필터
|
||||
*/
|
||||
export interface DataTableFilter {
|
||||
id: string;
|
||||
columnName: string;
|
||||
operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN";
|
||||
value: unknown;
|
||||
logicalOperator?: "AND" | "OR";
|
||||
}
|
||||
|
||||
// ===== 파일 업로드 관련 =====
|
||||
|
||||
/**
|
||||
* 업로드된 파일 정보
|
||||
*/
|
||||
export interface UploadedFile {
|
||||
objid: string;
|
||||
realFileName: string;
|
||||
savedFileName: string;
|
||||
fileSize: number;
|
||||
fileExt: string;
|
||||
filePath: string;
|
||||
docType?: string;
|
||||
docTypeName?: string;
|
||||
writer?: string;
|
||||
regdate?: string;
|
||||
status?: "uploading" | "completed" | "error";
|
||||
companyCode?: CompanyCode;
|
||||
}
|
||||
|
||||
// ===== 화면 정의 관련 =====
|
||||
|
||||
/**
|
||||
* 화면 정의
|
||||
*/
|
||||
export interface ScreenDefinition {
|
||||
screenId: number;
|
||||
screenName: string;
|
||||
screenCode: string;
|
||||
tableName: string;
|
||||
tableLabel?: string;
|
||||
companyCode: CompanyCode;
|
||||
description?: string;
|
||||
isActive: ActiveStatus;
|
||||
createdDate: Date;
|
||||
updatedDate: Date;
|
||||
createdBy?: string;
|
||||
updatedBy?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면 생성 요청
|
||||
*/
|
||||
export interface CreateScreenRequest {
|
||||
screenName: string;
|
||||
screenCode?: string;
|
||||
tableName: string;
|
||||
tableLabel?: string;
|
||||
companyCode: CompanyCode;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면 수정 요청
|
||||
*/
|
||||
export interface UpdateScreenRequest {
|
||||
screenName?: string;
|
||||
screenCode?: string;
|
||||
tableName?: string;
|
||||
tableLabel?: string;
|
||||
description?: string;
|
||||
isActive?: ActiveStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면 해상도 설정
|
||||
*/
|
||||
export interface ScreenResolution {
|
||||
width: number;
|
||||
height: number;
|
||||
name: string;
|
||||
category: "desktop" | "tablet" | "mobile" | "custom";
|
||||
}
|
||||
|
||||
/**
|
||||
* 미리 정의된 해상도 프리셋
|
||||
*/
|
||||
export const SCREEN_RESOLUTIONS: ScreenResolution[] = [
|
||||
// Desktop
|
||||
{ width: 1920, height: 1080, name: "Full HD (1920×1080)", category: "desktop" },
|
||||
{ width: 1366, height: 768, name: "HD (1366×768)", category: "desktop" },
|
||||
{ width: 1440, height: 900, name: "WXGA+ (1440×900)", category: "desktop" },
|
||||
{ width: 1280, height: 1024, name: "SXGA (1280×1024)", category: "desktop" },
|
||||
|
||||
// Tablet
|
||||
{ width: 1024, height: 768, name: "iPad Landscape (1024×768)", category: "tablet" },
|
||||
{ width: 768, height: 1024, name: "iPad Portrait (768×1024)", category: "tablet" },
|
||||
{ width: 1112, height: 834, name: 'iPad Pro 10.5" Landscape', category: "tablet" },
|
||||
{ width: 834, height: 1112, name: 'iPad Pro 10.5" Portrait', category: "tablet" },
|
||||
|
||||
// Mobile
|
||||
{ width: 375, height: 667, name: "iPhone 8 (375×667)", category: "mobile" },
|
||||
{ width: 414, height: 896, name: "iPhone 11 (414×896)", category: "mobile" },
|
||||
{ width: 390, height: 844, name: "iPhone 12/13 (390×844)", category: "mobile" },
|
||||
{ width: 360, height: 640, name: "Android Medium (360×640)", category: "mobile" },
|
||||
];
|
||||
|
||||
/**
|
||||
* 그룹화 상태
|
||||
*/
|
||||
export interface GroupState {
|
||||
isGrouping: boolean;
|
||||
selectedComponents: string[];
|
||||
groupTarget?: string | null;
|
||||
groupMode?: "create" | "add" | "remove" | "ungroup";
|
||||
groupTitle?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 레이아웃 데이터
|
||||
*/
|
||||
export interface LayoutData {
|
||||
screenId: number;
|
||||
components: ComponentData[];
|
||||
gridSettings?: GridSettings;
|
||||
metadata?: LayoutMetadata;
|
||||
screenResolution?: ScreenResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* 격자 설정
|
||||
*/
|
||||
export interface GridSettings {
|
||||
enabled: boolean;
|
||||
size: number;
|
||||
color: string;
|
||||
opacity: number;
|
||||
snapToGrid: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 레이아웃 메타데이터
|
||||
*/
|
||||
export interface LayoutMetadata {
|
||||
version: string;
|
||||
lastModified: Date;
|
||||
modifiedBy: string;
|
||||
description?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
// ===== 템플릿 관련 =====
|
||||
|
||||
/**
|
||||
* 화면 템플릿
|
||||
*/
|
||||
export interface ScreenTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
components: ComponentData[];
|
||||
previewImage?: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿 컴포넌트 (템플릿 패널에서 사용)
|
||||
*/
|
||||
export interface TemplateComponent {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
category: string;
|
||||
defaultProps: Partial<ComponentData>;
|
||||
children?: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
defaultProps: Partial<ComponentData>;
|
||||
}>;
|
||||
}
|
||||
|
||||
// ===== 타입 가드 함수들 =====
|
||||
|
||||
/**
|
||||
* WidgetComponent 타입 가드 (강화된 검증)
|
||||
*/
|
||||
export const isWidgetComponent = (component: ComponentData): component is WidgetComponent => {
|
||||
if (!component || typeof component !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 기본 타입 체크
|
||||
if (component.type !== "widget") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 필수 필드 존재 여부 체크
|
||||
if (!component.id || typeof component.id !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// widgetType이 유효한 WebType인지 체크
|
||||
if (!component.widgetType || !isWebType(component.widgetType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// position 검증
|
||||
if (
|
||||
!component.position ||
|
||||
typeof component.position.x !== "number" ||
|
||||
typeof component.position.y !== "number" ||
|
||||
!Number.isFinite(component.position.x) ||
|
||||
!Number.isFinite(component.position.y)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// size 검증
|
||||
if (
|
||||
!component.size ||
|
||||
typeof component.size.width !== "number" ||
|
||||
typeof component.size.height !== "number" ||
|
||||
!Number.isFinite(component.size.width) ||
|
||||
!Number.isFinite(component.size.height) ||
|
||||
component.size.width <= 0 ||
|
||||
component.size.height <= 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* ContainerComponent 타입 가드 (강화된 검증)
|
||||
*/
|
||||
export const isContainerComponent = (component: ComponentData): component is ContainerComponent => {
|
||||
if (!component || typeof component !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 기본 타입 체크
|
||||
if (!["container", "row", "column", "area"].includes(component.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 필수 필드 존재 여부 체크
|
||||
if (!component.id || typeof component.id !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// position 검증
|
||||
if (
|
||||
!component.position ||
|
||||
typeof component.position.x !== "number" ||
|
||||
typeof component.position.y !== "number" ||
|
||||
!Number.isFinite(component.position.x) ||
|
||||
!Number.isFinite(component.position.y)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// size 검증
|
||||
if (
|
||||
!component.size ||
|
||||
typeof component.size.width !== "number" ||
|
||||
typeof component.size.height !== "number" ||
|
||||
!Number.isFinite(component.size.width) ||
|
||||
!Number.isFinite(component.size.height) ||
|
||||
component.size.width <= 0 ||
|
||||
component.size.height <= 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* GroupComponent 타입 가드
|
||||
*/
|
||||
export const isGroupComponent = (component: ComponentData): component is GroupComponent => {
|
||||
return component.type === "group";
|
||||
};
|
||||
|
||||
/**
|
||||
* DataTableComponent 타입 가드
|
||||
*/
|
||||
export const isDataTableComponent = (component: ComponentData): component is DataTableComponent => {
|
||||
return component.type === "datatable";
|
||||
};
|
||||
|
||||
/**
|
||||
* FileComponent 타입 가드
|
||||
*/
|
||||
export const isFileComponent = (component: ComponentData): component is FileComponent => {
|
||||
return component.type === "file";
|
||||
};
|
||||
|
||||
// ===== 안전한 타입 캐스팅 유틸리티 =====
|
||||
|
||||
/**
|
||||
* ComponentData를 WidgetComponent로 안전하게 캐스팅
|
||||
*/
|
||||
export const asWidgetComponent = (component: ComponentData): WidgetComponent => {
|
||||
if (!isWidgetComponent(component)) {
|
||||
throw new Error(`Expected WidgetComponent, got ${component.type}`);
|
||||
}
|
||||
return component;
|
||||
};
|
||||
|
||||
/**
|
||||
* ComponentData를 ContainerComponent로 안전하게 캐스팅
|
||||
*/
|
||||
export const asContainerComponent = (component: ComponentData): ContainerComponent => {
|
||||
if (!isContainerComponent(component)) {
|
||||
throw new Error(`Expected ContainerComponent, got ${component.type}`);
|
||||
}
|
||||
return component;
|
||||
};
|
||||
|
||||
/**
|
||||
* ComponentData를 GroupComponent로 안전하게 캐스팅
|
||||
*/
|
||||
export const asGroupComponent = (component: ComponentData): GroupComponent => {
|
||||
if (!isGroupComponent(component)) {
|
||||
throw new Error(`Expected GroupComponent, got ${component.type}`);
|
||||
}
|
||||
return component;
|
||||
};
|
||||
|
||||
/**
|
||||
* ComponentData를 DataTableComponent로 안전하게 캐스팅
|
||||
*/
|
||||
export const asDataTableComponent = (component: ComponentData): DataTableComponent => {
|
||||
if (!isDataTableComponent(component)) {
|
||||
throw new Error(`Expected DataTableComponent, got ${component.type}`);
|
||||
}
|
||||
return component;
|
||||
};
|
||||
|
||||
/**
|
||||
* ComponentData를 FileComponent로 안전하게 캐스팅
|
||||
*/
|
||||
export const asFileComponent = (component: ComponentData): FileComponent => {
|
||||
if (!isFileComponent(component)) {
|
||||
throw new Error(`Expected FileComponent, got ${component.type}`);
|
||||
}
|
||||
return component;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
505
frontend/types/table-management.ts
Normal file
505
frontend/types/table-management.ts
Normal file
@@ -0,0 +1,505 @@
|
||||
/**
|
||||
* 🗄️ 테이블 타입관리 시스템 전용 타입 정의
|
||||
*
|
||||
* 데이터베이스 테이블 스키마, 컬럼 타입, 웹타입 매핑 등 테이블 관리에서만 사용하는 타입들
|
||||
*/
|
||||
|
||||
import {
|
||||
DynamicWebType,
|
||||
CompanyCode,
|
||||
ActiveStatus,
|
||||
TimestampFields,
|
||||
BaseApiResponse,
|
||||
PaginatedResponse,
|
||||
ConditionOperator,
|
||||
} from "./unified-core";
|
||||
|
||||
// ===== 기본 테이블 정보 =====
|
||||
|
||||
/**
|
||||
* 테이블 정보
|
||||
*/
|
||||
export interface TableInfo {
|
||||
tableName: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
columnCount: number;
|
||||
companyCode?: CompanyCode;
|
||||
isActive?: ActiveStatus;
|
||||
createdDate?: Date;
|
||||
updatedDate?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 통합된 컬럼 정보 (프론트엔드/백엔드 호환)
|
||||
*/
|
||||
export interface UnifiedColumnInfo {
|
||||
// 기본 정보
|
||||
tableName: string;
|
||||
columnName: string;
|
||||
displayName: string;
|
||||
|
||||
// 데이터 타입
|
||||
dataType: string; // DB 데이터 타입 (varchar, integer, timestamp 등)
|
||||
dbType: string; // DB 내부 타입
|
||||
webType: DynamicWebType; // 웹 입력 타입 (text, number, date 등)
|
||||
|
||||
// 입력 설정
|
||||
inputType: "direct" | "auto";
|
||||
detailSettings?: Record<string, unknown>; // JSON 파싱된 객체
|
||||
description?: string;
|
||||
|
||||
// 제약 조건
|
||||
isNullable: boolean; // Y/N → boolean 변환
|
||||
isPrimaryKey: boolean;
|
||||
defaultValue?: string;
|
||||
|
||||
// 크기 제한
|
||||
maxLength?: number;
|
||||
numericPrecision?: number;
|
||||
numericScale?: number;
|
||||
|
||||
// 표시 옵션
|
||||
isVisible?: boolean;
|
||||
displayOrder?: number;
|
||||
|
||||
// 참조 관계
|
||||
codeCategory?: string;
|
||||
codeValue?: string;
|
||||
referenceTable?: string;
|
||||
referenceColumn?: string;
|
||||
displayColumn?: string;
|
||||
|
||||
// 메타데이터
|
||||
companyCode?: CompanyCode;
|
||||
createdDate?: Date;
|
||||
updatedDate?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 백엔드 호환용 컬럼 타입 정보 (기존 ColumnTypeInfo)
|
||||
*/
|
||||
export interface ColumnTypeInfo {
|
||||
columnName: string;
|
||||
displayName: string;
|
||||
dataType: string;
|
||||
dbType: string;
|
||||
webType: string; // string 타입 (백엔드 호환)
|
||||
inputType?: "direct" | "auto";
|
||||
detailSettings: string; // JSON 문자열
|
||||
description: string; // 필수 필드
|
||||
isNullable: string; // Y/N 문자열
|
||||
isPrimaryKey: boolean;
|
||||
defaultValue?: string;
|
||||
maxLength?: number;
|
||||
numericPrecision?: number;
|
||||
numericScale?: number;
|
||||
codeCategory?: string;
|
||||
codeValue?: string;
|
||||
referenceTable?: string;
|
||||
referenceColumn?: string;
|
||||
displayColumn?: string;
|
||||
displayOrder?: number;
|
||||
isVisible?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 설정 (업데이트용)
|
||||
*/
|
||||
export interface ColumnSettings {
|
||||
columnName?: string; // 컬럼명 (업데이트 시 필요)
|
||||
columnLabel: string; // 컬럼 표시명
|
||||
webType: string; // 웹 입력 타입
|
||||
detailSettings: string; // 상세 설정 (JSON 문자열)
|
||||
codeCategory: string; // 코드 카테고리
|
||||
codeValue: string; // 코드 값
|
||||
referenceTable: string; // 참조 테이블
|
||||
referenceColumn: string; // 참조 컬럼
|
||||
displayColumn?: string; // 표시할 컬럼명
|
||||
displayOrder?: number; // 표시 순서
|
||||
isVisible?: boolean; // 표시 여부
|
||||
}
|
||||
|
||||
// ===== 웹타입 표준 정의 =====
|
||||
|
||||
/**
|
||||
* 웹타입 표준 정보 (DB의 web_type_standards 테이블)
|
||||
*/
|
||||
export interface WebTypeStandard extends TimestampFields {
|
||||
web_type: string;
|
||||
type_name: string;
|
||||
type_name_eng?: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
default_config?: unknown; // JSON
|
||||
validation_rules?: unknown; // JSON
|
||||
default_style?: unknown; // JSON
|
||||
input_properties?: unknown; // JSON
|
||||
sort_order?: number;
|
||||
is_active: ActiveStatus;
|
||||
component_name?: string;
|
||||
config_panel?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 프론트엔드용 웹타입 정의 (WebTypeStandard 변환)
|
||||
*/
|
||||
export interface WebTypeDefinition {
|
||||
webType: string; // web_type 필드
|
||||
typeName: string; // type_name 필드
|
||||
typeNameEng?: string; // type_name_eng 필드
|
||||
description?: string;
|
||||
category: string;
|
||||
defaultConfig: Record<string, unknown>; // JSON 타입 매핑
|
||||
validationRules?: Record<string, unknown>; // JSON 타입 매핑
|
||||
defaultStyle?: Record<string, unknown>; // JSON 타입 매핑
|
||||
inputProperties?: Record<string, unknown>; // JSON 타입 매핑
|
||||
componentName?: string; // component_name 필드
|
||||
configPanel?: string; // config_panel 필드
|
||||
sortOrder?: number; // sort_order 필드
|
||||
isActive: boolean; // is_active Y/N → boolean 변환
|
||||
}
|
||||
|
||||
// ===== 테이블 라벨 관리 =====
|
||||
|
||||
/**
|
||||
* 테이블 라벨
|
||||
*/
|
||||
export interface TableLabels extends TimestampFields {
|
||||
tableName: string;
|
||||
tableLabel?: string;
|
||||
description?: string;
|
||||
companyCode?: CompanyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 컬럼 라벨
|
||||
*/
|
||||
export interface ColumnLabels extends TimestampFields {
|
||||
id?: number;
|
||||
tableName: string;
|
||||
columnName: string;
|
||||
columnLabel?: string;
|
||||
webType?: string;
|
||||
detailSettings?: string;
|
||||
description?: string;
|
||||
displayOrder?: number;
|
||||
isVisible?: boolean;
|
||||
codeCategory?: string;
|
||||
codeValue?: string;
|
||||
referenceTable?: string;
|
||||
referenceColumn?: string;
|
||||
displayColumn?: string;
|
||||
companyCode?: CompanyCode;
|
||||
}
|
||||
|
||||
// ===== 엔티티 조인 관리 =====
|
||||
|
||||
/**
|
||||
* 엔티티 조인 설정
|
||||
*/
|
||||
export interface EntityJoinConfig {
|
||||
sourceTable: string; // 원본 테이블 (예: companies)
|
||||
sourceColumn: string; // 원본 컬럼 (예: writer)
|
||||
referenceTable: string; // 참조 테이블 (예: user_info)
|
||||
referenceColumn: string; // 조인 키 (예: user_id)
|
||||
displayColumn: string; // 표시할 값 (예: user_name)
|
||||
aliasColumn: string; // 결과 컬럼명 (예: writer_name)
|
||||
companyCode?: CompanyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 엔티티 조인 응답
|
||||
*/
|
||||
export interface EntityJoinResponse {
|
||||
data: Record<string, unknown>[];
|
||||
total: number;
|
||||
page: number;
|
||||
size: number;
|
||||
totalPages: number;
|
||||
entityJoinInfo?: {
|
||||
joinConfigs: EntityJoinConfig[];
|
||||
strategy: "full_join" | "cache_lookup" | "hybrid";
|
||||
performance: {
|
||||
queryTime: number;
|
||||
cacheHitRate?: number;
|
||||
hybridBreakdown?: {
|
||||
dbJoins: number;
|
||||
cacheJoins: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 배치 조회 요청
|
||||
*/
|
||||
export interface BatchLookupRequest {
|
||||
table: string;
|
||||
key: string;
|
||||
displayColumn: string;
|
||||
companyCode?: CompanyCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 배치 조회 응답
|
||||
*/
|
||||
export interface BatchLookupResponse {
|
||||
key: string;
|
||||
value: unknown;
|
||||
}
|
||||
|
||||
// ===== 테이블 관계 관리 =====
|
||||
|
||||
/**
|
||||
* 테이블 관계 정의
|
||||
*/
|
||||
export interface TableRelationship extends TimestampFields {
|
||||
relationship_id?: number;
|
||||
relationship_name?: string;
|
||||
from_table_name?: string;
|
||||
from_column_name?: string;
|
||||
to_table_name?: string;
|
||||
to_column_name?: string;
|
||||
relationship_type?: string;
|
||||
connection_type?: string;
|
||||
company_code?: CompanyCode;
|
||||
settings?: unknown; // JSON
|
||||
is_active?: ActiveStatus;
|
||||
diagram_id?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 관계 브릿지
|
||||
*/
|
||||
export interface DataRelationshipBridge extends TimestampFields {
|
||||
bridge_id?: number;
|
||||
relationship_id?: number;
|
||||
from_table_name: string;
|
||||
from_column_name: string;
|
||||
to_table_name: string;
|
||||
to_column_name: string;
|
||||
connection_type: string;
|
||||
company_code: CompanyCode;
|
||||
is_active?: ActiveStatus;
|
||||
bridge_data?: unknown; // JSON
|
||||
from_key_value?: string;
|
||||
from_record_id?: string;
|
||||
to_key_value?: string;
|
||||
to_record_id?: string;
|
||||
}
|
||||
|
||||
// ===== 컬럼 웹타입 설정 =====
|
||||
|
||||
/**
|
||||
* 컬럼 웹타입 설정
|
||||
*/
|
||||
export interface ColumnWebTypeSetting {
|
||||
tableName: string;
|
||||
columnName: string;
|
||||
webType: DynamicWebType;
|
||||
detailSettings?: Record<string, unknown>;
|
||||
codeCategory?: string;
|
||||
referenceTable?: string;
|
||||
referenceColumn?: string;
|
||||
displayColumn?: string;
|
||||
companyCode?: CompanyCode;
|
||||
}
|
||||
|
||||
// ===== API 응답 타입들 =====
|
||||
|
||||
/**
|
||||
* 테이블 목록 응답
|
||||
*/
|
||||
export interface TableListResponse extends BaseApiResponse<TableInfo[]> {}
|
||||
|
||||
/**
|
||||
* 컬럼 목록 응답
|
||||
*/
|
||||
export interface ColumnListResponse extends BaseApiResponse<UnifiedColumnInfo[]> {}
|
||||
|
||||
/**
|
||||
* 컬럼 타입 정보 응답 (백엔드 호환)
|
||||
*/
|
||||
export interface ColumnTypeInfoResponse extends BaseApiResponse<ColumnTypeInfo[]> {}
|
||||
|
||||
/**
|
||||
* 컬럼 설정 응답
|
||||
*/
|
||||
export interface ColumnSettingsResponse extends BaseApiResponse<void> {}
|
||||
|
||||
/**
|
||||
* 웹타입 표준 목록 응답
|
||||
*/
|
||||
export interface WebTypeStandardListResponse extends BaseApiResponse<WebTypeStandard[]> {}
|
||||
|
||||
/**
|
||||
* 웹타입 정의 목록 응답
|
||||
*/
|
||||
export interface WebTypeDefinitionListResponse extends BaseApiResponse<WebTypeDefinition[]> {}
|
||||
|
||||
/**
|
||||
* 테이블 데이터 조회 응답
|
||||
*/
|
||||
export interface TableDataResponse extends PaginatedResponse<Record<string, unknown>> {}
|
||||
|
||||
// ===== 웹타입 옵션 상수 =====
|
||||
|
||||
/**
|
||||
* 웹타입 옵션 (기존 호환성 유지)
|
||||
*/
|
||||
export const WEB_TYPE_OPTIONS = [
|
||||
{ value: "text", label: "text", description: "일반 텍스트 입력" },
|
||||
{ value: "number", label: "number", description: "숫자 입력" },
|
||||
{ value: "decimal", label: "decimal", description: "소수 입력" },
|
||||
{ value: "date", label: "date", description: "날짜 선택기" },
|
||||
{ value: "datetime", label: "datetime", description: "날짜시간 선택기" },
|
||||
{ value: "code", label: "code", description: "코드 선택 (공통코드 지정)" },
|
||||
{ value: "entity", label: "entity", description: "엔티티 참조 (참조테이블 지정)" },
|
||||
{ value: "textarea", label: "textarea", description: "여러 줄 텍스트" },
|
||||
{ value: "select", label: "select", description: "드롭다운 선택" },
|
||||
{ value: "dropdown", label: "dropdown", description: "드롭다운 선택" },
|
||||
{ value: "checkbox", label: "checkbox", description: "체크박스" },
|
||||
{ value: "boolean", label: "boolean", description: "참/거짓" },
|
||||
{ value: "radio", label: "radio", description: "라디오 버튼" },
|
||||
{ value: "file", label: "file", description: "파일 업로드" },
|
||||
{ value: "email", label: "email", description: "이메일 입력" },
|
||||
{ value: "tel", label: "tel", description: "전화번호 입력" },
|
||||
{ value: "url", label: "url", description: "URL 입력" },
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* 웹타입 (기존 호환성)
|
||||
*/
|
||||
export type WebType = (typeof WEB_TYPE_OPTIONS)[number]["value"];
|
||||
|
||||
// ===== 변환 유틸리티 함수들 =====
|
||||
|
||||
/**
|
||||
* WebTypeStandard를 WebTypeDefinition으로 변환
|
||||
*/
|
||||
export const mapWebTypeStandardToDefinition = (standard: WebTypeStandard): WebTypeDefinition => ({
|
||||
webType: standard.web_type,
|
||||
typeName: standard.type_name,
|
||||
typeNameEng: standard.type_name_eng || undefined,
|
||||
description: standard.description || undefined,
|
||||
category: standard.category || "input",
|
||||
defaultConfig: (standard.default_config as Record<string, unknown>) || {},
|
||||
validationRules: (standard.validation_rules as Record<string, unknown>) || undefined,
|
||||
defaultStyle: (standard.default_style as Record<string, unknown>) || undefined,
|
||||
inputProperties: (standard.input_properties as Record<string, unknown>) || undefined,
|
||||
componentName: standard.component_name || undefined,
|
||||
configPanel: standard.config_panel || undefined,
|
||||
sortOrder: standard.sort_order || 0,
|
||||
isActive: standard.is_active === "Y",
|
||||
});
|
||||
|
||||
/**
|
||||
* ColumnTypeInfo를 UnifiedColumnInfo로 변환
|
||||
*/
|
||||
export const mapColumnTypeInfoToUnified = (columnInfo: ColumnTypeInfo): UnifiedColumnInfo => ({
|
||||
tableName: columnInfo.tableName || "",
|
||||
columnName: columnInfo.columnName,
|
||||
displayName: columnInfo.displayName,
|
||||
dataType: columnInfo.dataType,
|
||||
dbType: columnInfo.dbType,
|
||||
webType: columnInfo.webType,
|
||||
inputType: columnInfo.inputType || "direct",
|
||||
detailSettings: columnInfo.detailSettings ? JSON.parse(columnInfo.detailSettings) : undefined,
|
||||
description: columnInfo.description,
|
||||
isNullable: columnInfo.isNullable === "Y",
|
||||
isPrimaryKey: columnInfo.isPrimaryKey,
|
||||
defaultValue: columnInfo.defaultValue,
|
||||
maxLength: columnInfo.maxLength,
|
||||
numericPrecision: columnInfo.numericPrecision,
|
||||
numericScale: columnInfo.numericScale,
|
||||
isVisible: columnInfo.isVisible,
|
||||
displayOrder: columnInfo.displayOrder,
|
||||
codeCategory: columnInfo.codeCategory,
|
||||
codeValue: columnInfo.codeValue,
|
||||
referenceTable: columnInfo.referenceTable,
|
||||
referenceColumn: columnInfo.referenceColumn,
|
||||
displayColumn: columnInfo.displayColumn,
|
||||
});
|
||||
|
||||
/**
|
||||
* UnifiedColumnInfo를 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 isReferenceWebType = (webType: string): boolean => {
|
||||
return ["code", "entity"].includes(webType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 웹타입이 숫자 타입인지 확인
|
||||
*/
|
||||
export const isNumericWebType = (webType: string): boolean => {
|
||||
return ["number", "decimal"].includes(webType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 웹타입이 날짜 타입인지 확인
|
||||
*/
|
||||
export const isDateWebType = (webType: string): boolean => {
|
||||
return ["date", "datetime"].includes(webType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 웹타입이 선택 타입인지 확인
|
||||
*/
|
||||
export const isSelectWebType = (webType: string): boolean => {
|
||||
return ["select", "dropdown", "radio", "checkbox", "boolean"].includes(webType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 컬럼이 필수 필드인지 확인
|
||||
*/
|
||||
export const isRequiredColumn = (column: UnifiedColumnInfo): boolean => {
|
||||
return !column.isNullable || column.isPrimaryKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* 컬럼이 시스템 컬럼인지 확인
|
||||
*/
|
||||
export const isSystemColumn = (columnName: string): boolean => {
|
||||
const systemColumns = [
|
||||
"created_date",
|
||||
"updated_date",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"is_active",
|
||||
"company_code",
|
||||
"version",
|
||||
"id",
|
||||
];
|
||||
return systemColumns.includes(columnName.toLowerCase());
|
||||
};
|
||||
355
frontend/types/unified-core.ts
Normal file
355
frontend/types/unified-core.ts
Normal file
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* 🎯 통합 핵심 타입 정의
|
||||
*
|
||||
* 모든 시스템에서 공통으로 사용하는 핵심 타입들을 중앙집중식으로 관리합니다.
|
||||
* - 화면관리 시스템
|
||||
* - 제어관리 시스템
|
||||
* - 테이블 타입관리 시스템
|
||||
*/
|
||||
|
||||
// ===== 핵심 공통 타입들 =====
|
||||
|
||||
/**
|
||||
* 통합 WebType 정의
|
||||
* 모든 시스템에서 사용하는 웹 입력 타입의 표준 정의
|
||||
*/
|
||||
export type WebType =
|
||||
// 기본 텍스트 입력
|
||||
| "text"
|
||||
| "textarea"
|
||||
| "email"
|
||||
| "tel"
|
||||
| "url"
|
||||
// 숫자 입력
|
||||
| "number"
|
||||
| "decimal"
|
||||
// 날짜/시간 입력
|
||||
| "date"
|
||||
| "datetime"
|
||||
// 선택 입력
|
||||
| "select"
|
||||
| "dropdown"
|
||||
| "radio"
|
||||
| "checkbox"
|
||||
| "boolean"
|
||||
// 특수 입력
|
||||
| "code" // 공통코드 참조
|
||||
| "entity" // 엔티티 참조
|
||||
| "file" // 파일 업로드
|
||||
| "button"; // 버튼 컴포넌트
|
||||
|
||||
/**
|
||||
* 동적 WebType 지원
|
||||
* DB에서 동적으로 로드되는 웹타입도 지원
|
||||
*/
|
||||
export type DynamicWebType = WebType | string;
|
||||
|
||||
/**
|
||||
* 통합 ButtonActionType 정의
|
||||
* 모든 버튼 액션 타입의 표준 정의
|
||||
*/
|
||||
export type ButtonActionType =
|
||||
// 데이터 조작
|
||||
| "save"
|
||||
| "cancel"
|
||||
| "delete"
|
||||
| "edit"
|
||||
| "add"
|
||||
// 검색 및 초기화
|
||||
| "search"
|
||||
| "reset"
|
||||
| "submit"
|
||||
// UI 제어
|
||||
| "close"
|
||||
| "popup"
|
||||
| "modal"
|
||||
// 네비게이션
|
||||
| "navigate"
|
||||
| "newWindow"
|
||||
// 제어관리 전용
|
||||
| "control";
|
||||
|
||||
/**
|
||||
* 컴포넌트 타입 정의
|
||||
*/
|
||||
export type ComponentType =
|
||||
| "container"
|
||||
| "row"
|
||||
| "column"
|
||||
| "widget"
|
||||
| "group"
|
||||
| "datatable"
|
||||
| "file"
|
||||
| "area"
|
||||
| "layout";
|
||||
|
||||
/**
|
||||
* 기본 위치 정보
|
||||
*/
|
||||
export interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
z?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 기본 크기 정보
|
||||
*/
|
||||
export interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 공통 스타일 속성
|
||||
*/
|
||||
export interface CommonStyle {
|
||||
// 여백
|
||||
margin?: string;
|
||||
marginTop?: string;
|
||||
marginRight?: string;
|
||||
marginBottom?: string;
|
||||
marginLeft?: string;
|
||||
padding?: string;
|
||||
paddingTop?: string;
|
||||
paddingRight?: string;
|
||||
paddingBottom?: string;
|
||||
paddingLeft?: string;
|
||||
|
||||
// 테두리
|
||||
border?: string;
|
||||
borderWidth?: string;
|
||||
borderStyle?: string;
|
||||
borderColor?: string;
|
||||
borderRadius?: string;
|
||||
|
||||
// 배경
|
||||
backgroundColor?: string;
|
||||
backgroundImage?: string;
|
||||
|
||||
// 텍스트
|
||||
color?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: string;
|
||||
fontFamily?: string;
|
||||
textAlign?: "left" | "center" | "right" | "justify";
|
||||
lineHeight?: string;
|
||||
|
||||
// 라벨 스타일
|
||||
labelFontSize?: string;
|
||||
labelColor?: string;
|
||||
labelFontWeight?: string;
|
||||
labelMarginBottom?: string;
|
||||
|
||||
// 레이아웃
|
||||
display?: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
minWidth?: string;
|
||||
minHeight?: string;
|
||||
maxWidth?: string;
|
||||
maxHeight?: string;
|
||||
|
||||
// 기타
|
||||
opacity?: string;
|
||||
zIndex?: string;
|
||||
overflow?: "visible" | "hidden" | "scroll" | "auto";
|
||||
}
|
||||
|
||||
/**
|
||||
* 검증 규칙
|
||||
*/
|
||||
export interface ValidationRule {
|
||||
type: "required" | "minLength" | "maxLength" | "pattern" | "min" | "max" | "email" | "url";
|
||||
value?: unknown;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 조건 연산자
|
||||
*/
|
||||
export type ConditionOperator =
|
||||
| "="
|
||||
| "!="
|
||||
| ">"
|
||||
| "<"
|
||||
| ">="
|
||||
| "<="
|
||||
| "LIKE"
|
||||
| "IN"
|
||||
| "NOT IN"
|
||||
| "IS NULL"
|
||||
| "IS NOT NULL";
|
||||
|
||||
/**
|
||||
* 기본 API 응답 형태
|
||||
*/
|
||||
export interface BaseApiResponse<T = unknown> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
message?: string;
|
||||
error?: {
|
||||
code: string;
|
||||
message: string;
|
||||
details?: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지네이션 응답
|
||||
*/
|
||||
export interface PaginatedResponse<T> {
|
||||
data: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
size: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회사 코드 (모든 데이터에 공통)
|
||||
*/
|
||||
export type CompanyCode = string;
|
||||
|
||||
/**
|
||||
* 활성 상태 (DB의 Y/N을 boolean으로 변환)
|
||||
*/
|
||||
export type ActiveStatus = "Y" | "N";
|
||||
|
||||
/**
|
||||
* boolean으로 변환하는 유틸리티 타입
|
||||
*/
|
||||
export type BooleanFromYN<T extends ActiveStatus> = T extends "Y" ? true : false;
|
||||
|
||||
// ===== 공통 유틸리티 타입들 =====
|
||||
|
||||
/**
|
||||
* 선택적 ID (신규 생성 시에는 없고, 기존 데이터는 있음)
|
||||
*/
|
||||
export type OptionalId<T> = Omit<T, "id"> & { id?: string | number };
|
||||
|
||||
/**
|
||||
* 타임스탬프 필드
|
||||
*/
|
||||
export interface TimestampFields {
|
||||
createdDate?: Date;
|
||||
updatedDate?: Date;
|
||||
createdBy?: string;
|
||||
updatedBy?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 감사 필드 (DB 표준)
|
||||
*/
|
||||
export interface AuditFields {
|
||||
created_date?: Date;
|
||||
updated_date?: Date;
|
||||
created_by?: string;
|
||||
updated_by?: string;
|
||||
is_active?: ActiveStatus;
|
||||
}
|
||||
|
||||
// ===== 이벤트 타입들 =====
|
||||
|
||||
/**
|
||||
* 웹타입 이벤트
|
||||
*/
|
||||
export interface WebTypeEvent {
|
||||
type: "change" | "blur" | "focus" | "click" | "submit";
|
||||
value: unknown;
|
||||
field?: string;
|
||||
component?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 컴포넌트 이벤트
|
||||
*/
|
||||
export interface ComponentEvent {
|
||||
type: "select" | "drag" | "drop" | "resize" | "delete" | "update";
|
||||
componentId: string;
|
||||
data?: unknown;
|
||||
}
|
||||
|
||||
// ===== 타입 가드용 유틸리티 =====
|
||||
|
||||
/**
|
||||
* 문자열이 WebType인지 확인
|
||||
*/
|
||||
export const isWebType = (value: string): value is WebType => {
|
||||
const webTypes: WebType[] = [
|
||||
"text",
|
||||
"textarea",
|
||||
"email",
|
||||
"tel",
|
||||
"url",
|
||||
"number",
|
||||
"decimal",
|
||||
"date",
|
||||
"datetime",
|
||||
"select",
|
||||
"dropdown",
|
||||
"radio",
|
||||
"checkbox",
|
||||
"boolean",
|
||||
"code",
|
||||
"entity",
|
||||
"file",
|
||||
"button",
|
||||
];
|
||||
return webTypes.includes(value as WebType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 문자열이 ButtonActionType인지 확인
|
||||
*/
|
||||
export const isButtonActionType = (value: string): value is ButtonActionType => {
|
||||
const actionTypes: ButtonActionType[] = [
|
||||
"save",
|
||||
"cancel",
|
||||
"delete",
|
||||
"edit",
|
||||
"add",
|
||||
"search",
|
||||
"reset",
|
||||
"submit",
|
||||
"close",
|
||||
"popup",
|
||||
"modal",
|
||||
"navigate",
|
||||
"newWindow",
|
||||
"control",
|
||||
];
|
||||
return actionTypes.includes(value as ButtonActionType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 문자열이 ComponentType인지 확인
|
||||
*/
|
||||
export const isComponentType = (value: string): value is ComponentType => {
|
||||
const componentTypes: ComponentType[] = [
|
||||
"container",
|
||||
"row",
|
||||
"column",
|
||||
"widget",
|
||||
"group",
|
||||
"datatable",
|
||||
"file",
|
||||
"area",
|
||||
"layout",
|
||||
];
|
||||
return componentTypes.includes(value as ComponentType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Y/N 문자열을 boolean으로 변환
|
||||
*/
|
||||
export const ynToBoolean = (value: ActiveStatus | string | undefined): boolean => {
|
||||
return value === "Y";
|
||||
};
|
||||
|
||||
/**
|
||||
* boolean을 Y/N 문자열로 변환
|
||||
*/
|
||||
export const booleanToYN = (value: boolean): ActiveStatus => {
|
||||
return value ? "Y" : "N";
|
||||
};
|
||||
195
frontend/types/unified-web-types.ts
Normal file
195
frontend/types/unified-web-types.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* 통합 웹 타입 정의
|
||||
* 프론트엔드와 백엔드에서 공통으로 사용하는 웹 타입 정의
|
||||
*
|
||||
* 주의: 이 파일을 수정할 때는 반드시 백엔드 타입도 함께 업데이트 해야 합니다.
|
||||
*/
|
||||
|
||||
// 기본 웹 타입 (DB web_type_standards와 동기화)
|
||||
export type BaseWebType =
|
||||
| "text" // 일반 텍스트
|
||||
| "number" // 숫자 (정수)
|
||||
| "decimal" // 소수점 숫자
|
||||
| "date" // 날짜
|
||||
| "datetime" // 날짜시간
|
||||
| "time" // 시간
|
||||
| "textarea" // 여러줄 텍스트
|
||||
| "select" // 선택박스
|
||||
| "dropdown" // 드롭다운 (select와 동일)
|
||||
| "checkbox" // 체크박스
|
||||
| "radio" // 라디오버튼
|
||||
| "boolean" // 불린값
|
||||
| "file" // 파일 업로드
|
||||
| "email" // 이메일
|
||||
| "tel" // 전화번호
|
||||
| "url" // URL
|
||||
| "password" // 패스워드
|
||||
| "code" // 공통코드 참조
|
||||
| "entity" // 엔티티 참조
|
||||
| "button"; // 버튼
|
||||
|
||||
// 레거시 지원용 (기존 시스템과의 호환성)
|
||||
export type LegacyWebType = "text_area"; // textarea와 동일
|
||||
|
||||
// 전체 웹 타입 (DB 동적 로딩 지원)
|
||||
export type WebType = BaseWebType | LegacyWebType;
|
||||
|
||||
// 동적 웹 타입 (런타임에 DB에서 로드되는 타입 포함)
|
||||
export type DynamicWebType = WebType | string;
|
||||
|
||||
// 웹 타입 카테고리
|
||||
export type WebTypeCategory =
|
||||
| "input" // 입력 컴포넌트
|
||||
| "selection" // 선택 컴포넌트
|
||||
| "display" // 표시 컴포넌트
|
||||
| "action" // 액션 컴포넌트
|
||||
| "upload" // 업로드 컴포넌트
|
||||
| "reference"; // 참조 컴포넌트
|
||||
|
||||
// 웹 타입 정보
|
||||
export interface WebTypeInfo {
|
||||
webType: WebType;
|
||||
typeName: string;
|
||||
typeNameEng?: string;
|
||||
description?: string;
|
||||
category: WebTypeCategory;
|
||||
defaultConfig?: Record<string, any>;
|
||||
validationRules?: Record<string, any>;
|
||||
componentName?: string;
|
||||
configPanel?: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
// 웹 타입 매핑 (레거시 지원)
|
||||
export const WEB_TYPE_MAPPINGS: Record<LegacyWebType, BaseWebType> = {
|
||||
text_area: "textarea",
|
||||
};
|
||||
|
||||
// 웹 타입 정규화 함수
|
||||
export const normalizeWebType = (webType: DynamicWebType): WebType => {
|
||||
if (webType in WEB_TYPE_MAPPINGS) {
|
||||
return WEB_TYPE_MAPPINGS[webType as LegacyWebType];
|
||||
}
|
||||
return webType as WebType;
|
||||
};
|
||||
|
||||
// 웹 타입 검증 함수
|
||||
export const isValidWebType = (webType: string): webType is WebType => {
|
||||
return (
|
||||
[
|
||||
"text",
|
||||
"number",
|
||||
"decimal",
|
||||
"date",
|
||||
"datetime",
|
||||
"time",
|
||||
"textarea",
|
||||
"select",
|
||||
"dropdown",
|
||||
"checkbox",
|
||||
"radio",
|
||||
"boolean",
|
||||
"file",
|
||||
"email",
|
||||
"tel",
|
||||
"url",
|
||||
"password",
|
||||
"code",
|
||||
"entity",
|
||||
"button",
|
||||
"text_area", // 레거시 지원
|
||||
] as string[]
|
||||
).includes(webType);
|
||||
};
|
||||
|
||||
// DB 타입과 웹 타입 매핑
|
||||
export const DB_TYPE_TO_WEB_TYPE: Record<string, WebType> = {
|
||||
// 텍스트 타입
|
||||
"character varying": "text",
|
||||
varchar: "text",
|
||||
text: "textarea",
|
||||
char: "text",
|
||||
|
||||
// 숫자 타입
|
||||
integer: "number",
|
||||
bigint: "number",
|
||||
smallint: "number",
|
||||
serial: "number",
|
||||
bigserial: "number",
|
||||
numeric: "decimal",
|
||||
decimal: "decimal",
|
||||
real: "decimal",
|
||||
"double precision": "decimal",
|
||||
|
||||
// 날짜/시간 타입
|
||||
date: "date",
|
||||
timestamp: "datetime",
|
||||
"timestamp with time zone": "datetime",
|
||||
"timestamp without time zone": "datetime",
|
||||
time: "time",
|
||||
"time with time zone": "time",
|
||||
"time without time zone": "time",
|
||||
|
||||
// 불린 타입
|
||||
boolean: "boolean",
|
||||
|
||||
// JSON 타입 (텍스트로 처리)
|
||||
json: "textarea",
|
||||
jsonb: "textarea",
|
||||
|
||||
// 배열 타입 (텍스트로 처리)
|
||||
ARRAY: "textarea",
|
||||
|
||||
// UUID 타입
|
||||
uuid: "text",
|
||||
};
|
||||
|
||||
// 웹 타입별 기본 설정
|
||||
export const WEB_TYPE_DEFAULT_CONFIGS: Record<WebType, Record<string, any>> = {
|
||||
text: { maxLength: 255, placeholder: "텍스트를 입력하세요" },
|
||||
number: { min: 0, max: 2147483647, step: 1 },
|
||||
decimal: { min: 0, step: 0.01, decimalPlaces: 2 },
|
||||
date: { format: "YYYY-MM-DD" },
|
||||
datetime: { format: "YYYY-MM-DD HH:mm:ss", showTime: true },
|
||||
time: { format: "HH:mm:ss" },
|
||||
textarea: { rows: 4, cols: 50, maxLength: 1000 },
|
||||
select: { placeholder: "선택하세요", searchable: false },
|
||||
dropdown: { placeholder: "선택하세요", searchable: true },
|
||||
checkbox: { defaultChecked: false },
|
||||
radio: { inline: false },
|
||||
boolean: { trueValue: true, falseValue: false },
|
||||
file: { multiple: false, preview: true },
|
||||
email: { placeholder: "이메일을 입력하세요" },
|
||||
tel: { placeholder: "전화번호를 입력하세요" },
|
||||
url: { placeholder: "URL을 입력하세요" },
|
||||
password: { placeholder: "비밀번호를 입력하세요" },
|
||||
code: { placeholder: "코드를 선택하세요", searchable: true },
|
||||
entity: { placeholder: "항목을 선택하세요", searchable: true },
|
||||
button: { variant: "default" },
|
||||
text_area: { rows: 4, cols: 50, maxLength: 1000 }, // 레거시 지원
|
||||
};
|
||||
|
||||
// 웹 타입별 검증 규칙
|
||||
export const WEB_TYPE_VALIDATION_RULES: Record<WebType, Record<string, any>> = {
|
||||
text: { type: "string", trim: true },
|
||||
number: { type: "number", integer: true },
|
||||
decimal: { type: "number", float: true },
|
||||
date: { type: "date", format: "YYYY-MM-DD" },
|
||||
datetime: { type: "datetime" },
|
||||
time: { type: "time" },
|
||||
textarea: { type: "string", multiline: true },
|
||||
select: { type: "string", options: true },
|
||||
dropdown: { type: "string", options: true },
|
||||
checkbox: { type: "boolean" },
|
||||
radio: { type: "string", options: true },
|
||||
boolean: { type: "boolean" },
|
||||
file: { type: "file" },
|
||||
email: { type: "email" },
|
||||
tel: { type: "tel" },
|
||||
url: { type: "url" },
|
||||
password: { type: "string", password: true },
|
||||
code: { type: "string", code: true },
|
||||
entity: { type: "string", entity: true },
|
||||
button: { type: "action" },
|
||||
text_area: { type: "string", multiline: true }, // 레거시 지원
|
||||
};
|
||||
Reference in New Issue
Block a user