diff --git a/apps/sim/app/(landing)/blog/authors/[id]/page.tsx b/apps/sim/app/(landing)/blog/authors/[id]/page.tsx index 302b843cae6..6d2869eeca5 100644 --- a/apps/sim/app/(landing)/blog/authors/[id]/page.tsx +++ b/apps/sim/app/(landing)/blog/authors/[id]/page.tsx @@ -11,18 +11,20 @@ export async function generateMetadata({ params: Promise<{ id: string }> }): Promise { const { id } = await params - const posts = (await getAllPostMeta()).filter((p) => p.author.id === id) - return buildAuthorMetadata(id, posts[0]?.author) + const posts = (await getAllPostMeta()).filter((p) => p.authors.some((a) => a.id === id)) + const author = posts[0]?.authors.find((a) => a.id === id) + return buildAuthorMetadata(id, author) } export default async function AuthorPage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params - const posts = (await getAllPostMeta()).filter((p) => p.author.id === id) - const author = posts[0]?.author + const posts = (await getAllPostMeta()).filter((p) => p.authors.some((a) => a.id === id)) + const author = posts[0]?.authors.find((a) => a.id === id) return ( -
- - +
+
+ +
+ + +
-
- {Array.from({ length: AUTHOR_POST_SKELETON_COUNT }).map((_, i) => ( -
- -
- - + +
+ +
+
+ {Array.from({ length: AUTHOR_POST_SKELETON_COUNT }).map((_, i) => ( +
+
+ +
+ + +
+ +
+
-
- ))} + ))} +
- +
) } diff --git a/apps/sim/app/(landing)/components/content-author-page/content-author-page.tsx b/apps/sim/app/(landing)/components/content-author-page/content-author-page.tsx index e57ad9e3c64..43225eb95ca 100644 --- a/apps/sim/app/(landing)/components/content-author-page/content-author-page.tsx +++ b/apps/sim/app/(landing)/components/content-author-page/content-author-page.tsx @@ -1,11 +1,16 @@ +import { ChipLink } from '@sim/emcn' import Image from 'next/image' import Link from 'next/link' import type { ContentMeta } from '@/lib/content/schema' +import { BackLink } from '@/app/(landing)/components/back-link' +import { Cta } from '@/app/(landing)/components/cta/cta' import { JsonLd } from '@/app/(landing)/components/json-ld' interface ContentAuthorPageProps { /** Route base path, e.g. `/blog` or `/library`. */ basePath: string + /** Section label used in the not-found fallback, e.g. "Blog" or "Library". */ + sectionName: string authorName?: string authorAvatarUrl?: string /** Posts already filtered down to this author. */ @@ -13,9 +18,15 @@ interface ContentAuthorPageProps { graphJsonLd?: Record } -/** Shared author-profile layout for a content section. */ +/** + * Shared author-profile layout for a content section: standard page-shell + * header (matching `ContentIndexPage`/`ContentPostPage`) with avatar + name, + * followed by the author's posts in the same framed-list card style used + * everywhere else on the site. + */ export function ContentAuthorPage({ basePath, + sectionName, authorName, authorAvatarUrl, posts, @@ -23,54 +34,104 @@ export function ContentAuthorPage({ }: ContentAuthorPageProps) { if (!authorName) { return ( -
-

Author not found

-
+
+

+ Author not found +

+

+ The author you're looking for doesn't exist or has been moved. +

+ + Browse {sectionName} + +
) } return ( -
- {graphJsonLd && } -
- {authorAvatarUrl ? ( - {authorName} - ) : null} -

{authorName}

-
-
- {posts.map((p) => ( - -
+ <> +
+ {graphJsonLd && } + +
+
+ +
+ +
+ {authorAvatarUrl ? ( {p.title} -
-
- {new Date(p.date).toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - })} -
-
{p.title}
+ ) : null} +

+ {authorName} +

+
+
+ +
+ +
+
+ {posts.map((p) => ( +
+ + + {new Date(p.date).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })} + + +
+ + {new Date(p.date).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })} + +

+ {p.title} +

+

+ {p.description} +

+
+ +
+ {p.title} +
+ +
-
- - ))} + ))} +
+
+ +
+
+ +
+
-
+ ) } diff --git a/apps/sim/app/(landing)/components/content-post-page/content-post-page.tsx b/apps/sim/app/(landing)/components/content-post-page/content-post-page.tsx index e7ab328e016..fccff3158de 100644 --- a/apps/sim/app/(landing)/components/content-post-page/content-post-page.tsx +++ b/apps/sim/app/(landing)/components/content-post-page/content-post-page.tsx @@ -96,9 +96,8 @@ export function ContentPostPage({ ) : null} }): Promise { const { id } = await params - const posts = (await getAllPostMeta()).filter((p) => p.author.id === id) - return buildAuthorMetadata(id, posts[0]?.author) + const posts = (await getAllPostMeta()).filter((p) => p.authors.some((a) => a.id === id)) + const author = posts[0]?.authors.find((a) => a.id === id) + return buildAuthorMetadata(id, author) } export default async function AuthorPage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params - const posts = (await getAllPostMeta()).filter((p) => p.author.id === id) - const author = posts[0]?.author + const posts = (await getAllPostMeta()).filter((p) => p.authors.some((a) => a.id === id)) + const author = posts[0]?.authors.find((a) => a.id === id) return ( ({ - url: `${SITE_URL}${basePath}/authors/${id}`, + url: `${SITE_URL}${basePath}/authors/${encodeURIComponent(id)}`, lastModified: date, })) } diff --git a/apps/sim/lib/content/seo.ts b/apps/sim/lib/content/seo.ts index df224f91963..dcad1a7adf0 100644 --- a/apps/sim/lib/content/seo.ts +++ b/apps/sim/lib/content/seo.ts @@ -285,7 +285,7 @@ export function buildAuthorMetadata( author?: Author ): Metadata { const name = author?.name ?? 'Author' - const canonical = `${SITE_URL}${section.basePath}/authors/${id}` + const canonical = `${SITE_URL}${section.basePath}/authors/${encodeURIComponent(id)}` const description = `Read articles by ${name} on the Sim ${section.name.toLowerCase()}.` return { title: `${name} | Sim ${section.name}`, @@ -318,9 +318,11 @@ export function buildAuthorGraphJsonLd(section: ContentSection, author: Author) { '@type': 'Person', name: author.name, - url: `${SITE_URL}${section.basePath}/authors/${author.id}`, + url: `${SITE_URL}${section.basePath}/authors/${encodeURIComponent(author.id)}`, sameAs: author.url ? [author.url] : [], - image: author.avatarUrl, + image: author.avatarUrl?.startsWith('http') + ? author.avatarUrl + : author.avatarUrl && `${SITE_URL}${author.avatarUrl}`, worksFor: { '@type': 'Organization', name: 'Sim', @@ -341,7 +343,7 @@ export function buildAuthorGraphJsonLd(section: ContentSection, author: Author) '@type': 'ListItem', position: 3, name: author.name, - item: `${SITE_URL}${section.basePath}/authors/${author.id}`, + item: `${SITE_URL}${section.basePath}/authors/${encodeURIComponent(author.id)}`, }, ], },