feat(awooop): expose incident evidence links
This commit is contained in:
@@ -379,6 +379,55 @@ function RemediationEvidenceCell({ summary }: { summary?: RemediationSummary | n
|
||||
);
|
||||
}
|
||||
|
||||
function linkedIncidentIds(summary?: RemediationSummary | null): string[] {
|
||||
const rawIds = summary?.incident_ids ?? [];
|
||||
return Array.from(
|
||||
new Set(
|
||||
rawIds
|
||||
.map((incidentId) => String(incidentId || "").trim().toUpperCase())
|
||||
.filter((incidentId) => INCIDENT_ID_FILTER_RE.test(incidentId))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function IncidentIdsCell({ run }: { run: Run }) {
|
||||
const t = useTranslations("awooop.listEvidence");
|
||||
const incidentIds = linkedIncidentIds(run.remediation_summary);
|
||||
const visibleIds = incidentIds.slice(0, 2);
|
||||
const hiddenCount = Math.max(incidentIds.length - visibleIds.length, 0);
|
||||
|
||||
if (visibleIds.length === 0) {
|
||||
return (
|
||||
<span className="text-xs text-[#77736a]">
|
||||
{t("incident.empty")}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-w-[220px] flex-wrap gap-1.5">
|
||||
{visibleIds.map((incidentId) => (
|
||||
<Link
|
||||
key={incidentId}
|
||||
href={`/awooop/runs?project_id=${encodeURIComponent(run.project_id)}&incident_id=${encodeURIComponent(incidentId)}` as never}
|
||||
className="inline-flex items-center border border-[#d8d3c7] bg-[#faf9f3] px-2 py-1 font-mono text-xs font-semibold text-[#2e2b26] hover:border-[#1f6feb] hover:bg-[#edf4ff] hover:text-[#0f4fa8]"
|
||||
title={t("incident.filterTitle", { incidentId })}
|
||||
>
|
||||
{incidentId}
|
||||
</Link>
|
||||
))}
|
||||
{hiddenCount > 0 && (
|
||||
<span
|
||||
className="inline-flex items-center border border-[#d8d3c7] bg-white px-2 py-1 font-mono text-xs text-[#5f5b52]"
|
||||
title={incidentIds.slice(2).join(", ")}
|
||||
>
|
||||
{t("incident.more", { count: hiddenCount })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RunRow({ run }: { run: Run }) {
|
||||
const formattedDate = run.created_at
|
||||
? new Date(run.created_at).toLocaleDateString("zh-TW", {
|
||||
@@ -406,6 +455,9 @@ function RunRow({ run }: { run: Run }) {
|
||||
{run.project_id || "--"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<IncidentIdsCell run={run} />
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="font-mono text-sm text-muted-foreground">
|
||||
{run.agent_id || "--"}
|
||||
@@ -843,6 +895,9 @@ export default function RunsPage() {
|
||||
<th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Project ID
|
||||
</th>
|
||||
<th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
{tEvidence("incident.column")}
|
||||
</th>
|
||||
<th className="text-left px-4 py-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Agent
|
||||
</th>
|
||||
@@ -870,7 +925,7 @@ export default function RunsPage() {
|
||||
{loading ? (
|
||||
Array.from({ length: 8 }).map((_, i) => (
|
||||
<tr key={i} className="border-b border-border">
|
||||
{Array.from({ length: 9 }).map((_, j) => (
|
||||
{Array.from({ length: 10 }).map((_, j) => (
|
||||
<td key={j} className="px-4 py-3">
|
||||
<div className="h-5 bg-muted animate-pulse rounded w-20" />
|
||||
</td>
|
||||
@@ -879,7 +934,7 @@ export default function RunsPage() {
|
||||
))
|
||||
) : runs.length === 0 && !error ? (
|
||||
<tr>
|
||||
<td colSpan={9} className="px-4 py-16 text-center">
|
||||
<td colSpan={10} className="px-4 py-16 text-center">
|
||||
<Activity className="w-10 h-10 text-muted-foreground/30 mx-auto mb-3" aria-hidden="true" />
|
||||
<p className="text-sm text-muted-foreground">尚無 Run 資料</p>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user