feat: 엔티티 타입 컬럼 표시 설정을 화면 편집기로 이동
- 테이블 타입 관리에서 엔티티 타입의 표시 컬럼 설정 완전 제거 - 컬럼 설정 패널에서 엔티티 타입일 때 표시 컬럼 조합 선택 기능 추가 - 기본 테이블과 조인 테이블의 컬럼을 자유롭게 조합 가능 - 구분자 설정 및 실시간 미리보기 기능 포함 - 별도 모달 방식 제거하고 기존 컬럼 설정 패널에 통합
This commit is contained in:
parent
4aefb5be6a
commit
de6c7a8008
|
|
@ -74,7 +74,10 @@ export class EntityJoinController {
|
||||||
typeof screenEntityConfigs === "string"
|
typeof screenEntityConfigs === "string"
|
||||||
? JSON.parse(screenEntityConfigs)
|
? JSON.parse(screenEntityConfigs)
|
||||||
: screenEntityConfigs;
|
: screenEntityConfigs;
|
||||||
logger.info("화면별 엔티티 설정 파싱 완료:", parsedScreenEntityConfigs);
|
logger.info(
|
||||||
|
"화면별 엔티티 설정 파싱 완료:",
|
||||||
|
parsedScreenEntityConfigs
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn("화면별 엔티티 설정 파싱 오류:", error);
|
logger.warn("화면별 엔티티 설정 파싱 오류:", error);
|
||||||
parsedScreenEntityConfigs = {};
|
parsedScreenEntityConfigs = {};
|
||||||
|
|
@ -365,14 +368,16 @@ export class EntityJoinController {
|
||||||
);
|
);
|
||||||
|
|
||||||
// 현재 display_column으로 사용 중인 컬럼 제외
|
// 현재 display_column으로 사용 중인 컬럼 제외
|
||||||
|
const currentDisplayColumn =
|
||||||
|
config.displayColumn || config.displayColumns[0];
|
||||||
const availableColumns = columns.filter(
|
const availableColumns = columns.filter(
|
||||||
(col) => col.columnName !== config.displayColumn
|
(col) => col.columnName !== currentDisplayColumn
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
joinConfig: config,
|
joinConfig: config,
|
||||||
tableName: config.referenceTable,
|
tableName: config.referenceTable,
|
||||||
currentDisplayColumn: config.displayColumn,
|
currentDisplayColumn: currentDisplayColumn,
|
||||||
availableColumns: availableColumns.map((col) => ({
|
availableColumns: availableColumns.map((col) => ({
|
||||||
columnName: col.columnName,
|
columnName: col.columnName,
|
||||||
columnLabel: col.displayName || col.columnName,
|
columnLabel: col.displayName || col.columnName,
|
||||||
|
|
@ -390,7 +395,8 @@ export class EntityJoinController {
|
||||||
return {
|
return {
|
||||||
joinConfig: config,
|
joinConfig: config,
|
||||||
tableName: config.referenceTable,
|
tableName: config.referenceTable,
|
||||||
currentDisplayColumn: config.displayColumn,
|
currentDisplayColumn:
|
||||||
|
config.displayColumn || config.displayColumns[0],
|
||||||
availableColumns: [],
|
availableColumns: [],
|
||||||
error: error instanceof Error ? error.message : "Unknown error",
|
error: error instanceof Error ? error.message : "Unknown error",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export class EntityJoinService {
|
||||||
* @param screenEntityConfigs 화면별 엔티티 설정 (선택사항)
|
* @param screenEntityConfigs 화면별 엔티티 설정 (선택사항)
|
||||||
*/
|
*/
|
||||||
async detectEntityJoins(
|
async detectEntityJoins(
|
||||||
tableName: string,
|
tableName: string,
|
||||||
screenEntityConfigs?: Record<string, any>
|
screenEntityConfigs?: Record<string, any>
|
||||||
): Promise<EntityJoinConfig[]> {
|
): Promise<EntityJoinConfig[]> {
|
||||||
try {
|
try {
|
||||||
|
|
@ -57,7 +57,7 @@ export class EntityJoinService {
|
||||||
const screenConfig = screenEntityConfigs?.[column.column_name];
|
const screenConfig = screenEntityConfigs?.[column.column_name];
|
||||||
let displayColumns: string[] = [];
|
let displayColumns: string[] = [];
|
||||||
let separator = " - ";
|
let separator = " - ";
|
||||||
|
|
||||||
if (screenConfig && screenConfig.displayColumns) {
|
if (screenConfig && screenConfig.displayColumns) {
|
||||||
// 화면에서 설정된 표시 컬럼들 사용
|
// 화면에서 설정된 표시 컬럼들 사용
|
||||||
displayColumns = screenConfig.displayColumns;
|
displayColumns = screenConfig.displayColumns;
|
||||||
|
|
@ -66,8 +66,11 @@ export class EntityJoinService {
|
||||||
// 기존 설정된 단일 표시 컬럼 사용
|
// 기존 설정된 단일 표시 컬럼 사용
|
||||||
displayColumns = [column.display_column];
|
displayColumns = [column.display_column];
|
||||||
} else {
|
} else {
|
||||||
// 기본값: reference_column 사용
|
// 화면에서 설정하도록 빈 배열로 초기화 (테이블 타입 관리에서 표시 컬럼 설정 제거)
|
||||||
displayColumns = [column.reference_column];
|
displayColumns = [];
|
||||||
|
console.log(
|
||||||
|
`🎯 표시 컬럼을 화면에서 설정하도록 초기화: ${column.column_name} (테이블 타입 관리에서 표시 컬럼 설정 제거됨)`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 별칭 컬럼명 생성 (writer -> writer_name)
|
// 별칭 컬럼명 생성 (writer -> writer_name)
|
||||||
|
|
@ -153,16 +156,18 @@ export class EntityJoinService {
|
||||||
const joinColumns = joinConfigs
|
const joinColumns = joinConfigs
|
||||||
.map((config) => {
|
.map((config) => {
|
||||||
const alias = aliasMap.get(config.referenceTable);
|
const alias = aliasMap.get(config.referenceTable);
|
||||||
const displayColumns = config.displayColumns || [config.displayColumn];
|
const displayColumns = config.displayColumns || [
|
||||||
|
config.displayColumn,
|
||||||
|
];
|
||||||
const separator = config.separator || " - ";
|
const separator = config.separator || " - ";
|
||||||
|
|
||||||
if (displayColumns.length === 1) {
|
if (displayColumns.length === 1) {
|
||||||
// 단일 컬럼인 경우
|
// 단일 컬럼인 경우
|
||||||
return `COALESCE(${alias}.${displayColumns[0]}, '') AS ${config.aliasColumn}`;
|
return `COALESCE(${alias}.${displayColumns[0]}, '') AS ${config.aliasColumn}`;
|
||||||
} else {
|
} else {
|
||||||
// 여러 컬럼인 경우 CONCAT으로 연결
|
// 여러 컬럼인 경우 CONCAT으로 연결
|
||||||
const concatParts = displayColumns
|
const concatParts = displayColumns
|
||||||
.map(col => `COALESCE(${alias}.${col}, '')`)
|
.map((col) => `COALESCE(${alias}.${col}, '')`)
|
||||||
.join(`, '${separator}', `);
|
.join(`, '${separator}', `);
|
||||||
return `CONCAT(${concatParts}) AS ${config.aliasColumn}`;
|
return `CONCAT(${concatParts}) AS ${config.aliasColumn}`;
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +241,7 @@ export class EntityJoinService {
|
||||||
const cachedData = await referenceCacheService.getCachedReference(
|
const cachedData = await referenceCacheService.getCachedReference(
|
||||||
config.referenceTable,
|
config.referenceTable,
|
||||||
config.referenceColumn,
|
config.referenceColumn,
|
||||||
config.displayColumn
|
config.displayColumn || config.displayColumns[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
return cachedData ? "cache" : "join";
|
return cachedData ? "cache" : "join";
|
||||||
|
|
|
||||||
|
|
@ -2044,7 +2044,10 @@ export class TableManagementService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entity 조인 설정 감지 (화면별 엔티티 설정 전달)
|
// Entity 조인 설정 감지 (화면별 엔티티 설정 전달)
|
||||||
let joinConfigs = await entityJoinService.detectEntityJoins(tableName, options.screenEntityConfigs);
|
let joinConfigs = await entityJoinService.detectEntityJoins(
|
||||||
|
tableName,
|
||||||
|
options.screenEntityConfigs
|
||||||
|
);
|
||||||
|
|
||||||
// 추가 조인 컬럼 정보가 있으면 조인 설정에 추가
|
// 추가 조인 컬럼 정보가 있으면 조인 설정에 추가
|
||||||
if (
|
if (
|
||||||
|
|
@ -2068,8 +2071,10 @@ export class TableManagementService {
|
||||||
sourceColumn: baseJoinConfig.sourceColumn, // 원본 컬럼 (writer)
|
sourceColumn: baseJoinConfig.sourceColumn, // 원본 컬럼 (writer)
|
||||||
referenceTable: additionalColumn.sourceTable, // 참조 테이블 (user_info)
|
referenceTable: additionalColumn.sourceTable, // 참조 테이블 (user_info)
|
||||||
referenceColumn: baseJoinConfig.referenceColumn, // 참조 키 (user_id)
|
referenceColumn: baseJoinConfig.referenceColumn, // 참조 키 (user_id)
|
||||||
displayColumn: additionalColumn.sourceColumn, // 표시할 컬럼 (email)
|
displayColumns: [additionalColumn.sourceColumn], // 표시할 컬럼들 (email)
|
||||||
|
displayColumn: additionalColumn.sourceColumn, // 하위 호환성
|
||||||
aliasColumn: additionalColumn.joinAlias, // 별칭 (writer_email)
|
aliasColumn: additionalColumn.joinAlias, // 별칭 (writer_email)
|
||||||
|
separator: " - ", // 기본 구분자
|
||||||
};
|
};
|
||||||
|
|
||||||
joinConfigs.push(additionalJoinConfig);
|
joinConfigs.push(additionalJoinConfig);
|
||||||
|
|
@ -2243,7 +2248,7 @@ export class TableManagementService {
|
||||||
await referenceCacheService.getCachedReference(
|
await referenceCacheService.getCachedReference(
|
||||||
config.referenceTable,
|
config.referenceTable,
|
||||||
config.referenceColumn,
|
config.referenceColumn,
|
||||||
config.displayColumn
|
config.displayColumn || config.displayColumns[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2430,7 +2435,7 @@ export class TableManagementService {
|
||||||
const lookupValue = referenceCacheService.getLookupValue(
|
const lookupValue = referenceCacheService.getLookupValue(
|
||||||
config.referenceTable,
|
config.referenceTable,
|
||||||
config.referenceColumn,
|
config.referenceColumn,
|
||||||
config.displayColumn,
|
config.displayColumn || config.displayColumns[0],
|
||||||
String(sourceValue)
|
String(sourceValue)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -2724,7 +2729,7 @@ export class TableManagementService {
|
||||||
const cachedData = await referenceCacheService.getCachedReference(
|
const cachedData = await referenceCacheService.getCachedReference(
|
||||||
config.referenceTable,
|
config.referenceTable,
|
||||||
config.referenceColumn,
|
config.referenceColumn,
|
||||||
config.displayColumn
|
config.displayColumn || config.displayColumns[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cachedData && cachedData.size > 0) {
|
if (cachedData && cachedData.size > 0) {
|
||||||
|
|
@ -2808,7 +2813,7 @@ export class TableManagementService {
|
||||||
const cachedData = await referenceCacheService.getCachedReference(
|
const cachedData = await referenceCacheService.getCachedReference(
|
||||||
config.referenceTable,
|
config.referenceTable,
|
||||||
config.referenceColumn,
|
config.referenceColumn,
|
||||||
config.displayColumn
|
config.displayColumn || config.displayColumns[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
|
|
@ -2847,7 +2852,7 @@ export class TableManagementService {
|
||||||
const hitRate = referenceCacheService.getCacheHitRate(
|
const hitRate = referenceCacheService.getCacheHitRate(
|
||||||
config.referenceTable,
|
config.referenceTable,
|
||||||
config.referenceColumn,
|
config.referenceColumn,
|
||||||
config.displayColumn
|
config.displayColumn || config.displayColumns[0]
|
||||||
);
|
);
|
||||||
totalHitRate += hitRate;
|
totalHitRate += hitRate;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
|
||||||
{/* 표시 컬럼들 (다중 선택) */}
|
{/* 표시 컬럼들 (다중 선택) */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label className="text-sm font-medium">표시 컬럼들</Label>
|
<Label className="text-sm font-medium">표시 컬럼들</Label>
|
||||||
|
|
||||||
{/* 현재 선택된 표시 컬럼들 */}
|
{/* 현재 선택된 표시 컬럼들 */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{localValues.displayColumns.map((column, index) => (
|
{localValues.displayColumns.map((column, index) => (
|
||||||
|
|
@ -183,7 +183,7 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{localValues.displayColumns.length === 0 && (
|
{localValues.displayColumns.length === 0 && (
|
||||||
<div className="text-sm text-gray-500 italic">표시할 컬럼을 추가해주세요</div>
|
<div className="text-sm text-gray-500 italic">표시할 컬럼을 추가해주세요</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -197,20 +197,19 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
|
||||||
placeholder="컬럼명 입력 (예: user_name, dept_name)"
|
placeholder="컬럼명 입력 (예: user_name, dept_name)"
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={addDisplayColumn}
|
onClick={addDisplayColumn}
|
||||||
disabled={!newDisplayColumn.trim() || localValues.displayColumns.includes(newDisplayColumn.trim())}
|
disabled={!newDisplayColumn.trim() || localValues.displayColumns.includes(newDisplayColumn.trim())}
|
||||||
>
|
>
|
||||||
<Plus className="h-3 w-3 mr-1" />
|
<Plus className="mr-1 h-3 w-3" />
|
||||||
추가
|
추가
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
• 여러 컬럼을 선택하면 "{localValues.separator || ' - '}"로 구분하여 표시됩니다
|
• 여러 컬럼을 선택하면 "{localValues.separator || " - "}"로 구분하여 표시됩니다
|
||||||
<br />
|
<br />• 예: 이름{localValues.separator || " - "}부서명
|
||||||
• 예: 이름{localValues.separator || ' - '}부서명
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -261,7 +260,6 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{/* 필터 관리 */}
|
{/* 필터 관리 */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label className="text-sm font-medium">데이터 필터</Label>
|
<Label className="text-sm font-medium">데이터 필터</Label>
|
||||||
|
|
@ -328,7 +326,10 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
|
||||||
<div className="mt-2 text-xs text-gray-500">
|
<div className="mt-2 text-xs text-gray-500">
|
||||||
참조테이블: {localValues.referenceTable || "없음"}, 조인컬럼: {localValues.referenceColumn}
|
참조테이블: {localValues.referenceTable || "없음"}, 조인컬럼: {localValues.referenceColumn}
|
||||||
<br />
|
<br />
|
||||||
표시컬럼: {localValues.displayColumns.length > 0 ? localValues.displayColumns.join(localValues.separator || ' - ') : "없음"}
|
표시컬럼:{" "}
|
||||||
|
{localValues.displayColumns.length > 0
|
||||||
|
? localValues.displayColumns.join(localValues.separator || " - ")
|
||||||
|
: "없음"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -337,14 +338,11 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
|
||||||
<div className="text-sm font-medium text-blue-900">엔터티 타입 설정 가이드</div>
|
<div className="text-sm font-medium text-blue-900">엔터티 타입 설정 가이드</div>
|
||||||
<div className="mt-1 text-xs text-blue-800">
|
<div className="mt-1 text-xs text-blue-800">
|
||||||
• <strong>참조 테이블</strong>: 데이터를 가져올 다른 테이블 이름
|
• <strong>참조 테이블</strong>: 데이터를 가져올 다른 테이블 이름
|
||||||
<br />
|
<br />• <strong>조인 컬럼</strong>: 테이블 간 연결에 사용할 기준 컬럼 (보통 ID)
|
||||||
• <strong>조인 컬럼</strong>: 테이블 간 연결에 사용할 기준 컬럼 (보통 ID)
|
<br />• <strong>표시 컬럼</strong>: 사용자에게 보여질 컬럼들 (여러 개 가능)
|
||||||
<br />
|
|
||||||
• <strong>표시 컬럼</strong>: 사용자에게 보여질 컬럼들 (여러 개 가능)
|
|
||||||
<br />
|
<br />
|
||||||
• 여러 표시 컬럼 설정 시 화면마다 다르게 표시할 수 있습니다
|
• 여러 표시 컬럼 설정 시 화면마다 다르게 표시할 수 있습니다
|
||||||
<br />
|
<br />• 예: 사용자 선택 시 "이름"만 보이거나 "이름 - 부서명" 형태로 표시
|
||||||
• 예: 사용자 선택 시 "이름"만 보이거나 "이름 - 부서명" 형태로 표시
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ export const entityJoinApi = {
|
||||||
sourceColumn: string;
|
sourceColumn: string;
|
||||||
joinAlias: string;
|
joinAlias: string;
|
||||||
}>;
|
}>;
|
||||||
|
screenEntityConfigs?: Record<string, any>; // 🎯 화면별 엔티티 설정
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<EntityJoinResponse> => {
|
): Promise<EntityJoinResponse> => {
|
||||||
const searchParams = new URLSearchParams();
|
const searchParams = new URLSearchParams();
|
||||||
|
|
@ -93,6 +94,7 @@ export const entityJoinApi = {
|
||||||
...params,
|
...params,
|
||||||
search: params.search ? JSON.stringify(params.search) : undefined,
|
search: params.search ? JSON.stringify(params.search) : undefined,
|
||||||
additionalJoinColumns: params.additionalJoinColumns ? JSON.stringify(params.additionalJoinColumns) : undefined,
|
additionalJoinColumns: params.additionalJoinColumns ? JSON.stringify(params.additionalJoinColumns) : undefined,
|
||||||
|
screenEntityConfigs: params.screenEntityConfigs ? JSON.stringify(params.screenEntityConfigs) : undefined, // 🎯 화면별 엔티티 설정
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
|
|
|
||||||
|
|
@ -192,12 +192,12 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
// 🎯 Entity 조인된 컬럼의 경우 표시 컬럼명 사용
|
// 🎯 Entity 조인된 컬럼의 경우 표시 컬럼명 사용
|
||||||
let displayLabel = column.displayName || column.columnName;
|
let displayLabel = column.displayName || column.columnName;
|
||||||
|
|
||||||
// Entity 타입이고 display_column이 있는 경우
|
// Entity 타입인 경우
|
||||||
if (column.webType === "entity" && column.displayColumn) {
|
if (column.webType === "entity") {
|
||||||
// 백엔드에서 받은 displayColumnLabel을 사용하거나, 없으면 displayColumn 사용
|
// 백엔드에서 받은 displayColumnLabel을 사용하거나, 없으면 기본값 사용
|
||||||
displayLabel = column.displayColumnLabel || column.displayColumn;
|
displayLabel = column.displayColumnLabel || column.displayColumn || `${column.columnName}_name`;
|
||||||
console.log(
|
console.log(
|
||||||
`🎯 Entity 조인 컬럼 라벨 설정: ${column.columnName} → "${displayLabel}" (${column.displayColumn})`,
|
`🎯 Entity 조인 컬럼 라벨 설정: ${column.columnName} → "${displayLabel}" (${column.displayColumn || "기본값"})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,7 +260,20 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
joinAlias: col.entityJoinInfo!.joinAlias,
|
joinAlias: col.entityJoinInfo!.joinAlias,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// 🎯 화면별 엔티티 표시 설정 생성
|
||||||
|
const screenEntityConfigs: Record<string, any> = {};
|
||||||
|
entityJoinColumns.forEach((col) => {
|
||||||
|
if (col.entityDisplayConfig) {
|
||||||
|
const sourceColumn = col.entityJoinInfo!.sourceColumn;
|
||||||
|
screenEntityConfigs[sourceColumn] = {
|
||||||
|
displayColumns: col.entityDisplayConfig.displayColumns,
|
||||||
|
separator: col.entityDisplayConfig.separator || " - ",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
console.log("🔗 추가 Entity 조인 컬럼:", additionalJoinColumns);
|
console.log("🔗 추가 Entity 조인 컬럼:", additionalJoinColumns);
|
||||||
|
console.log("🎯 화면별 엔티티 설정:", screenEntityConfigs);
|
||||||
|
|
||||||
const result = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, {
|
const result = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, {
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
|
|
@ -329,6 +342,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
sortOrder: sortDirection,
|
sortOrder: sortDirection,
|
||||||
enableEntityJoin: true, // 🎯 Entity 조인 활성화
|
enableEntityJoin: true, // 🎯 Entity 조인 활성화
|
||||||
additionalJoinColumns: additionalJoinColumns.length > 0 ? additionalJoinColumns : undefined, // 추가 조인 컬럼
|
additionalJoinColumns: additionalJoinColumns.length > 0 ? additionalJoinColumns : undefined, // 추가 조인 컬럼
|
||||||
|
screenEntityConfigs: Object.keys(screenEntityConfigs).length > 0 ? screenEntityConfigs : undefined, // 🎯 화면별 엔티티 설정
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,17 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||||
}>;
|
}>;
|
||||||
}>;
|
}>;
|
||||||
}>({ availableColumns: [], joinTables: [] });
|
}>({ availableColumns: [], joinTables: [] });
|
||||||
|
|
||||||
const [loadingEntityJoins, setLoadingEntityJoins] = useState(false);
|
const [loadingEntityJoins, setLoadingEntityJoins] = useState(false);
|
||||||
|
|
||||||
|
// 🎯 엔티티 컬럼 표시 설정을 위한 상태
|
||||||
|
const [entityDisplayConfigs, setEntityDisplayConfigs] = useState<Record<string, {
|
||||||
|
sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||||
|
joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||||
|
selectedColumns: string[];
|
||||||
|
separator: string;
|
||||||
|
}>>({});
|
||||||
|
|
||||||
// 화면 테이블명이 있으면 자동으로 설정
|
// 화면 테이블명이 있으면 자동으로 설정
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (screenTableName && (!config.selectedTable || config.selectedTable !== screenTableName)) {
|
if (screenTableName && (!config.selectedTable || config.selectedTable !== screenTableName)) {
|
||||||
|
|
@ -228,30 +237,38 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||||
handleChange("columns", [...(config.columns || []), newColumn]);
|
handleChange("columns", [...(config.columns || []), newColumn]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Entity 조인 컬럼 추가
|
// 🎯 엔티티 컬럼 추가 (컬럼 설정 패널에서 표시 컬럼 선택)
|
||||||
const addEntityJoinColumn = (joinColumn: (typeof entityJoinColumns.availableColumns)[0]) => {
|
const addEntityColumn = (joinColumn: (typeof entityJoinColumns.availableColumns)[0]) => {
|
||||||
const existingColumn = config.columns?.find((col) => col.columnName === joinColumn.joinAlias);
|
const existingColumn = config.columns?.find((col) => col.columnName === joinColumn.joinAlias);
|
||||||
if (existingColumn) return;
|
if (existingColumn) return;
|
||||||
|
|
||||||
|
// 기본 표시명으로 엔티티 컬럼 추가 (컬럼 설정 패널에서 나중에 표시 컬럼 조합 선택)
|
||||||
const newColumn: ColumnConfig = {
|
const newColumn: ColumnConfig = {
|
||||||
columnName: joinColumn.joinAlias,
|
columnName: joinColumn.joinAlias,
|
||||||
displayName: joinColumn.columnLabel, // 라벨명만 사용
|
displayName: joinColumn.columnLabel,
|
||||||
visible: true,
|
visible: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
searchable: true,
|
searchable: true,
|
||||||
align: "left",
|
align: "left",
|
||||||
format: "text",
|
format: "text",
|
||||||
order: config.columns?.length || 0,
|
order: config.columns?.length || 0,
|
||||||
isEntityJoin: true, // Entity 조인 컬럼임을 표시
|
isEntityJoin: true,
|
||||||
entityJoinInfo: {
|
entityJoinInfo: {
|
||||||
sourceTable: joinColumn.tableName,
|
sourceTable: config.selectedTable || "",
|
||||||
sourceColumn: joinColumn.columnName,
|
sourceColumn: joinColumn.columnName,
|
||||||
joinAlias: joinColumn.joinAlias,
|
joinAlias: joinColumn.joinAlias,
|
||||||
},
|
},
|
||||||
|
// 🎯 엔티티 표시 설정 (기본값으로 초기화, 컬럼 설정에서 수정 가능)
|
||||||
|
entityDisplayConfig: {
|
||||||
|
displayColumns: [], // 빈 배열로 초기화
|
||||||
|
separator: " - ",
|
||||||
|
sourceTable: config.selectedTable || "",
|
||||||
|
joinTable: joinColumn.tableName,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChange("columns", [...(config.columns || []), newColumn]);
|
handleChange("columns", [...(config.columns || []), newColumn]);
|
||||||
console.log("🔗 Entity 조인 컬럼 추가됨:", newColumn);
|
console.log("🔗 엔티티 컬럼 추가됨 (표시 컬럼은 컬럼 설정에서 선택):", newColumn);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 컬럼 제거
|
// 컬럼 제거
|
||||||
|
|
@ -267,6 +284,90 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||||
handleChange("columns", updatedColumns);
|
handleChange("columns", updatedColumns);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 🎯 엔티티 컬럼의 표시 컬럼 정보 로드
|
||||||
|
const loadEntityDisplayConfig = async (column: ColumnConfig) => {
|
||||||
|
if (!column.isEntityJoin || !column.entityJoinInfo || !column.entityDisplayConfig) return;
|
||||||
|
|
||||||
|
const { sourceTable, joinTable } = column.entityDisplayConfig;
|
||||||
|
const configKey = `${column.columnName}`;
|
||||||
|
|
||||||
|
// 이미 로드된 경우 스킵
|
||||||
|
if (entityDisplayConfigs[configKey]) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 기본 테이블과 조인 테이블의 컬럼 정보를 병렬로 로드
|
||||||
|
const [sourceResult, joinResult] = await Promise.all([
|
||||||
|
entityJoinApi.getReferenceTableColumns(sourceTable),
|
||||||
|
entityJoinApi.getReferenceTableColumns(joinTable),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const sourceColumns = sourceResult.columns || [];
|
||||||
|
const joinColumns = joinResult.columns || [];
|
||||||
|
|
||||||
|
setEntityDisplayConfigs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[configKey]: {
|
||||||
|
sourceColumns,
|
||||||
|
joinColumns,
|
||||||
|
selectedColumns: column.entityDisplayConfig?.displayColumns || [],
|
||||||
|
separator: column.entityDisplayConfig?.separator || " - ",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("엔티티 표시 컬럼 정보 로드 실패:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 🎯 엔티티 표시 컬럼 선택 토글
|
||||||
|
const toggleEntityDisplayColumn = (columnName: string, selectedColumn: string) => {
|
||||||
|
const configKey = `${columnName}`;
|
||||||
|
const config = entityDisplayConfigs[configKey];
|
||||||
|
if (!config) return;
|
||||||
|
|
||||||
|
const newSelectedColumns = config.selectedColumns.includes(selectedColumn)
|
||||||
|
? config.selectedColumns.filter(col => col !== selectedColumn)
|
||||||
|
: [...config.selectedColumns, selectedColumn];
|
||||||
|
|
||||||
|
setEntityDisplayConfigs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[configKey]: {
|
||||||
|
...prev[configKey],
|
||||||
|
selectedColumns: newSelectedColumns,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 컬럼 설정 업데이트
|
||||||
|
updateColumn(columnName, {
|
||||||
|
entityDisplayConfig: {
|
||||||
|
...config.entityDisplayConfig,
|
||||||
|
displayColumns: newSelectedColumns,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 🎯 엔티티 표시 구분자 업데이트
|
||||||
|
const updateEntityDisplaySeparator = (columnName: string, separator: string) => {
|
||||||
|
const configKey = `${columnName}`;
|
||||||
|
const config = entityDisplayConfigs[configKey];
|
||||||
|
if (!config) return;
|
||||||
|
|
||||||
|
setEntityDisplayConfigs(prev => ({
|
||||||
|
...prev,
|
||||||
|
[configKey]: {
|
||||||
|
...prev[configKey],
|
||||||
|
separator,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 컬럼 설정 업데이트
|
||||||
|
updateColumn(columnName, {
|
||||||
|
entityDisplayConfig: {
|
||||||
|
...config.entityDisplayConfig,
|
||||||
|
separator,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 컬럼 순서 변경
|
// 컬럼 순서 변경
|
||||||
const moveColumn = (columnName: string, direction: "up" | "down") => {
|
const moveColumn = (columnName: string, direction: "up" | "down") => {
|
||||||
const columns = [...(config.columns || [])];
|
const columns = [...(config.columns || [])];
|
||||||
|
|
@ -820,6 +921,108 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 🎯 엔티티 타입 컬럼일 때 표시 컬럼 선택 UI */}
|
||||||
|
{column.isEntityJoin && column.entityDisplayConfig && (
|
||||||
|
<div className="col-span-2 space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Label className="text-xs font-medium">표시 컬럼 조합</Label>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => loadEntityDisplayConfig(column)}
|
||||||
|
className="h-6 text-xs"
|
||||||
|
>
|
||||||
|
<Plus className="h-3 w-3 mr-1" />
|
||||||
|
컬럼 로드
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{entityDisplayConfigs[column.columnName] && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{/* 구분자 설정 */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-xs">구분자</Label>
|
||||||
|
<Input
|
||||||
|
value={entityDisplayConfigs[column.columnName].separator}
|
||||||
|
onChange={(e) => updateEntityDisplaySeparator(column.columnName, e.target.value)}
|
||||||
|
className="h-7 text-xs"
|
||||||
|
placeholder=" - "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 기본 테이블 컬럼 */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-xs text-blue-600">
|
||||||
|
기본 테이블: {column.entityDisplayConfig.sourceTable}
|
||||||
|
</Label>
|
||||||
|
<div className="grid grid-cols-2 gap-1 max-h-20 overflow-y-auto">
|
||||||
|
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||||
|
<div key={col.columnName} className="flex items-center space-x-1">
|
||||||
|
<Checkbox
|
||||||
|
id={`source-${column.columnName}-${col.columnName}`}
|
||||||
|
checked={entityDisplayConfigs[column.columnName].selectedColumns.includes(col.columnName)}
|
||||||
|
onCheckedChange={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||||
|
className="h-3 w-3"
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`source-${column.columnName}-${col.columnName}`}
|
||||||
|
className="text-xs cursor-pointer flex-1"
|
||||||
|
>
|
||||||
|
{col.displayName}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 조인 테이블 컬럼 */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-xs text-green-600">
|
||||||
|
조인 테이블: {column.entityDisplayConfig.joinTable}
|
||||||
|
</Label>
|
||||||
|
<div className="grid grid-cols-2 gap-1 max-h-20 overflow-y-auto">
|
||||||
|
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||||
|
<div key={col.columnName} className="flex items-center space-x-1">
|
||||||
|
<Checkbox
|
||||||
|
id={`join-${column.columnName}-${col.columnName}`}
|
||||||
|
checked={entityDisplayConfigs[column.columnName].selectedColumns.includes(col.columnName)}
|
||||||
|
onCheckedChange={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||||
|
className="h-3 w-3"
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor={`join-${column.columnName}-${col.columnName}`}
|
||||||
|
className="text-xs cursor-pointer flex-1"
|
||||||
|
>
|
||||||
|
{col.displayName}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 선택된 컬럼 미리보기 */}
|
||||||
|
{entityDisplayConfigs[column.columnName].selectedColumns.length > 0 && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-xs">미리보기</Label>
|
||||||
|
<div className="flex flex-wrap gap-1 rounded bg-gray-50 p-2 text-xs">
|
||||||
|
{entityDisplayConfigs[column.columnName].selectedColumns.map((colName, idx) => (
|
||||||
|
<React.Fragment key={colName}>
|
||||||
|
<Badge variant="secondary" className="text-xs">
|
||||||
|
{colName}
|
||||||
|
</Badge>
|
||||||
|
{idx < entityDisplayConfigs[column.columnName].selectedColumns.length - 1 && (
|
||||||
|
<span className="text-gray-400">{entityDisplayConfigs[column.columnName].separator}</span>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label className="text-xs">정렬</Label>
|
<Label className="text-xs">정렬</Label>
|
||||||
<Select
|
<Select
|
||||||
|
|
@ -1018,7 +1221,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => addEntityJoinColumn(matchingJoinColumn)}
|
onClick={() => addEntityColumn(matchingJoinColumn)}
|
||||||
className="text-xs"
|
className="text-xs"
|
||||||
>
|
>
|
||||||
<Plus className="mr-1 h-3 w-3" />
|
<Plus className="mr-1 h-3 w-3" />
|
||||||
|
|
@ -1057,7 +1260,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||||
key={index}
|
key={index}
|
||||||
variant={isAlreadyAdded ? "secondary" : "outline"}
|
variant={isAlreadyAdded ? "secondary" : "outline"}
|
||||||
className="cursor-pointer text-xs"
|
className="cursor-pointer text-xs"
|
||||||
onClick={() => !isAlreadyAdded && addEntityJoinColumn(column)}
|
onClick={() => !isAlreadyAdded && addEntityColumn(column)}
|
||||||
>
|
>
|
||||||
{column.columnLabel}
|
{column.columnLabel}
|
||||||
{!isAlreadyAdded && <Plus className="ml-1 h-2 w-2" />}
|
{!isAlreadyAdded && <Plus className="ml-1 h-2 w-2" />}
|
||||||
|
|
@ -1325,6 +1528,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,14 @@ export interface ColumnConfig {
|
||||||
isEntityJoin?: boolean; // Entity 조인된 컬럼인지 여부
|
isEntityJoin?: boolean; // Entity 조인된 컬럼인지 여부
|
||||||
entityJoinInfo?: EntityJoinInfo; // Entity 조인 상세 정보
|
entityJoinInfo?: EntityJoinInfo; // Entity 조인 상세 정보
|
||||||
|
|
||||||
|
// 🎯 엔티티 컬럼 표시 설정 (화면별 동적 설정)
|
||||||
|
entityDisplayConfig?: {
|
||||||
|
displayColumns: string[]; // 표시할 컬럼들 (기본 테이블 + 조인 테이블)
|
||||||
|
separator?: string; // 구분자 (기본: " - ")
|
||||||
|
sourceTable?: string; // 기본 테이블명
|
||||||
|
joinTable?: string; // 조인 테이블명
|
||||||
|
};
|
||||||
|
|
||||||
// 컬럼 고정 관련 속성
|
// 컬럼 고정 관련 속성
|
||||||
fixed?: "left" | "right" | false; // 컬럼 고정 위치 (왼쪽, 오른쪽, 고정 안함)
|
fixed?: "left" | "right" | false; // 컬럼 고정 위치 (왼쪽, 오른쪽, 고정 안함)
|
||||||
fixedOrder?: number; // 고정된 컬럼들 내에서의 순서
|
fixedOrder?: number; // 고정된 컬럼들 내에서의 순서
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ export interface EntityTypeConfig {
|
||||||
searchColumns?: string[];
|
searchColumns?: string[];
|
||||||
filters?: Record<string, unknown>;
|
filters?: Record<string, unknown>;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
displayFormat?: 'simple' | 'detailed' | 'custom'; // 표시 형식
|
displayFormat?: "simple" | "detailed" | "custom"; // 표시 형식
|
||||||
separator?: string; // 여러 컬럼 표시 시 구분자 (기본: ' - ')
|
separator?: string; // 여러 컬럼 표시 시 구분자 (기본: ' - ')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue