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.
|
|
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.
|
|
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',
|
|
@@ -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>
|