diff --git a/app/components/layout/SiteHeader.vue b/app/components/layout/SiteHeader.vue index a785fe5..07c9cf3 100755 --- a/app/components/layout/SiteHeader.vue +++ b/app/components/layout/SiteHeader.vue @@ -23,7 +23,9 @@ watch(() => route.path, () => { - todde.tv + todde + . + tv @@ -31,7 +33,7 @@ watch(() => route.path, () => { diff --git a/app/composables/useAllTestimonials.ts b/app/composables/useAllTestimonials.ts new file mode 100755 index 0000000..8aafcc9 --- /dev/null +++ b/app/composables/useAllTestimonials.ts @@ -0,0 +1,38 @@ +export interface LinkedTestimonial { + quote: string + author: string + role: string + linkTo: string +} + +/** + * Aggregates testimonials from projects and talks, sorted newest first. + * Uses talk date or project endDate/startDate for ordering. + */ +export function useAllTestimonials() { + return useAsyncData('all-testimonials', async () => { + const [projects, talks] = await Promise.all([ + queryCollection('projects').all(), + queryCollection('talks').all(), + ]) + + const result: (LinkedTestimonial & { _sortDate: string })[] = [] + + for (const p of projects) { + const sortDate = p.endDate ?? p.startDate + for (const t of p.testimonials ?? []) { + result.push({ ...t, linkTo: p.path, _sortDate: sortDate }) + } + } + + for (const talk of talks) { + for (const t of talk.testimonials ?? []) { + result.push({ ...t, linkTo: talk.path, _sortDate: talk.date }) + } + } + + result.sort((a, b) => b._sortDate.localeCompare(a._sortDate)) + + return result.map(({ _sortDate: _, ...rest }) => rest) + }) +} diff --git a/app/composables/useSortedProjects.ts b/app/composables/useSortedProjects.ts new file mode 100644 index 0000000..c2c0f14 --- /dev/null +++ b/app/composables/useSortedProjects.ts @@ -0,0 +1,32 @@ +/** + * Fetches all projects and returns them sorted by recency. + * Sort order: ongoing first, then by endDate, startDate, and stars. + * @param limit - Optional max number of projects to return. + */ +export function useSortedProjects(limit?: number) { + const key = limit ? `sorted-projects-${limit}` : 'sorted-projects' + + return useAsyncData(key, async () => { + const projects = await queryCollection('projects').all() + const sorted = [...projects].sort((a, b) => { + // 1. Ongoing (no endDate) before completed + const aOngoing = a.endDate == null + const bOngoing = b.endDate == null + if (aOngoing !== bOngoing) return aOngoing ? -1 : 1 + + // 2. Within completed: newest endDate first + if (!aOngoing && !bOngoing) { + const endCmp = b.endDate!.localeCompare(a.endDate!) + if (endCmp !== 0) return endCmp + } + + // 3. Newest startDate first + const startCmp = b.startDate.localeCompare(a.startDate) + if (startCmp !== 0) return startCmp + + // 4. More stars first + return (b.repoStars ?? 0) - (a.repoStars ?? 0) + }) + return limit ? sorted.slice(0, limit) : sorted + }) +} diff --git a/app/error.vue b/app/error.vue new file mode 100755 index 0000000..22a6018 --- /dev/null +++ b/app/error.vue @@ -0,0 +1,44 @@ + + + diff --git a/app/pages/index.vue b/app/pages/index.vue new file mode 100755 index 0000000..67a7224 --- /dev/null +++ b/app/pages/index.vue @@ -0,0 +1,201 @@ + + + diff --git a/app/pages/projects/index.vue b/app/pages/projects/index.vue index 1bed718..2affdec 100755 --- a/app/pages/projects/index.vue +++ b/app/pages/projects/index.vue @@ -9,28 +9,7 @@ defineOgImageComponent('Project', { description: 'Tools, experiments, and applications for 3D on the web, Vue/Nuxt, and developer tooling.', }) -const { data: projects } = await useAsyncData('projects', async () => { - const projects = await queryCollection('projects').all() - return projects.sort((a, b) => { - // 1. Ongoing (no endDate) before completed - const aOngoing = a.endDate == null - const bOngoing = b.endDate == null - if (aOngoing !== bOngoing) return aOngoing ? -1 : 1 - - // 2. Within completed: newest endDate first - if (!aOngoing && !bOngoing) { - const endCmp = b.endDate!.localeCompare(a.endDate!) - if (endCmp !== 0) return endCmp - } - - // 3. Newest startDate first - const startCmp = b.startDate.localeCompare(a.startDate) - if (startCmp !== 0) return startCmp - - // 4. More stars first - return (b.repoStars ?? 0) - (a.repoStars ?? 0) - }) -}) +const { data: projects } = await useSortedProjects()