리스트 카드 렌더링 문제 해결

This commit is contained in:
dohyeons
2025-11-14 11:16:03 +09:00
parent a491f08337
commit 2eb8c3a61b
2 changed files with 97 additions and 45 deletions

View File

@@ -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,
}); });

View File

@@ -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">