Feat Se añade Id de Agrupaciones en Componentes
This commit is contained in:
@@ -123,7 +123,7 @@ export const AgrupacionesManager = () => {
|
||||
<tbody>
|
||||
{agrupaciones.map(agrupacion => (
|
||||
<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="color" value={sanitizeColor(editedAgrupaciones[agrupacion.id]?.color)} onChange={(e) => handleInputChange(agrupacion.id, 'color', e.target.value)} /></td>
|
||||
<td>
|
||||
|
||||
@@ -89,7 +89,7 @@ export const BancasNacionalesManager = () => {
|
||||
onChange={(e) => handleAgrupacionChange(bancada.id, e.target.value || null)}
|
||||
>
|
||||
<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>
|
||||
</td>
|
||||
<td>{bancada.ocupante?.nombreOcupante || 'Sin asignar'}</td>
|
||||
|
||||
@@ -95,7 +95,7 @@ export const BancasPreviasManager = () => {
|
||||
<tbody>
|
||||
{agrupaciones.map(agrupacion => (
|
||||
<tr key={agrupacion.id}>
|
||||
<td>{agrupacion.nombre}</td>
|
||||
<td>({agrupacion.id}) {agrupacion.nombre}</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
|
||||
@@ -91,7 +91,7 @@ export const BancasProvincialesManager = () => {
|
||||
onChange={(e) => handleAgrupacionChange(bancada.id, e.target.value || null)}
|
||||
>
|
||||
<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>
|
||||
</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' }}>
|
||||
<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={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); }} />
|
||||
|
||||
{selectedAmbitoLevel.value === 'provincia' || selectedAmbitoLevel.value === 'municipio' ? (
|
||||
|
||||
@@ -56,15 +56,6 @@ export const ConfiguracionNacional = () => {
|
||||
return (
|
||||
<div className="admin-module">
|
||||
<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' }}>
|
||||
{/* Columna Diputados */}
|
||||
@@ -77,12 +68,12 @@ export const ConfiguracionNacional = () => {
|
||||
</p>
|
||||
<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>
|
||||
{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>
|
||||
{presidenciaDiputadosId && (
|
||||
<div>
|
||||
<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>
|
||||
@@ -97,7 +88,7 @@ export const ConfiguracionNacional = () => {
|
||||
</p>
|
||||
<select id="presidencia-senado-nacional" value={presidenciaSenadoId} onChange={e => setPresidenciaSenadoId(e.target.value)} style={{ width: '100%', padding: '8px' }}>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { MunicipioSimple, AgrupacionPolitica, LogoAgrupacionCategoria, Prov
|
||||
import { CATEGORIAS_NACIONALES_OPTIONS, CATEGORIAS_PROVINCIALES_OPTIONS } from '../constants/categorias';
|
||||
|
||||
const ELECCION_OPTIONS = [
|
||||
{ value: 0, label: 'General (Toda la elección)' },
|
||||
{ value: 0, label: 'General (Todas las elecciones)' },
|
||||
{ value: 2, label: 'Elecciones Nacionales' },
|
||||
{ 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' }}>
|
||||
<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={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); }} />
|
||||
|
||||
{selectedAmbitoLevel.value === 'provincia' || selectedAmbitoLevel.value === 'municipio' ? (
|
||||
|
||||
@@ -25,12 +25,12 @@ import './AgrupacionesManager.css'; // Reutilizamos los estilos
|
||||
const updateOrdenDiputadosApi = async (ids: string[]) => {
|
||||
const token = localStorage.getItem('admin-jwt-token');
|
||||
const response = await fetch('http://localhost:5217/api/admin/agrupaciones/orden-diputados', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(ids)
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(ids)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to save Diputados order");
|
||||
@@ -38,77 +38,77 @@ const updateOrdenDiputadosApi = async (ids: string[]) => {
|
||||
};
|
||||
|
||||
export const OrdenDiputadosManager = () => {
|
||||
const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
||||
);
|
||||
const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAndSortAgrupaciones = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await getAgrupaciones();
|
||||
// Ordenar por el orden de Diputados. Los nulos van al final.
|
||||
data.sort((a, b) => (a.ordenDiputados || 999) - (b.ordenDiputados || 999));
|
||||
setAgrupaciones(data);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch agrupaciones for Diputados:", error);
|
||||
} finally {
|
||||
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);
|
||||
});
|
||||
useEffect(() => {
|
||||
const fetchAndSortAgrupaciones = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await getAgrupaciones();
|
||||
// Ordenar por el orden de Diputados. Los nulos van al final.
|
||||
data.sort((a, b) => (a.ordenDiputados || 999) - (b.ordenDiputados || 999));
|
||||
setAgrupaciones(data);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch agrupaciones for Diputados:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchAndSortAgrupaciones();
|
||||
}, []);
|
||||
|
||||
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.');
|
||||
}
|
||||
};
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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 (
|
||||
<div className="admin-module">
|
||||
<h3>Ordenar Agrupaciones (Diputados)</h3>
|
||||
<p>Arrastre para reordenar.</p>
|
||||
<p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={agrupaciones.map(a => a.id)}
|
||||
strategy={horizontalListSortingStrategy}
|
||||
>
|
||||
<ul className="sortable-list-horizontal">
|
||||
{agrupaciones.map(agrupacion => (
|
||||
<SortableItem key={agrupacion.id} id={agrupacion.id}>
|
||||
{agrupacion.nombreCorto || agrupacion.nombre}
|
||||
</SortableItem>
|
||||
))}
|
||||
</ul>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
<button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden Diputados</button>
|
||||
</div>
|
||||
);
|
||||
if (loading) return <p>Cargando orden de Diputados...</p>;
|
||||
|
||||
return (
|
||||
<div className="admin-module">
|
||||
<h3>Ordenar Agrupaciones (Diputados)</h3>
|
||||
<p>Arrastre para reordenar.</p>
|
||||
<p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={agrupaciones.map(a => a.id)}
|
||||
strategy={horizontalListSortingStrategy}
|
||||
>
|
||||
<ul className="sortable-list-horizontal">
|
||||
{agrupaciones.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 Diputados</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,92 +11,92 @@ import './AgrupacionesManager.css';
|
||||
const ELECCION_ID_NACIONAL = 2;
|
||||
|
||||
export const OrdenDiputadosNacionalesManager = () => {
|
||||
// Estado para la lista que el usuario puede ordenar
|
||||
const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]);
|
||||
// Estado para la lista que el usuario puede ordenar
|
||||
const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]);
|
||||
|
||||
// Query 1: Obtener TODAS las agrupaciones para tener sus datos completos (nombre, etc.)
|
||||
const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({
|
||||
queryKey: ['agrupaciones'],
|
||||
queryFn: getAgrupaciones,
|
||||
});
|
||||
// Query 1: Obtener TODAS las agrupaciones para tener sus datos completos (nombre, etc.)
|
||||
const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({
|
||||
queryKey: ['agrupaciones'],
|
||||
queryFn: getAgrupaciones,
|
||||
});
|
||||
|
||||
// Query 2: Obtener los datos de composición para saber qué partidos tienen bancas
|
||||
const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({
|
||||
queryKey: ['composicionNacional', ELECCION_ID_NACIONAL],
|
||||
queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL),
|
||||
});
|
||||
// Query 2: Obtener los datos de composición para saber qué partidos tienen bancas
|
||||
const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({
|
||||
queryKey: ['composicionNacional', ELECCION_ID_NACIONAL],
|
||||
queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL),
|
||||
});
|
||||
|
||||
// Este efecto se ejecuta cuando los datos de las queries estén disponibles
|
||||
useEffect(() => {
|
||||
// No hacemos nada hasta que ambas queries hayan cargado sus datos
|
||||
if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Este efecto se ejecuta cuando los datos de las queries estén disponibles
|
||||
useEffect(() => {
|
||||
// No hacemos nada hasta que ambas queries hayan cargado sus datos
|
||||
if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Creamos un Set con los IDs de los partidos que tienen al menos una banca de diputado
|
||||
const partidosConBancasIds = new Set(
|
||||
composicionData.diputados.partidos
|
||||
.filter(p => p.bancasTotales > 0)
|
||||
.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 })
|
||||
// Creamos un Set con los IDs de los partidos que tienen al menos una banca de diputado
|
||||
const partidosConBancasIds = new Set(
|
||||
composicionData.diputados.partidos
|
||||
.filter(p => p.bancasTotales > 0)
|
||||
.map(p => p.id)
|
||||
);
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
// Filtramos la lista completa de agrupaciones, quedándonos solo con las relevantes
|
||||
const agrupacionesFiltradas = todasAgrupaciones.filter(a => partidosConBancasIds.has(a.id));
|
||||
|
||||
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.');
|
||||
}
|
||||
};
|
||||
// Ordenamos la lista filtrada según el orden guardado en la BD
|
||||
agrupacionesFiltradas.sort((a, b) => (a.ordenDiputadosNacionales || 999) - (b.ordenDiputadosNacionales || 999));
|
||||
|
||||
const isLoading = isLoadingAgrupaciones || isLoadingComposicion;
|
||||
if (isLoading) return <p>Cargando orden de Diputados Nacionales...</p>;
|
||||
// Actualizamos el estado que se renderiza y que el usuario puede ordenar
|
||||
setAgrupacionesOrdenadas(agrupacionesFiltradas);
|
||||
|
||||
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.nombreCorto || agrupacion.nombre}
|
||||
</SortableItem>
|
||||
))}
|
||||
</ul>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
<button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden</button>
|
||||
</div>
|
||||
);
|
||||
}, [todasAgrupaciones, composicionData]); // Dependencias: se re-ejecuta si los datos cambian
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
||||
);
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
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 () => {
|
||||
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 token = localStorage.getItem('admin-jwt-token');
|
||||
const response = await fetch('http://localhost:5217/api/admin/agrupaciones/orden-senadores', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(ids)
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(ids)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to save Senadores order");
|
||||
@@ -38,77 +38,77 @@ const updateOrdenSenadoresApi = async (ids: string[]) => {
|
||||
};
|
||||
|
||||
export const OrdenSenadoresManager = () => {
|
||||
const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
||||
);
|
||||
const [agrupaciones, setAgrupaciones] = useState<AgrupacionPolitica[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAndSortAgrupaciones = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await getAgrupaciones();
|
||||
// Ordenar por el orden de Senadores. Los nulos van al final.
|
||||
data.sort((a, b) => (a.ordenSenadores || 999) - (b.ordenSenadores || 999));
|
||||
setAgrupaciones(data);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch agrupaciones for Senadores:", error);
|
||||
} finally {
|
||||
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);
|
||||
});
|
||||
useEffect(() => {
|
||||
const fetchAndSortAgrupaciones = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await getAgrupaciones();
|
||||
// Ordenar por el orden de Senadores. Los nulos van al final.
|
||||
data.sort((a, b) => (a.ordenSenadores || 999) - (b.ordenSenadores || 999));
|
||||
setAgrupaciones(data);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch agrupaciones for Senadores:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchAndSortAgrupaciones();
|
||||
}, []);
|
||||
|
||||
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.');
|
||||
}
|
||||
};
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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 (
|
||||
<div className="admin-module">
|
||||
<h3>Ordenar Agrupaciones (Senado)</h3>
|
||||
<p>Arrastre para reordenar.</p>
|
||||
<p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={agrupaciones.map(a => a.id)}
|
||||
strategy={horizontalListSortingStrategy}
|
||||
>
|
||||
<ul className="sortable-list-horizontal">
|
||||
{agrupaciones.map(agrupacion => (
|
||||
<SortableItem key={agrupacion.id} id={agrupacion.id}>
|
||||
{agrupacion.nombreCorto || agrupacion.nombre}
|
||||
</SortableItem>
|
||||
))}
|
||||
</ul>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
<button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden Senado</button>
|
||||
</div>
|
||||
);
|
||||
if (loading) return <p>Cargando orden de Senadores...</p>;
|
||||
|
||||
return (
|
||||
<div className="admin-module">
|
||||
<h3>Ordenar Agrupaciones (Senado)</h3>
|
||||
<p>Arrastre para reordenar.</p>
|
||||
<p>Ancla izquierda. Prioridad de izquierda a derecha y de arriba abajo.</p>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={agrupaciones.map(a => a.id)}
|
||||
strategy={horizontalListSortingStrategy}
|
||||
>
|
||||
<ul className="sortable-list-horizontal">
|
||||
{agrupaciones.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 Senado</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -11,84 +11,84 @@ import './AgrupacionesManager.css';
|
||||
const ELECCION_ID_NACIONAL = 2;
|
||||
|
||||
export const OrdenSenadoresNacionalesManager = () => {
|
||||
const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]);
|
||||
const [agrupacionesOrdenadas, setAgrupacionesOrdenadas] = useState<AgrupacionPolitica[]>([]);
|
||||
|
||||
const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({
|
||||
queryKey: ['agrupaciones'],
|
||||
queryFn: getAgrupaciones,
|
||||
});
|
||||
const { data: todasAgrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery<AgrupacionPolitica[]>({
|
||||
queryKey: ['agrupaciones'],
|
||||
queryFn: getAgrupaciones,
|
||||
});
|
||||
|
||||
const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({
|
||||
queryKey: ['composicionNacional', ELECCION_ID_NACIONAL],
|
||||
queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL),
|
||||
});
|
||||
const { data: composicionData, isLoading: isLoadingComposicion } = useQuery({
|
||||
queryKey: ['composicionNacional', ELECCION_ID_NACIONAL],
|
||||
queryFn: () => getComposicionNacional(ELECCION_ID_NACIONAL),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) {
|
||||
return;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!composicionData || !todasAgrupaciones || todasAgrupaciones.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Creamos un Set con los IDs de los partidos que tienen al menos una banca de senador
|
||||
const partidosConBancasIds = new Set(
|
||||
composicionData.senadores.partidos
|
||||
.filter(p => p.bancasTotales > 0)
|
||||
.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 })
|
||||
// Creamos un Set con los IDs de los partidos que tienen al menos una banca de senador
|
||||
const partidosConBancasIds = new Set(
|
||||
composicionData.senadores.partidos
|
||||
.filter(p => p.bancasTotales > 0)
|
||||
.map(p => p.id)
|
||||
);
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
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 agrupacionesFiltradas = todasAgrupaciones.filter(a => partidosConBancasIds.has(a.id));
|
||||
|
||||
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.');
|
||||
}
|
||||
};
|
||||
agrupacionesFiltradas.sort((a, b) => (a.ordenSenadoresNacionales || 999) - (b.ordenSenadoresNacionales || 999));
|
||||
|
||||
const isLoading = isLoadingAgrupaciones || isLoadingComposicion;
|
||||
if (isLoading) return <p>Cargando orden de Senadores Nacionales...</p>;
|
||||
setAgrupacionesOrdenadas(agrupacionesFiltradas);
|
||||
|
||||
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.nombreCorto || agrupacion.nombre}
|
||||
</SortableItem>
|
||||
))}
|
||||
</ul>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
<button onClick={handleSaveOrder} style={{ marginTop: '1rem' }}>Guardar Orden</button>
|
||||
</div>
|
||||
);
|
||||
}, [todasAgrupaciones, composicionData]);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
||||
);
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
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 () => {
|
||||
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.AssemblyConfigurationAttribute("Debug")]
|
||||
[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.AssemblyTitleAttribute("Elecciones.Api")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
Reference in New Issue
Block a user