Files
HoruxDespachosNuevo/apps/web/components/membership-switcher.tsx
Horux Dev 66d68c652c Revert "feat(ui): make dashboard responsive for iPhone and mobile devices"
This reverts commit d3b326e.

The deployment caused reports of blank screens and 400 errors. Reverting to restore stable state while investigating root cause.
2026-06-13 20:16:04 +00:00

115 lines
4.4 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { useAuthStore } from '@/stores/auth-store';
import { switchTenant } from '@/lib/api/auth';
import { Building2, ChevronDown, Check, Loader2, Crown } from 'lucide-react';
import { cn } from '@horux/shared-ui';
import { isGlobalAdminRfc } from '@horux/shared';
/**
* Switcher para users con múltiples memberships (owner o contador con varias
* empresas). Distinto del TenantSelector de admin global:
* - Admin global: impersonación via X-View-Tenant (no cambia el JWT)
* - Membership switcher: cambia de tenant *real* con nuevo JWT
*
* Se oculta si:
* - El user tiene ≤1 membership
* - El user es admin global (ya tiene su propio TenantSelector, sería redundante)
*/
export function MembershipSwitcher() {
const [open, setOpen] = useState(false);
const [switching, setSwitching] = useState(false);
const { user, setUser, setTokens } = useAuthStore();
const queryClient = useQueryClient();
const isGlobalAdmin = isGlobalAdminRfc(user?.tenantRfc, user?.role, user?.platformRoles);
const tenants = user?.tenants || [];
const showSwitcher = !isGlobalAdmin && tenants.length > 1;
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (!target.closest('.membership-switcher')) setOpen(false);
};
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, []);
if (!showSwitcher) return null;
const activeTenant = tenants.find(t => t.id === user?.tenantId);
const handleSwitch = async (tenantId: string) => {
if (tenantId === user?.tenantId) { setOpen(false); return; }
setSwitching(true);
try {
const res = await switchTenant(tenantId);
setTokens(res.accessToken, res.refreshToken);
setUser(res.user);
// Refresca todo el cache — las queries dependen del tenant activo
queryClient.clear();
setOpen(false);
// Reload para que React Query re-fetche desde cero con el nuevo JWT
window.location.reload();
} catch (err: any) {
alert(err?.response?.data?.message || 'Error al cambiar de empresa');
} finally {
setSwitching(false);
}
};
return (
<div className="membership-switcher relative">
<button
onClick={() => setOpen(!open)}
disabled={switching}
className="flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium hover:bg-accent transition-colors disabled:opacity-50"
>
<Building2 className="h-4 w-4" />
<span className="max-w-[180px] truncate">
{activeTenant?.nombre || user?.tenantName}
</span>
{switching
? <Loader2 className="h-4 w-4 animate-spin" />
: <ChevronDown className={cn('h-4 w-4 transition-transform', open && 'rotate-180')} />
}
</button>
{open && (
<div className="absolute top-full right-0 mt-2 w-72 rounded-lg border bg-card shadow-lg z-50">
<div className="p-2 border-b">
<p className="text-xs text-muted-foreground px-2">Mis empresas</p>
</div>
<div className="max-h-80 overflow-y-auto p-1">
{tenants.map(t => (
<button
key={t.id}
onClick={() => handleSwitch(t.id)}
disabled={switching}
className={cn(
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors text-left',
t.id === user?.tenantId && 'bg-primary/10'
)}
>
<div className="h-8 w-8 rounded bg-muted flex items-center justify-center text-xs font-medium">
{t.nombre.substring(0, 2).toUpperCase()}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-1.5">
<p className="font-medium truncate">{t.nombre}</p>
{t.isOwner && <Crown className="h-3 w-3 text-amber-500 flex-shrink-0" />}
</div>
<p className="text-xs text-muted-foreground truncate">{t.rfc} · {t.role}</p>
</div>
{t.id === user?.tenantId && <Check className="h-4 w-4 text-primary flex-shrink-0" />}
</button>
))}
</div>
</div>
)}
</div>
);
}