Files
codetutor/frontend/src/app/patterns/[slug]/page.tsx

216 lines
6.7 KiB
TypeScript

import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { getPatternTutorial } from "@/lib/api";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { CodeBlock } from "@/components/ui/code-block";
import { Markdown } from "@/components/ui/markdown";
import { Callout } from "@/components/ui/callout";
import { Badge } from "@/components/ui/badge";
import {
CommonMistakesList,
LearningProgression,
PatternVariations,
RecognitionSignals,
RelatedPatterns,
} from "@/components/patterns";
import { PatternVisualization } from "@/components/visualization";
import { TwoPointersVisualization, PrefixSumVisualization } from "@/components/visualizations-new";
import { twoSumAlgorithm } from "@/content/algorithms/two-sum";
import { slidingWindowAlgorithm } from "@/content/algorithms/sliding-window";
import { binarySearchAlgorithm } from "@/content/algorithms/binary-search";
import { prefixSumAlgorithm } from "@/content/algorithms/prefix-sum";
interface PageProps {
params: Promise<{ slug: string }>;
}
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { slug } = await params;
try {
const pattern = await getPatternTutorial(slug);
const description =
pattern.description ||
`Learn the ${pattern.name} pattern with ${pattern.question_count} practice problems.`;
return {
title: `${pattern.name} Pattern`,
description,
openGraph: {
title: `${pattern.name} Pattern | CodeTutor`,
description,
type: "article",
},
};
} catch {
return {
title: "Pattern Not Found",
};
}
}
export default async function PatternDetailPage({ params }: PageProps) {
const { slug } = await params;
let pattern;
try {
pattern = await getPatternTutorial(slug);
} catch {
notFound();
}
const difficultyLabels = ["Beginner", "Easy", "Intermediate", "Advanced", "Expert"];
const difficultyLabel = pattern.difficulty_level
? difficultyLabels[pattern.difficulty_level - 1] || "Intermediate"
: null;
return (
<div className="max-w-4xl mx-auto space-y-8">
{/* Header */}
<div>
<div className="flex items-center gap-3 mb-2">
<h1 className="text-3xl font-bold">{pattern.name}</h1>
{difficultyLabel && (
<Badge variant="outline">{difficultyLabel}</Badge>
)}
</div>
<p className="text-[var(--muted-foreground)]">
{pattern.question_count} questions using this pattern
</p>
</div>
{/* Description */}
{pattern.description && (
<Card>
<CardHeader>
<CardTitle>What is {pattern.name}?</CardTitle>
</CardHeader>
<CardContent>
<Markdown>{pattern.description}</Markdown>
</CardContent>
</Card>
)}
{/* Metaphor - The relatable analogy */}
{pattern.metaphor && (
<Callout variant="insight" title="Think of it like this...">
<Markdown>{pattern.metaphor}</Markdown>
</Callout>
)}
{/* Core Concept - The "aha!" insight */}
{pattern.core_concept && (
<Card>
<CardHeader>
<CardTitle>Core Concept</CardTitle>
</CardHeader>
<CardContent>
<Markdown>{pattern.core_concept}</Markdown>
</CardContent>
</Card>
)}
{/* Interactive Visualization */}
{slug === "two-pointers" ? (
<TwoPointersVisualization algorithm={twoSumAlgorithm} />
) : slug === "sliding-window" ? (
<TwoPointersVisualization algorithm={slidingWindowAlgorithm} />
) : slug === "binary-search" ? (
<TwoPointersVisualization algorithm={binarySearchAlgorithm} />
) : slug === "prefix-sum" ? (
<PrefixSumVisualization algorithm={prefixSumAlgorithm} />
) : pattern.visualization_examples && pattern.visualization_examples.length > 0 ? (
<Card>
<CardHeader>
<CardTitle>Interactive Visualization</CardTitle>
</CardHeader>
<CardContent>
<PatternVisualization examples={pattern.visualization_examples} />
</CardContent>
</Card>
) : null}
{/* Static Visualization - ASCII diagram walkthrough (fallback) */}
{pattern.visualization && (
<Card>
<CardHeader>
<CardTitle>Visual Walkthrough</CardTitle>
</CardHeader>
<CardContent>
<Markdown>{pattern.visualization}</Markdown>
</CardContent>
</Card>
)}
{/* Code Template */}
{pattern.code_template && (
<Card>
<CardHeader>
<CardTitle>Code Template</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-[var(--muted-foreground)] mb-4">
Use this skeleton as a starting point for problems using this pattern:
</p>
<CodeBlock
code={pattern.code_template}
language="python"
label="Pattern code template"
/>
</CardContent>
</Card>
)}
{/* Recognition Signals */}
{pattern.recognition_signals && pattern.recognition_signals.length > 0 && (
<RecognitionSignals signals={pattern.recognition_signals} />
)}
{/* When to Use */}
{pattern.when_to_use && (
<Card>
<CardHeader>
<CardTitle>When to Use</CardTitle>
</CardHeader>
<CardContent>
<Markdown>{pattern.when_to_use}</Markdown>
</CardContent>
</Card>
)}
{/* Common Mistakes */}
{pattern.common_mistakes && pattern.common_mistakes.length > 0 && (
<CommonMistakesList mistakes={pattern.common_mistakes} />
)}
{/* Pattern Variations */}
{pattern.variations && pattern.variations.length > 0 && (
<PatternVariations variations={pattern.variations} />
)}
{/* Learning Progression */}
{pattern.learning_progression && (
<LearningProgression progression={pattern.learning_progression} />
)}
{/* Related Patterns */}
<div className="grid md:grid-cols-2 gap-6">
{pattern.prerequisite_patterns && pattern.prerequisite_patterns.length > 0 && (
<RelatedPatterns
title="Prerequisites"
description="Learn these patterns first:"
patterns={pattern.prerequisite_patterns}
/>
)}
{pattern.related_patterns && pattern.related_patterns.length > 0 && (
<RelatedPatterns
title="Related Patterns"
description="Explore similar techniques:"
patterns={pattern.related_patterns}
/>
)}
</div>
</div>
);
}