Feat Se añade Id de Agrupaciones en Componentes
This commit is contained in:
		| @@ -123,7 +123,7 @@ export const AgrupacionesManager = () => { | |||||||
|                             <tbody> |                             <tbody> | ||||||
|                                 {agrupaciones.map(agrupacion => ( |                                 {agrupaciones.map(agrupacion => ( | ||||||
|                                     <tr key={agrupacion.id}> |                                     <tr key={agrupacion.id}> | ||||||
|                                         <td>{agrupacion.nombre}</td> |                                         <td>({agrupacion.id}) {agrupacion.nombre}</td> | ||||||
|                                         <td><input type="text" value={editedAgrupaciones[agrupacion.id]?.nombreCorto ?? ''} onChange={(e) => handleInputChange(agrupacion.id, 'nombreCorto', e.target.value)} /></td> |                                         <td><input type="text" value={editedAgrupaciones[agrupacion.id]?.nombreCorto ?? ''} onChange={(e) => handleInputChange(agrupacion.id, 'nombreCorto', e.target.value)} /></td> | ||||||
|                                         <td><input type="color" value={sanitizeColor(editedAgrupaciones[agrupacion.id]?.color)} onChange={(e) => handleInputChange(agrupacion.id, 'color', e.target.value)} /></td> |                                         <td><input type="color" value={sanitizeColor(editedAgrupaciones[agrupacion.id]?.color)} onChange={(e) => handleInputChange(agrupacion.id, 'color', e.target.value)} /></td> | ||||||
|                                         <td> |                                         <td> | ||||||
|   | |||||||
| @@ -89,7 +89,7 @@ export const BancasNacionalesManager = () => { | |||||||
|                       onChange={(e) => handleAgrupacionChange(bancada.id, e.target.value || null)} |                       onChange={(e) => handleAgrupacionChange(bancada.id, e.target.value || null)} | ||||||
|                     > |                     > | ||||||
|                       <option value="">-- Vacante --</option> |                       <option value="">-- Vacante --</option> | ||||||
|                       {agrupaciones.map(a => <option key={a.id} value={a.id}>{a.nombre}</option>)} |                       {agrupaciones.map(a => <option key={a.id} value={a.id}>{`(${a.id}) ${a.nombre}`}</option>)} | ||||||
|                     </select> |                     </select> | ||||||
|                   </td> |                   </td> | ||||||
|                   <td>{bancada.ocupante?.nombreOcupante || 'Sin asignar'}</td> |                   <td>{bancada.ocupante?.nombreOcupante || 'Sin asignar'}</td> | ||||||
|   | |||||||
| @@ -95,7 +95,7 @@ export const BancasPreviasManager = () => { | |||||||
|                             <tbody> |                             <tbody> | ||||||
|                                 {agrupaciones.map(agrupacion => ( |                                 {agrupaciones.map(agrupacion => ( | ||||||
|                                     <tr key={agrupacion.id}> |                                     <tr key={agrupacion.id}> | ||||||
|                                         <td>{agrupacion.nombre}</td> |                                         <td>({agrupacion.id}) {agrupacion.nombre}</td> | ||||||
|                                         <td> |                                         <td> | ||||||
|                                             <input |                                             <input | ||||||
|                                                 type="number" |                                                 type="number" | ||||||
|   | |||||||
| @@ -91,7 +91,7 @@ export const BancasProvincialesManager = () => { | |||||||
|                     onChange={(e) => handleAgrupacionChange(bancada.id, e.target.value || null)} |                     onChange={(e) => handleAgrupacionChange(bancada.id, e.target.value || null)} | ||||||
|                   > |                   > | ||||||
|                     <option value="">-- Vacante --</option> |                     <option value="">-- Vacante --</option> | ||||||
|                     {agrupaciones.map(a => <option key={a.id} value={a.id}>{a.nombre}</option>)} |                     {agrupaciones.map(a => <option key={a.id} value={a.id}>{`(${a.id}) ${a.nombre}`}</option>)} | ||||||
|                   </select> |                   </select> | ||||||
|                 </td> |                 </td> | ||||||
|                 <td>{bancada.ocupante?.nombreOcupante || 'Sin asignar'}</td> |                 <td>{bancada.ocupante?.nombreOcupante || 'Sin asignar'}</td> | ||||||
|   | |||||||
| @@ -83,7 +83,14 @@ export const CandidatoOverridesManager = () => { | |||||||
|             <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem', alignItems: 'flex-end' }}> |             <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem', alignItems: 'flex-end' }}> | ||||||
|                 <Select options={ELECCION_OPTIONS} value={selectedEleccion} onChange={(opt) => { setSelectedEleccion(opt!); setSelectedCategoria(null); }} /> |                 <Select options={ELECCION_OPTIONS} value={selectedEleccion} onChange={(opt) => { setSelectedEleccion(opt!); setSelectedCategoria(null); }} /> | ||||||
|                 <Select options={categoriaOptions} value={selectedCategoria} onChange={setSelectedCategoria} placeholder="Seleccione Categoría..." isDisabled={!selectedEleccion} /> |                 <Select options={categoriaOptions} value={selectedCategoria} onChange={setSelectedCategoria} placeholder="Seleccione Categoría..." isDisabled={!selectedEleccion} /> | ||||||
|                 <Select options={agrupaciones.map(a => ({ value: a.id, label: a.nombre, ...a }))} getOptionValue={opt => opt.id} getOptionLabel={opt => opt.nombre} value={selectedAgrupacion} onChange={setSelectedAgrupacion} placeholder="Seleccione Agrupación..." /> |                 <Select | ||||||
|  |                     options={agrupaciones.map(a => ({ value: a.id, label: a.nombre, ...a }))} | ||||||
|  |                     getOptionValue={opt => opt.id} | ||||||
|  |                     getOptionLabel={opt => `(${opt.id}) ${opt.nombre}`} | ||||||
|  |                     value={selectedAgrupacion} | ||||||
|  |                     onChange={setSelectedAgrupacion} | ||||||
|  |                     placeholder="Seleccione Agrupación..." | ||||||
|  |                 /> | ||||||
|                 <Select options={AMBITO_LEVEL_OPTIONS} value={selectedAmbitoLevel} onChange={(opt) => { setSelectedAmbitoLevel(opt!); setSelectedProvincia(null); setSelectedMunicipio(null); }} /> |                 <Select options={AMBITO_LEVEL_OPTIONS} value={selectedAmbitoLevel} onChange={(opt) => { setSelectedAmbitoLevel(opt!); setSelectedProvincia(null); setSelectedMunicipio(null); }} /> | ||||||
|  |  | ||||||
|                 {selectedAmbitoLevel.value === 'provincia' || selectedAmbitoLevel.value === 'municipio' ? ( |                 {selectedAmbitoLevel.value === 'provincia' || selectedAmbitoLevel.value === 'municipio' ? ( | ||||||
|   | |||||||
| @@ -56,15 +56,6 @@ export const ConfiguracionNacional = () => { | |||||||
|     return ( |     return ( | ||||||
|         <div className="admin-module"> |         <div className="admin-module"> | ||||||
|             <h3>Configuración de Widgets Nacionales</h3> |             <h3>Configuración de Widgets Nacionales</h3> | ||||||
|             {/*<div className="form-group"> |  | ||||||
|                 <label> |  | ||||||
|                     <input type="checkbox" checked={modoOficialActivo} onChange={e => setModoOficialActivo(e.target.checked)} /> |  | ||||||
|                     **Activar Modo "Resultados Oficiales" para Widgets Nacionales** |  | ||||||
|                 </label> |  | ||||||
|                 <p style={{ fontSize: '0.8rem', color: '#666' }}> |  | ||||||
|                     Si está activo, los widgets nacionales usarán la composición manual de bancas. Si no, usarán la proyección en tiempo real. |  | ||||||
|                 </p> |  | ||||||
|             </div>*/} |  | ||||||
|  |  | ||||||
|             <div style={{ display: 'flex', gap: '2rem', marginTop: '1rem' }}> |             <div style={{ display: 'flex', gap: '2rem', marginTop: '1rem' }}> | ||||||
|                 {/* Columna Diputados */} |                 {/* Columna Diputados */} | ||||||
| @@ -77,12 +68,12 @@ export const ConfiguracionNacional = () => { | |||||||
|                     </p> |                     </p> | ||||||
|                     <select id="presidencia-diputados-nacional" value={presidenciaDiputadosId} onChange={e => setPresidenciaDiputadosId(e.target.value)} style={{ width: '100%', padding: '8px', marginBottom: '0.5rem' }}> |                     <select id="presidencia-diputados-nacional" value={presidenciaDiputadosId} onChange={e => setPresidenciaDiputadosId(e.target.value)} style={{ width: '100%', padding: '8px', marginBottom: '0.5rem' }}> | ||||||
|                         <option value="">-- No Asignado --</option> |                         <option value="">-- No Asignado --</option> | ||||||
|                         {agrupaciones.map(a => (<option key={a.id} value={a.id}>{a.nombre}</option>))} |                         {agrupaciones.map(a => (<option key={a.id} value={a.id}>{`(${a.id}) ${a.nombre}`}</option>))} | ||||||
|                     </select> |                     </select> | ||||||
|                     {presidenciaDiputadosId && ( |                     {presidenciaDiputadosId && ( | ||||||
|                         <div> |                         <div> | ||||||
|                             <label><input type="radio" value="ganada" checked={diputadosTipoBanca === 'ganada'} onChange={() => setDiputadosTipoBanca('ganada')} /> Descontar de Banca Ganada</label> |                             <label><input type="radio" value="ganada" checked={diputadosTipoBanca === 'ganada'} onChange={() => setDiputadosTipoBanca('ganada')} /> Descontar de Banca Ganada</label> | ||||||
|                             <label style={{marginLeft: '1rem'}}><input type="radio" value="previa" checked={diputadosTipoBanca === 'previa'} onChange={() => setDiputadosTipoBanca('previa')} /> Descontar de Banca Previa</label> |                             <label style={{ marginLeft: '1rem' }}><input type="radio" value="previa" checked={diputadosTipoBanca === 'previa'} onChange={() => setDiputadosTipoBanca('previa')} /> Descontar de Banca Previa</label> | ||||||
|                         </div> |                         </div> | ||||||
|                     )} |                     )} | ||||||
|                 </div> |                 </div> | ||||||
| @@ -97,7 +88,7 @@ export const ConfiguracionNacional = () => { | |||||||
|                     </p> |                     </p> | ||||||
|                     <select id="presidencia-senado-nacional" value={presidenciaSenadoId} onChange={e => setPresidenciaSenadoId(e.target.value)} style={{ width: '100%', padding: '8px' }}> |                     <select id="presidencia-senado-nacional" value={presidenciaSenadoId} onChange={e => setPresidenciaSenadoId(e.target.value)} style={{ width: '100%', padding: '8px' }}> | ||||||
|                         <option value="">-- No Asignado --</option> |                         <option value="">-- No Asignado --</option> | ||||||
|                         {agrupaciones.map(a => (<option key={a.id} value={a.id}>{a.nombre}</option>))} |                         {agrupaciones.map(a => (<option key={a.id} value={a.id}>{`(${a.id}) ${a.nombre}`}</option>))} | ||||||
|                     </select> |                     </select> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import type { MunicipioSimple, AgrupacionPolitica, LogoAgrupacionCategoria, Prov | |||||||
| import { CATEGORIAS_NACIONALES_OPTIONS, CATEGORIAS_PROVINCIALES_OPTIONS } from '../constants/categorias'; | import { CATEGORIAS_NACIONALES_OPTIONS, CATEGORIAS_PROVINCIALES_OPTIONS } from '../constants/categorias'; | ||||||
|  |  | ||||||
| const ELECCION_OPTIONS = [ | const ELECCION_OPTIONS = [ | ||||||
|     { value: 0, label: 'General (Toda la elección)' }, |     { value: 0, label: 'General (Todas las elecciones)' }, | ||||||
|     { value: 2, label: 'Elecciones Nacionales' }, |     { value: 2, label: 'Elecciones Nacionales' }, | ||||||
|     { value: 1, label: 'Elecciones Provinciales' } |     { value: 1, label: 'Elecciones Provinciales' } | ||||||
| ]; | ]; | ||||||
| @@ -84,7 +84,14 @@ export const LogoOverridesManager = () => { | |||||||
|             <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem', alignItems: 'flex-end' }}> |             <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem', alignItems: 'flex-end' }}> | ||||||
|                 <Select options={ELECCION_OPTIONS} value={selectedEleccion} onChange={(opt) => { setSelectedEleccion(opt!); setSelectedCategoria(null); }} /> |                 <Select options={ELECCION_OPTIONS} value={selectedEleccion} onChange={(opt) => { setSelectedEleccion(opt!); setSelectedCategoria(null); }} /> | ||||||
|                 <Select options={categoriaOptions} value={selectedCategoria} onChange={setSelectedCategoria} placeholder="Seleccione Categoría..." isDisabled={!selectedEleccion} /> |                 <Select options={categoriaOptions} value={selectedCategoria} onChange={setSelectedCategoria} placeholder="Seleccione Categoría..." isDisabled={!selectedEleccion} /> | ||||||
|                 <Select options={agrupaciones.map(a => ({ value: a.id, label: a.nombre, ...a }))} getOptionValue={opt => opt.id} getOptionLabel={opt => opt.nombre} value={selectedAgrupacion} onChange={setSelectedAgrupacion} placeholder="Seleccione Agrupación..." /> |                 <Select | ||||||
|  |                     options={agrupaciones.map(a => ({ value: a.id, label: a.nombre, ...a }))} | ||||||
|  |                     getOptionValue={opt => opt.id} | ||||||
|  |                     getOptionLabel={opt => `(${opt.id}) ${opt.nombre}`} | ||||||
|  |                     value={selectedAgrupacion} | ||||||
|  |                     onChange={setSelectedAgrupacion} | ||||||
|  |                     placeholder="Seleccione Agrupación..." | ||||||
|  |                 /> | ||||||
|                 <Select options={AMBITO_LEVEL_OPTIONS} value={selectedAmbitoLevel} onChange={(opt) => { setSelectedAmbitoLevel(opt!); setSelectedProvincia(null); setSelectedMunicipio(null); }} /> |                 <Select options={AMBITO_LEVEL_OPTIONS} value={selectedAmbitoLevel} onChange={(opt) => { setSelectedAmbitoLevel(opt!); setSelectedProvincia(null); setSelectedMunicipio(null); }} /> | ||||||
|  |  | ||||||
|                 {selectedAmbitoLevel.value === 'provincia' || selectedAmbitoLevel.value === 'municipio' ? ( |                 {selectedAmbitoLevel.value === 'provincia' || selectedAmbitoLevel.value === 'municipio' ? ( | ||||||
|   | |||||||
| @@ -25,12 +25,12 @@ import './AgrupacionesManager.css'; // Reutilizamos los estilos | |||||||
| const updateOrdenDiputadosApi = async (ids: string[]) => { | const updateOrdenDiputadosApi = async (ids: string[]) => { | ||||||
|   const token = localStorage.getItem('admin-jwt-token'); |   const token = localStorage.getItem('admin-jwt-token'); | ||||||
|   const response = await fetch('http://localhost:5217/api/admin/agrupaciones/orden-diputados', { |   const response = await fetch('http://localhost:5217/api/admin/agrupaciones/orden-diputados', { | ||||||
|       method: 'PUT', |     method: 'PUT', | ||||||
|       headers: { |     headers: { | ||||||
|           'Content-Type': 'application/json', |       'Content-Type': 'application/json', | ||||||
|           'Authorization': `Bearer ${token}` |       'Authorization': `Bearer ${token}` | ||||||
|       }, |     }, | ||||||
|       body: JSON.stringify(ids) |     body: JSON.stringify(ids) | ||||||
|   }); |   }); | ||||||
|   if (!response.ok) { |   if (!response.ok) { | ||||||
|     throw new Error("Failed to save Diputados order"); |     throw new Error("Failed to save Diputados order"); | ||||||
| @@ -38,77 +38,77 @@ const updateOrdenDiputadosApi = async (ids: string[]) => { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| export const OrdenDiputadosManager = () => { | export const OrdenDiputadosManager = () => { | ||||||
|     const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]); |   const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]); | ||||||
|     const [loading, setLoading] = useState(true); |   const [loading, setLoading] = useState(true); | ||||||
|     const sensors = useSensors( |   const sensors = useSensors( | ||||||
|       useSensor(PointerSensor), |     useSensor(PointerSensor), | ||||||
|       useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) |     useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) | ||||||
|     ); |   ); | ||||||
|  |  | ||||||
|     useEffect(() => { |   useEffect(() => { | ||||||
|         const fetchAndSortAgrupaciones = async () => { |     const fetchAndSortAgrupaciones = async () => { | ||||||
|             setLoading(true); |       setLoading(true); | ||||||
|             try { |       try { | ||||||
|                 const data = await getAgrupaciones(); |         const data = await getAgrupaciones(); | ||||||
|                 // Ordenar por el orden de Diputados. Los nulos van al final. |         // Ordenar por el orden de Diputados. Los nulos van al final. | ||||||
|                 data.sort((a, b) => (a.ordenDiputados || 999) - (b.ordenDiputados || 999)); |         data.sort((a, b) => (a.ordenDiputados || 999) - (b.ordenDiputados || 999)); | ||||||
|                 setAgrupaciones(data); |         setAgrupaciones(data); | ||||||
|             } catch (error) { |       } catch (error) { | ||||||
|                 console.error("Failed to fetch agrupaciones for Diputados:", error); |         console.error("Failed to fetch agrupaciones for Diputados:", error); | ||||||
|             } finally { |       } finally { | ||||||
|                 setLoading(false); |         setLoading(false); | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|         fetchAndSortAgrupaciones(); |  | ||||||
|     }, []); |  | ||||||
|  |  | ||||||
|     const handleDragEnd = (event: DragEndEvent) => { |  | ||||||
|       const { active, over } = event; |  | ||||||
|       if (over && active.id !== over.id) { |  | ||||||
|         setAgrupaciones((items) => { |  | ||||||
|           const oldIndex = items.findIndex((item) => item.id === active.id); |  | ||||||
|           const newIndex = items.findIndex((item) => item.id === over.id); |  | ||||||
|           return arrayMove(items, oldIndex, newIndex); |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
|  |     fetchAndSortAgrupaciones(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|     const handleSaveOrder = async () => { |   const handleDragEnd = (event: DragEndEvent) => { | ||||||
|         const idsOrdenados = agrupaciones.map(a => a.id); |     const { active, over } = event; | ||||||
|         try { |     if (over && active.id !== over.id) { | ||||||
|             await updateOrdenDiputadosApi(idsOrdenados); |       setAgrupaciones((items) => { | ||||||
|             alert('Orden de Diputados guardado con éxito!'); |         const oldIndex = items.findIndex((item) => item.id === active.id); | ||||||
|         } catch (error) { |         const newIndex = items.findIndex((item) => item.id === over.id); | ||||||
|             alert('Error al guardar el orden de Diputados.'); |         return arrayMove(items, oldIndex, newIndex); | ||||||
|         } |       }); | ||||||
|     }; |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|     if (loading) return <p>Cargando orden de Diputados...</p>; |   const handleSaveOrder = async () => { | ||||||
|  |     const idsOrdenados = agrupaciones.map(a => a.id); | ||||||
|  |     try { | ||||||
|  |       await updateOrdenDiputadosApi(idsOrdenados); | ||||||
|  |       alert('Orden de Diputados guardado con éxito!'); | ||||||
|  |     } catch (error) { | ||||||
|  |       alert('Error al guardar el orden de Diputados.'); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|     return ( |   if (loading) return <p>Cargando orden de Diputados...</p>; | ||||||
|         <div className="admin-module"> |  | ||||||
|             <h3>Ordenar Agrupaciones (Diputados)</h3> |   return ( | ||||||
|             <p>Arrastre para reordenar.</p> |     <div className="admin-module"> | ||||||
|             <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> |       <h3>Ordenar Agrupaciones (Diputados)</h3> | ||||||
|             <DndContext |       <p>Arrastre para reordenar.</p> | ||||||
|               sensors={sensors} |       <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> | ||||||
|               collisionDetection={closestCenter} |       <DndContext | ||||||
|               onDragEnd={handleDragEnd} |         sensors={sensors} | ||||||
|             > |         collisionDetection={closestCenter} | ||||||
|               <SortableContext |         onDragEnd={handleDragEnd} | ||||||
|                 items={agrupaciones.map(a => a.id)} |       > | ||||||
|                 strategy={horizontalListSortingStrategy} |         <SortableContext | ||||||
|               > |           items={agrupaciones.map(a => a.id)} | ||||||
|                 <ul className="sortable-list-horizontal"> |           strategy={horizontalListSortingStrategy} | ||||||
|                   {agrupaciones.map(agrupacion => ( |         > | ||||||
|                     <SortableItem key={agrupacion.id} id={agrupacion.id}> |           <ul className="sortable-list-horizontal"> | ||||||
|                       {agrupacion.nombreCorto || agrupacion.nombre} |             {agrupaciones.map(agrupacion => ( | ||||||
|                     </SortableItem> |               <SortableItem key={agrupacion.id} id={agrupacion.id}> | ||||||
|                   ))} |                 {`(${agrupacion.id}) ${agrupacion.nombreCorto || agrupacion.nombre}`} | ||||||
|                 </ul> |               </SortableItem> | ||||||
|               </SortableContext> |             ))} | ||||||
|             </DndContext> |           </ul> | ||||||
|             <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden Diputados</button> |         </SortableContext> | ||||||
|         </div> |       </DndContext> | ||||||
|     ); |       <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden Diputados</button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
| @@ -11,92 +11,92 @@ import './AgrupacionesManager.css'; | |||||||
| const ELECCION_ID_NACIONAL = 2; | const ELECCION_ID_NACIONAL = 2; | ||||||
|  |  | ||||||
| export const OrdenDiputadosNacionalesManager = () => { | export const OrdenDiputadosNacionalesManager = () => { | ||||||
|     // Estado para la lista que el usuario puede ordenar |   // Estado para la lista que el usuario puede ordenar | ||||||
|     const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]); |   const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]); | ||||||
|  |  | ||||||
|     // Query 1: Obtener TODAS las agrupaciones para tener sus datos completos (nombre, etc.) |   // Query 1: Obtener TODAS las agrupaciones para tener sus datos completos (nombre, etc.) | ||||||
|     const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({ |   const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({ | ||||||
|         queryKey: ['agrupaciones'], |     queryKey: ['agrupaciones'], | ||||||
|         queryFn: getAgrupaciones, |     queryFn: getAgrupaciones, | ||||||
|     }); |   }); | ||||||
|  |  | ||||||
|     // Query 2: Obtener los datos de composición para saber qué partidos tienen bancas |   // Query 2: Obtener los datos de composición para saber qué partidos tienen bancas | ||||||
|     const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({ |   const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({ | ||||||
|         queryKey: ['composicionNacional', ELECCION_ID_NACIONAL], |     queryKey: ['composicionNacional', ELECCION_ID_NACIONAL], | ||||||
|         queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL), |     queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL), | ||||||
|     }); |   }); | ||||||
|  |  | ||||||
|     // Este efecto se ejecuta cuando los datos de las queries estén disponibles |   // Este efecto se ejecuta cuando los datos de las queries estén disponibles | ||||||
|     useEffect(() => { |   useEffect(() => { | ||||||
|         // No hacemos nada hasta que ambas queries hayan cargado sus datos |     // No hacemos nada hasta que ambas queries hayan cargado sus datos | ||||||
|         if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) { |     if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) { | ||||||
|             return; |       return; | ||||||
|         } |     } | ||||||
|  |  | ||||||
|         // Creamos un Set con los IDs de los partidos que tienen al menos una banca de diputado |     // Creamos un Set con los IDs de los partidos que tienen al menos una banca de diputado | ||||||
|         const partidosConBancasIds = new Set( |     const partidosConBancasIds = new Set( | ||||||
|             composicionData.diputados.partidos |       composicionData.diputados.partidos | ||||||
|                 .filter(p => p.bancasTotales > 0) |         .filter(p => p.bancasTotales > 0) | ||||||
|                 .map(p => p.id) |         .map(p => p.id) | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         // Filtramos la lista completa de agrupaciones, quedándonos solo con las relevantes |  | ||||||
|         const agrupacionesFiltradas = todasAgrupaciones.filter(a => partidosConBancasIds.has(a.id)); |  | ||||||
|  |  | ||||||
|         // Ordenamos la lista filtrada según el orden guardado en la BD |  | ||||||
|         agrupacionesFiltradas.sort((a, b) => (a.ordenDiputadosNacionales || 999) - (b.ordenDiputadosNacionales || 999)); |  | ||||||
|          |  | ||||||
|         // Actualizamos el estado que se renderiza y que el usuario puede ordenar |  | ||||||
|         setAgrupacionesOrdenadas(agrupacionesFiltradas); |  | ||||||
|  |  | ||||||
|     }, [todasAgrupaciones, composicionData]); // Dependencias: se re-ejecuta si los datos cambian |  | ||||||
|  |  | ||||||
|     const sensors = useSensors( |  | ||||||
|       useSensor(PointerSensor), |  | ||||||
|       useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) |  | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     const handleDragEnd = (event: DragEndEvent) => { |     // Filtramos la lista completa de agrupaciones, quedándonos solo con las relevantes | ||||||
|       const { active, over } = event; |     const agrupacionesFiltradas = todasAgrupaciones.filter(a => partidosConBancasIds.has(a.id)); | ||||||
|       if (over && active.id !== over.id) { |  | ||||||
|         setAgrupacionesOrdenadas((items) => { |  | ||||||
|           const oldIndex = items.findIndex((item) => item.id === active.id); |  | ||||||
|           const newIndex = items.findIndex((item) => item.id === over.id); |  | ||||||
|           return arrayMove(items, oldIndex, newIndex); |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const handleSaveOrder = async () => { |     // Ordenamos la lista filtrada según el orden guardado en la BD | ||||||
|         const idsOrdenados = agrupacionesOrdenadas.map(a => a.id); |     agrupacionesFiltradas.sort((a, b) => (a.ordenDiputadosNacionales || 999) - (b.ordenDiputadosNacionales || 999)); | ||||||
|         try { |  | ||||||
|             await updateOrden('diputados-nacionales', idsOrdenados); |  | ||||||
|             alert('Orden de Diputados Nacionales guardado con éxito!'); |  | ||||||
|         } catch (error) { |  | ||||||
|             alert('Error al guardar el orden de Diputados Nacionales.'); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const isLoading = isLoadingAgrupaciones || isLoadingComposicion; |     // Actualizamos el estado que se renderiza y que el usuario puede ordenar | ||||||
|     if (isLoading) return <p>Cargando orden de Diputados Nacionales...</p>; |     setAgrupacionesOrdenadas(agrupacionesFiltradas); | ||||||
|  |  | ||||||
|     return ( |   }, [todasAgrupaciones, composicionData]); // Dependencias: se re-ejecuta si los datos cambian | ||||||
|         <div className="admin-module"> |  | ||||||
|             <h3>Ordenar Agrupaciones (Diputados Nacionales)</h3> |   const sensors = useSensors( | ||||||
|             <p>Arrastre para reordenar. Solo se muestran los partidos con bancas.</p> |     useSensor(PointerSensor), | ||||||
|             <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> |     useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) | ||||||
|             <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}> |   ); | ||||||
|               <SortableContext items={agrupacionesOrdenadas.map(a => a.id)} strategy={horizontalListSortingStrategy}> |  | ||||||
|                 <ul className="sortable-list-horizontal"> |   const handleDragEnd = (event: DragEndEvent) => { | ||||||
|                   {agrupacionesOrdenadas.map(agrupacion => ( |     const { active, over } = event; | ||||||
|                     <SortableItem key={agrupacion.id} id={agrupacion.id}> |     if (over && active.id !== over.id) { | ||||||
|                       {agrupacion.nombreCorto || agrupacion.nombre} |       setAgrupacionesOrdenadas((items) => { | ||||||
|                     </SortableItem> |         const oldIndex = items.findIndex((item) => item.id === active.id); | ||||||
|                   ))} |         const newIndex = items.findIndex((item) => item.id === over.id); | ||||||
|                 </ul> |         return arrayMove(items, oldIndex, newIndex); | ||||||
|               </SortableContext> |       }); | ||||||
|             </DndContext> |     } | ||||||
|             <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden</button> |   }; | ||||||
|         </div> |  | ||||||
|     ); |   const handleSaveOrder = async () => { | ||||||
|  |     const idsOrdenados = agrupacionesOrdenadas.map(a => a.id); | ||||||
|  |     try { | ||||||
|  |       await updateOrden('diputados-nacionales', idsOrdenados); | ||||||
|  |       alert('Orden de Diputados Nacionales guardado con éxito!'); | ||||||
|  |     } catch (error) { | ||||||
|  |       alert('Error al guardar el orden de Diputados Nacionales.'); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const isLoading = isLoadingAgrupaciones || isLoadingComposicion; | ||||||
|  |   if (isLoading) return <p>Cargando orden de Diputados Nacionales...</p>; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="admin-module"> | ||||||
|  |       <h3>Ordenar Agrupaciones (Diputados Nacionales)</h3> | ||||||
|  |       <p>Arrastre para reordenar. Solo se muestran los partidos con bancas.</p> | ||||||
|  |       <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> | ||||||
|  |       <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}> | ||||||
|  |         <SortableContext items={agrupacionesOrdenadas.map(a => a.id)} strategy={horizontalListSortingStrategy}> | ||||||
|  |           <ul className="sortable-list-horizontal"> | ||||||
|  |             {agrupacionesOrdenadas.map(agrupacion => ( | ||||||
|  |               <SortableItem key={agrupacion.id} id={agrupacion.id}> | ||||||
|  |                 {`(${agrupacion.id}) ${agrupacion.nombreCorto || agrupacion.nombre}`} | ||||||
|  |               </SortableItem> | ||||||
|  |             ))} | ||||||
|  |           </ul> | ||||||
|  |         </SortableContext> | ||||||
|  |       </DndContext> | ||||||
|  |       <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden</button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
| @@ -25,12 +25,12 @@ import './AgrupacionesManager.css'; // Reutilizamos los estilos | |||||||
| const updateOrdenSenadoresApi = async (ids: string[]) => { | const updateOrdenSenadoresApi = async (ids: string[]) => { | ||||||
|   const token = localStorage.getItem('admin-jwt-token'); |   const token = localStorage.getItem('admin-jwt-token'); | ||||||
|   const response = await fetch('http://localhost:5217/api/admin/agrupaciones/orden-senadores', { |   const response = await fetch('http://localhost:5217/api/admin/agrupaciones/orden-senadores', { | ||||||
|       method: 'PUT', |     method: 'PUT', | ||||||
|       headers: { |     headers: { | ||||||
|           'Content-Type': 'application/json', |       'Content-Type': 'application/json', | ||||||
|           'Authorization': `Bearer ${token}` |       'Authorization': `Bearer ${token}` | ||||||
|       }, |     }, | ||||||
|       body: JSON.stringify(ids) |     body: JSON.stringify(ids) | ||||||
|   }); |   }); | ||||||
|   if (!response.ok) { |   if (!response.ok) { | ||||||
|     throw new Error("Failed to save Senadores order"); |     throw new Error("Failed to save Senadores order"); | ||||||
| @@ -38,77 +38,77 @@ const updateOrdenSenadoresApi = async (ids: string[]) => { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| export const OrdenSenadoresManager = () => { | export const OrdenSenadoresManager = () => { | ||||||
|     const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]); |   const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]); | ||||||
|     const [loading, setLoading] = useState(true); |   const [loading, setLoading] = useState(true); | ||||||
|     const sensors = useSensors( |   const sensors = useSensors( | ||||||
|       useSensor(PointerSensor), |     useSensor(PointerSensor), | ||||||
|       useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) |     useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) | ||||||
|     ); |   ); | ||||||
|  |  | ||||||
|     useEffect(() => { |   useEffect(() => { | ||||||
|         const fetchAndSortAgrupaciones = async () => { |     const fetchAndSortAgrupaciones = async () => { | ||||||
|             setLoading(true); |       setLoading(true); | ||||||
|             try { |       try { | ||||||
|                 const data = await getAgrupaciones(); |         const data = await getAgrupaciones(); | ||||||
|                 // Ordenar por el orden de Senadores. Los nulos van al final. |         // Ordenar por el orden de Senadores. Los nulos van al final. | ||||||
|                 data.sort((a, b) => (a.ordenSenadores || 999) - (b.ordenSenadores || 999)); |         data.sort((a, b) => (a.ordenSenadores || 999) - (b.ordenSenadores || 999)); | ||||||
|                 setAgrupaciones(data); |         setAgrupaciones(data); | ||||||
|             } catch (error) { |       } catch (error) { | ||||||
|                 console.error("Failed to fetch agrupaciones for Senadores:", error); |         console.error("Failed to fetch agrupaciones for Senadores:", error); | ||||||
|             } finally { |       } finally { | ||||||
|                 setLoading(false); |         setLoading(false); | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|         fetchAndSortAgrupaciones(); |  | ||||||
|     }, []); |  | ||||||
|  |  | ||||||
|     const handleDragEnd = (event: DragEndEvent) => { |  | ||||||
|       const { active, over } = event; |  | ||||||
|       if (over && active.id !== over.id) { |  | ||||||
|         setAgrupaciones((items) => { |  | ||||||
|           const oldIndex = items.findIndex((item) => item.id === active.id); |  | ||||||
|           const newIndex = items.findIndex((item) => item.id === over.id); |  | ||||||
|           return arrayMove(items, oldIndex, newIndex); |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
|  |     fetchAndSortAgrupaciones(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|     const handleSaveOrder = async () => { |   const handleDragEnd = (event: DragEndEvent) => { | ||||||
|         const idsOrdenados = agrupaciones.map(a => a.id); |     const { active, over } = event; | ||||||
|         try { |     if (over && active.id !== over.id) { | ||||||
|             await updateOrdenSenadoresApi(idsOrdenados); |       setAgrupaciones((items) => { | ||||||
|             alert('Orden de Senadores guardado con éxito!'); |         const oldIndex = items.findIndex((item) => item.id === active.id); | ||||||
|         } catch (error) { |         const newIndex = items.findIndex((item) => item.id === over.id); | ||||||
|             alert('Error al guardar el orden de Senadores.'); |         return arrayMove(items, oldIndex, newIndex); | ||||||
|         } |       }); | ||||||
|     }; |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|     if (loading) return <p>Cargando orden de Senadores...</p>; |   const handleSaveOrder = async () => { | ||||||
|  |     const idsOrdenados = agrupaciones.map(a => a.id); | ||||||
|  |     try { | ||||||
|  |       await updateOrdenSenadoresApi(idsOrdenados); | ||||||
|  |       alert('Orden de Senadores guardado con éxito!'); | ||||||
|  |     } catch (error) { | ||||||
|  |       alert('Error al guardar el orden de Senadores.'); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|     return ( |   if (loading) return <p>Cargando orden de Senadores...</p>; | ||||||
|         <div className="admin-module"> |  | ||||||
|             <h3>Ordenar Agrupaciones (Senado)</h3> |   return ( | ||||||
|             <p>Arrastre para reordenar.</p> |     <div className="admin-module"> | ||||||
|             <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> |       <h3>Ordenar Agrupaciones (Senado)</h3> | ||||||
|             <DndContext |       <p>Arrastre para reordenar.</p> | ||||||
|               sensors={sensors} |       <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> | ||||||
|               collisionDetection={closestCenter} |       <DndContext | ||||||
|               onDragEnd={handleDragEnd} |         sensors={sensors} | ||||||
|             > |         collisionDetection={closestCenter} | ||||||
|               <SortableContext |         onDragEnd={handleDragEnd} | ||||||
|                 items={agrupaciones.map(a => a.id)} |       > | ||||||
|                 strategy={horizontalListSortingStrategy} |         <SortableContext | ||||||
|               > |           items={agrupaciones.map(a => a.id)} | ||||||
|                 <ul className="sortable-list-horizontal"> |           strategy={horizontalListSortingStrategy} | ||||||
|                   {agrupaciones.map(agrupacion => ( |         > | ||||||
|                     <SortableItem key={agrupacion.id} id={agrupacion.id}> |           <ul className="sortable-list-horizontal"> | ||||||
|                       {agrupacion.nombreCorto || agrupacion.nombre} |             {agrupaciones.map(agrupacion => ( | ||||||
|                     </SortableItem> |               <SortableItem key={agrupacion.id} id={agrupacion.id}> | ||||||
|                   ))} |                 {`(${agrupacion.id}) ${agrupacion.nombreCorto || agrupacion.nombre}`} | ||||||
|                 </ul> |               </SortableItem> | ||||||
|               </SortableContext> |             ))} | ||||||
|             </DndContext> |           </ul> | ||||||
|             <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden Senado</button> |         </SortableContext> | ||||||
|         </div> |       </DndContext> | ||||||
|     ); |       <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden Senado</button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
| @@ -11,84 +11,84 @@ import './AgrupacionesManager.css'; | |||||||
| const ELECCION_ID_NACIONAL = 2; | const ELECCION_ID_NACIONAL = 2; | ||||||
|  |  | ||||||
| export const OrdenSenadoresNacionalesManager = () => { | export const OrdenSenadoresNacionalesManager = () => { | ||||||
|     const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]); |   const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]); | ||||||
|  |  | ||||||
|     const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({ |   const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({ | ||||||
|         queryKey: ['agrupaciones'], |     queryKey: ['agrupaciones'], | ||||||
|         queryFn: getAgrupaciones, |     queryFn: getAgrupaciones, | ||||||
|     }); |   }); | ||||||
|  |  | ||||||
|     const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({ |   const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({ | ||||||
|         queryKey: ['composicionNacional', ELECCION_ID_NACIONAL], |     queryKey: ['composicionNacional', ELECCION_ID_NACIONAL], | ||||||
|         queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL), |     queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL), | ||||||
|     }); |   }); | ||||||
|  |  | ||||||
|     useEffect(() => { |   useEffect(() => { | ||||||
|         if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) { |     if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) { | ||||||
|             return; |       return; | ||||||
|         } |     } | ||||||
|  |  | ||||||
|         // Creamos un Set con los IDs de los partidos que tienen al menos una banca de senador |     // Creamos un Set con los IDs de los partidos que tienen al menos una banca de senador | ||||||
|         const partidosConBancasIds = new Set( |     const partidosConBancasIds = new Set( | ||||||
|             composicionData.senadores.partidos |       composicionData.senadores.partidos | ||||||
|                 .filter(p => p.bancasTotales > 0) |         .filter(p => p.bancasTotales > 0) | ||||||
|                 .map(p => p.id) |         .map(p => p.id) | ||||||
|         ); |  | ||||||
|  |  | ||||||
|         const agrupacionesFiltradas = todasAgrupaciones.filter(a => partidosConBancasIds.has(a.id)); |  | ||||||
|  |  | ||||||
|         agrupacionesFiltradas.sort((a, b) => (a.ordenSenadoresNacionales || 999) - (b.ordenSenadoresNacionales || 999)); |  | ||||||
|          |  | ||||||
|         setAgrupacionesOrdenadas(agrupacionesFiltradas); |  | ||||||
|  |  | ||||||
|     }, [todasAgrupaciones, composicionData]); |  | ||||||
|  |  | ||||||
|     const sensors = useSensors( |  | ||||||
|       useSensor(PointerSensor), |  | ||||||
|       useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) |  | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     const handleDragEnd = (event: DragEndEvent) => { |     const agrupacionesFiltradas = todasAgrupaciones.filter(a => partidosConBancasIds.has(a.id)); | ||||||
|       const { active, over } = event; |  | ||||||
|       if (over && active.id !== over.id) { |  | ||||||
|         setAgrupacionesOrdenadas((items) => { |  | ||||||
|           const oldIndex = items.findIndex((item) => item.id === active.id); |  | ||||||
|           const newIndex = items.findIndex((item) => item.id === over.id); |  | ||||||
|           return arrayMove(items, oldIndex, newIndex); |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const handleSaveOrder = async () => { |     agrupacionesFiltradas.sort((a, b) => (a.ordenSenadoresNacionales || 999) - (b.ordenSenadoresNacionales || 999)); | ||||||
|         const idsOrdenados = agrupacionesOrdenadas.map(a => a.id); |  | ||||||
|         try { |  | ||||||
|             await updateOrden('senadores-nacionales', idsOrdenados); |  | ||||||
|             alert('Orden de Senadores Nacionales guardado con éxito!'); |  | ||||||
|         } catch (error) { |  | ||||||
|             alert('Error al guardar el orden de Senadores Nacionales.'); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     const isLoading = isLoadingAgrupaciones || isLoadingComposicion; |     setAgrupacionesOrdenadas(agrupacionesFiltradas); | ||||||
|     if (isLoading) return <p>Cargando orden de Senadores Nacionales...</p>; |  | ||||||
|  |  | ||||||
|     return ( |   }, [todasAgrupaciones, composicionData]); | ||||||
|         <div className="admin-module"> |  | ||||||
|             <h3>Ordenar Agrupaciones (Senado de la Nación)</h3> |   const sensors = useSensors( | ||||||
|             <p>Arrastre para reordenar. Solo se muestran los partidos con bancas.</p> |     useSensor(PointerSensor), | ||||||
|             <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> |     useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) | ||||||
|             <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}> |   ); | ||||||
|               <SortableContext items={agrupacionesOrdenadas.map(a => a.id)} strategy={horizontalListSortingStrategy}> |  | ||||||
|                 <ul className="sortable-list-horizontal"> |   const handleDragEnd = (event: DragEndEvent) => { | ||||||
|                   {agrupacionesOrdenadas.map(agrupacion => ( |     const { active, over } = event; | ||||||
|                     <SortableItem key={agrupacion.id} id={agrupacion.id}> |     if (over && active.id !== over.id) { | ||||||
|                       {agrupacion.nombreCorto || agrupacion.nombre} |       setAgrupacionesOrdenadas((items) => { | ||||||
|                     </SortableItem> |         const oldIndex = items.findIndex((item) => item.id === active.id); | ||||||
|                   ))} |         const newIndex = items.findIndex((item) => item.id === over.id); | ||||||
|                 </ul> |         return arrayMove(items, oldIndex, newIndex); | ||||||
|               </SortableContext> |       }); | ||||||
|             </DndContext> |     } | ||||||
|             <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden</button> |   }; | ||||||
|         </div> |  | ||||||
|     ); |   const handleSaveOrder = async () => { | ||||||
|  |     const idsOrdenados = agrupacionesOrdenadas.map(a => a.id); | ||||||
|  |     try { | ||||||
|  |       await updateOrden('senadores-nacionales', idsOrdenados); | ||||||
|  |       alert('Orden de Senadores Nacionales guardado con éxito!'); | ||||||
|  |     } catch (error) { | ||||||
|  |       alert('Error al guardar el orden de Senadores Nacionales.'); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const isLoading = isLoadingAgrupaciones || isLoadingComposicion; | ||||||
|  |   if (isLoading) return <p>Cargando orden de Senadores Nacionales...</p>; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="admin-module"> | ||||||
|  |       <h3>Ordenar Agrupaciones (Senado de la Nación)</h3> | ||||||
|  |       <p>Arrastre para reordenar. Solo se muestran los partidos con bancas.</p> | ||||||
|  |       <p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p> | ||||||
|  |       <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}> | ||||||
|  |         <SortableContext items={agrupacionesOrdenadas.map(a => a.id)} strategy={horizontalListSortingStrategy}> | ||||||
|  |           <ul className="sortable-list-horizontal"> | ||||||
|  |             {agrupacionesOrdenadas.map(agrupacion => ( | ||||||
|  |               <SortableItem key={agrupacion.id} id={agrupacion.id}> | ||||||
|  |                 {`(${agrupacion.id}) ${agrupacion.nombreCorto || agrupacion.nombre}`} | ||||||
|  |               </SortableItem> | ||||||
|  |             ))} | ||||||
|  |           </ul> | ||||||
|  |         </SortableContext> | ||||||
|  |       </DndContext> | ||||||
|  |       <button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden</button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
| @@ -14,7 +14,7 @@ using System.Reflection; | |||||||
| [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")] | [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")] | ||||||
| [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | ||||||
| [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | ||||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+2b7fb927e2f0d9ff06dffa820bc9809d6e138b01")] | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+069446b90326f5acee630bf3b88238e4e054764f")] | ||||||
| [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] | [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] | ||||||
| [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] | [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] | ||||||
| [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user