feat(web): add AwoooP automation truth rail
This commit is contained in:
@@ -723,6 +723,72 @@ function OperationsDecisionMap({
|
||||
const verifiedPercent = percentValue(verified, evaluated);
|
||||
const qualityHealthyPercent = percentValue(green, green + yellow + red);
|
||||
const isDegraded = sourceError || qualityError;
|
||||
const productionClaimReady = Boolean(quality?.production_claim.can_claim_full_auto_repair);
|
||||
const blockerKey = isDegraded
|
||||
? "dataIncomplete"
|
||||
: verified < evaluated
|
||||
? "repairVerifierGap"
|
||||
: openWork + manualGates + numberValue(snapshot.approvals) > 0
|
||||
? "manualGate"
|
||||
: productionClaimReady
|
||||
? "ready"
|
||||
: "watch";
|
||||
const nextFocusKey = openWork > 0
|
||||
? "workItems"
|
||||
: manualGates + numberValue(snapshot.approvals) > 0
|
||||
? "approvals"
|
||||
: verified < evaluated
|
||||
? "runs"
|
||||
: "observe";
|
||||
const blockerText = (() => {
|
||||
switch (blockerKey) {
|
||||
case "dataIncomplete":
|
||||
return t("truth.blockers.dataIncomplete");
|
||||
case "repairVerifierGap":
|
||||
return t("truth.blockers.repairVerifierGap", { verified, evaluated });
|
||||
case "manualGate":
|
||||
return t("truth.blockers.manualGate", {
|
||||
open: openWork,
|
||||
gates: manualGates + numberValue(snapshot.approvals),
|
||||
});
|
||||
case "ready":
|
||||
return t("truth.blockers.ready");
|
||||
default:
|
||||
return t("truth.blockers.watch");
|
||||
}
|
||||
})();
|
||||
const nextFocusText = (() => {
|
||||
switch (nextFocusKey) {
|
||||
case "workItems":
|
||||
return t("truth.nextFocus.workItems");
|
||||
case "approvals":
|
||||
return t("truth.nextFocus.approvals");
|
||||
case "runs":
|
||||
return t("truth.nextFocus.runs");
|
||||
default:
|
||||
return t("truth.nextFocus.observe");
|
||||
}
|
||||
})();
|
||||
const truthLinks = [
|
||||
{
|
||||
key: "runs",
|
||||
href: "/awooop/runs?project_id=awoooi",
|
||||
value: sourceError ? "--" : linkedRuns,
|
||||
tone: linkedPercent >= 80 ? "good" : linkedPercent > 0 ? "warn" : "neutral",
|
||||
},
|
||||
{
|
||||
key: "workItems",
|
||||
href: "/awooop/work-items?project_id=awoooi",
|
||||
value: openWork,
|
||||
tone: openWork === 0 ? "good" : "warn",
|
||||
},
|
||||
{
|
||||
key: "approvals",
|
||||
href: "/awooop/approvals?project_id=awoooi",
|
||||
value: manualGates + numberValue(snapshot.approvals),
|
||||
tone: manualGates + numberValue(snapshot.approvals) === 0 ? "good" : "warn",
|
||||
},
|
||||
] as const;
|
||||
|
||||
return (
|
||||
<section className="border border-[#e0ddd4] bg-[#e0ddd4]">
|
||||
@@ -745,6 +811,80 @@ function OperationsDecisionMap({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-testid="awooop-automation-truth-rail"
|
||||
className="grid gap-px bg-[#e0ddd4] lg:grid-cols-[1.05fr_0.95fr_1.25fr]"
|
||||
>
|
||||
<div className="bg-[#2f2a24] px-4 py-4 text-white">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.14em] text-[#d9b36f]">
|
||||
{t("truth.claimEyebrow")}
|
||||
</p>
|
||||
<p className="mt-2 text-xl font-semibold">
|
||||
{productionClaimReady ? t("truth.claimReady") : t("truth.claimBlocked")}
|
||||
</p>
|
||||
<p className="mt-2 text-xs leading-5 text-[#e5ddd0]">
|
||||
{blockerText}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white px-4 py-4">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.14em] text-[#a15f3b]">
|
||||
{t("truth.nextEyebrow")}
|
||||
</p>
|
||||
<p className="mt-2 text-lg font-semibold text-[#141413]">
|
||||
{nextFocusText}
|
||||
</p>
|
||||
<div className="mt-3 grid grid-cols-3 gap-2 text-[11px]">
|
||||
<div className="border border-[#eee9dd] bg-[#faf9f3] px-2 py-1.5">
|
||||
<p className="text-[#77736a]">{t("truth.stats.verified")}</p>
|
||||
<p className="font-mono font-semibold text-[#141413]">{verified}/{evaluated}</p>
|
||||
</div>
|
||||
<div className="border border-[#eee9dd] bg-[#faf9f3] px-2 py-1.5">
|
||||
<p className="text-[#77736a]">{t("truth.stats.openWork")}</p>
|
||||
<p className="font-mono font-semibold text-[#141413]">{openWork}</p>
|
||||
</div>
|
||||
<div className="border border-[#eee9dd] bg-[#faf9f3] px-2 py-1.5">
|
||||
<p className="text-[#77736a]">{t("truth.stats.gates")}</p>
|
||||
<p className="font-mono font-semibold text-[#141413]">
|
||||
{manualGates + numberValue(snapshot.approvals)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-px bg-[#e0ddd4] sm:grid-cols-3">
|
||||
{truthLinks.map((item) => (
|
||||
<Link
|
||||
key={item.key}
|
||||
href={item.href as never}
|
||||
className="group flex min-h-[112px] items-start justify-between gap-3 bg-white px-4 py-4 hover:bg-[#faf9f3]"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs font-semibold text-[#77736a]">
|
||||
{t(`truth.links.${item.key}.label` as never)}
|
||||
</p>
|
||||
<p className="mt-2 font-mono text-2xl font-semibold leading-none text-[#141413]">
|
||||
{item.value}
|
||||
</p>
|
||||
<p className="mt-2 text-[11px] leading-4 text-[#5f5b52]">
|
||||
{t(`truth.links.${item.key}.detail` as never)}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-8 w-8 shrink-0 items-center justify-center border",
|
||||
item.tone === "good" && "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]",
|
||||
item.tone === "warn" && "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
|
||||
item.tone === "neutral" && "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]"
|
||||
)}
|
||||
>
|
||||
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" aria-hidden="true" />
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-2 xl:grid-cols-5">
|
||||
<VisualFlowNode
|
||||
label={t("flow.ingest.label")}
|
||||
|
||||
Reference in New Issue
Block a user