Consistent hero layouts, activity page redesign, new content, and YouTube embeds
- Product pages (Code Editor, Robot Simulator): text-left/image-right hero layout - Related resources capped at 3 items on all product pages - Making Music activities renamed to I, II, III - New Maze I/II/III and Sumo I/II/III difficulty-graded activities - YouTube demo videos restored on 12 activity pages from old site - Activity pages: two-column hero with coding skills & rover concepts tags - Blog/news pages: same two-column hero layout with date - Resource type extended with codingSkills, roverConcepts, tags fields - Removed raw "Relevant Coding Skills/Rover Concepts" text from activity MDX Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ const mdxComponents = {
|
||||
};
|
||||
|
||||
const NEWS_CATEGORIES = ["News & Updates", "Customer Stories"];
|
||||
const ACTIVITY_CATEGORIES = ["Activities", "Simulator Activities"];
|
||||
|
||||
const CATEGORY_SLUGS: Record<string, {
|
||||
name: string;
|
||||
@@ -141,72 +142,175 @@ export default async function ResourcePage({ params }: ResourcePageProps) {
|
||||
const isNews = resource.categories.some((cat) =>
|
||||
NEWS_CATEGORIES.includes(cat)
|
||||
);
|
||||
const isActivity = resource.categories.some((cat) =>
|
||||
ACTIVITY_CATEGORIES.includes(cat)
|
||||
);
|
||||
|
||||
if (isActivity) {
|
||||
return (
|
||||
<>
|
||||
{/* Activity Hero */}
|
||||
<section className="bg-white py-16 sm:py-24">
|
||||
<Container>
|
||||
<Button
|
||||
href="/resources"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mb-8"
|
||||
>
|
||||
← Back to Resources
|
||||
</Button>
|
||||
<div className="grid items-center gap-12 md:grid-cols-2">
|
||||
<div>
|
||||
<div className="mb-3 flex flex-wrap gap-1.5">
|
||||
{resource.categories.map((cat) => (
|
||||
<span
|
||||
key={cat}
|
||||
className="rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{cat}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||
{resource.title}
|
||||
</h1>
|
||||
{(resource.codingSkills.length > 0 || resource.roverConcepts.length > 0) && (
|
||||
<div className="mt-6 flex flex-wrap gap-x-8 gap-y-4">
|
||||
{resource.codingSkills.length > 0 && (
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
||||
Coding Skills
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{resource.codingSkills.map((skill) => (
|
||||
<span
|
||||
key={skill}
|
||||
className="rounded-full border border-brand/30 bg-brand/5 px-2.5 py-0.5 text-xs font-medium text-foreground"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{resource.roverConcepts.length > 0 && (
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
||||
Rover Concepts
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{resource.roverConcepts.map((concept) => (
|
||||
<span
|
||||
key={concept}
|
||||
className="rounded-full border border-foreground/20 bg-foreground/5 px-2.5 py-0.5 text-xs font-medium text-foreground"
|
||||
>
|
||||
{concept}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{resource.featuredImage && (
|
||||
<div>
|
||||
<Image
|
||||
src={resource.featuredImage}
|
||||
alt={resource.title}
|
||||
width={700}
|
||||
height={700}
|
||||
className="w-full rounded-2xl"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
{/* Activity Content */}
|
||||
<section className="border-t border-border bg-muted py-16">
|
||||
<Container className="max-w-3xl">
|
||||
<article className="mdx-content">
|
||||
<MDXRemote source={resource.content} components={mdxComponents} />
|
||||
</article>
|
||||
<div className="mt-12 border-t border-border pt-8">
|
||||
<Button href="/resources" variant="outline" size="sm">
|
||||
← Return to Resources
|
||||
</Button>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const backHref = isNews ? "/news" : "/resources";
|
||||
const backLabel = isNews ? "Back to News" : "Back to Resources";
|
||||
const returnLabel = isNews ? "Return to News" : "Return to Resources";
|
||||
|
||||
return (
|
||||
<section className="bg-white py-16 sm:py-20">
|
||||
<Container className="max-w-3xl">
|
||||
<Button
|
||||
href={isNews ? "/news" : "/resources"}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="mb-8"
|
||||
>
|
||||
← {isNews ? "Back to News" : "Back to Resources"}
|
||||
</Button>
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<div className="mb-3 flex flex-wrap gap-1.5">
|
||||
{resource.categories.map((cat) => (
|
||||
<span
|
||||
key={cat}
|
||||
className="rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{cat}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
||||
{resource.title}
|
||||
</h1>
|
||||
<p className="mt-3 text-muted-foreground">
|
||||
{new Date(resource.date).toLocaleDateString("en-AU", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Cover Image */}
|
||||
{resource.featuredImage && (
|
||||
<div className="relative mb-10 aspect-[16/9] overflow-hidden rounded-xl">
|
||||
<Image
|
||||
src={resource.featuredImage}
|
||||
alt={resource.title}
|
||||
fill
|
||||
className="object-cover"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* MDX Content */}
|
||||
<article className="mdx-content">
|
||||
<MDXRemote source={resource.content} components={mdxComponents} />
|
||||
</article>
|
||||
|
||||
{/* Return button */}
|
||||
<div className="mt-12 border-t border-border pt-8">
|
||||
<Button
|
||||
href={isNews ? "/news" : "/resources"}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
← {isNews ? "Return to News" : "Return to Resources"}
|
||||
<>
|
||||
{/* Hero */}
|
||||
<section className="bg-white py-16 sm:py-24">
|
||||
<Container>
|
||||
<Button href={backHref} variant="outline" size="sm" className="mb-8">
|
||||
← {backLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
<div className="grid items-center gap-12 md:grid-cols-2">
|
||||
<div>
|
||||
<div className="mb-3 flex flex-wrap gap-1.5">
|
||||
{resource.categories.map((cat) => (
|
||||
<span
|
||||
key={cat}
|
||||
className="rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{cat}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||
{resource.title}
|
||||
</h1>
|
||||
<p className="mt-3 text-muted-foreground">
|
||||
{new Date(resource.date).toLocaleDateString("en-AU", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{resource.featuredImage && (
|
||||
<div>
|
||||
<Image
|
||||
src={resource.featuredImage}
|
||||
alt={resource.title}
|
||||
width={700}
|
||||
height={467}
|
||||
className="w-full rounded-2xl"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
{/* Content */}
|
||||
<section className="border-t border-border bg-muted py-16">
|
||||
<Container className="max-w-3xl">
|
||||
<article className="mdx-content">
|
||||
<MDXRemote source={resource.content} components={mdxComponents} />
|
||||
</article>
|
||||
<div className="mt-12 border-t border-border pt-8">
|
||||
<Button href={backHref} variant="outline" size="sm">
|
||||
← {returnLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user