범용폼모달 외부소스 지원
This commit is contained in:
@@ -710,6 +710,9 @@ interface ColumnSettingItemProps {
|
||||
displayColumns: string[]; // 검색 설정에서 선택한 표시 컬럼 목록
|
||||
sourceTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string }[]; // 소스 테이블 컬럼
|
||||
sourceTableName: string; // 소스 테이블명
|
||||
externalTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string }[]; // 외부 데이터 테이블 컬럼
|
||||
externalTableName?: string; // 외부 데이터 테이블명
|
||||
externalDataEnabled?: boolean; // 외부 데이터 소스 활성화 여부
|
||||
tables: { table_name: string; comment?: string }[]; // 전체 테이블 목록
|
||||
tableColumns: Record<string, { column_name: string; data_type: string; is_nullable: string; comment?: string }[]>; // 테이블별 컬럼
|
||||
sections: { id: string; title: string }[]; // 섹션 목록
|
||||
@@ -731,6 +734,9 @@ function ColumnSettingItem({
|
||||
displayColumns,
|
||||
sourceTableColumns,
|
||||
sourceTableName,
|
||||
externalTableColumns,
|
||||
externalTableName,
|
||||
externalDataEnabled,
|
||||
tables,
|
||||
tableColumns,
|
||||
sections,
|
||||
@@ -745,6 +751,7 @@ function ColumnSettingItem({
|
||||
}: ColumnSettingItemProps) {
|
||||
const [fieldSearchOpen, setFieldSearchOpen] = useState(false);
|
||||
const [sourceFieldSearchOpen, setSourceFieldSearchOpen] = useState(false);
|
||||
const [externalFieldSearchOpen, setExternalFieldSearchOpen] = useState(false);
|
||||
const [parentFieldSearchOpen, setParentFieldSearchOpen] = useState(false);
|
||||
const [lookupTableOpenMap, setLookupTableOpenMap] = useState<Record<string, boolean>>({});
|
||||
|
||||
@@ -1014,6 +1021,88 @@ function ColumnSettingItem({
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
{/* 외부 필드 - Combobox (외부 데이터에서 가져올 컬럼) */}
|
||||
{externalDataEnabled && externalTableName && (
|
||||
<div>
|
||||
<Label className="text-xs">외부 필드</Label>
|
||||
<Popover open={externalFieldSearchOpen} onOpenChange={setExternalFieldSearchOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={externalFieldSearchOpen}
|
||||
className="h-8 w-full justify-between text-xs mt-1"
|
||||
disabled={externalTableColumns.length === 0}
|
||||
>
|
||||
<span className="truncate">
|
||||
{col.externalField || "(필드명과 동일)"}
|
||||
</span>
|
||||
<ChevronsUpDown className="ml-1 h-3 w-3 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0 w-[250px]" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="외부 필드 검색..." className="text-xs" />
|
||||
<CommandList className="max-h-[200px]">
|
||||
<CommandEmpty className="text-xs py-4 text-center">
|
||||
외부 필드를 찾을 수 없습니다.
|
||||
</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{/* 필드명과 동일 옵션 */}
|
||||
<CommandItem
|
||||
value="__same_as_field__"
|
||||
onSelect={() => {
|
||||
onUpdate({ externalField: undefined });
|
||||
setExternalFieldSearchOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
!col.externalField ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<span className="text-muted-foreground">(필드명과 동일)</span>
|
||||
</CommandItem>
|
||||
{/* 외부 테이블 컬럼 목록 */}
|
||||
{externalTableColumns.map((extCol) => (
|
||||
<CommandItem
|
||||
key={extCol.column_name}
|
||||
value={extCol.column_name}
|
||||
onSelect={() => {
|
||||
onUpdate({ externalField: extCol.column_name });
|
||||
setExternalFieldSearchOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
col.externalField === extCol.column_name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-col flex-1 min-w-0">
|
||||
<span className="font-medium truncate">{extCol.column_name}</span>
|
||||
{extCol.comment && (
|
||||
<span className="text-[10px] text-muted-foreground truncate">
|
||||
{extCol.comment}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<p className="text-[10px] text-muted-foreground mt-0.5">
|
||||
외부 데이터({externalTableName})에서 이 컬럼에 매핑할 필드
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 라벨 */}
|
||||
<div>
|
||||
<Label className="text-xs">라벨</Label>
|
||||
@@ -2450,6 +2539,7 @@ export function TableSectionSettingsModal({
|
||||
// 테이블 검색 Combobox 상태
|
||||
const [tableSearchOpen, setTableSearchOpen] = useState(false);
|
||||
const [saveTableSearchOpen, setSaveTableSearchOpen] = useState(false);
|
||||
const [externalTableSearchOpen, setExternalTableSearchOpen] = useState(false);
|
||||
|
||||
// 활성 탭
|
||||
const [activeTab, setActiveTab] = useState("source");
|
||||
@@ -2623,6 +2713,24 @@ export function TableSectionSettingsModal({
|
||||
});
|
||||
};
|
||||
|
||||
const updateExternalDataSource = (updates: Partial<NonNullable<TableSectionConfig["externalDataSource"]>>) => {
|
||||
updateTableConfig({
|
||||
externalDataSource: { ...tableConfig.externalDataSource, enabled: false, tableName: "", ...updates },
|
||||
});
|
||||
};
|
||||
|
||||
// 외부 데이터 소스 테이블 컬럼 목록
|
||||
const externalTableColumns = useMemo(() => {
|
||||
return tableColumns[tableConfig.externalDataSource?.tableName || ""] || [];
|
||||
}, [tableColumns, tableConfig.externalDataSource?.tableName]);
|
||||
|
||||
// 외부 데이터 소스 테이블 변경 시 컬럼 로드
|
||||
useEffect(() => {
|
||||
if (tableConfig.externalDataSource?.enabled && tableConfig.externalDataSource?.tableName) {
|
||||
onLoadTableColumns(tableConfig.externalDataSource.tableName);
|
||||
}
|
||||
}, [tableConfig.externalDataSource?.enabled, tableConfig.externalDataSource?.tableName, onLoadTableColumns]);
|
||||
|
||||
// 저장 함수
|
||||
const handleSave = () => {
|
||||
onSave({
|
||||
@@ -2986,6 +3094,98 @@ export function TableSectionSettingsModal({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 외부 데이터 소스 설정 */}
|
||||
<div className="space-y-3 border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium">외부 데이터 소스</h4>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
"데이터 전달 모달열기" 액션으로 전달받은 데이터를 테이블에 표시합니다.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={tableConfig.externalDataSource?.enabled ?? false}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
updateExternalDataSource({ enabled: true, tableName: "" });
|
||||
} else {
|
||||
updateTableConfig({ externalDataSource: undefined });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{tableConfig.externalDataSource?.enabled && (
|
||||
<div className="space-y-3 pt-2">
|
||||
<div>
|
||||
<Label className="text-xs font-medium mb-1.5 block">외부 데이터 테이블</Label>
|
||||
<Popover open={externalTableSearchOpen} onOpenChange={setExternalTableSearchOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={externalTableSearchOpen}
|
||||
className="h-9 w-full justify-between text-sm"
|
||||
>
|
||||
{tableConfig.externalDataSource?.tableName || "테이블 선택..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0 w-full min-w-[400px]" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="테이블 검색..." className="text-sm" />
|
||||
<CommandList className="max-h-[300px]">
|
||||
<CommandEmpty className="text-sm py-6 text-center">
|
||||
테이블을 찾을 수 없습니다.
|
||||
</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{tables.map((table) => (
|
||||
<CommandItem
|
||||
key={table.table_name}
|
||||
value={table.table_name}
|
||||
onSelect={() => {
|
||||
updateExternalDataSource({ enabled: true, tableName: table.table_name });
|
||||
onLoadTableColumns(table.table_name);
|
||||
setExternalTableSearchOpen(false);
|
||||
}}
|
||||
className="text-sm"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
tableConfig.externalDataSource?.tableName === table.table_name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{table.table_name}</span>
|
||||
{table.comment && (
|
||||
<span className="text-xs text-muted-foreground">{table.comment}</span>
|
||||
)}
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<HelpText>이전 화면에서 전달받을 데이터의 원본 테이블을 선택하세요. (예: 수주상세 데이터를 전달받는 경우 sales_order_detail)</HelpText>
|
||||
</div>
|
||||
|
||||
{tableConfig.externalDataSource?.tableName && externalTableColumns.length > 0 && (
|
||||
<div className="bg-muted/30 rounded-lg p-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
선택한 테이블 컬럼: {externalTableColumns.length}개
|
||||
</p>
|
||||
<p className="text-[10px] text-muted-foreground mt-1">
|
||||
"컬럼 설정" 탭에서 각 컬럼의 "외부 필드"를 설정하여 전달받은 데이터의 컬럼을 매핑하세요.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* 컬럼 설정 탭 */}
|
||||
@@ -3041,6 +3241,9 @@ export function TableSectionSettingsModal({
|
||||
displayColumns={tableConfig.source.displayColumns || []}
|
||||
sourceTableColumns={sourceTableColumns}
|
||||
sourceTableName={tableConfig.source.tableName}
|
||||
externalTableColumns={externalTableColumns}
|
||||
externalTableName={tableConfig.externalDataSource?.tableName}
|
||||
externalDataEnabled={tableConfig.externalDataSource?.enabled}
|
||||
tables={tables}
|
||||
tableColumns={tableColumns}
|
||||
sections={otherSections}
|
||||
|
||||
Reference in New Issue
Block a user