diff --git a/src/constants/Header.ts b/src/constants/Header.ts index fccf3625..97570c29 100644 --- a/src/constants/Header.ts +++ b/src/constants/Header.ts @@ -5,6 +5,7 @@ export const navigationData = { items: [ { label: 'About Us', path: '/about-us' }, { label: 'Leadership', path: '/leadership' }, + { label: 'Authors', path: '/authors' }, { label: 'Contact Us', path: '/contact-us' }, { label: 'FAQs', path: '/faqs' }, ], diff --git a/src/pages/News/AuthorsPage.tsx b/src/pages/News/AuthorsPage.tsx new file mode 100644 index 00000000..a49bc47e --- /dev/null +++ b/src/pages/News/AuthorsPage.tsx @@ -0,0 +1,235 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { motion } from 'framer-motion'; +import { User, Building, BookOpen } from 'lucide-react'; + +import Header from '@/sections/Header'; +import Footer from '@/sections/Footer'; +import { fetchAllAuthors, Author } from '@/utils/author-utils'; +import { getPostsByAuthor } from '@/utils/posts-utils'; + +type AuthorWithPostCount = Author & { + postCount: number; +}; + +const AuthorsPage: React.FC = () => { + const navigate = useNavigate(); + const [authors, setAuthors] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const loadAllAuthors = async () => { + setIsLoading(true); + document.title = 'Our Authors - SugarLabs'; + try { + const allAuthors = await fetchAllAuthors(); + + if (!allAuthors || allAuthors.length === 0) { + setAuthors([]); + return; + } + + const authorsWithData = await Promise.all( + allAuthors.map(async (author) => { + try { + const posts = await getPostsByAuthor(author.slug); + return { ...author, postCount: posts.length }; + } catch (postError) { + console.error( + `Failed to get post count for ${author.name}`, + postError, + ); + return { ...author, postCount: 0 }; + } + }), + ); + + setAuthors(authorsWithData); + } catch (err) { + console.error('Error loading authors:', err); + setError('Failed to load author information'); + } finally { + setIsLoading(false); + } + }; + + loadAllAuthors(); + }, []); + + const handleAuthorClick = (slug: string) => { + navigate(`/authors/${slug}`); + }; + + const renderContent = () => { + if (isLoading) { + return ( +
+
+
+

Loading authors...

+
+
+ ); + } + + if (error) { + return ( +
+

+ Something went wrong +

+

{error}

+
+ ); + } + + if (authors.length === 0) { + return ( +
+

No Authors

+

+ There are no authors to display at this time. +

+
+ ); + } + + return ( +
+ {authors.map((author, index) => ( + handleAuthorClick(author.slug)} + className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/20 + transition-shadow duration-300 group-hover:shadow-blue-200 dark:group-hover:shadow-blue-500/30 + cursor-pointer group flex flex-col" + initial={{ opacity: 0, y: 20 }} + animate={{ opacity: 1, y: 0 }} + transition={{ + duration: 0.5, + delay: index * 0.1, + scale: { + type: 'spring', + stiffness: 300, + damping: 15, + }, + }} + whileHover={{ scale: 1.05 }} + > +
+ {/* Avatar */} +
+ {author.avatar ? ( + {author.name} + ) : ( +
+ +
+ )} +
+ + {/* Author Info*/} +
+

+ {author.name} +

+ + {/* Title and Org */} +
+ + {author.title} + + {author.organization && ( +
+ + {author.organization} +
+ )} +
+ + {/* Description */} +

+ {author.description} +

+ + {/* Quick Stats */} +
+
+ + + {author.postCount}{' '} + {author.postCount === 1 ? 'Article' : 'Articles'} + +
+ {author.organization && ( +
+ + {author.organization} +
+ )} +
+
+
+
+ ))} +
+ ); + }; + + return ( +
+
+
+
+
+ + + Meet{' '} + + Our Authors + + +
+ +
+ + + Meet the talented authors, mentors, and contributors who share + their insights and drive the Sugar Labs mission forward. Explore + their work and connect with the community. + +
+ {renderContent()} +
+
+
+
+ ); +}; + +export default AuthorsPage; diff --git a/src/routes.tsx b/src/routes.tsx index edef466c..3e757385 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -25,6 +25,7 @@ import FlatHubPage from '@/pages/TryNow/FlatHub'; import Matrix from '@/pages/Matrix'; import NotFoundPage from '@/pages/NotFoundPage'; import Contributors from '@/pages/Contributors'; +import AuthorsPage from '@/pages/News/AuthorsPage'; const router = createBrowserRouter([ ...redirectRoutes, @@ -37,6 +38,7 @@ const router = createBrowserRouter([ { path: '/news/:category', element: }, { path: '/news/:category/:slug', element: }, { path: '/authors/:slug', element: }, + { path: '/authors', element: }, { path: '/tags/:tag', element: }, { path: '/more', element: }, { path: '/more/:slug', element: },