tango-app-api-trax 3.7.98 → 3.8.0

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.98",
3
+ "version": "3.8.0",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  "nodemon": "^3.1.4",
30
30
  "path": "^0.12.7",
31
31
  "puppeteer": "^24.39.1",
32
- "tango-api-schema": "^2.5.70",
32
+ "tango-api-schema": "^2.5.72",
33
33
  "tango-app-api-middleware": "^3.5.2",
34
34
  "url": "^0.11.4",
35
35
  "winston": "^3.13.1",
@@ -322,7 +322,7 @@ export async function PCLconfigCreation( req, res ) {
322
322
  },
323
323
  } );
324
324
  let getSections = await CLquestions.aggregate( sectionQuery );
325
- if ( getSections.length || [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'boxalert', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert' ].includes( getCLconfig.checkListType ) ) {
325
+ if ( getSections.length || [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'boxalert', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'storehygienemonitoring' ].includes( getCLconfig.checkListType ) ) {
326
326
  if ( getSections.length ) {
327
327
  for ( let element3 of getSections ) {
328
328
  let collectQuestions = {};
@@ -4042,7 +4042,7 @@ async function LamdaServiceCall( url, data ) {
4042
4042
 
4043
4043
  export async function sendAIEmailList( req, res ) {
4044
4044
  try {
4045
- let spocList;
4045
+ let spocList = [];
4046
4046
  let result = [];
4047
4047
  let getChecklistDetails = await CLconfig.findOne( { client_id: req.body.clientId, publish: true, checkListName: req.body.checklistName }, { aiConfig: 1 } );
4048
4048
  if ( getChecklistDetails && getChecklistDetails?.aiConfig?.alertConfig?.alertSendTo?.enabled && getChecklistDetails?.aiConfig?.alertConfig?.alertSendTo?.email?.type?.length ) {
@@ -4131,6 +4131,7 @@ export async function sendAIEmailList( req, res ) {
4131
4131
  stores = Array.from( storeIds );
4132
4132
  }
4133
4133
  let data = {
4134
+ id: getChecklistDetails?._id,
4134
4135
  email: email,
4135
4136
  stores,
4136
4137
  role: userDetails.role,
@@ -4145,6 +4146,7 @@ export async function sendAIEmailList( req, res ) {
4145
4146
  result[findIndex].stores = new Set( ...result[findIndex].stores, ele.store );
4146
4147
  } else {
4147
4148
  result.push( {
4149
+ id: getChecklistDetails?._id,
4148
4150
  email: ele.email,
4149
4151
  stores: ele.store,
4150
4152
  role: 'admin',
@@ -610,6 +610,7 @@ export const zoneList = async ( req, res ) => {
610
610
  'Queue Wait Time Breach',
611
611
  'Uniform detection',
612
612
  'Store Hygiene Monitoring',
613
+ 'Camera Infra Compliance',
613
614
  ];
614
615
  const allowedChecklistsStreams = [
615
616
  'Camera Angle Change Compliance',
@@ -1,244 +1,244 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8"/>
5
- <meta name="viewport" content="width=device-width,initial-scale=1"/>
6
- <title>{{checklistTitle}}</title>
7
- <style>
8
- *{box-sizing:border-box;margin:0;padding:0}
9
- body{font-family:Arial,Helvetica,sans-serif;background:#fff;padding:0}
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
- /* Cover — match brand PDF (cyan title, right geometry, grey footer rule) */
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;bottom:0;left:auto;width:450px;pointer-events:none;z-index:0;}
15
- .cover-brand{position:absolute;top:48px;left:48px;display:flex;align-items:center;gap:10px;z-index:1}
16
- .cover-brand-name{font-size:22px;font-weight:600;color:#1a1a1a;letter-spacing:.02em;text-transform:lowercase}
17
- .cover-title-block{position:absolute;top:188px;left:48px;max-width:400px;z-index:1}
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
- .cover-meta-block{position:absolute;top:418px;left:48px;z-index:1}
20
- .cover-ref{font-size:22px;font-weight:700;color:#1a1a1a;letter-spacing:.02em}
21
- .cover-datetime{font-size:15px;color:#1a1a1a;font-weight:400;margin-top:10px}
22
- .cover-summary{position:absolute;top:548px;left:48px;width:400px;display:flex;flex-direction:column;gap:16px;z-index:1}
23
- .cover-sum-row{display:grid;grid-template-columns:182px 14px 1fr;column-gap:4px;align-items:baseline;font-size:16px;line-height:1.35}
24
- .cover-sum-label{color:#1a1a1a;font-weight:400;text-align:left}
25
- .cover-sum-colon{color:#1a1a1a;font-weight:400;text-align:center}
26
- .cover-sum-val{color:#000;font-weight:700}
27
- .cover-footer{position:absolute;bottom:36px;left:48px;right:48px;display:flex;justify-content:space-between;align-items:center;border-top:1px solid #d9d9d9;padding-top:14px;z-index:1}
28
- .cover-footer-page{font-size:11px;color:#999}
29
- .cover-footer-gen{display:flex;align-items:center;gap:8px;font-size:12px;color:#00AEEF;font-weight:600}
30
- /* Score page */
31
- .score-page{margin-bottom:40px;padding-bottom:20px;border-bottom:1px solid #e0e0e0}
32
- .score-hero{text-align:center;margin-bottom:36px}
33
- .score-pct{font-size:72px;font-weight:700;color:#00AEEF;line-height:1}
34
- .score-sub{font-size:18px;color:#444;margin-top:8px}
35
- .score-date{font-size:14px;color:#888;margin-top:4px}
36
- .section-title{font-size:16px;font-weight:700;color:#1a1a2e;margin-bottom:30px;padding-bottom:10px;border-bottom:2px solid #00AEEF}
37
- .history-bars{display:flex;align-items:flex-end;gap:8px;height:160px;margin-bottom:36px}
38
- .bar-wrap{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px}
39
- .bar{width:100%;background:#00AEEF;border-radius:4px 4px 0 0;display:flex;align-items:flex-start;justify-content:center}
40
- .bar-pct{font-size:10px;color:#fff;padding-top:4px;font-weight:600}
41
- .bar-date{font-size:9px;color:#888;text-align:center;white-space:nowrap}
42
- .bar-axis{width:100%;height:1px;background:#ddd;margin-bottom:8px}
43
- /* Section wise table */
44
- .sw-table{width:100%;border-collapse:collapse;font-size:14px}
45
- .sw-table th{background:#f0f7ff;color:#1a1a2e;font-weight:600;padding:10px 14px;text-align:left}
46
- .sw-table td{padding:10px 14px;border-bottom:1px solid #f0f0f0;color:#444}
47
- .sw-table tr:last-child td{border-bottom:none}
48
- .pct-pill{display:inline-block;padding:2px 10px;border-radius:20px;font-size:12px;font-weight:600}
49
- .pct-hi{background:#e1f5ee;color:#0f6e56}
50
- .pct-mid{background:#faeeda;color:#854f0b}
51
- .pct-lo{background:#fcebeb;color:#a32d2d}
52
- /* Detail pages */
53
- .detail-page{padding:32px 40px}
54
- .q-row{break-inside:auto}
55
- .q-answer-item{break-inside:auto}
56
- .dp-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;padding-bottom:12px;border-bottom:2px solid #00AEEF}
57
- .dp-header h2{font-size:18px;font-weight:700;color:#1a1a2e}
58
- .dp-score{font-size:15px;font-weight:700;color:#000000}
59
- .q-row{display:flex;gap:12px;margin-bottom:14px;padding:12px;border-radius:8px;background:transparent}
60
- .q-num{font-size:12px;font-weight:700;color:#000;min-width:22px}
61
- .q-body{flex:1}
62
- .q-text{font-size:13px;color:#444;margin-bottom:4px}
63
- .q-ans{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:600;padding:2px 8px;border-radius:12px}
64
- .ans-yes{background:#e1f5ee;color:#0f6e56}
65
- .ans-no{background:#fcebeb;color:#a32d2d}
66
- .flag-badge{background:#faeeda;color:#854f0b;font-size:10px;font-weight:600;padding:1px 6px;border-radius:8px;margin-left:6px}
67
- .q-answer-list{margin-top:6px}
68
- .q-answer-item{margin-top:6px;padding:0;background:transparent;border:none;border-radius:0}
69
- .q-answer-text{font-size:12px;color:#1a1a1a;line-height:1.5}
70
- .q-answer-text.flagged{color:#a32d2d}
71
- .q-answer-media{margin-top:8px}
72
- .q-answer-media img,.q-answer-media video{display:block;max-width:220px;max-height:180px;border-radius:6px}
73
- .q-answer-link{font-size:12px;color:#0085D2;text-decoration:underline;word-break:break-all}
74
- .q-answer-caption{font-size:11px;color:#666;margin-bottom:4px}
75
- .q-answer-remarks{font-size:11px;color:#666;margin-top:6px}
76
- /* Footer */
77
- .page-footer{position:absolute;bottom:20px;left:40px;right:40px;display:flex;justify-content:space-between;align-items:center;font-size:11px;color:#999;border-top:1px solid #d9d9d9;padding-top:10px}
78
- .footer-brand{display:flex;align-items:center;gap:8px;font-weight:600;color:#0066CC;font-size:11px}
79
- .footer-gen-by{color:#666;font-weight:400}
80
- .cover-footer-gen .footer-gen-by{color:#1a1a1a}
81
- .footer-tangoye-logo{display:block;flex-shrink:0}
82
- @page{size:A4;margin:5mm 0mm 15mm 0mm}
83
- @page:first{margin:0}
84
- </style>
85
- </head>
86
- <body>
87
-
88
- {{!-- PAGE 1: COVER --}}
89
- <div class="page cover-wrap">
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" />
96
- </svg>
97
-
98
- <div class="cover-brand">
99
- <img src="{{brandLogo}}" width="100px" height="100px" />
100
- </div>
101
-
102
- <div class="cover-title-block">
103
- <div class="cover-title-line1">{{#if titleLine1}}{{titleLine1}}{{else}}{{checklistType}}{{/if}}</div>
104
- </div>
105
-
106
- <div class="cover-meta-block">
107
- <div class="cover-ref">{{referenceId}}</div>
108
- <div class="cover-datetime">{{date}} | {{time}}</div>
109
- </div>
110
-
111
- <div class="cover-summary">
112
- <div class="cover-sum-row"><span class="cover-sum-label">No. of questions</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{numQuestions}}</span></div>
113
- <div class="cover-sum-row"><span class="cover-sum-label">No. of flags</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{numFlags}}</span></div>
114
- <div class="cover-sum-row"><span class="cover-sum-label">AI Breached</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{aiBreached}}</span></div>
115
- <div class="cover-sum-row"><span class="cover-sum-label">Submitted By</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{submittedBy}}</span></div>
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>
117
- </div>
118
-
119
- {{!-- <div class="cover-footer">
120
- <span class="cover-footer-page"></span>
121
- <div class="cover-footer-gen">
122
- <span class="footer-gen-by">Generated by</span>
123
- {{> tangoyeFooterLogo gid="cover"}}
124
- </div>
125
- </div> --}}
126
- </div>
127
-
128
- {{!-- SCORE SUMMARY + DETAIL (continuous flow) --}}
129
- <div class="detail-page">
130
- {{#if hasCompliancePage}}
131
- <div class="score-page">
132
- <div class="score-hero">
133
- <div class="score-pct">{{totalPercentage}}%</div>
134
- <div class="score-sub">Total: <strong>{{totalScore}}</strong> out of <strong>{{maxScore}}</strong></div>
135
- <div class="score-date">{{reportDate}}</div>
136
- </div>
137
-
138
- {{#if historyData}}
139
- <div class="section-title">History — Last 7 Days</div>
140
- <div style="display:flex;flex-direction:column;margin-bottom:36px">
141
- <div class="history-bars">
142
- {{#each historyData}}
143
- <div class="bar-wrap">
144
- <div class="bar" style="height:{{this.barHeight}}px"><span class="bar-pct">{{this.value}}%</span></div>
145
- <span class="bar-date">{{this.date}}</span>
146
- </div>
147
- {{/each}}
148
- </div>
149
- <div class="bar-axis"></div>
150
- </div>
151
- {{/if}}
152
-
153
- <div class="section-title">Section Wise Insights</div>
154
- <table class="sw-table">
155
- <thead><tr><th>Sections</th><th>Target Score</th><th>Actual Scrore</th><th>%</th></tr></thead>
156
- <tbody>
157
- {{#each sectionInsights}}
158
- <tr>
159
- <td>{{this.sectionName}}</td>
160
- <td>{{this.targetScore}}</td>
161
- <td>{{this.actualScore}}</td>
162
- <td>{{this.percentage}}%</td>
163
- </tr>
164
- {{/each}}
165
- </tbody>
166
- </table>
167
- </div>
168
- {{/if}}
169
- {{#each sections}}
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>
171
- <div class="sec-questions">
172
- {{#each this.questions}}
173
- <div class="q-row">
174
- <span class="q-num">{{this.qno}}</span>
175
- <div class="q-body">
176
- <div class="q-text" style="display:flex;justify-content:space-between">
177
- <div>{{this.qname}}</div>
178
- {{#if this.compliance}}
179
- <div>Score:{{this.score}}</div>
180
- {{/if}}
181
- </div>
182
- {{!-- <span class="q-ans {{#if this.isYes}}ans-yes{{else}}{{#if this.isNo}}ans-no{{/if}}{{/if}}">{{#if this.isYes}}✓ Yes{{else}}{{#if this.isNo}}✗ No{{else}}{{this.answerDisplay}}{{/if}}{{/if}}</span> --}}
183
- {{#if this.userAnswer.length}}
184
- <div class="q-answer-list">
185
- {{#each this.userAnswer}}
186
- <div class="q-answer-item">
187
- {{#if this.answer}}
188
- {{#eq this.answerType 'image'}}
189
- <div class="q-answer-media">
190
- {{#if this.referenceImage}}
191
- <div class="q-answer-caption">Reference Image</div>
192
- <img src="{{this.referenceImage}}" alt="Reference Image" />
193
- {{/if}}
194
- <div class="q-answer-caption">Uploaded Image</div>
195
- <img src="{{this.answer}}" alt="Uploaded Image" />
196
- </div>
197
- {{/eq}}
198
- {{#eq this.answerType 'video'}}
199
- <div class="q-answer-media">
200
- <div class="q-answer-caption">Uploaded Video</div>
201
- <a class="q-answer-link" href="{{this.answer}}" target="_blank">{{this.answer}}</a>
202
- </div>
203
- {{/eq}}
204
- {{#eq this.answerType 'text'}}
205
- <div class="q-answer-text {{#if this.sopFlag}}flagged{{/if}}">{{this.answer}}</div>
206
- {{/eq}}
207
- {{/if}}
208
-
209
- {{#if this.validation}}
210
- {{#if this.validationAnswer}}
211
- {{#eq this.validationDisplayType 'image'}}
212
- <div class="q-answer-media">
213
- <div class="q-answer-caption">Validation Image</div>
214
- <img src="{{this.validationAnswer}}" alt="Validation Image" />
215
- </div>
216
- {{/eq}}
217
- {{#eq this.validationDisplayType 'video'}}
218
- <div class="q-answer-media">
219
- <div class="q-answer-caption">Validation Video</div>
220
- <a class="q-answer-link" href="{{this.validationAnswer}}" target="_blank">{{this.validationAnswer}}</a>
221
- </div>
222
- {{/eq}}
223
- {{#eq this.validationDisplayType 'text'}}
224
- <div class="q-answer-text">{{this.validationAnswer}}</div>
225
- {{/eq}}
226
- {{/if}}
227
- {{/if}}
228
-
229
- {{#if this.remarks}}
230
- <div class="q-answer-remarks">Remarks: {{this.remarks}}</div>
231
- {{/if}}
232
- </div>
233
- {{/each}}
234
- </div>
235
- {{/if}}
236
- </div>
237
- </div>
238
- {{/each}}
239
- </div>
240
- {{/each}}
241
- </div>
242
-
243
- </body>
244
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8"/>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
6
+ <title>{{checklistTitle}}</title>
7
+ <style>
8
+ *{box-sizing:border-box;margin:0;padding:0}
9
+ body{font-family:Arial,Helvetica,sans-serif;background:#fff;padding:0}
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
+ /* Cover — match brand PDF (cyan title, right geometry, grey footer rule) */
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;bottom:0;left:auto;width:450px;pointer-events:none;z-index:0;}
15
+ .cover-brand{position:absolute;top:48px;left:48px;display:flex;align-items:center;gap:10px;z-index:1}
16
+ .cover-brand-name{font-size:22px;font-weight:600;color:#1a1a1a;letter-spacing:.02em;text-transform:lowercase}
17
+ .cover-title-block{position:absolute;top:188px;left:48px;max-width:400px;z-index:1}
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
+ .cover-meta-block{position:absolute;top:418px;left:48px;z-index:1}
20
+ .cover-ref{font-size:22px;font-weight:700;color:#1a1a1a;letter-spacing:.02em}
21
+ .cover-datetime{font-size:15px;color:#1a1a1a;font-weight:400;margin-top:10px}
22
+ .cover-summary{position:absolute;top:548px;left:48px;width:400px;display:flex;flex-direction:column;gap:16px;z-index:1}
23
+ .cover-sum-row{display:grid;grid-template-columns:182px 14px 1fr;column-gap:4px;align-items:baseline;font-size:16px;line-height:1.35}
24
+ .cover-sum-label{color:#1a1a1a;font-weight:400;text-align:left}
25
+ .cover-sum-colon{color:#1a1a1a;font-weight:400;text-align:center}
26
+ .cover-sum-val{color:#000;font-weight:700}
27
+ .cover-footer{position:absolute;bottom:36px;left:48px;right:48px;display:flex;justify-content:space-between;align-items:center;border-top:1px solid #d9d9d9;padding-top:14px;z-index:1}
28
+ .cover-footer-page{font-size:11px;color:#999}
29
+ .cover-footer-gen{display:flex;align-items:center;gap:8px;font-size:12px;color:#00AEEF;font-weight:600}
30
+ /* Score page */
31
+ .score-page{margin-bottom:40px;padding-bottom:20px;border-bottom:1px solid #e0e0e0}
32
+ .score-hero{text-align:center;margin-bottom:36px}
33
+ .score-pct{font-size:72px;font-weight:700;color:#00AEEF;line-height:1}
34
+ .score-sub{font-size:18px;color:#444;margin-top:8px}
35
+ .score-date{font-size:14px;color:#888;margin-top:4px}
36
+ .section-title{font-size:16px;font-weight:700;color:#1a1a2e;margin-bottom:30px;padding-bottom:10px;border-bottom:2px solid #00AEEF}
37
+ .history-bars{display:flex;align-items:flex-end;gap:8px;height:160px;margin-bottom:36px}
38
+ .bar-wrap{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px}
39
+ .bar{width:100%;background:#00AEEF;border-radius:4px 4px 0 0;display:flex;align-items:flex-start;justify-content:center}
40
+ .bar-pct{font-size:10px;color:#fff;padding-top:4px;font-weight:600}
41
+ .bar-date{font-size:9px;color:#888;text-align:center;white-space:nowrap}
42
+ .bar-axis{width:100%;height:1px;background:#ddd;margin-bottom:8px}
43
+ /* Section wise table */
44
+ .sw-table{width:100%;border-collapse:collapse;font-size:14px}
45
+ .sw-table th{background:#f0f7ff;color:#1a1a2e;font-weight:600;padding:10px 14px;text-align:left}
46
+ .sw-table td{padding:10px 14px;border-bottom:1px solid #f0f0f0;color:#444}
47
+ .sw-table tr:last-child td{border-bottom:none}
48
+ .pct-pill{display:inline-block;padding:2px 10px;border-radius:20px;font-size:12px;font-weight:600}
49
+ .pct-hi{background:#e1f5ee;color:#0f6e56}
50
+ .pct-mid{background:#faeeda;color:#854f0b}
51
+ .pct-lo{background:#fcebeb;color:#a32d2d}
52
+ /* Detail pages */
53
+ .detail-page{padding:32px 40px}
54
+ .q-row{break-inside:auto}
55
+ .q-answer-item{break-inside:auto}
56
+ .dp-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;padding-bottom:12px;border-bottom:2px solid #00AEEF}
57
+ .dp-header h2{font-size:18px;font-weight:700;color:#1a1a2e}
58
+ .dp-score{font-size:15px;font-weight:700;color:#000000}
59
+ .q-row{display:flex;gap:12px;margin-bottom:14px;padding:12px;border-radius:8px;background:transparent}
60
+ .q-num{font-size:12px;font-weight:700;color:#000;min-width:22px}
61
+ .q-body{flex:1}
62
+ .q-text{font-size:13px;color:#444;margin-bottom:4px}
63
+ .q-ans{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:600;padding:2px 8px;border-radius:12px}
64
+ .ans-yes{background:#e1f5ee;color:#0f6e56}
65
+ .ans-no{background:#fcebeb;color:#a32d2d}
66
+ .flag-badge{background:#faeeda;color:#854f0b;font-size:10px;font-weight:600;padding:1px 6px;border-radius:8px;margin-left:6px}
67
+ .q-answer-list{margin-top:6px}
68
+ .q-answer-item{margin-top:6px;padding:0;background:transparent;border:none;border-radius:0}
69
+ .q-answer-text{font-size:12px;color:#1a1a1a;line-height:1.5}
70
+ .q-answer-text.flagged{color:#a32d2d}
71
+ .q-answer-media{margin-top:8px}
72
+ .q-answer-media img,.q-answer-media video{display:block;max-width:220px;max-height:180px;border-radius:6px}
73
+ .q-answer-link{font-size:12px;color:#0085D2;text-decoration:underline;word-break:break-all}
74
+ .q-answer-caption{font-size:11px;color:#666;margin-bottom:4px}
75
+ .q-answer-remarks{font-size:11px;color:#666;margin-top:6px}
76
+ /* Footer */
77
+ .page-footer{position:absolute;bottom:20px;left:40px;right:40px;display:flex;justify-content:space-between;align-items:center;font-size:11px;color:#999;border-top:1px solid #d9d9d9;padding-top:10px}
78
+ .footer-brand{display:flex;align-items:center;gap:8px;font-weight:600;color:#0066CC;font-size:11px}
79
+ .footer-gen-by{color:#666;font-weight:400}
80
+ .cover-footer-gen .footer-gen-by{color:#1a1a1a}
81
+ .footer-tangoye-logo{display:block;flex-shrink:0}
82
+ @page{size:A4;margin:5mm 0mm 15mm 0mm}
83
+ @page:first{margin:0}
84
+ </style>
85
+ </head>
86
+ <body>
87
+
88
+ {{!-- PAGE 1: COVER --}}
89
+ <div class="page cover-wrap">
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" />
96
+ </svg>
97
+
98
+ <div class="cover-brand">
99
+ <img src="{{brandLogo}}" width="100px" height="100px" />
100
+ </div>
101
+
102
+ <div class="cover-title-block">
103
+ <div class="cover-title-line1">{{#if titleLine1}}{{titleLine1}}{{else}}{{checklistType}}{{/if}}</div>
104
+ </div>
105
+
106
+ <div class="cover-meta-block">
107
+ <div class="cover-ref">{{referenceId}}</div>
108
+ <div class="cover-datetime">{{date}} | {{time}}</div>
109
+ </div>
110
+
111
+ <div class="cover-summary">
112
+ <div class="cover-sum-row"><span class="cover-sum-label">No. of questions</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{numQuestions}}</span></div>
113
+ <div class="cover-sum-row"><span class="cover-sum-label">No. of flags</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{numFlags}}</span></div>
114
+ <div class="cover-sum-row"><span class="cover-sum-label">AI Breached</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{aiBreached}}</span></div>
115
+ <div class="cover-sum-row"><span class="cover-sum-label">Submitted By</span><span class="cover-sum-colon">:</span><span class="cover-sum-val">{{submittedBy}}</span></div>
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>
117
+ </div>
118
+
119
+ {{!-- <div class="cover-footer">
120
+ <span class="cover-footer-page"></span>
121
+ <div class="cover-footer-gen">
122
+ <span class="footer-gen-by">Generated by</span>
123
+ {{> tangoyeFooterLogo gid="cover"}}
124
+ </div>
125
+ </div> --}}
126
+ </div>
127
+
128
+ {{!-- SCORE SUMMARY + DETAIL (continuous flow) --}}
129
+ <div class="detail-page">
130
+ {{#if hasCompliancePage}}
131
+ <div class="score-page">
132
+ <div class="score-hero">
133
+ <div class="score-pct">{{totalPercentage}}%</div>
134
+ <div class="score-sub">Total: <strong>{{totalScore}}</strong> out of <strong>{{maxScore}}</strong></div>
135
+ <div class="score-date">{{reportDate}}</div>
136
+ </div>
137
+
138
+ {{#if historyData}}
139
+ <div class="section-title">History — Last 7 Days</div>
140
+ <div style="display:flex;flex-direction:column;margin-bottom:36px">
141
+ <div class="history-bars">
142
+ {{#each historyData}}
143
+ <div class="bar-wrap">
144
+ <div class="bar" style="height:{{this.barHeight}}px"><span class="bar-pct">{{this.value}}%</span></div>
145
+ <span class="bar-date">{{this.date}}</span>
146
+ </div>
147
+ {{/each}}
148
+ </div>
149
+ <div class="bar-axis"></div>
150
+ </div>
151
+ {{/if}}
152
+
153
+ <div class="section-title">Section Wise Insights</div>
154
+ <table class="sw-table">
155
+ <thead><tr><th>Sections</th><th>Target Score</th><th>Actual Scrore</th><th>%</th></tr></thead>
156
+ <tbody>
157
+ {{#each sectionInsights}}
158
+ <tr>
159
+ <td>{{this.sectionName}}</td>
160
+ <td>{{this.targetScore}}</td>
161
+ <td>{{this.actualScore}}</td>
162
+ <td>{{this.percentage}}%</td>
163
+ </tr>
164
+ {{/each}}
165
+ </tbody>
166
+ </table>
167
+ </div>
168
+ {{/if}}
169
+ {{#each sections}}
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>
171
+ <div class="sec-questions">
172
+ {{#each this.questions}}
173
+ <div class="q-row">
174
+ <span class="q-num">{{this.qno}}</span>
175
+ <div class="q-body">
176
+ <div class="q-text" style="display:flex;justify-content:space-between">
177
+ <div>{{this.qname}}</div>
178
+ {{#if this.compliance}}
179
+ <div>Score:{{this.score}}</div>
180
+ {{/if}}
181
+ </div>
182
+ {{!-- <span class="q-ans {{#if this.isYes}}ans-yes{{else}}{{#if this.isNo}}ans-no{{/if}}{{/if}}">{{#if this.isYes}}✓ Yes{{else}}{{#if this.isNo}}✗ No{{else}}{{this.answerDisplay}}{{/if}}{{/if}}</span> --}}
183
+ {{#if this.userAnswer.length}}
184
+ <div class="q-answer-list">
185
+ {{#each this.userAnswer}}
186
+ <div class="q-answer-item">
187
+ {{#if this.answer}}
188
+ {{#eq this.answerType 'image'}}
189
+ <div class="q-answer-media">
190
+ {{#if this.referenceImage}}
191
+ <div class="q-answer-caption">Reference Image</div>
192
+ <img src="{{this.referenceImage}}" alt="Reference Image" />
193
+ {{/if}}
194
+ <div class="q-answer-caption">Uploaded Image</div>
195
+ <img src="{{this.answer}}" alt="Uploaded Image" />
196
+ </div>
197
+ {{/eq}}
198
+ {{#eq this.answerType 'video'}}
199
+ <div class="q-answer-media">
200
+ <div class="q-answer-caption">Uploaded Video</div>
201
+ <a class="q-answer-link" href="{{this.answer}}" target="_blank">{{this.answer}}</a>
202
+ </div>
203
+ {{/eq}}
204
+ {{#eq this.answerType 'text'}}
205
+ <div class="q-answer-text {{#if this.sopFlag}}flagged{{/if}}">{{this.answer}}</div>
206
+ {{/eq}}
207
+ {{/if}}
208
+
209
+ {{#if this.validation}}
210
+ {{#if this.validationAnswer}}
211
+ {{#eq this.validationDisplayType 'image'}}
212
+ <div class="q-answer-media">
213
+ <div class="q-answer-caption">Validation Image</div>
214
+ <img src="{{this.validationAnswer}}" alt="Validation Image" />
215
+ </div>
216
+ {{/eq}}
217
+ {{#eq this.validationDisplayType 'video'}}
218
+ <div class="q-answer-media">
219
+ <div class="q-answer-caption">Validation Video</div>
220
+ <a class="q-answer-link" href="{{this.validationAnswer}}" target="_blank">{{this.validationAnswer}}</a>
221
+ </div>
222
+ {{/eq}}
223
+ {{#eq this.validationDisplayType 'text'}}
224
+ <div class="q-answer-text">{{this.validationAnswer}}</div>
225
+ {{/eq}}
226
+ {{/if}}
227
+ {{/if}}
228
+
229
+ {{#if this.remarks}}
230
+ <div class="q-answer-remarks">Remarks: {{this.remarks}}</div>
231
+ {{/if}}
232
+ </div>
233
+ {{/each}}
234
+ </div>
235
+ {{/if}}
236
+ </div>
237
+ </div>
238
+ {{/each}}
239
+ </div>
240
+ {{/each}}
241
+ </div>
242
+
243
+ </body>
244
+ </html>