master
  1---
  2import type { BlogPostData } from '@/types/data';
  3import {
  4  getCategories,
  5  getCategoryUrl,
  6  getSortedPosts,
  7  getTagUrl,
  8} from '@/utils/content-utils';
  9import ProfileCard from '@components/aside/ProfileCard.astro';
 10import TOC from '@components/aside/TOC.astro';
 11import Button from '@components/widgets/Button.astro';
 12import MetaList from '@components/widgets/MetaList.astro';
 13import MainLayout from '@layouts/MainLayout.astro';
 14import { t } from '@utils/i18n';
 15import { Icon } from 'astro-icon/components';
 16
 17const categoriesMap = await getCategories();
 18const allPosts = await getSortedPosts();
 19
 20const categoryPosts = new Map<string, { body: string; data: BlogPostData }[]>();
 21for (const [category] of categoriesMap) {
 22  const posts = allPosts.filter((post) => post.data.category === category).slice(0, 3);
 23  categoryPosts.set(category, posts);
 24}
 25const uncategorizedPosts = allPosts.filter((post) => !post.data.category).slice(0, 3);
 26if (uncategorizedPosts.length > 0)
 27  categoryPosts.set(t.meta.unCategorized(), uncategorizedPosts);
 28---
 29
 30<MainLayout title={t.navigation.archive.categories()}>
 31  <div
 32    class="card bg-base-200 border-base-300 swup-transition-fade mx-auto flex flex-col items-center border px-6 py-4"
 33  >
 34    <div class="tooltip md:tooltip-right tooltip-bottom mx-auto w-fit">
 35      <h1 class="text-center text-3xl font-bold">{t.navigation.archive.categories()}</h1>
 36      <div class="tooltip-content flex flex-col">
 37        <div>
 38          {t.status.postsCount(allPosts.length) + ', '}
 39        </div>
 40        <div>
 41          {t.status.categoriesCount(categoriesMap.size)}
 42        </div>
 43      </div>
 44    </div>
 45    <ul
 46      class="timeline timeline-snap-icon timeline-vertical max-md:timeline-compact w-full p-4"
 47    >
 48      {
 49        Array.from(categoryPosts.entries()).map(([category, posts], index) => (
 50          <li>
 51            {index > 0 && <hr />}
 52            <div class="timeline-middle">
 53              <Icon
 54                name="material-symbols:add-circle-rounded"
 55                height="1.25rem"
 56                width="1.25rem"
 57              />
 58            </div>
 59            <div class:list={[`timeline-${index % 2 === 0 ? 'start' : 'end'}`, 'w-full']}>
 60              <div
 61                class:list={[
 62                  index % 2 === 0 && 'md:flex-row-reverse',
 63                  'mx-4 flex flex-row items-center justify-between',
 64                ]}
 65              >
 66                <h2 class="scroll-mt-20 text-2xl font-bold" id={`heading-${category}`}>
 67                  {category}
 68                  <span class="text-base-content/60 ml-2 text-base">
 69                    ({categoriesMap.get(category) || 0})
 70                  </span>
 71                </h2>
 72                <Button href={getCategoryUrl(category)} title={category} class="pl-3">
 73                  {t.button.more()}
 74                  <Icon
 75                    name="material-symbols:chevron-right-rounded"
 76                    height="1.5rem"
 77                    width="1.5rem"
 78                  />
 79                </Button>
 80              </div>
 81              <ul class="list">
 82                {posts.map(({ data }) => (
 83                  <li class="list-row">
 84                    <div class="list-col-grow">
 85                      <a
 86                        href={`/posts/${data.slug}`}
 87                        title={data.title}
 88                        class="text-lg font-bold"
 89                      >
 90                        {data.title}
 91                      </a>
 92                      <MetaList
 93                        class="text-base-content/60 mt-2 items-start text-sm"
 94                        metas={[
 95                          {
 96                            icon: 'material-symbols:category-outline-rounded',
 97                            text: data.category || t.meta.unCategorized(),
 98                            link: getCategoryUrl(data.category),
 99                          },
100                          {
101                            icon: 'material-symbols:tag-rounded',
102                            title: t.meta.tags(),
103                            group: data.tags.map((tag) => ({
104                              icon: 'material-symbols:tag-rounded',
105                              text: tag,
106                              link: getTagUrl(tag),
107                            })),
108                          },
109                        ]}
110                      />
111                    </div>
112                  </li>
113                ))}
114              </ul>
115            </div>
116            <hr />
117          </li>
118        ))
119      }
120    </ul>
121  </div>
122  <Fragment slot="aside-fixed">
123    <ProfileCard />
124  </Fragment>
125  <Fragment slot="aside-sticky">
126    <TOC
127      headings={Array.from(categoriesMap.keys()).map((category) => ({
128        text: category,
129        slug: `heading-${category}`,
130        depth: 2,
131      }))}
132    />
133  </Fragment>
134</MainLayout>