리스트 카드 렌더링 문제 해결
This commit is contained in:
@@ -352,6 +352,10 @@ export function WidgetConfigSidebar({ element, isOpen, onClose, onApply }: Widge
|
|||||||
|
|
||||||
console.log("🔧 [WidgetConfigSidebar] handleApply 호출:", {
|
console.log("🔧 [WidgetConfigSidebar] handleApply 호출:", {
|
||||||
subtype: element.subtype,
|
subtype: element.subtype,
|
||||||
|
isMultiDataSourceWidget,
|
||||||
|
dataSources,
|
||||||
|
listConfig,
|
||||||
|
finalChartConfig,
|
||||||
customMetricConfig,
|
customMetricConfig,
|
||||||
updatedElement,
|
updatedElement,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,8 +37,19 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||||||
// // console.log("🧪 ListTestWidget 렌더링!", element);
|
// // console.log("🧪 ListTestWidget 렌더링!", element);
|
||||||
|
|
||||||
const dataSources = useMemo(() => {
|
const dataSources = useMemo(() => {
|
||||||
return element?.dataSources || element?.chartConfig?.dataSources;
|
// 다중 데이터 소스 우선
|
||||||
}, [element?.dataSources, element?.chartConfig?.dataSources]);
|
const multiSources = element?.dataSources || element?.chartConfig?.dataSources;
|
||||||
|
if (multiSources && multiSources.length > 0) {
|
||||||
|
return multiSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 단일 데이터 소스 fallback (배열로 변환)
|
||||||
|
if (element?.dataSource) {
|
||||||
|
return [element.dataSource];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}, [element?.dataSources, element?.chartConfig?.dataSources, element?.dataSource]);
|
||||||
|
|
||||||
// // console.log("📊 dataSources 확인:", {
|
// // console.log("📊 dataSources 확인:", {
|
||||||
// hasDataSources: !!dataSources,
|
// hasDataSources: !!dataSources,
|
||||||
@@ -58,6 +69,27 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||||||
cardColumns: 3,
|
cardColumns: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// visible 컬럼 설정 객체 배열 (field + label)
|
||||||
|
const visibleColumnConfigs = useMemo(() => {
|
||||||
|
if (config.columns && config.columns.length > 0 && typeof config.columns[0] === "object") {
|
||||||
|
return config.columns.filter((col: any) => col.visible !== false);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [config.columns]);
|
||||||
|
|
||||||
|
// 표시할 컬럼 필드명 (데이터 접근용)
|
||||||
|
const displayColumns = useMemo(() => {
|
||||||
|
if (!data?.columns) return [];
|
||||||
|
|
||||||
|
// 컬럼 설정이 있으면 field 사용
|
||||||
|
if (visibleColumnConfigs.length > 0) {
|
||||||
|
return visibleColumnConfigs.map((col: any) => col.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 자동 모드: 모든 컬럼 표시
|
||||||
|
return data.columns;
|
||||||
|
}, [data?.columns, visibleColumnConfigs]);
|
||||||
|
|
||||||
// 다중 데이터 소스 로딩
|
// 다중 데이터 소스 로딩
|
||||||
const loadMultipleDataSources = useCallback(async () => {
|
const loadMultipleDataSources = useCallback(async () => {
|
||||||
if (!dataSources || dataSources.length === 0) {
|
if (!dataSources || dataSources.length === 0) {
|
||||||
@@ -313,50 +345,66 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||||||
const paginatedRows = data?.rows.slice(startIndex, endIndex) || [];
|
const paginatedRows = data?.rows.slice(startIndex, endIndex) || [];
|
||||||
|
|
||||||
// 테이블 뷰
|
// 테이블 뷰
|
||||||
const renderTable = () => (
|
const renderTable = () => {
|
||||||
<div className="overflow-auto">
|
// 헤더명 가져오기 (label 우선, 없으면 field 그대로)
|
||||||
<Table>
|
const getHeaderLabel = (field: string) => {
|
||||||
{config.showHeader && (
|
const colConfig = visibleColumnConfigs.find((col: any) => col.field === field);
|
||||||
<TableHeader>
|
return colConfig?.label || field;
|
||||||
<TableRow>
|
};
|
||||||
{data?.columns.map((col) => (
|
|
||||||
<TableHead key={col} className="whitespace-nowrap">
|
return (
|
||||||
{col}
|
<div className="overflow-auto">
|
||||||
</TableHead>
|
<Table>
|
||||||
))}
|
{config.showHeader && (
|
||||||
</TableRow>
|
<TableHeader>
|
||||||
</TableHeader>
|
<TableRow>
|
||||||
)}
|
{displayColumns.map((field) => (
|
||||||
<TableBody>
|
<TableHead key={field} className="whitespace-nowrap">
|
||||||
{paginatedRows.map((row, idx) => (
|
{getHeaderLabel(field)}
|
||||||
<TableRow key={idx} className={config.stripedRows && idx % 2 === 0 ? "bg-muted/50" : ""}>
|
</TableHead>
|
||||||
{data?.columns.map((col) => (
|
))}
|
||||||
<TableCell key={col} className="whitespace-nowrap">
|
</TableRow>
|
||||||
{String(row[col] ?? "")}
|
</TableHeader>
|
||||||
</TableCell>
|
)}
|
||||||
))}
|
<TableBody>
|
||||||
</TableRow>
|
{paginatedRows.map((row, idx) => (
|
||||||
))}
|
<TableRow key={idx} className={config.stripedRows && idx % 2 === 0 ? "bg-muted/50" : ""}>
|
||||||
</TableBody>
|
{displayColumns.map((field) => (
|
||||||
</Table>
|
<TableCell key={field} className="whitespace-nowrap">
|
||||||
</div>
|
{String(row[field] ?? "")}
|
||||||
);
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// 카드 뷰
|
// 카드 뷰
|
||||||
const renderCards = () => (
|
const renderCards = () => {
|
||||||
<div className={`grid gap-4 grid-cols-1 md:grid-cols-${config.cardColumns || 3}`}>
|
// 헤더명 가져오기 (label 우선, 없으면 field 그대로)
|
||||||
{paginatedRows.map((row, idx) => (
|
const getLabel = (field: string) => {
|
||||||
<Card key={idx} className="p-4">
|
const colConfig = visibleColumnConfigs.find((col: any) => col.field === field);
|
||||||
{data?.columns.map((col) => (
|
return colConfig?.label || field;
|
||||||
<div key={col} className="mb-2">
|
};
|
||||||
<span className="font-semibold">{col}: </span>
|
|
||||||
<span>{String(row[col] ?? "")}</span>
|
return (
|
||||||
</div>
|
<div className={`grid gap-4 grid-cols-1 md:grid-cols-${config.cardColumns || 3}`}>
|
||||||
))}
|
{paginatedRows.map((row, idx) => (
|
||||||
</Card>
|
<Card key={idx} className="p-4">
|
||||||
))}
|
{displayColumns.map((field) => (
|
||||||
</div>
|
<div key={field} className="mb-2">
|
||||||
);
|
<span className="font-semibold">{getLabel(field)}: </span>
|
||||||
|
<span>{String(row[field] ?? "")}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col bg-card shadow-sm">
|
<div className="flex h-full flex-col bg-card shadow-sm">
|
||||||
@@ -396,7 +444,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||||||
<div className="flex h-full items-center justify-center">
|
<div className="flex h-full items-center justify-center">
|
||||||
<p className="text-sm text-destructive">{error}</p>
|
<p className="text-sm text-destructive">{error}</p>
|
||||||
</div>
|
</div>
|
||||||
) : !(element?.dataSources || element?.chartConfig?.dataSources) || (element?.dataSources || element?.chartConfig?.dataSources)?.length === 0 ? (
|
) : !dataSources || dataSources.length === 0 ? (
|
||||||
<div className="flex h-full items-center justify-center">
|
<div className="flex h-full items-center justify-center">
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
데이터 소스를 연결해주세요
|
데이터 소스를 연결해주세요
|
||||||
|
|||||||
Reference in New Issue
Block a user