feat(TableSection): 테이블 컬럼 부모값 받기 기능 추가
TableColumnConfig에 receiveFromParent, parentFieldName 속성 추가 allComponents에서 부모 화면 필드 자동 추출 컬럼 설정에 "부모값" 스위치 및 부모 필드 선택 UI 추가 handleAddItems()에서 부모값 자동 적용 로직 구현
This commit is contained in:
@@ -48,13 +48,24 @@ const HelpText = ({ children }: { children: React.ReactNode }) => (
|
||||
<p className="text-[10px] text-muted-foreground mt-0.5">{children}</p>
|
||||
);
|
||||
|
||||
export function UniversalFormModalConfigPanel({ config, onChange }: UniversalFormModalConfigPanelProps) {
|
||||
// 부모 화면에서 전달 가능한 필드 타입
|
||||
interface AvailableParentField {
|
||||
name: string; // 필드명 (columnName)
|
||||
label: string; // 표시 라벨
|
||||
sourceComponent?: string; // 출처 컴포넌트 (예: "TableList", "SplitPanelLayout2")
|
||||
sourceTable?: string; // 출처 테이블명
|
||||
}
|
||||
|
||||
export function UniversalFormModalConfigPanel({ config, onChange, allComponents = [] }: UniversalFormModalConfigPanelProps) {
|
||||
// 테이블 목록
|
||||
const [tables, setTables] = useState<{ name: string; label: string }[]>([]);
|
||||
const [tableColumns, setTableColumns] = useState<{
|
||||
[tableName: string]: { name: string; type: string; label: string }[];
|
||||
}>({});
|
||||
|
||||
// 부모 화면에서 전달 가능한 필드 목록
|
||||
const [availableParentFields, setAvailableParentFields] = useState<AvailableParentField[]>([]);
|
||||
|
||||
// 채번규칙 목록
|
||||
const [numberingRules, setNumberingRules] = useState<{ id: string; name: string }[]>([]);
|
||||
|
||||
@@ -72,6 +83,186 @@ export function UniversalFormModalConfigPanel({ config, onChange }: UniversalFor
|
||||
loadNumberingRules();
|
||||
}, []);
|
||||
|
||||
// allComponents에서 부모 화면에서 전달 가능한 필드 추출
|
||||
useEffect(() => {
|
||||
const extractParentFields = async () => {
|
||||
if (!allComponents || allComponents.length === 0) {
|
||||
setAvailableParentFields([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const fields: AvailableParentField[] = [];
|
||||
|
||||
for (const comp of allComponents) {
|
||||
// 컴포넌트 타입 추출 (여러 위치에서 확인)
|
||||
const compType = comp.componentId || comp.componentConfig?.type || comp.componentConfig?.id || comp.type;
|
||||
const compConfig = comp.componentConfig || {};
|
||||
|
||||
// 1. TableList / InteractiveDataTable - 테이블 컬럼 추출
|
||||
if (compType === "table-list" || compType === "interactive-data-table") {
|
||||
const tableName = compConfig.selectedTable || compConfig.tableName;
|
||||
if (tableName) {
|
||||
// 테이블 컬럼 로드
|
||||
try {
|
||||
const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
|
||||
const columns = response.data?.data?.columns;
|
||||
if (response.data?.success && Array.isArray(columns)) {
|
||||
columns.forEach((col: any) => {
|
||||
const colName = col.columnName || col.column_name;
|
||||
const colLabel = col.displayName || col.columnComment || col.column_comment || colName;
|
||||
fields.push({
|
||||
name: colName,
|
||||
label: colLabel,
|
||||
sourceComponent: "TableList",
|
||||
sourceTable: tableName,
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`테이블 컬럼 로드 실패 (${tableName}):`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. SplitPanelLayout2 - 데이터 전달 필드 및 소스 테이블 컬럼 추출
|
||||
if (compType === "split-panel-layout2") {
|
||||
// dataTransferFields 추출
|
||||
const transferFields = compConfig.dataTransferFields;
|
||||
if (transferFields && Array.isArray(transferFields)) {
|
||||
transferFields.forEach((field: any) => {
|
||||
if (field.targetColumn) {
|
||||
fields.push({
|
||||
name: field.targetColumn,
|
||||
label: field.targetColumn,
|
||||
sourceComponent: "SplitPanelLayout2",
|
||||
sourceTable: compConfig.leftPanel?.tableName,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 좌측 패널 테이블 컬럼도 추출
|
||||
const leftTableName = compConfig.leftPanel?.tableName;
|
||||
if (leftTableName) {
|
||||
try {
|
||||
const response = await apiClient.get(`/table-management/tables/${leftTableName}/columns`);
|
||||
const columns = response.data?.data?.columns;
|
||||
if (response.data?.success && Array.isArray(columns)) {
|
||||
columns.forEach((col: any) => {
|
||||
const colName = col.columnName || col.column_name;
|
||||
const colLabel = col.displayName || col.columnComment || col.column_comment || colName;
|
||||
// 중복 방지
|
||||
if (!fields.some(f => f.name === colName && f.sourceTable === leftTableName)) {
|
||||
fields.push({
|
||||
name: colName,
|
||||
label: colLabel,
|
||||
sourceComponent: "SplitPanelLayout2 (좌측)",
|
||||
sourceTable: leftTableName,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`테이블 컬럼 로드 실패 (${leftTableName}):`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 기타 테이블 관련 컴포넌트
|
||||
if (compType === "card-display" || compType === "simple-repeater-table") {
|
||||
const tableName = compConfig.tableName || compConfig.initialDataConfig?.sourceTable;
|
||||
if (tableName) {
|
||||
try {
|
||||
const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
|
||||
const columns = response.data?.data?.columns;
|
||||
if (response.data?.success && Array.isArray(columns)) {
|
||||
columns.forEach((col: any) => {
|
||||
const colName = col.columnName || col.column_name;
|
||||
const colLabel = col.displayName || col.columnComment || col.column_comment || colName;
|
||||
if (!fields.some(f => f.name === colName && f.sourceTable === tableName)) {
|
||||
fields.push({
|
||||
name: colName,
|
||||
label: colLabel,
|
||||
sourceComponent: compType,
|
||||
sourceTable: tableName,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`테이블 컬럼 로드 실패 (${tableName}):`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 버튼 컴포넌트 - openModalWithData의 fieldMappings/dataMapping에서 소스 컬럼 추출
|
||||
if (compType === "button-primary" || compType === "button" || compType === "button-secondary") {
|
||||
const action = compConfig.action || {};
|
||||
|
||||
// fieldMappings에서 소스 컬럼 추출
|
||||
const fieldMappings = action.fieldMappings || [];
|
||||
fieldMappings.forEach((mapping: any) => {
|
||||
if (mapping.sourceColumn && !fields.some(f => f.name === mapping.sourceColumn)) {
|
||||
fields.push({
|
||||
name: mapping.sourceColumn,
|
||||
label: mapping.sourceColumn,
|
||||
sourceComponent: "Button (fieldMappings)",
|
||||
sourceTable: action.sourceTableName,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// dataMapping에서 소스 컬럼 추출
|
||||
const dataMapping = action.dataMapping || [];
|
||||
dataMapping.forEach((mapping: any) => {
|
||||
if (mapping.sourceColumn && !fields.some(f => f.name === mapping.sourceColumn)) {
|
||||
fields.push({
|
||||
name: mapping.sourceColumn,
|
||||
label: mapping.sourceColumn,
|
||||
sourceComponent: "Button (dataMapping)",
|
||||
sourceTable: action.sourceTableName,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 현재 모달의 저장 테이블 컬럼도 추가 (부모에서 전달받을 수 있는 값들)
|
||||
const currentTableName = config.saveConfig?.tableName;
|
||||
if (currentTableName) {
|
||||
try {
|
||||
const response = await apiClient.get(`/table-management/tables/${currentTableName}/columns`);
|
||||
const columns = response.data?.data?.columns;
|
||||
if (response.data?.success && Array.isArray(columns)) {
|
||||
columns.forEach((col: any) => {
|
||||
const colName = col.columnName || col.column_name;
|
||||
const colLabel = col.displayName || col.columnComment || col.column_comment || colName;
|
||||
if (!fields.some(f => f.name === colName)) {
|
||||
fields.push({
|
||||
name: colName,
|
||||
label: colLabel,
|
||||
sourceComponent: "현재 폼 테이블",
|
||||
sourceTable: currentTableName,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`현재 테이블 컬럼 로드 실패 (${currentTableName}):`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 중복 제거 (같은 name이면 첫 번째만 유지)
|
||||
const uniqueFields = fields.filter((field, index, self) =>
|
||||
index === self.findIndex(f => f.name === field.name)
|
||||
);
|
||||
|
||||
setAvailableParentFields(uniqueFields);
|
||||
};
|
||||
|
||||
extractParentFields();
|
||||
}, [allComponents, config.saveConfig?.tableName]);
|
||||
|
||||
// 저장 테이블 변경 시 컬럼 로드
|
||||
useEffect(() => {
|
||||
if (config.saveConfig.tableName) {
|
||||
@@ -85,9 +276,10 @@ export function UniversalFormModalConfigPanel({ config, onChange }: UniversalFor
|
||||
const data = response.data?.data;
|
||||
if (response.data?.success && Array.isArray(data)) {
|
||||
setTables(
|
||||
data.map((t: { tableName?: string; table_name?: string; tableLabel?: string; table_label?: string }) => ({
|
||||
data.map((t: { tableName?: string; table_name?: string; displayName?: string; tableLabel?: string; table_label?: string }) => ({
|
||||
name: t.tableName || t.table_name || "",
|
||||
label: t.tableLabel || t.table_label || t.tableName || t.table_name || "",
|
||||
// displayName 우선, 없으면 tableLabel, 그것도 없으면 테이블명
|
||||
label: t.displayName || t.tableLabel || t.table_label || "",
|
||||
})),
|
||||
);
|
||||
}
|
||||
@@ -620,6 +812,12 @@ export function UniversalFormModalConfigPanel({ config, onChange }: UniversalFor
|
||||
setSelectedField(field);
|
||||
setFieldDetailModalOpen(true);
|
||||
}}
|
||||
tableName={config.saveConfig.tableName}
|
||||
tableColumns={tableColumns[config.saveConfig.tableName || ""]?.map(col => ({
|
||||
name: col.name,
|
||||
type: col.type,
|
||||
label: col.label || col.name
|
||||
})) || []}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -666,6 +864,7 @@ export function UniversalFormModalConfigPanel({ config, onChange }: UniversalFor
|
||||
tableColumns={tableColumns}
|
||||
numberingRules={numberingRules}
|
||||
onLoadTableColumns={loadTableColumns}
|
||||
availableParentFields={availableParentFields}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -706,6 +905,7 @@ export function UniversalFormModalConfigPanel({ config, onChange }: UniversalFor
|
||||
)}
|
||||
onLoadTableColumns={loadTableColumns}
|
||||
allSections={config.sections as FormSectionConfig[]}
|
||||
availableParentFields={availableParentFields}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user