mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Generated by Spark: I have this issue when I click publish - Cannot GET /
This commit is contained in:
131
docs/ROUTE_FIX.md
Normal file
131
docs/ROUTE_FIX.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Route Fix for Published App
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
When publishing the app to a static hosting service (like GitHub Pages), accessing the root URL `/` results in "Cannot GET /" error. This happens because:
|
||||||
|
|
||||||
|
1. The app uses React Router for client-side routing
|
||||||
|
2. Static hosting servers don't understand client-side routes
|
||||||
|
3. When a user accesses `/`, the server looks for a physical file at that path
|
||||||
|
4. The server returns 404 because there's no file matching that route
|
||||||
|
|
||||||
|
## Solution Applied
|
||||||
|
|
||||||
|
### 1. Created `public/_redirects` File
|
||||||
|
This file tells compatible static hosts (Netlify, Render, etc.) to redirect all routes to `index.html`:
|
||||||
|
|
||||||
|
```
|
||||||
|
/* /index.html 200
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Created `public/404.html`
|
||||||
|
For hosts like GitHub Pages that use a 404 page, this file captures the route and stores it in sessionStorage:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Redirecting...</title>
|
||||||
|
<script>
|
||||||
|
sessionStorage.redirect = location.href;
|
||||||
|
</script>
|
||||||
|
<meta http-equiv="refresh" content="0;URL='/'" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Updated `index.html`
|
||||||
|
Added a script to restore the route from sessionStorage after the redirect:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var redirect = sessionStorage.redirect;
|
||||||
|
delete sessionStorage.redirect;
|
||||||
|
if (redirect && redirect !== location.href) {
|
||||||
|
history.replaceState(null, null, redirect);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. User accesses `https://your-app.github.io/dashboard`
|
||||||
|
2. GitHub Pages returns 404 (no physical file at that path)
|
||||||
|
3. GitHub Pages serves `404.html` instead
|
||||||
|
4. `404.html` stores the URL in sessionStorage and redirects to `/`
|
||||||
|
5. `index.html` loads with the React app
|
||||||
|
6. The script in `index.html` reads sessionStorage and updates the URL
|
||||||
|
7. React Router sees `/dashboard` in the URL and renders the correct route
|
||||||
|
|
||||||
|
## Enhanced Console Logging
|
||||||
|
|
||||||
|
Added comprehensive trace logging throughout the initialization flow:
|
||||||
|
|
||||||
|
### App Initialization (`App.tsx`)
|
||||||
|
- Current URL, pathname, search, hash
|
||||||
|
- Seed data loading progress
|
||||||
|
- App ready state
|
||||||
|
- Component preloading status
|
||||||
|
|
||||||
|
### Route Configuration (`routes.tsx`)
|
||||||
|
- Enabled pages list with details
|
||||||
|
- Root page detection
|
||||||
|
- Route creation with paths
|
||||||
|
|
||||||
|
### Router Provider (`RouterProvider.tsx`)
|
||||||
|
- Feature toggles state
|
||||||
|
- State and action context keys
|
||||||
|
- Routes memo updates
|
||||||
|
- Individual route rendering
|
||||||
|
|
||||||
|
### Page Loader (`page-loader.ts`)
|
||||||
|
- Page configuration loading
|
||||||
|
- Enabled pages filtering
|
||||||
|
- Props resolution for each state/action
|
||||||
|
- Keyboard shortcuts configuration
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
To verify the fix works:
|
||||||
|
|
||||||
|
1. Build the app: `npm run build`
|
||||||
|
2. Serve the `dist` folder with a static server
|
||||||
|
3. Navigate to `/` - should load the home page
|
||||||
|
4. Navigate to `/dashboard` - should load dashboard
|
||||||
|
5. Refresh on `/dashboard` - should stay on dashboard (not 404)
|
||||||
|
|
||||||
|
## Trace Console Output
|
||||||
|
|
||||||
|
You can now trace the full initialization flow in the browser console:
|
||||||
|
|
||||||
|
```
|
||||||
|
[INIT] 🚀 main.tsx starting - BEGIN
|
||||||
|
[INIT] 📦 Importing React DOM
|
||||||
|
[INIT] ✅ React DOM imported
|
||||||
|
...
|
||||||
|
[APP] 🚀 App component initializing
|
||||||
|
[APP] 🌐 Current URL: https://...
|
||||||
|
[APP] 📍 Current pathname: /
|
||||||
|
...
|
||||||
|
[ROUTES] 🏗️ Creating routes with feature toggles
|
||||||
|
[ROUTES] 📄 Enabled pages count: 24
|
||||||
|
[ROUTES] 🏠 Root page search result: Found: home (ProjectDashboard)
|
||||||
|
...
|
||||||
|
[ROUTER_PROVIDER] 🎨 Rendering 25 routes
|
||||||
|
[ROUTER_PROVIDER] 🛣️ Rendering route: /dashboard
|
||||||
|
[ROUTER_PROVIDER] 🛣️ Rendering route: /code
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
- The root route `/` is configured in `src/config/pages.json` with `"isRoot": true`
|
||||||
|
- The home page uses the `ProjectDashboard` component
|
||||||
|
- All routes are dynamically generated from the JSON configuration
|
||||||
|
- The console logs include emojis and structured prefixes for easy filtering
|
||||||
10
index.html
10
index.html
@@ -22,6 +22,16 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||||
<link href="/src/main.css" rel="stylesheet" />
|
<link href="/src/main.css" rel="stylesheet" />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var redirect = sessionStorage.redirect;
|
||||||
|
delete sessionStorage.redirect;
|
||||||
|
if (redirect && redirect !== location.href) {
|
||||||
|
history.replaceState(null, null, redirect);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
15
public/404.html
Normal file
15
public/404.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Redirecting...</title>
|
||||||
|
<script>
|
||||||
|
sessionStorage.redirect = location.href;
|
||||||
|
</script>
|
||||||
|
<meta http-equiv="refresh" content="0;URL='/'" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
public/_redirects
Normal file
1
public/_redirects
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/* /index.html 200
|
||||||
@@ -313,12 +313,18 @@ function AppLayout() {
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
console.log('[APP] 🚀 App component initializing')
|
console.log('[APP] 🚀 App component initializing')
|
||||||
|
console.log('[APP] 🌐 Current URL:', window.location.href)
|
||||||
|
console.log('[APP] 📍 Current pathname:', window.location.pathname)
|
||||||
|
console.log('[APP] 🔍 Current search:', window.location.search)
|
||||||
|
console.log('[APP] 🏷️ Current hash:', window.location.hash)
|
||||||
|
|
||||||
console.log('[APP] 🌱 Initializing seed data hook')
|
console.log('[APP] 🌱 Initializing seed data hook')
|
||||||
const { loadSeedData } = useSeedData()
|
const { loadSeedData } = useSeedData()
|
||||||
const [appReady, setAppReady] = useState(false)
|
const [appReady, setAppReady] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[APP] 🚀 Initialization effect triggered')
|
console.log('[APP] 🚀 Initialization effect triggered')
|
||||||
|
console.log('[APP] ⏰ Timestamp:', new Date().toISOString())
|
||||||
console.time('[APP] Seed data loading')
|
console.time('[APP] Seed data loading')
|
||||||
|
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@@ -356,7 +362,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
}, [loadSeedData])
|
}, [loadSeedData])
|
||||||
|
|
||||||
console.log('[APP] 🎨 Rendering App component')
|
console.log('[APP] 🎨 Rendering App component, appReady:', appReady)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -17,21 +17,27 @@ export function RouterProvider({
|
|||||||
stateContext,
|
stateContext,
|
||||||
actionContext
|
actionContext
|
||||||
}: RouterProviderProps) {
|
}: RouterProviderProps) {
|
||||||
console.log('[ROUTER_PROVIDER] 🏗️ Creating routes')
|
console.log('[ROUTER_PROVIDER] 🏗️ RouterProvider rendering')
|
||||||
|
console.log('[ROUTER_PROVIDER] 🎛️ Feature toggles:', featureToggles)
|
||||||
|
console.log('[ROUTER_PROVIDER] 📦 State context keys:', Object.keys(stateContext))
|
||||||
|
console.log('[ROUTER_PROVIDER] 🎬 Action context keys:', Object.keys(actionContext))
|
||||||
|
|
||||||
const routes = useMemo(() => {
|
const routes = useMemo(() => {
|
||||||
console.log('[ROUTER_PROVIDER] 🔄 Routes memo updating')
|
console.log('[ROUTER_PROVIDER] 🔄 Routes memo updating')
|
||||||
|
console.log('[ROUTER_PROVIDER] ⏰ Timestamp:', new Date().toISOString())
|
||||||
const routeConfigs = createRoutes(featureToggles, stateContext, actionContext)
|
const routeConfigs = createRoutes(featureToggles, stateContext, actionContext)
|
||||||
console.log('[ROUTER_PROVIDER] ✅ Routes created:', routeConfigs.length, 'routes')
|
console.log('[ROUTER_PROVIDER] ✅ Routes created:', routeConfigs.length, 'routes')
|
||||||
|
console.log('[ROUTER_PROVIDER] 📋 Route paths:', routeConfigs.map(r => r.path).join(', '))
|
||||||
return routeConfigs
|
return routeConfigs
|
||||||
}, [featureToggles, stateContext, actionContext])
|
}, [featureToggles, stateContext, actionContext])
|
||||||
|
|
||||||
console.log('[ROUTER_PROVIDER] 🎨 Rendering routes')
|
console.log('[ROUTER_PROVIDER] 🎨 Rendering', routes.length, 'routes')
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
{routes.map((route, index) => (
|
{routes.map((route, index) => {
|
||||||
<Route key={route.path || index} path={route.path} element={route.element} />
|
console.log('[ROUTER_PROVIDER] 🛣️ Rendering route:', route.path || `index-${index}`)
|
||||||
))}
|
return <Route key={route.path || index} path={route.path} element={route.element} />
|
||||||
|
})}
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,12 +72,19 @@ export function createRoutes(
|
|||||||
stateContext: any,
|
stateContext: any,
|
||||||
actionContext: any
|
actionContext: any
|
||||||
): RouteObject[] {
|
): RouteObject[] {
|
||||||
console.log('[ROUTES] 🏗️ Creating routes with feature toggles')
|
console.log('[ROUTES] 🏗️ Creating routes with feature toggles:', featureToggles)
|
||||||
const enabledPages = getEnabledPages(featureToggles)
|
const enabledPages = getEnabledPages(featureToggles)
|
||||||
console.log('[ROUTES] 📄 Enabled pages:', enabledPages.map(p => p.id).join(', '))
|
console.log('[ROUTES] 📄 Enabled pages count:', enabledPages.length)
|
||||||
|
console.log('[ROUTES] 📄 Enabled page IDs:', enabledPages.map(p => p.id).join(', '))
|
||||||
|
console.log('[ROUTES] 📄 Enabled pages details:', JSON.stringify(enabledPages.map(p => ({
|
||||||
|
id: p.id,
|
||||||
|
component: p.component,
|
||||||
|
isRoot: p.isRoot,
|
||||||
|
enabled: p.enabled
|
||||||
|
})), null, 2))
|
||||||
|
|
||||||
const rootPage = enabledPages.find(p => p.isRoot)
|
const rootPage = enabledPages.find(p => p.isRoot)
|
||||||
console.log('[ROUTES] 🏠 Root page:', rootPage?.id || 'none (will use dashboard)')
|
console.log('[ROUTES] 🏠 Root page search result:', rootPage ? `Found: ${rootPage.id} (${rootPage.component})` : 'NOT FOUND - will redirect to /dashboard')
|
||||||
|
|
||||||
const routes: RouteObject[] = enabledPages
|
const routes: RouteObject[] = enabledPages
|
||||||
.filter(p => !p.isRoot)
|
.filter(p => !p.isRoot)
|
||||||
|
|||||||
Reference in New Issue
Block a user