feat(web): add dynamic SEO article generator for showcase
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 7s

This commit is contained in:
OG T
2026-06-07 22:45:47 +08:00
parent 331f07384c
commit 35b87d5fc9
2 changed files with 173 additions and 2 deletions

View File

@@ -0,0 +1,171 @@
import { prisma } from "@/lib/prisma";
import { notFound } from "next/navigation";
import Link from "next/link";
import { TaskStatus } from "@agent-bounty/contracts";
export const dynamic = "force-dynamic";
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const task = await prisma.task.findUnique({
where: { id },
});
if (!task) {
return { title: "Article Not Found" };
}
const cleanDescription = task.description.replace(/\n/g, " ").substring(0, 150);
return {
title: `${task.title} - AI Automated Solution`,
description: `Learn how an autonomous AI agent solved: ${cleanDescription}... Read the full technical breakdown.`,
keywords: [...task.required_stack, "AI Developer", "Autonomous Agent", "Bug Fix", "Tutorial"],
};
}
export default async function ShowcaseArticlePage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const task = await prisma.task.findUnique({
where: { id, status: TaskStatus.COMPLETED },
include: {
builder_agent: true,
submissions: {
orderBy: { created_at: "desc" },
take: 1
}
}
});
if (!task || task.submissions.length === 0) {
notFound();
}
const submission = task.submissions[0];
const deliverables = submission.deliverables as Record<string, string>;
const files = Object.keys(deliverables || {});
// Generate JSON-LD for Google SGE & Rich Snippets
const jsonLd = {
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": `${task.title} - AI Automated Solution`,
"description": task.description.replace(/\n/g, " ").substring(0, 150),
"author": {
"@type": "SoftwareApplication",
"name": `VibeWork Agent (${task.builder_agent?.agent_id || "Anonymous"})`
},
"publisher": {
"@type": "Organization",
"name": "VibeWork",
"logo": {
"@type": "ImageObject",
"url": "https://agent.wooo.work/logo.png"
}
},
"datePublished": task.updated_at.toISOString(),
"articleSection": "Software Development",
"keywords": task.required_stack.join(", ")
};
return (
<div className="min-h-screen bg-gray-950 text-gray-100 p-8 font-sans selection:bg-blue-500/30">
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<div className="max-w-4xl mx-auto">
<Link href="/showcase" className="text-blue-400 hover:text-blue-300 mb-8 inline-flex items-center gap-2 font-medium">
Back to Showcase
</Link>
<article className="bg-gray-900 border border-gray-800 rounded-3xl p-8 md:p-12 shadow-2xl mt-4">
<header className="mb-12 border-b border-gray-800 pb-8">
<div className="flex gap-2 mb-6">
{task.required_stack.map(tech => (
<span key={tech} className="bg-blue-900/30 text-blue-300 border border-blue-800/50 px-3 py-1 rounded-md text-sm font-semibold tracking-wide">
{tech}
</span>
))}
</div>
<h1 className="text-3xl md:text-5xl font-extrabold text-white leading-tight mb-6">
{task.title}
</h1>
<div className="flex flex-wrap items-center gap-6 text-gray-400 text-sm">
<div className="flex items-center gap-2">
<span className="text-2xl">🤖</span>
<div>
<div className="text-xs text-gray-500 uppercase tracking-wider font-bold">Solved By</div>
<div className="text-white font-mono">{task.builder_agent?.agent_id || "Anonymous AI"}</div>
</div>
</div>
<div className="h-8 w-px bg-gray-800 hidden md:block"></div>
<div>
<div className="text-xs text-gray-500 uppercase tracking-wider font-bold">Bounty</div>
<div className="text-emerald-400 font-bold text-lg">${(task.reward_amount / 100).toFixed(2)} {task.reward_currency}</div>
</div>
<div className="h-8 w-px bg-gray-800 hidden md:block"></div>
<div>
<div className="text-xs text-gray-500 uppercase tracking-wider font-bold">Completed On</div>
<div className="text-white">{new Date(task.updated_at).toLocaleDateString()}</div>
</div>
</div>
</header>
<section className="mb-12">
<h2 className="text-2xl font-bold text-white mb-6 flex items-center gap-2">
<span className="text-blue-500">1.</span> The Problem
</h2>
<div className="prose prose-invert max-w-none text-gray-300 bg-gray-950 p-6 rounded-2xl border border-gray-800/50 whitespace-pre-wrap leading-relaxed">
{task.description}
</div>
</section>
<section className="mb-12">
<h2 className="text-2xl font-bold text-white mb-6 flex items-center gap-2">
<span className="text-emerald-500">2.</span> AI Automated Solution
</h2>
<p className="text-gray-400 mb-6">
This task was fully completed by an autonomous AI agent without human intervention. The agent successfully passed all Vitest integration checks and submitted the following deliverables:
</p>
<div className="space-y-8">
{files.map((filename) => (
<div key={filename} className="rounded-2xl border border-gray-800 overflow-hidden bg-black shadow-lg">
<div className="bg-gray-900 border-b border-gray-800 px-4 py-3 flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="w-3 h-3 rounded-full bg-red-500/50"></span>
<span className="w-3 h-3 rounded-full bg-yellow-500/50"></span>
<span className="w-3 h-3 rounded-full bg-green-500/50"></span>
</div>
<span className="font-mono text-sm text-gray-400">{filename}</span>
</div>
<div className="p-4 overflow-x-auto">
<pre className="text-sm font-mono text-gray-300">
<code>{deliverables[filename]}</code>
</pre>
</div>
</div>
))}
</div>
</section>
{/* Call to Action for Humans (Traffic Conversion) */}
<section className="mt-16 bg-gradient-to-br from-blue-900/20 to-purple-900/20 border border-blue-500/30 rounded-3xl p-8 text-center relative overflow-hidden">
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500 to-purple-500"></div>
<h3 className="text-2xl font-bold text-white mb-4">Facing a similar challenge?</h3>
<p className="text-gray-300 mb-8 max-w-2xl mx-auto">
Stop wasting hours debugging. Post your issue on VibeWork and let top-tier autonomous AI agents solve it for you in seconds.
</p>
<div className="flex justify-center gap-4">
<Link href="/tasks/create" className="bg-blue-600 hover:bg-blue-500 text-white font-bold py-3 px-8 rounded-xl transition-all shadow-lg shadow-blue-500/30">
Post a Task Now
</Link>
<a href={`mailto:recruit@vibework.com?subject=Hire Agent ${task.builder_agent?.agent_id}`} className="bg-gray-800 hover:bg-gray-700 text-white border border-gray-700 font-bold py-3 px-8 rounded-xl transition-all">
Hire this Agent
</a>
</div>
</section>
</article>
</div>
</div>
);
}

View File

@@ -83,8 +83,8 @@ export default async function ShowcasePage() {
</div>
<div className="flex items-center gap-3">
<Link href={`/tasks/${task.id}`} className="text-sm text-gray-400 hover:text-white transition-colors">
View Source
<Link href={`/showcase/${task.id}`} className="text-sm text-blue-400 font-bold hover:text-blue-300 transition-colors">
Read Full Solution
</Link>
{/* 仲介費獲利按鈕 */}