Switch organizations from the Learning space card
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useClerk, useOrganization, useUser } from '@clerk/react'
|
||||
import { useClerk, useOrganization, useOrganizationList, useUser } from '@clerk/react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { NavLink, Outlet, useLocation } from 'react-router-dom'
|
||||
import { LANGS, locale, setLang, t, useLang, type Key } from '../i18n'
|
||||
@@ -70,11 +70,57 @@ function CreditBalance() {
|
||||
)
|
||||
}
|
||||
|
||||
// The Learning space card switches the active organization. AuthGate keys the pages by orgId and
|
||||
// clears the query cache, so Brain, schedules, results and credits all reload for the new one.
|
||||
function OrgSwitcher() {
|
||||
const [open, setOpen] = useState(false)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const { organization } = useOrganization()
|
||||
const { setActive, userMemberships } = useOrganizationList({ userMemberships: { infinite: true } })
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
const onClick = (e: MouseEvent) => { if (!ref.current?.contains(e.target as Node)) setOpen(false) }
|
||||
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false) }
|
||||
document.addEventListener('mousedown', onClick)
|
||||
document.addEventListener('keydown', onKey)
|
||||
return () => { document.removeEventListener('mousedown', onClick); document.removeEventListener('keydown', onKey) }
|
||||
}, [open])
|
||||
|
||||
const choose = (id: string) => {
|
||||
setOpen(false)
|
||||
if (id !== organization?.id) void setActive?.({ organization: id })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="workspace-switch" ref={ref}>
|
||||
<button type="button" className="workspace" aria-haspopup="listbox" aria-expanded={open}
|
||||
title={t('layout.switchOrg')} onClick={() => setOpen(o => !o)}>
|
||||
<span className="workspace-icon">{(organization?.name || 'L').charAt(0).toUpperCase()}</span>
|
||||
<div>{t('layout.learningSpace')}<small>{organization?.name ?? '-'}</small></div>
|
||||
<Icon name="chevron-updown" />
|
||||
</button>
|
||||
{open && (
|
||||
<ul className="workspace-menu" role="listbox" aria-label={t('layout.switchOrg')}>
|
||||
{userMemberships.data?.map(({ organization: org }) => (
|
||||
<li key={org.id} role="option" aria-selected={org.id === organization?.id}>
|
||||
<button type="button" onClick={() => choose(org.id)}>
|
||||
<span className="workspace-icon">{org.name.charAt(0).toUpperCase()}</span>
|
||||
<span>{org.name}</span>
|
||||
{org.id === organization?.id && <Icon name="check" />}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function AppLayout() {
|
||||
useLang()
|
||||
const [help, setHelp] = useState(false)
|
||||
const { user } = useUser()
|
||||
const { organization } = useOrganization()
|
||||
const { signOut } = useClerk()
|
||||
const { pathname } = useLocation()
|
||||
const current = NAV.find(n => pathname.startsWith(n.to))
|
||||
@@ -84,10 +130,7 @@ export function AppLayout() {
|
||||
<div className="app">
|
||||
<aside className="sidebar">
|
||||
<NavLink to="/brain"><Brand /></NavLink>
|
||||
<div className="workspace">
|
||||
<span className="workspace-icon">{(organization?.name || 'L').charAt(0).toUpperCase()}</span>
|
||||
<div>{t('layout.learningSpace')}<small>{organization?.name ?? '-'}</small></div>
|
||||
</div>
|
||||
<OrgSwitcher />
|
||||
<div className="nav-label">{t('nav.workspace')}</div>
|
||||
<nav aria-label={t('layout.mainNav')}>
|
||||
{NAV.map(n => (
|
||||
|
||||
@@ -16,7 +16,8 @@ const paths: Record<string, string> = {
|
||||
search: '<circle cx="10" cy="10" r="6"/><path d="m15 15 5 5"/>',
|
||||
info: '<circle cx="12" cy="12" r="9"/><path d="M12 11v6M12 7v.2"/>',
|
||||
check: '<path d="m5 12 4 4L19 6"/>',
|
||||
logout: '<path d="M10 4H4v16h6M10 12h11m-4-4 4 4-4 4"/>',
|
||||
'chevron-updown': '<path d="m8 9 4-4 4 4M8 15l4 4 4-4"/>',
|
||||
logout:'<path d="M10 4H4v16h6M10 12h11m-4-4 4 4-4 4"/>',
|
||||
plus: '<path d="M12 4v16M4 12h16"/>',
|
||||
trash: '<path d="M3 6h18M9 3h6l1 3M6 6l1 15h10l1-15M10 10v7M14 10v7"/>',
|
||||
video: '<rect x="3" y="5" width="18" height="14" rx="3"/><path d="m10 9 5 3-5 3z"/>',
|
||||
|
||||
Reference in New Issue
Block a user