20260728_2 修复rpki-explorer体验问题7项(#134):证书资源可展开、CMS对象新增EE Parsed Tab、Overview增加repo数KPI/修复Object types标签/Top repositories按objects排序(后端sort参数)/reject reasons按前缀聚类可下钻、Run选择器30s轮询
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
import { Clock, GitBranch, Timer } from "lucide-react";
|
||||
import {
|
||||
getStatsObjectTypes,
|
||||
getStatsOverview,
|
||||
getStatsReasons,
|
||||
getStatsValidation,
|
||||
listRepos,
|
||||
@@ -35,6 +36,7 @@ import {
|
||||
objectTypeLabel,
|
||||
truncateMiddle,
|
||||
} from "../lib/format";
|
||||
import { clusterReasons } from "../lib/reasons";
|
||||
import { withRunParam } from "../lib/run";
|
||||
import { useRun, runErrorTitle } from "../lib/useRun";
|
||||
|
||||
@@ -61,7 +63,7 @@ export default function OverviewPage() {
|
||||
const { runId, runQuery } = useRun();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [validationQuery, typesQuery, reasonsQuery, reposQuery] = useQueries({
|
||||
const [validationQuery, typesQuery, reasonsQuery, reposQuery, overviewQuery] = useQueries({
|
||||
queries: [
|
||||
{
|
||||
queryKey: ["stats", runId, "validation"],
|
||||
@@ -80,7 +82,12 @@ export default function OverviewPage() {
|
||||
},
|
||||
{
|
||||
queryKey: ["repos", runId, "top"],
|
||||
queryFn: () => listRepos(runId, { limit: 8 }),
|
||||
queryFn: () => listRepos(runId, { limit: 8, sort: "objects", order: "desc" }),
|
||||
staleTime: 60_000,
|
||||
},
|
||||
{
|
||||
queryKey: ["stats", runId, "overview"],
|
||||
queryFn: () => getStatsOverview(runId),
|
||||
staleTime: 60_000,
|
||||
},
|
||||
],
|
||||
@@ -109,13 +116,10 @@ export default function OverviewPage() {
|
||||
.slice(0, 8);
|
||||
}, [typesQuery.data]);
|
||||
|
||||
const reasonRows = useMemo(() => {
|
||||
const map = reasonsQuery.data ?? {};
|
||||
return Object.entries(map)
|
||||
.map(([reason, count]) => ({ reason, count }))
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.slice(0, 8);
|
||||
}, [reasonsQuery.data]);
|
||||
const reasonRows = useMemo(
|
||||
() => clusterReasons(reasonsQuery.data ?? {}),
|
||||
[reasonsQuery.data],
|
||||
);
|
||||
|
||||
const maxReason = reasonRows[0]?.count ?? 0;
|
||||
|
||||
@@ -224,6 +228,11 @@ export default function OverviewPage() {
|
||||
value={formatInt(counts?.publicationPoints)}
|
||||
to={withRunParam("/publication-points", runId)}
|
||||
/>
|
||||
<KpiCard
|
||||
label="Repositories"
|
||||
value={formatInt(overviewQuery.data?.repos)}
|
||||
to={withRunParam("/repositories", runId)}
|
||||
/>
|
||||
<KpiCard
|
||||
label="Rejected"
|
||||
value={formatInt(counts?.rejectedObjects)}
|
||||
@@ -304,7 +313,7 @@ export default function OverviewPage() {
|
||||
<YAxis
|
||||
type="category"
|
||||
dataKey="name"
|
||||
width={104}
|
||||
width={150}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
tick={{ fontSize: 12, fill: "var(--text-2)" }}
|
||||
@@ -325,7 +334,7 @@ export default function OverviewPage() {
|
||||
<div className="overview-bottom">
|
||||
<Panel
|
||||
title="Top repositories"
|
||||
subtitle="First repositories by index order"
|
||||
subtitle="Repositories with the most objects"
|
||||
tools={
|
||||
<Link className="btn small" to={withRunParam("/repositories", runId)}>
|
||||
View all
|
||||
@@ -353,7 +362,7 @@ export default function OverviewPage() {
|
||||
|
||||
<Panel
|
||||
title="Top reject reasons"
|
||||
subtitle="Click a reason to inspect matching objects"
|
||||
subtitle="Grouped by reason prefix — click to inspect matching objects"
|
||||
tools={
|
||||
<Link className="btn small" to={withRunParam("/validation", runId)}>
|
||||
Validation
|
||||
@@ -370,12 +379,17 @@ export default function OverviewPage() {
|
||||
<div className="reason-list">
|
||||
{reasonRows.map((row) => (
|
||||
<Link
|
||||
key={row.reason}
|
||||
key={row.key}
|
||||
className="reason-row"
|
||||
to={withRunParam(`/validation?reason=${encodeURIComponent(row.reason)}`, runId)}
|
||||
title={row.reason}
|
||||
to={withRunParam(`/validation?reason=${encodeURIComponent(row.key)}`, runId)}
|
||||
title={row.key}
|
||||
>
|
||||
<span className="reason-text">{row.reason}</span>
|
||||
<span className="reason-text">
|
||||
{row.key}
|
||||
{row.variants > 1 ? (
|
||||
<span className="text-faint text-small"> ×{row.variants}</span>
|
||||
) : null}
|
||||
</span>
|
||||
<span className="reason-count">{formatInt(row.count)}</span>
|
||||
<span className="reason-bar" aria-hidden="true">
|
||||
<span style={{ width: `${maxReason ? (row.count / maxReason) * 100 : 0}%` }} />
|
||||
|
||||
Reference in New Issue
Block a user