diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs index e666dd5..f2c7431 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Estado.cs @@ -55,6 +55,9 @@ 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 = "#d946ef"; + private const string ColorPendienteSubidaMediaRutaPorDefecto = "#4c1d95"; + private const string ColorPendienteSubidaFuerteRutaPorDefecto = "#000000"; private const double UmbralPendienteBajadaRutaPorDefecto = 5.0; private const double UmbralAlturaAlertaPendienteRutaMetrosPorDefecto = 6.0; private const double MetrosPorPiso = 3.0; @@ -372,6 +375,9 @@ public partial class PlanificadorRutas : ComponentBase private double _umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto; private double _umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto; private double _umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto; + private string _colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto; + private string _colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto; + private string _colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto; private double _umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto; private double _umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto; private double _alturaPendientePositivaSignificativaRutaMetros = 0.0; diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Geocodificacion.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Geocodificacion.cs index 7c47b8e..e99e430 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Geocodificacion.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Geocodificacion.cs @@ -160,12 +160,22 @@ public partial class PlanificadorRutas .Concat(lugares.Select(CrearSugerenciaLugar)) .GroupBy(sugerencia => NormalizarClaveSugerencia(sugerencia), StringComparer.Ordinal) .Select(grupo => grupo.First()) - .OrderBy(sugerencia => sugerencia.Titulo, StringComparer.CurrentCultureIgnoreCase) + .OrderBy(sugerencia => OrdenTipoSugerencia(sugerencia.Tipo)) + .ThenBy(sugerencia => sugerencia.Titulo, StringComparer.CurrentCultureIgnoreCase) .ThenBy(sugerencia => sugerencia.Subtitulo, StringComparer.CurrentCultureIgnoreCase) .Take(Math.Clamp(maxResultados, 1, 50)) .ToList(); } + private static int OrdenTipoSugerencia(string? tipo) + => tipo switch + { + "Parada" => 0, + "Direccion" => 1, + "Lugar" => 2, + _ => 3 + }; + private IReadOnlyList BuscarSugerenciasParadas(string texto, int maxResultados) { var consulta = NormalizeForSearch(texto); diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs index 1b4ad4d..2050104 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Historial.cs @@ -2,6 +2,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using RutasDBUS.Modelos.Historial; @@ -40,8 +41,11 @@ public partial class PlanificadorRutas private bool _refrescandoHistorialCompartido = false; private bool _omitirRegistroHistorialEnSiguienteCalculo = false; private bool _aplicandoRegistroHistorial = false; + private bool _usarParametrosActualesHistorial = false; + private bool _cambiandoParametrosHistorial = false; private string? _historialCompartidoPendienteEliminarId; private ConfiguracionLocalPlanificador? _configuracionTemporalPreviaHistorial; + private ConfiguracionLocalPlanificador? _configuracionParametrosActualesHistorial; private Task? _tareaCargaHistorialLocal; private bool _historialLocalCargado = false; private bool _historialLocalCargando = false; @@ -837,6 +841,68 @@ public partial class PlanificadorRutas _historialCompartidoSeleccionadoId = string.Empty; } + private RegistroHistorialRuta? ObtenerRegistroHistorialSeleccionado() + { + if (!string.IsNullOrWhiteSpace(_historialLocalSeleccionadoId)) + return _historialLocal.FirstOrDefault(x => x.Id == _historialLocalSeleccionadoId); + + if (!string.IsNullOrWhiteSpace(_historialCompartidoSeleccionadoId)) + return _historialCompartido.FirstOrDefault(x => x.Id == _historialCompartidoSeleccionadoId); + + return null; + } + + private async Task CambiarUsoParametrosActualesHistorialAsync(ChangeEventArgs e) + { + if (_cambiandoParametrosHistorial) + return; + + var usarParametrosActuales = e.Value switch + { + bool valor => valor, + string texto when bool.TryParse(texto, out var valor) => valor, + _ => !_usarParametrosActualesHistorial + }; + if (usarParametrosActuales == _usarParametrosActualesHistorial) + return; + + _cambiandoParametrosHistorial = true; + try + { + var itemSeleccionado = ObtenerRegistroHistorialSeleccionado(); + + if (!usarParametrosActuales && _usarParametrosActualesHistorial) + _configuracionParametrosActualesHistorial = CrearConfiguracionLocal(); + + _usarParametrosActualesHistorial = usarParametrosActuales; + + if (usarParametrosActuales && _configuracionParametrosActualesHistorial is not null) + AplicarConfiguracionLocal(_configuracionParametrosActualesHistorial); + + if (itemSeleccionado is null) + return; + + await AplicarRegistroHistorialAsync(itemSeleccionado); + MostrarMensajeHistorial( + usarParametrosActuales + ? "Ruta recalculada con los parámetros actuales." + : "Ruta recalculada con los parámetros guardados."); + } + catch (Exception ex) + { + RegistrarErrorHistorialLocal( + "cambio del modo de parámetros del historial", + ex); + MostrarMensajeHistorial( + "No he podido recalcular la ruta con esos parámetros.", + esError: true); + } + finally + { + _cambiandoParametrosHistorial = false; + } + } + private bool PrepararDerivacionHistorialParaRecalculo() { var procedeDeHistorial = _tipoReferenciaHistorial != TipoReferenciaHistorial.Ninguna && @@ -1348,33 +1414,37 @@ public partial class PlanificadorRutas _coordenadasOrigen = new[] { item.OrigenLatitud, item.OrigenLongitud }; _coordenadasDestino = new[] { item.DestinoLatitud, item.DestinoLongitud }; - _usarHorariosTeoricos = item.UsarHorariosTeoricos; - _usarHoraSimulada = item.UsarHoraSimulada; - - if (DateOnly.TryParse(item.FechaSimulada, out var fecha)) - _fechaSimulada = fecha; - - if (TimeOnly.TryParse(item.HoraSimulada, out var hora)) - _horaSimulada = hora; - - _criterioPrioritarioRuta = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta); - SincronizarPreferenciaDesdeCriterioRuta(); - - if (item.FactorEquilibradoAndarBus is { } factorEquilibrado && - double.IsFinite(factorEquilibrado) && - factorEquilibrado > 0) + if (!_usarParametrosActualesHistorial) { - _factorEquilibradoAndarBus = factorEquilibrado; - SincronizarTextosPenalizacionPie(); + _usarHorariosTeoricos = item.UsarHorariosTeoricos; + _usarHoraSimulada = item.UsarHoraSimulada; - if (_configuracionTemporalPreviaHistorial is not null) - _configuracionTemporalPreviaHistorial.FactorEquilibradoAndarBus = _factorEquilibradoAndarBus; + if (DateOnly.TryParse(item.FechaSimulada, out var fecha)) + _fechaSimulada = fecha; + + if (TimeOnly.TryParse(item.HoraSimulada, out var hora)) + _horaSimulada = hora; + + _criterioPrioritarioRuta = NormalizarCriterioRuta(item.CriterioPrioritarioRuta, item.PreferenciaEtiquetaRuta); + SincronizarPreferenciaDesdeCriterioRuta(); + + if (item.FactorEquilibradoAndarBus is { } factorEquilibrado && + double.IsFinite(factorEquilibrado) && + factorEquilibrado > 0) + { + _factorEquilibradoAndarBus = factorEquilibrado; + SincronizarTextosPenalizacionPie(); + + if (_configuracionTemporalPreviaHistorial is not null) + _configuracionTemporalPreviaHistorial.FactorEquilibradoAndarBus = _factorEquilibradoAndarBus; + } + + _activarRangoLineas = item.ActivarFiltroLineas; + _lineasPermitidas.Clear(); + foreach (var linea in NormalizarLineasHistorial(item.LineasPermitidas)) + _lineasPermitidas.Add(linea); } - _activarRangoLineas = item.ActivarFiltroLineas; - _lineasPermitidas.Clear(); - foreach (var linea in NormalizarLineasHistorial(item.LineasPermitidas)) - _lineasPermitidas.Add(linea); _busFilteredCache.Clear(); SincronizarSegundosPorParadaConHorarios(); @@ -1410,15 +1480,19 @@ public partial class PlanificadorRutas return; _configuracionTemporalPreviaHistorial = CrearConfiguracionLocal(); + _configuracionParametrosActualesHistorial = CrearConfiguracionLocal(); } private async Task RestaurarConfiguracionTemporalHistorialAsync() { var configuracionPrevia = _configuracionTemporalPreviaHistorial; + _configuracionTemporalPreviaHistorial = null; + _configuracionParametrosActualesHistorial = null; + _usarParametrosActualesHistorial = false; + if (configuracionPrevia is null) return; - _configuracionTemporalPreviaHistorial = null; AplicarConfiguracionLocal(configuracionPrevia); StopClock(); diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs index 33483b6..296b966 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Itinerarios.cs @@ -1202,7 +1202,7 @@ public partial class PlanificadorRutas : ComponentBase private bool _elevPopupMinimizado = false; - private const string _versionApp = "(v-20260828a)"; + private const string _versionApp = "(v-20260831a)"; private double? _paradaElevIdee; diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs index 99988e1..4462bf5 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Persistencia.cs @@ -51,6 +51,9 @@ public partial class PlanificadorRutas public double? UmbralPendienteSubidaRuta { get; set; } public double? UmbralPendienteSubidaMediaRuta { get; set; } public double? UmbralPendienteSubidaFuerteRuta { get; set; } + public string? ColorPendienteSubidaSuaveRuta { get; set; } + public string? ColorPendienteSubidaMediaRuta { get; set; } + public string? ColorPendienteSubidaFuerteRuta { get; set; } public double? UmbralPendienteBajadaRuta { get; set; } public double? UmbralAlturaAlertaPendienteRutaMetros { get; set; } // Compatibilidad con configuraciones guardadas cuando el umbral se expresaba en pisos. @@ -171,6 +174,9 @@ public partial class PlanificadorRutas UmbralPendienteSubidaRuta = _umbralPendienteSubidaRuta, UmbralPendienteSubidaMediaRuta = _umbralPendienteSubidaMediaRuta, UmbralPendienteSubidaFuerteRuta = _umbralPendienteSubidaFuerteRuta, + ColorPendienteSubidaSuaveRuta = _colorPendienteSubidaSuaveRuta, + ColorPendienteSubidaMediaRuta = _colorPendienteSubidaMediaRuta, + ColorPendienteSubidaFuerteRuta = _colorPendienteSubidaFuerteRuta, UmbralPendienteBajadaRuta = _umbralPendienteBajadaRuta, UmbralAlturaAlertaPendienteRutaMetros = _umbralAlturaAlertaPendienteRutaMetros, VelocidadPieKmH = _velocidadPieKmH, @@ -276,6 +282,15 @@ public partial class PlanificadorRutas double.IsFinite(umbralSubidaFuerte) ? NormalizarUmbralPendienteRuta(umbralSubidaFuerte) : UmbralPendienteSubidaFuerteRutaPorDefecto; + _colorPendienteSubidaSuaveRuta = NormalizarColorHexRuta( + config.ColorPendienteSubidaSuaveRuta, + ColorPendienteSubidaSuaveRutaPorDefecto); + _colorPendienteSubidaMediaRuta = NormalizarColorHexRuta( + config.ColorPendienteSubidaMediaRuta, + ColorPendienteSubidaMediaRutaPorDefecto); + _colorPendienteSubidaFuerteRuta = NormalizarColorHexRuta( + config.ColorPendienteSubidaFuerteRuta, + ColorPendienteSubidaFuerteRutaPorDefecto); NormalizarOrdenUmbralesColorSubidaRuta(); if (config.UmbralPendienteBajadaRuta is { } umbralBajada && double.IsFinite(umbralBajada)) _umbralPendienteBajadaRuta = @@ -408,6 +423,9 @@ public partial class PlanificadorRutas _umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto; _umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto; _umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto; + _colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto; + _colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto; + _colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto; _umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto; _umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto; _velocidadPieKmH = 3.6; @@ -463,6 +481,9 @@ public partial class PlanificadorRutas _umbralPendienteSubidaRuta = UmbralPendienteSubidaRutaPorDefecto; _umbralPendienteSubidaMediaRuta = UmbralPendienteSubidaMediaRutaPorDefecto; _umbralPendienteSubidaFuerteRuta = UmbralPendienteSubidaFuerteRutaPorDefecto; + _colorPendienteSubidaSuaveRuta = ColorPendienteSubidaSuaveRutaPorDefecto; + _colorPendienteSubidaMediaRuta = ColorPendienteSubidaMediaRutaPorDefecto; + _colorPendienteSubidaFuerteRuta = ColorPendienteSubidaFuerteRutaPorDefecto; _umbralPendienteBajadaRuta = UmbralPendienteBajadaRutaPorDefecto; _umbralAlturaAlertaPendienteRutaMetros = UmbralAlturaAlertaPendienteRutaMetrosPorDefecto; _velocidadPieKmH = 3.6; diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs index 35c9940..4c07ab0 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.Renderizado.cs @@ -134,6 +134,47 @@ public partial class PlanificadorRutas : ComponentBase StateHasChanged(); } + private enum NivelColorSubidaRuta + { + Suave, + Media, + Fuerte + } + + private async Task CambiarColorPendienteSubidaRutaAsync( + ChangeEventArgs e, + NivelColorSubidaRuta nivel) + { + var (actual, porDefecto) = nivel switch + { + NivelColorSubidaRuta.Media => + (_colorPendienteSubidaMediaRuta, ColorPendienteSubidaMediaRutaPorDefecto), + NivelColorSubidaRuta.Fuerte => + (_colorPendienteSubidaFuerteRuta, ColorPendienteSubidaFuerteRutaPorDefecto), + _ => + (_colorPendienteSubidaSuaveRuta, ColorPendienteSubidaSuaveRutaPorDefecto) + }; + var nuevo = NormalizarColorHexRuta( + Convert.ToString(e.Value, CultureInfo.InvariantCulture), + NormalizarColorHexRuta(actual, porDefecto)); + + switch (nivel) + { + case NivelColorSubidaRuta.Media: + _colorPendienteSubidaMediaRuta = nuevo; + break; + case NivelColorSubidaRuta.Fuerte: + _colorPendienteSubidaFuerteRuta = nuevo; + break; + default: + _colorPendienteSubidaSuaveRuta = nuevo; + break; + } + + await PersistirConfiguracionAsync(); + StateHasChanged(); + } + private async Task RecalcularRankingTrasCambioEsfuerzoPendienteAsync(bool forzarRepintado = false) { if (_opcionesRuta.Count == 0) @@ -244,6 +285,32 @@ public partial class PlanificadorRutas : ComponentBase 100.0); } + private static string NormalizarColorHexRuta(string? valor, string porDefecto) + { + var color = valor?.Trim(); + if (EsColorHexRutaValido(color)) + return color!.ToLowerInvariant(); + + var respaldo = porDefecto?.Trim(); + return EsColorHexRutaValido(respaldo) + ? respaldo!.ToLowerInvariant() + : "#000000"; + } + + private static bool EsColorHexRutaValido(string? color) + { + if (color is null || color.Length != 7 || color[0] != '#') + return false; + + for (var i = 1; i < color.Length; i++) + { + if (!Uri.IsHexDigit(color[i])) + return false; + } + + return true; + } + private static double ParseUmbralAlturaAlertaPendienteRutaMetros(object? valor, double actual) { var texto = Convert.ToString(valor, CultureInfo.InvariantCulture)?.Replace(',', '.'); @@ -473,13 +540,25 @@ public partial class PlanificadorRutas : ComponentBase 100.0); if (pendientePorcentaje.Value >= umbralSubidaFuerte) - return ("subida-fuerte", "#000000"); + return ( + "subida-fuerte", + NormalizarColorHexRuta( + _colorPendienteSubidaFuerteRuta, + ColorPendienteSubidaFuerteRutaPorDefecto)); if (pendientePorcentaje.Value >= umbralSubidaMedia) - return ("subida-media", "#4c1d95"); + return ( + "subida-media", + NormalizarColorHexRuta( + _colorPendienteSubidaMediaRuta, + ColorPendienteSubidaMediaRutaPorDefecto)); if (pendientePorcentaje.Value >= umbralSubida) - return ("subida-suave", "#d946ef"); + return ( + "subida-suave", + NormalizarColorHexRuta( + _colorPendienteSubidaSuaveRuta, + ColorPendienteSubidaSuaveRutaPorDefecto)); if (pendientePorcentaje.Value <= -umbralBajada) return ("bajada", "#2563eb"); diff --git a/RutasDBUS/Components/Pages/PlanificadorRutas.razor b/RutasDBUS/Components/Pages/PlanificadorRutas.razor index 23990f6..d69a086 100644 --- a/RutasDBUS/Components/Pages/PlanificadorRutas.razor +++ b/RutasDBUS/Components/Pages/PlanificadorRutas.razor @@ -1083,6 +1083,18 @@
+ +
@if (_historialLocalCargando) @@ -1830,44 +1842,65 @@ -