feat: creacion de proyectos, gestor de empleados

This commit is contained in:
2026-09-17 15:04:52 -04:00
parent 0c652a0bd7
commit 0736db2b5c
44 changed files with 919 additions and 125 deletions
@@ -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')]);
}
}