aboutsummaryrefslogtreecommitdiffstats
path: root/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/events/[...id].astro168
-rw-r--r--src/pages/events/[...page].astro60
-rw-r--r--src/pages/index.astro90
-rw-r--r--src/pages/types/[...id].astro53
-rw-r--r--src/pages/types/index.astro35
5 files changed, 354 insertions, 52 deletions
diff --git a/src/pages/events/[...id].astro b/src/pages/events/[...id].astro
new file mode 100644
index 0000000..44d8ec4
--- /dev/null
+++ b/src/pages/events/[...id].astro
@@ -0,0 +1,168 @@
+---
+import Breadcrumbs from '@/components/Breadcrumbs.astro'
+import EventNavigation from '@/components/EventNavigation.astro'
+import TOCHeader from '@/components/TOCHeader.astro'
+import TOCSidebar from '@/components/TOCSidebar.astro'
+import { badgeVariants } from '@/components/ui/badge'
+import { Button } from '@/components/ui/button'
+import Layout from '@/layouts/Layout.astro'
+import {
+ getAdjacentEvents,
+ getAllEvents,
+ getTOCSections,
+} from '@/lib/data-utils'
+import { formatDate, iconMappings } from '@/lib/utils'
+import { Icon } from 'astro-icon/components'
+import { Image } from 'astro:assets'
+import { render } from 'astro:content'
+
+export async function getStaticPaths() {
+ const events = await getAllEvents()
+ return events.map((event) => ({
+ params: { id: event.id },
+ props: event,
+ }))
+}
+
+const event = Astro.props
+const currentPostId = Astro.params.id
+const navigation = await getAdjacentEvents(currentPostId)
+const { Content, headings } = await render(event)
+
+const tocSections = await getTOCSections(currentPostId)
+---
+
+<Layout>
+ {
+ headings?.length > 0 &&
+ !(headings.length === 1 && headings[0].text === event.data.title) && (
+ <TOCHeader slot="table-of-contents" headings={headings} />
+ )
+ }
+
+ <section
+ class="grid grid-cols-[minmax(0px,1fr)_min(calc(var(--breakpoint-md)-2rem),100%)_minmax(0px,1fr)] gap-y-6"
+ >
+ <div class="col-start-2">
+ <Breadcrumbs
+ items={[{ href: '/events', label: 'Events', icon: 'lucide:calendar' }]}
+ />
+ </div>
+
+ {
+ event.data.image && (
+ <Image
+ src={event.data.image}
+ alt={event.data.title}
+ width={1200}
+ height={630}
+ class="col-span-full mx-auto w-full max-w-5xl object-cover"
+ />
+ )
+ }
+
+ <section class="col-start-2 flex flex-col gap-y-6 text-center">
+ <div class="flex flex-col">
+ <h1
+ class="mb-2 scroll-mt-31 text-3xl leading-tight font-medium sm:text-4xl"
+ id="event-title"
+ >
+ {event.data.title}
+ </h1>
+
+ <div
+ class="text-muted-foreground divide-border mb-4 flex flex-col items-center justify-center divide-y text-xs sm:flex-row sm:flex-wrap sm:divide-x sm:divide-y-0 sm:text-sm"
+ >
+ <div
+ class="flex w-full items-center justify-center gap-2 py-2 sm:w-fit sm:px-2 sm:py-0 first:sm:pl-0 last:sm:pr-0"
+ >
+ <span>{formatDate(event.data.date)}</span>
+ </div>
+ </div>
+ <div class="flex flex-wrap justify-center gap-2">
+ {
+ event.data.type && (
+ <a
+ href={`/types/${event.data.type}`}
+ class={badgeVariants({ variant: 'muted' })}
+ >
+ <Icon
+ name={iconMappings[event.data.type] || 'lucide:hash'}
+ class="size-3"
+ />
+ {event.data.type}
+ </a>
+ )
+ }
+ </div>
+ </div>
+ </section>
+
+ {
+ tocSections.length > 0 && (
+ <TOCSidebar sections={tocSections} currentPostId={currentPostId} />
+ )
+ }
+
+ <article class="prose col-start-2 max-w-none">
+ <Content />
+ </article>
+
+ <EventNavigation
+ newerEvent={navigation.newer}
+ olderEvent={navigation.older}
+ />
+ </section>
+
+ <Button
+ variant="outline"
+ size="icon"
+ className="group fixed right-8 bottom-8 z-50 hidden"
+ id="scroll-to-top"
+ title="Scroll to top"
+ aria-label="Scroll to top"
+ >
+ <Icon
+ name="lucide:arrow-up"
+ class="mx-auto size-4 transition-all group-hover:-translate-y-0.5"
+ />
+ </Button>
+
+ <script>
+ document.addEventListener('astro:page-load', () => {
+ const scrollToTopButton = document.getElementById('scroll-to-top')
+ const footer = document.querySelector('footer')
+
+ if (scrollToTopButton && footer) {
+ scrollToTopButton.addEventListener('click', () => {
+ window.scrollTo({ top: 0, behavior: 'smooth' })
+ })
+
+ window.addEventListener('scroll', () => {
+ const footerRect = footer.getBoundingClientRect()
+ const isFooterVisible = footerRect.top <= window.innerHeight
+
+ scrollToTopButton.classList.toggle(
+ 'hidden',
+ window.scrollY <= 300 || isFooterVisible,
+ )
+ })
+ }
+ })
+ </script>
+</Layout>
+
+<script>
+ const ensureKatexStyles = () => {
+ if (!document.querySelector('.katex')) return
+ if (document.querySelector('link[href*="katex.min.css"]')) return
+
+ const link = document.createElement('link')
+ link.rel = 'stylesheet'
+ link.href = 'https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css'
+ document.head.appendChild(link)
+ }
+
+ document.addEventListener('astro:page-load', ensureKatexStyles)
+ document.addEventListener('astro:after-swap', ensureKatexStyles)
+</script>
diff --git a/src/pages/events/[...page].astro b/src/pages/events/[...page].astro
new file mode 100644
index 0000000..2625ab0
--- /dev/null
+++ b/src/pages/events/[...page].astro
@@ -0,0 +1,60 @@
+---
+import Breadcrumbs from '@/components/Breadcrumbs.astro'
+import EventCard from '@/components/EventCard.astro'
+import PageHead from '@/components/PageHead.astro'
+import PaginationComponent from '@/components/ui/pagination'
+import { SITE } from '@/consts'
+import Layout from '@/layouts/Layout.astro'
+import { getAllEvents, groupEventsByYear } from '@/lib/data-utils'
+import type { PaginateFunction } from 'astro'
+
+export async function getStaticPaths({
+ paginate,
+}: {
+ paginate: PaginateFunction
+}) {
+ const allEvents = await getAllEvents()
+ return paginate(allEvents, { pageSize: SITE.eventsPerPage })
+}
+
+const { page } = Astro.props
+
+const eventsByYear = groupEventsByYear(page.data)
+const years = Object.keys(eventsByYear).sort(
+ (a, b) => parseInt(b) - parseInt(a),
+)
+---
+
+<Layout class="max-w-3xl">
+ <PageHead slot="head" title="Events" />
+ <Breadcrumbs
+ items={[
+ { label: 'Events', href: '/events', icon: 'lucide:calendar-range' },
+ { label: `Page ${page.currentPage}`, icon: 'lucide:calendar' },
+ ]}
+ />
+
+ <div class="flex min-h-[calc(100vh-18rem)] flex-col gap-y-8">
+ {
+ years.map((year) => (
+ <section class="flex flex-col gap-y-4">
+ <div class="font-medium">{year}</div>
+ <ul class="flex flex-col gap-4">
+ {eventsByYear[year].map((event) => (
+ <li>
+ <EventCard entry={event} />
+ </li>
+ ))}
+ </ul>
+ </section>
+ ))
+ }
+ </div>
+
+ <PaginationComponent
+ currentPage={page.currentPage}
+ totalPages={page.lastPage}
+ baseUrl="/events/"
+ client:load
+ />
+</Layout>
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 019283d..9549a7c 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,13 +1,15 @@
---
import BlogCard from '@/components/BlogCard.astro'
+import EventCard from '@/components/EventCard.astro'
import Link from '@/components/Link.astro'
import PageHead from '@/components/PageHead.astro'
import { buttonVariants } from '@/components/ui/button'
import { SITE } from '@/consts'
import Layout from '@/layouts/Layout.astro'
-import { getRecentPosts } from '@/lib/data-utils'
+import { getRecentPosts, getRecentEvents } from '@/lib/data-utils'
const blog = await getRecentPosts(SITE.featuredPostCount)
+const events = await getRecentEvents(SITE.featuredEventsCount)
---
<Layout class="max-w-3xl">
@@ -16,69 +18,53 @@ const blog = await getRecentPosts(SITE.featuredPostCount)
<div class="flex flex-col space-y-1.5 p-6">
<h3 class="text-3xl leading-none font-medium">er·u·dite</h3>
<p class="text-muted-foreground text-sm">
- /ˈer(y)əˌdīt/ &bull; <span class="font-medium">adjective</span>
+ /ˈer(y)əˌdīt/ &bull; <span class="font-medium">adjective</span>
</p>
</div>
- <div class="p-6 pt-0">
- <p class="text-muted-foreground mb-2 text-sm">
- astro-erudite is an opinionated, unstyled static blogging template built
- with <Link
- href="https://astro.build"
- class="text-foreground"
- external
- underline>Astro</Link
- >, <Link
- href="https://tailwindcss.com"
- class="text-foreground"
- external
- underline>Tailwind</Link
- >, and <Link
- href="https://ui.shadcn.com"
- class="text-foreground"
- external
- underline>shadcn/ui</Link
- >. Extraordinarily loosely based on the <Link
- href="https://astro-micro.vercel.app/"
- class="text-foreground"
- external
- underline>Astro Micro</Link
- > theme.
- </p>
- <p class="text-muted-foreground text-sm">
- To use this template, check out the <Link
- href="https://github.com/jktrn/astro-erudite"
- class="text-foreground"
- underline
- external>GitHub</Link
- > repository. To learn more about why this template exists, read this blog
- post: <Link
- href="/blog/the-state-of-static-blogs"
- class="text-foreground"
- underline>The State of Static Blogs in 2024</Link
- >.
- </p>
+ </section>
+ <section class="flex flex-col gap-y-4">
+ <h2 class="text-2xl font-medium">Latest events</h2>
+ <ul class="flex flex-col gap-y-4">
+ {
+ events.map((event) => (
+ <li>
+ <EventCard entry={event} />
+ </li>
+ ))
+ }
+ </ul>
+ <div class="flex justify-center">
+ <Link
+ href="/events"
+ class={buttonVariants({ variant: 'ghost' }) + ' group'}
+ >
+ See all posts <span
+ class="ml-1.5 transition-transform group-hover:translate-x-1"
+ >&rarr;</span
+ >
+ </Link>
</div>
</section>
<section class="flex flex-col gap-y-4">
<h2 class="text-2xl font-medium">Latest posts</h2>
<ul class="flex flex-col gap-y-4">
{
- blog.map((post) => (
- <li>
- <BlogCard entry={post} />
- </li>
- ))
+ blog.map((post) => (
+ <li>
+ <BlogCard entry={post} />
+ </li>
+ ))
}
</ul>
<div class="flex justify-center">
<Link
- href="/blog"
- class={buttonVariants({ variant: 'ghost' }) + ' group'}
- >
- See all posts <span
- class="ml-1.5 transition-transform group-hover:translate-x-1"
- >&rarr;</span
- >
+ href="/blog"
+ class={buttonVariants({ variant: 'ghost' }) + ' group'}
+ >
+ See all posts <span
+ class="ml-1.5 transition-transform group-hover:translate-x-1"
+ >&rarr;</span
+ >
</Link>
</div>
</section>
diff --git a/src/pages/types/[...id].astro b/src/pages/types/[...id].astro
new file mode 100644
index 0000000..26640a9
--- /dev/null
+++ b/src/pages/types/[...id].astro
@@ -0,0 +1,53 @@
+---
+import Breadcrumbs from '@/components/Breadcrumbs.astro'
+import PageHead from '@/components/PageHead.astro'
+import Layout from '@/layouts/Layout.astro'
+import { getAllTypes, getEventsByType } from '@/lib/data-utils'
+import { capitalizeFirstLetter, iconMappings } from '@/lib/utils'
+import EventCard from '../../components/EventCard.astro'
+
+export async function getStaticPaths() {
+ const typeMap = await getAllTypes()
+ const uniqueTags = Array.from(typeMap.keys())
+
+ return Promise.all(
+ uniqueTags.map(async (type) => {
+ const events = await getEventsByType(type)
+ return {
+ params: { id: type },
+ props: {
+ type,
+ events,
+ },
+ }
+ }),
+ )
+}
+
+const { type, events } = Astro.props
+---
+
+<Layout class="max-w-3xl">
+ <PageHead
+ slot="head"
+ title={`${capitalizeFirstLetter(type)} Events`}
+ description={`A collection of ${type} events.`}
+ noindex
+ />
+ <Breadcrumbs
+ items={[
+ { href: '/types', label: 'Event types', icon: 'lucide:calendar' },
+ { label: type, icon: iconMappings[type] || 'lucide:ticket' },
+ ]}
+ />
+
+ <ul class="flex flex-col gap-y-4">
+ {
+ events.map((event) => (
+ <li>
+ <EventCard entry={event} />
+ </li>
+ ))
+ }
+ </ul>
+</Layout>
diff --git a/src/pages/types/index.astro b/src/pages/types/index.astro
new file mode 100644
index 0000000..c269c22
--- /dev/null
+++ b/src/pages/types/index.astro
@@ -0,0 +1,35 @@
+---
+import Breadcrumbs from '@/components/Breadcrumbs.astro'
+import Link from '@/components/Link.astro'
+import PageHead from '@/components/PageHead.astro'
+import { badgeVariants } from '@/components/ui/badge'
+import Layout from '@/layouts/Layout.astro'
+import { getAllTypes } from '@/lib/data-utils'
+import { iconMappings } from '@/lib/utils'
+import { Icon } from 'astro-icon/components'
+
+const types = await getAllTypes()
+---
+
+<Layout class="max-w-3xl">
+ <PageHead slot="head" title="Tags" />
+ <Breadcrumbs items={[{ label: 'Tags', icon: 'lucide:tags' }]} />
+
+ <div class="flex flex-col gap-4">
+ <div class="flex flex-wrap gap-2">
+ {console.log([...types.entries()].map((a) => console.log(a)))}
+ {
+ [...types.entries()].map(([type, count]) => (
+ <Link
+ href={`/types/${type}`}
+ class={badgeVariants({ variant: 'muted' })}
+ >
+ <Icon name={iconMappings[type] || 'lucide:hash'} class="size-3" />
+ {type}
+ <span class="text-muted-foreground ml-1.5">({count})</span>
+ </Link>
+ ))
+ }
+ </div>
+ </div>
+</Layout>