37 lines
2.4 KiB
TypeScript
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>
|
|
}
|