feature/screen-management #294

Merged
kjs merged 11 commits from feature/screen-management into main 2025-12-16 18:02:37 +09:00
3 changed files with 101 additions and 12 deletions
Showing only changes of commit 4e74c7b5ba - Show all commits

View File

@ -1447,7 +1447,8 @@ export class TableManagementService {
tableName,
columnName,
actualValue,
paramIndex
paramIndex,
operator // operator 전달 (equals면 직접 매칭)
);
default:
@ -1676,7 +1677,8 @@ export class TableManagementService {
tableName: string,
columnName: string,
value: any,
paramIndex: number
paramIndex: number,
operator: string = "contains" // 연결 필터에서 "equals"로 전달되면 직접 매칭
): Promise<{
whereClause: string;
values: any[];
@ -1688,7 +1690,7 @@ export class TableManagementService {
columnName
);
// 🆕 배열 처리: IN 절 사용
// 배열 처리: IN 절 사용
if (Array.isArray(value)) {
if (value.length === 0) {
// 빈 배열이면 항상 false 조건
@ -1720,13 +1722,35 @@ export class TableManagementService {
}
if (typeof value === "string" && value.trim() !== "") {
const displayColumn = entityTypeInfo.displayColumn || "name";
// equals 연산자인 경우: 직접 값 매칭 (연결 필터에서 코드 값으로 필터링 시 사용)
if (operator === "equals") {
logger.info(
`🔍 [buildEntitySearchCondition] equals 연산자 - 직접 매칭: ${columnName} = ${value}`
);
return {
whereClause: `${columnName} = $${paramIndex}`,
values: [value],
paramCount: 1,
};
}
// contains 연산자 (기본): 참조 테이블의 표시 컬럼으로 검색
const referenceColumn = entityTypeInfo.referenceColumn || "id";
const referenceTable = entityTypeInfo.referenceTable;
// displayColumn이 비어있거나 "none"이면 참조 테이블에서 자동 감지 (entityJoinService와 동일한 로직)
let displayColumn = entityTypeInfo.displayColumn;
if (!displayColumn || displayColumn === "none" || displayColumn === "") {
displayColumn = await this.findDisplayColumnForTable(referenceTable, referenceColumn);
logger.info(
`🔍 [buildEntitySearchCondition] displayColumn 자동 감지: ${referenceTable} -> ${displayColumn}`
);
}
// 참조 테이블의 표시 컬럼으로 검색
return {
whereClause: `EXISTS (
SELECT 1 FROM ${entityTypeInfo.referenceTable} ref
SELECT 1 FROM ${referenceTable} ref
WHERE ref.${referenceColumn} = ${columnName}
AND ref.${displayColumn} ILIKE $${paramIndex}
)`,
@ -1754,6 +1778,66 @@ export class TableManagementService {
}
}
/**
* (entityJoinService와 )
* : *_name > name > label/*_label > title > referenceColumn
*/
private async findDisplayColumnForTable(
tableName: string,
referenceColumn?: string
): Promise<string> {
try {
const result = await query<{ column_name: string }>(
`SELECT column_name
FROM information_schema.columns
WHERE table_name = $1
AND table_schema = 'public'
ORDER BY ordinal_position`,
[tableName]
);
const allColumns = result.map((r) => r.column_name);
// entityJoinService와 동일한 우선순위
// 1. *_name 컬럼 (item_name, customer_name, process_name 등) - company_name 제외
const nameColumn = allColumns.find(
(col) => col.endsWith("_name") && col !== "company_name"
);
if (nameColumn) {
return nameColumn;
}
// 2. name 컬럼
if (allColumns.includes("name")) {
return "name";
}
// 3. label 또는 *_label 컬럼
const labelColumn = allColumns.find(
(col) => col === "label" || col.endsWith("_label")
);
if (labelColumn) {
return labelColumn;
}
// 4. title 컬럼
if (allColumns.includes("title")) {
return "title";
}
// 5. 참조 컬럼 (referenceColumn)
if (referenceColumn && allColumns.includes(referenceColumn)) {
return referenceColumn;
}
// 6. 기본값: 첫 번째 비-id 컬럼 또는 id
return allColumns.find((col) => col !== "id") || "id";
} catch (error) {
logger.error(`표시 컬럼 감지 실패: ${tableName}`, error);
return referenceColumn || "id"; // 오류 시 기본값
}
}
/**
*
*/

View File

@ -233,18 +233,20 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
linkedFilterValues = splitPanelContext.getLinkedFilterValues();
// 현재 테이블에 해당하는 필터만 추출 (테이블명.컬럼명 형식에서)
// 연결 필터는 코드 값이므로 정확한 매칭(equals)을 사용해야 함
const tableSpecificFilters: Record<string, any> = {};
for (const [key, value] of Object.entries(linkedFilterValues)) {
// key가 "테이블명.컬럼명" 형식인 경우
if (key.includes(".")) {
const [tblName, columnName] = key.split(".");
if (tblName === tableNameToUse) {
tableSpecificFilters[columnName] = value;
// 연결 필터는 코드 값이므로 equals 연산자 사용
tableSpecificFilters[columnName] = { value, operator: "equals" };
hasLinkedFiltersConfigured = true;
}
} else {
// 테이블명 없이 컬럼명만 있는 경우 그대로 사용
tableSpecificFilters[key] = value;
// 테이블명 없이 컬럼명만 있는 경우 그대로 사용 (equals)
tableSpecificFilters[key] = { value, operator: "equals" };
}
}
linkedFilterValues = tableSpecificFilters;

View File

@ -1526,16 +1526,18 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
console.log("🔗 [TableList] 연결 필터 원본:", allLinkedFilters);
// 현재 테이블에 해당하는 필터만 추출 (테이블명.컬럼명 형식에서)
// 연결 필터는 코드 값이므로 정확한 매칭(equals)을 사용해야 함
for (const [key, value] of Object.entries(allLinkedFilters)) {
if (key.includes(".")) {
const [tableName, columnName] = key.split(".");
if (tableName === tableConfig.selectedTable) {
linkedFilterValues[columnName] = value;
// 연결 필터는 코드 값이므로 equals 연산자 사용
linkedFilterValues[columnName] = { value, operator: "equals" };
hasLinkedFiltersConfigured = true; // 이 테이블에 대한 필터가 있음
}
} else {
// 테이블명 없이 컬럼명만 있는 경우 그대로 사용
linkedFilterValues[key] = value;
// 테이블명 없이 컬럼명만 있는 경우 그대로 사용 (equals)
linkedFilterValues[key] = { value, operator: "equals" };
}
}
@ -1560,7 +1562,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 현재 테이블에 동일한 컬럼이 있는지 확인
if (tableColumns.includes(colName)) {
linkedFilterValues[colName] = colValue;
// 자동 컬럼 매칭도 equals 연산자 사용
linkedFilterValues[colName] = { value: colValue, operator: "equals" };
hasLinkedFiltersConfigured = true;
console.log(`🔗 [TableList] 자동 컬럼 매칭: ${colName} = ${colValue}`);
}