Shadcn 사용 수정
This commit is contained in:
parent
2959f66e0c
commit
8f38b176ab
|
|
@ -16,6 +16,10 @@ import { X } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import CustomMetricConfigSidebar from "./widgets/custom-metric/CustomMetricConfigSidebar";
|
import CustomMetricConfigSidebar from "./widgets/custom-metric/CustomMetricConfigSidebar";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
interface ElementConfigSidebarProps {
|
interface ElementConfigSidebarProps {
|
||||||
element: DashboardElement | null;
|
element: DashboardElement | null;
|
||||||
|
|
@ -50,16 +54,11 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
// 사이드바가 열릴 때 초기화
|
// 사이드바가 열릴 때 초기화
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen && element) {
|
if (isOpen && element) {
|
||||||
console.log("🔄 ElementConfigSidebar 초기화 - element.id:", element.id);
|
|
||||||
console.log("🔄 element.dataSources:", element.dataSources);
|
|
||||||
console.log("🔄 element.chartConfig?.dataSources:", element.chartConfig?.dataSources);
|
|
||||||
|
|
||||||
setDataSource(element.dataSource || { type: "database", connectionType: "current", refreshInterval: 0 });
|
setDataSource(element.dataSource || { type: "database", connectionType: "current", refreshInterval: 0 });
|
||||||
|
|
||||||
// dataSources는 element.dataSources 또는 chartConfig.dataSources에서 로드
|
// dataSources는 element.dataSources 또는 chartConfig.dataSources에서 로드
|
||||||
// ⚠️ 중요: 없으면 반드시 빈 배열로 초기화
|
// ⚠️ 중요: 없으면 반드시 빈 배열로 초기화
|
||||||
const initialDataSources = element.dataSources || element.chartConfig?.dataSources || [];
|
const initialDataSources = element.dataSources || element.chartConfig?.dataSources || [];
|
||||||
console.log("🔄 초기화된 dataSources:", initialDataSources);
|
|
||||||
setDataSources(initialDataSources);
|
setDataSources(initialDataSources);
|
||||||
|
|
||||||
setChartConfig(element.chartConfig || {});
|
setChartConfig(element.chartConfig || {});
|
||||||
|
|
@ -69,7 +68,6 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
setShowHeader(element.showHeader !== false);
|
setShowHeader(element.showHeader !== false);
|
||||||
} else if (!isOpen) {
|
} else if (!isOpen) {
|
||||||
// 사이드바가 닫힐 때 모든 상태 초기화
|
// 사이드바가 닫힐 때 모든 상태 초기화
|
||||||
console.log("🧹 ElementConfigSidebar 닫힘 - 상태 초기화");
|
|
||||||
setDataSource({ type: "database", connectionType: "current", refreshInterval: 0 });
|
setDataSource({ type: "database", connectionType: "current", refreshInterval: 0 });
|
||||||
setDataSources([]);
|
setDataSources([]);
|
||||||
setChartConfig({});
|
setChartConfig({});
|
||||||
|
|
@ -124,8 +122,8 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
(newConfig: ChartConfig) => {
|
(newConfig: ChartConfig) => {
|
||||||
setChartConfig(newConfig);
|
setChartConfig(newConfig);
|
||||||
|
|
||||||
// 🎯 실시간 미리보기: 즉시 부모에게 전달 (map-test 위젯용)
|
// 🎯 실시간 미리보기: 즉시 부모에게 전달 (map-summary-v2 위젯용)
|
||||||
if (element && element.subtype === "map-test" && newConfig.tileMapUrl) {
|
if (element && element.subtype === "map-summary-v2" && newConfig.tileMapUrl) {
|
||||||
onApply({
|
onApply({
|
||||||
...element,
|
...element,
|
||||||
chartConfig: newConfig,
|
chartConfig: newConfig,
|
||||||
|
|
@ -148,10 +146,6 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
const handleApply = useCallback(() => {
|
const handleApply = useCallback(() => {
|
||||||
if (!element) return;
|
if (!element) return;
|
||||||
|
|
||||||
console.log("🔧 적용 버튼 클릭 - dataSource:", dataSource);
|
|
||||||
console.log("🔧 적용 버튼 클릭 - dataSources:", dataSources);
|
|
||||||
console.log("🔧 적용 버튼 클릭 - chartConfig:", chartConfig);
|
|
||||||
|
|
||||||
// 다중 데이터 소스 위젯 체크
|
// 다중 데이터 소스 위젯 체크
|
||||||
const isMultiDS =
|
const isMultiDS =
|
||||||
element.subtype === "map-summary-v2" ||
|
element.subtype === "map-summary-v2" ||
|
||||||
|
|
@ -170,7 +164,6 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
showHeader,
|
showHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("🔧 적용할 요소:", updatedElement);
|
|
||||||
onApply(updatedElement);
|
onApply(updatedElement);
|
||||||
// 사이드바는 열린 채로 유지 (연속 수정 가능)
|
// 사이드바는 열린 채로 유지 (연속 수정 가능)
|
||||||
}, [element, dataSource, dataSources, chartConfig, customTitle, showHeader, onApply]);
|
}, [element, dataSource, dataSources, chartConfig, customTitle, showHeader, onApply]);
|
||||||
|
|
@ -179,7 +172,7 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
if (!element) return null;
|
if (!element) return null;
|
||||||
|
|
||||||
// 리스트 위젯은 별도 사이드바로 처리
|
// 리스트 위젯은 별도 사이드바로 처리
|
||||||
if (element.subtype === "list") {
|
if (element.subtype === "list-v2") {
|
||||||
return (
|
return (
|
||||||
<ListWidgetConfigSidebar
|
<ListWidgetConfigSidebar
|
||||||
element={element}
|
element={element}
|
||||||
|
|
@ -207,7 +200,7 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
}
|
}
|
||||||
|
|
||||||
// 사용자 커스텀 카드 위젯은 사이드바로 처리
|
// 사용자 커스텀 카드 위젯은 사이드바로 처리
|
||||||
if (element.subtype === "custom-metric") {
|
if (element.subtype === "custom-metric-v2") {
|
||||||
return (
|
return (
|
||||||
<CustomMetricConfigSidebar
|
<CustomMetricConfigSidebar
|
||||||
element={element}
|
element={element}
|
||||||
|
|
@ -226,7 +219,6 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
element.subtype === "booking-alert" ||
|
element.subtype === "booking-alert" ||
|
||||||
element.subtype === "maintenance" ||
|
element.subtype === "maintenance" ||
|
||||||
element.subtype === "document" ||
|
element.subtype === "document" ||
|
||||||
element.subtype === "risk-alert" ||
|
|
||||||
element.subtype === "vehicle-status" ||
|
element.subtype === "vehicle-status" ||
|
||||||
element.subtype === "vehicle-list" ||
|
element.subtype === "vehicle-list" ||
|
||||||
element.subtype === "status-summary" ||
|
element.subtype === "status-summary" ||
|
||||||
|
|
@ -244,8 +236,7 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
element.subtype === "weather" || element.subtype === "exchange" || element.subtype === "calculator";
|
element.subtype === "weather" || element.subtype === "exchange" || element.subtype === "calculator";
|
||||||
|
|
||||||
// 지도 위젯 (위도/경도 매핑 필요)
|
// 지도 위젯 (위도/경도 매핑 필요)
|
||||||
const isMapWidget =
|
const isMapWidget = element.subtype === "vehicle-map" || element.subtype === "map-summary-v2";
|
||||||
element.subtype === "vehicle-map" || element.subtype === "map-summary" || element.subtype === "map-test";
|
|
||||||
|
|
||||||
// 헤더 전용 위젯
|
// 헤더 전용 위젯
|
||||||
const isHeaderOnlyWidget =
|
const isHeaderOnlyWidget =
|
||||||
|
|
@ -254,12 +245,11 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
|
|
||||||
// 다중 데이터 소스 위젯
|
// 다중 데이터 소스 위젯
|
||||||
const isMultiDataSourceWidget =
|
const isMultiDataSourceWidget =
|
||||||
element.subtype === "map-summary-v2" ||
|
(element.subtype as string) === "map-summary-v2" ||
|
||||||
element.subtype === "chart" ||
|
(element.subtype as string) === "chart" ||
|
||||||
element.subtype === "list-v2" ||
|
(element.subtype as string) === "list-v2" ||
|
||||||
element.subtype === "custom-metric-v2" ||
|
(element.subtype as string) === "custom-metric-v2" ||
|
||||||
element.subtype === "status-summary-test" ||
|
(element.subtype as string) === "risk-alert-v2";
|
||||||
element.subtype === "risk-alert-v2";
|
|
||||||
|
|
||||||
// 저장 가능 여부 확인
|
// 저장 가능 여부 확인
|
||||||
const isPieChart = element.subtype === "pie" || element.subtype === "donut";
|
const isPieChart = element.subtype === "pie" || element.subtype === "donut";
|
||||||
|
|
@ -280,8 +270,8 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
: isSimpleWidget
|
: isSimpleWidget
|
||||||
? queryResult && queryResult.rows.length > 0
|
? queryResult && queryResult.rows.length > 0
|
||||||
: isMapWidget
|
: isMapWidget
|
||||||
? element.subtype === "map-test"
|
? element.subtype === "map-summary-v2"
|
||||||
? chartConfig.tileMapUrl || (queryResult && queryResult.rows.length > 0) // 🧪 지도 테스트 위젯: 타일맵 URL 또는 API 데이터
|
? chartConfig.tileMapUrl || (queryResult && queryResult.rows.length > 0) // 지도 위젯: 타일맵 URL 또는 API 데이터
|
||||||
: queryResult && queryResult.rows.length > 0 && chartConfig.latitudeColumn && chartConfig.longitudeColumn
|
: queryResult && queryResult.rows.length > 0 && chartConfig.latitudeColumn && chartConfig.longitudeColumn
|
||||||
: queryResult &&
|
: queryResult &&
|
||||||
queryResult.rows.length > 0 &&
|
queryResult.rows.length > 0 &&
|
||||||
|
|
@ -291,62 +281,58 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed top-14 left-0 z-[100] flex h-[calc(100vh-3.5rem)] w-72 flex-col bg-muted transition-transform duration-300 ease-in-out",
|
"bg-muted fixed top-14 left-0 z-[100] flex h-[calc(100vh-3.5rem)] w-72 flex-col transition-transform duration-300 ease-in-out",
|
||||||
isOpen ? "translate-x-0" : "translate-x-[-100%]",
|
isOpen ? "translate-x-0" : "translate-x-[-100%]",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* 헤더 */}
|
{/* 헤더 */}
|
||||||
<div className="flex items-center justify-between bg-background px-3 py-2 shadow-sm">
|
<div className="bg-background flex items-center justify-between px-3 py-2 shadow-sm">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="bg-primary/10 flex h-6 w-6 items-center justify-center rounded">
|
<div className="bg-primary/10 flex h-6 w-6 items-center justify-center rounded">
|
||||||
<span className="text-primary text-xs font-bold">⚙</span>
|
<span className="text-primary text-xs font-bold">⚙</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs font-semibold text-foreground">{element.title}</span>
|
<span className="text-foreground text-xs font-semibold">{element.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<Button onClick={onClose} variant="ghost" size="icon" className="h-6 w-6">
|
||||||
onClick={onClose}
|
<X className="h-3.5 w-3.5" />
|
||||||
className="flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-muted"
|
</Button>
|
||||||
>
|
|
||||||
<X className="h-3.5 w-3.5 text-muted-foreground" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 본문: 스크롤 가능 영역 */}
|
{/* 본문: 스크롤 가능 영역 */}
|
||||||
<div className="flex-1 overflow-y-auto p-3">
|
<div className="flex-1 overflow-y-auto p-3">
|
||||||
{/* 기본 설정 카드 */}
|
{/* 기본 설정 카드 */}
|
||||||
<div className="mb-3 rounded-lg bg-background p-3 shadow-sm">
|
<div className="bg-background mb-3 rounded-lg p-3 shadow-sm">
|
||||||
<div className="mb-2 text-[10px] font-semibold tracking-wide text-muted-foreground uppercase">기본 설정</div>
|
<div className="text-muted-foreground mb-2 text-[10px] font-semibold tracking-wide uppercase">기본 설정</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{/* 커스텀 제목 입력 */}
|
{/* 커스텀 제목 입력 */}
|
||||||
<div>
|
<div>
|
||||||
<input
|
<Input
|
||||||
type="text"
|
|
||||||
value={customTitle}
|
value={customTitle}
|
||||||
onChange={(e) => setCustomTitle(e.target.value)}
|
onChange={(e) => setCustomTitle(e.target.value)}
|
||||||
onKeyDown={(e) => e.stopPropagation()}
|
onKeyDown={(e) => e.stopPropagation()}
|
||||||
placeholder="위젯 제목"
|
placeholder="위젯 제목"
|
||||||
className="focus:border-primary focus:ring-primary/20 h-8 w-full rounded border border-border bg-muted px-2 text-xs placeholder:text-muted-foreground focus:bg-background focus:ring-1 focus:outline-none"
|
className="bg-muted focus:bg-background h-8 text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 헤더 표시 옵션 */}
|
{/* 헤더 표시 옵션 */}
|
||||||
<label className="flex cursor-pointer items-center gap-2 rounded border border-border bg-muted px-2 py-1.5 transition-colors hover:border-border">
|
<div className="border-border bg-muted flex items-center gap-2 rounded border px-2 py-1.5">
|
||||||
<input
|
<Checkbox
|
||||||
type="checkbox"
|
|
||||||
id="showHeader"
|
id="showHeader"
|
||||||
checked={showHeader}
|
checked={showHeader}
|
||||||
onChange={(e) => setShowHeader(e.target.checked)}
|
onCheckedChange={(checked) => setShowHeader(checked === true)}
|
||||||
className="text-primary focus:ring-primary h-3 w-3 rounded border-border"
|
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-foreground">헤더 표시</span>
|
<Label htmlFor="showHeader" className="cursor-pointer text-xs font-normal">
|
||||||
</label>
|
헤더 표시
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 다중 데이터 소스 위젯 */}
|
{/* 다중 데이터 소스 위젯 */}
|
||||||
{isMultiDataSourceWidget && (
|
{isMultiDataSourceWidget && (
|
||||||
<>
|
<>
|
||||||
<div className="rounded-lg bg-background p-3 shadow-sm">
|
<div className="bg-background rounded-lg p-3 shadow-sm">
|
||||||
<MultiDataSourceConfig
|
<MultiDataSourceConfig
|
||||||
dataSources={dataSources}
|
dataSources={dataSources}
|
||||||
onChange={setDataSources}
|
onChange={setDataSources}
|
||||||
|
|
@ -357,13 +343,11 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
totalRows: result.rows.length,
|
totalRows: result.rows.length,
|
||||||
executionTime: 0,
|
executionTime: 0,
|
||||||
});
|
});
|
||||||
console.log("📊 API 테스트 결과 수신:", result, "데이터 소스 ID:", dataSourceId);
|
|
||||||
|
|
||||||
// ChartTestWidget용: 각 데이터 소스의 테스트 결과 저장
|
// 각 데이터 소스의 테스트 결과 저장
|
||||||
setTestResults((prev) => {
|
setTestResults((prev) => {
|
||||||
const updated = new Map(prev);
|
const updated = new Map(prev);
|
||||||
updated.set(dataSourceId, result);
|
updated.set(dataSourceId, result);
|
||||||
console.log("📊 테스트 결과 저장:", dataSourceId, result);
|
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|
@ -372,11 +356,11 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
|
|
||||||
{/* 지도 위젯: 타일맵 URL 설정 */}
|
{/* 지도 위젯: 타일맵 URL 설정 */}
|
||||||
{element.subtype === "map-summary-v2" && (
|
{element.subtype === "map-summary-v2" && (
|
||||||
<div className="rounded-lg bg-background shadow-sm">
|
<div className="bg-background rounded-lg shadow-sm">
|
||||||
<details className="group">
|
<details className="group">
|
||||||
<summary className="flex cursor-pointer items-center justify-between p-3 hover:bg-muted">
|
<summary className="hover:bg-muted flex cursor-pointer items-center justify-between p-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs font-semibold tracking-wide text-muted-foreground uppercase">
|
<div className="text-muted-foreground text-xs font-semibold tracking-wide uppercase">
|
||||||
타일맵 설정 (선택사항)
|
타일맵 설정 (선택사항)
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted-foreground mt-0.5 text-[10px]">기본 VWorld 타일맵 사용 중</div>
|
<div className="text-muted-foreground mt-0.5 text-[10px]">기본 VWorld 타일맵 사용 중</div>
|
||||||
|
|
@ -403,11 +387,13 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
|
|
||||||
{/* 차트 위젯: 차트 설정 */}
|
{/* 차트 위젯: 차트 설정 */}
|
||||||
{element.subtype === "chart" && (
|
{element.subtype === "chart" && (
|
||||||
<div className="rounded-lg bg-background shadow-sm">
|
<div className="bg-background rounded-lg shadow-sm">
|
||||||
<details className="group" open>
|
<details className="group" open>
|
||||||
<summary className="flex cursor-pointer items-center justify-between p-3 hover:bg-muted">
|
<summary className="hover:bg-muted flex cursor-pointer items-center justify-between p-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs font-semibold tracking-wide text-muted-foreground uppercase">차트 설정</div>
|
<div className="text-muted-foreground text-xs font-semibold tracking-wide uppercase">
|
||||||
|
차트 설정
|
||||||
|
</div>
|
||||||
<div className="text-muted-foreground mt-0.5 text-[10px]">
|
<div className="text-muted-foreground mt-0.5 text-[10px]">
|
||||||
{testResults.size > 0
|
{testResults.size > 0
|
||||||
? `${testResults.size}개 데이터 소스 • X축, Y축, 차트 타입 설정`
|
? `${testResults.size}개 데이터 소스 • X축, Y축, 차트 타입 설정`
|
||||||
|
|
@ -439,24 +425,26 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
|
|
||||||
{/* 헤더 전용 위젯이 아닐 때만 데이터 소스 표시 */}
|
{/* 헤더 전용 위젯이 아닐 때만 데이터 소스 표시 */}
|
||||||
{!isHeaderOnlyWidget && !isMultiDataSourceWidget && (
|
{!isHeaderOnlyWidget && !isMultiDataSourceWidget && (
|
||||||
<div className="rounded-lg bg-background p-3 shadow-sm">
|
<div className="bg-background rounded-lg p-3 shadow-sm">
|
||||||
<div className="mb-2 text-[10px] font-semibold tracking-wide text-muted-foreground uppercase">데이터 소스</div>
|
<div className="text-muted-foreground mb-2 text-[10px] font-semibold tracking-wide uppercase">
|
||||||
|
데이터 소스
|
||||||
|
</div>
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue={dataSource.type}
|
defaultValue={dataSource.type}
|
||||||
onValueChange={(value) => handleDataSourceTypeChange(value as "database" | "api")}
|
onValueChange={(value) => handleDataSourceTypeChange(value as "database" | "api")}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<TabsList className="grid h-7 w-full grid-cols-2 bg-muted p-0.5">
|
<TabsList className="bg-muted grid h-7 w-full grid-cols-2 p-0.5">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="database"
|
value="database"
|
||||||
className="h-6 rounded text-[11px] data-[state=active]:bg-background data-[state=active]:shadow-sm"
|
className="data-[state=active]:bg-background h-6 rounded text-[11px] data-[state=active]:shadow-sm"
|
||||||
>
|
>
|
||||||
데이터베이스
|
데이터베이스
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="api"
|
value="api"
|
||||||
className="h-6 rounded text-[11px] data-[state=active]:bg-background data-[state=active]:shadow-sm"
|
className="data-[state=active]:bg-background h-6 rounded text-[11px] data-[state=active]:shadow-sm"
|
||||||
>
|
>
|
||||||
REST API
|
REST API
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
|
@ -472,10 +460,10 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
|
|
||||||
{/* 차트/지도 설정 */}
|
{/* 차트/지도 설정 */}
|
||||||
{!isSimpleWidget &&
|
{!isSimpleWidget &&
|
||||||
(element.subtype === "map-test" || (queryResult && queryResult.rows.length > 0)) && (
|
(element.subtype === "map-summary-v2" || (queryResult && queryResult.rows.length > 0)) && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
{isMapWidget ? (
|
{isMapWidget ? (
|
||||||
element.subtype === "map-test" ? (
|
element.subtype === "map-summary-v2" ? (
|
||||||
<MapTestConfigPanel
|
<MapTestConfigPanel
|
||||||
config={chartConfig}
|
config={chartConfig}
|
||||||
queryResult={queryResult || undefined}
|
queryResult={queryResult || undefined}
|
||||||
|
|
@ -513,10 +501,10 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
|
|
||||||
{/* 차트/지도 설정 */}
|
{/* 차트/지도 설정 */}
|
||||||
{!isSimpleWidget &&
|
{!isSimpleWidget &&
|
||||||
(element.subtype === "map-test" || (queryResult && queryResult.rows.length > 0)) && (
|
(element.subtype === "map-summary-v2" || (queryResult && queryResult.rows.length > 0)) && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
{isMapWidget ? (
|
{isMapWidget ? (
|
||||||
element.subtype === "map-test" ? (
|
element.subtype === "map-summary-v2" ? (
|
||||||
<MapTestConfigPanel
|
<MapTestConfigPanel
|
||||||
config={chartConfig}
|
config={chartConfig}
|
||||||
queryResult={queryResult || undefined}
|
queryResult={queryResult || undefined}
|
||||||
|
|
@ -552,11 +540,9 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
|
|
||||||
{/* 데이터 로드 상태 */}
|
{/* 데이터 로드 상태 */}
|
||||||
{queryResult && (
|
{queryResult && (
|
||||||
<div className="mt-2 flex items-center gap-1.5 rounded bg-success/10 px-2 py-1">
|
<div className="bg-success/10 mt-2 flex items-center gap-1.5 rounded px-2 py-1">
|
||||||
<div className="h-1.5 w-1.5 rounded-full bg-success" />
|
<div className="bg-success h-1.5 w-1.5 rounded-full" />
|
||||||
<span className="text-[10px] font-medium text-success">
|
<span className="text-success text-[10px] font-medium">{queryResult.rows.length}개 데이터 로드됨</span>
|
||||||
{queryResult.rows.length}개 데이터 로드됨
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -564,20 +550,13 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 푸터: 적용 버튼 */}
|
{/* 푸터: 적용 버튼 */}
|
||||||
<div className="flex gap-2 bg-background p-2 shadow-[0_-2px_8px_rgba(0,0,0,0.05)]">
|
<div className="bg-background flex gap-2 p-2 shadow-[0_-2px_8px_rgba(0,0,0,0.05)]">
|
||||||
<button
|
<Button onClick={onClose} variant="outline" className="flex-1 text-xs">
|
||||||
onClick={onClose}
|
|
||||||
className="flex-1 rounded bg-muted py-2 text-xs font-medium text-foreground transition-colors hover:bg-muted"
|
|
||||||
>
|
|
||||||
취소
|
취소
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button onClick={handleApply} disabled={isHeaderOnlyWidget ? false : !canApply} className="flex-1 text-xs">
|
||||||
onClick={handleApply}
|
|
||||||
disabled={isHeaderOnlyWidget ? false : !canApply}
|
|
||||||
className="bg-primary hover:bg-primary/90 flex-1 rounded py-2 text-xs font-medium text-white transition-colors disabled:cursor-not-allowed disabled:opacity-50"
|
|
||||||
>
|
|
||||||
적용
|
적용
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue