Skip to content

Commit 1451aa2

Browse files
committed
feat: make start page configurable via CMS
1 parent 0b0e743 commit 1451aa2

File tree

4 files changed

+172
-85
lines changed

4 files changed

+172
-85
lines changed

src/App.jsx

Lines changed: 4 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
1-
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
2-
import { Suspense, lazy } from 'react'
1+
import { BrowserRouter as Router } from 'react-router-dom'
2+
import { Suspense } from 'react'
33
import { HelmetProvider } from 'react-helmet-async'
44
import { AuthProvider } from './context/AuthContext'
55
import { ContentProvider } from './context/ContentContext'
66
import { EditProvider } from './context/EditContext'
77
import { TutorialProvider } from './context/TutorialContext'
88
import { ThemeProvider } from './context/ThemeContext'
99
import ErrorBoundary from './components/ui/ErrorBoundary'
10-
import Header from './components/layout/Header'
11-
import Footer from './components/layout/Footer'
12-
import ProtectedRoute from './components/ProtectedRoute'
1310
import GlobalSiteMeta from './components/GlobalSiteMeta'
14-
import Home from './pages/Home' // Landing/home page
15-
import LandingPage from './pages/LandingPage' // New IT Landing Page
16-
import PostDetail from './pages/PostDetail' // Individual blog post view
17-
18-
const Login = lazy(() => import('./pages/Login')) // User authentication page
19-
const TutorialDetail = lazy(() => import('./pages/TutorialDetail')) // Individual tutorial view
20-
const DynamicPage = lazy(() => import('./pages/DynamicPage')) // CMS-driven dynamic pages
21-
const AdminDashboard = lazy(() => import('./pages/AdminDashboard')) // Admin control panel
11+
import AppRoutes from './AppRoutes'
2212

2313
const LoadingSpinner = () => (
2414
<div className="flex items-center justify-center min-h-[50vh]">
@@ -39,75 +29,7 @@ function App() {
3929
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 transition-colors">
4030
<GlobalSiteMeta />
4131
<Suspense fallback={<LoadingSpinner />}>
42-
<Routes>
43-
<Route
44-
path="/"
45-
element={
46-
<ErrorBoundary>
47-
<LandingPage />
48-
</ErrorBoundary>
49-
}
50-
/>
51-
<Route
52-
path="/blog"
53-
element={
54-
<ErrorBoundary>
55-
<Header />
56-
<Home />
57-
<Footer />
58-
</ErrorBoundary>
59-
}
60-
/>
61-
<Route
62-
path="/tutorials/:id"
63-
element={
64-
<ErrorBoundary>
65-
<Header />
66-
<TutorialDetail />
67-
<Footer />
68-
</ErrorBoundary>
69-
}
70-
/>
71-
<Route
72-
path="/pages/:pageSlug/posts/:postSlug"
73-
element={
74-
<ErrorBoundary>
75-
<Header />
76-
<PostDetail />
77-
<Footer />
78-
</ErrorBoundary>
79-
}
80-
/>
81-
<Route
82-
path="/pages/:slug"
83-
element={
84-
<ErrorBoundary>
85-
<Header />
86-
<DynamicPage />
87-
<Footer />
88-
</ErrorBoundary>
89-
}
90-
/>
91-
<Route path="/login" element={<ErrorBoundary><Login /></ErrorBoundary>} />
92-
<Route
93-
path="/admin"
94-
element={
95-
<ProtectedRoute>
96-
<ErrorBoundary>
97-
<AdminDashboard />
98-
</ErrorBoundary>
99-
</ProtectedRoute>
100-
}
101-
/>
102-
<Route
103-
path="*"
104-
element={
105-
<ErrorBoundary>
106-
<LandingPage />
107-
</ErrorBoundary>
108-
}
109-
/>
110-
</Routes>
32+
<AppRoutes />
11133
</Suspense>
11234
</div>
11335
</TutorialProvider>

src/AppRoutes.jsx

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Routes, Route } from 'react-router-dom'
2+
import { lazy } from 'react'
3+
import { useContent } from './context/ContentContext'
4+
import ErrorBoundary from './components/ui/ErrorBoundary'
5+
import Header from './components/layout/Header'
6+
import Footer from './components/layout/Footer'
7+
import ProtectedRoute from './components/ProtectedRoute'
8+
import Home from './pages/Home' // Blog view
9+
import LandingPage from './pages/LandingPage' // Default Landing Page
10+
import PostDetail from './pages/PostDetail'
11+
import DynamicPage from './pages/DynamicPage'
12+
13+
const Login = lazy(() => import('./pages/Login'))
14+
const TutorialDetail = lazy(() => import('./pages/TutorialDetail'))
15+
const AdminDashboard = lazy(() => import('./pages/AdminDashboard'))
16+
17+
const AppRoutes = () => {
18+
const { contentType, getSection } = useContent()
19+
const settings = getSection('settings') || {}
20+
const homePageSlug = settings.homePageSlug
21+
22+
let HomePageComponent = LandingPage
23+
let homePageProps = {}
24+
25+
if (homePageSlug === 'blog') {
26+
HomePageComponent = () => (
27+
<>
28+
<Header />
29+
<Home />
30+
<Footer />
31+
</>
32+
)
33+
} else if (homePageSlug) {
34+
HomePageComponent = () => (
35+
<>
36+
<Header />
37+
<DynamicPage slug={homePageSlug} />
38+
<Footer />
39+
</>
40+
)
41+
}
42+
43+
return (
44+
<Routes>
45+
<Route
46+
path="/"
47+
element={
48+
<ErrorBoundary>
49+
<HomePageComponent {...homePageProps} />
50+
</ErrorBoundary>
51+
}
52+
/>
53+
<Route
54+
path="/blog"
55+
element={
56+
<ErrorBoundary>
57+
<Header />
58+
<Home />
59+
<Footer />
60+
</ErrorBoundary>
61+
}
62+
/>
63+
<Route
64+
path="/tutorials/:id"
65+
element={
66+
<ErrorBoundary>
67+
<Header />
68+
<TutorialDetail />
69+
<Footer />
70+
</ErrorBoundary>
71+
}
72+
/>
73+
<Route
74+
path="/pages/:pageSlug/posts/:postSlug"
75+
element={
76+
<ErrorBoundary>
77+
<Header />
78+
<PostDetail />
79+
<Footer />
80+
</ErrorBoundary>
81+
}
82+
/>
83+
<Route
84+
path="/pages/:slug"
85+
element={
86+
<ErrorBoundary>
87+
<Header />
88+
<DynamicPage />
89+
<Footer />
90+
</ErrorBoundary>
91+
}
92+
/>
93+
<Route path="/login" element={<ErrorBoundary><Login /></ErrorBoundary>} />
94+
<Route
95+
path="/admin"
96+
element={
97+
<ProtectedRoute>
98+
<ErrorBoundary>
99+
<AdminDashboard />
100+
</ErrorBoundary>
101+
</ProtectedRoute>
102+
}
103+
/>
104+
<Route
105+
path="*"
106+
element={
107+
<ErrorBoundary>
108+
<LandingPage />
109+
</ErrorBoundary>
110+
}
111+
/>
112+
</Routes>
113+
)
114+
}
115+
116+
export default AppRoutes

src/components/admin/SettingsEditor.jsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,24 @@ const SettingsEditor = () => {
1010
})
1111
const [message, setMessage] = useState(null)
1212

13+
const [availablePages, setAvailablePages] = useState([])
14+
1315
useEffect(() => {
1416
fetchSettings()
17+
fetchPages()
1518
}, [])
1619

20+
const fetchPages = async () => {
21+
try {
22+
const data = await api.listPages()
23+
if (data && data.items) {
24+
setAvailablePages(data.items)
25+
}
26+
} catch (error) {
27+
console.error('Error fetching pages:', error)
28+
}
29+
}
30+
1731
const fetchSettings = async () => {
1832
try {
1933
const data = await api.getSiteContentSection('settings')
@@ -87,6 +101,37 @@ const SettingsEditor = () => {
87101
/>
88102
</button>
89103
</div>
104+
105+
{/* Start Page Settings */}
106+
<div className="pt-6 border-t border-gray-200 dark:border-gray-700">
107+
<div className="flex flex-col gap-4">
108+
<div>
109+
<h3 className="text-base font-medium text-gray-900 dark:text-white">
110+
Startseite
111+
</h3>
112+
<p className="text-sm text-gray-500 dark:text-gray-400">
113+
Wähle aus, welche Seite beim Aufruf der Hauptdomain (/) angezeigt werden soll.
114+
</p>
115+
</div>
116+
<div className="w-full max-w-md">
117+
<select
118+
value={settings.homePageSlug || ''}
119+
onChange={(e) => setSettings(prev => ({ ...prev, homePageSlug: e.target.value }))}
120+
className="block w-full rounded-md border-gray-300 dark:border-gray-600 dark:bg-slate-800 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm p-2.5"
121+
>
122+
<option value="">Standard Landing Page</option>
123+
<option value="blog">Blog (Alle Artikel)</option>
124+
<optgroup label="CMS Seiten">
125+
{availablePages.map(page => (
126+
<option key={page.id} value={page.slug}>
127+
{page.title} ({page.slug})
128+
</option>
129+
))}
130+
</optgroup>
131+
</select>
132+
</div>
133+
</div>
134+
</div>
90135
</div>
91136

92137
<div className="mt-8 flex items-center justify-end gap-4">

src/pages/DynamicPage.jsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ import DynamicPagePostList from '../components/dynamic-page/DynamicPagePostList'
2020
* - List Integration: Automatically renders posts associated with the specific page.
2121
* - Loading States: Handles transitions, 404s, and API errors gracefully.
2222
*/
23-
const DynamicPage = () => {
24-
const { slug = '' } = useParams()
23+
const DynamicPage = ({ slug: propSlug }) => {
24+
const { slug: paramSlug } = useParams()
2525
const navigate = useNavigate()
2626
const { pages } = useContent()
27-
const normalizedSlug = useMemo(() => normalizeSlug(slug), [slug])
27+
28+
// Use propSlug if available (e.g. when used as Home), otherwise use paramSlug from router
29+
const activeSlug = propSlug || paramSlug
30+
const normalizedSlug = useMemo(() => normalizeSlug(activeSlug), [activeSlug])
31+
2832
const cachedPage = pages.cache?.[normalizedSlug]
2933
const [pageData, setPageData] = useState(cachedPage ?? null)
3034
const [loading, setLoading] = useState(!cachedPage)

0 commit comments

Comments
 (0)