diff --git a/src/app/stories/[slug]/page.tsx b/src/app/stories/[slug]/page.tsx
new file mode 100644
index 0000000..a3bd4b3
--- /dev/null
+++ b/src/app/stories/[slug]/page.tsx
@@ -0,0 +1,156 @@
+import { notFound } from 'next/navigation';
+import Link from 'next/link';
+import { loadStories } from '@/utils/mdxLoader';
+import { formatReadingTime } from '@/utils/readingTime';
+
+interface StoryPageProps {
+ params: {
+ slug: string;
+ };
+}
+
+import fs from 'fs';
+
+export async function generateStaticParams() {
+ const storiesDirectory = process.cwd() + '/src/content/stories';
+
+ try {
+ const filenames = fs.readdirSync(storiesDirectory);
+ return filenames.map((filename: string) => ({
+ slug: filename.replace(/\.mdx$/, ''),
+ }));
+ } catch {
+ return [];
+ }
+}
+
+export async function generateMetadata({ params }: StoryPageProps) {
+ const story = await getStoryBySlug(params.slug);
+
+ if (!story) {
+ return {
+ title: 'Story Not Found',
+ };
+ }
+
+ return {
+ title: `${story.title} | Mainasara Tsowa`,
+ description: story.excerpt,
+ openGraph: {
+ title: `${story.title} | Mainasara Tsowa`,
+ description: story.excerpt,
+ url: `https://mainasara.dev/stories/${params.slug}`,
+ },
+ twitter: {
+ title: `${story.title} | Mainasara Tsowa`,
+ description: story.excerpt,
+ },
+ };
+}
+
+async function getStoryBySlug(slug: string) {
+ const stories = await loadStories();
+ return stories.find(story => story.id === slug.replace(/[^a-zA-Z0-9]/g, '-'));
+}
+
+export default async function StoryPage({ params }: StoryPageProps) {
+ const story = await getStoryBySlug(params.slug);
+
+ if (!story) {
+ notFound();
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {story.title}
+
+
+
+
+ {formatReadingTime(story.readTime)} read
+
+
+ {new Date(story.createdAt).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ })}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/thoughts/[slug]/page.tsx b/src/app/thoughts/[slug]/page.tsx
new file mode 100644
index 0000000..ec30cee
--- /dev/null
+++ b/src/app/thoughts/[slug]/page.tsx
@@ -0,0 +1,159 @@
+import { notFound } from 'next/navigation';
+import Link from 'next/link';
+import { loadThoughts } from '@/utils/mdxLoader';
+import { formatReadingTime } from '@/utils/readingTime';
+
+interface ThoughtPageProps {
+ params: {
+ slug: string;
+ };
+}
+
+import fs from 'fs';
+
+export async function generateStaticParams() {
+ const thoughtsDirectory = process.cwd() + '/src/content/thoughts';
+
+ try {
+ const filenames = fs.readdirSync(thoughtsDirectory);
+ return filenames.map((filename: string) => ({
+ slug: filename.replace(/\.mdx$/, ''),
+ }));
+ } catch {
+ return [];
+ }
+}
+
+export async function generateMetadata({ params }: ThoughtPageProps) {
+ const thought = await getThoughtBySlug(params.slug);
+
+ if (!thought) {
+ return {
+ title: 'Thought Not Found',
+ };
+ }
+
+ return {
+ title: `${thought.title} | Mainasara Tsowa`,
+ description: thought.excerpt,
+ openGraph: {
+ title: `${thought.title} | Mainasara Tsowa`,
+ description: thought.excerpt,
+ url: `https://mainasara.dev/thoughts/${params.slug}`,
+ },
+ twitter: {
+ title: `${thought.title} | Mainasara Tsowa`,
+ description: thought.excerpt,
+ },
+ };
+}
+
+async function getThoughtBySlug(slug: string) {
+ const thoughts = await loadThoughts();
+ return thoughts.find(thought => thought.id === slug.replace(/[^a-zA-Z0-9]/g, '-'));
+}
+
+export default async function ThoughtPage({ params }: ThoughtPageProps) {
+ const thought = await getThoughtBySlug(params.slug);
+
+ if (!thought) {
+ notFound();
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {thought.title}
+
+
+
+
+ {thought.category}
+
+
+ {formatReadingTime(thought.readTime)} read
+
+
+ {new Date(thought.createdAt).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ })}
+
+
+
+
+
+
+ {thought.content}
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file