tango-app-api-trax 3.9.34 → 3.9.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-trax",
3
- "version": "3.9.34",
3
+ "version": "3.9.35",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -18,7 +18,7 @@ import timeZone from 'dayjs/plugin/timezone.js';
18
18
  import utc from 'dayjs/plugin/utc.js';
19
19
  import { logger } from 'tango-app-api-middleware';
20
20
  import mongoose from 'mongoose';
21
- import { sendPushNotification, sendAiPushNotification, sendEmailWithSES, signedUrl, fileUpload, getOpenSearchData } from 'tango-app-api-middleware';
21
+ import { sendPushNotification, sendAiPushNotification, sendEmailWithSES, signedUrl, fileUpload } from 'tango-app-api-middleware';
22
22
  // import * as planoService from '../services/planogram.service.js';
23
23
  import * as clusterServices from '../services/cluster.service.js';
24
24
  import * as teamsServices from '../services/teams.service.js';
@@ -3667,31 +3667,36 @@ export const downloadInsertPdf = async ( req, res ) => {
3667
3667
  const safeName = ( str ) =>
3668
3668
  ( str || '' ).toString().replace( /[<>:"/\\|?*]+/g, '_' );
3669
3669
 
3670
- const query = {
3671
- query: {
3672
- bool: {
3673
- must: [
3674
- {
3675
- term: {
3676
- _id: req.body.checklistId,
3677
- },
3678
- },
3679
- ],
3680
- },
3681
- },
3682
- };
3670
+ // const query = {
3671
+ // query: {
3672
+ // bool: {
3673
+ // must: [
3674
+ // {
3675
+ // term: {
3676
+ // _id: req.body.checklistId,
3677
+ // },
3678
+ // },
3679
+ // ],
3680
+ // },
3681
+ // },
3682
+ // };
3683
+
3683
3684
 
3684
3685
  // 1) Launch browser page + fetch OpenSearch data in parallel
3685
3686
  const [ browser, aiDetails ] = await Promise.all( [
3686
3687
  getBrowserInstance(),
3687
- getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).traxIndex, query ),
3688
+ // getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).traxIndex, query ),
3689
+ processedchecklist.findOne( { _id: req.body.checklistId } ),
3688
3690
  ] );
3691
+ console.log( aiDetails );
3689
3692
 
3690
- if ( aiDetails?.statusCode != 200 || !aiDetails?.body?.hits?.hits.length ) {
3693
+ if ( !aiDetails ) {
3691
3694
  return res.sendError( 'Checklist not found', 404 );
3692
3695
  }
3693
3696
 
3694
- const doc = { ...aiDetails.body.hits.hits[0]._source };
3697
+ const doc = { ...aiDetails?.toObject() };
3698
+
3699
+ console.log( doc );
3695
3700
 
3696
3701
  // 2) Fetch brandInfo + compliance data in parallel
3697
3702
  const complianceURL = JSON.parse( process.env.LAMBDAURL ).complianceHistory;
@@ -72,6 +72,10 @@
72
72
  .q-answer-media img,.q-answer-media video,.q-answer-item td img{display:block;width:200px;height:180px;object-fit:cover;border-radius:6px;margin-bottom:6px}
73
73
  .img-grid{display:flex;flex-wrap:wrap;gap:8px}
74
74
  .img-grid img{margin-bottom:0}
75
+ .answer-media-grid{display:flex;flex-wrap:wrap;gap:12px;margin-top:8px}
76
+ .answer-media-cell{width:calc(50% - 6px)}
77
+ .answer-media-cell .q-answer-caption{margin-bottom:4px}
78
+ .answer-media-cell img{display:block;width:100%;height:200px;object-fit:cover;border-radius:6px;margin-bottom:0}
75
79
  .q-answer-link{font-size:12px;color:#0085D2;text-decoration:underline;word-break:break-all}
76
80
  .q-answer-caption{font-size:11px;color:#666;margin-bottom:4px}
77
81
  .q-answer-remarks{font-size:11px;color:#666;margin-top:6px;white-space:pre-line}
@@ -82,6 +86,7 @@
82
86
  .user-verify-name{font-size:14px;color:#1a1a1a;font-weight:600}
83
87
  .user-verify-sign-label{font-size:12px;color:#666;margin-top:16px;margin-bottom:6px;font-weight:600}
84
88
  .user-verify-signature{display:block;width:220px;height:auto;max-height:110px;object-fit:contain;border:1px solid #eee;border-radius:6px;padding:6px;background:#fff}
89
+ .user-verify-signature-text{display:inline-block;min-width:180px;font-size:15px;font-weight:600;color:#1a1a1a;padding-bottom:6px;border-bottom:1px solid #d9d9d9;word-break:break-word}
85
90
  /* Footer */
86
91
  .page-footer{position:absolute;bottom:20px;left:40px;right:40px;display:flex;justify-content:space-between;align-items:center;font-size:11px;color:#999;border-top:1px solid #d9d9d9;padding-top:10px}
87
92
  .footer-brand{display:flex;align-items:center;gap:8px;font-weight:600;color:#0066CC;font-size:11px}
@@ -119,8 +124,12 @@
119
124
 
120
125
  <div class="cover-summary">
121
126
  <div class="cover-sum-row"><span class="cover-sum-label">No. of questions</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{numQuestions}}</span></div>
122
- <div class="cover-sum-row"><span class="cover-sum-label">No. of flags</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{numFlags}}</span></div>
123
- {{!-- <div class="cover-sum-row"><span class="cover-sum-label">AI Breached</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{aiBreached}}</span></div> --}}
127
+ {{#if showFlags}}
128
+ <div class="cover-sum-row"><span class="cover-sum-label">Question flags</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{numFlags}}</span></div>
129
+ {{/if}}
130
+ {{#if showRunAIFlag}}
131
+ <div class="cover-sum-row"><span class="cover-sum-label">Run AI flags</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{runAIFlag}}</span></div>
132
+ {{/if}}
124
133
  <div class="cover-sum-row"><span class="cover-sum-label">Submitted By</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{submittedBy}}</span></div>
125
134
  <div class="cover-sum-row"><span class="cover-sum-label">Country</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{country}}</span></div>
126
135
  </div>
@@ -234,125 +243,28 @@
234
243
  {{/if}}
235
244
  {{/eq}}
236
245
 
237
- <table style="width:100%;margin-top:8px;table-layout:fixed"><tr>
238
- <td style="width:50%;vertical-align:top;padding-right:8px">
239
- {{#neq ../answerType 'image/video'}}
240
- {{#neq ../answerType 'multipleImage'}}
241
- {{#if this.multiReferenceImage.length}}
242
- <div class="q-answer-media">
243
- <div class="q-answer-caption">Reference Images</div>
244
- {{#each this.multiReferenceImage}}
245
- <img src="{{this}}" alt="Reference Image" />
246
- {{/each}}
247
- </div>
248
- {{else}}
249
- {{#if this.referenceImage}}
250
- <div class="q-answer-media">
251
- <div class="q-answer-caption">Reference Image</div>
252
- <img src="{{this.referenceImage}}" alt="Reference Image" />
253
- </div>
254
- {{else}}
255
- {{#eq this.answerType 'image'}}
256
- {{#if this.answer}}
257
- <div class="q-answer-media">
258
- <div class="q-answer-caption">Uploaded Image</div>
259
- <img src="{{this.answer}}" alt="Uploaded Image" />
260
- </div>
261
- {{/if}}
262
- {{/eq}}
263
- {{/if}}
264
- {{/if}}
265
- {{else}}
266
- {{#eq this.answerType 'image'}}
267
- {{#if this.answer}}
268
- <div class="q-answer-media">
269
- <div class="q-answer-caption">Uploaded Image</div>
270
- <img src="{{this.answer}}" alt="Uploaded Image" />
271
- </div>
272
- {{/if}}
273
- {{/eq}}
274
- {{/neq}}
275
- {{else}}
276
- {{#eq this.answerType 'image'}}
277
- {{#if this.answer}}
278
- <div class="q-answer-media">
279
- <div class="q-answer-caption">Uploaded Image</div>
280
- <img src="{{this.answer}}" alt="Uploaded Image" />
281
- </div>
282
- {{/if}}
283
- {{/eq}}
284
- {{/neq}}
285
- {{#unless this.hasReferenceImage}}
286
- {{#if this.validation}}
287
- {{#eq this.validationDisplayType 'image'}}
288
- {{#if this.validationAnswer}}
289
- <div class="q-answer-caption">Validation Image</div>
290
- <img src="{{this.validationAnswer}}" alt="Validation Image" />
291
- {{/if}}
292
- {{/eq}}
293
- {{#eq this.validationDisplayType 'multiImage'}}
294
- {{#if this.validationImage.length}}
295
- <div class="q-answer-caption">Validation Image</div>
296
- {{#each this.validationImage}}
297
- <img src="{{this}}" alt="Validation Image" />
298
- {{/each}}
299
- {{/if}}
300
- {{#if this.validationVideo.length}}
301
- <div class="q-answer-caption">Validation Video</div>
302
- {{#each this.validationVideo}}
303
- <a class="q-answer-link" href="{{this}}" target="_blank">{{this}}</a>
304
- {{/each}}
305
- {{/if}}
306
- {{/eq}}
307
- {{/if}}
308
- {{/unless}}
309
- </td>
310
- <td style="width:50%;vertical-align:top;padding-left:8px">
311
- {{#eq this.answerType 'image'}}
312
- {{#if this.answer}}
313
- {{#neq ../answerType 'image/video'}}
314
- {{#neq ../answerType 'multipleImage'}}
315
- {{#if this.multiReferenceImage.length}}
316
- <div class="q-answer-caption">Uploaded Image</div>
317
- <img src="{{this.answer}}" alt="Uploaded Image" />
318
- {{else}}
319
- {{#if this.referenceImage}}
320
- <div class="q-answer-caption">Uploaded Image</div>
321
- <img src="{{this.answer}}" alt="Uploaded Image" />
322
- {{/if}}
323
- {{/if}}
324
- {{/neq}}
325
- {{/neq}}
326
- {{/if}}
327
- {{/eq}}
328
- {{#if this.hasReferenceImage}}
329
- {{#if this.validation}}
330
- {{#eq this.validationDisplayType 'image'}}
331
- {{#if this.validationAnswer}}
332
- <div class="q-answer-caption">Validation Image</div>
333
- <img src="{{this.validationAnswer}}" alt="Validation Image" />
334
- {{/if}}
335
- {{/eq}}
336
- {{#eq this.validationDisplayType 'multiImage'}}
337
- {{#if this.validationImage.length}}
338
- <div class="q-answer-caption">Validation Image</div>
339
- {{#each this.validationImage}}
340
- <img src="{{this}}" alt="Validation Image" />
341
- {{/each}}
342
- {{/if}}
343
- {{#if this.validationVideo.length}}
344
- <div class="q-answer-caption">Validation Video</div>
345
- {{#each this.validationVideo}}
346
- <a class="q-answer-link" href="{{this}}" target="_blank">{{this}}</a>
347
- {{/each}}
348
- {{/if}}
349
- {{/eq}}
350
- {{/if}}
351
- {{/if}}
352
- </td>
353
- </tr></table>
246
+ {{#eq this.validationDisplayType 'multiImage'}}
247
+ {{#if this.validationVideo.length}}
248
+ <div class="q-answer-media">
249
+ <div class="q-answer-caption">Validation Video</div>
250
+ {{#each this.validationVideo}}
251
+ <a class="q-answer-link" href="{{this}}" target="_blank">{{this}}</a>
252
+ {{/each}}
253
+ </div>
254
+ {{/if}}
255
+ {{/eq}}
354
256
  </div>
355
257
  {{/each}}
258
+ {{#if this.mediaItems.length}}
259
+ <div class="answer-media-grid">
260
+ {{#each this.mediaItems}}
261
+ <div class="answer-media-cell">
262
+ <div class="q-answer-caption">{{this.label}}</div>
263
+ <img src="{{this.url}}" alt="{{this.label}}" />
264
+ </div>
265
+ {{/each}}
266
+ </div>
267
+ {{/if}}
356
268
  </div>
357
269
  {{/if}}
358
270
  {{#if this.remarks}}
@@ -369,8 +281,8 @@
369
281
  {{#if userImage}}
370
282
  <img class="user-verify-photo" src="{{userImage}}" alt="Submitted by photo" />
371
283
  {{/if}}
372
- {{#if submittedBy}}
373
- <div class="user-verify-name">{{submittedBy}}</div>
284
+ {{#if userSignature}}
285
+ <div class="user-verify-signature-text">{{userSignature}}</div>
374
286
  {{/if}}
375
287
  </div>
376
288
  {{/if}}
@@ -209,6 +209,59 @@ function buildQuestionAnswerEntries( question ) {
209
209
  }
210
210
 
211
211
 
212
+ /**
213
+ * Flattens a question's user answers into one ordered, deduped image list for the
214
+ * 2-per-row answer media grid. Order: reference images first, then uploaded images,
215
+ * then validation images. Videos are shown as links elsewhere, so only images are
216
+ * collected here. When a question has no reference image, the uploaded images simply
217
+ * fill the grid from the start (taking the reference slot).
218
+ * @param {Array} userAnswers entries produced by buildQuestionAnswerEntries
219
+ * @return {Array} list of { type, label, url } media items in render order
220
+ */
221
+ function buildQuestionMediaItems( userAnswers = [] ) {
222
+ const items = [];
223
+ const seen = new Set();
224
+
225
+ const push = ( type, label, url ) => {
226
+ if ( !url || typeof url !== 'string' ) return;
227
+ if ( seen.has( url ) ) return;
228
+ seen.add( url );
229
+ items.push( { type, label, url } );
230
+ };
231
+
232
+ // 1. Reference images first (deduped, so a single shared reference shows once).
233
+ userAnswers.forEach( ( ua ) => {
234
+ if ( Array.isArray( ua.multiReferenceImage ) && ua.multiReferenceImage.length ) {
235
+ ua.multiReferenceImage.forEach( ( url ) => push( 'reference', 'Reference Image', url ) );
236
+ } else if ( ua.referenceImage ) {
237
+ push( 'reference', 'Reference Image', ua.referenceImage );
238
+ }
239
+ } );
240
+
241
+ // 2. Uploaded images.
242
+ userAnswers.forEach( ( ua ) => {
243
+ if ( ua.answerType === 'image' && ua.answer ) {
244
+ push( 'uploaded', 'Uploaded Image', ua.answer );
245
+ }
246
+ } );
247
+
248
+ // 3. Validation images (single 'Capture Image' and multi 'Capture Multiple Image').
249
+ userAnswers.forEach( ( ua ) => {
250
+ if ( !ua.validation ) return;
251
+
252
+ if ( ua.validationDisplayType === 'image' && ua.validationAnswer ) {
253
+ push( 'validation', 'Validation Image', ua.validationAnswer );
254
+ }
255
+
256
+ if ( ua.validationDisplayType === 'multiImage' && Array.isArray( ua.validationImage ) ) {
257
+ ua.validationImage.forEach( ( url ) => push( 'validation', 'Validation Image', url ) );
258
+ }
259
+ } );
260
+
261
+ return items;
262
+ }
263
+
264
+
212
265
  /**
213
266
  * Extracts every 'Matched/Not Matched' value from a userAnswer's runAIData.
214
267
  * Handles both shapes:
@@ -257,6 +310,8 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
257
310
 
258
311
  const flags = [];
259
312
 
313
+ let hasSopFlag = false;
314
+
260
315
 
261
316
  ( questionAnswer || [] ).forEach( ( section, sectionIdx ) => {
262
317
  let sectionScore = 0;
@@ -278,6 +333,10 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
278
333
 
279
334
  const ua = userAnswersWithRef[0];
280
335
 
336
+ if ( !hasSopFlag && userAnswersWithRef.some( ( entry ) => entry.sopFlag === true ) ) {
337
+ hasSopFlag = true;
338
+ }
339
+
281
340
 
282
341
  const max = q.compliance ? Math.max( ...q?.answers.map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatchedCount ?? 0 ) ) ) : 0;
283
342
 
@@ -358,6 +417,8 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
358
417
 
359
418
  answerDisplay: isYes ? 'Yes' : ( isNo ? 'No' : ( answerText && answerText.startsWith( 'http' ) ? 'Image' : ( answerText || '—' ) ) ),
360
419
 
420
+ mediaItems: buildQuestionMediaItems( userAnswersWithRef ),
421
+
361
422
  userAnswer: userAnswersWithRef.length ? userAnswersWithRef : [ {
362
423
  answer: '',
363
424
  answerType: 'text',
@@ -415,7 +476,7 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
415
476
  const numQuestions = questionAnswer.reduce( ( sum, s ) => sum + ( s.questions?.length || 0 ), 0 );
416
477
 
417
478
 
418
- return { totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions };
479
+ return { totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions, hasSopFlag };
419
480
  }
420
481
 
421
482
 
@@ -437,7 +498,7 @@ export function buildVisitChecklistTemplateDataFromProcessed( processedDoc, bran
437
498
 
438
499
  const {
439
500
 
440
- totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions,
501
+ totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions, hasSopFlag,
441
502
 
442
503
  } = mapSectionsFromQuestionAnswer( questionAnswer );
443
504
 
@@ -551,7 +612,11 @@ export function buildVisitChecklistTemplateDataFromProcessed( processedDoc, bran
551
612
 
552
613
  numFlags: typeof doc.questionFlag === 'number' ? doc.questionFlag : flags.length,
553
614
 
554
- aiBreached: typeof doc.runAIFlag === 'number' ? doc.runAIFlag : 0,
615
+ showFlags: hasSopFlag,
616
+
617
+ runAIFlag: typeof doc.runAIFlag === 'number' ? doc.runAIFlag : 0,
618
+
619
+ showRunAIFlag: ( typeof doc.runAIQuestionCount === 'number' ? doc.runAIQuestionCount : 0 ) > 0,
555
620
 
556
621
  submittedBy: doc.userName || '--',
557
622
 
@@ -618,7 +683,7 @@ function buildFromViewChecklistApi( getchecklistData, viewchecklistData, brandIn
618
683
 
619
684
  const {
620
685
 
621
- totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions,
686
+ totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions, hasSopFlag,
622
687
 
623
688
  } = mapSectionsFromQuestionAnswer( questionAnswer );
624
689
 
@@ -704,7 +769,11 @@ function buildFromViewChecklistApi( getchecklistData, viewchecklistData, brandIn
704
769
 
705
770
  numFlags: checklistAnswer?.flagCount ?? flags.length,
706
771
 
707
- aiBreached: checklistAnswer?.aiBreachedCount ?? 0,
772
+ showFlags: hasSopFlag,
773
+
774
+ runAIFlag: checklistAnswer?.runAIFlag ?? checklistInfo?.runAIFlag ?? checklistAnswer?.aiBreachedCount ?? 0,
775
+
776
+ showRunAIFlag: ( checklistInfo?.runAIQuestionCount ?? checklistAnswer?.runAIQuestionCount ?? 0 ) > 0,
708
777
 
709
778
  submittedBy: checklistInfo?.submittedBy || storeProfile?.userName || '--',
710
779
 
@@ -847,11 +916,6 @@ export function createImageCache() {
847
916
  }
848
917
 
849
918
 
850
- if ( resolvedData.userSignature && resolvedData.userSignature.startsWith( 'http' ) ) {
851
- urls.add( resolvedData.userSignature );
852
- }
853
-
854
-
855
919
  const collectFromSection = ( section ) => {
856
920
  section.questions?.forEach( ( q ) => {
857
921
  if ( q.questionReferenceImage && q.questionReferenceImage.startsWith( 'http' ) ) urls.add( q.questionReferenceImage );
@@ -876,6 +940,10 @@ export function createImageCache() {
876
940
  } );
877
941
  }
878
942
  } );
943
+
944
+ q.mediaItems?.forEach( ( m ) => {
945
+ if ( typeof m.url === 'string' && m.url.startsWith( 'http' ) ) urls.add( m.url );
946
+ } );
879
947
  } );
880
948
  };
881
949
 
@@ -917,11 +985,6 @@ export function createImageCache() {
917
985
  }
918
986
 
919
987
 
920
- if ( resolvedData.userSignature ) {
921
- resolvedData.userSignature = cache.get( resolvedData.userSignature ) || resolvedData.userSignature;
922
- }
923
-
924
-
925
988
  const replaceInSection = ( section ) => {
926
989
  section.questions?.forEach( ( q ) => {
927
990
  if ( q.questionReferenceImage && cache.has( q.questionReferenceImage ) ) q.questionReferenceImage = cache.get( q.questionReferenceImage );
@@ -944,6 +1007,12 @@ export function createImageCache() {
944
1007
  ua.validationImage = ua.validationImage.map( ( u ) => ( cache.has( u ) ? cache.get( u ) : u ) );
945
1008
  }
946
1009
  } );
1010
+
1011
+ if ( q.mediaItems?.length ) {
1012
+ q.mediaItems.forEach( ( m ) => {
1013
+ if ( cache.has( m.url ) ) m.url = cache.get( m.url );
1014
+ } );
1015
+ }
947
1016
  } );
948
1017
  };
949
1018
 
@@ -1025,6 +1094,12 @@ export function resolveTemplateUrls( templateData, baseUrl = 'https://d1r0hc2ssk
1025
1094
  ua.answer = resolveUrl( ua.answer );
1026
1095
  }
1027
1096
  } );
1097
+
1098
+ if ( q.mediaItems?.length ) {
1099
+ q.mediaItems.forEach( ( m ) => {
1100
+ m.url = resolveUrl( m.url );
1101
+ } );
1102
+ }
1028
1103
  } );
1029
1104
  };
1030
1105
 
@@ -1046,10 +1121,6 @@ export function resolveTemplateUrls( templateData, baseUrl = 'https://d1r0hc2ssk
1046
1121
  resolvedData.userImage = resolveUrl( resolvedData.userImage );
1047
1122
  }
1048
1123
 
1049
- if ( resolvedData?.userSignature && !resolvedData.userSignature.startsWith( 'http' ) && !resolvedData.userSignature.startsWith( 'data:' ) ) {
1050
- resolvedData.userSignature = resolveUrl( resolvedData.userSignature );
1051
- }
1052
-
1053
1124
 
1054
1125
  return resolvedData;
1055
1126
  }
@@ -1169,6 +1240,12 @@ export async function generateVisitChecklistPDF( templateData, baseUrl = 'https:
1169
1240
  ua.answer = resolveUrl( ua.answer );
1170
1241
  }
1171
1242
  } );
1243
+
1244
+ if ( q.mediaItems?.length ) {
1245
+ q.mediaItems.forEach( ( m ) => {
1246
+ m.url = resolveUrl( m.url );
1247
+ } );
1248
+ }
1172
1249
  } );
1173
1250
  };
1174
1251
 
@@ -1186,10 +1263,6 @@ export async function generateVisitChecklistPDF( templateData, baseUrl = 'https:
1186
1263
  resolvedData.userImage = resolveUrl( resolvedData.userImage );
1187
1264
  }
1188
1265
 
1189
- if ( resolvedData.userSignature && !resolvedData.userSignature.startsWith( 'http' ) && !resolvedData.userSignature.startsWith( 'data:' ) ) {
1190
- resolvedData.userSignature = resolveUrl( resolvedData.userSignature );
1191
- }
1192
-
1193
1266
 
1194
1267
  const html = template( resolvedData );
1195
1268