alerta al enviar pdf
This commit is contained in:
parent
6115bda555
commit
7d75fb1df7
@ -3,7 +3,7 @@
|
|||||||
"version": 1,
|
"version": 1,
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
"cronogramas-primeng": {
|
"cronogramas": {
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"schematics": {
|
"schematics": {
|
||||||
"@schematics/angular:component": {
|
"@schematics/angular:component": {
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-devkit/build-angular:application",
|
"builder": "@angular-devkit/build-angular:application",
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": "dist/cronogramas-primeng",
|
"outputPath": "dist/cronogramas",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
"browser": "src/main.ts",
|
"browser": "src/main.ts",
|
||||||
"polyfills": [
|
"polyfills": [
|
||||||
@ -80,10 +80,10 @@
|
|||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
"buildTarget": "cronogramas-primeng:build:production"
|
"buildTarget": "cronogramas:build:production"
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"buildTarget": "cronogramas-primeng:build:development"
|
"buildTarget": "cronogramas:build:development"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cronogramas-primeng",
|
"name": "cronogramas",
|
||||||
"version": "0.0.0",
|
"version": "0.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
@ -37,6 +37,7 @@
|
|||||||
"@angular/compiler-cli": "^19.2.0",
|
"@angular/compiler-cli": "^19.2.0",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
|
"@types/pdfmake": "^0.2.11",
|
||||||
"@types/xlsx": "^0.0.35",
|
"@types/xlsx": "^0.0.35",
|
||||||
"jasmine-core": "~5.6.0",
|
"jasmine-core": "~5.6.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { provideAnimations } from '@angular/platform-browser/animations';
|
|||||||
import { providePrimeNG } from 'primeng/config';
|
import { providePrimeNG } from 'primeng/config';
|
||||||
import Aura from '@primeng/themes/aura';
|
import Aura from '@primeng/themes/aura';
|
||||||
import { authInterceptor } from './interceptors/auth.interceptor';
|
import { authInterceptor } from './interceptors/auth.interceptor';
|
||||||
|
import { MessageService } from 'primeng/api';
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
@ -24,6 +25,7 @@ export const appConfig: ApplicationConfig = {
|
|||||||
darkModeSelector: false || 'none'
|
darkModeSelector: false || 'none'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
|
MessageService
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
28
src/app/components/alert-dialog/alert-dialog.component.html
Normal file
28
src/app/components/alert-dialog/alert-dialog.component.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<p-dialog
|
||||||
|
[(visible)]="visible"
|
||||||
|
[modal]="true"
|
||||||
|
[draggable]="false"
|
||||||
|
[resizable]="false"
|
||||||
|
[closeOnEscape]="true"
|
||||||
|
[style]="{width: '400px'}"
|
||||||
|
[contentStyle]="{'text-align': 'center', 'padding': '20px'}"
|
||||||
|
styleClass="alert-dialog"
|
||||||
|
[header]="title">
|
||||||
|
|
||||||
|
<div class="flex flex-column align-items-center">
|
||||||
|
<i class="pi text-8xl mb-4" [ngClass]="getIconClass()"></i>
|
||||||
|
<div class="text-xl font-semibold mb-3" *ngIf="title">{{ title }}</div>
|
||||||
|
<div class="text-center mb-5">{{ message }}</div>
|
||||||
|
|
||||||
|
<div class="flex justify-content-center gap-2">
|
||||||
|
<button *ngIf="showCancelButton" pButton (click)="cancel()"
|
||||||
|
class="p-button-outlined p-button-secondary"
|
||||||
|
[label]="cancelButtonText">
|
||||||
|
</button>
|
||||||
|
<button *ngIf="showConfirmButton" pButton (click)="confirm()"
|
||||||
|
class="p-button-primary"
|
||||||
|
[label]="confirmButtonText">
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</p-dialog>
|
||||||
11
src/app/components/alert-dialog/alert-dialog.component.scss
Normal file
11
src/app/components/alert-dialog/alert-dialog.component.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
:host ::ng-deep {
|
||||||
|
.alert-dialog {
|
||||||
|
.p-dialog-header {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-dialog-content {
|
||||||
|
overflow-y: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/app/components/alert-dialog/alert-dialog.component.ts
Normal file
75
src/app/components/alert-dialog/alert-dialog.component.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { DialogModule } from 'primeng/dialog';
|
||||||
|
import { ButtonModule } from 'primeng/button';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
export interface AlertDialogOptions {
|
||||||
|
visible: boolean;
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
icon: 'success' | 'error' | 'warning' | 'info' | 'question';
|
||||||
|
showConfirmButton: boolean;
|
||||||
|
confirmButtonText: string;
|
||||||
|
showCancelButton: boolean;
|
||||||
|
cancelButtonText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-alert-dialog',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, DialogModule, ButtonModule],
|
||||||
|
templateUrl: './alert-dialog.component.html',
|
||||||
|
styleUrls: ['./alert-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class AlertDialogComponent {
|
||||||
|
visible = false;
|
||||||
|
title = '';
|
||||||
|
message = '';
|
||||||
|
icon: 'success' | 'error' | 'warning' | 'info' | 'question' = 'success';
|
||||||
|
showConfirmButton = true;
|
||||||
|
confirmButtonText = 'Aceptar';
|
||||||
|
showCancelButton = false;
|
||||||
|
cancelButtonText = 'Cancelar';
|
||||||
|
|
||||||
|
private confirmSubject = new Subject<boolean>();
|
||||||
|
public onConfirm = this.confirmSubject.asObservable();
|
||||||
|
|
||||||
|
show(options: Partial<AlertDialogOptions> = {}): void {
|
||||||
|
this.visible = true;
|
||||||
|
this.title = options.title || this.title;
|
||||||
|
this.message = options.message || this.message;
|
||||||
|
this.icon = options.icon || this.icon;
|
||||||
|
this.showConfirmButton = options.showConfirmButton !== undefined ? options.showConfirmButton : this.showConfirmButton;
|
||||||
|
this.confirmButtonText = options.confirmButtonText || this.confirmButtonText;
|
||||||
|
this.showCancelButton = options.showCancelButton !== undefined ? options.showCancelButton : this.showCancelButton;
|
||||||
|
this.cancelButtonText = options.cancelButtonText || this.cancelButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm(): void {
|
||||||
|
this.visible = false;
|
||||||
|
this.confirmSubject.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(): void {
|
||||||
|
this.visible = false;
|
||||||
|
this.confirmSubject.next(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getIconClass(): string {
|
||||||
|
switch (this.icon) {
|
||||||
|
case 'success':
|
||||||
|
return 'pi-check-circle text-green-500';
|
||||||
|
case 'error':
|
||||||
|
return 'pi-times-circle text-red-500';
|
||||||
|
case 'warning':
|
||||||
|
return 'pi-exclamation-triangle text-yellow-500';
|
||||||
|
case 'info':
|
||||||
|
return 'pi-info-circle text-blue-500';
|
||||||
|
case 'question':
|
||||||
|
return 'pi-question-circle text-purple-500';
|
||||||
|
default:
|
||||||
|
return 'pi-check-circle text-green-500';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,8 +21,7 @@ import { MessageService } from 'primeng/api';
|
|||||||
],
|
],
|
||||||
templateUrl: './layout.component.html',
|
templateUrl: './layout.component.html',
|
||||||
styleUrl: './layout.component.scss',
|
styleUrl: './layout.component.scss',
|
||||||
standalone: true,
|
standalone: true
|
||||||
providers : [MessageService]
|
|
||||||
})
|
})
|
||||||
export class LayoutComponent implements OnInit {
|
export class LayoutComponent implements OnInit {
|
||||||
isSidebarVisible: boolean = true;
|
isSidebarVisible: boolean = true;
|
||||||
|
|||||||
@ -1,15 +1,21 @@
|
|||||||
<div class="pdf-container">
|
<div class="pdf-container">
|
||||||
<div class="pdf-viewer">
|
<div class="pdf-viewer">
|
||||||
<iframe [src]="pdfSrc | safe" width="100%" height="100%"></iframe>
|
<iframe [src]="pdfSrc | safe" width="100%" height="100%"></iframe>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<button pButton type="button" class="p-button p-button-danger" (click)="descargarPDF()">
|
<button pButton type="button" class="p-button p-button-danger" (click)="descargarPDF()">
|
||||||
<i class="pi pi-file-pdf" style="margin-right: 8px;"></i>
|
<i class="pi pi-file-pdf" style="margin-right: 8px;"></i>
|
||||||
Descargar PDF
|
Descargar PDF
|
||||||
</button>
|
</button>
|
||||||
<button pButton type="button" class="p-button p-button-success ml-2" (click)="enviarPDF()">
|
<button pButton type="button" class="p-button p-button-success ml-2" (click)="enviarPDF()" [disabled]="enviando">
|
||||||
|
<ng-container *ngIf="!enviando; else cargando">
|
||||||
<i class="pi pi-send" style="margin-right: 8px;"></i>
|
<i class="pi pi-send" style="margin-right: 8px;"></i>
|
||||||
Enviar
|
Enviar
|
||||||
</button>
|
</ng-container>
|
||||||
</div>
|
<ng-template #cargando>
|
||||||
</div>
|
<p-progressSpinner [style]="{width: '20px', height: '20px'}" styleClass="custom-spinner" strokeWidth="4" fill="var(--surface-ground)" animationDuration=".5s"></p-progressSpinner>
|
||||||
|
<span style="margin-left: 8px;">Enviando...</span>
|
||||||
|
</ng-template>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -2,21 +2,26 @@ import { Component, OnInit, inject } from '@angular/core';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { DynamicDialogRef, DynamicDialogConfig } from 'primeng/dynamicdialog';
|
import { DynamicDialogRef, DynamicDialogConfig } from 'primeng/dynamicdialog';
|
||||||
import { SafePipe } from '../../../pipes/safe.pipe';
|
import { SafePipe } from '../../../pipes/safe.pipe';
|
||||||
|
import { ButtonModule } from 'primeng/button';
|
||||||
import pdfMake from 'pdfmake/build/pdfmake';
|
import { ToastModule } from 'primeng/toast';
|
||||||
import pdfFonts from 'pdfmake/build/vfs_fonts';
|
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
||||||
import { TDocumentDefinitions } from 'pdfmake/interfaces';
|
|
||||||
import { MessageService } from 'primeng/api';
|
import { MessageService } from 'primeng/api';
|
||||||
pdfMake.vfs = pdfFonts.vfs;
|
|
||||||
|
|
||||||
|
import { PdfService } from '../../services/pdf.service';
|
||||||
|
import { AlertService } from '../../services/alert.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-visor-pdf',
|
selector: 'app-visor-pdf',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, SafePipe],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SafePipe,
|
||||||
|
ButtonModule,
|
||||||
|
ToastModule,
|
||||||
|
ProgressSpinnerModule
|
||||||
|
],
|
||||||
templateUrl: './visor-pdf.component.html',
|
templateUrl: './visor-pdf.component.html',
|
||||||
styleUrls: ['./visor-pdf.component.scss'],
|
styleUrls: ['./visor-pdf.component.scss']
|
||||||
providers: [MessageService]
|
|
||||||
})
|
})
|
||||||
export class VisorPdfComponent implements OnInit {
|
export class VisorPdfComponent implements OnInit {
|
||||||
product: any;
|
product: any;
|
||||||
@ -27,165 +32,8 @@ export class VisorPdfComponent implements OnInit {
|
|||||||
private dialogRef = inject(DynamicDialogRef);
|
private dialogRef = inject(DynamicDialogRef);
|
||||||
private config = inject(DynamicDialogConfig);
|
private config = inject(DynamicDialogConfig);
|
||||||
private messageService = inject(MessageService);
|
private messageService = inject(MessageService);
|
||||||
|
private pdfService = inject(PdfService);
|
||||||
docDefinition: TDocumentDefinitions = {
|
private alertService = inject(AlertService);
|
||||||
content: [
|
|
||||||
// Encabezado con información de empresa y cronograma
|
|
||||||
{
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
width: '50%',
|
|
||||||
text: 'Empresa: Constructora Los Andes S.A.',
|
|
||||||
margin: [0, 0, 0, 5]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: '50%',
|
|
||||||
text: [
|
|
||||||
{ text: 'Cronograma: SC-23-45\n' },
|
|
||||||
{ text: 'Nombre sistema: Terminal Portuario Norte' }
|
|
||||||
],
|
|
||||||
alignment: 'right',
|
|
||||||
margin: [0, 0, 0, 5]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Título y subtítulo
|
|
||||||
{
|
|
||||||
text: 'CRONOGRAMA BASE DE OBRAS E INVERSIONES',
|
|
||||||
alignment: 'center',
|
|
||||||
bold: true,
|
|
||||||
fontSize: 12,
|
|
||||||
margin: [0, 10, 0, 0]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'ACTUALIZACIÓN PLAN DE DESARROLLO O NUEVA CONCESIÓN',
|
|
||||||
alignment: 'center',
|
|
||||||
bold: true,
|
|
||||||
fontSize: 11,
|
|
||||||
margin: [0, 0, 0, 10]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Tabla principal
|
|
||||||
{
|
|
||||||
table: {
|
|
||||||
headerRows: 1,
|
|
||||||
widths: ['10%', '12%', '23%', '12%', '10%', '10%', '10%', '13%'],
|
|
||||||
body: [
|
|
||||||
[
|
|
||||||
{ text: 'ETAPA', style: 'tableHeader', alignment: 'center' },
|
|
||||||
{ text: 'CÓDIGO GLOSA', style: 'tableHeader', alignment: 'center' },
|
|
||||||
{ text: 'DESCRIPCIÓN GLOSA', style: 'tableHeader', alignment: 'center' },
|
|
||||||
{ text: 'MONTO INVERSIÓN UF', style: 'tableHeader', alignment: 'center' },
|
|
||||||
{ text: 'AÑO INICIO', style: 'tableHeader', alignment: 'center' },
|
|
||||||
{ text: 'AÑO TERMINO', style: 'tableHeader', alignment: 'center' },
|
|
||||||
{ text: 'MES TERMINO', style: 'tableHeader', alignment: 'center' },
|
|
||||||
{ text: 'NOTA', style: 'tableHeader', alignment: 'center' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '1', alignment: 'center' },
|
|
||||||
{ text: 'INF-001', alignment: 'center' },
|
|
||||||
{ text: 'Estudios preliminares', alignment: 'left' },
|
|
||||||
{ text: '2,500', alignment: 'right' },
|
|
||||||
{ text: '2025', alignment: 'center' },
|
|
||||||
{ text: '2025', alignment: 'center' },
|
|
||||||
{ text: 'Julio', alignment: 'center' },
|
|
||||||
{ text: 'Fase inicial', alignment: 'left' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '2', alignment: 'center' },
|
|
||||||
{ text: 'MOV-102', alignment: 'center' },
|
|
||||||
{ text: 'Movimiento de tierras', alignment: 'left' },
|
|
||||||
{ text: '15,750', alignment: 'right' },
|
|
||||||
{ text: '2025', alignment: 'center' },
|
|
||||||
{ text: '2026', alignment: 'center' },
|
|
||||||
{ text: 'Enero', alignment: 'center' },
|
|
||||||
{ text: 'En terreno', alignment: 'left' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '3', alignment: 'center' },
|
|
||||||
{ text: 'CIM-203', alignment: 'center' },
|
|
||||||
{ text: 'Construcción de cimientos', alignment: 'left' },
|
|
||||||
{ text: '32,800', alignment: 'right' },
|
|
||||||
{ text: '2026', alignment: 'center' },
|
|
||||||
{ text: '2026', alignment: 'center' },
|
|
||||||
{ text: 'Mayo', alignment: 'center' },
|
|
||||||
{ text: 'Crítico', alignment: 'left' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '4', alignment: 'center' },
|
|
||||||
{ text: 'EST-304', alignment: 'center' },
|
|
||||||
{ text: 'Estructura principal', alignment: 'left' },
|
|
||||||
{ text: '65,420', alignment: 'right' },
|
|
||||||
{ text: '2026', alignment: 'center' },
|
|
||||||
{ text: '2027', alignment: 'center' },
|
|
||||||
{ text: 'Febrero', alignment: 'center' },
|
|
||||||
{ text: 'Alta inversión', alignment: 'left' }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ text: '5', alignment: 'center' },
|
|
||||||
{ text: 'TER-405', alignment: 'center' },
|
|
||||||
{ text: 'Terminaciones y acabados', alignment: 'left' },
|
|
||||||
{ text: '18,300', alignment: 'right' },
|
|
||||||
{ text: '2027', alignment: 'center' },
|
|
||||||
{ text: '2027', alignment: 'center' },
|
|
||||||
{ text: 'Octubre', alignment: 'center' },
|
|
||||||
{ text: 'Etapa final', alignment: 'left' }
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Total
|
|
||||||
{
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
width: '80%',
|
|
||||||
text: 'TOTAL:',
|
|
||||||
alignment: 'right',
|
|
||||||
bold: true,
|
|
||||||
margin: [0, 15, 10, 20]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: '20%',
|
|
||||||
text: '134,770 UF',
|
|
||||||
alignment: 'left',
|
|
||||||
bold: true,
|
|
||||||
margin: [0, 15, 0, 20]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
// Firma
|
|
||||||
{
|
|
||||||
text: 'X',
|
|
||||||
alignment: 'center',
|
|
||||||
bold: true,
|
|
||||||
fontSize: 14,
|
|
||||||
margin: [0, 15, 0, 0]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Firma SSS',
|
|
||||||
alignment: 'center',
|
|
||||||
fontSize: 10,
|
|
||||||
margin: [0, 0, 0, 10]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// Estilos
|
|
||||||
styles: {
|
|
||||||
tableHeader: {
|
|
||||||
bold: true,
|
|
||||||
fontSize: 10,
|
|
||||||
fillColor: '#f2f2f2'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Definiciones de página
|
|
||||||
pageSize: 'A4',
|
|
||||||
pageOrientation: 'portrait',
|
|
||||||
pageMargins: [40, 40, 40, 40]
|
|
||||||
};
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// Obtener el producto pasado a través del servicio de diálogo
|
// Obtener el producto pasado a través del servicio de diálogo
|
||||||
@ -196,33 +44,29 @@ export class VisorPdfComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generarPDF() {
|
generarPDF() {
|
||||||
const pdfDocGenerator = pdfMake.createPdf(this.docDefinition);
|
this.pdfService.generateCronogramaPdf(this.product).then(dataUrl => {
|
||||||
pdfDocGenerator.getDataUrl((dataUrl) => {
|
|
||||||
this.pdfSrc = dataUrl;
|
this.pdfSrc = dataUrl;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
descargarPDF() {
|
descargarPDF() {
|
||||||
pdfMake.createPdf(this.docDefinition).download('cronogramaspdf');
|
this.pdfService.downloadCronogramaPdf('cronograma', this.product);
|
||||||
}
|
}
|
||||||
|
|
||||||
enviarPDF() {
|
enviarPDF() {
|
||||||
this.enviando = true;
|
this.enviando = true;
|
||||||
console.log('Enviando PDF...');
|
console.log('Enviando PDF...');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.enviando = false;
|
this.enviando = false;
|
||||||
console.log('Mostrando mensaje de envio...')
|
console.log('Mostrando mensaje de envio...')
|
||||||
|
this.alertService.success(
|
||||||
this.messageService.add({
|
'El cronograma ha sido enviado correctamente a la plataforma.',
|
||||||
severity: 'success',
|
'¡Enviado con éxito!'
|
||||||
summary: '¡Enviado con éxito!',
|
);
|
||||||
detail: 'El cronograma ha sido enviado correctamente',
|
|
||||||
life: 30000,
|
|
||||||
});
|
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
cerrar() {
|
cerrar() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|||||||
128
src/app/services/alert.service.ts
Normal file
128
src/app/services/alert.service.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { Injectable, ComponentRef, createComponent, EnvironmentInjector, ApplicationRef } from '@angular/core';
|
||||||
|
import { AlertDialogComponent, AlertDialogOptions } from '../components/alert-dialog/alert-dialog.component';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AlertService {
|
||||||
|
private alertRef: ComponentRef<AlertDialogComponent> | null = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private injector: EnvironmentInjector,
|
||||||
|
private appRef: ApplicationRef
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Muestra un mensaje de alerta tipo Sweet Alert
|
||||||
|
* @param options Opciones de configuración del alert
|
||||||
|
* @returns Una promesa que se resuelve con true (confirmar) o false (cancelar)
|
||||||
|
*/
|
||||||
|
show(options: Partial<AlertDialogOptions> = {}): Promise<boolean> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// Si ya existe un alert, lo eliminamos
|
||||||
|
this.closeCurrentAlert();
|
||||||
|
|
||||||
|
// Creamos el componente dinámicamente
|
||||||
|
this.alertRef = createComponent(AlertDialogComponent, {
|
||||||
|
environmentInjector: this.injector,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Agregamos a la aplicación y al DOM
|
||||||
|
document.body.appendChild(this.alertRef.location.nativeElement);
|
||||||
|
this.appRef.attachView(this.alertRef.hostView);
|
||||||
|
|
||||||
|
// Configuramos las opciones y mostramos
|
||||||
|
this.alertRef.instance.show(options);
|
||||||
|
|
||||||
|
// Manejamos la respuesta
|
||||||
|
const subscription = this.alertRef.instance.onConfirm.subscribe((result) => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
this.closeCurrentAlert();
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Muestra un mensaje de éxito
|
||||||
|
* @param message Mensaje a mostrar
|
||||||
|
* @param title Título del alert (opcional)
|
||||||
|
*/
|
||||||
|
success(message: string, title: string = '¡Operación exitosa!'): Promise<boolean> {
|
||||||
|
return this.show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
icon: 'success',
|
||||||
|
showCancelButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Muestra un mensaje de error
|
||||||
|
* @param message Mensaje a mostrar
|
||||||
|
* @param title Título del alert (opcional)
|
||||||
|
*/
|
||||||
|
error(message: string, title: string = 'Error'): Promise<boolean> {
|
||||||
|
return this.show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
icon: 'error',
|
||||||
|
showCancelButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Muestra un mensaje de advertencia
|
||||||
|
* @param message Mensaje a mostrar
|
||||||
|
* @param title Título del alert (opcional)
|
||||||
|
*/
|
||||||
|
warning(message: string, title: string = 'Advertencia'): Promise<boolean> {
|
||||||
|
return this.show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Muestra un mensaje informativo
|
||||||
|
* @param message Mensaje a mostrar
|
||||||
|
* @param title Título del alert (opcional)
|
||||||
|
*/
|
||||||
|
info(message: string, title: string = 'Información'): Promise<boolean> {
|
||||||
|
return this.show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
icon: 'info',
|
||||||
|
showCancelButton: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Muestra un diálogo de confirmación
|
||||||
|
* @param message Mensaje a mostrar
|
||||||
|
* @param title Título del alert (opcional)
|
||||||
|
*/
|
||||||
|
confirm(message: string, title: string = '¿Está seguro?'): Promise<boolean> {
|
||||||
|
return this.show({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Sí, confirmar',
|
||||||
|
cancelButtonText: 'Cancelar'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cierra el alert actual si existe
|
||||||
|
*/
|
||||||
|
private closeCurrentAlert(): void {
|
||||||
|
if (this.alertRef) {
|
||||||
|
this.appRef.detachView(this.alertRef.hostView);
|
||||||
|
this.alertRef.location.nativeElement.remove();
|
||||||
|
this.alertRef = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,3 +5,5 @@ export * from './ajuste-pd.service';
|
|||||||
export * from './unidad-informacion.service';
|
export * from './unidad-informacion.service';
|
||||||
export * from './tipo-carga.service';
|
export * from './tipo-carga.service';
|
||||||
export * from './estado-aprobacion.service';
|
export * from './estado-aprobacion.service';
|
||||||
|
export * from './pdf.service';
|
||||||
|
export * from './alert.service';
|
||||||
|
|||||||
214
src/app/services/pdf.service.ts
Normal file
214
src/app/services/pdf.service.ts
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import pdfMake from 'pdfmake/build/pdfmake';
|
||||||
|
import pdfFonts from 'pdfmake/build/vfs_fonts';
|
||||||
|
import { TDocumentDefinitions } from 'pdfmake/interfaces';
|
||||||
|
|
||||||
|
pdfMake.vfs = pdfFonts.vfs;
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class PdfService {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Genera un PDF con la definición por defecto para cronogramas
|
||||||
|
* @param data Datos opcionales para personalizar el documento
|
||||||
|
* @returns Promise con la URL de datos del PDF generado
|
||||||
|
*/
|
||||||
|
generateCronogramaPdf(data?: any): Promise<string> {
|
||||||
|
const docDefinition = this.getCronogramaDocDefinition(data);
|
||||||
|
return this.generatePdfDataUrl(docDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descarga un PDF con la definición de cronograma
|
||||||
|
* @param filename Nombre del archivo a descargar
|
||||||
|
* @param data Datos opcionales para personalizar el documento
|
||||||
|
*/
|
||||||
|
downloadCronogramaPdf(filename: string = 'cronograma', data?: any): void {
|
||||||
|
const docDefinition = this.getCronogramaDocDefinition(data);
|
||||||
|
pdfMake.createPdf(docDefinition).download(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Genera una URL de datos a partir de la definición del documento
|
||||||
|
* @param docDefinition Definición del documento PDF
|
||||||
|
* @returns Promise con la URL de datos del PDF
|
||||||
|
*/
|
||||||
|
private generatePdfDataUrl(docDefinition: TDocumentDefinitions): Promise<string> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const pdfDocGenerator = pdfMake.createPdf(docDefinition);
|
||||||
|
pdfDocGenerator.getDataUrl((dataUrl) => {
|
||||||
|
resolve(dataUrl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtiene la definición del documento para un cronograma
|
||||||
|
* @param data Datos opcionales para personalizar el documento
|
||||||
|
* @returns Definición del documento PDF
|
||||||
|
*/
|
||||||
|
private getCronogramaDocDefinition(data?: any): TDocumentDefinitions {
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
// Encabezado con información de empresa y cronograma
|
||||||
|
{
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
width: '50%',
|
||||||
|
text: 'Empresa: Constructora Los Andes S.A.',
|
||||||
|
margin: [0, 0, 0, 5]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
width: '50%',
|
||||||
|
text: [
|
||||||
|
{ text: 'Cronograma: SC-23-45\n' },
|
||||||
|
{ text: 'Nombre sistema: Terminal Portuario Norte' }
|
||||||
|
],
|
||||||
|
alignment: 'right',
|
||||||
|
margin: [0, 0, 0, 5]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Título y subtítulo
|
||||||
|
{
|
||||||
|
text: 'CRONOGRAMA BASE DE OBRAS E INVERSIONES',
|
||||||
|
alignment: 'center',
|
||||||
|
bold: true,
|
||||||
|
fontSize: 12,
|
||||||
|
margin: [0, 10, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'ACTUALIZACIÓN PLAN DE DESARROLLO O NUEVA CONCESIÓN',
|
||||||
|
alignment: 'center',
|
||||||
|
bold: true,
|
||||||
|
fontSize: 11,
|
||||||
|
margin: [0, 0, 0, 10]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Tabla principal
|
||||||
|
{
|
||||||
|
table: {
|
||||||
|
headerRows: 1,
|
||||||
|
widths: ['10%', '12%', '23%', '12%', '10%', '10%', '10%', '13%'],
|
||||||
|
body: [
|
||||||
|
[
|
||||||
|
{ text: 'ETAPA', style: 'tableHeader', alignment: 'center' },
|
||||||
|
{ text: 'CÓDIGO GLOSA', style: 'tableHeader', alignment: 'center' },
|
||||||
|
{ text: 'DESCRIPCIÓN GLOSA', style: 'tableHeader', alignment: 'center' },
|
||||||
|
{ text: 'MONTO INVERSIÓN UF', style: 'tableHeader', alignment: 'center' },
|
||||||
|
{ text: 'AÑO INICIO', style: 'tableHeader', alignment: 'center' },
|
||||||
|
{ text: 'AÑO TERMINO', style: 'tableHeader', alignment: 'center' },
|
||||||
|
{ text: 'MES TERMINO', style: 'tableHeader', alignment: 'center' },
|
||||||
|
{ text: 'NOTA', style: 'tableHeader', alignment: 'center' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '1', alignment: 'center' },
|
||||||
|
{ text: 'INF-001', alignment: 'center' },
|
||||||
|
{ text: 'Estudios preliminares', alignment: 'left' },
|
||||||
|
{ text: '2,500', alignment: 'right' },
|
||||||
|
{ text: '2025', alignment: 'center' },
|
||||||
|
{ text: '2025', alignment: 'center' },
|
||||||
|
{ text: 'Julio', alignment: 'center' },
|
||||||
|
{ text: 'Fase inicial', alignment: 'left' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '2', alignment: 'center' },
|
||||||
|
{ text: 'MOV-102', alignment: 'center' },
|
||||||
|
{ text: 'Movimiento de tierras', alignment: 'left' },
|
||||||
|
{ text: '15,750', alignment: 'right' },
|
||||||
|
{ text: '2025', alignment: 'center' },
|
||||||
|
{ text: '2026', alignment: 'center' },
|
||||||
|
{ text: 'Enero', alignment: 'center' },
|
||||||
|
{ text: 'En terreno', alignment: 'left' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '3', alignment: 'center' },
|
||||||
|
{ text: 'CIM-203', alignment: 'center' },
|
||||||
|
{ text: 'Construcción de cimientos', alignment: 'left' },
|
||||||
|
{ text: '32,800', alignment: 'right' },
|
||||||
|
{ text: '2026', alignment: 'center' },
|
||||||
|
{ text: '2026', alignment: 'center' },
|
||||||
|
{ text: 'Mayo', alignment: 'center' },
|
||||||
|
{ text: 'Crítico', alignment: 'left' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '4', alignment: 'center' },
|
||||||
|
{ text: 'EST-304', alignment: 'center' },
|
||||||
|
{ text: 'Estructura principal', alignment: 'left' },
|
||||||
|
{ text: '65,420', alignment: 'right' },
|
||||||
|
{ text: '2026', alignment: 'center' },
|
||||||
|
{ text: '2027', alignment: 'center' },
|
||||||
|
{ text: 'Febrero', alignment: 'center' },
|
||||||
|
{ text: 'Alta inversión', alignment: 'left' }
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '5', alignment: 'center' },
|
||||||
|
{ text: 'TER-405', alignment: 'center' },
|
||||||
|
{ text: 'Terminaciones y acabados', alignment: 'left' },
|
||||||
|
{ text: '18,300', alignment: 'right' },
|
||||||
|
{ text: '2027', alignment: 'center' },
|
||||||
|
{ text: '2027', alignment: 'center' },
|
||||||
|
{ text: 'Octubre', alignment: 'center' },
|
||||||
|
{ text: 'Etapa final', alignment: 'left' }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Total
|
||||||
|
{
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
width: '80%',
|
||||||
|
text: 'TOTAL:',
|
||||||
|
alignment: 'right',
|
||||||
|
bold: true,
|
||||||
|
margin: [0, 15, 10, 20]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
width: '20%',
|
||||||
|
text: '134,770 UF',
|
||||||
|
alignment: 'left',
|
||||||
|
bold: true,
|
||||||
|
margin: [0, 15, 0, 20]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// Firma
|
||||||
|
{
|
||||||
|
text: 'X',
|
||||||
|
alignment: 'center',
|
||||||
|
bold: true,
|
||||||
|
fontSize: 14,
|
||||||
|
margin: [0, 15, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Firma SSS',
|
||||||
|
alignment: 'center',
|
||||||
|
fontSize: 10,
|
||||||
|
margin: [0, 0, 0, 10]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Estilos
|
||||||
|
styles: {
|
||||||
|
tableHeader: {
|
||||||
|
bold: true,
|
||||||
|
fontSize: 10,
|
||||||
|
fillColor: '#f2f2f2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Definiciones de página
|
||||||
|
pageSize: 'A4',
|
||||||
|
pageOrientation: 'portrait',
|
||||||
|
pageMargins: [40, 40, 40, 40]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user