ERP-node/frontend/components/dataflow/connection/ActionFieldMappings.tsx

215 lines
8.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Plus, Trash2 } from "lucide-react";
import { TableInfo, ColumnInfo } from "@/lib/api/dataflow";
import { DataSaveSettings } from "@/types/connectionTypes";
interface ActionFieldMappingsProps {
action: DataSaveSettings["actions"][0];
actionIndex: number;
settings: DataSaveSettings;
onSettingsChange: (settings: DataSaveSettings) => void;
availableTables: TableInfo[];
tableColumnsCache: { [tableName: string]: ColumnInfo[] };
}
export const ActionFieldMappings: React.FC<ActionFieldMappingsProps> = ({
action,
actionIndex,
settings,
onSettingsChange,
availableTables,
tableColumnsCache,
}) => {
const addFieldMapping = () => {
const newActions = [...settings.actions];
newActions[actionIndex].fieldMappings.push({
sourceTable: "",
sourceField: "",
targetTable: "",
targetField: "",
defaultValue: "",
transformFunction: "",
});
onSettingsChange({ ...settings, actions: newActions });
};
const updateFieldMapping = (mappingIndex: number, field: string, value: string) => {
const newActions = [...settings.actions];
(newActions[actionIndex].fieldMappings[mappingIndex] as any)[field] = value;
onSettingsChange({ ...settings, actions: newActions });
};
const removeFieldMapping = (mappingIndex: number) => {
const newActions = [...settings.actions];
newActions[actionIndex].fieldMappings = newActions[actionIndex].fieldMappings.filter((_, i) => i !== mappingIndex);
onSettingsChange({ ...settings, actions: newActions });
};
return (
<div className="mt-3">
<div className="mb-2 flex items-center justify-between">
<Label className="text-xs font-medium"> </Label>
<Button size="sm" variant="outline" onClick={addFieldMapping} className="h-6 text-xs">
<Plus className="mr-1 h-2 w-2" />
</Button>
</div>
<div className="space-y-3">
{action.fieldMappings.map((mapping, mappingIndex) => (
<div
key={`${action.id}-mapping-${mappingIndex}-${mapping.sourceField || "empty"}-${mapping.targetField || "empty"}`}
className="rounded border bg-white p-2"
>
{/* 컴팩트한 매핑 표시 */}
<div className="flex items-center gap-2 text-xs">
{/* 소스 */}
<div className="flex items-center gap-1 rounded bg-blue-50 px-2 py-1">
<Select
value={mapping.sourceTable || "__EMPTY__"}
onValueChange={(value) => {
const actualValue = value === "__EMPTY__" ? "" : value;
updateFieldMapping(mappingIndex, "sourceTable", actualValue);
updateFieldMapping(mappingIndex, "sourceField", "");
if (actualValue) {
updateFieldMapping(mappingIndex, "defaultValue", "");
}
}}
disabled={!!(mapping.defaultValue && mapping.defaultValue.trim())}
>
<SelectTrigger className="h-6 w-24 border-0 bg-transparent p-0 text-xs">
<SelectValue placeholder="테이블" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__EMPTY__"> ( )</SelectItem>
{availableTables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}>
<div className="truncate" title={table.tableName}>
{table.tableName}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
{mapping.sourceTable && (
<button
onClick={() => {
updateFieldMapping(mappingIndex, "sourceTable", "");
updateFieldMapping(mappingIndex, "sourceField", "");
}}
className="ml-1 flex h-4 w-4 items-center justify-center rounded-full text-gray-400 hover:bg-gray-200 hover:text-gray-600"
title="소스 테이블 지우기"
>
×
</button>
)}
<span className="text-gray-400">.</span>
<Select
value={mapping.sourceField}
onValueChange={(value) => {
updateFieldMapping(mappingIndex, "sourceField", value);
if (value) {
updateFieldMapping(mappingIndex, "defaultValue", "");
}
}}
disabled={!mapping.sourceTable || !!(mapping.defaultValue && mapping.defaultValue.trim())}
>
<SelectTrigger className="h-6 w-24 border-0 bg-transparent p-0 text-xs">
<SelectValue placeholder="컬럼" />
</SelectTrigger>
<SelectContent>
{mapping.sourceTable &&
tableColumnsCache[mapping.sourceTable]?.map((column) => (
<SelectItem key={column.columnName} value={column.columnName}>
<div className="truncate" title={column.columnName}>
{column.columnName}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="text-gray-400"></div>
{/* 타겟 */}
<div className="flex items-center gap-1 rounded bg-green-50 px-2 py-1">
<Select
value={mapping.targetTable || ""}
onValueChange={(value) => {
updateFieldMapping(mappingIndex, "targetTable", value);
updateFieldMapping(mappingIndex, "targetField", "");
}}
>
<SelectTrigger className="h-6 w-24 border-0 bg-transparent p-0 text-xs">
<SelectValue placeholder="테이블" />
</SelectTrigger>
<SelectContent>
{availableTables.map((table) => (
<SelectItem key={table.tableName} value={table.tableName}>
<div className="truncate" title={table.tableName}>
{table.tableName}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-gray-400">.</span>
<Select
value={mapping.targetField}
onValueChange={(value) => updateFieldMapping(mappingIndex, "targetField", value)}
disabled={!mapping.targetTable}
>
<SelectTrigger className="h-6 w-24 border-0 bg-transparent p-0 text-xs">
<SelectValue placeholder="컬럼" />
</SelectTrigger>
<SelectContent>
{mapping.targetTable &&
tableColumnsCache[mapping.targetTable]?.map((column) => (
<SelectItem key={column.columnName} value={column.columnName}>
<div className="truncate" title={column.columnName}>
{column.columnName}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 기본값 (인라인) */}
<Input
value={mapping.defaultValue || ""}
onChange={(e) => {
updateFieldMapping(mappingIndex, "defaultValue", e.target.value);
if (e.target.value.trim()) {
updateFieldMapping(mappingIndex, "sourceTable", "");
updateFieldMapping(mappingIndex, "sourceField", "");
}
}}
disabled={!!mapping.sourceTable}
className="h-6 w-20 text-xs"
placeholder="기본값"
/>
{/* 삭제 버튼 */}
<Button
size="sm"
variant="ghost"
onClick={() => removeFieldMapping(mappingIndex)}
className="h-6 w-6 p-0 text-red-500 hover:text-red-700"
>
<Trash2 className="h-3 w-3" />
</Button>
</div>
</div>
))}
</div>
</div>
);
};