diff --git a/data/certifications.json b/data/certifications.json index f47b46cf..a54c0e85 100644 --- a/data/certifications.json +++ b/data/certifications.json @@ -1,175 +1,170 @@ [ { - "id": "sec-001", - "name": "Security Certified Agent", - "slug": "security-certified", - "icon": "🔒", - "description": "Validates that an agent follows security best practices including credential handling, input validation, and secure communication.", - "category": "Security", - "level": "Essential", - "badgeColor": "red", - "requirements": [ - "Zero hardcoded credentials or API keys", - "Environment variable validation on startup", - "Input sanitization for all user inputs", - "Rate limiting on all API endpoints", - "Encrypted communication for all external services", - "Audit logging for sensitive operations", - "Regular dependency vulnerability scans" + "id": "cert-101", + "title": "Agent Foundations", + "slug": "agent-foundations", + "description": "Master safe prompting, tool hygiene, and operational basics for production-ready agents.", + "level": "beginner", + "modules": [ + { + "name": "Prompt Guardrails", + "description": "Design prompts that reduce ambiguity and avoid unsafe execution paths." + }, + { + "name": "Tool Permissions", + "description": "Apply least-privilege rules and safe defaults for external tools and APIs." + }, + { + "name": "Error Recovery", + "description": "Handle partial failures with retries, fallbacks, and clear user messaging." + } ], - "verificationSteps": [ - "Submit GitHub repository for automated security scan", - "Pass OWASP Top 10 compliance check", - "Demonstrate secure credential rotation", - "Complete penetration test review" + "requirements": [ + "Complete the quickstart journey", + "Pass the basic safety and policy quiz", + "Submit one working agent task log" ], - "estimatedTime": "2-3 days", - "prerequisites": [], - "benefits": [ - "Display Security Certified badge on profile", - "Listed in security-certified agent directory", - "Priority support for security issues", - "Access to security best practices documentation" - ] + "enrollmentCount": 184, + "badge": "🧭" }, { - "id": "perf-001", - "name": "Performance Certified Agent", - "slug": "performance-certified", - "icon": "⚡", - "description": "Demonstrates optimal response times, efficient resource usage, and scalability under load.", - "category": "Performance", - "level": "Advanced", - "badgeColor": "yellow", - "requirements": [ - "Average response time under 500ms for 95th percentile", - "Memory usage under 512MB for standard workloads", - "Support for concurrent request handling (min 10 concurrent)", - "Graceful degradation under high load", - "Caching strategy for repeated operations", - "Database query optimization (if applicable)", - "CDN integration for static assets" + "id": "cert-201", + "title": "Workflow Automation Builder", + "slug": "workflow-automation-builder", + "description": "Build reliable multi-step automations with robust validation, branching, and observability.", + "level": "intermediate", + "modules": [ + { + "name": "Flow Orchestration", + "description": "Model decision trees and retries for long-running automation chains." + }, + { + "name": "Input Validation", + "description": "Guarantee clean input contracts to prevent hidden workflow failures." + }, + { + "name": "Runbook Design", + "description": "Document and operationalize support playbooks for incidents." + } ], - "verificationSteps": [ - "Submit to automated load testing service", - "Pass benchmark tests (response time, throughput, memory)", - "Provide architecture diagram showing optimization strategies", - "Complete 24-hour stress test" + "requirements": [ + "Agent Foundations certification", + "Implement a 5-step automation pipeline", + "Demonstrate success/failure telemetry" ], - "estimatedTime": "3-5 days", - "prerequisites": [], - "benefits": [ - "Display Performance Certified badge on profile", - "Featured in high-performance agent showcase", - "Access to performance monitoring dashboard", - "Eligibility for enterprise client listings" - ] + "enrollmentCount": 137, + "badge": "⚙️" }, { - "id": "rel-001", - "name": "Reliability Certified Agent", - "slug": "reliability-certified", - "icon": "✅", - "description": "Ensures high availability, proper error handling, comprehensive monitoring, and disaster recovery capabilities.", - "category": "Reliability", - "level": "Essential", - "badgeColor": "green", - "requirements": [ - "99.9% uptime over 30-day monitoring period", - "Comprehensive error handling and recovery", - "Health check endpoints implemented", - "Automated alerting for failures", - "Retry logic with exponential backoff", - "Circuit breaker pattern for external dependencies", - "Backup and recovery procedures documented" + "id": "cert-301", + "title": "Multi-Agent Systems Engineer", + "slug": "multi-agent-systems-engineer", + "description": "Coordinate specialist agents with clear contracts, role isolation, and handoff protocols.", + "level": "advanced", + "modules": [ + { + "name": "Role-Based Agent Design", + "description": "Define specialist responsibilities and interfaces for each lane agent." + }, + { + "name": "Handoff Protocols", + "description": "Pass context between agents without losing accuracy or task ownership." + }, + { + "name": "Conflict Resolution", + "description": "Resolve contradictory outputs using confidence and escalation policies." + } ], - "verificationSteps": [ - "Submit agent for 30-day uptime monitoring", - "Demonstrate failure recovery scenarios", - "Provide incident response documentation", - "Pass chaos engineering tests" + "requirements": [ + "Workflow Automation Builder certification", + "Submit architecture for at least 3 collaborating agents", + "Pass concurrency and coordination evaluation" ], - "estimatedTime": "30+ days (includes monitoring period)", - "prerequisites": [], - "benefits": [ - "Display Reliability Certified badge on profile", - "Listed in high-reliability agent directory", - "Access to uptime monitoring dashboard", - "Priority incident response support" - ] + "enrollmentCount": 93, + "badge": "🤝" }, { - "id": "ent-001", - "name": "Enterprise Ready Agent", - "slug": "enterprise-ready", - "icon": "🏢", - "description": "Meets enterprise-grade requirements including compliance, audit trails, SSO, and SLA guarantees.", - "category": "Enterprise", - "level": "Premium", - "badgeColor": "blue", + "id": "cert-302", + "title": "Agent Security Specialist", + "slug": "agent-security-specialist", + "description": "Harden agents against prompt injection, data exfiltration, and unsafe tool execution.", + "level": "advanced", + "modules": [ + { + "name": "Prompt Injection Defense", + "description": "Detect and neutralize adversarial prompt patterns in real time." + }, + { + "name": "Secrets & Data Boundaries", + "description": "Protect credentials and sensitive context across agent interactions." + }, + { + "name": "Policy-Aware Execution", + "description": "Enforce runtime safeguards before privileged actions occur." + } + ], "requirements": [ - "SOC 2 Type II or equivalent compliance documentation", - "GDPR and CCPA data privacy compliance", - "SSO/SAML authentication support", - "Role-based access control (RBAC)", - "Comprehensive audit logging (all actions, 90-day retention)", - "SLA commitment (99.95% uptime)", - "Dedicated support channel", - "Multi-region deployment capability", - "Data residency options" + "Agent Foundations certification", + "Pass security scenario simulations", + "Complete secure-tooling checklist" ], - "verificationSteps": [ - "Submit compliance documentation", - "Demonstrate SSO integration", - "Provide audit log samples", - "Complete enterprise security questionnaire", - "Pass third-party security audit" + "enrollmentCount": 109, + "badge": "🛡️" + }, + { + "id": "cert-401", + "title": "Enterprise Agent Architect", + "slug": "enterprise-agent-architect", + "description": "Design enterprise-grade agent platforms with governance, auditability, and SLA resilience.", + "level": "expert", + "modules": [ + { + "name": "Governance Architecture", + "description": "Define controls for approvals, traceability, and policy lifecycle management." + }, + { + "name": "Audit & Compliance", + "description": "Implement reporting paths for SOC2-style evidence and operational controls." + }, + { + "name": "Scale & Reliability", + "description": "Deliver durable agent systems with failover, SLOs, and incident response plans." + } ], - "estimatedTime": "2-4 weeks", - "prerequisites": [ - "Security Certified", - "Reliability Certified" + "requirements": [ + "Multi-Agent Systems Engineer certification", + "Agent Security Specialist certification", + "Submit enterprise deployment blueprint and review session" ], - "benefits": [ - "Display Enterprise Ready badge on profile", - "Featured in enterprise marketplace", - "Direct access to enterprise sales team", - "Co-marketing opportunities", - "Premium listing placement" - ] + "enrollmentCount": 58, + "badge": "🏛️" }, { - "id": "obs-001", - "name": "Observability Certified Agent", - "slug": "observability-certified", - "icon": "📊", - "description": "Implements comprehensive logging, metrics, tracing, and monitoring for full system visibility.", - "category": "Operations", - "level": "Advanced", - "badgeColor": "purple", - "requirements": [ - "Structured logging (JSON format) with correlation IDs", - "Metrics instrumentation (Prometheus/OpenTelemetry compatible)", - "Distributed tracing implemented", - "Custom dashboards for key metrics", - "Log aggregation and search capability", - "Performance profiling endpoints", - "Real-time alerting configuration", - "Integration with common observability platforms (Datadog, New Relic, etc.)" + "id": "cert-402", + "title": "Agent Program Mentor", + "slug": "agent-program-mentor", + "description": "Lead teams to ship repeatable agent programs with quality gates and coaching systems.", + "level": "intermediate", + "modules": [ + { + "name": "Coaching Frameworks", + "description": "Create growth pathways for builders through feedback loops and milestones." + }, + { + "name": "Quality Gates", + "description": "Set evidence-based review criteria before deployments and releases." + }, + { + "name": "Delivery Rituals", + "description": "Run weekly cadences that improve output quality and team alignment." + } ], - "verificationSteps": [ - "Submit observability stack documentation", - "Demonstrate live metrics dashboard", - "Provide sample traces and logs", - "Pass observability integration tests" + "requirements": [ + "Complete two peer review sessions", + "Publish one reusable mentorship runbook", + "Demonstrate coaching impact with before/after metrics" ], - "estimatedTime": "4-6 days", - "prerequisites": [], - "benefits": [ - "Display Observability Certified badge on profile", - "Featured in ops-focused agent directory", - "Access to advanced monitoring tools", - "Integration with forAgents.dev monitoring platform" - ] + "enrollmentCount": 76, + "badge": "🌱" } ] diff --git a/src/app/api/certifications/[slug]/route.ts b/src/app/api/certifications/[slug]/route.ts new file mode 100644 index 00000000..87dfb218 --- /dev/null +++ b/src/app/api/certifications/[slug]/route.ts @@ -0,0 +1,88 @@ +import { promises as fs } from "fs"; +import path from "path"; +import { NextRequest, NextResponse } from "next/server"; +import type { Certification } from "@/lib/certifications"; + +export const runtime = "nodejs"; + +const CERTIFICATIONS_PATH = path.join(process.cwd(), "data", "certifications.json"); + +async function readCertifications(): Promise { + try { + const raw = await fs.readFile(CERTIFICATIONS_PATH, "utf-8"); + const parsed = JSON.parse(raw) as Certification[]; + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +async function writeCertifications(certifications: Certification[]) { + await fs.writeFile(CERTIFICATIONS_PATH, `${JSON.stringify(certifications, null, 2)}\n`, "utf-8"); +} + +export async function GET(_request: NextRequest, context: { params: Promise<{ slug: string }> }) { + const { slug } = await context.params; + const certifications = await readCertifications(); + + const certification = certifications.find((item) => item.slug === slug); + + if (!certification) { + return NextResponse.json({ error: "Certification not found" }, { status: 404 }); + } + + return NextResponse.json( + { certification }, + { + headers: { + "Cache-Control": "no-store", + }, + } + ); +} + +export async function POST(request: NextRequest, context: { params: Promise<{ slug: string }> }) { + const { slug } = await context.params; + + let body: unknown; + try { + body = await request.json(); + } catch { + return NextResponse.json({ error: "Invalid request body" }, { status: 400 }); + } + + const agentHandle = + typeof body === "object" && body !== null && "agentHandle" in body + ? String((body as { agentHandle: unknown }).agentHandle).trim() + : ""; + + if (!agentHandle) { + return NextResponse.json({ error: "agentHandle is required" }, { status: 400 }); + } + + const certifications = await readCertifications(); + const index = certifications.findIndex((item) => item.slug === slug); + + if (index === -1) { + return NextResponse.json({ error: "Certification not found" }, { status: 404 }); + } + + const currentEnrollment = Number(certifications[index].enrollmentCount) || 0; + certifications[index].enrollmentCount = currentEnrollment + 1; + + await writeCertifications(certifications); + + return NextResponse.json( + { + success: true, + enrolledBy: agentHandle, + certification: certifications[index], + }, + { + status: 200, + headers: { + "Cache-Control": "no-store", + }, + } + ); +} diff --git a/src/app/api/certifications/route.ts b/src/app/api/certifications/route.ts new file mode 100644 index 00000000..ae3184b7 --- /dev/null +++ b/src/app/api/certifications/route.ts @@ -0,0 +1,53 @@ +import { promises as fs } from "fs"; +import path from "path"; +import { NextRequest, NextResponse } from "next/server"; +import type { Certification, CertificationLevel } from "@/lib/certifications"; + +export const runtime = "nodejs"; + +const CERTIFICATIONS_PATH = path.join(process.cwd(), "data", "certifications.json"); + +async function readCertifications(): Promise { + try { + const raw = await fs.readFile(CERTIFICATIONS_PATH, "utf-8"); + const parsed = JSON.parse(raw) as Certification[]; + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +export async function GET(request: NextRequest) { + const certifications = await readCertifications(); + + const levelParam = request.nextUrl.searchParams.get("level")?.toLowerCase(); + const searchParam = request.nextUrl.searchParams.get("search")?.trim().toLowerCase(); + + const validLevels: CertificationLevel[] = ["beginner", "intermediate", "advanced", "expert"]; + const levelFilter = validLevels.includes(levelParam as CertificationLevel) + ? (levelParam as CertificationLevel) + : null; + + const filtered = certifications.filter((cert) => { + const levelMatches = levelFilter ? cert.level === levelFilter : true; + const searchMatches = searchParam + ? cert.title.toLowerCase().includes(searchParam) || + cert.description.toLowerCase().includes(searchParam) || + cert.modules.some((module) => module.name.toLowerCase().includes(searchParam)) + : true; + + return levelMatches && searchMatches; + }); + + return NextResponse.json( + { + certifications: filtered, + total: filtered.length, + }, + { + headers: { + "Cache-Control": "no-store", + }, + } + ); +} diff --git a/src/app/certifications/[slug]/certification-detail-client.tsx b/src/app/certifications/[slug]/certification-detail-client.tsx new file mode 100644 index 00000000..cf0f95b8 --- /dev/null +++ b/src/app/certifications/[slug]/certification-detail-client.tsx @@ -0,0 +1,229 @@ +"use client"; + +import Link from "next/link"; +import { useEffect, useState } from "react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import type { Certification } from "@/lib/certifications"; +import { formatCertificationLevel } from "@/lib/certifications"; + +function levelBadgeClass(level: Certification["level"]): string { + const map: Record = { + beginner: "bg-green-500/20 text-green-400 border-green-500/30", + intermediate: "bg-blue-500/20 text-blue-400 border-blue-500/30", + advanced: "bg-purple-500/20 text-purple-400 border-purple-500/30", + expert: "bg-amber-500/20 text-amber-400 border-amber-500/30", + }; + + return map[level]; +} + +export function CertificationDetailClient({ slug }: { slug: string }) { + const [certification, setCertification] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [agentHandle, setAgentHandle] = useState(""); + const [enrolling, setEnrolling] = useState(false); + const [statusMessage, setStatusMessage] = useState(null); + + useEffect(() => { + let isMounted = true; + + async function loadCertification() { + setLoading(true); + setError(null); + + try { + const response = await fetch(`/api/certifications/${slug}`, { + cache: "no-store", + }); + + if (!response.ok) { + if (response.status === 404) { + throw new Error("Certification not found"); + } + throw new Error("Failed to load certification"); + } + + const payload = (await response.json()) as { certification?: Certification }; + + if (!payload.certification) { + throw new Error("Certification payload is missing"); + } + + if (isMounted) { + setCertification(payload.certification); + } + } catch (requestError) { + if (isMounted) { + const message = requestError instanceof Error ? requestError.message : "Failed to load certification"; + setError(message); + } + } finally { + if (isMounted) { + setLoading(false); + } + } + } + + void loadCertification(); + + return () => { + isMounted = false; + }; + }, [slug]); + + async function handleEnroll() { + const normalizedHandle = agentHandle.trim(); + + if (!normalizedHandle) { + setStatusMessage("Please enter your agent handle before enrolling."); + return; + } + + setEnrolling(true); + setStatusMessage(null); + + try { + const response = await fetch(`/api/certifications/${slug}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ agentHandle: normalizedHandle }), + }); + + const payload = (await response.json()) as { + error?: string; + enrolledBy?: string; + certification?: Certification; + }; + + if (!response.ok || !payload.certification) { + throw new Error(payload.error || "Enrollment failed"); + } + + setCertification(payload.certification); + setStatusMessage(`Enrollment confirmed for ${payload.enrolledBy}.`); + } catch (enrollmentError) { + const message = enrollmentError instanceof Error ? enrollmentError.message : "Enrollment failed"; + setStatusMessage(message); + } finally { + setEnrolling(false); + } + } + + if (loading) { + return ( +
+ + Loading certification details... + +
+ ); + } + + if (error || !certification) { + return ( +
+ + ← Back to Certifications + + + +

{error || "Certification not found"}

+ +
+
+
+ ); + } + + return ( +
+ + ← Back to Certifications + + + + +
+
+
{certification.badge}
+ {certification.title} +

{certification.description}

+
+ + {formatCertificationLevel(certification.level)} + +
+
+ +

+ Active enrollments: {certification.enrollmentCount} +

+
+
+ + + + Modules + + + {certification.modules.map((module, index) => ( +
+

Module {index + 1}

+

{module.name}

+

{module.description}

+
+ ))} +
+
+ + + + Requirements + + +
    + {certification.requirements.map((requirement) => ( +
  • + + {requirement} +
  • + ))} +
+
+
+ + + + Enroll in this certification + + +
+ + setAgentHandle(event.target.value)} + /> +
+ + + + {statusMessage &&

{statusMessage}

} +
+
+
+ ); +} diff --git a/src/app/certifications/[slug]/page.tsx b/src/app/certifications/[slug]/page.tsx index e83efbcb..a4e9051c 100644 --- a/src/app/certifications/[slug]/page.tsx +++ b/src/app/certifications/[slug]/page.tsx @@ -1,55 +1,38 @@ -import { notFound } from "next/navigation"; -import Link from "next/link"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Separator } from "@/components/ui/separator"; -import certificationsData from "@/../data/certifications.json"; +/* eslint-disable react/no-unescaped-entities */ -interface Certification { - id: string; - name: string; - slug: string; - icon: string; - description: string; - category: string; - level: "Essential" | "Advanced" | "Premium"; - badgeColor: string; - requirements: string[]; - verificationSteps: string[]; - estimatedTime: string; - prerequisites: string[]; - benefits: string[]; -} - -const certifications: Certification[] = certificationsData as Certification[]; +import certifications from "@/../data/certifications.json"; +import type { Certification } from "@/lib/certifications"; +import { CertificationDetailClient } from "./certification-detail-client"; interface Props { params: Promise<{ slug: string }>; } +const certificationSeed = certifications as Certification[]; + export async function generateStaticParams() { - return certifications.map((cert) => ({ - slug: cert.slug, + return certificationSeed.map((certification) => ({ + slug: certification.slug, })); } export async function generateMetadata({ params }: Props) { const { slug } = await params; - const cert = certifications.find((c) => c.slug === slug); + const certification = certificationSeed.find((item) => item.slug === slug); - if (!cert) { + if (!certification) { return { title: "Certification Not Found — forAgents.dev", + description: "The certification you are looking for is unavailable.", }; } return { - title: `${cert.name} — forAgents.dev Certification`, - description: cert.description, + title: `${certification.title} — forAgents.dev Certification`, + description: certification.description, openGraph: { - title: `${cert.name} — forAgents.dev Certification`, - description: cert.description, + title: `${certification.title} — forAgents.dev Certification`, + description: certification.description, url: `https://foragents.dev/certifications/${slug}`, siteName: "forAgents.dev", type: "website", @@ -59,218 +42,6 @@ export async function generateMetadata({ params }: Props) { export default async function CertificationDetailPage({ params }: Props) { const { slug } = await params; - const cert = certifications.find((c) => c.slug === slug); - - if (!cert) { - notFound(); - } - - const getLevelBadgeColor = (level: Certification["level"]) => { - switch (level) { - case "Essential": - return "bg-green-500/20 text-green-400 border-green-500/30"; - case "Advanced": - return "bg-blue-500/20 text-blue-400 border-blue-500/30"; - case "Premium": - return "bg-purple-500/20 text-purple-400 border-purple-500/30"; - default: - return "bg-gray-500/20 text-gray-400 border-gray-500/30"; - } - }; - - const getBadgeColorClass = (color: string) => { - const colorMap: Record = { - red: "bg-red-500/20 text-red-400 border-red-500/30", - yellow: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", - green: "bg-green-500/20 text-green-400 border-green-500/30", - blue: "bg-blue-500/20 text-blue-400 border-blue-500/30", - purple: "bg-purple-500/20 text-purple-400 border-purple-500/30", - }; - return colorMap[color] || "bg-gray-500/20 text-gray-400 border-gray-500/30"; - }; - - return ( -
- {/* Breadcrumb */} -
- - ← Back to Certifications - -
- - {/* Header */} -
-
-
{cert.icon}
-
-
- - {cert.level} - - - {cert.category} - -
-

{cert.name}

-

{cert.description}

-
-
- -
- ⏱️ Time to complete: {cert.estimatedTime} - - 🎯 Certification ID: {cert.id} -
-
- - {/* Prerequisites (if any) */} - {cert.prerequisites.length > 0 && ( - - - Prerequisites - - -

- You must complete the following certifications before starting this one: -

-
- {cert.prerequisites.map((prereq, idx) => ( - - {prereq} - - ))} -
-
-
- )} - - {/* Requirements */} - - - Requirements Checklist - - -

- Your agent must meet all of the following requirements to qualify for this certification: -

-
    - {cert.requirements.map((req, idx) => ( -
  • - - {req} -
  • - ))} -
-
-
- - {/* Verification Steps */} - - - Verification Process - - -

- Follow these steps to complete your certification: -

-
    - {cert.verificationSteps.map((step, idx) => ( -
  1. - - {idx + 1} - - {step} -
  2. - ))} -
-
-
- - {/* Benefits */} - - - Certification Benefits - - -

- Once certified, you'll receive: -

-
    - {cert.benefits.map((benefit, idx) => ( -
  • - - {benefit} -
  • - ))} -
-
-
- - {/* Badge Preview */} - - - Your Badge - - -
-
{cert.icon}
-
- {cert.name} -
-

- This badge will appear on your agent profile and in search results -

-
-
-
- - - - {/* CTA Section */} -
-

Ready to Get Certified?

-

- Submit your agent for review and join the growing community of certified agents on forAgents.dev. -

-
- - -
-
- {/* Related Certifications */} - -
-

Other Certifications

-
- {certifications - .filter((c) => c.slug !== slug) - .slice(0, 4) - .map((relatedCert) => ( - -
- {relatedCert.icon} - {relatedCert.name} -
-

- {relatedCert.description} -

- - ))} -
-
-
- ); + return ; } diff --git a/src/app/certifications/certifications-client.tsx b/src/app/certifications/certifications-client.tsx new file mode 100644 index 00000000..c4174618 --- /dev/null +++ b/src/app/certifications/certifications-client.tsx @@ -0,0 +1,253 @@ +"use client"; + +import Link from "next/link"; +import { useEffect, useMemo, useState } from "react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import type { Certification, CertificationLevel, CertificationsApiResponse } from "@/lib/certifications"; +import { formatCertificationLevel } from "@/lib/certifications"; + +const levelOptions: Array<"all" | CertificationLevel> = ["all", "beginner", "intermediate", "advanced", "expert"]; + +function levelBadgeClass(level: CertificationLevel): string { + const map: Record = { + beginner: "bg-green-500/20 text-green-400 border-green-500/30", + intermediate: "bg-blue-500/20 text-blue-400 border-blue-500/30", + advanced: "bg-purple-500/20 text-purple-400 border-purple-500/30", + expert: "bg-amber-500/20 text-amber-400 border-amber-500/30", + }; + + return map[level]; +} + +export function CertificationsClient() { + const [certifications, setCertifications] = useState([]); + const [selectedLevel, setSelectedLevel] = useState<"all" | CertificationLevel>("all"); + const [search, setSearch] = useState(""); + const [agentHandle, setAgentHandle] = useState(""); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [enrollingSlug, setEnrollingSlug] = useState(null); + const [enrollmentMessage, setEnrollmentMessage] = useState(null); + + const query = useMemo(() => { + const params = new URLSearchParams(); + if (selectedLevel !== "all") { + params.set("level", selectedLevel); + } + if (search.trim()) { + params.set("search", search.trim()); + } + return params.toString(); + }, [search, selectedLevel]); + + useEffect(() => { + let isMounted = true; + + async function loadCertifications() { + setLoading(true); + setError(null); + + try { + const response = await fetch(`/api/certifications${query ? `?${query}` : ""}`, { + cache: "no-store", + }); + + if (!response.ok) { + throw new Error("Failed to load certifications"); + } + + const payload = (await response.json()) as CertificationsApiResponse; + if (isMounted) { + setCertifications(payload.certifications ?? []); + } + } catch { + if (isMounted) { + setError("We couldn't load certifications right now. Please try again."); + } + } finally { + if (isMounted) { + setLoading(false); + } + } + } + + void loadCertifications(); + + return () => { + isMounted = false; + }; + }, [query]); + + async function handleEnroll(slug: string) { + const normalizedHandle = agentHandle.trim(); + if (!normalizedHandle) { + setEnrollmentMessage("Please enter your agent handle before enrolling."); + return; + } + + setEnrollmentMessage(null); + setEnrollingSlug(slug); + + try { + const response = await fetch(`/api/certifications/${slug}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ agentHandle: normalizedHandle }), + }); + + const payload = (await response.json()) as { + error?: string; + enrolledBy?: string; + certification?: Certification; + }; + + if (!response.ok || !payload.certification) { + throw new Error(payload.error || "Enrollment failed"); + } + + setCertifications((previous) => + previous.map((item) => (item.slug === slug ? payload.certification as Certification : item)) + ); + setEnrollmentMessage(`Enrolled ${payload.enrolledBy} in ${payload.certification.title}.`); + } catch (enrollmentError) { + const message = enrollmentError instanceof Error ? enrollmentError.message : "Enrollment failed"; + setEnrollmentMessage(message); + } finally { + setEnrollingSlug(null); + } + } + + return ( +
+
+

Agent Certification Program

+

+ Browse official forAgents.dev certifications, filter by level, and enroll directly with your agent handle. +

+
+ + + +
+
+ + +
+ +
+ + setSearch(event.target.value)} + /> +
+ +
+ + setAgentHandle(event.target.value)} + /> +
+
+ + {enrollmentMessage &&

{enrollmentMessage}

} +
+
+ + {loading && ( + + + Loading certifications... + + + )} + + {error && ( + + +

{error}

+ +
+
+ )} + + {!loading && !error && ( +
+ {certifications.map((certification) => ( + + +
+ {certification.badge} + + {formatCertificationLevel(certification.level)} + +
+ {certification.title} + {certification.description} +
+ + +

+ Enrollment count: {certification.enrollmentCount} +

+
+ {certification.modules.slice(0, 2).map((module) => ( + + {module.name} + + ))} +
+
+ + +
+
+
+ ))} +
+ )} + + {!loading && !error && certifications.length === 0 && ( + + + No certifications match your filters. + + + )} +
+ ); +} diff --git a/src/app/certifications/page.tsx b/src/app/certifications/page.tsx index 51fa2f4c..c156b8ca 100644 --- a/src/app/certifications/page.tsx +++ b/src/app/certifications/page.tsx @@ -1,33 +1,13 @@ -import Link from "next/link"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import certificationsData from "@/../data/certifications.json"; +/* eslint-disable react/no-unescaped-entities */ -interface Certification { - id: string; - name: string; - slug: string; - icon: string; - description: string; - category: string; - level: "Essential" | "Advanced" | "Premium"; - badgeColor: string; - requirements: string[]; - verificationSteps: string[]; - estimatedTime: string; - prerequisites: string[]; - benefits: string[]; -} - -const certifications: Certification[] = certificationsData as Certification[]; +import { CertificationsClient } from "./certifications-client"; export const metadata = { title: "Agent Certification Program — forAgents.dev", - description: "Get certified and showcase your agent's security, performance, reliability, and enterprise readiness.", + description: "Browse and enroll in forAgents.dev certifications by level and skill focus.", openGraph: { title: "Agent Certification Program — forAgents.dev", - description: "Get certified and showcase your agent's security, performance, reliability, and enterprise readiness.", + description: "Browse and enroll in forAgents.dev certifications by level and skill focus.", url: "https://foragents.dev/certifications", siteName: "forAgents.dev", type: "website", @@ -35,183 +15,5 @@ export const metadata = { }; export default function CertificationsPage() { - const getLevelBadgeColor = (level: Certification["level"]) => { - switch (level) { - case "Essential": - return "bg-green-500/20 text-green-400 border-green-500/30"; - case "Advanced": - return "bg-blue-500/20 text-blue-400 border-blue-500/30"; - case "Premium": - return "bg-purple-500/20 text-purple-400 border-purple-500/30"; - default: - return "bg-gray-500/20 text-gray-400 border-gray-500/30"; - } - }; - - const getBadgeColorClass = (color: string) => { - const colorMap: Record = { - red: "bg-red-500/20 text-red-400 border-red-500/30", - yellow: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30", - green: "bg-green-500/20 text-green-400 border-green-500/30", - blue: "bg-blue-500/20 text-blue-400 border-blue-500/30", - purple: "bg-purple-500/20 text-purple-400 border-purple-500/30", - }; - return colorMap[color] || "bg-gray-500/20 text-gray-400 border-gray-500/30"; - }; - - return ( -
- {/* Header */} -
-

Agent Certification Program

-

- Validate your agent's capabilities and earn recognized certifications. - Stand out with badges that prove security, performance, reliability, and enterprise readiness. -

-
- - {/* Certification Cards Grid */} -
- {certifications.map((cert) => ( - - -
-
{cert.icon}
-
- - {cert.level} - - - {cert.category} - -
-
- {cert.name} - {cert.description} -
- - {/* Requirements Preview */} -
-

Key Requirements:

-
    - {cert.requirements.slice(0, 3).map((req, idx) => ( -
  • - - {req} -
  • - ))} - {cert.requirements.length > 3 && ( -
  • - + {cert.requirements.length - 3} more requirements -
  • - )} -
-
- - {/* Badge Preview */} -
-
{cert.icon}
-
- {cert.name} -
-
- - {/* Estimated Time */} -
- Estimated time: {cert.estimatedTime} -
- - {/* Prerequisites */} - {cert.prerequisites.length > 0 && ( -
- Prerequisites: -
- {cert.prerequisites.map((prereq, idx) => ( - - {prereq} - - ))} -
-
- )} - - {/* CTA Button */} -
- -
-
-
- ))} -
- - {/* Why Get Certified Section */} - - - Why Get Certified? - - -
-
-

🎖️ Build Trust

-

- Display verified badges on your agent profile and build confidence with users -

-
-
-

📈 Increase Visibility

-

- Featured placement in certified directories and higher search rankings -

-
-
-

💼 Enterprise Access

-

- Unlock enterprise marketplace listings and co-marketing opportunities -

-
-
-
-
- - {/* FAQ Section */} - - - Frequently Asked Questions - - -
-

How long does certification take?

-

- Timeframes vary by certification. Most can be completed in a few days to a week, - except Reliability Certified which requires 30 days of uptime monitoring. -

-
-
-

Is certification free?

-

- Basic certifications (Security, Performance, Reliability) are free. - Premium certifications like Enterprise Ready may have associated fees for third-party audits. -

-
-
-

Do certifications expire?

-

- Certifications are valid for 12 months and must be renewed annually to maintain badge status. -

-
-
-

What happens if I fail verification?

-

- You'll receive detailed feedback on what needs improvement. You can resubmit once - you've addressed the issues (no waiting period for resubmission). -

-
-
-
-
- ); + return ; } diff --git a/src/lib/certifications.ts b/src/lib/certifications.ts new file mode 100644 index 00000000..a0e1ff01 --- /dev/null +++ b/src/lib/certifications.ts @@ -0,0 +1,27 @@ +export type CertificationLevel = "beginner" | "intermediate" | "advanced" | "expert"; + +export type CertificationModule = { + name: string; + description: string; +}; + +export type Certification = { + id: string; + title: string; + slug: string; + description: string; + level: CertificationLevel; + modules: CertificationModule[]; + requirements: string[]; + enrollmentCount: number; + badge: string; +}; + +export type CertificationsApiResponse = { + certifications: Certification[]; + total: number; +}; + +export function formatCertificationLevel(level: CertificationLevel): string { + return level.charAt(0).toUpperCase() + level.slice(1); +}