From 140c9cdaef6c82957cb9a4195dfe0b0a41b8e446 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 21 May 2026 14:20:13 +0800 Subject: [PATCH] feat(awooop): show source flow in approvals --- .../app/[locale]/awooop/approvals/page.tsx | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/[locale]/awooop/approvals/page.tsx b/apps/web/src/app/[locale]/awooop/approvals/page.tsx index 0cc82a47..87caf0b6 100644 --- a/apps/web/src/app/[locale]/awooop/approvals/page.tsx +++ b/apps/web/src/app/[locale]/awooop/approvals/page.tsx @@ -208,6 +208,65 @@ function RemediationEvidenceCell({ summary }: { summary?: RemediationSummary | n ); } +type ApprovalSourceFlowStatus = "verified" | "applied" | "evidence" | "provider" | "waiting"; + +function approvalSourceFlowStatus(chain?: AwoooPStatusChain | null): ApprovalSourceFlowStatus { + const correlation = chain?.source_refs?.correlation; + if (!correlation) return "waiting"; + + const verificationStatus = String(correlation.verification_status ?? correlation.status ?? "missing"); + if (verificationStatus === "applied_link_verified" || verificationStatus === "direct_ref_verified") { + return "verified"; + } + if ((correlation.applied_link_total ?? 0) > 0) return "applied"; + if ((correlation.direct_ref_total ?? 0) > 0 || (correlation.candidate_total ?? 0) > 0) { + return "evidence"; + } + if ((correlation.provider_event_total ?? 0) > 0) return "provider"; + return "waiting"; +} + +function approvalSourceFlowClass(status: ApprovalSourceFlowStatus) { + if (status === "verified") return "border-[#9bc7a4] bg-[#f0faf2] text-[#17602a]"; + if (status === "applied" || status === "evidence") return "border-[#9bb6d9] bg-[#eef5ff] text-[#1f5b9b]"; + if (status === "provider") return "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]"; + return "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]"; +} + +function ApprovalSourceFlowCell({ chain }: { chain?: AwoooPStatusChain | null }) { + const t = useTranslations("awooop.listEvidence.sourceFlow"); + const status = approvalSourceFlowStatus(chain); + const correlation = chain?.source_refs?.correlation; + const detail = t("detail", { + providers: correlation?.provider_event_total ?? 0, + direct: correlation?.direct_ref_total ?? 0, + candidate: correlation?.candidate_total ?? 0, + applied: correlation?.applied_link_total ?? 0, + }); + const Icon = status === "verified" || status === "applied" + ? ShieldCheck + : status === "waiting" + ? AlertCircle + : SearchCheck; + + return ( +
+ + +

+ {detail} +

+
+ ); +} + function TimeoutCell({ timeoutAt }: { timeoutAt: string | null }) { const [remaining, setRemaining] = useState( getRemainingMs(timeoutAt) @@ -299,6 +358,9 @@ function ApprovalRow({ approval }: { approval: Approval }) { + + + @@ -563,6 +625,9 @@ export default function ApprovalsPage() { {tEvidence("column")} + + {tEvidence("sourceFlow.column")} + {tStatusChain("title")} @@ -578,7 +643,7 @@ export default function ApprovalsPage() { {loading ? ( Array.from({ length: 5 }).map((_, i) => ( - {Array.from({ length: 8 }).map((_, j) => ( + {Array.from({ length: 9 }).map((_, j) => (