fix: resolve selected winners from populated CMS data

This commit is contained in:
syntaxbullet
2026-07-13 12:19:02 +02:00
parent 818f283e10
commit b0aec7ba94
4 changed files with 38 additions and 21 deletions

View File

@@ -42,6 +42,14 @@ export function useCmsCollection(collection: CmsRouteCollection) {
return useCmsRoute()?.lists?.[collection] || [] return useCmsRoute()?.lists?.[collection] || []
} }
export function resolveCmsRelationship(entry: unknown, docs: CmsRouteDoc[]) {
const entryID = entry && typeof entry === 'object' ? (entry as { id?: unknown }).id : entry
const populatedDoc = docs.find((doc) => String(doc.id) === String(entryID))
if (populatedDoc) return populatedDoc
return entry && typeof entry === 'object' ? (entry as CmsRouteDoc) : undefined
}
export function useApplicationPhase() { export function useApplicationPhase() {
return useCmsRoute()?.applicationPhase return useCmsRoute()?.applicationPhase
} }

View File

@@ -6,7 +6,7 @@ import TestimonialsSection from '@/spa/components/TestimonialsSection'
import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg' import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg'
import Image from '@/spa/components/ui/UnoptimizedImage' import Image from '@/spa/components/ui/UnoptimizedImage'
import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField' import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField'
import { useCmsCollection, useCmsRoute, type CmsRouteDoc } from '@/spa/cmsRoute' import { resolveCmsRelationship, useCmsCollection, useCmsRoute, type CmsRouteDoc } from '@/spa/cmsRoute'
import { WINNERS, type Winner } from '@/spa/data/winners' import { WINNERS, type Winner } from '@/spa/data/winners'
import { useIsMobile } from '@/spa/hooks/useIsMobile' import { useIsMobile } from '@/spa/hooks/useIsMobile'
import { Link } from '@/spa/router' import { Link } from '@/spa/router'
@@ -108,17 +108,6 @@ const normalizeWinner = (
} }
} }
const winnerFromRelationship = (
entry: unknown,
allDocs: CmsRouteDoc[],
placeholderImage: string,
fallbackWinner: typeof aboutContent.highlights.fallbackWinner,
) => {
if (entry && typeof entry === 'object') return normalizeWinner(entry as CmsRouteDoc, placeholderImage, fallbackWinner)
const doc = allDocs.find((item) => String(item.id) === String(entry))
return normalizeWinner(doc, placeholderImage, fallbackWinner)
}
const About: React.FC = () => { const About: React.FC = () => {
const isMobile = useIsMobile() const isMobile = useIsMobile()
const placeholderImage = usePreistraegerPlaceholderImage() const placeholderImage = usePreistraegerPlaceholderImage()
@@ -144,7 +133,7 @@ const About: React.FC = () => {
const highlightWinners = useMemo(() => { const highlightWinners = useMemo(() => {
const selected = fallbackArray<unknown>(highlights.selectedWinners, []) const selected = fallbackArray<unknown>(highlights.selectedWinners, [])
.map((entry) => winnerFromRelationship(entry, cmsWinners, placeholderImage, highlightFallbackWinner)) .map((entry) => normalizeWinner(resolveCmsRelationship(entry, cmsWinners), placeholderImage, highlightFallbackWinner))
.filter((winner): winner is Winner => Boolean(winner)) .filter((winner): winner is Winner => Boolean(winner))
if (selected.length) return selected.slice(0, 8) if (selected.length) return selected.slice(0, 8)

View File

@@ -11,7 +11,7 @@ import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg';
import { useIsMobile } from '@/spa/hooks/useIsMobile'; import { useIsMobile } from '@/spa/hooks/useIsMobile';
import Image from '@/spa/components/ui/UnoptimizedImage' import Image from '@/spa/components/ui/UnoptimizedImage'
import type { SpaApplicationPhaseData, SpaApplicationPhaseItem } from '@/spa/applicationPhase' import type { SpaApplicationPhaseData, SpaApplicationPhaseItem } from '@/spa/applicationPhase'
import { useApplicationPhase, useCmsCollection, useCmsRoute } from '@/spa/cmsRoute' import { resolveCmsRelationship, useApplicationPhase, useCmsCollection, useCmsRoute } from '@/spa/cmsRoute'
import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField' import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField'
import { homeContent } from '@/spa/homeContent' import { homeContent } from '@/spa/homeContent'
import { netzwerkContent } from '@/spa/netzwerkContent' import { netzwerkContent } from '@/spa/netzwerkContent'
@@ -276,13 +276,8 @@ const Home: React.FC = () => {
}; };
const showHeroSecondaryCta = !HIDE_MEMBERSHIP_HOME_HERO_CTA || !isMembershipCta(heroSecondaryCta); const showHeroSecondaryCta = !HIDE_MEMBERSHIP_HOME_HERO_CTA || !isMembershipCta(heroSecondaryCta);
const selectedWinnerDocs = selectedWinners const selectedWinnerDocs = selectedWinners
.map((selected) => { .map((selected) => resolveCmsRelationship(selected, cmsPreistraeger))
const selectedRecord = selected as { id?: string | number } .filter(Boolean)
const selectedID = typeof selected === 'object' && selected !== null ? selectedRecord.id : selected
const listDoc = cmsPreistraeger.find((doc) => String((doc as { id?: string | number }).id) === String(selectedID))
return listDoc || (typeof selected === 'object' ? selected : undefined)
})
.filter(Boolean)
const homeWinners = selectedWinnerDocs.length const homeWinners = selectedWinnerDocs.length
? selectedWinnerDocs ? selectedWinnerDocs
: (cmsPreistraeger.length ? cmsPreistraeger : WINNERS).filter((w) => Number((w as { year?: string | number }).year) === 2025).slice(0, 8); : (cmsPreistraeger.length ? cmsPreistraeger : WINNERS).filter((w) => Number((w as { year?: string | number }).year) === 2025).slice(0, 8);

View File

@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest'
import { resolveCmsRelationship, type CmsRouteDoc } from '@/spa/cmsRoute'
const fullWinner: CmsRouteDoc = {
id: 20,
title: 'Adelholzener Alpenquellen GmbH',
image: { id: 55, url: '/api/media/file/image%20(1).png' },
}
describe('CMS relationship resolution', () => {
it('prefers the fully populated collection document over a shallow relationship object', () => {
expect(resolveCmsRelationship({ id: 20, image: 55 }, [fullWinner])).toBe(fullWinner)
})
it('resolves an ID-only relationship', () => {
expect(resolveCmsRelationship(20, [fullWinner])).toBe(fullWinner)
})
it('preserves a populated relationship when no list document is available', () => {
const relationship = { id: 20, image: { id: 55, url: '/api/media/file/image%20(1).png' } }
expect(resolveCmsRelationship(relationship, [])).toBe(relationship)
})
})