This reverts commit d3b326e.
The deployment caused reports of blank screens and 400 errors. Reverting to restore stable state while investigating root cause.
119 lines
5.2 KiB
TypeScript
119 lines
5.2 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { usePathname } from 'next/navigation';
|
|
import { useContribuyentes } from '@/lib/hooks/use-contribuyentes';
|
|
import { useContribuyenteStore } from '@/stores/contribuyente-store';
|
|
import { cn } from '@horux/shared-ui';
|
|
import { Building2, ChevronDown, Check, Users } from 'lucide-react';
|
|
|
|
// Rutas donde el selector NO aplica (vistas cross-contribuyente del despacho).
|
|
const HIDDEN_PATHS = ['/despachos'];
|
|
|
|
export function ContribuyenteSelector() {
|
|
const pathname = usePathname();
|
|
const [open, setOpen] = useState(false);
|
|
const { data: contribuyentes, isLoading } = useContribuyentes();
|
|
const { selectedContribuyenteId, setSelectedContribuyente, clearSelectedContribuyente } =
|
|
useContribuyenteStore();
|
|
|
|
useEffect(() => {
|
|
const handleClickOutside = (e: MouseEvent) => {
|
|
const target = e.target as HTMLElement;
|
|
if (!target.closest('.contribuyente-selector')) setOpen(false);
|
|
};
|
|
document.addEventListener('click', handleClickOutside);
|
|
return () => document.removeEventListener('click', handleClickOutside);
|
|
}, []);
|
|
|
|
// Auto-select if user has exactly 1 contribuyente (common for clients)
|
|
useEffect(() => {
|
|
if (contribuyentes && contribuyentes.length === 1 && !selectedContribuyenteId) {
|
|
setSelectedContribuyente(contribuyentes[0].id, contribuyentes[0].rfc, contribuyentes[0].nombre);
|
|
}
|
|
}, [contribuyentes, selectedContribuyenteId, setSelectedContribuyente]);
|
|
|
|
// Clear invalid selection (e.g. stale localStorage from another tenant/session)
|
|
useEffect(() => {
|
|
if (contribuyentes && contribuyentes.length > 0 && selectedContribuyenteId) {
|
|
const exists = contribuyentes.some(c => c.id === selectedContribuyenteId);
|
|
if (!exists) {
|
|
clearSelectedContribuyente();
|
|
}
|
|
}
|
|
}, [contribuyentes, selectedContribuyenteId, clearSelectedContribuyente]);
|
|
|
|
if (isLoading || !contribuyentes || contribuyentes.length === 0) return null;
|
|
if (pathname && HIDDEN_PATHS.some(p => pathname === p || pathname.startsWith(`${p}/`))) return null;
|
|
|
|
const selected = contribuyentes.find((c) => c.id === selectedContribuyenteId);
|
|
|
|
return (
|
|
<div className="contribuyente-selector relative">
|
|
<button
|
|
onClick={() => setOpen(!open)}
|
|
className="flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium hover:bg-accent transition-colors"
|
|
>
|
|
<Building2 className="h-4 w-4" />
|
|
<span className="max-w-[180px] truncate">
|
|
{selected ? selected.nombre : 'Todos los RFCs'}
|
|
</span>
|
|
<ChevronDown className={cn('h-4 w-4 transition-transform', open && 'rotate-180')} />
|
|
</button>
|
|
|
|
{open && (
|
|
<div className="absolute top-full right-0 mt-2 w-80 rounded-lg border bg-card shadow-lg z-50">
|
|
<div className="p-2 border-b">
|
|
<p className="text-xs text-muted-foreground px-2">Contribuyentes</p>
|
|
</div>
|
|
<div className="max-h-80 overflow-y-auto p-1">
|
|
{/* Todos los RFCs — only show if more than 1 contribuyente */}
|
|
{contribuyentes.length > 1 && (
|
|
<>
|
|
<button
|
|
onClick={() => { clearSelectedContribuyente(); setOpen(false); }}
|
|
className={cn(
|
|
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors text-left',
|
|
!selectedContribuyenteId && 'bg-primary/10'
|
|
)}
|
|
>
|
|
<div className="h-8 w-8 rounded bg-muted flex items-center justify-center">
|
|
<Users className="h-4 w-4 text-muted-foreground" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="font-medium">Todos los RFCs</p>
|
|
<p className="text-xs text-muted-foreground">{contribuyentes.length} contribuyentes</p>
|
|
</div>
|
|
{!selectedContribuyenteId && <Check className="h-4 w-4 text-primary flex-shrink-0" />}
|
|
</button>
|
|
<div className="border-t my-1" />
|
|
</>
|
|
)}
|
|
|
|
{/* Lista de contribuyentes */}
|
|
{contribuyentes.map((c) => (
|
|
<button
|
|
key={c.id}
|
|
onClick={() => { setSelectedContribuyente(c.id, c.rfc, c.nombre); setOpen(false); }}
|
|
className={cn(
|
|
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm hover:bg-accent transition-colors text-left',
|
|
selectedContribuyenteId === c.id && 'bg-primary/10'
|
|
)}
|
|
>
|
|
<div className="h-8 w-8 rounded bg-muted flex items-center justify-center text-xs font-medium">
|
|
{c.nombre.substring(0, 2).toUpperCase()}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="font-medium truncate">{c.nombre}</p>
|
|
<p className="text-xs text-muted-foreground truncate">{c.rfc}</p>
|
|
</div>
|
|
{selectedContribuyenteId === c.id && <Check className="h-4 w-4 text-primary flex-shrink-0" />}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|