테이블에 카테고리 값 보이기
This commit is contained in:
parent
4c98839df8
commit
cf9e81a216
|
|
@ -144,6 +144,9 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
|||
const [filteredData, setFilteredData] = useState<any[]>([]); // 필터링된 데이터
|
||||
const [columnLabels, setColumnLabels] = useState<Record<string, string>>({}); // 컬럼명 -> 라벨 매핑
|
||||
|
||||
// 카테고리 값 매핑 캐시 (컬럼명 -> {코드 -> 라벨})
|
||||
const [categoryMappings, setCategoryMappings] = useState<Record<string, Record<string, string>>>({});
|
||||
|
||||
// 공통코드 옵션 가져오기
|
||||
const loadCodeOptions = useCallback(
|
||||
async (categoryCode: string) => {
|
||||
|
|
@ -191,6 +194,66 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
|||
};
|
||||
}, [currentPage, searchValues, loadData, component.tableName]);
|
||||
|
||||
// 카테고리 타입 컬럼의 값 매핑 로드
|
||||
useEffect(() => {
|
||||
const loadCategoryMappings = async () => {
|
||||
if (!component.tableName) return;
|
||||
|
||||
try {
|
||||
// 카테고리 타입 컬럼 찾기
|
||||
const categoryColumns = component.columns?.filter((col) => {
|
||||
const webType = getColumnWebType(col.columnName);
|
||||
return webType === "category";
|
||||
});
|
||||
|
||||
console.log(`🔍 InteractiveDataTable 카테고리 컬럼 확인:`, {
|
||||
tableName: component.tableName,
|
||||
totalColumns: component.columns?.length,
|
||||
categoryColumns: categoryColumns?.map(c => ({
|
||||
name: c.columnName,
|
||||
webType: getColumnWebType(c.columnName)
|
||||
}))
|
||||
});
|
||||
|
||||
if (!categoryColumns || categoryColumns.length === 0) return;
|
||||
|
||||
// 각 카테고리 컬럼의 값 목록 조회
|
||||
const mappings: Record<string, Record<string, string>> = {};
|
||||
|
||||
for (const col of categoryColumns) {
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/table-categories/${component.tableName}/${col.columnName}/values`
|
||||
);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
// valueCode -> valueLabel 매핑 생성
|
||||
const mapping: Record<string, string> = {};
|
||||
response.data.data.forEach((item: any) => {
|
||||
mapping[item.valueCode] = item.valueLabel;
|
||||
});
|
||||
mappings[col.columnName] = mapping;
|
||||
}
|
||||
} catch (error) {
|
||||
// 카테고리 값 로드 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ InteractiveDataTable 카테고리 매핑 완료:`, {
|
||||
tableName: component.tableName,
|
||||
mappedColumns: Object.keys(mappings),
|
||||
mappings
|
||||
});
|
||||
|
||||
setCategoryMappings(mappings);
|
||||
} catch (error) {
|
||||
console.error("카테고리 매핑 로드 실패:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadCategoryMappings();
|
||||
}, [component.tableName, component.columns, getColumnWebType]);
|
||||
|
||||
// 파일 상태 확인 함수
|
||||
const checkFileStatus = useCallback(
|
||||
async (rowData: Record<string, any>) => {
|
||||
|
|
@ -374,7 +437,6 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
|||
// 먼저 컴포넌트에 설정된 컬럼에서 찾기 (화면 관리에서 설정한 값 우선)
|
||||
const componentColumn = component.columns?.find((col) => col.columnName === columnName);
|
||||
if (componentColumn?.widgetType && componentColumn.widgetType !== "text") {
|
||||
console.log(`🔍 [${columnName}] componentColumn.widgetType 사용:`, componentColumn.widgetType);
|
||||
return componentColumn.widgetType;
|
||||
}
|
||||
|
||||
|
|
@ -384,14 +446,11 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
|||
// input_type 우선 사용 (category 등)
|
||||
const inputType = (tableColumn as any)?.input_type || (tableColumn as any)?.inputType;
|
||||
if (inputType) {
|
||||
console.log(`✅ [${columnName}] input_type 사용:`, inputType);
|
||||
return inputType;
|
||||
}
|
||||
|
||||
// 없으면 webType 사용
|
||||
const result = tableColumn?.webType || "text";
|
||||
console.log(`✅ [${columnName}] webType 사용:`, result);
|
||||
return result;
|
||||
return tableColumn?.webType || "text";
|
||||
},
|
||||
[component.columns, tableColumns],
|
||||
);
|
||||
|
|
@ -1862,9 +1921,12 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
|||
// 가상 파일 컬럼의 경우 value가 없어도 파일 아이콘을 표시해야 함
|
||||
if (!column.isVirtualFileColumn && (value === null || value === undefined)) return "";
|
||||
|
||||
// 실제 웹 타입 가져오기 (input_type 포함)
|
||||
const actualWebType = getColumnWebType(column.columnName);
|
||||
|
||||
// 파일 타입 컬럼 처리 (가상 파일 컬럼 포함)
|
||||
const isFileColumn =
|
||||
column.widgetType === "file" || column.columnName === "file_path" || column.isVirtualFileColumn;
|
||||
actualWebType === "file" || column.columnName === "file_path" || column.isVirtualFileColumn;
|
||||
|
||||
// 파일 타입 컬럼은 파일 아이콘으로 표시 (컬럼별 파일 관리)
|
||||
if (isFileColumn && rowData) {
|
||||
|
|
@ -1904,7 +1966,18 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
|||
);
|
||||
}
|
||||
|
||||
switch (column.widgetType) {
|
||||
// 실제 웹 타입으로 스위치 (input_type="category"도 포함됨)
|
||||
switch (actualWebType) {
|
||||
case "category": {
|
||||
// 카테고리 타입: 코드값 -> 라벨로 변환
|
||||
const mapping = categoryMappings[column.columnName];
|
||||
if (mapping && value) {
|
||||
const label = mapping[String(value)];
|
||||
return label || String(value);
|
||||
}
|
||||
return String(value || "");
|
||||
}
|
||||
|
||||
case "date":
|
||||
if (value) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -66,12 +66,21 @@ export function FlowWidget({
|
|||
const { isPreviewMode } = useScreenPreview(); // 프리뷰 모드 확인
|
||||
const { user } = useAuth(); // 사용자 정보 가져오기
|
||||
|
||||
// 숫자 포맷팅 함수
|
||||
const formatValue = (value: any): string => {
|
||||
// 값 포맷팅 함수 (숫자, 카테고리 등)
|
||||
const formatValue = useCallback((value: any, columnName?: string): string => {
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return "-";
|
||||
}
|
||||
|
||||
// 카테고리 타입: 코드값 -> 라벨로 변환
|
||||
if (columnName && categoryMappings[columnName]) {
|
||||
const mapping = categoryMappings[columnName];
|
||||
const label = mapping[String(value)];
|
||||
if (label) {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
// 숫자 타입이거나 숫자로 변환 가능한 문자열인 경우 포맷팅
|
||||
if (typeof value === "number") {
|
||||
return value.toLocaleString("ko-KR");
|
||||
|
|
@ -86,7 +95,7 @@ export function FlowWidget({
|
|||
}
|
||||
|
||||
return String(value);
|
||||
};
|
||||
}, [categoryMappings]);
|
||||
|
||||
// 🆕 전역 상태 관리
|
||||
const setSelectedStep = useFlowStepStore((state) => state.setSelectedStep);
|
||||
|
|
@ -114,6 +123,9 @@ export function FlowWidget({
|
|||
const [allAvailableColumns, setAllAvailableColumns] = useState<string[]>([]); // 전체 컬럼 목록
|
||||
const [filteredData, setFilteredData] = useState<any[]>([]); // 필터링된 데이터
|
||||
|
||||
// 카테고리 값 매핑 캐시 (컬럼명 -> {코드 -> 라벨})
|
||||
const [categoryMappings, setCategoryMappings] = useState<Record<string, Record<string, string>>>({});
|
||||
|
||||
// 🆕 그룹 설정 관련 상태
|
||||
const [isGroupSettingOpen, setIsGroupSettingOpen] = useState(false); // 그룹 설정 다이얼로그
|
||||
const [groupByColumns, setGroupByColumns] = useState<string[]>([]); // 그룹화할 컬럼 목록
|
||||
|
|
@ -677,6 +689,61 @@ export function FlowWidget({
|
|||
}
|
||||
};
|
||||
|
||||
// 카테고리 타입 컬럼의 값 매핑 로드
|
||||
useEffect(() => {
|
||||
const loadCategoryMappings = async () => {
|
||||
if (!selectedStepId || !steps.length) return;
|
||||
|
||||
try {
|
||||
const currentStep = steps.find((s) => s.id === selectedStepId);
|
||||
const tableName = currentStep?.stepConfig?.tableName;
|
||||
|
||||
if (!tableName) return;
|
||||
|
||||
// 테이블 컬럼 정보 조회하여 카테고리 타입 찾기
|
||||
const apiClient = (await import("@/lib/api/client")).apiClient;
|
||||
const columnsResponse = await apiClient.get(`/table-management/tables/${tableName}/columns`);
|
||||
|
||||
if (!columnsResponse.data?.success) return;
|
||||
|
||||
const columns = columnsResponse.data.data?.columns || [];
|
||||
const categoryColumns = columns.filter((col: any) =>
|
||||
(col.inputType === "category" || col.input_type === "category")
|
||||
);
|
||||
|
||||
if (categoryColumns.length === 0) return;
|
||||
|
||||
// 각 카테고리 컬럼의 값 목록 조회
|
||||
const mappings: Record<string, Record<string, string>> = {};
|
||||
|
||||
for (const col of categoryColumns) {
|
||||
const columnName = col.columnName || col.column_name;
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/table-categories/${tableName}/${columnName}/values`
|
||||
);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
const mapping: Record<string, string> = {};
|
||||
response.data.data.forEach((item: any) => {
|
||||
mapping[item.valueCode] = item.valueLabel;
|
||||
});
|
||||
mappings[columnName] = mapping;
|
||||
}
|
||||
} catch (error) {
|
||||
// 카테고리 값 로드 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
setCategoryMappings(mappings);
|
||||
} catch (error) {
|
||||
console.error("FlowWidget 카테고리 매핑 로드 실패:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadCategoryMappings();
|
||||
}, [selectedStepId, steps]);
|
||||
|
||||
// 체크박스 토글
|
||||
const toggleRowSelection = (rowIndex: number) => {
|
||||
// 프리뷰 모드에서는 행 선택 차단
|
||||
|
|
@ -1017,7 +1084,7 @@ export function FlowWidget({
|
|||
{stepDataColumns.map((col) => (
|
||||
<div key={col} className="flex justify-between gap-2 text-xs">
|
||||
<span className="text-muted-foreground font-medium">{columnLabels[col] || col}:</span>
|
||||
<span className="text-foreground truncate">{formatValue(row[col])}</span>
|
||||
<span className="text-foreground truncate">{formatValue(row[col], col)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -1095,7 +1162,7 @@ export function FlowWidget({
|
|||
)}
|
||||
{stepDataColumns.map((col) => (
|
||||
<TableCell key={col} className="h-16 border-b px-6 py-3 text-sm whitespace-nowrap">
|
||||
{formatValue(row[col])}
|
||||
{formatValue(row[col], col)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
|
|
@ -1125,7 +1192,7 @@ export function FlowWidget({
|
|||
)}
|
||||
{stepDataColumns.map((col) => (
|
||||
<TableCell key={col} className="h-16 border-b px-6 py-3 text-sm whitespace-nowrap">
|
||||
{formatValue(row[col])}
|
||||
{formatValue(row[col], col)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
|
|
|
|||
|
|
@ -244,7 +244,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
const [localPageSize, setLocalPageSize] = useState<number>(tableConfig.pagination?.pageSize || 20);
|
||||
const [displayColumns, setDisplayColumns] = useState<ColumnConfig[]>([]);
|
||||
const [joinColumnMapping, setJoinColumnMapping] = useState<Record<string, string>>({});
|
||||
const [columnMeta, setColumnMeta] = useState<Record<string, { webType?: string; codeCategory?: string }>>({});
|
||||
const [columnMeta, setColumnMeta] = useState<Record<string, { webType?: string; codeCategory?: string; inputType?: string }>>({});
|
||||
const [categoryMappings, setCategoryMappings] = useState<Record<string, Record<string, string>>>({});
|
||||
const [searchValues, setSearchValues] = useState<Record<string, any>>({});
|
||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
|
||||
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
||||
|
|
@ -428,6 +429,51 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
}
|
||||
}, [tableConfig.selectedTable]);
|
||||
|
||||
// ========================================
|
||||
// 카테고리 값 매핑 로드
|
||||
// ========================================
|
||||
|
||||
useEffect(() => {
|
||||
const loadCategoryMappings = async () => {
|
||||
if (!tableConfig.selectedTable || !columnMeta) return;
|
||||
|
||||
try {
|
||||
const categoryColumns = Object.entries(columnMeta)
|
||||
.filter(([_, meta]) => meta.inputType === "category")
|
||||
.map(([columnName, _]) => columnName);
|
||||
|
||||
if (categoryColumns.length === 0) return;
|
||||
|
||||
const mappings: Record<string, Record<string, string>> = {};
|
||||
|
||||
for (const columnName of categoryColumns) {
|
||||
try {
|
||||
const apiClient = (await import("@/lib/api/client")).apiClient;
|
||||
const response = await apiClient.get(
|
||||
`/table-categories/${tableConfig.selectedTable}/${columnName}/values`
|
||||
);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
const mapping: Record<string, string> = {};
|
||||
response.data.data.forEach((item: any) => {
|
||||
mapping[item.valueCode] = item.valueLabel;
|
||||
});
|
||||
mappings[columnName] = mapping;
|
||||
}
|
||||
} catch (error) {
|
||||
// 카테고리 값 로드 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
setCategoryMappings(mappings);
|
||||
} catch (error) {
|
||||
console.error("TableListComponent 카테고리 매핑 로드 실패:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadCategoryMappings();
|
||||
}, [tableConfig.selectedTable, columnMeta]);
|
||||
|
||||
// ========================================
|
||||
// 데이터 가져오기
|
||||
// ========================================
|
||||
|
|
@ -923,6 +969,18 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
// inputType 기반 포맷팅 (columnMeta에서 가져온 inputType 우선)
|
||||
const inputType = meta?.inputType || column.inputType;
|
||||
|
||||
// 카테고리 타입: 코드값 → 라벨로 변환
|
||||
if (inputType === "category") {
|
||||
const mapping = categoryMappings[column.columnName];
|
||||
if (mapping && value) {
|
||||
const label = mapping[String(value)];
|
||||
if (label) {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
// 코드 타입: 코드 값 → 코드명 변환
|
||||
if (inputType === "code" && meta?.codeCategory && value) {
|
||||
try {
|
||||
|
|
@ -979,7 +1037,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
return String(value);
|
||||
}
|
||||
},
|
||||
[columnMeta, optimizedConvertCode],
|
||||
[columnMeta, categoryMappings, optimizedConvertCode],
|
||||
);
|
||||
|
||||
// ========================================
|
||||
|
|
|
|||
Loading…
Reference in New Issue