Skip to content

Commit 6b68e45

Browse files
committed
feat(web): enhance Open Graph metadata and add dynamic OG image generation
- Updated layout metadata to include Open Graph images for the homepage and analysis pages, improving social sharing capabilities. - Implemented dynamic generation of OG images for analysis jobs, incorporating persona insights and metrics. - Introduced new API routes for serving OG images, ensuring tailored visuals based on user analysis data.
1 parent 0afc2c8 commit 6b68e45

5 files changed

Lines changed: 675 additions & 9 deletions

File tree

apps/web/src/app/analysis/[jobId]/page.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,124 @@
1+
import type { Metadata } from "next";
12
import AnalysisClient from "./AnalysisClient";
23
import { createSupabaseServerClient } from "@/lib/supabase/server";
34
import { redirect } from "next/navigation";
45
import { wrappedTheme } from "@/lib/theme";
56

67
export const runtime = "nodejs";
78

9+
const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:8108";
10+
11+
// Database row types for OG metadata
12+
type AnalysisJobRow = {
13+
repo_id: string;
14+
commit_count: number | null;
15+
status: string;
16+
};
17+
18+
type RepoRow = {
19+
full_name: string;
20+
};
21+
22+
type VibeInsightsRow = {
23+
persona_name: string | null;
24+
persona_tagline: string | null;
25+
};
26+
27+
type AnalysisInsightsRow = {
28+
persona_label: string | null;
29+
share_template: unknown;
30+
};
31+
32+
export async function generateMetadata({
33+
params,
34+
}: {
35+
params: Promise<{ jobId: string }>;
36+
}): Promise<Metadata> {
37+
const { jobId } = await params;
38+
const supabase = await createSupabaseServerClient();
39+
40+
// Fetch job and related data for OG metadata
41+
const { data: jobData } = await supabase
42+
.from("analysis_jobs")
43+
.select("repo_id, commit_count, status")
44+
.eq("id", jobId)
45+
.maybeSingle();
46+
47+
const job = jobData as AnalysisJobRow | null;
48+
49+
if (!job || job.status !== "done") {
50+
return {
51+
title: "Analysis | Vibe Coding Profiler",
52+
description: "Analyzing your commit history to discover your Vibe Coding Profile.",
53+
};
54+
}
55+
56+
// Fetch persona and repo info
57+
const [repoResult, vibeResult, insightsResult] = await Promise.all([
58+
supabase.from("repos").select("full_name").eq("id", job.repo_id).maybeSingle(),
59+
supabase
60+
.from("vibe_insights")
61+
.select("persona_name, persona_tagline")
62+
.eq("job_id", jobId)
63+
.maybeSingle(),
64+
supabase
65+
.from("analysis_insights")
66+
.select("persona_label, share_template")
67+
.eq("job_id", jobId)
68+
.maybeSingle(),
69+
]);
70+
71+
const repoRow = repoResult?.data as RepoRow | null;
72+
const vibeRow = vibeResult?.data as VibeInsightsRow | null;
73+
const insightsRow = insightsResult?.data as AnalysisInsightsRow | null;
74+
75+
const repoName =
76+
repoRow?.full_name && typeof repoRow.full_name === "string"
77+
? repoRow.full_name
78+
: "Repository";
79+
80+
const personaName =
81+
vibeRow?.persona_name ??
82+
insightsRow?.persona_label ??
83+
"Vibe Coder";
84+
85+
const personaTagline =
86+
vibeRow?.persona_tagline ??
87+
(insightsRow?.share_template as { tagline?: string } | null)?.tagline ??
88+
"Discover your AI coding style";
89+
90+
const title = `${personaName} | ${repoName} VCP`;
91+
const description = `${personaTagline}${job.commit_count?.toLocaleString() ?? 0} commits analyzed.`;
92+
const ogImageUrl = `${appUrl}/api/og/analysis/${jobId}`;
93+
const pageUrl = `${appUrl}/analysis/${jobId}`;
94+
95+
return {
96+
title,
97+
description,
98+
openGraph: {
99+
title,
100+
description,
101+
url: pageUrl,
102+
siteName: "Vibe Coding Profiler",
103+
type: "article",
104+
images: [
105+
{
106+
url: ogImageUrl,
107+
width: 1200,
108+
height: 630,
109+
alt: `${personaName} - Vibe Coding Profile for ${repoName}`,
110+
},
111+
],
112+
},
113+
twitter: {
114+
card: "summary_large_image",
115+
title,
116+
description,
117+
images: [ogImageUrl],
118+
},
119+
};
120+
}
121+
8122
export default async function AnalysisPage({
9123
params,
10124
}: {

0 commit comments

Comments
 (0)