feat: persist feature research and versioned alpha assessments
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button, Input, Toast } from "@douyinfe/semi-ui-19";
|
||||
import { api, post } from "../api";
|
||||
import { ResearchSelect } from "./ResearchSelect";
|
||||
export type ViewContent = {
|
||||
name: string;
|
||||
filters: Record<string, unknown>;
|
||||
columns: string[];
|
||||
};
|
||||
type View = { id: string; version: number; content: ViewContent };
|
||||
export function SavedViews({
|
||||
filters,
|
||||
columns,
|
||||
onRestore,
|
||||
}: {
|
||||
filters: Record<string, unknown>;
|
||||
columns: string[];
|
||||
onRestore: (view: ViewContent) => void;
|
||||
}) {
|
||||
const [items, setItems] = useState<View[]>([]);
|
||||
const [name, setName] = useState("");
|
||||
const [selected, setSelected] = useState<string>();
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [q, setQ] = useState("");
|
||||
const [version, setVersion] = useState(0);
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
api<{ items: View[] }>(
|
||||
`/research/assets?kind=view&q=${encodeURIComponent(q)}&limit=100`,
|
||||
{ signal: controller.signal },
|
||||
)
|
||||
.then((r) => setItems(r.items))
|
||||
.catch((e) => {
|
||||
if (!controller.signal.aborted) Toast.error(e.message);
|
||||
});
|
||||
return () => controller.abort();
|
||||
}, [q, version]);
|
||||
async function save(replace: boolean) {
|
||||
setBusy(true);
|
||||
try {
|
||||
const old = items.find((i) => i.id === selected);
|
||||
if (replace && !old) return;
|
||||
const content = {
|
||||
name: name.trim() || old?.content.name || "研究队列",
|
||||
filters: Object.fromEntries(
|
||||
Object.entries(filters).filter(
|
||||
([, v]) => v !== "" && v !== null && v !== undefined,
|
||||
),
|
||||
),
|
||||
columns,
|
||||
};
|
||||
const value = replace
|
||||
? await api<View>(`/research/assets/${old!.id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
kind: "view",
|
||||
version: old!.version,
|
||||
content,
|
||||
}),
|
||||
})
|
||||
: await post<View>("/research/assets", { kind: "view", content });
|
||||
setSelected(value.id);
|
||||
setName(value.content.name);
|
||||
setVersion((n) => n + 1);
|
||||
Toast.success("筛选视图已保存");
|
||||
} catch (e) {
|
||||
Toast.error((e as Error).message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="research-saved-views inline-actions">
|
||||
<ResearchSelect
|
||||
label="保存的 Alpha 视图"
|
||||
placeholder="搜索并恢复研究队列"
|
||||
filter
|
||||
remote
|
||||
onSearch={setQ}
|
||||
value={selected}
|
||||
optionList={items.map((i) => ({
|
||||
value: i.id,
|
||||
label: `${i.content.name} · v${i.version}`,
|
||||
}))}
|
||||
onChange={(v) => {
|
||||
const item = items.find((i) => i.id === v);
|
||||
if (item) {
|
||||
setSelected(item.id);
|
||||
setName(item.content.name);
|
||||
onRestore(item.content);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
aria-label="视图名称"
|
||||
placeholder="视图名称"
|
||||
value={name}
|
||||
onChange={setName}
|
||||
style={{ width: 160 }}
|
||||
/>
|
||||
<Button size="small" disabled={busy} onClick={() => void save(false)}>
|
||||
保存为新视图
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
disabled={busy || !selected}
|
||||
onClick={() => void save(true)}
|
||||
>
|
||||
更新当前视图
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user