Files
VoCat/web/src/components/dashboard/UpcomingTasksCard.tsx
T
MengMengCodeandClaude Opus 4.8 115598757a feat: dashboard host, performance, task, and online-rate cards
Add four cards to the dashboard:

- Host hardware card (CPU / motherboard / memory / disk model) backed by
  a new GET /api/dashboard/host endpoint that probes /proc and /sys once
  and caches the identities. x86 hosts read cpuinfo model name, DMI board
  data, dmidecode DIMM info, and block device models; ARM boards compose
  the device-tree SoC with the Cortex part name and fall back to memory
  capacity.
- Performance card with live CPU / memory / disk usage bars and real-time
  network up/down rates. Rates derive from cumulative kernel counters
  sampled on demand by dashboard polling (no background goroutine), with
  bridge/tunnel/vocat virtual interfaces excluded to avoid double
  counting.
- Upcoming scheduled tasks card listing the next three enabled automatic
  tasks with their run times.
- Module online rate card aggregating all recognized modules into one
  large percentage colored by four levels (red/orange/yellow/green).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 17:46:38 +08:00

59 lines
2.2 KiB
TypeScript

import { CalendarClockRegular } from "@fluentui/react-icons";
import { useNavigate } from "react-router-dom";
import type { DashboardUpcomingTask } from "../../types";
import { useI18n } from "../../lib/i18n";
function formatRunAt(value: string): string {
if (!value || value.startsWith("0001-")) return "--";
const date = new Date(value);
if (Number.isNaN(date.getTime())) return "--";
return date.toLocaleString(undefined, {
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
}
// 将要执行的定时任务卡:按 nextRunAt 升序取前 3 条已启用任务。
export function UpcomingTasksCard({ tasks }: { tasks: DashboardUpcomingTask[] }) {
const { t } = useI18n();
const navigate = useNavigate();
return (
<div className="ui-panel p-4">
<div className="mb-3 flex items-center justify-between">
<div className="flex items-center gap-2">
<CalendarClockRegular className="h-4 w-4 text-sky-500" />
<h3 className="text-sm font-bold text-gray-800 dark:text-gray-100">{t("将要执行的定时任务")}</h3>
</div>
<button
type="button"
onClick={() => navigate("/automatic-tasks")}
className="text-xs font-medium text-sky-600 transition-colors hover:text-sky-700 dark:text-sky-400 dark:hover:text-sky-300"
>
{t("查看全部")}
</button>
</div>
{tasks.length === 0 ? (
<div className="flex h-[4.5rem] items-center justify-center text-xs text-gray-400">
{t("暂无定时任务")}
</div>
) : (
<div className="divide-y divide-gray-100 dark:divide-white/5">
{tasks.map((task) => (
<div key={task.id} className="flex items-center justify-between gap-3 py-2 first:pt-0 last:pb-0">
<span className="min-w-0 flex-1 truncate text-xs font-medium text-gray-700 dark:text-gray-300" title={task.name}>
{task.name}
</span>
<span className="flex-shrink-0 font-mono text-xs tabular-nums text-gray-500 dark:text-gray-400">
{formatRunAt(task.nextRunAt)}
</span>
</div>
))}
</div>
)}
</div>
);
}