@isoftdata/utility-dashboard-backend 2.0.2 → 2.1.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/dist/index.js ADDED
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.formatterTemplates = exports.handleFormattingTemplates = exports.parameterizeQuery = exports.loadDataTableAndProcessedFormatting = exports.loadDataTable = exports.loadOutputParameterValues = exports.formatChartDataForReport = exports.formatReportMetadata = void 0;
30
+ const db = require('@isoftdata/utility-db');
31
+ const chart_helper_js_1 = require("./chart-helper.js");
32
+ Object.defineProperty(exports, "handleFormattingTemplates", { enumerable: true, get: function () { return chart_helper_js_1.handleFormattingTemplates; } });
33
+ const utility_date_time_1 = require("@isoftdata/utility-date-time");
34
+ const formatterTemplates = __importStar(require("./formatting-templates"));
35
+ exports.formatterTemplates = formatterTemplates;
36
+ const formatDate = require('date-fns/format');
37
+ const camelcase_1 = __importDefault(require("camelcase"));
38
+ const mysql = require('mysql');
39
+ const deepmerge = require('deepmerge');
40
+ const handleChartTypeSpecificQuirks = (chartType, resultSet) => {
41
+ if (resultSet.length) {
42
+ switch (chartType) {
43
+ case 'PieChart':
44
+ resultSet = (0, chart_helper_js_1.makeNegativeNumbericValuesZero)(resultSet);
45
+ break;
46
+ case 'TreeMap':
47
+ resultSet = (0, chart_helper_js_1.coerceNullColumn)(Object.keys(resultSet[0])[1], resultSet);
48
+ if (resultSet.length === 1) {
49
+ resultSet[0].id = 'No Data';
50
+ }
51
+ break;
52
+ default:
53
+ resultSet = (0, chart_helper_js_1.coerceNullColumn)(Object.keys(resultSet[0])[0], resultSet);
54
+ }
55
+ }
56
+ return resultSet;
57
+ };
58
+ async function loadChartData(mysqlConnection, chart, queryObject) {
59
+ let { formatting, chartWrapper, multiSeries, table, supertype, cumulative, ...theRest } = chart;
60
+ if (!chartWrapper) {
61
+ chartWrapper = {
62
+ chartType: supertype,
63
+ options: {
64
+ height: 450, width: 800,
65
+ },
66
+ };
67
+ }
68
+ cumulative = cumulative || (chartWrapper?.chartType === 'AreaChart' && cumulative !== false);
69
+ let dataTable = {}, processedFormatting = {};
70
+ try {
71
+ const dataTableAndProcessedFormatting = await loadDataTableAndProcessedFormatting(mysqlConnection, { query: queryObject, multiSeries, formatting, chartType: chartWrapper?.chartType, cumulative, table });
72
+ dataTable = dataTableAndProcessedFormatting.dataTable;
73
+ processedFormatting = dataTableAndProcessedFormatting.processedFormatting;
74
+ }
75
+ catch (err) {
76
+ console.error('Error loading dataTable / processedFormatting');
77
+ throw err;
78
+ }
79
+ if (chartWrapper?.chartType === 'Table') {
80
+ return {
81
+ ...theRest,
82
+ supertype,
83
+ chartWrapper,
84
+ table: { ...table, ...dataTable },
85
+ formatting: processedFormatting,
86
+ };
87
+ }
88
+ return {
89
+ ...theRest,
90
+ cumulative,
91
+ supertype,
92
+ chartWrapper: {
93
+ ...chartWrapper,
94
+ dataTable: dataTable,
95
+ },
96
+ formatting: processedFormatting,
97
+ };
98
+ }
99
+ async function loadDataTableAndProcessedFormatting(mysqlConnection, { query: queryObject, multiSeries, formatting, chartType, cumulative = false, table = {} }) {
100
+ let { results: queryResultSet, fields } = (queryObject?.sql) ? await (0, chart_helper_js_1.queryWithFields)(mysqlConnection, queryObject) : { fields: {}, results: [] };
101
+ queryResultSet = queryResultSet;
102
+ let data = handleChartTypeSpecificQuirks(chartType, queryResultSet);
103
+ if (multiSeries) {
104
+ ({ data, formatting, fields } = (0, chart_helper_js_1.getMultiSeriesData)({
105
+ data,
106
+ seriesOptions: multiSeries,
107
+ formatting,
108
+ fields,
109
+ }));
110
+ }
111
+ const processedFormatting = (0, chart_helper_js_1.handleFormattingTemplates)(formatting);
112
+ if (chartType?.toLowerCase() === 'table') {
113
+ return { dataTable: (0, chart_helper_js_1.getRactiveTableDataFormat)(data, fields, processedFormatting, table?.style), processedFormatting };
114
+ }
115
+ let dataTable = (0, chart_helper_js_1.getDataTableFormat)(data, fields, multiSeries);
116
+ if (cumulative || chartType === 'AreaChart') {
117
+ dataTable = (0, chart_helper_js_1.makeDataCumulative)(dataTable, cumulative);
118
+ }
119
+ return { dataTable, processedFormatting };
120
+ }
121
+ exports.loadDataTableAndProcessedFormatting = loadDataTableAndProcessedFormatting;
122
+ async function loadDataTable(mysqlConnection, { query: queryObject, multiSeries, formatting, chartType }) {
123
+ const { dataTable } = await loadDataTableAndProcessedFormatting(mysqlConnection, { query: queryObject, multiSeries, formatting, chartType });
124
+ return dataTable;
125
+ }
126
+ exports.loadDataTable = loadDataTable;
127
+ const loadOptionList = async (mysqlConnection, optionListQuery, optionList = []) => {
128
+ return [
129
+ ...optionList,
130
+ ...(optionListQuery && await db.query(mysqlConnection, optionListQuery)) || [],
131
+ ];
132
+ };
133
+ async function loadOutputParameterValues(mysqlConnection, { definitionList = [], selectionList = [], forClient = false, session, site = 'store', siteIdColumn, siteNameColumn }) {
134
+ if (!(definitionList instanceof Array) || !definitionList.length) {
135
+ return [];
136
+ }
137
+ const parameterValues = [];
138
+ for (const definition of definitionList) {
139
+ const matchingParameterSelection = selectionList.find(param => param.name === definition.name);
140
+ let typedParameter;
141
+ const baseParameter = {
142
+ name: definition.name,
143
+ title: definition.title || definition.name,
144
+ default: definition.default,
145
+ value: (matchingParameterSelection?.value) ?? definition.default,
146
+ queryValues: {},
147
+ };
148
+ if (definition.type === 'dateRange') {
149
+ baseParameter.value = baseParameter.value || 'Last 30 Days';
150
+ typedParameter = {
151
+ ...baseParameter,
152
+ type: 'dateRange',
153
+ dates: matchingParameterSelection?.dates ?? (0, utility_date_time_1.datesFromRange)(baseParameter.value),
154
+ };
155
+ if (typedParameter.value !== 'Custom') {
156
+ typedParameter.dates = (0, utility_date_time_1.datesFromRange)(typedParameter.value);
157
+ }
158
+ if (!forClient) {
159
+ typedParameter.queryValues = {
160
+ [`${typedParameter.name}_start_date`]: typedParameter.dates.from,
161
+ [`${typedParameter.name}_end_date`]: typedParameter.dates.to,
162
+ };
163
+ }
164
+ }
165
+ else if (definition.type === 'selection') {
166
+ typedParameter = {
167
+ ...baseParameter,
168
+ type: 'selection',
169
+ optionListQuery: definition?.optionListQuery,
170
+ optionList: definition?.optionList,
171
+ };
172
+ typedParameter.optionList = await loadOptionList(mysqlConnection, typedParameter.optionListQuery, typedParameter.optionList);
173
+ if (!typedParameter.optionList.some(option => option.id?.toString() === typedParameter.value?.toString())) {
174
+ typedParameter.value = typedParameter.optionList?.[0]?.id?.toString();
175
+ }
176
+ const option = typedParameter.optionList.find(option => option.id.toString() === typedParameter.value);
177
+ if (!forClient) {
178
+ typedParameter.queryValues = {
179
+ [typedParameter.name]: option?.rawSql ? mysql.raw(typedParameter.value) : typedParameter.value,
180
+ };
181
+ }
182
+ }
183
+ else if (definition.type === 'siteSelection') {
184
+ typedParameter = {
185
+ ...baseParameter,
186
+ type: 'siteSelection',
187
+ permission: definition?.permission,
188
+ optionList: definition?.optionList,
189
+ };
190
+ typedParameter.value = typedParameter.value ?? (session.siteId ?? session[`${site}Id`]);
191
+ siteIdColumn = siteIdColumn ? mysqlConnection.escapeId(siteIdColumn) : mysqlConnection.escapeId(`${site}id`);
192
+ siteNameColumn = siteNameColumn ? mysqlConnection.escapeId(siteNameColumn) : 'name';
193
+ const siteTableName = mysqlConnection.escapeId(site);
194
+ if (typedParameter.permission) {
195
+ typedParameter.optionList = await db.query(mysqlConnection, {
196
+ sql: `SELECT ${siteIdColumn} AS id, ${siteNameColumn} AS \`name\` FROM ${siteTableName} WHERE f_check_user_permission(?, ?, ${siteIdColumn})`,
197
+ values: [session.userAccountId, definition.permission],
198
+ });
199
+ }
200
+ else {
201
+ typedParameter.optionList = await db.query(mysqlConnection, {
202
+ sql: `SELECT ${siteIdColumn} AS id, ${siteNameColumn} AS \`name\` FROM ${siteTableName} WHERE f_useraccount_has_site_access(?, ${siteIdColumn})`,
203
+ values: [session.userAccountId],
204
+ });
205
+ }
206
+ if (typedParameter.optionList.length > 1) {
207
+ typedParameter.optionList.unshift({ id: 0, name: 'All' });
208
+ }
209
+ if (!typedParameter.optionList.some(option => option.id?.toString() === baseParameter.value?.toString())) {
210
+ typedParameter.value = typedParameter.optionList?.[0]?.id.toString();
211
+ }
212
+ if (!forClient && typedParameter.value?.toString() === '0') {
213
+ typedParameter.queryValues = {
214
+ [typedParameter.name]: typedParameter.optionList
215
+ .filter(({ id }) => id > 0)
216
+ .map(({ id }) => id?.toString()),
217
+ };
218
+ }
219
+ else if (!forClient) {
220
+ typedParameter.queryValues = {
221
+ [typedParameter.name]: [typedParameter.value],
222
+ };
223
+ }
224
+ }
225
+ else if (definition.type === 'date') {
226
+ typedParameter = {
227
+ ...baseParameter,
228
+ type: 'date',
229
+ };
230
+ typedParameter.value = typedParameter.value || formatDate(new Date(), 'yyyy-MM-dd');
231
+ if (!forClient) {
232
+ typedParameter.queryValues = {
233
+ [typedParameter.name]: typedParameter.value,
234
+ };
235
+ }
236
+ }
237
+ else if (definition.type === 'session') {
238
+ typedParameter = {
239
+ ...baseParameter,
240
+ type: 'session',
241
+ };
242
+ const parameterName = (0, camelcase_1.default)(definition.name);
243
+ baseParameter.value = (session[parameterName] ?? '').toString();
244
+ if (!forClient) {
245
+ typedParameter.queryValues = {
246
+ [typedParameter.name]: typedParameter.value,
247
+ };
248
+ }
249
+ }
250
+ else {
251
+ throw new Error(`Parameter type mismatch for ${baseParameter.name}`);
252
+ }
253
+ parameterValues.push(typedParameter);
254
+ }
255
+ return parameterValues;
256
+ }
257
+ exports.loadOutputParameterValues = loadOutputParameterValues;
258
+ const parameterizeQuery = (queryString, parameterValues) => {
259
+ if (!queryString) {
260
+ return {
261
+ sql: '',
262
+ values: [],
263
+ };
264
+ }
265
+ const queryParameterValues = parameterValues.reduce((acc, val) => {
266
+ return val.queryValues ? {
267
+ ...acc,
268
+ ...val.queryValues,
269
+ } : acc;
270
+ }, {});
271
+ const regexp = /\${([^}]+)}/g;
272
+ const queryParameterKeys = [...queryString.matchAll(regexp)].map(([, insideToken]) => insideToken.trim());
273
+ const queryParameters = queryParameterKeys.map(key => queryParameterValues[key] || null);
274
+ return {
275
+ sql: queryString.replace(regexp, '?'),
276
+ values: queryParameters,
277
+ };
278
+ };
279
+ exports.parameterizeQuery = parameterizeQuery;
280
+ async function formatReportMetadata(mysqlConnection, { report, chartsInReport, session = {}, site, siteIdColumn, siteNameColumn }) {
281
+ try {
282
+ const parameterValues = await loadOutputParameterValues(mysqlConnection, { definitionList: report.parameters, forClient: true, session, site, siteIdColumn, siteNameColumn });
283
+ return {
284
+ dashboardReportId: report.dashboardReportId,
285
+ name: report.reportName,
286
+ title: report.reportTitle,
287
+ charts: chartsInReport,
288
+ parameterValues,
289
+ ownerId: report.ownerId,
290
+ autoRefreshInterval: report.autoRefreshInterval,
291
+ description: report.description ?? '',
292
+ };
293
+ }
294
+ catch (err) {
295
+ console.error(err);
296
+ throw err;
297
+ }
298
+ }
299
+ exports.formatReportMetadata = formatReportMetadata;
300
+ async function formatChartDataForReport(mysqlConnection, { reportParameters, chart, reportChart, parameterSelectionList, session = {}, site, siteIdColumn, siteNameColumn }) {
301
+ try {
302
+ const parameterValues = await loadOutputParameterValues(mysqlConnection, { definitionList: reportParameters, selectionList: parameterSelectionList, session, site, siteIdColumn, siteNameColumn });
303
+ if (reportChart?.jsonOverride?.chartWrapper?.dataTable) {
304
+ const { dataTable, ...chartWrapper } = reportChart.jsonOverride.chartWrapper;
305
+ reportChart.jsonOverride.chartWrapper = chartWrapper;
306
+ }
307
+ const combinedChart = deepmerge(chart, reportChart.jsonOverride ?? {});
308
+ return await loadChartData(mysqlConnection, {
309
+ ...combinedChart,
310
+ rank: reportChart.rank,
311
+ reportChartId: reportChart.reportChartId,
312
+ description: combinedChart.description ?? '',
313
+ }, parameterizeQuery(chart.query, parameterValues));
314
+ }
315
+ catch (err) {
316
+ console.error(err);
317
+ throw err;
318
+ }
319
+ }
320
+ exports.formatChartDataForReport = formatChartDataForReport;
321
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsdUJBQXVCLENBQUMsQ0FBQTtBQUMzQyx1REFTNkM7QUF3YTVDLDBHQTdhQSwyQ0FBeUIsT0E2YUE7QUF2YTFCLG9FQUE2RDtBQUM3RCwyRUFBNEQ7QUF1YTNELGdEQUFrQjtBQXRhbkIsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUE7QUFDN0MsMERBQWlDO0FBQ2pDLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQTtBQUM5QixNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUE7QUFNdEMsTUFBTSw2QkFBNkIsR0FBRyxDQUFDLFNBQWlCLEVBQUUsU0FBMEIsRUFBRSxFQUFFO0lBQ3ZGLElBQUksU0FBUyxDQUFDLE1BQU0sRUFBRTtRQUNyQixRQUFRLFNBQVMsRUFBRTtZQUNsQixLQUFLLFVBQVU7Z0JBRWQsU0FBUyxHQUFHLElBQUEsZ0RBQThCLEVBQUMsU0FBUyxDQUFDLENBQUE7Z0JBQ3JELE1BQUs7WUFDTixLQUFLLFNBQVM7Z0JBRWIsU0FBUyxHQUFHLElBQUEsa0NBQWdCLEVBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQTtnQkFDckUsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtvQkFDM0IsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxTQUFTLENBQUE7aUJBQzNCO2dCQUNELE1BQUs7WUFDTjtnQkFFQyxTQUFTLEdBQUcsSUFBQSxrQ0FBZ0IsRUFBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FB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
@@ -0,0 +1,4 @@
1
+ import { RowDataPacket } from "mysql";
2
+ import { TableStyleTemplateObject } from "./types/chart";
3
+ declare function styleRow(row: RowDataPacket, templates: TableStyleTemplateObject): RowDataPacket;
4
+ export default styleRow;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const mathjs_1 = require("mathjs");
7
+ const snakecase_keys_1 = __importDefault(require("snakecase-keys"));
8
+ const klona_1 = require("klona");
9
+ function handleMathJsCellStyleTemplate(row, columnToStyle, templates) {
10
+ const outCell = { value: row[columnToStyle]?.toString() };
11
+ if (columnToStyle in templates) {
12
+ const scope = (0, snakecase_keys_1.default)((0, klona_1.klona)(row), { deep: true });
13
+ for (const key in templates[columnToStyle]) {
14
+ try {
15
+ const expression = templates[columnToStyle][key] ?? '';
16
+ outCell[key] = (0, mathjs_1.evaluate)(expression, scope);
17
+ }
18
+ catch (err) {
19
+ console.error(`Error evaluating expression: '${templates[columnToStyle][key]}'\n`, err);
20
+ }
21
+ }
22
+ }
23
+ return outCell;
24
+ }
25
+ function styleRow(row, templates) {
26
+ const outRow = {};
27
+ for (const columnName in row) {
28
+ outRow[columnName] = handleMathJsCellStyleTemplate(row, columnName, templates);
29
+ }
30
+ return outRow;
31
+ }
32
+ exports.default = styleRow;
33
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3R5bGUtdGVtcGxhdGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3N0eWxlLXRlbXBsYXRlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUlBLG1DQUFpQztBQUVqQyxvRUFBMEM7QUFDMUMsaUNBQTZCO0FBRTdCLFNBQVMsNkJBQTZCLENBQUMsR0FBa0IsRUFBRSxhQUFxQixFQUFFLFNBQW1DO0lBQ3BILE1BQU0sT0FBTyxHQUFHLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxhQUFhLENBQUMsRUFBRSxRQUFRLEVBQUUsRUFBcUMsQ0FBQTtJQUM1RixJQUFJLGFBQWEsSUFBSSxTQUFTLEVBQUU7UUFFL0IsTUFBTSxLQUFLLEdBQUcsSUFBQSx3QkFBYSxFQUFDLElBQUEsYUFBSyxFQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7UUFDdkQsS0FBSyxNQUFNLEdBQUcsSUFBSSxTQUFTLENBQUMsYUFBYSxDQUFDLEVBQUU7WUFDM0MsSUFBSTtnQkFDSCxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsYUFBYSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFBO2dCQUN0RCxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsSUFBQSxpQkFBUSxFQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsQ0FBQTthQUMxQztZQUFDLE9BQU8sR0FBRyxFQUFFO2dCQUdiLE9BQU8sQ0FBQyxLQUFLLENBQUMsaUNBQWlDLFNBQVMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFBO2FBQ3ZGO1NBQ0Q7S0FDRDtJQUNELE9BQU8sT0FBTyxDQUFBO0FBQ2YsQ0FBQztBQUVELFNBQVMsUUFBUSxDQUFDLEdBQWtCLEVBQUUsU0FBbUM7SUFDeEUsTUFBTSxNQUFNLEdBQUcsRUFBbUIsQ0FBQTtJQUNsQyxLQUFLLE1BQU0sVUFBVSxJQUFJLEdBQUcsRUFBRTtRQUM3QixNQUFNLENBQUMsVUFBVSxDQUFDLEdBQUcsNkJBQTZCLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxTQUFTLENBQUMsQ0FBQTtLQUM5RTtJQUNELE9BQU8sTUFBTSxDQUFBO0FBQ2QsQ0FBQztBQUVELGtCQUFlLFFBQVEsQ0FBQSJ9
@@ -0,0 +1,91 @@
1
+ export type GoogleDataTableCell = {
2
+ v?: any;
3
+ f?: string | undefined;
4
+ p?: any;
5
+ };
6
+ export type GoogleDataTableColumn = {
7
+ type: string;
8
+ id?: string | undefined;
9
+ label?: string | undefined;
10
+ pattern?: string | undefined;
11
+ p?: any;
12
+ };
13
+ export type GoogleDataTableRow = {
14
+ c: GoogleDataTableCell[];
15
+ p?: any;
16
+ };
17
+ export type GoogleDataTable = {
18
+ cols: GoogleDataTableColumn[];
19
+ rows: GoogleDataTableRow[];
20
+ p?: any;
21
+ };
22
+ export type ChartWrapper = {
23
+ options: Record<string, unknown>;
24
+ dataTable?: GoogleDataTable;
25
+ chartType: string;
26
+ };
27
+ export type MultiSeriesOptions = {
28
+ value: string;
29
+ series: string;
30
+ groupXAxisBy: string;
31
+ tooltip?: string;
32
+ };
33
+ export type FormattingRule = {
34
+ type: string;
35
+ format: string;
36
+ };
37
+ export type Formatting = Record<string, FormattingRule>;
38
+ export type FormattingTemplates = Record<string, FormattingTemplate>;
39
+ export type FormattingTemplate = {
40
+ prefix?: string;
41
+ fractionDigits?: number;
42
+ groupingSymbol?: string;
43
+ format?: string;
44
+ };
45
+ export type ProcessedFormatting = {
46
+ [key: string]: {
47
+ type: string;
48
+ format: FormattingTemplate | null;
49
+ };
50
+ };
51
+ export type TableColumn = {
52
+ property: string;
53
+ name: string;
54
+ class: string;
55
+ };
56
+ export type TableRow = {
57
+ [key: string]: unknown;
58
+ };
59
+ export type Table = {
60
+ rows: TableRow[];
61
+ columns: TableColumn[];
62
+ style?: TableStyleTemplateObject;
63
+ filterEnabled?: boolean;
64
+ perPageCount?: number;
65
+ };
66
+ export type TableStyleTemplateObject = Record<string, TableStyleTemplate>;
67
+ export type TableStyleTemplate = {
68
+ class?: string;
69
+ html?: string;
70
+ [key: string]: string | undefined;
71
+ };
72
+ export type ChartSuperType = 'google' | 'table' | 'embed';
73
+ export type Chart = {
74
+ chartId: string;
75
+ query: string;
76
+ multiSeries?: MultiSeriesOptions;
77
+ formatting?: Formatting;
78
+ chartWrapper: ChartWrapper;
79
+ table?: Table;
80
+ cumulative?: boolean;
81
+ greedyHeight?: boolean;
82
+ greedyWidth?: boolean;
83
+ hideTitle?: boolean;
84
+ name: string;
85
+ showChart: boolean;
86
+ supertype: ChartSuperType;
87
+ title: string;
88
+ description?: string;
89
+ rank?: number;
90
+ reportChartId?: number;
91
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdHlwZXMvY2hhcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -0,0 +1,37 @@
1
+ export interface OptionListEntry {
2
+ id: number | string;
3
+ name: string;
4
+ rawSql?: boolean;
5
+ }
6
+ export type OptionList = Array<OptionListEntry>;
7
+ export interface DashboardReportBaseParameter {
8
+ name: string;
9
+ title: string;
10
+ default: string;
11
+ value: string;
12
+ queryValues: Record<string, string | string[]>;
13
+ }
14
+ export interface DashboardReportDateParameter extends DashboardReportBaseParameter {
15
+ type: 'date';
16
+ }
17
+ export interface DashboardReportDateRangeParameter extends DashboardReportBaseParameter {
18
+ type: 'dateRange';
19
+ dates: {
20
+ from: string;
21
+ to: string;
22
+ };
23
+ }
24
+ export interface DashboardReportSelectionParameter extends DashboardReportBaseParameter {
25
+ type: 'selection';
26
+ optionList: OptionList;
27
+ optionListQuery?: string;
28
+ }
29
+ export interface DashboardReportSiteSelectionParameter extends DashboardReportBaseParameter {
30
+ type: 'siteSelection';
31
+ optionList: OptionList;
32
+ permission?: string;
33
+ }
34
+ export interface DashboardReportSessionParameter extends DashboardReportBaseParameter {
35
+ type: 'session';
36
+ }
37
+ export type DashboardReportParameter = DashboardReportDateParameter | DashboardReportSelectionParameter | DashboardReportSiteSelectionParameter | DashboardReportDateRangeParameter | DashboardReportSessionParameter;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyYW1ldGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3R5cGVzL3BhcmFtZXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
@@ -0,0 +1,24 @@
1
+ import { Chart } from './chart';
2
+ import { DashboardReportParameter } from './parameter';
3
+ export interface Report {
4
+ dashboardReportId: number;
5
+ name: string;
6
+ title: string;
7
+ description: string;
8
+ charts: Chart[];
9
+ ownerId: number | null;
10
+ parameterValues: DashboardReportParameter[];
11
+ autoRefreshInterval: number | null;
12
+ }
13
+ export interface ReportInput {
14
+ dashboardReportId: number;
15
+ reportName: string;
16
+ reportTitle: string;
17
+ description?: string;
18
+ shareId: number | null;
19
+ shareType: ShareType;
20
+ ownerId: number | null;
21
+ autoRefreshInterval: number | null;
22
+ parameters: DashboardReportParameter[];
23
+ }
24
+ export type ShareType = 'everyone' | 'owner' | 'user' | 'group';
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwb3J0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3R5cGVzL3JlcG9ydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
@@ -0,0 +1,9 @@
1
+ export type SiteType = 'plant' | 'store' | 'region';
2
+ export interface Session {
3
+ plantId?: number | null;
4
+ storeId?: number | null;
5
+ regionId?: number | null;
6
+ siteId: number | null;
7
+ userAccountId: number | null;
8
+ userName: string;
9
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vzc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eXBlcy9zZXNzaW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
package/package.json CHANGED
@@ -1,24 +1,45 @@
1
1
  {
2
2
  "name": "@isoftdata/utility-dashboard-backend",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "description": "A utility for formatting chart and report data to be usable by the frontend dashboard component.",
5
- "main": "index.js",
5
+ "main": "dist/index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "build": "npx tsc",
9
+ "prepublishOnly": "npm run build"
8
10
  },
9
11
  "author": "Charles Kaup",
10
12
  "license": "ISC",
13
+ "files": [
14
+ "dist/**/*.*"
15
+ ],
16
+ "eslintConfig": {
17
+ "extends": "@isoftdata/typescript",
18
+ "ignorePatterns": "dist/**",
19
+ "rules": {
20
+ "@typescript-eslint/no-var-requires": 0
21
+ }
22
+ },
11
23
  "dependencies": {
12
24
  "@isoftdata/utility-date-time": "^2.0.3",
13
25
  "@isoftdata/utility-db": "^1.4.0",
14
- "camelcase": "^5.3.1",
26
+ "camelcase": "^7.0.1",
15
27
  "date-fns": "^2.23.0",
16
28
  "deepmerge": "^4.2.2",
17
- "klona": "^1.1.2",
29
+ "klona": "^2.0.5",
18
30
  "mathjs": "^11.5.0",
19
31
  "mysql": "^2.18.1",
20
32
  "snakecase-keys": "^5.4.4",
21
33
  "snakeize": "^0.1.0",
22
34
  "to-snake-case": "^1.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@isoftdata/eslint-config-typescript": "^1.0.13",
38
+ "@types/google.visualization": "^0.0.68",
39
+ "@types/mysql": "github:types/mysql",
40
+ "@types/node": "^18.11.18",
41
+ "eslint": "^7.32.0",
42
+ "package-preview": "^4.0.0",
43
+ "typescript": "^4.9.4"
23
44
  }
24
45
  }
package/.gitattributes DELETED
@@ -1,2 +0,0 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto