"use client";
/**
* 归档页的**交互动作**(数据在服务端渲染,这里只管「页面之外」的动作)。
*
* 拆出来的原因:归档详情页改为 Server Component(只读内容必须能被 SSR 直出,
* 否则不带 JS 的抓取/分享预览看不到「选股条件」与「交易执行依据」);
* 而删除、导出、折叠 spec 这些必须有事件处理,就集中放在这个客户端组件里。
*
* 删除的确认文案必须写清后果:结果只存归档一份(`job.result_json` 对新记录为 NULL,
* `GET /api/jobs/{id}` 是从归档回读的),所以删归档 = 该次回测结果彻底消失。
* 先前的文案写成「不影响作业记录」,那是**误导**。
*/
import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { apiDelete, apiGetText } from "@/lib/api";
import { Banner, Btn, Card } from "@/components/ui";
/**
* 刻意只收 `id` / `spec`(**不接收完整 result**):归档结果可达数 MB,而服务端已经把它
* 传给图表组件了;再传一份给本组件会让 RSC 载荷翻倍。导出时按需拉取。
*/
export function ArchiveActions({ id, spec }: { id: string; spec: unknown }) {
const router = useRouter();
const [deleting, setDeleting] = useState(false);
const [error, setError] = useState("");
const [showSpec, setShowSpec] = useState(false);
async function downloadJson() {
setError("");
try {
const text = await apiGetText(`/experiments/${encodeURIComponent(id)}`);
const blob = new Blob([text], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${id}-archive.json`;
a.click();
URL.revokeObjectURL(url);
} catch (e) {
setError(`导出失败:${(e as Error).message}`);
}
}
async function remove() {
const ok = window.confirm(
`删除归档 ${id}?\n\n` +
`· 删除后这份快照(结果 + spec)无法再查看,也不可恢复;\n` +
`· 结果只存归档这一份 —— 删除后连该次执行的作业记录也读不回结果\n` +
` (完整存档上线前的早期归档在作业表里另有副本,但不应依赖);\n` +
`· 想留底请先点左侧「导出完整 JSON」。\n\n确认删除?`
);
if (!ok) return;
setDeleting(true);
setError("");
try {
await apiDelete(`/experiments/${encodeURIComponent(id)}`);
router.push("/experiments");
} catch (e) {
setError((e as Error).message);
setDeleting(false);
}
}
return (
<>
{error ?
{JSON.stringify(spec ?? {}, null, 2)}