ERP-node/frontend/lib/registry/utils/createComponentDefinition.ts

371 lines
8.9 KiB
TypeScript
Raw Normal View History

2025-09-11 18:38:28 +09:00
"use client";
import {
ComponentDefinition,
CreateComponentDefinitionOptions,
ComponentValidation,
DEFAULT_COMPONENT_SIZE,
} from "@/types/component";
/**
*
*
* createLayoutDefinition과
*/
export function createComponentDefinition(options: CreateComponentDefinitionOptions): ComponentDefinition {
const {
id,
name,
nameEng,
description,
category,
webType,
component,
renderer,
defaultConfig = {},
defaultSize = DEFAULT_COMPONENT_SIZE,
configPanel,
icon,
previewImage,
tags = [],
version = "1.0.0",
author,
documentation,
validation,
dependencies = [],
} = options;
// 기본 검증
if (!id) {
throw new Error("컴포넌트 ID는 필수입니다");
}
if (!name) {
throw new Error("컴포넌트 이름은 필수입니다");
}
if (!description) {
throw new Error("컴포넌트 설명은 필수입니다");
}
if (!component) {
throw new Error("React 컴포넌트는 필수입니다");
}
// ID 형식 검증 (kebab-case)
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(id)) {
throw new Error("컴포넌트 ID는 kebab-case 형식이어야 합니다 (예: button-primary)");
}
// 기본 태그 자동 추가
const enhancedTags = [
...tags,
category, // 카테고리를 태그로 추가
webType, // 웹타입을 태그로 추가
...name.split(" "), // 이름을 단어별로 분리하여 태그로 추가
]
.filter((tag) => tag && typeof tag === 'string' && tag.trim().length > 0) // undefined, null, 빈 문자열 제거
.filter((tag, index, array) => array.indexOf(tag) === index); // 중복 제거
2025-09-11 18:38:28 +09:00
// 컴포넌트 정의 생성
const definition: ComponentDefinition = {
id,
name,
nameEng,
description,
category,
webType,
component,
renderer,
defaultConfig,
defaultSize,
configPanel,
icon,
previewImage,
tags: enhancedTags,
version,
author,
documentation,
validation,
dependencies,
createdAt: new Date(),
};
// 정의 검증
const validationResult = validateComponentDefinition(definition);
if (!validationResult.isValid) {
throw new Error(`컴포넌트 정의 검증 실패: ${validationResult.errors.join(", ")}`);
}
// 경고사항 출력 (개발 모드에서만) - 로그 제거
2025-09-11 18:38:28 +09:00
return definition;
}
/**
*
*/
export function validateComponentDefinition(definition: ComponentDefinition): {
isValid: boolean;
errors: string[];
warnings: string[];
} {
const errors: string[] = [];
const warnings: string[] = [];
// 필수 필드 검사
if (!definition.id) errors.push("id는 필수입니다");
if (!definition.name) errors.push("name은 필수입니다");
if (!definition.description) errors.push("description은 필수입니다");
if (!definition.category) errors.push("category는 필수입니다");
if (!definition.webType) errors.push("webType은 필수입니다");
if (!definition.component) errors.push("component는 필수입니다");
if (!definition.defaultSize) errors.push("defaultSize는 필수입니다");
// ID 형식 검사
if (definition.id && !/^[a-z0-9]+(-[a-z0-9]+)*$/.test(definition.id)) {
errors.push("id는 kebab-case 형식이어야 합니다 (예: button-primary)");
}
// ID 길이 검사
if (definition.id && definition.id.length > 50) {
errors.push("id는 50자를 초과할 수 없습니다");
}
// 이름 길이 검사
if (definition.name && definition.name.length > 100) {
errors.push("name은 100자를 초과할 수 없습니다");
}
// 설명 길이 검사
if (definition.description && definition.description.length > 500) {
errors.push("description은 500자를 초과할 수 없습니다");
}
// 크기 유효성 검사
if (definition.defaultSize) {
if (definition.defaultSize.width <= 0) {
errors.push("defaultSize.width는 0보다 커야 합니다");
}
if (definition.defaultSize.height <= 0) {
errors.push("defaultSize.height는 0보다 커야 합니다");
}
if (definition.defaultSize.width > 2000) {
warnings.push("defaultSize.width가 매우 큽니다 (2000px 초과)");
}
if (definition.defaultSize.height > 1000) {
warnings.push("defaultSize.height가 매우 큽니다 (1000px 초과)");
}
}
// 태그 검사
if (definition.tags) {
if (definition.tags.length > 20) {
warnings.push("태그가 너무 많습니다 (20개 초과)");
}
definition.tags.forEach((tag) => {
if (tag && typeof tag === 'string' && tag.length > 30) {
2025-09-11 18:38:28 +09:00
warnings.push(`태그가 너무 깁니다: ${tag}`);
}
});
}
// 버전 형식 검사
if (definition.version && !/^\d+\.\d+\.\d+/.test(definition.version)) {
warnings.push("버전 형식은 semantic versioning을 권장합니다 (예: 1.0.0)");
}
// 의존성 검사
if (definition.dependencies && definition.dependencies.length > 10) {
warnings.push("의존성이 너무 많습니다 (10개 초과)");
}
// 권장사항 검사
if (!definition.icon) {
warnings.push("검색 및 식별을 위해 아이콘 설정을 권장합니다");
}
if (!definition.tags || definition.tags.length === 0) {
warnings.push("검색을 위한 태그 설정을 권장합니다");
}
if (!definition.author) {
warnings.push("작성자 정보 설정을 권장합니다");
}
if (!definition.documentation) {
warnings.push("사용법 안내를 위한 문서 URL 설정을 권장합니다");
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
/**
*
*/
export function cloneComponentDefinition(
original: ComponentDefinition,
overrides: Partial<CreateComponentDefinitionOptions>,
): ComponentDefinition {
return createComponentDefinition({
id: original.id,
name: original.name,
nameEng: original.nameEng,
description: original.description,
category: original.category,
webType: original.webType,
component: original.component,
renderer: original.renderer,
defaultConfig: original.defaultConfig,
defaultSize: original.defaultSize,
configPanel: original.configPanel,
icon: original.icon,
previewImage: original.previewImage,
tags: original.tags,
version: original.version,
author: original.author,
documentation: original.documentation,
validation: original.validation,
dependencies: original.dependencies,
...overrides,
});
}
/**
*
*/
export function getDefaultConfigByWebType(webType: string): Record<string, any> {
switch (webType) {
case "text":
return {
placeholder: "텍스트를 입력하세요",
maxLength: 255,
};
case "number":
return {
min: 0,
max: 999999,
step: 1,
};
case "email":
return {
placeholder: "이메일을 입력하세요",
};
case "password":
return {
placeholder: "비밀번호를 입력하세요",
minLength: 8,
};
case "date":
return {
format: "YYYY-MM-DD",
};
case "datetime":
return {
format: "YYYY-MM-DD HH:mm",
};
case "textarea":
return {
placeholder: "내용을 입력하세요",
rows: 3,
maxLength: 1000,
};
case "select":
case "dropdown":
return {
options: [],
placeholder: "선택하세요",
};
case "checkbox":
return {
checked: false,
};
case "radio":
return {
checked: false,
group: "radio-group",
};
case "button":
return {
text: "버튼",
actionType: "button",
variant: "primary",
};
case "file":
return {
multiple: false,
accept: "*/*",
};
case "range":
return {
min: 0,
max: 100,
step: 1,
value: 50,
};
case "color":
return {
value: "#000000",
};
default:
return {};
}
}
/**
*
*/
export function getDefaultIconByCategory(category: string): string {
switch (category) {
case "ui":
return "Square";
case "input":
return "Edit";
case "display":
return "Eye";
case "action":
return "MousePointer";
case "layout":
return "Layout";
case "chart":
return "BarChart";
case "form":
return "FormInput";
case "media":
return "Image";
case "navigation":
return "Menu";
case "feedback":
return "Bell";
case "utility":
return "Settings";
case "container":
return "Package";
case "system":
return "Cpu";
case "admin":
return "Shield";
default:
return "Component";
}
}