feat: 분할 패널 테이블에서 카테고리 값을 라벨로 표시하는 기능 추가
- 좌측/우측 패널 테이블의 카테고리 타입 컬럼 매핑 로드 - formatCellValue 함수 추가하여 카테고리 코드를 라벨+배지로 변환 - 좌측 패널 테이블 렌더링 부분(그룹화/일반)에 formatCellValue 적용 - 우측 패널 테이블 렌더링 부분에 formatCellValue 적용 - Badge 컴포넌트 import 및 카테고리별 색상 표시 변경 사항: - 카테고리 매핑 상태 추가 (leftCategoryMappings, rightCategoryMappings) - API 클라이언트 import 추가 - 카테고리 값 조회 API 호출 useEffect 2개 추가 - 셀 값 포맷팅 함수 formatCellValue 추가 - 모든 테이블 td 렌더링에서 String(value) → formatCellValue() 적용
This commit is contained in:
parent
2a52f25c10
commit
702b506665
|
|
@ -6,11 +6,13 @@ import { SplitPanelLayoutConfig } from "./types";
|
|||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Plus, Search, GripVertical, Loader2, ChevronDown, ChevronUp, Save, ChevronRight, Pencil, Trash2 } from "lucide-react";
|
||||
import { dataApi } from "@/lib/api/data";
|
||||
import { entityJoinApi } from "@/lib/api/entityJoin";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { tableTypeApi } from "@/lib/api/screen";
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useTableOptions } from "@/contexts/TableOptionsContext";
|
||||
|
|
@ -64,6 +66,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
const [expandedItems, setExpandedItems] = useState<Set<any>>(new Set()); // 펼쳐진 항목들
|
||||
const [leftColumnLabels, setLeftColumnLabels] = useState<Record<string, string>>({}); // 좌측 컬럼 라벨
|
||||
const [rightColumnLabels, setRightColumnLabels] = useState<Record<string, string>>({}); // 우측 컬럼 라벨
|
||||
const [leftCategoryMappings, setLeftCategoryMappings] = useState<Record<string, Record<string, { label: string; color?: string }>>>({}); // 좌측 카테고리 매핑
|
||||
const [rightCategoryMappings, setRightCategoryMappings] = useState<Record<string, Record<string, { label: string; color?: string }>>>({}); // 우측 카테고리 매핑
|
||||
const { toast } = useToast();
|
||||
|
||||
// 추가 모달 상태
|
||||
|
|
@ -241,6 +245,39 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
}));
|
||||
}, [leftData, leftGrouping]);
|
||||
|
||||
// 셀 값 포맷팅 함수 (카테고리 타입 처리)
|
||||
const formatCellValue = useCallback((
|
||||
columnName: string,
|
||||
value: any,
|
||||
categoryMappings: Record<string, Record<string, { label: string; color?: string }>>
|
||||
) => {
|
||||
if (value === null || value === undefined) return "-";
|
||||
|
||||
// 카테고리 매핑이 있는지 확인
|
||||
const mapping = categoryMappings[columnName];
|
||||
if (mapping && mapping[String(value)]) {
|
||||
const categoryData = mapping[String(value)];
|
||||
const displayLabel = categoryData.label || String(value);
|
||||
const displayColor = categoryData.color || "#64748b";
|
||||
|
||||
// 배지로 표시
|
||||
return (
|
||||
<Badge
|
||||
style={{
|
||||
backgroundColor: displayColor,
|
||||
borderColor: displayColor,
|
||||
}}
|
||||
className="text-white"
|
||||
>
|
||||
{displayLabel}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
// 일반 값
|
||||
return String(value);
|
||||
}, []);
|
||||
|
||||
// 좌측 데이터 로드
|
||||
const loadLeftData = useCallback(async () => {
|
||||
const leftTableName = componentConfig.leftPanel?.tableName;
|
||||
|
|
@ -523,6 +560,112 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
loadRightTableColumns();
|
||||
}, [componentConfig.rightPanel?.tableName, isDesignMode]);
|
||||
|
||||
// 좌측 테이블 카테고리 매핑 로드
|
||||
useEffect(() => {
|
||||
const loadLeftCategoryMappings = async () => {
|
||||
const leftTableName = componentConfig.leftPanel?.tableName;
|
||||
if (!leftTableName || isDesignMode) return;
|
||||
|
||||
try {
|
||||
// 1. 컬럼 메타 정보 조회
|
||||
const columnsResponse = await tableTypeApi.getColumns(leftTableName);
|
||||
const categoryColumns = columnsResponse.filter(
|
||||
(col: any) => col.inputType === "category"
|
||||
);
|
||||
|
||||
if (categoryColumns.length === 0) {
|
||||
setLeftCategoryMappings({});
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 각 카테고리 컬럼에 대한 값 조회
|
||||
const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
|
||||
|
||||
for (const col of categoryColumns) {
|
||||
const columnName = col.columnName || col.column_name;
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/table-type/category-values/${leftTableName}/${columnName}`
|
||||
);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
const valueMap: Record<string, { label: string; color?: string }> = {};
|
||||
response.data.data.forEach((item: any) => {
|
||||
valueMap[item.value_code || item.valueCode] = {
|
||||
label: item.value_label || item.valueLabel,
|
||||
color: item.color,
|
||||
};
|
||||
});
|
||||
mappings[columnName] = valueMap;
|
||||
console.log(`✅ 좌측 카테고리 매핑 로드 [${columnName}]:`, valueMap);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`좌측 카테고리 값 조회 실패 [${columnName}]:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
setLeftCategoryMappings(mappings);
|
||||
} catch (error) {
|
||||
console.error("좌측 카테고리 매핑 로드 실패:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadLeftCategoryMappings();
|
||||
}, [componentConfig.leftPanel?.tableName, isDesignMode]);
|
||||
|
||||
// 우측 테이블 카테고리 매핑 로드
|
||||
useEffect(() => {
|
||||
const loadRightCategoryMappings = async () => {
|
||||
const rightTableName = componentConfig.rightPanel?.tableName;
|
||||
if (!rightTableName || isDesignMode) return;
|
||||
|
||||
try {
|
||||
// 1. 컬럼 메타 정보 조회
|
||||
const columnsResponse = await tableTypeApi.getColumns(rightTableName);
|
||||
const categoryColumns = columnsResponse.filter(
|
||||
(col: any) => col.inputType === "category"
|
||||
);
|
||||
|
||||
if (categoryColumns.length === 0) {
|
||||
setRightCategoryMappings({});
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 각 카테고리 컬럼에 대한 값 조회
|
||||
const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
|
||||
|
||||
for (const col of categoryColumns) {
|
||||
const columnName = col.columnName || col.column_name;
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/table-type/category-values/${rightTableName}/${columnName}`
|
||||
);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
const valueMap: Record<string, { label: string; color?: string }> = {};
|
||||
response.data.data.forEach((item: any) => {
|
||||
valueMap[item.value_code || item.valueCode] = {
|
||||
label: item.value_label || item.valueLabel,
|
||||
color: item.color,
|
||||
};
|
||||
});
|
||||
mappings[columnName] = valueMap;
|
||||
console.log(`✅ 우측 카테고리 매핑 로드 [${columnName}]:`, valueMap);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`우측 카테고리 값 조회 실패 [${columnName}]:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
setRightCategoryMappings(mappings);
|
||||
} catch (error) {
|
||||
console.error("우측 카테고리 매핑 로드 실패:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadRightCategoryMappings();
|
||||
}, [componentConfig.rightPanel?.tableName, isDesignMode]);
|
||||
|
||||
// 항목 펼치기/접기 토글
|
||||
const toggleExpand = useCallback((itemId: any) => {
|
||||
setExpandedItems(prev => {
|
||||
|
|
@ -1193,9 +1336,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
className="whitespace-nowrap px-3 py-2 text-sm text-gray-900"
|
||||
style={{ textAlign: col.align || "left" }}
|
||||
>
|
||||
{item[col.name] !== null && item[col.name] !== undefined
|
||||
? String(item[col.name])
|
||||
: "-"}
|
||||
{formatCellValue(col.name, item[col.name], leftCategoryMappings)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
|
|
@ -1246,9 +1387,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
className="whitespace-nowrap px-3 py-2 text-sm text-gray-900"
|
||||
style={{ textAlign: col.align || "left" }}
|
||||
>
|
||||
{item[col.name] !== null && item[col.name] !== undefined
|
||||
? String(item[col.name])
|
||||
: "-"}
|
||||
{formatCellValue(col.name, item[col.name], leftCategoryMappings)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
|
|
@ -1619,9 +1758,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
className="whitespace-nowrap px-3 py-2 text-sm text-gray-900"
|
||||
style={{ textAlign: col.align || "left" }}
|
||||
>
|
||||
{item[col.name] !== null && item[col.name] !== undefined
|
||||
? String(item[col.name])
|
||||
: "-"}
|
||||
{formatCellValue(col.name, item[col.name], rightCategoryMappings)}
|
||||
</td>
|
||||
))}
|
||||
{!isDesignMode && (
|
||||
|
|
|
|||
Loading…
Reference in New Issue