+
+ {isModalMode
+ ? "소스 테이블 컬럼은 표시용, 저장 테이블 컬럼은 입력용이에요"
+ : "체크한 컬럼이 리피터에 입력 필드로 표시돼요"
+ }
+
+
+ {/* 모달 모드: 소스 테이블 컬럼 (표시용) */}
+ {isModalMode && config.dataSource?.sourceTable && (
+ <>
+
+
+ 소스 테이블 ({config.dataSource.sourceTable}) - 표시용
+
+ {loadingSourceColumns ? (
+
로딩 중...
+ ) : sourceTableColumns.length === 0 ? (
+
컬럼 정보가 없습니다
+ ) : (
+
+ {sourceTableColumns.map((column) => (
+
toggleSourceDisplayColumn(column)}
+ >
+ toggleSourceDisplayColumn(column)}
+ className="pointer-events-none h-3.5 w-3.5"
+ />
+
+ {column.displayName}
+ 표시
+
+ ))}
+
+ )}
+ >
+ )}
+
+ {/* 저장 테이블 컬럼 (입력용) */}
+
+
+ 저장 테이블 ({targetTableForColumns || "미선택"}) - 입력용
+
+ {loadingColumns ? (
로딩 중...
- ) : sourceTableColumns.length === 0 ? (
-
컬럼 정보가 없습니다
+ ) : inputableColumns.length === 0 ? (
+
+ 컬럼 정보가 없습니다
+
) : (
-
- {sourceTableColumns.map((column) => (
+
+ {inputableColumns.map((column) => (
toggleSourceDisplayColumn(column)}
+ onClick={() => toggleInputColumn(column)}
>
toggleSourceDisplayColumn(column)}
+ checked={isColumnAdded(column.columnName)}
+ onCheckedChange={() => toggleInputColumn(column)}
className="pointer-events-none h-3.5 w-3.5"
/>
-
+
{column.displayName}
- 표시
+ {column.inputType}
))}
)}
- >
- )}
-
- {/* 저장 테이블 컬럼 (입력용) */}
-
-
- 저장 테이블 ({targetTableForColumns || "미선택"}) - 입력용
-
- {loadingColumns ? (
-
로딩 중...
- ) : inputableColumns.length === 0 ? (
-
- 컬럼 정보가 없습니다
-
- ) : (
-
- {inputableColumns.map((column) => (
-
toggleInputColumn(column)}
- >
- toggleInputColumn(column)}
- className="pointer-events-none h-3.5 w-3.5"
- />
-
- {column.displayName}
- {column.inputType}
-
- ))}
- )}
-
+
+
- {/* 선택된 컬럼 상세 설정 */}
+ {/* 선택된 컬럼 상세 설정 - Collapsible */}
{config.columns.length > 0 && (
- <>
-
-
- 선택된 컬럼 ({config.columns.length})
- 드래그로 순서 변경
-
-
+
+
+
+
+
+
+
드래그로 순서 변경, 클릭하여 상세 설정
+
{config.columns.map((col, index) => (
{/* 컬럼 헤더 (드래그 가능) */}
@@ -1744,25 +1795,38 @@ export const V2RepeaterConfigPanel: React.FC = ({
))}
- >
+
+
)}
- {/* 계산 규칙 */}
+ {/* 계산 규칙 - Collapsible (기본 닫힘) */}
{(isModalMode || isInlineMode) && config.columns.length > 0 && (
- <>
-
-
+
+
+
+
+
+
+
-
+
{calculationRules.map((rule) => (
@@ -1862,7 +1926,8 @@ export const V2RepeaterConfigPanel: React.FC = ({
)}
- >
+
+
)}
@@ -1897,94 +1962,126 @@ export const V2RepeaterConfigPanel: React.FC
= ({
)}
) : (
-
+
{entityJoinData.joinTables.map((joinTable, tableIndex) => {
const sourceColumn = (joinTable as any).joinConfig?.sourceColumn || "";
+ const activeCount = joinTable.availableColumns.filter(col =>
+ isEntityJoinColumnActive(joinTable.tableName, sourceColumn, col.columnName)
+ ).length;
+ const isSubOpen = entityJoinSubOpen[tableIndex] ?? false;
return (
-
-
-
- {joinTable.tableName}
- ({sourceColumn})
-
-
- {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;
+
setEntityJoinSubOpen((prev) => ({ ...prev, [tableIndex]: open }))}>
+
+
+
+
+
+ {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 (
-
- toggleEntityJoinColumn(
- joinTable.tableName,
- sourceColumn,
- column.columnName,
- column.columnLabel,
- displayField,
- )
- }
- >
-
-
- {column.columnLabel}
-
- {column.inputType || column.dataType}
-
-
- );
- })}
-
-
+ return (
+
+ toggleEntityJoinColumn(
+ joinTable.tableName,
+ sourceColumn,
+ column.columnName,
+ column.columnLabel,
+ displayField,
+ )
+ }
+ >
+
+
+ {column.columnLabel}
+
+ {column.inputType || column.dataType}
+
+
+ );
+ })}
+
+
+
);
})}
)}
- {/* 현재 설정된 Entity 조인 목록 */}
+ {/* 현재 설정된 Entity 조인 목록 - Collapsible */}
{config.entityJoins && config.entityJoins.length > 0 && (
-
-
설정된 조인 ({config.entityJoins.length})
-
- {config.entityJoins.map((join, idx) => (
-
-
-
{join.sourceColumn}
-
-
{join.referenceTable}
-
- ({join.columns.map((c) => c.referenceField).join(", ")})
-
-
+
+
+
-
+
+
+
+
+
+ {config.entityJoins.map((join, idx) => (
+
+
+
{join.sourceColumn}
+
+
{join.referenceTable}
+
+ ({join.columns.map((c) => c.referenceField).join(", ")})
+
+
+
+ ))}
+
+
+
)}
diff --git a/frontend/components/v2/config-panels/V2TimelineSchedulerConfigPanel.tsx b/frontend/components/v2/config-panels/V2TimelineSchedulerConfigPanel.tsx
index 9d7380e5..d01db5ba 100644
--- a/frontend/components/v2/config-panels/V2TimelineSchedulerConfigPanel.tsx
+++ b/frontend/components/v2/config-panels/V2TimelineSchedulerConfigPanel.tsx
@@ -11,6 +11,7 @@ import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
@@ -47,6 +48,9 @@ export const V2TimelineSchedulerConfigPanel: React.FC
{/* ─── 1단계: 스케줄 데이터 테이블 설정 ─── */}
-
-
-
스케줄 데이터를 저장/조회할 테이블을 설정해요
-
-
-
+
+
+
+
+
+
{/* 스케줄 타입 */}
-
스케줄 타입
+
스케줄 타입