The Node.js® Website
1import { useTranslations } from 'next-intl';
2import type { FC } from 'react';
3
4import AvatarGroup from '@/components/Common/AvatarGroup';
5import FormattedTime from '@/components/Common/FormattedTime';
6import Preview from '@/components/Common/Preview';
7import Link from '@/components/Link';
8import { mapBlogCategoryToPreviewType } from '@/util/blogUtils';
9
10import styles from './index.module.css';
11
12// @todo: this should probably be a global type?
13type Author = { fullName: string; src: string };
14
15type BlogPostCardProps = {
16 title: string;
17 category: string;
18 description?: string;
19 authors?: Array<Author>;
20 date?: Date;
21 slug?: string;
22};
23
24const BlogPostCard: FC<BlogPostCardProps> = ({
25 title,
26 slug,
27 category,
28 description,
29 authors = [],
30 date,
31}) => {
32 const t = useTranslations();
33
34 const avatars = authors.map(({ fullName, src }) => ({ alt: fullName, src }));
35
36 const type = mapBlogCategoryToPreviewType(category);
37
38 return (
39 <article className={styles.container}>
40 <Link href={slug} aria-label={title}>
41 <Preview title={title} type={type} />
42 </Link>
43
44 <Link href={`/blog/${category}`} className={styles.subtitle}>
45 {t(`layouts.blog.categories.${category}`)}
46 </Link>
47
48 <Link href={slug} className={styles.title}>
49 {title}
50 </Link>
51
52 {description && <p className={styles.description}>{description}</p>}
53
54 <footer className={styles.footer}>
55 <AvatarGroup avatars={avatars ?? []} />
56
57 <div className={styles.author}>
58 {avatars && <p>{avatars.map(({ alt }) => alt).join(', ')}</p>}
59
60 {date && <FormattedTime date={date} />}
61 </div>
62 </footer>
63 </article>
64 );
65};
66
67export default BlogPostCard;