Skip to content

Commit dd980bb

Browse files
authored
Merge pull request #9 from TOM-BOHN/sandbox
feat: Blog posts, VIP navigation, and UX improvements
2 parents 6c787c7 + 8d38507 commit dd980bb

51 files changed

Lines changed: 2360 additions & 229 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/blog/[slug]/page.tsx

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { getBlogPost, getBlogPosts } from '@/lib/blog'
22
import { notFound } from 'next/navigation'
33
import Link from 'next/link'
4+
import { FaMedium } from 'react-icons/fa'
5+
6+
export const dynamicParams = false
47

58
export async function generateStaticParams() {
69
const posts = await getBlogPosts()
@@ -9,8 +12,9 @@ export async function generateStaticParams() {
912
}))
1013
}
1114

12-
export default async function BlogPost({ params }: { params: { slug: string } }) {
13-
const post = await getBlogPost(params.slug)
15+
export default async function BlogPost({ params }: { params: Promise<{ slug: string }> | { slug: string } }) {
16+
const resolvedParams = await Promise.resolve(params)
17+
const post = await getBlogPost(resolvedParams.slug)
1418

1519
if (!post) {
1620
notFound()
@@ -20,33 +24,80 @@ export default async function BlogPost({ params }: { params: { slug: string } })
2024
<div className="container mx-auto px-4 py-12">
2125
<div className="max-w-6xl mx-auto">
2226
<div className="mb-8">
23-
<Link
24-
href="/blog"
25-
className="text-link hover:text-link-hover underline mb-4 inline-block"
26-
>
27-
← Back to Blog
28-
</Link>
2927
<p className="text-sm text-accent mb-4 font-mono">{'>'} SOFTWARE ENGINEER</p>
30-
<h1 className="text-2xl font-semibold mb-4 text-text-primary font-mono">
31-
{'// BLOG'}
32-
</h1>
28+
<div className="flex items-center justify-between mb-6">
29+
<h1 className="text-2xl font-semibold text-text-primary font-mono">
30+
{'// BLOG'}
31+
</h1>
32+
<div className="flex items-center gap-3">
33+
<Link
34+
href="/blog/"
35+
className="px-4 py-2 bg-bg-secondary border border-border rounded-lg hover:bg-accent hover:text-white hover:border-accent transition-colors text-sm font-semibold"
36+
>
37+
← Back to Blog
38+
</Link>
39+
{post.mediumUrl && (
40+
<a
41+
href={post.mediumUrl}
42+
target="_blank"
43+
rel="noopener noreferrer"
44+
className="inline-flex items-center gap-2 px-4 py-2 bg-black text-white rounded-lg hover:bg-gray-800 transition-colors text-sm font-semibold"
45+
>
46+
<FaMedium className="w-4 h-4" />
47+
Read on Medium
48+
</a>
49+
)}
50+
</div>
51+
</div>
3352
</div>
3453

35-
<article className="prose prose-lg max-w-3xl">
36-
<h2 className="text-4xl font-bold mb-4 text-text-primary">{post.title}</h2>
37-
<p className="text-text-secondary text-sm mb-8">
38-
{new Date(post.date).toLocaleDateString('en-US', {
39-
year: 'numeric',
40-
month: 'long',
41-
day: 'numeric',
42-
})}
43-
</p>
54+
<article className="prose prose-lg max-w-6xl">
55+
<div className="mb-4">
56+
<p className="text-text-secondary text-sm">
57+
{new Date(post.date).toLocaleDateString('en-US', {
58+
year: 'numeric',
59+
month: 'long',
60+
day: 'numeric',
61+
})}
62+
</p>
63+
</div>
64+
{post.title.includes(':') ? (
65+
<div className="mb-6">
66+
<h2 className="text-4xl font-bold mb-2 text-text-primary">
67+
{post.title.split(':')[0]}
68+
</h2>
69+
<p className="text-xl text-text-secondary font-medium">
70+
{post.title.split(':').slice(1).join(':').trim()}
71+
</p>
72+
</div>
73+
) : (
74+
<h2 className="text-4xl font-bold mb-6 text-text-primary">{post.title}</h2>
75+
)}
4476
<div
4577
className="blog-content text-text-primary leading-relaxed"
4678
dangerouslySetInnerHTML={{ __html: post.content }}
4779
/>
80+
{post.mediumUrl && (
81+
<div className="mt-6 pt-6 border-t border-border">
82+
<div className="bg-bg-secondary rounded-lg p-6 border border-border">
83+
<p className="text-text-secondary mb-4 text-sm">
84+
This is a summary of the article. The full article continues on Medium with additional details, examples, and insights.
85+
</p>
86+
<a
87+
href={post.mediumUrl}
88+
target="_blank"
89+
rel="noopener noreferrer"
90+
className="inline-flex items-center gap-2 px-6 py-3 bg-black text-white rounded-lg hover:bg-gray-800 transition-colors font-semibold"
91+
>
92+
<FaMedium className="w-5 h-5" />
93+
Continue reading on Medium
94+
</a>
95+
</div>
96+
</div>
97+
)}
4898
</article>
4999
</div>
100+
<div className="pb-12"></div>
50101
</div>
51102
)
52103
}

app/blog/page.tsx

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Link from 'next/link'
22
import { getBlogPosts } from '@/lib/blog'
3+
import { FaMedium } from 'react-icons/fa'
34

45
export default async function Blog() {
56
const posts = await getBlogPosts()
@@ -13,7 +14,7 @@ export default async function Blog() {
1314
{'// BLOG'}
1415
</h1>
1516
<p className="text-text-secondary leading-relaxed">
16-
Thoughts, tutorials, and random musings
17+
Frameworks, insights, and practical approaches to software engineering, data governance, and technical leadership.
1718
</p>
1819
</div>
1920

@@ -31,11 +32,36 @@ export default async function Blog() {
3132
key={post.slug}
3233
className="border border-border rounded-lg p-6 hover:border-accent hover:shadow-lg transition-all bg-bg-secondary"
3334
>
34-
<Link href={`/blog/${post.slug}`}>
35-
<h2 className="text-2xl font-semibold mb-2 text-text-primary hover:text-accent transition-colors">
36-
{post.title}
37-
</h2>
38-
</Link>
35+
<div className="flex items-start justify-between gap-4 mb-2">
36+
<Link href={`/blog/${post.slug}/`} className="flex-1">
37+
{post.title.includes(':') ? (
38+
<div>
39+
<h2 className="text-2xl font-semibold mb-1 text-text-primary hover:text-accent transition-colors">
40+
{post.title.split(':')[0]}
41+
</h2>
42+
<p className="text-lg text-text-secondary font-medium">
43+
{post.title.split(':').slice(1).join(':').trim()}
44+
</p>
45+
</div>
46+
) : (
47+
<h2 className="text-2xl font-semibold mb-2 text-text-primary hover:text-accent transition-colors">
48+
{post.title}
49+
</h2>
50+
)}
51+
</Link>
52+
{post.mediumUrl && (
53+
<a
54+
href={post.mediumUrl}
55+
target="_blank"
56+
rel="noopener noreferrer"
57+
className="flex-shrink-0 p-2 hover:bg-bg-primary rounded transition-colors"
58+
title="Read on Medium"
59+
aria-label="Read on Medium"
60+
>
61+
<FaMedium className="w-5 h-5 text-text-secondary hover:text-black transition-colors" />
62+
</a>
63+
)}
64+
</div>
3965
<p className="text-text-secondary text-sm mb-4">
4066
{new Date(post.date).toLocaleDateString('en-US', {
4167
year: 'numeric',
@@ -47,7 +73,7 @@ export default async function Blog() {
4773
{post.excerpt}
4874
</p>
4975
<Link
50-
href={`/blog/${post.slug}`}
76+
href={`/blog/${post.slug}/`}
5177
className="inline-block mt-4 text-link hover:text-link-hover underline"
5278
>
5379
Read more →

app/certifications/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default async function Certifications() {
1818
</div>
1919

2020
<LearningSection
21+
education={learningData.education}
2122
accomplished={learningData.accomplished}
2223
planning={learningData.planning}
2324
/>

app/contact/page.tsx

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { FaLinkedin, FaEnvelope, FaCopy, FaCheck, FaLock } from 'react-icons/fa'
5+
16
export default function Contact() {
7+
const [copied, setCopied] = useState(false)
8+
const email = 'hello@thomaslbohn.com'
9+
const linkedinUrl = 'https://www.linkedin.com/in/thomaslbohn'
10+
11+
const copyToClipboard = async () => {
12+
try {
13+
await navigator.clipboard.writeText(email)
14+
setCopied(true)
15+
setTimeout(() => setCopied(false), 2000)
16+
} catch (err) {
17+
console.error('Failed to copy email:', err)
18+
}
19+
}
20+
221
return (
322
<div className="container mx-auto px-4 py-12">
423
<div className="max-w-6xl mx-auto">
@@ -8,49 +27,84 @@ export default function Contact() {
827
{'// CONTACT'}
928
</h1>
1029
<p className="text-text-secondary leading-relaxed">
11-
Get in touch with me through any of these channels
30+
I'm always open to discussing new opportunities, technical challenges, or potential collaborations. Reach out through any of the channels below.
1231
</p>
1332
</div>
1433

1534
<div className="space-y-6">
16-
<div className="border border-border rounded-lg p-6 bg-bg-secondary">
17-
<h2 className="text-2xl font-semibold mb-4 text-text-primary">Email</h2>
18-
<p className="text-text-secondary mb-4">
19-
[Your email address will go here]
20-
</p>
35+
{/* Email Section */}
36+
<div className="border border-border rounded-lg p-6 bg-bg-secondary hover:border-accent/50 transition-colors">
37+
<div className="flex items-center gap-3 mb-4">
38+
<div className="p-2 bg-accent/10 rounded-lg">
39+
<FaEnvelope className="w-5 h-5 text-accent" />
40+
</div>
41+
<h2 className="text-2xl font-semibold text-text-primary">Email</h2>
42+
</div>
43+
<div className="flex items-center gap-3 mb-4">
44+
<p className="text-text-primary text-lg font-mono">{email}</p>
45+
<button
46+
onClick={copyToClipboard}
47+
className="p-2 hover:bg-bg-primary rounded transition-colors group"
48+
title="Copy email to clipboard"
49+
aria-label="Copy email to clipboard"
50+
>
51+
{copied ? (
52+
<FaCheck className="w-4 h-4 text-green-500" />
53+
) : (
54+
<FaCopy className="w-4 h-4 text-text-secondary group-hover:text-accent transition-colors" />
55+
)}
56+
</button>
57+
</div>
2158
<a
22-
href="mailto:[your-email@example.com]"
23-
className="text-link hover:text-link-hover underline"
59+
href={`mailto:${email}`}
60+
className="inline-flex items-center gap-2 text-link hover:text-link-hover underline transition-colors"
2461
>
62+
<FaEnvelope className="w-4 h-4" />
2563
Send me an email
2664
</a>
2765
</div>
2866

29-
<div className="border border-border rounded-lg p-6 bg-bg-secondary">
30-
<h2 className="text-2xl font-semibold mb-4 text-text-primary">Social Media</h2>
31-
<div className="space-y-2 text-text-secondary">
32-
<p>
33-
<a href="#" className="text-link hover:text-link-hover underline">
34-
[Social media link 1]
35-
</a>
36-
</p>
37-
<p>
38-
<a href="#" className="text-link hover:text-link-hover underline">
39-
[Social media link 2]
40-
</a>
41-
</p>
67+
{/* LinkedIn Section */}
68+
<div className="border border-border rounded-lg p-6 bg-bg-secondary hover:border-accent/50 transition-colors">
69+
<div className="flex items-center gap-3 mb-4">
70+
<div className="p-2 bg-[#0077b5]/10 rounded-lg">
71+
<FaLinkedin className="w-5 h-5 text-[#0077b5]" />
72+
</div>
73+
<h2 className="text-2xl font-semibold text-text-primary">LinkedIn</h2>
4274
</div>
75+
<p className="text-text-secondary mb-4">
76+
Connect with me on LinkedIn to stay updated with my professional journey and network.
77+
</p>
78+
<a
79+
href={linkedinUrl}
80+
target="_blank"
81+
rel="noopener noreferrer"
82+
className="inline-flex items-center gap-2 text-link hover:text-link-hover underline transition-colors"
83+
>
84+
<FaLinkedin className="w-4 h-4" />
85+
Visit my LinkedIn profile
86+
</a>
4387
</div>
4488

89+
{/* Other Section */}
4590
<div className="border border-border rounded-lg p-6 bg-bg-secondary">
46-
<h2 className="text-2xl font-semibold mb-4 text-text-primary">Other</h2>
47-
<p className="text-text-secondary">
91+
<h2 className="text-2xl font-semibold mb-4 text-text-primary">More Ways to Connect</h2>
92+
<p className="text-text-secondary mb-4">
4893
Check out my{' '}
49-
<a href="/hub" className="text-link hover:text-link-hover underline">
94+
<a href="/hub" className="inline-flex items-center gap-1.5 text-link hover:text-link-hover underline">
95+
<FaLock className="w-3.5 h-3.5 opacity-70" />
5096
Hub page
5197
</a>
52-
{' '}for all the places you can find me online.
98+
{' '}for all the places you can find me online, including GitHub, Medium, Credly, and more.
5399
</p>
100+
<div className="mt-4 p-4 bg-accent/10 border border-accent/20 rounded-lg">
101+
<p className="text-sm text-text-secondary">
102+
<span className="font-semibold text-accent">Note:</span> The Hub is currently in{' '}
103+
<span className="font-semibold">beta testing</span> and{' '}
104+
<span className="font-semibold">locked</span>, but{' '}
105+
<span className="font-semibold text-accent">coming soon</span>!
106+
</p>
107+
</div>
54108
</div>
55109
</div>
56110
</div>

0 commit comments

Comments
 (0)