feat: implement workspace management and member invitations
- Added tenant API for managing workspaces, including listing, creating, and inviting members. - Introduced new models for organizations, workspaces, and workspace memberships in the backend. - Created frontend components for workspace administration, allowing users to manage workspaces and invite members. - Updated document listing to support workspace-specific filtering. - Implemented account settings page for user profile management and password changes. - Added end-to-end tests for workspace administration and dashboard functionalities.
This commit is contained in:
parent
809235e962
commit
bf5cd44dc1
20 changed files with 928 additions and 27 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { Routes, Route } from 'react-router-dom'
|
||||
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'
|
||||
|
|
@ -9,6 +10,37 @@ 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 { 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 (
|
||||
|
|
@ -23,6 +55,31 @@ export default function App() {
|
|||
</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="/auth/google/callback" element={<GoogleCallback />} />
|
||||
<Route
|
||||
path="/forbidden"
|
||||
element={
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue