@qbs-sign/sign-lib 2.1.26-dev.1

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,956 @@
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 { getWebInstrumentations, initializeFaro, faro } from '@grafana/faro-web-sdk';
5
+ import { TracingInstrumentation } from '@grafana/faro-web-tracing';
6
+ import * as i6 from '@angular/common';
7
+ import { CommonModule } from '@angular/common';
8
+ import * as i1 from '@angular/common/http';
9
+ import { HttpHeaders, HttpClientModule } from '@angular/common/http';
10
+ import * as i5 from 'ng2-pdf-viewer';
11
+ import { PdfViewerModule } from 'ng2-pdf-viewer';
12
+ import * as i7 from '@angular/forms';
13
+ import { FormsModule } from '@angular/forms';
14
+
15
+ class SignLibService {
16
+ constructor() { }
17
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
18
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibService, providedIn: 'root' }); }
19
+ }
20
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibService, decorators: [{
21
+ type: Injectable,
22
+ args: [{
23
+ providedIn: 'root',
24
+ }]
25
+ }], ctorParameters: () => [] });
26
+
27
+ class StateService {
28
+ // Default instance for backward compatibility
29
+ get config() {
30
+ return this.getConfig('default');
31
+ }
32
+ get config$() {
33
+ return this.getConfig$('default');
34
+ }
35
+ get notification() {
36
+ return this.getNotification('default');
37
+ }
38
+ getConfig(instanceId = 'default') {
39
+ if (!this.configs.has(instanceId)) {
40
+ this.configs.set(instanceId, { ...this.defaultConfig });
41
+ }
42
+ return this.configs.get(instanceId);
43
+ }
44
+ getConfig$(instanceId = 'default') {
45
+ if (!this.configSources.has(instanceId)) {
46
+ const config = this.getConfig(instanceId);
47
+ this.configSources.set(instanceId, new BehaviorSubject(config));
48
+ }
49
+ return this.configSources.get(instanceId).asObservable();
50
+ }
51
+ getNotification(instanceId = 'default') {
52
+ if (!this.notificationSources.has(instanceId)) {
53
+ this.notificationSources.set(instanceId, new BehaviorSubject(''));
54
+ }
55
+ return this.notificationSources.get(instanceId).asObservable();
56
+ }
57
+ configSet(config, instanceId = 'default') {
58
+ this.configs.set(instanceId, config);
59
+ if (!this.configSources.has(instanceId)) {
60
+ this.configSources.set(instanceId, new BehaviorSubject(config));
61
+ }
62
+ else {
63
+ this.configSources.get(instanceId).next(config);
64
+ }
65
+ // If this config has translations, share them with ALL instances
66
+ if (config.LabelTranslations && config.LabelTranslations.length > 0) {
67
+ // Share with all existing instances
68
+ for (const [id, existingConfig] of this.configs.entries()) {
69
+ if (id !== instanceId &&
70
+ (!existingConfig.LabelTranslations ||
71
+ existingConfig.LabelTranslations.length === 0)) {
72
+ existingConfig.LabelTranslations = [...config.LabelTranslations];
73
+ this.configSources.get(id)?.next(existingConfig);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ notify(notification, instanceId = 'default') {
79
+ if (!this.notificationSources.has(instanceId)) {
80
+ this.notificationSources.set(instanceId, new BehaviorSubject(notification));
81
+ }
82
+ else {
83
+ this.notificationSources.get(instanceId).next(notification);
84
+ }
85
+ }
86
+ constructor() {
87
+ this.defaultConfig = {
88
+ InputConfig: {
89
+ AppDataId: '',
90
+ PhoneNumber: '',
91
+ StepId: '',
92
+ Token: '',
93
+ BaseUrl: '',
94
+ SignMode: 'client',
95
+ AutoSign: false,
96
+ },
97
+ UrisConfig: {
98
+ QualifiedSmsApiUrl: '/web/evrotrust/doc-sign/send-otp',
99
+ QualifiedSignApiUrl: '/web/evrotrust/doc-sign/otp',
100
+ SmsApiUrl: '/web/doc-sign/send-otp',
101
+ SignApiUrl: '/web/doc-sign/otp',
102
+ DocsApiUrl: '/web/doc-sign/view',
103
+ AutoSignApiUrl: '/web/doc-sign/auto',
104
+ LabelsApiUrl: '/public/infrastructure/labels',
105
+ },
106
+ HeaderBackgroundColor: '#fffff',
107
+ HeaderLogo: 'https://placeholderlogo.com/img/placeholder-logo-1.png',
108
+ Show15DaysAgreement: true,
109
+ OrderId: '',
110
+ FirstName: '',
111
+ LastName: '',
112
+ PhoneNumber: '',
113
+ PDFs: [],
114
+ CallbackURL: '',
115
+ RedirectURL: '',
116
+ LabelTranslations: [],
117
+ };
118
+ // Instance-scoped configs
119
+ this.configs = new Map();
120
+ this.configSources = new Map();
121
+ this.notificationSources = new Map();
122
+ }
123
+ get data() {
124
+ return JSON.parse(localStorage.getItem('data') || '{}');
125
+ }
126
+ set data(value) {
127
+ localStorage.setItem('data', JSON.stringify(value));
128
+ }
129
+ // Cleanup method to remove instance data when no longer needed
130
+ cleanupInstance(instanceId) {
131
+ this.configs.delete(instanceId);
132
+ this.configSources.get(instanceId)?.complete();
133
+ this.configSources.delete(instanceId);
134
+ this.notificationSources.get(instanceId)?.complete();
135
+ this.notificationSources.delete(instanceId);
136
+ }
137
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: StateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
138
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: StateService, providedIn: 'root' }); }
139
+ }
140
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: StateService, decorators: [{
141
+ type: Injectable,
142
+ args: [{
143
+ providedIn: 'root',
144
+ }]
145
+ }], ctorParameters: () => [] });
146
+
147
+ // Auto-updated by npm version scripts — do not edit manually.
148
+ const version = '2.1.26-dev.1';
149
+
150
+ const DEFAULT_COLLECTOR_URL = 'https://telemetry.id-kyc.com/collect';
151
+ class TelemetryService {
152
+ constructor() {
153
+ this.initialized = false;
154
+ }
155
+ initialize(config = {}) {
156
+ if (this.initialized)
157
+ return;
158
+ const collectorUrl = (config.collectorUrl ?? DEFAULT_COLLECTOR_URL).trim();
159
+ if (!collectorUrl || collectorUrl.startsWith('_PLACEHOLDER_'))
160
+ return;
161
+ const tracingEnabled = config.tracingEnabled ?? true;
162
+ const instrumentations = [...getWebInstrumentations()];
163
+ if (tracingEnabled) {
164
+ instrumentations.push(new TracingInstrumentation({
165
+ instrumentationOptions: {
166
+ propagateTraceHeaderCorsUrls: this.parseCorsUrls(config.propagateTraceHeaderCorsUrls ?? ''),
167
+ },
168
+ }));
169
+ }
170
+ initializeFaro({
171
+ url: collectorUrl,
172
+ app: {
173
+ name: config.appName ?? 'qbs-sign',
174
+ version: config.appVersion ?? version,
175
+ environment: config.environment ?? 'prod',
176
+ },
177
+ instrumentations,
178
+ });
179
+ this.initialized = true;
180
+ }
181
+ trackEvent(name, attributes = {}) {
182
+ if (!this.initialized)
183
+ return;
184
+ faro.api.pushEvent(name, attributes, 'qbs-sign', { skipDedupe: true });
185
+ }
186
+ trackLog(level, args, context = {}) {
187
+ if (!this.initialized)
188
+ return;
189
+ faro.api.pushLog(args, { level, context, skipDedupe: true });
190
+ }
191
+ trackError(error, context = {}) {
192
+ if (!this.initialized)
193
+ return;
194
+ faro.api.pushError(this.toError(error), {
195
+ type: 'manual',
196
+ context,
197
+ skipDedupe: true,
198
+ });
199
+ }
200
+ parseCorsUrls(value) {
201
+ return value
202
+ .split(',')
203
+ .map((e) => e.trim())
204
+ .filter(Boolean)
205
+ .map((e) => (e.startsWith('/') && e.endsWith('/') ? new RegExp(e.slice(1, -1)) : e));
206
+ }
207
+ toError(error) {
208
+ if (error instanceof Error)
209
+ return error;
210
+ if (typeof error === 'string')
211
+ return new Error(error);
212
+ try {
213
+ return new Error(JSON.stringify(error, null, 2));
214
+ }
215
+ catch {
216
+ return new Error(String(error));
217
+ }
218
+ }
219
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
220
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TelemetryService, providedIn: 'root' }); }
221
+ }
222
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TelemetryService, decorators: [{
223
+ type: Injectable,
224
+ args: [{
225
+ providedIn: 'root',
226
+ }]
227
+ }] });
228
+
229
+ function smoothScroll(target, duration) {
230
+ const start = window.scrollY;
231
+ const targetPosition = target.getBoundingClientRect().top + start;
232
+ const distance = targetPosition - start;
233
+ let startTime = null;
234
+ function animation(currentTime) {
235
+ if (startTime === null)
236
+ startTime = currentTime;
237
+ const timeElapsed = currentTime - startTime;
238
+ const run = ease(timeElapsed, start, distance, duration);
239
+ window.scrollTo(0, run);
240
+ if (timeElapsed < duration)
241
+ requestAnimationFrame(animation);
242
+ }
243
+ function ease(t, b, c, d) {
244
+ t /= d / 2;
245
+ if (t < 1)
246
+ return c / 2 * t * t + b;
247
+ t--;
248
+ return -c / 2 * (t * (t - 2) - 1) + b;
249
+ }
250
+ requestAnimationFrame(animation);
251
+ }
252
+
253
+ class LabelKeys {
254
+ constructor() {
255
+ this.DocumentOf = 'signComponent.viewfiles.documentOf';
256
+ this.NoSignedDocs = 'signComponent.viewfiles.noSignedDocs';
257
+ this.Agreement = 'signComponent.viewfiles.agreement';
258
+ this.Sign = 'signComponent.viewfiles.sign';
259
+ this.SendCode = 'signComponent.viewfiles.sendCode';
260
+ this.PhoneNumber = 'signComponent.viewfiles.phoneNumber';
261
+ this.FirstName = 'signComponent.viewfiles.firstName';
262
+ this.LastName = 'signComponent.viewfiles.lastName';
263
+ this.Confirm = 'signComponent.viewfiles.confirm';
264
+ this.CheckCode = 'signComponent.viewfiles.checkCode';
265
+ this.InsertCode = 'signComponent.viewfiles.insertCode';
266
+ this.SignDocuments = 'signComponent.viewfiles.signDocuments';
267
+ this.InvalidCode = 'signComponent.viewfiles.invalidCode';
268
+ this.Success = 'signComponent.viewfiles.success';
269
+ }
270
+ }
271
+
272
+ class BaseService {
273
+ getHeaders(instanceId) {
274
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
275
+ return new HttpHeaders({
276
+ 'Content-Type': 'application/json',
277
+ Authorization: `Bearer ${configToUse.InputConfig.Token}`,
278
+ });
279
+ }
280
+ // Keep backward compatibility
281
+ get headers() {
282
+ return this.getHeaders();
283
+ }
284
+ constructor(http, state) {
285
+ this.http = http;
286
+ this.state = state;
287
+ this.instanceId = 'default';
288
+ }
289
+ setInstanceId(instanceId) {
290
+ this.instanceId = instanceId;
291
+ }
292
+ }
293
+
294
+ class OtpService extends BaseService {
295
+ constructor(http, state) {
296
+ super(http, state);
297
+ }
298
+ sendOTP(documentId, instanceId) {
299
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
300
+ let url = configToUse.InputConfig.BaseUrl;
301
+ if (configToUse.InputConfig.SignQualified) {
302
+ url += configToUse.UrisConfig.QualifiedSmsApiUrl;
303
+ }
304
+ else {
305
+ url += configToUse.UrisConfig.SmsApiUrl;
306
+ }
307
+ const body = {
308
+ appDataId: configToUse.InputConfig.AppDataId,
309
+ stepId: configToUse.InputConfig.StepId,
310
+ documentId: documentId,
311
+ phoneNumber: configToUse.PhoneNumber,
312
+ };
313
+ return this.http.post(url, body, {
314
+ headers: this.getHeaders(instanceId),
315
+ });
316
+ }
317
+ 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 }); }
318
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: OtpService, providedIn: 'root' }); }
319
+ }
320
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: OtpService, decorators: [{
321
+ type: Injectable,
322
+ args: [{
323
+ providedIn: 'root',
324
+ }]
325
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
326
+
327
+ class SignService extends BaseService {
328
+ constructor(http, state) {
329
+ super(http, state);
330
+ }
331
+ signPDF(pdfId, otpCode, instanceId) {
332
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
333
+ let url = configToUse.InputConfig.BaseUrl;
334
+ if (configToUse.InputConfig.SignQualified) {
335
+ url += configToUse.UrisConfig.QualifiedSignApiUrl;
336
+ }
337
+ else {
338
+ url += configToUse.UrisConfig.SignApiUrl;
339
+ }
340
+ const body = {
341
+ appDataId: configToUse.InputConfig.AppDataId,
342
+ stepId: configToUse.InputConfig.StepId,
343
+ documentId: pdfId,
344
+ otp: otpCode,
345
+ phoneNumber: configToUse.PhoneNumber,
346
+ signMode: configToUse.InputConfig.SignMode,
347
+ ipAddress: '0.0.0.0',
348
+ };
349
+ return this.http.post(url, body, {
350
+ headers: this.getHeaders(instanceId),
351
+ });
352
+ }
353
+ autoSignPdfs(instanceId) {
354
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
355
+ const url = `${configToUse.InputConfig.BaseUrl}${configToUse.UrisConfig.AutoSignApiUrl}`;
356
+ const body = {
357
+ appDataId: configToUse.InputConfig.AppDataId,
358
+ stepId: configToUse.InputConfig.StepId,
359
+ signMode: configToUse.InputConfig.SignMode
360
+ };
361
+ return this.http.post(url, body, {
362
+ headers: this.getHeaders(instanceId),
363
+ });
364
+ }
365
+ 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 }); }
366
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignService, providedIn: 'root' }); }
367
+ }
368
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignService, decorators: [{
369
+ type: Injectable,
370
+ args: [{
371
+ providedIn: 'root',
372
+ }]
373
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
374
+
375
+ class DocumentService extends BaseService {
376
+ constructor(http, state) {
377
+ super(http, state);
378
+ }
379
+ getDocumentsToBeSigned(instanceId) {
380
+ const configToUse = this.state.getConfig(instanceId || this.instanceId);
381
+ const url = `${configToUse.InputConfig.BaseUrl}${configToUse.UrisConfig.DocsApiUrl}`;
382
+ const body = {
383
+ appDataId: configToUse.InputConfig.AppDataId,
384
+ stepId: configToUse.InputConfig.StepId,
385
+ };
386
+ return this.http.post(url, body, { headers: this.getHeaders(instanceId) });
387
+ }
388
+ 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 }); }
389
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DocumentService, providedIn: 'root' }); }
390
+ }
391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DocumentService, decorators: [{
392
+ type: Injectable,
393
+ args: [{
394
+ providedIn: 'root',
395
+ }]
396
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
397
+
398
+ class LabelsService {
399
+ constructor(http, state) {
400
+ this.http = http;
401
+ this.state = state;
402
+ }
403
+ getLabels(instanceId) {
404
+ const configToUse = this.state.getConfig(instanceId || 'default');
405
+ return this.http.get(`${configToUse.InputConfig.BaseUrl}${configToUse.UrisConfig.LabelsApiUrl}?AppSiteId=SignComponent`);
406
+ }
407
+ 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 }); }
408
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: LabelsService, providedIn: 'root' }); }
409
+ }
410
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: LabelsService, decorators: [{
411
+ type: Injectable,
412
+ args: [{
413
+ providedIn: 'root',
414
+ }]
415
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: StateService }] });
416
+
417
+ class TranslatePipe {
418
+ constructor(stateService, labelsService, cdr) {
419
+ this.stateService = stateService;
420
+ this.labelsService = labelsService;
421
+ this.cdr = cdr;
422
+ this.isLoading = false;
423
+ this.config = this.stateService.config;
424
+ }
425
+ ngOnDestroy() {
426
+ if (this.subscription) {
427
+ this.subscription.unsubscribe();
428
+ }
429
+ }
430
+ ngOnInit() {
431
+ this.subscription = this.stateService.config$.subscribe((config) => {
432
+ this.config = config;
433
+ });
434
+ }
435
+ loadLabelsIfNecessary() {
436
+ if (this.config.LabelTranslations.length === 0 && this.isLoading === false) {
437
+ if (this.config.InputConfig.BaseUrl) {
438
+ this.isLoading = true;
439
+ this.labelsService.getLabels('default').subscribe((labels) => {
440
+ this.config.LabelTranslations = labels;
441
+ this.isLoading = false;
442
+ // Update the default config
443
+ this.stateService.configSet(this.config, 'default');
444
+ this.cdr.detectChanges();
445
+ }, (error) => {
446
+ console.error('🔷 TranslatePipe: Failed to load translation labels:', error);
447
+ this.isLoading = false;
448
+ });
449
+ }
450
+ else {
451
+ console.warn('🔷 TranslatePipe: No BaseUrl available for loading translations');
452
+ }
453
+ }
454
+ }
455
+ transform(value, args) {
456
+ this.loadLabelsIfNecessary();
457
+ var translatedValue = this.config.LabelTranslations.find((t) => t.labelName == value)?.labelValue;
458
+ if (!translatedValue) {
459
+ return value;
460
+ }
461
+ if (args?.length && args?.length > 0) {
462
+ args.forEach((arg, index) => {
463
+ translatedValue = translatedValue.replace(`{${index}}`, arg);
464
+ });
465
+ }
466
+ return translatedValue;
467
+ }
468
+ 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 }); }
469
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.25", ngImport: i0, type: TranslatePipe, isStandalone: false, name: "translate", pure: false }); }
470
+ }
471
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TranslatePipe, decorators: [{
472
+ type: Pipe,
473
+ args: [{
474
+ name: 'translate',
475
+ pure: false,
476
+ standalone: false,
477
+ }]
478
+ }], ctorParameters: () => [{ type: StateService }, { type: LabelsService }, { type: i0.ChangeDetectorRef }] });
479
+
480
+ class ViewFilesComponent {
481
+ constructor(cdr, state, otpService, signService, documentService) {
482
+ this.cdr = cdr;
483
+ this.state = state;
484
+ this.otpService = otpService;
485
+ this.signService = signService;
486
+ this.documentService = documentService;
487
+ this.instanceId = 'default';
488
+ this.currentIndex = 0;
489
+ this.showPdfContainer = false;
490
+ this.showCodeContainer = false;
491
+ this.showCodeError = false;
492
+ this.showPhoneError = false;
493
+ this.showSendCodeError = false;
494
+ this.otpFlow = false;
495
+ this.isPhoneNumberDisabled = true;
496
+ this.isFirstNameProvided = false;
497
+ this.isLastNameProvided = false;
498
+ this.codeSent = false;
499
+ this.scrollPending = false;
500
+ this.labelKeys = new LabelKeys();
501
+ this.otpCode = '';
502
+ this.currentPdf = '';
503
+ this.isLoading = false;
504
+ this.isSigningDocument = false;
505
+ this.isSendingCode = false;
506
+ this.showSuccessSigned = false;
507
+ this.showNoSignedDocuments = false;
508
+ this.showErrorSigned = false;
509
+ this.hasInitialized = false;
510
+ this.documentsLoadingInProgress = false;
511
+ this.lastInstanceId = '';
512
+ // Don't initialize config here - instanceId is not set yet by Angular
513
+ }
514
+ ngOnDestroy() {
515
+ if (this.subscription) {
516
+ this.subscription.unsubscribe();
517
+ }
518
+ // Cleanup instance data when component is destroyed
519
+ if (this.lastInstanceId) {
520
+ this.state.cleanupInstance(this.lastInstanceId);
521
+ }
522
+ }
523
+ ngOnInit() {
524
+ if (this.config &&
525
+ this.config.InputConfig.Styles &&
526
+ typeof this.config.InputConfig.Styles.buttonStyles !== 'object') {
527
+ 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`);
528
+ }
529
+ // Check if instanceId has changed - if so, reset initialization
530
+ if (this.lastInstanceId !== this.instanceId) {
531
+ // Clean up previous instance if it exists
532
+ if (this.lastInstanceId) {
533
+ this.state.cleanupInstance(this.lastInstanceId);
534
+ if (this.subscription) {
535
+ this.subscription.unsubscribe();
536
+ }
537
+ }
538
+ // Reset initialization state for new instance
539
+ this.hasInitialized = false;
540
+ this.lastInstanceId = this.instanceId;
541
+ // Reset component state
542
+ this.resetComponentState();
543
+ }
544
+ if (this.hasInitialized)
545
+ return;
546
+ this.hasInitialized = true;
547
+ // Now instanceId is properly set by Angular's @Input
548
+ this.config = this.state.getConfig(this.instanceId);
549
+ this.isPhoneNumberDisabled = this.config.PhoneNumber ? true : false;
550
+ this.isFirstNameProvided = !!this.config.FirstName;
551
+ this.isLastNameProvided = !!this.config.LastName;
552
+ // Set up subscription to config changes for this instance
553
+ this.subscription = this.state
554
+ .getConfig$(this.instanceId)
555
+ .subscribe(async (config) => {
556
+ if (!config)
557
+ return;
558
+ this.config = config;
559
+ this.isPhoneNumberDisabled = this.config.PhoneNumber ? true : false;
560
+ this.isFirstNameProvided = !!this.config.FirstName;
561
+ this.isLastNameProvided = !!this.config.LastName;
562
+ if (this.config.InputConfig?.AppDataId &&
563
+ this.config.InputConfig?.StepId) {
564
+ if (this.config.InputConfig.AutoSign) {
565
+ this.otpFlow = false;
566
+ this.showPdfContainer = false;
567
+ this.proceedSigning();
568
+ }
569
+ else {
570
+ this.loadDocuments();
571
+ }
572
+ }
573
+ else {
574
+ this.resetLoadingState();
575
+ }
576
+ });
577
+ // Initial check for existing config
578
+ if (this.config?.InputConfig?.AppDataId &&
579
+ this.config?.InputConfig?.StepId) {
580
+ this.loadDocuments();
581
+ }
582
+ else {
583
+ this.resetLoadingState();
584
+ }
585
+ }
586
+ resetComponentState() {
587
+ // Reset all component state variables
588
+ this.currentIndex = 0;
589
+ this.showPdfContainer = false;
590
+ this.showCodeContainer = false;
591
+ this.showCodeError = false;
592
+ this.showPhoneError = false;
593
+ this.showSendCodeError = false;
594
+ this.otpFlow = false;
595
+ this.codeSent = false;
596
+ this.scrollPending = false;
597
+ this.otpCode = '';
598
+ this.currentPdf = '';
599
+ this.isLoading = false;
600
+ this.isSigningDocument = false;
601
+ this.isSendingCode = false;
602
+ this.showSuccessSigned = false;
603
+ this.showNoSignedDocuments = false;
604
+ this.showErrorSigned = false;
605
+ this.documentsLoadingInProgress = false;
606
+ }
607
+ resetLoadingState() {
608
+ this.isLoading = false;
609
+ this.documentsLoadingInProgress = false;
610
+ this.cdr.markForCheck();
611
+ }
612
+ loadDocuments() {
613
+ // Prevent multiple simultaneous loads
614
+ if (this.documentsLoadingInProgress) {
615
+ return;
616
+ }
617
+ // Reset error states
618
+ this.showNoSignedDocuments = false;
619
+ this.showErrorSigned = false;
620
+ this.documentsLoadingInProgress = true;
621
+ this.isLoading = true;
622
+ this.cdr.markForCheck();
623
+ // Add timeout to prevent infinite loading
624
+ const loadingTimeout = setTimeout(() => {
625
+ console.error('🔷 ViewFilesComponent: Loading timeout - forcing reset');
626
+ this.resetLoadingState();
627
+ this.showNoSignedDocuments = true;
628
+ }, 30000);
629
+ try {
630
+ if (!this.config.PDFs || this.config.PDFs.length === 0) {
631
+ this.documentService.getDocumentsToBeSigned(this.instanceId).subscribe({
632
+ next: (docsToSign) => {
633
+ clearTimeout(loadingTimeout);
634
+ this.resetLoadingState();
635
+ if (docsToSign && docsToSign.length > 0) {
636
+ this.config.PDFs = this.convertToSignDocuments(docsToSign);
637
+ this.showPdfContainer = true;
638
+ this.refreshCurrentPDF();
639
+ this.otpFlow = true;
640
+ this.showNoSignedDocuments = false;
641
+ }
642
+ else {
643
+ this.showPdfContainer = false;
644
+ this.showNoSignedDocuments = true;
645
+ }
646
+ // Force change detection
647
+ setTimeout(() => {
648
+ this.cdr.detectChanges();
649
+ }, 100);
650
+ },
651
+ error: (error) => {
652
+ clearTimeout(loadingTimeout);
653
+ console.error('🔷 ViewFilesComponent: Error fetching documents:', error);
654
+ this.resetLoadingState();
655
+ this.showPdfContainer = false;
656
+ this.showNoSignedDocuments = true;
657
+ this.cdr.markForCheck();
658
+ },
659
+ });
660
+ }
661
+ else {
662
+ clearTimeout(loadingTimeout);
663
+ // Documents already loaded
664
+ this.resetLoadingState();
665
+ this.showPdfContainer = this.config.PDFs.length > 0;
666
+ if (this.showPdfContainer) {
667
+ this.refreshCurrentPDF();
668
+ this.otpFlow = true;
669
+ }
670
+ else {
671
+ this.showNoSignedDocuments = true;
672
+ }
673
+ this.cdr.markForCheck();
674
+ }
675
+ }
676
+ catch (ex) {
677
+ clearTimeout(loadingTimeout);
678
+ console.error('🔷 ViewFilesComponent: Exception in loadDocuments:', ex);
679
+ this.resetLoadingState();
680
+ this.showPdfContainer = false;
681
+ this.showNoSignedDocuments = true;
682
+ this.cdr.markForCheck();
683
+ }
684
+ }
685
+ convertToSignDocuments(documents) {
686
+ return documents.map((d) => {
687
+ return {
688
+ id: d.id,
689
+ name: d.name,
690
+ data: d.data,
691
+ pdfData: this.getB64ForSrc(d.data),
692
+ isSigned: false,
693
+ isAgreementChecked: this.config.InputConfig.ShowConfirm == false,
694
+ };
695
+ });
696
+ }
697
+ ngAfterViewChecked() {
698
+ if (this.scrollPending && this.codeContainer) {
699
+ smoothScroll(this.codeContainer.nativeElement, 300);
700
+ this.scrollPending = false;
701
+ }
702
+ }
703
+ nextPdf() {
704
+ if (this.currentIndex < this.config.PDFs.length - 1) {
705
+ this.currentIndex++;
706
+ this.refreshCurrentPDF();
707
+ }
708
+ }
709
+ previousPdf() {
710
+ if (this.currentIndex > 0) {
711
+ this.currentIndex--;
712
+ this.refreshCurrentPDF();
713
+ }
714
+ }
715
+ refreshCurrentPDF() {
716
+ this.currentPdf = this.config.PDFs[this.currentIndex].pdfData;
717
+ }
718
+ proceedSigning() {
719
+ if (this.otpFlow) {
720
+ if (!this.config.PDFs[this.currentIndex].isAgreementChecked) {
721
+ return;
722
+ }
723
+ this.showCodeContainer = true;
724
+ // If phone is pre-provided, skip to send code directly
725
+ // Otherwise show the form so user can enter phone (and name if needed)
726
+ if (this.isPhoneNumberDisabled) {
727
+ this.codeSent = true;
728
+ this.sendCode();
729
+ }
730
+ else {
731
+ this.codeSent = false;
732
+ }
733
+ }
734
+ else {
735
+ if (!this.isSigningDocument) {
736
+ this.showPdfContainer = false;
737
+ this.isSigningDocument = true;
738
+ this.signService.autoSignPdfs(this.instanceId).subscribe({
739
+ next: (data) => {
740
+ this.isSigningDocument = false;
741
+ if (data && data.length > 0) {
742
+ this.showSuccessSigned = true;
743
+ }
744
+ else {
745
+ this.showNoSignedDocuments = true;
746
+ }
747
+ this.state.notify('success', this.instanceId);
748
+ },
749
+ error: (err) => {
750
+ this.isSigningDocument = false;
751
+ this.showErrorSigned = true;
752
+ },
753
+ });
754
+ }
755
+ }
756
+ }
757
+ get canSendCode() {
758
+ return !!this.config.PhoneNumber;
759
+ }
760
+ sendCode() {
761
+ this.isSendingCode = true;
762
+ this.showCodeError = false;
763
+ this.showSendCodeError = false;
764
+ this.showPhoneError = false;
765
+ if (this.config.PhoneNumber) {
766
+ const docId = this.config.PDFs[this.currentIndex].id;
767
+ if (docId) {
768
+ this.otpService.sendOTP(docId, this.instanceId).subscribe({
769
+ next: () => {
770
+ this.isSendingCode = false;
771
+ this.codeSent = true;
772
+ },
773
+ error: (err) => {
774
+ this.isSendingCode = false;
775
+ this.showSendCodeError = true;
776
+ console.error('Error sending code', err);
777
+ },
778
+ });
779
+ }
780
+ }
781
+ else {
782
+ this.isSendingCode = false;
783
+ this.showPhoneError = true;
784
+ }
785
+ }
786
+ retrySendCode() {
787
+ this.showSendCodeError = false;
788
+ this.showPhoneError = false;
789
+ }
790
+ signDocuments() {
791
+ this.showCodeError = false;
792
+ this.showErrorSigned = false;
793
+ if (this.otpCode) {
794
+ this.isSigningDocument = true;
795
+ this.signService
796
+ .signPDF(this.config.PDFs[this.currentIndex].id, this.otpCode, this.instanceId)
797
+ .subscribe({
798
+ next: (data) => {
799
+ this.isSigningDocument = false;
800
+ this.otpCode = '';
801
+ this.showCodeError = false;
802
+ // Mark all documents as signed
803
+ this.config.PDFs.forEach((i) => (i.isSigned = true));
804
+ // If all documents are signed mark as success
805
+ if (!this.config.PDFs.find((d) => !d.isSigned)) {
806
+ this.showSuccessSigned = true;
807
+ this.state.notify('success', this.instanceId);
808
+ }
809
+ this.showCodeContainer = false;
810
+ },
811
+ error: (err) => {
812
+ this.isSigningDocument = false;
813
+ if (err && err.status === 498) {
814
+ this.showCodeError = true;
815
+ }
816
+ else {
817
+ this.showErrorSigned = true;
818
+ }
819
+ },
820
+ });
821
+ }
822
+ else {
823
+ this.showCodeError = true;
824
+ }
825
+ }
826
+ getB64ForSrc(b64Value) {
827
+ return `data:application/pdf;base64,${b64Value}`;
828
+ }
829
+ retry() {
830
+ this.showCodeContainer = true;
831
+ this.codeSent = false;
832
+ this.showPhoneError = false;
833
+ this.showCodeError = false;
834
+ this.showErrorSigned = false;
835
+ }
836
+ 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 }); }
837
+ 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" }] }); }
838
+ }
839
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ViewFilesComponent, decorators: [{
840
+ type: Component,
841
+ 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"] }]
842
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: StateService }, { type: OtpService }, { type: SignService }, { type: DocumentService }], propDecorators: { codeContainer: [{
843
+ type: ViewChild,
844
+ args: ['codeContainer']
845
+ }], instanceId: [{
846
+ type: Input
847
+ }] } });
848
+
849
+ class SignLibComponent {
850
+ constructor(state, telemetry) {
851
+ this.state = state;
852
+ this.telemetry = telemetry;
853
+ this.config = undefined;
854
+ this.instanceId = '';
855
+ this.event = new EventEmitter();
856
+ }
857
+ async ngOnInit() {
858
+ this.event.emit('loaded');
859
+ // Generate instanceId if not provided
860
+ if (!this.instanceId) {
861
+ this.instanceId = 'sign-lib-' + Math.random().toString(36).substr(2, 9);
862
+ }
863
+ if (typeof this.config === 'string') {
864
+ try {
865
+ this.config = JSON.parse(this.config);
866
+ }
867
+ catch (error) {
868
+ console.error('Invalid config: ', error);
869
+ }
870
+ }
871
+ if (this.config) {
872
+ this.telemetry.initialize(this.config.Telemetry ?? {});
873
+ // Create a fresh signConfig for this instance - don't reuse the old one!
874
+ this.signConfig = this.state.getConfig(this.instanceId);
875
+ // Update with new input config
876
+ this.signConfig.InputConfig = this.config;
877
+ this.signConfig.InputConfig.AppDataId = this.config.AppDataId || 'n/a';
878
+ this.signConfig.InputConfig.StepId = this.config.StepId || 'n/a';
879
+ this.signConfig.InputConfig.PhoneNumber =
880
+ this.config.PhoneNumber || 'n/a';
881
+ this.signConfig.PhoneNumber = this.config.PhoneNumber || 'n/a';
882
+ // Ensure PDFs array is empty for new instance
883
+ this.signConfig.PDFs = [];
884
+ this.signConfig.LabelTranslations = Object.entries(this.config.Labels || {}).map((i) => {
885
+ return {
886
+ labelName: 'signComponent.viewfiles.' + i[0],
887
+ labelValue: i[1],
888
+ };
889
+ });
890
+ // IMPORTANT: Also update the default config with connection info so TranslatePipe can work
891
+ const defaultConfig = this.state.getConfig('default');
892
+ if (!defaultConfig.InputConfig.BaseUrl ||
893
+ defaultConfig.InputConfig.BaseUrl === '') {
894
+ defaultConfig.InputConfig.BaseUrl = this.config.BaseUrl;
895
+ defaultConfig.InputConfig.Token = this.config.Token;
896
+ this.state.configSet(defaultConfig, 'default');
897
+ }
898
+ // Preserve existing translations if they exist in any instance
899
+ const existingTranslations = defaultConfig.LabelTranslations;
900
+ if (existingTranslations && existingTranslations.length > 0) {
901
+ this.signConfig.LabelTranslations = [...existingTranslations];
902
+ }
903
+ // Use instance-scoped state
904
+ this.state.configSet(this.signConfig, this.instanceId);
905
+ this.state.getNotification(this.instanceId).subscribe((message) => {
906
+ if (message) {
907
+ this.event.emit(message);
908
+ }
909
+ });
910
+ }
911
+ }
912
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibComponent, deps: [{ token: StateService }, { token: TelemetryService }], target: i0.ɵɵFactoryTarget.Component }); }
913
+ 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
914
+ *ngIf="config"
915
+ [instanceId]="instanceId"
916
+ ></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"] }] }); }
917
+ }
918
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibComponent, decorators: [{
919
+ type: Component,
920
+ args: [{ selector: 'lib-sign', template: `<lib-view-files
921
+ *ngIf="config"
922
+ [instanceId]="instanceId"
923
+ ></lib-view-files>`, standalone: false }]
924
+ }], ctorParameters: () => [{ type: StateService }, { type: TelemetryService }], propDecorators: { config: [{
925
+ type: Input
926
+ }], instanceId: [{
927
+ type: Input
928
+ }], event: [{
929
+ type: Output
930
+ }] } });
931
+
932
+ class SignLibModule {
933
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
934
+ 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] }); }
935
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibModule, providers: [TranslatePipe], imports: [PdfViewerModule, CommonModule, HttpClientModule, FormsModule] }); }
936
+ }
937
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: SignLibModule, decorators: [{
938
+ type: NgModule,
939
+ args: [{
940
+ declarations: [SignLibComponent, ViewFilesComponent, TranslatePipe],
941
+ imports: [PdfViewerModule, CommonModule, HttpClientModule, FormsModule],
942
+ exports: [SignLibComponent],
943
+ providers: [TranslatePipe],
944
+ }]
945
+ }] });
946
+
947
+ /*
948
+ * Public API Surface of sign-lib
949
+ */
950
+
951
+ /**
952
+ * Generated bundle index. Do not edit.
953
+ */
954
+
955
+ export { SignLibComponent, SignLibModule, SignLibService, TelemetryService, version };
956
+ //# sourceMappingURL=qbs-sign-sign-lib.mjs.map