feat(front-end): 依据《详细设计》文档对前端 UI 进行全面升级

- 设计系统:品牌主色统一为品红渐变 #c2185b→#e91e63,新增品牌令牌与呼吸/入场动画
- 登录页重建:内部员工/外部账号切换、账号/手机验证码 Tab、SRM 存量提示
- 注册页重建为多步向导:企业信息→联系人→AI 审核,OCR 状态、密码复杂度校验与强度条
- 小滨 AI 助手重建:品牌渐变悬浮球+呼吸光环,角色感知主动提醒(紧急/AI完成/待办/风险/建议)
- 新增公开门户首页 /portal:Banner 轮播 + 三列公告区 + 免责声明
- 各角色主题色对齐文档:供应商=红、评标专家=蓝、招标专员=绿

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-09 23:46:04 +08:00
parent f7a49ffb78
commit 787de5aa88
16 changed files with 1301 additions and 244 deletions
+563 -53
View File
@@ -1,81 +1,591 @@
import { useMemo, useState } from "react";
import { useNavigate, Link } from "react-router";
import { Hexagon, ArrowLeft, CheckCircle2 } from "lucide-react";
import {
Hexagon,
ArrowLeft,
ArrowRight,
UploadCloud,
CheckCircle2,
Loader2,
XCircle,
Building2,
UserRound,
ShieldCheck,
X,
Eye,
EyeOff,
Info,
} from "lucide-react";
import { toast } from "sonner";
import { Button } from "../components/ui/button";
import { cn } from "../components/ui/utils";
const fields = [
{ label: "企业全称", placeholder: "请输入营业执照上的企业全称", col: 2 },
{ label: "统一社会信用代码", placeholder: "18 位信用代码" },
{ label: "企业类型", placeholder: "如:有限责任公司" },
{ label: "法定代表人", placeholder: "请输入法人姓名" },
{ label: "注册资本(万元)", placeholder: "请输入注册资本" },
{ label: "联系人", placeholder: "请输入联系人姓名" },
{ label: "联系电话", placeholder: "请输入手机号码" },
{ label: "电子邮箱", placeholder: "用于接收平台通知" },
{ label: "经营类别", placeholder: "如:自动化控制 / 环保工程" },
{ label: "企业地址", placeholder: "请输入详细注册地址", col: 2 },
type Registrant = "supplier" | "expert";
type AuditState = "idle" | "auditing" | "success" | "rejected";
const SUPPLIER_STEPS = ["企业信息", "联系人", "提交 / 自动审核"];
const EXPERT_STEPS = ["基本信息", "专业资质 / 收款", "提交审核"];
interface PwdRule {
label: string;
test: (v: string) => boolean;
}
const PWD_RULES: PwdRule[] = [
{ label: "长度 8~20 位", test: (v) => v.length >= 8 && v.length <= 20 },
{ label: "包含大写字母(A-Z)与小写字母(a-z)", test: (v) => /[A-Z]/.test(v) && /[a-z]/.test(v) },
{ label: "包含数字(0-9)与特殊字符(!@#$%^&*", test: (v) => /\d/.test(v) && /[!@#$%^&*()_+\-=]/.test(v) },
{ label: "禁止连续/重复字符(如 12345678、aaaa", test: (v) => !/(.)\1{3,}/.test(v) && !/0123|1234|2345|3456|4567|5678|6789/.test(v) },
{ label: "非常见弱口令(password、admin123 等)", test: (v) => !["password", "admin123", "12345678", "qwerty"].includes(v.toLowerCase()) },
];
function Field({
label,
placeholder,
required,
hint,
className,
type = "text",
readOnly,
}: {
label: string;
placeholder?: string;
required?: boolean;
hint?: string;
className?: string;
type?: string;
readOnly?: boolean;
}) {
return (
<div className={className}>
<label className="mb-1.5 block text-sm text-[#4a5568]">
{label} {required && <span className="text-[#e91e63]">*</span>}
</label>
<input
type={type}
readOnly={readOnly}
placeholder={placeholder}
className={cn(
"h-11 w-full rounded-lg border bg-input-background px-3 text-sm outline-none transition-colors focus:border-primary",
readOnly && "bg-muted text-muted-foreground",
)}
/>
{hint && <p className="mt-1 text-xs text-muted-foreground">{hint}</p>}
</div>
);
}
function StepBar({ steps, current }: { steps: string[]; current: number }) {
return (
<div className="flex items-center justify-center rounded-full border bg-white px-6 py-4 shadow-sm">
{steps.map((s, i) => (
<div key={s} className="flex items-center">
<div className="flex flex-col items-center">
<div
className={cn(
"flex size-9 items-center justify-center rounded-full text-sm font-bold transition-colors",
i <= current ? "bg-brand-gradient text-white" : "bg-muted text-muted-foreground",
)}
>
{i < current ? <CheckCircle2 className="size-5" /> : i + 1}
</div>
<span className={cn("mt-2 text-xs", i <= current ? "font-medium text-primary" : "text-muted-foreground")}>{s}</span>
</div>
{i < steps.length - 1 && (
<div className={cn("mx-3 mb-6 h-0.5 w-16 rounded-full sm:w-24", i < current ? "bg-brand-gradient" : "bg-border")} />
)}
</div>
))}
</div>
);
}
function Notice({ items }: { items: string[] }) {
return (
<div className="rounded-xl border border-[#ffcc80] bg-[#fff3e0] p-4">
<div className="flex items-center gap-1.5 text-sm font-semibold text-[#e65100]">
<Info className="size-4" />
</div>
<ol className="mt-2 space-y-1.5 text-xs leading-relaxed text-[#bf360c]">
{items.map((t, i) => (
<li key={i}>
{i + 1}. {t}
</li>
))}
</ol>
</div>
);
}
export function RegisterPage() {
const navigate = useNavigate();
const [registrant, setRegistrant] = useState<Registrant>("supplier");
const [step, setStep] = useState(0);
const [ocr, setOcr] = useState<"none" | "scanning" | "done">("none");
const [audit, setAudit] = useState<AuditState>("idle");
const [products, setProducts] = useState<string[]>(["工业阀门", "管道配件"]);
const [productInput, setProductInput] = useState("");
const [pwd, setPwd] = useState("");
const [confirmPwd, setConfirmPwd] = useState("");
const [showPwd, setShowPwd] = useState(false);
const [smsCountdown, setSmsCountdown] = useState(0);
const steps = registrant === "supplier" ? SUPPLIER_STEPS : EXPERT_STEPS;
const passedRules = useMemo(() => PWD_RULES.map((r) => r.test(pwd)), [pwd]);
const passedCount = passedRules.filter(Boolean).length;
const strength: 0 | 1 | 2 = passedCount >= 5 && pwd.length >= 12 ? 2 : passedCount >= 4 ? 1 : 0;
function switchRegistrant(r: Registrant) {
setRegistrant(r);
setStep(0);
setAudit("idle");
setOcr("none");
}
function triggerOcr() {
setOcr("scanning");
setTimeout(() => {
setOcr("done");
toast.success("营业执照识别完成,已自动回填企业信息");
}, 1500);
}
function addProduct() {
const v = productInput.trim();
if (v && products.length < 20 && !products.includes(v)) {
setProducts((p) => [...p, v]);
}
setProductInput("");
}
function sendSms() {
toast.success("验证码已发送");
setSmsCountdown(60);
const t = setInterval(() => {
setSmsCountdown((c) => {
if (c <= 1) {
clearInterval(t);
return 0;
}
return c - 1;
});
}, 1000);
}
function submitAudit() {
setStep(steps.length - 1);
setAudit("auditing");
setTimeout(() => {
setAudit("success");
}, 3800);
}
const isLastFormStep = step === steps.length - 2;
return (
<div className="min-h-screen bg-slate-50">
<div className="min-h-screen bg-[#f5f7fa]">
<header className="flex h-16 items-center justify-between border-b bg-card px-8">
<div className="flex items-center gap-2">
<Hexagon className="size-6 text-primary" />
<span className="font-semibold"> · </span>
<div className="flex items-center gap-2.5">
<div className="flex size-8 items-center justify-center rounded-lg bg-brand-gradient">
<Hexagon className="size-5 text-white" />
</div>
<span className="font-semibold"> · </span>
</div>
<Link to="/" className="flex items-center gap-1 text-sm text-muted-foreground hover:text-primary">
<ArrowLeft className="size-4" />
</Link>
</header>
<div className="mx-auto max-w-3xl px-6 py-10">
<div className="mb-6">
<h1 className="text-foreground"></h1>
<p className="mt-1 text-sm text-muted-foreground">
1-3
</p>
<div className="mx-auto max-w-5xl px-6 py-8">
{/* 注册对象选择 */}
<div className="mb-6 flex justify-center gap-3">
{(
[
{ id: "supplier" as Registrant, label: "供应商注册", icon: Building2 },
{ id: "expert" as Registrant, label: "外部专家注册", icon: UserRound },
]
).map((r) => {
const Icon = r.icon;
return (
<button
key={r.id}
onClick={() => switchRegistrant(r.id)}
className={cn(
"flex items-center gap-2 rounded-full border px-5 py-2.5 text-sm font-medium transition-all",
registrant === r.id
? "border-transparent bg-brand-gradient text-white shadow-sm"
: "border-border bg-white text-muted-foreground hover:border-primary hover:text-primary",
)}
>
<Icon className="size-4" /> {r.label}
</button>
);
})}
</div>
<div className="rounded-xl border bg-card p-8">
<h3 className="mb-5 text-foreground"></h3>
<form
className="grid grid-cols-2 gap-5"
onSubmit={(e) => {
e.preventDefault();
toast.success("注册申请已提交,平台正在进行资质审核");
setTimeout(() => navigate("/"), 1200);
}}
>
{fields.map((f) => (
<div key={f.label} className={f.col === 2 ? "col-span-2" : ""}>
<label className="mb-1.5 block text-sm">{f.label}</label>
<input
className="h-10 w-full rounded-lg border bg-input-background px-3 text-sm outline-none focus:border-primary"
placeholder={f.placeholder}
<div className="mb-6 flex items-center justify-between gap-4">
<StepBar steps={steps} current={step} />
<div className="hidden shrink-0 items-center gap-1.5 rounded-full border border-[#4caf50] bg-[#e8f5e9] px-3 py-1.5 text-xs text-[#2e7d32] lg:flex">
<ShieldCheck className="size-3.5" /> AI ·
</div>
</div>
{/* ============ 供应商注册 ============ */}
{registrant === "supplier" && (
<>
{step === 0 && (
<div className="grid gap-6 lg:grid-cols-[1fr_300px]">
<div className="rounded-2xl bg-card p-8 shadow-sm">
<h3 className="text-foreground"></h3>
<p className="mt-1 text-sm text-muted-foreground"></p>
<div className="my-5 h-px bg-border" />
{/* 营业执照上传 */}
<div
onClick={triggerOcr}
className="flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed border-primary bg-[#fafbfc] py-6 transition-colors hover:bg-secondary/40"
>
<UploadCloud className="mb-2 size-7 text-primary" />
<div className="text-sm font-medium text-primary">JPG/PNG/PDF10MB</div>
<div className="mt-1 text-xs text-muted-foreground">AI </div>
</div>
{ocr === "scanning" && (
<div className="mt-3 flex items-center gap-2 rounded-lg bg-[#fff8e1] px-3 py-2 text-sm text-[#e65100]">
<Loader2 className="size-4 animate-spin" /> ...
</div>
)}
{ocr === "done" && (
<div className="mt-3 flex items-center gap-2 rounded-lg border border-[#a5d6a7] bg-[#e8f5e9] px-3 py-2 text-sm text-[#2e7d32]">
<CheckCircle2 className="size-4" /> 91370100XXXXXXXXXX
</div>
)}
<div className="mt-6 grid grid-cols-2 gap-x-5 gap-y-4">
<Field label="企业全称" required placeholder="系统自动识别或手动输入" className="col-span-2" />
<Field label="统一社会信用代码" required placeholder="18 位代码" />
<Field label="注册资本(万元)" required placeholder="请输入数字" />
<Field label="法定代表人" required placeholder="请输入法定代表人姓名" />
<Field label="行业分类" required placeholder="请选择或搜索行业(GB/T 4754" />
<Field label="注册地址" required placeholder="请填写详细地址" className="col-span-2" />
<div className="col-span-2">
<label className="mb-1.5 block text-sm text-[#4a5568]">
<span className="text-[#e91e63]">*</span>
</label>
<textarea
rows={2}
placeholder="系统自动识别或手动输入,不超过 500 字"
className="w-full resize-none rounded-lg border bg-input-background px-3 py-2 text-sm outline-none transition-colors focus:border-primary"
/>
</div>
<div className="col-span-2">
<label className="mb-1.5 block text-sm text-[#4a5568]">
<span className="text-[#e91e63]">*</span>
</label>
<div className="flex gap-2">
<input
value={productInput}
onChange={(e) => setProductInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && (e.preventDefault(), addProduct())}
placeholder="输入产品名称,回车添加"
className="h-11 flex-1 rounded-lg border bg-input-background px-3 text-sm outline-none transition-colors focus:border-primary"
/>
<Button type="button" variant="outline" className="h-11" onClick={addProduct}>
+
</Button>
</div>
<div className="mt-2 flex flex-wrap gap-2">
{products.map((p) => (
<span
key={p}
className="flex items-center gap-1 rounded-full border border-primary bg-secondary px-3 py-1 text-xs text-primary"
>
{p}
<button onClick={() => setProducts((ps) => ps.filter((x) => x !== p))}>
<X className="size-3" />
</button>
</span>
))}
</div>
</div>
</div>
</div>
<Notice
items={[
"请确保信息与营业执照一致",
"统一社会信用代码将作为唯一标识",
"提交后由 AI 即时自动审核",
"审核通过后可参与投标报名",
"已有 SRM 账号请直接登录",
]}
/>
</div>
))}
)}
<div className="col-span-2">
<label className="mb-1.5 block text-sm"> / </label>
<div className="flex h-28 cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed border-border bg-muted/30 text-sm text-muted-foreground hover:border-primary">
<CheckCircle2 className="mb-2 size-6 text-muted-foreground/60" />
PDF / JPG / PNG
{step === 1 && (
<div className="mx-auto max-w-2xl rounded-2xl bg-card p-8 shadow-sm">
<h3 className="text-foreground"></h3>
<div className="my-5 h-px bg-border" />
<div className="space-y-4">
<Field label="联系人姓名" required placeholder="请输入联系人姓名" hint="必填,最大 64 字符" />
<Field label="联系电话(手机号)" required placeholder="请输入 11 位手机号" hint="必填,格式校验,全局唯一" />
<Field label="电子邮箱" required placeholder="请输入常用邮箱" hint="必填,格式校验,全局唯一" />
<div>
<label className="mb-1.5 block text-sm text-[#4a5568]">
<span className="text-[#e91e63]">*</span>
</label>
<div className="flex gap-3">
<input
placeholder="点击发送验证码"
className="h-11 flex-1 rounded-lg border bg-input-background px-3 text-sm outline-none transition-colors focus:border-primary"
/>
<button
type="button"
onClick={sendSms}
disabled={smsCountdown > 0}
className="h-11 w-32 shrink-0 rounded-lg bg-brand-gradient text-sm font-medium text-white transition-opacity hover:opacity-90 disabled:opacity-50"
>
{smsCountdown > 0 ? `${smsCountdown}s 后重发` : "发送验证码"}
</button>
</div>
<p className="mt-1 text-xs text-muted-foreground"></p>
</div>
{/* 密码 */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="mb-1.5 block text-sm text-[#4a5568]">
<span className="text-[#e91e63]">*</span>
</label>
<div className="relative">
<input
type={showPwd ? "text" : "password"}
value={pwd}
onChange={(e) => setPwd(e.target.value)}
placeholder="请设置登录密码"
className="h-11 w-full rounded-lg border bg-input-background px-3 pr-10 text-sm outline-none transition-colors focus:border-primary"
/>
<button
type="button"
onClick={() => setShowPwd((s) => !s)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground"
>
{showPwd ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
</button>
</div>
</div>
<div>
<label className="mb-1.5 block text-sm text-[#4a5568]">
<span className="text-[#e91e63]">*</span>
</label>
<input
type={showPwd ? "text" : "password"}
value={confirmPwd}
onChange={(e) => setConfirmPwd(e.target.value)}
placeholder="请再次输入密码"
className={cn(
"h-11 w-full rounded-lg border bg-input-background px-3 text-sm outline-none transition-colors focus:border-primary",
confirmPwd && confirmPwd !== pwd && "border-destructive",
)}
/>
</div>
</div>
{/* 密码复杂度要求 */}
<div className="rounded-xl border bg-[#f8fafc] p-4">
<div className="text-sm font-medium text-[#374151]"></div>
<ul className="mt-2 space-y-1.5">
{PWD_RULES.map((r, i) => (
<li key={r.label} className="flex items-center gap-2 text-[13px]">
<span className={passedRules[i] ? "text-[#10b981]" : "text-[#ef4444]"}>{passedRules[i] ? "✔" : "✘"}</span>
<span className="text-[#374151]">{r.label}</span>
</li>
))}
</ul>
{/* 强度条 */}
<div className="mt-3 flex items-center gap-3">
<span className="text-xs text-muted-foreground"></span>
<div className="h-2 w-40 overflow-hidden rounded-full bg-[#e5e7eb]">
<div
className={cn(
"h-full rounded-full transition-all",
strength === 0 ? "w-1/3 bg-[#ef4444]" : strength === 1 ? "w-2/3 bg-[#f59e0b]" : "w-full bg-[#10b981]",
)}
/>
</div>
<span
className={cn(
"text-xs font-medium",
strength === 0 ? "text-[#ef4444]" : strength === 1 ? "text-[#f59e0b]" : "text-[#10b981]",
)}
>
{strength === 0 ? "弱" : strength === 1 ? "中" : "强"}
</span>
</div>
</div>
</div>
</div>
</div>
)}
</>
)}
<div className="col-span-2 mt-2 flex items-center gap-3">
<Button type="submit" className="h-11 flex-1">
{/* ============ 外部专家注册 ============ */}
{registrant === "expert" && (
<>
{step === 0 && (
<div className="grid gap-6 lg:grid-cols-[1fr_300px]">
<div className="rounded-2xl bg-card p-8 shadow-sm">
<h3 className="text-foreground"></h3>
<p className="mt-1 text-sm text-muted-foreground"></p>
<div className="my-5 h-px bg-border" />
<div className="grid grid-cols-2 gap-x-5 gap-y-4">
<Field label="真实姓名" required placeholder="请输入中文姓名" />
<Field label="性别" required placeholder="请选择" />
<Field label="身份证号码" required placeholder="请输入 18 位身份证号" hint="🔒 将使用 AES-256 加密存储" className="col-span-2" />
<Field label="出生日期" placeholder="由身份证自动识别" readOnly />
<Field label="年龄" placeholder="自动计算" readOnly />
<Field label="手机号码" required placeholder="请输入手机号" />
<Field label="电子邮箱" required placeholder="请输入常用邮箱" />
<Field label="工作单位" placeholder="请输入单位全称" />
<Field label="现从事专业" required placeholder="如:化工工艺、设备管理" />
<Field label="技术职称" required placeholder="高级 / 副高级 / 中级" />
<Field label="职称专业" placeholder="如:化学工程" />
<div className="col-span-2">
<label className="mb-1.5 block text-sm text-[#4a5568]">
/ <span className="text-[#e91e63]">*</span>
</label>
<div className="flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed border-primary bg-[#fafbfc] py-5 hover:bg-secondary/40">
<UploadCloud className="mb-1.5 size-6 text-primary" />
<div className="text-sm text-primary"> PDF/JPG/PNG20MB</div>
</div>
</div>
</div>
</div>
<Notice
items={[
"所有信息须与身份证及职称证书一致",
"集团公司将对信息进行真实性核验",
"入库专家须遵守《评委管理规定》",
"至少具备中级及以上职称方可入库",
"个人信息变更需及时在线更新",
]}
/>
</div>
)}
{step === 1 && (
<div className="mx-auto max-w-2xl rounded-2xl bg-card p-8 shadow-sm">
<h3 className="text-foreground"></h3>
<div className="my-5 h-px bg-border" />
<div className="grid grid-cols-2 gap-x-5 gap-y-4">
<div className="col-span-2">
<label className="mb-1.5 block text-sm text-[#4a5568]"></label>
<input
placeholder="如:石油化工、反应工程、分离技术,逗号分隔"
className="h-11 w-full rounded-lg border bg-input-background px-3 text-sm outline-none focus:border-primary"
/>
</div>
<Field label="开户银行" placeholder="如:中国银行北京分行" />
<Field label="银行账号" placeholder="请输入收款账号" hint="🔒 将使用 AES-256 加密存储" />
<label className="col-span-2 mt-2 flex items-start gap-2 rounded-lg border border-[#ffcc80] bg-[#fff3e0] p-3 text-xs text-[#bf360c]">
<input type="checkbox" className="mt-0.5 size-4 accent-[var(--primary)]" />
</label>
</div>
</div>
)}
</>
)}
{/* ============ 提交 / 自动审核态(两种注册对象共用最后一步) ============ */}
{step === steps.length - 1 && (
<div className="mx-auto max-w-lg">
{audit === "auditing" && (
<div className="rounded-2xl bg-card p-10 text-center shadow-sm">
<Loader2 className="mx-auto size-14 animate-spin text-primary" />
<div className="mt-5 text-lg font-bold text-foreground">
{registrant === "supplier" ? "AI 正在审核企业信息..." : "已提交,AI 正在初审..."}
</div>
<div className="mt-2 text-sm text-muted-foreground"> 3~8 </div>
<div className="mt-3 text-[13px] text-muted-foreground/80">     </div>
</div>
)}
{audit === "success" && (
<div className="rounded-2xl bg-card p-10 text-center shadow-sm">
<div className="mx-auto flex size-24 items-center justify-center rounded-full bg-[#e8f5e9]">
<CheckCircle2 className="size-14 text-[#4caf50]" />
</div>
<div className="mt-5 text-2xl font-bold text-foreground">🎉 </div>
<p className="mt-2 text-sm text-muted-foreground">
{registrant === "supplier" ? "您的供应商账号已激活,正在自动登录..." : "您的专家账号已创建,请留意激活通知短信/邮件。"}
</p>
<div className="my-5 h-px bg-border" />
<div className="space-y-1.5 text-sm text-muted-foreground">
<div>
{registrant === "supplier" ? "供应商编号" : "专家编号"}
<span className="font-semibold text-foreground">
{registrant === "supplier" ? "SUP_20260709_0158" : "EXP_20260709_002"}
</span>
</div>
<div>
<span className="font-semibold text-foreground">138****8001</span>
</div>
</div>
<div className="mt-5 rounded-lg border border-[#ffcc80] bg-[#fff3e0] px-4 py-2.5 text-xs text-[#e65100]">
📌
</div>
<Button className="mt-6 h-11 w-full bg-brand-gradient hover:opacity-90" onClick={() => navigate("/app")}>
<ArrowRight className="size-4" />
</Button>
</div>
)}
{audit === "rejected" && (
<div className="rounded-2xl bg-card p-10 text-center shadow-sm">
<div className="mx-auto flex size-24 items-center justify-center rounded-full bg-[#ffcdd2]">
<XCircle className="size-14 text-[#c62828]" />
</div>
<div className="mt-5 text-2xl font-bold text-[#c62828]"></div>
<div className="mt-5 rounded-lg border bg-[#fafafa] p-4 text-left text-[13px] text-[#c62828]">
<div> </div>
<div> </div>
<div className="mt-2 text-[#666]">💡 </div>
</div>
<Button
className="mt-6 h-11 w-full bg-brand-gradient hover:opacity-90"
onClick={() => {
setAudit("idle");
setStep(0);
}}
>
</Button>
</div>
)}
</div>
)}
{/* 底部导航按钮 */}
{audit === "idle" && (
<div className="mx-auto mt-6 flex max-w-5xl items-center justify-end gap-3">
{step > 0 && (
<Button variant="outline" className="h-11 px-6" onClick={() => setStep((s) => s - 1)}>
</Button>
<Button type="button" variant="outline" className="h-11" onClick={() => navigate("/")}>
)}
{isLastFormStep ? (
<Button className="h-11 bg-brand-gradient px-8 hover:opacity-90" onClick={submitAudit}>
</Button>
</div>
</form>
</div>
) : (
<Button className="h-11 bg-brand-gradient px-8 hover:opacity-90" onClick={() => setStep((s) => s + 1)}>
<ArrowRight className="size-4" />
</Button>
)}
</div>
)}
</div>
</div>
);