Skip to content

Commit

Permalink
Merge pull request #625 from zackproser/add-tutorials-to-nav
Browse files Browse the repository at this point in the history
Add tutorials page and add it to navigation
  • Loading branch information
zackproser authored Jan 20, 2025
2 parents f4366fd + 056e5a4 commit f4b7596
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 334 deletions.
77 changes: 77 additions & 0 deletions src/app/tutorials/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react'
import { getAllArticles } from "@/lib/articles"
import { getAllCourses } from "@/lib/courses"
import { BlogPostCard } from '@/components/BlogPostCard'
import { Container } from '@/components/Container'
import { createMetadata } from '@/utils/createMetadata'

export const metadata = createMetadata({
title: 'Hands-On Project-Based Learning',
description: 'Master modern development through practical, hands-on projects and in-depth tutorials',
});

export default async function TutorialsPage() {
const tutorialSlugs = [
'rag-pipeline-tutorial',
'multiple-git-profiles-automated'
]

const courseSlugs = [
'generative-ai-bootcamp'
]

try {
const allArticles = await getAllArticles()
const allCourses = await getAllCourses()

const tutorials = allArticles.filter(article => tutorialSlugs.includes(article.slug))
const courses = allCourses.filter(course => courseSlugs.includes(course.slug))

return (
<Container className="mt-16 sm:mt-32">
<header className="max-w-2xl">
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
Hands-On Project-Based Learning
</h1>
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
There&apos;s no substitute for hands-on learning in software development. Level up your skills through practical, real-world projects and comprehensive tutorials that teach by doing.
</p>
</header>
<div className="mt-16 sm:mt-20">
{tutorials.length > 0 && (
<div className="space-y-20">
<section>
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100">
Featured Tutorials
</h2>
<div className="mt-6 grid grid-cols-1 gap-x-8 gap-y-10 sm:grid-cols-2 lg:grid-cols-3">
{tutorials.map((article) => (
<BlogPostCard key={article.slug} article={article} />
))}
</div>
</section>
</div>
)}

{courses.length > 0 && (
<div className="mt-20 space-y-20">
<section>
<h2 className="text-2xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100">
Featured Courses
</h2>
<div className="mt-6 grid grid-cols-1 gap-x-8 gap-y-10 sm:grid-cols-2 lg:grid-cols-3">
{courses.map((course) => (
<BlogPostCard key={course.slug} article={course} />
))}
</div>
</section>
</div>
)}
</div>
</Container>
)
} catch (error) {
console.error('Error in TutorialsPage:', error)
return null
}
}
19 changes: 17 additions & 2 deletions src/components/BlogPostCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ const StatusBadge = ({ status }) => {
)
}

const PriceBadge = ({ price }) => {
if (!price) return null;

return (
<span className={cn(
"absolute top-4 right-4 inline-flex items-center gap-x-2 rounded-full px-3 py-1 text-sm font-semibold",
"bg-green-600 text-white shadow-lg transition duration-300 ease-in-out hover:bg-green-700",
"ring-2 ring-green-500 ring-offset-2 ring-offset-gray-800"
)}>
${(price / 100).toFixed(2)}
</span>
)
}

const rootPaths = {
collection: '/collections/',
video: '/videos/',
Expand All @@ -49,7 +63,7 @@ export function BlogPostCard({ article }) {
return null;
}

const { slug = '', title = 'Untitled', date = '', description = '', image = wakka, type = 'default', status } = article;
const { slug = '', title = 'Untitled', date = '', description = '', image = wakka, type = 'default', status, price } = article;

const isExternalLink = slug.startsWith('http://') || slug.startsWith('https://');
const href = isExternalLink ? slug : `${rootPaths[type] || rootPaths.default}${slug}`;
Expand All @@ -65,7 +79,7 @@ export function BlogPostCard({ article }) {
}) : '';

return (
<article className="flex flex-col overflow-hidden rounded-lg shadow-lg transition-all duration-300 hover:shadow-xl bg-white dark:bg-zinc-800">
<article className="flex flex-col overflow-hidden rounded-lg shadow-lg transition-all duration-300 hover:shadow-xl bg-white dark:bg-zinc-800 relative">
<LinkComponent {...linkProps} className="group">
<div className="relative w-full">
<Image
Expand All @@ -76,6 +90,7 @@ export function BlogPostCard({ article }) {
height={281}
/>
<div className="absolute inset-0 rounded-t-lg ring-1 ring-inset ring-gray-900/10" />
{price && <PriceBadge price={price} />}
</div>
<div className="flex-1 p-6 flex flex-col justify-between">
<div className="flex-1">
Expand Down
8 changes: 3 additions & 5 deletions src/components/CourseIndex.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { BlogPostCard } from '@/components/BlogPostCard'
import { getAllCourses } from '@/lib/courses'

import { type ArticleWithSlug } from '@/lib/shared-types';
import { getAllCourses, type Course } from '@/lib/courses'

export default async function CourseIndex() {
const courses = await getAllCourses();

return (
<div className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-3">
{courses.map((article: ArticleWithSlug) => (
<BlogPostCard key={article.slug} article={article} />
{courses.map((course: Course) => (
<BlogPostCard key={course.slug} article={course} />
))}
</div>
);
Expand Down
Loading

0 comments on commit f4b7596

Please sign in to comment.