1
0
Files
DiAL/src/web/app.tsx

150 lines
5.1 KiB
TypeScript

import type { SkeletonProps } from "tdesign-react";
import { lazy, Suspense, useState } from "react";
import { Alert, Layout, Menu, RadioGroup, Skeleton } from "tdesign-react";
import { RefreshCountdown } from "./components/RefreshCountdown";
import { SummaryCards } from "./components/SummaryCards";
import { TargetBoard } from "./components/TargetBoard";
import { useDashboard, useMeta } from "./hooks/use-queries";
import { useTargetDetail } from "./hooks/use-target-detail";
import { type ThemePreference, useThemePreference } from "./hooks/use-theme-preference";
const TargetDetailDrawer = lazy(() =>
import("./components/TargetDetailDrawer").then((m) => ({ default: m.TargetDetailDrawer })),
);
const { Content, Header } = Layout;
const DEFAULT_REFRESH_INTERVAL_MS = 30000;
const DASHBOARD_SKELETON_ROW_COL: SkeletonProps["rowCol"] = [
[{ height: "112px", type: "rect", width: "100%" }],
[{ height: "56px", type: "rect", width: "100%" }],
[{ height: "320px", type: "rect", width: "100%" }],
];
const REFRESH_OPTIONS = [
{ label: "手动", value: 0 },
{ label: "10秒", value: 10000 },
{ label: "30秒", value: 30000 },
{ label: "1分钟", value: 60000 },
{ label: "5分钟", value: 300000 },
] as const;
const THEME_OPTIONS = [
{ label: "系统", value: "system" },
{ label: "明亮", value: "light" },
{ label: "黑暗", value: "dark" },
] as const;
export function App() {
const [refreshInterval, setRefreshInterval] = useState(DEFAULT_REFRESH_INTERVAL_MS);
const { preference: themePreference, setPreference: setThemePreference } = useThemePreference();
const dashboardRefetchInterval = refreshInterval === 0 ? false : refreshInterval;
const {
data: dashboard,
dataUpdatedAt: dashboardUpdatedAt,
error: dashboardError,
isFetching: dashboardFetching,
isLoading: dashboardLoading,
refetch: refetchDashboard,
} = useDashboard(dashboardRefetchInterval);
const { data: meta } = useMeta();
const {
activeTab,
closeDrawer,
handlePageChange,
handleTabChange,
handleTimeChange,
historyData,
historyLoading,
metricsData,
metricsLoading,
openDrawer,
selectedTarget,
timeFrom,
timeTo,
} = useTargetDetail();
const isManualRefresh = refreshInterval === 0;
const versionDisplay = meta?.version ? `v${meta.version}` : null;
const handleIntervalChange = (value: number) => {
void refetchDashboard();
setRefreshInterval(value);
};
const handleThemeChange = (value: ThemePreference) => {
setThemePreference(value);
};
return (
<Layout className="dashboard">
<Header>
<Menu.HeadMenu
logo={
<span className="dashboard-brand">
<span className="dashboard-logo">DiAL</span>
<span className="dashboard-subtitle"></span>
{versionDisplay && <span className="dashboard-version">{versionDisplay}</span>}
</span>
}
operations={
<div className="dashboard-header-controls">
<RadioGroup
onChange={handleThemeChange}
options={THEME_OPTIONS.map((option) => ({ label: option.label, value: option.value }))}
theme="button"
value={themePreference}
variant="default-filled"
/>
<RadioGroup
onChange={handleIntervalChange}
options={REFRESH_OPTIONS.map((option) => ({ label: option.label, value: option.value }))}
theme="button"
value={refreshInterval}
variant="default-filled"
/>
<span className="dashboard-countdown">
<RefreshCountdown
dashboardUpdatedAt={dashboardUpdatedAt}
isFetching={dashboardFetching && !dashboardLoading}
isManualRefresh={isManualRefresh}
onRefresh={() => void refetchDashboard()}
refreshInterval={refreshInterval}
/>
</span>
</div>
}
/>
</Header>
<Content>
<div className="dashboard-content">
{dashboardError && <Alert closeBtn message={`请求失败: ${dashboardError.message}`} theme="error" />}
{dashboardLoading ? (
<Skeleton animation="gradient" rowCol={DASHBOARD_SKELETON_ROW_COL} />
) : (
<>
<SummaryCards summary={dashboard?.summary ?? null} />
<TargetBoard onTargetClick={openDrawer} targets={dashboard?.targets ?? []} />
</>
)}
</div>
</Content>
<Suspense fallback={null}>
<TargetDetailDrawer
activeTab={activeTab}
historyData={historyData}
historyLoading={historyLoading}
metricsData={metricsData}
metricsLoading={metricsLoading}
onClose={closeDrawer}
onPageChange={handlePageChange}
onTabChange={handleTabChange}
onTimeChange={handleTimeChange}
target={selectedTarget}
timeFrom={timeFrom}
timeTo={timeTo}
/>
</Suspense>
</Layout>
);
}