From c82af0cfa827ff25a3dedd59fceb3959f5770ffb Mon Sep 17 00:00:00 2001 From: santiago Date: Wed, 15 Oct 2025 23:52:34 +0200 Subject: [PATCH] Modificaciones en los archivos --- src/components/BuscarFuncionario.tsx | 2 +- src/components/OrganigramaInteractivo.tsx | 252 +++++++++++----------- 2 files changed, 127 insertions(+), 127 deletions(-) diff --git a/src/components/BuscarFuncionario.tsx b/src/components/BuscarFuncionario.tsx index 5501ae4..6c00b03 100644 --- a/src/components/BuscarFuncionario.tsx +++ b/src/components/BuscarFuncionario.tsx @@ -1,4 +1,4 @@ -'use client'; + 'use client'; import React, { useState, useEffect } from 'react'; import axios from 'axios'; diff --git a/src/components/OrganigramaInteractivo.tsx b/src/components/OrganigramaInteractivo.tsx index 10cc3e6..a219250 100644 --- a/src/components/OrganigramaInteractivo.tsx +++ b/src/components/OrganigramaInteractivo.tsx @@ -1,46 +1,46 @@ import React from 'react'; interface Props { - onAreaSelect: (areaId: number) => void; - className?: string; - style?: React.CSSProperties; - isPanningRef?: React.RefObject; + onAreaSelect: (areaId: number) => void; + className?: string; + style?: React.CSSProperties; + isPanningRef?: React.RefObject; } // Map display substrings (lowercase) to the area id used in BuscarFuncionario const areaMap: Record = { - 'coordinación de actividades deportivas y recreativas': 128, - 'coordinación de comunicación social': 122, - 'coordinación de cuerpos colegiados': 9, - 'coordinación de derecho del sistema de universidad abierta y educación a distancia': 126, - 'coordinación de especializaciones': 206, - 'coordinación de estudios de posgrado': 120, - 'coordinación de la unidad de investigación multidisciplinaria aplicada': 182, - 'coordinación de licel sistema de universidad abierta y educación a distancia': 174, - 'coordinación de maestrías y doctorados': 221, - 'coordinación de relaciones internacionales del sistema de universidad abierta y educación a distancia': 152, - 'coordinación de servicios académicos': 121, - 'coordinación del centro de difusión cultural': 125, - 'coordinación del centro de educación continua': 124, - 'coordinación del centro de enseñanza de idiomas': 129, - 'coordinación del centro de estudios municipales y metropolitanos': 214, - 'coordinación del centro tecnológico para la educación a distancia': 176, - 'dirección': 72, - 'división de ciencias jurídicas': 80, - 'división de ciencias socioeconómicas': 79, - 'división de diseño y edificación': 78, - 'división de humanidades': 77, - 'división de matemáticas e ingeniería': 76, - 'secretaría de asuntos académicos estudiantiles': 181, - 'secretaría de extensión universitaria y vinculación institucional': 177, - 'secretaría de posgrado e investigación': 119, - 'secretaría general': 8, - 'unidad de administración escolar': 6, - 'unidad de servicios editoriales': 2, - 'unidad de talleres, laboratorios y audiovisuales': 1, - 'unidad del centro de desarrollo tecnológico': 130, - 'unidad del centro de información y documentación': 123, - // add more mappings as needed + 'coordinación de actividades deportivas y recreativas': 128, + 'coordinación de comunicación social': 122, + 'coordinación de cuerpos colegiados': 9, + 'coordinación de derecho del sistema de universidad abierta y educación a distancia': 126, + 'coordinación de especializaciones': 206, + 'coordinación de estudios de posgrado': 120, + 'coordinación de la unidad de investigación multidisciplinaria aplicada': 182, + 'coordinación de licel sistema de universidad abierta y educación a distancia': 174, + 'coordinación de maestrías y doctorados': 221, + 'coordinación de relaciones internacionales del sistema de universidad abierta y educación a distancia': 152, + 'coordinación de servicios académicos': 121, + 'coordinación del centro de difusión cultural': 125, + 'coordinación del centro de educación continua': 124, + 'coordinación del centro de enseñanza de idiomas': 129, + 'coordinación del centro de estudios municipales y metropolitanos': 214, + 'coordinación del centro tecnológico para la educación a distancia': 176, + 'dirección': 72, + 'división de ciencias jurídicas': 80, + 'división de ciencias socioeconómicas': 79, + 'división de diseño y edificación': 78, + 'división de humanidades': 77, + 'división de matemáticas e ingeniería': 76, + 'secretaría de asuntos académicos estudiantiles': 181, + 'secretaría de extensión universitaria y vinculación institucional': 177, + 'secretaría de posgrado e investigación': 119, + 'secretaría general': 8, + 'unidad de administración escolar': 6, + 'unidad de servicios editoriales': 2, + 'unidad de talleres, laboratorios y audiovisuales': 1, + 'unidad del centro de desarrollo tecnológico': 130, + 'unidad del centro de información y documentación': 123, + // add more mappings as needed }; const svgContent = ` @@ -53,112 +53,112 @@ const svgContent = ` // But to keep this component self-contained, we'll render an fallback and then query inside it. export default function OrganigramaInteractivo({ onAreaSelect, className, style, isPanningRef }: Props) { - const containerRef = React.useRef(null); + const containerRef = React.useRef(null); - const normalize = (s: string) => - s - .normalize('NFD') - .replace(/\p{Diacritic}/gu, '') - .toLowerCase() - .replace(/\s+/g, ' ') - .trim(); + const normalize = (s: string) => + s + .normalize('NFD') + .replace(/\p{Diacritic}/gu, '') + .toLowerCase() + .replace(/\s+/g, ' ') + .trim(); - const normalizedAreaEntries = React.useMemo(() => { - return Object.keys(areaMap).map((k) => ({ key: normalize(k), id: areaMap[k] })); - }, []); + const normalizedAreaEntries = React.useMemo(() => { + return Object.keys(areaMap).map((k) => ({ key: normalize(k), id: areaMap[k] })); + }, []); - React.useEffect(() => { - const container = containerRef.current; - if (!container) return; + React.useEffect(() => { + const container = containerRef.current; + if (!container) return; - // Fetch the SVG file from the public path. The SVG is located at /organigrama_FES102025.svg (if placed in public/) - // There's a copy in src/public; to be served statically, ensure the SVG is available at '/organigrama_FES102025.svg'. - let mounted = true; - fetch('/organigrama_FES102025.svg') - .then((r) => r.text()) - .then((text) => { - if (!mounted) return; - container.innerHTML = text; + // Fetch the SVG file from the public path. The SVG is located at /organigrama_FES102025.svg (if placed in public/) + // There's a copy in src/public; to be served statically, ensure the SVG is available at '/organigrama_FES102025.svg'. + let mounted = true; + fetch('/organigrama_FES102025.svg') + .then((r) => r.text()) + .then((text) => { + if (!mounted) return; + container.innerHTML = text; - const svg = container.querySelector('svg'); - if (!svg) return; + const svg = container.querySelector('svg'); + if (!svg) return; - const onClick = (e: MouseEvent) => { - // if parent reports it's panning/dragging, ignore clicks - if (isPanningRef && isPanningRef.current) return; - const target = e.target as Element; - const clientX = (e as MouseEvent).clientX; - const clientY = (e as MouseEvent).clientY; + const onClick = (e: MouseEvent) => { + // if parent reports it's panning/dragging, ignore clicks + if (isPanningRef && isPanningRef.current) return; + const target = e.target as Element; + const clientX = (e as MouseEvent).clientX; + const clientY = (e as MouseEvent).clientY; - // Try to find a text element under the click - const texts = Array.from(svg.querySelectorAll('text')) as Element[]; + // Try to find a text element under the click + const texts = Array.from(svg.querySelectorAll('text')) as Element[]; - // First check which text elements have bounding rect containing the click - const hit = texts.find((t) => { - const r = (t as Element).getBoundingClientRect(); - return clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom; - }); - - let textContent = ''; - if (hit) { - textContent = hit.textContent || ''; - } else { - // If no direct hit, find nearest text within 120px - let nearest: { el: Element | null; dist: number } = { el: null, dist: Infinity }; - texts.forEach((t) => { - const r = (t as Element).getBoundingClientRect(); - const cx = (r.left + r.right) / 2; - const cy = (r.top + r.bottom) / 2; - const d = Math.hypot(clientX - cx, clientY - cy); - if (d < nearest.dist) { - nearest = { el: t, dist: d }; - } + // First check which text elements have bounding rect containing the click + const hit = texts.find((t) => { + const r = (t as Element).getBoundingClientRect(); + return clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom; }); - if (nearest.el && nearest.dist < 140) { - textContent = nearest.el.textContent || ''; + + let textContent = ''; + if (hit) { + textContent = hit.textContent || ''; + } else { + // If no direct hit, find nearest text within 120px + let nearest: { el: Element | null; dist: number } = { el: null, dist: Infinity }; + texts.forEach((t) => { + const r = (t as Element).getBoundingClientRect(); + const cx = (r.left + r.right) / 2; + const cy = (r.top + r.bottom) / 2; + const d = Math.hypot(clientX - cx, clientY - cy); + if (d < nearest.dist) { + nearest = { el: t, dist: d }; + } + }); + if (nearest.el && nearest.dist < 140) { + textContent = nearest.el.textContent || ''; + } } - } - if (!textContent) return; + if (!textContent) return; - const normalized = normalize(textContent); - // Try to match any normalized key in areaMap - for (const entry of normalizedAreaEntries) { - if (normalized.includes(entry.key) || entry.key.includes(normalized)) { - onAreaSelect(entry.id); - return; - } - } - - // If target element directly has a title or id - if (target) { - const el = target as HTMLElement; - const title = (el.getAttribute('title') || el.getAttribute('id') || '').toString(); - const n = normalize(title); + const normalized = normalize(textContent); + // Try to match any normalized key in areaMap for (const entry of normalizedAreaEntries) { - if (n.includes(entry.key) || entry.key.includes(n)) { + if (normalized.includes(entry.key) || entry.key.includes(normalized)) { onAreaSelect(entry.id); return; - } + } } - } + + // If target element directly has a title or id + if (target) { + const el = target as HTMLElement; + const title = (el.getAttribute('title') || el.getAttribute('id') || '').toString(); + const n = normalize(title); + for (const entry of normalizedAreaEntries) { + if (n.includes(entry.key) || entry.key.includes(n)) { + onAreaSelect(entry.id); + return; + } + } + } + }; + + svg.addEventListener('click', onClick); + + // allow pointer events + svg.style.pointerEvents = 'auto'; + + return () => svg.removeEventListener('click', onClick); + }) + .catch((err) => console.error('No se pudo cargar SVG:', err)); + + return () => { + mounted = false; }; + }, [onAreaSelect]); - svg.addEventListener('click', onClick); - - // allow pointer events - svg.style.pointerEvents = 'auto'; - - return () => svg.removeEventListener('click', onClick); - }) - .catch((err) => console.error('No se pudo cargar SVG:', err)); - - return () => { - mounted = false; - }; - }, [onAreaSelect]); - - return ( -
- ); + return ( +
+ ); }