feat: improve app ui and project metadata
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import type { ReviewConflict } from "@/lib/review/sse";
|
||||
import { AlertTriangle, CheckCircle2, LocateFixed, Sparkles } from "lucide-react";
|
||||
|
||||
import { Badge } from "@/components/ui/Badge";
|
||||
import { TextInput } from "@/components/ui/TextInput";
|
||||
import { buttonClass } from "@/lib/ui/variants";
|
||||
import type { DecisionDraft, Verdict } from "@/lib/review/decisions";
|
||||
import type { ReviewConflict } from "@/lib/review/sse";
|
||||
|
||||
interface ConflictCardProps {
|
||||
index: number;
|
||||
total: number;
|
||||
conflict: ReviewConflict;
|
||||
draft: DecisionDraft;
|
||||
missing: boolean;
|
||||
@@ -28,15 +34,16 @@ const VERDICT_OPTIONS: { value: Verdict; label: string; primary: boolean }[] = [
|
||||
|
||||
// 裁决按钮样式:激活=朱砂填充;未激活的主按钮=朱砂描边强调,次级=低对比描边。
|
||||
function verdictClass(active: boolean, primary: boolean): string {
|
||||
if (active) return "bg-cinnabar text-panel";
|
||||
if (primary) return "border border-cinnabar text-cinnabar hover:bg-cinnabar/10";
|
||||
return "border border-line text-ink-soft hover:border-cinnabar hover:text-ink";
|
||||
if (active) return buttonClass({ variant: "primary", size: "sm" });
|
||||
if (primary) return buttonClass({ variant: "outline", size: "sm" });
|
||||
return buttonClass({ variant: "secondary", size: "sm" });
|
||||
}
|
||||
|
||||
// 单个冲突报告卡(UX §6.4):五类徽标 + where + refs + suggestion + 裁决三态。
|
||||
// 冲突=赭红;未决/缺判时加图标+文案(不单靠色,a11y §10)。
|
||||
export function ConflictCard({
|
||||
index,
|
||||
total,
|
||||
conflict,
|
||||
draft,
|
||||
missing,
|
||||
@@ -60,13 +67,14 @@ export function ConflictCard({
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<span
|
||||
className="shrink-0 rounded bg-[var(--color-conflict)]/10 px-2 py-0.5 text-xs text-conflict"
|
||||
aria-hidden="true"
|
||||
>
|
||||
⚠ {conflict.type}
|
||||
</span>
|
||||
<p className="flex-1 text-sm text-ink">{conflict.suggestion}</p>
|
||||
<Badge variant="danger" className="shrink-0">
|
||||
<AlertTriangle className="h-3 w-3" aria-hidden="true" />
|
||||
冲突 {index + 1}/{total}
|
||||
</Badge>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-xs text-conflict">{conflict.type}</p>
|
||||
<p className="mt-1 text-sm text-ink">{conflict.suggestion}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{conflict.original && conflict.replacement ? (
|
||||
@@ -95,9 +103,10 @@ export function ConflictCard({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onJump(index)}
|
||||
className="rounded border border-line px-2 py-0.5 hover:border-cinnabar hover:text-cinnabar"
|
||||
className={buttonClass({ variant: "secondary", size: "sm" })}
|
||||
>
|
||||
▸ 跳转 {conflict.where}
|
||||
<LocateFixed className="h-4 w-4" aria-hidden="true" />
|
||||
跳转 {conflict.where}
|
||||
</button>
|
||||
) : null}
|
||||
{conflict.refs.map((ref) => (
|
||||
@@ -121,23 +130,24 @@ export function ConflictCard({
|
||||
aria-pressed={active}
|
||||
disabled={rewriting}
|
||||
onClick={() => onVerdict(index, opt.value)}
|
||||
className={`rounded px-3 py-1 text-xs disabled:cursor-not-allowed disabled:opacity-40 ${verdictClass(
|
||||
active,
|
||||
opt.primary,
|
||||
)}`}
|
||||
className={verdictClass(active, opt.primary)}
|
||||
>
|
||||
{opt.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
{rewriting ? (
|
||||
<span className="text-xs text-info">✦ AI 改写中…</span>
|
||||
<Badge variant="info">
|
||||
<Sparkles className="h-3 w-3" aria-hidden="true" />
|
||||
AI 改写中…
|
||||
</Badge>
|
||||
) : resolved ? (
|
||||
<span className="text-xs text-pass" aria-hidden="true">
|
||||
✓ 已裁决
|
||||
</span>
|
||||
<Badge variant="success">
|
||||
<CheckCircle2 className="h-3 w-3" aria-hidden="true" />
|
||||
已裁决
|
||||
</Badge>
|
||||
) : (
|
||||
<span className="text-xs text-conflict">未裁决</span>
|
||||
<Badge variant="danger">未裁决</Badge>
|
||||
)}
|
||||
</div>
|
||||
{draft.verdict === "manual" ? (
|
||||
@@ -145,14 +155,16 @@ export function ConflictCard({
|
||||
<label htmlFor={`note-${index}`} className="sr-only">
|
||||
手改说明
|
||||
</label>
|
||||
<input
|
||||
<TextInput
|
||||
id={`note-${index}`}
|
||||
type="text"
|
||||
value={draft.note}
|
||||
onChange={(e) => onNote(index, e.target.value)}
|
||||
placeholder="手改说明(可选)"
|
||||
className="w-full rounded border border-line bg-bg px-3 py-1.5 text-sm text-ink focus:border-cinnabar focus:outline-none"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-ink-soft">
|
||||
选择手改后,说明会随验收裁决一起保存,正文请在左侧直接修改。
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user