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:
Anurag Jangid 2026-10-23 01:19:11 +05:30
parent 809235e962
commit bf5cd44dc1
20 changed files with 928 additions and 27 deletions

View file

@ -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={