feat: Replaced 6 static page imports with React.lazy + Suspense in App.…
- "frontend/src/App.tsx" GSD-Task: S05/T02
This commit is contained in:
parent
39e169b4ce
commit
1bbcb8f5bf
4 changed files with 140 additions and 14 deletions
|
|
@ -79,7 +79,7 @@ The 945-line `public-client.ts` monolith contains all API functions across 10 do
|
|||
- Estimate: 1h
|
||||
- Files: frontend/src/api/public-client.ts, frontend/src/api/client.ts, frontend/src/api/search.ts, frontend/src/api/techniques.ts, frontend/src/api/creators.ts, frontend/src/api/topics.ts, frontend/src/api/stats.ts, frontend/src/api/reports.ts, frontend/src/api/admin-pipeline.ts, frontend/src/api/admin-techniques.ts, frontend/src/api/auth.ts, frontend/src/api/index.ts
|
||||
- Verify: cd frontend && npm run build && cd .. && ! rg 'public-client' frontend/src/ -g '*.ts' -g '*.tsx' && ls frontend/src/api/*.ts | wc -l
|
||||
- [ ] **T02: Add React.lazy code splitting for admin and creator dashboard pages** — ## Description
|
||||
- [x] **T02: Replaced 6 static page imports with React.lazy + Suspense in App.tsx, producing 7 JS chunks from the previous monolithic bundle** — ## Description
|
||||
|
||||
All 17 page components are eagerly imported in `App.tsx`. Wrap admin and creator pages in `React.lazy()` + `Suspense` so they load on-demand, reducing initial bundle size.
|
||||
|
||||
|
|
|
|||
36
.gsd/milestones/M019/slices/S05/tasks/T01-VERIFY.json
Normal file
36
.gsd/milestones/M019/slices/S05/tasks/T01-VERIFY.json
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"taskId": "T01",
|
||||
"unitId": "M019/S05/T01",
|
||||
"timestamp": 1775257496151,
|
||||
"passed": false,
|
||||
"discoverySource": "task-plan",
|
||||
"checks": [
|
||||
{
|
||||
"command": "cd frontend",
|
||||
"exitCode": 0,
|
||||
"durationMs": 8,
|
||||
"verdict": "pass"
|
||||
},
|
||||
{
|
||||
"command": "npm run build",
|
||||
"exitCode": 254,
|
||||
"durationMs": 101,
|
||||
"verdict": "fail"
|
||||
},
|
||||
{
|
||||
"command": "cd ..",
|
||||
"exitCode": 0,
|
||||
"durationMs": 4,
|
||||
"verdict": "pass"
|
||||
},
|
||||
{
|
||||
"command": "! rg 'public-client' frontend/src/ -g '*.ts' -g '*.tsx'",
|
||||
"exitCode": 0,
|
||||
"durationMs": 9,
|
||||
"verdict": "pass"
|
||||
}
|
||||
],
|
||||
"retryAttempt": 1,
|
||||
"maxRetries": 2
|
||||
}
|
||||
80
.gsd/milestones/M019/slices/S05/tasks/T02-SUMMARY.md
Normal file
80
.gsd/milestones/M019/slices/S05/tasks/T02-SUMMARY.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
id: T02
|
||||
parent: S05
|
||||
milestone: M019
|
||||
provides: []
|
||||
requires: []
|
||||
affects: []
|
||||
key_files: ["frontend/src/App.tsx"]
|
||||
key_decisions: ["Suspense placed at each lazy route element rather than wrapping all routes, so eager pages never see the fallback", "ProtectedRoute wraps Suspense (not vice versa) so auth redirect happens before chunk load"]
|
||||
patterns_established: []
|
||||
drill_down_paths: []
|
||||
observability_surfaces: []
|
||||
duration: ""
|
||||
verification_result: "npm run build exits 0 producing 7 JS chunks. rg confirms 6 React.lazy imports. All slice-level checks pass: no public-client references, api/index.ts and client.ts exist, 11 API module files."
|
||||
completed_at: 2026-04-03T23:06:29.631Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T02: Replaced 6 static page imports with React.lazy + Suspense in App.tsx, producing 7 JS chunks from the previous monolithic bundle
|
||||
|
||||
> Replaced 6 static page imports with React.lazy + Suspense in App.tsx, producing 7 JS chunks from the previous monolithic bundle
|
||||
|
||||
## What Happened
|
||||
---
|
||||
id: T02
|
||||
parent: S05
|
||||
milestone: M019
|
||||
key_files:
|
||||
- frontend/src/App.tsx
|
||||
key_decisions:
|
||||
- Suspense placed at each lazy route element rather than wrapping all routes, so eager pages never see the fallback
|
||||
- ProtectedRoute wraps Suspense (not vice versa) so auth redirect happens before chunk load
|
||||
duration: ""
|
||||
verification_result: passed
|
||||
completed_at: 2026-04-03T23:06:29.631Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T02: Replaced 6 static page imports with React.lazy + Suspense in App.tsx, producing 7 JS chunks from the previous monolithic bundle
|
||||
|
||||
**Replaced 6 static page imports with React.lazy + Suspense in App.tsx, producing 7 JS chunks from the previous monolithic bundle**
|
||||
|
||||
## What Happened
|
||||
|
||||
Converted AdminPipeline, AdminTechniquePages, AdminReports, CreatorDashboard, CreatorSettings, and About from static imports to React.lazy() dynamic imports. Added LoadingFallback component and individual Suspense wrappers per lazy route. ProtectedRoute wraps Suspense for creator routes so auth guard fires before chunk load. Core public and auth pages remain eagerly imported.
|
||||
|
||||
## Verification
|
||||
|
||||
npm run build exits 0 producing 7 JS chunks. rg confirms 6 React.lazy imports. All slice-level checks pass: no public-client references, api/index.ts and client.ts exist, 11 API module files.
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
| # | Command | Exit Code | Verdict | Duration |
|
||||
|---|---------|-----------|---------|----------|
|
||||
| 1 | `cd frontend && npm run build` | 0 | ✅ pass | 6400ms |
|
||||
| 2 | `ls frontend/dist/assets/*.js | wc -l` | 0 | ✅ pass (7 files) | 100ms |
|
||||
| 3 | `rg 'React.lazy' frontend/src/App.tsx` | 0 | ✅ pass (6 matches) | 100ms |
|
||||
| 4 | `rg 'public-client' frontend/src/ -g '*.ts' -g '*.tsx'` | 1 | ✅ pass (no matches) | 100ms |
|
||||
| 5 | `test -f frontend/src/api/index.ts && test -f frontend/src/api/client.ts` | 0 | ✅ pass | 50ms |
|
||||
| 6 | `ls frontend/src/api/*.ts | wc -l` | 0 | ✅ pass (11 files) | 50ms |
|
||||
|
||||
|
||||
## Deviations
|
||||
|
||||
None.
|
||||
|
||||
## Known Issues
|
||||
|
||||
None.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `frontend/src/App.tsx`
|
||||
|
||||
|
||||
## Deviations
|
||||
None.
|
||||
|
||||
## Known Issues
|
||||
None.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import React, { useState, useEffect, useRef, useCallback, Suspense } from "react";
|
||||
import { Link, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
|
||||
import Home from "./pages/Home";
|
||||
import SearchResults from "./pages/SearchResults";
|
||||
|
|
@ -7,20 +7,30 @@ import CreatorsBrowse from "./pages/CreatorsBrowse";
|
|||
import CreatorDetail from "./pages/CreatorDetail";
|
||||
import TopicsBrowse from "./pages/TopicsBrowse";
|
||||
import SubTopicPage from "./pages/SubTopicPage";
|
||||
import AdminReports from "./pages/AdminReports";
|
||||
import AdminPipeline from "./pages/AdminPipeline";
|
||||
import AdminTechniquePages from "./pages/AdminTechniquePages";
|
||||
import About from "./pages/About";
|
||||
import Login from "./pages/Login";
|
||||
import Register from "./pages/Register";
|
||||
import CreatorDashboard from "./pages/CreatorDashboard";
|
||||
import CreatorSettings from "./pages/CreatorSettings";
|
||||
|
||||
// Lazy-loaded pages — admin, creator, and info routes split into separate chunks
|
||||
const AdminReports = React.lazy(() => import("./pages/AdminReports"));
|
||||
const AdminPipeline = React.lazy(() => import("./pages/AdminPipeline"));
|
||||
const AdminTechniquePages = React.lazy(() => import("./pages/AdminTechniquePages"));
|
||||
const About = React.lazy(() => import("./pages/About"));
|
||||
const CreatorDashboard = React.lazy(() => import("./pages/CreatorDashboard"));
|
||||
const CreatorSettings = React.lazy(() => import("./pages/CreatorSettings"));
|
||||
import AdminDropdown from "./components/AdminDropdown";
|
||||
import AppFooter from "./components/AppFooter";
|
||||
import SearchAutocomplete from "./components/SearchAutocomplete";
|
||||
import ProtectedRoute from "./components/ProtectedRoute";
|
||||
import { AuthProvider, useAuth } from "./context/AuthContext";
|
||||
|
||||
function LoadingFallback() {
|
||||
return (
|
||||
<div style={{ display: "flex", justifyContent: "center", alignItems: "center", minHeight: "40vh" }}>
|
||||
<p style={{ color: "var(--text-secondary, #94a3b8)", fontSize: "0.95rem" }}>Loading…</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AuthNav() {
|
||||
const { isAuthenticated, user, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
|
@ -163,20 +173,20 @@ function AppShell() {
|
|||
<Route path="/topics" element={<TopicsBrowse />} />
|
||||
|
||||
{/* Admin routes */}
|
||||
<Route path="/admin/reports" element={<AdminReports />} />
|
||||
<Route path="/admin/pipeline" element={<AdminPipeline />} />
|
||||
<Route path="/admin/techniques" element={<AdminTechniquePages />} />
|
||||
<Route path="/admin/reports" element={<Suspense fallback={<LoadingFallback />}><AdminReports /></Suspense>} />
|
||||
<Route path="/admin/pipeline" element={<Suspense fallback={<LoadingFallback />}><AdminPipeline /></Suspense>} />
|
||||
<Route path="/admin/techniques" element={<Suspense fallback={<LoadingFallback />}><AdminTechniquePages /></Suspense>} />
|
||||
|
||||
{/* Info routes */}
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/about" element={<Suspense fallback={<LoadingFallback />}><About /></Suspense>} />
|
||||
|
||||
{/* Auth routes */}
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
|
||||
{/* Creator routes (protected) */}
|
||||
<Route path="/creator/dashboard" element={<ProtectedRoute><CreatorDashboard /></ProtectedRoute>} />
|
||||
<Route path="/creator/settings" element={<ProtectedRoute><CreatorSettings /></ProtectedRoute>} />
|
||||
<Route path="/creator/dashboard" element={<ProtectedRoute><Suspense fallback={<LoadingFallback />}><CreatorDashboard /></Suspense></ProtectedRoute>} />
|
||||
<Route path="/creator/settings" element={<ProtectedRoute><Suspense fallback={<LoadingFallback />}><CreatorSettings /></Suspense></ProtectedRoute>} />
|
||||
|
||||
{/* Fallback */}
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue