Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/danielroe/roe.dev. This is the code and content for my personal website, built in Nuxt. roe.dev
Something went wrong. Try again.
5.7 kB · 165 lines
Vue
at feat/nuxt-v5
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166<script setup lang="ts">import type { Talk } from '#shared/cms/talks'
const { data: groups } = await useAsyncData( 'past-talks', async () => { try { const talks = await $fetch<Talk[]>('/api/talks')
const groupedTalks: Record<string, [Talk, ...Talk[]]> = {}
for (const talk of talks) { // Use group _id if available, otherwise use individual talk _id const groupKey = talk.group?._id || talk._id const groupTitle = talk.group?.title || talk.title const groupDescription = talk.group?.description || talk.description
if (groupedTalks[groupKey]) { groupedTalks[groupKey]!.push(talk) } else { // Create a talk-like object with group information const talkWithGroupInfo = { ...talk, title: groupTitle, description: groupDescription, } groupedTalks[groupKey] = [talkWithGroupInfo] } }
// Sort talks within each group by date (newest first) for (const group in groupedTalks) { groupedTalks[group]!.sort( (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(), ) }
const groups = Object.entries(groupedTalks).sort( ([_s1, [a]], [_s2, [b]]) => { return new Date(b.date).getTime() - new Date(a.date).getTime() }, )
return groups } catch (error) { console.error('Failed to fetch talks:', error) return [] } },)</script>
<template> <section class="flex flex-row flex-wrap gap-4 max-w-[37.50rem]"> <section v-for="[groupKey, talks] of groups" :key="groupKey" class="mb-2 w-full relative flex flex-col justify-end min-h-12 transition-all border-1 border-solid border-transparent after:text-transparent flex-[100%]" > <h2 class="flex flex-row items-center gap-2 text-xl font-normal"> {{ talks[0].title }} </h2> <div v-if="talks[0].description" class="text-base mb-1" > {{ talks[0].description }} </div> <article v-for="talk of talks" :key="talk.link" :class="{ 'opacity-60': !talk.video && !talk.link && !talk.slides && !talk.demo && !talk.repo }" :aria-label="talk.title" > <header class="flex flex-row mt-1"> <dl v-if="talk.date" class="flex flex-row items-center gap-4 leading-normal text-sm w-full" > <dt class="sr-only"> Date </dt> <dd class="md:min-w-24 uppercase opacity-60 text-xs flex-shrink-0"> <NuxtTime :datetime="talk.date" day="numeric" month="short" year="numeric" /> </dd> <dt v-if="talk.source" class="sr-only" > Where </dt> <dd v-if="talk.source" class="text-ellipsis line-clamp-1" > {{ talk.source }} </dd> </dl> <ExpandableTray v-if="talk.video || (talk.type === 'podcast' && talk.link) || talk.slides || talk.demo || talk.repo" class="ml-auto flex items-start" > <NuxtLink v-if="talk.video || (talk.type === 'podcast' && talk.link)" :href="talk.video || (talk.type === 'podcast' ? talk.link : '')" class="text-xs items-center f-tray-item active:text-primary hover:text-primary focus:text-primary transition-colors touch-action-manipulation" :aria-label="`${talk.video ? 'Watch' : 'Listen to'} ${talks[0]?.title}`" > <span v-if="talk.type === 'podcast' || talk.video" :class="talk.video ? 'i-ri:play-line' : 'i-ri:broadcast-line'" class="h-4 w-4 flex-shrink-0" aria-hidden="true" /> {{ talk.video ? `watch` : `listen` }} </NuxtLink> <NuxtLink v-if="talk.slides" class="text-xs items-center f-tray-item active:text-primary hover:text-primary focus:text-primary transition-colors touch-action-manipulation" :to="`/slides/${talk.slides}.pdf`" :aria-label="`Slides for ${talks[0]?.title}`" data-external > <span class="i-ri:presentation-fill" aria-hidden="true" /> slides </NuxtLink> <NuxtLink v-if="talk.demo" class="text-xs items-center f-tray-item active:text-primary hover:text-primary focus:text-primary transition-colors touch-action-manipulation" :to="talk.demo" :aria-label="`Demo for ${talks[0]?.title}`" data-external > <span class="i-tabler:sparkles" aria-hidden="true" /> demo </NuxtLink> <NuxtLink v-if="talk.repo" class="text-xs items-center f-tray-item active:text-primary hover:text-primary focus:text-primary transition-colors touch-action-manipulation" :to="talk.repo" :aria-label="`Repository for ${talks[0]?.title}`" data-external > <span class="i-ri:github-fill" aria-hidden="true" /> repo </NuxtLink> </ExpandableTray> </header> </article> </section> </section></template>