@wibi-global/sdk 0.1.2 → 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 +172 -18
- 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 +0 -0
- package/dist/sdk/src/data/index.js +0 -0
- package/dist/sdk/src/data/index.js.map +0 -0
- 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":"AAIA,OAAO,KAAK,EAGV,mBAAmB,
|
|
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"}
|
|
@@ -21,20 +21,40 @@ export const WIBI_DASHBOARD_REQUIRED_INITIAL_FIELDS = [
|
|
|
21
21
|
];
|
|
22
22
|
const STABLE_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
23
23
|
const FILTER_ID_PATTERN = /^filter-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
24
|
-
const SECTION_ID_PATTERN = /^section-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
25
|
-
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]+)*$/;
|
|
26
26
|
const ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
27
|
-
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
|
+
]);
|
|
28
43
|
/** Widget types that are decorative — no data query required */
|
|
29
44
|
const DECORATIVE_WIDGET_TYPES = new Set(['date', 'companies', 'heading', 'text', 'image', 'divider', 'spacer']);
|
|
30
45
|
const OFFICIAL_DASHBOARD_ITEM_TYPES = new Set((widgetSchemaJson.dashboardItemType?.fields?.type?.values ?? []).filter(Boolean));
|
|
31
46
|
const OFFICIAL_WIDGET_CATALOG = (widgetSchemaJson.widgets ?? {});
|
|
32
|
-
const OFFICIAL_CHART_VARIANTS = new Set(
|
|
33
|
-
.
|
|
34
|
-
|
|
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));
|
|
35
53
|
const SERIALIZED_WIDGET_TO_OFFICIAL_TYPES = {
|
|
36
54
|
chart: ['chart'],
|
|
37
55
|
grid: ['grid'],
|
|
56
|
+
pivot: ['pivot'],
|
|
57
|
+
pivotgrid: ['pivotgrid', 'pivot'],
|
|
38
58
|
kpi: ['kpi'],
|
|
39
59
|
'progress-list': ['progress-list'],
|
|
40
60
|
panel: ['card'],
|
|
@@ -78,6 +98,8 @@ const CHART_ALLOWED_PROPERTY_KEYS = new Set([
|
|
|
78
98
|
'orientation',
|
|
79
99
|
'bar_width',
|
|
80
100
|
'rotate_labels',
|
|
101
|
+
'x_axis_label_rotate',
|
|
102
|
+
'x_axis_date_format',
|
|
81
103
|
'enable_zoom',
|
|
82
104
|
'smooth',
|
|
83
105
|
'show_symbol',
|
|
@@ -111,6 +133,33 @@ const GRID_ALLOWED_PROPERTY_KEYS = new Set([
|
|
|
111
133
|
'empty_message',
|
|
112
134
|
'loading_message',
|
|
113
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]);
|
|
114
163
|
const PANEL_ALLOWED_PROPERTY_KEYS = new Set([
|
|
115
164
|
'panel_type',
|
|
116
165
|
'title',
|
|
@@ -123,17 +172,32 @@ const PANEL_ALLOWED_PROPERTY_KEYS = new Set([
|
|
|
123
172
|
]);
|
|
124
173
|
const PROGRESS_LIST_ALLOWED_PROPERTY_KEYS = new Set([
|
|
125
174
|
'labelField',
|
|
175
|
+
'label_field',
|
|
126
176
|
'valueField',
|
|
177
|
+
'value_field',
|
|
127
178
|
'aggregation',
|
|
128
179
|
'maxItems',
|
|
180
|
+
'max_items',
|
|
129
181
|
'showValues',
|
|
182
|
+
'show_values',
|
|
130
183
|
'showPercentage',
|
|
184
|
+
'show_percentage',
|
|
131
185
|
'valueFormat',
|
|
186
|
+
'value_format',
|
|
132
187
|
'barHeight',
|
|
188
|
+
'bar_height',
|
|
133
189
|
'colorScheme',
|
|
190
|
+
'color_scheme',
|
|
134
191
|
'primaryColor',
|
|
192
|
+
'primary_color',
|
|
135
193
|
'sortBy',
|
|
194
|
+
'sort_by',
|
|
136
195
|
'sortOrder',
|
|
196
|
+
'sort_order',
|
|
197
|
+
'title',
|
|
198
|
+
'showInternalFilters',
|
|
199
|
+
'filter1',
|
|
200
|
+
'filter2',
|
|
137
201
|
]);
|
|
138
202
|
const DATE_WIDGET_ALLOWED_PROPERTY_KEYS = new Set([
|
|
139
203
|
'label',
|
|
@@ -195,6 +259,8 @@ const ACCEPTED_QUERY_PLACEHOLDER_PATTERNS = [
|
|
|
195
259
|
/^\$\{companyFilter:[a-zA-Z_][a-zA-Z0-9_]*\}$/,
|
|
196
260
|
/^\{\{start_date\}\}$/,
|
|
197
261
|
/^\{\{end_date\}\}$/,
|
|
262
|
+
/^\{\{startDate\}\}$/,
|
|
263
|
+
/^\{\{endDate\}\}$/,
|
|
198
264
|
/^\{\{year\}\}$/,
|
|
199
265
|
/^\{\{date_limit_2y\}\}$/,
|
|
200
266
|
];
|
|
@@ -202,12 +268,30 @@ const ALLOWED_MANUAL_QUERY_SOURCES = new Set(['manual-bootstrap']);
|
|
|
202
268
|
const pushIssue = (issues, path, message) => {
|
|
203
269
|
issues.push({ path, message });
|
|
204
270
|
};
|
|
205
|
-
const isNonEmptyString = (value) => value.trim().length > 0;
|
|
271
|
+
const isNonEmptyString = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
206
272
|
const validateNonEmptyString = (issues, path, value, label) => {
|
|
207
273
|
if (!isNonEmptyString(value)) {
|
|
208
274
|
pushIssue(issues, path, `${label} must be a non-empty string.`);
|
|
209
275
|
}
|
|
210
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
|
+
};
|
|
211
295
|
const validateUniqueStrings = (issues, values, path, label) => {
|
|
212
296
|
const seen = new Set();
|
|
213
297
|
values.forEach((value, index) => {
|
|
@@ -335,6 +419,62 @@ const validateGridProperties = (properties, issues, path) => {
|
|
|
335
419
|
pushIssue(issues, path, 'Official grid definition is missing from widgets.wbx.');
|
|
336
420
|
}
|
|
337
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
|
+
};
|
|
338
478
|
const validatePanelProperties = (properties, issues, path) => {
|
|
339
479
|
validateAllowedPropertyKeys(issues, path, properties, PANEL_ALLOWED_PROPERTY_KEYS, 'panel');
|
|
340
480
|
validateNonEmptyString(issues, `${path}.title`, properties.title, 'Panel title');
|
|
@@ -360,32 +500,40 @@ const validatePanelProperties = (properties, issues, path) => {
|
|
|
360
500
|
};
|
|
361
501
|
const validateProgressListProperties = (properties, issues, path) => {
|
|
362
502
|
validateAllowedPropertyKeys(issues, path, properties, PROGRESS_LIST_ALLOWED_PROPERTY_KEYS, 'progress-list');
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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)) {
|
|
366
514
|
pushIssue(issues, `${path}.maxItems`, 'ProgressList maxItems must be a positive integer when provided.');
|
|
367
515
|
}
|
|
368
|
-
if (
|
|
516
|
+
if (valueFormat !== undefined) {
|
|
369
517
|
const validFormats = new Set(['number', 'currency', 'compact', 'percentage']);
|
|
370
|
-
if (!validFormats.has(
|
|
518
|
+
if (!validFormats.has(valueFormat)) {
|
|
371
519
|
pushIssue(issues, `${path}.valueFormat`, 'ProgressList valueFormat must be one of: number, currency, compact, percentage.');
|
|
372
520
|
}
|
|
373
521
|
}
|
|
374
|
-
if (
|
|
522
|
+
if (colorScheme !== undefined) {
|
|
375
523
|
const validSchemes = new Set(['gradient', 'single', 'custom']);
|
|
376
|
-
if (!validSchemes.has(
|
|
524
|
+
if (!validSchemes.has(colorScheme)) {
|
|
377
525
|
pushIssue(issues, `${path}.colorScheme`, 'ProgressList colorScheme must be one of: gradient, single, custom.');
|
|
378
526
|
}
|
|
379
527
|
}
|
|
380
|
-
if (
|
|
528
|
+
if (sortBy !== undefined) {
|
|
381
529
|
const validSortBy = new Set(['value', 'label', 'none']);
|
|
382
|
-
if (!validSortBy.has(
|
|
530
|
+
if (!validSortBy.has(sortBy)) {
|
|
383
531
|
pushIssue(issues, `${path}.sortBy`, 'ProgressList sortBy must be one of: value, label, none.');
|
|
384
532
|
}
|
|
385
533
|
}
|
|
386
|
-
if (
|
|
534
|
+
if (sortOrder !== undefined) {
|
|
387
535
|
const validSortOrder = new Set(['asc', 'desc']);
|
|
388
|
-
if (!validSortOrder.has(
|
|
536
|
+
if (!validSortOrder.has(sortOrder)) {
|
|
389
537
|
pushIssue(issues, `${path}.sortOrder`, 'ProgressList sortOrder must be one of: asc, desc.');
|
|
390
538
|
}
|
|
391
539
|
}
|
|
@@ -488,6 +636,12 @@ const validateWidgetProperties = (widget, issues, path) => {
|
|
|
488
636
|
case 'grid':
|
|
489
637
|
validateGridProperties(widget.properties, issues, `${path}.properties`);
|
|
490
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;
|
|
491
645
|
case 'progress-list':
|
|
492
646
|
validateProgressListProperties(widget.properties, issues, `${path}.properties`);
|
|
493
647
|
break;
|