From 540bc46fc2f6479a72130754f265200cb1d1e93d Mon Sep 17 00:00:00 2001 From: EidanThen Date: Thu, 3 Sep 2026 13:48:17 -0400 Subject: [PATCH] feat: eliminar filtro de tiempo de distancia en las rutas --- .../planning-kpis.component.html | 4 -- .../planning-kpis/planning-kpis.component.ts | 7 -- .../route-overview-map.component.css | 7 ++ .../route-overview-map.component.html | 33 ++++----- .../route-overview-map.component.ts | 67 +++++++++++++++++-- 5 files changed, 83 insertions(+), 35 deletions(-) diff --git a/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.html b/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.html index 3459cd7..de01728 100644 --- a/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.html +++ b/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.html @@ -31,10 +31,6 @@
Distancia total{{ summary.totalDistance | number:'1.0-1' }} km{{ summary.averageDistance | number:'1.0-1' }} km promedio por ruta
-
-
-
Desplazamiento total{{ summary.totalTravelHours | number:'1.0-1' }} h{{ summary.averageTravelHours | number:'1.0-1' }} h promedio por ruta
-
diff --git a/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.ts b/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.ts index d370e3a..4e1b878 100644 --- a/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.ts +++ b/route-planner/src/app/private/home/planning-kpis/planning-kpis.component.ts @@ -14,9 +14,7 @@ interface KpiSummary { users: number; days: number; totalDistance: number; - totalTravelHours: number; averageDistance: number; - averageTravelHours: number; totalWorkHours: number; averageRoutesPerUser: number; averageUtilization: number; @@ -95,7 +93,6 @@ export class PlanningKpisComponent implements OnInit, OnDestroy { const totalPdvs = new Set([...assignedPdvIds, ...unassignedPdvIds]).size; const dailyRoutes = summarizeDailyRoutes(assigned); const totalDistance = dailyRoutes.reduce((sum, day) => sum + day.distanceKm, 0); - const totalTravelHours = dailyRoutes.reduce((sum, day) => sum + day.travelHours, 0); const totalWorkHours = dailyRoutes.reduce((sum, day) => sum + day.workHours, 0); const users = new Set(assigned.map(route => String(route.json.usuario_id))).size; const days = new Set(assigned.map(route => route.json.dia)).size; @@ -146,9 +143,7 @@ export class PlanningKpisComponent implements OnInit, OnDestroy { users, days, totalDistance, - totalTravelHours, averageDistance: assigned.length ? totalDistance / assigned.length : 0, - averageTravelHours: assigned.length ? totalTravelHours / assigned.length : 0, totalWorkHours, averageRoutesPerUser: users ? assigned.length / users : 0, averageUtilization, @@ -186,9 +181,7 @@ export class PlanningKpisComponent implements OnInit, OnDestroy { users: 0, days: 0, totalDistance: 0, - totalTravelHours: 0, averageDistance: 0, - averageTravelHours: 0, totalWorkHours: 0, averageRoutesPerUser: 0, averageUtilization: 0, diff --git a/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.css b/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.css index d2879f7..70a5a87 100644 --- a/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.css +++ b/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.css @@ -357,6 +357,13 @@ .daily-routes-card { padding: 12px; border: 1px solid var(--glm-gris-medio); border-top: 3px solid var(--glm-azul); border-radius: 4px; background: var(--glm-surface); } .daily-routes-card h3 { margin: 0 0 8px; color: var(--glm-azul); font-size: 0.82rem; text-transform: uppercase; } +.daily-route-group { border-bottom: 1px solid var(--glm-gris-claro); } +.daily-route-group:last-child { border-bottom: 0; } +.daily-route-toggle { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 0; border: 0; background: transparent; color: var(--glm-gris-oscuro); cursor: pointer; font: inherit; text-align: left; } +.daily-route-toggle:hover { background: var(--glm-gris-claro); } +.daily-route-toggle-title { color: var(--glm-azul); font-weight: 700; } +.daily-route-toggle > span:last-child { color: var(--glm-acero); font-size: 0.78rem; } +.daily-route-group-content { padding-left: 14px; } .daily-route { display: grid; gap: 3px; padding: 8px 0; border-bottom: 1px solid var(--glm-gris-claro); color: var(--glm-gris-oscuro); font-size: 0.78rem; } .daily-route:last-child { border-bottom: 0; } .daily-route span, .daily-route small { color: var(--glm-acero); } diff --git a/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.html b/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.html index d08bc9f..fcb4feb 100644 --- a/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.html +++ b/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.html @@ -31,7 +31,7 @@
- {{ filteredRoutes.length }} PDVs + {{ uniqueFilteredRoutes.length }} PDVs {{ totalDistancia }} km {{ totalDuracionMin }} min
@@ -152,26 +152,19 @@

Rutas diarias

-
- {{ day.userName }} · {{ day.day }} - {{ day.routes.length }} paradas · {{ day.distanceKm | number:'1.1-1' }} km · {{ (day.travelHours * 60) | number:'1.0-0' }} min - {{ day.totalHours | number:'1.1-1' }} / {{ day.capacityHours | number:'1.1-1' }} h {{ day.exceedsCapacity ? '· Excede capacidad' : '· Capacidad disponible' }} +
+ +
+
+ {{ route.userName }} + {{ route.routes.length }} paradas · {{ route.distanceKm | number:'1.1-1' }} km · {{ (route.travelHours * 60) | number:'1.0-0' }} min + {{ route.totalHours | number:'1.1-1' }} / {{ route.capacityHours | number:'1.1-1' }} h {{ route.exceedsCapacity ? '· Excede capacidad' : '· Capacidad disponible' }} +
+
-
-

{{ selectedUsuarioId === 'Todos' ? 'PDVs de todos los usuarios' : 'PDVs del usuario' }} ({{ filteredRoutes.length }})

-
    -
  • - {{ routeVisitOrder(r) }} -
    - {{ r.json.nombre_pdv }} - {{ selectedUsuarioId === 'Todos' ? r.json.nombre_usuario + ' • ' : '' }}{{ r.json.dia }} • {{ routeSegmentDistance(r) | number:'1.2-2' }} km • {{ routeSegmentDurationMinutes(r) | number:'1.0-0' }} min -
    - -
  • -
-
diff --git a/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.ts b/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.ts index e5a20d1..566aa67 100644 --- a/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.ts +++ b/route-planner/src/app/private/home/route-overview-map/route-overview-map.component.ts @@ -11,6 +11,14 @@ import { RouteAssignedResponse, RouteNoAssignedResponse } from '../../../core/mo import { colorForUsuario } from '../../../shared/utils/palette.utils'; import { DailyRouteSummary, haversineKm, sequenceRoutes, summarizeDailyRoutes } from '../../../shared/utils/route-sequencing.utils'; +interface DailyRouteGroup { + day: string; + routes: DailyRouteSummary[]; + totalStops: number; + totalDistance: number; + totalTravelHours: number; +} + interface MapRoute { data: RouteAssignedResponse; color: string; @@ -53,6 +61,7 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn dias: string[] = []; selectedUsuarioId: string = 'Todos'; selectedDia: string = 'Todos'; + expandedDailyRouteDay: string | null = null; selectedRouteDetail: RouteAssignedResponse | null = null; filteredRoutes: RouteAssignedResponse[] = []; @@ -81,16 +90,41 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn reassignError: string | null = null; // Summary stats + get uniqueFilteredRoutes(): RouteAssignedResponse[] { + const unique = new Map(); + for (const route of this.filteredRoutes) { + const pdvId = String(route.json.pdv_id); + if (!unique.has(pdvId)) unique.set(pdvId, route); + } + return Array.from(unique.values()); + } + get totalDistancia(): string { + const uniqueIds = new Set(this.uniqueFilteredRoutes.map(route => String(route.json.pdv_id))); const sum = this.mapRoutes.length - ? this.mapRoutes.reduce((acc, route) => acc + (route.segmentDistanceKm ?? haversineKm(route.origin, route.destination)), 0) - : this.filteredRoutes.reduce((acc, route) => acc + Number(route.json.distancia), 0); + ? this.mapRoutes + .filter(route => uniqueIds.has(String(route.data.json.pdv_id))) + .reduce((acc, route) => { + const firstSegment = this.mapRoutes.find(candidate => String(candidate.data.json.pdv_id) === String(route.data.json.pdv_id)); + return firstSegment === route + ? acc + (route.segmentDistanceKm ?? haversineKm(route.origin, route.destination)) + : acc; + }, 0) + : this.uniqueFilteredRoutes.reduce((acc, route) => acc + Number(route.json.distancia), 0); return sum.toFixed(2); } get totalDuracionMin(): string { + const uniqueIds = new Set(this.uniqueFilteredRoutes.map(route => String(route.json.pdv_id))); const sum = this.mapRoutes.length - ? this.mapRoutes.reduce((acc, route) => acc + (route.segmentDurationHours ?? Number(route.data.json.horas_desplazamiento)) * 60, 0) - : this.filteredRoutes.reduce((acc, route) => acc + Number(route.json.horas_desplazamiento) * 60, 0); + ? this.mapRoutes + .filter(route => uniqueIds.has(String(route.data.json.pdv_id))) + .reduce((acc, route) => { + const firstSegment = this.mapRoutes.find(candidate => String(candidate.data.json.pdv_id) === String(route.data.json.pdv_id)); + return firstSegment === route + ? acc + (route.segmentDurationHours ?? Number(route.data.json.horas_desplazamiento)) * 60 + : acc; + }, 0) + : this.uniqueFilteredRoutes.reduce((acc, route) => acc + Number(route.json.horas_desplazamiento) * 60, 0); return sum.toFixed(0); } @@ -98,6 +132,31 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn return summarizeDailyRoutes(this.filteredRoutes); } + get dailyGroups(): DailyRouteGroup[] { + const order = ['Lunes', 'Martes', 'Miércoles', 'Miercoles', 'Jueves', 'Viernes']; + const groups = new Map(); + for (const summary of this.dailySummaries) { + const routes = groups.get(summary.day) ?? []; + routes.push(summary); + groups.set(summary.day, routes); + } + return Array.from(groups, ([day, routes]) => ({ + day, + routes, + totalStops: routes.reduce((sum, route) => sum + route.routes.length, 0), + totalDistance: routes.reduce((sum, route) => sum + route.distanceKm, 0), + totalTravelHours: routes.reduce((sum, route) => sum + route.travelHours, 0) + })).sort((a, b) => order.indexOf(a.day) - order.indexOf(b.day)); + } + + toggleDailyRouteDay(day: string): void { + this.expandedDailyRouteDay = this.expandedDailyRouteDay === day ? null : day; + } + + isDailyRouteDayExpanded(day: string): boolean { + return this.expandedDailyRouteDay === day; + } + constructor( private routeState: RouteState, private orsService: OrsService,