Files
MikrotikManager/components/mcp/artifact-downloads.tsx
T

37 lines
2.4 KiB
TypeScript

"use client"
import { useEffect, useRef, useState } from "react"
import { Button } from "@/components/ui/button"
import { humanArtifactError, humanArtifactReceipt } from "@/shared/api/mcp-artifact"
import type { McpApi } from "@/shared/api/mcp"
function ArtifactDownload({ api, receipt }: { api: McpApi; receipt: unknown }) {
const [busy, setBusy] = useState(false), [error, setError] = useState(""), [saved, setSaved] = useState(false)
const controller = useRef<AbortController | null>(null)
useEffect(() => () => controller.current?.abort(), [])
const download = async () => {
if (controller.current) return
const request = new AbortController()
controller.current = request
setBusy(true); setError(""); setSaved(false)
try { await api.downloadArtifact(receipt, request.signal); if (!request.signal.aborted) setSaved(true) }
catch (failure) { if (!request.signal.aborted) setError(humanArtifactError(failure)) }
finally { if (!request.signal.aborted) setBusy(false); controller.current = null }
}
return <div className="flex flex-col items-start gap-2">
<Button variant="outline" disabled={busy} aria-busy={busy} onClick={() => { void download() }}>{busy ? "Скачивание…" : "Скачать файл"}</Button>
{error && <p role="alert" className="text-sm text-destructive">{error}</p>}
<p role="status" aria-live="polite" className="text-xs text-muted-foreground">{busy ? "Получаем файл с проверкой доступа…" : saved ? "Файл передан браузеру для сохранения." : "Файл доступен владельцу через авторизацию портала. Срок хранения ограничен."}</p>
</div>
}
export function ArtifactDownloads({ api, operation }: { api: McpApi; operation: Record<string, unknown> }) {
if (operation.state !== "applied" || !Array.isArray(operation.artifactReceipts)) return null
const receipts = [...new Map(operation.artifactReceipts.flatMap(value => {
const receipt = humanArtifactReceipt(value)
return receipt ? [[receipt.artifactId, value] as const] : []
})).entries()]
if (!receipts.length) return null
return <div className="flex flex-col gap-3 rounded-md border p-3"><h3 className="text-sm font-medium">Файлы операции</h3>{receipts.map(([id, receipt]) => <ArtifactDownload key={id} api={api} receipt={receipt} />)}</div>
}