tango-app-api-trax 3.9.38 → 3.9.39

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.
Files changed (55) hide show
  1. package/index.js +1 -2
  2. package/package.json +2 -2
  3. package/src/controllers/internalTrax.controller.js +99 -221
  4. package/src/controllers/mobileTrax.controller.js +29 -69
  5. package/src/controllers/teaxFlag.controller.js +953 -15
  6. package/src/controllers/trax.controller.js +2 -3
  7. package/src/controllers/traxDashboard.controllers.js +55 -69
  8. package/src/hbs/flag.hbs +1 -1
  9. package/src/hbs/login-otp.hbs +943 -943
  10. package/src/hbs/template.hbs +0 -7
  11. package/src/hbs/visit-checklist.hbs +91 -71
  12. package/src/routes/internalTraxApi.router.js +0 -1
  13. package/src/routes/trax.routes.js +3 -0
  14. package/src/routes/traxFlag.router.js +24 -0
  15. package/src/services/app.service.js +9 -15
  16. package/src/services/approver.service.js +15 -23
  17. package/src/services/authentication.service.js +3 -9
  18. package/src/services/camera.service.js +13 -19
  19. package/src/services/checklist.service.js +27 -35
  20. package/src/services/checklistAssign.service.js +38 -43
  21. package/src/services/checklistQuestion.service.js +34 -39
  22. package/src/services/checklistlog.service.js +34 -39
  23. package/src/services/clientRequest.service.js +2 -9
  24. package/src/services/clients.services.js +18 -23
  25. package/src/services/cluster.service.js +23 -31
  26. package/src/services/domain.service.js +18 -23
  27. package/src/services/download.services.js +25 -35
  28. package/src/services/group.service.js +17 -23
  29. package/src/services/lenskartEmployeeMapping.service.js +10 -15
  30. package/src/services/locus.service.js +28 -35
  31. package/src/services/notification.service.js +26 -35
  32. package/src/services/otp.service.js +13 -20
  33. package/src/services/planogram.service.js +2 -9
  34. package/src/services/processedTaskConfig.service.js +27 -35
  35. package/src/services/processedTaskList.service.js +26 -32
  36. package/src/services/processedchecklist.services.js +47 -55
  37. package/src/services/processedchecklistconfig.services.js +34 -39
  38. package/src/services/recurringFlagTracker.service.js +32 -39
  39. package/src/services/runAIFeatures.services.js +27 -32
  40. package/src/services/runAIRequest.services.js +38 -43
  41. package/src/services/store.service.js +27 -32
  42. package/src/services/tagging.service.js +2 -9
  43. package/src/services/taskConfig.service.js +27 -35
  44. package/src/services/teams.service.js +24 -35
  45. package/src/services/ticket.service.js +10 -15
  46. package/src/services/user.service.js +20 -27
  47. package/src/services/userAssignedstores.service.js +5 -12
  48. package/src/utils/visitChecklistPdf.utils.js +21 -449
  49. package/src/logging/activityLogFlusher.js +0 -59
  50. package/src/logging/activityLogMiddleware.js +0 -45
  51. package/src/logging/activityLogStore.js +0 -91
  52. package/src/logging/compressBatches.js +0 -83
  53. package/src/logging/config.js +0 -24
  54. package/src/logging/createLoggableService.js +0 -46
  55. package/src/logging/logExternalCall.js +0 -37
@@ -146,54 +146,6 @@ function flattenImageRefs( arr ) {
146
146
  .filter( Boolean );
147
147
  }
148
148
 
149
- /**
150
- * Builds the ordered media list (reference → uploaded → validation) for a SINGLE
151
- * answer entry, deduped within that entry. Used to render each answer's images
152
- * inline next to its own text/label, instead of pooling every answer's images
153
- * into one question-level grid (which decoupled labels from their images).
154
- * @param {Object} ua a single entry produced by buildQuestionAnswerEntries
155
- * @return {Array} list of { type, label, url, detectionStatus } for this entry, in render order
156
- */
157
- function buildEntryMediaItems( ua = {} ) {
158
- const items = [];
159
- const seen = new Set();
160
-
161
- const push = ( type, label, url, detectionStatus = '' ) => {
162
- if ( !url || typeof url !== 'string' ) return;
163
- if ( seen.has( url ) ) return;
164
- seen.add( url );
165
- items.push( { type, label, url, detectionStatus } );
166
- };
167
-
168
- // Each uploaded/validation image shows its OWN runAI verdict (per-image, never
169
- // aggregated). References are not AI-checked, so they carry no badge.
170
- const statusByUrl = buildAnswerImageStatus( ua );
171
- const statusFor = ( url ) => statusByUrl.get( url ) || '';
172
-
173
- if ( ua.hasReferenceImage ) {
174
- if ( Array.isArray( ua.multiReferenceImage ) && ua.multiReferenceImage.length ) {
175
- ua.multiReferenceImage.forEach( ( url ) => push( 'reference', 'Reference Image', url ) );
176
- } else if ( ua.referenceImage ) {
177
- push( 'reference', 'Reference Image', ua.referenceImage );
178
- }
179
- }
180
-
181
- if ( ua.answerType === 'image' && ua.answer ) {
182
- push( 'uploaded', 'Uploaded Image', ua.answer, statusFor( ua.answer ) );
183
- }
184
-
185
- if ( ua.validation ) {
186
- if ( ua.validationDisplayType === 'image' && ua.validationAnswer ) {
187
- push( 'validation', 'Validation Image', ua.validationAnswer, statusFor( ua.validationAnswer ) );
188
- }
189
- if ( ua.validationDisplayType === 'multiImage' && Array.isArray( ua.validationImage ) ) {
190
- ua.validationImage.forEach( ( url ) => push( 'validation', 'Validation Image', url, statusFor( url ) ) );
191
- }
192
- }
193
-
194
- return items;
195
- }
196
-
197
149
  function buildQuestionAnswerEntries( question ) {
198
150
  const rawUserAnswers = getSourceUserAnswers( question );
199
151
  const uniqueUserAnswers = [];
@@ -228,278 +180,21 @@ function buildQuestionAnswerEntries( question ) {
228
180
  }
229
181
  }
230
182
  const validationImage = flattenImageRefs( userAnswer?.validationImage );
231
- // Videos captured alongside 'Capture Multiple Image with description' validation (no runAI; shown as links).
232
- const validationVideo = flattenImageRefs( userAnswer?.validationVideo );
233
-
234
- const referenceImage = userAnswer?.referenceImage || matchedAnswer?.referenceImage || '';
235
- // Left column shows a reference image only for non-image/video, non-multipleImage answer types.
236
- // When there is no reference image, uploaded/validation media is rendered on the left instead of the right.
237
- const hasReferenceImage = question?.answerType !== 'image/video' &&
238
- question?.answerType !== 'multipleImage' &&
239
- Boolean( referenceImage || multiReferenceImage.length );
240
-
241
- // runAI verdicts for this answer, in source order. Used to tag each uploaded /
242
- // validation image with its OWN verdict (by answerImage filename, else by
243
- // position) so multi-image answers don't all collapse to one aggregated status.
244
- const isPerAnswerType = PER_ANSWER_DETECTION_TYPES.includes( question?.answerType );
245
- const runAIValues = getRunAIMatchValues( userAnswer?.runAIData );
246
- const imageStatus = buildImageStatusMap( userAnswer?.runAIData );
247
-
248
- // Choice answers (yes/no, multiple choice, dropdown) take this answer's own
249
- // verdict directly — never aggregated. Other types keep the aggregate only as
250
- // a last-resort fallback.
251
- const detectionStatus = isPerAnswerType ?
252
- verdictFromValue( runAIValues[0] ) :
253
- getDetectionStatus( userAnswer?.runAIData );
254
-
255
- const entry = {
183
+
184
+ return {
256
185
  answer: userAnswer?.answer || '',
257
186
  answerType: getMediaDisplayType( question?.answerType, userAnswer ),
258
- referenceImage,
259
- hasReferenceImage,
187
+ referenceImage: userAnswer?.referenceImage || matchedAnswer?.referenceImage || '',
260
188
  multiReferenceImage,
261
189
  remarks: userAnswer?.remarks || '',
262
190
  sopFlag: userAnswer?.sopFlag ?? matchedAnswer?.sopFlag ?? false,
263
- detectionStatus,
264
- imageStatus,
265
- runAIValues,
266
191
  validation,
267
192
  validationType,
268
193
  validationAnswer,
269
194
  validationImage,
270
- validationVideo,
271
195
  validationDisplayType: getValidationDisplayType( validationType ),
272
196
  };
273
-
274
- // Text-label answers (e.g. "Attach the image of Cat") carry their own
275
- // reference/validation images. Group those images with this entry so the
276
- // label and its images render together, instead of pooling every answer's
277
- // images into one question-level grid. Image/video answers keep flowing
278
- // into the pooled grid (handled by buildQuestionMediaItems).
279
- entry.answerMedia = entry.answerType === 'text' ? buildEntryMediaItems( entry ) : [];
280
-
281
- // Choice answers with no media show their verdict next to the answer text;
282
- // when they carry validation media the badge sits on that media instead.
283
- entry.showDetectionStatus = isPerAnswerType && Boolean( detectionStatus ) && !entry.answerMedia.length;
284
-
285
- return entry;
286
- } );
287
- }
288
-
289
-
290
- /**
291
- * Flattens a question's user answers into one ordered, deduped image list for the
292
- * 2-per-row answer media grid. Order: reference images first, then uploaded images,
293
- * then validation images. Videos are shown as links elsewhere, so only images are
294
- * collected here. When a question has no reference image, the uploaded images simply
295
- * fill the grid from the start (taking the reference slot).
296
- * @param {Array} userAnswers entries produced by buildQuestionAnswerEntries
297
- * @param {string} [forcedStatus] when set (image / image-video), every uploaded &
298
- * validation image shows this aggregated verdict instead of its own per-image one
299
- * @return {Array} list of { type, label, url, detectionStatus } media items in render order
300
- */
301
- function buildQuestionMediaItems( userAnswers = [], forcedStatus = '' ) {
302
- const items = [];
303
- const seen = new Set();
304
-
305
- const push = ( type, label, url, detectionStatus = '' ) => {
306
- if ( !url || typeof url !== 'string' ) return;
307
- if ( seen.has( url ) ) return;
308
- seen.add( url );
309
- items.push( { type, label, url, detectionStatus } );
310
- };
311
-
312
- // Entries that render their media inline with the answer (answerMedia) are
313
- // excluded here so their images are not duplicated in the pooled grid.
314
- const pool = userAnswers.filter( ( ua ) => !( ua.answerMedia && ua.answerMedia.length ) );
315
-
316
- // Resolve each image's status per answer (per-image; forcedStatus aggregates
317
- // image / image-video). Built per answer so positional matching stays aligned.
318
- const statusByUrl = new Map();
319
- pool.forEach( ( ua ) => {
320
- buildAnswerImageStatus( ua, forcedStatus ).forEach( ( status, url ) => statusByUrl.set( url, status ) );
321
- } );
322
- const statusFor = ( url ) => statusByUrl.get( url ) || '';
323
-
324
- // 1. Reference images first (deduped, so a single shared reference shows once).
325
- // Skipped for image/video & multipleImage upload questions (hasReferenceImage
326
- // is false), which have no comparison reference — only uploaded answers.
327
- pool.forEach( ( ua ) => {
328
- if ( !ua.hasReferenceImage ) return;
329
- if ( Array.isArray( ua.multiReferenceImage ) && ua.multiReferenceImage.length ) {
330
- ua.multiReferenceImage.forEach( ( url ) => push( 'reference', 'Reference Image', url ) );
331
- } else if ( ua.referenceImage ) {
332
- push( 'reference', 'Reference Image', ua.referenceImage );
333
- }
334
- } );
335
-
336
- // 2. Uploaded images — tagged with each image's own detection status (from runAIData).
337
- pool.forEach( ( ua ) => {
338
- if ( ua.answerType === 'image' && ua.answer ) {
339
- push( 'uploaded', 'Uploaded Image', ua.answer, statusFor( ua.answer ) );
340
- }
341
- } );
342
-
343
- // 3. Validation images (single 'Capture Image' and multi 'Capture Multiple Image').
344
- pool.forEach( ( ua ) => {
345
- if ( !ua.validation ) return;
346
-
347
- if ( ua.validationDisplayType === 'image' && ua.validationAnswer ) {
348
- push( 'validation', 'Validation Image', ua.validationAnswer, statusFor( ua.validationAnswer ) );
349
- }
350
-
351
- if ( ua.validationDisplayType === 'multiImage' && Array.isArray( ua.validationImage ) ) {
352
- ua.validationImage.forEach( ( url ) => push( 'validation', 'Validation Image', url, statusFor( url ) ) );
353
- }
354
- } );
355
-
356
- return items;
357
- }
358
-
359
-
360
- /**
361
- * Extracts every 'Matched/Not Matched' value from a userAnswer's runAIData.
362
- * Handles both shapes:
363
- * - [ { answerImage, results: [ { featureName, value } ] } ] (per-image nested)
364
- * - [ { featureName, value } ] (flat)
365
- * @param {Array} runAIData
366
- * @return {string[]} list of 'True' / 'False' values (in source order)
367
- */
368
- function getRunAIMatchValues( runAIData ) {
369
- if ( !Array.isArray( runAIData ) ) return [];
370
-
371
- const values = [];
372
-
373
- runAIData.forEach( ( item ) => {
374
- if ( Array.isArray( item?.results ) ) {
375
- item.results.forEach( ( res ) => {
376
- if ( res?.featureName === 'Matched/Not Matched' ) values.push( res.value );
377
- } );
378
- } else if ( item?.featureName === 'Matched/Not Matched' ) {
379
- values.push( item.value );
380
- }
381
- } );
382
-
383
- return values;
384
- }
385
-
386
-
387
- // Answer types whose runAI verdict is aggregated across all uploaded images:
388
- // if any image is not matched, the whole question reads 'Not Matched'. Every
389
- // other type shows each image's own verdict.
390
- const AGGREGATE_DETECTION_TYPES = [ 'image', 'image/video' ];
391
-
392
- // Choice answers: each selected answer carries its OWN runAI verdict and shows it
393
- // directly — never aggregated ('.every') across the question's other answers.
394
- const PER_ANSWER_DETECTION_TYPES = [ 'yes/no', 'multiplechoicesingle', 'multiplechoicemultiple', 'dropdown' ];
395
-
396
-
397
- /**
398
- * Maps a single runAI value to a display status.
399
- * @param {string|boolean} value
400
- * @return {string} 'Matched' | 'Not Matched' | ''
401
- */
402
- function verdictFromValue( value ) {
403
- if ( value === 'True' || value === true ) return 'Matched';
404
- if ( value === 'False' || value === false ) return 'Not Matched';
405
- return '';
406
- }
407
-
408
-
409
- /**
410
- * The filename of an image URL/path, ignoring any query string. Used to match an
411
- * uploaded/validation image against its runAIData entry (whose answerImage may
412
- * carry a different prefix / signed-URL query), regardless of CDN resolution.
413
- * @param {string} url
414
- * @return {string}
415
- */
416
- function imageBasename( url ) {
417
- return String( url || '' ).split( '?' )[0].split( '/' ).pop();
418
- }
419
-
420
-
421
- /**
422
- * Builds a per-image detection status map from the nested runAIData shape
423
- * ([ { answerImage, results: [ { featureName, value } ] } ]), keyed by the
424
- * answerImage's filename. The flat shape has no per-image key, so it yields an
425
- * empty map and callers fall back to the answer-level status.
426
- * @param {Array} runAIData
427
- * @return {Object} { [filename]: 'Matched' | 'Not Matched' }
428
- */
429
- function buildImageStatusMap( runAIData ) {
430
- const map = {};
431
- if ( !Array.isArray( runAIData ) ) return map;
432
-
433
- runAIData.forEach( ( item ) => {
434
- if ( !item || !item.answerImage ) return;
435
- const results = Array.isArray( item.results ) ? item.results : [ item ];
436
- const values = results
437
- .filter( ( r ) => r?.featureName === 'Matched/Not Matched' )
438
- .map( ( r ) => r.value );
439
- if ( !values.length ) return;
440
- const matched = values.every( ( v ) => v === 'True' || v === true );
441
- map[imageBasename( item.answerImage )] = matched ? 'Matched' : 'Not Matched';
442
197
  } );
443
-
444
- return map;
445
- }
446
-
447
-
448
- /**
449
- * Resolves the detection status for each checked image (uploaded + validation) of a
450
- * single answer, returned as a Map of url → 'Matched' | 'Not Matched' | ''.
451
- *
452
- * Each image gets its OWN verdict — never aggregated across the answer's other images:
453
- * 1. matched explicitly by its answerImage filename (nested runAIData), else
454
- * 2. matched positionally — the Nth checked image takes the Nth runAI verdict
455
- * (works for the flat runAIData shape and when filenames don't line up).
456
- * For image / image-video, forcedStatus (the question-level aggregate) overrides all.
457
- * @param {Object} ua a single entry produced by buildQuestionAnswerEntries
458
- * @param {string} [forcedStatus] aggregated status applied to every image (image/image-video)
459
- * @return {Map<string,string>} url → status
460
- */
461
- function buildAnswerImageStatus( ua = {}, forcedStatus = '' ) {
462
- const statusByUrl = new Map();
463
- const byName = ua.imageStatus || {};
464
- const values = Array.isArray( ua.runAIValues ) ? ua.runAIValues : [];
465
- let idx = 0;
466
-
467
- const assign = ( url ) => {
468
- if ( !url || typeof url !== 'string' || statusByUrl.has( url ) ) return;
469
- let status = forcedStatus;
470
- if ( !status ) {
471
- const key = imageBasename( url );
472
- status = ( key in byName ) ? byName[key] : verdictFromValue( values[idx] );
473
- idx++;
474
- }
475
- statusByUrl.set( url, status );
476
- };
477
-
478
- if ( ua.answerType === 'image' && ua.answer ) assign( ua.answer );
479
-
480
- if ( ua.validation ) {
481
- if ( ua.validationDisplayType === 'image' && ua.validationAnswer ) assign( ua.validationAnswer );
482
- if ( ua.validationDisplayType === 'multiImage' && Array.isArray( ua.validationImage ) ) {
483
- ua.validationImage.forEach( assign );
484
- }
485
- }
486
-
487
- return statusByUrl;
488
- }
489
-
490
-
491
- /**
492
- * Resolves a 'Matched' / 'Not Matched' detection status from runAIData. Handles both
493
- * runAIData shapes (per-image nested results, and flat). Any non-match verdict makes
494
- * the whole status 'Not Matched'. Returns '' when there is no runAI verdict to report.
495
- * @param {Array} runAIData
496
- * @return {string} 'Matched' | 'Not Matched' | ''
497
- */
498
- function getDetectionStatus( runAIData ) {
499
- const values = getRunAIMatchValues( runAIData );
500
- if ( !values.length ) return '';
501
- const allMatched = values.every( ( v ) => v === 'True' || v === true );
502
- return allMatched ? 'Matched' : 'Not Matched';
503
198
  }
504
199
 
505
200
 
@@ -524,8 +219,6 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
524
219
 
525
220
  const flags = [];
526
221
 
527
- let hasSopFlag = false;
528
-
529
222
 
530
223
  ( questionAnswer || [] ).forEach( ( section, sectionIdx ) => {
531
224
  let sectionScore = 0;
@@ -547,10 +240,6 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
547
240
 
548
241
  const ua = userAnswersWithRef[0];
549
242
 
550
- if ( !hasSopFlag && userAnswersWithRef.some( ( entry ) => entry.sopFlag === true ) ) {
551
- hasSopFlag = true;
552
- }
553
-
554
243
 
555
244
  const max = q.compliance ? Math.max( ...q?.answers.map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatchedCount ?? 0 ) ) ) : 0;
556
245
 
@@ -558,34 +247,14 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
558
247
  let score = q.compliance ? Math.max( ...q?.userAnswer?.map( ( o ) => o?.complianceScore ?? 0 ) ) : 0;
559
248
 
560
249
 
561
- if ( q.compliance && ( q.answerType === 'image' || q.answerType === 'image/video' ) ) {
562
- // image/video questions mix image + video entries in userAnswer, so only score the images.
563
- const imageAnswers = q.answerType === 'image/video' ?
564
- ( q.userAnswer || [] ).filter( ( ua ) => ua?.answerType === 'image' ) :
565
- ( q.userAnswer || [] );
566
-
567
- const matchValues = imageAnswers.flatMap( ( ua ) => getRunAIMatchValues( ua?.runAIData ) );
568
-
569
- if ( matchValues.length ) {
570
- // All images must be matched ('True'); if any one is not, treat as not matched.
571
- const allMatched = matchValues.every( ( value ) => value === 'True' );
572
- score = allMatched ? ( q?.answers?.[0]?.matchedCount ?? 0 ) : ( q?.answers?.[0]?.notMatchedCount ?? 0 );
573
- }
574
- }
575
-
576
-
577
- // image / image-video aggregate their runAI verdict across all uploaded images
578
- // (any 'Not Matched' → whole question 'Not Matched'). Every other type keeps
579
- // each image's own verdict, so this stays '' for them.
580
- let aggregateDetectionStatus = '';
581
- if ( AGGREGATE_DETECTION_TYPES.includes( q.answerType ) ) {
582
- const aiAnswers = q.answerType === 'image/video' ?
583
- ( q.userAnswer || [] ).filter( ( a ) => a?.answerType === 'image' ) :
584
- ( q.userAnswer || [] );
585
- const matchValues = aiAnswers.flatMap( ( a ) => getRunAIMatchValues( a?.runAIData ) );
586
- if ( matchValues.length ) {
587
- aggregateDetectionStatus = matchValues.every( ( v ) => v === 'True' || v === true ) ?
588
- 'Matched' : 'Not Matched';
250
+ if ( q.answerType == 'image' && q.compliance && q.userAnswer?.[0]?.runAIData ) {
251
+ let find = q.userAnswer?.[0]?.runAIData?.find( ( run ) => run?.featureName == 'Matched/Not Matched' );
252
+ if ( find ) {
253
+ if ( find?.value == 'True' ) {
254
+ score = q?.answers?.[0]?.matchedCount;
255
+ } else {
256
+ score = q?.answers?.[0]?.notMatchedCount;
257
+ }
589
258
  }
590
259
  }
591
260
 
@@ -647,8 +316,6 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
647
316
 
648
317
  answerDisplay: isYes ? 'Yes' : ( isNo ? 'No' : ( answerText && answerText.startsWith( 'http' ) ? 'Image' : ( answerText || '—' ) ) ),
649
318
 
650
- mediaItems: buildQuestionMediaItems( userAnswersWithRef, aggregateDetectionStatus ),
651
-
652
319
  userAnswer: userAnswersWithRef.length ? userAnswersWithRef : [ {
653
320
  answer: '',
654
321
  answerType: 'text',
@@ -672,15 +339,15 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
672
339
 
673
340
  sectionName: section.sectionName || `Section ${sectionIdx + 1}`,
674
341
 
675
- targetScore: sectionMax ? sectionMax : '--',
342
+ targetScore: sectionMax,
676
343
 
677
- actualScore: sectionMax ? sectionScore : '--',
344
+ actualScore: sectionScore,
678
345
 
679
346
  questionsCount,
680
347
 
681
348
  passedCount,
682
349
 
683
- percentage: sectionMax > 0 ? Math.round( ( sectionScore / sectionMax ) * 100 ) : '--',
350
+ percentage: sectionMax > 0 ? Math.round( ( sectionScore / sectionMax ) * 100 ) : 0,
684
351
 
685
352
  } );
686
353
 
@@ -706,7 +373,7 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
706
373
  const numQuestions = questionAnswer.reduce( ( sum, s ) => sum + ( s.questions?.length || 0 ), 0 );
707
374
 
708
375
 
709
- return { totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions, hasSopFlag };
376
+ return { totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions };
710
377
  }
711
378
 
712
379
 
@@ -728,7 +395,7 @@ export function buildVisitChecklistTemplateDataFromProcessed( processedDoc, bran
728
395
 
729
396
  const {
730
397
 
731
- totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions, hasSopFlag,
398
+ totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions,
732
399
 
733
400
  } = mapSectionsFromQuestionAnswer( questionAnswer );
734
401
 
@@ -814,9 +481,6 @@ export function buildVisitChecklistTemplateDataFromProcessed( processedDoc, bran
814
481
 
815
482
  let referenceId = doc.coverage == 'store' ? doc?.storeName : doc?.userName;
816
483
 
817
- const userImage = doc.userImage || '';
818
- const userSignature = doc.userSignature || '';
819
-
820
484
  return {
821
485
 
822
486
 
@@ -842,22 +506,12 @@ export function buildVisitChecklistTemplateDataFromProcessed( processedDoc, bran
842
506
 
843
507
  numFlags: typeof doc.questionFlag === 'number' ? doc.questionFlag : flags.length,
844
508
 
845
- showFlags: hasSopFlag,
846
-
847
- runAIFlag: typeof doc.runAIFlag === 'number' ? doc.runAIFlag : 0,
848
-
849
- showRunAIFlag: ( typeof doc.runAIQuestionCount === 'number' ? doc.runAIQuestionCount : 0 ) > 0,
509
+ aiBreached: typeof doc.runAIFlag === 'number' ? doc.runAIFlag : 0,
850
510
 
851
511
  submittedBy: doc.userName || '--',
852
512
 
853
513
  country: doc.country || '--',
854
514
 
855
- userImage,
856
-
857
- userSignature,
858
-
859
- showUserVerification: Boolean( userImage || userSignature ),
860
-
861
515
  hasCompliancePage,
862
516
 
863
517
  detailPageStart,
@@ -913,7 +567,7 @@ function buildFromViewChecklistApi( getchecklistData, viewchecklistData, brandIn
913
567
 
914
568
  const {
915
569
 
916
- totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions, hasSopFlag,
570
+ totalScore, maxScore, sectionInsights, questionAnswers, flags, numQuestions,
917
571
 
918
572
  } = mapSectionsFromQuestionAnswer( questionAnswer );
919
573
 
@@ -968,9 +622,6 @@ function buildFromViewChecklistApi( getchecklistData, viewchecklistData, brandIn
968
622
  } );
969
623
  }
970
624
 
971
- const userImage = checklistAnswer?.userImage || checklistInfo?.userImage || '';
972
- const userSignature = checklistAnswer?.userSignature || checklistInfo?.userSignature || '';
973
-
974
625
  return {
975
626
 
976
627
  brandLogo: brandInfo.brandLogo || '',
@@ -999,22 +650,12 @@ function buildFromViewChecklistApi( getchecklistData, viewchecklistData, brandIn
999
650
 
1000
651
  numFlags: checklistAnswer?.flagCount ?? flags.length,
1001
652
 
1002
- showFlags: hasSopFlag,
1003
-
1004
- runAIFlag: checklistAnswer?.runAIFlag ?? checklistInfo?.runAIFlag ?? checklistAnswer?.aiBreachedCount ?? 0,
1005
-
1006
- showRunAIFlag: ( checklistInfo?.runAIQuestionCount ?? checklistAnswer?.runAIQuestionCount ?? 0 ) > 0,
653
+ aiBreached: checklistAnswer?.aiBreachedCount ?? 0,
1007
654
 
1008
655
  submittedBy: checklistInfo?.submittedBy || storeProfile?.userName || '--',
1009
656
 
1010
657
  country: storeProfile?.Country || '--',
1011
658
 
1012
- userImage,
1013
-
1014
- userSignature,
1015
-
1016
- showUserVerification: Boolean( userImage || userSignature ),
1017
-
1018
659
  hasCompliancePage,
1019
660
 
1020
661
  detailPageStart,
@@ -1141,11 +782,6 @@ export function createImageCache() {
1141
782
  }
1142
783
 
1143
784
 
1144
- if ( resolvedData.userImage && resolvedData.userImage.startsWith( 'http' ) ) {
1145
- urls.add( resolvedData.userImage );
1146
- }
1147
-
1148
-
1149
785
  const collectFromSection = ( section ) => {
1150
786
  section.questions?.forEach( ( q ) => {
1151
787
  if ( q.questionReferenceImage && q.questionReferenceImage.startsWith( 'http' ) ) urls.add( q.questionReferenceImage );
@@ -1169,14 +805,6 @@ export function createImageCache() {
1169
805
  if ( typeof u === 'string' && u.startsWith( 'http' ) ) urls.add( u );
1170
806
  } );
1171
807
  }
1172
-
1173
- ua.answerMedia?.forEach( ( m ) => {
1174
- if ( typeof m.url === 'string' && m.url.startsWith( 'http' ) ) urls.add( m.url );
1175
- } );
1176
- } );
1177
-
1178
- q.mediaItems?.forEach( ( m ) => {
1179
- if ( typeof m.url === 'string' && m.url.startsWith( 'http' ) ) urls.add( m.url );
1180
808
  } );
1181
809
  } );
1182
810
  };
@@ -1214,11 +842,6 @@ export function createImageCache() {
1214
842
  }
1215
843
 
1216
844
 
1217
- if ( resolvedData.userImage ) {
1218
- resolvedData.userImage = cache.get( resolvedData.userImage ) || resolvedData.userImage;
1219
- }
1220
-
1221
-
1222
845
  const replaceInSection = ( section ) => {
1223
846
  section.questions?.forEach( ( q ) => {
1224
847
  if ( q.questionReferenceImage && cache.has( q.questionReferenceImage ) ) q.questionReferenceImage = cache.get( q.questionReferenceImage );
@@ -1240,19 +863,7 @@ export function createImageCache() {
1240
863
  if ( ua.validationDisplayType === 'multiImage' && ua.validationImage?.length ) {
1241
864
  ua.validationImage = ua.validationImage.map( ( u ) => ( cache.has( u ) ? cache.get( u ) : u ) );
1242
865
  }
1243
-
1244
- if ( ua.answerMedia?.length ) {
1245
- ua.answerMedia.forEach( ( m ) => {
1246
- if ( cache.has( m.url ) ) m.url = cache.get( m.url );
1247
- } );
1248
- }
1249
866
  } );
1250
-
1251
- if ( q.mediaItems?.length ) {
1252
- q.mediaItems.forEach( ( m ) => {
1253
- if ( cache.has( m.url ) ) m.url = cache.get( m.url );
1254
- } );
1255
- }
1256
867
  } );
1257
868
  };
1258
869
 
@@ -1325,27 +936,11 @@ export function resolveTemplateUrls( templateData, baseUrl = 'https://d1r0hc2ssk
1325
936
  ua.validationImage = ua.validationImage.map( ( ele ) => resolveUrl( ele ) );
1326
937
  }
1327
938
 
1328
- if ( ua?.validationVideo?.length ) {
1329
- ua.validationVideo = ua.validationVideo.map( ( ele ) => resolveUrl( ele ) );
1330
- }
1331
-
1332
939
 
1333
940
  if ( ua.answer && ua.answerType !== 'text' ) {
1334
941
  ua.answer = resolveUrl( ua.answer );
1335
942
  }
1336
-
1337
- if ( ua.answerMedia?.length ) {
1338
- ua.answerMedia.forEach( ( m ) => {
1339
- m.url = resolveUrl( m.url );
1340
- } );
1341
- }
1342
943
  } );
1343
-
1344
- if ( q.mediaItems?.length ) {
1345
- q.mediaItems.forEach( ( m ) => {
1346
- m.url = resolveUrl( m.url );
1347
- } );
1348
- }
1349
944
  } );
1350
945
  };
1351
946
 
@@ -1359,12 +954,9 @@ export function resolveTemplateUrls( templateData, baseUrl = 'https://d1r0hc2ssk
1359
954
 
1360
955
  resolvedData.sections?.forEach( resolveQuestionMedia );
1361
956
 
1362
- if ( resolvedData?.brandLogo && !resolvedData?.brandLogo?.startsWith( 'http' ) ) {
1363
- resolvedData.brandLogo = resolveUrl( resolvedData.brandLogo );
1364
- }
1365
957
 
1366
- if ( resolvedData?.userImage && !resolvedData.userImage.startsWith( 'http' ) && !resolvedData.userImage.startsWith( 'data:' ) ) {
1367
- resolvedData.userImage = resolveUrl( resolvedData.userImage );
958
+ if ( resolvedData.brandLogo && !resolvedData.brandLogo.startsWith( 'http' ) ) {
959
+ resolvedData.brandLogo = resolveUrl( resolvedData.brandLogo );
1368
960
  }
1369
961
 
1370
962
 
@@ -1478,26 +1070,10 @@ export async function generateVisitChecklistPDF( templateData, baseUrl = 'https:
1478
1070
  ua.validationImage = ua.validationImage.map( ( ele ) => resolveUrl( ele ) );
1479
1071
  }
1480
1072
 
1481
- if ( ua.validationVideo?.length ) {
1482
- ua.validationVideo = ua.validationVideo.map( ( ele ) => resolveUrl( ele ) );
1483
- }
1484
-
1485
1073
  if ( ua.answer && ua.answerType !== 'text' ) {
1486
1074
  ua.answer = resolveUrl( ua.answer );
1487
1075
  }
1488
-
1489
- if ( ua.answerMedia?.length ) {
1490
- ua.answerMedia.forEach( ( m ) => {
1491
- m.url = resolveUrl( m.url );
1492
- } );
1493
- }
1494
1076
  } );
1495
-
1496
- if ( q.mediaItems?.length ) {
1497
- q.mediaItems.forEach( ( m ) => {
1498
- m.url = resolveUrl( m.url );
1499
- } );
1500
- }
1501
1077
  } );
1502
1078
  };
1503
1079
 
@@ -1511,10 +1087,6 @@ export async function generateVisitChecklistPDF( templateData, baseUrl = 'https:
1511
1087
  resolvedData.brandLogo = resolveUrl( resolvedData.brandLogo );
1512
1088
  }
1513
1089
 
1514
- if ( resolvedData.userImage && !resolvedData.userImage.startsWith( 'http' ) && !resolvedData.userImage.startsWith( 'data:' ) ) {
1515
- resolvedData.userImage = resolveUrl( resolvedData.userImage );
1516
- }
1517
-
1518
1090
 
1519
1091
  const html = template( resolvedData );
1520
1092