feat: manage about highlight fallback media via payload
This commit is contained in:
@@ -129,6 +129,7 @@ export const aboutFields: Field[] = [
|
|||||||
type: 'group',
|
type: 'group',
|
||||||
admin: sectionAdmin('Header copy and editor-selected winners for the photo grid.'),
|
admin: sectionAdmin('Header copy and editor-selected winners for the photo grid.'),
|
||||||
fields: [
|
fields: [
|
||||||
|
uploadField('fallbackImage', 'Fallback winner image', `Current frontend fallback image: /images/${aboutContent.highlights.fallbackImageFilename}`),
|
||||||
text('eyebrow', 'Eyebrow', aboutContent.highlights.eyebrow),
|
text('eyebrow', 'Eyebrow', aboutContent.highlights.eyebrow),
|
||||||
textarea('heading', 'Heading', aboutContent.highlights.heading),
|
textarea('heading', 'Heading', aboutContent.highlights.heading),
|
||||||
ctaGroup('cta', 'CTA', aboutContent.highlights.cta.label, aboutContent.highlights.cta.url),
|
ctaGroup('cta', 'CTA', aboutContent.highlights.cta.label, aboutContent.highlights.cta.url),
|
||||||
|
|||||||
@@ -1219,6 +1219,10 @@ export interface Page {
|
|||||||
* Header copy and editor-selected winners for the photo grid.
|
* Header copy and editor-selected winners for the photo grid.
|
||||||
*/
|
*/
|
||||||
highlights?: {
|
highlights?: {
|
||||||
|
/**
|
||||||
|
* Current frontend fallback image: /images/gala-saal-overview.jpg
|
||||||
|
*/
|
||||||
|
fallbackImage?: (number | null) | Media;
|
||||||
eyebrow?: string | null;
|
eyebrow?: string | null;
|
||||||
heading?: string | null;
|
heading?: string | null;
|
||||||
cta?: {
|
cta?: {
|
||||||
@@ -4101,6 +4105,7 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
highlights?:
|
highlights?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
|
fallbackImage?: T;
|
||||||
eyebrow?: T;
|
eyebrow?: T;
|
||||||
heading?: T;
|
heading?: T;
|
||||||
cta?:
|
cta?:
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ async function main() {
|
|||||||
const page = pageResult.docs[0]
|
const page = pageResult.docs[0]
|
||||||
if (!page) throw new Error('About page not found. Expected spaPath=/der-bmp or slug=der-bmp/about.')
|
if (!page) throw new Error('About page not found. Expected spaPath=/der-bmp or slug=der-bmp/about.')
|
||||||
|
|
||||||
const [heroImage, prizeImage, mittelstandImage, winnerResult] = await Promise.all([
|
const [heroImage, prizeImage, mittelstandImage, fallbackWinnerImage, winnerResult] = await Promise.all([
|
||||||
mediaByFilename(payload, aboutContent.hero.backgroundImageFilename),
|
mediaByFilename(payload, aboutContent.hero.backgroundImageFilename),
|
||||||
mediaByFilename(payload, aboutContent.prize.imageFilename),
|
mediaByFilename(payload, aboutContent.prize.imageFilename),
|
||||||
mediaByFilename(payload, aboutContent.mittelstand.imageFilename),
|
mediaByFilename(payload, aboutContent.mittelstand.imageFilename),
|
||||||
|
mediaByFilename(payload, aboutContent.highlights.fallbackImageFilename),
|
||||||
payload.find({
|
payload.find({
|
||||||
collection: 'preistraeger',
|
collection: 'preistraeger',
|
||||||
limit: 8,
|
limit: 8,
|
||||||
@@ -49,6 +50,7 @@ async function main() {
|
|||||||
const { backgroundImageFilename: _heroFilename, ...heroContent } = aboutContent.hero
|
const { backgroundImageFilename: _heroFilename, ...heroContent } = aboutContent.hero
|
||||||
const { imageFilename: _prizeFilename, ...prizeContent } = aboutContent.prize
|
const { imageFilename: _prizeFilename, ...prizeContent } = aboutContent.prize
|
||||||
const { imageFilename: _mittelstandFilename, ...mittelstandContent } = aboutContent.mittelstand
|
const { imageFilename: _mittelstandFilename, ...mittelstandContent } = aboutContent.mittelstand
|
||||||
|
const { fallbackImageFilename: _highlightFallbackFilename, ...highlightsContent } = aboutContent.highlights
|
||||||
|
|
||||||
await payload.update({
|
await payload.update({
|
||||||
collection: 'pages',
|
collection: 'pages',
|
||||||
@@ -73,7 +75,8 @@ async function main() {
|
|||||||
body: aboutContent.mittelstand.body.map((text) => ({ text })),
|
body: aboutContent.mittelstand.body.map((text) => ({ text })),
|
||||||
},
|
},
|
||||||
highlights: {
|
highlights: {
|
||||||
...aboutContent.highlights,
|
...highlightsContent,
|
||||||
|
fallbackImage: fallbackWinnerImage,
|
||||||
selectedWinners: winnerResult.docs.map((winner) => winner.id),
|
selectedWinners: winnerResult.docs.map((winner) => winner.id),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export const aboutContent = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
highlights: {
|
highlights: {
|
||||||
|
fallbackImageFilename: 'gala-saal-overview.jpg',
|
||||||
eyebrow: 'Preisträger-Highlights',
|
eyebrow: 'Preisträger-Highlights',
|
||||||
heading: 'AUSGEZEICHNETE 2025',
|
heading: 'AUSGEZEICHNETE 2025',
|
||||||
cta: { label: 'Alle Preisträger', url: '/preistraeger' },
|
cta: { label: 'Alle Preisträger', url: '/preistraeger' },
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type AboutCms = Partial<typeof aboutContent> & {
|
|||||||
hero?: Partial<typeof aboutContent.hero> & { backgroundImage?: unknown }
|
hero?: Partial<typeof aboutContent.hero> & { backgroundImage?: unknown }
|
||||||
prize?: Partial<typeof aboutContent.prize> & { image?: unknown }
|
prize?: Partial<typeof aboutContent.prize> & { image?: unknown }
|
||||||
mittelstand?: Partial<typeof aboutContent.mittelstand> & { image?: unknown }
|
mittelstand?: Partial<typeof aboutContent.mittelstand> & { image?: unknown }
|
||||||
highlights?: Partial<typeof aboutContent.highlights> & { selectedWinners?: unknown[] | null }
|
highlights?: Partial<typeof aboutContent.highlights> & { fallbackImage?: unknown; selectedWinners?: unknown[] | null }
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextRow = { text?: string | null }
|
type TextRow = { text?: string | null }
|
||||||
@@ -63,7 +63,7 @@ function HighlightedText({ text, highlight }: { text: string; highlight: string
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeWinner = (doc: CmsRouteDoc | undefined): Winner | undefined => {
|
const normalizeWinner = (doc: CmsRouteDoc | undefined, fallbackImage: string): Winner | undefined => {
|
||||||
if (!doc) return undefined
|
if (!doc) return undefined
|
||||||
return {
|
return {
|
||||||
id: String(doc.id),
|
id: String(doc.id),
|
||||||
@@ -72,7 +72,7 @@ const normalizeWinner = (doc: CmsRouteDoc | undefined): Winner | undefined => {
|
|||||||
category: String(doc.category || 'Preisträger'),
|
category: String(doc.category || 'Preisträger'),
|
||||||
year: Number(doc.year || aboutContent.highlights.year),
|
year: Number(doc.year || aboutContent.highlights.year),
|
||||||
type: String(doc.awardType || 'Auszeichnung'),
|
type: String(doc.awardType || 'Auszeichnung'),
|
||||||
img: mediaUrl(doc.image, '/images/gala-saal-overview.jpg'),
|
img: mediaUrl(doc.image, fallbackImage),
|
||||||
shortDesc: String(doc.shortDesc || doc.description || doc.meta?.description || ''),
|
shortDesc: String(doc.shortDesc || doc.description || doc.meta?.description || ''),
|
||||||
longDesc: String(doc.longDesc || doc.description || doc.meta?.description || ''),
|
longDesc: String(doc.longDesc || doc.description || doc.meta?.description || ''),
|
||||||
quote: String(doc.quote || ''),
|
quote: String(doc.quote || ''),
|
||||||
@@ -85,10 +85,10 @@ const normalizeWinner = (doc: CmsRouteDoc | undefined): Winner | undefined => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const winnerFromRelationship = (entry: unknown, allDocs: CmsRouteDoc[]) => {
|
const winnerFromRelationship = (entry: unknown, allDocs: CmsRouteDoc[], fallbackImage: string) => {
|
||||||
if (entry && typeof entry === 'object') return normalizeWinner(entry as CmsRouteDoc)
|
if (entry && typeof entry === 'object') return normalizeWinner(entry as CmsRouteDoc, fallbackImage)
|
||||||
const doc = allDocs.find((item) => String(item.id) === String(entry))
|
const doc = allDocs.find((item) => String(item.id) === String(entry))
|
||||||
return normalizeWinner(doc)
|
return normalizeWinner(doc, fallbackImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
const About: React.FC = () => {
|
const About: React.FC = () => {
|
||||||
@@ -98,16 +98,20 @@ const About: React.FC = () => {
|
|||||||
const hero = { ...aboutContent.hero, ...(cms.hero || {}) }
|
const hero = { ...aboutContent.hero, ...(cms.hero || {}) }
|
||||||
const prize = { ...aboutContent.prize, ...(cms.prize || {}) }
|
const prize = { ...aboutContent.prize, ...(cms.prize || {}) }
|
||||||
const mittelstand = { ...aboutContent.mittelstand, ...(cms.mittelstand || {}) }
|
const mittelstand = { ...aboutContent.mittelstand, ...(cms.mittelstand || {}) }
|
||||||
const highlights = { ...aboutContent.highlights, ...(cms.highlights || {}) }
|
const highlights = useMemo(() => ({ ...aboutContent.highlights, ...(cms.highlights || {}) }), [cms.highlights])
|
||||||
const testimonials = { ...aboutContent.testimonials, ...(cms.testimonials || {}) }
|
const testimonials = { ...aboutContent.testimonials, ...(cms.testimonials || {}) }
|
||||||
const history = { ...aboutContent.history, ...(cms.history || {}) }
|
const history = { ...aboutContent.history, ...(cms.history || {}) }
|
||||||
const goals = { ...aboutContent.goals, ...(cms.goals || {}) }
|
const goals = { ...aboutContent.goals, ...(cms.goals || {}) }
|
||||||
const values = { ...aboutContent.values, ...(cms.values || {}) }
|
const values = { ...aboutContent.values, ...(cms.values || {}) }
|
||||||
const cta = { ...aboutContent.cta, ...(cms.cta || {}) }
|
const cta = { ...aboutContent.cta, ...(cms.cta || {}) }
|
||||||
|
const highlightFallbackImage = mediaUrl(
|
||||||
|
highlights.fallbackImage,
|
||||||
|
`/images/${aboutContent.highlights.fallbackImageFilename}`,
|
||||||
|
)
|
||||||
|
|
||||||
const highlightWinners = useMemo(() => {
|
const highlightWinners = useMemo(() => {
|
||||||
const selected = fallbackArray<unknown>(highlights.selectedWinners, [])
|
const selected = fallbackArray<unknown>(highlights.selectedWinners, [])
|
||||||
.map((entry) => winnerFromRelationship(entry, cmsWinners))
|
.map((entry) => winnerFromRelationship(entry, cmsWinners, highlightFallbackImage))
|
||||||
.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)
|
||||||
@@ -116,12 +120,12 @@ const About: React.FC = () => {
|
|||||||
Boolean(winner) && winner?.year === Number(highlights.year || aboutContent.highlights.year)
|
Boolean(winner) && winner?.year === Number(highlights.year || aboutContent.highlights.year)
|
||||||
|
|
||||||
const cmsByYear = cmsWinners
|
const cmsByYear = cmsWinners
|
||||||
.map(normalizeWinner)
|
.map((winner) => normalizeWinner(winner, highlightFallbackImage))
|
||||||
.filter(isWinnerFromYear)
|
.filter(isWinnerFromYear)
|
||||||
.slice(0, 8)
|
.slice(0, 8)
|
||||||
|
|
||||||
return cmsByYear.length ? cmsByYear : FALLBACK_HIGHLIGHTS
|
return cmsByYear.length ? cmsByYear : FALLBACK_HIGHLIGHTS
|
||||||
}, [cmsWinners, highlights.selectedWinners, highlights.year])
|
}, [cmsWinners, highlightFallbackImage, highlights])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="animate-fade-in">
|
<div className="animate-fade-in">
|
||||||
|
|||||||
Reference in New Issue
Block a user