Skip to content

Commit 6b21ccf

Browse files
committed
feat: add site content editor component, landing page, and backend handler for managing site content
1 parent db06ee5 commit 6b21ccf

3 files changed

Lines changed: 29 additions & 94 deletions

File tree

backend/src/handlers/site_content.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ fn allowed_sections() -> &'static HashSet<&'static str> {
2626
"tutorial_section",
2727
"header",
2828
"footer",
29-
"grundlagen_page",
3029
"site_meta",
30+
"stats",
31+
"cta_section",
3132
]
3233
.into_iter()
3334
.collect()
@@ -56,7 +57,8 @@ fn validate_content_structure(
5657
"tutorial_section" => validate_tutorial_section_structure(content),
5758
"header" => validate_header_structure(content),
5859
"footer" => validate_footer_structure(content),
59-
"grundlagen_page" => Ok(()),
60+
"stats" => Ok(()),
61+
"cta_section" => Ok(()),
6062
_ => Ok(()),
6163
};
6264

src/components/SiteContentEditor/index.jsx

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ const sectionLabels = {
1212
hero: 'Hero-Bereich (Startseite)',
1313
stats: 'Statistiken (Startseite)',
1414
cta_section: 'CTA-Bereich (Startseite)',
15-
tutorial_section: 'Blog-Sektion (Startseite)',
1615
header: 'Navigation & Header',
1716
footer: 'Footer',
1817
site_meta: 'Seitentitel & Beschreibung',
@@ -720,57 +719,7 @@ FooterForm.propTypes = {
720719
onFieldChange: PropTypes.func.isRequired,
721720
}
722721

723-
const BlogSectionForm = ({ content, onFieldChange }) => {
724-
const section = content || {}
725-
return (
726-
<div className="rounded-2xl border border-gray-200 bg-white p-6 shadow-sm dark:border-slate-700 dark:bg-slate-900/80">
727-
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Blog-Sektion (Startseite)</h3>
728-
<div className="grid grid-cols-1 gap-4">
729-
<div>
730-
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Badge</label>
731-
<input
732-
type="text"
733-
className="mt-1 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100"
734-
value={section.badge || ''}
735-
onChange={(e) => onFieldChange(['badge'], e.target.value)}
736-
/>
737-
</div>
738-
<div>
739-
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Titel</label>
740-
<input
741-
type="text"
742-
className="mt-1 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100"
743-
value={section.title || ''}
744-
onChange={(e) => onFieldChange(['title'], e.target.value)}
745-
/>
746-
</div>
747-
<div>
748-
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Untertitel</label>
749-
<textarea
750-
className="mt-1 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100"
751-
rows="2"
752-
value={section.subtitle || ''}
753-
onChange={(e) => onFieldChange(['subtitle'], e.target.value)}
754-
/>
755-
</div>
756-
<div>
757-
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Button-Text (Karte)</label>
758-
<input
759-
type="text"
760-
className="mt-1 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100"
761-
value={section.tutorialCardButton || ''}
762-
onChange={(e) => onFieldChange(['tutorialCardButton'], e.target.value)}
763-
/>
764-
</div>
765-
</div>
766-
</div>
767-
)
768-
}
769722

770-
BlogSectionForm.propTypes = {
771-
content: PropTypes.object,
772-
onFieldChange: PropTypes.func.isRequired,
773-
}
774723

775724

776725

@@ -891,41 +840,7 @@ SiteMetaPreview.propTypes = {
891840
content: PropTypes.object.isRequired,
892841
}
893842

894-
const TutorialSectionPreview = ({ content }) => {
895-
return (
896-
<div className="space-y-4 rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
897-
<div className="flex items-center justify-between border-b border-gray-100 pb-3">
898-
<h4 className="text-lg font-semibold text-gray-900">Blog-Sektion</h4>
899-
<span className="rounded-full border border-primary-200 bg-primary-50 px-3 py-1 text-xs font-medium text-primary-700">
900-
Vorschau
901-
</span>
902-
</div>
903-
<div className="space-y-3">
904-
<div className="rounded-lg border border-gray-200 bg-gray-50 p-3">
905-
<p className="text-xs font-medium uppercase tracking-wide text-gray-500">Badge</p>
906-
<p className="mt-1 text-sm font-semibold text-gray-900">{content.badge || 'Neueste Beiträge'}</p>
907-
</div>
908-
<div className="rounded-lg border border-gray-200 bg-gray-50 p-3">
909-
<p className="text-xs font-medium uppercase tracking-wide text-gray-500">Titel</p>
910-
<p className="mt-1 text-sm font-semibold text-gray-900">{content.title || 'Aktuelle Artikel'}</p>
911-
</div>
912-
<div className="rounded-lg border border-gray-200 bg-gray-50 p-3">
913-
<p className="text-xs font-medium uppercase tracking-wide text-gray-500">Untertitel</p>
914-
<p className="mt-1 text-sm text-gray-700">{content.subtitle || 'Entdecke die neuesten Insights'}</p>
915-
</div>
916-
<div className="rounded-lg border border-primary-200 bg-primary-50 p-3">
917-
<p className="text-xs font-medium uppercase tracking-wide text-primary-600">Button-Text</p>
918-
<p className="mt-1 text-sm font-semibold text-primary-700">{content.tutorialCardButton || 'Zum Artikel'}</p>
919-
<p className="mt-1 text-xs text-primary-600">Dieser Text erscheint auf den Blog-Karten</p>
920-
</div>
921-
</div>
922-
</div>
923-
)
924-
}
925843

926-
TutorialSectionPreview.propTypes = {
927-
content: PropTypes.object.isRequired,
928-
}
929844

930845
const SectionPreview = ({ section, content }) => {
931846
switch (section) {
@@ -935,8 +850,6 @@ const SectionPreview = ({ section, content }) => {
935850
return <StatsPreview content={content} />
936851
case 'cta_section':
937852
return <CtaSectionPreview content={content} />
938-
case 'tutorial_section':
939-
return <TutorialSectionPreview content={content} />
940853
case 'site_meta':
941854
return <SiteMetaPreview content={content} />
942855
default:
@@ -1184,10 +1097,6 @@ const SiteContentEditor = () => {
11841097
<FooterForm content={draftContent} onFieldChange={handleStructuredFieldChange} />
11851098
)}
11861099

1187-
{selectedSection === 'tutorial_section' && (
1188-
<BlogSectionForm content={draftContent} onFieldChange={handleStructuredFieldChange} />
1189-
)}
1190-
11911100
{/* JSON Editor (Toggleable) */}
11921101
{showJson && (
11931102
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">

src/pages/LandingPage.jsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const LandingPage = () => {
5757

5858
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
5959
<button
60-
onClick={() => navigate('/tutorials')}
60+
onClick={() => navigate('/blog')}
6161
className="group relative px-8 py-4 bg-primary-600 hover:bg-primary-500 text-white rounded-xl font-semibold transition-all duration-200 shadow-lg shadow-primary-600/20 hover:shadow-primary-600/40 flex items-center gap-2"
6262
>
6363
{heroContent.primaryCta?.label || 'Jetzt starten'}
@@ -98,6 +98,30 @@ const LandingPage = () => {
9898
</div>
9999
</section>
100100

101+
{/* Blog Section (Hardcoded defaults as requested) */}
102+
<section id="tutorials" className="py-24 relative">
103+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
104+
<div className="text-center mb-16">
105+
<span className="text-primary-400 font-semibold tracking-wider uppercase text-sm">
106+
Neueste Beiträge
107+
</span>
108+
<h2 className="mt-2 text-3xl md:text-4xl font-bold text-white">
109+
Aktuelle Artikel
110+
</h2>
111+
<p className="mt-4 text-xl text-slate-400 max-w-2xl mx-auto">
112+
Entdecke die neuesten Insights aus der IT-Welt.
113+
</p>
114+
</div>
115+
{/* Note: The actual blog cards are rendered by a separate component or this is just the header section.
116+
If this page is supposed to list blogs, it needs to fetch them.
117+
Currently, the Home page (now at /blog) does that.
118+
This LandingPage seems to be just the landing page.
119+
If we need to show blog cards here, we need to fetch them.
120+
For now, I am just fixing the static content structure.
121+
*/}
122+
</div>
123+
</section>
124+
101125
{/* Stats / Trust Section */}
102126
<section className="py-20 border-y border-slate-800/50 bg-slate-950">
103127
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">

0 commit comments

Comments
 (0)