tango-app-api-trax 3.7.89 → 3.7.91

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.7.89",
3
+ "version": "3.7.91",
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 } 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';
@@ -3489,214 +3489,230 @@ export const downloadInsertPdf = async ( req, res ) => {
3489
3489
  const safeName = ( str ) =>
3490
3490
  ( str || '' ).toString().replace( /[<>:"/\\|?*]+/g, '_' );
3491
3491
  for ( const checklistInfo of checklistInfoList ) {
3492
- console.log( dayjs.utc().diff( dayjs.utc( checklistInfo.scheduleEndTime, 'hh:mm A' ), 'minute' ) );
3493
- if ( dayjs.utc().diff( dayjs.utc( checklistInfo.scheduleEndTime, 'hh:mm A' ), 'minute' ) == 30 ) {
3494
- let data = {
3495
- checklistName: checklistInfo.checkListName,
3496
- date: todayStr,
3497
- total: checklistInfo?.storeCount,
3498
- };
3499
- const emailFileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/autoEmail.hbs', 'utf8' );
3500
- const emailHtmlContent = handlebars.compile( emailFileContent );
3501
- const emailHtml = emailHtmlContent( { data: data } );
3502
- let emailList = [];
3503
-
3504
- if ( checklistInfo?.autoEmail?.type?.includes( 'approver' ) ) {
3505
- emailList = ( checklistInfo.approver || [] )
3506
- .map( ( e ) => e?.value )
3507
- .filter( Boolean );
3508
- }
3509
-
3510
- if ( Array.isArray( checklistInfo?.autoEmail?.users ) ) {
3511
- emailList.push(
3512
- ...checklistInfo.autoEmail.users
3513
- .map( ( e ) => e?.value )
3514
- .filter( Boolean ),
3515
- );
3516
- }
3517
-
3518
- if ( !emailList.length ) continue;
3492
+ // console.log( dayjs.utc().diff( dayjs.utc( checklistInfo.scheduleEndTime, 'hh:mm A' ), 'minute' ) );
3493
+ // if ( dayjs.utc().diff( dayjs.utc( checklistInfo.scheduleEndTime, 'hh:mm A' ), 'minute' ) == 30 ) {
3494
+ let emailList = [];
3495
+
3496
+ if ( checklistInfo?.autoEmail?.type?.includes( 'approver' ) ) {
3497
+ emailList = ( checklistInfo.approver || [] )
3498
+ .map( ( e ) => e?.value )
3499
+ .filter( Boolean );
3500
+ }
3519
3501
 
3520
- const brandInfo = await getBrandInfo( checklistInfo.client_id );
3502
+ if ( Array.isArray( checklistInfo?.autoEmail?.users ) ) {
3503
+ emailList.push(
3504
+ ...checklistInfo.autoEmail.users
3505
+ .map( ( e ) => e?.value )
3506
+ .filter( Boolean ),
3507
+ );
3508
+ }
3521
3509
 
3522
- const submittedDetails = await processedchecklist.find( {
3523
- sourceCheckList_id: checklistInfo._id,
3524
- checklistStatus: 'submit',
3525
- date_string: todayStr,
3526
- } );
3510
+ if ( !emailList.length ) continue;
3527
3511
 
3528
- if ( !submittedDetails.length ) continue;
3512
+ const brandInfo = await getBrandInfo( checklistInfo.client_id );
3529
3513
 
3530
- // console.log(
3531
- // `Processing checklist: ${checklistInfo.checkListName} (${submittedDetails.length} records)`,
3532
- // );
3514
+ const submittedDetails = await processedchecklist.find( {
3515
+ sourceCheckList_id: checklistInfo._id,
3516
+ checklistStatus: 'submit',
3517
+ date_string: todayStr,
3518
+ } );
3533
3519
 
3534
- const ZIP_LIMIT = 100;
3520
+ if ( !submittedDetails.length ) continue;
3535
3521
 
3536
- for ( let i = 0; i < submittedDetails.length; i += ZIP_LIMIT ) {
3537
- const batch = submittedDetails.slice( i, i + ZIP_LIMIT );
3522
+ // console.log(
3523
+ // `Processing checklist: ${checklistInfo.checkListName} (${submittedDetails.length} records)`,
3524
+ // );
3538
3525
 
3539
- const zipName = `${safeName(
3540
- checklistInfo.checkListName,
3541
- )}_${todayStr}_part_${Math.floor( i / ZIP_LIMIT ) + 1}.zip`;
3526
+ const ZIP_LIMIT = 100;
3527
+ let appendedCount = 0;
3542
3528
 
3543
- const zipPath = path.join( '/tmp', zipName );
3529
+ const zipName = `${safeName(
3530
+ checklistInfo.checkListName,
3531
+ )}_${todayStr}.zip`;
3544
3532
 
3545
- const output = fs.createWriteStream( zipPath );
3546
- const archive = archiver( 'zip', { zlib: { level: 9 } } );
3533
+ const zipPath = path.join( '/tmp', zipName );
3534
+ const output = fs.createWriteStream( zipPath );
3535
+ const archive = archiver( 'zip', { zlib: { level: 9 } } );
3547
3536
 
3548
- archive.on( 'error', ( err ) => {
3549
- logger.error( { message: 'Archiver error', error: err } );
3550
- // console.error( '❌ Archiver error:', err );
3551
- throw err;
3552
- } );
3537
+ archive.on( 'error', ( err ) => {
3538
+ logger.error( { message: 'Archiver error', error: err } );
3539
+ throw err;
3540
+ } );
3553
3541
 
3554
- archive.pipe( output );
3542
+ archive.pipe( output );
3555
3543
 
3556
- let appendedCount = 0;
3544
+ for ( let i = 0; i < submittedDetails.length; i += ZIP_LIMIT ) {
3545
+ const batch = submittedDetails.slice( i, i + ZIP_LIMIT );
3557
3546
 
3558
- for ( const checklistDetails of batch ) {
3559
- const doc =
3547
+ for ( const checklistDetails of batch ) {
3548
+ const doc =
3560
3549
  typeof checklistDetails.toObject === 'function' ?
3561
3550
  checklistDetails.toObject() :
3562
3551
  { ...checklistDetails };
3563
3552
 
3564
3553
 
3565
- const detectionPayload = {
3566
- 'storeId': [ doc.store_id ],
3567
- 'userEmail': doc.userEmail,
3568
- 'sourceChecklist_id': doc.sourceCheckList_id,
3569
- };
3554
+ const detectionPayload = {
3555
+ 'storeId': [ doc.store_id ],
3556
+ 'userEmail': doc.userEmail,
3557
+ 'sourceChecklist_id': doc.sourceCheckList_id,
3558
+ };
3570
3559
 
3571
3560
 
3572
- let complianceURL = JSON.parse( process.env.LAMBDAURL ).complianceHistory;
3573
- const complianceData = await LamdaServiceCall( complianceURL, detectionPayload );
3574
- if ( complianceData?.data.length ) {
3575
- doc['historyData'] = complianceData.data;
3576
- }
3577
- // CDN fix
3578
- ( doc.questionAnswers || [] ).forEach( ( section ) => {
3579
- ( section.questions || [] ).forEach( ( question ) => {
3580
- ( question.userAnswer || [] ).forEach( ( answer ) => {
3581
- if ( answer?.referenceImage?.trim() ) {
3582
- answer.referenceImage =
3561
+ // let complianceURL = JSON.parse( process.env.LAMBDAURL ).complianceHistory;
3562
+ let complianceURL = 'https://pbxdm4nstcpbaz6gf6c53ofeee0zvmfy.lambda-url.ap-south-1.on.aws/';
3563
+ const complianceData = await LamdaServiceCall( complianceURL, detectionPayload );
3564
+ if ( complianceData?.data.length ) {
3565
+ doc['historyData'] = complianceData.data;
3566
+ }
3567
+ // CDN fix
3568
+ ( doc.questionAnswers || [] ).forEach( ( section ) => {
3569
+ ( section.questions || [] ).forEach( ( question ) => {
3570
+ ( question.userAnswer || [] ).forEach( ( answer ) => {
3571
+ if ( answer?.referenceImage?.trim() ) {
3572
+ answer.referenceImage =
3583
3573
  cdnBase + answer.referenceImage;
3584
- }
3585
- } );
3574
+ }
3586
3575
  } );
3587
3576
  } );
3577
+ } );
3588
3578
 
3589
- const templateData = buildVisitChecklistTemplateData(
3590
- doc,
3591
- brandInfo,
3592
- );
3579
+ const templateData = buildVisitChecklistTemplateData(
3580
+ doc,
3581
+ brandInfo,
3582
+ );
3593
3583
 
3594
- const resolvedData = resolveTemplateUrls( templateData, cdnBase );
3595
- await imageCache.resolveAllImages( resolvedData );
3584
+ const resolvedData = resolveTemplateUrls( templateData, cdnBase );
3585
+ await imageCache.resolveAllImages( resolvedData );
3596
3586
 
3597
- const html = pdfTemplate( resolvedData );
3587
+ const html = pdfTemplate( resolvedData );
3598
3588
 
3599
- // console.log( 'HTML length:', html?.length );
3589
+ // console.log( 'HTML length:', html?.length );
3600
3590
 
3601
- if ( !html || html.length < 50 ) {
3602
- logger.error( { functionName: 'Invalid HTML skipped:', error: doc._id } );
3603
- // console.error( '❌ Invalid HTML skipped:', doc._id );
3604
- continue;
3605
- }
3591
+ if ( !html || html.length < 50 ) {
3592
+ logger.error( { functionName: 'Invalid HTML skipped:', error: doc._id } );
3593
+ // console.error( '❌ Invalid HTML skipped:', doc._id );
3594
+ continue;
3595
+ }
3606
3596
 
3607
- try {
3608
- await page.setContent( html, {
3609
- waitUntil: 'domcontentloaded',
3610
- timeout: 0,
3611
- } );
3597
+ try {
3598
+ await page.setContent( html, {
3599
+ waitUntil: 'domcontentloaded',
3600
+ timeout: 0,
3601
+ } );
3612
3602
 
3613
- await new Promise( ( r ) => setTimeout( r, 300 ) );
3614
- await page.emulateMediaType( 'screen' );
3615
- } catch ( err ) {
3616
- logger.error( { functionName: 'setContent failed:', error: err } );
3617
- // console.error( '❌ setContent failed:', err );
3618
- continue;
3619
- }
3603
+ await new Promise( ( r ) => setTimeout( r, 300 ) );
3604
+ await page.emulateMediaType( 'screen' );
3605
+ } catch ( err ) {
3606
+ logger.error( { functionName: 'setContent failed:', error: err } );
3607
+ // console.error( '❌ setContent failed:', err );
3608
+ continue;
3609
+ }
3620
3610
 
3621
- let pdfBuffer;
3611
+ let pdfBuffer;
3622
3612
 
3623
- try {
3624
- pdfBuffer = await page.pdf( {
3625
- format: 'A4',
3626
- printBackground: true,
3627
- margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' },
3628
- } );
3613
+ try {
3614
+ pdfBuffer = await page.pdf( {
3615
+ format: 'A4',
3616
+ printBackground: true,
3617
+ margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' },
3618
+ } );
3629
3619
 
3630
- // console.log( 'PDF size:', pdfBuffer?.length );
3631
- // console.log(
3632
- // 'IsBuffer:',
3633
- // Buffer.isBuffer( pdfBuffer ),
3634
- // 'Type:',
3635
- // pdfBuffer?.constructor?.name,
3636
- // );
3637
- } catch ( err ) {
3638
- logger.error( { functionName: 'downloadInsertPdf', message: 'PDF generation failed', error: err } );
3639
- continue;
3640
- }
3620
+ // console.log( 'PDF size:', pdfBuffer?.length );
3621
+ // console.log(
3622
+ // 'IsBuffer:',
3623
+ // Buffer.isBuffer( pdfBuffer ),
3624
+ // 'Type:',
3625
+ // pdfBuffer?.constructor?.name,
3626
+ // );
3627
+ } catch ( err ) {
3628
+ logger.error( { functionName: 'downloadInsertPdf', message: 'PDF generation failed', error: err } );
3629
+ continue;
3630
+ }
3641
3631
 
3642
- if ( !pdfBuffer || pdfBuffer.length === 0 ) {
3643
- logger.error( { functionName: 'downloadInsertPdf', message: 'Empty PDF skipped', docId: doc._id } );
3644
- continue;
3645
- }
3632
+ if ( !pdfBuffer || pdfBuffer.length === 0 ) {
3633
+ logger.error( { functionName: 'downloadInsertPdf', message: 'Empty PDF skipped', docId: doc._id } );
3634
+ continue;
3635
+ }
3646
3636
 
3647
- const finalBuffer = Buffer.isBuffer( pdfBuffer ) ?
3637
+ const finalBuffer = Buffer.isBuffer( pdfBuffer ) ?
3648
3638
  pdfBuffer :
3649
3639
  Buffer.from( pdfBuffer );
3650
3640
 
3651
- const pdfName = `${safeName(
3652
- doc.storeName || doc.store_id || doc._id || 'store',
3653
- )}.pdf`;
3641
+ const pdfName = `${safeName(
3642
+ doc.storeName || doc.store_id || doc._id || 'store',
3643
+ )}.pdf`;
3654
3644
 
3655
- archive.append( finalBuffer, { name: pdfName } );
3656
- appendedCount++;
3657
- }
3645
+ archive.append( finalBuffer, { name: pdfName } );
3646
+ appendedCount++;
3647
+ }
3648
+ }
3649
+ if ( appendedCount === 0 ) {
3650
+ logger.error( { functionName: 'No PDFs generated for batch, skipping ZIP', error: zipName } );
3651
+ // console.error( `❌ No PDFs generated for batch, skipping ZIP: ${zipName}` );
3652
+ archive.abort();
3653
+ output.destroy();
3654
+ continue;
3655
+ }
3658
3656
 
3659
- if ( appendedCount === 0 ) {
3660
- logger.error( { functionName: 'No PDFs generated for batch, skipping ZIP', error: zipName } );
3661
- // console.error( `❌ No PDFs generated for batch, skipping ZIP: ${zipName}` );
3662
- archive.abort();
3663
- output.destroy();
3664
- continue;
3665
- }
3657
+ logger.info( { functionName: 'downloadInsertPdf', message: `${appendedCount} PDFs added to ${zipName}` } );
3666
3658
 
3667
- // console.log( `📄 ${appendedCount} PDFs added to ${zipName}` );
3659
+ await new Promise( ( resolve, reject ) => {
3660
+ output.on( 'close', resolve );
3661
+ output.on( 'error', reject );
3662
+ archive.on( 'error', reject );
3668
3663
 
3669
- await new Promise( ( resolve, reject ) => {
3670
- output.on( 'close', resolve );
3671
- output.on( 'error', reject );
3672
- archive.on( 'error', reject );
3664
+ archive.finalize();
3665
+ } );
3673
3666
 
3674
- archive.finalize();
3675
- } );
3667
+ const zipBuffer = fs.readFileSync( zipPath );
3668
+ // console.log( `✅ ZIP ready: ${zipName}, archive pointer: ${archive.pointer()} bytes, file size: ${zipBuffer.length} bytes` );
3676
3669
 
3677
- const zipBuffer = fs.readFileSync( zipPath );
3678
- // console.log( `✅ ZIP ready: ${zipName}, archive pointer: ${archive.pointer()} bytes, file size: ${zipBuffer.length} bytes` );
3670
+ // const attachment = {
3671
+ // filename: zipName,
3672
+ // content: zipBuffer,
3673
+ // contentType: 'application/zip',
3674
+ // };
3679
3675
 
3680
- const attachment = {
3681
- filename: zipName,
3682
- content: zipBuffer,
3683
- contentType: 'application/zip',
3684
- };
3676
+ let bucket = JSON.parse( process.env.BUCKET );
3685
3677
 
3686
- await Promise.all(
3687
- emailList.map( ( email ) =>
3688
- sendEmailWithSES(
3689
- email,
3690
- 'Checklist Report',
3691
- emailHtml,
3692
- attachment,
3693
- sourceEmail,
3694
- ),
3695
- ),
3696
- );
3678
+ let params = {
3679
+ fileName: zipName,
3680
+ Key: 'reports/',
3681
+ Bucket: bucket.sop,
3682
+ body: zipBuffer,
3683
+ ContentType: 'application/zip',
3684
+ };
3697
3685
 
3698
- // console.log( `📧 Emails sent for ${zipName}` );
3699
- }
3686
+ let zipURL = await fileUpload( params );
3687
+
3688
+ zipURL = await signedUrl( { file_path: zipURL.Key, Bucket: bucket.sop } );
3689
+
3690
+ let data = {
3691
+ checklistName: checklistInfo.checkListName,
3692
+ date: dayjs( todayStr, 'YYYY-MM-DD' ).format( 'DD-MM-YYYY' ),
3693
+ total: submittedDetails.length,
3694
+ zipURL: zipURL,
3695
+ };
3696
+ const emailFileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/autoEmail.hbs', 'utf8' );
3697
+ const emailHtmlContent = handlebars.compile( emailFileContent );
3698
+ const emailHtml = emailHtmlContent( { data: data } );
3699
+
3700
+ await Promise.all(
3701
+ emailList.map( ( email ) =>
3702
+ sendEmailWithSES(
3703
+ email,
3704
+ 'Checklist Report',
3705
+ emailHtml,
3706
+ '',
3707
+ sourceEmail,
3708
+ ),
3709
+ ),
3710
+ );
3711
+
3712
+ try {
3713
+ fs.unlinkSync( zipPath );
3714
+ } catch ( cleanupErr ) {
3715
+ logger.error( { functionName: 'downloadInsertPdf', message: 'Failed to cleanup tmp file', error: cleanupErr } );
3700
3716
  }
3701
3717
  }
3702
3718
  } finally {
@@ -1,216 +1,235 @@
1
- <!DOCTYPE html>
2
- <html>
3
-
4
- <head>
5
-
6
- <meta charset="utf-8">
7
- <meta http-equiv="x-ua-compatible" content="ie=edge">
8
- <title>Trial Intiate Email</title>
9
- <meta name="viewport" content="width=device-width, initial-scale=1">
10
- <style type="text/css">
11
- @media screen {
12
- @font-face {
13
- font-family: 'Inter';
14
- font-style: normal;
15
- font-weight: 400;
16
- font-display: swap;
17
- src: local("Inter"), local("Inter-Regular"), url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiI2B.woff2) format('woff2');
18
- unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
19
- }
20
- }
21
-
22
- body {
23
- font-family: "Inter", sans-serif !important;
24
- }
25
-
26
- body,
27
- table,
28
- td,
29
- a {
30
- -ms-text-size-adjust: 100%;
31
- -webkit-text-size-adjust: 100%;
32
- }
33
-
34
- table,
35
- td {
36
- mso-table-rspace: 0pt;
37
- mso-table-lspace: 0pt;
38
- }
39
-
40
- img {
41
- -ms-interpolation-mode: bicubic;
42
- }
43
-
44
- a[x-apple-data-detectors] {
45
- font-family: "inherit" !important;
46
- font-size: inherit !important;
47
- font-weight: inherit !important;
48
- line-height: inherit !important;
49
- color: inherit !important;
50
- text-decoration: none !important;
51
- }
52
-
53
-
54
- div[style*="margin: 16px 0;"
55
-
56
- ] {
57
- margin: 0 !important;
58
- }
59
-
60
- body {
61
- width: 100% !important;
62
- height: 100% !important;
63
- padding: 0 !important;
64
- margin: 0 !important;
65
- }
66
-
67
-
68
- table {
69
- border-collapse: collapse !important;
70
- }
71
-
72
- a {
73
- color: #1a82e2;
74
- }
75
-
76
- img {
77
- height: auto;
78
- line-height: 100%;
79
- text-decoration: none;
80
- border: 0;
81
- outline: none;
82
- }
83
-
84
- .flagText {
85
- /* font-family: 'Inter'; */
86
- /* color: #667085 !important; */
87
- font-size: 16px;
88
- font-weight: 600;
89
- line-height: 24px;
90
- text-align: left;
91
-
92
- }
93
- </style>
94
-
95
- </head>
96
-
97
- <body style="background-color: #dbe5ea;">
98
-
99
- <div class="preheader"
100
- style="display: none; max-width: 0; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: #fff; opacity: 0;">
101
- Email Summary (Hidden)
102
- </div>
103
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-left:10px;padding-right:10px">
104
- <tr>
105
- <td bgcolor="#dbe5ea" style="padding:32px 10px 0 10px">
106
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;" align="center">
107
- <tr>
108
- <td class="o_bg-white o_px-md o_py-md o_sans o_text"
109
- style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 24px;background-color: #ffffff;padding-left: 18px;padding-right: 24px;padding-top: 24px;padding-bottom: 24px;">
110
- <p style="margin-top: 0px;margin-bottom: 0px;"><a class="o_text-white"
111
- href="https://tangoeye.ai/"
112
- style="text-decoration: none;outline: none;color: #ffffff;"><img
113
- src="https://devtangoretail-api.tangoeye.ai/logo.png" width="200" height="100"
114
- alt="SimpleApp"
115
- style="-ms-interpolation-mode: bicubic;vertical-align: middle;border: 0;line-height: 100%;height: auto;outline: none;text-decoration: none;"></a>
116
- </p>
117
- </td>
118
- </tr>
119
- <tr>
120
- <td align="left" bgcolor="#ffffff"
121
- style="padding-left: 30px;padding-right: 24px; font-size: 14px; line-height: 24px;">
122
- <p class="o_heading o_mb-xxs"
123
- style="width: 624px;height: 0px;border: 1px solid #CBD5E1;flex: none;order: 1;flex-grow: 0;">
124
- </p>
125
- </td>
126
- </tr>
127
- <tr>
128
- <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
129
- style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom: 10px;">
130
- <div class="o_col-6s o_sans o_text-md o_text-light o_center"
131
- style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 28px;color: #82899a;">
132
- <span class="o_heading o_text-dark o_mb-xxs"
133
- style="font-weight: 400;margin-top: 0px;margin-bottom: 4px;color: #121A26;line-height: 140%;">
134
- Hi,<br />
135
- Store compliance summary has been generated for a recently completed checklist across multiple stores. Review the
136
- compliance performance and identify stores that require attention from the dashboard.
137
- </span>
138
- </div>
139
- </td>
140
- </tr>
141
- <tr>
142
- <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
143
- style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom: 10px;">
144
- <div class="o_col-6s o_sans o_text-md o_text-light o_center"
145
- style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 28px;color: #82899a;">
146
- <span class="o_heading o_text-dark o_mb-xxs"
147
- style="font-weight: 400;margin-top: 0px;margin-bottom: 4px;color: #121A26;line-height: 140%;">
148
- Details of the checklist are as follows:
149
- </span>
150
- </div>
151
- </td>
152
- </tr>
153
- </table>
154
- </td>
155
- </tr>
156
- <tr>
157
- <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
158
- <table align="center" bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0" width="100%"
159
- style="max-width: 680px;">
160
- <tr bgcolor="#ffffff" style="border:none;margin-top:0px;">
161
- <td class="flagText" style="padding-left:30px; line-height: 24px;color:#000000">Checklist Name:
162
- </td>
163
- <td></td>
164
- <td></td>
165
- <td class="flagText">{{data.checklistName}}</td>
166
- </tr>
167
- <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
168
- <td class="flagText" style="padding-left:30px; line-height: 24px;">Total Stores :</td>
169
- <td></td>
170
- <td></td>
171
- <td class="flagText">{{data.total}}</td>
172
- </tr>
173
-
174
- <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
175
- <td class="flagText" style="padding-left:30px; line-height: 24px;">Date :</td>
176
- <td></td>
177
- <td></td>
178
- <td class="flagText">{{data.date}}</td>
179
- </tr>
180
- </table>
181
- </td>
182
- </tr>
183
- <tr>
184
- <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 32px 10px;">
185
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;">
186
- <tr>
187
- <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
188
- style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-top:5px">
189
- <div class="o_col-6s o_sans o_text-md o_text-light o_center"
190
- style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
191
- <p>
192
- If you have any questions or need assistance, please reach out to us at
193
- support@tangotech.co.in.
194
- </p>
195
- </div>
196
- </td>
197
- </tr>
198
- <tr>
199
- <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
200
- style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom:15px">
201
- <div class="o_col-6s o_sans o_text-md o_text-light o_center"
202
- style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
203
- <p>
204
- © Tango Eye. All rights reserved.</p>
205
- </div>
206
- </td>
207
- </tr>
208
- </table>
209
- </td>
210
- </tr>
211
-
212
- </table>
213
-
214
- </body>
215
-
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+
6
+ <meta charset="utf-8">
7
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
8
+ <title>Trial Intiate Email</title>
9
+ <meta name="viewport" content="width=device-width, initial-scale=1">
10
+ <style type="text/css">
11
+ @media screen {
12
+ @font-face {
13
+ font-family: 'Inter';
14
+ font-style: normal;
15
+ font-weight: 400;
16
+ font-display: swap;
17
+ src: local("Inter"), local("Inter-Regular"), url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiI2B.woff2) format('woff2');
18
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
19
+ }
20
+ }
21
+
22
+ body {
23
+ font-family: "Inter", sans-serif !important;
24
+ }
25
+
26
+ body,
27
+ table,
28
+ td,
29
+ a {
30
+ -ms-text-size-adjust: 100%;
31
+ -webkit-text-size-adjust: 100%;
32
+ }
33
+
34
+ table,
35
+ td {
36
+ mso-table-rspace: 0pt;
37
+ mso-table-lspace: 0pt;
38
+ }
39
+
40
+ img {
41
+ -ms-interpolation-mode: bicubic;
42
+ }
43
+
44
+ a[x-apple-data-detectors] {
45
+ font-family: "inherit" !important;
46
+ font-size: inherit !important;
47
+ font-weight: inherit !important;
48
+ line-height: inherit !important;
49
+ color: inherit !important;
50
+ text-decoration: none !important;
51
+ }
52
+
53
+
54
+ div[style*="margin: 16px 0;"
55
+
56
+ ] {
57
+ margin: 0 !important;
58
+ }
59
+
60
+ body {
61
+ width: 100% !important;
62
+ height: 100% !important;
63
+ padding: 0 !important;
64
+ margin: 0 !important;
65
+ }
66
+
67
+
68
+ table {
69
+ border-collapse: collapse !important;
70
+ }
71
+
72
+ a {
73
+ color: #1a82e2;
74
+ }
75
+
76
+ img {
77
+ height: auto;
78
+ line-height: 100%;
79
+ text-decoration: none;
80
+ border: 0;
81
+ outline: none;
82
+ }
83
+
84
+ .flagText {
85
+ /* font-family: 'Inter'; */
86
+ /* color: #667085 !important; */
87
+ font-size: 16px;
88
+ font-weight: 600;
89
+ line-height: 24px;
90
+ text-align: left;
91
+
92
+ }
93
+ </style>
94
+
95
+ </head>
96
+
97
+ <body style="background-color: #dbe5ea;">
98
+
99
+ <div class="preheader"
100
+ style="display: none; max-width: 0; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: #fff; opacity: 0;">
101
+ Email Summary (Hidden)
102
+ </div>
103
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-left:10px;padding-right:10px">
104
+ <tr>
105
+ <td bgcolor="#dbe5ea" style="padding:32px 10px 0 10px">
106
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;" align="center">
107
+ <tr>
108
+ <td class="o_bg-white o_px-md o_py-md o_sans o_text"
109
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 24px;background-color: #ffffff;padding-left: 18px;padding-right: 24px;padding-top: 24px;padding-bottom: 24px;">
110
+ <p style="margin-top: 0px;margin-bottom: 0px;"><a class="o_text-white"
111
+ href="https://tangoeye.ai/"
112
+ style="text-decoration: none;outline: none;color: #ffffff;"><img
113
+ src="https://devtangoretail-api.tangoeye.ai/logo.png" width="200" height="100"
114
+ alt="SimpleApp"
115
+ style="-ms-interpolation-mode: bicubic;vertical-align: middle;border: 0;line-height: 100%;height: auto;outline: none;text-decoration: none;"></a>
116
+ </p>
117
+ </td>
118
+ </tr>
119
+ <tr>
120
+ <td align="left" bgcolor="#ffffff"
121
+ style="padding-left: 30px;padding-right: 24px; font-size: 14px; line-height: 24px;">
122
+ <p class="o_heading o_mb-xxs"
123
+ style="width: 624px;height: 0px;border: 1px solid #CBD5E1;flex: none;order: 1;flex-grow: 0;">
124
+ </p>
125
+ </td>
126
+ </tr>
127
+ <tr>
128
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
129
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom: 10px;">
130
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
131
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 28px;color: #82899a;">
132
+ <span class="o_heading o_text-dark o_mb-xxs"
133
+ style="font-weight: 400;margin-top: 0px;margin-bottom: 4px;color: #121A26;line-height: 140%;">
134
+ Hi,<br />
135
+ Store compliance summary has been generated for a recently completed checklist across multiple stores. Review the
136
+ compliance performance and identify stores that require attention from the dashboard.
137
+ </span>
138
+ </div>
139
+ </td>
140
+ </tr>
141
+ <tr>
142
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
143
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom: 10px;">
144
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
145
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 28px;color: #82899a;">
146
+ <span class="o_heading o_text-dark o_mb-xxs"
147
+ style="font-weight: 400;margin-top: 0px;margin-bottom: 4px;color: #121A26;line-height: 140%;">
148
+ Details of the checklist are as follows:
149
+ </span>
150
+ </div>
151
+ </td>
152
+ </tr>
153
+ </table>
154
+ </td>
155
+ </tr>
156
+ <tr>
157
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
158
+ <table align="center" bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0" width="100%"
159
+ style="max-width: 680px;">
160
+ <tr bgcolor="#ffffff" style="border:none;margin-top:0px;">
161
+ <td class="flagText" style="padding-left:30px; line-height: 24px;color:#000000">Checklist Name:
162
+ </td>
163
+ <td></td>
164
+ <td></td>
165
+ <td class="flagText">{{data.checklistName}}</td>
166
+ </tr>
167
+ <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
168
+ <td class="flagText" style="padding-left:30px; line-height: 24px;">Total Stores :</td>
169
+ <td></td>
170
+ <td></td>
171
+ <td class="flagText">{{data.total}}</td>
172
+ </tr>
173
+
174
+ <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
175
+ <td class="flagText" style="padding-left:30px; line-height: 24px;">Date :</td>
176
+ <td></td>
177
+ <td></td>
178
+ <td class="flagText">{{data.date}}</td>
179
+ </tr>
180
+ </table>
181
+ </td>
182
+ </tr>
183
+ <tr>
184
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
185
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;">
186
+ <tr>
187
+ <td bgcolor="#ffffff" style="padding: 20px;padding-top:15px;padding-left:30px;">
188
+ <table border="0" cellpadding="0" cellspacing="0">
189
+ <tr>
190
+ <td align="center" bgcolor="#00A3FF" style="border-radius: 6px;height:50px;">
191
+ <a href="{{data.zipURL}}" target="_blank"
192
+ style="display: inline-block; padding: 16px 36px; font-size: 16px; color: #ffffff; text-decoration: none; border-radius: 6px;cursor:pointer">Download
193
+ </a>
194
+ </td>
195
+ </tr>
196
+ </table>
197
+ </td>
198
+ </tr>
199
+ </table>
200
+ </td>
201
+ </tr>
202
+ <tr>
203
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 32px 10px;">
204
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;">
205
+ <tr>
206
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
207
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-top:5px">
208
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
209
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
210
+ <p>
211
+ If you have any questions or need assistance, please reach out to us at
212
+ support@tangotech.co.in.
213
+ </p>
214
+ </div>
215
+ </td>
216
+ </tr>
217
+ <tr>
218
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
219
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom:15px">
220
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
221
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
222
+ <p>
223
+ © Tango Eye. All rights reserved.</p>
224
+ </div>
225
+ </td>
226
+ </tr>
227
+ </table>
228
+ </td>
229
+ </tr>
230
+
231
+ </table>
232
+
233
+ </body>
234
+
216
235
  </html>
@@ -176,7 +176,7 @@
176
176
  <div class="page">
177
177
  <div class="detail-page">
178
178
  {{#each this.sections}}
179
- <div class="dp-header" {{#unless @first}}style="margin-top:20px"{{/unless}}><h2>{{this.sectionName}}</h2><span class="dp-score">{{this.currentScore}}/{{this.maxScore}}</span></div>
179
+ <div class="dp-header" {{#unless @first}}style="margin-top:20px"{{/unless}}><h2>{{this.sectionName}}</h2>{{#if this.maxScore}}<span class="dp-score">{{this.currentScore}}/{{this.maxScore}}</span>{{/if}}</div>
180
180
  <div class="sec-questions">
181
181
  {{#each this.questions}}
182
182
  <div class="q-row">
@@ -449,7 +449,7 @@ export function buildVisitChecklistTemplateDataFromProcessed( processedDoc, bran
449
449
 
450
450
  titleLine2,
451
451
 
452
- referenceId: doc.store_id || doc.storeName || refFromId,
452
+ referenceId: doc?.storeName ?? doc?.userEmail,
453
453
 
454
454
  date: formattedDate,
455
455