// src/components/TelegramaWidget.tsx import { useState, useEffect, useMemo } from 'react'; import Select, { type FilterOptionOption } from 'react-select'; // <-- Importar react-select import { getSecciones, getMunicipiosPorSeccion, getEstablecimientosPorMunicipio, // <-- Nueva función getMesasPorEstablecimiento, getTelegramaPorId, assetBaseUrl } from '../apiService'; import type { TelegramaData, CatalogoItem } from '../types/types'; import './TelegramaWidget.css'; import { pdfjs, Document, Page } from 'react-pdf'; import 'react-pdf/dist/Page/AnnotationLayer.css'; import 'react-pdf/dist/Page/TextLayer.css'; pdfjs.GlobalWorkerOptions.workerSrc = `${assetBaseUrl}/pdf.worker.min.mjs`; // Estilos para los selectores const customSelectStyles = { control: (base: any) => ({ ...base, marginBottom: '1rem' }), menu: (base: any) => ({ ...base, zIndex: 10 }), }; // --- FUNCIÓN DE FILTRO "SMART SEARCH" --- const smartSearchFilter = ( option: FilterOptionOption<{ label: string; value: string }>, inputValue: string ) => { // 1. Si no hay entrada de búsqueda, muestra todas las opciones. if (!inputValue) { return true; } // 2. Normalizamos tanto la etiqueta de la opción como la entrada del usuario: // - a minúsculas // - quitamos los acentos (si fuera necesario, aunque aquí no tanto) const normalizedLabel = option.label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); const normalizedInput = inputValue.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); // 3. Dividimos la entrada del usuario en palabras individuales. const searchTerms = normalizedInput.split(' ').filter(term => term.length > 0); // 4. La opción es válida si CADA TÉRMINO de búsqueda está incluido en la etiqueta. return searchTerms.every(term => normalizedLabel.includes(term)); }; export const TelegramaWidget = () => { // Estados para los datos de los dropdowns const [secciones, setSecciones] = useState([]); const [municipios, setMunicipios] = useState([]); const [establecimientos, setEstablecimientos] = useState([]); const [mesas, setMesas] = useState([]); // Estados para los valores seleccionados (adaptados para react-select) const [selectedSeccion, setSelectedSeccion] = useState<{ value: string; label: string } | null>(null); const [selectedMunicipio, setSelectedMunicipio] = useState<{ value: string; label: string } | null>(null); const [selectedEstablecimiento, setSelectedEstablecimiento] = useState<{ value: string; label: string } | null>(null); const [selectedMesa, setSelectedMesa] = useState<{ value: string; label: string } | null>(null); // Estados para la visualización del telegrama (sin cambios) const [telegrama, setTelegrama] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); // Cargar secciones iniciales y aplicar orden useEffect(() => { getSecciones().then(seccionesData => { const orden = new Map([ ['Capital', 0], ['Primera', 1], ['Segunda', 2], ['Tercera', 3], ['Cuarta', 4], ['Quinta', 5], ['Sexta', 6], ['Séptima', 7] ]); const getOrden = (nombre: string) => { const match = nombre.match(/Capital|Primera|Segunda|Tercera|Cuarta|Quinta|Sexta|Séptima/); return match ? orden.get(match[0]) ?? 99 : 99; }; seccionesData.sort((a, b) => getOrden(a.nombre) - getOrden(b.nombre)); setSecciones(seccionesData); }); }, []); // Cargar municipios cuando cambia la sección useEffect(() => { if (selectedSeccion) { setMunicipios([]); setEstablecimientos([]); setMesas([]); setSelectedMunicipio(null); setSelectedEstablecimiento(null); setSelectedMesa(null); getMunicipiosPorSeccion(selectedSeccion.value).then(setMunicipios); } }, [selectedSeccion]); // Cargar establecimientos cuando cambia el municipio (SIN pasar por circuito) useEffect(() => { if (selectedMunicipio) { setEstablecimientos([]); setMesas([]); setSelectedEstablecimiento(null); setSelectedMesa(null); getEstablecimientosPorMunicipio(selectedMunicipio.value).then(setEstablecimientos); } }, [selectedMunicipio]); // Cargar mesas cuando cambia el establecimiento useEffect(() => { if (selectedEstablecimiento) { setMesas([]); setSelectedMesa(null); getMesasPorEstablecimiento(selectedEstablecimiento.value).then(setMesas); } }, [selectedEstablecimiento]); // Buscar el telegrama cuando se selecciona una mesa useEffect(() => { if (selectedMesa) { setLoading(true); setError(null); setTelegrama(null); getTelegramaPorId(selectedMesa.value) .then(setTelegrama) .catch(() => setError(`El telegrama para la mesa seleccionada, aún no se cargó en el sistema.`)) .finally(() => setLoading(false)); } }, [selectedMesa]); // Formateo de opciones para react-select const seccionOptions = useMemo(() => secciones.map(s => ({ value: s.id, label: s.nombre })), [secciones]); const municipioOptions = useMemo(() => municipios.map(m => ({ value: m.id, label: m.nombre })), [municipios]); const establecimientoOptions = useMemo(() => establecimientos.map(e => ({ value: e.id, label: e.nombre })), [establecimientos]); const mesaOptions = useMemo(() => mesas.map(m => ({ value: m.id, label: m.nombre })), [mesas]); return (

Consulta de Telegramas por Ubicación

{loading &&
} {error &&

{error}

} {telegrama && (
setError(`Error al cargar el PDF: ${error.message}`)} loading={
} >
)} {!loading && !telegrama && !error &&

Seleccione una mesa para visualizar el telegrama.

}
); };