cambios del dia 02/08

This commit is contained in:
2026-09-03 08:18:55 +02:00
parent cd3da67a72
commit 792992a67d
9 changed files with 425 additions and 102 deletions

View File

@@ -54,6 +54,7 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
try { await JS.InvokeVoidAsync("rt.initMobileMapControls"); } catch (Exception ex) { Console.WriteLine("rt.initMobileMapControls: " + ex); }
try { await JS.InvokeVoidAsync("rt.initDraggablePopups"); } catch (Exception ex) { Console.WriteLine("rt.initDraggablePopups: " + ex); }
await CargarConfiguracionLocalAsync();
await CargarPreferenciaParametrosActualesHistorialAsync();
await AsegurarHistorialLocalCargadoAsync();
await CargarHistorialCompartidoAsync();
await CargarModelosHorariosAsync();
@@ -215,8 +216,6 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
return;
}
QuitarSalidasParadaActivasPorClaves(claves);
foreach (var clave in claves)
{
if (!_itPreviewIdsPorClave.TryGetValue(clave, out var ids) || ids.Count == 0)
@@ -255,7 +254,6 @@ public partial class PlanificadorRutas : ComponentBase, IDisposable
_itPreviewActivos.Clear();
_itPreviewIdsPorClave.Clear();
_salidasParadaItinerariosActivas.Clear();
LimpiarParadasItinerarioVisibles();
_previewLineasActivas.Clear(); // <- esto ya lo usas para los "full_..."

View File

@@ -55,8 +55,10 @@ public partial class PlanificadorRutas : ComponentBase
private const double UmbralPendienteSubidaRutaPorDefecto = 2.0;
private const double UmbralPendienteSubidaMediaRutaPorDefecto = 5.0;
private const double UmbralPendienteSubidaFuerteRutaPorDefecto = 10.0;
private const string ColorPendienteSubidaSuaveRutaPorDefecto = "#ec4899";
private const string ColorPendienteSubidaMediaRutaPorDefecto = "#2563eb";
private const string ColorPendienteBajadaRutaPorDefecto = "#9ebdff";
private const string ColorPendienteLlanoRutaPorDefecto = "#166534";
private const string ColorPendienteSubidaSuaveRutaPorDefecto = "#dc2626";
private const string ColorPendienteSubidaMediaRutaPorDefecto = "#7c3aed";
private const string ColorPendienteSubidaFuerteRutaPorDefecto = "#000000";
private const double UmbralPendienteBajadaRutaPorDefecto = 5.0;
private const double UmbralAlturaAlertaPendienteRutaMetrosPorDefecto = 6.0;
@@ -375,6 +377,8 @@ public partial class PlanificadorRutas : ComponentBase
private double _umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
private double _umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto;
private double _umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto;
private string _colorPendienteBajadaRuta = ColorPendienteBajadaRutaPorDefecto;
private string _colorPendienteLlanoRuta = ColorPendienteLlanoRutaPorDefecto;
private string _colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto;
private string _colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto;
private string _colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto;

View File

@@ -12,6 +12,8 @@ public partial class PlanificadorRutas
{
private const string ClaveHistorialLocal = "rutasdbus.planificador.routeHistory.v1";
private const string ClaveRespaldoHistorialLocal = "rutasdbus.planificador.routeHistory.recovery.v1";
private const string ClavePreferenciaParametrosActualesHistorial =
"rutasdbus.planificador.history.useCurrentConfig.v1";
private const int MaxItemsHistorialLocal = 25;
private const int MaxBytesHistorialLocal = 24 * 1024;
private const int IntervaloRefrescoHistorialCompartidoSegundos = 3;
@@ -875,6 +877,7 @@ public partial class PlanificadorRutas
_configuracionParametrosActualesHistorial = CrearConfiguracionLocal();
_usarParametrosActualesHistorial = usarParametrosActuales;
await PersistirPreferenciaParametrosActualesHistorialAsync();
if (usarParametrosActuales && _configuracionParametrosActualesHistorial is not null)
AplicarConfiguracionLocal(_configuracionParametrosActualesHistorial);
@@ -903,6 +906,35 @@ public partial class PlanificadorRutas
}
}
private async Task CargarPreferenciaParametrosActualesHistorialAsync()
{
try
{
var valor = await JS.InvokeAsync<string?>(
"rt.loadLocalValue",
ClavePreferenciaParametrosActualesHistorial);
if (bool.TryParse(valor, out var usarConfiguracionActual))
_usarParametrosActualesHistorial = usarConfiguracionActual;
}
catch (JSException)
{
}
}
private async Task PersistirPreferenciaParametrosActualesHistorialAsync()
{
try
{
await JS.InvokeVoidAsync(
"rt.saveLocalValue",
ClavePreferenciaParametrosActualesHistorial,
_usarParametrosActualesHistorial.ToString(CultureInfo.InvariantCulture));
}
catch (JSException)
{
}
}
private bool PrepararDerivacionHistorialParaRecalculo()
{
var procedeDeHistorial = _tipoReferenciaHistorial != TipoReferenciaHistorial.Ninguna &&
@@ -1488,7 +1520,6 @@ public partial class PlanificadorRutas
var configuracionPrevia = _configuracionTemporalPreviaHistorial;
_configuracionTemporalPreviaHistorial = null;
_configuracionParametrosActualesHistorial = null;
_usarParametrosActualesHistorial = true;
if (configuracionPrevia is null)
return;

View File

@@ -296,8 +296,84 @@ public partial class PlanificadorRutas : ComponentBase
/// <summary>
/// Alterna desde el popup de parada el itinerario asociado a una salida.
/// </summary>
private readonly Dictionary<string, string> _salidasParadaItinerariosActivas =
new(StringComparer.OrdinalIgnoreCase);
private bool _procesandoTodosItinerariosParada;
private bool _ocultandoTodosItinerariosParada;
private bool TodosItinerariosParadaPintados
{
get
{
var itinerarios = ObtenerItinerariosUnicosParada();
return itinerarios.Count > 0 && itinerarios.All(EstaItinerarioPintado);
}
}
private string TextoBotonTodosItinerariosParada => _procesandoTodosItinerariosParada
? (_ocultandoTodosItinerariosParada ? "Ocultando..." : "Pintando...")
: (TodosItinerariosParadaPintados ? "Ocultar todos" : "Ver todos");
private List<ElementoItinerario> ObtenerItinerariosUnicosParada()
{
var clavesUnicas = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var itinerariosUnicos = new List<ElementoItinerario>();
foreach (var salida in NextDepsOrdenados.ToList())
{
var itinerario = ObtenerItinerarioSalidaParada(salida);
if (itinerario is null || itinerario.Variantes.Count == 0)
continue;
var clave = ItClave(itinerario.Variantes[0]);
if (clavesUnicas.Add(clave))
itinerariosUnicos.Add(itinerario);
}
return itinerariosUnicos;
}
private async Task ToggleTodosItinerariosParadaAsync()
{
if (_procesandoTodosItinerariosParada)
return;
var itinerariosUnicos = ObtenerItinerariosUnicosParada();
if (itinerariosUnicos.Count == 0)
return;
var ocultarTodos = itinerariosUnicos.All(EstaItinerarioPintado);
_procesandoTodosItinerariosParada = true;
_ocultandoTodosItinerariosParada = ocultarTodos;
StateHasChanged();
try
{
if (ocultarTodos)
{
foreach (var itinerario in itinerariosUnicos)
await QuitarItItemAsync(itinerario);
return;
}
var coordenadas = new List<double[]>();
foreach (var itinerario in itinerariosUnicos)
{
if (!EstaItinerarioPintado(itinerario))
await PintarItItemAsync(itinerario);
coordenadas.AddRange(ConstruirCoordenadasItinerario(itinerario));
}
if (coordenadas.Count > 0)
await FitRoutesJsAsync(coordenadas);
}
finally
{
_procesandoTodosItinerariosParada = false;
_ocultandoTodosItinerariosParada = false;
StateHasChanged();
}
}
private async Task ToggleItinerarioSalidaParadaAsync(ProximaSalidaVm salida)
{
@@ -305,28 +381,18 @@ public partial class PlanificadorRutas : ComponentBase
if (itinerario is null)
return;
var salidaId = CrearIdSalidaParada(salida);
var claveItinerario = ItClave(itinerario.Variantes[0]);
var esSalidaActiva = _salidasParadaItinerariosActivas.TryGetValue(salidaId, out var claveActiva) &&
string.Equals(claveActiva, claveItinerario, StringComparison.OrdinalIgnoreCase);
var estaPintado = EstaItinerarioPintado(itinerario);
if (esSalidaActiva && estaPintado)
if (estaPintado)
{
await QuitarItItemAsync(itinerario);
}
else
{
if (!estaPintado)
{
await PintarItItemAsync(itinerario);
await FitRoutesJsAsync(ConstruirCoordenadasItinerario(itinerario));
}
QuitarSalidasParadaActivasPorClaves([claveItinerario]);
_salidasParadaItinerariosActivas[salidaId] = claveItinerario;
}
if (_itItemsFiltrados.Any(x => x.Id.Equals(itinerario.Id, StringComparison.OrdinalIgnoreCase)))
{
_itSeleccionadoId = itinerario.Id;
@@ -339,31 +405,45 @@ public partial class PlanificadorRutas : ComponentBase
private bool EstaItinerarioSalidaParadaPintado(ProximaSalidaVm salida)
{
var itinerario = ObtenerItinerarioSalidaParada(salida);
return itinerario is not null &&
_salidasParadaItinerariosActivas.TryGetValue(CrearIdSalidaParada(salida), out var claveActiva) &&
itinerario.Variantes.Any(variante =>
string.Equals(claveActiva, ItClave(variante), StringComparison.OrdinalIgnoreCase)) &&
EstaItinerarioPintado(itinerario);
return itinerario is not null && EstaItinerarioPintado(itinerario);
}
private void QuitarSalidasParadaActivasPorClaves(IEnumerable<string> claves)
private string ObtenerTextoVisibleSalidaParada(ProximaSalidaVm salida)
{
var clavesAEliminar = new HashSet<string>(claves, StringComparer.OrdinalIgnoreCase);
foreach (var salidaId in _salidasParadaItinerariosActivas
.Where(x => clavesAEliminar.Contains(x.Value))
.Select(x => x.Key)
.ToList())
var texto = salida.LineaTxt?.Trim() ?? string.Empty;
var separadorDestino = texto.IndexOf(" - ", StringComparison.Ordinal);
var codigo = separadorDestino > 0 ? texto[..separadorDestino].Trim() : texto;
var itinerario = ObtenerItinerarioSalidaParada(salida);
var nombreLinea = itinerario?.Variantes.FirstOrDefault()?.Linea.Nombre?.Trim();
if (string.IsNullOrWhiteSpace(nombreLinea) &&
separadorDestino >= 0 &&
separadorDestino + 3 < texto.Length)
{
_salidasParadaItinerariosActivas.Remove(salidaId);
}
nombreLinea = texto[(separadorDestino + 3)..].Trim();
}
private static string CrearIdSalidaParada(ProximaSalidaVm salida)
=> string.Join('|',
salida.ExpId,
salida.RutaId,
salida.Hora,
salida.ItinerarioClave);
return string.IsNullOrWhiteSpace(nombreLinea)
? codigo
: $"{codigo} ({nombreLinea})";
}
private string ObtenerNombreDestinoSalidaParada(ProximaSalidaVm salida)
{
var itinerario = ObtenerItinerarioSalidaParada(salida);
if (itinerario?.Variantes.Count > 0)
{
var destino = ObtenerNombreDestinoItinerario(itinerario.Variantes[0]);
if (!string.IsNullOrWhiteSpace(destino))
return destino;
}
var texto = salida.LineaTxt?.Trim() ?? string.Empty;
var separadorDestino = texto.IndexOf(" - ", StringComparison.Ordinal);
return separadorDestino >= 0 && separadorDestino + 3 < texto.Length
? texto[(separadorDestino + 3)..].Trim()
: string.Empty;
}
private bool EstaItinerarioPintado(ElementoItinerario itinerario)
=> itinerario.Variantes.Any(variante =>
@@ -1309,7 +1389,7 @@ public partial class PlanificadorRutas : ComponentBase
private bool _elevPopupMinimizado = false;
private const string _versionApp = "(v-20260901a)";
private const string _versionApp = "(v-20260902d)";
private double? _paradaElevIdee;
@@ -1438,28 +1518,35 @@ public partial class PlanificadorRutas : ComponentBase
}
/// <summary>
/// Obtiene texto itinerario clicado.
/// Obtiene el codigo y el nombre de la linea del itinerario clicado.
/// </summary>
private string GetTextoItinerarioClicado()
private string GetTextoLineaItinerarioClicado(InfoVariante? vReal)
{
if (_itinerarioClicado is null)
return "";
var vReal = GetVarianteRealClicada();
if (vReal is null)
return _itinerarioClicado.Text;
var linea = vReal.Linea?.Codigo?.ToString() ?? "?";
var itin = vReal.CodigoItinerario;
var sentido = NormalizarSentidoCodigo(vReal.Sentido?.Codigo); // I / V
var nombre = vReal.Linea?.Nombre ?? "";
var trayecto = ConstruirTextoRecorridoVariante(vReal);
var nombre = vReal.Linea?.Nombre?.Trim() ?? "";
var codigo = $"{linea}.{itin}-{sentido}";
if (!string.IsNullOrWhiteSpace(trayecto))
return $"{linea}.{itin}-{sentido} — {trayecto}";
return string.IsNullOrWhiteSpace(nombre)
? codigo
: $"{codigo} ({nombre})";
}
// ✅ Siempre devuelve el texto del sentido pinchado, no "I+V"
return $"{linea}.{itin}-{sentido} — {nombre}";
/// <summary>
/// Obtiene la parada final del itinerario clicado.
/// </summary>
private string GetDestinoItinerarioClicado(InfoVariante? vReal)
{
return vReal is null
? ""
: ObtenerNombreDestinoItinerario(vReal);
}
/// <summary>

View File

@@ -11,7 +11,7 @@ public partial class PlanificadorRutas
private const int VersionPersistenciaFactorPenalizacionPie = 1;
private const int VersionPersistenciaFactorEquilibrado = 2;
private const int VersionPersistenciaPendientes = 2;
private const int VersionPersistenciaColoresPendientes = 1;
private const int VersionPersistenciaColoresPendientes = 2;
private const int VersionPersistenciaCoeficienteEsfuerzo = 1;
private const int VersionPersistenciaFuenteElevacion = 1;
private bool _configuracionLocalCargada = false;
@@ -55,6 +55,8 @@ public partial class PlanificadorRutas
public double? UmbralPendienteSubidaRuta { get; set; }
public double? UmbralPendienteSubidaMediaRuta { get; set; }
public double? UmbralPendienteSubidaFuerteRuta { get; set; }
public string? ColorPendienteBajadaRuta { get; set; }
public string? ColorPendienteLlanoRuta { get; set; }
public string? ColorPendienteSubidaSuaveRuta { get; set; }
public string? ColorPendienteSubidaMediaRuta { get; set; }
public string? ColorPendienteSubidaFuerteRuta { get; set; }
@@ -180,6 +182,8 @@ public partial class PlanificadorRutas
UmbralPendienteSubidaRuta = _umbralPendienteSubidaRuta,
UmbralPendienteSubidaMediaRuta = _umbralPendienteSubidaMediaRuta,
UmbralPendienteSubidaFuerteRuta = _umbralPendienteSubidaFuerteRuta,
ColorPendienteBajadaRuta = _colorPendienteBajadaRuta,
ColorPendienteLlanoRuta = _colorPendienteLlanoRuta,
ColorPendienteSubidaSuaveRuta = _colorPendienteSubidaSuaveRuta,
ColorPendienteSubidaMediaRuta = _colorPendienteSubidaMediaRuta,
ColorPendienteSubidaFuerteRuta = _colorPendienteSubidaFuerteRuta,
@@ -295,6 +299,18 @@ public partial class PlanificadorRutas
: UmbralPendienteSubidaFuerteRutaPorDefecto;
var migrarColoresPendientes =
(config.VersionColoresPendientes ?? 0) < VersionPersistenciaColoresPendientes;
_colorPendienteBajadaRuta = migrarColoresPendientes
? ColorPendienteBajadaRutaPorDefecto
: NormalizarColorHexRuta(
config.ColorPendienteBajadaRuta,
ColorPendienteBajadaRutaPorDefecto);
if (string.Equals(_colorPendienteBajadaRuta, "#2563eb", StringComparison.OrdinalIgnoreCase))
_colorPendienteBajadaRuta = ColorPendienteBajadaRutaPorDefecto;
_colorPendienteLlanoRuta = migrarColoresPendientes
? ColorPendienteLlanoRutaPorDefecto
: NormalizarColorHexRuta(
config.ColorPendienteLlanoRuta,
ColorPendienteLlanoRutaPorDefecto);
_colorPendienteSubidaSuaveRuta = migrarColoresPendientes
? ColorPendienteSubidaSuaveRutaPorDefecto
: NormalizarColorHexRuta(
@@ -442,6 +458,8 @@ public partial class PlanificadorRutas
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
_umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto;
_umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto;
_colorPendienteBajadaRuta = ColorPendienteBajadaRutaPorDefecto;
_colorPendienteLlanoRuta = ColorPendienteLlanoRutaPorDefecto;
_colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto;
_colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto;
_colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto;
@@ -457,6 +475,7 @@ public partial class PlanificadorRutas
_nivelBrilloMapa = 0;
_usarColorRealLineaEnItinerarios = false;
_usarOsrmParaRanking = false;
_usarParametrosActualesHistorial = true;
if (_usarHoraSimulada)
StopClock();
@@ -466,6 +485,7 @@ public partial class PlanificadorRutas
SincronizarSegundosPorParadaConHorarios();
await RefrescarHoraUIAsync();
await PersistirConfiguracionAsync();
await PersistirPreferenciaParametrosActualesHistorialAsync();
StateHasChanged();
}
@@ -500,6 +520,8 @@ public partial class PlanificadorRutas
_umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto;
_umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto;
_umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto;
_colorPendienteBajadaRuta = ColorPendienteBajadaRutaPorDefecto;
_colorPendienteLlanoRuta = ColorPendienteLlanoRutaPorDefecto;
_colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto;
_colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto;
_colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto;
@@ -515,6 +537,7 @@ public partial class PlanificadorRutas
_nivelBrilloMapa = 0;
_usarColorRealLineaEnItinerarios = false;
_usarOsrmParaRanking = false;
_usarParametrosActualesHistorial = true;
if (_usarHoraSimulada)
StopClock();
@@ -524,6 +547,7 @@ public partial class PlanificadorRutas
SincronizarSegundosPorParadaConHorarios();
await RefrescarHoraUIAsync();
await PersistirConfiguracionAsync();
await PersistirPreferenciaParametrosActualesHistorialAsync();
StateHasChanged();
}
}

View File

@@ -134,14 +134,16 @@ public partial class PlanificadorRutas : ComponentBase
StateHasChanged();
}
private enum NivelColorSubidaRuta
private enum NivelColorPendienteRuta
{
Suave,
Media,
Fuerte
Bajada,
Llano,
Subida,
SubidaMedia,
SubidaFuerte
}
private NivelColorSubidaRuta? _selectorColorPendienteRuta;
private NivelColorPendienteRuta? _selectorColorPendienteRuta;
private void AbrirConfiguracionPendientesRuta()
{
@@ -155,29 +157,37 @@ public partial class PlanificadorRutas : ComponentBase
_mostrarConfiguracionPendientesRuta = false;
}
private void AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta nivel)
private void AlternarSelectorColorPendienteRuta(NivelColorPendienteRuta nivel)
=> _selectorColorPendienteRuta = _selectorColorPendienteRuta == nivel ? null : nivel;
private string ObtenerColorPendienteSubidaRuta(NivelColorSubidaRuta nivel)
private string ObtenerColorPendienteRuta(NivelColorPendienteRuta nivel)
=> nivel switch
{
NivelColorSubidaRuta.Media => _colorPendienteSubidaMediaRuta,
NivelColorSubidaRuta.Fuerte => _colorPendienteSubidaFuerteRuta,
NivelColorPendienteRuta.Bajada => _colorPendienteBajadaRuta,
NivelColorPendienteRuta.Llano => _colorPendienteLlanoRuta,
NivelColorPendienteRuta.SubidaMedia => _colorPendienteSubidaMediaRuta,
NivelColorPendienteRuta.SubidaFuerte => _colorPendienteSubidaFuerteRuta,
_ => _colorPendienteSubidaSuaveRuta
};
private async Task AplicarColorPendienteSubidaRutaAsync(
private async Task AplicarColorPendienteRutaAsync(
string color,
NivelColorSubidaRuta nivel)
NivelColorPendienteRuta nivel)
{
var nuevo = NormalizarColorHexRuta(color, ObtenerColorPendienteSubidaRuta(nivel));
var nuevo = NormalizarColorHexRuta(color, ObtenerColorPendienteRuta(nivel));
switch (nivel)
{
case NivelColorSubidaRuta.Media:
case NivelColorPendienteRuta.Bajada:
_colorPendienteBajadaRuta = nuevo;
break;
case NivelColorPendienteRuta.Llano:
_colorPendienteLlanoRuta = nuevo;
break;
case NivelColorPendienteRuta.SubidaMedia:
_colorPendienteSubidaMediaRuta = nuevo;
break;
case NivelColorSubidaRuta.Fuerte:
case NivelColorPendienteRuta.SubidaFuerte:
_colorPendienteSubidaFuerteRuta = nuevo;
break;
default:
@@ -588,9 +598,17 @@ public partial class PlanificadorRutas : ComponentBase
ColorPendienteSubidaSuaveRutaPorDefecto));
if (pendientePorcentaje.Value <= -umbralBajada)
return ("bajada", "#16a34a");
return (
"bajada",
NormalizarColorHexRuta(
_colorPendienteBajadaRuta,
ColorPendienteBajadaRutaPorDefecto));
return ("llano", "#dc2626");
return (
"llano",
NormalizarColorHexRuta(
_colorPendienteLlanoRuta,
ColorPendienteLlanoRutaPorDefecto));
}

View File

@@ -287,19 +287,33 @@
}
else
{
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:.25rem;">
<div style="font-weight:600;">
var todosItinerariosParadaPintados = TodosItinerariosParadaPintados;
<div class="stop-schedule-header">
<div class="stop-schedule-header__title">
Horarios (@_nextDeps.Count)
</div>
<div class="stop-schedule-header__actions">
<button type="button"
class="btn-ghost"
class="stop-itinerary-show-all @(todosItinerariosParadaPintados ? "is-active" : "")"
title="@(todosItinerariosParadaPintados
? "Ocultar los itinerarios de esta parada"
: "Ver una vez cada itinerario de esta parada")"
aria-pressed="@todosItinerariosParadaPintados"
disabled="@_procesandoTodosItinerariosParada"
@onclick="ToggleTodosItinerariosParadaAsync">
<span aria-hidden="true">👁</span>
<span>@TextoBotonTodosItinerariosParada</span>
</button>
<button type="button"
class="btn-ghost stop-schedule-order-button"
title="@HorarioOrdenTooltip"
style="padding:.1rem .45rem; min-width:auto; line-height:1.1;"
@onclick="ToggleOrdenHorarios">
@HorarioOrdenIcono
</button>
</div>
</div>
@if (HorariosTaxibusSimuladosVisibles)
{
@@ -313,10 +327,18 @@
@foreach (var x in NextDepsOrdenados)
{
var itinerarioPintado = EstaItinerarioSalidaParadaPintado(x);
var textoItinerario = ObtenerTextoVisibleSalidaParada(x);
var destinoItinerario = ObtenerNombreDestinoSalidaParada(x);
<li id="@x.AnchorId"
class="stop-schedule-row @(x.EsProximo ? "stop-schedule-row--next" : "")">
<span class="stop-schedule-row__text">
<b>@x.Hora</b> - @x.LineaTxt
<b>@x.Hora</b> - @textoItinerario
@if (!string.IsNullOrWhiteSpace(destinoItinerario))
{
<span class="stop-schedule-row__destination">
<span aria-hidden="true">→</span> @destinoItinerario
</span>
}
</span>
<button type="button"
class="stop-itinerary-toggle @(itinerarioPintado ? "is-active" : "")"
@@ -345,18 +367,26 @@
@if (_itinerarioClicado is not null)
{
var vReal = GetVarianteRealClicada();
var textoLineaItinerario = GetTextoLineaItinerarioClicado(vReal);
var destinoItinerario = GetDestinoItinerarioClicado(vReal);
<div class="popup-parada popup-slot popup-itinerario draggable-popup" data-drag-key="popup-itinerario">
<div class="popup-parada-header draggable-popup-handle">
@GetTextoItinerarioClicado()
<div class="popup-parada-header popup-itinerario-header draggable-popup-handle">
<span>@textoLineaItinerario</span>
@if (!string.IsNullOrWhiteSpace(destinoItinerario))
{
<span class="popup-itinerario-header__destination">
<span aria-hidden="true">→</span> @destinoItinerario
</span>
}
</div>
<div class="popup-parada-body popup-itinerario-body">
@if (vReal is not null)
{
<div style="margin-top:.25rem;">
<div><b>Paradas:</b> @(vReal.CodigosParadas?.Count ?? 0)</div>
<div><b>Trayecto:</b> @ConstruirTextoRecorridoVariante(vReal)</div>
<div><b>Recorrido:</b> @FormatearDistanciaVariante(vReal)</div>
<div><b>Recorrido:</b> @ConstruirTextoRecorridoVariante(vReal)</div>
<div><b>Longitud:</b> @FormatearDistanciaVariante(vReal)</div>
</div>
}
</div>
@@ -1855,6 +1885,53 @@
</select>
</label>
<div class="pendientes-config-modal__field">
<span class="pendientes-config-modal__threshold-label">
<button type="button"
class="pendientes-config-modal__swatch"
style="background-color: @_colorPendienteBajadaRuta"
title="Cambiar color de bajada"
aria-label="Cambiar color de bajada"
disabled="@_pintandoElevacionesRuta"
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorPendienteRuta.Bajada)">
</button>
<span>Umbral Bajada (%)</span>
@if (_selectorColorPendienteRuta == NivelColorPendienteRuta.Bajada)
{
<SelectorColorSimple Color="@_colorPendienteBajadaRuta"
Disabled="@_pintandoElevacionesRuta"
ColorChanged="@(color => AplicarColorPendienteRutaAsync(color, NivelColorPendienteRuta.Bajada))" />
}
</span>
<input type="text"
class="config-input"
inputmode="decimal"
aria-label="Umbral Bajada (%)"
value="@_umbralPendienteBajadaRuta.ToString("0.#", CultureInfo.InvariantCulture)"
disabled="@_pintandoElevacionesRuta"
@onchange="@(e => CambiarUmbralElevacionRutaAsync(e, false))" />
</div>
<div class="pendientes-config-modal__field pendientes-config-modal__field--color-only">
<span class="pendientes-config-modal__threshold-label">
<button type="button"
class="pendientes-config-modal__swatch"
style="background-color: @_colorPendienteLlanoRuta"
title="Cambiar color de andando normal"
aria-label="Cambiar color de andando normal"
disabled="@_pintandoElevacionesRuta"
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorPendienteRuta.Llano)">
</button>
<span>Andando normal</span>
@if (_selectorColorPendienteRuta == NivelColorPendienteRuta.Llano)
{
<SelectorColorSimple Color="@_colorPendienteLlanoRuta"
Disabled="@_pintandoElevacionesRuta"
ColorChanged="@(color => AplicarColorPendienteRutaAsync(color, NivelColorPendienteRuta.Llano))" />
}
</span>
</div>
<div class="pendientes-config-modal__field">
<span class="pendientes-config-modal__threshold-label">
<button type="button"
@@ -1863,14 +1940,14 @@
title="Cambiar color de subida"
aria-label="Cambiar color de subida"
disabled="@_pintandoElevacionesRuta"
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta.Suave)">
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorPendienteRuta.Subida)">
</button>
<span>Umbral Subida (%)</span>
@if (_selectorColorPendienteRuta == NivelColorSubidaRuta.Suave)
@if (_selectorColorPendienteRuta == NivelColorPendienteRuta.Subida)
{
<SelectorColorSimple Color="@_colorPendienteSubidaSuaveRuta"
Disabled="@_pintandoElevacionesRuta"
ColorChanged="@(color => AplicarColorPendienteSubidaRutaAsync(color, NivelColorSubidaRuta.Suave))" />
ColorChanged="@(color => AplicarColorPendienteRutaAsync(color, NivelColorPendienteRuta.Subida))" />
}
</span>
<input type="text"
@@ -1890,14 +1967,14 @@
title="Cambiar color de subida media"
aria-label="Cambiar color de subida media"
disabled="@_pintandoElevacionesRuta"
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta.Media)">
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorPendienteRuta.SubidaMedia)">
</button>
<span>Subida Media (%)</span>
@if (_selectorColorPendienteRuta == NivelColorSubidaRuta.Media)
@if (_selectorColorPendienteRuta == NivelColorPendienteRuta.SubidaMedia)
{
<SelectorColorSimple Color="@_colorPendienteSubidaMediaRuta"
Disabled="@_pintandoElevacionesRuta"
ColorChanged="@(color => AplicarColorPendienteSubidaRutaAsync(color, NivelColorSubidaRuta.Media))" />
ColorChanged="@(color => AplicarColorPendienteRutaAsync(color, NivelColorPendienteRuta.SubidaMedia))" />
}
</span>
<input type="text"
@@ -1917,14 +1994,14 @@
title="Cambiar color de subida fuerte"
aria-label="Cambiar color de subida fuerte"
disabled="@_pintandoElevacionesRuta"
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorSubidaRuta.Fuerte)">
@onclick="() => AlternarSelectorColorPendienteRuta(NivelColorPendienteRuta.SubidaFuerte)">
</button>
<span>Subida Fuerte (%)</span>
@if (_selectorColorPendienteRuta == NivelColorSubidaRuta.Fuerte)
@if (_selectorColorPendienteRuta == NivelColorPendienteRuta.SubidaFuerte)
{
<SelectorColorSimple Color="@_colorPendienteSubidaFuerteRuta"
Disabled="@_pintandoElevacionesRuta"
ColorChanged="@(color => AplicarColorPendienteSubidaRutaAsync(color, NivelColorSubidaRuta.Fuerte))" />
ColorChanged="@(color => AplicarColorPendienteRutaAsync(color, NivelColorPendienteRuta.SubidaFuerte))" />
}
</span>
<input type="text"
@@ -1936,16 +2013,6 @@
@onchange="@(e => CambiarUmbralColorSubidaRutaAsync(e, true))" />
</div>
<label class="pendientes-config-modal__field">
<span>Umbral Bajada (%)</span>
<input type="text"
class="config-input"
inputmode="decimal"
value="@_umbralPendienteBajadaRuta.ToString("0.#", CultureInfo.InvariantCulture)"
disabled="@_pintandoElevacionesRuta"
@onchange="@(e => CambiarUmbralElevacionRutaAsync(e, false))" />
</label>
<label class="pendientes-config-modal__field">
<span>Umbral Alerta (m)</span>
<input type="text"

View File

@@ -29,13 +29,13 @@
@code {
private static readonly (string Nombre, string Hex)[] ColoresPredefinidos =
[
("Verde", "#16a34a"),
("Verde oscuro", "#166534"),
("Amarillo", "#eab308"),
("Naranja", "#f97316"),
("Rojo", "#dc2626"),
("Rosa", "#ec4899"),
("Violeta", "#7c3aed"),
("Azul", "#2563eb"),
("Azul", "#9ebdff"),
("Cian", "#0891b2"),
("Gris", "#64748b"),
("Negro", "#000000")

View File

@@ -783,6 +783,17 @@ html, body {
margin-bottom: .25rem;
}
.popup-itinerario-header {
padding-right: 1.8rem;
line-height: 1.3;
}
.popup-itinerario-header__destination {
margin-left: .3rem;
color: #bae6fd;
font-weight: 600;
}
.popup-parada-body {
font-size: .8rem;
}
@@ -791,6 +802,78 @@ html, body {
margin-bottom: .15rem;
}
.stop-schedule-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: .5rem;
margin-bottom: .3rem;
}
.popup-parada-body .stop-schedule-header {
margin-bottom: .3rem;
}
.stop-schedule-header__title {
min-width: 0;
font-weight: 600;
}
.stop-schedule-header__actions {
display: inline-flex;
align-items: center;
gap: .3rem;
flex: 0 0 auto;
}
.popup-parada-body .stop-schedule-header__title,
.popup-parada-body .stop-schedule-header__actions {
margin-bottom: 0;
}
.stop-itinerary-show-all {
display: inline-flex;
align-items: center;
justify-content: center;
gap: .28rem;
min-height: 26px;
padding: .18rem .48rem;
border: 1px solid rgba(56, 189, 248, .55);
border-radius: 5px;
color: #e0f2fe;
background: rgba(14, 116, 144, .24);
font-size: .72rem;
line-height: 1;
white-space: nowrap;
cursor: pointer;
}
.stop-itinerary-show-all:hover,
.stop-itinerary-show-all:focus-visible {
border-color: #7dd3fc;
color: #ffffff;
background: rgba(14, 116, 144, .45);
outline: none;
}
.stop-itinerary-show-all.is-active {
border-color: #7dd3fc;
color: #ffffff;
background: #0369a1;
}
.stop-itinerary-show-all:disabled {
cursor: default;
opacity: .58;
}
.stop-schedule-order-button {
min-width: 26px;
min-height: 26px;
padding: .1rem .38rem;
line-height: 1.1;
}
.stop-schedule-list {
max-height: 220px;
overflow-y: auto;
@@ -827,6 +910,12 @@ html, body {
line-height: 1.25;
}
.stop-schedule-row__destination {
margin-left: .3rem;
color: #bae6fd;
font-weight: 600;
}
.stop-itinerary-toggle {
display: grid;
place-items: center;
@@ -2343,6 +2432,11 @@ html, body {
text-align: center;
}
.pendientes-config-modal__field--color-only .pendientes-config-modal__threshold-label {
grid-column: 1 / -1;
min-height: 32px;
}
.pendientes-config-modal__threshold-label {
display: inline-flex;
align-items: center;