From fc2017b21068a7ef6a9ff7bbe38e6f0aa8185c6d Mon Sep 17 00:00:00 2001 From: Daniel S Date: Sun, 9 Aug 2026 23:50:33 +0200 Subject: [PATCH] feat: enable full section editing, background styling, animations and live file persistence --- app/data/site.config.json | 19 ++----------------- src/components/sections/BentoGrid.astro | 12 ++++++++---- src/components/sections/ContactSection.astro | 10 +++++++--- src/components/sections/HeroSection.astro | 10 ++++++---- src/pages/admin/editor.astro | 17 ++++++++++++++--- src/pages/api/editor/add-section.ts | 1 + src/pages/api/editor/delete-section.ts | 1 + .../api/editor/update-section-settings.ts | 1 + src/pages/api/editor/update-section.ts | 1 + 9 files changed, 41 insertions(+), 31 deletions(-) diff --git a/app/data/site.config.json b/app/data/site.config.json index 6c9c835..50dba86 100644 --- a/app/data/site.config.json +++ b/app/data/site.config.json @@ -1,4 +1,5 @@ { + "updated_at": "2026-08-09T21:48:17.969Z", "siteName": "N&D IT Solutions", "metaDescription": "High-Performance Webdesign & Digital Solutions - 100% DSGVO-konform.", "is_demo_mode": false, @@ -131,23 +132,7 @@ "slug": "/", "title": "Startseite (Hauptseite)", "is_published": true, - "sections": [ - { - "id": "hero", - "type": "HeroSection", - "settings": {} - }, - { - "id": "bento", - "type": "BentoGrid", - "settings": {} - }, - { - "id": "contact", - "type": "ContactSection", - "settings": {} - } - ] + "sections": [] }, { "id": "page_templates", diff --git a/src/components/sections/BentoGrid.astro b/src/components/sections/BentoGrid.astro index 7475d6c..954b148 100644 --- a/src/components/sections/BentoGrid.astro +++ b/src/components/sections/BentoGrid.astro @@ -5,18 +5,22 @@ interface Props { config: SiteConfig['bento']; } -const { config } = Astro.props; +const props = Astro.props; +const config = props.config || props.settings || props; +const sectionTitle = config.sectionTitle || config.title || 'Unsere Leistungen'; +const sectionSubtitle = config.sectionSubtitle || config.subtitle || ''; +const cards = config.cards || config.items || []; ---
-

{config.sectionTitle}

-

{config.sectionSubtitle}

+

{sectionTitle}

+

{sectionSubtitle}

- {config.cards.map((card) => ( + {cards.map((card: any) => (
{card.badge && ( diff --git a/src/components/sections/ContactSection.astro b/src/components/sections/ContactSection.astro index 69af488..771f2bc 100644 --- a/src/components/sections/ContactSection.astro +++ b/src/components/sections/ContactSection.astro @@ -6,15 +6,19 @@ interface Props { smtpConfig: SmtpConfig; } -const { contactConfig, smtpConfig } = Astro.props; +const props = Astro.props; +const contactConfig = props.contactConfig || props.settings || props; +const smtpConfig = props.smtpConfig || { isConfigured: false }; +const title = contactConfig.title || 'Kontaktieren Sie uns'; +const subtitle = contactConfig.subtitle || 'Wir freuen uns auf Ihre Nachricht.'; ---
-

{contactConfig.title}

-

{contactConfig.subtitle}

+

{title}

+

{subtitle}

{!smtpConfig.isConfigured && ( diff --git a/src/components/sections/HeroSection.astro b/src/components/sections/HeroSection.astro index 2f2e445..d95ecc3 100644 --- a/src/components/sections/HeroSection.astro +++ b/src/components/sections/HeroSection.astro @@ -7,11 +7,13 @@ interface Props { contextData?: Record; } -const { config, contextData = {} } = Astro.props; +const props = Astro.props; +const config = props.config || props.settings || props; +const contextData = props.contextData || {}; -const heroTitle = resolveDynamicBinding(config.title, contextData); -const heroSubtitle = resolveDynamicBinding(config.subtitle, contextData); -const heroBadge = resolveDynamicBinding(config.badge, contextData); +const heroTitle = resolveDynamicBinding(config.title || config.heroTitle || 'Willkommen', contextData); +const heroSubtitle = resolveDynamicBinding(config.subtitle || config.heroSubtitle || '', contextData); +const heroBadge = resolveDynamicBinding(config.badge || config.heroBadge || '', contextData); const elements = config.elements || []; const badgeElements = elements.filter((el: UIComponent) => el.slotId === 'hero-badge-slot'); diff --git a/src/pages/admin/editor.astro b/src/pages/admin/editor.astro index 62bcb46..1806d31 100644 --- a/src/pages/admin/editor.astro +++ b/src/pages/admin/editor.astro @@ -285,9 +285,10 @@ const previewUrl = `${targetPage?.slug || '/'}?preview=draft`; if (fieldsContainer) { fieldsContainer.innerHTML = ''; const allKeys = new Set(Object.keys(currentSettings)); - if (!allKeys.has('bg_color')) { - allKeys.add('bg_color'); - } + if (!allKeys.has('title')) allKeys.add('title'); + if (!allKeys.has('subtitle')) allKeys.add('subtitle'); + if (!allKeys.has('bg_color')) allKeys.add('bg_color'); + if (!allKeys.has('animation')) allKeys.add('animation'); allKeys.forEach((key) => { const value = currentSettings[key] || ''; @@ -312,6 +313,16 @@ const previewUrl = `${targetPage?.slug || '/'}?preview=draft`; />
`; + } else if (key === 'animation') { + fieldWrapper.innerHTML = ` + + + `; } else { fieldWrapper.innerHTML = ` diff --git a/src/pages/api/editor/add-section.ts b/src/pages/api/editor/add-section.ts index 810b1fd..b7ee6f4 100644 --- a/src/pages/api/editor/add-section.ts +++ b/src/pages/api/editor/add-section.ts @@ -73,6 +73,7 @@ export const POST: APIRoute = async ({ request }) => { targetPage.sections.push(newSection); await fs.mkdir(DATA_DIR, { recursive: true }); + await fs.writeFile(livePath, JSON.stringify(config, null, 2), 'utf-8'); await fs.writeFile(draftPath, JSON.stringify(config, null, 2), 'utf-8'); return new Response(JSON.stringify({ success: true, section: newSection }), { status: 201 }); diff --git a/src/pages/api/editor/delete-section.ts b/src/pages/api/editor/delete-section.ts index 55cdb37..518723f 100644 --- a/src/pages/api/editor/delete-section.ts +++ b/src/pages/api/editor/delete-section.ts @@ -35,6 +35,7 @@ export const POST: APIRoute = async ({ request }) => { targetPage.sections = targetPage.sections.filter((sec: any) => sec.id !== section_id); await fs.mkdir(DATA_DIR, { recursive: true }); + await fs.writeFile(livePath, JSON.stringify(config, null, 2), 'utf-8'); await fs.writeFile(draftPath, JSON.stringify(config, null, 2), 'utf-8'); return new Response(JSON.stringify({ success: true, message: 'Sektion gelöscht' }), { status: 200 }); diff --git a/src/pages/api/editor/update-section-settings.ts b/src/pages/api/editor/update-section-settings.ts index eb1ff4e..8fbc091 100644 --- a/src/pages/api/editor/update-section-settings.ts +++ b/src/pages/api/editor/update-section-settings.ts @@ -40,6 +40,7 @@ export const POST: APIRoute = async ({ request }) => { targetSection.settings = { ...targetSection.settings, ...settings }; await fs.mkdir(DATA_DIR, { recursive: true }); + await fs.writeFile(livePath, JSON.stringify(config, null, 2), 'utf-8'); await fs.writeFile(draftPath, JSON.stringify(config, null, 2), 'utf-8'); return new Response(JSON.stringify({ success: true, settings: targetSection.settings }), { status: 200 }); diff --git a/src/pages/api/editor/update-section.ts b/src/pages/api/editor/update-section.ts index d3e3008..3624276 100644 --- a/src/pages/api/editor/update-section.ts +++ b/src/pages/api/editor/update-section.ts @@ -37,6 +37,7 @@ export const POST: APIRoute = async ({ request }) => { // 3. Speichere ausschließlich in site.config.draft.json await fs.mkdir(DATA_DIR, { recursive: true }); + await fs.writeFile(livePath, JSON.stringify(config, null, 2), 'utf-8'); await fs.writeFile(draftPath, JSON.stringify(config, null, 2), 'utf-8'); return new Response(