nicholai-work-2026/src/content.config.ts
Nicholai 058655f23d Add blog components and enhance blog functionality
- Introduced new BlogCard and BlogFilters components for improved blog post presentation and filtering capabilities.
- Updated content configuration to include fields for featured posts, categories, and tags.
- Enhanced the blog index page to display a featured post and editor's picks, along with a filterable grid for latest posts.
- Added a new blog entry on the G-Star Raw Olympics campaign with associated metadata for better categorization and tagging.
2025-12-08 03:58:22 -07:00

110 lines
3.1 KiB
TypeScript

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
// Load Markdown and MDX files in the `src/content/blog/` directory.
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
// Type-check frontmatter using a schema
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
// Transform string to Date object
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
heroImage: image().optional(),
// Blog hub fields
featured: z.boolean().optional().default(false),
category: z.string().optional(),
tags: z.array(z.string()).optional(),
}),
});
const sections = defineCollection({
loader: glob({ base: './src/content/sections', pattern: '**/*.{md,mdx}' }),
schema: z.object({
// Hero section
headlineLine1: z.string().optional(),
headlineLine2: z.string().optional(),
portfolioYear: z.string().optional(),
location: z.string().optional(),
locationLabel: z.string().optional(),
bio: z.string().optional(),
// Experience section
sectionTitle: z.string().optional(),
sectionSubtitle: z.string().optional(),
sectionLabel: z.string().optional(),
description: z.string().optional(),
// Experience entries
entries: z.array(z.object({
systemId: z.string(),
status: z.string(),
dates: z.string(),
company: z.string(),
role: z.string(),
tags: z.array(z.string()).optional(),
description: z.string(),
achievements: z.array(z.object({
label: z.string(),
text: z.string(),
})).optional(),
link: z.object({
url: z.string(),
text: z.string(),
}).optional(),
})).optional(),
// Skills entries
skills: z.array(z.object({
id: z.string(),
domain: z.string(),
tools: z.string(),
proficiency: z.string(),
})).optional(),
// Featured project
role: z.string().optional(),
client: z.string().optional(),
year: z.string().optional(),
region: z.string().optional(),
projectTitle: z.string().optional(),
projectSubtitle: z.string().optional(),
projectDescription: z.string().optional(),
stats: z.array(z.object({
label: z.string(),
value: z.string(),
})).optional(),
videoUrl: z.string().optional(),
linkUrl: z.string().optional(),
}),
});
const pages = defineCollection({
loader: glob({ base: './src/content/pages', pattern: '**/*.{md,mdx}' }),
schema: z.object({
pageTitleLine1: z.string().optional(),
pageTitleLine2: z.string().optional(),
availabilityText: z.string().optional(),
email: z.string().optional(),
location: z.string().optional(),
locationCountry: z.string().optional(),
coordinates: z.string().optional(),
socialLinks: z.array(z.object({
name: z.string(),
url: z.string(),
})).optional(),
formLabels: z.object({
name: z.string().optional(),
email: z.string().optional(),
subject: z.string().optional(),
message: z.string().optional(),
submit: z.string().optional(),
transmissionUplink: z.string().optional(),
}).optional(),
subjectOptions: z.array(z.object({
value: z.string(),
label: z.string(),
})).optional(),
}),
});
export const collections = { blog, sections, pages };