Skip to content

Commit

Permalink
Implement author profiles (#1366)
Browse files Browse the repository at this point in the history
* Implement author profiles

* Implement suggestions from review

* Remove github links

* Add back link & remove duplicate separation

* Updates from talking & doc'ing

* Remove alt image

* Fix article label & implement 404 logic

* Apply suggestions from code review

Co-authored-by: Chris Swithinbank <[email protected]>

* Update other 404 logic

* Move profile component

* fix import

* Revert 404 changes

* Use status

---------

Co-authored-by: Chris Swithinbank <[email protected]>
  • Loading branch information
TheOtterlord and delucis authored Jan 26, 2025
1 parent ca3c75d commit 47b12d5
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 7 deletions.
4 changes: 3 additions & 1 deletion src/components/Card.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ export type Props = {
background?: 'neutral' | 'blue-purple' | 'blue-green' | 'red-pink';
isLink?: boolean;
className?: string;
labelledby?: string;
};
const { background = 'neutral', isLink = false, className } = Astro.props;
const { background = 'neutral', isLink = false, className, labelledby } = Astro.props;
---

<article
aria-labelledby={labelledby}
class:list={[
'panel relative flex min-w-0 flex-col',
background === 'blue-purple' && 'bg-blue-purple-gradient',
Expand Down
54 changes: 54 additions & 0 deletions src/pages/themes/_components/ThemeAuthorProfile.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
import { Image } from 'astro:assets';
import LeftArrowIcon from '~/icons/LeftArrowIcon.tsx'
import type { Author } from '../_types/index.ts';
interface Props {
author: Author;
}
const { author } = Astro.props;
---

<a
href="/themes/"
data-astro-prefetch
class="flex items-center gap-2 self-start text-sm text-astro-gray-200 mb-4"
>
<LeftArrowIcon aria-hidden="true" />
<span class="pr-2">Back to all themes</span>
</a>
<div class="flex lg:flex-col flex-wrap items-center gap-4">
<Image
inferSize
src={author.avatar ?? ''}
height={48}
width={48}
class="size-12 lg:w-full h-fit"
alt=""
/>
<h1 class="border-astro-gray-500 lg:hidden heading-3">{author.name}</h1>
<div class="flex flex-col gap-4 pt-2 lg:pt-8 w-full">
<div class="flex items-baseline justify-between">
<small class="code text-astro-gray-200">Joined</small>
{
new Date(author.createdAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
}
</div>
{
author.url && (
<hr class="border-astro-gray-500" />
<div class="flex items-baseline justify-between">
<small class="code text-astro-gray-200">Website</small>
<a class="link" href={author.url}>
{author.url.replace(/^https?:\/\/|\/$/g, '')}
</a>
</div>
)
}
</div>
</div>
11 changes: 6 additions & 5 deletions src/pages/themes/_components/ThemeCard.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
import { Image } from 'astro:assets';
import type { HTMLTag } from 'astro/types'
import Card from '~/components/Card.astro';
import CardBody from '~/components/CardBody.astro';
import CardFooter from '~/components/CardFooter.astro';
Expand All @@ -8,15 +9,16 @@ import Avatar from './Avatar.astro';
import Badge from './Badge.astro';
export type Props = {
as?: HTMLTag;
theme: ThemeAndAuthor;
};
const { theme } = Astro.props;
const { as: Tag = 'h3', theme } = Astro.props;
const image = theme.Theme.image;
---

<Card isLink>
<Card isLink labelledby={theme.Theme.slug}>
<!-- {!!theme.data.badge && <CardBadge>{theme.data.badge}</CardBadge>} -->
<a class="flex h-full flex-col" href={`/themes/details/${theme.Theme.slug}/`} data-astro-prefetch>
<Image
Expand All @@ -30,10 +32,9 @@ const image = theme.Theme.image;
/>
<CardBody>
<div>
<!-- <h3 class:list={["heading-5 mb-2 text-white", { official: theme.data.official }]}> -->
<h3 class:list={['heading-5 mb-2 text-white']}>
<Tag class:list={['heading-5 mb-2 text-white']} id={theme.Theme.slug}>
{theme.Theme.title}
</h3>
</Tag>
<p class="line-clamp-3">{theme.Theme.description}</p>
</div>
<CardFooter
Expand Down
2 changes: 1 addition & 1 deletion src/pages/themes/_components/ThemeStats.astro
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ if (theme.Theme.repoUrl && typeof theme.Theme.stars !== 'undefined') {
<hr class="border-astro-gray-500" />
<div class="flex items-center justify-between">
<small class="code text-astro-gray-200">Created by</small>
<a href={theme.Author.url} target="_blank" class="link">
<a href={`/themes/author/${theme.Author.id}`} class="link">
<Avatar theme={theme} />
</a>
</div>
Expand Down
82 changes: 82 additions & 0 deletions src/pages/themes/author/[id]/[...page].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
import CardGrid from '~/components/CardGrid.astro';
import CardGridGroup from '~/components/CardGridGroup.astro';
import Pagination from '~/components/Pagination.astro';
import SidebarLayout from '~/components/SidebarLayout.astro';
import SidebarPanel from '~/components/SidebarPanel.astro';
import { THEMES_API_URL } from '~/helpers/constants.ts';
import { paginate } from '~/helpers/paginate.js';
import MainLayout from '~/layouts/MainLayout.astro';
import SubmitTheme from '../../_components/SubmitTheme.astro';
import Profile from '../../_components/ThemeAuthorProfile.astro';
import ThemeCard from '../../_components/ThemeCard.astro';
import type { ThemeAndAuthor } from '../../_types/index.ts';
export const prerender = false;
// with '[...page]' rest routes we'll get undefined for the first page, default that to 1
// otherwise, try to parse the page number from the URL
const currentPage =
typeof Astro.params.page === 'undefined' ? 1 : Number.parseInt(Astro.params.page);
// invalid page number!
if (!currentPage || Number.isNaN(currentPage)) {
return Astro.redirect('/404', 404);
}
const res = await fetch(
`${THEMES_API_URL}/api/themes/author?id=${Astro.params.id}`,
);
if (res.status !== 200) return Astro.redirect('/404', 404);
const allThemes: ThemeAndAuthor[] = await res.json();
// take all matching themes and create a paginated list of results
const paginatedResults = paginate({
data: allThemes,
pageSize: 12,
currentPage,
route: `/themes/author/${Astro.params.id}/[...page]`,
});
const { page, allPages } = paginatedResults;
// make sure the requested page number is valid, if not redirect to the first page of results
if (allPages.length && !page) {
return Astro.redirect(allPages[0]);
}
const themes = page.data;
---

<MainLayout title="Themes" image={{ src: '/og/themes.jpg', alt: 'Explore the possibilities' }}>
<SidebarLayout>
<Fragment slot="sidebar">
<SidebarPanel>
<Profile author={themes[0].Author} />
</SidebarPanel>
<SubmitTheme class="hidden lg:block" />
</Fragment>

<div
class="flex flex-col items-center gap-8 pb-10 pt-8 sm:px-4 sm:pb-12 md:gap-10 md:pb-16 lg:gap-12 lg:px-8 lg:pb-20 lg:pt-10 xl:px-10"
>
<CardGridGroup>
<header class="w-full">
<h1 class="heading-3 max-lg:hidden">{themes[0].Author.name}</h1>
</header>
<CardGrid class="w-full max-w-screen-xl sm:grid-cols-2 xl:grid-cols-3">
{themes.map((theme) => <ThemeCard as="h2" theme={theme} />)}
</CardGrid>
</CardGridGroup>
{
allPages.length > 1 && (
<Pagination restRoute page={page} allPages={allPages} class="mx-auto" />
)
}
</div>

<SubmitTheme class="lg:hidden" />
</SidebarLayout>
</MainLayout>

0 comments on commit 47b12d5

Please sign in to comment.