114 lines
4.4 KiB
TypeScript
114 lines
4.4 KiB
TypeScript
import { Component, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { TableModule } from 'primeng/table';
|
|
import { RouteNoAssignedResponse } from '../../../core/models/route.model';
|
|
import { RouteState, UnassignedAssignment } from '../../../core/route-state/route';
|
|
import { Subscription } from 'rxjs';
|
|
import { OrsService } from '../../../core/services/ors.service';
|
|
|
|
@Component({
|
|
standalone: true,
|
|
selector: 'app-no-assigned-route',
|
|
imports: [CommonModule, FormsModule, TableModule],
|
|
templateUrl: './no-assigned-route.component.html',
|
|
styleUrls: ['./no-assigned-route.component.css']
|
|
})
|
|
export class NoAssignedRouteComponent implements OnDestroy {
|
|
|
|
noAssignedRoutes: RouteNoAssignedResponse[] = [];
|
|
isLoading = false;
|
|
cols: { field: string; header: string }[] = [];
|
|
private subs: Subscription[] = [];
|
|
assignmentUser: Record<string, string> = {};
|
|
assignmentDays: Record<string, string[]> = {};
|
|
assigningId: number | null = null;
|
|
actionError: string | null = null;
|
|
|
|
constructor(
|
|
private routesState: RouteState,
|
|
private cdr: ChangeDetectorRef,
|
|
private orsService: OrsService
|
|
) {}
|
|
|
|
ngOnInit() {
|
|
this.subs.push(
|
|
this.routesState.loading$.subscribe(value => {
|
|
this.isLoading = value;
|
|
this.cdr.detectChanges();
|
|
})
|
|
);
|
|
this.subs.push(
|
|
this.routesState.noAssignedRoutes$.subscribe(data => {
|
|
this.noAssignedRoutes = [...data];
|
|
this.cdr.detectChanges();
|
|
})
|
|
);
|
|
this.cols = [
|
|
{ field: 'pdv_id', header: 'PDV ID' },
|
|
{ field: 'pdv_nombre', header: 'Nombre' },
|
|
{ field: 'minimo_horas_semana', header: 'Min. Horas/Sem' },
|
|
{ field: 'latitud', header: 'Latitud' },
|
|
{ field: 'longitud', header: 'Longitud' },
|
|
{ field: 'motivo', header: 'Motivo' }
|
|
];
|
|
}
|
|
|
|
get userOptions(): { id: string; name: string }[] {
|
|
return this.routesState.getUsuariosUnicos().map(user => ({ id: user.id, name: user.nombre }));
|
|
}
|
|
|
|
get dayOptions(): string[] {
|
|
return this.routesState.getDiasUnicos();
|
|
}
|
|
|
|
toggleDay(pdvId: number, day: string, checked: boolean): void {
|
|
const key = String(pdvId);
|
|
const days = new Set(this.assignmentDays[key] ?? []);
|
|
checked ? days.add(day) : days.delete(day);
|
|
this.assignmentDays[key] = Array.from(days);
|
|
}
|
|
|
|
async assignRoute(route: RouteNoAssignedResponse): Promise<void> {
|
|
const userId = this.assignmentUser[String(route.pdv_id)];
|
|
const days = this.assignmentDays[String(route.pdv_id)] ?? [];
|
|
if (!userId || !days.length) {
|
|
this.actionError = 'Selecciona usuario y al menos un día antes de asignar.';
|
|
return;
|
|
}
|
|
const user = this.routesState.getAssignedSnapshot().find(item => String(item.json.usuario_id) === userId);
|
|
if (!user) return;
|
|
this.assigningId = route.pdv_id;
|
|
this.actionError = null;
|
|
try {
|
|
const origin = [Number(user.json.longitud_usuario), Number(user.json.latitud_usuario)];
|
|
const destination = [Number(route.longitud), Number(route.latitud)];
|
|
const assignments: UnassignedAssignment[] = [];
|
|
for (const day of days) {
|
|
const result = await this.orsService.processPairs([{ pdv_id: route.pdv_id, origin, destination }], 1, true);
|
|
const geo = (result[0] as { geojson?: { features?: { properties?: { summary?: { distance: number; duration: number } } }[] } }).geojson;
|
|
const summary = geo?.features?.[0]?.properties?.summary;
|
|
if (!summary) throw new Error('ORS no devolvió una ruta vial');
|
|
const capacity = this.routesState.canAssignToDay(userId, day, route.minimo_horas_semana, undefined, summary.duration / 3600);
|
|
if (!capacity.allowed) {
|
|
this.actionError = `${day}: excede la capacidad diaria (${capacity.totalHours.toFixed(1)} h de ${capacity.capacityHours.toFixed(1)} h).`;
|
|
return;
|
|
}
|
|
assignments.push({ userId, day, distance: summary.distance / 1000, duration: summary.duration / 3600 });
|
|
}
|
|
this.routesState.addAssignedFromUnassignedMany(route, assignments);
|
|
delete this.assignmentUser[String(route.pdv_id)];
|
|
delete this.assignmentDays[String(route.pdv_id)];
|
|
} catch {
|
|
this.actionError = `No se pudo calcular la ruta vial para ${route.pdv_nombre}.`;
|
|
} finally {
|
|
this.assigningId = null;
|
|
this.cdr.detectChanges();
|
|
}
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
this.subs.forEach(s => s.unsubscribe());
|
|
}
|
|
}
|