tango-app-api-trax 3.7.90 → 3.7.92
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
|
@@ -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, getOpenSearchData } 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';
|
|
@@ -3455,8 +3455,158 @@ async function getBrandInfo( clientId ) {
|
|
|
3455
3455
|
|
|
3456
3456
|
export const downloadInsertPdf = async ( req, res ) => {
|
|
3457
3457
|
try {
|
|
3458
|
-
|
|
3458
|
+
if ( !req.body.checklistId ) {
|
|
3459
|
+
return res.sendError( 'Checklist Id is required', 400 );
|
|
3460
|
+
}
|
|
3461
|
+
const cdnBase = JSON.parse( process.env.CDNURL )?.TraxAnswerCDN || '';
|
|
3462
|
+
const pdfTemplate = getCompiledVisitChecklistTemplate();
|
|
3463
|
+
const imageCache = createImageCache();
|
|
3464
|
+
|
|
3465
|
+
const browser = await puppeteer.launch( {
|
|
3466
|
+
headless: 'new',
|
|
3467
|
+
args: [ '--no-sandbox', '--disable-dev-shm-usage' ],
|
|
3468
|
+
} );
|
|
3469
|
+
|
|
3470
|
+
try {
|
|
3471
|
+
const page = await browser.newPage();
|
|
3472
|
+
|
|
3473
|
+
await page.setViewport( {
|
|
3474
|
+
width: 1280,
|
|
3475
|
+
height: 1800,
|
|
3476
|
+
} );
|
|
3477
|
+
|
|
3478
|
+
const safeName = ( str ) =>
|
|
3479
|
+
( str || '' ).toString().replace( /[<>:"/\\|?*]+/g, '_' );
|
|
3480
|
+
|
|
3481
|
+
let query = {
|
|
3482
|
+
query: {
|
|
3483
|
+
bool: {
|
|
3484
|
+
must: [
|
|
3485
|
+
{
|
|
3486
|
+
term: {
|
|
3487
|
+
_id: req.body.checklistId,
|
|
3488
|
+
},
|
|
3489
|
+
},
|
|
3490
|
+
],
|
|
3491
|
+
},
|
|
3492
|
+
},
|
|
3493
|
+
};
|
|
3494
|
+
|
|
3495
|
+
let aiDetails = await getOpenSearchData( JSON.parse( process.env.OPENSEARCH ).traxIndex, query );
|
|
3496
|
+
if ( aiDetails?.statusCode != 200 || !aiDetails?.body?.hits?.hits.length ) {
|
|
3497
|
+
return res.sendError( 'Checklist not found', 404 );
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
const doc = { ...aiDetails.body.hits.hits[0]._source };
|
|
3501
|
+
|
|
3502
|
+
const brandInfo = await getBrandInfo( doc.client_id );
|
|
3503
|
+
|
|
3504
|
+
const detectionPayload = {
|
|
3505
|
+
'storeId': [ doc.store_id ],
|
|
3506
|
+
'userEmail': doc.userEmail,
|
|
3507
|
+
'sourceChecklist_id': doc.sourceCheckList_id,
|
|
3508
|
+
};
|
|
3509
|
+
|
|
3510
|
+
let complianceURL = JSON.parse( process.env.LAMBDAURL ).complianceHistory;
|
|
3511
|
+
const complianceData = await LamdaServiceCall( complianceURL, detectionPayload );
|
|
3512
|
+
if ( complianceData?.data.length ) {
|
|
3513
|
+
doc['historyData'] = complianceData.data;
|
|
3514
|
+
}
|
|
3515
|
+
// CDN fix
|
|
3516
|
+
( doc.questionAnswers || [] ).forEach( ( section ) => {
|
|
3517
|
+
( section.questions || [] ).forEach( ( question ) => {
|
|
3518
|
+
( question.userAnswer || [] ).forEach( ( answer ) => {
|
|
3519
|
+
if ( answer?.referenceImage?.trim() ) {
|
|
3520
|
+
answer.referenceImage =
|
|
3521
|
+
cdnBase + answer.referenceImage;
|
|
3522
|
+
}
|
|
3523
|
+
} );
|
|
3524
|
+
} );
|
|
3525
|
+
} );
|
|
3526
|
+
|
|
3527
|
+
const templateData = buildVisitChecklistTemplateData(
|
|
3528
|
+
doc,
|
|
3529
|
+
brandInfo,
|
|
3530
|
+
);
|
|
3531
|
+
|
|
3532
|
+
const resolvedData = resolveTemplateUrls( templateData, cdnBase );
|
|
3533
|
+
await imageCache.resolveAllImages( resolvedData );
|
|
3534
|
+
|
|
3535
|
+
const html = pdfTemplate( resolvedData );
|
|
3536
|
+
|
|
3537
|
+
try {
|
|
3538
|
+
await page.setContent( html, {
|
|
3539
|
+
waitUntil: 'networkidle0',
|
|
3540
|
+
timeout: 30000,
|
|
3541
|
+
} );
|
|
3542
|
+
|
|
3543
|
+
await page.emulateMediaType( 'screen' );
|
|
3544
|
+
} catch ( err ) {
|
|
3545
|
+
logger.error( { functionName: 'setContent failed:', error: err } );
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
let pdfBuffer;
|
|
3549
|
+
|
|
3550
|
+
try {
|
|
3551
|
+
pdfBuffer = await page.pdf( {
|
|
3552
|
+
format: 'A4',
|
|
3553
|
+
printBackground: true,
|
|
3554
|
+
margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' },
|
|
3555
|
+
} );
|
|
3556
|
+
} catch ( err ) {
|
|
3557
|
+
logger.error( { functionName: 'downloadInsertPdfOld', message: 'PDF generation failed', error: err } );
|
|
3558
|
+
return res.sendError( 'PDF generation failed', 500 );
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
if ( !pdfBuffer || pdfBuffer.length === 0 ) {
|
|
3562
|
+
logger.error( { functionName: 'downloadInsertPdfOld', message: 'Empty PDF', docId: doc._id } );
|
|
3563
|
+
return res.sendError( 'PDF generation resulted in empty buffer', 500 );
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
const finalBuffer = Buffer.isBuffer( pdfBuffer ) ?
|
|
3567
|
+
pdfBuffer :
|
|
3568
|
+
Buffer.from( pdfBuffer );
|
|
3569
|
+
|
|
3570
|
+
const pdfName = `${safeName(
|
|
3571
|
+
doc.storeName || doc.store_id || doc._id || 'store',
|
|
3572
|
+
)}.pdf`;
|
|
3573
|
+
|
|
3574
|
+
res.set( {
|
|
3575
|
+
'Content-Type': 'application/pdf',
|
|
3576
|
+
'Content-Disposition': `attachment; filename="${pdfName}"`,
|
|
3577
|
+
'Content-Length': finalBuffer.length,
|
|
3578
|
+
} );
|
|
3579
|
+
|
|
3580
|
+
return res.send( finalBuffer );
|
|
3581
|
+
} finally {
|
|
3582
|
+
await browser.close();
|
|
3583
|
+
}
|
|
3584
|
+
} catch ( e ) {
|
|
3585
|
+
console.log( e );
|
|
3586
|
+
logger.error( { functionName: 'downloadInsertPdfOld', error: e } );
|
|
3587
|
+
return res.sendError( e, 500 );
|
|
3588
|
+
}
|
|
3589
|
+
};
|
|
3590
|
+
|
|
3591
|
+
export async function checklistAutoMailList( req, res ) {
|
|
3592
|
+
try {
|
|
3593
|
+
const checklistInfoList = await CLconfig.find( {
|
|
3594
|
+
'autoEmail.type': { $exists: true },
|
|
3595
|
+
'$expr': {
|
|
3596
|
+
$gt: [ { $size: { $ifNull: [ '$autoEmail.type', [] ] } }, 0 ],
|
|
3597
|
+
},
|
|
3598
|
+
}, { _id: 1 } );
|
|
3599
|
+
|
|
3600
|
+
return res.sendSuccess( checklistInfoList?.map( ( ele ) => ele?._id ) );
|
|
3601
|
+
} catch ( e ) {
|
|
3602
|
+
logger.error( { functionName: 'checklistAutoMailList', error: e } );
|
|
3603
|
+
return res.sendError( e, 500 );
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3459
3607
|
|
|
3608
|
+
export const downloadInsertPdfOld = async ( req, res ) => {
|
|
3609
|
+
try {
|
|
3460
3610
|
setImmediate( async () => {
|
|
3461
3611
|
try {
|
|
3462
3612
|
const cdnBase = JSON.parse( process.env.CDNURL )?.TraxAnswerCDN || '';
|
|
@@ -3489,16 +3639,8 @@ export const downloadInsertPdf = async ( req, res ) => {
|
|
|
3489
3639
|
const safeName = ( str ) =>
|
|
3490
3640
|
( str || '' ).toString().replace( /[<>:"/\\|?*]+/g, '_' );
|
|
3491
3641
|
for ( const checklistInfo of checklistInfoList ) {
|
|
3492
|
-
console.log( dayjs.utc().diff( dayjs.utc( checklistInfo.scheduleEndTime, 'hh:mm A' ), 'minute' ) );
|
|
3642
|
+
// console.log( dayjs.utc().diff( dayjs.utc( checklistInfo.scheduleEndTime, 'hh:mm A' ), 'minute' ) );
|
|
3493
3643
|
// 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
3644
|
let emailList = [];
|
|
3503
3645
|
|
|
3504
3646
|
if ( checklistInfo?.autoEmail?.type?.includes( 'approver' ) ) {
|
|
@@ -3532,28 +3674,25 @@ export const downloadInsertPdf = async ( req, res ) => {
|
|
|
3532
3674
|
// );
|
|
3533
3675
|
|
|
3534
3676
|
const ZIP_LIMIT = 100;
|
|
3677
|
+
let appendedCount = 0;
|
|
3535
3678
|
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
const zipName = `${safeName(
|
|
3540
|
-
checklistInfo.checkListName,
|
|
3541
|
-
)}_${todayStr}_part_${Math.floor( i / ZIP_LIMIT ) + 1}.zip`;
|
|
3679
|
+
const zipName = `${safeName(
|
|
3680
|
+
checklistInfo.checkListName,
|
|
3681
|
+
)}_${todayStr}.zip`;
|
|
3542
3682
|
|
|
3543
|
-
|
|
3683
|
+
const zipPath = path.join( '/tmp', zipName );
|
|
3684
|
+
const output = fs.createWriteStream( zipPath );
|
|
3685
|
+
const archive = archiver( 'zip', { zlib: { level: 9 } } );
|
|
3544
3686
|
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
logger.error( { message: 'Archiver error', error: err } );
|
|
3550
|
-
// console.error( '❌ Archiver error:', err );
|
|
3551
|
-
throw err;
|
|
3552
|
-
} );
|
|
3687
|
+
archive.on( 'error', ( err ) => {
|
|
3688
|
+
logger.error( { message: 'Archiver error', error: err } );
|
|
3689
|
+
throw err;
|
|
3690
|
+
} );
|
|
3553
3691
|
|
|
3554
|
-
|
|
3692
|
+
archive.pipe( output );
|
|
3555
3693
|
|
|
3556
|
-
|
|
3694
|
+
for ( let i = 0; i < submittedDetails.length; i += ZIP_LIMIT ) {
|
|
3695
|
+
const batch = submittedDetails.slice( i, i + ZIP_LIMIT );
|
|
3557
3696
|
|
|
3558
3697
|
for ( const checklistDetails of batch ) {
|
|
3559
3698
|
const doc =
|
|
@@ -3607,11 +3746,10 @@ export const downloadInsertPdf = async ( req, res ) => {
|
|
|
3607
3746
|
|
|
3608
3747
|
try {
|
|
3609
3748
|
await page.setContent( html, {
|
|
3610
|
-
waitUntil: '
|
|
3611
|
-
timeout:
|
|
3749
|
+
waitUntil: 'networkidle0',
|
|
3750
|
+
timeout: 30000,
|
|
3612
3751
|
} );
|
|
3613
3752
|
|
|
3614
|
-
await new Promise( ( r ) => setTimeout( r, 300 ) );
|
|
3615
3753
|
await page.emulateMediaType( 'screen' );
|
|
3616
3754
|
} catch ( err ) {
|
|
3617
3755
|
logger.error( { functionName: 'setContent failed:', error: err } );
|
|
@@ -3656,49 +3794,75 @@ export const downloadInsertPdf = async ( req, res ) => {
|
|
|
3656
3794
|
archive.append( finalBuffer, { name: pdfName } );
|
|
3657
3795
|
appendedCount++;
|
|
3658
3796
|
}
|
|
3797
|
+
}
|
|
3798
|
+
if ( appendedCount === 0 ) {
|
|
3799
|
+
logger.error( { functionName: 'No PDFs generated for batch, skipping ZIP', error: zipName } );
|
|
3800
|
+
// console.error( `❌ No PDFs generated for batch, skipping ZIP: ${zipName}` );
|
|
3801
|
+
archive.abort();
|
|
3802
|
+
output.destroy();
|
|
3803
|
+
continue;
|
|
3804
|
+
}
|
|
3659
3805
|
|
|
3660
|
-
|
|
3661
|
-
logger.error( { functionName: 'No PDFs generated for batch, skipping ZIP', error: zipName } );
|
|
3662
|
-
// console.error( `❌ No PDFs generated for batch, skipping ZIP: ${zipName}` );
|
|
3663
|
-
archive.abort();
|
|
3664
|
-
output.destroy();
|
|
3665
|
-
continue;
|
|
3666
|
-
}
|
|
3806
|
+
logger.info( { functionName: 'downloadInsertPdf', message: `${appendedCount} PDFs added to ${zipName}` } );
|
|
3667
3807
|
|
|
3668
|
-
|
|
3808
|
+
await new Promise( ( resolve, reject ) => {
|
|
3809
|
+
output.on( 'close', resolve );
|
|
3810
|
+
output.on( 'error', reject );
|
|
3811
|
+
archive.on( 'error', reject );
|
|
3669
3812
|
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
output.on( 'error', reject );
|
|
3673
|
-
archive.on( 'error', reject );
|
|
3813
|
+
archive.finalize();
|
|
3814
|
+
} );
|
|
3674
3815
|
|
|
3675
|
-
|
|
3676
|
-
|
|
3816
|
+
const zipBuffer = fs.readFileSync( zipPath );
|
|
3817
|
+
// console.log( `✅ ZIP ready: ${zipName}, archive pointer: ${archive.pointer()} bytes, file size: ${zipBuffer.length} bytes` );
|
|
3677
3818
|
|
|
3678
|
-
|
|
3679
|
-
|
|
3819
|
+
// const attachment = {
|
|
3820
|
+
// filename: zipName,
|
|
3821
|
+
// content: zipBuffer,
|
|
3822
|
+
// contentType: 'application/zip',
|
|
3823
|
+
// };
|
|
3680
3824
|
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3825
|
+
let bucket = JSON.parse( process.env.BUCKET );
|
|
3826
|
+
|
|
3827
|
+
let params = {
|
|
3828
|
+
fileName: zipName,
|
|
3829
|
+
Key: 'reports/',
|
|
3830
|
+
Bucket: bucket.sop,
|
|
3831
|
+
body: zipBuffer,
|
|
3832
|
+
ContentType: 'application/zip',
|
|
3833
|
+
};
|
|
3834
|
+
|
|
3835
|
+
let zipURL = await fileUpload( params );
|
|
3686
3836
|
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3837
|
+
zipURL = await signedUrl( { file_path: zipURL.Key, Bucket: bucket.sop } );
|
|
3838
|
+
|
|
3839
|
+
let data = {
|
|
3840
|
+
checklistName: checklistInfo.checkListName,
|
|
3841
|
+
date: dayjs( todayStr, 'YYYY-MM-DD' ).format( 'DD-MM-YYYY' ),
|
|
3842
|
+
total: submittedDetails.length,
|
|
3843
|
+
zipURL: zipURL,
|
|
3844
|
+
};
|
|
3845
|
+
const emailFileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/autoEmail.hbs', 'utf8' );
|
|
3846
|
+
const emailHtmlContent = handlebars.compile( emailFileContent );
|
|
3847
|
+
const emailHtml = emailHtmlContent( { data: data } );
|
|
3848
|
+
|
|
3849
|
+
await Promise.all(
|
|
3850
|
+
emailList.map( ( email ) =>
|
|
3851
|
+
sendEmailWithSES(
|
|
3852
|
+
email,
|
|
3853
|
+
'Checklist Report',
|
|
3854
|
+
emailHtml,
|
|
3855
|
+
'',
|
|
3856
|
+
sourceEmail,
|
|
3696
3857
|
),
|
|
3697
|
-
|
|
3858
|
+
),
|
|
3859
|
+
);
|
|
3698
3860
|
|
|
3699
|
-
|
|
3861
|
+
try {
|
|
3862
|
+
fs.unlinkSync( zipPath );
|
|
3863
|
+
} catch ( cleanupErr ) {
|
|
3864
|
+
logger.error( { functionName: 'downloadInsertPdf', message: 'Failed to cleanup tmp file', error: cleanupErr } );
|
|
3700
3865
|
}
|
|
3701
|
-
// }
|
|
3702
3866
|
}
|
|
3703
3867
|
} finally {
|
|
3704
3868
|
await browser.close();
|
package/src/hbs/autoEmail.hbs
CHANGED
|
@@ -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
|
|
185
|
-
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;">
|
|
186
|
-
<tr>
|
|
187
|
-
<td
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
</
|
|
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
|
|
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">
|
|
@@ -38,6 +38,7 @@ internalTraxRouter
|
|
|
38
38
|
.post( '/posblock', isAllowedInternalAPIHandler, internalController.getStoreTaskDetails )
|
|
39
39
|
.post( '/runAIFlag', isAllowedInternalAPIHandler, internalController.runAIFlag )
|
|
40
40
|
.post( '/downloadInsertPdf', isAllowedInternalAPIHandler, internalController.downloadInsertPdf )
|
|
41
|
+
.post( '/checklistAutoMailList', isAllowedInternalAPIHandler, internalController.checklistAutoMailList )
|
|
41
42
|
;
|
|
42
43
|
|
|
43
44
|
|