first commit

This commit is contained in:
2026-08-27 17:29:18 -04:00
commit 2405dda41b
44 changed files with 11368 additions and 0 deletions
@@ -0,0 +1,71 @@
.assigned-route-container {
width: 100%;
}
.header-actions {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 14px;
flex-wrap: wrap;
}
.actions-right {
display: flex;
gap: 8px;
}
.btn-secondary {
background: var(--glm-surface);
color: var(--glm-azul);
border: 1px solid var(--glm-gris-medio);
border-radius: 4px;
padding: 8px 14px;
font-size: 13px;
font-family: Arial, sans-serif;
font-weight: 600;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
}
.btn-secondary:hover {
background: var(--glm-verde-bg);
border-color: var(--glm-verde);
}
.btn-icon {
background: transparent;
border: 1px solid var(--glm-gris-medio);
border-radius: 4px;
color: var(--glm-azul);
cursor: pointer;
font-size: 15px;
padding: 6px 8px;
transition: all 0.2s;
}
.btn-icon:hover {
background: var(--glm-verde);
border-color: var(--glm-verde);
color: #ffffff;
}
.table-caption {
display: flex;
justify-content: space-between;
align-items: center;
}
.table-caption .font-bold {
color: var(--glm-azul);
font-weight: 700;
}
.table-caption .count {
color: var(--glm-acero);
font-size: 13px;
}
@@ -0,0 +1,91 @@
<div class="assigned-route-container">
<div class="header-actions">
<input
pInputText
type="text"
placeholder="Buscar..."
(input)="dt.filterGlobal($any($event.target).value, 'contains')"
/>
<div class="actions-right">
<button type="button" class="btn-secondary" (click)="downloadCSV()">
<i class="pi pi-download"></i> Descargar CSV
</button>
<button
type="button"
class="btn-secondary"
(click)="toggleRutas()"
pTooltip="Mostrar u ocultar el detalle de rutas"
tooltipPosition="top"
>
<i [class]="rutasExpandido ? 'pi pi-chevron-up' : 'pi pi-chevron-down'"></i>
{{ rutasExpandido ? 'Ocultar rutas' : 'Ver rutas' }}
</button>
</div>
</div>
<p-table
#dt
[value]="assignedRoutes"
[loading]="isLoading"
[paginator]="true"
[rows]="10"
size="small"
[columns]="cols"
[rowsPerPageOptions]="[10, 25, 50]"
styleClass="p-datatable-sm text-sm"
>
<ng-template pTemplate="caption">
<div class="table-caption">
<span class="font-bold">Rutas asignadas</span>
<span class="count">{{ assignedRoutes.length }} registros</span>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th *ngFor="let col of cols" pSortableColumn="{{ col.field }}">
{{ col.header }}
<p-sortIcon [field]="col.field"></p-sortIcon>
<p-columnFilter
[field]="col.field"
display="menu"
matchMode="contains"
[showMatchModes]="false"
[showOperator]="false"
[showAddButton]="false"
></p-columnFilter>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-row>
<tr>
<td>{{ row.json.nombre_usuario }}</td>
<td>{{ row.json.nombre_pdv }}</td>
<td>{{ row.json.hora_laboral_semanal_usuario }}</td>
<td>{{ row.json.hora_minima_semanal_pdv }}</td>
<td>{{ row.json.horas_trabajo }}</td>
<td>{{ row.json.dia }}</td>
<td>{{ row.json.distancia }}</td>
<td>{{ row.json.horas_desplazamiento }}</td>
<td class="text-center">
<button
type="button"
class="btn-icon"
(click)="onShowMap(row)"
pTooltip="Ver en mapa"
tooltipPosition="top"
>
<i class="pi pi-map-marker"></i>
</button>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="9" class="text-center">No hay rutas asignadas</td>
</tr>
</ng-template>
</p-table>
</div>
@@ -0,0 +1,122 @@
import { Component, ChangeDetectorRef, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { RouteAssignedResponse } from '../../../core/models/route.model';
import { saveAs } from 'file-saver';
import { Table } from 'primeng/table';
import { TableModule } from 'primeng/table';
import { InputTextModule } from 'primeng/inputtext';
import { TooltipModule } from 'primeng/tooltip';
import { RouteState } from '../../../core/route-state/route';
@Component({
standalone: true,
selector: 'app-assigned-route',
imports: [
CommonModule,
FormsModule,
TableModule,
InputTextModule,
TooltipModule
],
templateUrl: './assigned-route.component.html',
styleUrl: './assigned-route.component.css'
})
export class AssignedRouteComponent {
@ViewChild('dt') table!: Table;
rutasExpandido = false;
tablaRutas = '';
assignedRoutes: RouteAssignedResponse[] = [];
isLoading = false;
cols: any[] = [];
constructor(
private routesState: RouteState,
private cdr: ChangeDetectorRef,
private router: Router
) { }
ngOnInit() {
this.routesState.resetLoading();
this.routesState.loading$.subscribe(value => {
this.isLoading = value;
this.cdr.detectChanges();
});
this.routesState.assignedRoutes$.subscribe(data => {
this.assignedRoutes = [...data];
this.cdr.detectChanges();
});
this.cols = [
{ field: 'json.nombre_usuario', header: 'Usuario' },
{ field: 'json.nombre_pdv', header: 'PDV' },
{ field: 'json.hora_laboral_semanal_usuario', header: 'Sem. Usuario (hr)' },
{ field: 'json.hora_minima_semanal_pdv', header: 'Horas Min. PDV' },
{ field: 'json.horas_trabajo', header: 'Horas Trabajo' },
{ field: 'json.dia', header: 'Día' },
{ field: 'json.distancia', header: 'Distancia (km)' },
{ field: 'json.horas_desplazamiento', header: 'Duración (min)' },
{ field: '', header: '' }
];
}
onShowMap(row: RouteAssignedResponse) {
this.routesState.sendRoute(row);
this.router.navigate(['/home/assigned-route']);
}
toggleRutas() {
this.rutasExpandido = !this.rutasExpandido;
}
downloadCSV() {
const headers = [
'Usuario ID',
'Usuario',
'Long Usuario',
'Lat Usuario',
'PDV ID',
'PDV',
'Long PDV',
'Lat PDV',
'Horas Usuario',
'Horas PDV',
'Horas Trabajo',
'Día',
'Distancia',
'Duracion'
];
const data =
this.table.filteredValue && this.table.filteredValue.length
? this.table.filteredValue
: this.assignedRoutes;
const rows = data.map(r => [
r.json.usuario_id,
r.json.nombre_usuario,
r.json.longitud_usuario,
r.json.latitud_usuario,
r.json.pdv_id,
r.json.nombre_pdv,
r.json.longitud_pdv,
r.json.latitud_pdv,
r.json.hora_laboral_semanal_usuario,
r.json.hora_minima_semanal_pdv,
r.json.horas_trabajo,
r.json.dia,
r.json.distancia,
r.json.horas_desplazamiento
]);
const csvContent =
[headers, ...rows].map(r => r.join(',')).join('\n');
const bom = '\uFEFF';
const blob = new Blob([bom + csvContent], {
type: 'text/csv;charset=utf-8;'
});
saveAs(blob, 'rutas_planificadas.csv');
}
}