Initial commit - Horux Despachos NL
This commit is contained in:
108
apps/web/components/contribuyente-selector.tsx
Normal file
108
apps/web/components/contribuyente-selector.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
'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]);
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user