feat: rebuild review detail decision surface
This commit is contained in:
@@ -51,14 +51,14 @@ export function ReviewActionPanel({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<CardEyebrow>Review action</CardEyebrow>
|
<CardEyebrow>Review action</CardEyebrow>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<CardTitle>审核动作面板</CardTitle>
|
<CardTitle>审核动作</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
审核页只负责做决策,不改详情数据。提交后等待后端结果,再刷新左侧待审核队列。
|
通过、重跑和驳回都在这里完成。详情页只负责决策,不直接修改订单资料。
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{order ? (
|
{order ? (
|
||||||
<div className="rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-4 py-4 text-sm text-[var(--ink-muted)]">
|
<div className="rounded-[var(--panel-radius)] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-3 py-3 text-sm text-[var(--ink-muted)]">
|
||||||
<p className="text-sm font-semibold text-[var(--ink-strong)]">
|
<p className="text-sm font-semibold text-[var(--ink-strong)]">
|
||||||
当前订单 #{order.orderId}
|
当前订单 #{order.orderId}
|
||||||
</p>
|
</p>
|
||||||
@@ -74,20 +74,20 @@ export function ReviewActionPanel({
|
|||||||
<textarea
|
<textarea
|
||||||
value={comment}
|
value={comment}
|
||||||
onChange={(event) => setComment(event.target.value)}
|
onChange={(event) => setComment(event.target.value)}
|
||||||
rows={5}
|
rows={4}
|
||||||
placeholder="重跑或驳回时填写原因,便于流程追踪和复盘。"
|
placeholder="重跑或驳回时填写原因,便于流程追踪和复盘。"
|
||||||
className="min-h-[132px] w-full rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-4 py-3 text-sm leading-6 text-[var(--ink-strong)] outline-none transition placeholder:text-[var(--ink-faint)] focus:border-[var(--accent-primary)] focus:bg-[var(--surface)]"
|
className="min-h-[112px] w-full rounded-[var(--panel-radius)] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-3 py-2.5 text-sm leading-6 text-[var(--ink-strong)] outline-none transition placeholder:text-[var(--ink-faint)] focus:border-[var(--accent-primary)] focus:bg-[var(--surface)]"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{submissionError ? (
|
{submissionError ? (
|
||||||
<div className="rounded-[24px] border border-[#b88472] bg-[#f8ece5] px-5 py-4 text-sm text-[#7f4b3b]">
|
<div className="rounded-[var(--panel-radius)] border border-[#b88472] bg-[#f8ece5] px-4 py-3 text-sm text-[#7f4b3b]">
|
||||||
{submissionError}
|
{submissionError}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{submissionResult ? (
|
{submissionResult ? (
|
||||||
<div className="rounded-[24px] border border-[rgba(88,106,56,0.18)] bg-[rgba(110,127,82,0.12)] px-5 py-4 text-sm text-[#50633b]">
|
<div className="rounded-[var(--panel-radius)] border border-[rgba(88,106,56,0.18)] bg-[rgba(110,127,82,0.12)] px-4 py-3 text-sm text-[#50633b]">
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
<span>已提交审核动作:</span>
|
<span>已提交审核动作:</span>
|
||||||
<StatusBadge variant="reviewDecision" status={submissionResult.decision} />
|
<StatusBadge variant="reviewDecision" status={submissionResult.decision} />
|
||||||
@@ -96,12 +96,12 @@ export function ReviewActionPanel({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-2">
|
||||||
{ACTIONS.map((action) => (
|
{ACTIONS.map((action) => (
|
||||||
<Button
|
<Button
|
||||||
key={action.decision}
|
key={action.decision}
|
||||||
variant={action.variant}
|
variant={action.variant}
|
||||||
size="lg"
|
size="md"
|
||||||
disabled={!order || isSubmitting}
|
disabled={!order || isSubmitting}
|
||||||
onClick={() => onSubmit(action.decision, comment)}
|
onClick={() => onSubmit(action.decision, comment)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ export function ReviewImagePanel({
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-6">
|
<CardContent className="space-y-6">
|
||||||
{selectedAsset ? (
|
{selectedAsset ? (
|
||||||
<div className="rounded-[28px] border border-[var(--border-soft)] bg-[linear-gradient(180deg,rgba(250,247,242,0.96),rgba(238,231,221,0.92))] p-5">
|
<div className="rounded-[var(--panel-radius)] border border-[var(--border-soft)] bg-[linear-gradient(180deg,rgba(250,247,242,0.96),rgba(238,231,221,0.92))] p-4">
|
||||||
<div className="flex min-h-[320px] items-center justify-center rounded-[22px] border border-dashed border-[var(--border-strong)] bg-[rgba(255,255,255,0.55)] p-6 text-center">
|
<div className="flex min-h-[280px] items-center justify-center rounded-[12px] border border-dashed border-[var(--border-strong)] bg-[rgba(255,255,255,0.55)] p-6 text-center">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.22em] text-[var(--ink-faint)]">
|
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.22em] text-[var(--ink-faint)]">
|
||||||
{selectedAsset.isMock ? "Mock preview asset" : "Preview asset"}
|
{selectedAsset.isMock ? "Mock preview asset" : "Preview asset"}
|
||||||
@@ -135,13 +135,13 @@ export function ReviewImagePanel({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{order.hasMockAssets ? (
|
{order.hasMockAssets ? (
|
||||||
<div className="rounded-[24px] border border-[rgba(145,104,46,0.2)] bg-[rgba(202,164,97,0.12)] px-5 py-4 text-sm text-[#7a5323]">
|
<div className="rounded-[var(--panel-radius)] border border-[rgba(145,104,46,0.2)] bg-[rgba(202,164,97,0.12)] px-4 py-3 text-sm text-[#7a5323]">
|
||||||
当前订单包含 mock 资产,审核结论仅用于前后台联调,不代表真实生产结果。
|
当前订单包含 mock 资产,审核结论仅用于前后台联调,不代表真实生产结果。
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{assets.length ? (
|
{assets.length ? (
|
||||||
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
<div className="grid gap-2 md:grid-cols-2 xl:grid-cols-3">
|
||||||
{assets.map((asset) => {
|
{assets.map((asset) => {
|
||||||
const isSelected = asset.id === selectedAsset?.id;
|
const isSelected = asset.id === selectedAsset?.id;
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ export function ReviewImagePanel({
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => onSelectAsset(asset.id)}
|
onClick={() => onSelectAsset(asset.id)}
|
||||||
className={joinClasses(
|
className={joinClasses(
|
||||||
"rounded-[24px] border px-4 py-4 text-left transition duration-150",
|
"rounded-[var(--panel-radius)] border px-3 py-3 text-left transition duration-150",
|
||||||
isSelected
|
isSelected
|
||||||
? "border-[var(--accent-primary)] bg-[rgba(110,127,82,0.09)]"
|
? "border-[var(--accent-primary)] bg-[rgba(110,127,82,0.09)]"
|
||||||
: "border-[var(--border-soft)] bg-[var(--surface-muted)] hover:bg-[var(--surface)]",
|
: "border-[var(--border-soft)] bg-[var(--surface-muted)] hover:bg-[var(--surface)]",
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function ReviewRevisionPanel({
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<CardEyebrow>Manual revision</CardEyebrow>
|
<CardEyebrow>Manual revision</CardEyebrow>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<CardTitle>人工修订稿</CardTitle>
|
<CardTitle>人工修订</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
当前后端支持登记离线修订稿,并在修订稿确认后复用既有 approve
|
当前后端支持登记离线修订稿,并在修订稿确认后复用既有 approve
|
||||||
signal 继续流水线。
|
signal 继续流水线。
|
||||||
@@ -83,7 +83,7 @@ export function ReviewRevisionPanel({
|
|||||||
value={uploadedUri}
|
value={uploadedUri}
|
||||||
onChange={(event) => setUploadedUri(event.target.value)}
|
onChange={(event) => setUploadedUri(event.target.value)}
|
||||||
placeholder="mock://manual-revision-v1"
|
placeholder="mock://manual-revision-v1"
|
||||||
className="min-h-12 w-full rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-4 text-sm text-[var(--ink-strong)] outline-none transition placeholder:text-[var(--ink-faint)] focus:border-[var(--accent-primary)] focus:bg-[var(--surface)]"
|
className="h-9 w-full rounded-[var(--panel-radius)] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-3 text-sm text-[var(--ink-strong)] outline-none transition placeholder:text-[var(--ink-faint)] focus:border-[var(--accent-primary)] focus:bg-[var(--surface)]"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -96,32 +96,32 @@ export function ReviewRevisionPanel({
|
|||||||
onChange={(event) => setComment(event.target.value)}
|
onChange={(event) => setComment(event.target.value)}
|
||||||
rows={4}
|
rows={4}
|
||||||
placeholder="说明这版离线修订解决了什么问题。"
|
placeholder="说明这版离线修订解决了什么问题。"
|
||||||
className="min-h-[112px] w-full rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-4 py-3 text-sm leading-6 text-[var(--ink-strong)] outline-none transition placeholder:text-[var(--ink-faint)] focus:border-[var(--accent-primary)] focus:bg-[var(--surface)]"
|
className="min-h-[104px] w-full rounded-[var(--panel-radius)] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-3 py-2.5 text-sm leading-6 text-[var(--ink-strong)] outline-none transition placeholder:text-[var(--ink-faint)] focus:border-[var(--accent-primary)] focus:bg-[var(--surface)]"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{revisionError ? (
|
{revisionError ? (
|
||||||
<div className="rounded-[24px] border border-[#b88472] bg-[#f8ece5] px-5 py-4 text-sm text-[#7f4b3b]">
|
<div className="rounded-[var(--panel-radius)] border border-[#b88472] bg-[#f8ece5] px-4 py-3 text-sm text-[#7f4b3b]">
|
||||||
{revisionError}
|
{revisionError}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{revisionResult ? (
|
{revisionResult ? (
|
||||||
<div className="rounded-[24px] border border-[rgba(88,106,56,0.18)] bg-[rgba(110,127,82,0.12)] px-5 py-4 text-sm text-[#50633b]">
|
<div className="rounded-[var(--panel-radius)] border border-[rgba(88,106,56,0.18)] bg-[rgba(110,127,82,0.12)] px-4 py-3 text-sm text-[#50633b]">
|
||||||
已登记修订稿 v{revisionResult.versionNo},当前共有 {revisionResult.revisionCount} 个修订版本。
|
已登记修订稿 v{revisionResult.versionNo},当前共有 {revisionResult.revisionCount} 个修订版本。
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{confirmResult ? (
|
{confirmResult ? (
|
||||||
<div className="rounded-[24px] border border-[rgba(88,106,56,0.18)] bg-[rgba(110,127,82,0.12)] px-5 py-4 text-sm text-[#50633b]">
|
<div className="rounded-[var(--panel-radius)] border border-[rgba(88,106,56,0.18)] bg-[rgba(110,127,82,0.12)] px-4 py-3 text-sm text-[#50633b]">
|
||||||
已确认修订稿并继续流水线,等待返回审核队列。
|
已确认修订稿并继续流水线,等待返回审核队列。
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="lg"
|
size="md"
|
||||||
disabled={!order || !selectedAsset || isSubmitting}
|
disabled={!order || !selectedAsset || isSubmitting}
|
||||||
onClick={() => onRegisterRevision({ uploadedUri, comment })}
|
onClick={() => onRegisterRevision({ uploadedUri, comment })}
|
||||||
>
|
>
|
||||||
@@ -130,7 +130,7 @@ export function ReviewRevisionPanel({
|
|||||||
{pendingManualConfirm ? (
|
{pendingManualConfirm ? (
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="lg"
|
size="md"
|
||||||
disabled={!order || isSubmitting}
|
disabled={!order || isSubmitting}
|
||||||
onClick={() => onConfirmRevision(comment)}
|
onClick={() => onConfirmRevision(comment)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ export function ReviewWorkflowSummary({
|
|||||||
|
|
||||||
{!isLoading && !error && workflow ? (
|
{!isLoading && !error && workflow ? (
|
||||||
<>
|
<>
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
<div className="grid gap-2 sm:grid-cols-2">
|
||||||
<div className="rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-4 py-4">
|
<div className="rounded-[var(--panel-radius)] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-3 py-3">
|
||||||
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
||||||
Current step
|
Current step
|
||||||
</p>
|
</p>
|
||||||
@@ -64,7 +64,7 @@ export function ReviewWorkflowSummary({
|
|||||||
<StatusBadge status={workflow.status} />
|
<StatusBadge status={workflow.status} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-4 py-4">
|
<div className="rounded-[var(--panel-radius)] border border-[var(--border-soft)] bg-[var(--surface-muted)] px-3 py-3">
|
||||||
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
||||||
Failure count
|
Failure count
|
||||||
</p>
|
</p>
|
||||||
@@ -75,7 +75,7 @@ export function ReviewWorkflowSummary({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{workflow.hasMockAssets ? (
|
{workflow.hasMockAssets ? (
|
||||||
<div className="rounded-[24px] border border-[rgba(145,104,46,0.2)] bg-[rgba(202,164,97,0.12)] px-5 py-4 text-sm text-[#7a5323]">
|
<div className="rounded-[var(--panel-radius)] border border-[rgba(145,104,46,0.2)] bg-[rgba(202,164,97,0.12)] px-4 py-3 text-sm text-[#7a5323]">
|
||||||
当前流程包含 mock 资产,适合联调审核动作,但不应被视为最终生产输出。
|
当前流程包含 mock 资产,适合联调审核动作,但不应被视为最终生产输出。
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -85,7 +85,7 @@ export function ReviewWorkflowSummary({
|
|||||||
<div
|
<div
|
||||||
key={step.id}
|
key={step.id}
|
||||||
className={joinClasses(
|
className={joinClasses(
|
||||||
"rounded-[24px] border px-4 py-4",
|
"rounded-[var(--panel-radius)] border px-3 py-3",
|
||||||
step.isCurrent
|
step.isCurrent
|
||||||
? "border-[var(--accent-primary)] bg-[rgba(110,127,82,0.09)]"
|
? "border-[var(--accent-primary)] bg-[rgba(110,127,82,0.09)]"
|
||||||
: "border-[var(--border-soft)] bg-[var(--surface-muted)]",
|
: "border-[var(--border-soft)] bg-[var(--surface-muted)]",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useRouter } from "next/navigation";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { EmptyState } from "@/components/ui/empty-state";
|
import { EmptyState } from "@/components/ui/empty-state";
|
||||||
import { PageHeader } from "@/components/ui/page-header";
|
import { MetricChip } from "@/components/ui/metric-chip";
|
||||||
import { StatusBadge } from "@/components/ui/status-badge";
|
import { StatusBadge } from "@/components/ui/status-badge";
|
||||||
import { ReviewActionPanel } from "@/features/reviews/components/review-action-panel";
|
import { ReviewActionPanel } from "@/features/reviews/components/review-action-panel";
|
||||||
import { ReviewImagePanel } from "@/features/reviews/components/review-image-panel";
|
import { ReviewImagePanel } from "@/features/reviews/components/review-image-panel";
|
||||||
@@ -340,61 +340,48 @@ export function ReviewWorkbenchDetailScreen({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="space-y-8">
|
<section className="space-y-5">
|
||||||
<PageHeader
|
<div className="sticky top-0 z-10 -mx-4 border-b border-[var(--border-soft)] bg-[rgba(255,250,242,0.95)] px-4 py-4 backdrop-blur md:-mx-6 md:px-6">
|
||||||
eyebrow="Review detail"
|
<div className="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||||
title={`订单 #${orderDetail.orderId}`}
|
<div className="space-y-2">
|
||||||
description="审核详情页只处理单个订单,列表筛选和切单行为统一留在审核工作台首页。"
|
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.24em] text-[var(--ink-faint)]">
|
||||||
meta={`更新于 ${formatTimestamp(orderDetail.updatedAt)}`}
|
Review detail
|
||||||
actions={
|
</p>
|
||||||
<Link
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
href="/reviews/workbench"
|
<h1 className="text-2xl font-semibold tracking-[-0.03em] text-[var(--ink-strong)]">
|
||||||
className="inline-flex min-h-11 items-center justify-center rounded-full border border-[var(--border-strong)] bg-[var(--surface)] px-4 text-sm font-medium text-[var(--ink-strong)] transition hover:bg-[var(--surface-muted)]"
|
订单 #{orderDetail.orderId}
|
||||||
>
|
</h1>
|
||||||
返回审核列表
|
<StatusBadge status={orderDetail.status} />
|
||||||
</Link>
|
<StatusBadge variant="workflowStep" status={orderDetail.currentStep} />
|
||||||
}
|
</div>
|
||||||
/>
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<MetricChip
|
||||||
|
label="workflow"
|
||||||
|
value={orderDetail.workflowId ?? "暂未分配"}
|
||||||
|
/>
|
||||||
|
<MetricChip label="step" value={orderDetail.currentStepLabel} />
|
||||||
|
<MetricChip
|
||||||
|
label="revision"
|
||||||
|
value={orderDetail.pendingManualConfirm ? "修订待确认" : "无待确认修订"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
<div className="flex flex-col items-start gap-2 xl:items-end">
|
||||||
<div className="rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface)] px-4 py-4">
|
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.18em] text-[var(--ink-faint)]">
|
||||||
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
更新于 {formatTimestamp(orderDetail.updatedAt)}
|
||||||
Order status
|
</p>
|
||||||
</p>
|
<Link
|
||||||
<div className="mt-3">
|
href="/reviews/workbench"
|
||||||
<StatusBadge status={orderDetail.status} />
|
className="inline-flex h-9 items-center justify-center rounded-md border border-[var(--border-strong)] bg-[var(--surface)] px-3 text-sm font-medium text-[var(--ink-strong)] transition hover:bg-[var(--surface-muted)]"
|
||||||
</div>
|
>
|
||||||
</div>
|
返回审核列表
|
||||||
<div className="rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface)] px-4 py-4">
|
</Link>
|
||||||
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
|
||||||
Workflow
|
|
||||||
</p>
|
|
||||||
<p className="mt-3 text-lg font-semibold tracking-[-0.02em] text-[var(--ink-strong)]">
|
|
||||||
{orderDetail.workflowId ?? "暂未分配"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface)] px-4 py-4">
|
|
||||||
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
|
||||||
Current step
|
|
||||||
</p>
|
|
||||||
<div className="mt-3 flex flex-wrap items-center gap-2">
|
|
||||||
<StatusBadge variant="workflowStep" status={orderDetail.currentStep} />
|
|
||||||
<span className="text-sm text-[var(--ink-muted)]">
|
|
||||||
{orderDetail.currentStepLabel}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-[24px] border border-[var(--border-soft)] bg-[var(--surface)] px-4 py-4">
|
|
||||||
<p className="font-[var(--font-mono)] text-[11px] uppercase tracking-[0.2em] text-[var(--ink-faint)]">
|
|
||||||
Workflow status
|
|
||||||
</p>
|
|
||||||
<div className="mt-3">
|
|
||||||
<StatusBadge status={workflowDetail.status} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px]">
|
<div className="grid gap-5 xl:grid-cols-[minmax(0,1fr)_380px]">
|
||||||
<ReviewImagePanel
|
<ReviewImagePanel
|
||||||
error={contextError}
|
error={contextError}
|
||||||
isLoading={isLoadingContext}
|
isLoading={isLoadingContext}
|
||||||
@@ -403,7 +390,16 @@ export function ReviewWorkbenchDetailScreen({
|
|||||||
onSelectAsset={setSelectedAssetId}
|
onSelectAsset={setSelectedAssetId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="grid gap-6">
|
<div className="grid gap-4 xl:sticky xl:top-24 xl:self-start">
|
||||||
|
<ReviewActionPanel
|
||||||
|
key={`${orderDetail.orderId}-${submissionResult?.decision ?? "idle"}`}
|
||||||
|
isSubmitting={isSubmitting}
|
||||||
|
order={orderDetail}
|
||||||
|
selectedAsset={selectedAsset}
|
||||||
|
submissionError={submissionError}
|
||||||
|
submissionResult={submissionResult}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
/>
|
||||||
<ReviewRevisionPanel
|
<ReviewRevisionPanel
|
||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSubmitting}
|
||||||
order={orderDetail}
|
order={orderDetail}
|
||||||
@@ -415,15 +411,6 @@ export function ReviewWorkbenchDetailScreen({
|
|||||||
onRegisterRevision={handleRegisterRevision}
|
onRegisterRevision={handleRegisterRevision}
|
||||||
onConfirmRevision={handleConfirmRevision}
|
onConfirmRevision={handleConfirmRevision}
|
||||||
/>
|
/>
|
||||||
<ReviewActionPanel
|
|
||||||
key={`${orderDetail.orderId}-${submissionResult?.decision ?? "idle"}`}
|
|
||||||
isSubmitting={isSubmitting}
|
|
||||||
order={orderDetail}
|
|
||||||
selectedAsset={selectedAsset}
|
|
||||||
submissionError={submissionError}
|
|
||||||
submissionResult={submissionResult}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
/>
|
|
||||||
<ReviewWorkflowSummary
|
<ReviewWorkflowSummary
|
||||||
error={contextError}
|
error={contextError}
|
||||||
isLoading={isLoadingContext}
|
isLoading={isLoadingContext}
|
||||||
|
|||||||
@@ -222,6 +222,17 @@ afterEach(() => {
|
|||||||
pushMock.mockReset();
|
pushMock.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("renders a sticky summary with grouped decision panels", async () => {
|
||||||
|
const fetchMock = createFetchMock();
|
||||||
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
|
|
||||||
|
render(<ReviewWorkbenchDetailScreen orderId={101} />);
|
||||||
|
|
||||||
|
expect(await screen.findByRole("link", { name: "返回审核列表" })).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("审核动作")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("人工修订")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test("requires a comment before rerun_face submission in detail view", async () => {
|
test("requires a comment before rerun_face submission in detail view", async () => {
|
||||||
const fetchMock = createFetchMock();
|
const fetchMock = createFetchMock();
|
||||||
vi.stubGlobal("fetch", fetchMock);
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
|
|||||||
Reference in New Issue
Block a user