summaryrefslogtreecommitdiffstats
path: root/src/components/organisms/layout/posts-list.tsx
blob: daf449138bc5679b509849ac78f04125463f42b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import Heading, { type HeadingLevel } from '@components/atoms/headings/heading';
import { FC } from 'react';
import { useIntl } from 'react-intl';
import Summary, { type SummaryProps } from './summary';
import styles from './posts-list.module.scss';
import ProgressBar from '@components/atoms/loaders/progress-bar';
import Button from '@components/atoms/buttons/button';

export type Post = SummaryProps & {
  /**
   * The post id.
   */
  id: string | number;
};

export type YearCollection = {
  [key: string]: Post[];
};

export type PostsListProps = {
  /**
   * True to display the posts by year. Default: false.
   */
  byYear?: boolean;
  /**
   * The posts data.
   */
  posts: Post[];
  /**
   * The posts heading level (hn).
   */
  titleLevel?: HeadingLevel;
  /**
   * The total posts number.
   */
  total: number;
};

/**
 * Create a collection of posts sorted by year.
 *
 * @param {Posts[]} data - A collection of posts.
 * @returns {YearCollection} The posts sorted by year.
 */
const sortPostsByYear = (data: Post[]): YearCollection => {
  const yearCollection: YearCollection = {};

  data.forEach((post) => {
    const postYear = new Date(post.meta.dates.publication)
      .getFullYear()
      .toString();
    yearCollection[postYear] = [...(yearCollection[postYear] || []), post];
  });

  return yearCollection;
};

/**
 * PostsList component
 *
 * Render a list of post summaries.
 */
const PostsList: FC<PostsListProps> = ({
  byYear = false,
  posts,
  titleLevel,
  total,
}) => {
  const intl = useIntl();

  /**
   * Retrieve the list of posts.
   *
   * @param {Posts[]} data - A collection fo posts.
   * @param {HeadingLevel} [headingLevel] - The posts heading level (hn).
   * @returns {JSX.Element} The list of posts.
   */
  const getList = (
    data: Post[],
    headingLevel: HeadingLevel = 2
  ): JSX.Element => {
    return (
      <ol className={styles.list}>
        {data.map(({ id, ...post }) => (
          <li key={id} className={styles.item}>
            <Summary {...post} titleLevel={headingLevel} />
          </li>
        ))}
      </ol>
    );
  };

  /**
   * Retrieve the list of posts.
   *
   * @returns {JSX.Element | JSX.Element[]} - The posts list.
   */
  const getPosts = (): JSX.Element | JSX.Element[] => {
    if (!byYear) return getList(posts);

    const postsPerYear = sortPostsByYear(posts);
    const years = Object.keys(postsPerYear).reverse();

    return years.map((year) => {
      return (
        <section key={year} className={styles.section}>
          <Heading level={2} className={styles.year}>
            {year}
          </Heading>
          {getList(postsPerYear[year], titleLevel)}
        </section>
      );
    });
  };

  const progressInfo = intl.formatMessage(
    {
      defaultMessage:
        '{articlesCount, plural, =0 {# loaded articles} one {# loaded article} other {# loaded articles}} out of a total of {total}',
      description: 'PostsList: loaded articles progress',
      id: '9MeLN3',
    },
    { articlesCount: posts.length, total: total }
  );

  const loadMore = intl.formatMessage({
    defaultMessage: 'Load more articles?',
    id: 'uaqd5F',
    description: 'PostsList: load more button',
  });

  return posts.length === 0 ? (
    <p>
      {intl.formatMessage({
        defaultMessage: 'No results found.',
        description: 'PostsList: no results',
        id: 'vK7Sxv',
      })}
    </p>
  ) : (
    <>
      {getPosts()}
      <ProgressBar
        min={1}
        max={total}
        current={posts.length}
        info={progressInfo}
      />
      <Button className={styles.btn}>{loadMore}</Button>
    </>
  );
};

export default PostsList;