Skip to content

Commit 0faf71a

Browse files
committed
refactor: enhance Author page with dynamic social media links and improve layout
1 parent bd5f955 commit 0faf71a

File tree

2 files changed

+71
-97
lines changed

2 files changed

+71
-97
lines changed

app/authors/[author]/page.tsx

+69-97
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { Metadata } from 'next';
2-
import formatString from "@/app/functions/formatString";
1+
import { db } from "@/lib/firebase";
2+
import { collection, query, where, getDocs, orderBy } from "firebase/firestore";
33
import PostNavigation from "@/components/PostNavigation";
44
import SocialSharing from "@/components/SocialSharing";
55
import Link from "next/link";
6-
import { db } from "@/lib/firebase";
7-
import { collection, query, where, getDocs, orderBy } from "firebase/firestore";
6+
import {
7+
RiInstagramLine,
8+
RiTwitterFill,
9+
RiYoutubeFill,
10+
RiGithubFill,
11+
RiTiktokFill,
12+
RiPatreonFill,
13+
RiFacebookFill,
14+
RiLinkedinFill,
15+
RiDiscordFill,
16+
} from "react-icons/ri";
817

918
type AuthorData = {
1019
uid: string;
@@ -14,10 +23,8 @@ type AuthorData = {
1423
avatar: string;
1524
imgAlt: string;
1625
slug: string;
17-
biography: {
18-
summary: string;
19-
body: string;
20-
};
26+
biography: { summary: string; body: string };
27+
socials?: { [key: string]: string }; // Social media map
2128
};
2229

2330
type ArticleData = {
@@ -31,33 +38,6 @@ type ArticleData = {
3138
authorUID: string;
3239
};
3340

34-
export async function generateMetadata({
35-
params,
36-
}: {
37-
params: { author: string };
38-
}): Promise<Metadata> {
39-
const decodedAuthor = decodeURIComponent(params.author);
40-
try {
41-
const authorsRef = collection(db, "authors");
42-
const q = query(authorsRef, where("slug", "==", decodedAuthor));
43-
const snapshot = await getDocs(q);
44-
45-
if (snapshot.empty) {
46-
return { title: "Author Not Found | L.A.P Docs" };
47-
}
48-
49-
const authorData = snapshot.docs[0].data() as AuthorData;
50-
return {
51-
title: `${authorData.name} | L.A.P Docs`,
52-
description: authorData.biography.summary
53-
};
54-
} catch (error) {
55-
return {
56-
title: "Author Profile | L.A.P Docs"
57-
};
58-
}
59-
}
60-
6141
// Function to fetch author data
6242
async function getAuthorData(slug: string) {
6343
try {
@@ -66,19 +46,18 @@ async function getAuthorData(slug: string) {
6646
const q = query(authorsRef, where("slug", "==", decodedAuthor));
6747
const authorSnapshot = await getDocs(q);
6848

69-
if (authorSnapshot.empty) {
70-
return null;
71-
}
49+
if (authorSnapshot.empty) return null;
7250

7351
const authorData = authorSnapshot.docs[0].data() as AuthorData;
52+
7453
const articlesQuery = query(
7554
collection(db, "articles"),
7655
where("authorUID", "==", authorData.uid),
7756
orderBy("date", "desc")
7857
);
7958
const articlesSnapshot = await getDocs(articlesQuery);
8059

81-
const articles = articlesSnapshot.docs.map(doc => ({
60+
const articles = articlesSnapshot.docs.map((doc) => ({
8261
uid: doc.id,
8362
...doc.data(),
8463
date: doc.data().date?.toDate() || new Date(),
@@ -91,71 +70,74 @@ async function getAuthorData(slug: string) {
9170
}
9271
}
9372

94-
// Non-async page component
73+
// **Dynamically Map Social Links to Icons**
74+
const SOCIAL_ICONS: { [key: string]: any } = {
75+
youtube: RiYoutubeFill,
76+
github: RiGithubFill,
77+
instagram: RiInstagramLine,
78+
twitter: RiTwitterFill,
79+
tiktok: RiTiktokFill,
80+
patreon: RiPatreonFill,
81+
facebook: RiFacebookFill,
82+
linkedin: RiLinkedinFill,
83+
discord: RiDiscordFill,
84+
};
85+
86+
// **Non-async page component**
9587
export default function Page({ params }: { params: { author: string } }) {
9688
return <AuthorPage authorSlug={params.author} />;
9789
}
9890

99-
// Async server component
91+
// **Async server component**
10092
async function AuthorPage({ authorSlug }: { authorSlug: string }) {
10193
const data = await getAuthorData(authorSlug);
102-
94+
10395
if (!data) {
10496
return <div className="p-8">Author not found</div>;
10597
}
106-
98+
10799
const { author: authorData, articles } = data;
108100

101+
// **Dynamically Generate Social Media Links**
102+
const socialLinks = authorData.socials
103+
? Object.entries(authorData.socials)
104+
.filter(([platform, url]) => url) // Remove empty links
105+
.map(([platform, url]) => ({
106+
href: url,
107+
ariaLabel: `Visit ${authorData.name}'s ${platform} page`,
108+
Icon: SOCIAL_ICONS[platform.toLowerCase()] || RiGithubFill, // Default to GitHub icon if unknown
109+
}))
110+
: [];
111+
109112
return (
110113
<main className="max-w-[95rem] w-full mx-auto px-4 sm:pt-4 xs:pt-2 lg:pb-4 md:pb-4 sm:pb-2 xs:pb-2">
111114
<PostNavigation href="/authors">Author</PostNavigation>
112-
115+
113116
<article className="max-w-[75rem] w-full mx-auto grid lg:grid-cols-[300px_680px] gap-8 md:gap-6 justify-around">
114-
{/* Author Profile Section */}
117+
{/* **Author Profile Section** */}
115118
<div className="w-fit">
116119
<img
117120
src={authorData.avatar}
118121
alt={authorData.imgAlt}
119-
className="w-full max-w-[300px] h-auto"
122+
className="w-full max-w-[300px] h-auto rounded-full"
120123
/>
121-
<div className="flex justify-between border-t border-black mt-12 pt-6">
122-
<p className="uppercase font-semibold text-lg">Follow</p>
123-
<SocialSharing
124-
links={[
125-
{
126-
href: "#",
127-
ariaLabel: "Visit our Instagram page",
128-
src: "/icons/ri_instagram-line.svg",
129-
alt: "Instagram logo",
130-
},
131-
{
132-
href: "#",
133-
ariaLabel: "Visit our Twitter page",
134-
src: "/icons/ri_twitter-fill.svg",
135-
alt: "Twitter logo",
136-
},
137-
{
138-
href: "#",
139-
ariaLabel: "Visit our YouTube page",
140-
src: "/icons/ri_youtube-fill.svg",
141-
alt: "YouTube logo",
142-
},
143-
]}
144-
/>
145-
</div>
124+
{socialLinks.length > 0 && (
125+
<div className="flex justify-between border-t border-white mt-12 pt-6">
126+
<p className="uppercase font-semibold text-lg">Follow:</p>
127+
<SocialSharing links={socialLinks} />
128+
</div>
129+
)}
146130
</div>
147131

148-
{/* Author Biography */}
132+
{/* **Author Biography** */}
149133
<article>
150134
<h1 className="text-subheading pb-8">{authorData.name}</h1>
151-
<p className="text-blog-summary pb-12">
152-
{authorData.biography.summary}
153-
</p>
135+
<p className="text-blog-summary pb-12">{authorData.biography.summary}</p>
154136
<p className="text-blog-body">{authorData.biography.body}</p>
155137
</article>
156138
</article>
157139

158-
{/* Author Articles */}
140+
{/* **Author Articles** */}
159141
<div className="pb-12 md:pb-48">
160142
<h2 className="text-blog-subheading mt-[9.5rem] pt-12 pb-12 md:pb-24">
161143
Articles by {authorData.name}
@@ -166,6 +148,7 @@ async function AuthorPage({ authorSlug }: { authorSlug: string }) {
166148
);
167149
}
168150

151+
// **Component to Render Author’s Articles**
169152
function AuthorArticles({ articles }: { articles: ArticleData[] }) {
170153
if (articles.length === 0) {
171154
return (
@@ -176,55 +159,44 @@ function AuthorArticles({ articles }: { articles: ArticleData[] }) {
176159
}
177160

178161
return (
179-
<div className="grid md:grid-cols-2 border border-black border-collapse">
162+
<div className="grid md:grid-cols-2">
180163
{articles.map((article) => (
181-
<article
182-
className="flex items-center gap-2 md:gap-12 p-8 border border-black"
183-
key={article.uid}
184-
>
185-
<Link href={`/magazine/${article.slug}`} className="flex-shrink-0">
164+
<article className="flex items-center gap-2 md:gap-12 p-8 border border-white" key={article.uid}>
165+
<Link href={`/posts/${article.slug}`} className="flex-shrink-0">
186166
<img
187167
className="h-[150px] w-[150px] object-cover hover:scale-105 transition-transform"
188168
src={article.img}
189169
alt={article.title}
190170
/>
191171
</Link>
192-
172+
193173
<div>
194174
<p className="heading3-title pb-4">
195-
<Link
196-
href={`/magazine/${article.slug}`}
197-
className="hover:text-gray-600 transition-colors"
198-
>
175+
<Link href={`/posts/${article.slug}`} className="hover:text-white transition-colors">
199176
{article.title}
200177
</Link>
201178
</p>
202-
179+
203180
<div className="flex flex-wrap gap-4">
204181
<div className="flex items-center">
205182
<p className="font-semibold pr-2">Date:</p>
206-
<time
207-
dateTime={article.date.toISOString()}
208-
className="text-gray-600"
209-
>
183+
<time dateTime={article.date.toISOString()} className="text-white">
210184
{article.date.toLocaleDateString("en-US", {
211185
year: "numeric",
212186
month: "long",
213187
day: "numeric",
214188
})}
215189
</time>
216190
</div>
217-
191+
218192
<div className="flex items-center">
219193
<p className="font-semibold pr-2">Category:</p>
220-
<span className="text-gray-600">
221-
{formatString(article.label)}
222-
</span>
194+
<span className="text-white">{article.label}</span>
223195
</div>
224196
</div>
225197
</div>
226198
</article>
227199
))}
228200
</div>
229201
);
230-
}
202+
}

components/AuthorList.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,15 @@ export default function AuthorsList() {
5454
<article className="flex flex-col md:flex-row justify-between md:items-center gap-4 ml-5">
5555
<div className="flex flex-col md:flex-row md:items-center gap-4 md:gap-16">
5656
{/* Ensure image has a transparent background */}
57+
<Link href={`authors/${author.slug}`}>
5758
<Image
5859
className="h-[9.375rem] w-[9.375rem] object-cover rounded-full bg-transparent"
5960
src={author.avatar}
6061
alt={author.imgAlt}
6162
width={150}
6263
height={150}
6364
/>
65+
</Link>
6466
<Link href={`authors/${author.slug}`}>
6567
<h2 className="text-2xl font-semibold">{author.name}</h2>
6668
</Link>

0 commit comments

Comments
 (0)