[agent-pipeline] pipe-20260311052455-y968 round-5

This commit is contained in:
DDD1542 2026-03-11 14:54:47 +09:00
parent f071777131
commit 9125d04345
5 changed files with 436 additions and 433 deletions

View File

@ -1,70 +1,80 @@
"use client";
import React from "react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { ComponentData } from "@/types/screen";
import { ConfigPanelBuilder } from "@/lib/registry/components/common/ConfigPanelBuilder";
import { ConfigSectionDefinition } from "@/lib/registry/components/common/ConfigPanelTypes";
interface AlertConfigPanelProps {
component: ComponentData;
onUpdateProperty: (path: string, value: any) => void;
config?: Record<string, any>;
onChange?: (key: string, value: any) => void;
component?: any;
onUpdateProperty?: (path: string, value: any) => void;
}
export const AlertConfigPanel: React.FC<AlertConfigPanelProps> = ({ component, onUpdateProperty }) => {
const config = component.componentConfig || {};
const sections: ConfigSectionDefinition[] = [
{
id: "content",
title: "콘텐츠",
fields: [
{
key: "title",
label: "제목",
type: "text",
placeholder: "알림 제목을 입력하세요",
},
{
key: "message",
label: "메시지",
type: "textarea",
placeholder: "알림 메시지를 입력하세요",
},
],
},
{
id: "style",
title: "스타일",
fields: [
{
key: "type",
label: "알림 타입",
type: "select",
options: [
{ label: "정보 (Info)", value: "info" },
{ label: "경고 (Warning)", value: "warning" },
{ label: "성공 (Success)", value: "success" },
{ label: "오류 (Error)", value: "error" },
],
},
{
key: "showIcon",
label: "아이콘 표시",
type: "switch",
},
],
},
];
export const AlertConfigPanel: React.FC<AlertConfigPanelProps> = ({
config: directConfig,
onChange: directOnChange,
component,
onUpdateProperty,
}) => {
const config = directConfig || component?.componentConfig || {};
const handleChange = (key: string, value: any) => {
if (directOnChange) {
directOnChange(key, value);
} else if (onUpdateProperty) {
onUpdateProperty(`componentConfig.${key}`, value);
}
};
return (
<div className="space-y-4">
<div>
<Label htmlFor="alert-title"></Label>
<Input
id="alert-title"
value={config.title || "알림 제목"}
onChange={(e) => onUpdateProperty("componentConfig.title", e.target.value)}
placeholder="알림 제목을 입력하세요"
/>
</div>
<div>
<Label htmlFor="alert-message"></Label>
<Textarea
id="alert-message"
value={config.message || "알림 메시지입니다."}
onChange={(e) => onUpdateProperty("componentConfig.message", e.target.value)}
placeholder="알림 메시지를 입력하세요"
rows={3}
/>
</div>
<div>
<Label htmlFor="alert-type"> </Label>
<Select
value={config.type || "info"}
onValueChange={(value) => onUpdateProperty("componentConfig.type", value)}
>
<SelectTrigger>
<SelectValue placeholder="알림 타입 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="info"> (Info)</SelectItem>
<SelectItem value="warning"> (Warning)</SelectItem>
<SelectItem value="success"> (Success)</SelectItem>
<SelectItem value="error"> (Error)</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center space-x-2">
<Switch
id="show-icon"
checked={config.showIcon ?? true}
onCheckedChange={(checked) => onUpdateProperty("componentConfig.showIcon", checked)}
/>
<Label htmlFor="show-icon"> </Label>
</div>
</div>
<ConfigPanelBuilder
config={config}
onChange={handleChange}
sections={sections}
/>
);
};

View File

@ -1,65 +1,79 @@
"use client";
import React from "react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { ComponentData } from "@/types/screen";
import { ConfigPanelBuilder } from "@/lib/registry/components/common/ConfigPanelBuilder";
import { ConfigSectionDefinition } from "@/lib/registry/components/common/ConfigPanelTypes";
interface BadgeConfigPanelProps {
component: ComponentData;
onUpdateProperty: (path: string, value: any) => void;
config?: Record<string, any>;
onChange?: (key: string, value: any) => void;
component?: any;
onUpdateProperty?: (path: string, value: any) => void;
}
export const BadgeConfigPanel: React.FC<BadgeConfigPanelProps> = ({ component, onUpdateProperty }) => {
const config = component.componentConfig || {};
const sections: ConfigSectionDefinition[] = [
{
id: "content",
title: "콘텐츠",
fields: [
{
key: "text",
label: "뱃지 텍스트",
type: "text",
placeholder: "뱃지 텍스트를 입력하세요",
},
],
},
{
id: "style",
title: "스타일",
fields: [
{
key: "variant",
label: "뱃지 스타일",
type: "select",
options: [
{ label: "기본 (Default)", value: "default" },
{ label: "보조 (Secondary)", value: "secondary" },
{ label: "위험 (Destructive)", value: "destructive" },
{ label: "외곽선 (Outline)", value: "outline" },
],
},
{
key: "size",
label: "뱃지 크기",
type: "select",
options: [
{ label: "작음 (Small)", value: "small" },
{ label: "기본 (Default)", value: "default" },
{ label: "큼 (Large)", value: "large" },
],
},
],
},
];
export const BadgeConfigPanel: React.FC<BadgeConfigPanelProps> = ({
config: directConfig,
onChange: directOnChange,
component,
onUpdateProperty,
}) => {
const config = directConfig || component?.componentConfig || {};
const handleChange = (key: string, value: any) => {
if (directOnChange) {
directOnChange(key, value);
} else if (onUpdateProperty) {
onUpdateProperty(`componentConfig.${key}`, value);
}
};
return (
<div className="space-y-4">
<div>
<Label htmlFor="badge-text"> </Label>
<Input
id="badge-text"
value={config.text || "상태"}
onChange={(e) => onUpdateProperty("componentConfig.text", e.target.value)}
placeholder="뱃지 텍스트를 입력하세요"
/>
</div>
<div>
<Label htmlFor="badge-variant"> </Label>
<Select
value={config.variant || "default"}
onValueChange={(value) => onUpdateProperty("componentConfig.variant", value)}
>
<SelectTrigger>
<SelectValue placeholder="뱃지 스타일 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="default"> (Default)</SelectItem>
<SelectItem value="secondary"> (Secondary)</SelectItem>
<SelectItem value="destructive"> (Destructive)</SelectItem>
<SelectItem value="outline"> (Outline)</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="badge-size"> </Label>
<Select
value={config.size || "default"}
onValueChange={(value) => onUpdateProperty("componentConfig.size", value)}
>
<SelectTrigger>
<SelectValue placeholder="뱃지 크기 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="small"> (Small)</SelectItem>
<SelectItem value="default"> (Default)</SelectItem>
<SelectItem value="large"> (Large)</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<ConfigPanelBuilder
config={config}
onChange={handleChange}
sections={sections}
/>
);
};

View File

@ -1,119 +1,108 @@
"use client";
import React from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { ComponentData } from "@/types/screen";
import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent";
import { ConfigPanelBuilder } from "@/lib/registry/components/common/ConfigPanelBuilder";
import { ConfigSectionDefinition } from "@/lib/registry/components/common/ConfigPanelTypes";
interface CardConfigPanelProps {
component: ComponentData;
onUpdateProperty: (path: string, value: any) => void;
config?: Record<string, any>;
onChange?: (key: string, value: any) => void;
component?: any;
onUpdateProperty?: (path: string, value: any) => void;
}
export const CardConfigPanel: React.FC<CardConfigPanelProps> = ({ component, onUpdateProperty }) => {
const config = component.componentConfig || {};
const sections: ConfigSectionDefinition[] = [
{
id: "content",
title: "콘텐츠",
fields: [
{
key: "title",
label: "카드 제목",
type: "text",
placeholder: "카드 제목을 입력하세요",
},
{
key: "content",
label: "카드 내용",
type: "textarea",
placeholder: "카드 내용을 입력하세요",
},
],
},
{
id: "style",
title: "스타일",
fields: [
{
key: "variant",
label: "카드 스타일",
type: "select",
options: [
{ label: "기본 (Default)", value: "default" },
{ label: "테두리 (Outlined)", value: "outlined" },
{ label: "그림자 (Elevated)", value: "elevated" },
{ label: "채움 (Filled)", value: "filled" },
],
},
{
key: "padding",
label: "패딩",
type: "select",
options: [
{ label: "없음 (None)", value: "none" },
{ label: "작게 (Small)", value: "small" },
{ label: "기본 (Default)", value: "default" },
{ label: "크게 (Large)", value: "large" },
],
},
{
key: "backgroundColor",
label: "배경색",
type: "color",
},
{
key: "borderRadius",
label: "테두리 반경",
type: "text",
placeholder: "8px",
},
],
},
{
id: "display",
title: "표시 옵션",
fields: [
{
key: "showHeader",
label: "헤더 표시",
type: "switch",
},
],
},
];
const handleConfigChange = (key: string, value: any) => {
onUpdateProperty(`componentConfig.${key}`, value);
export const CardConfigPanel: React.FC<CardConfigPanelProps> = ({
config: directConfig,
onChange: directOnChange,
component,
onUpdateProperty,
}) => {
const config = directConfig || component?.componentConfig || {};
const handleChange = (key: string, value: any) => {
if (directOnChange) {
directOnChange(key, value);
} else if (onUpdateProperty) {
onUpdateProperty(`componentConfig.${key}`, value);
}
};
return (
<Card>
<CardHeader>
<CardTitle className="text-sm font-medium"> </CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{/* 카드 제목 */}
<div className="space-y-2">
<Label htmlFor="card-title"> </Label>
<Input
id="card-title"
placeholder="카드 제목을 입력하세요"
value={config.title || "카드 제목"}
onChange={(e) => handleConfigChange("title", e.target.value)}
/>
</div>
{/* 카드 내용 */}
<div className="space-y-2">
<Label htmlFor="card-content"> </Label>
<Textarea
id="card-content"
placeholder="카드 내용을 입력하세요"
value={config.content || "카드 내용 영역"}
onChange={(e) => handleConfigChange("content", e.target.value)}
rows={3}
/>
</div>
{/* 카드 스타일 */}
<div className="space-y-2">
<Label htmlFor="card-variant"> </Label>
<Select value={config.variant || "default"} onValueChange={(value) => handleConfigChange("variant", value)}>
<SelectTrigger>
<SelectValue placeholder="카드 스타일 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="default"> (Default)</SelectItem>
<SelectItem value="outlined"> (Outlined)</SelectItem>
<SelectItem value="elevated"> (Elevated)</SelectItem>
<SelectItem value="filled"> (Filled)</SelectItem>
</SelectContent>
</Select>
</div>
{/* 헤더 표시 여부 */}
<div className="flex items-center space-x-2">
<Switch
id="show-header"
checked={config.showHeader !== false}
onCheckedChange={(checked) => handleConfigChange("showHeader", checked)}
/>
<Label htmlFor="show-header"> </Label>
</div>
{/* 패딩 설정 */}
<div className="space-y-2">
<Label htmlFor="card-padding"></Label>
<Select value={config.padding || "default"} onValueChange={(value) => handleConfigChange("padding", value)}>
<SelectTrigger>
<SelectValue placeholder="패딩 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none"> (None)</SelectItem>
<SelectItem value="small"> (Small)</SelectItem>
<SelectItem value="default"> (Default)</SelectItem>
<SelectItem value="large"> (Large)</SelectItem>
</SelectContent>
</Select>
</div>
{/* 배경색 */}
<div className="space-y-2">
<Label htmlFor="background-color"></Label>
<ColorPickerWithTransparent
id="background-color"
value={config.backgroundColor}
onChange={(value) => handleConfigChange("backgroundColor", value)}
defaultColor="#ffffff"
placeholder="#ffffff"
/>
</div>
{/* 테두리 반경 */}
<div className="space-y-2">
<Label htmlFor="border-radius"> </Label>
<Input
id="border-radius"
placeholder="8px"
value={config.borderRadius || "8px"}
onChange={(e) => handleConfigChange("borderRadius", e.target.value)}
/>
</div>
</CardContent>
</Card>
<ConfigPanelBuilder
config={config}
onChange={handleChange}
sections={sections}
/>
);
};

View File

@ -1,150 +1,131 @@
"use client";
import React from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { ComponentData } from "@/types/screen";
import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent";
import { ConfigPanelBuilder } from "@/lib/registry/components/common/ConfigPanelBuilder";
import { ConfigSectionDefinition } from "@/lib/registry/components/common/ConfigPanelTypes";
interface DashboardConfigPanelProps {
component: ComponentData;
onUpdateProperty: (path: string, value: any) => void;
config?: Record<string, any>;
onChange?: (key: string, value: any) => void;
component?: any;
onUpdateProperty?: (path: string, value: any) => void;
}
export const DashboardConfigPanel: React.FC<DashboardConfigPanelProps> = ({ component, onUpdateProperty }) => {
const config = component.componentConfig || {};
const sections: ConfigSectionDefinition[] = [
{
id: "content",
title: "콘텐츠",
fields: [
{
key: "title",
label: "그리드 제목",
type: "text",
placeholder: "그리드 제목을 입력하세요",
},
],
},
{
id: "grid",
title: "그리드 설정",
fields: [
{
key: "rows",
label: "행 개수",
type: "select",
options: [
{ label: "1행", value: "1" },
{ label: "2행", value: "2" },
{ label: "3행", value: "3" },
{ label: "4행", value: "4" },
],
},
{
key: "columns",
label: "열 개수",
type: "select",
options: [
{ label: "1열", value: "1" },
{ label: "2열", value: "2" },
{ label: "3열", value: "3" },
{ label: "4열", value: "4" },
{ label: "6열", value: "6" },
],
},
{
key: "gap",
label: "그리드 간격",
type: "select",
options: [
{ label: "없음 (0px)", value: "none" },
{ label: "작게 (8px)", value: "small" },
{ label: "보통 (16px)", value: "medium" },
{ label: "크게 (24px)", value: "large" },
],
},
{
key: "itemHeight",
label: "아이템 높이",
type: "text",
placeholder: "120px",
},
],
},
{
id: "style",
title: "스타일",
fields: [
{
key: "backgroundColor",
label: "배경색",
type: "color",
},
{
key: "borderRadius",
label: "테두리 반경",
type: "text",
placeholder: "8px",
},
],
},
{
id: "display",
title: "표시 옵션",
fields: [
{
key: "responsive",
label: "반응형 레이아웃",
type: "switch",
},
{
key: "showBorders",
label: "그리드 테두리 표시",
type: "switch",
},
],
},
];
const handleConfigChange = (key: string, value: any) => {
onUpdateProperty(`componentConfig.${key}`, value);
export const DashboardConfigPanel: React.FC<DashboardConfigPanelProps> = ({
config: directConfig,
onChange: directOnChange,
component,
onUpdateProperty,
}) => {
const config = directConfig || component?.componentConfig || {};
const handleChange = (key: string, value: any) => {
if (directOnChange) {
directOnChange(key, value);
} else if (onUpdateProperty) {
onUpdateProperty(`componentConfig.${key}`, value);
}
};
return (
<Card>
<CardHeader>
<CardTitle className="text-sm font-medium"> </CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{/* 그리드 제목 */}
<div className="space-y-2">
<Label htmlFor="grid-title"> </Label>
<Input
id="grid-title"
placeholder="그리드 제목을 입력하세요"
value={config.title || "대시보드 그리드"}
onChange={(e) => handleConfigChange("title", e.target.value)}
/>
</div>
{/* 행 개수 */}
<div className="space-y-2">
<Label htmlFor="grid-rows"> </Label>
<Select
value={String(config.rows || 2)}
onValueChange={(value) => handleConfigChange("rows", parseInt(value))}
>
<SelectTrigger>
<SelectValue placeholder="행 개수 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1</SelectItem>
<SelectItem value="2">2</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="4">4</SelectItem>
</SelectContent>
</Select>
</div>
{/* 열 개수 */}
<div className="space-y-2">
<Label htmlFor="grid-columns"> </Label>
<Select
value={String(config.columns || 3)}
onValueChange={(value) => handleConfigChange("columns", parseInt(value))}
>
<SelectTrigger>
<SelectValue placeholder="열 개수 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1</SelectItem>
<SelectItem value="2">2</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="4">4</SelectItem>
<SelectItem value="6">6</SelectItem>
</SelectContent>
</Select>
</div>
{/* 간격 설정 */}
<div className="space-y-2">
<Label htmlFor="grid-gap"> </Label>
<Select value={config.gap || "medium"} onValueChange={(value) => handleConfigChange("gap", value)}>
<SelectTrigger>
<SelectValue placeholder="간격 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none"> (0px)</SelectItem>
<SelectItem value="small"> (8px)</SelectItem>
<SelectItem value="medium"> (16px)</SelectItem>
<SelectItem value="large"> (24px)</SelectItem>
</SelectContent>
</Select>
</div>
{/* 그리드 아이템 높이 */}
<div className="space-y-2">
<Label htmlFor="item-height"> </Label>
<Input
id="item-height"
placeholder="120px"
value={config.itemHeight || "120px"}
onChange={(e) => handleConfigChange("itemHeight", e.target.value)}
/>
</div>
{/* 반응형 설정 */}
<div className="flex items-center space-x-2">
<Switch
id="responsive"
checked={config.responsive !== false}
onCheckedChange={(checked) => handleConfigChange("responsive", checked)}
/>
<Label htmlFor="responsive"> </Label>
</div>
{/* 테두리 표시 */}
<div className="flex items-center space-x-2">
<Switch
id="show-borders"
checked={config.showBorders !== false}
onCheckedChange={(checked) => handleConfigChange("showBorders", checked)}
/>
<Label htmlFor="show-borders"> </Label>
</div>
{/* 배경색 */}
<div className="space-y-2">
<Label htmlFor="background-color"></Label>
<ColorPickerWithTransparent
id="background-color"
value={config.backgroundColor}
onChange={(value) => handleConfigChange("backgroundColor", value)}
defaultColor="#f8f9fa"
placeholder="#f8f9fa"
/>
</div>
{/* 테두리 반경 */}
<div className="space-y-2">
<Label htmlFor="border-radius"> </Label>
<Input
id="border-radius"
placeholder="8px"
value={config.borderRadius || "8px"}
onChange={(e) => handleConfigChange("borderRadius", e.target.value)}
/>
</div>
</CardContent>
</Card>
<ConfigPanelBuilder
config={config}
onChange={handleChange}
sections={sections}
/>
);
};

View File

@ -1,84 +1,93 @@
"use client";
import React from "react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { ComponentData } from "@/types/screen";
import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent";
import { ConfigPanelBuilder } from "@/lib/registry/components/common/ConfigPanelBuilder";
import { ConfigSectionDefinition } from "@/lib/registry/components/common/ConfigPanelTypes";
interface ProgressBarConfigPanelProps {
component: ComponentData;
onUpdateProperty: (path: string, value: any) => void;
config?: Record<string, any>;
onChange?: (key: string, value: any) => void;
component?: any;
onUpdateProperty?: (path: string, value: any) => void;
}
export const ProgressBarConfigPanel: React.FC<ProgressBarConfigPanelProps> = ({ component, onUpdateProperty }) => {
const config = component.componentConfig || {};
const sections: ConfigSectionDefinition[] = [
{
id: "content",
title: "콘텐츠",
fields: [
{
key: "label",
label: "라벨",
type: "text",
placeholder: "진행률 라벨을 입력하세요",
},
{
key: "value",
label: "현재 값",
type: "number",
min: 0,
placeholder: "현재 값",
},
{
key: "max",
label: "최대 값",
type: "number",
min: 1,
placeholder: "최대 값",
},
],
},
{
id: "style",
title: "스타일",
fields: [
{
key: "color",
label: "진행률 색상",
type: "color",
},
],
},
{
id: "display",
title: "표시 옵션",
fields: [
{
key: "showPercentage",
label: "퍼센트 표시",
type: "switch",
},
{
key: "showValue",
label: "값 표시",
type: "switch",
},
],
},
];
export const ProgressBarConfigPanel: React.FC<ProgressBarConfigPanelProps> = ({
config: directConfig,
onChange: directOnChange,
component,
onUpdateProperty,
}) => {
const config = directConfig || component?.componentConfig || {};
const handleChange = (key: string, value: any) => {
if (directOnChange) {
directOnChange(key, value);
} else if (onUpdateProperty) {
onUpdateProperty(`componentConfig.${key}`, value);
}
};
return (
<div className="space-y-4">
<div>
<Label htmlFor="progress-label"></Label>
<Input
id="progress-label"
value={config.label || "진행률"}
onChange={(e) => onUpdateProperty("componentConfig.label", e.target.value)}
placeholder="진행률 라벨을 입력하세요"
/>
</div>
<div>
<Label htmlFor="progress-value"> </Label>
<Input
id="progress-value"
type="number"
value={config.value || 65}
onChange={(e) => onUpdateProperty("componentConfig.value", parseInt(e.target.value) || 0)}
placeholder="현재 값"
min="0"
/>
</div>
<div>
<Label htmlFor="progress-max"> </Label>
<Input
id="progress-max"
type="number"
value={config.max || 100}
onChange={(e) => onUpdateProperty("componentConfig.max", parseInt(e.target.value) || 100)}
placeholder="최대 값"
min="1"
/>
</div>
<div>
<Label htmlFor="progress-color"> </Label>
<ColorPickerWithTransparent
id="progress-color"
value={config.color}
onChange={(value) => onUpdateProperty("componentConfig.color", value)}
defaultColor="#3b82f6"
placeholder="#3b82f6"
/>
</div>
<div className="flex items-center space-x-2">
<Switch
id="show-percentage"
checked={config.showPercentage ?? true}
onCheckedChange={(checked) => onUpdateProperty("componentConfig.showPercentage", checked)}
/>
<Label htmlFor="show-percentage"> </Label>
</div>
<div className="flex items-center space-x-2">
<Switch
id="show-value"
checked={config.showValue ?? true}
onCheckedChange={(checked) => onUpdateProperty("componentConfig.showValue", checked)}
/>
<Label htmlFor="show-value"> </Label>
</div>
</div>
<ConfigPanelBuilder
config={config}
onChange={handleChange}
sections={sections}
/>
);
};