ERP-node/frontend/components/admin/dashboard/ElementConfigModal.tsx

183 lines
6.1 KiB
TypeScript
Raw Normal View History

2025-10-14 10:12:40 +09:00
"use client";
2025-10-14 10:12:40 +09:00
import React, { useState, useCallback } from "react";
import { DashboardElement, ChartDataSource, ChartConfig, QueryResult, ClockConfig } from "./types";
import { QueryEditor } from "./QueryEditor";
import { ChartConfigPanel } from "./ChartConfigPanel";
import { ClockConfigModal } from "./widgets/ClockConfigModal";
interface ElementConfigModalProps {
element: DashboardElement;
isOpen: boolean;
onClose: () => void;
onSave: (element: DashboardElement) => void;
}
/**
*
* - /
* -
* -
*/
export function ElementConfigModal({ element, isOpen, onClose, onSave }: ElementConfigModalProps) {
const [dataSource, setDataSource] = useState<ChartDataSource>(
2025-10-14 10:12:40 +09:00
element.dataSource || { type: "database", refreshInterval: 30000 },
);
2025-10-14 10:12:40 +09:00
const [chartConfig, setChartConfig] = useState<ChartConfig>(element.chartConfig || {});
const [queryResult, setQueryResult] = useState<QueryResult | null>(null);
2025-10-14 10:12:40 +09:00
const [activeTab, setActiveTab] = useState<"query" | "chart">("query");
// 데이터 소스 변경 처리
const handleDataSourceChange = useCallback((newDataSource: ChartDataSource) => {
setDataSource(newDataSource);
}, []);
// 차트 설정 변경 처리
const handleChartConfigChange = useCallback((newConfig: ChartConfig) => {
setChartConfig(newConfig);
}, []);
// 쿼리 테스트 결과 처리
const handleQueryTest = useCallback((result: QueryResult) => {
setQueryResult(result);
// 쿼리 결과가 나오면 자동으로 차트 설정 탭으로 이동
if (result.rows.length > 0) {
2025-10-14 10:12:40 +09:00
setActiveTab("chart");
}
}, []);
// 저장 처리
const handleSave = useCallback(() => {
const updatedElement: DashboardElement = {
...element,
dataSource,
chartConfig,
};
onSave(updatedElement);
onClose();
}, [element, dataSource, chartConfig, onSave, onClose]);
2025-10-14 10:12:40 +09:00
// 시계 위젯 설정 저장
const handleClockConfigSave = useCallback(
(clockConfig: ClockConfig) => {
const updatedElement: DashboardElement = {
...element,
clockConfig,
};
onSave(updatedElement);
},
[element, onSave],
);
// 모달이 열려있지 않으면 렌더링하지 않음
if (!isOpen) return null;
2025-10-14 10:12:40 +09:00
// 시계 위젯인 경우 시계 설정 모달 표시
if (element.type === "widget" && element.subtype === "clock") {
return (
<ClockConfigModal
config={
element.clockConfig || {
style: "digital",
timezone: "Asia/Seoul",
showDate: true,
showSeconds: true,
format24h: true,
theme: "light",
}
}
onSave={handleClockConfigSave}
onClose={onClose}
/>
);
}
return (
2025-10-14 10:12:40 +09:00
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
<div className="flex h-[80vh] w-full max-w-4xl flex-col rounded-lg bg-white shadow-xl">
{/* 모달 헤더 */}
2025-10-14 10:12:40 +09:00
<div className="flex items-center justify-between border-b border-gray-200 p-6">
<div>
2025-10-14 10:12:40 +09:00
<h2 className="text-xl font-semibold text-gray-800">{element.title} </h2>
<p className="text-muted-foreground mt-1 text-sm"> </p>
</div>
2025-10-14 10:12:40 +09:00
<button onClick={onClose} className="hover:text-muted-foreground text-2xl text-gray-400">
×
</button>
</div>
{/* 탭 네비게이션 */}
<div className="flex border-b border-gray-200">
<button
2025-10-14 10:12:40 +09:00
onClick={() => setActiveTab("query")}
className={`border-b-2 px-6 py-3 text-sm font-medium transition-colors ${
activeTab === "query"
? "border-primary text-primary bg-accent"
: "border-transparent text-gray-500 hover:text-gray-700"
} `}
>
📝 &
</button>
<button
2025-10-14 10:12:40 +09:00
onClick={() => setActiveTab("chart")}
className={`border-b-2 px-6 py-3 text-sm font-medium transition-colors ${
activeTab === "chart"
? "border-primary text-primary bg-accent"
: "border-transparent text-gray-500 hover:text-gray-700"
} `}
>
📊
{queryResult && (
2025-10-14 10:12:40 +09:00
<span className="ml-2 rounded-full bg-green-100 px-2 py-0.5 text-xs text-green-800">
{queryResult.rows.length}
</span>
)}
</button>
</div>
{/* 탭 내용 */}
<div className="flex-1 overflow-auto p-6">
2025-10-14 10:12:40 +09:00
{activeTab === "query" && (
<QueryEditor
dataSource={dataSource}
onDataSourceChange={handleDataSourceChange}
onQueryTest={handleQueryTest}
/>
)}
2025-10-14 10:12:40 +09:00
{activeTab === "chart" && (
<ChartConfigPanel config={chartConfig} queryResult={queryResult} onConfigChange={handleChartConfigChange} />
)}
</div>
{/* 모달 푸터 */}
2025-10-14 10:12:40 +09:00
<div className="flex items-center justify-between border-t border-gray-200 p-6">
<div className="text-sm text-gray-500">
{dataSource.query && (
<>
2025-10-14 10:12:40 +09:00
💾 : {dataSource.query.length > 50 ? `${dataSource.query.substring(0, 50)}...` : dataSource.query}
</>
)}
</div>
2025-10-14 10:12:40 +09:00
<div className="flex gap-3">
<button
onClick={onClose}
2025-10-14 10:12:40 +09:00
className="text-muted-foreground rounded-lg border border-gray-300 px-4 py-2 hover:bg-gray-50"
>
</button>
<button
onClick={handleSave}
2025-10-14 10:12:40 +09:00
disabled={!dataSource.query || !chartConfig.xAxis || !chartConfig.yAxis}
className="bg-accent0 rounded-lg px-4 py-2 text-white hover:bg-blue-600 disabled:cursor-not-allowed disabled:bg-gray-300"
>
</button>
</div>
</div>
</div>
</div>
);
}