feat: Add fresh database setup script and media handling improvements

- Introduced a new script for setting up a fresh database with ordered migration tasks.
- Added a utility function to find media by filename, improving media handling in various scripts.
- Updated preload scripts to utilize the new media fetching utility.
- Refactored datenschutz, impressum, and mitglied-werden fields to use stable IDs and anchor IDs.
- Enhanced home and network pages to accommodate new field structures.
- Added new preload script for global CMS settings.
- Improved code readability and organization across multiple files.
This commit is contained in:
syntaxbullet
2026-06-22 17:20:45 +02:00
parent 09a43062a0
commit 9545f2513d
30 changed files with 359 additions and 171 deletions

View File

@@ -21,6 +21,11 @@ const sectionAdmin = (description: string) => ({
initCollapsed: true,
})
const sectionsDefault = datenschutzContent.sections.map(({ id, ...section }) => ({
...section,
anchorId: id,
}))
export const datenschutzFields: Field[] = [
{
name: 'datenschutz',
@@ -60,9 +65,9 @@ export const datenschutzFields: Field[] = [
type: 'array',
dbName: 'data_sections',
admin: sectionAdmin('Each section controls one heading, table-of-contents label, and ordered JSON body blocks.'),
defaultValue: datenschutzContent.sections,
defaultValue: sectionsDefault,
fields: [
text('id', 'Anchor ID'),
text('anchorId', 'Anchor ID'),
text('tocLabel', 'TOC label'),
text('title', 'Section title'),
{

View File

@@ -180,8 +180,20 @@ export const homeFields: Field[] = [
maxRows: 3,
defaultValue: homeContent.status.phases,
fields: [
text('num', 'Slide number'), text('tag', 'Badge'), { name: 'pulse', label: 'Show pulse dot', type: 'checkbox', defaultValue: true }, text('phase', 'Phase label'), text('headline', 'Headline'), textarea('body', 'Body copy'),
{ name: 'cta', label: 'Slide CTA', type: 'group', fields: linkGroup('', '') }, text('accent', 'Accent color'), text('bg', 'Background color'), text('glow', 'Glow color'), text('meta', 'Meta label'), text('successApplications', 'Success applications number'), text('successWinners', 'Success winners number'), { name: 'membershipCta', label: 'Membership CTA', type: 'group', fields: linkGroup('Mitglied werden', '/mitglied-werden') },
text('num', 'Slide number'),
text('tag', 'Badge'),
{ name: 'pulse', label: 'Show pulse dot', type: 'checkbox', defaultValue: true },
text('phase', 'Phase label'),
text('headline', 'Headline'),
textarea('body', 'Body copy'),
{ name: 'cta', label: 'Slide CTA', type: 'group', fields: linkGroup('', '') },
text('accent', 'Accent color'),
text('bg', 'Background color'),
text('glow', 'Glow color'),
text('meta', 'Meta label'),
text('successApplications', 'Success applications number'),
text('successWinners', 'Success winners number'),
{ name: 'membershipCta', label: 'Membership CTA', type: 'group', fields: linkGroup('Mitglied werden', '/mitglied-werden') },
],
},
text('noActionLabel', 'No CTA fallback label', homeContent.status.noActionLabel),

View File

@@ -21,6 +21,11 @@ const sectionAdmin = (description: string) => ({
initCollapsed: true,
})
const sectionsDefault = impressumContent.sections.map(({ id, ...section }) => ({
...section,
anchorId: id,
}))
export const impressumFields: Field[] = [
{
name: 'impressum',
@@ -60,9 +65,9 @@ export const impressumFields: Field[] = [
type: 'array',
dbName: 'imp_sections',
admin: sectionAdmin('Each section controls one heading, table-of-contents label, and ordered JSON body blocks.'),
defaultValue: impressumContent.sections,
defaultValue: sectionsDefault,
fields: [
text('id', 'Anchor ID'),
text('anchorId', 'Anchor ID'),
text('tocLabel', 'TOC label'),
text('title', 'Section title'),
{

View File

@@ -46,6 +46,10 @@ const sectionAdmin = (description: string) => ({
const statFields: Field[] = [text('value', 'Value'), text('label', 'Label')]
const factFields: Field[] = [text('num', 'Number'), text('label', 'Label'), textarea('body', 'Body')]
const faqFields: Field[] = [text('q', 'Question'), textarea('a', 'Answer')]
const applicationOptionDefaults = mitgliedWerdenContent.applicationOptions.items.map(({ id, ...item }) => ({
...item,
stableId: id,
}))
const testimonialFields: Field[] = [
textarea('quote', 'Quote'),
text('initials', 'Initials'),
@@ -204,9 +208,9 @@ export const mitgliedWerdenFields: Field[] = [
label: 'Options',
type: 'array',
dbName: 'member_app_options',
defaultValue: mitgliedWerdenContent.applicationOptions.items,
defaultValue: applicationOptionDefaults,
fields: [
text('id', 'Stable ID'),
text('stableId', 'Stable ID'),
text('number', 'Number'),
text('eyebrow', 'Eyebrow'),
text('title', 'Title'),

View File

@@ -59,7 +59,7 @@ const juryMemberFields: Field[] = [
const rowFields: Field[] = [text('num', 'Number'), text('label', 'Label'), textarea('body', 'Body')]
const partnerFields: Field[] = [
text('id', 'Stable ID'),
text('stableId', 'Stable ID'),
text('name', 'Name'),
text('role', 'Role'),
numberField('tier', 'Tier'),
@@ -94,6 +94,10 @@ const partnerFields: Field[] = [
const benefitFields: Field[] = [text('num', 'Number'), text('label', 'Label'), text('sub', 'Subline')]
const packageFields: Field[] = [text('value', 'Value'), text('title', 'Title'), textarea('desc', 'Description')]
const formStepFields: Field[] = [text('label', 'Label')]
const partnerDefaults = netzwerkContent.partners.items.map(({ id, ...partner }) => ({
...partner,
stableId: id,
}))
export const netzwerkFields: Field[] = [
{
@@ -252,7 +256,7 @@ export const netzwerkFields: Field[] = [
label: 'Partners',
type: 'array',
dbName: 'network_partners',
defaultValue: netzwerkContent.partners.items,
defaultValue: partnerDefaults,
fields: partnerFields,
},
],