@acontplus/ng-common 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +357 -0
- package/fesm2022/acontplus-ng-common.mjs +1072 -0
- package/fesm2022/acontplus-ng-common.mjs.map +1 -0
- package/package.json +76 -0
- package/types/acontplus-ng-common.d.ts +455 -0
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, Injectable, signal, Input, Component } from '@angular/core';
|
|
3
|
+
import { HttpClient } from '@angular/common/http';
|
|
4
|
+
import { Observable, throwError } from 'rxjs';
|
|
5
|
+
import * as i1 from '@angular/forms';
|
|
6
|
+
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
7
|
+
import { CommonModule } from '@angular/common';
|
|
8
|
+
import * as i2 from '@angular/material/card';
|
|
9
|
+
import { MatCardModule } from '@angular/material/card';
|
|
10
|
+
import * as i3 from '@angular/material/form-field';
|
|
11
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
12
|
+
import * as i4 from '@angular/material/input';
|
|
13
|
+
import { MatInputModule } from '@angular/material/input';
|
|
14
|
+
import * as i5 from '@angular/material/button';
|
|
15
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
16
|
+
import * as i6 from '@angular/material/icon';
|
|
17
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
18
|
+
import * as i7 from '@angular/material/progress-spinner';
|
|
19
|
+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
20
|
+
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
21
|
+
import * as i8 from '@angular/material/select';
|
|
22
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
23
|
+
|
|
24
|
+
const API_PATHS = {
|
|
25
|
+
WHATSAPP: '/api/common/whatsapp-cloud',
|
|
26
|
+
REPORTS: '/api/reports',
|
|
27
|
+
CONFIG: '/api/config',
|
|
28
|
+
};
|
|
29
|
+
const DOCUMENT_TYPES = {
|
|
30
|
+
NORMAL: 'NORMAL',
|
|
31
|
+
FACTURA: 'FACTURA',
|
|
32
|
+
NOTA_ENTREGA: 'NOTA_ENTREGA',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const REPORT_PORT = new InjectionToken('REPORT_PORT');
|
|
36
|
+
|
|
37
|
+
const WHATSAPP_MESSAGING_PORT = new InjectionToken('WHATSAPP_MESSAGING_PORT');
|
|
38
|
+
|
|
39
|
+
const PRINTER_PORT = new InjectionToken('PRINTER_PORT');
|
|
40
|
+
|
|
41
|
+
class ReportFacade {
|
|
42
|
+
reportPort = inject(REPORT_PORT);
|
|
43
|
+
// Método principal unificado
|
|
44
|
+
generate(options) {
|
|
45
|
+
return this.reportPort.generate(options);
|
|
46
|
+
}
|
|
47
|
+
// Métodos de conveniencia para casos comunes
|
|
48
|
+
generatePDF(data, forceDownload = false) {
|
|
49
|
+
this.reportPort.generate({
|
|
50
|
+
data,
|
|
51
|
+
format: 'pdf',
|
|
52
|
+
forceDownload,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
generateExcel(data, forceDownload = false) {
|
|
56
|
+
this.reportPort.generate({
|
|
57
|
+
data,
|
|
58
|
+
format: 'excel',
|
|
59
|
+
forceDownload,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
generateWord(data, forceDownload = false) {
|
|
63
|
+
this.reportPort.generate({
|
|
64
|
+
data,
|
|
65
|
+
format: 'word',
|
|
66
|
+
forceDownload,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Método para generar y retornar blob
|
|
70
|
+
generateBlob(data, format = 'pdf') {
|
|
71
|
+
return this.reportPort.generate({
|
|
72
|
+
data,
|
|
73
|
+
format,
|
|
74
|
+
returnBlob: true,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
78
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportFacade, providedIn: 'root' });
|
|
79
|
+
}
|
|
80
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportFacade, decorators: [{
|
|
81
|
+
type: Injectable,
|
|
82
|
+
args: [{ providedIn: 'root' }]
|
|
83
|
+
}] });
|
|
84
|
+
|
|
85
|
+
class WhatsAppMessageBuilder {
|
|
86
|
+
buildDocumentDeliveryMessages(data) {
|
|
87
|
+
const main = this.buildMainMessage(data);
|
|
88
|
+
const promo = this.buildPromoMessage(data.establecimiento);
|
|
89
|
+
return { main, promo };
|
|
90
|
+
}
|
|
91
|
+
buildWelcomeMessage(customerName, establishmentName) {
|
|
92
|
+
return `¡Hola *${customerName}*! 👋\n\nBienvenido a *${establishmentName}*. Estamos aquí para ayudarte.`;
|
|
93
|
+
}
|
|
94
|
+
buildOrderConfirmationMessage(orderNumber, customerName) {
|
|
95
|
+
return `Estimado(a) *${customerName}*,\n\n✅ Tu pedido #${orderNumber} ha sido confirmado.\n\nTe notificaremos cuando esté listo.`;
|
|
96
|
+
}
|
|
97
|
+
buildPaymentReminderMessage(customerName, amount, dueDate) {
|
|
98
|
+
return `Estimado(a) *${customerName}*,\n\n💰 Recordatorio de pago:\nMonto: $${amount}\nVencimiento: ${dueDate}\n\nGracias por tu preferencia.`;
|
|
99
|
+
}
|
|
100
|
+
buildMainMessage(data) {
|
|
101
|
+
const documentTypeDisplay = this.getDocumentTypeDisplay(data.tipo);
|
|
102
|
+
return (`Estimado(a) *${data.comprador}*,\n\n` +
|
|
103
|
+
`📄 Su ${documentTypeDisplay} ha sido generado exitosamente.\n\n` +
|
|
104
|
+
`🏪 Establecimiento: *${data.establecimiento}*\n` +
|
|
105
|
+
`📋 Serie: ${data.serie}\n\n` +
|
|
106
|
+
`Adjunto encontrará el documento solicitado.`);
|
|
107
|
+
}
|
|
108
|
+
buildPromoMessage(establishmentName) {
|
|
109
|
+
return (`📱 Documento enviado por *${establishmentName}*\n` +
|
|
110
|
+
`Powered by *Acontplus* 🚀\n\n` +
|
|
111
|
+
`¿Necesita ayuda? Contáctenos.`);
|
|
112
|
+
}
|
|
113
|
+
getDocumentTypeDisplay(tipo) {
|
|
114
|
+
const types = {
|
|
115
|
+
FACTURA: 'factura electrónica',
|
|
116
|
+
NOTA_ENTREGA: 'nota de entrega',
|
|
117
|
+
PROFORMA: 'proforma',
|
|
118
|
+
COTIZACION: 'cotización',
|
|
119
|
+
RECIBO: 'recibo',
|
|
120
|
+
};
|
|
121
|
+
return types[tipo] || 'documento';
|
|
122
|
+
}
|
|
123
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppMessageBuilder, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
124
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppMessageBuilder, providedIn: 'root' });
|
|
125
|
+
}
|
|
126
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppMessageBuilder, decorators: [{
|
|
127
|
+
type: Injectable,
|
|
128
|
+
args: [{ providedIn: 'root' }]
|
|
129
|
+
}] });
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Utilidades para formateo y validación de números telefónicos
|
|
133
|
+
*/
|
|
134
|
+
class PhoneFormatterUtil {
|
|
135
|
+
/**
|
|
136
|
+
* Limpia el teléfono removiendo todos los caracteres no numéricos
|
|
137
|
+
*/
|
|
138
|
+
static cleanPhone(phone) {
|
|
139
|
+
return phone.replace(/\D/g, '');
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Valida si un teléfono ecuatoriano es válido
|
|
143
|
+
*/
|
|
144
|
+
static validateEcuadorianPhone(phone) {
|
|
145
|
+
const cleanPhone = this.cleanPhone(phone);
|
|
146
|
+
if (!cleanPhone) {
|
|
147
|
+
return {
|
|
148
|
+
isValid: false,
|
|
149
|
+
cleanPhone,
|
|
150
|
+
errorMessage: 'El número de celular no puede estar vacío',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (cleanPhone.length < 10) {
|
|
154
|
+
return {
|
|
155
|
+
isValid: false,
|
|
156
|
+
cleanPhone,
|
|
157
|
+
errorMessage: 'El número de celular no puede ser menor a diez dígitos',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (cleanPhone.length > 10) {
|
|
161
|
+
return {
|
|
162
|
+
isValid: false,
|
|
163
|
+
cleanPhone,
|
|
164
|
+
errorMessage: 'El número de celular no puede ser mayor a diez dígitos',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// Validar formato ecuatoriano (09xxxxxxxx) o internacional (593xxxxxxxxx)
|
|
168
|
+
const isValidEcuadorian = /^09\d{8}$/.test(cleanPhone);
|
|
169
|
+
const isValidInternational = /^593\d{9}$/.test(cleanPhone);
|
|
170
|
+
if (!isValidEcuadorian && !isValidInternational) {
|
|
171
|
+
return {
|
|
172
|
+
isValid: false,
|
|
173
|
+
cleanPhone,
|
|
174
|
+
errorMessage: 'Formato inválido. Use: 09xxxxxxxx',
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
isValid: true,
|
|
179
|
+
cleanPhone,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Convierte número local ecuatoriano a formato internacional
|
|
184
|
+
*/
|
|
185
|
+
static toInternationalFormat(phone) {
|
|
186
|
+
let cleanPhone = this.cleanPhone(phone);
|
|
187
|
+
// Si empieza con 0, convertir a formato internacional ecuatoriano
|
|
188
|
+
if (cleanPhone.startsWith('0')) {
|
|
189
|
+
cleanPhone = '593' + cleanPhone.slice(1);
|
|
190
|
+
}
|
|
191
|
+
return cleanPhone;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Formatea teléfono para la API de WhatsApp Business (con @c.us)
|
|
195
|
+
*/
|
|
196
|
+
static formatForWhatsAppApi(phone) {
|
|
197
|
+
const internationalPhone = this.toInternationalFormat(phone);
|
|
198
|
+
if (!internationalPhone.endsWith('@c.us')) {
|
|
199
|
+
return `${internationalPhone}@c.us`;
|
|
200
|
+
}
|
|
201
|
+
return internationalPhone;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Formatea teléfono para WhatsApp Web (sin @c.us)
|
|
205
|
+
*/
|
|
206
|
+
static formatForWhatsAppWeb(phone) {
|
|
207
|
+
return this.toInternationalFormat(phone);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Formatea teléfono para mostrar al usuario (con espacios y +)
|
|
211
|
+
*/
|
|
212
|
+
static formatForDisplay(phone) {
|
|
213
|
+
const cleanPhone = this.cleanPhone(phone);
|
|
214
|
+
if (!cleanPhone)
|
|
215
|
+
return '';
|
|
216
|
+
// Si es número local ecuatoriano (09xxxxxxxx)
|
|
217
|
+
if (/^09\d{8}$/.test(cleanPhone)) {
|
|
218
|
+
return `+593 ${cleanPhone.slice(1, 3)} ${cleanPhone.slice(3, 6)} ${cleanPhone.slice(6)}`;
|
|
219
|
+
}
|
|
220
|
+
// Si ya tiene formato internacional (593xxxxxxxxx)
|
|
221
|
+
if (/^593\d{9}$/.test(cleanPhone)) {
|
|
222
|
+
return `+593 ${cleanPhone.slice(3, 5)} ${cleanPhone.slice(5, 8)} ${cleanPhone.slice(8)}`;
|
|
223
|
+
}
|
|
224
|
+
// Para otros formatos internacionales
|
|
225
|
+
if (cleanPhone.length > 10) {
|
|
226
|
+
return `+${cleanPhone}`;
|
|
227
|
+
}
|
|
228
|
+
return phone;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Validación general para teléfonos internacionales
|
|
232
|
+
*/
|
|
233
|
+
static isValidInternationalPhone(phone) {
|
|
234
|
+
const cleanPhone = this.cleanPhone(phone);
|
|
235
|
+
return /^\+?[1-9]\d{1,14}$/.test(cleanPhone);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Genera el hint apropiado según el estado del teléfono
|
|
239
|
+
*/
|
|
240
|
+
static getPhoneHint(phone) {
|
|
241
|
+
if (!phone) {
|
|
242
|
+
return 'Ingrese número ecuatoriano (09xxxxxxxx)';
|
|
243
|
+
}
|
|
244
|
+
const validation = this.validateEcuadorianPhone(phone);
|
|
245
|
+
if (validation.isValid) {
|
|
246
|
+
return `Se enviará a: ${this.formatForDisplay(phone)}`;
|
|
247
|
+
}
|
|
248
|
+
return 'Formato: 09xxxxxxxx';
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Obtiene el placeholder para el campo de teléfono
|
|
252
|
+
*/
|
|
253
|
+
static getPhonePlaceholder() {
|
|
254
|
+
return 'Ej: 0987654321';
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class WhatsAppMessagingFacade {
|
|
259
|
+
messagingPort = inject(WHATSAPP_MESSAGING_PORT);
|
|
260
|
+
messageBuilder = inject(WhatsAppMessageBuilder);
|
|
261
|
+
// Métodos de alto nivel con lógica de negocio
|
|
262
|
+
sendSimpleText(to, message, previewUrl = true) {
|
|
263
|
+
const formattedPhone = PhoneFormatterUtil.formatForWhatsAppApi(to);
|
|
264
|
+
return this.messagingPort.sendText({
|
|
265
|
+
to: formattedPhone,
|
|
266
|
+
message,
|
|
267
|
+
previewUrl,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
sendTemplateText(to, templateName, params, languageCode = 'es_EC') {
|
|
271
|
+
const formattedPhone = PhoneFormatterUtil.formatForWhatsAppApi(to);
|
|
272
|
+
return this.messagingPort.sendTextTemplate({
|
|
273
|
+
to: formattedPhone,
|
|
274
|
+
templateName,
|
|
275
|
+
languageCode,
|
|
276
|
+
bodyParams: params,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
sendDocument(to, documentUrl, caption, filename) {
|
|
280
|
+
const formattedPhone = PhoneFormatterUtil.formatForWhatsAppApi(to);
|
|
281
|
+
return this.messagingPort.sendDocument({
|
|
282
|
+
to: formattedPhone,
|
|
283
|
+
documentUrl,
|
|
284
|
+
caption,
|
|
285
|
+
filename,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
// Caso de uso específico: Entrega de documentos
|
|
289
|
+
deliverDocument(params) {
|
|
290
|
+
const formattedPhone = PhoneFormatterUtil.formatForWhatsAppApi(params.phone);
|
|
291
|
+
const messages = this.messageBuilder.buildDocumentDeliveryMessages({
|
|
292
|
+
comprador: params.customerName,
|
|
293
|
+
establecimiento: params.establishmentName,
|
|
294
|
+
serie: params.documentSeries,
|
|
295
|
+
tipo: params.documentType,
|
|
296
|
+
});
|
|
297
|
+
return this.messagingPort.sendDocumentTemplate({
|
|
298
|
+
to: formattedPhone,
|
|
299
|
+
file: params.file,
|
|
300
|
+
templateName: 'notificacion_documento',
|
|
301
|
+
languageCode: 'es_EC',
|
|
302
|
+
bodyParams: [messages.main, messages.promo],
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
// Método mejorado para envío de documentos con template
|
|
306
|
+
sendDocumentWithTemplate(params) {
|
|
307
|
+
const formattedPhone = PhoneFormatterUtil.formatForWhatsAppApi(params.phone);
|
|
308
|
+
return this.messagingPort.sendDocumentTemplate({
|
|
309
|
+
to: formattedPhone,
|
|
310
|
+
file: params.file,
|
|
311
|
+
templateName: params.templateName,
|
|
312
|
+
languageCode: params.languageCode || 'es_EC',
|
|
313
|
+
bodyParams: params.bodyParams,
|
|
314
|
+
filename: params.filename,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
// Método legacy para compatibilidad (pero mejorado)
|
|
318
|
+
sendDocumentWithMessages(params) {
|
|
319
|
+
return this.sendDocumentWithTemplate({
|
|
320
|
+
phone: params.phone,
|
|
321
|
+
file: params.file,
|
|
322
|
+
templateName: 'documento_generico',
|
|
323
|
+
bodyParams: [params.messages.main, params.messages.promo],
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
getProviderStatus() {
|
|
327
|
+
return this.messagingPort.getStatus();
|
|
328
|
+
}
|
|
329
|
+
// Métodos públicos que delegan a las utilidades estáticas
|
|
330
|
+
isValidPhone(phone) {
|
|
331
|
+
return PhoneFormatterUtil.isValidInternationalPhone(phone);
|
|
332
|
+
}
|
|
333
|
+
toDisplayFormat(phone) {
|
|
334
|
+
return PhoneFormatterUtil.formatForDisplay(phone);
|
|
335
|
+
}
|
|
336
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppMessagingFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
337
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppMessagingFacade, providedIn: 'root' });
|
|
338
|
+
}
|
|
339
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppMessagingFacade, decorators: [{
|
|
340
|
+
type: Injectable,
|
|
341
|
+
args: [{ providedIn: 'root' }]
|
|
342
|
+
}] });
|
|
343
|
+
|
|
344
|
+
class PrinterFacade {
|
|
345
|
+
printerPort = inject(PRINTER_PORT);
|
|
346
|
+
// Métodos de impresión
|
|
347
|
+
askToPrint(params) {
|
|
348
|
+
this.printerPort.askToPrint(params);
|
|
349
|
+
}
|
|
350
|
+
printOrder(id) {
|
|
351
|
+
this.printerPort.createPrintOrder(id);
|
|
352
|
+
}
|
|
353
|
+
printCommanda(id) {
|
|
354
|
+
this.printerPort.createPrintCommanda(id);
|
|
355
|
+
}
|
|
356
|
+
printOrderWithCommanda(id) {
|
|
357
|
+
this.printerPort.createPrintOrderWithCommanda(id);
|
|
358
|
+
}
|
|
359
|
+
printInvoice(id, documentoId) {
|
|
360
|
+
this.printerPort.createInvoicePrint(id, documentoId);
|
|
361
|
+
}
|
|
362
|
+
printNotaEntrega(id, documentoId) {
|
|
363
|
+
this.printerPort.createNotaEntregaPrint(id, documentoId);
|
|
364
|
+
}
|
|
365
|
+
// Método para configurar automáticamente la impresión
|
|
366
|
+
autoPrint(data, codDoc) {
|
|
367
|
+
if (data.printerAutomatic) {
|
|
368
|
+
if (codDoc === 'FACTURA') {
|
|
369
|
+
this.printInvoice(data.orderId, data.idDocumento);
|
|
370
|
+
}
|
|
371
|
+
if (codDoc === 'NOTA_ENTREGA') {
|
|
372
|
+
this.printNotaEntrega(data.orderId, data.idDocumento);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PrinterFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
377
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PrinterFacade, providedIn: 'root' });
|
|
378
|
+
}
|
|
379
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PrinterFacade, decorators: [{
|
|
380
|
+
type: Injectable,
|
|
381
|
+
args: [{ providedIn: 'root' }]
|
|
382
|
+
}] });
|
|
383
|
+
|
|
384
|
+
class MetaWhatsAppAdapter {
|
|
385
|
+
http = inject(HttpClient);
|
|
386
|
+
// 1. Texto directo (requiere ventana 24h)
|
|
387
|
+
sendText(request) {
|
|
388
|
+
return this.http.post(`${API_PATHS.WHATSAPP}/text`, request);
|
|
389
|
+
}
|
|
390
|
+
// 2. Texto con template (funciona siempre)
|
|
391
|
+
sendTextTemplate(request) {
|
|
392
|
+
return this.http.post(`${API_PATHS.WHATSAPP}/text-template`, request);
|
|
393
|
+
}
|
|
394
|
+
// 3. Documento directo via URL (requiere ventana 24h)
|
|
395
|
+
sendDocument(request) {
|
|
396
|
+
return this.http.post(`${API_PATHS.WHATSAPP}/document`, request);
|
|
397
|
+
}
|
|
398
|
+
// 4. Documento con template (funciona siempre)
|
|
399
|
+
sendDocumentTemplate(request) {
|
|
400
|
+
const formData = new FormData();
|
|
401
|
+
// Usar nombres de campos que espera tu API (con mayúsculas)
|
|
402
|
+
formData.append('To', request.to);
|
|
403
|
+
formData.append('File', request.file);
|
|
404
|
+
if (request.templateName)
|
|
405
|
+
formData.append('TemplateName', request.templateName);
|
|
406
|
+
if (request.languageCode)
|
|
407
|
+
formData.append('LanguageCode', request.languageCode);
|
|
408
|
+
if (request.filename)
|
|
409
|
+
formData.append('Filename', request.filename);
|
|
410
|
+
// Enviar bodyParams como array (formato que espera tu API)
|
|
411
|
+
if (request.bodyParams && request.bodyParams.length > 0) {
|
|
412
|
+
request.bodyParams.forEach((param, index) => {
|
|
413
|
+
formData.append(`BodyParams[${index}]`, param);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
return this.http.post(`${API_PATHS.WHATSAPP}/document-template`, formData);
|
|
417
|
+
}
|
|
418
|
+
getStatus() {
|
|
419
|
+
return this.http.get(`${API_PATHS.WHATSAPP}/status`);
|
|
420
|
+
}
|
|
421
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MetaWhatsAppAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
422
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MetaWhatsAppAdapter });
|
|
423
|
+
}
|
|
424
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: MetaWhatsAppAdapter, decorators: [{
|
|
425
|
+
type: Injectable
|
|
426
|
+
}] });
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Adaptador para Green API (legacy)
|
|
430
|
+
* Implementa la interfaz WhatsApp usando Green API
|
|
431
|
+
*/
|
|
432
|
+
class GreenWhatsAppAdapter {
|
|
433
|
+
http = inject(HttpClient);
|
|
434
|
+
// Implementación para Green API - mapea los métodos de WhatsApp a Green
|
|
435
|
+
sendText(request) {
|
|
436
|
+
// Mapear a Green API call
|
|
437
|
+
return this.http.post('/api/green/send-text', {
|
|
438
|
+
chatId: request.to,
|
|
439
|
+
message: request.message,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
sendTextTemplate(request) {
|
|
443
|
+
// Green API no soporta templates, enviar como texto simple
|
|
444
|
+
return this.http.post('/api/green/send-text', {
|
|
445
|
+
chatId: request.to,
|
|
446
|
+
message: `Template: ${request.templateName}`,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
sendDocument(request) {
|
|
450
|
+
return this.http.post('/api/green/send-file', {
|
|
451
|
+
chatId: request.to,
|
|
452
|
+
fileUrl: request.documentUrl,
|
|
453
|
+
caption: request.caption,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
sendDocumentTemplate(request) {
|
|
457
|
+
// Green API - enviar archivo con template como caption
|
|
458
|
+
const formData = new FormData();
|
|
459
|
+
formData.append('chatId', request.to);
|
|
460
|
+
formData.append('file', request.file);
|
|
461
|
+
formData.append('caption', `Template: ${request.templateName}`);
|
|
462
|
+
return this.http.post('/api/green/send-file-upload', formData);
|
|
463
|
+
}
|
|
464
|
+
getStatus() {
|
|
465
|
+
return this.http.get('/api/green/status');
|
|
466
|
+
}
|
|
467
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GreenWhatsAppAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
468
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GreenWhatsAppAdapter });
|
|
469
|
+
}
|
|
470
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GreenWhatsAppAdapter, decorators: [{
|
|
471
|
+
type: Injectable
|
|
472
|
+
}] });
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Utilidades para manejo de archivos y respuestas HTTP
|
|
476
|
+
*/
|
|
477
|
+
class FileMapperUtil {
|
|
478
|
+
/**
|
|
479
|
+
* Extrae el nombre de archivo desde el header Content-Disposition
|
|
480
|
+
*/
|
|
481
|
+
static extractFileName(response) {
|
|
482
|
+
const contentDisposition = response.headers?.get('content-disposition');
|
|
483
|
+
if (!contentDisposition) {
|
|
484
|
+
return this.generateDefaultFileName();
|
|
485
|
+
}
|
|
486
|
+
// Método más robusto para extraer filename
|
|
487
|
+
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);
|
|
488
|
+
if (matches && matches[1]) {
|
|
489
|
+
let fileName = matches[1].replace(/['"]/g, '');
|
|
490
|
+
// Limpiar caracteres especiales
|
|
491
|
+
fileName = fileName.replaceAll('+', ' ').trim();
|
|
492
|
+
return fileName || this.generateDefaultFileName();
|
|
493
|
+
}
|
|
494
|
+
// Fallback: método simple
|
|
495
|
+
const simpleParse = contentDisposition.split('filename=')[1];
|
|
496
|
+
if (simpleParse) {
|
|
497
|
+
return simpleParse.replace(/['"]/g, '').trim() || this.generateDefaultFileName();
|
|
498
|
+
}
|
|
499
|
+
return this.generateDefaultFileName();
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Crea un objeto File desde una respuesta HTTP
|
|
503
|
+
*/
|
|
504
|
+
static fromResponse(response) {
|
|
505
|
+
const fileName = this.extractFileName(response);
|
|
506
|
+
const contentType = response.headers?.get('content-type') || 'application/octet-stream';
|
|
507
|
+
return new File([response.body], fileName, { type: contentType });
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Descarga un archivo usando la API nativa del navegador
|
|
511
|
+
*/
|
|
512
|
+
static downloadFile(blob, fileName) {
|
|
513
|
+
const url = URL.createObjectURL(blob);
|
|
514
|
+
const link = document.createElement('a');
|
|
515
|
+
link.href = url;
|
|
516
|
+
link.download = fileName;
|
|
517
|
+
link.style.display = 'none';
|
|
518
|
+
document.body.appendChild(link);
|
|
519
|
+
link.click();
|
|
520
|
+
document.body.removeChild(link);
|
|
521
|
+
// Limpiar el objeto URL
|
|
522
|
+
URL.revokeObjectURL(url);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Abre un archivo en una nueva ventana (para PDFs)
|
|
526
|
+
*/
|
|
527
|
+
static openFile(blob, fileName) {
|
|
528
|
+
const fileURL = URL.createObjectURL(blob);
|
|
529
|
+
window.open(fileURL, fileName);
|
|
530
|
+
// Limpiar URL después de un tiempo
|
|
531
|
+
setTimeout(() => URL.revokeObjectURL(fileURL), 1000);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Detecta si es un navegador legacy que requiere descarga forzada
|
|
535
|
+
*/
|
|
536
|
+
static isLegacyBrowser() {
|
|
537
|
+
const userAgent = navigator.userAgent;
|
|
538
|
+
return !!(userAgent.match(/Edge/g) || userAgent.match(/.NET/g) || userAgent.match(/MSIE/g));
|
|
539
|
+
}
|
|
540
|
+
static generateDefaultFileName() {
|
|
541
|
+
const timestamp = Date.now();
|
|
542
|
+
return `document_${timestamp}.pdf`;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
class ReportAdapter {
|
|
547
|
+
http = inject(HttpClient);
|
|
548
|
+
generate(options) {
|
|
549
|
+
const { data, format = 'pdf', useV1Api = false, forceDownload = false, returnBlob = false, } = options;
|
|
550
|
+
const reportVersionPath = useV1Api ? '/v1/' : '/v2/';
|
|
551
|
+
const fullPath = `${API_PATHS.REPORTS}${reportVersionPath}download`;
|
|
552
|
+
const requestOptions = {
|
|
553
|
+
observe: 'response',
|
|
554
|
+
responseType: 'blob',
|
|
555
|
+
};
|
|
556
|
+
if (returnBlob) {
|
|
557
|
+
return this.http.post(fullPath, data, requestOptions);
|
|
558
|
+
}
|
|
559
|
+
this.http.post(fullPath, data, requestOptions).subscribe({
|
|
560
|
+
next: (response) => this.saveFile(response, format, forceDownload),
|
|
561
|
+
error: (err) => {
|
|
562
|
+
console.error('Error al descargar el reporte:', err);
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
getFileName(response) {
|
|
568
|
+
return FileMapperUtil.extractFileName(response);
|
|
569
|
+
}
|
|
570
|
+
saveFile(response, format, forceDownload = false) {
|
|
571
|
+
const fileName = this.getFileName(response);
|
|
572
|
+
if (!response.body)
|
|
573
|
+
return;
|
|
574
|
+
if (format === 'pdf' && !forceDownload && !FileMapperUtil.isLegacyBrowser()) {
|
|
575
|
+
// Abrir PDF en nueva ventana para navegadores modernos
|
|
576
|
+
FileMapperUtil.openFile(response.body, fileName);
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
// Descargar archivo (Excel, Word, o PDF forzado)
|
|
580
|
+
FileMapperUtil.downloadFile(response.body, fileName);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
584
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportAdapter });
|
|
585
|
+
}
|
|
586
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportAdapter, decorators: [{
|
|
587
|
+
type: Injectable
|
|
588
|
+
}] });
|
|
589
|
+
|
|
590
|
+
class PrinterAdapter {
|
|
591
|
+
http = inject(HttpClient);
|
|
592
|
+
askToPrint(obj) {
|
|
593
|
+
if (typeof window !== 'undefined') {
|
|
594
|
+
const confirmed = window.confirm('¿Desea Imprimir este comprobante?');
|
|
595
|
+
if (confirmed) {
|
|
596
|
+
this.getPrintConfig(obj).subscribe();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
getPrintConfig(obj) {
|
|
601
|
+
const json = JSON.stringify({
|
|
602
|
+
codDoc: obj.codDoc,
|
|
603
|
+
getUrlPrintServer: true,
|
|
604
|
+
id: obj.id,
|
|
605
|
+
codEstab: obj.codEstab,
|
|
606
|
+
});
|
|
607
|
+
return new Observable((observer) => {
|
|
608
|
+
const url = `${API_PATHS.CONFIG}Print/?json=${json}`;
|
|
609
|
+
this.http.get(url).subscribe({
|
|
610
|
+
next: (response) => {
|
|
611
|
+
if (response.code === '0') {
|
|
612
|
+
console.warn(response.message);
|
|
613
|
+
}
|
|
614
|
+
if (response.code === '1') {
|
|
615
|
+
this.print(response.payload, obj).subscribe();
|
|
616
|
+
}
|
|
617
|
+
observer.next(response);
|
|
618
|
+
observer.complete();
|
|
619
|
+
},
|
|
620
|
+
error: (err) => {
|
|
621
|
+
console.error(err);
|
|
622
|
+
observer.error(err);
|
|
623
|
+
},
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
print(urlPrinter, paramsToPrint) {
|
|
628
|
+
return new Observable((observer) => {
|
|
629
|
+
this.http.post(`${urlPrinter}/Print`, paramsToPrint).subscribe({
|
|
630
|
+
next: (response) => {
|
|
631
|
+
console.log({
|
|
632
|
+
type: response.code === '1' ? 'success' : 'warning',
|
|
633
|
+
message: response.message,
|
|
634
|
+
});
|
|
635
|
+
observer.next(response);
|
|
636
|
+
observer.complete();
|
|
637
|
+
},
|
|
638
|
+
error: (err) => {
|
|
639
|
+
console.error(err);
|
|
640
|
+
observer.error(err);
|
|
641
|
+
},
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
createPrintOrder(id) {
|
|
646
|
+
this.getPrintConfig({
|
|
647
|
+
id,
|
|
648
|
+
documentoId: 0,
|
|
649
|
+
tipo: 5,
|
|
650
|
+
codDoc: DOCUMENT_TYPES.NORMAL,
|
|
651
|
+
}).subscribe();
|
|
652
|
+
}
|
|
653
|
+
createPrintCommanda(id) {
|
|
654
|
+
this.getPrintConfig({
|
|
655
|
+
id,
|
|
656
|
+
documentoId: 0,
|
|
657
|
+
tipo: 6,
|
|
658
|
+
codDoc: DOCUMENT_TYPES.NORMAL,
|
|
659
|
+
}).subscribe();
|
|
660
|
+
}
|
|
661
|
+
createPrintOrderWithCommanda(id) {
|
|
662
|
+
this.getPrintConfig({
|
|
663
|
+
id,
|
|
664
|
+
documentoId: 0,
|
|
665
|
+
tipo: 2,
|
|
666
|
+
codDoc: DOCUMENT_TYPES.NORMAL,
|
|
667
|
+
}).subscribe();
|
|
668
|
+
}
|
|
669
|
+
createInvoicePrint(id, documentoId) {
|
|
670
|
+
this.getPrintConfig({
|
|
671
|
+
id,
|
|
672
|
+
documentoId,
|
|
673
|
+
tipo: 3,
|
|
674
|
+
codDoc: DOCUMENT_TYPES.FACTURA,
|
|
675
|
+
}).subscribe();
|
|
676
|
+
}
|
|
677
|
+
createNotaEntregaPrint(id, documentoId) {
|
|
678
|
+
this.getPrintConfig({
|
|
679
|
+
id,
|
|
680
|
+
documentoId,
|
|
681
|
+
tipo: 4,
|
|
682
|
+
codDoc: DOCUMENT_TYPES.NOTA_ENTREGA,
|
|
683
|
+
}).subscribe();
|
|
684
|
+
}
|
|
685
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PrinterAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
686
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PrinterAdapter });
|
|
687
|
+
}
|
|
688
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PrinterAdapter, decorators: [{
|
|
689
|
+
type: Injectable
|
|
690
|
+
}] });
|
|
691
|
+
|
|
692
|
+
class ReportParamsBuilder {
|
|
693
|
+
reportConfigs = {
|
|
694
|
+
FV: {
|
|
695
|
+
codigo: 'FV',
|
|
696
|
+
hasService: true,
|
|
697
|
+
useV1Api: true,
|
|
698
|
+
idField: 'id',
|
|
699
|
+
includeEstabInData: true,
|
|
700
|
+
includeCodigoInData: true,
|
|
701
|
+
},
|
|
702
|
+
NE: {
|
|
703
|
+
codigo: 'NE',
|
|
704
|
+
hasService: true,
|
|
705
|
+
useV1Api: true,
|
|
706
|
+
idField: 'id',
|
|
707
|
+
extraDataParams: { tipo: 1 },
|
|
708
|
+
},
|
|
709
|
+
NORMAL: {
|
|
710
|
+
codigo: 'OR',
|
|
711
|
+
hasService: false,
|
|
712
|
+
useV1Api: false,
|
|
713
|
+
idField: 'id',
|
|
714
|
+
},
|
|
715
|
+
// Fácil agregar nuevos tipos sin modificar código
|
|
716
|
+
NC: {
|
|
717
|
+
codigo: 'NC',
|
|
718
|
+
hasService: true,
|
|
719
|
+
useV1Api: true,
|
|
720
|
+
idField: 'id',
|
|
721
|
+
includeEstabInData: true,
|
|
722
|
+
},
|
|
723
|
+
ND: {
|
|
724
|
+
codigo: 'ND',
|
|
725
|
+
hasService: true,
|
|
726
|
+
useV1Api: true,
|
|
727
|
+
idField: 'id',
|
|
728
|
+
includeEstabInData: true,
|
|
729
|
+
},
|
|
730
|
+
GR: {
|
|
731
|
+
codigo: 'GR',
|
|
732
|
+
hasService: true,
|
|
733
|
+
useV1Api: false,
|
|
734
|
+
idField: 'id',
|
|
735
|
+
},
|
|
736
|
+
};
|
|
737
|
+
/**
|
|
738
|
+
* Construye los parámetros de reporte según el tipo de documento
|
|
739
|
+
*/
|
|
740
|
+
buildParams(docData, format = 'pdf') {
|
|
741
|
+
const config = this.getReportConfig(docData.codDoc);
|
|
742
|
+
return {
|
|
743
|
+
hasService: config.hasService,
|
|
744
|
+
reportParams: this.buildReportParams(docData, config, format),
|
|
745
|
+
dataParams: this.buildDataParams(docData, config, format),
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Determina si debe usar la API v1 según el tipo de documento
|
|
750
|
+
*/
|
|
751
|
+
shouldUseV1Api(codDoc) {
|
|
752
|
+
const config = this.getReportConfig(codDoc);
|
|
753
|
+
return config.useV1Api;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Obtiene la configuración para un tipo de documento
|
|
757
|
+
*/
|
|
758
|
+
getReportConfig(codDoc) {
|
|
759
|
+
const config = this.reportConfigs[codDoc];
|
|
760
|
+
if (!config) {
|
|
761
|
+
throw new Error(`Tipo de documento no soportado: ${codDoc}. Tipos disponibles: ${Object.keys(this.reportConfigs).join(', ')}`);
|
|
762
|
+
}
|
|
763
|
+
return config;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Construye los parámetros del reporte
|
|
767
|
+
*/
|
|
768
|
+
buildReportParams(docData, config, format) {
|
|
769
|
+
const params = {
|
|
770
|
+
codigo: config.codigo,
|
|
771
|
+
codEstab: docData.codEstab,
|
|
772
|
+
format,
|
|
773
|
+
};
|
|
774
|
+
// Solo agregar hasParams y aditionalParams si hasService es true
|
|
775
|
+
if (config.hasService) {
|
|
776
|
+
params.hasParams = true;
|
|
777
|
+
params.aditionalParams = [];
|
|
778
|
+
}
|
|
779
|
+
return JSON.stringify(params);
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Construye los parámetros de datos
|
|
783
|
+
*/
|
|
784
|
+
buildDataParams(docData, config, format) {
|
|
785
|
+
const params = {
|
|
786
|
+
id: docData[config.idField],
|
|
787
|
+
};
|
|
788
|
+
// Agregar parámetros adicionales según configuración
|
|
789
|
+
if (config.includeEstabInData) {
|
|
790
|
+
params.codEstab = docData.codEstab;
|
|
791
|
+
}
|
|
792
|
+
if (config.includeCodigoInData) {
|
|
793
|
+
params.codigo = config.codigo;
|
|
794
|
+
}
|
|
795
|
+
if (config.hasService) {
|
|
796
|
+
params.hasParams = true;
|
|
797
|
+
if (config.includeCodigoInData) {
|
|
798
|
+
params.format = format;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// Agregar parámetros extra específicos del tipo
|
|
802
|
+
if (config.extraDataParams) {
|
|
803
|
+
Object.assign(params, config.extraDataParams);
|
|
804
|
+
}
|
|
805
|
+
return JSON.stringify(params);
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Método de conveniencia para generar reporte directamente
|
|
809
|
+
*/
|
|
810
|
+
generateReportFor(docData, format = 'pdf', returnBlob = false) {
|
|
811
|
+
return {
|
|
812
|
+
data: this.buildParams(docData, format),
|
|
813
|
+
format,
|
|
814
|
+
useV1Api: this.shouldUseV1Api(docData.codDoc),
|
|
815
|
+
returnBlob,
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Obtiene la lista de tipos de documento soportados
|
|
820
|
+
*/
|
|
821
|
+
getSupportedDocumentTypes() {
|
|
822
|
+
return Object.keys(this.reportConfigs);
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Verifica si un tipo de documento está soportado
|
|
826
|
+
*/
|
|
827
|
+
isDocumentTypeSupported(codDoc) {
|
|
828
|
+
return codDoc in this.reportConfigs;
|
|
829
|
+
}
|
|
830
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportParamsBuilder, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
831
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportParamsBuilder, providedIn: 'root' });
|
|
832
|
+
}
|
|
833
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportParamsBuilder, decorators: [{
|
|
834
|
+
type: Injectable,
|
|
835
|
+
args: [{ providedIn: 'root' }]
|
|
836
|
+
}] });
|
|
837
|
+
|
|
838
|
+
class WhatsAppSender {
|
|
839
|
+
config;
|
|
840
|
+
fb = inject(FormBuilder);
|
|
841
|
+
whatsappFacade = inject(WhatsAppMessagingFacade);
|
|
842
|
+
reportFacade = inject(ReportFacade);
|
|
843
|
+
snackBar = inject(MatSnackBar);
|
|
844
|
+
reportBuilder = inject(ReportParamsBuilder);
|
|
845
|
+
whatsappForm;
|
|
846
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
847
|
+
messageLength = 0;
|
|
848
|
+
phoneNumbers = signal([], ...(ngDevMode ? [{ debugName: "phoneNumbers" }] : []));
|
|
849
|
+
showWhatsAppWebLink = signal(false, ...(ngDevMode ? [{ debugName: "showWhatsAppWebLink" }] : []));
|
|
850
|
+
whatsAppWebUrl = signal('', ...(ngDevMode ? [{ debugName: "whatsAppWebUrl" }] : []));
|
|
851
|
+
// Datos del documento y establecimiento
|
|
852
|
+
establishmentPhone = '';
|
|
853
|
+
establishmentName = '';
|
|
854
|
+
customerName = '';
|
|
855
|
+
ngOnInit() {
|
|
856
|
+
this.initializeForm();
|
|
857
|
+
this.setupMessageLengthCounter();
|
|
858
|
+
this.loadConfigData();
|
|
859
|
+
}
|
|
860
|
+
loadConfigData() {
|
|
861
|
+
if (this.config) {
|
|
862
|
+
if (this.config.phoneNumbers) {
|
|
863
|
+
this.phoneNumbers.set(this.config.phoneNumbers);
|
|
864
|
+
if (this.config.phoneNumbers.length > 0) {
|
|
865
|
+
this.whatsappForm.patchValue({ phoneNumber: this.config.phoneNumbers[0] });
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (this.config.defaultMessage) {
|
|
869
|
+
this.whatsappForm.patchValue({ message: this.config.defaultMessage });
|
|
870
|
+
}
|
|
871
|
+
this.establishmentPhone = this.config.establishmentPhone || '';
|
|
872
|
+
this.establishmentName = this.config.establishmentName || '';
|
|
873
|
+
this.customerName = this.config.customerName || '';
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
initializeForm() {
|
|
877
|
+
this.whatsappForm = this.fb.group({
|
|
878
|
+
phoneNumber: ['', [Validators.required, this.ecuadorianPhoneValidator.bind(this)]],
|
|
879
|
+
message: ['', [Validators.required, Validators.maxLength(500)]],
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
// Validador personalizado para números ecuatorianos
|
|
883
|
+
ecuadorianPhoneValidator(control) {
|
|
884
|
+
if (!control.value)
|
|
885
|
+
return null;
|
|
886
|
+
const validation = PhoneFormatterUtil.validateEcuadorianPhone(control.value);
|
|
887
|
+
return validation.isValid ? null : { invalidPhone: true };
|
|
888
|
+
}
|
|
889
|
+
setupMessageLengthCounter() {
|
|
890
|
+
this.whatsappForm.get('message')?.valueChanges.subscribe((value) => {
|
|
891
|
+
this.messageLength = value ? value.length : 0;
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
sendWhatsApp() {
|
|
895
|
+
if (this.whatsappForm.valid) {
|
|
896
|
+
if (!this.validatePhoneNumber())
|
|
897
|
+
return;
|
|
898
|
+
const { phoneNumber, message } = this.whatsappForm.value;
|
|
899
|
+
// Si hay configuración de documento, generar y enviar archivo
|
|
900
|
+
if (this.config?.documentData) {
|
|
901
|
+
this.sendWithDocument(phoneNumber, message);
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
// Envío simple de texto
|
|
905
|
+
this.sendSimpleMessage(phoneNumber, message);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
validatePhoneNumber() {
|
|
910
|
+
const phoneNumber = this.whatsappForm.get('phoneNumber')?.value;
|
|
911
|
+
const validation = PhoneFormatterUtil.validateEcuadorianPhone(phoneNumber);
|
|
912
|
+
if (!validation.isValid) {
|
|
913
|
+
this.showError(validation.errorMessage || 'Número de teléfono inválido');
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
return true;
|
|
917
|
+
}
|
|
918
|
+
sendSimpleMessage(phoneNumber, message) {
|
|
919
|
+
this.isLoading.set(true);
|
|
920
|
+
this.whatsappFacade.sendSimpleText(phoneNumber, message).subscribe({
|
|
921
|
+
next: (response) => {
|
|
922
|
+
this.isLoading.set(false);
|
|
923
|
+
if (response.success) {
|
|
924
|
+
this.showSuccess('Mensaje enviado exitosamente');
|
|
925
|
+
this.clearForm();
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
this.showError(response.error?.message || 'Error al enviar mensaje');
|
|
929
|
+
this.prepareWhatsAppWebLink(phoneNumber, message);
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
error: (_error) => {
|
|
933
|
+
this.isLoading.set(false);
|
|
934
|
+
this.showError('Error de conexión');
|
|
935
|
+
this.prepareWhatsAppWebLink(phoneNumber, message);
|
|
936
|
+
},
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
sendWithDocument(phoneNumber, message) {
|
|
940
|
+
if (!this.config?.documentData)
|
|
941
|
+
return;
|
|
942
|
+
this.isLoading.set(true);
|
|
943
|
+
// Generar el reporte primero
|
|
944
|
+
this.generateReport().subscribe({
|
|
945
|
+
next: (reportResponse) => {
|
|
946
|
+
if (reportResponse) {
|
|
947
|
+
const { file, fileName } = this.processReportFile(reportResponse);
|
|
948
|
+
this.sendMediaWithMessages(phoneNumber, message, file, fileName);
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
error: (error) => {
|
|
952
|
+
this.isLoading.set(false);
|
|
953
|
+
console.error('Error generando reporte:', error);
|
|
954
|
+
this.showError('Error al generar el documento');
|
|
955
|
+
},
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
generateReport() {
|
|
959
|
+
const docData = this.config.documentData;
|
|
960
|
+
// Usar el builder centralizado para generar parámetros
|
|
961
|
+
const reportOptions = this.reportBuilder.generateReportFor(docData, 'pdf', true);
|
|
962
|
+
const reportResult = this.reportFacade.generate(reportOptions);
|
|
963
|
+
// Manejar el caso donde generate puede retornar void
|
|
964
|
+
return reportResult || throwError(() => new Error('No se pudo generar el reporte'));
|
|
965
|
+
}
|
|
966
|
+
processReportFile(response) {
|
|
967
|
+
const file = FileMapperUtil.fromResponse(response);
|
|
968
|
+
const fileName = FileMapperUtil.extractFileName(response);
|
|
969
|
+
return { file, fileName };
|
|
970
|
+
}
|
|
971
|
+
sendMediaWithMessages(phoneNumber, message, file, fileName) {
|
|
972
|
+
const formattedPhone = PhoneFormatterUtil.formatForWhatsAppApi(phoneNumber);
|
|
973
|
+
// Enviar solo el documento con el mensaje como caption
|
|
974
|
+
this.whatsappFacade
|
|
975
|
+
.sendDocumentWithTemplate({
|
|
976
|
+
phone: formattedPhone,
|
|
977
|
+
file: file,
|
|
978
|
+
templateName: 'documento_generico',
|
|
979
|
+
bodyParams: [message],
|
|
980
|
+
filename: fileName,
|
|
981
|
+
})
|
|
982
|
+
.subscribe({
|
|
983
|
+
next: () => {
|
|
984
|
+
this.isLoading.set(false);
|
|
985
|
+
this.showSuccess('Documento enviado correctamente');
|
|
986
|
+
this.clearForm();
|
|
987
|
+
},
|
|
988
|
+
error: (error) => {
|
|
989
|
+
this.isLoading.set(false);
|
|
990
|
+
console.error('Error enviando documento:', error);
|
|
991
|
+
this.showError('Error al enviar el documento');
|
|
992
|
+
this.prepareWhatsAppWebLink(phoneNumber, message);
|
|
993
|
+
},
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
prepareWhatsAppWebLink(phoneNumber, message) {
|
|
997
|
+
const cleanPhoneNumber = PhoneFormatterUtil.formatForWhatsAppWeb(phoneNumber);
|
|
998
|
+
const encodedMessage = encodeURIComponent(message);
|
|
999
|
+
const whatsappUrl = `https://wa.me/${cleanPhoneNumber}?text=${encodedMessage}`;
|
|
1000
|
+
this.whatsAppWebUrl.set(whatsappUrl);
|
|
1001
|
+
this.showWhatsAppWebLink.set(true);
|
|
1002
|
+
}
|
|
1003
|
+
openWhatsAppWeb() {
|
|
1004
|
+
const url = this.whatsAppWebUrl();
|
|
1005
|
+
if (url) {
|
|
1006
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
hideWhatsAppWebLink() {
|
|
1010
|
+
this.showWhatsAppWebLink.set(false);
|
|
1011
|
+
this.whatsAppWebUrl.set('');
|
|
1012
|
+
}
|
|
1013
|
+
showSuccess(message) {
|
|
1014
|
+
this.snackBar.open(message, 'Cerrar', {
|
|
1015
|
+
duration: 3000,
|
|
1016
|
+
panelClass: ['success-snackbar'],
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
showError(message) {
|
|
1020
|
+
this.snackBar.open(message, 'Cerrar', {
|
|
1021
|
+
duration: 5000,
|
|
1022
|
+
panelClass: ['error-snackbar'],
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
clearForm() {
|
|
1026
|
+
this.whatsappForm.reset();
|
|
1027
|
+
this.messageLength = 0;
|
|
1028
|
+
this.hideWhatsAppWebLink();
|
|
1029
|
+
}
|
|
1030
|
+
// Método para mostrar el formato del teléfono
|
|
1031
|
+
get formattedPhoneDisplay() {
|
|
1032
|
+
const phone = this.whatsappForm.get('phoneNumber')?.value;
|
|
1033
|
+
return PhoneFormatterUtil.formatForDisplay(phone || '');
|
|
1034
|
+
}
|
|
1035
|
+
// Método para obtener placeholder dinámico
|
|
1036
|
+
get phonePlaceholder() {
|
|
1037
|
+
return PhoneFormatterUtil.getPhonePlaceholder();
|
|
1038
|
+
}
|
|
1039
|
+
// Método para obtener el hint del campo
|
|
1040
|
+
get phoneHint() {
|
|
1041
|
+
const phone = this.whatsappForm.get('phoneNumber')?.value;
|
|
1042
|
+
return PhoneFormatterUtil.getPhoneHint(phone || '');
|
|
1043
|
+
}
|
|
1044
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppSender, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1045
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: WhatsAppSender, isStandalone: true, selector: "acp-whatsapp-sender", inputs: { config: "config" }, ngImport: i0, template: "<div class=\"whatsapp-sender-container\">\n <mat-card class=\"sender-card\">\n <mat-card-header>\n <div mat-card-avatar class=\"whatsapp-avatar\">\n <mat-icon>chat</mat-icon>\n </div>\n <mat-card-title>Enviar por WhatsApp</mat-card-title>\n <mat-card-subtitle>Comparte contenido directamente</mat-card-subtitle>\n </mat-card-header>\n\n <mat-card-content>\n <form [formGroup]=\"whatsappForm\" (ngSubmit)=\"sendWhatsApp()\">\n <!-- Campo para n\u00FAmero de tel\u00E9fono -->\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>N\u00FAmero de tel\u00E9fono</mat-label>\n @if (phoneNumbers().length > 1) {\n <mat-select formControlName=\"phoneNumber\">\n @for (phone of phoneNumbers(); track phone) {\n <mat-option [value]=\"phone\">{{ phone }}</mat-option>\n }\n </mat-select>\n } @else {\n <input\n matInput\n formControlName=\"phoneNumber\"\n [placeholder]=\"phonePlaceholder\"\n type=\"tel\"\n />\n }\n <mat-icon matSuffix>phone</mat-icon>\n <mat-hint>{{ phoneHint }}</mat-hint>\n @if (whatsappForm.get('phoneNumber')?.hasError('required')) {\n <mat-error>El n\u00FAmero de tel\u00E9fono es requerido</mat-error>\n }\n @if (whatsappForm.get('phoneNumber')?.hasError('invalidPhone')) {\n <mat-error>Ingrese un n\u00FAmero ecuatoriano v\u00E1lido (09xxxxxxxx)</mat-error>\n }\n </mat-form-field>\n\n <!-- Campo para mensaje -->\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Mensaje</mat-label>\n <textarea\n matInput\n formControlName=\"message\"\n placeholder=\"Escribe tu mensaje aqu\u00ED...\"\n rows=\"4\"\n maxlength=\"500\"\n >\n </textarea>\n <mat-icon matSuffix>message</mat-icon>\n <mat-hint align=\"end\">{{ messageLength }}/500 caracteres</mat-hint>\n @if (whatsappForm.get('message')?.hasError('required')) {\n <mat-error>El mensaje es requerido</mat-error>\n }\n </mat-form-field>\n\n <!-- Botones de acci\u00F3n -->\n <div class=\"action-buttons\">\n <button\n mat-raised-button\n color=\"primary\"\n type=\"submit\"\n [disabled]=\"whatsappForm.invalid || isLoading()\"\n class=\"send-button\"\n >\n @if (!isLoading()) {\n <mat-icon>send</mat-icon>\n }\n @if (isLoading()) {\n <mat-spinner diameter=\"20\"></mat-spinner>\n }\n {{ isLoading() ? 'Enviando...' : 'Enviar por WhatsApp' }}\n </button>\n\n <button mat-button type=\"button\" (click)=\"clearForm()\" [disabled]=\"isLoading()\">\n <mat-icon>clear</mat-icon>\n Limpiar\n </button>\n </div>\n\n <!-- Enlace opcional a WhatsApp Web -->\n @if (showWhatsAppWebLink()) {\n <div class=\"whatsapp-web-link\">\n <mat-icon>info</mat-icon>\n <span>\u00BFNo se pudo enviar? Prueba enviando directamente:</span>\n <button mat-button color=\"accent\" (click)=\"openWhatsAppWeb()\">\n <mat-icon>open_in_new</mat-icon>\n Abrir WhatsApp Web\n </button>\n <button mat-icon-button (click)=\"hideWhatsAppWebLink()\" aria-label=\"Cerrar\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n }\n </form>\n </mat-card-content>\n </mat-card>\n</div>\n", styles: [".whatsapp-sender-container{max-width:500px;margin:0 auto;padding:16px}.whatsapp-sender-container .sender-card{box-shadow:0 4px 8px #0000001a;border-radius:12px}.whatsapp-sender-container .sender-card mat-card-header{padding-bottom:16px}.whatsapp-sender-container .sender-card mat-card-header .whatsapp-avatar{background:linear-gradient(135deg,#25d366,#128c7e);color:#fff;display:flex;align-items:center;justify-content:center;border-radius:50%;width:40px;height:40px}.whatsapp-sender-container .sender-card mat-card-header .whatsapp-avatar mat-icon{font-size:24px}.whatsapp-sender-container .sender-card mat-card-header mat-card-title{color:#128c7e;font-weight:600}.whatsapp-sender-container .sender-card mat-card-header mat-card-subtitle{color:#666}.whatsapp-sender-container .sender-card mat-card-content{padding-top:0}.whatsapp-sender-container .sender-card mat-card-content .full-width{width:100%;margin-bottom:16px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons{display:flex;gap:12px;justify-content:flex-end;margin-top:24px;flex-wrap:wrap}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button{background:linear-gradient(135deg,#25d366,#128c7e);color:#fff;min-width:180px;height:44px;border-radius:22px;font-weight:500}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button:hover:not(:disabled){background:linear-gradient(135deg,#128c7e,#25d366);transform:translateY(-1px);box-shadow:0 4px 12px #25d3664d}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button:disabled{background:#ccc;color:#666}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button mat-icon{margin-right:8px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button mat-spinner{margin-right:8px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button[mat-button]{color:#666;border-radius:22px;height:44px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button[mat-button]:hover:not(:disabled){background-color:#0000000a}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button[mat-button] mat-icon{margin-right:4px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link{display:flex;align-items:center;gap:8px;margin-top:16px;padding:12px 16px;background-color:#f5f5f5;border-radius:8px;border-left:4px solid #25d366;flex-wrap:wrap}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link mat-icon:first-child{color:#25d366;font-size:20px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link span{color:#666;font-size:14px;flex:1;min-width:200px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button]{color:#25d366;font-weight:500;border-radius:20px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button]:hover{background-color:#25d3661a}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button] mat-icon{margin-right:4px;font-size:18px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button]{color:#999;width:32px;height:32px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button]:hover{background-color:#0000001a}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button] mat-icon{font-size:18px}@media(max-width:600px){.whatsapp-sender-container{padding:8px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons{flex-direction:column;align-items:stretch}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button{width:100%;margin-bottom:8px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link{flex-direction:column;align-items:flex-start;gap:12px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link span{min-width:auto;width:100%}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button]{width:100%;justify-content:center}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button]{align-self:flex-end;position:absolute;top:8px;right:8px}}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline .mat-form-field-outline{color:#e0e0e0}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline.mat-focused .mat-form-field-outline-thick{color:#25d366}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline .mat-form-field-label{color:#666}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline.mat-focused .mat-form-field-label{color:#25d366}.whatsapp-sender-container mat-form-field mat-icon[matSuffix]{color:#25d366}.whatsapp-sender-container mat-form-field .mat-hint{color:#999;font-size:12px}.whatsapp-sender-container mat-form-field .mat-error{color:#f44336;font-size:12px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i2.MatCardAvatar, selector: "[mat-card-avatar], [matCardAvatar]" }, { kind: "directive", type: i2.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i2.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i2.MatCardSubtitle, selector: "mat-card-subtitle, [mat-card-subtitle], [matCardSubtitle]" }, { kind: "directive", type: i2.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
|
|
1046
|
+
}
|
|
1047
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: WhatsAppSender, decorators: [{
|
|
1048
|
+
type: Component,
|
|
1049
|
+
args: [{ selector: 'acp-whatsapp-sender', imports: [
|
|
1050
|
+
CommonModule,
|
|
1051
|
+
ReactiveFormsModule,
|
|
1052
|
+
MatCardModule,
|
|
1053
|
+
MatFormFieldModule,
|
|
1054
|
+
MatInputModule,
|
|
1055
|
+
MatButtonModule,
|
|
1056
|
+
MatIconModule,
|
|
1057
|
+
MatProgressSpinnerModule,
|
|
1058
|
+
MatSnackBarModule,
|
|
1059
|
+
MatSelectModule,
|
|
1060
|
+
], template: "<div class=\"whatsapp-sender-container\">\n <mat-card class=\"sender-card\">\n <mat-card-header>\n <div mat-card-avatar class=\"whatsapp-avatar\">\n <mat-icon>chat</mat-icon>\n </div>\n <mat-card-title>Enviar por WhatsApp</mat-card-title>\n <mat-card-subtitle>Comparte contenido directamente</mat-card-subtitle>\n </mat-card-header>\n\n <mat-card-content>\n <form [formGroup]=\"whatsappForm\" (ngSubmit)=\"sendWhatsApp()\">\n <!-- Campo para n\u00FAmero de tel\u00E9fono -->\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>N\u00FAmero de tel\u00E9fono</mat-label>\n @if (phoneNumbers().length > 1) {\n <mat-select formControlName=\"phoneNumber\">\n @for (phone of phoneNumbers(); track phone) {\n <mat-option [value]=\"phone\">{{ phone }}</mat-option>\n }\n </mat-select>\n } @else {\n <input\n matInput\n formControlName=\"phoneNumber\"\n [placeholder]=\"phonePlaceholder\"\n type=\"tel\"\n />\n }\n <mat-icon matSuffix>phone</mat-icon>\n <mat-hint>{{ phoneHint }}</mat-hint>\n @if (whatsappForm.get('phoneNumber')?.hasError('required')) {\n <mat-error>El n\u00FAmero de tel\u00E9fono es requerido</mat-error>\n }\n @if (whatsappForm.get('phoneNumber')?.hasError('invalidPhone')) {\n <mat-error>Ingrese un n\u00FAmero ecuatoriano v\u00E1lido (09xxxxxxxx)</mat-error>\n }\n </mat-form-field>\n\n <!-- Campo para mensaje -->\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Mensaje</mat-label>\n <textarea\n matInput\n formControlName=\"message\"\n placeholder=\"Escribe tu mensaje aqu\u00ED...\"\n rows=\"4\"\n maxlength=\"500\"\n >\n </textarea>\n <mat-icon matSuffix>message</mat-icon>\n <mat-hint align=\"end\">{{ messageLength }}/500 caracteres</mat-hint>\n @if (whatsappForm.get('message')?.hasError('required')) {\n <mat-error>El mensaje es requerido</mat-error>\n }\n </mat-form-field>\n\n <!-- Botones de acci\u00F3n -->\n <div class=\"action-buttons\">\n <button\n mat-raised-button\n color=\"primary\"\n type=\"submit\"\n [disabled]=\"whatsappForm.invalid || isLoading()\"\n class=\"send-button\"\n >\n @if (!isLoading()) {\n <mat-icon>send</mat-icon>\n }\n @if (isLoading()) {\n <mat-spinner diameter=\"20\"></mat-spinner>\n }\n {{ isLoading() ? 'Enviando...' : 'Enviar por WhatsApp' }}\n </button>\n\n <button mat-button type=\"button\" (click)=\"clearForm()\" [disabled]=\"isLoading()\">\n <mat-icon>clear</mat-icon>\n Limpiar\n </button>\n </div>\n\n <!-- Enlace opcional a WhatsApp Web -->\n @if (showWhatsAppWebLink()) {\n <div class=\"whatsapp-web-link\">\n <mat-icon>info</mat-icon>\n <span>\u00BFNo se pudo enviar? Prueba enviando directamente:</span>\n <button mat-button color=\"accent\" (click)=\"openWhatsAppWeb()\">\n <mat-icon>open_in_new</mat-icon>\n Abrir WhatsApp Web\n </button>\n <button mat-icon-button (click)=\"hideWhatsAppWebLink()\" aria-label=\"Cerrar\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n }\n </form>\n </mat-card-content>\n </mat-card>\n</div>\n", styles: [".whatsapp-sender-container{max-width:500px;margin:0 auto;padding:16px}.whatsapp-sender-container .sender-card{box-shadow:0 4px 8px #0000001a;border-radius:12px}.whatsapp-sender-container .sender-card mat-card-header{padding-bottom:16px}.whatsapp-sender-container .sender-card mat-card-header .whatsapp-avatar{background:linear-gradient(135deg,#25d366,#128c7e);color:#fff;display:flex;align-items:center;justify-content:center;border-radius:50%;width:40px;height:40px}.whatsapp-sender-container .sender-card mat-card-header .whatsapp-avatar mat-icon{font-size:24px}.whatsapp-sender-container .sender-card mat-card-header mat-card-title{color:#128c7e;font-weight:600}.whatsapp-sender-container .sender-card mat-card-header mat-card-subtitle{color:#666}.whatsapp-sender-container .sender-card mat-card-content{padding-top:0}.whatsapp-sender-container .sender-card mat-card-content .full-width{width:100%;margin-bottom:16px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons{display:flex;gap:12px;justify-content:flex-end;margin-top:24px;flex-wrap:wrap}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button{background:linear-gradient(135deg,#25d366,#128c7e);color:#fff;min-width:180px;height:44px;border-radius:22px;font-weight:500}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button:hover:not(:disabled){background:linear-gradient(135deg,#128c7e,#25d366);transform:translateY(-1px);box-shadow:0 4px 12px #25d3664d}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button:disabled{background:#ccc;color:#666}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button mat-icon{margin-right:8px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons .send-button mat-spinner{margin-right:8px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button[mat-button]{color:#666;border-radius:22px;height:44px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button[mat-button]:hover:not(:disabled){background-color:#0000000a}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button[mat-button] mat-icon{margin-right:4px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link{display:flex;align-items:center;gap:8px;margin-top:16px;padding:12px 16px;background-color:#f5f5f5;border-radius:8px;border-left:4px solid #25d366;flex-wrap:wrap}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link mat-icon:first-child{color:#25d366;font-size:20px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link span{color:#666;font-size:14px;flex:1;min-width:200px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button]{color:#25d366;font-weight:500;border-radius:20px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button]:hover{background-color:#25d3661a}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button] mat-icon{margin-right:4px;font-size:18px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button]{color:#999;width:32px;height:32px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button]:hover{background-color:#0000001a}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button] mat-icon{font-size:18px}@media(max-width:600px){.whatsapp-sender-container{padding:8px}.whatsapp-sender-container .sender-card mat-card-content .action-buttons{flex-direction:column;align-items:stretch}.whatsapp-sender-container .sender-card mat-card-content .action-buttons button{width:100%;margin-bottom:8px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link{flex-direction:column;align-items:flex-start;gap:12px}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link span{min-width:auto;width:100%}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-button]{width:100%;justify-content:center}.whatsapp-sender-container .sender-card mat-card-content .whatsapp-web-link button[mat-icon-button]{align-self:flex-end;position:absolute;top:8px;right:8px}}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline .mat-form-field-outline{color:#e0e0e0}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline.mat-focused .mat-form-field-outline-thick{color:#25d366}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline .mat-form-field-label{color:#666}.whatsapp-sender-container mat-form-field.mat-form-field-appearance-outline.mat-focused .mat-form-field-label{color:#25d366}.whatsapp-sender-container mat-form-field mat-icon[matSuffix]{color:#25d366}.whatsapp-sender-container mat-form-field .mat-hint{color:#999;font-size:12px}.whatsapp-sender-container mat-form-field .mat-error{color:#f44336;font-size:12px}\n"] }]
|
|
1061
|
+
}], propDecorators: { config: [{
|
|
1062
|
+
type: Input
|
|
1063
|
+
}] } });
|
|
1064
|
+
|
|
1065
|
+
// Contracts
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Generated bundle index. Do not edit.
|
|
1069
|
+
*/
|
|
1070
|
+
|
|
1071
|
+
export { API_PATHS, DOCUMENT_TYPES, FileMapperUtil, GreenWhatsAppAdapter, MetaWhatsAppAdapter, PRINTER_PORT, PhoneFormatterUtil, PrinterAdapter, PrinterFacade, REPORT_PORT, ReportAdapter, ReportFacade, ReportParamsBuilder, WHATSAPP_MESSAGING_PORT, WhatsAppMessageBuilder, WhatsAppMessagingFacade, WhatsAppSender };
|
|
1072
|
+
//# sourceMappingURL=acontplus-ng-common.mjs.map
|