Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'media.beehiiv.com',
port: '',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'media.licdn.com',
port: '',
pathname: '/**',
},
],
},
/* config options here */
};

Expand Down
Binary file not shown.
12 changes: 11 additions & 1 deletion src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Metadata } from 'next'
import { getPostBySlug, compileMDXContent } from '@/lib/mdx'
import { getPostBySlug, compileMDXContent, getAllPosts } from '@/lib/mdx'
import PostHeader from '@/components/blog/PostHeader'
import PostNavigation from '@/components/blog/PostNavigation'

interface BlogPostPageProps {
params: {
Expand All @@ -20,11 +21,20 @@ export async function generateMetadata({ params }: BlogPostPageProps): Promise<M
export default async function BlogPostPage({ params }: BlogPostPageProps) {
const post = await getPostBySlug(params.slug)
const content = await compileMDXContent(post.content)

// Get all posts and find the current post's index
const allPosts = await getAllPosts()
const currentIndex = allPosts.findIndex(p => p.slug === params.slug)

// Get previous and next posts
const previousPost = currentIndex < allPosts.length - 1 ? allPosts[currentIndex + 1] : null
const nextPost = currentIndex > 0 ? allPosts[currentIndex - 1] : null

return (
<article className="prose prose-invert max-w-none">
<PostHeader post={post} />
{content}
<PostNavigation previousPost={previousPost} nextPost={nextPost} />
</article>
)
}
11 changes: 10 additions & 1 deletion src/app/blog/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Metadata } from 'next'
import Link from 'next/link'

export const metadata: Metadata = {
title: 'Blog | Pythoness Programmer',
Expand All @@ -13,7 +14,15 @@ export default function BlogLayout({
return (
<div className="min-h-screen bg-gradient-to-b from-brand-green-dark to-brand-purple-dark">
<div className="max-w-4xl mx-auto px-4 py-16">
<h1 className="text-4xl font-display text-white mb-8">Blog</h1>
<h1 className="text-4xl font-display text-white mb-8">
<Link
href="/blog"
className="hover:underline hover:text-brand-green-accent transition-colors"
aria-label="Go to all blog posts"
>
Blog
</Link>
</h1>
<div className="prose prose-invert max-w-none">
{children}
</div>
Expand Down
31 changes: 7 additions & 24 deletions src/app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,19 @@
import { Metadata } from 'next'
import { getAllPosts } from '@/lib/mdx'
import PostCard from '@/components/blog/PostCard'
import BlogContent from '@/components/blog/BlogContent'
import { Suspense } from 'react'

export const metadata: Metadata = {
title: 'Blog | Pythoness Programmer',
description: 'Technical articles, coding tips, and industry insights from Pythoness Programmer.',
title: 'Blog | The Pythoness Programmer',
description: 'Thoughts on technology, accessibility, and the human experience.',
}

export default async function BlogPage() {
const posts = await getAllPosts()

return (
<div className="space-y-8">
<div className="prose prose-invert max-w-none">
<p className="text-white/80">
Welcome to the Pythoness Programmer blog! Here you&apos;ll find technical articles,
coding tips, and industry insights to help you on your programming journey.
</p>
<p className="text-lg text-brand-purple-dark/80 mb-8">
Let&apos;s explore the world of tech together!
</p>
</div>

<div
className="grid gap-6 md:grid-cols-2"
role="feed"
aria-label="Blog posts"
>
{posts.map((post) => (
<PostCard key={post.slug} post={post} />
))}
</div>
</div>
<Suspense fallback={<div>Loading blog...</div>}>
<BlogContent posts={posts} />
</Suspense>
)
}
73 changes: 73 additions & 0 deletions src/app/mindful-automation/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Metadata } from 'next'
import Link from 'next/link'

export const metadata: Metadata = {
title: 'Mindful Automation Resources',
description: 'Download worksheets and resources for creating mindful automation systems that work with your brain.',
}

export default function MindfulAutomationPage() {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-8">Mindful Automation Resources</h1>

<div className="grid gap-8 md:grid-cols-2">
<div className="bg-white p-6 rounded-lg shadow-md">
<h2 className="text-2xl font-semibold mb-4">Mindful Automation Documentation</h2>
<p className="mb-4">
A comprehensive template for documenting your automation systems in a way that works with your brain.
</p>
<Link
href="/mindful-automation/Mindful Automation Documentation Template.pdf"
className="inline-block bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 transition-colors"
>
Download Template
</Link>
</div>

<div className="bg-white p-6 rounded-lg shadow-md">
<h2 className="text-2xl font-semibold mb-4">Error-Proofing Your Systems</h2>
<p className="mb-4">
Create resilient automation systems with clear documentation and effective error handling.
</p>
<Link
href="/mindful-automation/error-proofing-worksheet.pdf"
className="inline-block bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 transition-colors"
>
Download Worksheet
</Link>
</div>
</div>

<div className="mt-12">
<h2 className="text-2xl font-semibold mb-4">Additional Resources</h2>
<div className="grid gap-4">
<Link
href="https://open.spotify.com/show/0VLZyZrD50Pk9JcyJL2AdX"
className="text-blue-600 hover:text-blue-800"
target="_blank"
rel="noopener noreferrer"
>
Listen on Spotify
</Link>
<Link
href="https://www.youtube.com/playlist?list=PLl8kW5pVKC-p8kzPChiNPoEHkfi_kR1LA"
className="text-blue-600 hover:text-blue-800"
target="_blank"
rel="noopener noreferrer"
>
Watch on YouTube
</Link>
<Link
href="https://pythoness.substack.com/"
className="text-blue-600 hover:text-blue-800"
target="_blank"
rel="noopener noreferrer"
>
Read on Substack
</Link>
</div>
</div>
</div>
)
}
1 change: 1 addition & 0 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export default function Footer() {
</FooterSection>
<FooterSection title="Read">
<ul className="space-y-1" role="list" aria-label="Read options">
<li><Link href="/blog" className={footerLinkClass}>Blog</Link></li>
<li><Link href="https://pythoness.beehiiv.com/" target="_blank" rel="noopener noreferrer" className={footerLinkClass}>Newsletter</Link></li>
</ul>
</FooterSection>
Expand Down
68 changes: 28 additions & 40 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import Link from 'next/link'
export default function Header() {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [isWorkMenuOpen, setIsWorkMenuOpen] = useState(false)
const [isNewsletterMenuOpen, setIsNewsletterMenuOpen] = useState(false)
const pathname = usePathname()
const isHomePage = pathname === '/'
const workMenuRef = useRef<HTMLDivElement>(null)
const newsletterMenuRef = useRef<HTMLDivElement>(null)

const toggleMenu = () => {
setIsMenuOpen(!isMenuOpen)
Expand Down Expand Up @@ -61,6 +59,12 @@ export default function Header() {
role="navigation"
aria-label="Primary navigation"
>
<Link
href="/blog"
className="text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2"
>
Blog
</Link>
<Link
href={getNavHref('#services')}
className="text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2"
Expand All @@ -77,7 +81,7 @@ export default function Header() {
onClick={() => setIsWorkMenuOpen(!isWorkMenuOpen)}
onKeyDown={e => handleDropdownKeyDown(e, setIsWorkMenuOpen)}
>
Work
Resources
<svg className="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
Expand All @@ -96,50 +100,29 @@ export default function Header() {
className="block px-4 py-2 text-sm text-white hover:text-brand-green-accent hover:bg-white/10"
onClick={() => setIsWorkMenuOpen(!isWorkMenuOpen)}
>
<span role="img" aria-label="Podcast microphone">🎙️</span>{' '}
NotebookLM Podcast
</Link>
</div>
</div>
</div>
<div className="relative" ref={newsletterMenuRef} onBlur={e => handleBlur(e, setIsNewsletterMenuOpen, newsletterMenuRef)}>
<button
type="button"
aria-haspopup="menu"
aria-expanded={isNewsletterMenuOpen}
aria-controls="newsletter-menu"
className="text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2 inline-flex items-center"
onClick={() => setIsNewsletterMenuOpen(!isNewsletterMenuOpen)}
onKeyDown={e => handleDropdownKeyDown(e, setIsNewsletterMenuOpen)}
>
Newsletter
<svg className="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
<div
id="newsletter-menu"
role="menu"
className={`absolute left-0 mt-2 w-48 bg-brand-green-dark/95 backdrop-blur-sm rounded-lg shadow-lg transition-all duration-200 ${isNewsletterMenuOpen ? 'opacity-100 visible' : 'opacity-0 invisible'}`}
>
<div className="py-2">
<Link
href="/digital-spring-cleaning"
role="menuitem"
tabIndex={isNewsletterMenuOpen ? 0 : -1}
tabIndex={isWorkMenuOpen ? 0 : -1}
className="block px-4 py-2 text-sm text-white hover:text-brand-green-accent hover:bg-white/10"
onClick={() => setIsNewsletterMenuOpen(!isNewsletterMenuOpen)}
onClick={() => setIsWorkMenuOpen(!isWorkMenuOpen)}
>
<span role="img" aria-label="Download worksheet">📥</span>{' '}
GRIT Digital Cleaning
</Link>
<Link
href="https://pythoness.beehiiv.com/subscribe"
target="_blank"
role="menuitem"
tabIndex={isNewsletterMenuOpen ? 0 : -1}
tabIndex={isWorkMenuOpen ? 0 : -1}
className="block px-4 py-2 text-sm text-white hover:text-brand-green-accent hover:bg-white/10"
onClick={() => setIsNewsletterMenuOpen(!isNewsletterMenuOpen)}
onClick={() => setIsWorkMenuOpen(!isWorkMenuOpen)}
>
Subscribe
<span role="img" aria-label="Newsletter email">📧</span>{' '}
Subscribe to Newsletter
</Link>
</div>
</div>
Expand Down Expand Up @@ -183,7 +166,7 @@ export default function Header() {
<div
id="mobile-menu"
className={`
absolute left-0 top-20 w-full
fixed left-0 top-20 w-full
bg-brand-green-dark/95 backdrop-blur-sm
md:hidden
z-50
Expand All @@ -194,6 +177,13 @@ export default function Header() {
aria-label="Mobile navigation"
>
<div className="flex flex-col space-y-4 p-6">
<Link
href="/blog"
className="text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2 py-1"
onClick={() => setIsMenuOpen(false)}
>
Blog
</Link>
<Link
href={getNavHref('#services')}
className="text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2 py-1"
Expand All @@ -202,26 +192,23 @@ export default function Header() {
Services
</Link>
<div className="space-y-2">
<p className="text-white/80 text-sm px-2">Work</p>
<p className="text-white/80 text-sm px-2">Resources</p>
<div className="pl-6 space-y-1">
<Link
href="https://pythoness.substack.com/podcast"
target="_blank"
className="block text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2 py-1"
onClick={() => setIsMenuOpen(false)}
>
<span role="img" aria-label="Podcast microphone">🎙️</span>{' '}
NotebookLM Podcast
</Link>
</div>
</div>
<div className="space-y-2">
<p className="text-white/80 text-sm px-2">Newsletter</p>
<div className="pl-6 space-y-1">
<Link
href="/digital-spring-cleaning"
className="block text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2 py-1"
onClick={() => setIsMenuOpen(false)}
>
<span role="img" aria-label="Download worksheet">📥</span>{' '}
GRIT Digital Cleaning
</Link>
<Link
Expand All @@ -230,7 +217,8 @@ export default function Header() {
className="block text-white hover:text-brand-green-accent transition-colors focus:outline-none focus:ring-2 focus:ring-brand-green-accent focus:ring-offset-2 rounded-lg px-2 py-1"
onClick={() => setIsMenuOpen(false)}
>
Subscribe
<span role="img" aria-label="Newsletter email">📧</span>{' '}
Subscribe to Newsletter
</Link>
</div>
</div>
Expand Down
Loading