리스트 위젯 컨텐츠가 렌더링이 안되는 문제 해결
This commit is contained in:
parent
b54413978b
commit
085679a95a
|
|
@ -903,11 +903,6 @@ export function CanvasElement({
|
|||
<div className="widget-interactive-area h-full w-full">
|
||||
<ChartTestWidget element={element} />
|
||||
</div>
|
||||
) : element.type === "widget" && element.subtype === "list-v2" ? (
|
||||
// 리스트 위젯 (다중 데이터 소스) - 승격 완료
|
||||
<div className="widget-interactive-area h-full w-full">
|
||||
<ListTestWidget element={element} />
|
||||
</div>
|
||||
) : element.type === "widget" && element.subtype === "custom-metric-v2" ? (
|
||||
// 통계 카드 위젯 (다중 데이터 소스) - 승격 완료
|
||||
<div className="widget-interactive-area h-full w-full">
|
||||
|
|
@ -1014,8 +1009,8 @@ export function CanvasElement({
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
) : element.type === "widget" && element.subtype === "list" ? (
|
||||
// 리스트 위젯 렌더링 (구버전)
|
||||
) : element.type === "widget" && (element.subtype === "list" || element.subtype === "list-v2") ? (
|
||||
// 리스트 위젯 렌더링 (v1 & v2)
|
||||
<div className="h-full w-full">
|
||||
<ListWidget element={element} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -270,7 +270,14 @@ export default function DashboardDesigner({ dashboardId: initialDashboardId }: D
|
|||
|
||||
// 요소 업데이트
|
||||
const updateElement = useCallback((id: string, updates: Partial<DashboardElement>) => {
|
||||
setElements((prev) => prev.map((el) => (el.id === id ? { ...el, ...updates } : el)));
|
||||
setElements((prev) =>
|
||||
prev.map((el) => {
|
||||
if (el.id === id) {
|
||||
return { ...el, ...updates };
|
||||
}
|
||||
return el;
|
||||
}),
|
||||
);
|
||||
}, []);
|
||||
|
||||
// 요소 삭제
|
||||
|
|
@ -359,14 +366,17 @@ export default function DashboardDesigner({ dashboardId: initialDashboardId }: D
|
|||
(updatedElement: DashboardElement) => {
|
||||
// 현재 요소의 최신 상태를 가져와서 position과 size는 유지
|
||||
const currentElement = elements.find((el) => el.id === updatedElement.id);
|
||||
|
||||
if (currentElement) {
|
||||
// position과 size는 현재 상태 유지, 나머지만 업데이트
|
||||
// id, position, size 제거 후 나머지만 업데이트
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { id, position, size, ...updates } = updatedElement;
|
||||
const finalElement = {
|
||||
...updatedElement,
|
||||
position: currentElement.position,
|
||||
size: currentElement.size,
|
||||
...currentElement,
|
||||
...updates,
|
||||
};
|
||||
updateElement(finalElement.id, finalElement);
|
||||
|
||||
updateElement(id, updates);
|
||||
// 사이드바도 최신 상태로 업데이트
|
||||
setSidebarElement(finalElement);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,14 +270,28 @@ export function WidgetConfigSidebar({ element, isOpen, onClose, onApply }: Widge
|
|||
const handleApply = useCallback(() => {
|
||||
if (!element) return;
|
||||
|
||||
// 다중 데이터 소스를 사용하는 위젯 체크
|
||||
const isMultiDataSourceWidget =
|
||||
element.subtype === "map-summary-v2" ||
|
||||
element.subtype === "chart" ||
|
||||
element.subtype === "list-v2" ||
|
||||
element.subtype === "custom-metric-v2" ||
|
||||
element.subtype === "risk-alert-v2";
|
||||
|
||||
const updatedElement: DashboardElement = {
|
||||
...element,
|
||||
customTitle: customTitle.trim() || undefined,
|
||||
showHeader,
|
||||
// 데이터 소스가 필요한 위젯만 dataSource 포함
|
||||
// 데이터 소스 처리
|
||||
...(needsDataSource(element.subtype)
|
||||
? {
|
||||
dataSource,
|
||||
// 다중 데이터 소스 위젯은 dataSources도 포함
|
||||
...(isMultiDataSourceWidget
|
||||
? {
|
||||
dataSources: element.dataSources || [],
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
: {}),
|
||||
// 리스트 위젯 설정
|
||||
|
|
@ -291,7 +305,16 @@ export function WidgetConfigSidebar({ element, isOpen, onClose, onApply }: Widge
|
|||
element.subtype === "chart" ||
|
||||
["bar", "horizontal-bar", "pie", "line", "area", "stacked-bar", "donut", "combo"].includes(element.subtype)
|
||||
? {
|
||||
chartConfig,
|
||||
// 다중 데이터 소스 위젯은 chartConfig에 dataSources 포함
|
||||
chartConfig: isMultiDataSourceWidget
|
||||
? { ...chartConfig, dataSources: element.dataSources || [] }
|
||||
: chartConfig,
|
||||
// 프론트엔드 호환성을 위해 dataSources도 element에 직접 포함
|
||||
...(isMultiDataSourceWidget
|
||||
? {
|
||||
dataSources: element.dataSources || [],
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
: {}),
|
||||
// 커스텀 메트릭 설정
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
// 데이터 로드
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
if (!element.dataSource || (!element.dataSource.query && !element.dataSource.endpoint)) return;
|
||||
if (!element.dataSource || (!element.dataSource.query && !element.dataSource.endpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
|
@ -168,8 +170,8 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="mx-auto mb-2 h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||
<div className="text-sm text-foreground">데이터 로딩 중...</div>
|
||||
<div className="border-primary mx-auto mb-2 h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" />
|
||||
<div className="text-foreground text-sm">데이터 로딩 중...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -181,8 +183,8 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="mb-2 text-2xl">⚠️</div>
|
||||
<div className="text-sm font-medium text-destructive">오류 발생</div>
|
||||
<div className="mt-1 text-xs text-muted-foreground">{error}</div>
|
||||
<div className="text-destructive text-sm font-medium">오류 발생</div>
|
||||
<div className="text-muted-foreground mt-1 text-xs">{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -194,8 +196,8 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
<div className="flex h-full w-full flex-col items-center justify-center gap-4 p-4">
|
||||
<div className="text-center">
|
||||
<div className="mb-2 text-4xl">📋</div>
|
||||
<div className="text-sm font-medium text-foreground">리스트를 설정하세요</div>
|
||||
<div className="mt-1 text-xs text-muted-foreground">⚙️ 버튼을 클릭하여 데이터 소스와 컬럼을 설정해주세요</div>
|
||||
<div className="text-foreground text-sm font-medium">리스트를 설정하세요</div>
|
||||
<div className="text-muted-foreground mt-1 text-xs">⚙️ 버튼을 클릭하여 데이터 소스와 컬럼을 설정해주세요</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -222,7 +224,7 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
<div className="flex h-full w-full flex-col p-4">
|
||||
{/* 제목 - 항상 표시 */}
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm font-semibold text-foreground">{element.customTitle || element.title}</h3>
|
||||
<h3 className="text-foreground text-sm font-semibold">{element.customTitle || element.title}</h3>
|
||||
</div>
|
||||
|
||||
{/* 테이블 뷰 */}
|
||||
|
|
@ -251,7 +253,7 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={displayColumns.filter((col) => col.visible).length}
|
||||
className="text-center text-muted-foreground"
|
||||
className="text-muted-foreground text-center"
|
||||
>
|
||||
데이터가 없습니다
|
||||
</TableCell>
|
||||
|
|
@ -281,7 +283,7 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
{config.viewMode === "card" && (
|
||||
<div className="flex-1 overflow-auto">
|
||||
{paginatedRows.length === 0 ? (
|
||||
<div className="flex h-full items-center justify-center text-muted-foreground">데이터가 없습니다</div>
|
||||
<div className="text-muted-foreground flex h-full items-center justify-center">데이터가 없습니다</div>
|
||||
) : (
|
||||
<div
|
||||
className={`grid gap-4 ${config.compactMode ? "text-xs" : "text-sm"}`}
|
||||
|
|
@ -296,9 +298,9 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
|
|||
.filter((col) => col.visible)
|
||||
.map((col) => (
|
||||
<div key={col.id}>
|
||||
<div className="text-xs font-medium text-muted-foreground">{col.label || col.name}</div>
|
||||
<div className="text-muted-foreground text-xs font-medium">{col.label || col.name}</div>
|
||||
<div
|
||||
className={`font-medium text-foreground ${col.align === "center" ? "text-center" : col.align === "right" ? "text-right" : ""}`}
|
||||
className={`text-foreground font-medium ${col.align === "center" ? "text-center" : col.align === "right" ? "text-right" : ""}`}
|
||||
>
|
||||
{String(row[col.dataKey || col.field] ?? "")}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue