다국어 지원 및 테이블 설정 현황 문서를 업데이트하고, SplitPanelLayoutConfigPanel에서 좌측 패널 테이블 선택 기능을 추가했습니다. 또한, 조인 키를 연결 키로 변경하고, 조건 필터 모드에 대한 설명을 수정하여 사용자 경험을 개선했습니다.
This commit is contained in:
@@ -388,9 +388,9 @@ const AdditionalTabConfigPanel: React.FC<AdditionalTabConfigPanelProps> = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ===== 4. 컬럼 매핑 (조인 키) ===== */}
|
||||
{/* ===== 4. 컬럼 매핑 (연결 키) ===== */}
|
||||
<div className="space-y-3 rounded-lg border bg-white p-3">
|
||||
<Label className="text-xs font-semibold text-blue-600">컬럼 매핑 (조인 키)</Label>
|
||||
<Label className="text-xs font-semibold text-blue-600">컬럼 매핑 (연결 키)</Label>
|
||||
<p className="text-[10px] text-gray-500">
|
||||
좌측 패널 선택 시 관련 데이터만 표시합니다
|
||||
</p>
|
||||
@@ -1067,10 +1067,11 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
screenTableName, // 현재 화면의 테이블명
|
||||
menuObjid, // 🆕 메뉴 OBJID
|
||||
}) => {
|
||||
const [leftTableOpen, setLeftTableOpen] = useState(false); // 🆕 좌측 테이블 Combobox 상태
|
||||
const [rightTableOpen, setRightTableOpen] = useState(false);
|
||||
const [loadedTableColumns, setLoadedTableColumns] = useState<Record<string, ColumnInfo[]>>({});
|
||||
const [loadingColumns, setLoadingColumns] = useState<Record<string, boolean>>({});
|
||||
const [allTables, setAllTables] = useState<any[]>([]); // 조인 모드용 전체 테이블 목록
|
||||
const [allTables, setAllTables] = useState<any[]>([]); // 테이블 목록
|
||||
// 엔티티 참조 테이블 컬럼
|
||||
type EntityRefTable = { tableName: string; columns: ColumnInfo[] };
|
||||
const [entityReferenceTables, setEntityReferenceTables] = useState<Record<string, EntityRefTable[]>>({});
|
||||
@@ -1095,35 +1096,27 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
}
|
||||
}, [config.leftPanel?.title, config.rightPanel?.title, isUserEditing]);
|
||||
|
||||
// 조인 모드일 때만 전체 테이블 목록 로드
|
||||
// 전체 테이블 목록 항상 로드 (좌측/우측 모두 사용)
|
||||
useEffect(() => {
|
||||
if (relationshipType === "join") {
|
||||
const loadAllTables = async () => {
|
||||
try {
|
||||
const { tableManagementApi } = await import("@/lib/api/tableManagement");
|
||||
const response = await tableManagementApi.getTableList();
|
||||
if (response.success && response.data) {
|
||||
console.log("✅ 분할패널 조인 모드: 전체 테이블 목록 로드", response.data.length, "개");
|
||||
setAllTables(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 전체 테이블 목록 로드 실패:", error);
|
||||
const loadAllTables = async () => {
|
||||
try {
|
||||
const { tableManagementApi } = await import("@/lib/api/tableManagement");
|
||||
const response = await tableManagementApi.getTableList();
|
||||
if (response.success && response.data) {
|
||||
console.log("✅ 분할패널: 전체 테이블 목록 로드", response.data.length, "개");
|
||||
setAllTables(response.data);
|
||||
}
|
||||
};
|
||||
loadAllTables();
|
||||
} else {
|
||||
// 상세 모드일 때는 기본 테이블만 사용
|
||||
setAllTables([]);
|
||||
}
|
||||
}, [relationshipType]);
|
||||
|
||||
// screenTableName이 변경되면 좌측 패널 테이블을 항상 화면 테이블로 설정
|
||||
useEffect(() => {
|
||||
if (screenTableName) {
|
||||
// 좌측 패널은 항상 현재 화면의 테이블 사용
|
||||
if (config.leftPanel?.tableName !== screenTableName) {
|
||||
updateLeftPanel({ tableName: screenTableName });
|
||||
} catch (error) {
|
||||
console.error("❌ 전체 테이블 목록 로드 실패:", error);
|
||||
}
|
||||
};
|
||||
loadAllTables();
|
||||
}, []);
|
||||
|
||||
// 초기 로드 시 좌측 패널 테이블이 없으면 화면 테이블로 설정
|
||||
useEffect(() => {
|
||||
if (screenTableName && !config.leftPanel?.tableName) {
|
||||
updateLeftPanel({ tableName: screenTableName });
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [screenTableName]);
|
||||
@@ -1345,8 +1338,13 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
return leftTableName ? loadedTableColumns[leftTableName] || [] : [];
|
||||
}, [loadedTableColumns, leftTableName]);
|
||||
|
||||
// 우측 테이블명
|
||||
const rightTableName = config.rightPanel?.tableName || "";
|
||||
// 우측 테이블명 (상세 모드에서는 좌측과 동일)
|
||||
const rightTableName = useMemo(() => {
|
||||
if (relationshipType === "detail") {
|
||||
return leftTableName; // 상세 모드에서는 좌측과 동일
|
||||
}
|
||||
return config.rightPanel?.tableName || "";
|
||||
}, [relationshipType, leftTableName, config.rightPanel?.tableName]);
|
||||
|
||||
// 우측 테이블 컬럼 (로드된 컬럼 사용)
|
||||
const rightTableColumns = useMemo(() => {
|
||||
@@ -1406,8 +1404,8 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
</SelectItem>
|
||||
<SelectItem value="join">
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">조인 (JOIN)</span>
|
||||
<span className="text-xs text-gray-500">좌측 테이블 → 우측 관련 테이블 (다른 테이블)</span>
|
||||
<span className="font-medium">조건 필터 (FILTERED)</span>
|
||||
<span className="text-xs text-gray-500">좌측 선택 항목 기준으로 우측 테이블 필터링</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
@@ -1418,6 +1416,103 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
<div className="mt-4 space-y-4 border-t pt-4">
|
||||
<h3 className="text-sm font-semibold">좌측 패널 설정 (마스터)</h3>
|
||||
|
||||
{/* 🆕 좌측 패널 테이블 선택 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-medium">표시 테이블</Label>
|
||||
<Popover open={leftTableOpen} onOpenChange={setLeftTableOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={leftTableOpen}
|
||||
className="h-9 w-full justify-between text-xs"
|
||||
>
|
||||
<div className="flex items-center gap-2 truncate">
|
||||
<Database className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span className="truncate">
|
||||
{config.leftPanel?.tableName
|
||||
? allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.displayName ||
|
||||
config.leftPanel?.tableName
|
||||
: "테이블을 선택하세요"}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[300px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="테이블 검색..." className="text-xs" />
|
||||
<CommandList>
|
||||
<CommandEmpty className="py-3 text-center text-xs text-muted-foreground">
|
||||
테이블을 찾을 수 없습니다.
|
||||
</CommandEmpty>
|
||||
|
||||
{/* 화면 기본 테이블 */}
|
||||
{screenTableName && (
|
||||
<CommandGroup heading="기본 (화면 테이블)">
|
||||
<CommandItem
|
||||
value={screenTableName}
|
||||
onSelect={() => {
|
||||
updateLeftPanel({ tableName: screenTableName, columns: [] });
|
||||
setLeftTableOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
config.leftPanel?.tableName === screenTableName ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<Database className="mr-2 h-3.5 w-3.5 text-blue-500" />
|
||||
{allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName ||
|
||||
screenTableName}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
)}
|
||||
|
||||
{/* 전체 테이블 */}
|
||||
<CommandGroup heading="전체 테이블">
|
||||
{allTables
|
||||
.filter((t) => (t.tableName || t.table_name) !== screenTableName)
|
||||
.map((table) => {
|
||||
const tableName = table.tableName || table.table_name;
|
||||
const displayName = table.tableLabel || table.displayName || tableName;
|
||||
return (
|
||||
<CommandItem
|
||||
key={tableName}
|
||||
value={tableName}
|
||||
onSelect={() => {
|
||||
updateLeftPanel({ tableName, columns: [] });
|
||||
setLeftTableOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
config.leftPanel?.tableName === tableName ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<Database className="mr-2 h-3.5 w-3.5 text-muted-foreground" />
|
||||
<span className="truncate">{displayName}</span>
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{config.leftPanel?.tableName && config.leftPanel?.tableName !== screenTableName && (
|
||||
<p className="text-[10px] text-muted-foreground">
|
||||
화면 기본 테이블이 아닌 다른 테이블의 데이터를 표시합니다.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>패널 제목</Label>
|
||||
<Input
|
||||
@@ -1661,7 +1756,7 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
|
||||
{/* 우측 패널 설정 */}
|
||||
<div className="mt-4 space-y-4 border-t pt-4">
|
||||
<h3 className="text-sm font-semibold">우측 패널 설정 ({relationshipType === "detail" ? "상세" : "조인"})</h3>
|
||||
<h3 className="text-sm font-semibold">우측 패널 설정 ({relationshipType === "detail" ? "상세" : "조건 필터"})</h3>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>패널 제목</Label>
|
||||
@@ -1706,9 +1801,9 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
// 조인 모드: 전체 테이블에서 선택 가능
|
||||
// 조건 필터 모드: 전체 테이블에서 선택 가능
|
||||
<div className="space-y-2">
|
||||
<Label>테이블 선택 (전체 테이블)</Label>
|
||||
<Label>필터링 대상 테이블</Label>
|
||||
<Popover open={rightTableOpen} onOpenChange={setRightTableOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
@@ -1814,12 +1909,12 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 엔티티 설정 선택 - 조인 모드에서만 표시 */}
|
||||
{/* 엔티티 설정 선택 - 조건 필터 모드에서만 표시 */}
|
||||
{relationshipType !== "detail" && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-medium">연결 컬럼 (엔티티 설정)</Label>
|
||||
<Label className="text-xs font-medium">필터 연결 컬럼</Label>
|
||||
<p className="text-muted-foreground text-[10px]">
|
||||
우측 테이블에서 좌측 테이블을 참조하는 엔티티 컬럼을 선택하세요
|
||||
우측 테이블에서 좌측 테이블을 참조하는 컬럼을 선택하세요
|
||||
</p>
|
||||
<Select
|
||||
value={config.rightPanel?.relation?.foreignKey || ""}
|
||||
|
||||
Reference in New Issue
Block a user