11import type { Metadata } from 'next'
2- import Link from 'next/link'
32import { getAllPostMeta } from '@/lib/blog/registry'
4- import { StudioHero } from '@/app/(landing)/blog/hero'
5- import { FeaturedGrid , PostGrid } from '@/app/(landing)/blog/post-grid'
6- import { getCategoryById , getPrimaryCategory } from '@/app/(landing)/blog/tag-colors'
7- import { WithSidebar } from '@/app/(landing)/blog/with-sidebar'
3+ import { StudioContent } from '@/app/(landing)/blog/studio-content'
84
95export const metadata : Metadata = {
106 title : 'Blog' ,
117 description : 'Announcements, insights, and guides from the Sim team.' ,
128}
139
14- export const revalidate = 3600
15-
16- export default async function BlogIndex ( {
10+ export default async function StudioIndex ( {
1711 searchParams,
1812} : {
19- searchParams : Promise < { page ?: string ; tag ?: string ; q ?: string } >
13+ searchParams : Promise < { tag ?: string ; q ?: string } >
2014} ) {
21- const { page, tag, q } = await searchParams
22- const pageNum = Math . max ( 1 , Number ( page || 1 ) )
23- const perPage = 20
24- const query = q ?. trim ( ) . toLowerCase ( ) ?? ''
25-
15+ const { tag, q } = await searchParams
2616 const all = await getAllPostMeta ( )
2717
28- let filtered = all
29- let resolvedTag = tag
30- if ( tag ) {
31- const byCategory = all . filter ( ( p ) => getPrimaryCategory ( p . tags ) . id === tag )
32- if ( byCategory . length > 0 ) {
33- filtered = byCategory
34- } else {
35- const byRawTag = all . filter ( ( p ) => p . tags . includes ( tag ) )
36- if ( byRawTag . length > 0 ) {
37- filtered = byRawTag
38- resolvedTag = getPrimaryCategory ( byRawTag [ 0 ] . tags ) . id
39- }
40- }
41- }
42-
43- if ( query ) {
44- filtered = filtered . filter ( ( p ) => {
45- const haystack = [
46- p . title ,
47- p . description ,
48- ...p . tags ,
49- p . author . name ,
50- ...( p . authors ?. map ( ( a ) => a . name ) ?? [ ] ) ,
51- ]
52- . join ( ' ' )
53- . toLowerCase ( )
54- return haystack . includes ( query )
55- } )
56- }
57-
58- const activeCategory = resolvedTag ? getCategoryById ( resolvedTag ) : null
59-
60- const sorted = filtered . sort ( ( a , b ) => {
61- if ( a . featured && ! b . featured ) return - 1
62- if ( ! a . featured && b . featured ) return 1
63- return new Date ( b . date ) . getTime ( ) - new Date ( a . date ) . getTime ( )
64- } )
65-
66- const totalPages = Math . max ( 1 , Math . ceil ( sorted . length / perPage ) )
67- const start = ( pageNum - 1 ) * perPage
68- const pagePosts = sorted . slice ( start , start + perPage )
69-
70- const featured = pageNum === 1 && ! tag ? pagePosts . filter ( ( p ) => p . featured ) : [ ]
71- const feed = pageNum === 1 && ! tag ? pagePosts . filter ( ( p ) => ! p . featured ) : pagePosts
18+ const posts = all . map ( ( p ) => ( {
19+ slug : p . slug ,
20+ title : p . title ,
21+ description : p . description ,
22+ date : p . date ,
23+ ogImage : p . ogImage ,
24+ readingTime : p . readingTime ,
25+ tags : p . tags ,
26+ author : p . author ,
27+ authors : p . authors ,
28+ featured : p . featured ?? false ,
29+ } ) )
7230
7331 const studioJsonLd = {
7432 '@context' : 'https://schema.org' ,
@@ -79,113 +37,12 @@ export default async function BlogIndex({
7937 }
8038
8139 return (
82- < WithSidebar activeTag = { tag } >
83- < div className = 'flex flex-col' >
84- < script
85- type = 'application/ld+json'
86- dangerouslySetInnerHTML = { { __html : JSON . stringify ( studioJsonLd ) } }
87- />
88- { pageNum === 1 && ! tag && ! query && < StudioHero /> }
89- < div className = 'mx-auto w-full max-w-5xl py-12' >
90- { query && (
91- < div className = 'mb-8 flex items-center gap-3' >
92- < span className = 'font-season text-[10px] uppercase tracking-widest text-[#666]' >
93- Results for:
94- </ span >
95- < span
96- className = 'px-2 py-0.5 font-season text-[10px] uppercase tracking-wider text-[#ECECEC]'
97- style = { { border : '1px solid #3d3d3d' } }
98- >
99- { q }
100- </ span >
101- < Link
102- href = '/studio'
103- className = 'font-mono text-[10px] uppercase tracking-wider text-[#999] transition-colors hover:text-[#ECECEC]'
104- >
105- Clear
106- </ Link >
107- </ div >
108- ) }
109- { activeCategory && ! query && (
110- < div className = 'mb-8 flex items-center gap-3' >
111- < span className = 'font-mono text-[10px] uppercase tracking-widest text-[#666]' >
112- Filtered by:
113- </ span >
114- < span
115- className = 'px-2 py-0.5 font-mono text-[10px] uppercase tracking-wider'
116- style = { {
117- border : `1px solid ${ activeCategory . color } ` ,
118- color : activeCategory . color ,
119- } }
120- >
121- { activeCategory . label }
122- </ span >
123- < Link
124- href = '/studio'
125- className = 'font-mono text-[10px] uppercase tracking-wider text-[#999] transition-colors hover:text-[#ECECEC]'
126- >
127- Clear
128- </ Link >
129- </ div >
130- ) }
131- { featured . length > 0 && (
132- < section className = 'mb-10' >
133- < h2 className = 'mb-8 flex items-center gap-2 font-mono text-[11px] uppercase tracking-widest text-[#666]' >
134- < span className = 'inline-block h-2 w-2 bg-[#FA4EDF]' aria-hidden = 'true' />
135- Featured Content
136- </ h2 >
137- < FeaturedGrid posts = { featured } />
138- </ section >
139- ) }
140- { feed . length > 0 && (
141- < section >
142- < h2 className = 'mb-8 flex items-center gap-2 font-mono text-[11px] uppercase tracking-widest text-[#666]' >
143- < span className = 'inline-block h-2 w-2 bg-[#00F701]' aria-hidden = 'true' />
144- { query ? 'Search Results' : activeCategory ? activeCategory . label : 'All Posts' }
145- </ h2 >
146- < PostGrid posts = { feed } />
147- </ section >
148- ) }
149- { pagePosts . length === 0 && (
150- < div className = 'py-20 text-center' >
151- < p className = 'text-[14px] text-[#666]' >
152- { query ? `No posts matching "${ q } ".` : 'No posts found.' }
153- </ p >
154- < Link
155- href = '/studio'
156- className = 'mt-4 inline-block font-mono text-[12px] uppercase tracking-wider text-[#999] transition-colors hover:text-[#ECECEC]'
157- >
158- View all posts
159- </ Link >
160- </ div >
161- ) }
162- { totalPages > 1 && (
163- < div className = 'mt-20 flex items-center justify-center gap-4 border-t border-[#2A2A2A] pt-12' >
164- { pageNum > 1 && (
165- < Link
166- href = { `/studio?page=${ pageNum - 1 } ${ tag ? `&tag=${ encodeURIComponent ( tag ) } ` : '' } ` }
167- className = 'border border-[#3d3d3d] bg-[#232323] px-6 py-2.5 font-mono text-[11px] uppercase tracking-wider text-[#999] transition-colors hover:border-[#666] hover:text-[#ECECEC]'
168- style = { { borderRadius : '5px' } }
169- >
170- Previous
171- </ Link >
172- ) }
173- < span className = 'font-mono text-[10px] uppercase tracking-wider text-[#666]' >
174- Page { pageNum } of { totalPages }
175- </ span >
176- { pageNum < totalPages && (
177- < Link
178- href = { `/studio?page=${ pageNum + 1 } ${ tag ? `&tag=${ encodeURIComponent ( tag ) } ` : '' } ` }
179- className = 'border border-[#3d3d3d] bg-[#232323] px-6 py-2.5 font-mono text-[11px] uppercase tracking-wider text-[#999] transition-colors hover:border-[#666] hover:text-[#ECECEC]'
180- style = { { borderRadius : '5px' } }
181- >
182- Load more articles
183- </ Link >
184- ) }
185- </ div >
186- ) }
187- </ div >
188- </ div >
189- </ WithSidebar >
40+ < >
41+ < script
42+ type = 'application/ld+json'
43+ dangerouslySetInnerHTML = { { __html : JSON . stringify ( studioJsonLd ) } }
44+ />
45+ < StudioContent posts = { posts } initialTag = { tag ?? null } initialQuery = { q ?? '' } />
46+ </ >
19047 )
19148}
0 commit comments