import React, { useState, useRef, useEffect, useMemo } from 'react'; import { ColumnFilterCondition } from '../types'; import { CloseIcon, FunnelIcon } from './Icons'; interface ExcelFilterProps { columnKey: string; title: string; uniqueValues: string[]; currentFilter?: ColumnFilterCondition; onFilterChange: (columnKey: string, condition: ColumnFilterCondition | undefined) => void; icon?: React.ReactNode; } const OPERATORS = [ { label: 'Elija uno', value: '' }, { label: 'Es igual a', value: 'equals' }, { label: 'No es igual a', value: 'notEquals' }, { label: 'Contiene', value: 'contains' }, { label: 'No contiene', value: 'notContains' }, { label: 'Comienza por', value: 'startsWith' }, { label: 'No comienza por', value: 'notStartsWith' }, { label: 'Termina con', value: 'endsWith' }, { label: 'No termina con', value: 'notEndsWith' }, { label: 'Mayor que', value: 'gt' }, { label: 'Menor que', value: 'lt' }, { label: 'Mayor o igual que', value: 'gte' }, { label: 'Menor o igual que', value: 'lte' }, ] as const; export const ExcelFilter: React.FC = ({ columnKey, title, uniqueValues, currentFilter, onFilterChange, icon, buttonClassName }) => { const [isOpen, setIsOpen] = useState(false); const [operator, setOperator] = useState(currentFilter?.textFilter?.operator || ''); const [textValue, setTextValue] = useState(currentFilter?.textFilter?.value || ''); const [searchValue, setSearchValue] = useState(''); const [tempSelectedValues, setTempSelectedValues] = useState(currentFilter?.selectedValues || []); const containerRef = useRef(null); // Synchronization with currentFilter prop useEffect(() => { if (isOpen) { setOperator(currentFilter?.textFilter?.operator || ''); setTextValue(currentFilter?.textFilter?.value || ''); setTempSelectedValues(currentFilter?.selectedValues || []); } }, [isOpen, currentFilter]); // Close when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const filteredValues = useMemo(() => { if (!searchValue) return uniqueValues; const lowerSearch = searchValue.toLowerCase(); return uniqueValues.filter(v => String(v).toLowerCase().includes(lowerSearch)); }, [uniqueValues, searchValue]); const handleToggleSelectAll = () => { if (tempSelectedValues.length === uniqueValues.length) { setTempSelectedValues([]); } else { setTempSelectedValues([...uniqueValues]); } }; const handleToggleValue = (val: string) => { setTempSelectedValues(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val] ); }; const handleApply = () => { const newCondition: ColumnFilterCondition = { ...currentFilter, selectedValues: tempSelectedValues.length > 0 ? tempSelectedValues : undefined, textFilter: operator ? { operator: operator as any, value: textValue } : undefined }; // Remove empty properties if (!newCondition.selectedValues) delete newCondition.selectedValues; if (!newCondition.textFilter) delete newCondition.textFilter; onFilterChange(columnKey, Object.keys(newCondition).length > 0 ? newCondition : undefined); setIsOpen(false); }; const handleClear = () => { onFilterChange(columnKey, undefined); setOperator(''); setTextValue(''); setTempSelectedValues([]); setIsOpen(false); }; const handleSort = (direction: 'asc' | 'desc') => { onFilterChange(columnKey, { ...currentFilter, sort: direction }); setIsOpen(false); }; const hasActiveFilter = !!(currentFilter?.textFilter || currentFilter?.selectedValues); return (
{isOpen && (
{title}
{/* Sort Section */}
Ordenar
{/* Condition Filter Section */}
Filtro
{operator && ( setTextValue(e.target.value)} placeholder="Valor..." className="w-full bg-slate-800 border border-slate-700 rounded-lg px-2 py-1.5 text-[11px] text-white focus:outline-none focus:border-indigo-500" autoFocus /> )}
{/* List Selection Section */}
Seleccionar valores
setSearchValue(e.target.value)} className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2 py-1 text-[11px] text-white focus:outline-none focus:border-indigo-500 mb-1.5" />
{tempSelectedValues.length === uniqueValues.length && }
(Seleccionar todo)
{filteredValues.map(val => { const isSelected = tempSelectedValues.includes(val); return (
handleToggleValue(val)} className="flex items-center gap-1.5 px-1.5 py-1 rounded hover:bg-white/5 cursor-pointer text-[11px] text-slate-300" >
{isSelected && }
{val}
); })}
)}
); }; const CheckMark = () => ( );