Skip to content

Commit cf5c789

Browse files
committed
refactor: rebuild InfraLens landing example and OG card to match the real report (v0.9.0)
- extract src/infralens/lib/mock-report.ts as a single source of mock data, shared by both surfaces instead of two hand-rolled copies that had already drifted (stale "OK" vocabulary and a wrong icon/color mapping, found and fixed in Phase 2's deleted section) - rebuild results-preview.tsx to reuse the real PrioritySummary and CheckResultCard components with mock data instead of a parallel reimplementation — adds the hostname/URL/score header and "At a glance" section that are now central to the real report but were entirely missing from the example - opengraph-image.tsx now reads from the same shared mock (hostname, score, categories, example checks) instead of a separate hardcoded copy
1 parent 7d9dec2 commit cf5c789

5 files changed

Lines changed: 146 additions & 138 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [0.9.0] — 2026-08-11
4+
5+
### Contenu
6+
7+
- **Exemple de rapport et carte OG d'InfraLens reconstruits pour refléter le vrai produit** — l'exemple sur la landing (`results-preview.tsx`) n'affichait qu'un badge de statut nu et 3 checks non repliables limités à 2 statuts sur 6, sans le bandeau hostname/URL/score ni le "At a glance" ("Worth fixing first" / "Working well") pourtant devenus le cœur visuel du vrai rapport depuis son redesign. Reconstruit en réutilisant directement les vrais composants `PrioritySummary` et `CheckResultCard` (au lieu d'une réimplémentation parallèle avec son propre badge de statut, qui avait déjà dérivé — vocabulaire "OK" et mapping icône/couleur incorrect trouvés en passant, corrigés en Phase 2). La carte OG partage désormais la même source de données (`src/infralens/lib/mock-report.ts`) au lieu d'une deuxième copie séparée qui pouvait diverger silencieusement.
8+
39
## [0.8.3] — 2026-08-11
410

511
### Nettoyage

app/tools/infralens/opengraph-image.tsx

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import { CATEGORY_LABELS } from "@infralens-lib/checks/category-labels";
2+
import {
3+
MOCK_CHECKS,
4+
MOCK_HOSTNAME,
5+
MOCK_SCORE,
6+
} from "@infralens-lib/mock-report";
27
import { readFileSync } from "fs";
38
import { ImageResponse } from "next/og";
49
import { join } from "path";
@@ -8,20 +13,18 @@ export const alt = "InfraLens — Website inspection tool";
813
export const size = { width: 1200, height: 630 };
914
export const contentType = "image/png";
1015

11-
// Same mock report as src/components/landing/results-preview.tsx (§6.3) —
12-
// keep both surfaces showing the exact same example so a visitor who saw
13-
// this card doesn't see a different-looking "example" on the site.
14-
const MOCK_CATEGORIES = [
15-
{ category: "http-security", score: 21, maxScore: 25 },
16-
{ category: "network-dns", score: 17, maxScore: 20 },
17-
{ category: "infrastructure", score: 20, maxScore: 20 },
18-
{ category: "website-structure", score: 12, maxScore: 15 },
19-
{ category: "metadata-stack", score: 8, maxScore: 10 },
20-
{ category: "performance", score: 6, maxScore: 10 },
21-
] as const;
22-
const MOCK_SCORE = 84;
23-
const MOCK_GRADE = "B";
24-
const MOCK_GRADE_COLOR = "#a3e635"; // lime-400, matches ScoreBadge's grade-color mapping
16+
// Sourced from src/infralens/lib/mock-report.ts — the same example shown
17+
// in results-preview.tsx on the landing, so a visitor who saw this card
18+
// never sees a different-looking "example" once they click through.
19+
// next/og (satori) can't render the real React components or read CSS
20+
// custom properties, so the two mini check rows below are picked by id
21+
// from MOCK_CHECKS and MOCK_GRADE_COLOR is a plain hex tied to grade "B"
22+
// (matches ScoreBadge's grade-color mapping) rather than computed.
23+
const MOCK_GRADE_COLOR = "#a3e635"; // lime-400 — update if MOCK_SCORE.grade ever changes
24+
const OG_CHECK_IDS = ["https-tls", "security-headers"] as const;
25+
const ogChecks = OG_CHECK_IDS.map(
26+
(id) => MOCK_CHECKS.find((check) => check.id === id)!,
27+
);
2528

2629
export default async function OgImage() {
2730
const fontBold = readFileSync(
@@ -164,14 +167,11 @@ export default async function OgImage() {
164167
real CheckResultCard, so this mirrors an actual check instead
165168
of a generic category tag. */}
166169
<div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
167-
{[
168-
{ label: "HTTPS & TLS", status: "pass" as const },
169-
{ label: "HTTP Security Headers", status: "warning" as const },
170-
].map((check) => {
170+
{ogChecks.map((check) => {
171171
const color = check.status === "pass" ? "#10b981" : "#f59e0b";
172172
return (
173173
<div
174-
key={check.label}
174+
key={check.id}
175175
style={{ display: "flex", alignItems: "center", gap: 10 }}
176176
>
177177
<span
@@ -257,7 +257,7 @@ export default async function OgImage() {
257257
<span
258258
style={{ color: "#52525b", fontSize: 13, fontWeight: 600, flex: 1 }}
259259
>
260-
example.com
260+
{MOCK_HOSTNAME}
261261
</span>
262262
{/* Score badge — mirrors ScoreBadge's grade-color mapping */}
263263
<div
@@ -278,12 +278,12 @@ export default async function OgImage() {
278278
color: MOCK_GRADE_COLOR,
279279
}}
280280
>
281-
{MOCK_GRADE}
281+
{MOCK_SCORE.grade}
282282
</span>
283283
<span
284284
style={{ fontSize: 12, color: MOCK_GRADE_COLOR, opacity: 0.75 }}
285285
>
286-
{MOCK_SCORE} / 100
286+
{MOCK_SCORE.score} / 100
287287
</span>
288288
</div>
289289
</div>
@@ -298,7 +298,7 @@ export default async function OgImage() {
298298
marginTop: 6,
299299
}}
300300
>
301-
{MOCK_CATEGORIES.map((c) => (
301+
{MOCK_SCORE.categories.map((c) => (
302302
<div
303303
key={c.category}
304304
style={{

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "randy-code",
3-
"version": "0.8.3",
3+
"version": "0.9.0",
44
"private": true,
55
"packageManager": "pnpm@10.7.0",
66
"scripts": {

src/infralens/components/landing/results-preview.tsx

Lines changed: 32 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,39 @@
11
import { Badge } from "@/components/ui/badge";
22
import { Card, CardContent, CardHeader } from "@/components/ui/card";
3+
import { CheckResultCard } from "@infralens-components/results/check-result-card";
4+
import { PrioritySummary } from "@infralens-components/results/priority-summary";
35
import { ScoreBadge } from "@infralens-components/results/score-badge";
4-
import { CATEGORY_LABELS } from "@infralens-lib/checks/category-labels";
5-
import { GlobalScore } from "@infralens-lib/checks/types";
6-
import { cn } from "@infralens-lib/utils";
7-
import { CircleCheckBig, TriangleAlert } from "lucide-react";
6+
import {
7+
MOCK_CHECKS,
8+
MOCK_HOSTNAME,
9+
MOCK_SCORE,
10+
MOCK_URL,
11+
} from "@infralens-lib/mock-report";
812

9-
// Static, hand-picked numbers — not derived from a real analysis. Master
10-
// plan §6.3: "un exemple statique crédible", clearly labeled as a
11-
// demonstration so it's never confused with a real report. Check cards
12-
// below reuse the exact same status-badge styling as the real
13-
// CheckResultCard (src/components/results/check-result-card.tsx) so this
14-
// preview isn't just thematically similar to a real report but pixel
15-
// consistent with one.
16-
const MOCK_SCORE: GlobalScore = {
17-
score: 84,
18-
grade: "B",
19-
categories: [
20-
{ category: "http-security", score: 21, maxScore: 25 },
21-
{ category: "network-dns", score: 17, maxScore: 20 },
22-
{ category: "infrastructure", score: 20, maxScore: 20 },
23-
{ category: "website-structure", score: 12, maxScore: 15 },
24-
{ category: "metadata-stack", score: 8, maxScore: 10 },
25-
{ category: "performance", score: 6, maxScore: 10 },
26-
],
27-
scoredCount: 16,
28-
excludedCount: 2,
29-
strongestCategory: "infrastructure",
30-
topPriorityCategory: "performance",
31-
};
32-
33-
const STATUS_STYLE = {
34-
pass: {
35-
icon: CircleCheckBig,
36-
label: "Pass",
37-
className: "text-emerald-500 border-emerald-500/20 bg-emerald-500/10",
38-
},
39-
warning: {
40-
icon: TriangleAlert,
41-
label: "Warning",
42-
className: "text-amber-500 border-amber-500/20 bg-amber-500/10",
43-
},
44-
} as const;
45-
46-
const MOCK_CHECKS = [
47-
{
48-
id: "https",
49-
label: "HTTPS & TLS",
50-
status: "pass",
51-
summary:
52-
"HTTPS is properly configured with a valid, non-expiring TLS certificate.",
53-
points: 6,
54-
},
55-
{
56-
id: "dns-security",
57-
label: "DNS Security",
58-
status: "pass",
59-
summary: "SPF and DMARC records are both present.",
60-
points: 7,
61-
},
62-
{
63-
id: "headers",
64-
label: "HTTP Security Headers",
65-
status: "warning",
66-
summary:
67-
"Content-Security-Policy is present but still allows unsafe-inline scripts.",
68-
points: 4,
69-
},
70-
] as const;
13+
// Which of the shared MOCK_CHECKS to show as example cards below the
14+
// summary — a mix of pass/warning/fail, same as PrioritySummary reads from
15+
// the same array above it.
16+
const EXAMPLE_CHECK_IDS = ["https-tls", "security-headers", "robots-txt"];
7117

7218
export function ResultsPreview() {
19+
const exampleChecks = MOCK_CHECKS.filter((check) =>
20+
EXAMPLE_CHECK_IDS.includes(check.id),
21+
);
22+
7323
return (
7424
<section className="py-8 md:py-12 lg:py-16 px-6 sm:px-8 md:px-12">
7525
<div className="max-w-4xl mx-auto space-y-6 md:space-y-8">
7626
<div className="text-center space-y-2">
7727
<h2 className="text-2xl sm:text-3xl font-bold tracking-tight">
7828
See what a report looks like
7929
</h2>
80-
<p className="text-zinc-400 text-sm sm:text-base">
30+
<p className="text-muted-foreground text-sm sm:text-base">
8131
A shortened example — no need to run a real analysis to see the
8232
shape of it.
8333
</p>
8434
</div>
8535

86-
<Card className="border-2 border-dashed border-zinc-700 bg-zinc-900/50">
36+
<Card className="border-2 border-dashed border-border bg-card/50">
8737
<CardHeader>
8838
<Badge
8939
variant="outline"
@@ -95,57 +45,25 @@ export function ResultsPreview() {
9545
<CardContent className="space-y-6">
9646
<div className="flex flex-col sm:flex-row items-center gap-6">
9747
<ScoreBadge score={MOCK_SCORE} />
98-
<div className="flex-1 w-full space-y-2">
99-
<p className="text-sm text-zinc-400">example.com</p>
100-
<div className="grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1.5">
101-
{MOCK_SCORE.categories.map((c) => (
102-
<div
103-
key={c.category}
104-
className="flex justify-between text-xs text-zinc-400"
105-
>
106-
<span>{CATEGORY_LABELS[c.category]}</span>
107-
<span>
108-
{c.score}/{c.maxScore}
109-
</span>
110-
</div>
111-
))}
112-
</div>
48+
<div className="flex-1 w-full min-w-0 space-y-1 text-center sm:text-left">
49+
<p className="font-semibold truncate">{MOCK_HOSTNAME}</p>
50+
<p className="text-sm text-muted-foreground truncate">
51+
{MOCK_URL}
52+
</p>
11353
</div>
11454
</div>
11555

116-
<div className="space-y-2">
117-
<p className="text-xs font-semibold uppercase tracking-wide text-zinc-400">
56+
<PrioritySummary checks={MOCK_CHECKS} />
57+
58+
<div className="space-y-3">
59+
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
11860
A few checks, same as the real report
11961
</p>
120-
{MOCK_CHECKS.map((check) => {
121-
const style = STATUS_STYLE[check.status];
122-
const Icon = style.icon;
123-
return (
124-
<div
125-
key={check.id}
126-
className="rounded-lg border border-zinc-800 bg-zinc-900/70 p-3 space-y-1"
127-
>
128-
<div className="flex items-center justify-between gap-2">
129-
<span className="text-sm font-semibold text-zinc-100">
130-
{check.label}
131-
</span>
132-
<div className="flex items-center gap-2 shrink-0">
133-
<span className="text-xs text-zinc-400 font-mono">
134-
+{check.points} pts
135-
</span>
136-
<Badge
137-
variant="outline"
138-
className={cn("border", style.className)}
139-
>
140-
<Icon className="size-3 mr-1.5" aria-hidden="true" />
141-
{style.label}
142-
</Badge>
143-
</div>
144-
</div>
145-
<p className="text-sm text-zinc-400">{check.summary}</p>
146-
</div>
147-
);
148-
})}
62+
<div className="space-y-3">
63+
{exampleChecks.map((check) => (
64+
<CheckResultCard key={check.id} result={check} />
65+
))}
66+
</div>
14967
</div>
15068
</CardContent>
15169
</Card>

src/infralens/lib/mock-report.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { CheckResult, GlobalScore } from "./checks/types";
2+
3+
// Static, hand-picked numbers — not derived from a real analysis. Shared by
4+
// the landing "example report" preview (results-preview.tsx) and the OG
5+
// card image (opengraph-image.tsx) so both surfaces always show the exact
6+
// same example instead of two separately hand-rolled copies that can (and
7+
// did) drift apart from each other and from the real report's anatomy.
8+
export const MOCK_HOSTNAME = "example.com";
9+
export const MOCK_URL = "https://example.com/";
10+
11+
export const MOCK_SCORE: GlobalScore = {
12+
score: 84,
13+
grade: "B",
14+
categories: [
15+
{ category: "http-security", score: 21, maxScore: 25 },
16+
{ category: "network-dns", score: 17, maxScore: 20 },
17+
{ category: "infrastructure", score: 20, maxScore: 20 },
18+
{ category: "website-structure", score: 12, maxScore: 15 },
19+
{ category: "metadata-stack", score: 8, maxScore: 10 },
20+
{ category: "performance", score: 6, maxScore: 10 },
21+
],
22+
scoredCount: 16,
23+
excludedCount: 2,
24+
strongestCategory: "infrastructure",
25+
topPriorityCategory: "performance",
26+
};
27+
28+
// Covers pass/warning/fail/info on purpose — real CheckResultCard and
29+
// PrioritySummary instances render straight off this array, so it needs to
30+
// exercise the same status range a real report would.
31+
export const MOCK_CHECKS: CheckResult[] = [
32+
{
33+
id: "https-tls",
34+
label: "HTTPS & TLS",
35+
category: "http-security",
36+
status: "pass",
37+
summary:
38+
"HTTPS is properly configured with a valid, non-expiring TLS certificate.",
39+
durationMs: 180,
40+
scored: true,
41+
scoreContribution: 6,
42+
},
43+
{
44+
id: "dns-security",
45+
label: "DNS Security",
46+
category: "network-dns",
47+
status: "pass",
48+
summary: "SPF and DMARC records are both present.",
49+
durationMs: 95,
50+
scored: true,
51+
scoreContribution: 7,
52+
},
53+
{
54+
id: "security-headers",
55+
label: "HTTP Security Headers",
56+
category: "http-security",
57+
status: "warning",
58+
summary:
59+
"Content-Security-Policy is present but still allows unsafe-inline scripts.",
60+
durationMs: 140,
61+
scored: true,
62+
scoreContribution: 4,
63+
},
64+
{
65+
id: "robots-txt",
66+
label: "robots.txt",
67+
category: "website-structure",
68+
status: "fail",
69+
summary: "robots.txt not found.",
70+
durationMs: 110,
71+
scored: true,
72+
scoreContribution: 0,
73+
},
74+
{
75+
id: "waf-detection",
76+
label: "Firewall / WAF Detection",
77+
category: "infrastructure",
78+
status: "info",
79+
summary:
80+
"Response headers suggest a CDN may be in front of this site (not a confirmed detection).",
81+
durationMs: 60,
82+
scored: false,
83+
},
84+
];

0 commit comments

Comments
 (0)