@wibi-global/sdk 0.1.1 → 0.1.3
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/README.md +0 -0
- package/dist/artifact-schema/src/biCatalog.d.ts +0 -0
- package/dist/artifact-schema/src/biCatalog.d.ts.map +0 -0
- package/dist/artifact-schema/src/biCatalog.js +0 -0
- package/dist/artifact-schema/src/biCatalog.js.map +0 -0
- package/dist/artifact-schema/src/hash.d.ts +0 -0
- package/dist/artifact-schema/src/hash.d.ts.map +0 -0
- package/dist/artifact-schema/src/hash.js +0 -0
- package/dist/artifact-schema/src/hash.js.map +0 -0
- package/dist/artifact-schema/src/index.d.ts +0 -0
- package/dist/artifact-schema/src/index.d.ts.map +0 -0
- package/dist/artifact-schema/src/index.js +0 -0
- package/dist/artifact-schema/src/index.js.map +0 -0
- package/dist/artifact-schema/src/schema.d.ts +0 -0
- package/dist/artifact-schema/src/schema.d.ts.map +0 -0
- package/dist/artifact-schema/src/schema.js +0 -0
- package/dist/artifact-schema/src/schema.js.map +0 -0
- package/dist/artifact-schema/src/types.d.ts +0 -0
- package/dist/artifact-schema/src/types.d.ts.map +0 -0
- package/dist/artifact-schema/src/types.js +0 -0
- package/dist/artifact-schema/src/types.js.map +0 -0
- package/dist/artifact-schema/src/validate.d.ts +0 -0
- package/dist/artifact-schema/src/validate.d.ts.map +0 -0
- package/dist/artifact-schema/src/validate.js +0 -0
- package/dist/artifact-schema/src/validate.js.map +0 -0
- package/dist/instructions/AGENTS.md +47 -0
- package/dist/instructions/AGENT_COMMON.md +0 -0
- package/dist/instructions/CLAUDE.md +9 -0
- package/dist/instructions/CODEX.md +0 -0
- package/dist/instructions/GEMINI.md +0 -0
- package/dist/prompts/authoring.wbp +0 -0
- package/dist/sdk/src/builder.d.ts +0 -0
- package/dist/sdk/src/builder.d.ts.map +0 -0
- package/dist/sdk/src/builder.js +0 -0
- package/dist/sdk/src/builder.js.map +0 -0
- package/dist/sdk/src/context.d.ts +0 -0
- package/dist/sdk/src/context.d.ts.map +0 -0
- package/dist/sdk/src/context.js +0 -0
- package/dist/sdk/src/context.js.map +0 -0
- package/dist/sdk/src/dashboard-context.d.ts +0 -0
- package/dist/sdk/src/dashboard-context.d.ts.map +0 -0
- package/dist/sdk/src/dashboard-context.js +0 -0
- package/dist/sdk/src/dashboard-context.js.map +0 -0
- package/dist/sdk/src/dashboard-spec.d.ts +0 -0
- package/dist/sdk/src/dashboard-spec.d.ts.map +1 -1
- package/dist/sdk/src/dashboard-spec.js +176 -19
- package/dist/sdk/src/dashboard-spec.js.map +1 -1
- package/dist/sdk/src/data/ctx.wbx +0 -0
- package/dist/sdk/src/data/index.d.ts +0 -0
- package/dist/sdk/src/data/index.d.ts.map +1 -1
- package/dist/sdk/src/data/index.js +10 -10
- package/dist/sdk/src/data/index.js.map +1 -1
- package/dist/sdk/src/data/queries.wbx +0 -0
- package/dist/sdk/src/data/serialization/dashboard-serialization.types.d.ts +110 -2
- package/dist/sdk/src/data/serialization/dashboard-serialization.types.d.ts.map +1 -1
- package/dist/sdk/src/data/serialization/dashboard-serialization.types.js +0 -0
- package/dist/sdk/src/data/serialization/dashboard-serialization.types.js.map +0 -0
- package/dist/sdk/src/data/serialization/dashboard-serialization.types.ts +161 -32
- package/dist/sdk/src/data/serialization/index.d.ts +1 -1
- package/dist/sdk/src/data/serialization/index.d.ts.map +1 -1
- package/dist/sdk/src/data/serialization/index.js +0 -0
- package/dist/sdk/src/data/serialization/index.js.map +0 -0
- package/dist/sdk/src/data/syntax.wbx +0 -0
- package/dist/sdk/src/data/view-categories.wbx +0 -0
- package/dist/sdk/src/data/views.wbx +0 -0
- package/dist/sdk/src/data/widgets.wbx +79 -16
- package/dist/sdk/src/data.d.ts +0 -0
- package/dist/sdk/src/data.d.ts.map +0 -0
- package/dist/sdk/src/data.js +0 -0
- package/dist/sdk/src/data.js.map +0 -0
- package/dist/sdk/src/index.d.ts +0 -0
- package/dist/sdk/src/index.d.ts.map +0 -0
- package/dist/sdk/src/index.js +0 -0
- package/dist/sdk/src/index.js.map +0 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -65,6 +65,53 @@ Consulte APENAS estes arquivos locais antes de gerar widgets, queries ou layout:
|
|
|
65
65
|
12. Use o mesmo fluxo de validacao e serializacao tanto para geracao inicial quanto para refinamento incremental.
|
|
66
66
|
13. Nao inclua na resposta final secoes de encerramento como `Proximos Passos`, `Ajustes Recomendados` ou listas operacionais de CLI quando isso ja estiver coberto pelo frontend `DashboardStudio`.
|
|
67
67
|
|
|
68
|
+
## Filtros Internos no progress-list
|
|
69
|
+
|
|
70
|
+
O widget `progress-list` suporta ate 2 filtros dropdown internos (`filter1`, `filter2`) que permitem segmentacao interativa dos dados sem nova query ao banco (filtro client-side).
|
|
71
|
+
|
|
72
|
+
### Quando usar
|
|
73
|
+
|
|
74
|
+
- Quando o usuario precisa filtrar o ranking por uma dimensao (ex: filial, vendedor, periodo, categoria) sem trocar o widget.
|
|
75
|
+
- Cada filtro tem sua propria query SQL para popular as opcoes do select.
|
|
76
|
+
|
|
77
|
+
### Regras obrigatorias
|
|
78
|
+
|
|
79
|
+
1. **`filterField`** deve ser alias camelCase EXATAMENTE igual ao campo nos dados principais (rawData da query `query_id` do widget).
|
|
80
|
+
2. **`valueField`** e **`labelField`** devem ser aliases camelCase dos campos retornados pela query do filtro.
|
|
81
|
+
3. As SQLs de `filter1.sql` e `filter2.sql` suportam as mesmas variaveis da query principal: `${startDate}`, `${endDate}`, `${empresaId}`, `${companyFilter:alias}`.
|
|
82
|
+
4. `showInternalFilters` pode ser omitido (padrao `true` quando filter1/filter2 definidos) ou `false` para ocultar os selects.
|
|
83
|
+
5. O filtro NAO reexecuta a query principal — ele filtra localmente os dados ja carregados.
|
|
84
|
+
|
|
85
|
+
### Exemplo completo
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"widget_type": "progress-list",
|
|
90
|
+
"properties": {
|
|
91
|
+
"labelField": "nomeCliente",
|
|
92
|
+
"valueField": "valorVendas",
|
|
93
|
+
"maxItems": 15,
|
|
94
|
+
"valueFormat": "currency",
|
|
95
|
+
"colorScheme": "gradient",
|
|
96
|
+
"showInternalFilters": true,
|
|
97
|
+
"filter1": {
|
|
98
|
+
"label": "Filial",
|
|
99
|
+
"sql": "SELECT filialId AS value, nomeFilial AS label FROM vw_filiais WHERE empresaId = ${empresaId} ORDER BY nomeFilial",
|
|
100
|
+
"valueField": "value",
|
|
101
|
+
"labelField": "label",
|
|
102
|
+
"filterField": "filialId"
|
|
103
|
+
},
|
|
104
|
+
"filter2": {
|
|
105
|
+
"label": "Vendedor",
|
|
106
|
+
"sql": "SELECT vendedorId AS value, nomeVendedor AS label FROM vw_vendedores WHERE empresaId = ${empresaId} ORDER BY nomeVendedor",
|
|
107
|
+
"valueField": "value",
|
|
108
|
+
"labelField": "label",
|
|
109
|
+
"filterField": "vendedorId"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
68
115
|
## Checklist de requisitos
|
|
69
116
|
|
|
70
117
|
Antes de editar `dashboard.serialized.json`, confirme:
|
|
File without changes
|
|
@@ -47,6 +47,15 @@ Veja regras detalhadas em `AGENTS.md`.
|
|
|
47
47
|
- Em refinamentos, evite recriar o dashboard inteiro; preserve a base existente e aplique diffs pequenos e localizados.
|
|
48
48
|
- A resposta final nao deve incluir blocos como `Proximos Passos` ou `Ajustes Recomendados`.
|
|
49
49
|
|
|
50
|
+
## Filtros Internos no progress-list
|
|
51
|
+
|
|
52
|
+
O widget `progress-list` suporta `filter1` e `filter2`: selects discretos dentro do widget que filtram os dados localmente.
|
|
53
|
+
|
|
54
|
+
- Consulte `AGENTS.md` para as regras completas e exemplo de uso.
|
|
55
|
+
- `filterField` deve ser alias camelCase existente nos dados da query principal do widget.
|
|
56
|
+
- `filter1.sql` / `filter2.sql` suportam `${startDate}`, `${endDate}`, `${empresaId}`, `${companyFilter:alias}`.
|
|
57
|
+
- O filtro e client-side — nao dispara nova query ao banco.
|
|
58
|
+
|
|
50
59
|
## Preferencias de autoria
|
|
51
60
|
|
|
52
61
|
- Prefira editar `dashboard.serialized.json` preservando a estrutura canonica do schema oficial.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/sdk/src/builder.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/sdk/src/context.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-spec.d.ts","sourceRoot":"","sources":["../../../src/dashboard-spec.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dashboard-spec.d.ts","sourceRoot":"","sources":["../../../src/dashboard-spec.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAGV,mBAAmB,EAiBpB,MAAM,uDAAuD,CAAA;AAE9D,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAA;AAE/C,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,6BAA6B,GACrC;IACE,EAAE,EAAE,IAAI,CAAA;IACR,MAAM,EAAE,EAAE,CAAA;CACX,GACD;IACE,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,4BAA4B,EAAE,CAAA;CACvC,CAAA;AAEL,eAAO,MAAM,2BAA2B,EAAG,OAAgB,CAAA;AAC3D,eAAO,MAAM,+BAA+B,EAAG,eAAwB,CAAA;AACvE,eAAO,MAAM,uCAAuC,+BAAgC,CAAA;AACpF,eAAO,MAAM,sCAAsC,EAAG,IAAa,CAAA;AACnE,eAAO,MAAM,sCAAsC,sNAYzC,CAAA;AAwwCV,eAAO,MAAM,qBAAqB,GAAI,MAAM,aAAa,KAAG,6BAmD3D,CAAA;AAED,eAAO,MAAM,wBAAwB,GAAI,MAAM,aAAa,KAAG,aAS9D,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,MAAM,aAAa,KAAG,aAEzD,CAAA"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const widgetSchemaJson = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'data/widgets.wbx'), 'utf8'));
|
|
2
5
|
export const WIBI_DASHBOARD_SPEC_VERSION = '1.0.0';
|
|
3
6
|
export const WIBI_DASHBOARD_SPEC_EXPORT_NAME = 'dashboardSpec';
|
|
4
7
|
export const WIBI_DASHBOARD_TEMPLATE_ALLOWED_IMPORTS = ['@wibi-global/sdk'];
|
|
@@ -18,20 +21,40 @@ export const WIBI_DASHBOARD_REQUIRED_INITIAL_FIELDS = [
|
|
|
18
21
|
];
|
|
19
22
|
const STABLE_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
20
23
|
const FILTER_ID_PATTERN = /^filter-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
21
|
-
const SECTION_ID_PATTERN = /^section-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
22
|
-
const QUERY_ID_PATTERN = /^query-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
24
|
+
const SECTION_ID_PATTERN = /^(?:section|sec)-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
25
|
+
const QUERY_ID_PATTERN = /^(?:query|q)-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
23
26
|
const ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
24
|
-
const WIDGET_TYPES = new Set([
|
|
27
|
+
const WIDGET_TYPES = new Set([
|
|
28
|
+
'kpi',
|
|
29
|
+
'chart',
|
|
30
|
+
'grid',
|
|
31
|
+
'pivot',
|
|
32
|
+
'pivotgrid',
|
|
33
|
+
'progress-list',
|
|
34
|
+
'panel',
|
|
35
|
+
'date',
|
|
36
|
+
'companies',
|
|
37
|
+
'heading',
|
|
38
|
+
'text',
|
|
39
|
+
'image',
|
|
40
|
+
'divider',
|
|
41
|
+
'spacer',
|
|
42
|
+
]);
|
|
25
43
|
/** Widget types that are decorative — no data query required */
|
|
26
44
|
const DECORATIVE_WIDGET_TYPES = new Set(['date', 'companies', 'heading', 'text', 'image', 'divider', 'spacer']);
|
|
27
45
|
const OFFICIAL_DASHBOARD_ITEM_TYPES = new Set((widgetSchemaJson.dashboardItemType?.fields?.type?.values ?? []).filter(Boolean));
|
|
28
46
|
const OFFICIAL_WIDGET_CATALOG = (widgetSchemaJson.widgets ?? {});
|
|
29
|
-
const OFFICIAL_CHART_VARIANTS = new Set(
|
|
30
|
-
.
|
|
31
|
-
|
|
47
|
+
const OFFICIAL_CHART_VARIANTS = new Set([
|
|
48
|
+
...Object.entries(OFFICIAL_WIDGET_CATALOG)
|
|
49
|
+
.filter(([, definition]) => definition.render === 'EChartsChart')
|
|
50
|
+
.map(([type]) => type),
|
|
51
|
+
...((widgetSchemaJson.widgets ?? {}).chart?.propertiesDetailed?.chart_type?.values ?? []),
|
|
52
|
+
].filter(Boolean));
|
|
32
53
|
const SERIALIZED_WIDGET_TO_OFFICIAL_TYPES = {
|
|
33
54
|
chart: ['chart'],
|
|
34
55
|
grid: ['grid'],
|
|
56
|
+
pivot: ['pivot'],
|
|
57
|
+
pivotgrid: ['pivotgrid', 'pivot'],
|
|
35
58
|
kpi: ['kpi'],
|
|
36
59
|
'progress-list': ['progress-list'],
|
|
37
60
|
panel: ['card'],
|
|
@@ -75,6 +98,8 @@ const CHART_ALLOWED_PROPERTY_KEYS = new Set([
|
|
|
75
98
|
'orientation',
|
|
76
99
|
'bar_width',
|
|
77
100
|
'rotate_labels',
|
|
101
|
+
'x_axis_label_rotate',
|
|
102
|
+
'x_axis_date_format',
|
|
78
103
|
'enable_zoom',
|
|
79
104
|
'smooth',
|
|
80
105
|
'show_symbol',
|
|
@@ -108,6 +133,33 @@ const GRID_ALLOWED_PROPERTY_KEYS = new Set([
|
|
|
108
133
|
'empty_message',
|
|
109
134
|
'loading_message',
|
|
110
135
|
]);
|
|
136
|
+
const PIVOT_ALLOWED_PROPERTY_KEYS = new Set([
|
|
137
|
+
'row_field',
|
|
138
|
+
'rowField',
|
|
139
|
+
'col_field',
|
|
140
|
+
'column_field',
|
|
141
|
+
'colField',
|
|
142
|
+
'columnField',
|
|
143
|
+
'value_field',
|
|
144
|
+
'valueField',
|
|
145
|
+
'aggregation',
|
|
146
|
+
'show_totals',
|
|
147
|
+
'showTotals',
|
|
148
|
+
'format',
|
|
149
|
+
'value_format',
|
|
150
|
+
'valueFormat',
|
|
151
|
+
'decimals',
|
|
152
|
+
'currency',
|
|
153
|
+
'locale',
|
|
154
|
+
'searchable',
|
|
155
|
+
'search_placeholder',
|
|
156
|
+
'searchPlaceholder',
|
|
157
|
+
'loading_message',
|
|
158
|
+
'loadingMessage',
|
|
159
|
+
'empty_message',
|
|
160
|
+
'emptyMessage',
|
|
161
|
+
]);
|
|
162
|
+
const PIVOTGRID_ALLOWED_PROPERTY_KEYS = new Set([...PIVOT_ALLOWED_PROPERTY_KEYS]);
|
|
111
163
|
const PANEL_ALLOWED_PROPERTY_KEYS = new Set([
|
|
112
164
|
'panel_type',
|
|
113
165
|
'title',
|
|
@@ -120,17 +172,32 @@ const PANEL_ALLOWED_PROPERTY_KEYS = new Set([
|
|
|
120
172
|
]);
|
|
121
173
|
const PROGRESS_LIST_ALLOWED_PROPERTY_KEYS = new Set([
|
|
122
174
|
'labelField',
|
|
175
|
+
'label_field',
|
|
123
176
|
'valueField',
|
|
177
|
+
'value_field',
|
|
124
178
|
'aggregation',
|
|
125
179
|
'maxItems',
|
|
180
|
+
'max_items',
|
|
126
181
|
'showValues',
|
|
182
|
+
'show_values',
|
|
127
183
|
'showPercentage',
|
|
184
|
+
'show_percentage',
|
|
128
185
|
'valueFormat',
|
|
186
|
+
'value_format',
|
|
129
187
|
'barHeight',
|
|
188
|
+
'bar_height',
|
|
130
189
|
'colorScheme',
|
|
190
|
+
'color_scheme',
|
|
131
191
|
'primaryColor',
|
|
192
|
+
'primary_color',
|
|
132
193
|
'sortBy',
|
|
194
|
+
'sort_by',
|
|
133
195
|
'sortOrder',
|
|
196
|
+
'sort_order',
|
|
197
|
+
'title',
|
|
198
|
+
'showInternalFilters',
|
|
199
|
+
'filter1',
|
|
200
|
+
'filter2',
|
|
134
201
|
]);
|
|
135
202
|
const DATE_WIDGET_ALLOWED_PROPERTY_KEYS = new Set([
|
|
136
203
|
'label',
|
|
@@ -192,6 +259,8 @@ const ACCEPTED_QUERY_PLACEHOLDER_PATTERNS = [
|
|
|
192
259
|
/^\$\{companyFilter:[a-zA-Z_][a-zA-Z0-9_]*\}$/,
|
|
193
260
|
/^\{\{start_date\}\}$/,
|
|
194
261
|
/^\{\{end_date\}\}$/,
|
|
262
|
+
/^\{\{startDate\}\}$/,
|
|
263
|
+
/^\{\{endDate\}\}$/,
|
|
195
264
|
/^\{\{year\}\}$/,
|
|
196
265
|
/^\{\{date_limit_2y\}\}$/,
|
|
197
266
|
];
|
|
@@ -199,12 +268,30 @@ const ALLOWED_MANUAL_QUERY_SOURCES = new Set(['manual-bootstrap']);
|
|
|
199
268
|
const pushIssue = (issues, path, message) => {
|
|
200
269
|
issues.push({ path, message });
|
|
201
270
|
};
|
|
202
|
-
const isNonEmptyString = (value) => value.trim().length > 0;
|
|
271
|
+
const isNonEmptyString = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
203
272
|
const validateNonEmptyString = (issues, path, value, label) => {
|
|
204
273
|
if (!isNonEmptyString(value)) {
|
|
205
274
|
pushIssue(issues, path, `${label} must be a non-empty string.`);
|
|
206
275
|
}
|
|
207
276
|
};
|
|
277
|
+
const getStringAlias = (record, ...keys) => {
|
|
278
|
+
for (const key of keys) {
|
|
279
|
+
const value = record[key];
|
|
280
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
281
|
+
return value;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
};
|
|
286
|
+
const getNumberAlias = (record, ...keys) => {
|
|
287
|
+
for (const key of keys) {
|
|
288
|
+
const value = record[key];
|
|
289
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
290
|
+
return value;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
};
|
|
208
295
|
const validateUniqueStrings = (issues, values, path, label) => {
|
|
209
296
|
const seen = new Set();
|
|
210
297
|
values.forEach((value, index) => {
|
|
@@ -332,6 +419,62 @@ const validateGridProperties = (properties, issues, path) => {
|
|
|
332
419
|
pushIssue(issues, path, 'Official grid definition is missing from widgets.wbx.');
|
|
333
420
|
}
|
|
334
421
|
};
|
|
422
|
+
const validatePivotBaseProperties = (properties, issues, path, widgetType) => {
|
|
423
|
+
validateAllowedPropertyKeys(issues, path, properties, widgetType === 'pivot' ? PIVOT_ALLOWED_PROPERTY_KEYS : PIVOTGRID_ALLOWED_PROPERTY_KEYS, widgetType);
|
|
424
|
+
const propertyRecord = properties;
|
|
425
|
+
const rowField = getStringAlias(propertyRecord, 'row_field', 'rowField');
|
|
426
|
+
const columnField = getStringAlias(propertyRecord, 'col_field', 'column_field', 'colField', 'columnField');
|
|
427
|
+
const valueField = getStringAlias(propertyRecord, 'value_field', 'valueField');
|
|
428
|
+
const aggregation = getStringAlias(propertyRecord, 'aggregation');
|
|
429
|
+
const showTotals = propertyRecord.show_totals ?? propertyRecord.showTotals;
|
|
430
|
+
const format = getStringAlias(propertyRecord, 'format');
|
|
431
|
+
const valueFormat = getStringAlias(propertyRecord, 'value_format', 'valueFormat');
|
|
432
|
+
const decimals = getNumberAlias(propertyRecord, 'decimals');
|
|
433
|
+
const currency = getStringAlias(propertyRecord, 'currency');
|
|
434
|
+
const locale = getStringAlias(propertyRecord, 'locale');
|
|
435
|
+
validateNonEmptyString(issues, `${path}.row_field`, rowField, `${widgetType} row_field`);
|
|
436
|
+
validateNonEmptyString(issues, `${path}.col_field`, columnField, `${widgetType} col_field`);
|
|
437
|
+
validateNonEmptyString(issues, `${path}.value_field`, valueField, `${widgetType} value_field`);
|
|
438
|
+
if (aggregation !== undefined) {
|
|
439
|
+
const validAggregations = new Set(['sum', 'avg', 'count', 'min', 'max']);
|
|
440
|
+
if (!validAggregations.has(aggregation)) {
|
|
441
|
+
pushIssue(issues, `${path}.aggregation`, `${widgetType} aggregation must be one of sum, avg, count, min or max.`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (showTotals !== undefined && typeof showTotals !== 'boolean') {
|
|
445
|
+
pushIssue(issues, `${path}.show_totals`, `${widgetType} show_totals must be a boolean when provided.`);
|
|
446
|
+
}
|
|
447
|
+
if (format !== undefined || valueFormat !== undefined) {
|
|
448
|
+
const effectiveFormat = format ?? valueFormat;
|
|
449
|
+
const validFormats = new Set(['number', 'currency', 'percent']);
|
|
450
|
+
if (typeof effectiveFormat !== 'string' || !validFormats.has(effectiveFormat)) {
|
|
451
|
+
pushIssue(issues, `${path}.format`, `${widgetType} format must be one of number, currency or percent.`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (decimals !== undefined && (!Number.isInteger(decimals) || decimals < 0)) {
|
|
455
|
+
pushIssue(issues, `${path}.decimals`, `${widgetType} decimals must be a non-negative integer when provided.`);
|
|
456
|
+
}
|
|
457
|
+
if (currency !== undefined) {
|
|
458
|
+
validateNonEmptyString(issues, `${path}.currency`, currency, `${widgetType} currency`);
|
|
459
|
+
}
|
|
460
|
+
if (locale !== undefined) {
|
|
461
|
+
validateNonEmptyString(issues, `${path}.locale`, locale, `${widgetType} locale`);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
const validatePivotProperties = (properties, issues, path) => {
|
|
465
|
+
validatePivotBaseProperties(properties, issues, path, 'pivot');
|
|
466
|
+
const officialPivot = OFFICIAL_WIDGET_CATALOG.pivot;
|
|
467
|
+
if (!officialPivot) {
|
|
468
|
+
pushIssue(issues, path, 'Official pivot definition is missing from widgets.wbx.');
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
const validatePivotGridProperties = (properties, issues, path) => {
|
|
472
|
+
validatePivotBaseProperties(properties, issues, path, 'pivotgrid');
|
|
473
|
+
const officialPivotGrid = OFFICIAL_WIDGET_CATALOG.pivotgrid;
|
|
474
|
+
if (!officialPivotGrid) {
|
|
475
|
+
pushIssue(issues, path, 'Official pivotgrid definition is missing from widgets.wbx.');
|
|
476
|
+
}
|
|
477
|
+
};
|
|
335
478
|
const validatePanelProperties = (properties, issues, path) => {
|
|
336
479
|
validateAllowedPropertyKeys(issues, path, properties, PANEL_ALLOWED_PROPERTY_KEYS, 'panel');
|
|
337
480
|
validateNonEmptyString(issues, `${path}.title`, properties.title, 'Panel title');
|
|
@@ -357,32 +500,40 @@ const validatePanelProperties = (properties, issues, path) => {
|
|
|
357
500
|
};
|
|
358
501
|
const validateProgressListProperties = (properties, issues, path) => {
|
|
359
502
|
validateAllowedPropertyKeys(issues, path, properties, PROGRESS_LIST_ALLOWED_PROPERTY_KEYS, 'progress-list');
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
503
|
+
const propertyRecord = properties;
|
|
504
|
+
const labelField = getStringAlias(propertyRecord, 'labelField', 'label_field');
|
|
505
|
+
const valueField = getStringAlias(propertyRecord, 'valueField', 'value_field');
|
|
506
|
+
const maxItems = getNumberAlias(propertyRecord, 'maxItems', 'max_items');
|
|
507
|
+
const valueFormat = getStringAlias(propertyRecord, 'valueFormat', 'value_format');
|
|
508
|
+
const colorScheme = getStringAlias(propertyRecord, 'colorScheme', 'color_scheme');
|
|
509
|
+
const sortBy = getStringAlias(propertyRecord, 'sortBy', 'sort_by');
|
|
510
|
+
const sortOrder = getStringAlias(propertyRecord, 'sortOrder', 'sort_order');
|
|
511
|
+
validateNonEmptyString(issues, `${path}.labelField`, labelField, 'ProgressList labelField');
|
|
512
|
+
validateNonEmptyString(issues, `${path}.valueField`, valueField, 'ProgressList valueField');
|
|
513
|
+
if (maxItems !== undefined && (!Number.isInteger(maxItems) || maxItems <= 0)) {
|
|
363
514
|
pushIssue(issues, `${path}.maxItems`, 'ProgressList maxItems must be a positive integer when provided.');
|
|
364
515
|
}
|
|
365
|
-
if (
|
|
516
|
+
if (valueFormat !== undefined) {
|
|
366
517
|
const validFormats = new Set(['number', 'currency', 'compact', 'percentage']);
|
|
367
|
-
if (!validFormats.has(
|
|
518
|
+
if (!validFormats.has(valueFormat)) {
|
|
368
519
|
pushIssue(issues, `${path}.valueFormat`, 'ProgressList valueFormat must be one of: number, currency, compact, percentage.');
|
|
369
520
|
}
|
|
370
521
|
}
|
|
371
|
-
if (
|
|
522
|
+
if (colorScheme !== undefined) {
|
|
372
523
|
const validSchemes = new Set(['gradient', 'single', 'custom']);
|
|
373
|
-
if (!validSchemes.has(
|
|
524
|
+
if (!validSchemes.has(colorScheme)) {
|
|
374
525
|
pushIssue(issues, `${path}.colorScheme`, 'ProgressList colorScheme must be one of: gradient, single, custom.');
|
|
375
526
|
}
|
|
376
527
|
}
|
|
377
|
-
if (
|
|
528
|
+
if (sortBy !== undefined) {
|
|
378
529
|
const validSortBy = new Set(['value', 'label', 'none']);
|
|
379
|
-
if (!validSortBy.has(
|
|
530
|
+
if (!validSortBy.has(sortBy)) {
|
|
380
531
|
pushIssue(issues, `${path}.sortBy`, 'ProgressList sortBy must be one of: value, label, none.');
|
|
381
532
|
}
|
|
382
533
|
}
|
|
383
|
-
if (
|
|
534
|
+
if (sortOrder !== undefined) {
|
|
384
535
|
const validSortOrder = new Set(['asc', 'desc']);
|
|
385
|
-
if (!validSortOrder.has(
|
|
536
|
+
if (!validSortOrder.has(sortOrder)) {
|
|
386
537
|
pushIssue(issues, `${path}.sortOrder`, 'ProgressList sortOrder must be one of: asc, desc.');
|
|
387
538
|
}
|
|
388
539
|
}
|
|
@@ -485,6 +636,12 @@ const validateWidgetProperties = (widget, issues, path) => {
|
|
|
485
636
|
case 'grid':
|
|
486
637
|
validateGridProperties(widget.properties, issues, `${path}.properties`);
|
|
487
638
|
break;
|
|
639
|
+
case 'pivot':
|
|
640
|
+
validatePivotProperties(widget.properties, issues, `${path}.properties`);
|
|
641
|
+
break;
|
|
642
|
+
case 'pivotgrid':
|
|
643
|
+
validatePivotGridProperties(widget.properties, issues, `${path}.properties`);
|
|
644
|
+
break;
|
|
488
645
|
case 'progress-list':
|
|
489
646
|
validateProgressListProperties(widget.properties, issues, `${path}.properties`);
|
|
490
647
|
break;
|