fix(settings): lock site context to current URL, rename Workspace to Organization

- Site context is locked to the site from the current URL — no dropdown
  switcher. If not on a site page, defaults to Organization context.
- Renamed "Workspace" to "Organization" in all user-facing text.
- Removed unused CaretDown import and dropdown state.
This commit is contained in:
Usman Baig
2026-03-24 16:52:59 +01:00
parent ea2c47b53f
commit e12a3661fa
3 changed files with 34 additions and 77 deletions

View File

@@ -2,7 +2,7 @@
import { useState, useCallback, useEffect } from 'react'
import { AnimatePresence, motion } from 'framer-motion'
import { X, GearSix, Buildings, User, CaretDown } from '@phosphor-icons/react'
import { X, GearSix, Buildings, User } from '@phosphor-icons/react'
import { useUnifiedSettings } from '@/lib/unified-settings-context'
import { useAuth } from '@/lib/auth/context'
import { useSite } from '@/lib/swr/dashboard'
@@ -65,28 +65,18 @@ const ACCOUNT_TABS: TabDef[] = [
function ContextSwitcher({
active,
onChange,
sites,
activeSiteId,
onSiteChange,
activeSiteDomain,
}: {
active: SettingsContext
onChange: (ctx: SettingsContext) => void
sites: Site[]
activeSiteId: string | null
onSiteChange: (id: string) => void
activeSiteDomain: string | null
}) {
const [siteDropdownOpen, setSiteDropdownOpen] = useState(false)
const activeSite = sites.find(s => s.id === activeSiteId)
return (
<div className="flex items-center gap-1 p-1 bg-neutral-800/50 rounded-xl">
{/* Site button with dropdown */}
<div className="relative">
{/* Site button — locked to current site, no dropdown */}
{activeSiteDomain && (
<button
onClick={() => {
onChange('site')
if (active === 'site') setSiteDropdownOpen(!siteDropdownOpen)
}}
onClick={() => onChange('site')}
className={`flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg transition-all duration-200 ${
active === 'site'
? 'bg-neutral-700 text-white shadow-sm'
@@ -94,45 +84,12 @@ function ContextSwitcher({
}`}
>
<GearSix weight="bold" className="w-4 h-4" />
<span className="hidden sm:inline">
{activeSite ? activeSite.domain : 'Site'}
</span>
<CaretDown weight="bold" className="w-3 h-3" />
<span className="hidden sm:inline">{activeSiteDomain}</span>
</button>
<AnimatePresence>
{siteDropdownOpen && active === 'site' && sites.length > 1 && (
<motion.div
initial={{ opacity: 0, y: -4 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -4 }}
transition={{ duration: 0.15 }}
className="absolute top-full left-0 mt-1 w-56 rounded-xl bg-neutral-800 border border-neutral-700 shadow-xl z-50 py-1 overflow-hidden"
>
{sites.map(site => (
<button
key={site.id}
onClick={() => {
onSiteChange(site.id)
setSiteDropdownOpen(false)
}}
className={`w-full text-left px-3 py-2 text-sm transition-colors ${
site.id === activeSiteId
? 'bg-brand-orange/10 text-brand-orange'
: 'text-neutral-300 hover:bg-neutral-700/50'
}`}
>
<span className="font-medium">{site.name}</span>
<span className="ml-2 text-neutral-500 text-xs">{site.domain}</span>
</button>
))}
</motion.div>
)}
</AnimatePresence>
</div>
)}
<button
onClick={() => { onChange('workspace'); setSiteDropdownOpen(false) }}
onClick={() => onChange('workspace')}
className={`flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg transition-all duration-200 ${
active === 'workspace'
? 'bg-neutral-700 text-white shadow-sm'
@@ -140,11 +97,11 @@ function ContextSwitcher({
}`}
>
<Buildings weight="bold" className="w-4 h-4" />
<span className="hidden sm:inline">Workspace</span>
<span className="hidden sm:inline">Organization</span>
</button>
<button
onClick={() => { onChange('account'); setSiteDropdownOpen(false) }}
onClick={() => onChange('account')}
className={`flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg transition-all duration-200 ${
active === 'account'
? 'bg-neutral-700 text-white shadow-sm'
@@ -282,26 +239,28 @@ export default function UnifiedSettingsModal() {
}
}, [isOpen, initTab])
// Load sites when modal opens
// Detect site from URL and load sites list when modal opens
useEffect(() => {
if (isOpen && user?.org_id) {
listSites().then(data => {
const list = Array.isArray(data) ? data : []
setSites(list)
if (!activeSiteId && list.length > 0) {
setActiveSiteId(list[0].id)
}
}).catch(() => {})
}
}, [isOpen, user?.org_id])
if (!isOpen || !user?.org_id) return
// Try to pick up site from URL
useEffect(() => {
if (isOpen && typeof window !== 'undefined') {
// Pick up site ID from URL — this is the only site the user can configure
if (typeof window !== 'undefined') {
const match = window.location.pathname.match(/\/sites\/([a-f0-9-]+)/)
if (match) setActiveSiteId(match[1])
if (match) {
setActiveSiteId(match[1])
setContext('site')
} else {
// Not on a site page — default to organization context
setActiveSiteId(null)
if (!initTab?.context) setContext('workspace')
}
}
}, [isOpen])
// Load sites for domain display
listSites().then(data => {
setSites(Array.isArray(data) ? data : [])
}).catch(() => {})
}, [isOpen, user?.org_id])
// Escape key closes
useEffect(() => {
@@ -363,9 +322,7 @@ export default function UnifiedSettingsModal() {
<ContextSwitcher
active={context}
onChange={handleContextChange}
sites={sites}
activeSiteId={activeSiteId}
onSiteChange={setActiveSiteId}
activeSiteDomain={sites.find(s => s.id === activeSiteId)?.domain ?? null}
/>
{/* Tabs */}

View File

@@ -31,9 +31,9 @@ export default function WorkspaceGeneralTab() {
setSaving(true)
try {
await updateOrganization(user.org_id, name, slug)
toast.success('Workspace updated')
toast.success('Organization updated')
} catch (err) {
toast.error(getAuthErrorMessage(err as Error) || 'Failed to update workspace')
toast.error(getAuthErrorMessage(err as Error) || 'Failed to update organization')
} finally {
setSaving(false)
}
@@ -52,7 +52,7 @@ export default function WorkspaceGeneralTab() {
<div className="space-y-4">
<div>
<h3 className="text-base font-semibold text-white mb-1">General Information</h3>
<p className="text-sm text-neutral-400">Basic details about your workspace.</p>
<p className="text-sm text-neutral-400">Basic details about your organization.</p>
</div>
<div>

View File

@@ -82,7 +82,7 @@ export default function WorkspaceMembersTab() {
<div className="flex items-center justify-between">
<div>
<h3 className="text-base font-semibold text-white mb-1">Members</h3>
<p className="text-sm text-neutral-400">{members.length} member{members.length !== 1 ? 's' : ''} in your workspace.</p>
<p className="text-sm text-neutral-400">{members.length} member{members.length !== 1 ? 's' : ''} in your organization.</p>
</div>
{canManage && !showInvite && (
<Button onClick={() => setShowInvite(true)} variant="primary" className="text-sm gap-1.5">