Browse Source

limpieza y reestructuración de la tabla de visitas tecnicas no programadas, tambien se agrega la consulta de detalles usando componente de preventive-order-details modal

EmilianoOrtiz 1 week ago
parent
commit
85c7f4fa76

+ 1 - 5
src/app/app.module.ts

@@ -201,10 +201,7 @@ import { UpdateCharacteristicComponent } from './components/preventive-maintenan
 import { MaintenanceSimulationComponent } from './components/preventive-maintenance/maintenance-simulation/maintenance-simulation.component';
 import { SimulationDialogComponent } from './components/preventive-maintenance/maintenance-simulation/simulation-dialog/simulation-dialog.component';
 import { ExtractMaintenancePlanComponent } from './components/preventive-maintenance/extract-maintenance-plan/extract-maintenance-plan.component';
-import {
-  CommentsDialog,
-  UnprogrammedVisitsComponent,
-} from './components/preventive-maintenance/unprogrammed-visits/unprogrammed-visits.component';
+import { UnprogrammedVisitsComponent } from './components/preventive-maintenance/unprogrammed-visits/unprogrammed-visits.component';
 import { VisitDetailsComponent } from './components/preventive-maintenance/unprogrammed-visits/visit-details/visit-details.component';
 import { NewVisitComponent } from './components/preventive-maintenance/active-orders/new-visit/new-visit.component';
 import { ProcessManagementComponent } from './components/process-management/process-management.component';
@@ -617,7 +614,6 @@ import { HelpDialogComponent } from './components/template/help-dialog/help-dial
     UnprogrammedVisitsComponent,
     VisitDetailsComponent,
     NewVisitComponent,
-    CommentsDialog,
     ProcessManagementComponent,
     BtnNavigateComponent,
     ChangeLanguageComponent,

+ 66 - 120
src/app/components/preventive-maintenance/unprogrammed-visits/unprogrammed-visits.component.html

@@ -94,147 +94,93 @@
           class="animated fadeIn"
           [style.display]="!isLoading && !hasError ? 'revert' : 'none'"
         >
-          <ng-container matColumnDef="ID">
-            <th mat-header-cell *matHeaderCellDef mat-sort-header>ID</th>
-            <td mat-cell *matCellDef="let row">{{ row.IDVISITA }}</td>
+          <ng-container matColumnDef="numeracion">
+            <th mat-header-cell *matHeaderCellDef>#</th>
+            <td mat-cell *matCellDef="let row; let i = index">
+              {{ getRowIndex(i) }}
+            </td>
           </ng-container>
 
-          <ng-container matColumnDef="EQUIP">
-            <th mat-header-cell *matHeaderCellDef mat-sort-header>
+          <ng-container matColumnDef="equipamiento">
+            <th
+              mat-header-cell
+              *matHeaderCellDef
+              mat-sort-header="TIPO_EQUIPAMIENTO"
+            >
               Equipamiento
             </th>
-            <td mat-cell *matCellDef="let row" style="width: 40%">
+            <td mat-cell *matCellDef="let row" style="width: 34%">
               {{ row.TIPO_EQUIPAMIENTO }} - {{ row.MODELO_EQUIPAMIENTO }}
             </td>
           </ng-container>
 
-          <ng-container matColumnDef="ESTA" style="width: 30%">
-            <th mat-header-cell *matHeaderCellDef mat-sort-header>Estado</th>
+          <ng-container matColumnDef="descripcion">
+            <th mat-header-cell *matHeaderCellDef mat-sort-header="DESCRIPCION">
+              Descripción
+            </th>
             <td mat-cell *matCellDef="let row">
-              {{ statusMap.get(row.ESTATUS) }}
+              {{ row.DESCRIPCION || "N/A" }}
             </td>
           </ng-container>
 
-          <ng-container matColumnDef="ACCONES">
-            <th mat-header-cell *matHeaderCellDef mat-sort-header>Acciones</th>
+          <ng-container matColumnDef="estado">
+            <th mat-header-cell *matHeaderCellDef mat-sort-header="ESTADO">
+              Estado
+            </th>
+            <td mat-cell *matCellDef="let row">
+              {{ row.ESTADO_LABEL || row.ESTADO || "N/A" }}
+            </td>
+          </ng-container>
+
+          <ng-container matColumnDef="prioridad">
+            <th mat-header-cell *matHeaderCellDef mat-sort-header="PRIORIDAD">
+              Prioridad
+            </th>
+            <td mat-cell *matCellDef="let row">
+              <span [style.color]="row.PRIORIDAD_OBJ?.color || null">
+                {{ row.PRIORIDAD_OBJ?.label || row.PRIORIDAD || "N/A" }}
+              </span>
+            </td>
+          </ng-container>
+
+          <ng-container matColumnDef="tipoActivacion">
+            <th
+              mat-header-cell
+              *matHeaderCellDef
+              mat-sort-header="TIPO_ACTIVACION"
+            >
+              Tipo activación
+            </th>
+            <td mat-cell *matCellDef="let row">
+              {{ row.TIPO_ACTIVACION_LABEL || row.TIPO_ACTIVACION || "N/A" }}
+            </td>
+          </ng-container>
+
+          <ng-container matColumnDef="fechaRegistro">
+            <th mat-header-cell *matHeaderCellDef mat-sort-header="FECREG">
+              Registro
+            </th>
+            <td mat-cell *matCellDef="let row">
+              {{ row.FECREG_FMT || row.FECREG || "N/A" }}
+            </td>
+          </ng-container>
+
+          <ng-container matColumnDef="acciones">
+            <th mat-header-cell *matHeaderCellDef>Acciones</th>
             <td mat-cell *matCellDef="let row">
-              @if (!btnSmall && row.ESTATUS == 'P') {
-              <button
-                mat-mini-fab
-                color="primary"
-                class="mr-4 override_no_shadow animated fadeIn white_font pink_primary_background"
-                matTooltip="Ver detalles"
-                (click)="openVisitDetails(row.IDVISITA)"
-              >
-                <mat-icon>visibility</mat-icon>
-              </button>
-              } @if (!btnSmall) {
-              <button
-                mat-mini-fab
-                #toolTip="matTooltip"
-                matTooltip="Aprobar visita"
-                [disabled]="row.ESTATUS == 'F' || row.ESTATUS == 'C'"
-                [ngClass]="{
-                  green_primary_background: !isLoading && row.ESTATUS == 'P',
-                  white_font: !isLoading && row.ESTATUS == 'P',
-                  hidden: row.ESTATUS == 'A' || row.ESTATUS == 'R'
-                }"
-                class="no_shadow"
-                (click)="openCommentsDialog(row.IDVISITA, 'auth')"
-              >
-                <mat-icon>done</mat-icon>
-              </button>
-              <button
-                mat-mini-fab
-                class="ml-4 no_shadow"
-                matTooltip="Rechazar visita"
-                [disabled]="row.ESTATUS == 'C' || row.ESTATUS == 'F'"
-                [ngClass]="{
-                  red_primary_background: row.ESTATUS == 'P',
-                  white_font: row.ESTATUS == 'P',
-                  hidden: row.ESTATUS == 'A' || row.ESTATUS == 'R'
-                }"
-                (click)="openCommentsDialog(row.IDVISITA, 'decline')"
-              >
-                <mat-icon>close</mat-icon>
-              </button>
-              } @if (row.ESTATUS == 'A' && !btnSmall) {
-              <button
-                mat-mini-fab
-                class="mt-4 white_font green_primary_background mr-4 override_no_shadow animated fadeIn"
-                matTooltip="Finalizar visita"
-                (click)="openCommentsDialog(row.IDVISITA, 'end')"
-              >
-                <mat-icon>check_circle</mat-icon>
-              </button>
-              <button
-                mat-mini-fab
-                class="mt-4 white_font red_primary_background mr-4 override_no_shadow animated fadeIn"
-                matTooltip="Cancelar visita"
-                (click)="openCommentsDialog(row.IDVISITA, 'cancel')"
-              >
-                <mat-icon>cancel</mat-icon>
-              </button>
-              } @if (btnSmall) {
               <button
-                mat-mini-fab
-                color="primary"
-                class="override_no_shadow animated fadeIn gray_dark_font transparent_background"
-                [matMenuTriggerFor]="menu"
+                mat-icon-button
+                class="override_no_shadow"
+                [matMenuTriggerFor]="actionsMenu"
+                matTooltip="Más opciones"
               >
                 <mat-icon>more_vert</mat-icon>
               </button>
-              }
-              <button
-                mat-mini-fab
-                class="white_font green_primary_background mr-4 override_no_shadow animated fadeIn"
-                matTooltip="Aprobar visita"
-                [ngClass]="{
-                  hidden:
-                    row.ESTATUS == 'A' ||
-                    row.ESTATUS == 'P' ||
-                    row.ESTATUS == 'C' ||
-                    row.ESTATUS == 'R' ||
-                    row.ESTATUS == 'F'
-                }"
-                (click)="openCommentsDialog(row.IDVISITA, 'cancel')"
-              ></button>
-              <mat-menu #menu>
+              <mat-menu #actionsMenu="matMenu">
                 <button mat-menu-item (click)="openVisitDetails(row.IDVISITA)">
                   <mat-icon>visibility</mat-icon>
                   <span>Ver detalles</span>
                 </button>
-                @if (row.ESTATUS == 'P') {
-                <button
-                  mat-menu-item
-                  (click)="openCommentsDialog(row.IDVISITA, 'auth')"
-                >
-                  <mat-icon>done</mat-icon>
-                  <span>Aprobar visita</span>
-                </button>
-                <button
-                  mat-menu-item
-                  (click)="openCommentsDialog(row.IDVISITA, 'decline')"
-                >
-                  <mat-icon>close</mat-icon>
-                  <span>Rechazar visita</span>
-                </button>
-                } @if (row.ESTATUS == 'A') {
-                <button
-                  mat-menu-item
-                  (click)="openCommentsDialog(row.IDVISITA, 'end')"
-                >
-                  <mat-icon>check_circle</mat-icon>
-                  <span>Finalizar visita</span>
-                </button>
-                <button
-                  mat-menu-item
-                  (click)="openCommentsDialog(row.IDVISITA, 'cancel')"
-                >
-                  <mat-icon>cancel</mat-icon>
-                  <span>Cancelar visita</span>
-                </button>
-                }
               </mat-menu>
             </td>
           </ng-container>

+ 166 - 112
src/app/components/preventive-maintenance/unprogrammed-visits/unprogrammed-visits.component.ts

@@ -1,28 +1,42 @@
 import { Component, OnInit, ViewChild } from '@angular/core';
 import { MatTableDataSource } from '@angular/material/table';
-import { UnprogrammedVisit, UnprogrammedVisitsResponse } from '../../../interfaces/unprogrammed-visits.interface';
+import {
+  UnprogrammedVisit,
+  UnprogrammedVisitsResponse,
+} from '../../../interfaces/unprogrammed-visits.interface';
 import { MatPaginator } from '@angular/material/paginator';
 import { MatSort } from '@angular/material/sort';
 import { EncService } from '../../../services/enc.service';
 import { PreventiveMaintenanceService } from '../../../services/preventive-maintenance.service';
-import { MatDialog } from '@angular/material/dialog';
-import { Router } from '@angular/router';
-import { ResourcesService } from '../../../services/resources.service';
 import { lastValueFrom } from 'rxjs';
-import { VisitDetailsComponent } from './visit-details/visit-details.component';
+import { Router } from '@angular/router';
+import { PriorityInterface } from '../../system-admin/system-params/system-params.component';
+import { SystemAdminService } from '../../../services/system-admin.service';
+import { FunctionsService } from '../../../services/functions.service';
+import { MatDialog } from '@angular/material/dialog';
+import { PreventiveOrderDetailsComponent } from '../preventive-order-details/preventive-order-details.component';
 
 @Component({
-    selector: 'app-unprogrammed-visits',
-    templateUrl: './unprogrammed-visits.component.html',
-    styleUrl: './unprogrammed-visits.component.css',
-    standalone: false
+  selector: 'app-unprogrammed-visits',
+  templateUrl: './unprogrammed-visits.component.html',
+  styleUrl: './unprogrammed-visits.component.css',
+  standalone: false,
 })
 export class UnprogrammedVisitsComponent implements OnInit {
   btnSmall: boolean;
-  txtBuscador:string;
+  txtBuscador: string;
 
   dataSource?: MatTableDataSource<UnprogrammedVisit>;
-  displayedColumns = ['ID', 'EQUIP', 'ESTA', 'ACCONES'];
+  displayedColumns = [
+    'numeracion',
+    'equipamiento',
+    'descripcion',
+    'estado',
+    'prioridad',
+    'tipoActivacion',
+    'fechaRegistro',
+    'acciones',
+  ];
 
   @ViewChild(MatPaginator) paginator?: MatPaginator;
   @ViewChild(MatSort) sort?: MatSort;
@@ -30,76 +44,150 @@ export class UnprogrammedVisitsComponent implements OnInit {
   isLoading: boolean;
   hasError: boolean;
   errorStr: string;
-  statusMap: Map<string, string>;
+  priorityMap: Map<string, PriorityInterface>;
+
+  private readonly activationTypeLabels: Record<string, string> = {
+    A: 'Automática',
+    M: 'Manual',
+  };
+
+  private readonly statusLabels: Record<string, string> = {
+    VA: 'Validado',
+    EP: 'En proceso',
+    CP: 'Cerrado pendiente',
+    CE: 'Cerrado',
+    P: 'Pendiente',
+    C: 'Cancelado',
+    R: 'Rechazado',
+    A: 'Aprobado',
+    F: 'Finalizado',
+  };
 
   constructor(
     private _encService: EncService,
     private _prevMaintService: PreventiveMaintenanceService,
-    private _dialog: MatDialog,
     private _router: Router,
-    private _resourcesService: ResourcesService,
-  ) { 
+    private _sysAdminService: SystemAdminService,
+    private _functionsService: FunctionsService,
+    private _dialog: MatDialog
+  ) {
     this.btnSmall = window.innerWidth <= 1530;
     this.txtBuscador = '';
     this.isLoading = true;
     this.hasError = false;
     this.errorStr = '';
     this.dataSource = new MatTableDataSource();
-
-    this.statusMap = new Map();
+    this.priorityMap = new Map();
   }
 
   ngOnInit(): void {
-    this.statusMap.set('P', 'Pendiente de autorización');
-    this.statusMap.set('A', 'Autorizado');
-    this.statusMap.set('F', 'Finalizado');
-    this.statusMap.set('R', 'Rechazado');
-    this.statusMap.set('C', 'Cancelado');
+    this.getOrderPriorities();
+  }
+
+  async getOrderPriorities() {
+    try {
+      let idUser = localStorage.getItem('idusuario')!;
+      let priorities = await lastValueFrom(
+        this._sysAdminService.getOrderPriorities(idUser, 1)
+      );
 
-    this.getUnprogrammedVisits();
+      if (!priorities.error) {
+        this.priorityMap.clear();
+        const prioritiesArr = priorities.response?.order_priorities as
+          | PriorityInterface[]
+          | undefined;
+
+        if (prioritiesArr?.length) {
+          for (const item of prioritiesArr) {
+            const valueKey = `${item.value}`.trim();
+            const formattedPriority: PriorityInterface = {
+              ...item,
+              value: valueKey,
+              label: `${item.label} (${item.value})`,
+            };
+            this.priorityMap.set(valueKey, formattedPriority);
+          }
+        }
+      }
+    } catch (error) {
+      console.error('Error loading order priorities', error);
+    } finally {
+      this.getUnprogrammedVisits();
+    }
   }
 
-  async getUnprogrammedVisits(){
-    try{
+  async getUnprogrammedVisits() {
+    try {
       this.isLoading = true;
       this.hasError = false;
       this.errorStr = '';
 
       let idUser = localStorage.getItem('idusuario')!;
-      let visits: UnprogrammedVisitsResponse = await lastValueFrom(this._prevMaintService.getUnprogrammedVisits(
-        idUser, 
-        1
-      ));
+      let visits: UnprogrammedVisitsResponse = await lastValueFrom(
+        this._prevMaintService.getUnprogrammedVisits(idUser, 1)
+      );
 
       this.hasError = visits.error;
       this.errorStr = visits.msg;
-
-      if(!this.hasError){
+      if (!this.hasError) {
         let visitsArr: UnprogrammedVisit[] = [];
-        for(const visit of visits.response){
+        for (const visit of visits.response) {
           visit.IDVISITA = await this._encService.decrypt(visit.IDVISITA);
 
-          let equipmentCodeDec = await this._encService.decrypt(visit.EQUIPAMIENTO);
+          let equipmentCodeDec = await this._encService.decrypt(
+            visit.EQUIPAMIENTO
+          );
           visit.TIPO_EQUIPAMIENTO = `${equipmentCodeDec} - ${visit.TIPO_EQUIPAMIENTO}`;
 
-          let equipmentIDDec = await this._encService.decrypt(visit.ID_EQUIPAMIENTO);
+          let equipmentIDDec = await this._encService.decrypt(
+            visit.ID_EQUIPAMIENTO
+          );
           visit.MODELO_EQUIPAMIENTO = `${visit.MODELO_EQUIPAMIENTO} (${equipmentIDDec})`;
 
+          if (visit.PRIORIDAD) {
+            const priorityValue = await this.decryptWithFallback(
+              visit.PRIORIDAD
+            );
+            visit.PRIORIDAD = priorityValue;
+
+            const priorityInfo = this.priorityMap.get(priorityValue.trim());
+            if (priorityInfo) {
+              visit.PRIORIDAD_OBJ = priorityInfo;
+            }
+          }
+
+          const activationKey = (visit.TIPO_ACTIVACION || '').toUpperCase();
+          visit.TIPO_ACTIVACION_LABEL =
+            this.activationTypeLabels[activationKey] ||
+            visit.TIPO_ACTIVACION ||
+            'N/A';
+
+          const statusKey = (visit.ESTADO || '').toUpperCase();
+          visit.ESTADO_LABEL =
+            this.statusLabels[statusKey] || visit.ESTADO || 'N/A';
+
+          if (visit.FECREG) {
+            visit.FECREG_FMT = this._functionsService.orderDate(visit.FECREG);
+            if (!visit.FECREG_FMT) {
+              visit.FECREG_FMT = visit.FECREG;
+            }
+          }
+
           visitsArr.push(visit);
         }
-        
+
         this.dataSource = new MatTableDataSource(visitsArr);
         this.dataSource.paginator = this.paginator!;
         this.dataSource.sort = this.sort!;
       }
 
       this.isLoading = false;
-    }catch(error: any){
-      if(error.error == undefined){
+    } catch (error: any) {
+      if (error.error == undefined) {
         this.errorStr = 'Ocurrió un error inesperado.';
-      }else if(error.error.msg == undefined){
+      } else if (error.error.msg == undefined) {
         this.errorStr = 'Ocurrió un error inesperado.';
-      }else{
+      } else {
         this.errorStr = error.error.msg;
       }
 
@@ -108,100 +196,66 @@ export class UnprogrammedVisitsComponent implements OnInit {
     }
   }
 
-  public onResize():void {
+  public onResize(): void {
     this.btnSmall = window.innerWidth <= 1530;
   }
 
-  goBack(steps: number){
+  goBack(steps: number) {
     window.history.go(steps * -1);
   }
 
-  applyFilter(event: any, type: string){
-    let filterValue:string;
-    if (type == 'INP') {
+  applyFilter(event: any, type: string) {
+    let filterValue: string;
+    if (type === 'INP') {
       filterValue = (event.target as HTMLInputElement).value;
-    }else{
+    } else {
       this.txtBuscador = event;
       filterValue = event;
     }
     this.dataSource!.filter = filterValue.trim().toLowerCase();
 
-    if(this.dataSource?.paginator){
+    if (this.dataSource?.paginator) {
       this.dataSource.paginator.firstPage();
     }
   }
 
-  registerVisit(){
-    this._router.navigate(['sam/GMPR/AOTR/RVTP/register']); 
-  }
-
-  openVisitDetails(id: number){
-    this._dialog.open(VisitDetailsComponent, {
-      width: '560px',
-      maxWidth: '560px',
-      data: {
-        idVisit: id,
-      }
-    })
+  registerVisit() {
+    this._router.navigate(['sam/GMPR/AOTR/RVTP/register']);
   }
 
-  openCommentsDialog(idVisit: number, action: string){
-    let dialogRef = this._dialog.open(CommentsDialog, {
-      width: '380px',
-      maxWidth: '380px',
-    });
-
-    dialogRef.afterClosed().subscribe(res => {
-      if(res != null && res != undefined && res != ''){
-        switch(action){
-          case 'auth':
-            this.updateVisitStatus(idVisit, res, 'A');
-          break;
-          case 'end':
-            this.updateVisitStatus(idVisit, res, 'F');
-          break;
-          case 'decline':
-            this.updateVisitStatus(idVisit, res, 'R');
-          break;
-          case 'cancel':
-            this.updateVisitStatus(idVisit, res, 'C');
-          break;
-        }
-      }
-    });
+  async openVisitDetails(idVisit: string) {
+    try {
+      const idVisitEnc = await this._encService.encrypt(idVisit);
+      this._dialog.open(PreventiveOrderDetailsComponent, {
+        width: '480px',
+        maxWidth: '480px',
+        data: {
+          idOrder: idVisitEnc,
+        },
+      });
+    } catch (error) {
+      console.error('Error opening visit details dialog', error);
+    }
   }
 
-  async updateVisitStatus(idVisit: number, comments: string, status: string){
-    try{
-      let idUser = localStorage.getItem('idusuario');
-      let idVisitEnc = await this._encService.encrypt(`${idVisit}`);
-      let formData = new FormData();
-
-      formData.append('id_user', idUser!);
-      formData.append('linea', '1');
-      formData.append('id_visit', idVisitEnc);
-      formData.append('comments', comments);
-      formData.append('status', status);
-
-      await lastValueFrom(this._prevMaintService.updateVisitStatus(formData));
+  private async decryptWithFallback(
+    value: string | null | undefined
+  ): Promise<string> {
+    if (!value) {
+      return '';
+    }
 
-      this._resourcesService.openSnackBar('La visita se actualizó correctamente.');
-      this.getUnprogrammedVisits();
-    }catch(error: any){
-      if(error.error == undefined){
-        this._resourcesService.openSnackBar('Ocurrió un error inesperado.');
-      }else if(error.error.msg == undefined){
-        this._resourcesService.openSnackBar('Ocurrió un error inesperado.');
-      }else{
-        this._resourcesService.openSnackBar(error.error.msg);
-      }
+    try {
+      const decrypted = await this._encService.decrypt(value);
+      return decrypted.trim();
+    } catch (error) {
+      return `${value}`.trim();
     }
   }
-}
 
-@Component({
-    selector: 'comments-dialog',
-    templateUrl: './comments-dialog.html',
-    standalone: false
-})
-export class CommentsDialog {}
+  getRowIndex(index: number): number {
+    const pageIndex = this.paginator?.pageIndex ?? 0;
+    const pageSize = this.paginator?.pageSize ?? 10;
+    return pageIndex * pageSize + index + 1;
+  }
+}

+ 13 - 11
src/app/interfaces/unprogrammed-visits.interface.ts

@@ -1,24 +1,26 @@
-export interface UnprogrammedVisitsResponse{
+import { PriorityInterface } from '../components/system-admin/system-params/system-params.component';
+
+export interface UnprogrammedVisitsResponse {
   error: boolean;
   msg: string;
   response: UnprogrammedVisit[];
 }
 
-export interface UnprogrammedVisit{
+export interface UnprogrammedVisit {
   IDVISITA: string;
   EQUIPAMIENTO: string;
   TIPO_EQUIPAMIENTO: string;
   MODELO_EQUIPAMIENTO: string;
   ID_EQUIPAMIENTO: string;
-  ESTATUS: string;
+  ESTADO: string;
   USRREG: string;
   FECREG: string;
-  USAURE: string | null;
-  FEAURE: string | null;
-  USRCAN: string | null;
-  FECCAN: string | null;
-  USRFIN: string | null;
-  FECFIN: string | null;
-  USRMOD: string | null;
   FECMOD: string | null;
-}
+  PRIORIDAD: string;
+  DESCRIPCION: string;
+  TIPO_ACTIVACION: string;
+  PRIORIDAD_OBJ?: PriorityInterface;
+  TIPO_ACTIVACION_LABEL?: string;
+  FECREG_FMT?: string;
+  ESTADO_LABEL?: string;
+}