Add multi-tenancy with Teams feature
This commit is contained in:
@@ -2,6 +2,8 @@ import { ReactNode, useState, useRef, useEffect } from 'react';
|
||||
import { Link, NavLink, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { GlobalSearch } from './GlobalSearch';
|
||||
import { listTeams } from '../api';
|
||||
import { TeamDetail } from '../types';
|
||||
import './Layout.css';
|
||||
|
||||
interface LayoutProps {
|
||||
@@ -13,8 +15,22 @@ function Layout({ children }: LayoutProps) {
|
||||
const navigate = useNavigate();
|
||||
const { user, loading, logout } = useAuth();
|
||||
const [showUserMenu, setShowUserMenu] = useState(false);
|
||||
const [userTeams, setUserTeams] = useState<TeamDetail[]>([]);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Fetch user's teams
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
listTeams({ limit: 10 }).then(data => {
|
||||
setUserTeams(data.items);
|
||||
}).catch(() => {
|
||||
setUserTeams([]);
|
||||
});
|
||||
} else {
|
||||
setUserTeams([]);
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
// Close menu when clicking outside
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
@@ -77,6 +93,20 @@ function Layout({ children }: LayoutProps) {
|
||||
</svg>
|
||||
Dashboard
|
||||
</Link>
|
||||
{user && userTeams.length > 0 && (
|
||||
<Link
|
||||
to={userTeams.length === 1 ? `/teams/${userTeams[0].slug}` : '/teams'}
|
||||
className={location.pathname.startsWith('/teams') ? 'active' : ''}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="9" cy="7" r="4"/>
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||||
</svg>
|
||||
{userTeams.length === 1 ? 'Team' : 'Teams'}
|
||||
</Link>
|
||||
)}
|
||||
<a href="/docs" className="nav-link-muted">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
@@ -188,7 +218,17 @@ function Layout({ children }: LayoutProps) {
|
||||
<footer className="footer">
|
||||
<div className="container footer-content">
|
||||
<div className="footer-brand">
|
||||
<svg className="footer-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 14 Q6 8 3 8 Q6 4 6 4 Q6 4 9 8 Q6 8 6 14" fill="currentColor" opacity="0.6"/>
|
||||
<rect x="5.25" y="13" width="1.5" height="4" fill="currentColor" opacity="0.6"/>
|
||||
<path d="M12 12 Q12 5 8 5 Q12 1 12 1 Q12 1 16 5 Q12 5 12 12" fill="currentColor"/>
|
||||
<rect x="11.25" y="11" width="1.5" height="5" fill="currentColor"/>
|
||||
<path d="M18 14 Q18 8 15 8 Q18 4 18 4 Q18 4 21 8 Q18 8 18 14" fill="currentColor" opacity="0.6"/>
|
||||
<rect x="17.25" y="13" width="1.5" height="4" fill="currentColor" opacity="0.6"/>
|
||||
<ellipse cx="12" cy="19" rx="9" ry="1.5" fill="currentColor" opacity="0.3"/>
|
||||
</svg>
|
||||
<span className="footer-logo">Orchard</span>
|
||||
<span className="footer-separator">·</span>
|
||||
<span className="footer-tagline">Content-Addressable Storage</span>
|
||||
</div>
|
||||
<div className="footer-links">
|
||||
|
||||
Reference in New Issue
Block a user