This reverts commit d3b326e.
The deployment caused reports of blank screens and 400 errors. Reverting to restore stable state while investigating root cause.
165 lines
6.4 KiB
TypeScript
165 lines
6.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { getTenants, type Tenant } from '@/lib/api/tenants';
|
|
import { useTenantViewStore } from '@/stores/tenant-view-store';
|
|
import { useContribuyenteStore } from '@/stores/contribuyente-store';
|
|
import { useAuthStore } from '@/stores/auth-store';
|
|
import { Building, ChevronDown, Check, X } from 'lucide-react';
|
|
import { cn } from '@horux/shared-ui';
|
|
import { isGlobalAdminRfc } from '@horux/shared';
|
|
|
|
export function TenantSelector() {
|
|
const [open, setOpen] = useState(false);
|
|
const { user } = useAuthStore();
|
|
const queryClient = useQueryClient();
|
|
const { viewingTenantId, viewingTenantName, setViewingTenant, clearViewingTenant } = useTenantViewStore();
|
|
const { clearSelectedContribuyente } = useContribuyenteStore();
|
|
const isGlobalAdmin = isGlobalAdminRfc(user?.tenantRfc, user?.role, user?.platformRoles);
|
|
|
|
const { data: tenants, isLoading } = useQuery({
|
|
queryKey: ['tenants'],
|
|
queryFn: getTenants,
|
|
enabled: isGlobalAdmin,
|
|
});
|
|
|
|
// Close dropdown when clicking outside
|
|
useEffect(() => {
|
|
const handleClickOutside = (e: MouseEvent) => {
|
|
const target = e.target as HTMLElement;
|
|
if (!target.closest('.tenant-selector')) {
|
|
setOpen(false);
|
|
}
|
|
};
|
|
document.addEventListener('click', handleClickOutside);
|
|
return () => document.removeEventListener('click', handleClickOutside);
|
|
}, []);
|
|
|
|
// Solo admin global — ningún otro admin puede cambiar de tenant
|
|
if (!isGlobalAdmin) {
|
|
return null;
|
|
}
|
|
|
|
const currentTenant = viewingTenantId
|
|
? tenants?.find(t => t.id === viewingTenantId)
|
|
: null;
|
|
|
|
const displayName = viewingTenantName || currentTenant?.nombre || user?.tenantName;
|
|
const isViewingOther = viewingTenantId && viewingTenantId !== user?.tenantId;
|
|
|
|
return (
|
|
<div className="tenant-selector relative">
|
|
<button
|
|
onClick={() => setOpen(!open)}
|
|
className={cn(
|
|
'flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
|
|
isViewingOther
|
|
? 'bg-primary/10 text-primary border border-primary/30'
|
|
: 'hover:bg-accent'
|
|
)}
|
|
>
|
|
<Building className="h-4 w-4" />
|
|
<span className="max-w-[150px] truncate">{displayName}</span>
|
|
{isViewingOther && (
|
|
<span
|
|
role="button"
|
|
tabIndex={0}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
clearViewingTenant();
|
|
clearSelectedContribuyente();
|
|
queryClient.invalidateQueries();
|
|
}}
|
|
onKeyDown={(e) => {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.stopPropagation();
|
|
clearViewingTenant();
|
|
clearSelectedContribuyente();
|
|
queryClient.invalidateQueries();
|
|
}
|
|
}}
|
|
className="ml-1 p-0.5 rounded hover:bg-primary/20 cursor-pointer"
|
|
title="Volver a mi empresa"
|
|
>
|
|
<X className="h-3 w-3" />
|
|
</span>
|
|
)}
|
|
<ChevronDown className={cn('h-4 w-4 transition-transform', open && 'rotate-180')} />
|
|
</button>
|
|
|
|
{open && (
|
|
<div className="absolute top-full left-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">Seleccionar cliente</p>
|
|
</div>
|
|
<div className="max-h-64 overflow-y-auto p-1">
|
|
{isLoading ? (
|
|
<div className="px-3 py-2 text-sm text-muted-foreground">Cargando...</div>
|
|
) : tenants && tenants.length > 0 ? (
|
|
<>
|
|
{/* Option to go back to own tenant */}
|
|
<button
|
|
onClick={() => {
|
|
clearViewingTenant();
|
|
clearSelectedContribuyente();
|
|
setOpen(false);
|
|
queryClient.invalidateQueries();
|
|
}}
|
|
className={cn(
|
|
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors',
|
|
!viewingTenantId && 'bg-primary/10'
|
|
)}
|
|
>
|
|
<div className="h-8 w-8 rounded bg-primary/10 flex items-center justify-center">
|
|
<Building className="h-4 w-4 text-primary" />
|
|
</div>
|
|
<div className="flex-1 text-left">
|
|
<p className="font-medium">{user?.tenantName}</p>
|
|
<p className="text-xs text-muted-foreground">Mi empresa</p>
|
|
</div>
|
|
{!viewingTenantId && <Check className="h-4 w-4 text-primary" />}
|
|
</button>
|
|
|
|
<div className="my-1 border-t" />
|
|
|
|
{/* Other tenants */}
|
|
{tenants
|
|
.filter(t => t.id !== user?.tenantId)
|
|
.map((tenant) => (
|
|
<button
|
|
key={tenant.id}
|
|
onClick={() => {
|
|
setViewingTenant(tenant.id, tenant.nombre, tenant.rfc);
|
|
clearSelectedContribuyente();
|
|
setOpen(false);
|
|
queryClient.invalidateQueries();
|
|
}}
|
|
className={cn(
|
|
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors',
|
|
viewingTenantId === tenant.id && 'bg-primary/10'
|
|
)}
|
|
>
|
|
<div className="h-8 w-8 rounded bg-muted flex items-center justify-center text-xs font-medium">
|
|
{tenant.nombre.substring(0, 2).toUpperCase()}
|
|
</div>
|
|
<div className="flex-1 text-left">
|
|
<p className="font-medium truncate">{tenant.nombre}</p>
|
|
<p className="text-xs text-muted-foreground">{tenant.rfc}</p>
|
|
</div>
|
|
{viewingTenantId === tenant.id && <Check className="h-4 w-4 text-primary" />}
|
|
</button>
|
|
))}
|
|
</>
|
|
) : (
|
|
<div className="px-3 py-2 text-sm text-muted-foreground">
|
|
No hay otros clientes
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|