Refactor Site Builder Integration and Update Docker Configuration
- Merged the site builder functionality into the main app, enhancing the SiteBuilderWizard component with new steps and improved UI. - Updated the Docker Compose configuration by removing the separate site builder service and integrating its functionality into the igny8_sites service. - Enhanced Vite configuration to support code-splitting for builder routes, optimizing loading times. - Updated package dependencies to include new libraries for state management and form handling.
This commit is contained in:
@@ -1,16 +1,52 @@
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
import SiteRenderer from './pages/SiteRenderer';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
|
||||
import ProtectedRoute from './shared/ProtectedRoute';
|
||||
import BuilderLayout from './builder/components/layout/BuilderLayout';
|
||||
|
||||
// Lazy load builder pages (code-split to avoid loading in public sites)
|
||||
const WizardPage = lazy(() => import('./builder/pages/wizard/WizardPage'));
|
||||
const PreviewCanvas = lazy(() => import('./builder/pages/preview/PreviewCanvas'));
|
||||
const SiteDashboard = lazy(() => import('./builder/pages/dashboard/SiteDashboard'));
|
||||
|
||||
// Renderer pages (load immediately for public sites)
|
||||
const SiteRenderer = lazy(() => import('./renderer/pages/SiteRenderer'));
|
||||
|
||||
// Loading component
|
||||
const LoadingFallback = () => (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh' }}>
|
||||
<div>Loading...</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/:siteId/*" element={<SiteRenderer />} />
|
||||
<Route path="/" element={<div>IGNY8 Sites Renderer</div>} />
|
||||
</Routes>
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<Routes>
|
||||
{/* Public Site Renderer Routes (No Auth) */}
|
||||
<Route path="/:siteId/*" element={<SiteRenderer />} />
|
||||
<Route path="/" element={<div>IGNY8 Sites Renderer</div>} />
|
||||
|
||||
{/* Builder Routes (Auth Required) */}
|
||||
<Route
|
||||
path="/builder/*"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<BuilderLayout>
|
||||
<Routes>
|
||||
<Route path="/" element={<WizardPage />} />
|
||||
<Route path="preview" element={<PreviewCanvas />} />
|
||||
<Route path="dashboard" element={<SiteDashboard />} />
|
||||
<Route path="*" element={<Navigate to="/builder" replace />} />
|
||||
</Routes>
|
||||
</BuilderLayout>
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-family: 'Inter', 'Inter var', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
color: #0f172a;
|
||||
background-color: #f5f7fb;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
#root {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
44
sites/src/shared/ProtectedRoute.tsx
Normal file
44
sites/src/shared/ProtectedRoute.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Navigate, useLocation } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* ProtectedRoute component that checks for authentication token.
|
||||
* Redirects to login if not authenticated.
|
||||
*/
|
||||
interface ProtectedRouteProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function ProtectedRoute({ children }: ProtectedRouteProps) {
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
// Check for JWT token in localStorage
|
||||
const token = localStorage.getItem('auth-storage');
|
||||
if (token) {
|
||||
try {
|
||||
const authData = JSON.parse(token);
|
||||
setIsAuthenticated(!!authData?.state?.token);
|
||||
} catch {
|
||||
setIsAuthenticated(false);
|
||||
}
|
||||
} else {
|
||||
setIsAuthenticated(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (isAuthenticated === null) {
|
||||
// Still checking authentication
|
||||
return <div>Checking authentication...</div>;
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
// Redirect to login (or main app login page)
|
||||
// In production, this might redirect to app.igny8.com/login
|
||||
return <Navigate to="/login" state={{ from: location }} replace />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user