@cellaware/utils 8.11.17 → 8.11.19

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.
Files changed (63) hide show
  1. package/package.json +1 -1
  2. package/dist/azure/cosmos.d.ts +0 -112
  3. package/dist/azure/cosmos.js +0 -305
  4. package/dist/azure/email.d.ts +0 -3
  5. package/dist/azure/email.js +0 -20
  6. package/dist/azure/function.d.ts +0 -14
  7. package/dist/azure/function.js +0 -124
  8. package/dist/azure/slot.d.ts +0 -1
  9. package/dist/azure/slot.js +0 -4
  10. package/dist/azure/storage.d.ts +0 -14
  11. package/dist/azure/storage.js +0 -81
  12. package/dist/chatwms/alert.d.ts +0 -97
  13. package/dist/chatwms/alert.js +0 -74
  14. package/dist/chatwms/azure/cosmos.d.ts +0 -25
  15. package/dist/chatwms/azure/cosmos.js +0 -43
  16. package/dist/chatwms/azure/function.d.ts +0 -21
  17. package/dist/chatwms/azure/function.js +0 -29
  18. package/dist/chatwms/azure/storage.d.ts +0 -15
  19. package/dist/chatwms/azure/storage.js +0 -27
  20. package/dist/chatwms/client.d.ts +0 -18
  21. package/dist/chatwms/client.js +0 -48
  22. package/dist/chatwms/cosmos.d.ts +0 -24
  23. package/dist/chatwms/cosmos.js +0 -532
  24. package/dist/chatwms/dashboard.d.ts +0 -80
  25. package/dist/chatwms/dashboard.js +0 -17
  26. package/dist/chatwms/datagrid.d.ts +0 -212
  27. package/dist/chatwms/datagrid.js +0 -1402
  28. package/dist/chatwms/developer.d.ts +0 -27
  29. package/dist/chatwms/developer.js +0 -12
  30. package/dist/chatwms/github/issue.d.ts +0 -1
  31. package/dist/chatwms/github/issue.js +0 -4
  32. package/dist/chatwms/instance.d.ts +0 -16
  33. package/dist/chatwms/instance.js +0 -18
  34. package/dist/chatwms/integration.d.ts +0 -24
  35. package/dist/chatwms/integration.js +0 -19
  36. package/dist/chatwms/pdf.d.ts +0 -95
  37. package/dist/chatwms/pdf.js +0 -147
  38. package/dist/chatwms/report.d.ts +0 -126
  39. package/dist/chatwms/report.js +0 -55
  40. package/dist/chatwms/response.d.ts +0 -18
  41. package/dist/chatwms/response.js +0 -25
  42. package/dist/chatwms/search.d.ts +0 -12
  43. package/dist/chatwms/search.js +0 -9
  44. package/dist/chatwms/teams.d.ts +0 -237
  45. package/dist/chatwms/teams.js +0 -205
  46. package/dist/chatwms/user.d.ts +0 -31
  47. package/dist/chatwms/user.js +0 -42
  48. package/dist/chatwms/warehouse.d.ts +0 -3
  49. package/dist/chatwms/warehouse.js +0 -3
  50. package/dist/github/issue.d.ts +0 -1
  51. package/dist/github/issue.js +0 -23
  52. package/dist/llm/chain-store.d.ts +0 -49
  53. package/dist/llm/chain-store.js +0 -284
  54. package/dist/llm/cost.d.ts +0 -3
  55. package/dist/llm/cost.js +0 -42
  56. package/dist/llm/model.d.ts +0 -12
  57. package/dist/llm/model.js +0 -1
  58. package/dist/stopwatch.d.ts +0 -8
  59. package/dist/stopwatch.js +0 -36
  60. package/dist/util.d.ts +0 -45
  61. package/dist/util.js +0 -288
  62. package/dist/version.d.ts +0 -4
  63. package/dist/version.js +0 -12
@@ -1,1402 +0,0 @@
1
- import { isDateString, truncateValue } from "../util.js";
2
- import { CHATWMS_DEFAULT_LANGUAGE } from "./user.js";
3
- export const DATAGRID_HTML_ROWS = 1000;
4
- export const DATAGRID_HTML_COLS = 8;
5
- export const DATAGRID_TEAMS_ROWS = 24;
6
- export function initDatagridState() {
7
- return {
8
- adjRows: []
9
- };
10
- }
11
- export function initVisualDatagridState() {
12
- return {
13
- ...initDatagridState(),
14
- chartRows: [],
15
- html: '',
16
- teamsRows: [],
17
- htmlTranspose: '',
18
- teamsTranspose: []
19
- };
20
- }
21
- export const DEFAULT_VALUE_FORMAT_VALUE = 'none';
22
- export function evaluateValueFormat(colFmt, value, locale) {
23
- // NOTE: this syntax checks for both undefined and null.
24
- if (value == null) {
25
- return value;
26
- }
27
- const fmt = colFmt.valueFormat;
28
- if (!fmt) {
29
- return value;
30
- }
31
- try {
32
- switch (colFmt.type) {
33
- case 'text':
34
- return formatText(value, fmt);
35
- case 'number':
36
- return formatNumber(value, fmt);
37
- case 'date':
38
- return formatDate(value, fmt, locale);
39
- default:
40
- return value;
41
- }
42
- }
43
- catch {
44
- return value;
45
- }
46
- }
47
- function formatTextEnabled(fmt) {
48
- return fmt.txtCaseVal !== DEFAULT_VALUE_FORMAT_VALUE;
49
- }
50
- function formatText(value, fmt) {
51
- if (!formatTextEnabled(fmt)) {
52
- return value;
53
- }
54
- const str = String(value);
55
- switch (fmt.txtCaseVal) {
56
- case 'lower': return str.toLowerCase();
57
- case 'upper': return str.toUpperCase();
58
- case 'title':
59
- return str
60
- .toLowerCase()
61
- .split(' ')
62
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
63
- .join(' ');
64
- default:
65
- return value;
66
- }
67
- }
68
- export function formatNumberEnabled(fmt) {
69
- return fmt.numCommaVal !== DEFAULT_VALUE_FORMAT_VALUE ||
70
- fmt.numCurrencyVal !== DEFAULT_VALUE_FORMAT_VALUE ||
71
- fmt.numPercentVal !== DEFAULT_VALUE_FORMAT_VALUE ||
72
- fmt.numRoundVal !== DEFAULT_VALUE_FORMAT_VALUE;
73
- }
74
- function formatNumber(value, fmt) {
75
- if (!formatNumberEnabled(fmt)) {
76
- return value;
77
- }
78
- let num = parseFloat(value);
79
- if (isNaN(num)) {
80
- return value;
81
- }
82
- /*
83
- We need to be intentional about the order of operations for number formatting.
84
- For example, some formatting operations will transform the number to a string.
85
- If we need to perform some arithmetic or round it, we need to do so first before
86
- number is transformed into a string.
87
- */
88
- if (fmt.numPercentVal === '100') {
89
- num *= 100;
90
- }
91
- const decimals = parseInt(fmt.numRoundVal ?? '', 10);
92
- if (!isNaN(decimals)) {
93
- num = parseFloat(num.toFixed(decimals));
94
- }
95
- let formatted = num;
96
- if (fmt.numCommaVal === 'on') {
97
- formatted = num.toLocaleString();
98
- }
99
- if (fmt.numCurrencyVal === 'dollar') {
100
- formatted = `$${formatted}`;
101
- }
102
- if (fmt.numPercentVal === '1' || fmt.numPercentVal === '100') {
103
- formatted = `${formatted}%`;
104
- }
105
- return formatted;
106
- }
107
- function formatDate(value, fmt, locale) {
108
- let valueStr;
109
- if (value instanceof Date) {
110
- valueStr = value.toISOString();
111
- }
112
- else {
113
- valueStr = String(value);
114
- }
115
- // Make sure we are dealing with a valid date string before we go any further.
116
- if (!isDateString(valueStr)) {
117
- return value;
118
- }
119
- // Remove time zone and milliseconds from date string.
120
- valueStr = valueStr.replace(/Z/i, '');
121
- valueStr = valueStr.replace(/\.\d+$/, '');
122
- // Initialize date options and functionality.
123
- const options = { hourCycle: 'h23' };
124
- let formatDateEnabled = false;
125
- const setOpt = (k, v) => {
126
- if (v !== DEFAULT_VALUE_FORMAT_VALUE) {
127
- options[k] = v;
128
- formatDateEnabled = true;
129
- }
130
- };
131
- setOpt('year', fmt.dteYearVal);
132
- setOpt('month', fmt.dteMonthVal);
133
- setOpt('day', fmt.dteDayVal);
134
- setOpt('weekday', fmt.dteWeekdayVal);
135
- setOpt('hour', fmt.dteHourVal);
136
- setOpt('minute', fmt.dteMinuteVal);
137
- setOpt('second', fmt.dteSecondVal);
138
- setOpt('hourCycle', fmt.dteHourcycleVal === 'h24' ? 'h23' : fmt.dteHourcycleVal);
139
- // Handle simple date edge cases.
140
- if (valueStr.length === 10) {
141
- // Special handling for ISO-ish short date string (YYYY-AA-BB or AA-BB-YYYY).
142
- const c4 = valueStr.at(4);
143
- const c5 = valueStr.at(5);
144
- if (c4 === '-' || c4 === '/' || c5 === '-' || c5 === '/') {
145
- valueStr += ' 00:00:00'; // Make sure time is zero'd out.
146
- if (!formatDateEnabled) {
147
- setOpt('day', '2-digit');
148
- setOpt('month', '2-digit');
149
- setOpt('year', 'numeric');
150
- }
151
- }
152
- }
153
- else if (valueStr.length === 8) {
154
- // Special handling for ISO-ish shorter date string (YY-MM-DD in any order).
155
- const c2 = valueStr.at(2);
156
- const c5 = valueStr.at(5);
157
- if ((c2 === '-' && c5 === '-') || (c2 === '/' && c5 === '/')) {
158
- valueStr += ' 00:00:00'; // Make sure time is zero'd out.
159
- if (!formatDateEnabled) {
160
- setOpt('day', '2-digit');
161
- setOpt('month', '2-digit');
162
- setOpt('year', '2-digit');
163
- }
164
- }
165
- }
166
- let dteStr = new Date(valueStr).toLocaleString(locale, options);
167
- // NOTE: by default, we should use slash as separator.
168
- if (fmt.dteSeparatorVal === DEFAULT_VALUE_FORMAT_VALUE) {
169
- dteStr = dteStr.replaceAll('-', '/');
170
- }
171
- else if (fmt.dteSeparatorVal === 'dash') {
172
- dteStr = dteStr.replaceAll('/', '-');
173
- }
174
- else if (fmt.dteSeparatorVal === 'slash') {
175
- dteStr = dteStr.replaceAll('-', '/');
176
- }
177
- dteStr = dteStr.replaceAll(',', '');
178
- // We want to preserve simple dates.
179
- // If no is formatting detected, the date's length should not be greater than the original value's.
180
- if (!formatDateEnabled) {
181
- dteStr = dteStr.substring(0, value.length);
182
- dteStr = dteStr.trim();
183
- // Trim any separator characters off.
184
- if (dteStr.endsWith('-') || dteStr.endsWith('/') || dteStr.endsWith(':') || dteStr.endsWith('.') || dteStr.endsWith('T')) {
185
- dteStr = dteStr.substring(0, dteStr.length - 1).trim();
186
- }
187
- }
188
- return dteStr;
189
- }
190
- export var ConditionalFormatRule;
191
- (function (ConditionalFormatRule) {
192
- // TEXT:
193
- ConditionalFormatRule["CONTAINS"] = "conditional-format-rule-contains";
194
- ConditionalFormatRule["DOES_NOT_CONTAIN"] = "conditional-format-rule-does-not-contain";
195
- ConditionalFormatRule["BEGINS_WITH"] = "conditional-format-rule-begins-with";
196
- ConditionalFormatRule["ENDS_WITH"] = "conditional-format-rule-ends-with";
197
- // NUMBER:
198
- ConditionalFormatRule["GREATER_THAN"] = "conditional-format-rule-greater-than";
199
- ConditionalFormatRule["GREATER_THAN_OR_EQUAL_TO"] = "conditional-format-rule-greater-than-or-equal-to";
200
- ConditionalFormatRule["LESS_THAN"] = "conditional-format-rule-less-than";
201
- ConditionalFormatRule["LESS_THAN_OR_EQUAL_TO"] = "conditional-format-rule-less-than-or-equal-to";
202
- ConditionalFormatRule["BETWEEN"] = "conditional-format-rule-between";
203
- // ANY:
204
- ConditionalFormatRule["EQUALS"] = "conditional-format-rule-equals";
205
- ConditionalFormatRule["DOES_NOT_EQUAL"] = "conditional-format-rule-does-not-equal";
206
- ConditionalFormatRule["BLANK"] = "conditional-format-rule-blank";
207
- ConditionalFormatRule["NOT_BLANK"] = "conditional-format-rule-not-blank";
208
- })(ConditionalFormatRule || (ConditionalFormatRule = {}));
209
- export const CONDITIONAL_FORMAT_RULES = [
210
- ConditionalFormatRule.EQUALS,
211
- ConditionalFormatRule.DOES_NOT_EQUAL,
212
- ConditionalFormatRule.BLANK,
213
- ConditionalFormatRule.NOT_BLANK,
214
- ConditionalFormatRule.CONTAINS,
215
- ConditionalFormatRule.DOES_NOT_CONTAIN,
216
- ConditionalFormatRule.BEGINS_WITH,
217
- ConditionalFormatRule.ENDS_WITH,
218
- ConditionalFormatRule.GREATER_THAN,
219
- ConditionalFormatRule.GREATER_THAN_OR_EQUAL_TO,
220
- ConditionalFormatRule.LESS_THAN,
221
- ConditionalFormatRule.LESS_THAN_OR_EQUAL_TO,
222
- ConditionalFormatRule.BETWEEN
223
- ];
224
- export const CONDITIONAL_FORMAT_RULES_TEXT = [
225
- ConditionalFormatRule.EQUALS,
226
- ConditionalFormatRule.DOES_NOT_EQUAL,
227
- ConditionalFormatRule.BLANK,
228
- ConditionalFormatRule.NOT_BLANK,
229
- ConditionalFormatRule.CONTAINS,
230
- ConditionalFormatRule.DOES_NOT_CONTAIN,
231
- ConditionalFormatRule.BEGINS_WITH,
232
- ConditionalFormatRule.ENDS_WITH
233
- ];
234
- export const CONDITIONAL_FORMAT_RULES_NUMBER = [
235
- ConditionalFormatRule.EQUALS,
236
- ConditionalFormatRule.DOES_NOT_EQUAL,
237
- ConditionalFormatRule.BLANK,
238
- ConditionalFormatRule.NOT_BLANK,
239
- ConditionalFormatRule.GREATER_THAN,
240
- ConditionalFormatRule.GREATER_THAN_OR_EQUAL_TO,
241
- ConditionalFormatRule.LESS_THAN,
242
- ConditionalFormatRule.LESS_THAN_OR_EQUAL_TO,
243
- ConditionalFormatRule.BETWEEN
244
- ];
245
- export const CONDITIONAL_FORMAT_RULES_BOOLEAN = [
246
- ConditionalFormatRule.EQUALS,
247
- ConditionalFormatRule.DOES_NOT_EQUAL,
248
- ConditionalFormatRule.BLANK,
249
- ConditionalFormatRule.NOT_BLANK
250
- ];
251
- export var ConditionalFormatStyle;
252
- (function (ConditionalFormatStyle) {
253
- ConditionalFormatStyle["NONE"] = "conditional-format-style-none";
254
- ConditionalFormatStyle["GREEN_BACKGROUND"] = "conditional-format-style-green-background";
255
- ConditionalFormatStyle["YELLOW_BACKGROUND"] = "conditional-format-style-yellow-background";
256
- ConditionalFormatStyle["RED_BACKGROUND"] = "conditional-format-style-red-background";
257
- ConditionalFormatStyle["BLUE_BACKGROUND"] = "conditional-format-style-blue-background";
258
- ConditionalFormatStyle["PURPLE_BACKGROUND"] = "conditional-format-style-purple-background";
259
- ConditionalFormatStyle["BOLD"] = "conditional-format-style-bold";
260
- ConditionalFormatStyle["GREEN_BOLD"] = "conditional-format-style-green-bold";
261
- ConditionalFormatStyle["YELLOW_BOLD"] = "conditional-format-style-yellow-bold";
262
- ConditionalFormatStyle["RED_BOLD"] = "conditional-format-style-red-bold";
263
- ConditionalFormatStyle["BLUE_BOLD"] = "conditional-format-style-blue-bold";
264
- ConditionalFormatStyle["PURPLE_BOLD"] = "conditional-format-style-purple-bold";
265
- ConditionalFormatStyle["NOT_IMPORTANT"] = "conditional-format-style-not-important";
266
- })(ConditionalFormatStyle || (ConditionalFormatStyle = {}));
267
- export function evaluateConditionalFormat(condFmt, dataType, value) {
268
- const isBlank = (v) => v === null || v === undefined || (dataType !== 'number' && dataType !== 'boolean' && v === '');
269
- const v1 = condFmt.value;
270
- const v2 = condFmt.value2;
271
- const ruleHandlers = {
272
- [ConditionalFormatRule.EQUALS]: () => value == v1,
273
- [ConditionalFormatRule.DOES_NOT_EQUAL]: () => value != v1,
274
- [ConditionalFormatRule.GREATER_THAN]: () => value > v1,
275
- [ConditionalFormatRule.GREATER_THAN_OR_EQUAL_TO]: () => value >= v1,
276
- [ConditionalFormatRule.LESS_THAN]: () => value < v1,
277
- [ConditionalFormatRule.LESS_THAN_OR_EQUAL_TO]: () => value <= v1,
278
- [ConditionalFormatRule.BETWEEN]: () => value >= v1 && value <= (v2 ?? 0),
279
- [ConditionalFormatRule.CONTAINS]: () => String(value).includes(v1),
280
- [ConditionalFormatRule.DOES_NOT_CONTAIN]: () => !String(value).includes(v1),
281
- [ConditionalFormatRule.BEGINS_WITH]: () => String(value).startsWith(v1),
282
- [ConditionalFormatRule.ENDS_WITH]: () => String(value).endsWith(v1),
283
- [ConditionalFormatRule.BLANK]: () => isBlank(value),
284
- [ConditionalFormatRule.NOT_BLANK]: () => !isBlank(value)
285
- };
286
- try {
287
- const fn = ruleHandlers[condFmt.rule];
288
- return fn ? fn() : false;
289
- }
290
- catch {
291
- return false;
292
- }
293
- }
294
- export function evaluateValueStyles(datagridState, columnName, value) {
295
- const styles = [];
296
- // Reminder: Value Formatting is performed **before** Conditional Formatting.
297
- // https://chatwms.io/user-manual/concepts/column-formatting#conditional-formatting
298
- const colFmt = datagridState.columnFormats?.find(colFmt => colFmt.displayName === columnName);
299
- if (!!colFmt) {
300
- for (const condFmt of colFmt.conditionalFormats.slice().reverse()) {
301
- // `conditionalFormats` has already been sorted by `sequence`.
302
- // NOTE: need to reverse the array for sequence to be correctly applied.
303
- // NOTE: using `slice` to make shallow copy of array since `reverse` does so in place.
304
- if (evaluateConditionalFormat(condFmt, colFmt.type, formatNumberEnabled(colFmt.valueFormat) ? stripNumericValueFormat(value) : value)) {
305
- styles.push(condFmt.style);
306
- }
307
- }
308
- }
309
- return styles;
310
- }
311
- export function initDatagridCondition() {
312
- return {
313
- columnName: '',
314
- dataType: '',
315
- rule: '',
316
- value: ''
317
- };
318
- }
319
- export async function summarizeCondition(condition, localeMessageFn) {
320
- let ruleStr = '';
321
- switch (condition.rule) {
322
- case ConditionalFormatRule.BETWEEN:
323
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} ${condition.value} ${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, 'conditional-format-rule-and')} ${condition.value2}`;
324
- break;
325
- case ConditionalFormatRule.BLANK:
326
- case ConditionalFormatRule.NOT_BLANK:
327
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)}`;
328
- break;
329
- default:
330
- if (condition.dataType === 'text') {
331
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} '${condition.value}'`;
332
- }
333
- else {
334
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} ${condition.value}`;
335
- }
336
- break;
337
- }
338
- return `${condition.columnName} ${ruleStr}`;
339
- }
340
- export async function summarizeConditionMarkdown(condition, localeMessageFn) {
341
- let ruleStr = '';
342
- switch (condition.rule) {
343
- case ConditionalFormatRule.BETWEEN:
344
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} ${condition.value} ${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, 'conditional-format-rule-and')} ${condition.value2}`;
345
- break;
346
- case ConditionalFormatRule.BLANK:
347
- case ConditionalFormatRule.NOT_BLANK:
348
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)}`;
349
- break;
350
- default:
351
- if (condition.dataType === 'text') {
352
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} '${condition.value}'`;
353
- }
354
- else {
355
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} ${condition.value}`;
356
- }
357
- break;
358
- }
359
- return `\`${condition.columnName}\` ${ruleStr}`;
360
- }
361
- export async function summarizeConditionHtml(condition, localeMessageFn) {
362
- let ruleStr = '';
363
- switch (condition.rule) {
364
- case ConditionalFormatRule.BETWEEN:
365
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} ${condition.value} ${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, 'conditional-format-rule-and')} ${condition.value2}`;
366
- break;
367
- case ConditionalFormatRule.BLANK:
368
- case ConditionalFormatRule.NOT_BLANK:
369
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)}`;
370
- break;
371
- default:
372
- if (condition.dataType === 'text') {
373
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} '${condition.value}'`;
374
- }
375
- else {
376
- ruleStr = `${await localeMessageFn(CHATWMS_DEFAULT_LANGUAGE, condition.rule)} ${condition.value}`;
377
- }
378
- break;
379
- }
380
- return `<code>${condition.columnName}</code> ${ruleStr}`;
381
- }
382
- export function codifyCondition(condition) {
383
- // NOTE: since we check for action/unsafe SQL with every query, we do not need to check for SQL injection here.
384
- if (condition.dataType === 'number') {
385
- const v1 = !condition.value || condition.value == '' ? 0 : condition.value;
386
- const v2 = !condition.value2 || condition.value2 == '' ? 0 : condition.value2;
387
- // Need to wrap in quotes due to how ChatWMS will alias columns (ex: location_id -> Location ID).
388
- const quotedColumnName = `"${condition.columnName}"`;
389
- switch (condition.rule) {
390
- case ConditionalFormatRule.EQUALS:
391
- return `${quotedColumnName} = ${v1}`;
392
- case ConditionalFormatRule.DOES_NOT_EQUAL:
393
- return `${quotedColumnName} != ${v1}`;
394
- case ConditionalFormatRule.GREATER_THAN:
395
- return `${quotedColumnName} > ${v1}`;
396
- case ConditionalFormatRule.GREATER_THAN_OR_EQUAL_TO:
397
- return `${quotedColumnName} >= ${v1}`;
398
- case ConditionalFormatRule.LESS_THAN:
399
- return `${quotedColumnName} < ${v1}`;
400
- case ConditionalFormatRule.LESS_THAN_OR_EQUAL_TO:
401
- return `${quotedColumnName} <= ${v1}`;
402
- case ConditionalFormatRule.BETWEEN:
403
- return `${quotedColumnName} BETWEEN ${v1} AND ${v2}`;
404
- case ConditionalFormatRule.BLANK:
405
- return `${quotedColumnName} IS NULL`;
406
- case ConditionalFormatRule.NOT_BLANK:
407
- return `${quotedColumnName} IS NOT NULL`;
408
- default:
409
- return '1 = 2'; // Fail if we get here.
410
- }
411
- }
412
- else if (condition.dataType === 'date') {
413
- const v1 = !condition.value || condition.value == '' ? 0 : condition.value;
414
- const v2 = !condition.value2 || condition.value2 == '' ? 0 : condition.value2;
415
- // Need to wrap in quotes due to how ChatWMS will alias columns (ex: location_id -> Location ID).
416
- const quotedColumnName = `"${condition.columnName}"`;
417
- switch (condition.rule) {
418
- case ConditionalFormatRule.EQUALS:
419
- return `${quotedColumnName} = '${v1}'`;
420
- case ConditionalFormatRule.DOES_NOT_EQUAL:
421
- return `${quotedColumnName} != '${v1}'`;
422
- case ConditionalFormatRule.GREATER_THAN:
423
- return `${quotedColumnName} > '${v1}'`;
424
- case ConditionalFormatRule.GREATER_THAN_OR_EQUAL_TO:
425
- return `${quotedColumnName} >= '${v1}'`;
426
- case ConditionalFormatRule.LESS_THAN:
427
- return `${quotedColumnName} < '${v1}'`;
428
- case ConditionalFormatRule.LESS_THAN_OR_EQUAL_TO:
429
- return `${quotedColumnName} <= '${v1}'`;
430
- case ConditionalFormatRule.BETWEEN:
431
- return `${quotedColumnName} BETWEEN '${v1}' AND '${v2}'`;
432
- case ConditionalFormatRule.BLANK:
433
- return `${quotedColumnName} IS NULL`;
434
- case ConditionalFormatRule.NOT_BLANK:
435
- return `${quotedColumnName} IS NOT NULL`;
436
- default:
437
- return '1 = 2'; // Fail if we get here.
438
- }
439
- }
440
- else {
441
- // NOTE: it is a safe assumption to trim any whitespace off the value.
442
- const v1 = (condition.value ?? '__DUMMY__').trim();
443
- // Need to wrap in quotes due to how ChatWMS will alias columns (ex: location_id -> Location ID).
444
- // NOTE: wrapping column and values in `UPPER` to support case insensitivity.
445
- const quotedColumnName = `UPPER("${condition.columnName}")`;
446
- switch (condition.rule) {
447
- case ConditionalFormatRule.EQUALS:
448
- return `${quotedColumnName} = UPPER('${v1}')`;
449
- case ConditionalFormatRule.DOES_NOT_EQUAL:
450
- return `${quotedColumnName} != UPPER('${v1}')`;
451
- case ConditionalFormatRule.CONTAINS:
452
- return `${quotedColumnName} LIKE UPPER('%${v1}%')`;
453
- case ConditionalFormatRule.DOES_NOT_CONTAIN:
454
- return `${quotedColumnName} NOT LIKE UPPER('%${v1}%')`;
455
- case ConditionalFormatRule.BEGINS_WITH:
456
- return `${quotedColumnName} LIKE UPPER('${v1}%')`;
457
- case ConditionalFormatRule.ENDS_WITH:
458
- return `${quotedColumnName} LIKE UPPER('%${v1}')`;
459
- case ConditionalFormatRule.BLANK:
460
- return `${quotedColumnName} IS NULL`;
461
- case ConditionalFormatRule.NOT_BLANK:
462
- return `${quotedColumnName} IS NOT NULL`;
463
- default:
464
- return '1 = 2'; // Fail if we get here.
465
- }
466
- }
467
- }
468
- /**
469
- * Equivalent of `parseNumeric` in front end
470
- *
471
- * https://github.com/cellaware/chatwms-az-swa-ng/blob/development/src/app/utils/data.ts#L126
472
- */
473
- export function stripNumericValueFormat(value) {
474
- // NOTE: this syntax checks for both undefined and null.
475
- if (value == null) {
476
- return null;
477
- }
478
- if (typeof value === 'number') {
479
- return value;
480
- }
481
- // Remove commas, dollar signs, percent signs
482
- const cleaned = value.replace(/[$,%]/g, '').replace(/,/g, '');
483
- // NOTE: not sure we need this based on how this function is used in these contexts...
484
- // const number = parseFloat(cleaned);
485
- // return isNaN(number) ? null : number;
486
- return cleaned;
487
- }
488
- function buildDatagridConditionEvaluator(condition) {
489
- const v1 = condition.value;
490
- const v2 = condition.value2;
491
- const isBlank = (v) => v === null || v === undefined || (condition.dataType !== 'number' && condition.dataType !== 'boolean' && v === '');
492
- if (condition.dataType === 'number') {
493
- switch (condition.rule) {
494
- case ConditionalFormatRule.EQUALS:
495
- return v => stripNumericValueFormat(v) == v1;
496
- case ConditionalFormatRule.DOES_NOT_EQUAL:
497
- return v => stripNumericValueFormat(v) != v1;
498
- case ConditionalFormatRule.GREATER_THAN:
499
- return v => stripNumericValueFormat(v) > v1;
500
- case ConditionalFormatRule.GREATER_THAN_OR_EQUAL_TO:
501
- return v => stripNumericValueFormat(v) >= v1;
502
- case ConditionalFormatRule.LESS_THAN:
503
- return v => stripNumericValueFormat(v) < v1;
504
- case ConditionalFormatRule.LESS_THAN_OR_EQUAL_TO:
505
- return v => stripNumericValueFormat(v) <= v1;
506
- case ConditionalFormatRule.BETWEEN:
507
- return v => stripNumericValueFormat(v) >= v1 && stripNumericValueFormat(v) <= (v2 ?? v1); // fallback to v1 if v2 missing
508
- case ConditionalFormatRule.BLANK:
509
- return v => isBlank(v);
510
- case ConditionalFormatRule.NOT_BLANK:
511
- return v => !isBlank(v);
512
- default:
513
- return () => false;
514
- }
515
- }
516
- else {
517
- switch (condition.rule) {
518
- case ConditionalFormatRule.EQUALS:
519
- return v => v == v1;
520
- case ConditionalFormatRule.DOES_NOT_EQUAL:
521
- return v => v != v1;
522
- case ConditionalFormatRule.CONTAINS:
523
- return v => typeof v === 'string' && v.includes(v1);
524
- case ConditionalFormatRule.DOES_NOT_CONTAIN:
525
- return v => typeof v === 'string' && !v.includes(v1);
526
- case ConditionalFormatRule.BEGINS_WITH:
527
- return v => typeof v === 'string' && v.startsWith(v1);
528
- case ConditionalFormatRule.ENDS_WITH:
529
- return v => typeof v === 'string' && v.endsWith(v1);
530
- case ConditionalFormatRule.BLANK:
531
- return v => isBlank(v);
532
- case ConditionalFormatRule.NOT_BLANK:
533
- return v => !isBlank(v);
534
- default:
535
- return () => false;
536
- }
537
- }
538
- }
539
- function buildPivotDatagridConditionEvaluator(condition, columnNames) {
540
- const fn = buildDatagridConditionEvaluator(condition);
541
- return row => columnNames.some(columnName => fn(row[columnName]));
542
- }
543
- function evaluateDatagridCondition(rows, condition) {
544
- const fn = buildDatagridConditionEvaluator(condition);
545
- return rows.filter(row => fn(row[condition.columnName]));
546
- }
547
- function evaluatePivotDatagridCondition(rows, condition, mappedColumnNames) {
548
- let columnNames = [condition.columnName];
549
- const mappedColumnNamesSet = mappedColumnNames.get(condition.columnName);
550
- if (!!mappedColumnNamesSet) {
551
- columnNames = Array.from(mappedColumnNamesSet);
552
- }
553
- const fn = buildPivotDatagridConditionEvaluator(condition, columnNames);
554
- return rows.filter(row => fn(row));
555
- }
556
- function parseColumnState(columnState) {
557
- const rowGroupCols = [];
558
- const pivotCols = [];
559
- const valueCols = [];
560
- const sortModel = [];
561
- for (const col of columnState) {
562
- // Row Group Columns:
563
- // NOTE: we only want the highest level data.
564
- if (col.rowGroup && col.rowGroupIndex === 0) {
565
- rowGroupCols.push({
566
- field: col.colId
567
- });
568
- }
569
- // Pivot Columns:
570
- // NOTE: we only want the highest level data.
571
- if (col.pivot && col.pivotIndex === 0) {
572
- pivotCols.push({
573
- field: col.colId
574
- });
575
- }
576
- // Value Columns (Aggregations):
577
- if (col.aggFunc) {
578
- valueCols.push({
579
- field: col.colId,
580
- aggFunc: col.aggFunc
581
- });
582
- }
583
- // Sorting:
584
- if (col.sort) {
585
- sortModel.push({
586
- colId: col.colId,
587
- sort: col.sort
588
- });
589
- }
590
- }
591
- // NOTE: commenting this out since we only care about the highest level data.
592
- // rowGroupCols.sort((a, b) => {
593
- // const iA = columnState.find(c => c.colId === a.field)?.rowGroupIndex ?? 0;
594
- // const iB = columnState.find(c => c.colId === b.field)?.rowGroupIndex ?? 0;
595
- // return iA - iB;
596
- // });
597
- // pivotCols.sort((a, b) => {
598
- // const iA = columnState.find(c => c.colId === a.field)?.pivotIndex ?? 0;
599
- // const iB = columnState.find(c => c.colId === b.field)?.pivotIndex ?? 0;
600
- // return iA - iB;
601
- // });
602
- sortModel.sort((a, b) => {
603
- const iA = columnState.find(c => c.colId === a.colId)?.sortIndex ?? 0;
604
- const iB = columnState.find(c => c.colId === b.colId)?.sortIndex ?? 0;
605
- return iA - iB;
606
- });
607
- return {
608
- rowGroupCols,
609
- pivotCols,
610
- valueCols,
611
- sortModel
612
- };
613
- }
614
- function filterRows(data, filterModel) {
615
- const textOps = {
616
- equals: (v, f) => (v ?? '').toLowerCase() === f.toLowerCase(),
617
- notEqual: (v, f) => (v ?? '').toLowerCase() !== f.toLowerCase(),
618
- contains: (v, f) => (v ?? '').toLowerCase().includes(f.toLowerCase()),
619
- notContains: (v, f) => !(v ?? '').toLowerCase().includes(f.toLowerCase()),
620
- startsWith: (v, f) => (v ?? '').toLowerCase().startsWith(f.toLowerCase()),
621
- endsWith: (v, f) => (v ?? '').toLowerCase().endsWith(f.toLowerCase()),
622
- blank: (v) => v == null || v === '',
623
- notBlank: (v) => !(v == null || v === '')
624
- };
625
- const numberOps = {
626
- equals: (v, f) => v == f,
627
- notEqual: (v, f) => v != f,
628
- lessThan: (v, f) => v < f,
629
- lessThanOrEqual: (v, f) => v <= f,
630
- greaterThan: (v, f) => v > f,
631
- greaterThanOrEqual: (v, f) => v >= f,
632
- inRange: (v, f, t) => v >= f && v <= t,
633
- blank: (v) => v == null,
634
- notBlank: (v) => v != null
635
- };
636
- function checkCondition(value, cond) {
637
- const { filterType, type, filter, filterTo, values } = cond;
638
- if (filterType === 'text') {
639
- const fn = textOps[type ?? 'contains'];
640
- return fn?.(value, filter);
641
- }
642
- if (filterType === 'number') {
643
- const fn = numberOps[type ?? 'equals'];
644
- return type === 'inRange'
645
- ? fn?.(value, filter, filterTo)
646
- : fn?.(value, filter);
647
- }
648
- if (filterType === 'set') {
649
- // Only boolean types are supported by set filter -- need to represent as string for comparison.
650
- return (values ?? []).includes(`${value}`);
651
- }
652
- return true;
653
- }
654
- function compilePredicate(model) {
655
- return (row) => {
656
- return Object.entries(model).every(([field, filter]) => {
657
- const value = row[field];
658
- if ('conditions' in filter && Array.isArray(filter.conditions)) {
659
- const logicFn = filter.operator === 'OR' ? 'some' : 'every';
660
- return filter.conditions[logicFn](cond => checkCondition(value, cond));
661
- }
662
- else {
663
- return checkCondition(value, filter);
664
- }
665
- });
666
- };
667
- }
668
- const predicate = compilePredicate(filterModel);
669
- return data.filter(predicate);
670
- }
671
- function pivotData(data, rowGroupCols, pivotCols, valueCols) {
672
- const groupBy = (row) => Object.fromEntries(rowGroupCols.map(col => [col.field, row[col.field]]));
673
- const pivotBy = (row) => pivotCols.map(col => row[col.field]).join('_');
674
- const grouped = new Map();
675
- for (const row of data) {
676
- const groupKey = JSON.stringify(groupBy(row));
677
- if (!grouped.has(groupKey))
678
- grouped.set(groupKey, []);
679
- grouped.get(groupKey).push(row);
680
- }
681
- const rows = [];
682
- const mappedColumnNames = new Map();
683
- for (const [groupKey, groupRows] of grouped.entries()) {
684
- const groupObj = JSON.parse(groupKey);
685
- const pivotBuckets = new Map();
686
- for (const row of groupRows) {
687
- const pivotKey = pivotBy(row);
688
- if (!pivotBuckets.has(pivotKey))
689
- pivotBuckets.set(pivotKey, []);
690
- pivotBuckets.get(pivotKey).push(row);
691
- }
692
- for (const [pivotKey, rows] of pivotBuckets.entries()) {
693
- for (const { field, aggFunc } of valueCols) {
694
- const values = rows.map(r => r[field]).filter(v => v != null);
695
- const key = `${pivotKey} ${field}`;
696
- let columnNames = mappedColumnNames.get(field);
697
- if (!!columnNames) {
698
- columnNames.add(key);
699
- }
700
- else {
701
- columnNames = new Set([key]);
702
- mappedColumnNames.set(field, columnNames);
703
- }
704
- switch (aggFunc) {
705
- case 'sum':
706
- groupObj[key] = values.map(Number).reduce((a, b) => a + b, 0);
707
- break;
708
- case 'count':
709
- groupObj[key] = values.length;
710
- break;
711
- case 'min':
712
- groupObj[key] = values.reduce((a, b) => (a < b ? a : b), values[0]);
713
- break;
714
- case 'max':
715
- groupObj[key] = values.reduce((a, b) => (a > b ? a : b), values[0]);
716
- break;
717
- case 'avg': {
718
- const nums = values.map(Number).filter(n => !isNaN(n));
719
- groupObj[key] = nums.length ? nums.reduce((a, b) => a + b, 0) / nums.length : null;
720
- break;
721
- }
722
- case 'first':
723
- groupObj[key] = values[0];
724
- break;
725
- case 'last':
726
- groupObj[key] = values[values.length - 1];
727
- break;
728
- default:
729
- groupObj[key] = null;
730
- }
731
- }
732
- }
733
- rows.push(groupObj);
734
- }
735
- return { rows, mappedColumnNames };
736
- }
737
- function groupAndAggregate(data, rowGroupCols, groupKeys, valueCols) {
738
- const level = groupKeys.length;
739
- const groupField = rowGroupCols[level]?.field;
740
- if (!groupField) {
741
- return data;
742
- }
743
- const grouped = new Map();
744
- for (const row of data) {
745
- const key = row[groupField];
746
- if (!grouped.has(key))
747
- grouped.set(key, []);
748
- grouped.get(key).push(row);
749
- }
750
- const output = [];
751
- for (const [key, rows] of grouped.entries()) {
752
- if (level < rowGroupCols.length - 1) {
753
- output.push({
754
- group: true,
755
- key,
756
- children: groupAndAggregate(rows, rowGroupCols, [...groupKeys, key], valueCols)
757
- });
758
- }
759
- else {
760
- const aggData = {};
761
- for (const { field, aggFunc } of valueCols) {
762
- const rawValues = rows.map(r => r[field]).filter(v => v != null);
763
- switch (aggFunc) {
764
- case 'sum': {
765
- const nums = rawValues.map(Number).filter(v => !isNaN(v));
766
- aggData[field] = nums.reduce((a, b) => a + b, 0);
767
- break;
768
- }
769
- case 'avg': {
770
- const nums = rawValues.map(Number).filter(v => !isNaN(v));
771
- const total = nums.reduce((a, b) => a + b, 0);
772
- aggData[field] = nums.length > 0 ? total / nums.length : null;
773
- break;
774
- }
775
- case 'min':
776
- aggData[field] = rawValues.reduce((a, b) => (a < b ? a : b), rawValues[0]);
777
- break;
778
- case 'max':
779
- aggData[field] = rawValues.reduce((a, b) => (a > b ? a : b), rawValues[0]);
780
- break;
781
- case 'count':
782
- aggData[field] = rawValues.length;
783
- break;
784
- case 'first':
785
- aggData[field] = rawValues[0];
786
- break;
787
- case 'last':
788
- aggData[field] = rawValues[rawValues.length - 1];
789
- break;
790
- default:
791
- aggData[field] = null;
792
- }
793
- }
794
- // NOTE: we only want the highest level data -- we don't care about child rows.
795
- output.push({
796
- [groupField]: key,
797
- ...aggData,
798
- // children: rows
799
- });
800
- }
801
- }
802
- return output;
803
- }
804
- function sortRows(data, sortModel) {
805
- return [...data].sort((a, b) => {
806
- for (const { colId, sort } of sortModel) {
807
- const valA = a[colId];
808
- const valB = b[colId];
809
- if (valA === valB)
810
- continue;
811
- const cmp = valA > valB ? 1 : -1;
812
- return sort === 'asc' ? cmp : -cmp;
813
- }
814
- return 0;
815
- });
816
- }
817
- function processHtmlStyles(value, columnName, columnFormat, rowStyles, columnStyles) {
818
- let columnStyle = {
819
- columnName,
820
- styles: []
821
- };
822
- // Reminder: Value Formatting is performed **before** Conditional Formatting.
823
- // https://chatwms.io/user-manual/concepts/column-formatting#conditional-formatting
824
- for (const condFmt of columnFormat.conditionalFormats.slice().reverse()) {
825
- // `conditionalFormats` has already been sorted by `sequence`.
826
- // NOTE: need to reverse the array for sequence to be correctly applied.
827
- // NOTE: using `slice` to make shallow copy of array since `reverse` does so in place.
828
- if (evaluateConditionalFormat(condFmt, columnFormat.type, formatNumberEnabled(columnFormat.valueFormat) ? stripNumericValueFormat(value) : value)) {
829
- if (condFmt.row) {
830
- rowStyles.push(condFmt.style);
831
- }
832
- else {
833
- columnStyle.styles.push(condFmt.style);
834
- }
835
- }
836
- }
837
- columnStyles.push(columnStyle);
838
- return [rowStyles, columnStyles];
839
- }
840
- function processHtmlTransposeStyles(value, columnName, columnFormat, columnStyles) {
841
- let columnStyle = {
842
- columnName,
843
- styles: []
844
- };
845
- // Everything is the same as ^^^ except for row styles -- we will consolidate those into columns styles.
846
- for (const condFmt of columnFormat.conditionalFormats.slice().reverse()) {
847
- if (evaluateConditionalFormat(condFmt, columnFormat.type, formatNumberEnabled(columnFormat.valueFormat) ? stripNumericValueFormat(value) : value)) {
848
- columnStyle.styles.push(condFmt.style);
849
- }
850
- }
851
- columnStyles.push(columnStyle);
852
- return columnStyles;
853
- }
854
- export function mapTeamsStyles(columnNames, htmlRowStyles, htmlColumnStyles, teamsRowStyles, teamsColumnStyles) {
855
- // IMPORTANT: both column/row style sorting have **already** been reversed (line 1170).
856
- // We can simply iterate over all the matched styles and accumulate them.
857
- const getColumnStyle = (style) => {
858
- switch (style) {
859
- case ConditionalFormatStyle.GREEN_BACKGROUND:
860
- return {
861
- color: 'Good',
862
- style: 'good'
863
- };
864
- case ConditionalFormatStyle.YELLOW_BACKGROUND:
865
- return {
866
- color: 'Warning',
867
- style: 'warning'
868
- };
869
- case ConditionalFormatStyle.RED_BACKGROUND:
870
- return {
871
- color: 'Attention',
872
- style: 'attention'
873
- };
874
- case ConditionalFormatStyle.BLUE_BACKGROUND:
875
- case ConditionalFormatStyle.PURPLE_BACKGROUND:
876
- return {
877
- color: 'Accent',
878
- style: 'accent'
879
- };
880
- case ConditionalFormatStyle.BOLD:
881
- return {
882
- weight: 'Bolder'
883
- };
884
- case ConditionalFormatStyle.GREEN_BOLD:
885
- return {
886
- color: 'Good',
887
- weight: 'Bolder'
888
- };
889
- case ConditionalFormatStyle.YELLOW_BOLD:
890
- return {
891
- color: 'Warning',
892
- weight: 'Bolder'
893
- };
894
- case ConditionalFormatStyle.RED_BOLD:
895
- return {
896
- color: 'Attention',
897
- weight: 'Bolder'
898
- };
899
- case ConditionalFormatStyle.BLUE_BOLD:
900
- case ConditionalFormatStyle.PURPLE_BOLD:
901
- return {
902
- color: 'Accent',
903
- weight: 'Bolder'
904
- };
905
- case ConditionalFormatStyle.NOT_IMPORTANT:
906
- return {
907
- isSubtle: true,
908
- italic: true,
909
- weight: 'Lighter'
910
- };
911
- default:
912
- return {};
913
- }
914
- };
915
- for (const htmlColumnStyle of htmlColumnStyles) {
916
- teamsColumnStyles.push({
917
- columnName: htmlColumnStyle.columnName,
918
- styles: htmlColumnStyle.styles.map(style => getColumnStyle(style))
919
- });
920
- }
921
- const applyRowStyleToColumns = (style) => {
922
- for (const columnName of columnNames) {
923
- const columnStyle = getColumnStyle(style);
924
- const teamsColumnStyle = teamsColumnStyles.find(c => c.columnName === columnName);
925
- if (!!teamsColumnStyle) {
926
- teamsColumnStyle.styles.push(columnStyle);
927
- }
928
- else {
929
- teamsColumnStyles.push({
930
- columnName,
931
- styles: [columnStyle]
932
- });
933
- }
934
- }
935
- };
936
- for (const htmlRowStyle of htmlRowStyles) {
937
- switch (htmlRowStyle) {
938
- case ConditionalFormatStyle.GREEN_BACKGROUND:
939
- teamsRowStyles.push('good');
940
- applyRowStyleToColumns(htmlRowStyle);
941
- break;
942
- case ConditionalFormatStyle.YELLOW_BACKGROUND:
943
- teamsRowStyles.push('warning');
944
- applyRowStyleToColumns(htmlRowStyle);
945
- break;
946
- case ConditionalFormatStyle.RED_BACKGROUND:
947
- teamsRowStyles.push('attention');
948
- applyRowStyleToColumns(htmlRowStyle);
949
- break;
950
- case ConditionalFormatStyle.BLUE_BACKGROUND:
951
- case ConditionalFormatStyle.PURPLE_BACKGROUND:
952
- teamsRowStyles.push('accent');
953
- applyRowStyleToColumns(htmlRowStyle);
954
- break;
955
- case ConditionalFormatStyle.BOLD:
956
- applyRowStyleToColumns(htmlRowStyle);
957
- break;
958
- case ConditionalFormatStyle.GREEN_BOLD:
959
- applyRowStyleToColumns(htmlRowStyle);
960
- break;
961
- case ConditionalFormatStyle.YELLOW_BOLD:
962
- applyRowStyleToColumns(htmlRowStyle);
963
- break;
964
- case ConditionalFormatStyle.RED_BOLD:
965
- applyRowStyleToColumns(htmlRowStyle);
966
- break;
967
- case ConditionalFormatStyle.BLUE_BOLD:
968
- case ConditionalFormatStyle.PURPLE_BOLD:
969
- applyRowStyleToColumns(htmlRowStyle);
970
- break;
971
- case ConditionalFormatStyle.NOT_IMPORTANT:
972
- applyRowStyleToColumns(htmlRowStyle);
973
- break;
974
- default:
975
- break;
976
- }
977
- }
978
- return [teamsRowStyles, teamsColumnStyles];
979
- }
980
- export function mapTeamsTransposeStyles(htmlColumnStyle) {
981
- // Generally the same logic as ^^^, just handling a few things differently for transpose.
982
- const getColumnStyle = (style) => {
983
- switch (style) {
984
- case ConditionalFormatStyle.GREEN_BACKGROUND:
985
- return {
986
- color: 'Good',
987
- style: 'good'
988
- };
989
- case ConditionalFormatStyle.YELLOW_BACKGROUND:
990
- return {
991
- color: 'Warning',
992
- style: 'warning'
993
- };
994
- case ConditionalFormatStyle.RED_BACKGROUND:
995
- return {
996
- color: 'Attention',
997
- style: 'attention'
998
- };
999
- case ConditionalFormatStyle.BLUE_BACKGROUND:
1000
- case ConditionalFormatStyle.PURPLE_BACKGROUND:
1001
- return {
1002
- color: 'Accent',
1003
- style: 'accent'
1004
- };
1005
- case ConditionalFormatStyle.BOLD:
1006
- return {
1007
- weight: 'Bolder'
1008
- };
1009
- case ConditionalFormatStyle.GREEN_BOLD:
1010
- return {
1011
- color: 'Good',
1012
- weight: 'Bolder'
1013
- };
1014
- case ConditionalFormatStyle.YELLOW_BOLD:
1015
- return {
1016
- color: 'Warning',
1017
- weight: 'Bolder'
1018
- };
1019
- case ConditionalFormatStyle.RED_BOLD:
1020
- return {
1021
- color: 'Attention',
1022
- weight: 'Bolder'
1023
- };
1024
- case ConditionalFormatStyle.BLUE_BOLD:
1025
- case ConditionalFormatStyle.PURPLE_BOLD:
1026
- return {
1027
- color: 'Accent',
1028
- weight: 'Bolder'
1029
- };
1030
- case ConditionalFormatStyle.NOT_IMPORTANT:
1031
- return {
1032
- isSubtle: true,
1033
- italic: true,
1034
- weight: 'Lighter'
1035
- };
1036
- default:
1037
- return {};
1038
- }
1039
- };
1040
- return {
1041
- columnName: htmlColumnStyle.columnName,
1042
- styles: htmlColumnStyle.styles.map(style => getColumnStyle(style))
1043
- };
1044
- }
1045
- function buildHtmlTableHeader(columnNames) {
1046
- let buf = '<table>\n\t<thead>\n\t\t<tr>';
1047
- for (const columnName of columnNames) {
1048
- buf += `\n\t\t\t<th>${truncateValue(columnName, 24)}</th>`;
1049
- }
1050
- buf += '\n\t\t</tr>\n\t</thead>';
1051
- buf += '\n\t<tbody>';
1052
- return buf;
1053
- }
1054
- function buildHtmlTableTransposeHeader() {
1055
- return '<table>\n\t<thead></thead>\n\t<tbody>';
1056
- }
1057
- function buildHtmlTableFooter() {
1058
- return '\n\t</tbody>\n</table>';
1059
- }
1060
- function buildHtmlRow(rowValues, rowStyles, columnStyles, columnCount) {
1061
- let buf = '';
1062
- // Apply row styles.
1063
- if (rowStyles.length > 0) {
1064
- buf += `\n\t\t<tr class="${rowStyles.join(' ')}">`;
1065
- }
1066
- else {
1067
- buf += '\n\t\t<tr>';
1068
- }
1069
- // Write cell values with styles.
1070
- let colIdx = 0;
1071
- for (const rowValue of rowValues) {
1072
- if (colIdx >= columnCount) {
1073
- break;
1074
- }
1075
- const truncValue = truncateValue(rowValue, 24);
1076
- if (columnStyles[colIdx].styles.length > 0) {
1077
- buf += `\n\t\t\t<td class="${columnStyles[colIdx].styles.join(' ')}">${truncValue}</td>`;
1078
- }
1079
- else {
1080
- buf += `\n\t\t\t<td>${truncValue}</td>`;
1081
- }
1082
- colIdx++;
1083
- }
1084
- buf += '\n\t\t</tr>';
1085
- return buf;
1086
- }
1087
- function buildHtmlTransposeRow(row, columnStyle) {
1088
- let buf = '';
1089
- buf += `\n\t\t\t<td style="font-size: 13px; font-weight: 600; padding: 15px 14px; background: white; justify-items: end; align-items: center; width: 33%;"><span style="display: grid;">${truncateValue(row.key, 24)}</span></td>`;
1090
- const truncValue = truncateValue(row.value, 42);
1091
- if (columnStyle.styles.length > 0) {
1092
- buf += `\n\t\t\t<td style="font-size: 16px; padding: 15px 14px; background: white;" class="${columnStyle.styles.join(' ')}">${truncValue}</td>`;
1093
- }
1094
- else {
1095
- buf += `\n\t\t\t<td style="font-size: 16px; padding: 15px 14px; background: white;">${truncValue}</td>`;
1096
- }
1097
- buf += '\n\t\t</tr>';
1098
- return buf;
1099
- }
1100
- function createTeamsTableColumnText(columnName) {
1101
- return {
1102
- type: "TextBlock",
1103
- text: truncateValue(columnName, 16),
1104
- size: "Small",
1105
- weight: "Bolder"
1106
- };
1107
- }
1108
- export function createTeamsTableColumns(columnNames) {
1109
- return {
1110
- type: "TableRow",
1111
- cells: columnNames.map((columnName) => ({
1112
- type: "TableCell",
1113
- items: [createTeamsTableColumnText(columnName)]
1114
- }))
1115
- };
1116
- }
1117
- function createTeamsTableCellText(value, columnStyle) {
1118
- let color = undefined;
1119
- let weight = undefined;
1120
- let isSubtle = undefined;
1121
- let italic = undefined;
1122
- for (const styles of columnStyle?.styles ?? []) {
1123
- if (!!styles.color) {
1124
- color = styles.color;
1125
- }
1126
- if (!!styles.weight) {
1127
- weight = styles.weight;
1128
- }
1129
- if (!!styles.isSubtle) {
1130
- isSubtle = styles.isSubtle;
1131
- }
1132
- if (!!styles.italic) {
1133
- italic = styles.italic;
1134
- }
1135
- }
1136
- const truncValue = truncateValue(value, 16);
1137
- return {
1138
- type: "TextBlock",
1139
- text: italic ? `*${truncValue}*` : truncValue,
1140
- size: "Small",
1141
- color,
1142
- weight,
1143
- isSubtle
1144
- };
1145
- }
1146
- function createTeamsTransposeTableCellText(value, key, columnStyle) {
1147
- // Same as ^^^, just a couple minor tweaks.
1148
- let color = undefined;
1149
- let weight = undefined;
1150
- let isSubtle = undefined;
1151
- let italic = undefined;
1152
- for (const styles of columnStyle?.styles ?? []) {
1153
- if (!!styles.color) {
1154
- color = styles.color;
1155
- }
1156
- if (!!styles.weight) {
1157
- weight = styles.weight;
1158
- }
1159
- if (!!styles.isSubtle) {
1160
- isSubtle = styles.isSubtle;
1161
- }
1162
- if (!!styles.italic) {
1163
- italic = styles.italic;
1164
- }
1165
- }
1166
- const truncValue = truncateValue(value, 16);
1167
- return {
1168
- type: "TextBlock",
1169
- text: italic ? `*${truncValue}*` : truncValue,
1170
- size: key ? "Small" : "Medium",
1171
- horizontalAlignment: key ? "Right" : "Left",
1172
- color,
1173
- weight,
1174
- isSubtle
1175
- };
1176
- }
1177
- export function createTeamsTableRow(columnNames, values, rowStyles, columnStyles) {
1178
- let rowStyle = undefined;
1179
- for (const style of rowStyles ?? []) {
1180
- rowStyle = style;
1181
- }
1182
- return {
1183
- type: "TableRow",
1184
- style: rowStyle,
1185
- cells: values.map((value, idx) => {
1186
- const columnName = columnNames[idx];
1187
- const columnStyle = columnStyles?.find(cs => cs.columnName === columnName);
1188
- let style = undefined;
1189
- for (const styles of columnStyle?.styles ?? []) {
1190
- if (!!styles.style) {
1191
- style = styles.style;
1192
- }
1193
- }
1194
- return {
1195
- type: "TableCell",
1196
- style,
1197
- items: [createTeamsTableCellText(value, columnStyle)]
1198
- };
1199
- })
1200
- };
1201
- }
1202
- export function createTeamsTransposeTableRow(row, columnStyle) {
1203
- const keyCell = {
1204
- type: "TableCell",
1205
- items: [createTeamsTransposeTableCellText(row.key, true, {
1206
- columnName: 'key',
1207
- styles: [{ weight: 'Bolder' }]
1208
- })]
1209
- };
1210
- let style = undefined;
1211
- for (const styles of columnStyle?.styles ?? []) {
1212
- if (!!styles.style) {
1213
- style = styles.style;
1214
- }
1215
- }
1216
- const valueCell = {
1217
- type: "TableCell",
1218
- style,
1219
- items: [createTeamsTransposeTableCellText(row.value, false, columnStyle)]
1220
- };
1221
- return {
1222
- type: "TableRow",
1223
- cells: [keyCell, valueCell]
1224
- };
1225
- }
1226
- /**
1227
- * **Important:** `html` and `teamsRows` output will be limited to **1,000 rows/24 rows** and **8 columns**, respectively.
1228
- *
1229
- * ~All outputs respect and will be filtered by optional `condition` argument.~
1230
- */
1231
- export function transformDatagrid(rows, datagridState, locale, condition) {
1232
- const columnState = datagridState.columnState ?? [];
1233
- const filterModel = datagridState.filterModel ?? {};
1234
- const isPivotMode = !!datagridState.isPivotMode;
1235
- const columnFormats = datagridState.columnFormats ?? [];
1236
- const { rowGroupCols, pivotCols, valueCols, sortModel } = parseColumnState(columnState);
1237
- rows = filterRows(rows, filterModel);
1238
- let adjRows = [];
1239
- const chartRows = [];
1240
- let htmlBuf = '';
1241
- let htmlTransposeBuf = '';
1242
- const htmlColumnNames = [];
1243
- const teamsRows = [];
1244
- const teamsTranspose = [];
1245
- // IMPORTANT: we evaluate the datagrid condition AFTER we are done with all transformations.
1246
- // NOTE: we do not need any pivot columns for pivot mode to be valid.
1247
- // https://chatwms.io/user-manual/chatwms/faq#q-do-i-have-to-have-a-group-column-to-just-display-a-count-in-the-datagrid
1248
- if (isPivotMode && valueCols.length > 0) {
1249
- const pivotOutput = pivotData(rows, rowGroupCols, pivotCols, valueCols);
1250
- rows = pivotOutput.rows;
1251
- rows = sortModel.length > 0 ? sortRows(rows, sortModel) : rows;
1252
- let mappedDisplayColumnNames = new Map();
1253
- // Should not need to do hidden/visible column analysis -- pivoting creates new results with only the necessary columns.
1254
- // Also should not need to analyze column sequence -- our newly created results *should* respect the correct sequence.
1255
- let rowIdx = 0;
1256
- rows.forEach((row) => {
1257
- let adjRow = {};
1258
- let chartRow = {};
1259
- let htmlRowValues = [];
1260
- let htmlRowStyles = [];
1261
- let htmlColumnStyles = [];
1262
- let htmlTransposeColumnStyles = [];
1263
- let teamsRowStyles = [];
1264
- let teamsColumnStyles = [];
1265
- columnFormats.forEach(columnFormat => {
1266
- if (columnFormat.name in row) {
1267
- const value = row[columnFormat.name];
1268
- const formattedValue = evaluateValueFormat(columnFormat, value, locale);
1269
- adjRow[columnFormat.displayName] = formattedValue;
1270
- chartRow[columnFormat.displayName] = formatNumberEnabled(columnFormat.valueFormat) ? value : formattedValue;
1271
- htmlRowValues.push(formattedValue);
1272
- [htmlRowStyles, htmlColumnStyles] = processHtmlStyles(formattedValue, columnFormat.displayName, columnFormat, htmlRowStyles, htmlColumnStyles);
1273
- htmlTransposeColumnStyles = processHtmlTransposeStyles(formattedValue, columnFormat.displayName, columnFormat, htmlTransposeColumnStyles);
1274
- if (rowIdx === 0) {
1275
- htmlColumnNames.push(columnFormat.displayName);
1276
- }
1277
- }
1278
- else {
1279
- const mappedColumnNamesSet = pivotOutput.mappedColumnNames.get(columnFormat.name);
1280
- if (!!mappedColumnNamesSet) {
1281
- const mappedColumnNamesArr = Array.from(mappedColumnNamesSet);
1282
- for (const mappedColumnName of mappedColumnNamesArr) {
1283
- if (mappedColumnName in row) {
1284
- const adjDisplayName = mappedColumnName.replace(columnFormat.name, columnFormat.displayName);
1285
- const value = row[mappedColumnName];
1286
- const formattedValue = evaluateValueFormat(columnFormat, value, locale);
1287
- adjRow[adjDisplayName] = formattedValue;
1288
- chartRow[adjDisplayName] = formatNumberEnabled(columnFormat.valueFormat) ? value : formattedValue;
1289
- htmlRowValues.push(formattedValue);
1290
- [htmlRowStyles, htmlColumnStyles] = processHtmlStyles(formattedValue, adjDisplayName, columnFormat, htmlRowStyles, htmlColumnStyles);
1291
- htmlTransposeColumnStyles = processHtmlTransposeStyles(formattedValue, adjDisplayName, columnFormat, htmlTransposeColumnStyles);
1292
- if (rowIdx === 0) {
1293
- htmlColumnNames.push(adjDisplayName);
1294
- }
1295
- let displayColumnNames = mappedDisplayColumnNames.get(columnFormat.displayName);
1296
- if (!!displayColumnNames) {
1297
- displayColumnNames.add(adjDisplayName);
1298
- }
1299
- else {
1300
- displayColumnNames = new Set([adjDisplayName]);
1301
- mappedDisplayColumnNames.set(columnFormat.displayName, displayColumnNames);
1302
- }
1303
- }
1304
- }
1305
- }
1306
- }
1307
- });
1308
- adjRows.push(adjRow);
1309
- chartRows.push(chartRow);
1310
- if (rowIdx < DATAGRID_HTML_ROWS) {
1311
- htmlBuf += buildHtmlRow(htmlRowValues, htmlRowStyles, htmlColumnStyles, DATAGRID_HTML_COLS);
1312
- if (rowIdx < DATAGRID_TEAMS_ROWS) {
1313
- [teamsRowStyles, teamsColumnStyles] = mapTeamsStyles(htmlColumnNames, htmlRowStyles, htmlColumnStyles, teamsRowStyles, teamsColumnStyles);
1314
- teamsRows.push(createTeamsTableRow(htmlColumnNames, htmlRowValues, teamsRowStyles, teamsColumnStyles));
1315
- }
1316
- }
1317
- if (rowIdx === 0) {
1318
- const transposedRows = htmlColumnNames.map((key, index) => ({
1319
- key,
1320
- value: htmlRowValues[index],
1321
- }));
1322
- for (let i = 0; i < transposedRows.length; i++) {
1323
- htmlTransposeBuf += buildHtmlTransposeRow(transposedRows[i], htmlTransposeColumnStyles[i]);
1324
- teamsTranspose.push(createTeamsTransposeTableRow(transposedRows[i], mapTeamsTransposeStyles(htmlTransposeColumnStyles[i])));
1325
- }
1326
- }
1327
- rowIdx++;
1328
- });
1329
- if (!!condition) {
1330
- rows = evaluatePivotDatagridCondition(rows, condition, mappedDisplayColumnNames);
1331
- }
1332
- }
1333
- else {
1334
- if (rowGroupCols.length > 0) {
1335
- rows = groupAndAggregate(rows, rowGroupCols, [], valueCols);
1336
- }
1337
- rows = sortModel.length > 0 ? sortRows(rows, sortModel) : rows;
1338
- const visibleColumnState = columnState.filter(column => !column.hide || !!column.rowGroup);
1339
- let rowIdx = 0;
1340
- rows.forEach((row) => {
1341
- let adjRow = {};
1342
- let chartRow = {};
1343
- let htmlRowValues = [];
1344
- let htmlRowStyles = [];
1345
- let htmlColumnStyles = [];
1346
- let htmlTransposeColumnStyles = [];
1347
- let teamsRowStyles = [];
1348
- let teamsColumnStyles = [];
1349
- // Important: anchoring to column state will ensure we add columns in the correct sequence.
1350
- visibleColumnState.forEach(column => {
1351
- const columnFormat = columnFormats.find(columnFormat => columnFormat.name === column.colId);
1352
- if (!!columnFormat) {
1353
- const value = row[columnFormat.name];
1354
- const formattedValue = evaluateValueFormat(columnFormat, value, locale);
1355
- adjRow[columnFormat.displayName] = formattedValue;
1356
- chartRow[columnFormat.displayName] = formatNumberEnabled(columnFormat.valueFormat) ? value : formattedValue;
1357
- htmlRowValues.push(formattedValue);
1358
- [htmlRowStyles, htmlColumnStyles] = processHtmlStyles(formattedValue, columnFormat.displayName, columnFormat, htmlRowStyles, htmlColumnStyles);
1359
- htmlTransposeColumnStyles = processHtmlTransposeStyles(formattedValue, columnFormat.displayName, columnFormat, htmlTransposeColumnStyles);
1360
- if (rowIdx === 0) {
1361
- htmlColumnNames.push(columnFormat.displayName);
1362
- }
1363
- }
1364
- });
1365
- adjRows.push(adjRow);
1366
- chartRows.push(chartRow);
1367
- if (rowIdx < DATAGRID_HTML_ROWS) {
1368
- htmlBuf += buildHtmlRow(htmlRowValues, htmlRowStyles, htmlColumnStyles, DATAGRID_HTML_COLS);
1369
- if (rowIdx < DATAGRID_TEAMS_ROWS) {
1370
- [teamsRowStyles, teamsColumnStyles] = mapTeamsStyles(htmlColumnNames, htmlRowStyles, htmlColumnStyles, teamsRowStyles, teamsColumnStyles);
1371
- teamsRows.push(createTeamsTableRow(htmlColumnNames, htmlRowValues, teamsRowStyles, teamsColumnStyles));
1372
- }
1373
- }
1374
- if (rowIdx === 0) {
1375
- const transposedRows = htmlColumnNames.map((key, index) => ({
1376
- key,
1377
- value: htmlRowValues[index],
1378
- }));
1379
- for (let i = 0; i < transposedRows.length; i++) {
1380
- htmlTransposeBuf += buildHtmlTransposeRow(transposedRows[i], htmlTransposeColumnStyles[i]);
1381
- teamsTranspose.push(createTeamsTransposeTableRow(transposedRows[i], mapTeamsTransposeStyles(htmlTransposeColumnStyles[i])));
1382
- }
1383
- }
1384
- rowIdx++;
1385
- });
1386
- if (!!condition) {
1387
- adjRows = evaluateDatagridCondition(rows, condition);
1388
- }
1389
- }
1390
- return {
1391
- ...datagridState,
1392
- adjRows,
1393
- chartRows,
1394
- html: `<p style="font-size: 12px;">Displaying first <b>${DATAGRID_HTML_ROWS} rows</b> and <b>${DATAGRID_HTML_COLS} columns</b></p>\n\n${buildHtmlTableHeader(htmlColumnNames.slice(0, DATAGRID_HTML_COLS))}${htmlBuf}${buildHtmlTableFooter()}`,
1395
- teamsRows: [
1396
- createTeamsTableColumns(htmlColumnNames),
1397
- ...teamsRows
1398
- ],
1399
- htmlTranspose: `${buildHtmlTableTransposeHeader()}${htmlTransposeBuf}${buildHtmlTableFooter()}`,
1400
- teamsTranspose
1401
- };
1402
- }