[agent-pipeline] pipe-20260311071246-rhvz round-6

This commit is contained in:
DDD1542 2026-03-11 16:42:06 +09:00
parent ca001408f8
commit f3eca6b02c
3 changed files with 470 additions and 509 deletions

View File

@ -9,7 +9,6 @@ import React, { useState, useEffect } from "react";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { tableTypeApi } from "@/lib/api/screen"; import { tableTypeApi } from "@/lib/api/screen";
@ -32,23 +31,18 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
config, config,
onChange, onChange,
}) => { }) => {
// 테이블 목록
const [tables, setTables] = useState<TableOption[]>([]); const [tables, setTables] = useState<TableOption[]>([]);
const [loadingTables, setLoadingTables] = useState(false); const [loadingTables, setLoadingTables] = useState(false);
// 컬럼 목록 (소스/대상/관련 테이블용)
const [sourceColumns, setSourceColumns] = useState<ColumnOption[]>([]); const [sourceColumns, setSourceColumns] = useState<ColumnOption[]>([]);
const [targetColumns, setTargetColumns] = useState<ColumnOption[]>([]); const [targetColumns, setTargetColumns] = useState<ColumnOption[]>([]);
const [relatedColumns, setRelatedColumns] = useState<ColumnOption[]>([]); const [relatedColumns, setRelatedColumns] = useState<ColumnOption[]>([]);
const [categoryColumns, setCategoryColumns] = useState<ColumnOption[]>([]); const [categoryColumns, setCategoryColumns] = useState<ColumnOption[]>([]);
const [loadingColumns, setLoadingColumns] = useState(false); const [loadingColumns, setLoadingColumns] = useState(false);
// 설정 업데이트 핸들러
const updateConfig = (field: string, value: any) => { const updateConfig = (field: string, value: any) => {
onChange({ ...config, [field]: value }); onChange({ ...config, [field]: value });
}; };
// 테이블 목록 로드
useEffect(() => { useEffect(() => {
const loadTables = async () => { const loadTables = async () => {
setLoadingTables(true); setLoadingTables(true);
@ -67,13 +61,9 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
loadTables(); loadTables();
}, []); }, []);
// 소스 테이블 선택 시 컬럼 목록 로드
useEffect(() => { useEffect(() => {
const loadColumns = async () => { const loadColumns = async () => {
if (!config.sourceTable) { if (!config.sourceTable) { setSourceColumns([]); return; }
setSourceColumns([]);
return;
}
try { try {
const data = await tableTypeApi.getColumns(config.sourceTable); const data = await tableTypeApi.getColumns(config.sourceTable);
setSourceColumns(data.map((c: any) => ({ setSourceColumns(data.map((c: any) => ({
@ -87,13 +77,9 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
loadColumns(); loadColumns();
}, [config.sourceTable]); }, [config.sourceTable]);
// 대상 테이블 선택 시 컬럼 목록 로드
useEffect(() => { useEffect(() => {
const loadColumns = async () => { const loadColumns = async () => {
if (!config.targetTable) { if (!config.targetTable) { setTargetColumns([]); return; }
setTargetColumns([]);
return;
}
try { try {
const data = await tableTypeApi.getColumns(config.targetTable); const data = await tableTypeApi.getColumns(config.targetTable);
setTargetColumns(data.map((c: any) => ({ setTargetColumns(data.map((c: any) => ({
@ -107,13 +93,9 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
loadColumns(); loadColumns();
}, [config.targetTable]); }, [config.targetTable]);
// 관련 테이블 선택 시 컬럼 목록 로드
useEffect(() => { useEffect(() => {
const loadColumns = async () => { const loadColumns = async () => {
if (!config.relatedTable) { if (!config.relatedTable) { setRelatedColumns([]); return; }
setRelatedColumns([]);
return;
}
try { try {
const data = await tableTypeApi.getColumns(config.relatedTable); const data = await tableTypeApi.getColumns(config.relatedTable);
setRelatedColumns(data.map((c: any) => ({ setRelatedColumns(data.map((c: any) => ({
@ -127,13 +109,9 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
loadColumns(); loadColumns();
}, [config.relatedTable]); }, [config.relatedTable]);
// 카테고리 테이블 선택 시 컬럼 목록 로드
useEffect(() => { useEffect(() => {
const loadColumns = async () => { const loadColumns = async () => {
if (!config.tableName) { if (!config.tableName) { setCategoryColumns([]); return; }
setCategoryColumns([]);
return;
}
setLoadingColumns(true); setLoadingColumns(true);
try { try {
const data = await tableTypeApi.getColumns(config.tableName); const data = await tableTypeApi.getColumns(config.tableName);
@ -151,74 +129,73 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
}, [config.tableName]); }, [config.tableName]);
return ( return (
<div className="space-y-4"> <div className="space-y-1">
{/* 비즈니스 타입 */} {/* BUSINESS TYPE 섹션 */}
<div className="space-y-2"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">BUSINESS TYPE</h4>
<Select <div className="flex items-center justify-between py-1.5">
value={config.bizType || config.type || "flow"} <span className="text-xs text-muted-foreground"> </span>
onValueChange={(value) => updateConfig("bizType", value)} <div className="w-[140px]">
> <Select
<SelectTrigger className="h-8 text-xs"> value={config.bizType || config.type || "flow"}
<SelectValue placeholder="타입 선택" /> onValueChange={(value) => updateConfig("bizType", value)}
</SelectTrigger> >
<SelectContent> <SelectTrigger className="h-7 text-xs">
<SelectItem value="flow"></SelectItem> <SelectValue placeholder="타입 선택" />
<SelectItem value="rack"> </SelectItem> </SelectTrigger>
<SelectItem value="map"></SelectItem> <SelectContent>
<SelectItem value="numbering"> </SelectItem> <SelectItem value="flow"></SelectItem>
<SelectItem value="category"></SelectItem> <SelectItem value="rack"> </SelectItem>
<SelectItem value="data-mapping"> </SelectItem> <SelectItem value="map"></SelectItem>
<SelectItem value="related-data"> </SelectItem> <SelectItem value="numbering"> </SelectItem>
</SelectContent> <SelectItem value="category"></SelectItem>
</Select> <SelectItem value="data-mapping"> </SelectItem>
<SelectItem value="related-data"> </SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div> </div>
<Separator /> {/* FLOW SETTINGS 섹션 */}
{/* 플로우 설정 */}
{config.bizType === "flow" && ( {config.bizType === "flow" && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">FLOW SETTINGS</h4>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"> ID</span>
<Label className="text-[10px] text-muted-foreground"> ID</Label> <div className="w-[140px]">
<Input <Input
type="number" type="number"
value={config.flowId || ""} value={config.flowId || ""}
onChange={(e) => updateConfig("flowId", e.target.value ? Number(e.target.value) : undefined)} onChange={(e) => updateConfig("flowId", e.target.value ? Number(e.target.value) : undefined)}
placeholder="플로우 ID" placeholder="플로우 ID"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="editable"
checked={config.editable || false} checked={config.editable || false}
onCheckedChange={(checked) => updateConfig("editable", checked)} onCheckedChange={(checked) => updateConfig("editable", checked)}
/> />
<label htmlFor="editable" className="text-xs"> </label>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="showMinimap"
checked={config.showMinimap || false} checked={config.showMinimap || false}
onCheckedChange={(checked) => updateConfig("showMinimap", checked)} onCheckedChange={(checked) => updateConfig("showMinimap", checked)}
/> />
<label htmlFor="showMinimap" className="text-xs"> </label>
</div> </div>
</div> </div>
)} )}
{/* 랙 구조 설정 */} {/* RACK SETTINGS 섹션 */}
{config.bizType === "rack" && ( {config.bizType === "rack" && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">RACK SETTINGS</h4>
<div className="flex gap-2">
<div className="grid grid-cols-2 gap-2"> <div className="flex-1">
<div className="space-y-2">
<Label className="text-[10px] text-muted-foreground"> </Label> <Label className="text-[10px] text-muted-foreground"> </Label>
<Input <Input
type="number" type="number"
@ -226,10 +203,10 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
onChange={(e) => updateConfig("rows", e.target.value ? Number(e.target.value) : undefined)} onChange={(e) => updateConfig("rows", e.target.value ? Number(e.target.value) : undefined)}
placeholder="5" placeholder="5"
min="1" min="1"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div> </div>
<div className="space-y-2"> <div className="flex-1">
<Label className="text-[10px] text-muted-foreground"> </Label> <Label className="text-[10px] text-muted-foreground"> </Label>
<Input <Input
type="number" type="number"
@ -237,215 +214,220 @@ export const V2BizConfigPanel: React.FC<V2BizConfigPanelProps> = ({
onChange={(e) => updateConfig("columns", e.target.value ? Number(e.target.value) : undefined)} onChange={(e) => updateConfig("columns", e.target.value ? Number(e.target.value) : undefined)}
placeholder="10" placeholder="10"
min="1" min="1"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div> </div>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="showLabels"
checked={config.showLabels !== false} checked={config.showLabels !== false}
onCheckedChange={(checked) => updateConfig("showLabels", checked)} onCheckedChange={(checked) => updateConfig("showLabels", checked)}
/> />
<label htmlFor="showLabels" className="text-xs"> </label>
</div> </div>
</div> </div>
)} )}
{/* 채번 규칙 설정 */} {/* NUMBERING SETTINGS 섹션 */}
{config.bizType === "numbering" && ( {config.bizType === "numbering" && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">NUMBERING SETTINGS</h4>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"> ID</span>
<Label className="text-[10px] text-muted-foreground"> ID</Label> <div className="w-[140px]">
<Input <Input
type="number" type="number"
value={config.ruleId || ""} value={config.ruleId || ""}
onChange={(e) => updateConfig("ruleId", e.target.value ? Number(e.target.value) : undefined)} onChange={(e) => updateConfig("ruleId", e.target.value ? Number(e.target.value) : undefined)}
placeholder="규칙 ID" placeholder="규칙 ID"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"></span>
<Label className="text-[10px] text-muted-foreground"></Label> <div className="w-[140px]">
<Input <Input
value={config.prefix || ""} value={config.prefix || ""}
onChange={(e) => updateConfig("prefix", e.target.value)} onChange={(e) => updateConfig("prefix", e.target.value)}
placeholder="예: INV-" placeholder="예: INV-"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="autoGenerate"
checked={config.autoGenerate !== false} checked={config.autoGenerate !== false}
onCheckedChange={(checked) => updateConfig("autoGenerate", checked)} onCheckedChange={(checked) => updateConfig("autoGenerate", checked)}
/> />
<label htmlFor="autoGenerate" className="text-xs"> </label>
</div> </div>
</div> </div>
)} )}
{/* 카테고리 설정 */} {/* CATEGORY SETTINGS 섹션 */}
{config.bizType === "category" && ( {config.bizType === "category" && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">CATEGORY SETTINGS</h4>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"> </span>
<Label className="text-[10px] text-muted-foreground"> </Label> <div className="w-[140px]">
<Select <Select
value={config.tableName || ""} value={config.tableName || ""}
onValueChange={(value) => { onValueChange={(value) => {
updateConfig("tableName", value); updateConfig("tableName", value);
updateConfig("columnName", ""); updateConfig("columnName", "");
}} }}
disabled={loadingTables} disabled={loadingTables}
> >
<SelectTrigger className="h-8 text-xs"> <SelectTrigger className="h-7 text-xs">
<SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} /> <SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{tables.map((table) => ( {tables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}> <SelectItem key={table.tableName} value={table.tableName}>
{table.displayName} {table.displayName}
</SelectItem> </SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
</div>
</div> </div>
{config.tableName && ( {config.tableName && (
<div className="space-y-2"> <div className="flex items-center justify-between py-1.5">
<Label className="text-[10px] text-muted-foreground"></Label> <span className="text-xs text-muted-foreground"></span>
<Select <div className="w-[140px]">
value={config.columnName || ""} <Select
onValueChange={(value) => updateConfig("columnName", value)} value={config.columnName || ""}
disabled={loadingColumns} onValueChange={(value) => updateConfig("columnName", value)}
> disabled={loadingColumns}
<SelectTrigger className="h-8 text-xs"> >
<SelectValue placeholder={loadingColumns ? "로딩 중..." : "컬럼 선택"} /> <SelectTrigger className="h-7 text-xs">
</SelectTrigger> <SelectValue placeholder={loadingColumns ? "로딩 중..." : "컬럼 선택"} />
<SelectContent> </SelectTrigger>
{categoryColumns.map((col) => ( <SelectContent>
<SelectItem key={col.columnName} value={col.columnName}> {categoryColumns.map((col) => (
{col.displayName} <SelectItem key={col.columnName} value={col.columnName}>
</SelectItem> {col.displayName}
))} </SelectItem>
</SelectContent> ))}
</Select> </SelectContent>
</Select>
</div>
</div> </div>
)} )}
</div> </div>
)} )}
{/* 데이터 매핑 설정 */} {/* DATA MAPPING 섹션 */}
{config.bizType === "data-mapping" && ( {config.bizType === "data-mapping" && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">DATA MAPPING</h4>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"> </span>
<Label className="text-[10px] text-muted-foreground"> </Label> <div className="w-[140px]">
<Select
value={config.sourceTable || ""}
onValueChange={(value) => updateConfig("sourceTable", value)}
disabled={loadingTables}
>
<SelectTrigger className="h-8 text-xs">
<SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} />
</SelectTrigger>
<SelectContent>
{tables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}>
{table.displayName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label className="text-[10px] text-muted-foreground"> </Label>
<Select
value={config.targetTable || ""}
onValueChange={(value) => updateConfig("targetTable", value)}
disabled={loadingTables}
>
<SelectTrigger className="h-8 text-xs">
<SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} />
</SelectTrigger>
<SelectContent>
{tables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}>
{table.displayName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
)}
{/* 관련 데이터 설정 */}
{config.bizType === "related-data" && (
<div className="space-y-3">
<Label className="text-xs font-medium"> </Label>
<div className="space-y-2">
<Label className="text-[10px] text-muted-foreground"> </Label>
<Select
value={config.relatedTable || ""}
onValueChange={(value) => {
updateConfig("relatedTable", value);
updateConfig("linkColumn", "");
}}
disabled={loadingTables}
>
<SelectTrigger className="h-8 text-xs">
<SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} />
</SelectTrigger>
<SelectContent>
{tables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}>
{table.displayName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{config.relatedTable && (
<div className="space-y-2">
<Label className="text-[10px] text-muted-foreground"> </Label>
<Select <Select
value={config.linkColumn || ""} value={config.sourceTable || ""}
onValueChange={(value) => updateConfig("linkColumn", value)} onValueChange={(value) => updateConfig("sourceTable", value)}
disabled={loadingTables}
> >
<SelectTrigger className="h-8 text-xs"> <SelectTrigger className="h-7 text-xs">
<SelectValue placeholder="컬럼 선택" /> <SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{relatedColumns.map((col) => ( {tables.map((table) => (
<SelectItem key={col.columnName} value={col.columnName}> <SelectItem key={table.tableName} value={table.tableName}>
{col.displayName} {table.displayName}
</SelectItem> </SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
)} </div>
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> </span>
<div className="w-[140px]">
<Select
value={config.targetTable || ""}
onValueChange={(value) => updateConfig("targetTable", value)}
disabled={loadingTables}
>
<SelectTrigger className="h-7 text-xs">
<SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} />
</SelectTrigger>
<SelectContent>
{tables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}>
{table.displayName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</div>
)}
<div className="space-y-2"> {/* RELATED DATA 섹션 */}
<Label className="text-[10px] text-muted-foreground"> </Label> {config.bizType === "related-data" && (
<Input <div className="border-b border-border/50 pb-3 mb-3">
value={config.buttonText || ""} <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">RELATED DATA</h4>
onChange={(e) => updateConfig("buttonText", e.target.value)} <div className="flex items-center justify-between py-1.5">
placeholder="관련 데이터 보기" <span className="text-xs text-muted-foreground"> </span>
className="h-8 text-xs" <div className="w-[140px]">
/> <Select
value={config.relatedTable || ""}
onValueChange={(value) => {
updateConfig("relatedTable", value);
updateConfig("linkColumn", "");
}}
disabled={loadingTables}
>
<SelectTrigger className="h-7 text-xs">
<SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블 선택"} />
</SelectTrigger>
<SelectContent>
{tables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}>
{table.displayName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{config.relatedTable && (
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> </span>
<div className="w-[140px]">
<Select
value={config.linkColumn || ""}
onValueChange={(value) => updateConfig("linkColumn", value)}
>
<SelectTrigger className="h-7 text-xs">
<SelectValue placeholder="컬럼 선택" />
</SelectTrigger>
<SelectContent>
{relatedColumns.map((col) => (
<SelectItem key={col.columnName} value={col.columnName}>
{col.displayName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
)}
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> </span>
<div className="w-[140px]">
<Input
value={config.buttonText || ""}
onChange={(e) => updateConfig("buttonText", e.target.value)}
placeholder="관련 데이터 보기"
className="h-7 text-xs"
/>
</div>
</div> </div>
</div> </div>
)} )}

View File

@ -9,7 +9,6 @@ import React from "react";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
interface V2LayoutConfigPanelProps { interface V2LayoutConfigPanelProps {
@ -21,57 +20,56 @@ export const V2LayoutConfigPanel: React.FC<V2LayoutConfigPanelProps> = ({
config, config,
onChange, onChange,
}) => { }) => {
// 설정 업데이트 핸들러
const updateConfig = (field: string, value: any) => { const updateConfig = (field: string, value: any) => {
onChange({ ...config, [field]: value }); onChange({ ...config, [field]: value });
}; };
return ( return (
<div className="space-y-4"> <div className="space-y-1">
{/* 레이아웃 타입 */} {/* LAYOUT TYPE 섹션 */}
<div className="space-y-2"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">LAYOUT TYPE</h4>
<Select <div className="flex items-center justify-between py-1.5">
value={config.layoutType || config.type || "grid"} <span className="text-xs text-muted-foreground"> </span>
onValueChange={(value) => updateConfig("layoutType", value)} <div className="w-[140px]">
> <Select
<SelectTrigger className="h-8 text-xs"> value={config.layoutType || config.type || "grid"}
<SelectValue placeholder="타입 선택" /> onValueChange={(value) => updateConfig("layoutType", value)}
</SelectTrigger> >
<SelectContent> <SelectTrigger className="h-7 text-xs">
<SelectItem value="grid"></SelectItem> <SelectValue placeholder="타입 선택" />
<SelectItem value="split"> </SelectItem> </SelectTrigger>
<SelectItem value="flex"></SelectItem> <SelectContent>
<SelectItem value="divider"></SelectItem> <SelectItem value="grid"></SelectItem>
<SelectItem value="screen-embed"> </SelectItem> <SelectItem value="split"> </SelectItem>
</SelectContent> <SelectItem value="flex"></SelectItem>
</Select> <SelectItem value="divider"></SelectItem>
<SelectItem value="screen-embed"> </SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div> </div>
<Separator /> {/* GRID SETTINGS 섹션 */}
{/* 그리드 설정 */}
{(config.layoutType === "grid" || !config.layoutType) && ( {(config.layoutType === "grid" || !config.layoutType) && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">GRID SETTINGS</h4>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground">12 </span>
<Checkbox <Checkbox
id="use12Column"
checked={config.use12Column !== false} checked={config.use12Column !== false}
onCheckedChange={(checked) => updateConfig("use12Column", checked)} onCheckedChange={(checked) => updateConfig("use12Column", checked)}
/> />
<label htmlFor="use12Column" className="text-xs">12 </label>
</div> </div>
<div className="flex gap-2">
<div className="grid grid-cols-2 gap-2"> <div className="flex-1">
<div className="space-y-2">
<Label className="text-[10px] text-muted-foreground"> </Label> <Label className="text-[10px] text-muted-foreground"> </Label>
<Select <Select
value={String(config.columns || 12)} value={String(config.columns || 12)}
onValueChange={(value) => updateConfig("columns", Number(value))} onValueChange={(value) => updateConfig("columns", Number(value))}
> >
<SelectTrigger className="h-8 text-xs"> <SelectTrigger className="h-7 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -84,43 +82,43 @@ export const V2LayoutConfigPanel: React.FC<V2LayoutConfigPanelProps> = ({
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className="space-y-2"> <div className="flex-1">
<Label className="text-[10px] text-muted-foreground"> (px)</Label> <Label className="text-[10px] text-muted-foreground"> (px)</Label>
<Input <Input
value={config.gap || "16"} value={config.gap || "16"}
onChange={(e) => updateConfig("gap", e.target.value)} onChange={(e) => updateConfig("gap", e.target.value)}
placeholder="16" placeholder="16"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div> </div>
</div> </div>
</div> </div>
)} )}
{/* 분할 패널 설정 */} {/* SPLIT SETTINGS 섹션 */}
{config.layoutType === "split" && ( {config.layoutType === "split" && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">SPLIT SETTINGS</h4>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"> </span>
<Label className="text-[10px] text-muted-foreground"> </Label> <div className="w-[140px]">
<Select <Select
value={config.direction || "horizontal"} value={config.direction || "horizontal"}
onValueChange={(value) => updateConfig("direction", value)} onValueChange={(value) => updateConfig("direction", value)}
> >
<SelectTrigger className="h-8 text-xs"> <SelectTrigger className="h-7 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="horizontal"></SelectItem> <SelectItem value="horizontal"></SelectItem>
<SelectItem value="vertical"></SelectItem> <SelectItem value="vertical"></SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div>
</div> </div>
<div className="flex gap-2 py-1.5">
<div className="space-y-2"> <div className="flex-1">
<Label className="text-[10px] text-muted-foreground"> (%)</Label> <Label className="text-[10px] text-muted-foreground"> (%)</Label>
<div className="grid grid-cols-2 gap-2">
<Input <Input
type="number" type="number"
value={config.splitRatio?.[0] || 50} value={config.splitRatio?.[0] || 50}
@ -128,59 +126,60 @@ export const V2LayoutConfigPanel: React.FC<V2LayoutConfigPanelProps> = ({
placeholder="50" placeholder="50"
min="10" min="10"
max="90" max="90"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div>
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Input <Input
type="number" type="number"
value={config.splitRatio?.[1] || 50} value={config.splitRatio?.[1] || 50}
disabled disabled
className="h-8 text-xs bg-muted" className="h-7 text-xs bg-muted"
/> />
</div> </div>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="resizable"
checked={config.resizable !== false} checked={config.resizable !== false}
onCheckedChange={(checked) => updateConfig("resizable", checked)} onCheckedChange={(checked) => updateConfig("resizable", checked)}
/> />
<label htmlFor="resizable" className="text-xs"> </label>
</div> </div>
</div> </div>
)} )}
{/* 플렉스 설정 */} {/* FLEX SETTINGS 섹션 */}
{config.layoutType === "flex" && ( {config.layoutType === "flex" && (
<div className="space-y-3"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">FLEX SETTINGS</h4>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"></span>
<Label className="text-[10px] text-muted-foreground"></Label> <div className="w-[140px]">
<Select <Select
value={config.direction || "row"} value={config.direction || "row"}
onValueChange={(value) => updateConfig("direction", value)} onValueChange={(value) => updateConfig("direction", value)}
> >
<SelectTrigger className="h-8 text-xs"> <SelectTrigger className="h-7 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="row"></SelectItem> <SelectItem value="row"></SelectItem>
<SelectItem value="column"></SelectItem> <SelectItem value="column"></SelectItem>
<SelectItem value="row-reverse"> ()</SelectItem> <SelectItem value="row-reverse"> ()</SelectItem>
<SelectItem value="column-reverse"> ()</SelectItem> <SelectItem value="column-reverse"> ()</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div>
</div> </div>
<div className="flex gap-2">
<div className="grid grid-cols-2 gap-2"> <div className="flex-1">
<div className="space-y-2">
<Label className="text-[10px] text-muted-foreground"></Label> <Label className="text-[10px] text-muted-foreground"></Label>
<Select <Select
value={config.justifyContent || "flex-start"} value={config.justifyContent || "flex-start"}
onValueChange={(value) => updateConfig("justifyContent", value)} onValueChange={(value) => updateConfig("justifyContent", value)}
> >
<SelectTrigger className="h-8 text-xs"> <SelectTrigger className="h-7 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -192,13 +191,13 @@ export const V2LayoutConfigPanel: React.FC<V2LayoutConfigPanelProps> = ({
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className="space-y-2"> <div className="flex-1">
<Label className="text-[10px] text-muted-foreground"> </Label> <Label className="text-[10px] text-muted-foreground"> </Label>
<Select <Select
value={config.alignItems || "stretch"} value={config.alignItems || "stretch"}
onValueChange={(value) => updateConfig("alignItems", value)} onValueChange={(value) => updateConfig("alignItems", value)}
> >
<SelectTrigger className="h-8 text-xs"> <SelectTrigger className="h-7 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -210,39 +209,43 @@ export const V2LayoutConfigPanel: React.FC<V2LayoutConfigPanelProps> = ({
</Select> </Select>
</div> </div>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="space-y-2"> <span className="text-xs text-muted-foreground"> (px)</span>
<Label className="text-[10px] text-muted-foreground"> (px)</Label> <div className="w-[140px]">
<Input <Input
value={config.gap || "16"} value={config.gap || "16"}
onChange={(e) => updateConfig("gap", e.target.value)} onChange={(e) => updateConfig("gap", e.target.value)}
placeholder="16" placeholder="16"
className="h-8 text-xs" className="h-7 text-xs"
/> />
</div>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="wrap"
checked={config.wrap || false} checked={config.wrap || false}
onCheckedChange={(checked) => updateConfig("wrap", checked)} onCheckedChange={(checked) => updateConfig("wrap", checked)}
/> />
<label htmlFor="wrap" className="text-xs"> </label>
</div> </div>
</div> </div>
)} )}
{/* 화면 임베드 설정 */} {/* SCREEN EMBED 섹션 */}
{config.layoutType === "screen-embed" && ( {config.layoutType === "screen-embed" && (
<div className="space-y-2"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> ID</Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">SCREEN EMBED</h4>
<Input <div className="flex items-center justify-between py-1.5">
type="number" <span className="text-xs text-muted-foreground"> ID</span>
value={config.screenId || ""} <div className="w-[140px]">
onChange={(e) => updateConfig("screenId", e.target.value ? Number(e.target.value) : undefined)} <Input
placeholder="화면 ID" type="number"
className="h-8 text-xs" value={config.screenId || ""}
/> onChange={(e) => updateConfig("screenId", e.target.value ? Number(e.target.value) : undefined)}
placeholder="화면 ID"
className="h-7 text-xs"
/>
</div>
</div>
</div> </div>
)} )}
</div> </div>
@ -252,5 +255,3 @@ export const V2LayoutConfigPanel: React.FC<V2LayoutConfigPanelProps> = ({
V2LayoutConfigPanel.displayName = "V2LayoutConfigPanel"; V2LayoutConfigPanel.displayName = "V2LayoutConfigPanel";
export default V2LayoutConfigPanel; export default V2LayoutConfigPanel;

View File

@ -9,7 +9,6 @@ import React from "react";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
interface V2MediaConfigPanelProps { interface V2MediaConfigPanelProps {
@ -21,185 +20,166 @@ export const V2MediaConfigPanel: React.FC<V2MediaConfigPanelProps> = ({
config, config,
onChange, onChange,
}) => { }) => {
// 설정 업데이트 핸들러
const updateConfig = (field: string, value: any) => { const updateConfig = (field: string, value: any) => {
onChange({ ...config, [field]: value }); onChange({ ...config, [field]: value });
}; };
return ( return (
<div className="space-y-4"> <div className="space-y-1">
{/* 미디어 타입 */} {/* MEDIA TYPE 섹션 */}
<div className="space-y-2"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> </Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">MEDIA TYPE</h4>
<Select <div className="flex items-center justify-between py-1.5">
value={config.mediaType || config.type || "image"} <span className="text-xs text-muted-foreground"> </span>
onValueChange={(value) => updateConfig("mediaType", value)} <div className="w-[140px]">
> <Select
<SelectTrigger className="h-8 text-xs"> value={config.mediaType || config.type || "image"}
<SelectValue placeholder="타입 선택" /> onValueChange={(value) => updateConfig("mediaType", value)}
</SelectTrigger> >
<SelectContent> <SelectTrigger className="h-7 text-xs">
<SelectItem value="file"></SelectItem> <SelectValue placeholder="타입 선택" />
<SelectItem value="image"></SelectItem> </SelectTrigger>
<SelectItem value="video"></SelectItem> <SelectContent>
<SelectItem value="audio"></SelectItem> <SelectItem value="file"></SelectItem>
</SelectContent> <SelectItem value="image"></SelectItem>
</Select> <SelectItem value="video"></SelectItem>
<SelectItem value="audio"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div> </div>
<Separator /> {/* FILE SETTINGS 섹션 */}
<div className="border-b border-border/50 pb-3 mb-3">
{/* 허용 파일 형식 */} <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">FILE SETTINGS</h4>
<div className="space-y-2"> <div className="flex items-center justify-between py-1.5">
<Label className="text-xs font-medium"> </Label> <span className="text-xs text-muted-foreground"> </span>
<Input <div className="w-[140px]">
value={config.accept || ""} <Input
onChange={(e) => updateConfig("accept", e.target.value)} value={config.accept || ""}
placeholder="예: .jpg,.png,.pdf" onChange={(e) => updateConfig("accept", e.target.value)}
className="h-8 text-xs" placeholder=".jpg,.png,.pdf"
/> className="h-7 text-xs"
<p className="text-[10px] text-muted-foreground"> />
. : .jpg,.png,.gif image/* </div>
</p> </div>
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> (MB)</span>
<div className="w-[140px]">
<Input
type="number"
value={config.maxSize || ""}
onChange={(e) => updateConfig("maxSize", e.target.value ? Number(e.target.value) : undefined)}
placeholder="10"
min="1"
className="h-7 text-xs"
/>
</div>
</div>
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> </span>
<div className="w-[140px]">
<Input
type="number"
value={config.maxFiles || ""}
onChange={(e) => updateConfig("maxFiles", e.target.value ? Number(e.target.value) : undefined)}
placeholder="제한 없음"
min="1"
className="h-7 text-xs"
/>
</div>
</div>
</div> </div>
{/* 최대 파일 크기 */} {/* OPTIONS 섹션 */}
<div className="space-y-2"> <div className="border-b border-border/50 pb-3 mb-3">
<Label className="text-xs font-medium"> (MB)</Label> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">OPTIONS</h4>
<Input <div className="flex items-center justify-between py-1.5">
type="number" <span className="text-xs text-muted-foreground"> </span>
value={config.maxSize || ""}
onChange={(e) => updateConfig("maxSize", e.target.value ? Number(e.target.value) : undefined)}
placeholder="10"
min="1"
className="h-8 text-xs"
/>
</div>
{/* 최대 파일 수 */}
<div className="space-y-2">
<Label className="text-xs font-medium"> </Label>
<Input
type="number"
value={config.maxFiles || ""}
onChange={(e) => updateConfig("maxFiles", e.target.value ? Number(e.target.value) : undefined)}
placeholder="제한 없음"
min="1"
className="h-8 text-xs"
/>
</div>
<Separator />
{/* 옵션 */}
<div className="space-y-3">
<Label className="text-xs font-medium"></Label>
<div className="flex items-center space-x-2">
<Checkbox <Checkbox
id="multiple"
checked={config.multiple || false} checked={config.multiple || false}
onCheckedChange={(checked) => updateConfig("multiple", checked)} onCheckedChange={(checked) => updateConfig("multiple", checked)}
/> />
<label htmlFor="multiple" className="text-xs"> </label>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="preview"
checked={config.preview !== false} checked={config.preview !== false}
onCheckedChange={(checked) => updateConfig("preview", checked)} onCheckedChange={(checked) => updateConfig("preview", checked)}
/> />
<label htmlFor="preview" className="text-xs"> </label>
</div> </div>
<div className="flex items-center justify-between py-1.5">
<div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground"> </span>
<Checkbox <Checkbox
id="dragDrop"
checked={config.dragDrop !== false} checked={config.dragDrop !== false}
onCheckedChange={(checked) => updateConfig("dragDrop", checked)} onCheckedChange={(checked) => updateConfig("dragDrop", checked)}
/> />
<label htmlFor="dragDrop" className="text-xs"> </label>
</div> </div>
</div> </div>
{/* 이미지 전용 설정 */} {/* IMAGE SETTINGS 섹션 - 이미지 타입 전용 */}
{config.mediaType === "image" && ( {config.mediaType === "image" && (
<> <div className="border-b border-border/50 pb-3 mb-3">
<Separator /> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">IMAGE SETTINGS</h4>
<div className="space-y-3"> <div className="flex gap-2">
<Label className="text-xs font-medium"> </Label> <div className="flex-1">
<Label className="text-[10px] text-muted-foreground"> (px)</Label>
<div className="grid grid-cols-2 gap-2"> <Input
<div className="space-y-2"> type="number"
<Label className="text-[10px] text-muted-foreground"> (px)</Label> value={config.maxWidth || ""}
<Input onChange={(e) => updateConfig("maxWidth", e.target.value ? Number(e.target.value) : undefined)}
type="number" placeholder="자동"
value={config.maxWidth || ""} className="h-7 text-xs"
onChange={(e) => updateConfig("maxWidth", e.target.value ? Number(e.target.value) : undefined)} />
placeholder="자동" </div>
className="h-8 text-xs" <div className="flex-1">
/> <Label className="text-[10px] text-muted-foreground"> (px)</Label>
</div> <Input
<div className="space-y-2"> type="number"
<Label className="text-[10px] text-muted-foreground"> (px)</Label> value={config.maxHeight || ""}
<Input onChange={(e) => updateConfig("maxHeight", e.target.value ? Number(e.target.value) : undefined)}
type="number" placeholder="자동"
value={config.maxHeight || ""} className="h-7 text-xs"
onChange={(e) => updateConfig("maxHeight", e.target.value ? Number(e.target.value) : undefined)}
placeholder="자동"
className="h-8 text-xs"
/>
</div>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="crop"
checked={config.crop || false}
onCheckedChange={(checked) => updateConfig("crop", checked)}
/> />
<label htmlFor="crop" className="text-xs"> </label>
</div> </div>
</div> </div>
</> <div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> </span>
<Checkbox
checked={config.crop || false}
onCheckedChange={(checked) => updateConfig("crop", checked)}
/>
</div>
</div>
)} )}
{/* 비디오/오디오 전용 설정 */} {/* PLAYER SETTINGS 섹션 - 비디오/오디오 전용 */}
{(config.mediaType === "video" || config.mediaType === "audio") && ( {(config.mediaType === "video" || config.mediaType === "audio") && (
<> <div className="border-b border-border/50 pb-3 mb-3">
<Separator /> <h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">PLAYER SETTINGS</h4>
<div className="space-y-3"> <div className="flex items-center justify-between py-1.5">
<Label className="text-xs font-medium"> </Label> <span className="text-xs text-muted-foreground"> </span>
<Checkbox
<div className="flex items-center space-x-2"> checked={config.autoplay || false}
<Checkbox onCheckedChange={(checked) => updateConfig("autoplay", checked)}
id="autoplay" />
checked={config.autoplay || false}
onCheckedChange={(checked) => updateConfig("autoplay", checked)}
/>
<label htmlFor="autoplay" className="text-xs"> </label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="controls"
checked={config.controls !== false}
onCheckedChange={(checked) => updateConfig("controls", checked)}
/>
<label htmlFor="controls" className="text-xs"> </label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="loop"
checked={config.loop || false}
onCheckedChange={(checked) => updateConfig("loop", checked)}
/>
<label htmlFor="loop" className="text-xs"> </label>
</div>
</div> </div>
</> <div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> </span>
<Checkbox
checked={config.controls !== false}
onCheckedChange={(checked) => updateConfig("controls", checked)}
/>
</div>
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"> </span>
<Checkbox
checked={config.loop || false}
onCheckedChange={(checked) => updateConfig("loop", checked)}
/>
</div>
</div>
)} )}
</div> </div>
); );
@ -208,5 +188,3 @@ export const V2MediaConfigPanel: React.FC<V2MediaConfigPanelProps> = ({
V2MediaConfigPanel.displayName = "V2MediaConfigPanel"; V2MediaConfigPanel.displayName = "V2MediaConfigPanel";
export default V2MediaConfigPanel; export default V2MediaConfigPanel;