feat: creacion de proyectos, gestor de empleados
This commit is contained in:
+24
-3
@@ -99,12 +99,26 @@ export class AssignedRouteDetailComponent implements OnInit, OnDestroy, AfterVie
|
||||
}
|
||||
|
||||
private initMap() {
|
||||
this.map = L.map('map').setView([18.4861, -69.9312], 13);
|
||||
const territoryBounds = this.routeData ? L.latLngBounds([
|
||||
[Number(this.routeData.json.latitud_usuario), Number(this.routeData.json.longitud_usuario)],
|
||||
[Number(this.routeData.json.latitud_pdv), Number(this.routeData.json.longitud_pdv)]
|
||||
]) : null;
|
||||
this.map = L.map('map', { maxBoundsViscosity: 0.85 });
|
||||
if (territoryBounds?.isValid()) {
|
||||
this.map.fitBounds(territoryBounds.pad(0.3));
|
||||
this.map.setMaxBounds(territoryBounds.pad(0.6));
|
||||
} else {
|
||||
this.map.setView([18.4861, -69.9312], 13);
|
||||
}
|
||||
const pdvPane = this.map.createPane('pdvPane');
|
||||
pdvPane.style.zIndex = '650';
|
||||
|
||||
this.tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
attribution: '© OpenStreetMap contributors',
|
||||
bounds: territoryBounds?.isValid() ? territoryBounds.pad(0.6) : undefined,
|
||||
keepBuffer: 1,
|
||||
noWrap: true,
|
||||
updateWhenIdle: true
|
||||
}).addTo(this.map);
|
||||
}
|
||||
|
||||
@@ -127,6 +141,13 @@ export class AssignedRouteDetailComponent implements OnInit, OnDestroy, AfterVie
|
||||
|
||||
this.centerMapOnPoints(origin, destination);
|
||||
|
||||
if (json.geojson) {
|
||||
this.showRouteOnMap(json.geojson, origin, destination, json, this.segmentOriginName);
|
||||
this.isLoading = false;
|
||||
this.cdr.detectChanges();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const results = await this.orsService.processPairs(
|
||||
[{ pdv_id: json.pdv_id, origin, destination }],
|
||||
@@ -292,6 +313,6 @@ export class AssignedRouteDetailComponent implements OnInit, OnDestroy, AfterVie
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.router.navigate(['/home']);
|
||||
this.router.navigate(['/proyectos', this.activatedRoute.snapshot.paramMap.get('id')]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<ng-template #dayValue>{{ row.json.dia }}</ng-template>
|
||||
</td>
|
||||
<td>{{ row.json.distancia }}</td>
|
||||
<td>{{ row.json.horas_desplazamiento }}</td>
|
||||
<td>{{ (row.json.horas_desplazamiento * 60).toFixed(0) }} min</td>
|
||||
<td class="text-center edit-actions">
|
||||
<ng-container *ngIf="editingRoute === row; else editButton">
|
||||
<button type="button" class="btn-icon" (click)="saveEdit()" title="Guardar"><i class="pi pi-check"></i></button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { RouteAssignedResponse } from '../../../core/models/route.model';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { Table } from 'primeng/table';
|
||||
@@ -44,6 +44,7 @@ export class AssignedRouteComponent {
|
||||
private routesState: RouteState,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private rutasService: RutasService,
|
||||
private planningInput: PlanningInputService
|
||||
) { }
|
||||
@@ -79,7 +80,7 @@ export class AssignedRouteComponent {
|
||||
}
|
||||
onShowMap(row: RouteAssignedResponse) {
|
||||
this.routesState.sendRoute(row);
|
||||
this.router.navigate(['/home/assigned-route'], {
|
||||
this.router.navigate(['/proyectos', this.projectId, 'ruta'], {
|
||||
queryParams: { pdv_id: row.json.pdv_id, usuario_id: row.json.usuario_id, dia: row.json.dia }
|
||||
});
|
||||
}
|
||||
@@ -88,18 +89,20 @@ export class AssignedRouteComponent {
|
||||
// If table has filtered view, use first visible row's usuario as default; else first assigned
|
||||
const target = (this.table?.filteredValue?.[0] as RouteAssignedResponse | undefined) ?? this.assignedRoutes[0];
|
||||
if (target) {
|
||||
this.router.navigate(['/home/mapa'], {
|
||||
this.router.navigate(['/proyectos', this.projectId, 'mapa'], {
|
||||
queryParams: { usuario_id: target.json.usuario_id, dia: 'Todos' }
|
||||
});
|
||||
} else {
|
||||
this.router.navigate(['/home/mapa']);
|
||||
this.router.navigate(['/proyectos', this.projectId, 'mapa']);
|
||||
}
|
||||
}
|
||||
|
||||
goToUserMap(usuarioId: string) {
|
||||
this.router.navigate(['/home/mapa'], { queryParams: { usuario_id: usuarioId, dia: 'Todos' } });
|
||||
this.router.navigate(['/proyectos', this.projectId, 'mapa'], { queryParams: { usuario_id: usuarioId, dia: 'Todos' } });
|
||||
}
|
||||
|
||||
private get projectId(): string { return this.activatedRoute.snapshot.paramMap.get('id') ?? ''; }
|
||||
|
||||
get userOptions(): { id: string; name: string }[] {
|
||||
const users = new Map<string, string>();
|
||||
for (const route of this.assignedRoutes) users.set(String(route.json.usuario_id), route.json.nombre_usuario);
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
color: var(--glm-verde);
|
||||
}
|
||||
|
||||
.projects-link { margin-left: auto; color: var(--glm-azul); font-size: .82rem; text-decoration: none; }
|
||||
.user-profile { display:flex; align-items:center; gap:8px; margin-left:auto; color:var(--glm-azul); font-size:.84rem; font-weight:700; }.user-profile img,.avatar-fallback { width:30px; height:30px; border-radius:50%; }.user-profile img { object-fit:cover; }.avatar-fallback { display:inline-flex; align-items:center; justify-content:center; background:var(--glm-verde); color:#fff; }
|
||||
.projects-link button,.home-employees-link,.edit-project,.project-edit-actions button { border:1px solid var(--glm-gris-medio); border-radius:4px; background:#fff; color:var(--glm-azul); cursor:pointer; font:inherit; font-weight:700; padding:8px 11px; text-decoration:none; }.home-employees-link { display:inline-block; margin-bottom:12px; }.project-heading { width:100%; }.project-title-line { display:flex; align-items:center; gap:14px; }.project-title-line h1 { margin:0; }.back-projects { color:var(--glm-azul); font-size:.85rem; font-weight:700; text-decoration:none; white-space:nowrap; }.edit-project,.project-edit-actions { margin-left:auto; }.project-title-line input,.project-heading textarea { border:1px solid #9aaab3; border-radius:4px; background:#fff; color:#2f3f48; font:inherit; padding:8px; }.project-heading textarea { display:block; width:100%; margin-top:10px; resize:vertical; }.project-edit-actions { display:flex; gap:6px; }.project-edit-actions button:first-child { background:var(--glm-azul); color:#fff; }
|
||||
|
||||
.home-container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
<div class="glm-topbar">
|
||||
<img src="GLM_completo.png" alt="GomezLee Marketing" class="glm-topbar-logo" />
|
||||
<span class="glm-topbar-sep">|</span>
|
||||
<span class="glm-topbar-context">Route Planner</span>
|
||||
<span class="glm-topbar-context">Route Planner</span><div class="user-profile" *ngIf="profile.current"><img *ngIf="profile.avatarUrl" [src]="profile.avatarUrl" alt=""><span *ngIf="!profile.avatarUrl" class="avatar-fallback">{{ profile.current.nombre.charAt(0) }}</span><span>{{ profile.current.nombre }}</span></div><div class="projects-link"><button type="button" (click)="signOut()"><i class="pi pi-sign-out"></i> Salir</button></div>
|
||||
</div>
|
||||
|
||||
<div class="home-container">
|
||||
<a class="employees-link home-employees-link" routerLink="/empleados"><i class="pi pi-users"></i> Gestionar empleados</a>
|
||||
<header class="home-header">
|
||||
<div><h1>Route Planner</h1><p>Gestión de rutas asignadas y puntos de venta pendientes</p></div>
|
||||
<a class="employees-link" routerLink="/empleados"><i class="pi pi-users"></i> Gestionar empleados</a>
|
||||
<div class="project-heading"><div class="project-title-line"><a class="back-projects" routerLink="/home"><i class="pi pi-arrow-left"></i> Mis proyectos</a><h1 *ngIf="!editingProject">{{ projectName || 'Proyecto' }}</h1><input *ngIf="editingProject" [(ngModel)]="projectName" maxlength="120"><button *ngIf="!editingProject" type="button" class="edit-project" (click)="startProjectEdit()"><i class="pi pi-pencil"></i> Editar proyecto</button><div *ngIf="editingProject" class="project-edit-actions"><button type="button" (click)="saveProject()" [disabled]="projectSaving">Guardar</button><button type="button" (click)="cancelProjectEdit()" [disabled]="projectSaving">Cancelar</button></div></div><textarea *ngIf="editingProject" [(ngModel)]="projectDescription" maxlength="500" placeholder="Descripción del proyecto"></textarea><p *ngIf="!editingProject">Gestión de rutas asignadas y puntos de venta pendientes</p></div>
|
||||
</header>
|
||||
|
||||
<section class="card upload-card">
|
||||
<h2 class="section-title">Subir PDVs para optimización</h2>
|
||||
<p class="employees-summary" [class.empty]="!employeesLoading && !activeEmployees"><i class="pi pi-users"></i> <span *ngIf="employeesLoading">Cargando empleados activos...</span><span *ngIf="!employeesLoading">{{ activeEmployees }} empleados activos se usarán para planificar.</span></p>
|
||||
<h2 class="section-title">PDVs del proyecto</h2>
|
||||
<p class="employees-summary" [class.empty]="!employeesLoading && !activeEmployees"><i class="pi pi-users"></i> <span *ngIf="employeesLoading">Cargando empleados activos...</span><span *ngIf="!employeesLoading">{{ activeEmployees }} empleados activos de {{ projectCountry }} se usarán para planificar.</span></p>
|
||||
|
||||
<div class="upload-grid">
|
||||
<div class="file-input-wrapper">
|
||||
<label class="file-label" for="pdvFile" [class.has-error]="pdvPreview && !pdvPreview.valid" [class.has-success]="pdvPreview?.valid">
|
||||
<i class="pi pi-file-excel"></i>
|
||||
<span>Archivo PDVs (Excel)</span>
|
||||
<span class="file-hint">.xlsx / .xls • máx 10MB • máx 2000 filas</span>
|
||||
<span>Archivo PDVs guardado</span>
|
||||
<span class="file-hint">Los PDVs pertenecen a este proyecto</span>
|
||||
</label>
|
||||
<input
|
||||
id="pdvFile"
|
||||
type="file"
|
||||
accept=".xlsx,.xls"
|
||||
(change)="onPdvFileChange($event)"
|
||||
disabled
|
||||
class="file-input"
|
||||
[class.has-file]="pdvFile"
|
||||
/>
|
||||
|
||||
@@ -10,8 +10,11 @@ import { ExcelPreviewService, PreviewResult } from '../../shared/services/excel-
|
||||
import { Subscription } from 'rxjs';
|
||||
import { PlanningKpisComponent } from './planning-kpis/planning-kpis.component';
|
||||
import { PlanningInputService } from '../../core/services/planning-input.service';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { EmployeeService } from '../../core/services/employee.service';
|
||||
import { ProjectService } from '../../core/services/project.service';
|
||||
import { AuthService } from '../../core/services/auth.service';
|
||||
import { UserProfileService } from '../../core/services/user-profile.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@@ -31,6 +34,12 @@ export class HomeComponent implements OnInit {
|
||||
employeesLoading = true;
|
||||
uploadError: string | null = null;
|
||||
isLoading = false;
|
||||
projectName = '';
|
||||
projectId = '';
|
||||
editingProject = false;
|
||||
projectDescription = '';
|
||||
projectCountry = '';
|
||||
projectSaving = false;
|
||||
private calcSub: Subscription | null = null;
|
||||
|
||||
constructor(
|
||||
@@ -39,17 +48,60 @@ export class HomeComponent implements OnInit {
|
||||
private previewService: ExcelPreviewService,
|
||||
private planningInput: PlanningInputService,
|
||||
private employeesService: EmployeeService,
|
||||
private projectsService: ProjectService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private auth: AuthService,
|
||||
private router: Router,
|
||||
public profile: UserProfileService,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
void this.loadEmployees();
|
||||
void this.initialize();
|
||||
}
|
||||
|
||||
private async initialize(): Promise<void> {
|
||||
await this.loadProject();
|
||||
await this.loadEmployees();
|
||||
}
|
||||
|
||||
private async loadProject(): Promise<void> {
|
||||
this.projectId = this.activatedRoute.snapshot.paramMap.get('id') ?? '';
|
||||
if (!this.projectId) return;
|
||||
try {
|
||||
const [project, input] = await Promise.all([this.projectsService.get(this.projectId), this.projectsService.projectInput(this.projectId)]);
|
||||
this.projectName = project.nombre;
|
||||
this.projectDescription = project.descripcion ?? '';
|
||||
this.projectCountry = input.country;
|
||||
this.pdvFile = input.file;
|
||||
this.pdvPreview = await this.previewService.previewFile(input.file, 'PDV');
|
||||
} catch (error: any) {
|
||||
this.uploadError = error?.message ?? 'No se pudo cargar el proyecto.';
|
||||
} finally {
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
startProjectEdit(): void { this.editingProject = true; }
|
||||
|
||||
cancelProjectEdit(): void { this.editingProject = false; void this.loadProject(); }
|
||||
|
||||
async saveProject(): Promise<void> {
|
||||
if (!this.projectId || !this.projectName.trim()) return;
|
||||
this.projectSaving = true;
|
||||
try {
|
||||
await this.projectsService.update(this.projectId, { nombre: this.projectName.trim(), descripcion: this.projectDescription.trim() || null });
|
||||
this.editingProject = false;
|
||||
} catch (error: any) { this.uploadError = error?.message ?? 'No se pudo actualizar el proyecto.'; }
|
||||
finally { this.projectSaving = false; this.cdr.detectChanges(); }
|
||||
}
|
||||
|
||||
async signOut(): Promise<void> { await this.auth.signOut(); await this.router.navigate(['/login']); }
|
||||
|
||||
private async loadEmployees(): Promise<void> {
|
||||
this.employeesLoading = true;
|
||||
try {
|
||||
this.activeEmployees = (await this.employeesService.list()).filter(employee => employee.activo).length;
|
||||
this.activeEmployees = (await this.employeesService.active(this.projectCountry)).length;
|
||||
} catch {
|
||||
this.activeEmployees = 0;
|
||||
} finally {
|
||||
@@ -190,7 +242,7 @@ export class HomeComponent implements OnInit {
|
||||
}
|
||||
let employeesFile: File;
|
||||
try {
|
||||
employeesFile = await this.employeesService.activeEmployeesFile();
|
||||
employeesFile = await this.employeesService.activeEmployeesFile(this.projectCountry);
|
||||
} catch {
|
||||
this.uploadError = 'No se pudieron obtener los empleados activos.';
|
||||
this.routeState.setLoading(false);
|
||||
@@ -202,6 +254,7 @@ export class HomeComponent implements OnInit {
|
||||
const formData = new FormData();
|
||||
formData.append('pdv_file', pdvForOptimizer, pdvForOptimizer.name);
|
||||
formData.append('usuarios_file', employeesFile, employeesFile.name);
|
||||
formData.append('pais', this.projectCountry);
|
||||
|
||||
this.calcSub?.unsubscribe();
|
||||
this.calcSub = this.rutasService.calcularRutas(formData).subscribe({
|
||||
@@ -247,7 +300,7 @@ export class HomeComponent implements OnInit {
|
||||
|
||||
private mapBackendError(err: unknown): string {
|
||||
const anyErr = err as { error?: { error?: string; message?: string }; message?: string; status?: number; name?: string };
|
||||
if (anyErr?.name === 'TimeoutError') return 'El cálculo tardó demasiado (timeout 30s). Intenta de nuevo con menos filas.';
|
||||
if (anyErr?.name === 'TimeoutError') return 'El cálculo tardó demasiado (timeout 180s). Intenta de nuevo con menos filas.';
|
||||
if (anyErr?.error?.error) return anyErr.error.error;
|
||||
if (anyErr?.error?.message) return anyErr.error.message;
|
||||
if (anyErr?.message) return anyErr.message;
|
||||
|
||||
@@ -77,6 +77,9 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.territory-summary { margin: 4px 0 0; color: var(--glm-acero); font-size: .76rem; line-height: 1.35; }
|
||||
.territory-summary i { color: var(--glm-verde); margin-right: 3px; }
|
||||
|
||||
.filters-card {
|
||||
background: var(--glm-verde-bg);
|
||||
border: 1px solid var(--glm-verde-claro);
|
||||
|
||||
+1
@@ -16,6 +16,7 @@
|
||||
</button>
|
||||
<h2 class="sidebar-title">Mapa de rutas</h2>
|
||||
</div>
|
||||
<p class="territory-summary" *ngIf="activeTerritories.length"><i class="pi pi-map-marker"></i> {{ activeTerritories.join(', ') }}</p>
|
||||
|
||||
<div class="filters-card" *ngIf="usuarios.length">
|
||||
<label class="filter-label">Usuario</label>
|
||||
|
||||
+59
-7
@@ -96,6 +96,14 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
reassignDay = '';
|
||||
reassignError: string | null = null;
|
||||
|
||||
get activeTerritories(): string[] {
|
||||
const values = [
|
||||
...this.allRoutes.map(route => route.json.metro || route.json.ciudad || route.json.estado || route.json.provincia || route.json.region || route.json.zona),
|
||||
...this.unassignedRoutes.map(route => route.metro || route.ciudad || route.estado || route.provincia || route.region || route.zona)
|
||||
].filter((value): value is string => !!value?.trim());
|
||||
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
// Summary stats
|
||||
get uniqueFilteredRoutes(): RouteAssignedResponse[] {
|
||||
const unique = new Map<string, RouteAssignedResponse>();
|
||||
@@ -140,7 +148,7 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
}
|
||||
|
||||
get dailyGroups(): DailyRouteGroup[] {
|
||||
const order = ['Lunes', 'Martes', 'Miércoles', 'Miercoles', 'Jueves', 'Viernes'];
|
||||
const order = ['Lunes', 'Martes', 'Miércoles', 'Miercoles', 'Jueves', 'Viernes', 'Sábado', 'Sabado'];
|
||||
const groups = new Map<string, DailyRouteSummary[]>();
|
||||
for (const summary of this.dailySummaries) {
|
||||
const routes = groups.get(summary.day) ?? [];
|
||||
@@ -289,7 +297,14 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
}
|
||||
|
||||
private initMap() {
|
||||
this.map = L.map('map-overview').setView([18.4861, -69.9312], 10);
|
||||
const territoryBounds = this.projectBounds();
|
||||
this.map = L.map('map-overview', { maxBoundsViscosity: 0.85 });
|
||||
if (territoryBounds) {
|
||||
this.map.fitBounds(territoryBounds.pad(0.15));
|
||||
this.map.setMaxBounds(territoryBounds.pad(0.35));
|
||||
} else {
|
||||
this.map.setView([18.4861, -69.9312], 10);
|
||||
}
|
||||
const pdvPane = this.map.createPane('pdvPane');
|
||||
const heatPane = this.map.createPane('heatPane');
|
||||
pdvPane.style.zIndex = '750';
|
||||
@@ -297,7 +312,11 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
const popupPane = this.map.getPane('popupPane');
|
||||
if (popupPane) popupPane.style.zIndex = '1000';
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
attribution: '© OpenStreetMap contributors',
|
||||
bounds: territoryBounds?.pad(0.35),
|
||||
keepBuffer: 1,
|
||||
noWrap: true,
|
||||
updateWhenIdle: true
|
||||
}).addTo(this.map);
|
||||
// Fix tiles after sidebar overlay
|
||||
setTimeout(() => this.map?.invalidateSize(), 200);
|
||||
@@ -336,6 +355,9 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
this.orsProgress = 0;
|
||||
this.orsTotal = 0;
|
||||
if (!this.filteredRoutes.length) {
|
||||
this.renderUnassignedMarkers();
|
||||
const territoryBounds = this.projectBounds();
|
||||
if (territoryBounds) this.map.fitBounds(territoryBounds.pad(0.15));
|
||||
this.cdr.detectChanges();
|
||||
return;
|
||||
}
|
||||
@@ -414,6 +436,12 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
marker.on('click', onClick);
|
||||
});
|
||||
|
||||
if (this.selectedUsuarioId === 'Todos' && this.selectedDia === 'Todos') {
|
||||
for (const route of this.unassignedRoutes) {
|
||||
if (this.validPoint(route.latitud, route.longitud)) bounds.push([Number(route.latitud), Number(route.longitud)]);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateHeatLayer();
|
||||
this.toggleLayerVisibility();
|
||||
|
||||
@@ -428,6 +456,25 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
private projectBounds(): L.LatLngBounds | null {
|
||||
const points: L.LatLngTuple[] = [];
|
||||
for (const route of this.allRoutes) {
|
||||
const data = route.json;
|
||||
if (this.validPoint(data.latitud_usuario, data.longitud_usuario)) points.push([Number(data.latitud_usuario), Number(data.longitud_usuario)]);
|
||||
if (this.validPoint(data.latitud_pdv, data.longitud_pdv)) points.push([Number(data.latitud_pdv), Number(data.longitud_pdv)]);
|
||||
}
|
||||
for (const route of this.unassignedRoutes) {
|
||||
if (this.validPoint(route.latitud, route.longitud)) points.push([Number(route.latitud), Number(route.longitud)]);
|
||||
}
|
||||
return points.length ? L.latLngBounds(points) : null;
|
||||
}
|
||||
|
||||
private validPoint(latitude: unknown, longitude: unknown): boolean {
|
||||
const lat = Number(latitude);
|
||||
const lng = Number(longitude);
|
||||
return Number.isFinite(lat) && Number.isFinite(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
|
||||
}
|
||||
|
||||
private renderUnassignedMarkers(): void {
|
||||
if (!this.map || this.selectedUsuarioId !== 'Todos' || this.selectedDia !== 'Todos') return;
|
||||
for (const route of this.unassignedRoutes) {
|
||||
@@ -452,15 +499,20 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
this.orsProgress = 0;
|
||||
this.cdr.detectChanges();
|
||||
|
||||
const pares = this.mapRoutes.map(route => ({
|
||||
const embeddedResults = this.mapRoutes
|
||||
.filter(route => !!route.data.json.geojson)
|
||||
.map(route => ({ pdv_id: route.requestId, geojson: route.data.json.geojson }));
|
||||
const pares = this.mapRoutes.filter(route => !route.data.json.geojson).map(route => ({
|
||||
pdv_id: route.requestId,
|
||||
origin: route.origin,
|
||||
destination: route.destination
|
||||
}));
|
||||
|
||||
const { promise } = this.orsService.processPairsWithProgress(pares, 2, true, this.abort$);
|
||||
try {
|
||||
const results = await promise;
|
||||
const remoteResults = pares.length
|
||||
? await this.orsService.processPairsWithProgress(pares, 2, true, this.abort$).promise
|
||||
: [];
|
||||
const results = [...embeddedResults, ...remoteResults];
|
||||
if (!results.length) {
|
||||
this.isLoading = false;
|
||||
this.cdr.detectChanges();
|
||||
@@ -950,6 +1002,6 @@ export class RouteOverviewMapComponent implements OnInit, OnDestroy, AfterViewIn
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.router.navigate(['/home']);
|
||||
this.router.navigate(['/proyectos', this.activatedRoute.snapshot.paramMap.get('id')]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user