Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 79 additions & 27 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,69 @@
import React from 'react'
import React, { Suspense, Component, useEffect } from 'react'
import { Routes, Route, useLocation } from 'react-router-dom'
import { useEffect } from 'react'
import { AppProvider } from './context/AppContext'
import { ThemeProvider } from './context/ThemeContext'
import Navbar from './components/Navbar'
import RateLimitBanner from './components/RateLimitBanner'
import HomePage from './pages/HomePage'
import OverviewPage from './pages/OverviewPage'
import RepositoriesPage from './pages/RepositoriesPage'
import ContributorsPage from './pages/ContributorsPage'
import ContributorProfilePage from './pages/ContributorProfilePage'
import NetworkPage from './pages/NetworkPage'
import AnalyticsPage from './pages/AnalyticsPage'
import GovernancePage from './pages/GovernancePage'
import SettingsPage from './pages/SettingsPage'
import Footer from './components/layout/Footer'
import Support from './pages/Support'
import Footer from './components/layout/Footer'
import RequireAnalysis from './components/RequireAnalysis'
import NotFoundPage from './pages/NotFoundPage'
import { Spinner } from './components/UI'

const HomePage = React.lazy(() => import('./pages/HomePage'))
const OverviewPage = React.lazy(() => import('./pages/OverviewPage'))
const RepositoriesPage = React.lazy(() => import('./pages/RepositoriesPage'))
const ContributorsPage = React.lazy(() => import('./pages/ContributorsPage'))
const ContributorProfilePage = React.lazy(() => import('./pages/ContributorProfilePage'))
const NetworkPage = React.lazy(() => import('./pages/NetworkPage'))
const AnalyticsPage = React.lazy(() => import('./pages/AnalyticsPage'))
const GovernancePage = React.lazy(() => import('./pages/GovernancePage'))
const SettingsPage = React.lazy(() => import('./pages/SettingsPage'))
const Support = React.lazy(() => import('./pages/Support'))
const NotFoundPage = React.lazy(() => import('./pages/NotFoundPage'))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

const ROUTE_STRINGS = {
ERROR_TITLE: 'Unable to load page',
ERROR_MESSAGE: 'An error occurred while loading this page.',
RELOAD_BUTTON: 'Reload Page',
LOADING_LABEL: 'Loading page',
}

class ErrorBoundary extends Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}

static getDerivedStateFromError() {
return { hasError: true }
}

componentDidCatch(error, errorInfo) {
console.error('Route load error:', error, errorInfo)
}

render() {
if (this.state.hasError) {
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '64px 16px', textAlign: 'center' }}>
<h3>{ROUTE_STRINGS.ERROR_TITLE}</h3>
<p style={{ color: 'var(--text2)', marginBottom: '16px' }}>{ROUTE_STRINGS.ERROR_MESSAGE}</p>
<button className="btn btn-primary" onClick={() => window.location.reload()}>
{ROUTE_STRINGS.RELOAD_BUTTON}
</button>
</div>
)
}
return this.props.children
}
}

function PageLoader() {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '60vh' }} role="status" aria-label={ROUTE_STRINGS.LOADING_LABEL}>
<Spinner size={36} />
</div>
)
}

function Layout({ children }) {
return (
Expand All @@ -41,22 +87,28 @@ function ScrollToTop() {
}

function AppContent() {
const { pathname } = useLocation()

return (
<Layout>
<ScrollToTop />
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/overview" element={<RequireAnalysis><OverviewPage /></RequireAnalysis>} />
<Route path="/repositories" element={<RequireAnalysis><RepositoriesPage /></RequireAnalysis>} />
<Route path="/contributors" element={<RequireAnalysis><ContributorsPage /></RequireAnalysis>} />
<Route path="/contributors/:username" element={<RequireAnalysis><ContributorProfilePage /></RequireAnalysis>} />
<Route path="/network" element={<RequireAnalysis><NetworkPage /></RequireAnalysis>} />
<Route path="/analytics" element={<RequireAnalysis><AnalyticsPage /></RequireAnalysis>} />
<Route path="/governance" element={<RequireAnalysis><GovernancePage /></RequireAnalysis>} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/support-us" element={<Support />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
<ErrorBoundary key={pathname}>
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/overview" element={<RequireAnalysis><OverviewPage /></RequireAnalysis>} />
<Route path="/repositories" element={<RequireAnalysis><RepositoriesPage /></RequireAnalysis>} />
<Route path="/contributors" element={<RequireAnalysis><ContributorsPage /></RequireAnalysis>} />
<Route path="/contributors/:username" element={<RequireAnalysis><ContributorProfilePage /></RequireAnalysis>} />
<Route path="/network" element={<RequireAnalysis><NetworkPage /></RequireAnalysis>} />
<Route path="/analytics" element={<RequireAnalysis><AnalyticsPage /></RequireAnalysis>} />
<Route path="/governance" element={<RequireAnalysis><GovernancePage /></RequireAnalysis>} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/support-us" element={<Support />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</Suspense>
</ErrorBoundary>
</Layout>
)
}
Expand Down
4 changes: 2 additions & 2 deletions src/App.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ vi.mock('./context/AppContext', () => ({
}))

describe('App routing', () => {
it('renders the Not Found page for an unknown route', () => {
it('renders the Not Found page for an unknown route', async () => {
render(
<MemoryRouter initialEntries={['/this-route-does-not-exist']}>
<App />
</MemoryRouter>
)

expect(screen.getByRole('heading', { name: /page not found/i })).toBeInTheDocument()
expect(await screen.findByRole('heading', { name: /page not found/i })).toBeInTheDocument()
})
})
Loading