feat: add fixed submission-blocked Alpha tab
Deploy production / deploy (push) Successful in 53s

This commit is contained in:
yuxuanhui
2026-09-12 23:22:59 +08:00
parent 7547dbc565
commit bbf0e37130
11 changed files with 201 additions and 6 deletions
+26 -4
View File
@@ -184,6 +184,7 @@ export function AlphaPage({
const [filters, setFilters] = useState<Record<string, string>>({});
const [filterOpen, setFilterOpen] = useState(false);
const [selectedViewId, setSelectedViewId] = useState<string | null>(null);
const [submissionBlocked, setSubmissionBlocked] = useState(false);
const [submission, setSubmission] = useState<Submission>("UNSUBMITTED");
const [syncingScope, setSyncingScope] = useState<Submission | null>(null);
const [page, setPage] = useState(1);
@@ -250,12 +251,13 @@ export function AlphaPage({
() => ({
...filters,
submission,
submission_blocked: submissionBlocked || undefined,
sort,
direction,
limit: pageSize,
offset: (page - 1) * pageSize,
}),
[filters, submission, sort, direction, pageSize, page],
[filters, submission, submissionBlocked, sort, direction, pageSize, page],
);
const query = queryString(params);
useEffect(() => {
@@ -286,6 +288,7 @@ export function AlphaPage({
limit: _limit,
offset: _offset,
submission: nextSubmission,
submission_blocked: nextBlocked,
...values
} = action.filters;
const next = Object.fromEntries(
@@ -299,6 +302,7 @@ export function AlphaPage({
setSelected([]);
if (nextSubmission === "SUBMITTED" || nextSubmission === "UNSUBMITTED")
setSubmission(nextSubmission);
setSubmissionBlocked(nextBlocked === true || nextBlocked === "true");
if (nextSort) setSort(String(nextSort));
if (nextDirection) setDirection(String(nextDirection));
}
@@ -352,7 +356,8 @@ export function AlphaPage({
setSelectedViewId(null);
setFilterOpen(false);
setDirection("desc");
setSubmission(value as Submission);
setSubmission(value === "SUBMITTED" ? "SUBMITTED" : "UNSUBMITTED");
setSubmissionBlocked(value === "SUBMISSION_BLOCKED");
setDraft({});
setFilters({});
setPage(1);
@@ -925,17 +930,26 @@ export function AlphaPage({
<section className="library-panel" ref={tablePanel}>
<SavedViews
submission={submission}
submissionBlocked={submissionBlocked}
selectedId={selectedViewId}
onSelect={setSelectedViewId}
onSubmissionChange={changeSubmission}
suspended={overlaySuspended || !active}
onOverlay={onOverlay}
filters={{ ...filters, submission, sort, direction, limit: pageSize }}
filters={{
...filters,
submission,
submission_blocked: submissionBlocked || undefined,
sort,
direction,
limit: pageSize,
}}
columns={displayedColumns}
onRestore={(view) => {
setFilterOpen(false);
const {
submission: savedSubmission,
submission_blocked: savedBlocked,
sort: savedSort,
direction: savedDirection,
limit,
@@ -954,6 +968,9 @@ export function AlphaPage({
setSubmission(
savedSubmission === "SUBMITTED" ? "SUBMITTED" : "UNSUBMITTED",
);
setSubmissionBlocked(
savedBlocked === true || savedBlocked === "true",
);
setSort(String(savedSort || "date_created"));
setDirection(String(savedDirection || "desc"));
if (typeof limit === "number") setPageSize(limit);
@@ -1228,7 +1245,12 @@ export function AlphaPage({
<footer className="table-pagination" aria-label="Alpha 分页">
<div className="alpha-table-meta">
<span className="muted">
{submission === "UNSUBMITTED" ? "待提交" : "已提交"} Alpha{" "}
{submissionBlocked
? "提交受阻"
: submission === "UNSUBMITTED"
? "待提交"
: "已提交"}{" "}
Alpha{" "}
<span className="count-pill">{data.total.toLocaleString()}</span>{" "}
条
</span>
+16 -2
View File
@@ -47,6 +47,7 @@ export function SavedViews({
filters,
columns,
submission,
submissionBlocked,
selectedId,
onSelect,
onSubmissionChange,
@@ -57,6 +58,7 @@ export function SavedViews({
filters: Record<string, unknown>;
columns: string[];
submission: Submission;
submissionBlocked: boolean;
selectedId: string | null;
onSelect: (id: string | null) => void;
onSubmissionChange: (submission: string) => void;
@@ -152,7 +154,10 @@ export function SavedViews({
setItems((previous) =>
previous.filter((item) => item.id !== deleting.id),
);
if (selectedId === deleting.id) onSubmissionChange(submission);
if (selectedId === deleting.id)
onSubmissionChange(
submissionBlocked ? "SUBMISSION_BLOCKED" : submission,
);
setDeleting(null);
Toast.success("视图已删除");
} catch (error) {
@@ -168,7 +173,11 @@ export function SavedViews({
className="alpha-view-tabs"
type="card"
collapsible="auto"
activeKey={selectedId ?? submission}
tabPaneMotion={false}
activeKey={
selectedId ??
(submissionBlocked ? "SUBMISSION_BLOCKED" : submission)
}
onChange={(key) => {
const view = items.find((item) => item.id === key);
if (view) {
@@ -187,6 +196,11 @@ export function SavedViews({
tab: "已提交",
icon: <IconList aria-hidden="true" />,
},
{
itemKey: "SUBMISSION_BLOCKED",
tab: "提交受阻",
icon: <IconList aria-hidden="true" />,
},
...items.map((view) => ({
itemKey: view.id,
icon: <IconList aria-hidden="true" />,
+46
View File
@@ -0,0 +1,46 @@
import { expect, test } from "@playwright/test";
test("fixed blocked tab retains its scope through filtering, reset and saved views", async ({
page,
}) => {
let params = new URLSearchParams();
await page.route(/\/api\/v1\/alphas\?/, async (route) => {
params = new URL(route.request().url()).searchParams;
await route.fulfill({
json: { items: [], total: 0, limit: 25, offset: 0 },
});
});
await page.goto("/#alphas");
await page.getByLabel("密码", { exact: true }).fill("browser-test-password");
await page.getByRole("button", { name: "进入工作空间" }).click();
const blocked = page.getByRole("tab", { name: "提交受阻", exact: true });
await blocked.click();
await expect.poll(() => params.get("submission_blocked")).toBe("true");
expect(params.get("submission")).toBe("UNSUBMITTED");
await page.getByRole("button", { name: "筛选 Alpha", exact: true }).click();
await page
.getByRole("textbox", { name: "搜索 Alpha", exact: true })
.fill("candidate");
await page.getByRole("button", { name: "查询", exact: true }).click();
await expect.poll(() => params.get("q")).toBe("candidate");
expect(params.get("submission_blocked")).toBe("true");
await page.getByRole("button", { name: "保存为新视图", exact: true }).click();
await page
.getByRole("textbox", { name: "视图名称", exact: true })
.fill("受阻候选");
await page.getByRole("button", { name: "保存视图", exact: true }).click();
const saved = page.getByRole("tab", { name: "受阻候选" });
await expect(saved).toBeVisible();
await page.getByRole("tab", { name: "已提交", exact: true }).click();
await expect.poll(() => params.get("submission")).toBe("SUBMITTED");
expect(params.has("submission_blocked")).toBe(false);
await saved.click();
await expect.poll(() => params.get("submission_blocked")).toBe("true");
expect(params.get("q")).toBe("candidate");
await blocked.click();
await page.getByRole("button", { name: "筛选 Alpha", exact: true }).click();
await page.getByRole("button", { name: "重置", exact: true }).click();
await expect.poll(() => params.has("q")).toBe(false);
expect(params.get("submission_blocked")).toBe("true");
await expect(blocked).toHaveAttribute("aria-selected", "true");
});