엔티티컬럼 표시설정 수정

This commit is contained in:
kjs
2025-12-17 17:41:29 +09:00
parent e50ddd03d3
commit 3589e4a5b9
15 changed files with 90 additions and 59 deletions

View File

@@ -93,7 +93,7 @@ export default function TableManagementPage() {
const [createTableModalOpen, setCreateTableModalOpen] = useState(false);
const [addColumnModalOpen, setAddColumnModalOpen] = useState(false);
const [ddlLogViewerOpen, setDdlLogViewerOpen] = useState(false);
// 테이블 복제 관련 상태
const [duplicateModalMode, setDuplicateModalMode] = useState<"create" | "duplicate">("create");
const [duplicateSourceTable, setDuplicateSourceTable] = useState<string | null>(null);
@@ -109,7 +109,7 @@ export default function TableManagementPage() {
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [tableToDelete, setTableToDelete] = useState<string>("");
const [isDeleting, setIsDeleting] = useState(false);
// 선택된 테이블 목록 (체크박스)
const [selectedTableIds, setSelectedTableIds] = useState<Set<string>>(new Set());
@@ -459,11 +459,39 @@ export default function TableManagementPage() {
if (!selectedTable) return;
try {
// 🎯 Entity 타입인 경우 detailSettings에 엔티티 설정을 JSON으로 포함
let finalDetailSettings = column.detailSettings || "";
if (column.inputType === "entity" && column.referenceTable) {
// 기존 detailSettings를 파싱하거나 새로 생성
let existingSettings: Record<string, unknown> = {};
if (typeof column.detailSettings === "string" && column.detailSettings.trim().startsWith("{")) {
try {
existingSettings = JSON.parse(column.detailSettings);
} catch {
existingSettings = {};
}
}
// 엔티티 설정 추가
const entitySettings = {
...existingSettings,
entityTable: column.referenceTable,
entityCodeColumn: column.referenceColumn || "id",
entityLabelColumn: column.displayColumn || "name",
placeholder: (existingSettings.placeholder as string) || "항목을 선택하세요",
searchable: existingSettings.searchable ?? true,
};
finalDetailSettings = JSON.stringify(entitySettings);
console.log("🔧 Entity 설정 JSON 생성:", entitySettings);
}
const columnSetting = {
columnName: column.columnName, // 실제 DB 컬럼명 (변경 불가)
columnLabel: column.displayName, // 사용자가 입력한 표시명
inputType: column.inputType || "text",
detailSettings: column.detailSettings || "",
detailSettings: finalDetailSettings,
codeCategory: column.codeCategory || "",
codeValue: column.codeValue || "",
referenceTable: column.referenceTable || "",
@@ -487,7 +515,7 @@ export default function TableManagementPage() {
if (response.data.success) {
console.log("✅ 컬럼 설정 저장 성공");
// 🆕 Category 타입인 경우 컬럼 매핑 처리
console.log("🔍 카테고리 조건 체크:", {
isCategory: column.inputType === "category",
@@ -547,7 +575,7 @@ export default function TableManagementPage() {
} else if (successCount > 0 && failCount > 0) {
toast.warning(`컬럼 설정 저장 성공. ${successCount}개 메뉴 매핑 성공, ${failCount}개 실패.`);
} else if (failCount > 0) {
toast.error(`컬럼 설정 저장 성공. 메뉴 매핑 생성 실패.`);
toast.error("컬럼 설정 저장 성공. 메뉴 매핑 생성 실패.");
}
} else {
toast.success("컬럼 설정이 저장되었습니다. (메뉴 매핑 없음)");
@@ -680,9 +708,7 @@ export default function TableManagementPage() {
console.log("📊 전체 매핑 결과:", { totalSuccessCount, totalFailCount });
if (totalSuccessCount > 0) {
toast.success(
`테이블 설정 및 ${totalSuccessCount}개 카테고리 메뉴 매핑이 저장되었습니다.`
);
toast.success(`테이블 설정 및 ${totalSuccessCount}개 카테고리 메뉴 매핑이 저장되었습니다.`);
} else if (totalFailCount > 0) {
toast.warning(`테이블 설정은 저장되었으나 ${totalFailCount}개 메뉴 매핑 생성 실패.`);
} else {
@@ -1000,14 +1026,15 @@ export default function TableManagementPage() {
.filter(
(table) =>
table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) ||
(table.displayName && table.displayName.toLowerCase().includes(searchTerm.toLowerCase())),
(table.displayName &&
table.displayName.toLowerCase().includes(searchTerm.toLowerCase())),
)
.every((table) => selectedTableIds.has(table.tableName))
}
onCheckedChange={handleSelectAll}
aria-label="전체 선택"
/>
<span className="text-sm text-muted-foreground">
<span className="text-muted-foreground text-sm">
{selectedTableIds.size > 0 && `${selectedTableIds.size}개 선택됨`}
</span>
</div>
@@ -1047,9 +1074,9 @@ export default function TableManagementPage() {
<div
key={table.tableName}
className={`bg-card rounded-lg p-4 shadow-sm transition-all ${
selectedTable === table.tableName
? "shadow-md bg-muted/30"
: "hover:shadow-lg hover:bg-muted/20"
selectedTable === table.tableName
? "bg-muted/30 shadow-md"
: "hover:bg-muted/20 hover:shadow-lg"
}`}
style={
selectedTable === table.tableName
@@ -1068,10 +1095,7 @@ export default function TableManagementPage() {
onClick={(e) => e.stopPropagation()}
/>
)}
<div
className="flex-1 cursor-pointer"
onClick={() => handleTableSelect(table.tableName)}
>
<div className="flex-1 cursor-pointer" onClick={() => handleTableSelect(table.tableName)}>
<h4 className="text-sm font-semibold">{table.displayName || table.tableName}</h4>
<p className="text-muted-foreground mt-1 text-xs">
{table.description || getTextFromUI(TABLE_MANAGEMENT_KEYS.TABLE_DESCRIPTION, "설명 없음")}
@@ -1147,7 +1171,10 @@ export default function TableManagementPage() {
) : (
<div className="flex flex-1 flex-col overflow-hidden">
{/* 컬럼 헤더 (고정) */}
<div className="text-foreground grid h-12 flex-shrink-0 items-center border-b px-6 py-3 text-sm font-semibold" style={{ gridTemplateColumns: "160px 200px 250px 1fr" }}>
<div
className="text-foreground grid h-12 flex-shrink-0 items-center border-b px-6 py-3 text-sm font-semibold"
style={{ gridTemplateColumns: "160px 200px 250px 1fr" }}
>
<div className="pr-4"></div>
<div className="px-4"></div>
<div className="pr-6"> </div>
@@ -1171,7 +1198,7 @@ export default function TableManagementPage() {
className="bg-background hover:bg-muted/50 grid min-h-16 items-start border-b px-6 py-3 transition-colors"
style={{ gridTemplateColumns: "160px 200px 250px 1fr" }}
>
<div className="pr-4 pt-1">
<div className="pt-1 pr-4">
<div className="font-mono text-sm">{column.columnName}</div>
</div>
<div className="px-4">
@@ -1226,9 +1253,9 @@ export default function TableManagementPage() {
<label className="text-muted-foreground mb-1 block text-xs">
(2)
</label>
<div className="border rounded-lg p-3 space-y-2 max-h-48 overflow-y-auto">
<div className="max-h-48 space-y-2 overflow-y-auto rounded-lg border p-3">
{secondLevelMenus.length === 0 ? (
<p className="text-xs text-muted-foreground">
<p className="text-muted-foreground text-xs">
2 . .
</p>
) : (
@@ -1236,7 +1263,7 @@ export default function TableManagementPage() {
// menuObjid를 숫자로 변환하여 비교
const menuObjidNum = Number(menu.menuObjid);
const isChecked = (column.categoryMenus || []).includes(menuObjidNum);
return (
<div key={menu.menuObjid} className="flex items-center gap-2">
<input
@@ -1253,15 +1280,15 @@ export default function TableManagementPage() {
prev.map((col) =>
col.columnName === column.columnName
? { ...col, categoryMenus: newMenus }
: col
)
: col,
),
);
}}
className="h-4 w-4 rounded border-gray-300 text-primary focus:ring-2 focus:ring-ring"
className="text-primary focus:ring-ring h-4 w-4 rounded border-gray-300 focus:ring-2"
/>
<label
htmlFor={`category-menu-${column.columnName}-${menu.menuObjid}`}
className="text-xs cursor-pointer flex-1"
className="flex-1 cursor-pointer text-xs"
>
{menu.parentMenuName} {menu.menuName}
</label>
@@ -1282,9 +1309,7 @@ export default function TableManagementPage() {
<>
{/* 참조 테이블 */}
<div className="w-48">
<label className="text-muted-foreground mb-1 block text-xs">
</label>
<label className="text-muted-foreground mb-1 block text-xs"> </label>
<Select
value={column.referenceTable || "none"}
onValueChange={(value) =>
@@ -1296,15 +1321,10 @@ export default function TableManagementPage() {
</SelectTrigger>
<SelectContent>
{referenceTableOptions.map((option, index) => (
<SelectItem
key={`entity-${option.value}-${index}`}
value={option.value}
>
<SelectItem key={`entity-${option.value}-${index}`} value={option.value}>
<div className="flex flex-col">
<span className="font-medium">{option.label}</span>
<span className="text-muted-foreground text-xs">
{option.value}
</span>
<span className="text-muted-foreground text-xs">{option.value}</span>
</div>
</SelectItem>
))}
@@ -1315,9 +1335,7 @@ export default function TableManagementPage() {
{/* 조인 컬럼 */}
{column.referenceTable && column.referenceTable !== "none" && (
<div className="w-48">
<label className="text-muted-foreground mb-1 block text-xs">
</label>
<label className="text-muted-foreground mb-1 block text-xs"> </label>
<Select
value={column.referenceColumn || "none"}
onValueChange={(value) =>
@@ -1361,9 +1379,7 @@ export default function TableManagementPage() {
column.referenceColumn &&
column.referenceColumn !== "none" && (
<div className="w-48">
<label className="text-muted-foreground mb-1 block text-xs">
</label>
<label className="text-muted-foreground mb-1 block text-xs"> </label>
<Select
value={column.displayColumn || "none"}
onValueChange={(value) =>
@@ -1408,7 +1424,7 @@ export default function TableManagementPage() {
column.referenceColumn !== "none" &&
column.displayColumn &&
column.displayColumn !== "none" && (
<div className="bg-primary/10 text-primary flex items-center gap-1 rounded px-2 py-1 text-xs w-48">
<div className="bg-primary/10 text-primary flex w-48 items-center gap-1 rounded px-2 py-1 text-xs">
<span></span>
<span className="truncate"> </span>
</div>
@@ -1460,9 +1476,10 @@ export default function TableManagementPage() {
setDuplicateSourceTable(null);
}}
onSuccess={async (result) => {
const message = duplicateModalMode === "duplicate"
? "테이블이 성공적으로 복제되었습니다!"
: "테이블이 성공적으로 생성되었습니다!";
const message =
duplicateModalMode === "duplicate"
? "테이블이 성공적으로 복제되었습니다!"
: "테이블이 성공적으로 생성되었습니다!";
toast.success(message);
// 테이블 목록 새로고침
await loadTables();
@@ -1516,13 +1533,10 @@ export default function TableManagementPage() {
{selectedTableIds.size > 0 ? (
<>
<strong>{selectedTableIds.size}</strong> ?
<br />
.
<br /> .
</>
) : (
<>
? .
</>
<> ? .</>
)}
</DialogDescription>
</DialogHeader>
@@ -1600,4 +1614,3 @@ export default function TableManagementPage() {
</div>
);
}