mhkim-node #412
|
|
@ -375,12 +375,15 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
|
|
||||||
// Entity 조인 컬럼 토글 (추가/제거)
|
// Entity 조인 컬럼 토글 (추가/제거)
|
||||||
const toggleEntityJoinColumn = useCallback(
|
const toggleEntityJoinColumn = useCallback(
|
||||||
(joinTableName: string, sourceColumn: string, refColumnName: string, refColumnLabel: string, displayField: string) => {
|
(joinTableName: string, sourceColumn: string, refColumnName: string, refColumnLabel: string, displayField: string, columnType?: string) => {
|
||||||
const currentJoins = config.entityJoins || [];
|
const currentJoins = config.entityJoins || [];
|
||||||
const existingJoinIdx = currentJoins.findIndex(
|
const existingJoinIdx = currentJoins.findIndex(
|
||||||
(j) => j.sourceColumn === sourceColumn && j.referenceTable === joinTableName,
|
(j) => j.sourceColumn === sourceColumn && j.referenceTable === joinTableName,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let newEntityJoins = [...currentJoins];
|
||||||
|
let newColumns = [...config.columns];
|
||||||
|
|
||||||
if (existingJoinIdx >= 0) {
|
if (existingJoinIdx >= 0) {
|
||||||
const existingJoin = currentJoins[existingJoinIdx];
|
const existingJoin = currentJoins[existingJoinIdx];
|
||||||
const existingColIdx = existingJoin.columns.findIndex((c) => c.referenceField === refColumnName);
|
const existingColIdx = existingJoin.columns.findIndex((c) => c.referenceField === refColumnName);
|
||||||
|
|
@ -388,34 +391,49 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
if (existingColIdx >= 0) {
|
if (existingColIdx >= 0) {
|
||||||
const updatedColumns = existingJoin.columns.filter((_, i) => i !== existingColIdx);
|
const updatedColumns = existingJoin.columns.filter((_, i) => i !== existingColIdx);
|
||||||
if (updatedColumns.length === 0) {
|
if (updatedColumns.length === 0) {
|
||||||
updateConfig({ entityJoins: currentJoins.filter((_, i) => i !== existingJoinIdx) });
|
newEntityJoins = newEntityJoins.filter((_, i) => i !== existingJoinIdx);
|
||||||
} else {
|
} else {
|
||||||
const updated = [...currentJoins];
|
newEntityJoins[existingJoinIdx] = { ...existingJoin, columns: updatedColumns };
|
||||||
updated[existingJoinIdx] = { ...existingJoin, columns: updatedColumns };
|
|
||||||
updateConfig({ entityJoins: updated });
|
|
||||||
}
|
}
|
||||||
|
// config.columns에서도 제거
|
||||||
|
newColumns = newColumns.filter(c => !(c.key === displayField && c.isJoinColumn));
|
||||||
} else {
|
} else {
|
||||||
const updated = [...currentJoins];
|
newEntityJoins[existingJoinIdx] = {
|
||||||
updated[existingJoinIdx] = {
|
|
||||||
...existingJoin,
|
...existingJoin,
|
||||||
columns: [...existingJoin.columns, { referenceField: refColumnName, displayField }],
|
columns: [...existingJoin.columns, { referenceField: refColumnName, displayField }],
|
||||||
};
|
};
|
||||||
updateConfig({ entityJoins: updated });
|
// config.columns에 추가
|
||||||
|
newColumns.push({
|
||||||
|
key: displayField,
|
||||||
|
title: refColumnLabel,
|
||||||
|
width: "auto",
|
||||||
|
visible: true,
|
||||||
|
editable: false,
|
||||||
|
isJoinColumn: true,
|
||||||
|
inputType: columnType || "text",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateConfig({
|
newEntityJoins.push({
|
||||||
entityJoins: [
|
|
||||||
...currentJoins,
|
|
||||||
{
|
|
||||||
sourceColumn,
|
sourceColumn,
|
||||||
referenceTable: joinTableName,
|
referenceTable: joinTableName,
|
||||||
columns: [{ referenceField: refColumnName, displayField }],
|
columns: [{ referenceField: refColumnName, displayField }],
|
||||||
},
|
});
|
||||||
],
|
// config.columns에 추가
|
||||||
|
newColumns.push({
|
||||||
|
key: displayField,
|
||||||
|
title: refColumnLabel,
|
||||||
|
width: "auto",
|
||||||
|
visible: true,
|
||||||
|
editable: false,
|
||||||
|
isJoinColumn: true,
|
||||||
|
inputType: columnType || "text",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateConfig({ entityJoins: newEntityJoins, columns: newColumns });
|
||||||
},
|
},
|
||||||
[config.entityJoins, updateConfig],
|
[config.entityJoins, config.columns, updateConfig],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Entity 조인에 특정 컬럼이 설정되어 있는지 확인
|
// Entity 조인에 특정 컬럼이 설정되어 있는지 확인
|
||||||
|
|
@ -604,9 +622,9 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
|
|
||||||
// 컬럼 토글 (현재 테이블 컬럼 - 입력용)
|
// 컬럼 토글 (현재 테이블 컬럼 - 입력용)
|
||||||
const toggleInputColumn = (column: ColumnOption) => {
|
const toggleInputColumn = (column: ColumnOption) => {
|
||||||
const existingIndex = config.columns.findIndex((c) => c.key === column.columnName);
|
const existingIndex = config.columns.findIndex((c) => c.key === column.columnName && !c.isJoinColumn && !c.isSourceDisplay);
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0) {
|
||||||
const newColumns = config.columns.filter((c) => c.key !== column.columnName);
|
const newColumns = config.columns.filter((_, i) => i !== existingIndex);
|
||||||
updateConfig({ columns: newColumns });
|
updateConfig({ columns: newColumns });
|
||||||
} else {
|
} else {
|
||||||
// 컬럼의 inputType과 detailSettings 정보 포함
|
// 컬럼의 inputType과 detailSettings 정보 포함
|
||||||
|
|
@ -651,7 +669,7 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const isColumnAdded = (columnName: string) => {
|
const isColumnAdded = (columnName: string) => {
|
||||||
return config.columns.some((c) => c.key === columnName && !c.isSourceDisplay);
|
return config.columns.some((c) => c.key === columnName && !c.isSourceDisplay && !c.isJoinColumn);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSourceColumnSelected = (columnName: string) => {
|
const isSourceColumnSelected = (columnName: string) => {
|
||||||
|
|
@ -761,10 +779,9 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Tabs defaultValue="basic" className="w-full">
|
<Tabs defaultValue="basic" className="w-full">
|
||||||
<TabsList className="grid w-full grid-cols-3">
|
<TabsList className="grid w-full grid-cols-2">
|
||||||
<TabsTrigger value="basic" className="text-xs">기본</TabsTrigger>
|
<TabsTrigger value="basic" className="text-xs">기본</TabsTrigger>
|
||||||
<TabsTrigger value="columns" className="text-xs">컬럼</TabsTrigger>
|
<TabsTrigger value="columns" className="text-xs">컬럼</TabsTrigger>
|
||||||
<TabsTrigger value="entityJoin" className="text-xs">Entity 조인</TabsTrigger>
|
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
{/* 기본 설정 탭 */}
|
{/* 기본 설정 탭 */}
|
||||||
|
|
@ -1365,6 +1382,84 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* ===== 🆕 Entity 조인 컬럼 (표시용) ===== */}
|
||||||
|
<div className="space-y-2 mt-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Link2 className="h-4 w-4 text-primary" />
|
||||||
|
<Label className="text-xs font-medium text-primary">Entity 조인 컬럼 (읽기전용)</Label>
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-muted-foreground">
|
||||||
|
FK 컬럼을 기반으로 참조 테이블의 데이터를 자동으로 조회하여 표시합니다.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{loadingEntityJoins ? (
|
||||||
|
<p className="text-muted-foreground py-2 text-xs">로딩 중...</p>
|
||||||
|
) : entityJoinData.joinTables.length === 0 ? (
|
||||||
|
<p className="text-muted-foreground py-2 text-xs">
|
||||||
|
{entityJoinTargetTable
|
||||||
|
? `${entityJoinTargetTable} 테이블에 Entity 조인 가능한 컬럼이 없습니다`
|
||||||
|
: "저장 테이블을 먼저 설정해주세요"}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{entityJoinData.joinTables.map((joinTable, tableIndex) => {
|
||||||
|
const sourceColumn = (joinTable as any).joinConfig?.sourceColumn || "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={tableIndex} className="space-y-1">
|
||||||
|
<div className="mb-1 flex items-center gap-2 text-[10px] font-medium text-primary">
|
||||||
|
<Link2 className="h-3 w-3" />
|
||||||
|
<span>{joinTable.tableName}</span>
|
||||||
|
<span className="text-muted-foreground">({sourceColumn})</span>
|
||||||
|
</div>
|
||||||
|
<div className="max-h-40 space-y-0.5 overflow-y-auto rounded-md border border-primary/20 bg-primary/10/30 p-2">
|
||||||
|
{joinTable.availableColumns.map((column, colIndex) => {
|
||||||
|
const isActive = isEntityJoinColumnActive(
|
||||||
|
joinTable.tableName,
|
||||||
|
sourceColumn,
|
||||||
|
column.columnName,
|
||||||
|
);
|
||||||
|
const matchingCol = config.columns.find((c) => c.key === column.columnName && c.isJoinColumn);
|
||||||
|
const displayField = matchingCol?.key || column.columnName;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={colIndex}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-pointer items-center gap-2 rounded px-2 py-1 hover:bg-primary/10/50",
|
||||||
|
isActive && "bg-primary/10",
|
||||||
|
)}
|
||||||
|
onClick={() =>
|
||||||
|
toggleEntityJoinColumn(
|
||||||
|
joinTable.tableName,
|
||||||
|
sourceColumn,
|
||||||
|
column.columnName,
|
||||||
|
column.columnLabel,
|
||||||
|
displayField,
|
||||||
|
column.inputType || column.dataType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={isActive}
|
||||||
|
className="pointer-events-none h-3.5 w-3.5"
|
||||||
|
/>
|
||||||
|
<Link2 className="h-3 w-3 flex-shrink-0 text-primary" />
|
||||||
|
<span className="truncate text-xs">{column.columnLabel}</span>
|
||||||
|
<span className="ml-auto text-[10px] text-primary/80">
|
||||||
|
{column.inputType || column.dataType}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 선택된 컬럼 상세 설정 - 🆕 모든 컬럼 통합, 순서 변경 가능 */}
|
{/* 선택된 컬럼 상세 설정 - 🆕 모든 컬럼 통합, 순서 변경 가능 */}
|
||||||
{config.columns.length > 0 && (
|
{config.columns.length > 0 && (
|
||||||
<>
|
<>
|
||||||
|
|
@ -1381,7 +1476,7 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2 rounded-md border p-2",
|
"flex items-center gap-2 rounded-md border p-2",
|
||||||
col.isSourceDisplay ? "border-primary/20 bg-primary/10/50" : "border-border bg-muted/30",
|
(col.isSourceDisplay || col.isJoinColumn) ? "border-primary/20 bg-primary/10/50" : "border-border bg-muted/30",
|
||||||
col.hidden && "opacity-50",
|
col.hidden && "opacity-50",
|
||||||
)}
|
)}
|
||||||
draggable
|
draggable
|
||||||
|
|
@ -1403,7 +1498,7 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
<GripVertical className="text-muted-foreground h-3 w-3 cursor-grab flex-shrink-0" />
|
<GripVertical className="text-muted-foreground h-3 w-3 cursor-grab flex-shrink-0" />
|
||||||
|
|
||||||
{/* 확장/축소 버튼 (입력 컬럼만) */}
|
{/* 확장/축소 버튼 (입력 컬럼만) */}
|
||||||
{!col.isSourceDisplay && (
|
{(!col.isSourceDisplay && !col.isJoinColumn) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setExpandedColumn(expandedColumn === col.key ? null : col.key)}
|
onClick={() => setExpandedColumn(expandedColumn === col.key ? null : col.key)}
|
||||||
|
|
@ -1419,6 +1514,8 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
|
|
||||||
{col.isSourceDisplay ? (
|
{col.isSourceDisplay ? (
|
||||||
<Link2 className="text-primary h-3 w-3 flex-shrink-0" title="소스 표시 (읽기 전용)" />
|
<Link2 className="text-primary h-3 w-3 flex-shrink-0" title="소스 표시 (읽기 전용)" />
|
||||||
|
) : col.isJoinColumn ? (
|
||||||
|
<Link2 className="text-amber-500 h-3 w-3 flex-shrink-0" title="Entity 조인 (읽기 전용)" />
|
||||||
) : (
|
) : (
|
||||||
<Database className="text-muted-foreground h-3 w-3 flex-shrink-0" />
|
<Database className="text-muted-foreground h-3 w-3 flex-shrink-0" />
|
||||||
)}
|
)}
|
||||||
|
|
@ -1431,7 +1528,7 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 히든 토글 (입력 컬럼만) */}
|
{/* 히든 토글 (입력 컬럼만) */}
|
||||||
{!col.isSourceDisplay && (
|
{(!col.isSourceDisplay && !col.isJoinColumn) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => updateColumnProp(col.key, "hidden", !col.hidden)}
|
onClick={() => updateColumnProp(col.key, "hidden", !col.hidden)}
|
||||||
|
|
@ -1446,12 +1543,12 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 자동입력 표시 아이콘 */}
|
{/* 자동입력 표시 아이콘 */}
|
||||||
{!col.isSourceDisplay && col.autoFill?.type && col.autoFill.type !== "none" && (
|
{(!col.isSourceDisplay && !col.isJoinColumn) && col.autoFill?.type && col.autoFill.type !== "none" && (
|
||||||
<Wand2 className="h-3 w-3 text-purple-500 flex-shrink-0" title="자동 입력" />
|
<Wand2 className="h-3 w-3 text-purple-500 flex-shrink-0" title="자동 입력" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 편집 가능 토글 */}
|
{/* 편집 가능 토글 */}
|
||||||
{!col.isSourceDisplay && (
|
{(!col.isSourceDisplay && !col.isJoinColumn) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => updateColumnProp(col.key, "editable", !(col.editable ?? true))}
|
onClick={() => updateColumnProp(col.key, "editable", !(col.editable ?? true))}
|
||||||
|
|
@ -1474,6 +1571,13 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (col.isSourceDisplay) {
|
if (col.isSourceDisplay) {
|
||||||
toggleSourceDisplayColumn({ columnName: col.key, displayName: col.title });
|
toggleSourceDisplayColumn({ columnName: col.key, displayName: col.title });
|
||||||
|
} else if (col.isJoinColumn) {
|
||||||
|
const newColumns = config.columns.filter(c => c.key !== col.key);
|
||||||
|
const newEntityJoins = config.entityJoins?.map(join => ({
|
||||||
|
...join,
|
||||||
|
columns: join.columns.filter(c => c.displayField !== col.key)
|
||||||
|
})).filter(join => join.columns.length > 0);
|
||||||
|
updateConfig({ columns: newColumns, entityJoins: newEntityJoins });
|
||||||
} else {
|
} else {
|
||||||
toggleInputColumn({ columnName: col.key, displayName: col.title });
|
toggleInputColumn({ columnName: col.key, displayName: col.title });
|
||||||
}
|
}
|
||||||
|
|
@ -1485,7 +1589,7 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 확장된 상세 설정 (입력 컬럼만) */}
|
{/* 확장된 상세 설정 (입력 컬럼만) */}
|
||||||
{!col.isSourceDisplay && expandedColumn === col.key && (
|
{(!col.isSourceDisplay && !col.isJoinColumn) && expandedColumn === col.key && (
|
||||||
<div className="ml-6 space-y-2 rounded-md border border-dashed border-input bg-muted p-2">
|
<div className="ml-6 space-y-2 rounded-md border border-dashed border-input bg-muted p-2">
|
||||||
{/* 자동 입력 설정 */}
|
{/* 자동 입력 설정 */}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|
@ -1812,120 +1916,6 @@ export const V2RepeaterConfigPanel: React.FC<V2RepeaterConfigPanelProps> = ({
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
{/* Entity 조인 설정 탭 */}
|
|
||||||
<TabsContent value="entityJoin" className="mt-4 space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-semibold">Entity 조인 연결</h3>
|
|
||||||
<p className="text-muted-foreground text-[10px]">
|
|
||||||
FK 컬럼을 기반으로 참조 테이블의 데이터를 자동으로 조회하여 표시합니다
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<hr className="border-border" />
|
|
||||||
|
|
||||||
{loadingEntityJoins ? (
|
|
||||||
<p className="text-muted-foreground py-2 text-center text-xs">로딩 중...</p>
|
|
||||||
) : entityJoinData.joinTables.length === 0 ? (
|
|
||||||
<div className="rounded-md border border-dashed p-4 text-center">
|
|
||||||
<p className="text-muted-foreground text-xs">
|
|
||||||
{entityJoinTargetTable
|
|
||||||
? `${entityJoinTargetTable} 테이블에 Entity 조인 가능한 컬럼이 없습니다`
|
|
||||||
: "저장 테이블을 먼저 설정해주세요"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-3">
|
|
||||||
{entityJoinData.joinTables.map((joinTable, tableIndex) => {
|
|
||||||
const sourceColumn = (joinTable as any).joinConfig?.sourceColumn || "";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={tableIndex} className="space-y-1">
|
|
||||||
<div className="mb-1 flex items-center gap-2 text-[10px] font-medium text-primary">
|
|
||||||
<Link2 className="h-3 w-3" />
|
|
||||||
<span>{joinTable.tableName}</span>
|
|
||||||
<span className="text-muted-foreground">({sourceColumn})</span>
|
|
||||||
</div>
|
|
||||||
<div className="max-h-40 space-y-0.5 overflow-y-auto rounded-md border border-primary/20 bg-primary/10/30 p-2">
|
|
||||||
{joinTable.availableColumns.map((column, colIndex) => {
|
|
||||||
const isActive = isEntityJoinColumnActive(
|
|
||||||
joinTable.tableName,
|
|
||||||
sourceColumn,
|
|
||||||
column.columnName,
|
|
||||||
);
|
|
||||||
const matchingCol = config.columns.find((c) => c.key === column.columnName);
|
|
||||||
const displayField = matchingCol?.key || column.columnName;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={colIndex}
|
|
||||||
className={cn(
|
|
||||||
"flex cursor-pointer items-center gap-2 rounded px-2 py-1 hover:bg-primary/10/50",
|
|
||||||
isActive && "bg-primary/10",
|
|
||||||
)}
|
|
||||||
onClick={() =>
|
|
||||||
toggleEntityJoinColumn(
|
|
||||||
joinTable.tableName,
|
|
||||||
sourceColumn,
|
|
||||||
column.columnName,
|
|
||||||
column.columnLabel,
|
|
||||||
displayField,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={isActive}
|
|
||||||
className="pointer-events-none h-3.5 w-3.5"
|
|
||||||
/>
|
|
||||||
<Link2 className="h-3 w-3 flex-shrink-0 text-primary" />
|
|
||||||
<span className="truncate text-xs">{column.columnLabel}</span>
|
|
||||||
<span className="ml-auto text-[10px] text-primary/80">
|
|
||||||
{column.inputType || column.dataType}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 현재 설정된 Entity 조인 목록 */}
|
|
||||||
{config.entityJoins && config.entityJoins.length > 0 && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<h4 className="text-xs font-medium">설정된 조인</h4>
|
|
||||||
<div className="space-y-1">
|
|
||||||
{config.entityJoins.map((join, idx) => (
|
|
||||||
<div key={idx} className="flex items-center gap-1 rounded border bg-muted/30 px-2 py-1 text-[10px]">
|
|
||||||
<Database className="h-3 w-3 text-primary" />
|
|
||||||
<span className="font-medium">{join.sourceColumn}</span>
|
|
||||||
<ArrowRight className="h-3 w-3 text-muted-foreground" />
|
|
||||||
<span>{join.referenceTable}</span>
|
|
||||||
<span className="text-muted-foreground">
|
|
||||||
({join.columns.map((c) => c.referenceField).join(", ")})
|
|
||||||
</span>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => {
|
|
||||||
updateConfig({
|
|
||||||
entityJoins: config.entityJoins!.filter((_, i) => i !== idx),
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className="ml-auto h-4 w-4 p-0 text-destructive"
|
|
||||||
>
|
|
||||||
<Trash2 className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TabsContent>
|
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue