@@ -2,16 +2,21 @@ import Link from 'next/link';
22import { BlogPostDataEntry } from '@nx/nx-dev/data-access-documents/node-only' ;
33import Image from 'next/image' ;
44import { BlogAuthors } from './authors' ;
5- import { ChevronLeftIcon } from '@heroicons/react/24/outline' ;
5+ import { ChevronLeftIcon , ListBulletIcon } from '@heroicons/react/24/outline' ;
66import { renderMarkdown } from '@nx/nx-dev/ui-markdoc' ;
77import { EpisodePlayer } from './episode-player' ;
88import { YouTube } from '@nx/nx-dev/ui-common' ;
9+ import { FeaturedBlogs } from './featured-blogs' ;
10+ import { MoreBlogs } from './more-blogs' ;
11+ import { ALL_TOPICS , type Topic } from './topics' ;
12+ import { Metrics } from '@nx/nx-dev/ui-markdoc' ;
913
1014export interface BlogDetailsProps {
1115 post : BlogPostDataEntry ;
16+ allPosts : BlogPostDataEntry [ ] ;
1217}
1318
14- export function BlogDetails ( { post } : BlogDetailsProps ) {
19+ export function BlogDetails ( { post, allPosts } : BlogDetailsProps ) {
1520 const { node } = renderMarkdown ( post . content , {
1621 filePath : post . filePath ?? '' ,
1722 headingClass : 'scroll-mt-20' ,
@@ -23,30 +28,48 @@ export function BlogDetails({ post }: BlogDetailsProps) {
2328 year : 'numeric' ,
2429 } ) ;
2530
31+ // Find the primary topic of the current post
32+ const primaryTopic = ALL_TOPICS . find ( ( topic : Topic ) =>
33+ post . tags . includes ( topic . value . toLowerCase ( ) )
34+ ) ;
35+
36+ const relatedPosts = allPosts
37+ . filter (
38+ ( p ) =>
39+ p . slug !== post . slug && // Exclude current post
40+ p . tags . some ( ( tag ) => post . tags . includes ( tag ) ) // Include posts with matching tags
41+ )
42+ . slice ( 0 , 5 ) ;
43+
2644 return (
2745 < main id = "main" role = "main" className = "w-full py-8" >
28- < div className = "mx-auto flex max-w-3xl justify-between px-4 lg:px-0" >
29- < Link
30- href = "/blog"
31- className = "flex w-20 shrink-0 items-center gap-2 text-slate-400 hover:text-slate-800 dark:text-slate-600 dark:hover:text-slate-200"
32- prefetch = { false }
33- >
34- < ChevronLeftIcon className = "h-3 w-3" />
35- Blog
36- </ Link >
37- < div className = "flex max-w-sm flex-1 grow items-center justify-end gap-2" >
38- < BlogAuthors authors = { post . authors } />
39- < span className = "text-sm text-slate-400 dark:text-slate-600" >
40- { formattedDate }
41- </ span >
46+ < div className = "mx-auto max-w-screen-md" >
47+ { /* Top navigation and author info */ }
48+ < div className = "mx-auto flex justify-between px-4" >
49+ < Link
50+ href = "/blog"
51+ className = "flex w-20 shrink-0 items-center gap-2 text-slate-400 hover:text-slate-800 dark:text-slate-600 dark:hover:text-slate-200"
52+ prefetch = { false }
53+ >
54+ < ChevronLeftIcon className = "h-3 w-3" />
55+ Blog
56+ </ Link >
57+ < div className = "flex max-w-sm flex-1 grow items-center justify-end gap-2" >
58+ < BlogAuthors authors = { post . authors } />
59+ < span className = "text-sm text-slate-400 dark:text-slate-600" >
60+ { formattedDate }
61+ </ span >
62+ </ div >
4263 </ div >
43- </ div >
44- < div id = "content-wrapper" >
45- < header className = "mx-auto mb-16 mt-8 max-w-3xl px-4 lg:px-0 " >
64+
65+ { /* Title */ }
66+ < header className = "mx-auto mb-16 mt-8 px-4" >
4667 < h1 className = "text-center text-4xl font-semibold text-slate-900 dark:text-white" >
4768 { post . title }
4869 </ h1 >
4970 </ header >
71+
72+ { /* Media content (podcast, youtube, or image) */ }
5073 { post . podcastYoutubeId && post . podcastSpotifyId ? (
5174 < div className = "mx-auto mb-16 w-full max-w-screen-md" >
5275 < EpisodePlayer
@@ -74,17 +97,73 @@ export function BlogDetails({ post }: BlogDetailsProps) {
7497 </ div >
7598 )
7699 ) }
77- < div className = "mx-auto min-w-0 max-w-3xl flex-auto px-4 pb-24 lg:px-0 lg:pb-16" >
78- < div className = "relative" >
100+ </ div >
101+
102+ { /* Main grid layout */ }
103+ < div className = "mx-auto max-w-7xl px-4 lg:px-8" >
104+ < div className = "relative isolate grid grid-cols-1 gap-8 xl:grid-cols-[200px_minmax(0,1fr)_200px]" >
105+ < div className = "hidden min-h-full xl:block" >
106+ { post . metrics && (
107+ < div className = "sticky top-28 pr-4 pt-8" >
108+ < Metrics metrics = { post . metrics } variant = "vertical" />
109+ </ div >
110+ ) }
111+ </ div >
112+
113+ { /* Middle column - main content */ }
114+ < div className = "w-full min-w-0 md:mx-auto md:max-w-screen-md" >
115+ { post . metrics && (
116+ < div className = "mb-8 xl:hidden" >
117+ < Metrics metrics = { post . metrics } variant = "horizontal" />
118+ </ div >
119+ ) }
79120 < div
80121 data-document = "main"
81- className = "prose prose-lg prose-slate dark:prose-invert w-full max-w-none 2xl:max-w-4xl "
122+ className = "prose prose-lg prose-slate dark:prose-invert w-full max-w-none"
82123 >
83124 { node }
84125 </ div >
85126 </ div >
127+
128+ { /* Right column - for future sticky content */ }
129+ < div className = "hidden xl:block" >
130+ < div className = "sticky top-24" >
131+ { /* Right sidebar content can go here */ }
132+ </ div >
133+ </ div >
86134 </ div >
87135 </ div >
136+
137+ { /* Related Posts Section */ }
138+ { post . tags . length > 0 && relatedPosts . length > 0 && (
139+ < section className = "mt-24 border-b border-t border-slate-200 bg-slate-50 py-24 sm:py-32 dark:border-slate-800 dark:bg-slate-900" >
140+ < div className = "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8" >
141+ < div className = "mx-auto max-w-2xl lg:mx-0 lg:max-w-none" >
142+ < h2 className = "mb-8 flex items-center gap-3 text-2xl font-semibold text-slate-900 dark:text-white" >
143+ { primaryTopic ? (
144+ < >
145+ < primaryTopic . icon className = "h-7 w-7" />
146+ More { primaryTopic . label }
147+ </ >
148+ ) : (
149+ < >
150+ < ListBulletIcon className = "h-7 w-7" />
151+ More Articles
152+ </ >
153+ ) }
154+ </ h2 >
155+ { /* Show list view on small screens */ }
156+ < div className = "md:hidden" >
157+ < MoreBlogs blogs = { relatedPosts } />
158+ </ div >
159+ { /* Show grid view on larger screens */ }
160+ < div className = "hidden md:block" >
161+ < FeaturedBlogs blogs = { relatedPosts } />
162+ </ div >
163+ </ div >
164+ </ div >
165+ </ section >
166+ ) }
88167 </ main >
89168 ) ;
90169}
0 commit comments