@qbs-sign/sign-lib 2.1.26

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.
@@ -0,0 +1,871 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, Pipe, Input, ViewChild, Component, EventEmitter, Output, NgModule } from '@angular/core';
3
+ import { BehaviorSubject } from 'rxjs';
4
+ import * as i6 from '@angular/common';
5
+ import { CommonModule } from '@angular/common';
6
+ import * as i1 from '@angular/common/http';
7
+ import { HttpHeaders, HttpClientModule } from '@angular/common/http';
8
+ import * as i5 from 'ng2-pdf-viewer';
9
+ import { PdfViewerModule } from 'ng2-pdf-viewer';
10
+ import * as i7 from '@angular/forms';
11
+ import { FormsModule } from '@angular/forms';
12
+
13
+ class SignLibService {
14
+ constructor() { }
15
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
16
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibService, providedIn: 'root' }); }
17
+ }
18
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibService, decorators: [{
19
+ type: Injectable,
20
+ args: [{
21
+ providedIn: 'root',
22
+ }]
23
+ }], ctorParameters: () => [] });
24
+
25
+ class StateService {
26
+ // Default instance for backward compatibility
27
+ get config() {
28
+ return this.getConfig('default');
29
+ }
30
+ get config$() {
31
+ return this.getConfig$('default');
32
+ }
33
+ get notification() {
34
+ return this.getNotification('default');
35
+ }
36
+ getConfig(instanceId = 'default') {
37
+ if (!this.configs.has(instanceId)) {
38
+ this.configs.set(instanceId, { ...this.defaultConfig });
39
+ }
40
+ return this.configs.get(instanceId);
41
+ }
42
+ getConfig$(instanceId = 'default') {
43
+ if (!this.configSources.has(instanceId)) {
44
+ const config = this.getConfig(instanceId);
45
+ this.configSources.set(instanceId, new BehaviorSubject(config));
46
+ }
47
+ return this.configSources.get(instanceId).asObservable();
48
+ }
49
+ getNotification(instanceId = 'default') {
50
+ if (!this.notificationSources.has(instanceId)) {
51
+ this.notificationSources.set(instanceId, new BehaviorSubject(''));
52
+ }
53
+ return this.notificationSources.get(instanceId).asObservable();
54
+ }
55
+ configSet(config, instanceId = 'default') {
56
+ this.configs.set(instanceId, config);
57
+ if (!this.configSources.has(instanceId)) {
58
+ this.configSources.set(instanceId, new BehaviorSubject(config));
59
+ }
60
+ else {
61
+ this.configSources.get(instanceId).next(config);
62
+ }
63
+ // If this config has translations, share them with ALL instances
64
+ if (config.LabelTranslations && config.LabelTranslations.length > 0) {
65
+ // Share with all existing instances
66
+ for (const [id, existingConfig] of this.configs.entries()) {
67
+ if (id !== instanceId &&
68
+ (!existingConfig.LabelTranslations ||
69
+ existingConfig.LabelTranslations.length === 0)) {
70
+ existingConfig.LabelTranslations = [...config.LabelTranslations];
71
+ this.configSources.get(id)?.next(existingConfig);
72
+ }
73
+ }
74
+ }
75
+ }
76
+ notify(notification, instanceId = 'default') {
77
+ if (!this.notificationSources.has(instanceId)) {
78
+ this.notificationSources.set(instanceId, new BehaviorSubject(notification));
79
+ }
80
+ else {
81
+ this.notificationSources.get(instanceId).next(notification);
82
+ }
83
+ }
84
+ constructor() {
85
+ this.defaultConfig = {
86
+ InputConfig: {
87
+ AppDataId: '',
88
+ PhoneNumber: '',
89
+ StepId: '',
90
+ Token: '',
91
+ BaseUrl: '',
92
+ SignMode: 'client',
93
+ AutoSign: false,
94
+ },
95
+ UrisConfig: {
96
+ QualifiedSmsApiUrl: '/web/evrotrust/doc-sign/send-otp',
97
+ QualifiedSignApiUrl: '/web/evrotrust/doc-sign/otp',
98
+ SmsApiUrl: '/web/doc-sign/send-otp',
99
+ SignApiUrl: '/web/doc-sign/otp',
100
+ DocsApiUrl: '/web/doc-sign/view',
101
+ AutoSignApiUrl: '/web/doc-sign/auto',
102
+ LabelsApiUrl: '/public/infrastructure/labels',
103
+ },
104
+ HeaderBackgroundColor: '#fffff',
105
+ HeaderLogo: 'https://placeholderlogo.com/img/placeholder-logo-1.png',
106
+ Show15DaysAgreement: true,
107
+ OrderId: '',
108
+ FirstName: '',
109
+ LastName: '',
110
+ PhoneNumber: '',
111
+ PDFs: [],
112
+ CallbackURL: '',
113
+ RedirectURL: '',
114
+ LabelTranslations: [],
115
+ };
116
+ // Instance-scoped configs
117
+ this.configs = new Map();
118
+ this.configSources = new Map();
119
+ this.notificationSources = new Map();
120
+ }
121
+ get data() {
122
+ return JSON.parse(localStorage.getItem('data') || '{}');
123
+ }
124
+ set data(value) {
125
+ localStorage.setItem('data', JSON.stringify(value));
126
+ }
127
+ // Cleanup method to remove instance data when no longer needed
128
+ cleanupInstance(instanceId) {
129
+ this.configs.delete(instanceId);
130
+ this.configSources.get(instanceId)?.complete();
131
+ this.configSources.delete(instanceId);
132
+ this.notificationSources.get(instanceId)?.complete();
133
+ this.notificationSources.delete(instanceId);
134
+ }
135
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: StateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
136
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: StateService, providedIn: 'root' }); }
137
+ }
138
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: StateService, decorators: [{
139
+ type: Injectable,
140
+ args: [{
141
+ providedIn: 'root',
142
+ }]
143
+ }], ctorParameters: () => [] });
144
+
145
+ function smoothScroll(target, duration) {
146
+ const start = window.scrollY;
147
+ const targetPosition = target.getBoundingClientRect().top + start;
148
+ const distance = targetPosition - start;
149
+ let startTime = null;
150
+ function animation(currentTime) {
151
+ if (startTime === null)
152
+ startTime = currentTime;
153
+ const timeElapsed = currentTime - startTime;
154
+ const run = ease(timeElapsed, start, distance, duration);
155
+ window.scrollTo(0, run);
156
+ if (timeElapsed < duration)
157
+ requestAnimationFrame(animation);
158
+ }
159
+ function ease(t, b, c, d) {
160
+ t /= d / 2;
161
+ if (t < 1)
162
+ return c / 2 * t * t + b;
163
+ t--;
164
+ return -c / 2 * (t * (t - 2) - 1) + b;
165
+ }
166
+ requestAnimationFrame(animation);
167
+ }
168
+
169
+ class LabelKeys {
170
+ constructor() {
171
+ this.DocumentOf = 'signComponent.viewfiles.documentOf';
172
+ this.NoSignedDocs = 'signComponent.viewfiles.noSignedDocs';
173
+ this.Agreement = 'signComponent.viewfiles.agreement';
174
+ this.Sign = 'signComponent.viewfiles.sign';
175
+ this.SendCode = 'signComponent.viewfiles.sendCode';
176
+ this.PhoneNumber = 'signComponent.viewfiles.phoneNumber';
177
+ this.FirstName = 'signComponent.viewfiles.firstName';
178
+ this.LastName = 'signComponent.viewfiles.lastName';
179
+ this.Confirm = 'signComponent.viewfiles.confirm';
180
+ this.CheckCode = 'signComponent.viewfiles.checkCode';
181
+ this.InsertCode = 'signComponent.viewfiles.insertCode';
182
+ this.SignDocuments = 'signComponent.viewfiles.signDocuments';
183
+ this.InvalidCode = 'signComponent.viewfiles.invalidCode';
184
+ this.Success = 'signComponent.viewfiles.success';
185
+ }
186
+ }
187
+
188
+ class BaseService {
189
+ getHeaders(instanceId) {
190
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
191
+ return new HttpHeaders({
192
+ 'Content-Type': 'application/json',
193
+ Authorization: `Bearer ${configToUse.InputConfig.Token}`,
194
+ });
195
+ }
196
+ // Keep backward compatibility
197
+ get headers() {
198
+ return this.getHeaders();
199
+ }
200
+ constructor(http, state) {
201
+ this.http = http;
202
+ this.state = state;
203
+ this.instanceId = 'default';
204
+ }
205
+ setInstanceId(instanceId) {
206
+ this.instanceId = instanceId;
207
+ }
208
+ }
209
+
210
+ class OtpService extends BaseService {
211
+ constructor(http, state) {
212
+ super(http, state);
213
+ }
214
+ sendOTP(documentId, instanceId) {
215
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
216
+ let url = configToUse.InputConfig.BaseUrl;
217
+ if (configToUse.InputConfig.SignQualified) {
218
+ url += configToUse.UrisConfig.QualifiedSmsApiUrl;
219
+ }
220
+ else {
221
+ url += configToUse.UrisConfig.SmsApiUrl;
222
+ }
223
+ const body = {
224
+ appDataId: configToUse.InputConfig.AppDataId,
225
+ stepId: configToUse.InputConfig.StepId,
226
+ documentId: documentId,
227
+ phoneNumber: configToUse.PhoneNumber,
228
+ };
229
+ return this.http.post(url, body, {
230
+ headers: this.getHeaders(instanceId),
231
+ });
232
+ }
233
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: OtpService, deps: [{ token: i1.HttpClient }, { token: StateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
234
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: OtpService, providedIn: 'root' }); }
235
+ }
236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: OtpService, decorators: [{
237
+ type: Injectable,
238
+ args: [{
239
+ providedIn: 'root',
240
+ }]
241
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
242
+
243
+ class SignService extends BaseService {
244
+ constructor(http, state) {
245
+ super(http, state);
246
+ }
247
+ signPDF(pdfId, otpCode, instanceId) {
248
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
249
+ let url = configToUse.InputConfig.BaseUrl;
250
+ if (configToUse.InputConfig.SignQualified) {
251
+ url += configToUse.UrisConfig.QualifiedSignApiUrl;
252
+ }
253
+ else {
254
+ url += configToUse.UrisConfig.SignApiUrl;
255
+ }
256
+ const body = {
257
+ appDataId: configToUse.InputConfig.AppDataId,
258
+ stepId: configToUse.InputConfig.StepId,
259
+ documentId: pdfId,
260
+ otp: otpCode,
261
+ phoneNumber: configToUse.PhoneNumber,
262
+ signMode: configToUse.InputConfig.SignMode,
263
+ ipAddress: '0.0.0.0',
264
+ };
265
+ return this.http.post(url, body, {
266
+ headers: this.getHeaders(instanceId),
267
+ });
268
+ }
269
+ autoSignPdfs(instanceId) {
270
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
271
+ const url = `${configToUse.InputConfig.BaseUrl}${configToUse.UrisConfig.AutoSignApiUrl}`;
272
+ const body = {
273
+ appDataId: configToUse.InputConfig.AppDataId,
274
+ stepId: configToUse.InputConfig.StepId,
275
+ signMode: configToUse.InputConfig.SignMode
276
+ };
277
+ return this.http.post(url, body, {
278
+ headers: this.getHeaders(instanceId),
279
+ });
280
+ }
281
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignService, deps: [{ token: i1.HttpClient }, { token: StateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
282
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignService, providedIn: 'root' }); }
283
+ }
284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignService, decorators: [{
285
+ type: Injectable,
286
+ args: [{
287
+ providedIn: 'root',
288
+ }]
289
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
290
+
291
+ class DocumentService extends BaseService {
292
+ constructor(http, state) {
293
+ super(http, state);
294
+ }
295
+ getDocumentsToBeSigned(instanceId) {
296
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
297
+ const url = `${configToUse.InputConfig.BaseUrl}${configToUse.UrisConfig.DocsApiUrl}`;
298
+ const body = {
299
+ appDataId: configToUse.InputConfig.AppDataId,
300
+ stepId: configToUse.InputConfig.StepId,
301
+ };
302
+ return this.http.post(url, body, { headers: this.getHeaders(instanceId) });
303
+ }
304
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DocumentService, deps: [{ token: i1.HttpClient }, { token: StateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
305
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DocumentService, providedIn: 'root' }); }
306
+ }
307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DocumentService, decorators: [{
308
+ type: Injectable,
309
+ args: [{
310
+ providedIn: 'root',
311
+ }]
312
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
313
+
314
+ class LabelsService {
315
+ constructor(http, state) {
316
+ this.http = http;
317
+ this.state = state;
318
+ }
319
+ getLabels(instanceId) {
320
+ const configToUse = this.state.getConfig(instanceId || 'default');
321
+ return this.http.get(`${configToUse.InputConfig.BaseUrl}${configToUse.UrisConfig.LabelsApiUrl}?AppSiteId=SignComponent`);
322
+ }
323
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: LabelsService, deps: [{ token: i1.HttpClient }, { token: StateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
324
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: LabelsService, providedIn: 'root' }); }
325
+ }
326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: LabelsService, decorators: [{
327
+ type: Injectable,
328
+ args: [{
329
+ providedIn: 'root',
330
+ }]
331
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
332
+
333
+ class TranslatePipe {
334
+ constructor(stateService, labelsService, cdr) {
335
+ this.stateService = stateService;
336
+ this.labelsService = labelsService;
337
+ this.cdr = cdr;
338
+ this.isLoading = false;
339
+ this.config = this.stateService.config;
340
+ }
341
+ ngOnDestroy() {
342
+ if (this.subscription) {
343
+ this.subscription.unsubscribe();
344
+ }
345
+ }
346
+ ngOnInit() {
347
+ this.subscription = this.stateService.config$.subscribe((config) => {
348
+ this.config = config;
349
+ });
350
+ }
351
+ loadLabelsIfNecessary() {
352
+ if (this.config.LabelTranslations.length === 0 && this.isLoading === false) {
353
+ if (this.config.InputConfig.BaseUrl) {
354
+ this.isLoading = true;
355
+ this.labelsService.getLabels('default').subscribe((labels) => {
356
+ this.config.LabelTranslations = labels;
357
+ this.isLoading = false;
358
+ // Update the default config
359
+ this.stateService.configSet(this.config, 'default');
360
+ this.cdr.detectChanges();
361
+ }, (error) => {
362
+ console.error('🔷 TranslatePipe: Failed to load translation labels:', error);
363
+ this.isLoading = false;
364
+ });
365
+ }
366
+ else {
367
+ console.warn('🔷 TranslatePipe: No BaseUrl available for loading translations');
368
+ }
369
+ }
370
+ }
371
+ transform(value, args) {
372
+ this.loadLabelsIfNecessary();
373
+ var translatedValue = this.config.LabelTranslations.find((t) => t.labelName == value)?.labelValue;
374
+ if (!translatedValue) {
375
+ return value;
376
+ }
377
+ if (args?.length && args?.length > 0) {
378
+ args.forEach((arg, index) => {
379
+ translatedValue = translatedValue.replace(`{${index}}`, arg);
380
+ });
381
+ }
382
+ return translatedValue;
383
+ }
384
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TranslatePipe, deps: [{ token: StateService }, { token: LabelsService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Pipe }); }
385
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.25", ngImport: i0, type: TranslatePipe, isStandalone: false, name: "translate", pure: false }); }
386
+ }
387
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TranslatePipe, decorators: [{
388
+ type: Pipe,
389
+ args: [{
390
+ name: 'translate',
391
+ pure: false,
392
+ standalone: false,
393
+ }]
394
+ }], ctorParameters: () => [{ type: StateService }, { type: LabelsService }, { type: i0.ChangeDetectorRef }] });
395
+
396
+ class ViewFilesComponent {
397
+ constructor(cdr, state, otpService, signService, documentService) {
398
+ this.cdr = cdr;
399
+ this.state = state;
400
+ this.otpService = otpService;
401
+ this.signService = signService;
402
+ this.documentService = documentService;
403
+ this.instanceId = 'default';
404
+ this.currentIndex = 0;
405
+ this.showPdfContainer = false;
406
+ this.showCodeContainer = false;
407
+ this.showCodeError = false;
408
+ this.showPhoneError = false;
409
+ this.showSendCodeError = false;
410
+ this.otpFlow = false;
411
+ this.isPhoneNumberDisabled = true;
412
+ this.isFirstNameProvided = false;
413
+ this.isLastNameProvided = false;
414
+ this.codeSent = false;
415
+ this.scrollPending = false;
416
+ this.labelKeys = new LabelKeys();
417
+ this.otpCode = '';
418
+ this.currentPdf = '';
419
+ this.isLoading = false;
420
+ this.isSigningDocument = false;
421
+ this.isSendingCode = false;
422
+ this.showSuccessSigned = false;
423
+ this.showNoSignedDocuments = false;
424
+ this.showErrorSigned = false;
425
+ this.hasInitialized = false;
426
+ this.documentsLoadingInProgress = false;
427
+ this.lastInstanceId = '';
428
+ // Don't initialize config here - instanceId is not set yet by Angular
429
+ }
430
+ ngOnDestroy() {
431
+ if (this.subscription) {
432
+ this.subscription.unsubscribe();
433
+ }
434
+ // Cleanup instance data when component is destroyed
435
+ if (this.lastInstanceId) {
436
+ this.state.cleanupInstance(this.lastInstanceId);
437
+ }
438
+ }
439
+ ngOnInit() {
440
+ if (this.config &&
441
+ this.config.InputConfig.Styles &&
442
+ typeof this.config.InputConfig.Styles.buttonStyles !== 'object') {
443
+ throw new Error(`Invalid config.InputConfig.Styles.buttonStyles: Expected an object.\n\nExample usage:\nconfig = {\n ...,\n InputConfig: {\n Styles: {\n buttonStyles: {\n backgroundColor: '#b9193f',\n color: '#1a1818',\n border: '6px solid',\n borderColor: '#bf3929',\n borderRadius: '12px',\n padding: '10px 30px',\n fontFamily: 'Trebuchet MS, sans-serif',\n fontSize: '16px',\n letterSpacing: '0px',\n width: 'auto',\n height: 'auto',\n minWidth: '80px',\n minHeight: 'fit-content',\n maxWidth: 'fit-content',\n maxHeight: 'fit-content'\n }\n }\n }\n}\n`);
444
+ }
445
+ // Check if instanceId has changed - if so, reset initialization
446
+ if (this.lastInstanceId !== this.instanceId) {
447
+ // Clean up previous instance if it exists
448
+ if (this.lastInstanceId) {
449
+ this.state.cleanupInstance(this.lastInstanceId);
450
+ if (this.subscription) {
451
+ this.subscription.unsubscribe();
452
+ }
453
+ }
454
+ // Reset initialization state for new instance
455
+ this.hasInitialized = false;
456
+ this.lastInstanceId = this.instanceId;
457
+ // Reset component state
458
+ this.resetComponentState();
459
+ }
460
+ if (this.hasInitialized)
461
+ return;
462
+ this.hasInitialized = true;
463
+ // Now instanceId is properly set by Angular's @Input
464
+ this.config = this.state.getConfig(this.instanceId);
465
+ this.isPhoneNumberDisabled = this.config.PhoneNumber ? true : false;
466
+ this.isFirstNameProvided = !!this.config.FirstName;
467
+ this.isLastNameProvided = !!this.config.LastName;
468
+ // Set up subscription to config changes for this instance
469
+ this.subscription = this.state
470
+ .getConfig$(this.instanceId)
471
+ .subscribe(async (config) => {
472
+ if (!config)
473
+ return;
474
+ this.config = config;
475
+ this.isPhoneNumberDisabled = this.config.PhoneNumber ? true : false;
476
+ this.isFirstNameProvided = !!this.config.FirstName;
477
+ this.isLastNameProvided = !!this.config.LastName;
478
+ if (this.config.InputConfig?.AppDataId &&
479
+ this.config.InputConfig?.StepId) {
480
+ if (this.config.InputConfig.AutoSign) {
481
+ this.otpFlow = false;
482
+ this.showPdfContainer = false;
483
+ this.proceedSigning();
484
+ }
485
+ else {
486
+ this.loadDocuments();
487
+ }
488
+ }
489
+ else {
490
+ this.resetLoadingState();
491
+ }
492
+ });
493
+ // Initial check for existing config
494
+ if (this.config?.InputConfig?.AppDataId &&
495
+ this.config?.InputConfig?.StepId) {
496
+ this.loadDocuments();
497
+ }
498
+ else {
499
+ this.resetLoadingState();
500
+ }
501
+ }
502
+ resetComponentState() {
503
+ // Reset all component state variables
504
+ this.currentIndex = 0;
505
+ this.showPdfContainer = false;
506
+ this.showCodeContainer = false;
507
+ this.showCodeError = false;
508
+ this.showPhoneError = false;
509
+ this.showSendCodeError = false;
510
+ this.otpFlow = false;
511
+ this.codeSent = false;
512
+ this.scrollPending = false;
513
+ this.otpCode = '';
514
+ this.currentPdf = '';
515
+ this.isLoading = false;
516
+ this.isSigningDocument = false;
517
+ this.isSendingCode = false;
518
+ this.showSuccessSigned = false;
519
+ this.showNoSignedDocuments = false;
520
+ this.showErrorSigned = false;
521
+ this.documentsLoadingInProgress = false;
522
+ }
523
+ resetLoadingState() {
524
+ this.isLoading = false;
525
+ this.documentsLoadingInProgress = false;
526
+ this.cdr.markForCheck();
527
+ }
528
+ loadDocuments() {
529
+ // Prevent multiple simultaneous loads
530
+ if (this.documentsLoadingInProgress) {
531
+ return;
532
+ }
533
+ // Reset error states
534
+ this.showNoSignedDocuments = false;
535
+ this.showErrorSigned = false;
536
+ this.documentsLoadingInProgress = true;
537
+ this.isLoading = true;
538
+ this.cdr.markForCheck();
539
+ // Add timeout to prevent infinite loading
540
+ const loadingTimeout = setTimeout(() => {
541
+ console.error('🔷 ViewFilesComponent: Loading timeout - forcing reset');
542
+ this.resetLoadingState();
543
+ this.showNoSignedDocuments = true;
544
+ }, 30000);
545
+ try {
546
+ if (!this.config.PDFs || this.config.PDFs.length === 0) {
547
+ this.documentService.getDocumentsToBeSigned(this.instanceId).subscribe({
548
+ next: (docsToSign) => {
549
+ clearTimeout(loadingTimeout);
550
+ this.resetLoadingState();
551
+ if (docsToSign && docsToSign.length > 0) {
552
+ this.config.PDFs = this.convertToSignDocuments(docsToSign);
553
+ this.showPdfContainer = true;
554
+ this.refreshCurrentPDF();
555
+ this.otpFlow = true;
556
+ this.showNoSignedDocuments = false;
557
+ }
558
+ else {
559
+ this.showPdfContainer = false;
560
+ this.showNoSignedDocuments = true;
561
+ }
562
+ // Force change detection
563
+ setTimeout(() => {
564
+ this.cdr.detectChanges();
565
+ }, 100);
566
+ },
567
+ error: (error) => {
568
+ clearTimeout(loadingTimeout);
569
+ console.error('🔷 ViewFilesComponent: Error fetching documents:', error);
570
+ this.resetLoadingState();
571
+ this.showPdfContainer = false;
572
+ this.showNoSignedDocuments = true;
573
+ this.cdr.markForCheck();
574
+ },
575
+ });
576
+ }
577
+ else {
578
+ clearTimeout(loadingTimeout);
579
+ // Documents already loaded
580
+ this.resetLoadingState();
581
+ this.showPdfContainer = this.config.PDFs.length > 0;
582
+ if (this.showPdfContainer) {
583
+ this.refreshCurrentPDF();
584
+ this.otpFlow = true;
585
+ }
586
+ else {
587
+ this.showNoSignedDocuments = true;
588
+ }
589
+ this.cdr.markForCheck();
590
+ }
591
+ }
592
+ catch (ex) {
593
+ clearTimeout(loadingTimeout);
594
+ console.error('🔷 ViewFilesComponent: Exception in loadDocuments:', ex);
595
+ this.resetLoadingState();
596
+ this.showPdfContainer = false;
597
+ this.showNoSignedDocuments = true;
598
+ this.cdr.markForCheck();
599
+ }
600
+ }
601
+ convertToSignDocuments(documents) {
602
+ return documents.map((d) => {
603
+ return {
604
+ id: d.id,
605
+ name: d.name,
606
+ data: d.data,
607
+ pdfData: this.getB64ForSrc(d.data),
608
+ isSigned: false,
609
+ isAgreementChecked: this.config.InputConfig.ShowConfirm == false,
610
+ };
611
+ });
612
+ }
613
+ ngAfterViewChecked() {
614
+ if (this.scrollPending && this.codeContainer) {
615
+ smoothScroll(this.codeContainer.nativeElement, 300);
616
+ this.scrollPending = false;
617
+ }
618
+ }
619
+ nextPdf() {
620
+ if (this.currentIndex < this.config.PDFs.length - 1) {
621
+ this.currentIndex++;
622
+ this.refreshCurrentPDF();
623
+ }
624
+ }
625
+ previousPdf() {
626
+ if (this.currentIndex > 0) {
627
+ this.currentIndex--;
628
+ this.refreshCurrentPDF();
629
+ }
630
+ }
631
+ refreshCurrentPDF() {
632
+ this.currentPdf = this.config.PDFs[this.currentIndex].pdfData;
633
+ }
634
+ proceedSigning() {
635
+ if (this.otpFlow) {
636
+ if (!this.config.PDFs[this.currentIndex].isAgreementChecked) {
637
+ return;
638
+ }
639
+ this.showCodeContainer = true;
640
+ // If phone is pre-provided, skip to send code directly
641
+ // Otherwise show the form so user can enter phone (and name if needed)
642
+ if (this.isPhoneNumberDisabled) {
643
+ this.codeSent = true;
644
+ this.sendCode();
645
+ }
646
+ else {
647
+ this.codeSent = false;
648
+ }
649
+ }
650
+ else {
651
+ if (!this.isSigningDocument) {
652
+ this.showPdfContainer = false;
653
+ this.isSigningDocument = true;
654
+ this.signService.autoSignPdfs(this.instanceId).subscribe({
655
+ next: (data) => {
656
+ this.isSigningDocument = false;
657
+ if (data && data.length > 0) {
658
+ this.showSuccessSigned = true;
659
+ }
660
+ else {
661
+ this.showNoSignedDocuments = true;
662
+ }
663
+ this.state.notify('success', this.instanceId);
664
+ },
665
+ error: (err) => {
666
+ this.isSigningDocument = false;
667
+ this.showErrorSigned = true;
668
+ },
669
+ });
670
+ }
671
+ }
672
+ }
673
+ get canSendCode() {
674
+ return !!this.config.PhoneNumber;
675
+ }
676
+ sendCode() {
677
+ this.isSendingCode = true;
678
+ this.showCodeError = false;
679
+ this.showSendCodeError = false;
680
+ this.showPhoneError = false;
681
+ if (this.config.PhoneNumber) {
682
+ const docId = this.config.PDFs[this.currentIndex].id;
683
+ if (docId) {
684
+ this.otpService.sendOTP(docId, this.instanceId).subscribe({
685
+ next: () => {
686
+ this.isSendingCode = false;
687
+ this.codeSent = true;
688
+ },
689
+ error: (err) => {
690
+ this.isSendingCode = false;
691
+ this.showSendCodeError = true;
692
+ console.error('Error sending code', err);
693
+ },
694
+ });
695
+ }
696
+ }
697
+ else {
698
+ this.isSendingCode = false;
699
+ this.showPhoneError = true;
700
+ }
701
+ }
702
+ retrySendCode() {
703
+ this.showSendCodeError = false;
704
+ this.showPhoneError = false;
705
+ }
706
+ signDocuments() {
707
+ this.showCodeError = false;
708
+ this.showErrorSigned = false;
709
+ if (this.otpCode) {
710
+ this.isSigningDocument = true;
711
+ this.signService
712
+ .signPDF(this.config.PDFs[this.currentIndex].id, this.otpCode, this.instanceId)
713
+ .subscribe({
714
+ next: (data) => {
715
+ this.isSigningDocument = false;
716
+ this.otpCode = '';
717
+ this.showCodeError = false;
718
+ // Mark all documents as signed
719
+ this.config.PDFs.forEach((i) => (i.isSigned = true));
720
+ // If all documents are signed mark as success
721
+ if (!this.config.PDFs.find((d) => !d.isSigned)) {
722
+ this.showSuccessSigned = true;
723
+ this.state.notify('success', this.instanceId);
724
+ }
725
+ this.showCodeContainer = false;
726
+ },
727
+ error: (err) => {
728
+ this.isSigningDocument = false;
729
+ if (err && err.status === 498) {
730
+ this.showCodeError = true;
731
+ }
732
+ else {
733
+ this.showErrorSigned = true;
734
+ }
735
+ },
736
+ });
737
+ }
738
+ else {
739
+ this.showCodeError = true;
740
+ }
741
+ }
742
+ getB64ForSrc(b64Value) {
743
+ return `data:application/pdf;base64,${b64Value}`;
744
+ }
745
+ retry() {
746
+ this.showCodeContainer = true;
747
+ this.codeSent = false;
748
+ this.showPhoneError = false;
749
+ this.showCodeError = false;
750
+ this.showErrorSigned = false;
751
+ }
752
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ViewFilesComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: StateService }, { token: OtpService }, { token: SignService }, { token: DocumentService }], target: i0.ɵɵFactoryTarget.Component }); }
753
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: ViewFilesComponent, isStandalone: false, selector: "lib-view-files", inputs: { instanceId: "instanceId" }, viewQueries: [{ propertyName: "codeContainer", first: true, predicate: ["codeContainer"], descendants: true }], ngImport: i0, template: "<!-- Loading State -->\n<div class=\"loading-section\" *ngIf=\"isLoading === true\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">{{ \"loading\" | translate }}...</p>\n</div>\n\n<!-- Signing Document State -->\n<div class=\"loading-section\" *ngIf=\"isSigningDocument === true\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">\n {{ \"signComponent.viewfiles.signingDocuments\" | translate }}...\n </p>\n</div>\n\n<div *ngIf=\"isLoading === false\">\n <!-- PDF Viewer -->\n <div *ngIf=\"showPdfContainer\" class=\"pdf-viewer-container\">\n <div class=\"documents-navigation\">\n <button\n (click)=\"previousPdf()\"\n [disabled]=\"currentIndex === 0 || isSigningDocument === true\"\n class=\"nav-button\"\n >\n &#8592;\n </button>\n\n <p class=\"file-info font-paragraph\">\n {{\n labelKeys.DocumentOf\n | translate : [currentIndex + 1, config.PDFs.length]\n }}\n </p>\n\n <button\n (click)=\"nextPdf()\"\n [disabled]=\"\n currentIndex === config.PDFs.length - 1 ||\n isSigningDocument === true ||\n showCodeContainer === true\n \"\n class=\"nav-button\"\n >\n &#8594;\n </button>\n </div>\n\n <pdf-viewer\n [src]=\"currentPdf\"\n [render-text]=\"true\"\n [original-size]=\"false\"\n [external-link-target]=\"'blank'\"\n class=\"pdf-viewer\"\n ></pdf-viewer>\n </div>\n\n <!-- No Signed Documents -->\n <div *ngIf=\"showNoSignedDocuments\" class=\"no-files font-paragraph\">\n <p>{{ labelKeys.NoSignedDocs | translate }}</p>\n </div>\n\n <!-- Agreement Section -->\n <div\n class=\"agreement-section\"\n *ngIf=\"\n config.PDFs.length > 0 &&\n !config.PDFs[currentIndex].isSigned &&\n !showCodeContainer\n \"\n >\n <div class=\"agreement-content\">\n <p class=\"font-paragraph\">\n {{ labelKeys.Agreement | translate }}\n </p>\n\n <div\n class=\"agreement-confirm-section\"\n *ngIf=\"config.InputConfig.ShowConfirm != false\"\n >\n <input\n class=\"checkbox component-checkbox\"\n type=\"checkbox\"\n id=\"termsCheckbox\"\n [(ngModel)]=\"config.PDFs[currentIndex].isAgreementChecked\"\n />\n <label\n for=\"termsCheckbox\"\n class=\"checkbox-confirm-label component-checkbox-label\"\n >{{ labelKeys.Confirm | translate }}</label\n >\n </div>\n </div>\n\n <button\n (click)=\"proceedSigning()\"\n [disabled]=\"!config.PDFs[currentIndex].isAgreementChecked\"\n class=\"sign-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ labelKeys.Sign | translate }}\n </button>\n </div>\n\n <!-- OTP / Send Code Flow -->\n <div\n #codeContainer\n class=\"send-code-container\"\n *ngIf=\"\n showCodeContainer &&\n config.PDFs.length > 0 &&\n config.PDFs[currentIndex].isSigned === false\n \"\n >\n <!-- Phase 1: Send Code (with name + phone inputs) -->\n <div class=\"send-code-content\" *ngIf=\"!codeSent\">\n <h2 class=\"font-heading-2\">{{ labelKeys.SendCode | translate }}</h2>\n\n <!-- First Name input (when not provided in config) -->\n <div *ngIf=\"!isFirstNameProvided\" class=\"name-input-group\">\n <label for=\"firstName\">\n {{ labelKeys.FirstName | translate }}\n </label>\n <input\n type=\"text\"\n id=\"firstName\"\n [(ngModel)]=\"config.FirstName\"\n />\n </div>\n\n <!-- Last Name input (when not provided in config) -->\n <div *ngIf=\"!isLastNameProvided\" class=\"name-input-group\">\n <label for=\"lastName\">\n {{ labelKeys.LastName | translate }}\n </label>\n <input\n type=\"text\"\n id=\"lastName\"\n [(ngModel)]=\"config.LastName\"\n />\n </div>\n\n <!-- Phone Number input -->\n <label class=\"component-camp-text-label\" for=\"phoneNumber\">\n {{ labelKeys.PhoneNumber | translate }}\n </label>\n <input\n class=\"component-camp-text\"\n type=\"text\"\n id=\"phoneNumber\"\n [(ngModel)]=\"config.PhoneNumber\"\n [disabled]=\"isPhoneNumberDisabled\"\n required\n />\n\n <button\n type=\"button\"\n (click)=\"sendCode()\"\n [disabled]=\"isSendingCode || !canSendCode\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{\n isSendingCode\n ? (\"signComponent.viewfiles.sending\" | translate) + \"...\"\n : (labelKeys.SendCode | translate)\n }}\n </button>\n\n <!-- Phone validation error -->\n <div *ngIf=\"showPhoneError\">\n <p class=\"error-message\">\n {{ \"signComponent.viewfiles.invalidPhoneNumber\" | translate }}\n </p>\n </div>\n\n <!-- Send code error -->\n <div *ngIf=\"showSendCodeError\">\n <p class=\"error-message\">\n {{ \"signComponent.viewfiles.errorSigning\" | translate }}\n </p>\n <button\n (click)=\"retrySendCode()\"\n class=\"retry-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ \"signComponent.viewfiles.retry\" | translate }}\n </button>\n </div>\n </div>\n\n <!-- Phase 2: Enter OTP Code -->\n <div class=\"code-sent-content\" *ngIf=\"codeSent\">\n <h2 class=\"font-heading-2\">{{ labelKeys.CheckCode | translate }}</h2>\n <label class=\"component-camp-text-label\" for=\"code-sent\">\n {{ labelKeys.InsertCode | translate }}\n </label>\n <input\n class=\"component-camp-text\"\n type=\"text\"\n id=\"code-sent\"\n autocomplete=\"off\"\n [(ngModel)]=\"otpCode\"\n required\n />\n <button\n (click)=\"signDocuments()\"\n [disabled]=\"isSigningDocument\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{\n isSigningDocument\n ? (\"signComponent.viewfiles.signing\" | translate) + \"...\"\n : (\"signComponent.viewfiles.signDocument\" | translate)\n }}\n </button>\n\n <!-- Invalid OTP code error -->\n <div *ngIf=\"showCodeError\">\n <p class=\"error-message\">{{ labelKeys.InvalidCode | translate }}</p>\n <button\n (click)=\"retry()\"\n class=\"retry-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ \"signComponent.viewfiles.retry\" | translate }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Success State -->\n <div class=\"success-section\" *ngIf=\"showSuccessSigned\">\n <div class=\"success-content\">\n <p class=\"font-paragraph success-message\">\n {{ \"signComponent.viewfiles.documentIsSigned\" | translate }}\n </p>\n </div>\n </div>\n\n <!-- Error State (with retry) -->\n <div class=\"success-section\" *ngIf=\"showErrorSigned\">\n <div class=\"success-content\">\n <p class=\"font-paragraph error-message\">\n {{ \"signComponent.viewfiles.errorSigning\" | translate }}\n </p>\n <button\n (click)=\"retry()\"\n class=\"retry-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ \"signComponent.viewfiles.retry\" | translate }}\n </button>\n </div>\n </div>\n</div>\n", styles: ["@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}*{box-sizing:border-box}div{text-align:center;margin:10px}.loading-spinner{width:36px;height:36px;border:3px solid #e5eaef;border-top-color:#5d87ff;border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 12px}.loading-section{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:32px 30px;box-shadow:0 2px 12px #00000014;border-radius:7px;animation:fadeIn .3s ease-out}.loading-text{color:#5a6a85;font-size:.95rem;margin:0}ng2-pdf-viewer{display:block;margin:0 auto;width:80%;height:600px}.pdf-viewer-container{display:flex;flex-direction:column;align-items:center;margin:20px}.documents-navigation{width:100%;display:flex;justify-content:center;align-items:center;gap:12px;margin-bottom:16px}.nav-button{width:40px;height:40px;background-color:#5d87ff;display:flex;justify-content:center;align-items:center;color:#fff;text-align:center;border:none;padding:0;border-radius:7px;cursor:pointer;font-size:1.2rem;font-weight:700;transition:background-color .2s ease;flex-shrink:0}.nav-button:hover{background-color:#5d87ffd9}.nav-button:disabled{background-color:#ccc;cursor:not-allowed}.file-info{margin:0 8px;font-weight:600;font-size:1rem;color:#2a3547}.pdf-viewer{width:100%;height:70vh;max-width:800px;max-height:900px;min-width:300px;min-height:400px;border:1px solid #e5eaef;border-radius:7px;overflow:auto}.no-files{text-align:center;margin-top:20px;color:#5a6a85}.agreement-section{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:20px 30px;position:relative;box-shadow:0 2px 12px #00000014;border-radius:7px}.agreement-content{display:flex;flex-direction:column;justify-content:center;align-items:center}.success-section{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:20px 30px;position:relative;box-shadow:0 2px 12px #00000014;border-radius:7px}.success-content{display:flex;flex-direction:column;justify-content:center;align-items:center}.success-message{color:#13deb9}.error-message{color:#fa896b}.checkbox{width:20px;height:20px;cursor:pointer;margin:0;accent-color:#5d87ff}.agreement-confirm-section{display:flex;flex-direction:row;justify-content:center;align-items:center}.checkbox-confirm-label{margin-left:4px;cursor:pointer}.sign-button{margin-top:10px;background-color:#5d87ff;color:#fff;border:none;padding:10px 24px;border-radius:7px;cursor:pointer;font-size:1rem;font-weight:600;transition:background-color .2s ease;min-height:44px}.sign-button:hover{background-color:#5d87ffd9}button:disabled{background-color:#ccc;cursor:not-allowed}.send-code-container button:disabled{background-color:#ccc;cursor:not-allowed}.send-code-container button:disabled:hover{background-color:#ccc}.send-code-container{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px 30px;gap:16px;position:relative;box-shadow:0 2px 12px #00000014;border-radius:7px}.send-code-container h2{font-size:1.3rem;color:#2a3547;font-weight:700}.send-code-container button{width:100%;background-color:#5d87ff;color:#fff;border:none;padding:10px 20px;border-radius:7px;cursor:pointer;font-size:1rem;font-weight:600;transition:background-color .2s ease;min-height:44px}.send-code-container button:hover{background-color:#5d87ffd9}.send-code-container input{background-color:#f2f6fa;width:100%;margin-bottom:10px;padding:10px 20px;text-align:center;border:1px solid #e5eaef;border-radius:7px;caret-color:#5d87ff;outline:none;font-weight:500;transition:border-color .2s ease;font-size:1rem}.send-code-container input:focus{border-color:#5d87ff}.send-code-content,.code-sent-content{display:flex;flex-direction:column;align-items:center;color:#2a3547}.send-code-container label{font-size:1rem;color:#5a6a85;line-height:17px;text-align:left;padding-left:2px;margin-right:auto;margin-bottom:10px}.name-input-group{width:100%;display:flex;flex-direction:column;align-items:flex-start;margin:0}.name-input-group label{font-size:1rem;color:#5a6a85;line-height:17px;text-align:left;padding-left:2px;margin-bottom:6px}.name-input-group input{background-color:#f2f6fa;width:100%;padding:10px 20px;text-align:left;border:1px solid #e5eaef;border-radius:7px;caret-color:#5d87ff;outline:none;font-weight:500;transition:border-color .2s ease;font-size:1rem}.name-input-group input:focus{border-color:#5d87ff}.retry-button{margin-top:12px;background-color:#5d87ff;color:#fff;border:none;padding:10px 24px;border-radius:7px;cursor:pointer;font-size:1rem;font-weight:600;transition:background-color .2s ease;min-height:44px}.retry-button:hover{background-color:#5d87ffd9}@media(max-width:600px){.documents-navigation{flex-wrap:wrap;gap:8px}.file-info{order:-1;width:100%;margin:0 0 4px;font-size:.9rem}.nav-button{margin:0}.pdf-viewer{height:60vh;min-height:300px}.agreement-section,.success-section,.send-code-container{padding:16px 20px}}\n"], dependencies: [{ kind: "component", type: i5.PdfViewerComponent, selector: "pdf-viewer", inputs: ["src", "c-maps-url", "page", "render-text", "render-text-mode", "original-size", "show-all", "stick-to-page", "zoom", "zoom-scale", "rotation", "external-link-target", "autoresize", "fit-to-page", "show-borders"], outputs: ["after-load-complete", "page-rendered", "pages-initialized", "text-layer-rendered", "error", "on-progress", "pageChange"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i6.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i7.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: i7.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
754
+ }
755
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ViewFilesComponent, decorators: [{
756
+ type: Component,
757
+ args: [{ selector: 'lib-view-files', standalone: false, template: "<!-- Loading State -->\n<div class=\"loading-section\" *ngIf=\"isLoading === true\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">{{ \"loading\" | translate }}...</p>\n</div>\n\n<!-- Signing Document State -->\n<div class=\"loading-section\" *ngIf=\"isSigningDocument === true\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">\n {{ \"signComponent.viewfiles.signingDocuments\" | translate }}...\n </p>\n</div>\n\n<div *ngIf=\"isLoading === false\">\n <!-- PDF Viewer -->\n <div *ngIf=\"showPdfContainer\" class=\"pdf-viewer-container\">\n <div class=\"documents-navigation\">\n <button\n (click)=\"previousPdf()\"\n [disabled]=\"currentIndex === 0 || isSigningDocument === true\"\n class=\"nav-button\"\n >\n &#8592;\n </button>\n\n <p class=\"file-info font-paragraph\">\n {{\n labelKeys.DocumentOf\n | translate : [currentIndex + 1, config.PDFs.length]\n }}\n </p>\n\n <button\n (click)=\"nextPdf()\"\n [disabled]=\"\n currentIndex === config.PDFs.length - 1 ||\n isSigningDocument === true ||\n showCodeContainer === true\n \"\n class=\"nav-button\"\n >\n &#8594;\n </button>\n </div>\n\n <pdf-viewer\n [src]=\"currentPdf\"\n [render-text]=\"true\"\n [original-size]=\"false\"\n [external-link-target]=\"'blank'\"\n class=\"pdf-viewer\"\n ></pdf-viewer>\n </div>\n\n <!-- No Signed Documents -->\n <div *ngIf=\"showNoSignedDocuments\" class=\"no-files font-paragraph\">\n <p>{{ labelKeys.NoSignedDocs | translate }}</p>\n </div>\n\n <!-- Agreement Section -->\n <div\n class=\"agreement-section\"\n *ngIf=\"\n config.PDFs.length > 0 &&\n !config.PDFs[currentIndex].isSigned &&\n !showCodeContainer\n \"\n >\n <div class=\"agreement-content\">\n <p class=\"font-paragraph\">\n {{ labelKeys.Agreement | translate }}\n </p>\n\n <div\n class=\"agreement-confirm-section\"\n *ngIf=\"config.InputConfig.ShowConfirm != false\"\n >\n <input\n class=\"checkbox component-checkbox\"\n type=\"checkbox\"\n id=\"termsCheckbox\"\n [(ngModel)]=\"config.PDFs[currentIndex].isAgreementChecked\"\n />\n <label\n for=\"termsCheckbox\"\n class=\"checkbox-confirm-label component-checkbox-label\"\n >{{ labelKeys.Confirm | translate }}</label\n >\n </div>\n </div>\n\n <button\n (click)=\"proceedSigning()\"\n [disabled]=\"!config.PDFs[currentIndex].isAgreementChecked\"\n class=\"sign-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ labelKeys.Sign | translate }}\n </button>\n </div>\n\n <!-- OTP / Send Code Flow -->\n <div\n #codeContainer\n class=\"send-code-container\"\n *ngIf=\"\n showCodeContainer &&\n config.PDFs.length > 0 &&\n config.PDFs[currentIndex].isSigned === false\n \"\n >\n <!-- Phase 1: Send Code (with name + phone inputs) -->\n <div class=\"send-code-content\" *ngIf=\"!codeSent\">\n <h2 class=\"font-heading-2\">{{ labelKeys.SendCode | translate }}</h2>\n\n <!-- First Name input (when not provided in config) -->\n <div *ngIf=\"!isFirstNameProvided\" class=\"name-input-group\">\n <label for=\"firstName\">\n {{ labelKeys.FirstName | translate }}\n </label>\n <input\n type=\"text\"\n id=\"firstName\"\n [(ngModel)]=\"config.FirstName\"\n />\n </div>\n\n <!-- Last Name input (when not provided in config) -->\n <div *ngIf=\"!isLastNameProvided\" class=\"name-input-group\">\n <label for=\"lastName\">\n {{ labelKeys.LastName | translate }}\n </label>\n <input\n type=\"text\"\n id=\"lastName\"\n [(ngModel)]=\"config.LastName\"\n />\n </div>\n\n <!-- Phone Number input -->\n <label class=\"component-camp-text-label\" for=\"phoneNumber\">\n {{ labelKeys.PhoneNumber | translate }}\n </label>\n <input\n class=\"component-camp-text\"\n type=\"text\"\n id=\"phoneNumber\"\n [(ngModel)]=\"config.PhoneNumber\"\n [disabled]=\"isPhoneNumberDisabled\"\n required\n />\n\n <button\n type=\"button\"\n (click)=\"sendCode()\"\n [disabled]=\"isSendingCode || !canSendCode\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{\n isSendingCode\n ? (\"signComponent.viewfiles.sending\" | translate) + \"...\"\n : (labelKeys.SendCode | translate)\n }}\n </button>\n\n <!-- Phone validation error -->\n <div *ngIf=\"showPhoneError\">\n <p class=\"error-message\">\n {{ \"signComponent.viewfiles.invalidPhoneNumber\" | translate }}\n </p>\n </div>\n\n <!-- Send code error -->\n <div *ngIf=\"showSendCodeError\">\n <p class=\"error-message\">\n {{ \"signComponent.viewfiles.errorSigning\" | translate }}\n </p>\n <button\n (click)=\"retrySendCode()\"\n class=\"retry-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ \"signComponent.viewfiles.retry\" | translate }}\n </button>\n </div>\n </div>\n\n <!-- Phase 2: Enter OTP Code -->\n <div class=\"code-sent-content\" *ngIf=\"codeSent\">\n <h2 class=\"font-heading-2\">{{ labelKeys.CheckCode | translate }}</h2>\n <label class=\"component-camp-text-label\" for=\"code-sent\">\n {{ labelKeys.InsertCode | translate }}\n </label>\n <input\n class=\"component-camp-text\"\n type=\"text\"\n id=\"code-sent\"\n autocomplete=\"off\"\n [(ngModel)]=\"otpCode\"\n required\n />\n <button\n (click)=\"signDocuments()\"\n [disabled]=\"isSigningDocument\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{\n isSigningDocument\n ? (\"signComponent.viewfiles.signing\" | translate) + \"...\"\n : (\"signComponent.viewfiles.signDocument\" | translate)\n }}\n </button>\n\n <!-- Invalid OTP code error -->\n <div *ngIf=\"showCodeError\">\n <p class=\"error-message\">{{ labelKeys.InvalidCode | translate }}</p>\n <button\n (click)=\"retry()\"\n class=\"retry-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ \"signComponent.viewfiles.retry\" | translate }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Success State -->\n <div class=\"success-section\" *ngIf=\"showSuccessSigned\">\n <div class=\"success-content\">\n <p class=\"font-paragraph success-message\">\n {{ \"signComponent.viewfiles.documentIsSigned\" | translate }}\n </p>\n </div>\n </div>\n\n <!-- Error State (with retry) -->\n <div class=\"success-section\" *ngIf=\"showErrorSigned\">\n <div class=\"success-content\">\n <p class=\"font-paragraph error-message\">\n {{ \"signComponent.viewfiles.errorSigning\" | translate }}\n </p>\n <button\n (click)=\"retry()\"\n class=\"retry-button\"\n [ngStyle]=\"config.InputConfig.Styles?.buttonStyles\"\n >\n {{ \"signComponent.viewfiles.retry\" | translate }}\n </button>\n </div>\n </div>\n</div>\n", styles: ["@keyframes spin{to{transform:rotate(360deg)}}@keyframes fadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}*{box-sizing:border-box}div{text-align:center;margin:10px}.loading-spinner{width:36px;height:36px;border:3px solid #e5eaef;border-top-color:#5d87ff;border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 12px}.loading-section{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:32px 30px;box-shadow:0 2px 12px #00000014;border-radius:7px;animation:fadeIn .3s ease-out}.loading-text{color:#5a6a85;font-size:.95rem;margin:0}ng2-pdf-viewer{display:block;margin:0 auto;width:80%;height:600px}.pdf-viewer-container{display:flex;flex-direction:column;align-items:center;margin:20px}.documents-navigation{width:100%;display:flex;justify-content:center;align-items:center;gap:12px;margin-bottom:16px}.nav-button{width:40px;height:40px;background-color:#5d87ff;display:flex;justify-content:center;align-items:center;color:#fff;text-align:center;border:none;padding:0;border-radius:7px;cursor:pointer;font-size:1.2rem;font-weight:700;transition:background-color .2s ease;flex-shrink:0}.nav-button:hover{background-color:#5d87ffd9}.nav-button:disabled{background-color:#ccc;cursor:not-allowed}.file-info{margin:0 8px;font-weight:600;font-size:1rem;color:#2a3547}.pdf-viewer{width:100%;height:70vh;max-width:800px;max-height:900px;min-width:300px;min-height:400px;border:1px solid #e5eaef;border-radius:7px;overflow:auto}.no-files{text-align:center;margin-top:20px;color:#5a6a85}.agreement-section{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:20px 30px;position:relative;box-shadow:0 2px 12px #00000014;border-radius:7px}.agreement-content{display:flex;flex-direction:column;justify-content:center;align-items:center}.success-section{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:20px 30px;position:relative;box-shadow:0 2px 12px #00000014;border-radius:7px}.success-content{display:flex;flex-direction:column;justify-content:center;align-items:center}.success-message{color:#13deb9}.error-message{color:#fa896b}.checkbox{width:20px;height:20px;cursor:pointer;margin:0;accent-color:#5d87ff}.agreement-confirm-section{display:flex;flex-direction:row;justify-content:center;align-items:center}.checkbox-confirm-label{margin-left:4px;cursor:pointer}.sign-button{margin-top:10px;background-color:#5d87ff;color:#fff;border:none;padding:10px 24px;border-radius:7px;cursor:pointer;font-size:1rem;font-weight:600;transition:background-color .2s ease;min-height:44px}.sign-button:hover{background-color:#5d87ffd9}button:disabled{background-color:#ccc;cursor:not-allowed}.send-code-container button:disabled{background-color:#ccc;cursor:not-allowed}.send-code-container button:disabled:hover{background-color:#ccc}.send-code-container{width:min(500px,90%);background-color:#fff;margin:0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px 30px;gap:16px;position:relative;box-shadow:0 2px 12px #00000014;border-radius:7px}.send-code-container h2{font-size:1.3rem;color:#2a3547;font-weight:700}.send-code-container button{width:100%;background-color:#5d87ff;color:#fff;border:none;padding:10px 20px;border-radius:7px;cursor:pointer;font-size:1rem;font-weight:600;transition:background-color .2s ease;min-height:44px}.send-code-container button:hover{background-color:#5d87ffd9}.send-code-container input{background-color:#f2f6fa;width:100%;margin-bottom:10px;padding:10px 20px;text-align:center;border:1px solid #e5eaef;border-radius:7px;caret-color:#5d87ff;outline:none;font-weight:500;transition:border-color .2s ease;font-size:1rem}.send-code-container input:focus{border-color:#5d87ff}.send-code-content,.code-sent-content{display:flex;flex-direction:column;align-items:center;color:#2a3547}.send-code-container label{font-size:1rem;color:#5a6a85;line-height:17px;text-align:left;padding-left:2px;margin-right:auto;margin-bottom:10px}.name-input-group{width:100%;display:flex;flex-direction:column;align-items:flex-start;margin:0}.name-input-group label{font-size:1rem;color:#5a6a85;line-height:17px;text-align:left;padding-left:2px;margin-bottom:6px}.name-input-group input{background-color:#f2f6fa;width:100%;padding:10px 20px;text-align:left;border:1px solid #e5eaef;border-radius:7px;caret-color:#5d87ff;outline:none;font-weight:500;transition:border-color .2s ease;font-size:1rem}.name-input-group input:focus{border-color:#5d87ff}.retry-button{margin-top:12px;background-color:#5d87ff;color:#fff;border:none;padding:10px 24px;border-radius:7px;cursor:pointer;font-size:1rem;font-weight:600;transition:background-color .2s ease;min-height:44px}.retry-button:hover{background-color:#5d87ffd9}@media(max-width:600px){.documents-navigation{flex-wrap:wrap;gap:8px}.file-info{order:-1;width:100%;margin:0 0 4px;font-size:.9rem}.nav-button{margin:0}.pdf-viewer{height:60vh;min-height:300px}.agreement-section,.success-section,.send-code-container{padding:16px 20px}}\n"] }]
758
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: StateService }, { type: OtpService }, { type: SignService }, { type: DocumentService }], propDecorators: { codeContainer: [{
759
+ type: ViewChild,
760
+ args: ['codeContainer']
761
+ }], instanceId: [{
762
+ type: Input
763
+ }] } });
764
+
765
+ class SignLibComponent {
766
+ constructor(state) {
767
+ this.state = state;
768
+ this.config = undefined;
769
+ this.instanceId = '';
770
+ this.event = new EventEmitter();
771
+ // Don't initialize signConfig here - we'll create a fresh one in ngOnInit
772
+ }
773
+ async ngOnInit() {
774
+ this.event.emit('loaded');
775
+ // Generate instanceId if not provided
776
+ if (!this.instanceId) {
777
+ this.instanceId = 'sign-lib-' + Math.random().toString(36).substr(2, 9);
778
+ }
779
+ if (typeof this.config === 'string') {
780
+ try {
781
+ this.config = JSON.parse(this.config);
782
+ }
783
+ catch (error) {
784
+ console.error('Invalid config: ', error);
785
+ }
786
+ }
787
+ if (this.config) {
788
+ // Create a fresh signConfig for this instance - don't reuse the old one!
789
+ this.signConfig = this.state.getConfig(this.instanceId);
790
+ // Update with new input config
791
+ this.signConfig.InputConfig = this.config;
792
+ this.signConfig.InputConfig.AppDataId = this.config.AppDataId || 'n/a';
793
+ this.signConfig.InputConfig.StepId = this.config.StepId || 'n/a';
794
+ this.signConfig.InputConfig.PhoneNumber =
795
+ this.config.PhoneNumber || 'n/a';
796
+ this.signConfig.PhoneNumber = this.config.PhoneNumber || 'n/a';
797
+ // Ensure PDFs array is empty for new instance
798
+ this.signConfig.PDFs = [];
799
+ this.signConfig.LabelTranslations = Object.entries(this.config.Labels || {}).map((i) => {
800
+ return {
801
+ labelName: 'signComponent.viewfiles.' + i[0],
802
+ labelValue: i[1],
803
+ };
804
+ });
805
+ // IMPORTANT: Also update the default config with connection info so TranslatePipe can work
806
+ const defaultConfig = this.state.getConfig('default');
807
+ if (!defaultConfig.InputConfig.BaseUrl ||
808
+ defaultConfig.InputConfig.BaseUrl === '') {
809
+ defaultConfig.InputConfig.BaseUrl = this.config.BaseUrl;
810
+ defaultConfig.InputConfig.Token = this.config.Token;
811
+ this.state.configSet(defaultConfig, 'default');
812
+ }
813
+ // Preserve existing translations if they exist in any instance
814
+ const existingTranslations = defaultConfig.LabelTranslations;
815
+ if (existingTranslations && existingTranslations.length > 0) {
816
+ this.signConfig.LabelTranslations = [...existingTranslations];
817
+ }
818
+ // Use instance-scoped state
819
+ this.state.configSet(this.signConfig, this.instanceId);
820
+ this.state.getNotification(this.instanceId).subscribe((message) => {
821
+ if (message) {
822
+ this.event.emit(message);
823
+ }
824
+ });
825
+ }
826
+ }
827
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibComponent, deps: [{ token: StateService }], target: i0.ɵɵFactoryTarget.Component }); }
828
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: SignLibComponent, isStandalone: false, selector: "lib-sign", inputs: { config: "config", instanceId: "instanceId" }, outputs: { event: "event" }, ngImport: i0, template: `<lib-view-files
829
+ *ngIf="config"
830
+ [instanceId]="instanceId"
831
+ ></lib-view-files>`, isInline: true, dependencies: [{ kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ViewFilesComponent, selector: "lib-view-files", inputs: ["instanceId"] }] }); }
832
+ }
833
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibComponent, decorators: [{
834
+ type: Component,
835
+ args: [{ selector: 'lib-sign', template: `<lib-view-files
836
+ *ngIf="config"
837
+ [instanceId]="instanceId"
838
+ ></lib-view-files>`, standalone: false }]
839
+ }], ctorParameters: () => [{ type: StateService }], propDecorators: { config: [{
840
+ type: Input
841
+ }], instanceId: [{
842
+ type: Input
843
+ }], event: [{
844
+ type: Output
845
+ }] } });
846
+
847
+ class SignLibModule {
848
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
849
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.25", ngImport: i0, type: SignLibModule, declarations: [SignLibComponent, ViewFilesComponent, TranslatePipe], imports: [PdfViewerModule, CommonModule, HttpClientModule, FormsModule], exports: [SignLibComponent] }); }
850
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibModule, providers: [TranslatePipe], imports: [PdfViewerModule, CommonModule, HttpClientModule, FormsModule] }); }
851
+ }
852
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibModule, decorators: [{
853
+ type: NgModule,
854
+ args: [{
855
+ declarations: [SignLibComponent, ViewFilesComponent, TranslatePipe],
856
+ imports: [PdfViewerModule, CommonModule, HttpClientModule, FormsModule],
857
+ exports: [SignLibComponent],
858
+ providers: [TranslatePipe],
859
+ }]
860
+ }] });
861
+
862
+ /*
863
+ * Public API Surface of sign-lib
864
+ */
865
+
866
+ /**
867
+ * Generated bundle index. Do not edit.
868
+ */
869
+
870
+ export { SignLibComponent, SignLibModule, SignLibService };
871
+ //# sourceMappingURL=qbs-sign-sign-lib.mjs.map