import { useCallback, useEffect, useState } from 'react'; import { BarChart3, Plus, Loader2, ListChecks } from 'lucide-react'; import { usePluginContext } from './client'; import { CredentialPanel } from './CredentialPanel'; import { NewJobDialog } from './NewJobDialog'; import { JobDetail } from './JobDetail'; import type { S3Credential, AnalysisJob } from './types'; const API_BASE = '/api/v1/plugin/data_analytics'; interface Props { pluginName: string; } export function Dashboard({ pluginName }: Props) { const { client } = usePluginContext(); const [credentials, setCredentials] = useState([]); const [credLoading, setCredLoading] = useState(true); const [jobs, setJobs] = useState([]); const [jobLoading, setJobLoading] = useState(true); const [showNewJob, setShowNewJob] = useState(false); const [openJobId, setOpenJobId] = useState(null); const [error, setError] = useState(''); const loadCredentials = useCallback(async () => { setCredLoading(true); try { const resp = await client.get<{ credentials: S3Credential[] }>(`${API_BASE}/credentials`); setCredentials(resp.data.credentials || []); } catch (e: unknown) { const msg = (e as { response?: { data?: { detail?: string } } }).response?.data?.detail; setError(msg || '加载凭证失败'); } finally { setCredLoading(false); } }, [client]); const loadJobs = useCallback(async () => { setJobLoading(true); try { const resp = await client.get<{ jobs: AnalysisJob[] }>(`${API_BASE}/jobs`); setJobs(resp.data.jobs || []); } catch (e: unknown) { const msg = (e as { response?: { data?: { detail?: string } } }).response?.data?.detail; setError(msg || '加载任务失败'); } finally { setJobLoading(false); } }, [client]); useEffect(() => { loadCredentials(); loadJobs(); }, [loadCredentials, loadJobs]); // 轮询任务列表,方便看状态变化 useEffect(() => { const t = setInterval(loadJobs, 5000); return () => clearInterval(t); }, [loadJobs]); return (

数据分析

对接 S3,让 agent 自主决定分析路径(python_executor / ray_submit)。{' '} {pluginName}

{error && (
{error}
)}

分析任务

点行查看详情和事件流。每 5 秒自动刷新。

{jobLoading && jobs.length === 0 ? (
) : jobs.length === 0 ? (
还没有分析任务。点右上角「新建任务」开始。
) : (
{jobs.map((j) => ( ))}
)}
{showNewJob && ( setShowNewJob(false)} onCreated={loadJobs} /> )} {openJobId && setOpenJobId(null)} />}
); } function StatusBadge({ status }: { status: string }) { const map: Record = { pending: 'bg-bg-base text-text-muted border-border-primary', running: 'bg-warning-bg text-warning border-warning/20', completed: 'bg-success-bg text-success border-success/20', failed: 'bg-danger-bg text-danger border-danger/20', }; const cls = map[status] || map.pending; return ( {status} ); }