feat(web): show IwoooS owner response gate in approvals

This commit is contained in:
Your Name
2026-05-20 16:00:13 +08:00
parent 2a1f10aa89
commit cb3928c47c
8 changed files with 271 additions and 3 deletions

View File

@@ -287,6 +287,104 @@ function ApprovalRow({ approval }: { approval: Approval }) {
);
}
function SecurityOwnerResponseGatePanel() {
const t = useTranslations("awooop.approvals.securityOwnerResponseGate");
const metrics = [
{
label: t("metrics.received"),
value: "0",
detail: t("metrics.receivedDetail"),
},
{
label: t("metrics.accepted"),
value: "0",
detail: t("metrics.acceptedDetail"),
},
{
label: t("metrics.activeRuntimeGates"),
value: "0",
detail: t("metrics.activeRuntimeGatesDetail"),
},
{
label: t("metrics.headline"),
value: "58%",
detail: t("metrics.headlineDetail"),
},
];
const checks = [
{ phase: "S4.9", key: "s49OwnerAttestation" },
{ phase: "S4.10", key: "s410GithubTarget" },
{ phase: "S4.11", key: "s411RefsTruth" },
{ phase: "S4.12", key: "s412WorkflowSecret" },
];
return (
<section className="border border-[#e0ddd4] bg-white">
<div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
<div className="flex items-start gap-2">
<ShieldCheck className="mt-0.5 h-4 w-4 text-[#d97757]" aria-hidden="true" />
<div>
<h3 className="text-sm font-semibold text-[#141413]">{t("title")}</h3>
<p className="mt-1 max-w-3xl text-xs leading-5 text-[#77736a]">{t("subtitle")}</p>
</div>
</div>
<span className="inline-flex border border-[#9bb6d9] bg-[#eef5ff] px-2 py-0.5 text-xs font-semibold text-[#1f5b9b]">
{t("badge")}
</span>
</div>
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-2 xl:grid-cols-4">
{metrics.map((item) => (
<div key={item.label} className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
<p className="mt-2 font-mono text-2xl font-semibold text-[#141413]">{item.value}</p>
<p className="mt-2 text-xs leading-5 text-[#5f5b52]">{item.detail}</p>
</div>
))}
</div>
<div className="grid gap-4 p-4 xl:grid-cols-[1.1fr_0.9fr]">
<div className="border border-[#e0ddd4] bg-white">
<div className="border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3 text-xs font-semibold text-[#141413]">
{t("ownerChecksTitle")}
</div>
<div className="divide-y divide-[#eee9dd]">
{checks.map((item) => (
<div key={item.key} className="grid gap-3 px-4 py-3 md:grid-cols-[72px_1fr]">
<span className="font-mono text-xs font-semibold text-[#141413]">{item.phase}</span>
<div>
<p className="text-sm font-semibold text-[#141413]">
{t(`checks.${item.key}.title`)}
</p>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{t(`checks.${item.key}.detail`)}
</p>
</div>
</div>
))}
</div>
</div>
<div className="border border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("boundaryLabel")}</p>
<p className="mt-2 text-sm font-semibold text-[#141413]">{t("boundaryTitle")}</p>
<p className="mt-2 text-xs leading-5 text-[#5f5b52]">{t("boundaryDetail")}</p>
<div className="mt-4 grid gap-2 font-mono text-xs text-[#141413]">
<span>approval_record_created=false</span>
<span>owner_response_accepted_count=0</span>
<span>runtime_execution_authorized=false</span>
<span>action_buttons_allowed=false</span>
</div>
<Link
href="/iwooos"
className="mt-4 inline-flex items-center gap-2 border border-[#d8d3c7] bg-white px-3 py-1.5 text-xs font-semibold text-[#141413] hover:border-[#d97757]"
>
{t("openIwooos")}
<ArrowRight className="h-3.5 w-3.5 text-[#77736a]" aria-hidden="true" />
</Link>
</div>
</div>
</section>
);
}
// =============================================================================
// Main Component
// =============================================================================
@@ -454,6 +552,8 @@ export default function ApprovalsPage() {
})}
</section>
<SecurityOwnerResponseGatePanel />
<div className="flex flex-wrap items-center gap-3 border border-[#e0ddd4] bg-white p-4">
<Filter className="w-4 h-4 text-muted-foreground flex-shrink-0" aria-hidden="true" />
<span className="text-sm text-muted-foreground">{tEvidence("filters.label")}</span>