Ver Fonte

subo componentes de la interfaz carga de datos

SalemRoman há 3 meses atrás
pai
commit
154a8b3889

+ 11 - 0
package-lock.json

@@ -30,6 +30,7 @@
         "@types/jszip": "^3.4.0",
         "@types/libsodium-wrappers": "^0.7.14",
         "@types/quill": "^2.0.14",
+        "@types/xlsx": "^0.0.36",
         "buffer": "^6.0.3",
         "chart.js": "^4.4.3",
         "chartjs-plugin-annotation": "^3.0.1",
@@ -6703,6 +6704,16 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/xlsx": {
+      "version": "0.0.36",
+      "resolved": "https://registry.npmjs.org/@types/xlsx/-/xlsx-0.0.36.tgz",
+      "integrity": "sha512-mvfrKiKKMErQzLMF8ElYEH21qxWCZtN59pHhWGmWCWFJStYdMWjkDSAy6mGowFxHXaXZWe5/TW7pBUiWclIVOw==",
+      "deprecated": "This is a stub types definition for xlsx (https://github.com/sheetjs/js-xlsx). xlsx provides its own type definitions, so you don't need @types/xlsx installed!",
+      "license": "MIT",
+      "dependencies": {
+        "xlsx": "*"
+      }
+    },
     "node_modules/@vitejs/plugin-basic-ssl": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.2.0.tgz",

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "@types/jszip": "^3.4.0",
     "@types/libsodium-wrappers": "^0.7.14",
     "@types/quill": "^2.0.14",
+    "@types/xlsx": "^0.0.36",
     "buffer": "^6.0.3",
     "chart.js": "^4.4.3",
     "chartjs-plugin-annotation": "^3.0.1",

+ 230 - 177
src/app/components/initial-data-upload/individual-equipment-upload/individual-upload.component.ts

@@ -22,7 +22,6 @@ export interface Document {
   fecha_carga?: string
 }
 
-
 interface SheetHeaderStructure {
   [column: string]: string;
 }
@@ -53,18 +52,19 @@ export class IndividualUploadComponent implements OnInit {
   public dataSource: MatTableDataSource<Document>;
   public displayedColumns: string[] = ['nombre_del_archivo', 'tamano_del_archivo', 'acciones'];
   
-
   public excelFile: Document | null = null;
   public excelData: any = null;
-
+  
+  // Nueva propiedad para manejar el archivo modificado
+  private modifiedExcelFile: File | null = null;
+  private hasModifiedData: boolean = false;
+private excelFileBuffer: ArrayBuffer | null = null;
 
   public isDragOver: boolean = false;
 
-
   private readonly EXCEL_EXTENSIONS = ['xlsx', 'xls'];
   private readonly MAX_FILE_SIZE_MB = 10;
-  private readonly HEADER_ROW = 7; 
-
+  private readonly HEADER_ROW = 7;
 
   private readonly TEMPLATE_STRUCTURE: TemplateStructure = {
     'CARGA DE EQUIPOS': {
@@ -82,6 +82,7 @@ export class IndividualUploadComponent implements OnInit {
       'M': 'FECHA DE ADQUSICIÓN DEL ARTÍCULO',
       'N': 'FECHA DE VENCIMIENTO DEL ARTÍCULO',
       'O': 'ETIQUETA FINAL DEL EQUIPO',
+      'P': 'CÓDIGO EQUIVALENTE'
     },
     'LRU': {
       'B': 'TIPO / DESCRIPCIÓN',
@@ -93,7 +94,8 @@ export class IndividualUploadComponent implements OnInit {
       'H': 'NO. CÓDIGO DE BARRAS',
       'I': 'CARÁCTER',
       'J': 'FECHA DE VENCIMIENTO DEL ARTÍCULO',
-      'K': 'ETIQUETA FINAL DEL EQUIPO'
+      'K': 'ETIQUETA FINAL DEL EQUIPO',
+      'L': 'CÓDIGO EQUIVALENTE'
     },
     'CASO 1': {
       'B': 'LÍNEA',
@@ -229,7 +231,6 @@ export class IndividualUploadComponent implements OnInit {
       'AF': 'CÓDIGO COMPLETO',
       'AG': 'CÓDIGO EQUIVALENTE',    
     }
-
   };
 
   private readonly CATÁLOGOS_STRUCTURE = {
@@ -249,10 +250,8 @@ export class IndividualUploadComponent implements OnInit {
       'S': 'ESTADO',
       'T': 'ACRÓNIMO'
     },
-
   };
 
-
   private readonly REQUIRED_SHEETS = ['CARGA DE EQUIPOS', 'LRU', 'CASO 1', 'CASO 2', 'CASO 3', 'CASO 4', 'CASO 5', 'CASO 6', 'CATÁLOGOS'];
 
   constructor (
@@ -280,7 +279,6 @@ export class IndividualUploadComponent implements OnInit {
     this.uploader?.nativeElement.click();
   }
 
-
   onDragOver(event: DragEvent): void {
     event.preventDefault();
     event.stopPropagation();
@@ -311,7 +309,6 @@ export class IndividualUploadComponent implements OnInit {
   }
 
   private processFile(file: File): void {
-
     if (!this.isValidExcelFile(file)) {
       return;
     }
@@ -337,7 +334,7 @@ export class IndividualUploadComponent implements OnInit {
   }
 
   private isValidFileSize(file: File): boolean {
-    const maxFileSize = this.MAX_FILE_SIZE_MB * 1024 * 1024;
+    const maxFileSize = this.MAX_FILE_SIZE_MB * 7024 * 7024;
     
     if (file.size > maxFileSize) {
       this._resourcesService.openSnackBar(`El tamaño del archivo supera el límite de ${this.MAX_FILE_SIZE_MB} MB`);
@@ -347,87 +344,73 @@ export class IndividualUploadComponent implements OnInit {
     return true;
   }
 
+  private validateExcelTemplate(workbook: XLSX.WorkBook): { isValid: boolean, errors: string[] } {
+    const errors: string[] = [];
+    
+    const existingSheets = workbook.SheetNames;
+    const missingSheets = this.REQUIRED_SHEETS.filter(sheet => !existingSheets.includes(sheet));
+    
+    if (missingSheets.length > 0) {
+      errors.push(`Tu documento no cumple con las hojas requeridas de la plantilla`);
+    }
 
-private validateExcelTemplate(workbook: XLSX.WorkBook): { isValid: boolean, errors: string[] } {
-  const errors: string[] = [];
-  
-
-  const existingSheets = workbook.SheetNames;
-  const missingSheets = this.REQUIRED_SHEETS.filter(sheet => !existingSheets.includes(sheet));
-  
-  if (missingSheets.length > 0) {
-    errors.push(`Tu documento no cumple con las hojas requeridas de la plantilla`);
-  }
-
-  for (const sheetName of this.REQUIRED_SHEETS) {
-    if (existingSheets.includes(sheetName)) {
-      const sheetErrors = this.validateSheetHeaders(workbook.Sheets[sheetName], sheetName);
-      errors.push(...sheetErrors);
-      
-
-      if (sheetName === 'CARGA DE EQUIPOS') {
-        const dataErrors = this.validateSheetHasData(workbook.Sheets[sheetName], sheetName);
-        errors.push(...dataErrors);
+    for (const sheetName of this.REQUIRED_SHEETS) {
+      if (existingSheets.includes(sheetName)) {
+        const sheetErrors = this.validateSheetHeaders(workbook.Sheets[sheetName], sheetName);
+        errors.push(...sheetErrors);
+        
+        if (sheetName === 'CARGA DE EQUIPOS') {
+          const dataErrors = this.validateSheetHasData(workbook.Sheets[sheetName], sheetName);
+          errors.push(...dataErrors);
+        }
       }
-
     }
-  }
-
-  return {
-    isValid: errors.length === 0,
-    errors: errors
-  };
-}
-
-
-private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): string[] {
-  const errors: string[] = [];
-  
-  try {
 
-    const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
-    
-    
-    const dataStartRow = this.HEADER_ROW + 2; 
-    
+    return {
+      isValid: errors.length === 0,
+      errors: errors
+    };
+  }
 
-    let hasData = false;
+  private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): string[] {
+    const errors: string[] = [];
     
-
-    if (range.e.r >= dataStartRow) {
-
-      const mainColumns = ['B', 'D', 'F', 'H']; 
+    try {
+      const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
+      const dataStartRow = this.HEADER_ROW + 2; 
+      let hasData = false;
       
-      for (let row = dataStartRow; row <= range.e.r; row++) {
-        for (const col of mainColumns) {
-          const cellAddress = `${col}${row}`;
-          const cell = worksheet[cellAddress];
-          
-          
-          if (cell && cell.v !== null && cell.v !== undefined && String(cell.v).trim() !== '') {
-            hasData = true;
-            break;
+      if (range.e.r >= dataStartRow) {
+        const mainColumns = ['B', 'D', 'F', 'H']; 
+        
+        for (let row = dataStartRow; row <= range.e.r; row++) {
+          for (const col of mainColumns) {
+            const cellAddress = `${col}${row}`;
+            const cell = worksheet[cellAddress];
+            
+            if (cell && cell.v !== null && cell.v !== undefined && String(cell.v).trim() !== '') {
+              hasData = true;
+              break;
+            }
           }
+          if (hasData) break;
         }
-        if (hasData) break;
       }
+      
+      if (!hasData) {
+        errors.push(`La hoja "${sheetName}" no contiene datos. Se requiere al menos un registro con información.`);
+      }
+      
+    } catch (error) {
+      errors.push(`Error al validar datos en la hoja "${sheetName}": No se pudo verificar el contenido.`);
     }
     
-    if (!hasData) {
-      errors.push(`La hoja "${sheetName}" no contiene datos. Se requiere al menos un registro con información.`);
-    }
-    
-  } catch (error) {
-    errors.push(`Error al validar datos en la hoja "${sheetName}": No se pudo verificar el contenido.`);
+    return errors;
   }
-  
-  return errors;
-}
 
   private validateSheetHeaders(worksheet: XLSX.WorkSheet, sheetName: string): string[] {
     const errors: string[] = [];
     
-
     if (sheetName === 'CATÁLOGOS') {
       return this.validateCatalogosSheet(worksheet);
     }
@@ -439,7 +422,6 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
       return errors;
     }
 
-
     const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
     
     for (const [column, expectedHeader] of Object.entries(expectedHeaders)) {
@@ -455,11 +437,9 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
     return errors;
   }
 
-
   private validateCatalogosSheet(worksheet: XLSX.WorkSheet): string[] {
     const errors: string[] = [];
 
-   
     for (const [column, expectedHeader] of Object.entries(this.CATÁLOGOS_STRUCTURE.row5)) {
       const cellAddress = `${column}5`;
       const cell = worksheet[cellAddress];
@@ -470,15 +450,11 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
       }
     }
 
-
-
-
     return errors;
   }
 
   async uploadExcelFile(fileData: File): Promise<void> {
     try {
-
       const validationResult = await this.validateFileStructure(fileData);
       
       if (!validationResult.isValid) {
@@ -500,14 +476,12 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
       if (response.error) {
         this._resourcesService.openSnackBar(response.msg);
       } else {
-
         this.uploadedFile = {
           id: response.response.idArchivo,
           name: fileData.name,
           size: this.formatBytes(fileData.size)
         };
 
-
         await this.readExcelFile(fileData, response.response.idArchivo);
         
         const newDocument: Document = {
@@ -518,7 +492,6 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
           fecha_carga: new Date().toLocaleString()
         };
 
-
         this.excelFile = newDocument;
         this.updateTableData();
         this._resourcesService.openSnackBar('Plantilla Excel válida y cargada exitosamente');
@@ -531,7 +504,6 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
     }
   }
 
-
   private async validateFileStructure(file: File): Promise<{ isValid: boolean, errors: string[] }> {
     return new Promise((resolve, reject) => {
       const reader = new FileReader();
@@ -551,40 +523,40 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
     });
   }
 
-  private async readExcelFile(file: File, fileId: any): Promise<void> {
-    return new Promise((resolve, reject) => {
-      const reader = new FileReader();
-      reader.onload = (e: any) => {
-        try {
-          const data = new Uint8Array(e.target.result);
-          const workbook = XLSX.read(data, { type: 'array' });
-          
-
-          const sheetNames = workbook.SheetNames;
-          const sheetsData: any = {};
-          
-          sheetNames.forEach(sheetName => {
-            const worksheet = workbook.Sheets[sheetName];
-            sheetsData[sheetName] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
-          });
-
-
-          this.excelData = {
-            fileName: file.name,
-            sheets: sheetsData,
-            sheetNames: sheetNames,
-            fileId: fileId
-          };
+private async readExcelFile(file: File, fileId: any): Promise<void> {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.onload = (e: any) => {
+      try {
+        const buffer = e.target.result;
+        this.excelFileBuffer = buffer; // Guardar el buffer
+        
+        const data = new Uint8Array(buffer);
+        const workbook = XLSX.read(data, { type: 'array' });
+        const sheetNames = workbook.SheetNames;
+        const sheetsData: any = {};
+        
+        sheetNames.forEach(sheetName => {
+          const worksheet = workbook.Sheets[sheetName];
+          sheetsData[sheetName] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
+        });
+
+        this.excelData = {
+          fileName: file.name,
+          sheets: sheetsData,
+          sheetNames: sheetNames,
+          fileId: fileId,
+          fileBuffer: buffer // Añadir buffer a los datos
+        };
 
-          resolve();
-        } catch (error) {
-          reject(error);
-        }
-      };
-      reader.onerror = reject;
-      reader.readAsArrayBuffer(file);
-    });
-  }
+        resolve();
+      } catch (error) {
+        reject(error);
+      }
+    };
+    reader.readAsArrayBuffer(file);
+  });
+}
 
   async deleteTempFile(): Promise<void> {
     if (!this.uploadedFile) return;
@@ -602,6 +574,8 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
       this.uploadedFile = null;
       this.excelFile = null;
       this.excelData = null;
+      this.modifiedExcelFile = null;
+      this.hasModifiedData = false;
       this.updateTableData();
       this._resourcesService.openSnackBar('Archivo eliminado exitosamente');
     } catch (error: any) {
@@ -626,7 +600,6 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
   }
 
   private loadExcelFile(): void {
-
     this.excelFile = null;
     this.updateTableData();
   }
@@ -641,33 +614,127 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
     }
   }
 
-  previewExcelFile(): void {
-    if (!this.excelData) {
-      this._resourcesService.openSnackBar('No hay datos del archivo Excel para mostrar');
-      return;
-    }
 
-    const dialogRef = this._matDialog.open(PreviewExcelDocumentComponent, {
+previewExcelFile(): void {
+  const dialogRef = this._matDialog.open(PreviewExcelDocumentComponent, {
       width: '1800px',
       height: '900px',
       maxWidth: '1800px',
-      data: this.excelData,
-      disableClose: true
-    });
 
-    dialogRef.afterClosed().subscribe(result => {
+    data: {
+      ...this.excelData,
+      fileBuffer: this.excelFileBuffer 
+    }
+  });
 
-      if (result) {
-        console.log('Preview cerrado:', result);
-      }
-    });
+  dialogRef.afterClosed().subscribe(result => {
+    if (result && result.hasChanges) {
+
+      this.handleModifiedExcelFile(result.modifiedFile, result.data);
+    }
+  });
+}
+
+
+private readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.onload = () => resolve(reader.result as ArrayBuffer);
+    reader.onerror = reject;
+    reader.readAsArrayBuffer(file);
+  });
+}
+
+private async handleModifiedExcelFile(modifiedFile: File, modifiedData: any): Promise<void> {
+  try {
+    this.isLoading = true;
+    
+    const newBuffer = await this.readFileAsArrayBuffer(modifiedFile);
+    
+    if (this.uploadedFile) {
+      await this.deleteTempFileOnly();
+    }
+    
+    const uploadResponse = await this.uploadModifiedFile(modifiedFile);
+    
+    if (!uploadResponse || uploadResponse.error) {
+      throw new Error(uploadResponse?.msg || 'Error al subir archivo modificado');
+    }
+    
+    this.uploadedFile = {
+      id: uploadResponse.response.idArchivo,
+      name: modifiedFile.name,
+      size: this.formatBytes(modifiedFile.size)
+    };
+    
+    this.excelData = {
+      fileName: modifiedFile.name,
+      sheets: modifiedData,
+      sheetNames: Object.keys(modifiedData),
+      fileId: uploadResponse.response.idArchivo,
+      fileBuffer: newBuffer  
+    };
+    
+    this.excelFileBuffer = newBuffer;
+    
+    if (this.excelFile) {
+      this.excelFile.nombre_del_archivo = modifiedFile.name;
+      this.excelFile.tamano_del_archivo = this.formatBytes(modifiedFile.size);
+      this.updateTableData();
+    }
+    
+    this.hasModifiedData = true;
+    this._resourcesService.openSnackBar('Archivo modificado guardado correctamente');
+    
+  } catch (error) {
+    console.error('Error al procesar archivo modificado:', error);
+    this._resourcesService.openSnackBar('Error al guardar cambios: ' + (error as Error).message);
+  } finally {
+    this.isLoading = false;
+  }
+}
+
+
+
+private async uploadModifiedFile(modifiedFile: File): Promise<any> {
+  const idUser = localStorage.getItem('idusuario');
+  const formData = new FormData();
+
+  formData.append('file', modifiedFile);
+  formData.append('id_user', idUser!);
+  formData.append('linea', '1');
+  formData.append('tipo_archivo', 'excel');
+
+  return lastValueFrom(this._gdelService.uploadTempFile(formData));
+}
+
+
+
+  /**
+   * Elimina solo el archivo temporal sin resetear los datos locales
+   */
+private async deleteTempFileOnly(): Promise<void> {
+  if (!this.uploadedFile) return;
+
+  try {
+    const idUser = localStorage.getItem('idusuario');
+    const formData = new FormData();
+
+    formData.append('id_user', idUser!);
+    formData.append('id_file', this.uploadedFile.id);
+    formData.append('linea', '1');
+
+    await lastValueFrom(this._gdelService.deleteTempFile(formData));
+  } catch (error) {
+    console.warn('Error al eliminar archivo temporal anterior:', error);
   }
+}
+
 
   removeExcelFile(): void {
     this.deleteTempFile();
   }
 
-
   get hasExcelFile(): boolean {
     return this.excelFile !== null;
   }
@@ -676,50 +743,36 @@ private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): stri
     return this.excelFile ? [this.excelFile] : [];
   }
 
+  /**
+   * Getter para saber si hay modificaciones pendientes
+   */
+  get hasModifications(): boolean {
+    return this.hasModifiedData;
+  }
 
-  async saveFinalFile(): Promise<void> {
-    if (!this.uploadedFile) {
-      this._resourcesService.openSnackBar('No hay archivo cargado para guardar');
-      return;
-    }
-
-    try {
-      this.isLoading = true;
-      let idUser = localStorage.getItem('idusuario');
-      let formData = new FormData();
-
-
-      formData.append('id_user', idUser!);
-      formData.append('id_file', this.uploadedFile.id);
-      formData.append('linea', '1');
-      
+async saveFinalFile(): Promise<void> {
+  if (!this.uploadedFile) return;
 
-      formData.append('module', 'ADSI'); 
-      formData.append('clasification', 'LA'); 
-      formData.append('has_order', 'N'); 
+  try {
+    const idUser = localStorage.getItem('idusuario');
+    const formData = new FormData();
 
-      console.log('Datos enviados al servidor:', {
-        id_user: idUser,
-        id_file: this.uploadedFile.id,
-        module: 'ADSI',
-        clasification: 'LA'
-      });
+    formData.append('id_file', this.uploadedFile.id);
+    formData.append('id_user', idUser!);
+    formData.append('linea', '1');
+    formData.append('module', 'ADSI');
+    formData.append('clasification', 'LA');
+    formData.append('has_order', 'N');
 
-      const response = await lastValueFrom(this._gdelService.saveFinalFile(formData));
-      
-      console.log('Respuesta del servidor:', response);
+    const response = await lastValueFrom(this._gdelService.saveFinalFile(formData));
+    
+    if (response.error) throw new Error(response.msg);
+    
+    this._resourcesService.openSnackBar('Archivo guardado correctamente');
+    this.dialogRef.close({ success: true });
+  } catch (error) {
+  }
+}
 
-      if (response.error) {
-        throw new Error(response.msg || 'Error al guardar el archivo');
-      }
 
-      this._resourcesService.openSnackBar('Archivo guardado correctamente');
-      this.dialogRef.close(true); 
-    } catch (error: any) {
-      console.error('Error al guardar archivo:', error);
-      this._resourcesService.openSnackBar(error.message || 'Error al guardar el archivo');
-    } finally {
-      this.isLoading = false;
-    }
-  }
 }

+ 9 - 6
src/app/components/initial-data-upload/individual-preventive-upload/individual-preventive-upload.component.ts

@@ -109,8 +109,9 @@ export class IndividualPreventiveUploadComponent implements OnInit{
       'AF': 'ESPECIALIDADES DEL OPERARIO',
       'AH': 'CANTIDAD DE OPERARIOS PREVISTOS',
       'AJ': 'ARTÍCULOS A UTILIZAR',
-      'AL': 'OBSERVACIONES',
-      'AN': 'DETALLES ADICIONALES'
+      'AL': 'INSTRUCCIONES',
+      'AN': 'OBSERVACIONES',
+      'AP': 'DETALLES ADICIONALES'
     },
     'MEDIDA': {
       'B': 'CÓDIGO EQUIVALENTE',
@@ -133,8 +134,9 @@ export class IndividualPreventiveUploadComponent implements OnInit{
       'AJ': 'ESPECIALIDADES DEL OPERARIO',
       'AL': 'CANTIDAD DE OPERARIOS PREVISTOS',
       'AN': 'ARTÍCULOS A UTILIZAR',
-      'AP': 'OBSERVACIONES',
-      'AR': 'DETALLES ADICIONALES'
+      'AP': 'INSTRUCCIONES',
+      'AR': 'OBSERVACIONES',
+      'AT': 'DETALLES ADICIONALES'
     },
     'VALOR': {
       'B': 'CÓDIGO EQUIVALENTE',
@@ -153,8 +155,9 @@ export class IndividualPreventiveUploadComponent implements OnInit{
       'AA': 'ESPECIALIDADES DEL OPERARIO',
       'AC': 'CANTIDAD DE OPERARIOS PREVISTOS',
       'AE': 'ARTÍCULOS A UTILIZAR',
-      'AG': 'OBSERVACIONES',
-      'AI': 'DETALLES ADICIONALES'
+      'AG': 'INSTRUCCIONES',
+      'AI': 'OBSERVACIONES',
+      'AK': 'DETALLES ADICIONALES'
     }
   };
 

+ 1 - 10
src/app/components/initial-data-upload/initial-data-upload.component.ts

@@ -126,19 +126,15 @@ export class InitialDataUploadComponent implements OnInit {
       this.hasError = false;
       this.errorStr = "";
 
-      console.log('Iniciando carga de submodulos...');
 
 
       const permissions = await this.getPermissionsFromStorage();
-      console.log('Permisos obtenidos:', permissions);
 
 
       const modulePermissions = this.filterModulePermissions(permissions);
-      console.log('Permisos del módulo:', modulePermissions);
 
 
       const submodulesResponse = await this.fetchSubmodulesFromService();
-      console.log('Respuesta de submodulos:', submodulesResponse);
 
       if (submodulesResponse.error) {
         this.hasError = true;
@@ -148,9 +144,7 @@ export class InitialDataUploadComponent implements OnInit {
 
 
       await this.processSubmodules(submodulesResponse.response, modulePermissions);
-      
-      console.log('Submodulos procesados:', this.submodules);
-      console.log('Submodulos auxiliares:', this.submodulesAux);
+
 
     } catch (error: any) {
       console.error('Error en getSubmodules:', error);
@@ -219,14 +213,12 @@ export class InitialDataUploadComponent implements OnInit {
 
     const modulePermission = modulePermissions[0];
     if (!modulePermission.children || modulePermission.children.length === 0) {
-      console.log('No hay permisos de submodulo, permitiendo acceso por defecto');
       return true;
     }
 
     const subPermission = modulePermission.children.find(child => child.id === submoduleId);
     const hasAccess = Boolean(subPermission && subPermission.access > 0);
     
-    console.log(`Permiso para submodulo ${submoduleId}:`, hasAccess);
     return hasAccess;
   }
 
@@ -259,7 +251,6 @@ export class InitialDataUploadComponent implements OnInit {
     // Navegar directamente al submodulo
     this._router.navigate([`sam/initial-data-upload/${idSub}`]);
     
-    console.log(`Navegando a submodulo: ${submoduleId} -> sam/initial-data-upload/${idSub}`);
   }
 
 }

+ 10 - 7
src/app/components/initial-data-upload/multiple-preventive-upload/multiple-preventive-upload.component.ts

@@ -67,7 +67,7 @@ export class MultiplePreventiveUploadComponent {
 
 
   private readonly TEMPLATE_STRUCTURE: TemplateStructure = {
-    'CALENDARIO': {
+       'CALENDARIO': {
       'B': 'CÓDIGO EQUIVALENTE',
       'D': 'CÓDIGO DEL EQUIPO',
       'F': 'CÓDIGO DEL LRU',
@@ -108,8 +108,9 @@ export class MultiplePreventiveUploadComponent {
       'AF': 'ESPECIALIDADES DEL OPERARIO',
       'AH': 'CANTIDAD DE OPERARIOS PREVISTOS',
       'AJ': 'ARTÍCULOS A UTILIZAR',
-      'AL': 'OBSERVACIONES',
-      'AN': 'DETALLES ADICIONALES'
+      'AL': 'INSTRUCCIONES',
+      'AN': 'OBSERVACIONES',
+      'AP': 'DETALLES ADICIONALES'
     },
     'MEDIDA': {
       'B': 'CÓDIGO EQUIVALENTE',
@@ -132,8 +133,9 @@ export class MultiplePreventiveUploadComponent {
       'AJ': 'ESPECIALIDADES DEL OPERARIO',
       'AL': 'CANTIDAD DE OPERARIOS PREVISTOS',
       'AN': 'ARTÍCULOS A UTILIZAR',
-      'AP': 'OBSERVACIONES',
-      'AR': 'DETALLES ADICIONALES'
+      'AP': 'INSTRUCCIONES',
+      'AR': 'OBSERVACIONES',
+      'AT': 'DETALLES ADICIONALES'
     },
     'VALOR': {
       'B': 'CÓDIGO EQUIVALENTE',
@@ -152,8 +154,9 @@ export class MultiplePreventiveUploadComponent {
       'AA': 'ESPECIALIDADES DEL OPERARIO',
       'AC': 'CANTIDAD DE OPERARIOS PREVISTOS',
       'AE': 'ARTÍCULOS A UTILIZAR',
-      'AG': 'OBSERVACIONES',
-      'AI': 'DETALLES ADICIONALES'
+      'AG': 'INSTRUCCIONES',
+      'AI': 'OBSERVACIONES',
+      'AK': 'DETALLES ADICIONALES'
     }
   };
 

+ 17 - 12
src/app/components/initial-data-upload/preview-Equipmentexcel-document/preview-excel-document.component.html

@@ -13,7 +13,7 @@
     <div class="header-controls" style="display: flex; gap: 16px; width: 100%; justify-content: end; align-items: center;">
       <div class="docu">
         <h2 mat-dialog-title style="margin: 0; font-size: 18px; display: flex; align-items: center; gap: 8px; color: #424242; margin-bottom: 13px;" >
-          <mat-icon style="color: #19d241;">table_chart</mat-icon>
+          <mat-icon style="color: #17752b;">table_chart</mat-icon>
           {{ fileName }}
         </h2>
       </div>
@@ -38,8 +38,6 @@
                 [ngClass]="editMode ? 'pink_primary_background white_font' : 'orange_primary_background white_font'">
           <mat-icon>{{ editMode ? 'visibility' : 'edit' }}</mat-icon>
         </button>
-
-
         
         <ng-container *ngIf="editMode">
           <button mat-icon-button 
@@ -65,7 +63,7 @@
                   color="primary"
                   style="margin-left: 8px; border-radius: 8px;">
             <mat-icon>save</mat-icon>
-            Guardar
+            Guardar Cambios
           </button>
           
           <button mat-stroked-button 
@@ -75,7 +73,6 @@
             <mat-icon>undo</mat-icon>
             Descartar
           </button>
-
         </ng-container>
       </div>
     </div>
@@ -133,25 +130,22 @@
             </div>
           </td>
           
-
           <td *ngFor="let cell of row; let colIndex = index; let isLastCol = last; trackBy: trackByIndex" 
               class="data-cell" 
               [style.border-radius]="isLast && isLastCol ? '0 0 8px 0' : '0'"
               style="min-width: 140px; max-width: 250px; padding: 12px 16px; border-right: 1px solid #dee2e6; vertical-align: top; position: relative;">
             
-
             <div *ngIf="!editMode" 
                  class="cell-content"
                  style="min-height: 20px; white-space: pre-wrap; word-wrap: break-word; font-family: 'Roboto', sans-serif; color: #424242; line-height: 1.4;">
               {{ getCellValue(row, colIndex) }}
             </div>
 
-
             <input *ngIf="editMode"
                    type="text"
                    class="cell-input"
                    [value]="getCellValue(row, colIndex)"
-                   (input)="onCellInput(rowIndex, colIndex, $event)"
+                   (input)="onCellChange(rowIndex, colIndex, $event)"
                    (blur)="onCellBlur(rowIndex, colIndex, $event)"
                    [attr.data-row]="rowIndex"
                    [attr.data-col]="colIndex"
@@ -179,7 +173,7 @@
     </div>
   </div>
 
-  <div class="table-summary">
+  <div class="table-summary" style="padding: 16px; border-top: 1px solid #e0e0e0; background: #f8f9fa; display: flex; justify-content: space-between; align-items: center;">
     <div class="summary-info" style="display: flex; gap: 24px;">
       <div style="display: flex; align-items: center; gap: 6px;">
         <mat-hint style="color: #1976d2;">INFORMACIÓN:</mat-hint>
@@ -215,6 +209,7 @@
     <div class="status-info" style="margin-right: auto; display: flex; align-items: center; gap: 8px; font-size: 13px; color: #6c757d;">
       <mat-icon style="font-size: 16px; color: #1976d2;">info</mat-icon>
       <span>{{ selectedSheet }} • {{ editMode ? 'Modo edición' : 'Solo lectura' }}</span>
+      <span *ngIf="hasUnsavedChanges" style="color: #ff9800; margin-left: 8px;">• Cambios pendientes</span>
     </div>
     
     <div class="action-buttons" style="display: flex; gap: 12px;">
@@ -226,10 +221,20 @@
      
       <button mat-stroked-button
               (click)="ValidateExcel()"
-              class="white_font blue_send_background">
+              class="white_font blue_send_background"
+              [disabled]="hasUnsavedChanges"
+              [matTooltip]="hasUnsavedChanges ? 'Guarde los cambios antes de validar' : 'Validar documento'">
         <mat-icon>check</mat-icon>
         Validar documento
       </button>
+
+      <button mat-raised-button
+              (click)="saveChanges()"
+              color="primary"
+              *ngIf="hasUnsavedChanges || !editMode">
+        <mat-icon>save</mat-icon>
+        Aplicar y Cerrar
+      </button>
     </div>
   </mat-dialog-actions>
-</div>
+</div>

+ 405 - 85
src/app/components/initial-data-upload/preview-Equipmentexcel-document/preview-excel-document.component.ts

@@ -1,16 +1,15 @@
 import { Component, Inject, OnInit, ChangeDetectorRef } from '@angular/core';
 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import * as XLSX from 'xlsx';
 
-interface IDColumns 
-{
+
+interface IDColumns {
   sheet: string,
   column1: string,
   column2?: string,
   row: number,
 }
 
-
-
 @Component({
   selector: 'app-preview-excel-document',
   standalone: false,
@@ -24,11 +23,16 @@ export class PreviewExcelDocumentComponent implements OnInit {
   public isLoading: boolean = false;
   public fileName: string = '';
   public currentSheetData: any[][] = [];
-  public editMode: boolean = false;
+  public editMode: boolean = false; 
   public hasUnsavedChanges: boolean = false;
   public originalData: any = {};
+  
+  // Nuevas propiedades para manejar el archivo Excel
+  private originalWorkbook: XLSX.WorkBook | null = null;
+  private modifiedWorkbook: XLSX.WorkBook | null = null;
+  private originalFile: File | null = null;
 
-  public IDcols: IDColumns[]= [
+  public IDcols: IDColumns[] = [
     {sheet: 'CARGA DE EQUIPOS', column1: 'F', row: 2},
     {sheet: 'LRU', column1: 'F', row: 9},
     {sheet: 'CASO 1', column1: 'Z', column2: 'AF', row: 9},   
@@ -37,7 +41,7 @@ export class PreviewExcelDocumentComponent implements OnInit {
     {sheet: 'CASO 4', column1: 'AP', column2: 'AV', row: 8}, 
     {sheet: 'CASO 5', column1: 'X', column2: 'AD', row: 8}, 
     {sheet: 'CASO 6', column1: 'X', column2: 'AD', row: 8},
-  ]
+  ];
 
   constructor(
     public dialogRef: MatDialogRef<PreviewExcelDocumentComponent>,
@@ -51,27 +55,159 @@ export class PreviewExcelDocumentComponent implements OnInit {
       this.sheetNames = this.data.sheetNames;
       this.selectedSheet = this.sheetNames[0] || '';
       
+      // Guardar los datos originales para poder revertir cambios
       this.originalData = JSON.parse(JSON.stringify(this.data.sheets));
       
-      console.log('Datos originales:', this.data.sheets[this.selectedSheet]);
+      // Crear el workbook inicial si no existe
+      this.initializeWorkbook();
       
       this.loadSheetData();
+    }
+  }
+
+ // preview-excel-document.component.ts
+
+private cloneWorkbook(workbook: XLSX.WorkBook): XLSX.WorkBook {
+    const newWorkbook: XLSX.WorkBook = {
+      SheetNames: [...workbook.SheetNames],
+      Sheets: {}
+    };
+
+    workbook.SheetNames.forEach(sheetName => {
+      const originalSheet = workbook.Sheets[sheetName];
+      const newSheet: XLSX.WorkSheet = {};
+      
+      // Copiar propiedades especiales
+      if (originalSheet['!ref']) newSheet['!ref'] = originalSheet['!ref'];
+      if (originalSheet['!margins']) newSheet['!margins'] = {...originalSheet['!margins']};
+      
+      // Copiar todas las celdas
+      Object.keys(originalSheet).forEach(key => {
+        if (key === '!ref' || key === '!margins') return;
+        
+        const cell = originalSheet[key];
+        if (cell) {
+          // Clonar celda preservando todas las propiedades
+          newSheet[key] = {
+            ...cell,
+            s: cell.s ? {...cell.s} : undefined,
+            z: cell.z ? cell.z : undefined
+          };
+        }
+      });
+      
+      // Copiar metadatos de formato
+      if (originalSheet['!cols']) {
+        newSheet['!cols'] = originalSheet['!cols'].map((col: any) => 
+          col ? {...col} : undefined
+        );
+      }
+      
+      if (originalSheet['!rows']) {
+        newSheet['!rows'] = originalSheet['!rows'].map((row: any) => 
+          row ? {...row} : undefined
+        );
+      }
+      
+      if (originalSheet['!merges']) {
+        newSheet['!merges'] = [...originalSheet['!merges']];
+      }
       
-      console.log('Datos procesados:', this.currentSheetData);
+      if (originalSheet['!pageSetup']) {
+        newSheet['!pageSetup'] = {...originalSheet['!pageSetup']};
+      }
+
+      newWorkbook.Sheets[sheetName] = newSheet;
+    });
+
+    return newWorkbook;
+  }
+
+  /**
+   * Inicializa el workbook desde los datos existentes
+   */
+  private initializeWorkbook(): void {
+    try {
+      if (this.data.fileBuffer) {
+        // Leer workbook desde buffer
+        this.originalWorkbook = XLSX.read(this.data.fileBuffer, { 
+          type: 'array',
+          cellStyles: true,
+          cellDates: true
+        });
+        
+        // Clonar workbook para preservar todos los estilos
+        this.modifiedWorkbook = this.cloneWorkbook(this.originalWorkbook);
+      } else {
+        // Inicialización alternativa sin buffer
+        this.originalWorkbook = XLSX.utils.book_new();
+        this.modifiedWorkbook = XLSX.utils.book_new();
+        
+        this.sheetNames.forEach(sheetName => {
+          const sheetData = this.data.sheets[sheetName] || [];
+          const worksheet = XLSX.utils.aoa_to_sheet(sheetData);
+          
+          XLSX.utils.book_append_sheet(this.originalWorkbook!, worksheet, sheetName);
+          XLSX.utils.book_append_sheet(this.modifiedWorkbook!, XLSX.utils.aoa_to_sheet(sheetData), sheetName);
+        });
+      }
+    } catch (error) {
+      console.error('Error al inicializar workbooks:', error);
     }
   }
-  
 
+  /**
+   * Carga los datos de la hoja seleccionada
+   */
   private loadSheetData(): void {
-    if (this.data?.sheets && this.selectedSheet) {
-      this.currentSheetData = this.data.sheets[this.selectedSheet] || [];
-      
-      this.processAndFormatData();
-      this.normalizeTableData();
+    if (!this.modifiedWorkbook || !this.selectedSheet) return;
+
+    const worksheet = this.modifiedWorkbook.Sheets[this.selectedSheet];
+    if (!worksheet) return;
+
+    // Obtener datos como matriz preservando estilos
+    this.currentSheetData = this.getSheetDataAsMatrix(worksheet);
+    this.cdr.detectChanges();
+  }
+
+  private getSheetDataAsMatrix(worksheet: XLSX.WorkSheet): any[][] {
+    if (!worksheet['!ref']) return [[]];
+    
+    const range = XLSX.utils.decode_range(worksheet['!ref']);
+    const data: any[][] = [];
+    
+    for (let rowNum = range.s.r; rowNum <= range.e.r; rowNum++) {
+      const row: any[] = [];
+      for (let colNum = range.s.c; colNum <= range.e.c; colNum++) {
+        const cellAddress = XLSX.utils.encode_cell({ r: rowNum, c: colNum });
+        const cell = worksheet[cellAddress];
+        
+        // Usar valor formateado (w) si está disponible, de lo contrario el valor (v)
+        let value = cell ? (cell.w !== undefined ? cell.w : cell.v) : '';
+        
+        // Formatear fechas
+        if (cell && cell.t === 'd' && value instanceof Date) {
+          value = this.formatDate(value);
+        }
+        
+        row.push(value);
+      }
+      data.push(row);
     }
+    
+    return data;
   }
 
+    private formatDate(date: Date): string {
+    const day = date.getDate().toString().padStart(2, '0');
+    const month = (date.getMonth() + 1).toString().padStart(2, '0');
+    const year = date.getFullYear();
+    return `${day}/${month}/${year}`;
+  }
 
+  /**
+   * Procesa y formatea los datos para mostrar en la tabla
+   */
   private processAndFormatData(): void {
     this.currentSheetData = this.currentSheetData.map((row, rowIndex) => 
       row.map((cell, colIndex) => {
@@ -90,18 +226,237 @@ export class PreviewExcelDocumentComponent implements OnInit {
     );
   }
 
-    public validateIDs (index: any, ids: IDColumns) {
-      this.data = this.IDcols;
+  /**
+   * Actualiza tanto los datos de visualización como el workbook modificado
+   */
+  private updateWorkbookData(rowIndex: number, colIndex: number, newValue: any): void {
+    try {
+      if (!this.modifiedWorkbook || !this.selectedSheet) return;
+      
+      const worksheet = this.modifiedWorkbook.Sheets[this.selectedSheet];
+      if (!worksheet) return;
+      
+      const cellAddress = XLSX.utils.encode_cell({ r: rowIndex, c: colIndex });
+      const originalCell = worksheet[cellAddress];
+      
+      // Procesar el valor para Excel
+      let processedValue = this.preprocessValueForExcel(newValue, rowIndex, colIndex);
+      
+      if (processedValue === '' || processedValue === null || processedValue === undefined) {
+        // Eliminar la celda si está vacía
+        delete worksheet[cellAddress];
+      } else {
+        // Crear o actualizar la celda
+        worksheet[cellAddress] = {
+          ...(originalCell || {}),
+          v: processedValue,
+          t: this.getExcelCellType(processedValue),
+          w: undefined // Se regenerará al guardar
+        };
+      }
+      
+      // Actualizar el rango de la hoja si es necesario
+      this.updateSheetRange(worksheet, rowIndex, colIndex);
+      
+      // Volver a cargar los datos para reflejar cambios
+      this.loadSheetData();
+      
+    } catch (error) {
+      console.error('Error al actualizar workbook:', error);
+    }
+  }
+
 
-      try {
-        this.selectedSheet && this.sheetNames
-      } catch(e: any){
-        console.log('Los ids de tus hojas son incorrectos, verificalos de nuevo')
+ // preview-excel-document.component.ts
+  private preprocessValueForExcel(value: any, rowIndex: number, colIndex: number): any {
+    if (value === null || value === undefined || value === '') {
+      return '';
+    }
+    
+    // Manejo de fechas
+    const isDate = this.isDateColumn(colIndex);
+    const worksheet = this.modifiedWorkbook?.Sheets[this.selectedSheet];
+    const cellAddress = XLSX.utils.encode_cell({ r: rowIndex, c: colIndex });
+    const originalCell = worksheet?.[cellAddress];
+    
+    if (isDate && typeof value === 'string' && rowIndex >= 7) {
+      const dateMatch = value.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
+      if (dateMatch) {
+        const [, day, month, year] = dateMatch;
+        const jsDate = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
+        const excelDate = this.jsDateToExcelDate(jsDate);
+        
+        // Preservar formato numérico si existe
+        return {
+          v: excelDate,
+          t: 'n',
+          z: originalCell?.z // Mantener formato original
+        };
       }
     }
+    
+    // Mantener números como números
+    if (typeof value === 'string' && !isNaN(Number(value)) && value.trim() !== '') {
+      return Number(value);
+    }
+    
+    return value;
+  }
 
-  private isDateColumn(colIndex: number): boolean {
+  /**
+   * Convierte una fecha JavaScript a número de Excel
+   */
+  private jsDateToExcelDate(jsDate: Date): number {
+    const excelEpoch = new Date(1899, 11, 30);
+    const timeDiff = jsDate.getTime() - excelEpoch.getTime();
+    return Math.floor(timeDiff / (24 * 60 * 60 * 1000));
+  }
+
+  /**
+   * Determina el tipo de celda para Excel
+   */
+  private getExcelCellType(value: any): string {
+    if (typeof value === 'number') {
+      return 'n';
+    } else if (typeof value === 'boolean') {
+      return 'b';
+    } else if (value instanceof Date) {
+      return 'd';
+    } else {
+      return 's';
+    }
+  }
+
+  /**
+   * Actualiza el rango de la hoja si es necesario
+   */
+  private updateSheetRange(worksheet: XLSX.WorkSheet, rowIndex: number, colIndex: number): void {
+    const currentRange = worksheet['!ref'];
+    if (!currentRange) {
+      worksheet['!ref'] = XLSX.utils.encode_range({
+        s: { r: 0, c: 0 },
+        e: { r: rowIndex, c: colIndex }
+      });
+      return;
+    }
+    
+    const range = XLSX.utils.decode_range(currentRange);
+    let needsUpdate = false;
+    
+    if (rowIndex > range.e.r) {
+      range.e.r = rowIndex;
+      needsUpdate = true;
+    }
+    
+    if (colIndex > range.e.c) {
+      range.e.c = colIndex;
+      needsUpdate = true;
+    }
+    
+    if (needsUpdate) {
+      worksheet['!ref'] = XLSX.utils.encode_range(range);
+    }
+  }
+
+  /**
+   * Genera un archivo Excel modificado
+   */
+// preview-excel-document.component.ts
+
+  public generateModifiedExcelFile(): File | null {
+    try {
+      if (!this.modifiedWorkbook) return null;
+      
+      // Escribir con opciones para mantener estilos
+      const excelBuffer = XLSX.write(this.modifiedWorkbook, {
+        bookType: 'xlsx',
+        type: 'array',
+        bookSST: true,
+        cellStyles: true
+      });
+      
+      const blob = new Blob([excelBuffer], {
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+      });
+      
+      const originalName = this.fileName.replace(/\.[^/.]+$/, "");
+      const modifiedFileName = `${originalName}_modificado.xlsx`;
+      
+      return new File([blob], modifiedFileName, {
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+      });
+      
+    } catch (error) {
+      console.error('Error al generar archivo modificado:', error);
+      return null;
+    }
+  }
+
+  /**
+   * Método para obtener el archivo modificado (para usar desde el componente padre)
+   */
+  public getModifiedExcelFile(): File | null {
+    return this.generateModifiedExcelFile();
+  }
 
+  // Mantener los métodos existentes con modificaciones para actualizar el workbook
+
+  onCellChange(rowIndex: number, colIndex: number, event: any): void {
+    const newValue = event.target.value;
+    
+    if (!this.currentSheetData[rowIndex]) {
+      this.currentSheetData[rowIndex] = [];
+    }
+    
+    this.currentSheetData[rowIndex][colIndex] = newValue;
+    
+    // Actualizar el workbook modificado
+    this.updateWorkbookData(rowIndex, colIndex, newValue);
+    
+    // Actualizar los datos originales para mantener sincronización
+    if (!this.data.sheets[this.selectedSheet]) {
+      this.data.sheets[this.selectedSheet] = [];
+    }
+    
+    if (!this.data.sheets[this.selectedSheet][rowIndex]) {
+      this.data.sheets[this.selectedSheet][rowIndex] = [];
+    }
+    
+    this.data.sheets[this.selectedSheet][rowIndex][colIndex] = newValue;
+    
+    this.hasUnsavedChanges = true;
+  }
+
+  onCellBlur(rowIndex: number, colIndex: number, event: any): void {
+    const newValue = event.target.value;
+    
+    // Actualizar el workbook cuando se pierde el foco
+    this.updateWorkbookData(rowIndex, colIndex, newValue);
+    
+    if (!this.data.sheets[this.selectedSheet]) {
+      this.data.sheets[this.selectedSheet] = [];
+    }
+    
+    if (!this.data.sheets[this.selectedSheet][rowIndex]) {
+      this.data.sheets[this.selectedSheet][rowIndex] = [];
+    }
+    
+    this.data.sheets[this.selectedSheet][rowIndex][colIndex] = newValue;
+  }
+
+  // Resto de métodos existentes sin cambios...
+  
+  public validateIDs(index: any, ids: IDColumns) {
+    this.data = this.IDcols;
+
+    try {
+      this.selectedSheet && this.sheetNames
+    } catch(e: any){
+      console.log('Los ids de tus hojas son incorrectos, verificalos de nuevo')
+    }
+  }
+
+  private isDateColumn(colIndex: number): boolean {
     if (this.currentSheetData.length <= 7) {
       return false;
     }
@@ -140,7 +495,6 @@ export class PreviewExcelDocumentComponent implements OnInit {
   }
 
   private isExcelDate(value: number): boolean {
-
     if (value < 1 || value > 80000) {
       return false;
     }
@@ -153,7 +507,6 @@ export class PreviewExcelDocumentComponent implements OnInit {
       return true;
     }
     
-
     if (!Number.isInteger(value) && value > 25000 && value < 80000) {
       return true;
     }
@@ -161,26 +514,21 @@ export class PreviewExcelDocumentComponent implements OnInit {
     return false;
   }
 
-
   private excelDateToJSDate(excelDate: number): string {
     try {
-
       const excelEpoch = new Date(1899, 11, 30); 
       const jsDate = new Date(excelEpoch.getTime() + (excelDate * 24 * 60 * 60 * 1000));
       
-     
       if (isNaN(jsDate.getTime())) {
         return excelDate.toString(); 
       }
       
-
       const day = jsDate.getDate().toString().padStart(2, '0');
       const month = (jsDate.getMonth() + 1).toString().padStart(2, '0');
       const year = jsDate.getFullYear();
       
       return `${day}/${month}/${year}`;
     } catch (error) {
-
       return excelDate.toString();
     }
   }
@@ -216,36 +564,9 @@ export class PreviewExcelDocumentComponent implements OnInit {
     this.cdr.detectChanges();
   }
 
-
-  onCellChange(rowIndex: number, colIndex: number, event: any): void {
-    const newValue = event.target.value;
-    
-    if (!this.currentSheetData[rowIndex]) {
-      this.currentSheetData[rowIndex] = [];
-    }
-    
-    this.currentSheetData[rowIndex][colIndex] = newValue;
-    
- 
-    if (!this.data.sheets[this.selectedSheet]) {
-      this.data.sheets[this.selectedSheet] = [];
-    }
-    
-
-    if (!this.data.sheets[this.selectedSheet][rowIndex]) {
-      this.data.sheets[this.selectedSheet][rowIndex] = [];
-    }
-    
-    this.data.sheets[this.selectedSheet][rowIndex][colIndex] = newValue;
-    
-    this.hasUnsavedChanges = true;
-  }
-
-
   onCellInput(rowIndex: number, colIndex: number, event: any): void {
     const newValue = event.target.value;
     
-
     if (!this.currentSheetData[rowIndex]) {
       this.currentSheetData[rowIndex] = [];
     }
@@ -254,43 +575,37 @@ export class PreviewExcelDocumentComponent implements OnInit {
     this.hasUnsavedChanges = true;
   }
 
-
-  onCellBlur(rowIndex: number, colIndex: number, event: any): void {
-    const newValue = event.target.value;
-    
-
-    if (!this.data.sheets[this.selectedSheet]) {
-      this.data.sheets[this.selectedSheet] = [];
-    }
-    
-    if (!this.data.sheets[this.selectedSheet][rowIndex]) {
-      this.data.sheets[this.selectedSheet][rowIndex] = [];
-    }
-    
-    this.data.sheets[this.selectedSheet][rowIndex][colIndex] = newValue;
-  }
-
   toggleEditMode(): void {
     this.editMode = !this.editMode;
     
     if (!this.editMode && this.hasUnsavedChanges) {
-      console.log('Saliendo del modo edición. Datos modificados:', this.data.sheets[this.selectedSheet]);
+      console.log('Saliendo del modo edición. Workbook modificado disponible.');
     }
   }
 
-  saveChanges(): void {
-    console.log('Guardando cambios para la hoja:', this.selectedSheet);
-    console.log('Datos modificados:', this.data.sheets[this.selectedSheet]);
-    
-    this.originalData = JSON.parse(JSON.stringify(this.data.sheets));
-    this.hasUnsavedChanges = false;
-    
-    alert('Cambios guardados correctamente');
-  }
+saveChanges(): void {
+  const modifiedFile = this.generateModifiedExcelFile();
+  
+  this.dialogRef.close({
+    hasChanges: true,
+    modifiedFile: modifiedFile, // Archivo modificado
+    data: this.data.sheets
+  });
+}
+
+private cloneCellStyle(originalCell: XLSX.CellObject): any {
+  if (!originalCell) return {};
+  
+  return {
+    s: originalCell.s ? {...originalCell.s} : undefined,
+    // Copiar otras propiedades de estilo si existen
+  };
+}
 
   discardChanges(): void {
     if (confirm('¿Estás seguro de que deseas descartar todos los cambios?')) {
       this.data.sheets = JSON.parse(JSON.stringify(this.originalData));
+      this.initializeWorkbook(); // Reinicializar workbooks
       this.loadSheetData();
       this.hasUnsavedChanges = false;
       this.editMode = false;
@@ -378,7 +693,11 @@ export class PreviewExcelDocumentComponent implements OnInit {
   }
 
   ValidateExcel(): void {
-    console.log('Exportando Excel con datos:', this.data.sheets);
+    console.log('Validando Excel con datos:', this.data.sheets);
+    const modifiedFile = this.generateModifiedExcelFile();
+    if (modifiedFile) {
+      console.log('Archivo Excel para validación:', modifiedFile);
+    }
   }
 
   closeDialog(): void {
@@ -388,9 +707,11 @@ export class PreviewExcelDocumentComponent implements OnInit {
       }
     }
     
+    // Devolver tanto los datos como el archivo modificado
     this.dialogRef.close({
       hasChanges: this.hasUnsavedChanges,
-      data: this.data.sheets
+      data: this.data.sheets,
+      modifiedFile: this.hasUnsavedChanges ? this.generateModifiedExcelFile() : null
     });
   }
 
@@ -403,7 +724,6 @@ export class PreviewExcelDocumentComponent implements OnInit {
     return index + 1;
   }
 
- 
   trackByIndex(index: number, item: any): number {
     return index;
   }

+ 398 - 375
src/app/components/initial-data-upload/preview-file-content/preview-file-content.component.ts

@@ -1,13 +1,15 @@
 import { Component, Inject, OnInit } from '@angular/core';
 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
 import { MatTableDataSource } from '@angular/material/table';
+import { GdelService } from '../../../services/gdel.service';
+import { ResourcesService } from '../../../services/resources.service';
 
 export interface ExcelSheetData {
   fileName: string;
   sheets: any;
   sheetNames: string[];
   fileId: string;
- excelData?: ExcelSheetData; 
+  originalFile?: File; // Archivo original para validación
 }
 
 export interface ZipContentData {
@@ -21,7 +23,8 @@ export interface ZipContentData {
     totalSize: number;
   };
   fileId: string;
-  excelData?: ExcelSheetData; 
+  originalFile?: File; // Archivo original para validación
+  excelData?: ExcelSheetData;
 }
 
 export interface ZipFileEntry {
@@ -42,7 +45,7 @@ export interface FileItem {
   level: number;
   parent?: string;
   fullPath: string;
-  isValidated?: boolean; 
+  isValidated?: boolean;
 }
 
 export interface ValidationResult {
@@ -55,6 +58,11 @@ export interface ValidationResult {
     foundCount: number;
     matchedCount: number;
   };
+  tempFiles?: {
+    excel: string;
+    zip: string;
+  };
+  individualTempFiles?: any[];
 }
 
 export interface ExcelDocumentEntry {
@@ -82,7 +90,7 @@ export class PreviewFileContentComponent implements OnInit {
   public zipDisplayedColumns: string[] = ['icon', 'name', 'size', 'type', 'status'];
   public totalFiles: number = 0;
   public totalSize: string = '';
-  
+
   public searchTerm: string = '';
   public expandedFolders: Set<string> = new Set();
   
@@ -91,13 +99,29 @@ export class PreviewFileContentComponent implements OnInit {
   public isValidating: boolean = false;
   public showValidationSummary: boolean = false;
   public expectedDocuments: ExcelDocumentEntry[] = [];
+  public isUploading: boolean = false;
+  public validationStep: string = '';
 
   constructor(
     public dialogRef: MatDialogRef<PreviewFileContentComponent>,
-    @Inject(MAT_DIALOG_DATA) public data: ExcelSheetData | ZipContentData
+    @Inject(MAT_DIALOG_DATA) public data: ExcelSheetData | ZipContentData,
+    private _gdelService: GdelService,
+    private _resourcesService: ResourcesService,
   ) {}
 
-
+  ngOnInit(): void {
+    console.log('Inicializando PreviewFileContentComponent');
+    console.log('Datos recibidos:', this.data);
+    
+    this.initializeComponent();
+    
+    if (this.fileType === 'zip' && this.expectedDocuments.length > 0) {
+      console.log(`Se encontraron ${this.expectedDocuments.length} documentos esperados en Excel`);
+      this.expectedDocuments.forEach((doc, index) => {
+        console.log(`   ${index + 1}. ${doc.documentName} (${doc.category})`);
+      });
+    }
+  }
 
   private initializeComponent(): void {
     if ('sheets' in this.data) {
@@ -118,11 +142,238 @@ export class PreviewFileContentComponent implements OnInit {
     }
   }
 
+  private loadZipContent(): void {
+    if (!this.data || !('zipContent' in this.data)) return;
+    
+    const zipData = this.data.zipContent;
+    this.totalFiles = zipData.totalFiles;
+    this.totalSize = this.formatBytes(zipData.totalSize);
+    
+    // Procesar archivos del ZIP
+    this.allZipFiles = this.processZipFiles(zipData.files);
+    this.updateVisibleFiles();
+  }
+
+ private extractDocumentNamesFromExcel(): ExcelDocumentEntry[] {
+  // CORRECCIÓN: Verificar que sea ZipContentData antes de acceder a excelData
+  if (!('zipContent' in this.data) || !this.data.excelData || !this.data.excelData.sheets || !this.data.excelData.sheets['CARGA DE DOCUMENTOS']) {
+    console.warn('No se encontraron datos del Excel o la hoja "CARGA DE DOCUMENTOS"');
+    return [];
+  }
+
+  const documentEntries: ExcelDocumentEntry[] = [];
+  const sheet = this.data.excelData.sheets['CARGA DE DOCUMENTOS'];
+  
+  // ... resto del método permanece igual
+  
+  // Definir las columnas de documentos y sus categorías
+  const DOCUMENT_COLUMNS = [
+    { letter: 'H', index: 7, category: 'FICHA DE SEGURIDAD DE PRODUCTOS QUÍMICOS' },
+    { letter: 'J', index: 9, category: 'FICHA TÉCNICA' },
+    { letter: 'L', index: 11, category: 'FOTOGRAFÍAS - DIAGRAMAS' },
+    { letter: 'N', index: 13, category: 'DATOS DEL PROVEEDOR' },
+    { letter: 'P', index: 15, category: 'MANUALES DE OPERACIÓN' },
+    { letter: 'R', index: 17, category: 'MANUALES DE MANTENIMIENTO PREVENTIVO' },
+    { letter: 'T', index: 19, category: 'MANUALES DE MANTENIMIENTO CORRECTIVO' },
+    { letter: 'V', index: 21, category: 'DOCUMENTOS ADICIONALES' }
+  ];
+
+  const DATA_START_ROW = 7;
+
+  try {
+    console.log('Analizando hoja de Excel:', sheet.length, 'filas');
+    
+    for (let rowIndex = DATA_START_ROW; rowIndex < sheet.length; rowIndex++) {
+      const row = sheet[rowIndex];
+      if (!row || !Array.isArray(row)) continue;
+
+      const equipmentCode = row[3];
+      if (!equipmentCode || typeof equipmentCode !== 'string' || equipmentCode.trim() === '') {
+        continue;
+      }
+
+      console.log(`Procesando fila ${rowIndex + 1}:`, row);
 
+      DOCUMENT_COLUMNS.forEach(column => {
+        const cellValue = row[column.index];
+        
+        if (cellValue && typeof cellValue === 'string' && cellValue.trim() !== '') {
+          const documentNames = cellValue.split(',').map(name => name.trim());
+          
+          documentNames.forEach(documentName => {
+            if (documentName && this.hasValidFileExtension(documentName)) {
+              console.log(`Documento encontrado: ${documentName} (Columna ${column.letter}, Fila ${rowIndex + 1})`);
+              
+              documentEntries.push({
+                rowIndex: rowIndex + 1,
+                columnLetter: column.letter,
+                columnIndex: column.index,
+                documentName: documentName,
+                category: column.category
+              });
+            }
+          });
+        }
+      });
+    }
+    
+    console.log(`Total de documentos encontrados en Excel: ${documentEntries.length}`);
+    
+  } catch (error) {
+    console.error('Error al extraer nombres de documentos del Excel:', error);
+  }
 
+  return documentEntries;
+}
+
+public async validateDocuments(): Promise<void> {
+  if (!this.canValidate) {
+    this._resourcesService.openSnackBar('No se puede validar: faltan datos necesarios');
+    return;
+  }
+
+  this.isValidating = true;
+  this.validationStep = 'Preparando archivos...';
+  
+  try {
+    // Obtener ID de usuario
+    const idUser = localStorage.getItem('idusuario');
+    if (!idUser) {
+      throw new Error('No se encontró ID de usuario');
+    }
+
+    // Paso 1: Subir archivo Excel como temporal
+    this.validationStep = 'Subiendo archivo Excel...';
+    let excelTempId: string;
+    
+    // CORRECCIÓN: Verificar que sea ZipContentData y que tenga excelData
+    if ('zipContent' in this.data && this.data.excelData?.originalFile) {
+      const excelFormData = new FormData();
+      excelFormData.append('file', this.data.excelData.originalFile);
+      excelFormData.append('id_user', idUser);
+      excelFormData.append('linea', '1');
+      
+      const excelUploadResponse = await this._gdelService.uploadTempFile(excelFormData).toPromise();
+      
+      if (excelUploadResponse.error) {
+        throw new Error(`Error subiendo Excel: ${excelUploadResponse.message}`);
+      }
+      
+      excelTempId = excelUploadResponse.response.idArchivo;
+      console.log('Excel subido como temporal:', excelTempId);
+    } else {
+      throw new Error('No se encontró el archivo Excel original');
+    }
+
+    // Paso 2: Subir archivo ZIP como temporal
+    this.validationStep = 'Subiendo archivo ZIP...';
+    let zipTempId: string;
+    
+    // CORRECCIÓN: Verificar que sea ZipContentData
+    if ('zipContent' in this.data && this.data.originalFile) {
+      const zipFormData = new FormData();
+      zipFormData.append('file', this.data.originalFile);
+      zipFormData.append('id_user', idUser);
+      zipFormData.append('linea', '1');
+      
+      const zipUploadResponse = await this._gdelService.uploadTempFile(zipFormData).toPromise();
+      
+      if (zipUploadResponse.error) {
+        throw new Error(`Error subiendo ZIP: ${zipUploadResponse.message}`);
+      }
+      
+      zipTempId = zipUploadResponse.response.idArchivo;
+      console.log('ZIP subido como temporal:', zipTempId);
+    } else {
+      throw new Error('No se encontró el archivo ZIP original');
+    }
+
+    // Paso 3: Validar archivos
+    this.validationStep = 'Validando documentos...';
+    const validationFormData = new FormData();
+    
+    // CORRECCIÓN: Verificar tipos antes de usar
+    if ('zipContent' in this.data && this.data.excelData?.originalFile && this.data.originalFile) {
+      const excelBlob = new Blob([this.data.excelData.originalFile], { 
+        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 
+      });
+      const zipBlob = new Blob([this.data.originalFile], { type: 'application/zip' });
+      
+      validationFormData.append('excel_file', excelBlob, this.data.excelData.fileName);
+      validationFormData.append('zip_file', zipBlob, this.fileName);
+      validationFormData.append('id_user', idUser);
+      validationFormData.append('linea', '1');
+
+      const validationResponse = await this._gdelService.validateZipFile(validationFormData).toPromise();
+      
+      if (validationResponse.error) {
+        throw new Error(validationResponse.message || 'Error en validación');
+      }
+
+      // Procesar respuesta de validación
+      this.processValidationResponse(validationResponse, excelTempId, zipTempId);
+      
+      this._resourcesService.openSnackBar(
+        this.validationResult?.isValid 
+          ? 'Validación exitosa: Todos los documentos encontrados'
+          : `Validación completada: Faltan ${this.validationResult?.missingFiles.length || 0} documentos`
+      );
+    } else {
+      throw new Error('Datos insuficientes para validación');
+    }
+    
+  } catch (error: any) {
+    console.error('Error validando documentos:', error);
+    this._resourcesService.openSnackBar('Error en validación: ' + error.message);
+  } finally {
+    this.isValidating = false;
+    this.validationStep = '';
+  }
+}
+  private processValidationResponse(response: any, excelTempId: string, zipTempId: string): void {
+    // Extraer nombres de archivos esperados
+    const expectedFileNames = this.expectedDocuments.map(doc => doc.documentName);
+    
+    // Obtener archivos encontrados en ZIP (sin directorios)
+    const zipFileNames = this.allZipFiles
+      .filter(f => !f.isDirectory)
+      .map(f => f.name.split('/').pop() || f.name);
+    
+    // Comparar archivos esperados vs encontrados
+    const foundFiles = expectedFileNames.filter(expectedFile => 
+      zipFileNames.some(zipFile => zipFile.toLowerCase() === expectedFile.toLowerCase())
+    );
+    
+    const missingFiles = expectedFileNames.filter(expectedFile => 
+      !zipFileNames.some(zipFile => zipFile.toLowerCase() === expectedFile.toLowerCase())
+    );
+    
+    const extraFiles = zipFileNames.filter(zipFile => 
+      !expectedFileNames.some(expectedFile => expectedFile.toLowerCase() === zipFile.toLowerCase())
+    );
+
+    // Mapear respuesta del backend a nuestro modelo
+    this.validationResult = {
+      isValid: response.valid || missingFiles.length === 0,
+      foundFiles: foundFiles,
+      missingFiles: missingFiles,
+      extraFiles: extraFiles,
+      validationDetails: {
+        expectedCount: expectedFileNames.length,
+        foundCount: zipFileNames.length,
+        matchedCount: foundFiles.length
+      },
+      tempFiles: {
+        excel: excelTempId,
+        zip: zipTempId
+      },
+      individualTempFiles: response.individual_temp_files || []
+    };
 
+    // Marcar archivos validados en la tabla
+    this.markValidatedFiles();
+  }
 
-  // Marcar archivos como validados en la tabla
   private markValidatedFiles(): void {
     if (!this.validationResult) return;
 
@@ -138,18 +389,114 @@ export class PreviewFileContentComponent implements OnInit {
     this.updateVisibleFiles();
   }
 
+  public processValidatedDocuments(): void {
+    if (!this.validationResult) {
+      this._resourcesService.openSnackBar('Debe validar los documentos primero');
+      return;
+    }
 
+    if (!this.validationResult.isValid) {
+      const missingCount = this.validationResult.missingFiles.length;
+      const message = `No se puede procesar: faltan ${missingCount} documento(s)`;
+      this._resourcesService.openSnackBar(message);
+      return;
+    }
 
-  private loadZipContent(): void {
-    if (!this.data || !('zipContent' in this.data)) return;
+    console.log('Procesando documentos validados:', this.validationResult);
+
+    // Cerrar diálogo con los datos de validación
+    this.dialogRef.close({
+      action: 'validate',
+      validationResult: this.validationResult,
+      tempFiles: this.validationResult.tempFiles,
+      individualTempFiles: this.validationResult.individualTempFiles
+    });
+  }
+
+  // Métodos auxiliares que se mantienen igual
+  private hasValidFileExtension(fileName: string): boolean {
+    if (!fileName || !fileName.includes('.')) return false;
     
-    const zipData = this.data.zipContent;
-    this.totalFiles = zipData.totalFiles;
-    this.totalSize = this.formatBytes(zipData.totalSize);
+    const validExtensions = [
+      '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
+      '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp',
+      '.txt', '.rtf', '.csv', '.xml', '.json',
+      '.zip', '.rar', '.7z',
+      '.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv',
+      '.mp3', '.wav', '.ogg', '.aac', '.flac', '.dwg', '.dxf', '.ai', '.psd'
+    ];
     
-    // Procesar archivos del ZIP
-    this.allZipFiles = this.processZipFiles(zipData.files);
-    this.updateVisibleFiles();
+    const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
+    return validExtensions.includes(extension);
+  }
+
+  private processZipFiles(files: ZipFileEntry[]): FileItem[] {
+    const processedFiles: FileItem[] = [];
+    const folderSet = new Set<string>();
+    
+    // Crear carpetas
+    files.forEach(file => {
+      if (file.isDirectory) {
+        const cleanPath = file.name.replace(/\/$/, '');
+        if (cleanPath) {
+          folderSet.add(cleanPath);
+        }
+      } else {
+        const pathParts = file.name.split('/');
+        for (let i = 0; i < pathParts.length - 1; i++) {
+          const folderPath = pathParts.slice(0, i + 1).join('/');
+          if (folderPath) {
+            folderSet.add(folderPath);
+          }
+        }
+      }
+    });
+
+    // Crear elementos para carpetas
+    Array.from(folderSet).forEach(folderPath => {
+      const pathParts = folderPath.split('/');
+      const level = pathParts.length - 1;
+      const parent = level > 0 ? pathParts.slice(0, -1).join('/') : '';
+      const displayName = pathParts[pathParts.length - 1];
+
+      processedFiles.push({
+        name: folderPath,
+        displayName: displayName,
+        fullPath: folderPath,
+        size: '-',
+        type: 'Carpeta',
+        isDirectory: true,
+        icon: 'folder',
+        level: level,
+        parent: parent,
+        isValidated: false
+      });
+    });
+
+    // Crear elementos para archivos
+    files.forEach(file => {
+      if (!file.isDirectory) {
+        const pathParts = file.name.split('/');
+        const level = pathParts.length - 1;
+        const parent = level > 0 ? pathParts.slice(0, -1).join('/') : '';
+        const displayName = pathParts[pathParts.length - 1];
+
+        processedFiles.push({
+          name: file.name,
+          displayName: displayName,
+          fullPath: file.name,
+          size: this.formatBytes(file.size),
+          type: this.getFileTypeDescription(file.type || 'unknown'),
+          isDirectory: false,
+          icon: this.getFileIcon(file.type || 'unknown', false),
+          level: level,
+          parent: parent,
+          isValidated: false
+        });
+      }
+    });
+
+    return processedFiles;
   }
 
   private getFileTypeDescription(type: string): string {
@@ -270,121 +617,10 @@ export class PreviewFileContentComponent implements OnInit {
     return buildHierarchy();
   }
 
-  private processZipFiles(files: ZipFileEntry[]): FileItem[] {
-    const processedFiles: FileItem[] = [];
-    const folderSet = new Set<string>();
-    
-    // Crear carpetas
-    files.forEach(file => {
-      if (file.isDirectory) {
-        const cleanPath = file.name.replace(/\/$/, '');
-        if (cleanPath) {
-          folderSet.add(cleanPath);
-        }
-      } else {
-        const pathParts = file.name.split('/');
-        for (let i = 0; i < pathParts.length - 1; i++) {
-          const folderPath = pathParts.slice(0, i + 1).join('/');
-          if (folderPath) {
-            folderSet.add(folderPath);
-          }
-        }
-      }
-    });
-
-    // Crear elementos para carpetas
-    Array.from(folderSet).forEach(folderPath => {
-      const pathParts = folderPath.split('/');
-      const level = pathParts.length - 1;
-      const parent = level > 0 ? pathParts.slice(0, -1).join('/') : '';
-      const displayName = pathParts[pathParts.length - 1];
-
-      processedFiles.push({
-        name: folderPath,
-        displayName: displayName,
-        fullPath: folderPath,
-        size: '-',
-        type: 'Carpeta',
-        isDirectory: true,
-        icon: 'folder',
-        level: level,
-        parent: parent,
-        isValidated: false
-      });
-    });
-
-    // Crear elementos para archivos
-    files.forEach(file => {
-      if (!file.isDirectory) {
-        const pathParts = file.name.split('/');
-        const level = pathParts.length - 1;
-        const parent = level > 0 ? pathParts.slice(0, -1).join('/') : '';
-        const displayName = pathParts[pathParts.length - 1];
-
-        processedFiles.push({
-          name: file.name,
-          displayName: displayName,
-          fullPath: file.name,
-          size: this.formatBytes(file.size),
-          type: this.getFileTypeDescription(file.type || 'unknown'),
-          isDirectory: false,
-          icon: this.getFileIcon(file.type || 'unknown', false),
-          level: level,
-          parent: parent,
-          isValidated: false
-        });
-      }
-    });
-
-    return processedFiles;
-  }
-
-  private hasValidFileExtension(fileName: string): boolean {
-    if (!fileName || !fileName.includes('.')) return false;
-    
-    const validExtensions = [
-      '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
-      '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp',
-      '.txt', '.rtf', '.csv', '.xml', '.json',
-      '.zip', '.rar', '.7z',
-      '.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv',
-      '.mp3', '.wav', '.ogg', '.aac', '.flac', '.img'
-    ];
-    
-    const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
-    return validExtensions.includes(extension);
-  }
-
   public getIndentStyle(level: number): string {
     return `padding-left: ${level * 20}px`;
   }
 
-  public close(): void {
-    this.dialogRef.close();
-  }
-
-  get hasZipData(): boolean {
-    return this.fileType === 'zip' && this.allZipFiles.length > 0;
-  }
-
-  get canValidate(): boolean {
-    return this.hasZipData && this.expectedDocuments.length > 0;
-  }
-
-  get validationSummary(): string {
-    if (!this.validationResult) {
-      return `${this.expectedDocuments.length} documentos esperados`;
-    }
-
-    const { matchedCount, expectedCount } = this.validationResult.validationDetails;
-    return `${matchedCount}/${expectedCount} documentos encontrados`;
-  }
-
-  get validationClass(): string {
-    if (!this.validationResult) return '';
-    return this.validationResult.isValid ? 'validation-success' : 'validation-error';
-  }
-
   public getFileStatusIcon(file: FileItem): string {
     if (file.isDirectory) return '';
     
@@ -401,274 +637,61 @@ export class PreviewFileContentComponent implements OnInit {
     return file.isValidated ? 'file-validated' : 'file-missing';
   }
 
-
-private extractDocumentNamesFromExcel(): ExcelDocumentEntry[] {
-  // Verificar que tengamos datos del Excel
-  if (!this.data.excelData || !this.data.excelData.sheets || !this.data.excelData.sheets['CARGA DE DOCUMENTOS']) {
-    console.warn('No se encontraron datos del Excel o la hoja "CARGA DE DOCUMENTOS"');
-    return [];
+  public close(): void {
+    this.dialogRef.close();
   }
 
-  const documentEntries: ExcelDocumentEntry[] = [];
-  const sheet = this.data.excelData.sheets['CARGA DE DOCUMENTOS'];
-  
-  // Definir las columnas de documentos y sus categorías
-  const DOCUMENT_COLUMNS = [
-    { letter: 'H', index: 6, category: 'FICHA DE SEGURIDAD DE PRODUCTOS QUÍMICOS' },  // H = índice 7
-    { letter: 'J', index: 8, category: 'FICHA TÉCNICA' },                           // J = índice 9
-    { letter: 'L', index: 10, category: 'FOTOGRAFÍAS - DIAGRAMAS' },                // L = índice 11
-    { letter: 'N', index: 12, category: 'DATOS DEL PROVEEDOR' },                    // N = índice 13
-    { letter: 'P', index: 14, category: 'MANUALES DE OPERACIÓN' },                  // P = índice 15
-    { letter: 'R', index: 16, category: 'MANUALES DE MANTENIMIENTO PREVENTIVO' },   // R = índice 17
-    { letter: 'T', index: 18, category: 'MANUALES DE MANTENIMIENTO CORRECTIVO' },   // T = índice 19
-    { letter: 'V', index: 20, category: 'DOCUMENTOS ADICIONALES' }                  // V = índice 21
-  ];
-
-  const DATA_START_ROW = 6; // Fila 9 en Excel (índice 8 en array)
-
-  try {
-    console.log('Analizando hoja de Excel:', sheet.length, 'filas');
-    
-    // Recorrer desde la fila 9 hacia abajo
-    for (let rowIndex = DATA_START_ROW - 1; rowIndex < sheet.length; rowIndex++) {
-      const row = sheet[rowIndex];
-      if (!row || !Array.isArray(row)) continue;
-
-
-      const hasMainData = row[0] || row[2] || row[4];
-      if (!hasMainData) continue; 
-
-      console.log(` Procesando fila ${rowIndex + 1}:`, row);
-
-      // Revisar cada columna de documentos
-      DOCUMENT_COLUMNS.forEach(column => {
-        const cellValue = row[column.index];
-        
-        if (cellValue && typeof cellValue === 'string' && cellValue.trim() !== '') {
-          const documentName = cellValue.trim();
-          
-          // Validar que tenga una extensión de archivo válida
-          if (this.hasValidFileExtension(documentName)) {
-            console.log(`Documento encontrado: ${documentName} (Columna ${column.letter}, Fila ${rowIndex + 1})`);
-            
-            documentEntries.push({
-              rowIndex: rowIndex + 1, // +1 para mostrar número de fila real
-              columnLetter: column.letter,
-              columnIndex: column.index,
-              documentName: documentName,
-              category: column.category
-            });
-          } else {
-            console.warn(`Archivo sin extensión válida: ${documentName} (Columna ${column.letter}, Fila ${rowIndex + 1})`);
-          }
-        }
-      });
-    }
-    
-    console.log(`Total de documentos encontrados en Excel: ${documentEntries.length}`);
-    
-  } catch (error) {
-    console.error('Error al extraer nombres de documentos del Excel:', error);
+  // Getters
+  get hasZipData(): boolean {
+    return this.fileType === 'zip' && this.allZipFiles.length > 0;
   }
 
-  return documentEntries;
+get canValidate(): boolean {
+  return this.hasZipData && 
+         this.expectedDocuments.length > 0 && 
+         'zipContent' in this.data &&
+         !!this.data.originalFile && 
+         !!this.data.excelData?.originalFile;
 }
 
+  get validationSummary(): string {
+    if (!this.validationResult) {
+      return `${this.expectedDocuments.length} documentos esperados`;
+    }
 
-public async validateDocuments(): Promise<void> {
-  if (!this.canValidate) {
-    console.warn('No se puede validar: faltan datos');
-    return;
+    const { matchedCount, expectedCount } = this.validationResult.validationDetails;
+    return `${matchedCount}/${expectedCount} documentos encontrados`;
   }
 
-  this.isValidating = true;
-  
-  try {
-    
-    await new Promise(resolve => setTimeout(resolve, 500)); // Simular procesamiento
-    
-    const expectedFileNames = this.expectedDocuments.map(doc => {
-      console.log(`Documento esperado: ${doc.documentName} (${doc.category})`);
-      return doc.documentName.toLowerCase();
-    });
-    
-    console.log(`Total documentos esperados: ${expectedFileNames.length}`);
-    
-    // Obtener nombres de archivos del ZIP (solo archivos, no directorios)
-    const zipFileNames = this.allZipFiles
-      .filter(file => !file.isDirectory)
-      .map(file => {
-        // Obtener solo el nombre del archivo sin la ruta
-        const fileName = file.name.split('/').pop() || file.name;
-        console.log(`Archivo en ZIP: ${fileName} (path completo: ${file.name})`);
-        return fileName.toLowerCase();
-      });
-
-    console.log(`Total archivos en ZIP: ${zipFileNames.length}`);
-
-    // Encontrar archivos que coinciden
-    const foundFiles: string[] = [];
-    const missingFiles: string[] = [];
-
-    expectedFileNames.forEach(expectedFile => {
-      const found = zipFileNames.find(zipFile => {
-        // Comparación exacta de nombres
-        const matches = zipFile === expectedFile;
-        if (matches) {
-          console.log(`MATCH encontrado: ${expectedFile} = ${zipFile}`);
-        }
-        return matches;
-      });
-
-      if (found) {
-        foundFiles.push(expectedFile);
-      } else {
-        console.log(`MISSING: ${expectedFile} no encontrado en ZIP`);
-        missingFiles.push(expectedFile);
-      }
-    });
-
-    // Encontrar archivos extra (en ZIP pero no en Excel)
-    const extraFiles = zipFileNames.filter(zipFile => {
-      const isExtra = !expectedFileNames.includes(zipFile);
-      if (isExtra) {
-        console.log(`EXTRA: ${zipFile} está en ZIP pero no en Excel`);
-      }
-      return isExtra;
-    });
-
-    const isValid = missingFiles.length === 0 && foundFiles.length > 0;
-
-    this.validationResult = {
-      isValid,
-      foundFiles,
-      missingFiles,
-      extraFiles,
-      validationDetails: {
-        expectedCount: expectedFileNames.length,
-        foundCount: foundFiles.length,
-        matchedCount: foundFiles.length
-      }
-    };
+  get detailedValidationSummary(): string {
+    if (!this.validationResult) {
+      return `${this.expectedDocuments.length} documentos por validar`;
+    }
 
-    // Log del resultado final
-    console.log('RESULTADO DE VALIDACIÓN:');
-    console.log(`   Válido: ${isValid}`);
-    console.log(`   Esperados: ${expectedFileNames.length}`);
-    console.log(`   Encontrados: ${foundFiles.length}`);
-    console.log(`   Faltantes: ${missingFiles.length}`);
-    console.log(`   Extra: ${extraFiles.length}`);
+    const { matchedCount, expectedCount } = this.validationResult.validationDetails;
+    const missing = this.validationResult.missingFiles.length;
+    const extra = this.validationResult.extraFiles.length;
     
-    if (foundFiles.length > 0) {
-      console.log('Archivos encontrados:', foundFiles);
-    }
+    let summary = `${matchedCount}/${expectedCount} documentos encontrados`;
     
-    if (missingFiles.length > 0) {
-      console.log('Archivos faltantes:', missingFiles);
+    if (missing > 0) {
+      summary += `, ${missing} faltantes`;
     }
-
-    // Marcar archivos validados en la tabla
-    this.markValidatedFiles();
     
-  } catch (error) {
-    console.error('Error en validación:', error);
-  } finally {
-    this.isValidating = false;
-  }
-}
-
-
-public processValidatedDocuments(): void {
-  if (!this.validationResult || !this.validationResult.isValid) {
-    console.warn('No se puede procesar: validación inválida');
-    const message = this.validationResult 
-      ? `Faltan ${this.validationResult.missingFiles.length} documentos`
-      : 'Debe validar primero';
+    if (extra > 0) {
+      summary += `, ${extra} extra`;
+    }
     
-
-    alert(message);
-    return;
+    return summary;
   }
 
-  console.log('Procesando documentos validados:', this.validationResult.foundFiles);
-
- 
-  this.dialogRef.close({
-    action: 'validate',
-    validationResult: this.validationResult,
-    foundFiles: this.validationResult.foundFiles,
-    validationDetails: this.validationResult.validationDetails
-  });
-}
-
-
-ngOnInit(): void {
-  console.log('Inicializando PreviewFileContentComponent');
-  console.log('Datos recibidos:', this.data);
-  
-  this.initializeComponent();
-  
-  if (this.fileType === 'zip' && this.expectedDocuments.length > 0) {
-    console.log(`Se encontraron ${this.expectedDocuments.length} documentos esperados en Excel`);
-    this.expectedDocuments.forEach((doc, index) => {
-      console.log(`   ${index + 1}. ${doc.documentName} (${doc.category})`);
-    });
-  }
-}
-
-//TEST
-public debugValidation(): void {
-  console.group('DEBUG DE VALIDACIÓN');
-  
-  console.log('Estado actual:');
-  console.log('   - Tipo de archivo:', this.fileType);
-  console.log('   - Puede validar:', this.canValidate);
-  console.log('   - Documentos esperados:', this.expectedDocuments.length);
-  console.log('   - Archivos en ZIP:', this.allZipFiles.length);
-  
-  console.log('Documentos esperados:');
-  this.expectedDocuments.forEach(doc => {
-    console.log(`   - ${doc.documentName} (${doc.category})`);
-  });
-  
-  console.log('Archivos en ZIP:');
-  this.allZipFiles.filter(f => !f.isDirectory).forEach(file => {
-    const fileName = file.name.split('/').pop() || file.name;
-    console.log(`   - ${fileName} (path: ${file.name})`);
-  });
-  
-  if (this.validationResult) {
-    console.log('Resultado de validación:');
-    console.log('   - Es válido:', this.validationResult.isValid);
-    console.log('   - Encontrados:', this.validationResult.foundFiles);
-    console.log('   - Faltantes:', this.validationResult.missingFiles);
-    console.log('   - Extra:', this.validationResult.extraFiles);
-  }
-  
-  console.groupEnd();
-}
-
-
-get detailedValidationSummary(): string {
-  if (!this.validationResult) {
-    return `${this.expectedDocuments.length} documentos por validar`;
+  get validationClass(): string {
+    if (!this.validationResult) return '';
+    return this.validationResult.isValid ? 'validation-success' : 'validation-error';
   }
 
-  const { matchedCount, expectedCount } = this.validationResult.validationDetails;
-  const missing = this.validationResult.missingFiles.length;
-  const extra = this.validationResult.extraFiles.length;
-  
-  let summary = `${matchedCount}/${expectedCount} documentos encontrados`;
-  
-  if (missing > 0) {
-    summary += `, ${missing} faltantes`;
-  }
-  
-  if (extra > 0) {
-    summary += `, ${extra} extra`;
+  get currentStep(): string {
+    return this.validationStep;
   }
-  
-  return summary;
-}
 
 }

+ 98 - 7
src/app/components/initial-data-upload/zipfile-upload/zipfile-upload.component.html

@@ -257,26 +257,117 @@ Para la carga de archivos tipo zip
   </div>
 </div>
 
+<!-- Sección de acciones del diálogo -->
 <mat-dialog-actions align="end" style="padding: 16px;">
   <button 
      mat-button
     (click)="cancelar()"
     style="margin-right: 8px;"
-    [disabled]="isLoading || isUploading">
+    [disabled]="isLoading || isUploading || isValidating">
     <mat-icon>close</mat-icon>
     Cancelar
   </button>
   
-
+  <!-- Botón para mostrar resumen antes de validar -->
+  <button 
+     mat-button
+     color="accent"
+     *ngIf="canValidate && !validationResult"
+     (click)="showValidationSummary()"
+     [disabled]="isLoading || isUploading || isValidating"
+     style="margin-right: 8px;">
+    <mat-icon>info</mat-icon>
+    Ver Resumen
+  </button>
+  
+  <!-- Botón principal de validación y procesamiento -->
   <button 
      mat-raised-button
      color="primary"
      class="blue_send_background white_font"
-     *ngIf="hasAnyFile"
-     (click)="saveFinalFile()"
-     [disabled]="isLoading || isUploading">
-    <mat-icon>{{ hasExcelFile ? 'table_chart' : 'folder_zip' }}</mat-icon>
-    {{ hasExcelFile ? 'Procesar Excel' : 'Procesar ZIP' }}
+     *ngIf="canValidate"
+     (click)="validateAndProcessDocuments()"
+     [disabled]="isLoading || isUploading || isValidating">
+    <mat-icon>{{ isValidating ? 'hourglass_empty' : 'verified' }}</mat-icon>
+    {{ isValidating ? 'Validando...' : 'Validar y Procesar' }}
+  </button>
+  
+  <!-- Botón para cuando solo hay archivos pero no se puede validar -->
+  <button 
+     mat-raised-button
+     color="warn"
+     *ngIf="hasAnyFile && !canValidate"
+     [disabled]="true"
+     matTooltip="{{ requirementsMessage }}">
+    <mat-icon>warning</mat-icon>
+    {{ requirementsMessage }}
   </button>
 </mat-dialog-actions>
 
+<!-- Sección de estado de validación (opcional, para mostrar progreso) -->
+<div *ngIf="isValidating" class="validation-progress" style="padding: 16px; text-align: center;">
+  <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+  <p style="margin-top: 8px; color: #666;">{{ currentStep }}</p>
+</div>
+
+<!-- Sección de resultados de validación -->
+<div *ngIf="validationResult" class="validation-results" style="padding: 16px;">
+  <mat-card [ngClass]="validationClass">
+    <mat-card-header>
+      <mat-icon mat-card-avatar [style.color]="validationResult.isValid ? '#4caf50' : '#f44336'">
+        {{ validationResult.isValid ? 'check_circle' : 'error' }}
+      </mat-icon>
+      <mat-card-title>
+        {{ validationResult.isValid ? 'Validación Exitosa' : 'Validación con Observaciones' }}
+      </mat-card-title>
+      <mat-card-subtitle>{{ validationSummary }}</mat-card-subtitle>
+    </mat-card-header>
+    
+    <mat-card-content>
+      <!-- Documentos faltantes -->
+      <div *ngIf="validationResult.missingFiles.length > 0" style="margin-bottom: 16px;">
+        <h4 style="color: #f44336; margin-bottom: 8px;">
+          <mat-icon style="vertical-align: middle; margin-right: 4px;">warning</mat-icon>
+          Documentos Faltantes ({{ validationResult.missingFiles.length }})
+        </h4>
+        <mat-chip-listbox>
+          <mat-chip *ngFor="let missing of validationResult.missingFiles" 
+                   style="margin: 2px; background-color: #ffebee; color: #c62828;">
+            {{ missing }}
+          </mat-chip>
+        </mat-chip-listbox>
+      </div>
+      
+      <!-- Archivos extra -->
+      <div *ngIf="validationResult.extraFiles.length > 0" style="margin-bottom: 16px;">
+        <h4 style="color: #ff9800; margin-bottom: 8px;">
+          <mat-icon style="vertical-align: middle; margin-right: 4px;">info</mat-icon>
+          Archivos Extra ({{ validationResult.extraFiles.length }})
+        </h4>
+        <mat-chip-listbox>
+          <mat-chip *ngFor="let extra of validationResult.extraFiles" 
+                   style="margin: 2px; background-color: #fff3e0; color: #ef6c00;">
+            {{ extra }}
+          </mat-chip>
+        </mat-chip-listbox>
+      </div>
+      
+      <!-- Estadísticas -->
+      <div class="validation-stats" style="display: flex; justify-content: space-between; margin-top: 16px;">
+        <div style="text-align: center;">
+          <strong style="color: #4caf50;">{{ validationResult.validationDetails.matchedCount }}</strong>
+          <br><small>Encontrados</small>
+        </div>
+        <div style="text-align: center;">
+          <strong style="color: #f44336;">{{ validationResult.missingFiles.length }}</strong>
+          <br><small>Faltantes</small>
+        </div>
+        <div style="text-align: center;">
+          <strong style="color: #ff9800;">{{ validationResult.extraFiles.length }}</strong>
+          <br><small>Extra</small>
+        </div>
+      </div>
+    </mat-card-content>
+  </mat-card>
+</div>
+

+ 529 - 1280
src/app/components/initial-data-upload/zipfile-upload/zipfile-upload.component.ts

@@ -24,7 +24,6 @@ export interface Document {
   fecha_carga?: string
 }
 
-
 export interface ProcessedDocument {
   nombre_del_archivo: string;
   tamano_del_archivo: string;
@@ -43,8 +42,7 @@ export interface ZipFileEntry {
   type?: string;
 }
 
-
-export interface DocumentValidationResult {
+export interface ValidationResult {
   isValid: boolean;
   foundFiles: string[];
   missingFiles: string[];
@@ -54,6 +52,11 @@ export interface DocumentValidationResult {
     foundCount: number;
     matchedCount: number;
   };
+  tempFiles?: {
+    excel: string;
+    zip: string;
+  };
+  individualTempFiles?: any[];
 }
 
 export interface ExcelDocumentEntry {
@@ -80,15 +83,22 @@ interface TemplateStructure {
 })
 export class ZipfileUploadComponent {
 
-private originalZipFile: File | null = null;
+  // Archivos originales para validación
+  private originalExcelFile: File | null = null;
+  private originalZipFile: File | null = null;
   
-public processedDocuments: ProcessedDocument[] = [];
+  public processedDocuments: ProcessedDocument[] = [];
   public isLoading: boolean = false;
   public hasError: boolean = false;
   public errorMessage: string = '';
   public isUploading: boolean;
   public currentFileType: 'excel' | 'zip' | null = null;
 
+  // Variables para validación
+  public validationResult: ValidationResult | null = null;
+  public isValidating: boolean = false;
+  public validationStep: string = '';
+
   uploadedFile: TempFileInfo | null;
   @ViewChild('file') private uploader?: ElementRef;
 
@@ -168,7 +178,6 @@ public processedDocuments: ProcessedDocument[] = [];
     this.uploader?.nativeElement.click();
   }
 
-
   onDragOver(event: DragEvent): void {
     event.preventDefault();
     event.stopPropagation();
@@ -329,9 +338,273 @@ public processedDocuments: ProcessedDocument[] = [];
     return errors;
   }
 
+  async uploadExcelFile(fileData: File): Promise<void> {
+    try {
+      // Guardar referencia al archivo original
+      this.originalExcelFile = fileData;
+      
+      // Primero validar la estructura antes de subir
+      const validationResult = await this.validateFileStructure(fileData);
+      
+      if (!validationResult.isValid) {
+        this._resourcesService.openSnackBar(`Debe contener al menos un registro y cumplir con la estructura requerida`);
+        this.isUploading = false;
+        return;
+      }
+
+      await this.readExcelFile(fileData, 'temp-excel-' + Date.now());
+      
+      const newDocument: Document = {
+        nombre_del_archivo: fileData.name,
+        tamano_del_archivo: this.formatBytes(fileData.size),
+        archivo_id: 'temp-excel-' + Date.now(),
+        tipo_archivo: 'excel',
+        fecha_carga: new Date().toLocaleString()
+      };
+
+      this.excelFile = newDocument;
+      this.updateTableData();
+      this._resourcesService.openSnackBar('Plantilla Excel válida y cargada exitosamente');
+
+      this.isUploading = false;
+    } catch (error: any) {
+      this.handleUploadError(error);
+      this.isUploading = false;
+    }
+  }
+
+  async uploadZipFile(fileData: File): Promise<void> {
+    try {
+      // Guardar referencia al archivo original para validación
+      this.originalZipFile = fileData;
+      
+      // Validar contenido del ZIP
+      const zipValidation = await this.validateZipContent(fileData);
+      
+      if (!zipValidation.isValid) {
+        this._resourcesService.openSnackBar(`Error en el archivo ZIP: ${zipValidation.errors.join(', ')}`);
+        this.isUploading = false;
+        return;
+      }
+
+      const tempZipId = 'temp-zip-' + Date.now();
+      
+      // Leer el contenido del ZIP para visualización
+      await this.readZipFile(fileData, tempZipId);
+      
+      const newDocument: Document = {
+        nombre_del_archivo: fileData.name,
+        tamano_del_archivo: this.formatBytes(fileData.size),
+        archivo_id: tempZipId,
+        tipo_archivo: 'zip',
+        fecha_carga: new Date().toLocaleString()
+      };
+
+      this.zipFile = newDocument;
+      this.updateTableData();
+      this._resourcesService.openSnackBar('Archivo ZIP cargado y listo para validación');
+
+      this.isUploading = false;
+      
+    } catch (error: any) {
+      this.handleUploadError(error);
+      this.isUploading = false;
+    }
+  }
+
+  // NUEVA LÓGICA DE VALIDACIÓN USANDO EL BACKEND
+  public async validateAndProcessDocuments(): Promise<void> {
+    if (!this.canValidate) {
+      this._resourcesService.openSnackBar('Debe cargar tanto el archivo Excel como el ZIP antes de validar');
+      return;
+    }
+
+    this.isValidating = true;
+    this.validationStep = 'Preparando archivos...';
+    
+    try {
+      // Obtener ID de usuario
+      const idUser = localStorage.getItem('idusuario');
+      if (!idUser) {
+        throw new Error('No se encontró ID de usuario');
+      }
+
+      // Usar el endpoint de validación del backend
+      this.validationStep = 'Validando documentos...';
+      const validationFormData = new FormData();
+      
+      if (this.originalExcelFile && this.originalZipFile) {
+        validationFormData.append('excel_file', this.originalExcelFile, this.originalExcelFile.name);
+        validationFormData.append('zip_file', this.originalZipFile, this.originalZipFile.name);
+        validationFormData.append('id_user', idUser);
+        validationFormData.append('linea', '1');
+
+        const validationResponse = await this._gdelService.validateZipFile(validationFormData).toPromise();
+        
+        if (validationResponse.error) {
+          throw new Error(validationResponse.message || 'Error en validación');
+        }
+
+        // Procesar respuesta de validación
+        this.processValidationResponse(validationResponse);
+        
+        // Si la validación es exitosa, procesar archivos
+        if (this.validationResult?.isValid) {
+          this.validationStep = 'Procesando documentos validados...';
+          await this.processValidatedDocuments();
+          
+          this._resourcesService.openSnackBar('Validación y procesamiento completado exitosamente');
+          this.dialogRef.close({ 
+            success: true, 
+            validationResult: this.validationResult,
+            processedCount: this.validationResult.foundFiles.length 
+          });
+        } else {
+          const missingCount = this.validationResult?.missingFiles.length || 0;
+          this._resourcesService.openSnackBar(
+            `Validación completada: Faltan ${missingCount} documento(s). Revise los detalles.`
+          );
+        }
+      } else {
+        throw new Error('Archivos originales no disponibles');
+      }
+      
+    } catch (error: any) {
+      console.error('Error validando documentos:', error);
+      this._resourcesService.openSnackBar('Error en validación: ' + error.message);
+    } finally {
+      this.isValidating = false;
+      this.validationStep = '';
+    }
+  }
+
+  private processValidationResponse(response: any): void {
+    // El backend ya hace la comparación, usar sus resultados
+    this.validationResult = {
+      isValid: response.valid || false,
+      foundFiles: [], // El backend maneja esto internamente
+      missingFiles: response.missing_in_zip || [],
+      extraFiles: response.extra_in_zip || [],
+      validationDetails: {
+        expectedCount: 0, // Se calculará después si es necesario
+        foundCount: 0,
+        matchedCount: 0
+      },
+      tempFiles: response.temp_files || null,
+      individualTempFiles: response.individual_temp_files || []
+    };
+
+    // Calcular estadísticas
+    const expectedDocuments = this.extractDocumentNamesFromExcel();
+    this.validationResult.validationDetails.expectedCount = expectedDocuments.length;
+    this.validationResult.validationDetails.matchedCount = 
+      expectedDocuments.length - this.validationResult.missingFiles.length;
+    
+    console.log('Resultado de validación procesado:', this.validationResult);
+  }
+
+  private async processValidatedDocuments(): Promise<void> {
+    if (!this.validationResult?.tempFiles || !this.validationResult?.individualTempFiles) {
+      throw new Error('No hay archivos temporales para procesar');
+    }
+
+    try {
+      const idUser = localStorage.getItem('idusuario');
+      
+      // Usar el endpoint de procesamiento del backend
+      const processBody = {
+        id_user: idUser,
+        linea: 1,
+        temp_files: this.validationResult.tempFiles,
+        individual_temp_files: this.validationResult.individualTempFiles
+      };
+
+      const processResponse = await this._gdelService.processZipFile(processBody).toPromise();
+      
+      if (processResponse.error) {
+        throw new Error(processResponse.message || 'Error en procesamiento');
+      }
+
+      console.log('Procesamiento completado:', processResponse);
+      
+      // Actualizar estado de documentos procesados
+      this.updateProcessedDocuments(processResponse);
+      
+    } catch (error: any) {
+      console.error('Error procesando documentos:', error);
+      throw error;
+    }
+  }
+
+  private updateProcessedDocuments(processResponse: any): void {
+    // Actualizar la lista de documentos procesados basado en la respuesta
+    if (processResponse.individual_files) {
+      this.processedDocuments = processResponse.individual_files.map((file: any) => ({
+        nombre_del_archivo: file.original_name,
+        tamano_del_archivo: '0 B', // El backend no devuelve tamaño
+        archivo_id: file.final_id,
+        tipo_archivo: this.getFileTypeFromExtension(file.original_name),
+        fecha_carga: new Date().toLocaleString(),
+        estado: 'final_saved' as const
+      }));
+    }
+  }
+
+  // MÉTODOS AUXILIARES QUE SE MANTIENEN
+  
+  private async validateFileStructure(file: File): Promise<{ isValid: boolean, errors: string[] }> {
+    return new Promise((resolve, reject) => {
+      const reader = new FileReader();
+      reader.onload = (e: any) => {
+        try {
+          const data = new Uint8Array(e.target.result);
+          const workbook = XLSX.read(data, { type: 'array' });
+          
+          const validation = this.validateExcelTemplate(workbook);
+          resolve(validation);
+        } catch (error) {
+          reject(error);
+        }
+      };
+      reader.onerror = reject;
+      reader.readAsArrayBuffer(file);
+    });
+  }
+
+  private async readExcelFile(file: File, fileId: any): Promise<void> {
+    return new Promise((resolve, reject) => {
+      const reader = new FileReader();
+      reader.onload = (e: any) => {
+        try {
+          const data = new Uint8Array(e.target.result);
+          const workbook = XLSX.read(data, { type: 'array' });
+          
+          const sheetNames = workbook.SheetNames;
+          const sheetsData: any = {};
+          
+          sheetNames.forEach(sheetName => {
+            const worksheet = workbook.Sheets[sheetName];
+            sheetsData[sheetName] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
+          });
+
+          this.excelData = {
+            fileName: file.name,
+            sheets: sheetsData,
+            sheetNames: sheetNames,
+            fileId: fileId,
+            originalFile: file // Mantener referencia al archivo original
+          };
 
+          resolve();
+        } catch (error) {
+          reject(error);
+        }
+      };
+      reader.onerror = reject;
+      reader.readAsArrayBuffer(file);
+    });
+  }
 
-  // Método para validar el contenido del ZIP
   private async validateZipContent(file: File): Promise<{ isValid: boolean, errors: string[] }> {
     return new Promise((resolve) => {
       const reader = new FileReader();
@@ -357,13 +630,11 @@ public processedDocuments: ProcessedDocument[] = [];
             if (!zipEntry.dir) {
               fileCount++;
               
-              // Para obtener el tamaño real, necesitamos leer el contenido
               try {
                 const content = await zipEntry.async('uint8array');
                 const fileSize = content.length;
                 
-                // Validar tamaño individual (10MB por archivo)
-                if (fileSize > 1000 * 1024 * 1024) {
+                if (fileSize > 10 * 1024 * 1024) {
                   errors.push(`El archivo ${fileName} es demasiado grande (supera los 10MB)`);
                 }
                 
@@ -374,12 +645,10 @@ public processedDocuments: ProcessedDocument[] = [];
             }
           }
           
-          // Validar tamaño total descomprimido (100MB total)
           if (totalSize > 250 * 1024 * 1024) {
-            errors.push('El contenido total del ZIP supera los 100MB');
+            errors.push('El contenido total del ZIP supera los 250MB');
           }
           
-          // Validar número máximo de archivos (opcional)
           if (fileCount > 1000) {
             errors.push('El ZIP contiene demasiados archivos (máximo 1000)');
           }
@@ -406,7 +675,6 @@ public processedDocuments: ProcessedDocument[] = [];
     });
   }
 
-  // Método para leer el contenido del ZIP
   private async readZipFile(file: File, fileId: string): Promise<void> {
     return new Promise((resolve, reject) => {
       const reader = new FileReader();
@@ -420,40 +688,16 @@ public processedDocuments: ProcessedDocument[] = [];
           let totalSize = 0;
           let fileCount = 0;
           
-          // Procesar cada entrada del ZIP
           for (const [relativePath, zipEntry] of Object.entries(zipContent.files)) {
             if (!zipEntry.dir) {
-              // Es un archivo
               try {
-                // Leer el contenido para obtener el tamaño real
                 const content = await zipEntry.async('uint8array');
                 const fileSize = content.length;
                 totalSize += fileSize;
                 fileCount++;
                 
-                // Determinar el tipo de archivo
                 const extension = relativePath.split('.').pop()?.toLowerCase();
-                let fileType = 'unknown';
-                
-                if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'].includes(extension || '')) {
-                  fileType = 'image';
-                } else if (['pdf'].includes(extension || '')) {
-                  fileType = 'pdf';
-                } else if (['doc', 'docx'].includes(extension || '')) {
-                  fileType = 'document';
-                } else if (['txt', 'md', 'readme'].includes(extension || '')) {
-                  fileType = 'text';
-                } else if (['xlsx', 'xls', 'csv'].includes(extension || '')) {
-                  fileType = 'excel';
-                } else if (['ppt', 'pptx'].includes(extension || '')) {
-                  fileType = 'presentation';
-                } else if (['zip', 'rar', '7z'].includes(extension || '')) {
-                  fileType = 'archive';
-                } else if (['mp4', 'avi', 'mov', 'wmv'].includes(extension || '')) {
-                  fileType = 'video';
-                } else if (['mp3', 'wav', 'ogg'].includes(extension || '')) {
-                  fileType = 'audio';
-                }
+                let fileType = this.determineFileType(extension);
                 
                 files.push({
                   name: relativePath,
@@ -464,16 +708,14 @@ public processedDocuments: ProcessedDocument[] = [];
                 
               } catch (fileError) {
                 console.warn(`No se pudo leer el archivo ${relativePath}:`, fileError);
-                // Agregar el archivo con tamaño estimado
                 files.push({
                   name: relativePath,
-                  size: 0, // No pudimos obtener el tamaño
+                  size: 0,
                   isDirectory: false,
                   type: 'unknown'
                 });
               }
             } else {
-              // Es un directorio
               files.push({
                 name: relativePath,
                 size: 0,
@@ -486,7 +728,6 @@ public processedDocuments: ProcessedDocument[] = [];
           this.zipData = {
             fileName: file.name,
             files: files.sort((a, b) => {
-              // Ordenar: directorios primero, luego archivos por nombre
               if (a.isDirectory && !b.isDirectory) return -1;
               if (!a.isDirectory && b.isDirectory) return 1;
               return a.name.localeCompare(b.name);
@@ -507,116 +748,32 @@ public processedDocuments: ProcessedDocument[] = [];
     });
   }
 
-  async uploadExcelFile(fileData: File): Promise<void> {
-    try {
-      // Primero validar la estructura antes de subir
-      const validationResult = await this.validateFileStructure(fileData);
-      
-      if (!validationResult.isValid) {
-        this._resourcesService.openSnackBar(`Debe contener al menos un registro y cumplir con la estructura requerida`);
-        this.isUploading = false;
-        return;
-      }
-
-      const idUser = localStorage.getItem('idusuario');
-      const formData = new FormData();
-
-      formData.append('file', fileData);
-      formData.append('id_user', idUser!);
-      formData.append('linea', "1");
-      formData.append('tipo_archivo', 'excel');
-
-      const response = await lastValueFrom(this._gdelService.uploadTempFile(formData));
-
-      if (response.error) {
-        this._resourcesService.openSnackBar(response.msg);
-      } else {
-        this.uploadedFile = {
-          id: response.response.idArchivo,
-          name: fileData.name,
-          size: this.formatBytes(fileData.size)
-        };
-
-        await this.readExcelFile(fileData, response.response.idArchivo);
-        
-        const newDocument: Document = {
-          nombre_del_archivo: fileData.name,
-          tamano_del_archivo: this.formatBytes(fileData.size),
-          archivo_id: response.response.idArchivo,
-          tipo_archivo: 'excel',
-          fecha_carga: new Date().toLocaleString()
-        };
-
-        this.excelFile = newDocument;
-        this.updateTableData();
-        this._resourcesService.openSnackBar('Plantilla Excel válida y cargada exitosamente');
-      }
-
-      this.isUploading = false;
-    } catch (error: any) {
-      this.handleUploadError(error);
-      this.isUploading = false;
+  private determineFileType(extension?: string): string {
+    if (!extension) return 'unknown';
+    
+    if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'].includes(extension)) {
+      return 'image';
+    } else if (['pdf'].includes(extension)) {
+      return 'pdf';
+    } else if (['doc', 'docx'].includes(extension)) {
+      return 'document';
+    } else if (['txt', 'md', 'readme'].includes(extension)) {
+      return 'text';
+    } else if (['xlsx', 'xls', 'csv'].includes(extension)) {
+      return 'excel';
+    } else if (['ppt', 'pptx'].includes(extension)) {
+      return 'presentation';
+    } else if (['zip', 'rar', '7z'].includes(extension)) {
+      return 'archive';
+    } else if (['mp4', 'avi', 'mov', 'wmv'].includes(extension)) {
+      return 'video';
+    } else if (['mp3', 'wav', 'ogg'].includes(extension)) {
+      return 'audio';
     }
-  }
-
-  private async testndtry (er: any) {
     
-  }
-  
-  // Método para validar la estructura del archivo antes de subirlo
-  private async validateFileStructure(file: File): Promise<{ isValid: boolean, errors: string[] }> {
-    return new Promise((resolve, reject) => {
-      const reader = new FileReader();
-      reader.onload = (e: any) => {
-        try {
-          const data = new Uint8Array(e.target.result);
-          const workbook = XLSX.read(data, { type: 'array' });
-          
-          const validation = this.validateExcelTemplate(workbook);
-          resolve(validation);
-        } catch (error) {
-          reject(error);
-        }
-      };
-      reader.onerror = reject;
-      reader.readAsArrayBuffer(file);
-    });
-  }
-
-  private async readExcelFile(file: File, fileId: any): Promise<void> {
-    return new Promise((resolve, reject) => {
-      const reader = new FileReader();
-      reader.onload = (e: any) => {
-        try {
-          const data = new Uint8Array(e.target.result);
-          const workbook = XLSX.read(data, { type: 'array' });
-          
-          const sheetNames = workbook.SheetNames;
-          const sheetsData: any = {};
-          
-          sheetNames.forEach(sheetName => {
-            const worksheet = workbook.Sheets[sheetName];
-            sheetsData[sheetName] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
-          });
-
-          this.excelData = {
-            fileName: file.name,
-            sheets: sheetsData,
-            sheetNames: sheetNames,
-            fileId: fileId
-          };
-
-          resolve();
-        } catch (error) {
-          reject(error);
-        }
-      };
-      reader.onerror = reject;
-      reader.readAsArrayBuffer(file);
-    });
+    return 'unknown';
   }
 
-
   formatBytes(bytes: number, decimals = 2): string {
     if (bytes === 0) return '0 Bytes';
 
@@ -674,1204 +831,296 @@ public processedDocuments: ProcessedDocument[] = [];
     });
   }
 
+  // Método para mostrar previsualización del ZIP
+  previewZipFile(): void {
+    if (!this.zipData) {
+      this._resourcesService.openSnackBar('No hay datos del archivo ZIP para mostrar');
+      return;
+    }
+
+    const dialogRef = this._matDialog.open(PreviewFileContentComponent, {
+      width: '1200px',
+      height: '800px',
+      maxWidth: '1200px',
+      data: {
+        fileName: this.zipData.fileName,
+        fileType: 'zip',
+        zipContent: this.zipData,
+        fileId: this.zipData.fileId,
+        originalFile: this.originalZipFile, // Pasar archivo original
+        excelData: this.excelData // Pasar datos del Excel para validación
+      },
+      disableClose: true
+    });
 
+    dialogRef.afterClosed().subscribe(result => {
+      if (result && result.action === 'validate' && result.validationResult) {
+        console.log('Validación desde preview completada:', result);
+        
+        // Procesar resultado de validación desde preview
+        this.validationResult = result.validationResult;
+        
+        if (this.validationResult?.isValid) {
+          this._resourcesService.openSnackBar('Validación exitosa desde preview');
+          // Opcionalmente cerrar el diálogo principal
+          this.dialogRef.close({ 
+            success: true, 
+            validationResult: this.validationResult 
+          });
+        }
+      }
+    });
+  }
 
   // Métodos para eliminar archivos específicos
   removeExcelFile(): void {
-    this.deleteTempFile('excel');
+    this.originalExcelFile = null;
+    this.excelFile = null;
+    this.excelData = null;
+    this.updateTableData();
+    this._resourcesService.openSnackBar('Archivo Excel eliminado');
   }
 
   removeZipFile(): void {
-    this.deleteTempFile('zip');
-  }
-
-  // Getters para verificar si hay archivos cargados
-  get hasExcelFile(): boolean {
-    return this.excelFile !== null;
+    this.originalZipFile = null;
+    this.zipFile = null;
+    this.zipData = null;
+    this.updateTableData();
+    this._resourcesService.openSnackBar('Archivo ZIP eliminado');
   }
 
-  get hasZipFile(): boolean {
-    return this.zipFile !== null;
-  }
+  // Método para extraer nombres de documentos del Excel (para mostrar estadísticas)
+  public extractDocumentNamesFromExcel(): ExcelDocumentEntry[] {
+    if (!this.excelData || !this.excelData.sheets['CARGA DE DOCUMENTOS']) {
+      return [];
+    }
 
-  get hasAnyFile(): boolean {
-    return this.hasExcelFile || this.hasZipFile;
-  }
-
-  // Getter para obtener información del ZIP
-  get zipInfo(): string {
-    if (!this.zipData) return '';
-    return `${this.zipData.totalFiles} archivos (${this.formatBytes(this.zipData.totalSize)})`;
-  }
-
- 
-
-
-
-
-
-
-
-private async processValidatedFiles(validationResult: any): Promise<void> {
-  if (!validationResult.foundFiles || validationResult.foundFiles.length === 0) {
-    this._resourcesService.openSnackBar('No hay archivos validados para procesar');
-    return;
-  }
-
-  try {
-    this._resourcesService.openSnackBar(`Procesando ${validationResult.foundFiles.length} archivos validados...`);
-     
-    console.log('Archivos encontrados para procesar:', validationResult.foundFiles);
-    console.log('Detalles de validación:', validationResult.validationDetails);
-    
-  await this.processIndividualFiles(validationResult.foundFiles);
+    const documentEntries: ExcelDocumentEntry[] = [];
+    const sheet = this.excelData.sheets['CARGA DE DOCUMENTOS'];
     
-  } catch (error) {
-    console.error('Error al procesar archivos validados:', error);
-    this._resourcesService.openSnackBar('Error al procesar los archivos validados');
-  }
-}
-
+    // Definir las columnas de documentos y sus categorías
+    const DOCUMENT_COLUMNS = [
+      { letter: 'H', index: 7, category: 'FICHA DE SEGURIDAD DE PRODUCTOS QUÍMICOS' },
+      { letter: 'J', index: 9, category: 'FICHA TÉCNICA' },
+      { letter: 'L', index: 11, category: 'FOTOGRAFÍAS - DIAGRAMAS' },
+      { letter: 'N', index: 13, category: 'DATOS DEL PROVEEDOR' },
+      { letter: 'P', index: 15, category: 'MANUALES DE OPERACIÓN' },
+      { letter: 'R', index: 17, category: 'MANUALES DE MANTENIMIENTO PREVENTIVO' },
+      { letter: 'T', index: 19, category: 'MANUALES DE MANTENIMIENTO CORRECTIVO' },
+      { letter: 'V', index: 21, category: 'DOCUMENTOS ADICIONALES' }
+    ];
+
+    const DATA_START_ROW = 9; // Fila 9 en Excel (índice 8 en array)
 
-private async processIndividualFiles(foundFiles: string[]): Promise<void> {
-  for (const fileName of foundFiles) {
     try {
-      console.log(`Procesando archivo: ${fileName}`);
-      
-      
-    } catch (error) {
-      console.error(`Error al procesar archivo ${fileName}:`, error);
-    }
-  }
-}
-
-
-
-// Método para mostrar resumen de archivos esperados (opcional):
-
-public getExpectedFilesFromExcel(): string[] {
-  if (!this.excelData) return [];
-  
-  const expectedFiles: string[] = [];
-  const TEMPLATE_COLUMNS = ['H', 'J', 'L', 'N', 'P', 'R', 'T', 'V'];
-  const DATA_START_ROW = 9;
-  
-  try {
-    const sheet = this.excelData.sheets['CARGA DE DOCUMENTOS'];
-    if (!sheet) return [];
-
-    for (let rowIndex = DATA_START_ROW - 1; rowIndex < sheet.length; rowIndex++) {
-      const row = sheet[rowIndex];
-      if (!row) continue;
-
-      TEMPLATE_COLUMNS.forEach(column => {
-        const columnIndex = this.getColumnIndex(column);
-        const cellValue = row[columnIndex];
-        
-        if (cellValue && typeof cellValue === 'string' && cellValue.trim() !== '') {
-          const fileName = cellValue.trim();
-          if (this.hasValidFileExtension(fileName)) {
-            expectedFiles.push(fileName);
-          }
+      // Recorrer desde la fila 9 hacia abajo
+      for (let rowIndex = DATA_START_ROW - 1; rowIndex < sheet.length; rowIndex++) {
+        const row = sheet[rowIndex];
+        if (!row) continue;
+
+        // Verificar si la fila tiene datos principales (equipos)
+        const equipmentCode = row[3]; // Columna D
+        if (!equipmentCode || typeof equipmentCode !== 'string' || equipmentCode.trim() === '') {
+          continue;
         }
-      });
-    }
-  } catch (error) {
-    console.error('Error al extraer archivos esperados:', error);
-  }
-
-  return expectedFiles;
-}
-
-private getColumnIndex(column: string): number {
-  const columnMap: { [key: string]: number } = {
-    'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7,
-    'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15,
-    'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23
-  };
-  return columnMap[column] || 0;
-}
-
-
 
-// Método para extraer nombres de documentos del Excel
-public extractDocumentNamesFromExcel(): ExcelDocumentEntry[] {
-  if (!this.excelData || !this.excelData.sheets['CARGA DE DOCUMENTOS']) {
-    return [];
-  }
-
-  const documentEntries: ExcelDocumentEntry[] = [];
-  const sheet = this.excelData.sheets['CARGA DE DOCUMENTOS'];
-  
-  // Definir las columnas de documentos y sus categorías
-  const DOCUMENT_COLUMNS = [
-    { letter: 'H', index: 7, category: 'FICHA DE SEGURIDAD DE PRODUCTOS QUÍMICOS' },
-    { letter: 'J', index: 9, category: 'FICHA TÉCNICA' },
-    { letter: 'L', index: 11, category: 'FOTOGRAFÍAS - DIAGRAMAS' },
-    { letter: 'N', index: 13, category: 'DATOS DEL PROVEEDOR' },
-    { letter: 'P', index: 15, category: 'MANUALES DE OPERACIÓN' },
-    { letter: 'R', index: 17, category: 'MANUALES DE MANTENIMIENTO PREVENTIVO' },
-    { letter: 'T', index: 19, category: 'MANUALES DE MANTENIMIENTO CORRECTIVO' },
-    { letter: 'V', index: 21, category: 'DOCUMENTOS ADICIONALES' }
-  ];
-
-  const DATA_START_ROW = 9; // Fila 9 en Excel (índice 8 en array)
-
-  try {
-    // Recorrer desde la fila 9 hacia abajo
-    for (let rowIndex = DATA_START_ROW - 1; rowIndex < sheet.length; rowIndex++) {
-      const row = sheet[rowIndex];
-      if (!row) continue;
-
-      // Verificar si la fila tiene datos principales (columnas A, C, E)
-      const hasMainData = row[1] || row[3] || row[5]; 
-      if (!hasMainData) continue; // Saltar filas vacías
-
-      // Revisar cada columna de documentos
-      DOCUMENT_COLUMNS.forEach(column => {
-        const cellValue = row[column.index];
-        
-        if (cellValue && typeof cellValue === 'string' && cellValue.trim() !== '') {
-          const documentName = cellValue.trim();
+        // Revisar cada columna de documentos
+        DOCUMENT_COLUMNS.forEach(column => {
+          const cellValue = row[column.index];
           
-          // Validar que tenga una extensión de archivo válida
-          if (this.hasValidFileExtension(documentName)) {
-            documentEntries.push({
-              rowIndex: rowIndex + 1, // +1 para mostrar número de fila real
-              columnLetter: column.letter,
-              columnIndex: column.index,
-              documentName: documentName,
-              category: column.category
+          if (cellValue && typeof cellValue === 'string' && cellValue.trim() !== '') {
+            const documentNames = cellValue.split(',').map(name => name.trim());
+            
+            documentNames.forEach(documentName => {
+              if (documentName && this.hasValidFileExtension(documentName)) {
+                documentEntries.push({
+                  rowIndex: rowIndex + 1, // +1 para mostrar número de fila real
+                  columnLetter: column.letter,
+                  columnIndex: column.index,
+                  documentName: documentName,
+                  category: column.category
+                });
+              }
             });
           }
-        }
-      });
-    }
-  } catch (error) {
-    console.error('Error al extraer nombres de documentos del Excel:', error);
-  }
-
-  return documentEntries;
-}
-
-// Método para validar documentos entre Excel y ZIP
-public validateDocumentsInZip(): DocumentValidationResult {
-  const expectedDocuments = this.extractDocumentNamesFromExcel();
-  const expectedFileNames = expectedDocuments.map(doc => doc.documentName.toLowerCase());
-  
-  if (!this.zipData || !this.zipData.files) {
-    return {
-      isValid: false,
-      foundFiles: [],
-      missingFiles: expectedFileNames,
-      extraFiles: [],
-      validationDetails: {
-        expectedCount: expectedFileNames.length,
-        foundCount: 0,
-        matchedCount: 0
+        });
       }
-    };
-  }
-
-  // Obtener nombres de archivos del ZIP (solo archivos, no directorios)
-  const zipFileNames = this.zipData.files
-    .filter(file => !file.isDirectory)
-    .map(file => {
-      // Obtener solo el nombre del archivo sin la ruta
-      const fileName = file.name.split('/').pop() || file.name;
-      return fileName.toLowerCase();
-    });
-
-  // Encontrar archivos que coinciden
-  const foundFiles: string[] = [];
-  const missingFiles: string[] = [];
-
-  expectedFileNames.forEach(expectedFile => {
-    const found = zipFileNames.find(zipFile => {
-      // Comparación exacta de nombres
-      return zipFile === expectedFile;
-    });
-
-    if (found) {
-      foundFiles.push(expectedFile);
-    } else {
-      missingFiles.push(expectedFile);
-    }
-  });
-
-  // Encontrar archivos extra (en ZIP pero no en Excel)
-  const extraFiles = zipFileNames.filter(zipFile => 
-    !expectedFileNames.includes(zipFile)
-  );
-
-  const isValid = missingFiles.length === 0 && foundFiles.length > 0;
-
-  return {
-    isValid,
-    foundFiles,
-    missingFiles,
-    extraFiles,
-    validationDetails: {
-      expectedCount: expectedFileNames.length,
-      foundCount: foundFiles.length,
-      matchedCount: foundFiles.length
+    } catch (error) {
+      console.error('Error al extraer nombres de documentos del Excel:', error);
     }
-  };
-}
-
 
-
-// Método para procesar archivos en lotes
-private async processBatchFiles(validationResult: DocumentValidationResult): Promise<void> {
-  const idUser = localStorage.getItem('idusuario');
-  
-  if (!idUser || !this.uploadedFile) {
-    throw new Error('No se encontró información del usuario o archivo');
+    return documentEntries;
   }
 
-  // Procesar archivos en lotes para evitar sobrecargar el servidor
-  const batchSize = 3; // Procesar de 3 en 3
-  const foundFiles = validationResult.foundFiles;
-  
-  for (let i = 0; i < foundFiles.length; i += batchSize) {
-    const batch = foundFiles.slice(i, i + batchSize);
+  // Método auxiliar para validar extensiones de archivo
+  private hasValidFileExtension(fileName: string): boolean {
+    if (!fileName || !fileName.includes('.')) return false;
     
-    // Procesar lote actual
-    await Promise.all(batch.map(async (fileName) => {
-      try {
-        // Primero saveTempFile
-        await this.saveTempFileForDocument(fileName, idUser);
-        
-        // Luego saveFinalFile
-        await this.saveFinalFileForDocument(fileName, idUser);
-        
-        console.log(`Documento procesado exitosamente: ${fileName}`);
-      } catch (error) {
-        console.error(`Error al procesar documento ${fileName}:`, error);
-        throw error; // Re-lanzar error para detener el proceso
-      }
-    }));
+    const validExtensions = [
+      '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
+      '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp',
+      '.txt', '.rtf', '.csv', '.xml', '.json',
+      '.zip', '.rar', '.7z',
+      '.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv',
+      '.mp3', '.wav', '.ogg', '.aac', '.flac', '.dwg', '.dxf', '.ai', '.psd'
+    ];
     
-    // Pequeña pausa entre lotes
-    if (i + batchSize < foundFiles.length) {
-      await new Promise(resolve => setTimeout(resolve, 500));
-    }
+    const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
+    return validExtensions.includes(extension);
   }
-}
 
-// Método para saveTempFile de un documento específico
-private async saveTempFileForDocument(fileName: string, idUser: string): Promise<any> {
-  const formData = new FormData();
-  
-  formData.append('id_user', idUser);
-  formData.append('file_name', fileName);
-  formData.append('zip_file_id', this.uploadedFile!.id);
-  formData.append('linea', '1');
-  formData.append('tipo_archivo','');
-
-  const response = await lastValueFrom(this._gdelService.uploadTempFile(formData));
-  
-  if (response.error) {
-    throw new Error(`Error en saveTempFile para ${fileName}: ${response.msg}`);
-  }
-  
-  return response;
-}
-
-// Método para saveFinalFile de un documento específico
-private async saveFinalFileForDocument(fileName: string, idUser: string): Promise<any> {
-  const formData = new FormData();
-  
-  formData.append('id_user', idUser);
-  formData.append('file_name', fileName);
-  formData.append('zip_file_id', this.uploadedFile!.id);
-  formData.append('linea', '1');
-  formData.append('module', 'ADSI');
-  formData.append('clasification', 'LA');
-  formData.append('has_order', 'N');
-
-  const response = await lastValueFrom(this._gdelService.saveFinalFile(formData));
-  
-  if (response.error) {
-    throw new Error(`Error en saveFinalFile para ${fileName}: ${response.msg}`);
-  }
-  
-  return response;
-}
-
-// Método actualizado para validar extensiones de archivo
-private hasValidFileExtension(fileName: string): boolean {
-  if (!fileName || !fileName.includes('.')) return false;
-  
-  const validExtensions = [
-    '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
-    '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp',
-    '.txt', '.rtf', '.csv', '.xml', '.json',
-    '.zip', '.rar', '.7z',
-    '.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv',
-    '.mp3', '.wav', '.ogg', '.aac', '.flac'
-  ];
-  
-  const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
-  return validExtensions.includes(extension);
-}
-
-// Método para obtener estadísticas de validación
-public getValidationSummary(): string {
-  if (!this.canValidateFiles) return 'Cargue ambos archivos para ver el resumen';
-  
-  const validation = this.validateDocumentsInZip();
-  const { expectedCount, foundCount, matchedCount } = validation.validationDetails;
-  
-  if (expectedCount === 0) return 'No se encontraron documentos en el Excel';
-  
-  return `${matchedCount}/${expectedCount} documentos encontrados en el ZIP`;
-}
-
-// Getter actualizado para verificar si se puede validar
-get canValidateFiles(): boolean {
-  return this.hasExcelFile && this.hasZipFile && 
-         this.excelData && this.zipData && 
-         this.extractDocumentNamesFromExcel().length > 0;
-}
-
-
-
-
-
-// Método para procesar archivos en lotes desde preview
-private async processBatchFilesFromPreview(foundFiles: string[]): Promise<void> {
-  const idUser = localStorage.getItem('idusuario');
-  
-  if (!idUser || !this.uploadedFile) {
-    throw new Error('No se encontró información del usuario o archivo');
-  }
-
-  // Procesar archivos en lotes para evitar sobrecargar el servidor
-  const batchSize = 3; // Procesar de 3 en 3
-  
-  for (let i = 0; i < foundFiles.length; i += batchSize) {
-    const batch = foundFiles.slice(i, i + batchSize);
+  private getFileTypeFromExtension(fileName: string): string {
+    const extension = fileName.toLowerCase().split('.').pop();
     
-    // Procesar lote actual
-    await Promise.all(batch.map(async (fileName) => {
-      try {
-        console.log(`Procesando documento: ${fileName}`);
-        
-        // Primero saveTempFile
-        const tempResult = await this.saveTempFileForDocument(fileName, idUser);
-        console.log(`saveTempFile exitoso para ${fileName}:`, tempResult);
-        
-        // Luego saveFinalFile
-        const finalResult = await this.saveFinalFileForDocument(fileName, idUser);
-        console.log(`saveFinalFile exitoso para ${fileName}:`, finalResult);
-        
-      } catch (error) {
-        console.error(`Error al procesar documento ${fileName}:`, error);
-        throw error; // Re-lanzar error para detener el proceso
-      }
-    }));
+    const typeMap: { [key: string]: string } = {
+      'pdf': 'pdf',
+      'doc': 'docx',
+      'docx': 'docx',
+      'xlsx': 'excel',
+      'xls': 'excel',
+      'jpg': 'imagen',
+      'jpeg': 'imagen',
+      'png': 'imagen',
+      'gif': 'imagen',
+      'bmp': 'imagen',
+      'svg': 'imagen',
+      'txt': 'texto',
+      'zip': 'archivo',
+      'rar': 'archivo'
+    };
     
-    // Pequeña pausa entre lotes
-    if (i + batchSize < foundFiles.length) {
-      await new Promise(resolve => setTimeout(resolve, 1000));
-    }
+    return typeMap[extension || ''] || 'documento';
   }
-}
-
-
 
-
-
-// Método alternativo para validar y procesar directamente (sin preview)
-public async validateAndProcessDocuments(): Promise<void> {
-  if (!this.canValidateFiles) {
-    this._resourcesService.openSnackBar('Debe cargar tanto el archivo Excel como el ZIP antes de validar');
-    return;
-  }
-
-  try {
-    this.isLoading = true;
-    
-    // Extraer documentos esperados del Excel
-    const expectedDocuments = this.extractDocumentNamesFromExcel();
-    
-    if (expectedDocuments.length === 0) {
-      this._resourcesService.openSnackBar('No se encontraron documentos en el Excel para validar');
+  // Método para mostrar resumen de validación
+  public showValidationSummary(): void {
+    if (!this.canValidate) {
+      this._resourcesService.openSnackBar('Debe cargar tanto el archivo Excel como el ZIP antes de validar');
       return;
     }
 
-    // Validar documentos
-    const validationResult = this.validateDocumentsInZip();
-    
-    if (!validationResult.isValid || validationResult.foundFiles.length === 0) {
-      const missingCount = validationResult.missingFiles.length;
-      const message = missingCount > 0 
-        ? `Faltan ${missingCount} documentos en el ZIP. Documentos faltantes: ${validationResult.missingFiles.slice(0, 3).join(', ')}${missingCount > 3 ? '...' : ''}`
-        : 'No se encontraron documentos válidos para procesar';
-      
-      this._resourcesService.openSnackBar(message);
-      return;
-    }
-
-    // Mostrar resultado de validación
-    const foundCount = validationResult.foundFiles.length;
-    const expectedCount = validationResult.validationDetails.expectedCount;
+    const expectedDocuments = this.extractDocumentNamesFromExcel();
     
-    this._resourcesService.openSnackBar(
-      `Validación exitosa: ${foundCount}/${expectedCount} documentos encontrados. Procesando...`
-    );
-
-    // Procesar cada archivo encontrado
-    await this.processBatchFilesFromPreview(validationResult.foundFiles);
-
-    this._resourcesService.openSnackBar('Validación y procesamiento completado exitosamente');
-    this.dialogRef.close({ 
-      success: true, 
-      validationResult,
-      processedCount: foundCount 
-    });
-
-  } catch (error: any) {
-    console.error('Error en validación y procesamiento:', error);
-    this._resourcesService.openSnackBar('Error: ' + (error.message || 'Error desconocido'));
-  } finally {
-    this.isLoading = false;
-  }
-}
-
-// Método para mostrar resumen detallado de validación
-public showValidationSummary(): void {
-  if (!this.canValidateFiles) {
-    this._resourcesService.openSnackBar('Debe cargar tanto el archivo Excel como el ZIP antes de validar');
-    return;
+    console.log('=== RESUMEN ANTES DE VALIDACIÓN ===');
+    console.log('Documentos esperados del Excel:', expectedDocuments.length);
+    console.log('Archivos en ZIP:', this.zipData?.totalFiles || 0);
+
+    const summary = `
+    RESUMEN PREVIO:
+    • Documentos esperados en Excel: ${expectedDocuments.length}
+    • Archivos en ZIP: ${this.zipData?.totalFiles || 0}
+    • Estado: Listo para validar
+    `;
+
+    this._resourcesService.openSnackBar(summary);
   }
 
-  const expectedDocuments = this.extractDocumentNamesFromExcel();
-  const validationResult = this.validateDocumentsInZip();
-
-  console.log('=== RESUMEN DE VALIDACIÓN ===');
-  console.log('Documentos esperados del Excel:', expectedDocuments);
-  console.log('Resultado de validación:', validationResult);
-  console.log('Archivos encontrados:', validationResult.foundFiles);
-  console.log('Archivos faltantes:', validationResult.missingFiles);
-  console.log('Archivos extra en ZIP:', validationResult.extraFiles);
-
-  const summary = `
-     RESUMEN DE VALIDACIÓN:
-    • Documentos esperados: ${validationResult.validationDetails.expectedCount}
-    • Documentos encontrados: ${validationResult.validationDetails.foundCount}
-    • Estado: ${validationResult.isValid ? ' VÁLIDO' : ' INCOMPLETO'}
-    ${validationResult.missingFiles.length > 0 ? `\n• Faltan: ${validationResult.missingFiles.join(', ')}` : ''}
-  `;
-
-  this._resourcesService.openSnackBar(summary);
-}
-
-// Getter para verificar el estado de validación en tiempo real
-get validationStatus(): string {
-  if (!this.canValidateFiles) return 'Sin datos para validar';
-  
-  const expectedDocuments = this.extractDocumentNamesFromExcel();
-  if (expectedDocuments.length === 0) return 'Sin documentos en Excel';
-  
-  const validationResult = this.validateDocumentsInZip();
-  const { expectedCount, foundCount } = validationResult.validationDetails;
-  
-  return `${foundCount}/${expectedCount} documentos encontrados`;
-}
-
-// Método para limpiar datos de validación
-public clearValidationData(): void {
-  // Este método se puede usar si necesitas resetear el estado de validación
-  console.log('Limpiando datos de validación...');
-}
-
-
-
-// Método para exportar datos de validación 
-public exportValidationReport(): void {
-  if (!this.canValidateFiles) return;
-
-  const expectedDocuments = this.extractDocumentNamesFromExcel();
-  const validationResult = this.validateDocumentsInZip();
-
-  const report = {
-    timestamp: new Date().toISOString(),
-    excelFile: this.excelFile?.nombre_del_archivo,
-    zipFile: this.zipFile?.nombre_del_archivo,
-    expectedDocuments: expectedDocuments,
-    validation: validationResult,
-    summary: {
-      expected: validationResult.validationDetails.expectedCount,
-      found: validationResult.validationDetails.foundCount,
-      missing: validationResult.missingFiles.length,
-      extra: validationResult.extraFiles.length,
-      isComplete: validationResult.isValid
-    }
-  };
-
-    const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
-  const url = window.URL.createObjectURL(blob);
-  const a = document.createElement('a');
-  a.href = url;
-  a.download = `validation-report-${Date.now()}.json`;
-  a.click();
-  window.URL.revokeObjectURL(url);
-}
-
-
-
-private async extractFileFromZip(fileName: string): Promise<File | null> {
-  if (!this.zipData) return null;
-
-  return new Promise((resolve) => {
-    const reader = new FileReader();
-
-    reader.onload = async (e: any) => {
-      try {
-        const zipData = new Uint8Array(e.target.result);
-        const zip = new JSZip();
-        const zipContent = await zip.loadAsync(zipData);
-        
-        // Buscar el archivo específico en el ZIP
-        const targetFile = Object.entries(zipContent.files).find(([path, entry]) => {
-          const entryFileName = path.split('/').pop()?.toLowerCase();
-          return entryFileName === fileName.toLowerCase() && !entry.dir;
-        });
-
-        if (targetFile) {
-          const [filePath, zipEntry] = targetFile;
-          const content = await zipEntry.async('blob');
-          const file = new File([content], fileName, { 
-            type: this.getMimeType(fileName) 
-          });
-          resolve(file);
-        } else {
-          resolve(null);
-        }
-      } catch (error) {
-        console.error('Error al extraer archivo del ZIP:', error);
-        resolve(null);
-      }
-    };
-    reader.onerror = () => resolve(null);
-    
-    // Aquí necesitas acceso al archivo ZIP original
-    // Debes guardar una referencia al File original del ZIP
-    if (this.originalZipFile) {
-      reader.readAsArrayBuffer(this.originalZipFile);
-    } else {
-      resolve(null);
-    }
-  });
-}
-
-// Método auxiliar para obtener MIME type
-private getMimeType(fileName: string): string {
-  const extension = fileName.toLowerCase().split('.').pop();
-  const mimeTypes: { [key: string]: string } = {
-    'pdf': 'application/pdf',
-    'doc': 'application/msword',
-    'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-    'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
-    'xls': 'application/vnd.ms-excel',
-    'jpg': 'image/jpeg',
-    'jpeg': 'image/jpeg',
-    'png': 'image/png',
-    'gif': 'image/gif',
-    'bmp': 'image/bmp',
-    'svg': 'image/svg+xml',
-    'txt': 'text/plain'
-  };
-  return mimeTypes[extension || ''] || 'application/octet-stream';
-}
-
-// Método corregido para subir archivo individual desde ZIP
-private async uploadTempFileFromZip(fileName: string): Promise<string | null> {
-  try {
-    // Extraer el archivo individual del ZIP
-    const individualFile = await this.extractFileFromZip(fileName);
-    
-    if (!individualFile) {
-      throw new Error(`No se pudo extraer el archivo ${fileName} del ZIP`);
-    }
-
-    const idUser = localStorage.getItem('idusuario');
-    const formData = new FormData();
-
-    formData.append('file', individualFile);
-    formData.append('id_user', idUser!);
-    formData.append('linea', '1');
-    
-    // Determinar tipo de archivo basado en extensión
-    const extension = fileName.toLowerCase().split('.').pop();
-    let tipoArchivo = 'documento';
-    
-    if (['pdf'].includes(extension || '')) {
-      tipoArchivo = 'pdf';
-    } else if (['doc', 'docx'].includes(extension || '')) {
-      tipoArchivo = 'docx';
-    } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(extension || '')) {
-      tipoArchivo = 'img';
-    } else if (['xlsx', 'xls'].includes(extension || '')) {
-      tipoArchivo = 'excel';
-    }
-    
-    formData.append('tipo_archivo', tipoArchivo);
-
-    const response = await lastValueFrom(this._gdelService.uploadTempFile(formData));
-
-    if (response.error) {
-      throw new Error(response.msg || `Error al subir ${fileName}`);
-    }
-
-    console.log(` uploadTempFile exitoso para ${fileName}:`, response.response.idArchivo);
-    return response.response.idArchivo;
-
-  } catch (error: any) {
-    console.error(` Error en uploadTempFile para ${fileName}:`, error);
-    throw error;
+  // Método para limpiar todos los datos
+  public clearAllData(): void {
+    this.originalExcelFile = null;
+    this.originalZipFile = null;
+    this.excelFile = null;
+    this.excelData = null;
+    this.zipFile = null;
+    this.zipData = null;
+    this.validationResult = null;
+    this.processedDocuments = [];
+    this.currentFileType = null;
+    this.updateTableData();
+    this._resourcesService.openSnackBar('Todos los datos han sido limpiados');
   }
-}
-
-// Método corregido para guardar archivo final individual
-private async saveFinalFileIndividual(fileId: string, fileName: string): Promise<void> {
-  try {
-    const idUser = localStorage.getItem('idusuario');
-    const formData = new FormData();
 
-    // Parámetros obligatorios
-    formData.append('id_user', idUser!);
-    formData.append('id_file', fileId);
-    formData.append('linea', '1');
-    
-    // Parámetros específicos para equipamiento
-    formData.append('module', 'ADSI');
-    formData.append('clasification', 'LA');
-    formData.append('has_order', 'N');
-
-    const response = await lastValueFrom(this._gdelService.saveFinalFile(formData));
-
-    if (response.error) {
-      throw new Error(response.msg || `Error al guardar ${fileName}`);
-    }
-
-    console.log(` saveFinalFile exitoso para ${fileName}:`, response);
-
-  } catch (error: any) {
-    console.error(` Error en saveFinalFile para ${fileName}:`, error);
-    throw error;
+  // Getters para verificar estado
+  get hasExcelFile(): boolean {
+    return this.excelFile !== null && this.originalExcelFile !== null;
   }
-}
 
-// Método principal corregido para procesar documentos validados
-public async processValidatedDocuments(): Promise<void> {
-  if (!this.canValidateFiles) {
-    this._resourcesService.openSnackBar('Debe cargar tanto el archivo Excel como el ZIP antes de validar');
-    return;
+  get hasZipFile(): boolean {
+    return this.zipFile !== null && this.originalZipFile !== null;
   }
 
-  try {
-    this.isLoading = true;
-    
-    // Validar documentos
-    const validationResult = this.validateDocumentsInZip();
-    
-    if (!validationResult.isValid || validationResult.foundFiles.length === 0) {
-      const missingCount = validationResult.missingFiles.length;
-      const message = missingCount > 0 
-        ? `Faltan ${missingCount} documentos en el ZIP: ${validationResult.missingFiles.slice(0, 3).join(', ')}${missingCount > 3 ? '...' : ''}`
-        : 'No se encontraron documentos válidos para procesar';
-      
-      this._resourcesService.openSnackBar(message);
-      return;
-    }
-
-    const foundCount = validationResult.foundFiles.length;
-    this._resourcesService.openSnackBar(
-      `Validación exitosa: ${foundCount} documentos encontrados. Procesando...`
-    );
-
-    // Procesar cada documento individualmente
-    let processedCount = 0;
-    let errors: string[] = [];
-
-    for (const fileName of validationResult.foundFiles) {
-      try {
-        console.log(` Procesando documento: ${fileName}`);
-        
-
-        const fileId = await this.uploadTempFileFromZip(fileName);
-        
-        if (!fileId) {
-          throw new Error(`No se obtuvo ID del archivo para ${fileName}`);
-        }
-
-
-        await this.saveFinalFileIndividual(fileId, fileName);
-        
-        processedCount++;
-        console.log(` Documento procesado exitosamente: ${fileName} (${processedCount}/${foundCount})`);
-        
-        // Actualizar progreso en la UI
-        this._resourcesService.openSnackBar(
-          `Procesado: ${fileName} (${processedCount}/${foundCount})`
-        );
-
-        // Pausa pequeña entre archivos para no sobrecargar el servidor
-        await new Promise(resolve => setTimeout(resolve, 500));
-
-      } catch (error: any) {
-        const errorMsg = `Error procesando ${fileName}: ${error.message}`;
-        console.error('', errorMsg);
-        errors.push(errorMsg);
-        
-        // Opcional: continuar con el siguiente archivo o detener todo
-        // Para continuar, simplemente log el error y sigue
-        // Para detener, descomenta la siguiente línea:
-        // throw error;
-      }
-    }
-
-    // Mostrar resultado final
-    if (processedCount > 0) {
-      const successMsg = ` ${processedCount} documentos procesados exitosamente`;
-      this._resourcesService.openSnackBar(successMsg);
-      
-      if (errors.length > 0) {
-        console.warn(' Errores durante el procesamiento:', errors);
-        this._resourcesService.openSnackBar(`Procesados: ${processedCount}, Errores: ${errors.length}`);
-      }
-      
-      // Cerrar diálogo con éxito
-      this.dialogRef.close({ 
-        success: true, 
-        validationResult,
-        processedCount,
-        errors: errors.length > 0 ? errors : undefined
-      });
-    } else {
-      throw new Error('No se pudo procesar ningún documento');
-    }
-
-  } catch (error: any) {
-    console.error('Error general en procesamiento:', error);
-    this._resourcesService.openSnackBar('Error: ' + (error.message || 'Error desconocido'));
-  } finally {
-    this.isLoading = false;
+  get hasAnyFile(): boolean {
+    return this.hasExcelFile || this.hasZipFile;
   }
-}
-
-//método uploadZipFile para guardar referencia al archivo original
-
-async uploadZipFile(fileData: File): Promise<void> {
-  try {
-    // Guardar referencia al archivo original para extracciones posteriores
-    this.originalZipFile = fileData;
-    
-    // ... resto del código existente del método uploadZipFile ...
-    
-    // Validar contenido del ZIP
-    const zipValidation = await this.validateZipContent(fileData);
-    
-    if (!zipValidation.isValid) {
-      this._resourcesService.openSnackBar(`Error en el archivo ZIP: ${zipValidation.errors.join(', ')}`);
-      this.isUploading = false;
-      return;
-    }
 
-    
-
-    this.uploadedFile = {
-      id: 'temp-zip-' + Date.now(), // ID temporal
-      name: fileData.name,
-      size: this.formatBytes(fileData.size)
-    };
-
-    // Leer el contenido del ZIP para visualización
-    await this.readZipFile(fileData, this.uploadedFile.id);
-    
-    const newDocument: Document = {
-      nombre_del_archivo: fileData.name,
-      tamano_del_archivo: this.formatBytes(fileData.size),
-      archivo_id: this.uploadedFile.id,
-      tipo_archivo: 'zip',
-      fecha_carga: new Date().toLocaleString()
-    };
-
-    this.zipFile = newDocument;
-    this.updateTableData();
-    this._resourcesService.openSnackBar('Archivo ZIP cargado y listo para validación');
-
-    this.isUploading = false;
-    
-  } catch (error: any) {
-    this.handleUploadError(error);
-    this.isUploading = false;
+  get hasRequiredFiles(): boolean {
+    return this.hasExcelFile && this.hasZipFile;
   }
-}
-
-//Actualizar el método de limpieza
-async deleteTempFile(fileType: 'excel' | 'zip'): Promise<void> {
-  try {
-    if (fileType === 'zip') {
-      // Para ZIP, solo limpiar referencias locales
-      this.originalZipFile = null;
-      this.zipFile = null;
-      this.zipData = null;
-      this.uploadedFile = null;
-    } else if (fileType === 'excel' && this.uploadedFile) {
-      // Para Excel, eliminar del servidor si fue subido
-      const idUser = localStorage.getItem('idusuario');
-      const formData = new FormData();
 
-      formData.append('id_user', idUser!);
-      formData.append('id_file', this.uploadedFile.id);
-      formData.append('linea', '1');
-
-      await lastValueFrom(this._gdelService.deleteTempFile(formData));
-      
-      this.excelFile = null;
-      this.excelData = null;
-      this.uploadedFile = null;
-    }
-    
-    this.currentFileType = null;
-    this.updateTableData();
-    this._resourcesService.openSnackBar('Archivo eliminado exitosamente');
-    
-  } catch (error: any) {
-    this.handleUploadError(error);
+  get canValidate(): boolean {
+    return this.hasRequiredFiles && 
+           this.excelData && 
+           this.zipData && 
+           this.extractDocumentNamesFromExcel().length > 0;
   }
-}
 
-// Nuevo método para subir archivos individuales del ZIP a uploadTempFile
-async uploadDocumentFromZip(fileName: string, zipFileContent: Uint8Array): Promise<string | null> {
-  try {
-    const idUser = localStorage.getItem('idusuario');
-    
-    // Crear un File object desde el contenido del ZIP
-    const file = new File([zipFileContent], fileName, { 
-      type: this.getMimeType(fileName) 
-    });
-
-    const formData = new FormData();
-    formData.append('file', file);
-    formData.append('id_user', idUser!);
-    formData.append('linea', '1');
-    
-    // Determinar tipo de archivo basado en extensión
-    const extension = fileName.toLowerCase().split('.').pop();
-    let tipoArchivo = 'documento';
-    
-    if (['pdf'].includes(extension || '')) {
-      tipoArchivo = 'pdf';
-    } else if (['doc', 'docx'].includes(extension || '')) {
-      tipoArchivo = 'docx';
-    } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(extension || '')) {
-      tipoArchivo = 'img';
-    } else if (['xlsx', 'xls'].includes(extension || '')) {
-      tipoArchivo = 'excel';
-    }
-    
-    formData.append('tipo_archivo', tipoArchivo);
-
-    const response = await lastValueFrom(this._gdelService.uploadTempFile(formData));
-
-    if (response.error) {
-      throw new Error(response.msg || `Error al subir ${fileName}`);
-    }
-
-    console.log(` Documento subido a temp: ${fileName} - ID: ${response.response.idArchivo}`);
-    return response.response.idArchivo;
-
-  } catch (error: any) {
-    console.error(` Error subiendo ${fileName}:`, error);
-    throw error;
-  }
+get canProcessDocuments(): boolean {
+    return this.validationResult?.isValid === true &&
+           Array.isArray(this.validationResult?.tempFiles) &&
+           Array.isArray(this.validationResult?.individualTempFiles);
 }
 
-// 2. Método para procesar documentos validados desde el preview
-private async processValidatedDocumentsFromPreview(validationResult: any): Promise<void> {
-  if (!validationResult.foundFiles || validationResult.foundFiles.length === 0) {
-    this._resourcesService.openSnackBar('No hay archivos validados para procesar');
-    return;
+  get zipInfo(): string {
+    if (!this.zipData) return '';
+    return `${this.zipData.totalFiles} archivos (${this.formatBytes(this.zipData.totalSize)})`;
   }
 
-  try {
-    this.isLoading = true;
+  get validationStatus(): string {
+    if (!this.canValidate) return 'Sin datos para validar';
     
-    const foundCount = validationResult.foundFiles.length;
-    this._resourcesService.openSnackBar(`Procesando ${foundCount} documentos...`);
-
-    // Procesar cada archivo encontrado
-    const processedDocuments: ProcessedDocument[] = [];
-    
-    for (const fileName of validationResult.foundFiles) {
-      try {
-        console.log(` Procesando: ${fileName}`);
-        
-        // Extraer contenido del archivo desde el ZIP
-        const fileContent = await this.extractFileContentFromZip(fileName);
-        
-        if (!fileContent) {
-          throw new Error(`No se pudo extraer el contenido de ${fileName}`);
-        }
-
-        // Subir a uploadTempFile
-        const fileId = await this.uploadDocumentFromZip(fileName, fileContent);
-        
-        if (!fileId) {
-          throw new Error(`No se obtuvo ID para ${fileName}`);
-        }
-
-        // Crear registro del documento procesado
-        const processedDoc: ProcessedDocument = {
-          nombre_del_archivo: fileName,
-          tamano_del_archivo: this.formatBytes(fileContent.length),
-          archivo_id: fileId,
-          tipo_archivo: this.getFileTypeFromExtension(fileName),
-          fecha_carga: new Date().toLocaleString(),
-          estado: 'temp_uploaded'
-        };
-
-        processedDocuments.push(processedDoc);
-        
-        console.log(` Procesado exitosamente: ${fileName}`);
-        
-        // Pausa pequeña entre archivos
-        await new Promise(resolve => setTimeout(resolve, 300));
-
-      } catch (error: any) {
-        console.error(` Error procesando ${fileName}:`, error);
-        
-        // Agregar documento con error
-        processedDocuments.push({
-          nombre_del_archivo: fileName,
-          tamano_del_archivo: '0 B',
-          archivo_id: '',
-          tipo_archivo: 'error',
-          fecha_carga: new Date().toLocaleString(),
-          estado: 'error',
-          error_message: error.message
-        });
+    if (this.validationResult) {
+      if (this.validationResult.isValid) {
+        return '✅ Validación exitosa';
+      } else {
+        const missingCount = this.validationResult.missingFiles.length;
+        return `❌ Faltan ${missingCount} documento(s)`;
       }
     }
+    
+    const expectedDocuments = this.extractDocumentNamesFromExcel();
+    return `Listo para validar (${expectedDocuments.length} documentos esperados)`;
+  }
 
-    // Actualizar la lista de documentos procesados
-    this.processedDocuments = processedDocuments;
-    this.updateAllTableData();
-
-    const successCount = processedDocuments.filter(doc => doc.estado === 'temp_uploaded').length;
-    const errorCount = processedDocuments.filter(doc => doc.estado === 'error').length;
-
-    if (successCount > 0) {
-      this._resourcesService.openSnackBar(
-        ` ${successCount} documentos procesados${errorCount > 0 ? `, ${errorCount} errores` : ''}`
-      );
-    } else {
-      throw new Error('No se pudo procesar ningún documento');
+  get requirementsMessage(): string {
+    if (!this.hasExcelFile && !this.hasZipFile) {
+      return 'Se necesita cargar el archivo Excel y el ZIP';
+    } else if (!this.hasExcelFile) {
+      return 'Se necesita cargar el archivo Excel';
+    } else if (!this.hasZipFile) {
+      return 'Se necesita cargar el archivo ZIP';
     }
-
-  } catch (error: any) {
-    console.error('Error en procesamiento:', error);
-    this._resourcesService.openSnackBar('Error: ' + (error.message || 'Error desconocido'));
-  } finally {
-    this.isLoading = false;
+    return 'Archivos cargados correctamente';
   }
-}
 
-//Método para extraer contenido de archivo desde ZIP
-private async extractFileContentFromZip(fileName: string): Promise<Uint8Array | null> {
-  if (!this.originalZipFile) {
-    console.error('No hay archivo ZIP original disponible');
-    return null;
+  get currentStep(): string {
+    return this.validationStep || (this.isValidating ? 'Validando...' : '');
   }
 
-  return new Promise((resolve) => {
-    const reader = new FileReader();
-    reader.onload = async (e: any) => {
-      try {
-        const zipData = new Uint8Array(e.target.result);
-        const zip = new JSZip();
-        const zipContent = await zip.loadAsync(zipData);
-        
-        // Buscar el archivo específico en el ZIP
-        const targetFile = Object.entries(zipContent.files).find(([path, entry]) => {
-          const entryFileName = path.split('/').pop()?.toLowerCase();
-          return entryFileName === fileName.toLowerCase() && !entry.dir;
-        });
-
-        if (targetFile) {
-          const [filePath, zipEntry] = targetFile;
-          const content = await zipEntry.async('uint8array');
-          resolve(content);
-        } else {
-          console.error(`Archivo ${fileName} no encontrado en ZIP`);
-          resolve(null);
-        }
-      } catch (error) {
-        console.error('Error extrayendo archivo del ZIP:', error);
-        resolve(null);
-      }
-    };
-    reader.onerror = () => resolve(null);
-    reader.readAsArrayBuffer(this.originalZipFile!);
-  });
-}
-
-//Método auxiliar para obtener tipo de archivo
-private getFileTypeFromExtension(fileName: string): string {
-  const extension = fileName.toLowerCase().split('.').pop();
-  
-  const typeMap: { [key: string]: string } = {
-    'pdf': 'pdf',
-    'doc': 'docx',
-    'docx': 'docx',
-    'xlsx': 'excel',
-    'xls': 'excel',
-    'jpg': 'imagen',
-    'jpeg': 'imagen',
-    'png': 'imagen',
-    'gif': 'imagen',
-    'bmp': 'imagen',
-    'svg': 'imagen',
-    'txt': 'texto',
-    'zip': 'archivo',
-    'rar': 'archivo'
-  };
-  
-  return typeMap[extension || ''] || 'documento';
-}
-
-// Método actualizado para manejar el resultado del preview
-previewZipFile(): void {
-  if (!this.zipData) {
-    this._resourcesService.openSnackBar('No hay datos del archivo ZIP para mostrar');
-    return;
-  }
-
-  const dialogRef = this._matDialog.open(PreviewFileContentComponent, {
-    width: '1200px',
-    height: '800px',
-    maxWidth: '1200px',
-    data: {
-      fileName: this.zipData.fileName,
-      fileType: 'zip',
-      zipContent: this.zipData,
-      fileId: this.zipData.fileId,
-      excelData: this.excelData // Pasar datos del Excel para validación
-    },
-    disableClose: true
-  });
-
-  dialogRef.afterClosed().subscribe(result => {
-    if (result && result.action === 'validate' && result.validationResult) {
-      console.log('Validación completada:', result);
-      
-      // Procesar documentos validados
-      this.processValidatedDocumentsFromPreview(result.validationResult);
+  get validationSummary(): string {
+    if (!this.validationResult) {
+      const expectedDocuments = this.extractDocumentNamesFromExcel();
+      return `${expectedDocuments.length} documentos por validar`;
     }
-  });
-}
-
-
-// Método para actualizar todas las tablas
-private updateAllTableData(): void {
-  // Esto se actualizará según el nuevo HTML
-}
-
-// Getters para validar estado
-get hasRequiredFiles(): boolean {
-  return this.hasExcelFile && this.hasZipFile;
-}
-
-get canProcessDocuments(): boolean {
-  return this.hasRequiredFiles && this.processedDocuments.length > 0;
-}
-
-get requirementsMessage(): string {
-  if (!this.hasExcelFile && !this.hasZipFile) {
-    return 'Se necesita cargar el archivo Excel y el ZIP';
-  } else if (!this.hasExcelFile) {
-    return 'Se necesita cargar el archivo Excel';
-  } else if (!this.hasZipFile) {
-    return 'Se necesita cargar el archivo ZIP';
-  }
-  return '';
-}
-
-async saveFinalFile(): Promise<void> {
-  const documentsToSave = this.processedDocuments.filter(doc => 
-    doc.estado === 'temp_uploaded' && doc.archivo_id
-  );
-
-  if (documentsToSave.length === 0) {
-    this._resourcesService.openSnackBar('No hay documentos procesados para guardar');
-    return;
-  }
 
-  try {
-    this.isLoading = true;
-    const idUser = localStorage.getItem('idusuario');
+    const { matchedCount, expectedCount } = this.validationResult.validationDetails;
+    const missing = this.validationResult.missingFiles.length;
+    const extra = this.validationResult.extraFiles.length;
     
-    let savedCount = 0;
-    let errorCount = 0;
-
-    for (const doc of documentsToSave) {
-      try {
-        const formData = new FormData();
-        formData.append('id_user', idUser!);
-        formData.append('id_file', doc.archivo_id);
-        formData.append('linea', '1');
-        formData.append('module', 'ADSI');
-        formData.append('clasification', 'LA');
-        formData.append('has_order', 'N');
-
-        const response = await lastValueFrom(this._gdelService.saveFinalFile(formData));
-
-        if (response.error) {
-          throw new Error(response.msg || 'Error al guardar');
-        }
-
-        // Actualizar estado del documento
-        doc.estado = 'final_saved';
-        savedCount++;
-        
-        console.log(` Guardado: ${doc.nombre_del_archivo}`);
-
-      } catch (error: any) {
-        console.error(` Error guardando ${doc.nombre_del_archivo}:`, error);
-        doc.estado = 'error';
-        doc.error_message = error.message;
-        errorCount++;
-      }
+    let summary = `${matchedCount}/${expectedCount} documentos encontrados`;
+    
+    if (missing > 0) {
+      summary += `, ${missing} faltantes`;
     }
-        
-    // Actualizar tabla
-    this.updateAllTableData();
-
-    if (savedCount > 0) {
-      const message = ` ${savedCount} documentos guardados exitosamente${errorCount > 0 ? `, ${errorCount} errores` : ''}`;
-      this._resourcesService.openSnackBar(message);
-      
-      if (errorCount === 0) {
-        // Solo cerrar si todos se guardaron exitosamente
-        this.dialogRef.close(true);
-      }
-    } else {
-      throw new Error('No se pudo guardar ningún documento');
+    
+    if (extra > 0) {
+      summary += `, ${extra} extra`;
     }
-
-  } catch (error: any) {
-    console.error('Error al guardar archivos:', error);
-    this._resourcesService.openSnackBar('Error: ' + (error.message || 'Error desconocido'));
-  } finally {
-    this.isLoading = false;
+    
+    return summary;
   }
-}
-
-
 
+  get validationClass(): string {
+    if (!this.validationResult) return '';
+    return this.validationResult.isValid ? 'validation-success' : 'validation-error';
+  }
 }

+ 8 - 0
src/app/services/gdel.service.ts

@@ -46,6 +46,14 @@ export class GdelService {
     return this.postQuery('upload-temp-file', body).pipe(map((data: any) => data));
   }
 
+  validateZipFile (body: any) {
+    return this.postQuery('validate-load-archives', body).pipe(map((data: any) => data));
+  }
+
+  processZipFile (body: any) {
+    return this.postQuery('process-load-archives', body).pipe(map((data: any) => data));
+  }
+
   deleteTempFile(body: any) {
     return this.postQuery('delete-temp-file', body).pipe(map((data: any) => data));
   }