카테고리값 자동감지
This commit is contained in:
@@ -398,6 +398,9 @@ export function TableSectionRenderer({
|
||||
// 소스 테이블의 컬럼 라벨 (API에서 동적 로드)
|
||||
const [sourceColumnLabels, setSourceColumnLabels] = useState<Record<string, string>>({});
|
||||
|
||||
// 카테고리 타입 컬럼의 옵션 (column.type === "category")
|
||||
const [categoryOptionsMap, setCategoryOptionsMap] = useState<Record<string, { value: string; label: string }[]>>({});
|
||||
|
||||
// 외부 데이터(groupedData) 처리: 데이터 전달 모달열기 액션으로 전달받은 데이터를 초기 테이블 데이터로 설정
|
||||
useEffect(() => {
|
||||
// 외부 데이터 소스가 활성화되지 않았거나, groupedData가 없으면 스킵
|
||||
@@ -511,6 +514,46 @@ export function TableSectionRenderer({
|
||||
loadColumnLabels();
|
||||
}, [tableConfig.source.tableName, tableConfig.source.columnLabels]);
|
||||
|
||||
// 카테고리 타입 컬럼의 옵션 로드
|
||||
useEffect(() => {
|
||||
const loadCategoryOptions = async () => {
|
||||
const sourceTableName = tableConfig.source.tableName;
|
||||
if (!sourceTableName) return;
|
||||
if (!tableConfig.columns) return;
|
||||
|
||||
// 카테고리 타입인 컬럼만 필터링
|
||||
const categoryColumns = tableConfig.columns.filter((col) => col.type === "category");
|
||||
if (categoryColumns.length === 0) return;
|
||||
|
||||
const newOptionsMap: Record<string, { value: string; label: string }[]> = {};
|
||||
|
||||
for (const col of categoryColumns) {
|
||||
// 소스 필드 또는 필드명으로 카테고리 값 조회
|
||||
const actualColumnName = col.sourceField || col.field;
|
||||
if (!actualColumnName) continue;
|
||||
|
||||
try {
|
||||
const { getCategoryValues } = await import("@/lib/api/tableCategoryValue");
|
||||
const result = await getCategoryValues(sourceTableName, actualColumnName, false);
|
||||
|
||||
if (result && result.success && Array.isArray(result.data)) {
|
||||
const options = result.data.map((item: any) => ({
|
||||
value: item.valueCode || item.value_code || item.value || "",
|
||||
label: item.valueLabel || item.displayLabel || item.display_label || item.label || item.valueCode || item.value_code || item.value || "",
|
||||
}));
|
||||
newOptionsMap[col.field] = options;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`카테고리 옵션 로드 실패 (${col.field}):`, error);
|
||||
}
|
||||
}
|
||||
|
||||
setCategoryOptionsMap((prev) => ({ ...prev, ...newOptionsMap }));
|
||||
};
|
||||
|
||||
loadCategoryOptions();
|
||||
}, [tableConfig.source.tableName, tableConfig.columns]);
|
||||
|
||||
// 조건부 테이블: 동적 옵션 로드 (optionSource 설정이 있는 경우)
|
||||
useEffect(() => {
|
||||
if (!isConditionalMode) return;
|
||||
@@ -952,9 +995,15 @@ export function TableSectionRenderer({
|
||||
baseColumn.selectOptions = dynamicSelectOptionsMap[col.field];
|
||||
}
|
||||
|
||||
// 카테고리 타입인 경우 옵션 적용 및 select 타입으로 변환
|
||||
if (col.type === "category" && categoryOptionsMap[col.field]) {
|
||||
baseColumn.type = "select"; // RepeaterTable에서 select로 렌더링
|
||||
baseColumn.selectOptions = categoryOptionsMap[col.field];
|
||||
}
|
||||
|
||||
return baseColumn;
|
||||
});
|
||||
}, [tableConfig.columns, dynamicSelectOptionsMap]);
|
||||
}, [tableConfig.columns, dynamicSelectOptionsMap, categoryOptionsMap]);
|
||||
|
||||
// 원본 계산 규칙 (조건부 계산 포함)
|
||||
const originalCalculationRules: TableCalculationRule[] = useMemo(
|
||||
|
||||
@@ -308,12 +308,29 @@ export function UniversalFormModalConfigPanel({
|
||||
column_comment?: string;
|
||||
inputType?: string;
|
||||
input_type?: string;
|
||||
}) => ({
|
||||
name: c.columnName || c.column_name || "",
|
||||
type: c.dataType || c.data_type || "text",
|
||||
label: c.displayName || c.columnComment || c.column_comment || c.columnName || c.column_name || "",
|
||||
inputType: c.inputType || c.input_type || "text",
|
||||
}),
|
||||
isNullable?: string;
|
||||
is_nullable?: string;
|
||||
}) => {
|
||||
const colName = c.columnName || c.column_name || "";
|
||||
const dataType = c.dataType || c.data_type || "text";
|
||||
const inputType = c.inputType || c.input_type || "text";
|
||||
const displayName = c.displayName || c.columnComment || c.column_comment || colName;
|
||||
const isNullable = c.isNullable || c.is_nullable || "YES";
|
||||
|
||||
return {
|
||||
// camelCase (기존 호환성)
|
||||
name: colName,
|
||||
type: dataType,
|
||||
label: displayName,
|
||||
inputType: inputType,
|
||||
// snake_case (TableSectionSettingsModal 호환성)
|
||||
column_name: colName,
|
||||
data_type: dataType,
|
||||
is_nullable: isNullable,
|
||||
comment: displayName,
|
||||
input_type: inputType,
|
||||
};
|
||||
},
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -48,12 +48,12 @@ interface TableColumnSettingsModalProps {
|
||||
onOpenChange: (open: boolean) => void;
|
||||
column: TableColumnConfig;
|
||||
sourceTableName: string; // 소스 테이블명
|
||||
sourceTableColumns: { column_name: string; data_type: string; comment?: string }[];
|
||||
sourceTableColumns: { column_name: string; data_type: string; comment?: string; input_type?: string }[];
|
||||
formFields: { columnName: string; label: string; sectionId?: string; sectionTitle?: string }[]; // formData 필드 목록 (섹션 정보 포함)
|
||||
sections: { id: string; title: string }[]; // 섹션 목록
|
||||
onSave: (updatedColumn: TableColumnConfig) => void;
|
||||
tables: { table_name: string; comment?: string }[];
|
||||
tableColumns: Record<string, { column_name: string; data_type: string; is_nullable: string; comment?: string }[]>;
|
||||
tableColumns: Record<string, { column_name: string; data_type: string; is_nullable: string; comment?: string; input_type?: string }[]>;
|
||||
onLoadTableColumns: (tableName: string) => void;
|
||||
}
|
||||
|
||||
@@ -103,6 +103,18 @@ export function TableColumnSettingsModal({
|
||||
return tableColumns[externalTableName] || [];
|
||||
}, [tableColumns, externalTableName]);
|
||||
|
||||
// 소스 필드 기준으로 카테고리 타입인지 확인
|
||||
const actualSourceField = localColumn.sourceField || localColumn.field;
|
||||
const sourceColumnInfo = sourceTableColumns.find((c) => c.column_name === actualSourceField);
|
||||
const isCategoryColumn = sourceColumnInfo?.input_type === "category";
|
||||
|
||||
// 카테고리 컬럼인 경우 타입을 자동으로 category로 설정
|
||||
useEffect(() => {
|
||||
if (isCategoryColumn && localColumn.type !== "category") {
|
||||
updateColumn({ type: "category" });
|
||||
}
|
||||
}, [isCategoryColumn, localColumn.type]);
|
||||
|
||||
// 컬럼 업데이트 함수
|
||||
const updateColumn = (updates: Partial<TableColumnConfig>) => {
|
||||
setLocalColumn((prev) => ({ ...prev, ...updates }));
|
||||
@@ -574,10 +586,11 @@ export function TableColumnSettingsModal({
|
||||
<div>
|
||||
<Label className="text-xs">타입</Label>
|
||||
<Select
|
||||
value={localColumn.type}
|
||||
value={isCategoryColumn ? "category" : localColumn.type}
|
||||
onValueChange={(value: any) => updateColumn({ type: value })}
|
||||
disabled={isCategoryColumn}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs mt-1">
|
||||
<SelectTrigger className={cn("h-8 text-xs mt-1", isCategoryColumn && "opacity-70 cursor-not-allowed")}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -588,6 +601,9 @@ export function TableColumnSettingsModal({
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{isCategoryColumn && (
|
||||
<p className="text-[10px] text-blue-600 mt-0.5">테이블 타입 관리에서 카테고리로 설정됨</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">너비</Label>
|
||||
|
||||
@@ -706,15 +706,15 @@ interface ColumnSettingItemProps {
|
||||
col: TableColumnConfig;
|
||||
index: number;
|
||||
totalCount: number;
|
||||
saveTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string }[];
|
||||
saveTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string; input_type?: string }[];
|
||||
displayColumns: string[]; // 검색 설정에서 선택한 표시 컬럼 목록
|
||||
sourceTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string }[]; // 소스 테이블 컬럼
|
||||
sourceTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string; input_type?: string }[]; // 소스 테이블 컬럼
|
||||
sourceTableName: string; // 소스 테이블명
|
||||
externalTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string }[]; // 외부 데이터 테이블 컬럼
|
||||
externalTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string; input_type?: string }[]; // 외부 데이터 테이블 컬럼
|
||||
externalTableName?: string; // 외부 데이터 테이블명
|
||||
externalDataEnabled?: boolean; // 외부 데이터 소스 활성화 여부
|
||||
tables: { table_name: string; comment?: string }[]; // 전체 테이블 목록
|
||||
tableColumns: Record<string, { column_name: string; data_type: string; is_nullable: string; comment?: string }[]>; // 테이블별 컬럼
|
||||
tableColumns: Record<string, { column_name: string; data_type: string; is_nullable: string; comment?: string; input_type?: string }[]>; // 테이블별 컬럼
|
||||
sections: { id: string; title: string }[]; // 섹션 목록
|
||||
formFields: { columnName: string; label: string; sectionId?: string }[]; // 폼 필드 목록
|
||||
tableConfig: TableSectionConfig; // 현재 행 필드 목록 표시용
|
||||
@@ -755,6 +755,18 @@ function ColumnSettingItem({
|
||||
const [parentFieldSearchOpen, setParentFieldSearchOpen] = useState(false);
|
||||
const [lookupTableOpenMap, setLookupTableOpenMap] = useState<Record<string, boolean>>({});
|
||||
|
||||
// 소스 필드 기준으로 카테고리 타입인지 확인
|
||||
const actualSourceField = col.sourceField || col.field;
|
||||
const sourceColumnInfo = sourceTableColumns.find((c) => c.column_name === actualSourceField);
|
||||
const isCategoryColumn = sourceColumnInfo?.input_type === "category";
|
||||
|
||||
// 카테고리 컬럼인 경우 타입을 자동으로 category로 설정
|
||||
useEffect(() => {
|
||||
if (isCategoryColumn && col.type !== "category") {
|
||||
onUpdate({ type: "category" });
|
||||
}
|
||||
}, [isCategoryColumn, col.type, onUpdate]);
|
||||
|
||||
// 조회 옵션 추가
|
||||
const addLookupOption = () => {
|
||||
const newOption: LookupOption = {
|
||||
@@ -1117,8 +1129,12 @@ function ColumnSettingItem({
|
||||
{/* 타입 */}
|
||||
<div>
|
||||
<Label className="text-xs">타입</Label>
|
||||
<Select value={col.type} onValueChange={(value: any) => onUpdate({ type: value })}>
|
||||
<SelectTrigger className="h-8 text-xs mt-1">
|
||||
<Select
|
||||
value={isCategoryColumn ? "category" : col.type}
|
||||
onValueChange={(value: any) => onUpdate({ type: value })}
|
||||
disabled={isCategoryColumn}
|
||||
>
|
||||
<SelectTrigger className={cn("h-8 text-xs mt-1", isCategoryColumn && "opacity-70 cursor-not-allowed")}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -1129,6 +1145,9 @@ function ColumnSettingItem({
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{isCategoryColumn && (
|
||||
<p className="text-[10px] text-blue-600 mt-0.5">테이블 타입 관리에서 카테고리로 설정됨</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 너비 */}
|
||||
|
||||
@@ -899,6 +899,7 @@ export const TABLE_COLUMN_TYPE_OPTIONS = [
|
||||
{ value: "number", label: "숫자" },
|
||||
{ value: "date", label: "날짜" },
|
||||
{ value: "select", label: "선택(드롭다운)" },
|
||||
{ value: "category", label: "카테고리" },
|
||||
] as const;
|
||||
|
||||
// 값 매핑 타입 옵션
|
||||
|
||||
Reference in New Issue
Block a user