135 lines
4.1 KiB
TypeScript
135 lines
4.1 KiB
TypeScript
import { Routes, Route, Navigate, useSearchParams, useNavigate } from 'react-router-dom'
|
|
import { useEffect, useState } from 'react'
|
|
import LandingPage from './pages/LandingPage'
|
|
import LoginPage from './pages/LoginPage'
|
|
import DashboardPage from './pages/DashboardPage'
|
|
import ShedPage from './pages/workspace/ShedPage'
|
|
import SiloPage from './pages/workspace/SiloPage'
|
|
import SketchPage from './pages/workspace/SketchPage'
|
|
import SteadPage from './pages/workspace/SteadPage'
|
|
import SteadCreatePage from './pages/workspace/SteadCreatePage'
|
|
import ForbiddenPage from './pages/ForbiddenPage'
|
|
import ProtectedRoute from './components/ProtectedRoute'
|
|
import AccountPage from './pages/AccountPage'
|
|
import WorkspaceAdminPage from './pages/WorkspaceAdminPage'
|
|
import InvitationPage from './pages/InvitationPage'
|
|
import { authApi, saveAuth } from './lib/api'
|
|
|
|
function GoogleCallback() {
|
|
const [params] = useSearchParams()
|
|
const navigate = useNavigate()
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
const accessToken = params.get('access_token')
|
|
const refreshToken = params.get('refresh_token')
|
|
if (!accessToken || !refreshToken) {
|
|
setError('Google sign-in did not return valid session credentials.')
|
|
return
|
|
}
|
|
localStorage.setItem('silos_access_token', accessToken)
|
|
localStorage.setItem('silos_refresh_token', refreshToken)
|
|
void authApi.me().then((user) => {
|
|
saveAuth({ access_token: accessToken, refresh_token: refreshToken, token_type: 'bearer', user })
|
|
navigate('/dashboard', { replace: true })
|
|
}).catch(() => {
|
|
localStorage.removeItem('silos_access_token')
|
|
localStorage.removeItem('silos_refresh_token')
|
|
setError('Google sign-in could not be completed.')
|
|
})
|
|
}, [navigate, params])
|
|
|
|
if (error) return <main className="flex min-h-screen items-center justify-center bg-silos-cream p-6"><div className="rounded-2xl bg-white p-6 text-center shadow"><p className="text-sm text-red-700">{error}</p><button type="button" onClick={() => navigate('/login')} className="mt-4 rounded-lg bg-silos-brown px-4 py-2 text-sm text-white">Return to login</button></div></main>
|
|
return <Navigate to="/dashboard" replace />
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<Routes>
|
|
<Route path="/" element={<LandingPage />} />
|
|
<Route path="/login" element={<LoginPage />} />
|
|
<Route
|
|
path="/dashboard"
|
|
element={
|
|
<ProtectedRoute>
|
|
<DashboardPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/settings"
|
|
element={
|
|
<ProtectedRoute>
|
|
<AccountPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/profile"
|
|
element={
|
|
<ProtectedRoute>
|
|
<AccountPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/settings/workspaces"
|
|
element={
|
|
<ProtectedRoute>
|
|
<WorkspaceAdminPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route path="/invite/:token" element={<InvitationPage />} />
|
|
<Route path="/auth/google/callback" element={<GoogleCallback />} />
|
|
<Route
|
|
path="/forbidden"
|
|
element={
|
|
<ProtectedRoute>
|
|
<ForbiddenPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/workspace/shed/:id"
|
|
element={
|
|
<ProtectedRoute>
|
|
<ShedPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/workspace/silo/:id"
|
|
element={
|
|
<ProtectedRoute>
|
|
<SiloPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/workspace/sketch/:id"
|
|
element={
|
|
<ProtectedRoute>
|
|
<SketchPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/workspace/stead/new"
|
|
element={
|
|
<ProtectedRoute>
|
|
<SteadCreatePage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/workspace/stead/:id"
|
|
element={
|
|
<ProtectedRoute>
|
|
<SteadPage />
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
</Routes>
|
|
)
|
|
}
|