tango-app-api-trax 3.8.14 → 3.8.15

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.8.14",
3
+ "version": "3.8.15",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -96,6 +96,7 @@
96
96
  font-size: 10px !important;
97
97
  font-weight: 400;
98
98
  line-height: 28px;
99
+ white-space: pre-line;
99
100
  }
100
101
 
101
102
  .text-center {
@@ -69,10 +69,10 @@
69
69
  .q-answer-text{font-size:12px;color:#1a1a1a;line-height:1.5}
70
70
  .q-answer-text.flagged{color:#a32d2d}
71
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}
72
+ .q-answer-media img,.q-answer-media video,.q-answer-item td img{display:block;width:200px;height:180px;object-fit:cover;border-radius:6px;margin-bottom:6px}
73
73
  .q-answer-link{font-size:12px;color:#0085D2;text-decoration:underline;word-break:break-all}
74
74
  .q-answer-caption{font-size:11px;color:#666;margin-bottom:4px}
75
- .q-answer-remarks{font-size:11px;color:#666;margin-top:6px}
75
+ .q-answer-remarks{font-size:11px;color:#666;margin-top:6px;white-space:pre-line}
76
76
  /* Footer */
77
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
78
  .footer-brand{display:flex;align-items:center;gap:8px;font-weight:600;color:#0066CC;font-size:11px}
@@ -179,60 +179,99 @@
179
179
  <div>Score:{{this.score}}</div>
180
180
  {{/if}}
181
181
  </div>
182
+ {{#if this.questionReferenceImage}}
183
+ <div class="q-answer-media">
184
+ <div class="q-answer-caption">Reference Image</div>
185
+ <img src="{{this.questionReferenceImage}}" alt="Reference Image" />
186
+ </div>
187
+ {{/if}}
188
+ {{#if this.multiQuestionReferenceImage.length}}
189
+ <div class="q-answer-media">
190
+ <div class="q-answer-caption">Reference Images</div>
191
+ {{#each this.multiQuestionReferenceImage}}
192
+ <img src="{{this}}" alt="Reference Image" />
193
+ {{/each}}
194
+ </div>
195
+ {{/if}}
182
196
  {{!-- <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
197
  {{#if this.userAnswer.length}}
184
198
  <div class="q-answer-list">
185
199
  {{#each this.userAnswer}}
186
200
  <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'}}
201
+ {{#eq this.answerType 'text'}}
202
+ {{#if this.answer}}
203
+ <div class="q-answer-text {{#if this.sopFlag}}flagged{{/if}}">{{this.answer}}</div>
204
+ {{/if}}
205
+ {{/eq}}
206
+ {{#eq this.answerType 'video'}}
207
+ {{#if this.answer}}
199
208
  <div class="q-answer-media">
200
209
  <div class="q-answer-caption">Uploaded Video</div>
201
210
  <a class="q-answer-link" href="{{this.answer}}" target="_blank">{{this.answer}}</a>
202
211
  </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
212
  {{/if}}
227
- {{/if}}
213
+ {{/eq}}
228
214
 
229
- {{#if this.remarks}}
230
- <div class="q-answer-remarks">Remarks: {{this.remarks}}</div>
231
- {{/if}}
215
+ <table style="width:100%;margin-top:8px"><tr>
216
+ {{#if this.multiReferenceImage.length}}
217
+ <td style="width:50%;vertical-align:top;padding-right:8px">
218
+ <div class="q-answer-caption">Reference Image</div>
219
+ {{#each this.multiReferenceImage}}
220
+ <img src="{{this}}" alt="Reference Image" />
221
+ {{/each}}
222
+ </td>
223
+ {{/if}}
224
+ {{#unless this.multiReferenceImage.length}}
225
+ {{#if this.referenceImage}}
226
+ <td style="width:50%;vertical-align:top;padding-right:8px">
227
+ <div class="q-answer-caption">Reference Image</div>
228
+ <img src="{{this.referenceImage}}" alt="Reference Image" />
229
+ </td>
230
+ {{/if}}
231
+ {{/unless}}
232
+ <td style="vertical-align:top">
233
+ {{#eq this.answerType 'image'}}
234
+ {{#if this.answer}}
235
+ <div class="q-answer-caption">Uploaded Image</div>
236
+ <img src="{{this.answer}}" alt="Uploaded Image" />
237
+ {{/if}}
238
+ {{/eq}}
239
+ {{#if this.validation}}
240
+ {{#eq this.validationDisplayType 'image'}}
241
+ {{#if this.validationAnswer}}
242
+ <div class="q-answer-caption">Validation Image</div>
243
+ <img src="{{this.validationAnswer}}" alt="Validation Image" />
244
+ {{/if}}
245
+ {{/eq}}
246
+ {{#eq this.validationDisplayType 'video'}}
247
+ {{#if this.validationAnswer}}
248
+ <div class="q-answer-caption">Validation Video</div>
249
+ <a class="q-answer-link" href="{{this.validationAnswer}}" target="_blank">{{this.validationAnswer}}</a>
250
+ {{/if}}
251
+ {{/eq}}
252
+ {{#eq this.validationDisplayType 'text'}}
253
+ {{#if this.validationAnswer}}
254
+ <div class="q-answer-text">{{this.validationAnswer}}</div>
255
+ {{/if}}
256
+ {{/eq}}
257
+ {{#eq this.validationDisplayType 'multiImage'}}
258
+ {{#if this.validationImage.length}}
259
+ <div class="q-answer-caption">Validation Image</div>
260
+ {{#each this.validationImage}}
261
+ <img src="{{this}}" alt="Validation Image" />
262
+ {{/each}}
263
+ {{/if}}
264
+ {{/eq}}
265
+ {{/if}}
266
+ </td>
267
+ </tr></table>
232
268
  </div>
233
269
  {{/each}}
234
270
  </div>
235
271
  {{/if}}
272
+ {{#if this.remarks}}
273
+ <div class="q-answer-remarks">Remarks: {{this.remarks}}</div>
274
+ {{/if}}
236
275
  </div>
237
276
  </div>
238
277
  {{/each}}
@@ -129,9 +129,23 @@ function getMediaDisplayType( questionAnswerType, userAnswer = {} ) {
129
129
  function getValidationDisplayType( validationType ) {
130
130
  if ( validationType === 'Capture Image' ) return 'image';
131
131
  if ( validationType === 'Capture Video' ) return 'video';
132
+ if ( validationType === 'Capture Multiple Image with description' ) return 'multiImage';
132
133
  return 'text';
133
134
  }
134
135
 
136
+ function flattenImageRefs( arr ) {
137
+ if ( !Array.isArray( arr ) ) return [];
138
+ return arr
139
+ .map( ( ele ) => {
140
+ if ( typeof ele === 'string' ) return ele;
141
+ if ( ele && typeof ele === 'object' ) {
142
+ return ele.imageURL || ele.imageUrl || ele.url || ele.image || ele.path || '';
143
+ }
144
+ return '';
145
+ } )
146
+ .filter( Boolean );
147
+ }
148
+
135
149
  function buildQuestionAnswerEntries( question ) {
136
150
  const rawUserAnswers = getSourceUserAnswers( question );
137
151
  const uniqueUserAnswers = [];
@@ -153,15 +167,29 @@ function buildQuestionAnswerEntries( question ) {
153
167
  const validationType = matchedAnswer?.validationType || userAnswer?.validationType || '';
154
168
  const validationAnswer = matchedAnswer?.validationAnswer || userAnswer?.validationAnswer || '';
155
169
 
170
+ const rawMultiRefSources = [
171
+ userAnswer?.multiReferenceImage,
172
+ matchedAnswer?.multiReferenceImage,
173
+ question?.answers?.[0]?.multiReferenceImage,
174
+ ];
175
+ let multiReferenceImage = [];
176
+ for ( const src of rawMultiRefSources ) {
177
+ const flat = flattenImageRefs( src );
178
+ if ( flat.length ) { multiReferenceImage = flat; break; }
179
+ }
180
+ const validationImage = flattenImageRefs( userAnswer?.validationImage );
181
+
156
182
  return {
157
183
  answer: userAnswer?.answer || '',
158
184
  answerType: getMediaDisplayType( question?.answerType, userAnswer ),
159
185
  referenceImage: userAnswer?.referenceImage || matchedAnswer?.referenceImage || '',
186
+ multiReferenceImage,
160
187
  remarks: userAnswer?.remarks || '',
161
188
  sopFlag: userAnswer?.sopFlag ?? matchedAnswer?.sopFlag ?? false,
162
189
  validation,
163
190
  validationType,
164
191
  validationAnswer,
192
+ validationImage,
165
193
  validationDisplayType: getValidationDisplayType( validationType ),
166
194
  };
167
195
  } );
@@ -276,6 +304,10 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
276
304
 
277
305
  compliance: Boolean( q.compliance ),
278
306
 
307
+ questionReferenceImage: q.questionReferenceImage || '',
308
+
309
+ multiQuestionReferenceImage: flattenImageRefs( q.multiQuestionReferenceImage ),
310
+
279
311
  isYes: isYes || ( !isNo && score >= 10 ),
280
312
 
281
313
  isNo,
@@ -750,14 +782,27 @@ export function createImageCache() {
750
782
 
751
783
  const collectFromSection = ( section ) => {
752
784
  section.questions?.forEach( ( q ) => {
785
+ if ( q.questionReferenceImage && q.questionReferenceImage.startsWith( 'http' ) ) urls.add( q.questionReferenceImage );
786
+ q.multiQuestionReferenceImage?.forEach( ( u ) => {
787
+ if ( typeof u === 'string' && u.startsWith( 'http' ) ) urls.add( u );
788
+ } );
753
789
  q.userAnswer?.forEach( ( ua ) => {
754
790
  if ( ua.referenceImage && ua.referenceImage.startsWith( 'http' ) ) urls.add( ua.referenceImage );
755
791
 
792
+ ua.multiReferenceImage?.forEach( ( u ) => {
793
+ if ( typeof u === 'string' && u.startsWith( 'http' ) ) urls.add( u );
794
+ } );
756
795
 
757
796
  if ( ua.answer && ua.answerType === 'image' && ua.answer.startsWith( 'http' ) ) urls.add( ua.answer );
758
797
 
759
798
 
760
799
  if ( ua.validationAnswer && ua.validationDisplayType === 'image' && ua.validationAnswer.startsWith( 'http' ) ) urls.add( ua.validationAnswer );
800
+
801
+ if ( ua.validationDisplayType === 'multiImage' ) {
802
+ ua.validationImage?.forEach( ( u ) => {
803
+ if ( typeof u === 'string' && u.startsWith( 'http' ) ) urls.add( u );
804
+ } );
805
+ }
761
806
  } );
762
807
  } );
763
808
  };
@@ -797,14 +842,25 @@ export function createImageCache() {
797
842
 
798
843
  const replaceInSection = ( section ) => {
799
844
  section.questions?.forEach( ( q ) => {
845
+ if ( q.questionReferenceImage && cache.has( q.questionReferenceImage ) ) q.questionReferenceImage = cache.get( q.questionReferenceImage );
846
+ if ( q.multiQuestionReferenceImage?.length ) {
847
+ q.multiQuestionReferenceImage = q.multiQuestionReferenceImage.map( ( u ) => ( cache.has( u ) ? cache.get( u ) : u ) );
848
+ }
800
849
  q.userAnswer?.forEach( ( ua ) => {
801
850
  if ( ua.referenceImage && cache.has( ua.referenceImage ) ) ua.referenceImage = cache.get( ua.referenceImage );
802
851
 
852
+ if ( ua.multiReferenceImage?.length ) {
853
+ ua.multiReferenceImage = ua.multiReferenceImage.map( ( u ) => ( cache.has( u ) ? cache.get( u ) : u ) );
854
+ }
803
855
 
804
856
  if ( ua.answer && ua.answerType === 'image' && cache.has( ua.answer ) ) ua.answer = cache.get( ua.answer );
805
857
 
806
858
 
807
859
  if ( ua.validationAnswer && ua.validationDisplayType === 'image' && cache.has( ua.validationAnswer ) ) ua.validationAnswer = cache.get( ua.validationAnswer );
860
+
861
+ if ( ua.validationDisplayType === 'multiImage' && ua.validationImage?.length ) {
862
+ ua.validationImage = ua.validationImage.map( ( u ) => ( cache.has( u ) ? cache.get( u ) : u ) );
863
+ }
808
864
  } );
809
865
  } );
810
866
  };
@@ -859,7 +915,15 @@ export function resolveTemplateUrls( templateData, baseUrl = 'https://d1r0hc2ssk
859
915
 
860
916
  const resolveQuestionMedia = ( section ) => {
861
917
  section.questions?.forEach( ( q ) => {
918
+ if ( q.questionReferenceImage ) q.questionReferenceImage = resolveUrl( q.questionReferenceImage );
919
+ if ( q.multiQuestionReferenceImage?.length ) {
920
+ q.multiQuestionReferenceImage = q.multiQuestionReferenceImage.map( ( ele ) => resolveUrl( ele ) );
921
+ }
862
922
  q.userAnswer?.forEach( ( ua ) => {
923
+
924
+ if ( ua.multiReferenceImage?.length ) {
925
+ ua.multiReferenceImage = ua.multiReferenceImage.map( ( ele ) => resolveUrl( ele ) );
926
+ }
863
927
  if ( ua.referenceImage ) ua.referenceImage = resolveUrl( ua.referenceImage );
864
928
 
865
929
 
@@ -867,6 +931,10 @@ export function resolveTemplateUrls( templateData, baseUrl = 'https://d1r0hc2ssk
867
931
  ua.validationAnswer = resolveUrl( ua.validationAnswer );
868
932
  }
869
933
 
934
+ if ( ua?.validationImage?.length ) {
935
+ ua.validationImage = ua.validationImage.map( ( ele ) => resolveUrl( ele ) );
936
+ }
937
+
870
938
 
871
939
  if ( ua.answer && ua.answerType !== 'text' ) {
872
940
  ua.answer = resolveUrl( ua.answer );
@@ -982,13 +1050,25 @@ export async function generateVisitChecklistPDF( templateData, baseUrl = 'https:
982
1050
 
983
1051
  const resolveQuestionMedia = ( section ) => {
984
1052
  section.questions?.forEach( ( q ) => {
1053
+ if ( q.questionReferenceImage ) q.questionReferenceImage = resolveUrl( q.questionReferenceImage );
1054
+ if ( q.multiQuestionReferenceImage?.length ) {
1055
+ q.multiQuestionReferenceImage = q.multiQuestionReferenceImage.map( ( ele ) => resolveUrl( ele ) );
1056
+ }
985
1057
  q.userAnswer?.forEach( ( ua ) => {
986
1058
  if ( ua.referenceImage ) ua.referenceImage = resolveUrl( ua.referenceImage );
987
1059
 
1060
+ if ( ua.multiReferenceImage?.length ) {
1061
+ ua.multiReferenceImage = ua.multiReferenceImage.map( ( ele ) => resolveUrl( ele ) );
1062
+ }
1063
+
988
1064
  if ( ua.validationAnswer && ua.validationDisplayType !== 'text' ) {
989
1065
  ua.validationAnswer = resolveUrl( ua.validationAnswer );
990
1066
  }
991
1067
 
1068
+ if ( ua.validationImage?.length ) {
1069
+ ua.validationImage = ua.validationImage.map( ( ele ) => resolveUrl( ele ) );
1070
+ }
1071
+
992
1072
  if ( ua.answer && ua.answerType !== 'text' ) {
993
1073
  ua.answer = resolveUrl( ua.answer );
994
1074
  }