refactor: unify AI capabilities and workspace integration
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import type { ComponentType } from "react";
|
||||
import { Button, Tag } from "@douyinfe/semi-ui-19";
|
||||
import { BacktestToolCard } from "../backtests/BacktestToolCard";
|
||||
import { CatalogToolCard } from "../research/CatalogToolCard";
|
||||
import { AlphaToolCard } from "./AlphaToolCard";
|
||||
import { JobToolCard } from "./JobToolCard";
|
||||
import type { ToolCardProps } from "./types";
|
||||
|
||||
// The server owns labels, effects and refresh targets. This is the only place
|
||||
// where a new presentation shape joins the workspace; tool names are domain-local.
|
||||
const renderers: Record<string, ComponentType<ToolCardProps>> = {
|
||||
alpha: AlphaToolCard,
|
||||
jobs: JobToolCard,
|
||||
catalog: CatalogToolCard,
|
||||
backtest: BacktestToolCard,
|
||||
};
|
||||
const labels: Record<string, string> = {
|
||||
pending: "待确认",
|
||||
completed: "已完成",
|
||||
failed: "失败",
|
||||
denied: "已拒绝",
|
||||
cancelled: "已取消",
|
||||
};
|
||||
|
||||
export function BusinessCard(
|
||||
props: ToolCardProps & {
|
||||
onDecision: (id: string, approved: boolean) => Promise<void>;
|
||||
disabled: boolean;
|
||||
},
|
||||
) {
|
||||
const { call, onDecision, disabled } = props;
|
||||
const renderer = call.presentation?.renderer ?? "generic";
|
||||
const Renderer = Object.hasOwn(renderers, renderer)
|
||||
? renderers[renderer]
|
||||
: undefined;
|
||||
const pending = call.status === "pending";
|
||||
const result = call.result ?? {};
|
||||
return (
|
||||
<section className="ai-tool-card" data-status={call.status}>
|
||||
<div className="ai-card-title">
|
||||
<strong>{call.presentation?.label ?? call.name}</strong>
|
||||
<Tag
|
||||
color={pending ? "orange" : call.status === "failed" ? "red" : "grey"}
|
||||
>
|
||||
{labels[call.status] ?? call.status}
|
||||
</Tag>
|
||||
</div>
|
||||
{Renderer ? (
|
||||
<Renderer {...props} />
|
||||
) : (
|
||||
<>
|
||||
<p>此记录暂不支持专用展示,原始结果仍可查看。</p>
|
||||
<details>
|
||||
<summary>查看业务记录</summary>
|
||||
<pre>
|
||||
{JSON.stringify(pending ? call.preview : result, null, 2)}
|
||||
</pre>
|
||||
</details>
|
||||
</>
|
||||
)}
|
||||
{pending && (
|
||||
<div className="ai-approval">
|
||||
<p className="muted">确认后执行以上预览中的固定操作。</p>
|
||||
<div className="inline-actions">
|
||||
<Button
|
||||
theme="solid"
|
||||
disabled={
|
||||
disabled || !Renderer || call.presentation?.effect !== "confirm"
|
||||
}
|
||||
onClick={() => void onDecision(call.id, true)}
|
||||
>
|
||||
确认执行
|
||||
</Button>
|
||||
<Button
|
||||
type="tertiary"
|
||||
disabled={disabled}
|
||||
onClick={() => void onDecision(call.id, false)}
|
||||
>
|
||||
拒绝
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{typeof result.error === "string" && (
|
||||
<p className="error-text">{result.error}</p>
|
||||
)}
|
||||
{typeof result._warning === "string" && (
|
||||
<p role="status">{result._warning}</p>
|
||||
)}
|
||||
{result.denied === true && <p>已拒绝,未执行此操作。</p>}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user