import { Activity, Clock, Play, RefreshCw, TriangleAlert } from "lucide-react"; import { useState } from "react"; import { LineSeriesChart } from "../../components/LineSeriesChart"; import { chartRangesThrough, type ChartRangeValue, } from "../../components/chartRanges"; import { useRunSchedulerAction, useSchedulerRuns, useSchedulerSamples, useSchedulerStatus, } from "../../hooks/useScheduler"; import type { ServiceInstance } from "../../types"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; function formatTimestamp(value: number | null): string { return value ? new Date(value * 1000).toLocaleString() : "Never"; } function statusVariant( status: string, ): "default" | "secondary" | "destructive" | "outline" { if (status === "success") return "default"; if (status === "failure") return "destructive"; if (status === "running") return "secondary"; return "outline"; } export function QbittorrentTab({ instance }: { instance: ServiceInstance }) { const [selectedRange, setSelectedRange] = useState(1800); const status = useSchedulerStatus(instance.id); const samples = useSchedulerSamples(instance.id, selectedRange); const runs = useSchedulerRuns(instance.id); const runNow = useRunSchedulerAction(); const stale = Boolean(status.data?.enabled && status.data.is_stale); const hasError = Boolean(status.data?.last_error || runNow.error); const chartSeries = samples.data ? [ { label: "download", points: samples.data.samples.map((sample) => ({ t: sample.ts * 1000, v: sample.dl_speed, })), }, { label: "upload", points: samples.data.samples.map((sample) => ({ t: sample.ts * 1000, v: sample.up_speed, })), }, ] : []; return (
Headless speed polling {status.data && ( {status.data.enabled ? "Enabled" : "Paused"} )}
{(stale || hasError) && ( {hasError ? "Polling error" : "Data may be stale"} {status.data?.last_error || runNow.error?.message || "No successful sample has been recorded recently."} )} Speed history {samples.isLoading ? ( ) : ( )}
Last success
{formatTimestamp(status.data?.last_success_at ?? null)}
Next poll
{formatTimestamp(status.data?.next_run_at ?? null)}
Consecutive failures
{status.data?.consecutive_failures ?? 0}
Recent runs {runs.isLoading ? ( ) : runs.data?.items.length ? ( runs.data.items.map((run) => (
{run.status} {run.trigger} ยท {formatTimestamp(run.started_at)}
{run.error && ( {run.error} )}
)) ) : (
No runs recorded yet.
)}
); }