65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
import { SectionCard } from "../components/SectionCard";
|
|
import { useWidgetData } from "../hooks/useWidgets";
|
|
import type { WidgetInstance } from "../types";
|
|
|
|
interface Props {
|
|
widget: WidgetInstance;
|
|
refreshIntervalMs: number;
|
|
description?: string;
|
|
}
|
|
|
|
type SshTaskResult = {
|
|
exit_status: number;
|
|
stdout: string;
|
|
stderr: string;
|
|
};
|
|
|
|
export function SshTaskWidget({
|
|
widget,
|
|
refreshIntervalMs,
|
|
description,
|
|
}: Props) {
|
|
const { data, isLoading } = useWidgetData(widget.id, refreshIntervalMs);
|
|
const result = data?.data as SshTaskResult | undefined;
|
|
|
|
return (
|
|
<SectionCard title={widget.title} description={description}>
|
|
{isLoading && !data ? (
|
|
<div className="flex flex-col gap-2">
|
|
<Skeleton className="h-4 w-full" />
|
|
<Skeleton className="h-4 w-3/4" />
|
|
</div>
|
|
) : data?.error ? (
|
|
<Alert variant="destructive">
|
|
<AlertDescription>{data.error}</AlertDescription>
|
|
</Alert>
|
|
) : result ? (
|
|
<div className="flex flex-col gap-3">
|
|
<div className="text-xs text-muted-foreground">
|
|
Exit status:{" "}
|
|
<span
|
|
className={
|
|
result.exit_status === 0 ? "text-green-600" : "text-destructive"
|
|
}
|
|
>
|
|
{result.exit_status}
|
|
</span>
|
|
</div>
|
|
{result.stdout ? (
|
|
<pre className="max-h-64 overflow-auto rounded bg-muted p-2 text-xs">
|
|
{result.stdout}
|
|
</pre>
|
|
) : null}
|
|
{result.stderr ? (
|
|
<pre className="max-h-64 overflow-auto rounded bg-destructive/10 p-2 text-xs text-destructive">
|
|
{result.stderr}
|
|
</pre>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
</SectionCard>
|
|
);
|
|
}
|