tango-app-api-trax 3.7.93 → 3.7.94
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
|
@@ -33,6 +33,13 @@ import puppeteer from 'puppeteer';
|
|
|
33
33
|
import { getBrowser as getBrowserInstance } from '../utils/browserPool.utils.js';
|
|
34
34
|
import fs from 'fs';
|
|
35
35
|
import path from 'path';
|
|
36
|
+
import { fileURLToPath as toPath } from 'url';
|
|
37
|
+
|
|
38
|
+
const __ctrlDir = path.dirname( toPath( import.meta.url ) );
|
|
39
|
+
const tangoyeLogoSvg = fs.readFileSync(
|
|
40
|
+
path.join( __ctrlDir, '../hbs/partials/tangoye-footer-logo.hbs' ),
|
|
41
|
+
'utf8',
|
|
42
|
+
).replaceAll( '{{gid}}', 'ft' );
|
|
36
43
|
|
|
37
44
|
|
|
38
45
|
const ObjectId = mongoose.Types.ObjectId;
|
|
@@ -3546,7 +3553,23 @@ export const downloadInsertPdf = async ( req, res ) => {
|
|
|
3546
3553
|
pdfBuffer = await page.pdf( {
|
|
3547
3554
|
format: 'A4',
|
|
3548
3555
|
printBackground: true,
|
|
3549
|
-
|
|
3556
|
+
preferCSSPageSize: true,
|
|
3557
|
+
displayHeaderFooter: true,
|
|
3558
|
+
headerTemplate: '<span></span>',
|
|
3559
|
+
footerTemplate: [
|
|
3560
|
+
'<div style="width:100%;padding:0 10mm;font-size:10px;',
|
|
3561
|
+
'font-family:Arial,sans-serif;display:flex;',
|
|
3562
|
+
'justify-content:space-between;align-items:center;',
|
|
3563
|
+
'border-top:1px solid #d9d9d9;padding-top:4px;">',
|
|
3564
|
+
'<span style="color:#999;">Page ',
|
|
3565
|
+
'<span class="pageNumber"></span>',
|
|
3566
|
+
' of <span class="totalPages"></span></span>',
|
|
3567
|
+
'<span style="display:flex;align-items:center;gap:6px;">',
|
|
3568
|
+
'<span style="color:#666;font-weight:400;font-size:10px;">',
|
|
3569
|
+
'Generated by</span>',
|
|
3570
|
+
tangoyeLogoSvg,
|
|
3571
|
+
'</span></div>',
|
|
3572
|
+
].join( '' ),
|
|
3550
3573
|
} );
|
|
3551
3574
|
} catch ( err ) {
|
|
3552
3575
|
logger.error( { functionName: 'downloadInsertPdf', message: 'PDF generation failed', error: err } );
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
<style>
|
|
8
8
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
9
9
|
body{font-family:Arial,Helvetica,sans-serif;background:#fff;padding:0}
|
|
10
|
-
.page{width:794px;
|
|
11
|
-
.cover-wrap{position:relative;width:794px;height:
|
|
10
|
+
.page{width:794px;background:#fff;padding:0;overflow:hidden;position:relative;page-break-after:always}
|
|
11
|
+
.cover-wrap{position:relative;width:794px;height:284mm;background:#fff;overflow:hidden;font-family:Arial,Helvetica,sans-serif}
|
|
12
12
|
/* Cover — match brand PDF (cyan title, right geometry, grey footer rule) */
|
|
13
13
|
/* Right-edge artwork: flush right; height 1043px leaves ~80px for footer band on 1123px cover */
|
|
14
|
-
.cover-deco{position:absolute;top:0;right:0;left:auto;width:
|
|
14
|
+
.cover-deco{position:absolute;top:0;right:0;bottom:0;left:auto;width:450px;pointer-events:none;z-index:0;}
|
|
15
15
|
.cover-brand{position:absolute;top:48px;left:48px;display:flex;align-items:center;gap:10px;z-index:1}
|
|
16
16
|
.cover-brand-name{font-size:22px;font-weight:600;color:#1a1a1a;letter-spacing:.02em;text-transform:lowercase}
|
|
17
17
|
.cover-title-block{position:absolute;top:188px;left:48px;max-width:440px;z-index:1}
|
|
18
|
-
.cover-title-line1,.cover-title-line2{font-size:
|
|
18
|
+
.cover-title-line1,.cover-title-line2{font-size:40px;font-weight:600;color:#00AEEF;line-height:1.15;letter-spacing:-.5px;word-wrap:break-word;overflow-wrap:break-word}
|
|
19
19
|
.cover-meta-block{position:absolute;top:418px;left:48px;z-index:1}
|
|
20
20
|
.cover-ref{font-size:22px;font-weight:700;color:#1a1a1a;letter-spacing:.02em}
|
|
21
21
|
.cover-datetime{font-size:15px;color:#1a1a1a;font-weight:400;margin-top:10px}
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
.cover-footer-page{font-size:11px;color:#999}
|
|
29
29
|
.cover-footer-gen{display:flex;align-items:center;gap:8px;font-size:12px;color:#00AEEF;font-weight:600}
|
|
30
30
|
/* Score page */
|
|
31
|
-
.score-page{padding:
|
|
31
|
+
.score-page{margin-bottom:40px;padding-bottom:20px;border-bottom:1px solid #e0e0e0}
|
|
32
32
|
.score-hero{text-align:center;margin-bottom:36px}
|
|
33
33
|
.score-pct{font-size:72px;font-weight:700;color:#00AEEF;line-height:1}
|
|
34
34
|
.score-sub{font-size:18px;color:#444;margin-top:8px}
|
|
35
35
|
.score-date{font-size:14px;color:#888;margin-top:4px}
|
|
36
|
-
.section-title{font-size:16px;font-weight:700;color:#1a1a2e;margin-bottom:
|
|
36
|
+
.section-title{font-size:16px;font-weight:700;color:#1a1a2e;margin-bottom:30px;padding-bottom:10px;border-bottom:2px solid #00AEEF}
|
|
37
37
|
.history-bars{display:flex;align-items:flex-end;gap:8px;height:160px;margin-bottom:36px}
|
|
38
38
|
.bar-wrap{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px}
|
|
39
39
|
.bar{width:100%;background:#00AEEF;border-radius:4px 4px 0 0;display:flex;align-items:flex-start;justify-content:center}
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
.pct-mid{background:#faeeda;color:#854f0b}
|
|
51
51
|
.pct-lo{background:#fcebeb;color:#a32d2d}
|
|
52
52
|
/* Detail pages */
|
|
53
|
-
.detail-page{padding:32px 40px
|
|
54
|
-
.q-row{break-inside:
|
|
55
|
-
.q-answer-item{break-inside:
|
|
53
|
+
.detail-page{padding:32px 40px}
|
|
54
|
+
.q-row{break-inside:auto}
|
|
55
|
+
.q-answer-item{break-inside:auto}
|
|
56
56
|
.dp-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;padding-bottom:12px;border-bottom:2px solid #00AEEF}
|
|
57
57
|
.dp-header h2{font-size:18px;font-weight:700;color:#1a1a2e}
|
|
58
58
|
.dp-score{font-size:15px;font-weight:700;color:#000000}
|
|
@@ -79,19 +79,20 @@
|
|
|
79
79
|
.footer-gen-by{color:#666;font-weight:400}
|
|
80
80
|
.cover-footer-gen .footer-gen-by{color:#1a1a1a}
|
|
81
81
|
.footer-tangoye-logo{display:block;flex-shrink:0}
|
|
82
|
-
@page{size:A4;margin:
|
|
82
|
+
@page{size:A4;margin:5mm 0mm 15mm 0mm}
|
|
83
|
+
@page:first{margin:0}
|
|
83
84
|
</style>
|
|
84
85
|
</head>
|
|
85
86
|
<body>
|
|
86
87
|
|
|
87
88
|
{{!-- PAGE 1: COVER --}}
|
|
88
89
|
<div class="page cover-wrap">
|
|
89
|
-
<svg
|
|
90
|
-
<path opacity="0.8" d="
|
|
91
|
-
<path opacity="0.7" d="
|
|
92
|
-
<path opacity="0.7" d="
|
|
93
|
-
<path opacity="0.7" d="
|
|
94
|
-
<path opacity="0.6" d="
|
|
90
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="cover-deco" viewBox="0 0 650 2000" fill="none">
|
|
91
|
+
<path opacity="0.8" d="M711.5 2000V1391.92L568.063 693.586L313.062 2000H711.5Z" fill="#99DAFF" />
|
|
92
|
+
<path opacity="0.7" d="M92.2148 0.0078125H131.706L711.501 484.677V902.622L92.2148 0.0078125Z" fill="#51C1FF" />
|
|
93
|
+
<path opacity="0.7" d="M0 0.00959645L92.203 0L568.392 694.051L486.082 1114.03L0 0.00959645Z" fill="#99DAFF" />
|
|
94
|
+
<path opacity="0.7" d="M711.5 856.786L568.062 693.586L711.5 1391.92V856.786Z" fill="#6BCAFF" />
|
|
95
|
+
<path opacity="0.6" d="M711.501 522.574L92.2148 0.0078125H711.501V522.574Z" fill="#009BF3" />
|
|
95
96
|
</svg>
|
|
96
97
|
|
|
97
98
|
<div class="cover-brand">
|
|
@@ -115,25 +116,25 @@
|
|
|
115
116
|
<div class="cover-sum-row"><span class="cover-sum-label">Country</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{country}}</span></div>
|
|
116
117
|
</div>
|
|
117
118
|
|
|
118
|
-
<div class="cover-footer">
|
|
119
|
+
{{!-- <div class="cover-footer">
|
|
119
120
|
<span class="cover-footer-page"></span>
|
|
120
121
|
<div class="cover-footer-gen">
|
|
121
122
|
<span class="footer-gen-by">Generated by</span>
|
|
122
123
|
{{> tangoyeFooterLogo gid="cover"}}
|
|
123
124
|
</div>
|
|
124
|
-
</div>
|
|
125
|
+
</div> --}}
|
|
125
126
|
</div>
|
|
126
127
|
|
|
127
|
-
{{!--
|
|
128
|
+
{{!-- SCORE SUMMARY + DETAIL (continuous flow) --}}
|
|
129
|
+
<div class="detail-page">
|
|
128
130
|
{{#if hasCompliancePage}}
|
|
129
|
-
<div class="page">
|
|
130
131
|
<div class="score-page">
|
|
131
132
|
<div class="score-hero">
|
|
132
133
|
<div class="score-pct">{{totalPercentage}}%</div>
|
|
133
134
|
<div class="score-sub">Total: <strong>{{totalScore}}</strong> out of <strong>{{maxScore}}</strong></div>
|
|
134
135
|
<div class="score-date">{{reportDate}}</div>
|
|
135
136
|
</div>
|
|
136
|
-
|
|
137
|
+
|
|
137
138
|
{{#if historyData}}
|
|
138
139
|
<div class="section-title">History — Last 7 Days</div>
|
|
139
140
|
<div style="display:flex;flex-direction:column;margin-bottom:36px">
|
|
@@ -148,7 +149,7 @@
|
|
|
148
149
|
<div class="bar-axis"></div>
|
|
149
150
|
</div>
|
|
150
151
|
{{/if}}
|
|
151
|
-
|
|
152
|
+
|
|
152
153
|
<div class="section-title">Section Wise Insights</div>
|
|
153
154
|
<table class="sw-table">
|
|
154
155
|
<thead><tr><th>Sections</th><th>Target Score</th><th>Actual Scrore</th><th>%</th></tr></thead>
|
|
@@ -164,18 +165,8 @@
|
|
|
164
165
|
</tbody>
|
|
165
166
|
</table>
|
|
166
167
|
</div>
|
|
167
|
-
{{!-- <div class="page-footer">
|
|
168
|
-
<span>Page 2 of {{totalPages}}</span>
|
|
169
|
-
<span class="footer-brand"><span class="footer-gen-by">Generated by</span>{{> tangoyeFooterLogo gid="p2"}}</span>
|
|
170
|
-
</div>
|
|
171
|
-
</div> --}}
|
|
172
168
|
{{/if}}
|
|
173
|
-
|
|
174
|
-
{{!-- PAGES 3+: DETAIL - Question sections --}}
|
|
175
|
-
{{#each detailPageGroups}}
|
|
176
|
-
<div class="page">
|
|
177
|
-
<div class="detail-page">
|
|
178
|
-
{{#each this.sections}}
|
|
169
|
+
{{#each sections}}
|
|
179
170
|
<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
171
|
<div class="sec-questions">
|
|
181
172
|
{{#each this.questions}}
|
|
@@ -247,22 +238,7 @@
|
|
|
247
238
|
{{/each}}
|
|
248
239
|
</div>
|
|
249
240
|
{{/each}}
|
|
250
|
-
|
|
251
|
-
{{!-- {{#if this.isLastGroup}}
|
|
252
|
-
{{#each ../flags}}
|
|
253
|
-
<div style="margin-top:12px;padding:12px;background:#faeeda;border-radius:8px;border-left:4px solid #ef9f27">
|
|
254
|
-
<strong style="font-size:13px;color:#854f0b">⚠ Flag: {{this.sectionName}} — {{this.qname}}</strong>
|
|
255
|
-
<p style="font-size:12px;color:#854f0b;margin-top:4px">Q{{this.qno}} ({{this.sectionName}}): "{{this.qname}}" — Answered: <strong>{{this.answer}}</strong>. Action required.</p>
|
|
256
|
-
</div>
|
|
257
|
-
{{/each}}
|
|
258
|
-
{{/if}} --}}
|
|
259
|
-
</div>
|
|
260
|
-
{{!-- <div class="page-footer">
|
|
261
|
-
<span>Page {{this.pageNumber}} of {{../totalPages}}</span>
|
|
262
|
-
<span class="footer-brand"><span class="footer-gen-by">Generated by</span>{{> tangoyeFooterLogo gid=(strConcat 'd' @index)}}</span>
|
|
263
|
-
</div> --}}
|
|
264
241
|
</div>
|
|
265
|
-
{{/each}}
|
|
266
242
|
|
|
267
243
|
</body>
|
|
268
244
|
</html>
|
|
@@ -214,7 +214,19 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
|
|
|
214
214
|
const max = q.compliance ? Math.max( ...q?.answers.map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatchedCount ?? 0 ) ) ) : 0;
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
let score = q.compliance ? Math.max( ...q?.userAnswer?.map( ( o ) => o?.complianceScore ?? 0 ) ) : 0;
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if ( q.answerType == 'image' && q.compliance && q.userAnswer?.[0]?.runAIData ) {
|
|
221
|
+
let find = q.userAnswer?.[0]?.runAIData?.find( ( run ) => run?.featureName == 'Matched/Not Matched' );
|
|
222
|
+
if ( find ) {
|
|
223
|
+
if ( find?.value == 'True' ) {
|
|
224
|
+
score = q?.answers?.[0]?.matchedCount;
|
|
225
|
+
} else {
|
|
226
|
+
score = q?.answers?.[0]?.notMatchedCount;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
218
230
|
|
|
219
231
|
|
|
220
232
|
sectionScore += score;
|
|
@@ -491,6 +503,8 @@ export function buildVisitChecklistTemplateDataFromProcessed( processedDoc, bran
|
|
|
491
503
|
|
|
492
504
|
detailPageGroups,
|
|
493
505
|
|
|
506
|
+
sections: detailPageGroups.flatMap( ( g ) => g.sections ),
|
|
507
|
+
|
|
494
508
|
flags,
|
|
495
509
|
|
|
496
510
|
};
|
|
@@ -629,6 +643,8 @@ function buildFromViewChecklistApi( getchecklistData, viewchecklistData, brandIn
|
|
|
629
643
|
|
|
630
644
|
detailPageGroups,
|
|
631
645
|
|
|
646
|
+
sections: detailPageGroups.flatMap( ( g ) => g.sections ),
|
|
647
|
+
|
|
632
648
|
flags,
|
|
633
649
|
|
|
634
650
|
complianceCount: hasCompliancePage,
|
|
@@ -753,6 +769,8 @@ export function createImageCache() {
|
|
|
753
769
|
group.sections?.forEach( collectFromSection );
|
|
754
770
|
} );
|
|
755
771
|
|
|
772
|
+
resolvedData.sections?.forEach( collectFromSection );
|
|
773
|
+
|
|
756
774
|
|
|
757
775
|
// Fetch all unique URLs in parallel (max 20 concurrent)
|
|
758
776
|
|
|
@@ -798,6 +816,8 @@ export function createImageCache() {
|
|
|
798
816
|
group.sections?.forEach( replaceInSection );
|
|
799
817
|
} );
|
|
800
818
|
|
|
819
|
+
resolvedData.sections?.forEach( replaceInSection );
|
|
820
|
+
|
|
801
821
|
|
|
802
822
|
return resolvedData;
|
|
803
823
|
}
|
|
@@ -862,6 +882,8 @@ export function resolveTemplateUrls( templateData, baseUrl = 'https://d1r0hc2ssk
|
|
|
862
882
|
group.sections?.forEach( resolveQuestionMedia );
|
|
863
883
|
} );
|
|
864
884
|
|
|
885
|
+
resolvedData.sections?.forEach( resolveQuestionMedia );
|
|
886
|
+
|
|
865
887
|
|
|
866
888
|
if ( resolvedData.brandLogo && !resolvedData.brandLogo.startsWith( 'http' ) ) {
|
|
867
889
|
resolvedData.brandLogo = resolveUrl( resolvedData.brandLogo );
|
|
@@ -977,6 +999,7 @@ export async function generateVisitChecklistPDF( templateData, baseUrl = 'https:
|
|
|
977
999
|
resolvedData.detailPageGroups?.forEach( ( group ) => {
|
|
978
1000
|
group.sections?.forEach( resolveQuestionMedia );
|
|
979
1001
|
} );
|
|
1002
|
+
resolvedData.sections?.forEach( resolveQuestionMedia );
|
|
980
1003
|
|
|
981
1004
|
if ( resolvedData.brandLogo && !resolvedData.brandLogo.startsWith( 'http' ) ) {
|
|
982
1005
|
resolvedData.brandLogo = resolveUrl( resolvedData.brandLogo );
|