/** * ๐Ÿ–ฅ๏ธ ํ™”๋ฉด๊ด€๋ฆฌ ์‹œ์Šคํ…œ ์ „์šฉ ํƒ€์ž… ์ •์˜ * * ํ™”๋ฉด ์„ค๊ณ„, ์ปดํฌ๋„ŒํŠธ ๊ด€๋ฆฌ, ๋ ˆ์ด์•„์›ƒ ๋“ฑ ํ™”๋ฉด๊ด€๋ฆฌ ์‹œ์Šคํ…œ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ํƒ€์ž…๋“ค */ import { ComponentType, WebType, DynamicWebType, Position, Size, CommonStyle, ValidationRule, TimestampFields, CompanyCode, ActiveStatus, isWebType, } from "./unified-core"; import { ColumnSpanPreset } from "@/lib/constants/columnSpans"; import { ResponsiveComponentConfig } from "./responsive"; // ===== ๊ธฐ๋ณธ ์ปดํฌ๋„ŒํŠธ ์ธํ„ฐํŽ˜์ด์Šค ===== /** * ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์˜ ๊ธฐ๋ณธ ์ธํ„ฐํŽ˜์ด์Šค */ export interface BaseComponent { id: string; type: ComponentType; // ๐Ÿ”„ ๋ ˆ๊ฑฐ์‹œ ์œ„์น˜/ํฌ๊ธฐ (๋‹จ๊ณ„์  ์ œ๊ฑฐ ์˜ˆ์ •) position: Position; // y ์ขŒํ‘œ๋Š” ์œ ์ง€ (ํ–‰ ์ •๋ ฌ์šฉ) size: Size; // height๋งŒ ์‚ฌ์šฉ // ๐Ÿ†• ๊ทธ๋ฆฌ๋“œ ์‹œ์Šคํ…œ ์†์„ฑ gridColumnSpan?: ColumnSpanPreset; // ์ปฌ๋Ÿผ ๋„ˆ๋น„ gridColumnStart?: number; // ์‹œ์ž‘ ์ปฌ๋Ÿผ (1-12) gridRowIndex?: number; // ํ–‰ ์ธ๋ฑ์Šค parentId?: string; label?: string; required?: boolean; readonly?: boolean; style?: ComponentStyle; className?: string; // ์ƒˆ ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ์—์„œ ํ•„์š”ํ•œ ์†์„ฑ๋“ค gridColumns?: number; // ๐Ÿ”„ deprecated - gridColumnSpan ์‚ฌ์šฉ zoneId?: string; // ๋ ˆ์ด์•„์›ƒ ์กด ID componentConfig?: any; // ์ปดํฌ๋„ŒํŠธ๋ณ„ ์„ค์ • componentType?: string; // ์ƒˆ ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ์˜ ID webTypeConfig?: WebTypeConfig; // ์›นํƒ€์ž…๋ณ„ ์„ค์ • // ๋ฐ˜์‘ํ˜• ์„ค์ • responsiveConfig?: ResponsiveComponentConfig; responsiveDisplay?: any; // ๋Ÿฐํƒ€์ž„์— ์ถ”๊ฐ€๋˜๋Š” ์ž„์‹œ ํ•„๋“œ } /** * ํ™”๋ฉด๊ด€๋ฆฌ์šฉ ํ™•์žฅ ์Šคํƒ€์ผ (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; arrayConfig?: ArrayTypeConfig; // ๐Ÿ†• ์ž๋™ ์ž…๋ ฅ ์„ค์ • (ํ…Œ์ด๋ธ” ์กฐํšŒ ๊ธฐ๋ฐ˜) autoFill?: { enabled: boolean; // ์ž๋™ ์ž…๋ ฅ ํ™œ์„ฑํ™” sourceTable: string; // ์กฐํšŒํ•  ํ…Œ์ด๋ธ” (์˜ˆ: company_mng) filterColumn: string; // ํ•„ํ„ฐ๋งํ•  ์ปฌ๋Ÿผ (์˜ˆ: company_code) userField: 'companyCode' | 'userId' | 'deptCode'; // ์‚ฌ์šฉ์ž ์ •๋ณด ํ•„๋“œ displayColumn: string; // ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ (์˜ˆ: company_name) }; } /** * ์ปจํ…Œ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ (๋ ˆ์ด์•„์›ƒ) */ 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[]; // ๐Ÿ†• ํ˜„์žฌ ์‚ฌ์šฉ์ž ์ •๋ณด๋กœ ์ž๋™ ํ•„ํ„ฐ๋ง autoFilter?: { enabled: boolean; // ์ž๋™ ํ•„ํ„ฐ ํ™œ์„ฑํ™” ์—ฌ๋ถ€ filterColumn: string; // ํ•„ํ„ฐ๋งํ•  ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ (์˜ˆ: company_code, dept_code) userField: 'companyCode' | 'userId' | 'deptCode'; // ์‚ฌ์šฉ์ž ์ •๋ณด์—์„œ ๊ฐ€์ ธ์˜ฌ ํ•„๋“œ }; } /** * ํŒŒ์ผ ์—…๋กœ๋“œ ์ปดํฌ๋„ŒํŠธ */ export interface FileComponent extends BaseComponent { type: "file"; fileConfig: FileTypeConfig; uploadedFiles?: UploadedFile[]; columnName?: string; tableName?: string; lastFileUpdate?: number; } /** * ํ”Œ๋กœ์šฐ ์Šคํ…๋ณ„ ์ปฌ๋Ÿผ ํ‘œ์‹œ ์„ค์ • */ export interface FlowStepColumnConfig { selectedColumns: string[]; // ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ ๋ชฉ๋ก columnOrder?: string[]; // ์ปฌ๋Ÿผ ์ˆœ์„œ (์„ ํƒ์‚ฌํ•ญ) } /** * ํ”Œ๋กœ์šฐ ์ปดํฌ๋„ŒํŠธ */ export interface FlowComponent extends BaseComponent { type: "flow"; flowId?: number; // ์„ ํƒ๋œ ํ”Œ๋กœ์šฐ ID flowName?: string; // ํ”Œ๋กœ์šฐ ์ด๋ฆ„ (ํ‘œ์‹œ์šฉ) showStepCount?: boolean; // ๊ฐ ์Šคํ…์˜ ๋ฐ์ดํ„ฐ ๊ฑด์ˆ˜ ํ‘œ์‹œ ์—ฌ๋ถ€ allowDataMove?: boolean; // ๋ฐ์ดํ„ฐ ์ด๋™ ํ—ˆ์šฉ ์—ฌ๋ถ€ displayMode?: "horizontal" | "vertical"; // ํ”Œ๋กœ์šฐ ํ‘œ์‹œ ๋ฐฉํ–ฅ // ๐Ÿ†• ๋‹จ๊ณ„๋ณ„ ์ปฌ๋Ÿผ ์˜ค๋ฒ„๋ผ์ด๋“œ ์„ค์ • (ํ™”๋ฉด๊ด€๋ฆฌ์—์„œ ์„ค์ •) stepColumnConfig?: { [stepId: number]: FlowStepColumnConfig; }; } /** * ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ ์ปดํฌ๋„ŒํŠธ */ export interface ComponentComponent extends BaseComponent { type: "component"; widgetType: WebType; // ์›นํƒ€์ž… (๊ธฐ์กด ํ˜ธํ™˜์„ฑ) componentType: string; // ์ƒˆ ์ปดํฌ๋„ŒํŠธ ์‹œ์Šคํ…œ์˜ ID componentConfig: any; // ์ปดํฌ๋„ŒํŠธ๋ณ„ ์„ค์ • } /** * ํ†ตํ•ฉ ์ปดํฌ๋„ŒํŠธ ๋ฐ์ดํ„ฐ ํƒ€์ž… */ export type ComponentData = | WidgetComponent | ContainerComponent | GroupComponent | DataTableComponent | FileComponent | FlowComponent | ComponentComponent; // ===== ์›นํƒ€์ž…๋ณ„ ์„ค์ • ์ธํ„ฐํŽ˜์ด์Šค ===== /** * ๊ธฐ๋ณธ ์›นํƒ€์ž… ์„ค์ • */ 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; defaultValue?: string; multiline?: boolean; rows?: number; // ์ž๋™์ž…๋ ฅ ๊ด€๋ จ ์„ค์ • autoInput?: boolean; autoValueType?: | "current_datetime" | "current_date" | "current_time" | "current_user" | "uuid" | "sequence" | "numbering_rule" | "custom"; customValue?: string; numberingRuleId?: string; // ์ฑ„๋ฒˆ ๊ทœ์น™ ID } /** * ๋ฐฐ์—ด(๋‹ค์ค‘ ์ž…๋ ฅ) ํƒ€์ž… ์„ค์ • */ export interface ArrayTypeConfig { itemType?: "text" | "number" | "email" | "tel"; // ๊ฐ ํ•ญ๋ชฉ์˜ ์ž…๋ ฅ ํƒ€์ž… minItems?: number; // ์ตœ์†Œ ํ•ญ๋ชฉ ์ˆ˜ maxItems?: number; // ์ตœ๋Œ€ ํ•ญ๋ชฉ ์ˆ˜ placeholder?: string; // ์ž…๋ ฅ ํ•„๋“œ placeholder addButtonText?: string; // + ๋ฒ„ํŠผ ํ…์ŠคํŠธ removeButtonText?: string; // - ๋ฒ„ํŠผ ํ…์ŠคํŠธ (๋ณดํ†ต ์•„์ด์ฝ˜) allowReorder?: boolean; // ์ˆœ์„œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ showIndex?: boolean; // ์ธ๋ฑ์Šค ๋ฒˆํ˜ธ ํ‘œ์‹œ ์—ฌ๋ถ€ } /** * ํŒŒ์ผ ํƒ€์ž… ์„ค์ • */ export interface FileTypeConfig { accept?: string[]; multiple?: boolean; maxSize?: number; // MB maxFiles?: number; showPreview?: boolean; showProgress?: boolean; docType?: string; docTypeName?: string; dragDropText?: string; uploadButtonText?: string; autoUpload?: boolean; chunkedUpload?: boolean; linkedTable?: string; linkedField?: string; autoLink?: boolean; companyCode?: CompanyCode; } /** * ์—”ํ‹ฐํ‹ฐ ํƒ€์ž… ์„ค์ • */ export interface EntityTypeConfig { referenceTable: string; referenceColumn: string; displayColumns: string[]; // ์—ฌ๋Ÿฌ ํ‘œ์‹œ ์ปฌ๋Ÿผ์„ ๋ฐฐ์—ด๋กœ ๋ณ€๊ฒฝ displayColumn?: string; // ํ•˜์œ„ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด ์œ ์ง€ (deprecated) searchColumns?: string[]; filters?: Record; placeholder?: string; displayFormat?: "simple" | "detailed" | "custom"; // ํ‘œ์‹œ ํ˜•์‹ separator?: 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 FlowVisibilityConfig { /** * ํ”Œ๋กœ์šฐ ๋‹จ๊ณ„๋ณ„ ํ‘œ์‹œ ์ œ์–ด ํ™œ์„ฑํ™” ์—ฌ๋ถ€ */ enabled: boolean; /** * ๋Œ€์ƒ ํ”Œ๋กœ์šฐ ์ปดํฌ๋„ŒํŠธ ID * ํ™”๋ฉด์— ์—ฌ๋Ÿฌ ํ”Œ๋กœ์šฐ ์œ„์ ฏ์ด ์žˆ์„ ๊ฒฝ์šฐ, ์–ด๋–ค ํ”Œ๋กœ์šฐ์— ๋ฐ˜์‘ํ• ์ง€ ์ง€์ • */ targetFlowComponentId: string; /** * ๋Œ€์ƒ ํ”Œ๋กœ์šฐ ์ •์˜ ID (์„ ํƒ์‚ฌํ•ญ, ๊ฒ€์ฆ์šฉ) */ targetFlowId?: number; /** * ๋Œ€์ƒ ํ”Œ๋กœ์šฐ ์ด๋ฆ„ (ํ‘œ์‹œ์šฉ) */ targetFlowName?: string; /** * ํ‘œ์‹œ ์กฐ๊ฑด ๋ชจ๋“œ * - whitelist: visibleSteps์— ํฌํ•จ๋œ ๋‹จ๊ณ„์—์„œ๋งŒ ํ‘œ์‹œ * - blacklist: hiddenSteps์— ํฌํ•จ๋œ ๋‹จ๊ณ„์—์„œ ์ˆจ๊น€ * - all: ๋ชจ๋“  ๋‹จ๊ณ„์—์„œ ํ‘œ์‹œ (๊ธฐ๋ณธ๊ฐ’) */ mode: "whitelist" | "blacklist" | "all"; /** * ํ‘œ์‹œํ•  ๋‹จ๊ณ„ ID ๋ชฉ๋ก (mode="whitelist"์ผ ๋•Œ ์‚ฌ์šฉ) */ visibleSteps?: number[]; /** * ์ˆจ๊ธธ ๋‹จ๊ณ„ ID ๋ชฉ๋ก (mode="blacklist"์ผ ๋•Œ ์‚ฌ์šฉ) */ hiddenSteps?: number[]; /** * ๋ ˆ์ด์•„์›ƒ ๋™์ž‘ ๋ฐฉ์‹ * - preserve-position: ์›๋ž˜ ์œ„์น˜ ์œ ์ง€ (display: none, ๋นˆ ๊ณต๊ฐ„ ์œ ์ง€) * - auto-compact: ๋นˆ ๊ณต๊ฐ„ ์ž๋™ ์ œ๊ฑฐ (Flexbox ๊ทธ๋ฃน์œผ๋กœ ์ž๋™ ์ •๋ ฌ) */ layoutBehavior: "preserve-position" | "auto-compact"; /** * ๊ทธ๋ฃน ID (auto-compact ๋ชจ๋“œ์ผ ๋•Œ ์‚ฌ์šฉ) * ๊ฐ™์€ ๊ทธ๋ฃน ID๋ฅผ ๊ฐ€์ง„ ๋ฒ„ํŠผ๋“ค์ด ํ•˜๋‚˜์˜ FlowButtonGroup์œผ๋กœ ๋ฌถ์ž„ */ groupId?: string; /** * ๊ทธ๋ฃน ์ •๋ ฌ ๋ฐฉํ–ฅ (auto-compact ๋ชจ๋“œ์ผ ๋•Œ ์‚ฌ์šฉ) */ groupDirection?: "horizontal" | "vertical"; /** * ๊ทธ๋ฃน ๋‚ด ๋ฒ„ํŠผ ๊ฐ„๊ฒฉ (px, auto-compact ๋ชจ๋“œ์ผ ๋•Œ ์‚ฌ์šฉ) */ groupGap?: number; /** * ๊ทธ๋ฃน ์ •๋ ฌ ๋ฐฉ์‹ (auto-compact ๋ชจ๋“œ์ผ ๋•Œ ์‚ฌ์šฉ) */ groupAlign?: "start" | "center" | "end" | "space-between" | "space-around"; } // ===== ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ๊ด€๋ จ ===== /** * ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ */ 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; targetObjid: string; parentTargetObjid?: 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; dbSourceType?: "internal" | "external"; dbConnectionId?: number; } /** * ํ™”๋ฉด ์ƒ์„ฑ ์š”์ฒญ */ export interface CreateScreenRequest { screenName: string; screenCode?: string; tableName: string; tableLabel?: string; companyCode: CompanyCode; description?: string; dbSourceType?: "internal" | "external"; dbConnectionId?: number; } /** * ํ™”๋ฉด ์ˆ˜์ • ์š”์ฒญ */ 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; // gridUtils์—์„œ ํ•„์š”ํ•œ ์†์„ฑ๋“ค ์ถ”๊ฐ€ columns: number; gap: number; padding: number; showGrid?: boolean; gridColor?: string; gridOpacity?: number; } /** * ๋ ˆ์ด์•„์›ƒ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ */ 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; children?: Array<{ id: string; name: string; defaultProps: Partial; }>; } // ===== ํƒ€์ž… ๊ฐ€๋“œ ํ•จ์ˆ˜๋“ค ===== /** * 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"; }; /** * FlowComponent ํƒ€์ž… ๊ฐ€๋“œ */ export const isFlowComponent = (component: ComponentData): component is FlowComponent => { return component.type === "flow"; }; // ===== ์•ˆ์ „ํ•œ ํƒ€์ž… ์บ์ŠคํŒ… ์œ ํ‹ธ๋ฆฌํ‹ฐ ===== /** * 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; }; /** * ComponentData๋ฅผ FlowComponent๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์บ์ŠคํŒ… */ export const asFlowComponent = (component: ComponentData): FlowComponent => { if (!isFlowComponent(component)) { throw new Error(`Expected FlowComponent, got ${component.type}`); } return component; };