fix(sidebar): show org settings link for platform admins without memberships
Platform admins query GET /organizations (not memberships) so currentOrgId was always null — hiding the Settings nav link. Now falls back to the first org from useOrganizations() for admins, gated with enabled:isPlatformAdmin to avoid 403 for non-admin roles. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e60e7c96e7
commit
27286e23db
2 changed files with 54 additions and 25 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { useAuthStore } from '../../lib/auth';
|
||||
import { useMyMemberships } from '../../hooks/useClients';
|
||||
import { useMyMemberships, useOrganizations } from '../../hooks/useClients';
|
||||
import { useJobs, useBriefs, useMyQCQueueCount } from '../../hooks/useJob';
|
||||
|
||||
interface SidebarItem {
|
||||
|
|
@ -24,6 +24,7 @@ export function Sidebar({ onMobileClose }: SidebarProps) {
|
|||
const isQCRole = ['linguist', 'reviewer', 'production', 'admin'].includes(user?.role || '');
|
||||
const isPMOrAdmin = ['project_manager', 'admin'].includes(user?.role || '');
|
||||
const isAdminOrProduction = ['production', 'admin'].includes(user?.role || '');
|
||||
const isPlatformAdmin = user?.role === 'admin';
|
||||
|
||||
const myQCCount = useMyQCQueueCount(isQCRole);
|
||||
const { data: finalData } = useJobs(
|
||||
|
|
@ -36,18 +37,20 @@ export function Sidebar({ onMobileClose }: SidebarProps) {
|
|||
);
|
||||
|
||||
const { data: allBriefs = [] } = useBriefs();
|
||||
const { data: allOrgs = [] } = useOrganizations({ enabled: isPlatformAdmin });
|
||||
|
||||
const qcBadge = isQCRole ? myQCCount : 0;
|
||||
const finalBadge = isPMOrAdmin ? (finalData?.total || 0) : 0;
|
||||
const failuresBadge = isAdminOrProduction ? (failuresData?.total || 0) : 0;
|
||||
const briefsBadge = allBriefs.filter(b => b.status === 'submitted').length;
|
||||
|
||||
// Determine current org ID from route params or first membership.
|
||||
// Determine current org ID from route params, first membership, or first org (admin fallback).
|
||||
// The route param :orgSlug actually carries the organization _id (hex string),
|
||||
// not the human-readable slug — the backend queries memberships by organization_id.
|
||||
const currentOrgId =
|
||||
params.orgSlug ||
|
||||
(memberships.length === 1 ? memberships[0].organization_id : null);
|
||||
(memberships.length > 0 ? memberships[0].organization_id : null) ||
|
||||
(isPlatformAdmin && allOrgs.length > 0 ? allOrgs[0].id : null);
|
||||
|
||||
const sidebarItems: SidebarItem[] = [
|
||||
{
|
||||
|
|
@ -152,29 +155,54 @@ export function Sidebar({ onMobileClose }: SidebarProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Org Switcher — shown when user has memberships */}
|
||||
{memberships.length > 0 && (
|
||||
{/* Org Switcher — shown when user has memberships or is platform admin with orgs */}
|
||||
{(memberships.length > 0 || (isPlatformAdmin && allOrgs.length > 0)) && (
|
||||
<div className="px-4 py-3 border-b border-gray-100 bg-gray-50">
|
||||
{memberships.length === 1 ? (
|
||||
<div className="text-xs text-gray-500">
|
||||
<span className="font-medium text-gray-700">{memberships[0].organization_name}</span>
|
||||
<span className="ml-1 capitalize text-gray-400">· {memberships[0].role_in_org}</span>
|
||||
</div>
|
||||
{memberships.length > 0 ? (
|
||||
memberships.length === 1 ? (
|
||||
<div className="text-xs text-gray-500">
|
||||
<span className="font-medium text-gray-700">{memberships[0].organization_name}</span>
|
||||
<span className="ml-1 capitalize text-gray-400">· {memberships[0].role_in_org}</span>
|
||||
</div>
|
||||
) : (
|
||||
<select
|
||||
value={currentOrgId || ''}
|
||||
onChange={(e) => {
|
||||
const orgId = e.target.value;
|
||||
window.location.href = `/video-accessibility/org/${orgId}/settings/members`;
|
||||
}}
|
||||
className="w-full text-xs border-0 bg-transparent text-gray-700 font-medium focus:outline-none cursor-pointer"
|
||||
>
|
||||
{memberships.map(m => (
|
||||
<option key={m.organization_id} value={m.organization_id}>
|
||||
{m.organization_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)
|
||||
) : (
|
||||
<select
|
||||
value={currentOrgId || ''}
|
||||
onChange={(e) => {
|
||||
const orgId = e.target.value;
|
||||
window.location.href = `/video-accessibility/org/${orgId}/settings/members`;
|
||||
}}
|
||||
className="w-full text-xs border-0 bg-transparent text-gray-700 font-medium focus:outline-none cursor-pointer"
|
||||
>
|
||||
{memberships.map(m => (
|
||||
<option key={m.organization_id} value={m.organization_id}>
|
||||
{m.organization_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
/* Platform admin with no personal memberships — show org switcher from full org list */
|
||||
allOrgs.length === 1 ? (
|
||||
<div className="text-xs text-gray-500">
|
||||
<span className="font-medium text-gray-700">{allOrgs[0].name}</span>
|
||||
<span className="ml-1 text-gray-400">· admin</span>
|
||||
</div>
|
||||
) : (
|
||||
<select
|
||||
value={currentOrgId || ''}
|
||||
onChange={(e) => {
|
||||
const orgId = e.target.value;
|
||||
window.location.href = `/video-accessibility/org/${orgId}/settings/members`;
|
||||
}}
|
||||
className="w-full text-xs border-0 bg-transparent text-gray-700 font-medium focus:outline-none cursor-pointer"
|
||||
>
|
||||
{allOrgs.map(org => (
|
||||
<option key={org.id} value={org.id}>
|
||||
{org.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -223,10 +223,11 @@ export function useArchiveProject(clientId: string) {
|
|||
|
||||
// ── Organizations (SaaS) ──────────────────────────────────────────────────────
|
||||
|
||||
export function useOrganizations() {
|
||||
export function useOrganizations(options?: { enabled?: boolean }) {
|
||||
return useQuery({
|
||||
queryKey: ['organizations'],
|
||||
queryFn: () => apiClient.listOrganizations(),
|
||||
enabled: options?.enabled !== false,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue