zinkee 0.1.37 → 0.1.39
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 +298 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/displays.ts +2 -2
- package/src/command-registry.ts +1 -1
- package/src/commands/displays.test.ts +206 -0
- package/src/commands/displays.ts +209 -19
- package/src/utils/examples.ts +172 -36
package/dist/index.js
CHANGED
|
@@ -461,7 +461,7 @@ var commandRegistry = {
|
|
|
461
461
|
command: "displays cross-widget-filter set",
|
|
462
462
|
accessLevel: "write",
|
|
463
463
|
supportsExample: true,
|
|
464
|
-
supportsRaw:
|
|
464
|
+
supportsRaw: true,
|
|
465
465
|
output: writeOutput
|
|
466
466
|
},
|
|
467
467
|
"displays cross-widget-filter delete": {
|
|
@@ -3554,6 +3554,48 @@ var specificExamples = {
|
|
|
3554
3554
|
response: `{
|
|
3555
3555
|
"displayId": "0f6f062f-1478-4df2-96a1-c495d037d260",
|
|
3556
3556
|
"widgetId": "11111111-1111-4111-a111-111111111111"
|
|
3557
|
+
}`
|
|
3558
|
+
},
|
|
3559
|
+
{
|
|
3560
|
+
description: "Create a TABLE widget with locked fields: contact-name locked permanently (no conditions) and status locked while invoice-number is empty, with before/after lock messages.",
|
|
3561
|
+
command: `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type table --name Invoices --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":12,"h":8}' --field contact-name --field invoice-number --field status --field amount --lock-field contact-name --lock-condition "status:invoice-number:is_empty" --lock-message-before "status:Status locks until the invoice number is set." --lock-message-after "status:Status is locked because the invoice number is missing."`,
|
|
3562
|
+
raw: `{
|
|
3563
|
+
"type": "TABLE_OR_SUBTABLE",
|
|
3564
|
+
"source": {
|
|
3565
|
+
"originResourceId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
|
|
3566
|
+
"isOriginSubSchema": false
|
|
3567
|
+
},
|
|
3568
|
+
"placement": {
|
|
3569
|
+
"layout": { "x": 0, "y": 0, "w": 12, "h": 8 }
|
|
3570
|
+
},
|
|
3571
|
+
"presentation": { "name": "Invoices" },
|
|
3572
|
+
"view": {
|
|
3573
|
+
"fields": ["contact-name", "invoice-number", "status", "amount"]
|
|
3574
|
+
},
|
|
3575
|
+
"behavior": {
|
|
3576
|
+
"lockedFields": [
|
|
3577
|
+
{
|
|
3578
|
+
"field": { "fieldId": "contact-name" },
|
|
3579
|
+
"conditions": []
|
|
3580
|
+
},
|
|
3581
|
+
{
|
|
3582
|
+
"field": { "fieldId": "status" },
|
|
3583
|
+
"conditions": [
|
|
3584
|
+
{
|
|
3585
|
+
"origin": { "fieldId": "invoice-number" },
|
|
3586
|
+
"comparisonOperator": "IS_EMPTY",
|
|
3587
|
+
"values": []
|
|
3588
|
+
}
|
|
3589
|
+
],
|
|
3590
|
+
"messageBeforeLock": "Status locks until the invoice number is set.",
|
|
3591
|
+
"messageAfterLock": "Status is locked because the invoice number is missing."
|
|
3592
|
+
}
|
|
3593
|
+
]
|
|
3594
|
+
}
|
|
3595
|
+
}`,
|
|
3596
|
+
response: `{
|
|
3597
|
+
"displayId": "0f6f062f-1478-4df2-96a1-c495d037d260",
|
|
3598
|
+
"widgetId": "11111111-1111-4111-a111-111111111111"
|
|
3557
3599
|
}`
|
|
3558
3600
|
},
|
|
3559
3601
|
{
|
|
@@ -3935,6 +3977,43 @@ var specificExamples = {
|
|
|
3935
3977
|
"hideDeleteButton": true,
|
|
3936
3978
|
"hideFilterButton": true
|
|
3937
3979
|
}
|
|
3980
|
+
}`
|
|
3981
|
+
},
|
|
3982
|
+
{
|
|
3983
|
+
description: "Configure locked fields (replaces the stored configuration): contact-name locked permanently, status locked while invoice-number is empty. Empty conditions = always locked; conditions combine with AND. Enforcement happens in display widgets only, never in the database view.",
|
|
3984
|
+
command: `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --lock-field contact-name --lock-condition "status:invoice-number:is_empty" --lock-message-before "status:Status locks until the invoice number is set." --lock-message-after "status:Status is locked because the invoice number is missing."`,
|
|
3985
|
+
raw: `{
|
|
3986
|
+
"behavior": {
|
|
3987
|
+
"lockedFields": [
|
|
3988
|
+
{
|
|
3989
|
+
"field": { "fieldId": "contact-name" },
|
|
3990
|
+
"conditions": []
|
|
3991
|
+
},
|
|
3992
|
+
{
|
|
3993
|
+
"field": { "fieldId": "status" },
|
|
3994
|
+
"conditions": [
|
|
3995
|
+
{
|
|
3996
|
+
"origin": { "fieldId": "invoice-number" },
|
|
3997
|
+
"comparisonOperator": "IS_EMPTY",
|
|
3998
|
+
"values": []
|
|
3999
|
+
}
|
|
4000
|
+
],
|
|
4001
|
+
"messageBeforeLock": "Status locks until the invoice number is set.",
|
|
4002
|
+
"messageAfterLock": "Status is locked because the invoice number is missing."
|
|
4003
|
+
}
|
|
4004
|
+
]
|
|
4005
|
+
}
|
|
4006
|
+
}`,
|
|
4007
|
+
response: `{
|
|
4008
|
+
"displayId": "0f6f062f-1478-4df2-96a1-c495d037d260",
|
|
4009
|
+
"widgetId": "11111111-1111-4111-a111-111111111111"
|
|
4010
|
+
}`
|
|
4011
|
+
},
|
|
4012
|
+
{
|
|
4013
|
+
description: "Clear the locked-fields configuration.",
|
|
4014
|
+
command: "zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 11111111-1111-4111-a111-111111111111 --clear-locked-fields",
|
|
4015
|
+
raw: `{
|
|
4016
|
+
"behavior": { "lockedFields": [] }
|
|
3938
4017
|
}`
|
|
3939
4018
|
},
|
|
3940
4019
|
{
|
|
@@ -4036,40 +4115,86 @@ var specificExamples = {
|
|
|
4036
4115
|
]),
|
|
4037
4116
|
"displays cross-widget-filter set": buildCommandExamples([
|
|
4038
4117
|
{
|
|
4039
|
-
description: "
|
|
4118
|
+
description: "Single configuration with flags: link one source widget/field to several targets. The whole array is replaced.",
|
|
4040
4119
|
command: "zinkee --json displays cross-widget-filter set 0f6f062f-1478-4df2-96a1-c495d037d260 --source-widget 11111111-1111-4111-a111-111111111111 --source-field c60b8526-5835-4c9f-aecd-9a16c7704a34 --target 22222222-2222-4222-a222-222222222222:c60b8526-5835-4c9f-aecd-9a16c7704a34 --target 33333333-3333-4333-a333-333333333333:c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
4041
|
-
raw: `
|
|
4042
|
-
|
|
4043
|
-
"
|
|
4044
|
-
|
|
4045
|
-
},
|
|
4046
|
-
"targets": [
|
|
4047
|
-
{
|
|
4048
|
-
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
4120
|
+
raw: `[
|
|
4121
|
+
{
|
|
4122
|
+
"source": {
|
|
4123
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
4049
4124
|
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4050
4125
|
},
|
|
4126
|
+
"targets": [
|
|
4127
|
+
{
|
|
4128
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
4129
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4130
|
+
},
|
|
4131
|
+
{
|
|
4132
|
+
"widgetId": "33333333-3333-4333-a333-333333333333",
|
|
4133
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4134
|
+
}
|
|
4135
|
+
]
|
|
4136
|
+
}
|
|
4137
|
+
]`
|
|
4138
|
+
},
|
|
4051
4139
|
{
|
|
4052
|
-
|
|
4140
|
+
description: "Several independent configurations with --raw (each source filters its own targets). A widget cannot be a target of two configurations.",
|
|
4141
|
+
command: `zinkee --json displays cross-widget-filter set 0f6f062f-1478-4df2-96a1-c495d037d260 --raw '[{"source":{"widgetId":"11111111-1111-4111-a111-111111111111","fieldId":"c60b8526-5835-4c9f-aecd-9a16c7704a34"},"targets":[{"widgetId":"22222222-2222-4222-a222-222222222222","fieldId":"c60b8526-5835-4c9f-aecd-9a16c7704a34"}]},{"source":{"widgetId":"44444444-4444-4444-a444-444444444444","fieldId":"a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"},"targets":[{"widgetId":"55555555-5555-4555-a555-555555555555","fieldId":"a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"}]}]'`,
|
|
4142
|
+
raw: `[
|
|
4143
|
+
{
|
|
4144
|
+
"source": {
|
|
4145
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
4053
4146
|
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4054
|
-
}
|
|
4055
|
-
]
|
|
4056
|
-
}`
|
|
4057
4147
|
},
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
4148
|
+
"targets": [
|
|
4149
|
+
{
|
|
4150
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
4151
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4152
|
+
}
|
|
4153
|
+
]
|
|
4065
4154
|
},
|
|
4066
|
-
|
|
4155
|
+
{
|
|
4156
|
+
"source": {
|
|
4157
|
+
"widgetId": "44444444-4444-4444-a444-444444444444",
|
|
4158
|
+
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
4159
|
+
},
|
|
4160
|
+
"targets": [
|
|
4161
|
+
{
|
|
4162
|
+
"widgetId": "55555555-5555-4555-a555-555555555555",
|
|
4163
|
+
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
4164
|
+
}
|
|
4165
|
+
]
|
|
4166
|
+
}
|
|
4167
|
+
]`
|
|
4168
|
+
},
|
|
4067
4169
|
{
|
|
4068
|
-
|
|
4170
|
+
description: "Hierarchy chain with --raw-file: a target of one configuration is the source of the next (click Parent \u2192 filters Child, click Child \u2192 filters Grandchild).",
|
|
4171
|
+
command: "zinkee --json displays cross-widget-filter set 0f6f062f-1478-4df2-96a1-c495d037d260 --raw-file cross-widget-filter.json",
|
|
4172
|
+
raw: `[
|
|
4173
|
+
{
|
|
4174
|
+
"source": {
|
|
4175
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
4176
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4177
|
+
},
|
|
4178
|
+
"targets": [
|
|
4179
|
+
{
|
|
4180
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
4181
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4182
|
+
}
|
|
4183
|
+
]
|
|
4184
|
+
},
|
|
4185
|
+
{
|
|
4186
|
+
"source": {
|
|
4187
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
4069
4188
|
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
4070
|
-
}
|
|
4071
|
-
|
|
4072
|
-
|
|
4189
|
+
},
|
|
4190
|
+
"targets": [
|
|
4191
|
+
{
|
|
4192
|
+
"widgetId": "33333333-3333-4333-a333-333333333333",
|
|
4193
|
+
"fieldId": "a8f1d7e3-2b5c-4d9e-8a1b-3c4d5e6f7890"
|
|
4194
|
+
}
|
|
4195
|
+
]
|
|
4196
|
+
}
|
|
4197
|
+
]`
|
|
4073
4198
|
}
|
|
4074
4199
|
]),
|
|
4075
4200
|
"schemas list": buildCommandExamples([
|
|
@@ -4962,18 +5087,20 @@ var specificExamples = {
|
|
|
4962
5087
|
}
|
|
4963
5088
|
}
|
|
4964
5089
|
],
|
|
4965
|
-
"crossWidgetFilter":
|
|
4966
|
-
|
|
4967
|
-
"
|
|
4968
|
-
|
|
4969
|
-
},
|
|
4970
|
-
"targets": [
|
|
4971
|
-
{
|
|
4972
|
-
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
5090
|
+
"crossWidgetFilter": [
|
|
5091
|
+
{
|
|
5092
|
+
"sourceWidget": {
|
|
5093
|
+
"widgetId": "11111111-1111-4111-a111-111111111111",
|
|
4973
5094
|
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
4974
|
-
}
|
|
4975
|
-
|
|
4976
|
-
|
|
5095
|
+
},
|
|
5096
|
+
"targetWidgets": [
|
|
5097
|
+
{
|
|
5098
|
+
"widgetId": "22222222-2222-4222-a222-222222222222",
|
|
5099
|
+
"fieldId": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
5100
|
+
}
|
|
5101
|
+
]
|
|
5102
|
+
}
|
|
5103
|
+
],
|
|
4977
5104
|
"navigationTargets": [
|
|
4978
5105
|
{
|
|
4979
5106
|
"id": "nav-target-customer",
|
|
@@ -8859,6 +8986,83 @@ function parseFilterExpression(input) {
|
|
|
8859
8986
|
function collectFilters(value, previous = []) {
|
|
8860
8987
|
return [...previous, parseFilterExpression(value)];
|
|
8861
8988
|
}
|
|
8989
|
+
function parseLockConditionOption(input) {
|
|
8990
|
+
const trimmed = input.trim();
|
|
8991
|
+
let lockedField;
|
|
8992
|
+
let expressionText;
|
|
8993
|
+
if (trimmed.includes(":") && !trimmed.includes(" ")) {
|
|
8994
|
+
const separatorIndex = trimmed.indexOf(":");
|
|
8995
|
+
lockedField = trimmed.slice(0, separatorIndex).trim();
|
|
8996
|
+
expressionText = trimmed.slice(separatorIndex + 1).trim();
|
|
8997
|
+
} else {
|
|
8998
|
+
const parts = trimmed.split(/\s+/);
|
|
8999
|
+
lockedField = parts[0]?.trim() ?? "";
|
|
9000
|
+
expressionText = parts.slice(1).join(" ").trim();
|
|
9001
|
+
}
|
|
9002
|
+
if (!lockedField || !expressionText) {
|
|
9003
|
+
throw new InvalidArgumentError3(
|
|
9004
|
+
'Expected "<lockedField>:<field>:<comparator>[:<value>]" (or space-separated), e.g. "status:invoice-number:is_empty".'
|
|
9005
|
+
);
|
|
9006
|
+
}
|
|
9007
|
+
return { lockedField, expression: parseFilterExpression(expressionText) };
|
|
9008
|
+
}
|
|
9009
|
+
function collectLockConditions(value, previous = []) {
|
|
9010
|
+
return [...previous, parseLockConditionOption(value)];
|
|
9011
|
+
}
|
|
9012
|
+
function parseLockMessageOption(input) {
|
|
9013
|
+
const separatorIndex = input.indexOf(":");
|
|
9014
|
+
const lockedField = separatorIndex > 0 ? input.slice(0, separatorIndex).trim() : "";
|
|
9015
|
+
const message = separatorIndex > 0 ? input.slice(separatorIndex + 1).trim() : "";
|
|
9016
|
+
if (!lockedField || !message) {
|
|
9017
|
+
throw new InvalidArgumentError3('Expected "<lockedField>:<message>", e.g. "status:Locked once approved.".');
|
|
9018
|
+
}
|
|
9019
|
+
return { lockedField, message };
|
|
9020
|
+
}
|
|
9021
|
+
function collectLockMessages(value, previous = []) {
|
|
9022
|
+
return [...previous, parseLockMessageOption(value)];
|
|
9023
|
+
}
|
|
9024
|
+
function buildLockedFields(options) {
|
|
9025
|
+
const hasLockFlags = (options.lockField?.length ?? 0) > 0 || (options.lockCondition?.length ?? 0) > 0 || (options.lockMessageBefore?.length ?? 0) > 0 || (options.lockMessageAfter?.length ?? 0) > 0;
|
|
9026
|
+
if (options.clearLockedFields) {
|
|
9027
|
+
if (hasLockFlags) {
|
|
9028
|
+
throw new CliError(
|
|
9029
|
+
"invalid_cli_usage",
|
|
9030
|
+
'Use either "--clear-locked-fields" or the "--lock-*" flags, not both.'
|
|
9031
|
+
);
|
|
9032
|
+
}
|
|
9033
|
+
return [];
|
|
9034
|
+
}
|
|
9035
|
+
if (!hasLockFlags) {
|
|
9036
|
+
return void 0;
|
|
9037
|
+
}
|
|
9038
|
+
const rules = /* @__PURE__ */ new Map();
|
|
9039
|
+
const ensureRule = (lockedField) => {
|
|
9040
|
+
let rule = rules.get(lockedField);
|
|
9041
|
+
if (!rule) {
|
|
9042
|
+
rule = { conditions: [] };
|
|
9043
|
+
rules.set(lockedField, rule);
|
|
9044
|
+
}
|
|
9045
|
+
return rule;
|
|
9046
|
+
};
|
|
9047
|
+
for (const lockedField of options.lockField ?? []) {
|
|
9048
|
+
ensureRule(lockedField);
|
|
9049
|
+
}
|
|
9050
|
+
for (const { lockedField, expression } of options.lockCondition ?? []) {
|
|
9051
|
+
ensureRule(lockedField).conditions.push(toDisplayFilter(expression));
|
|
9052
|
+
}
|
|
9053
|
+
for (const { lockedField, message } of options.lockMessageBefore ?? []) {
|
|
9054
|
+
ensureRule(lockedField).messageBeforeLock = message;
|
|
9055
|
+
}
|
|
9056
|
+
for (const { lockedField, message } of options.lockMessageAfter ?? []) {
|
|
9057
|
+
ensureRule(lockedField).messageAfterLock = message;
|
|
9058
|
+
}
|
|
9059
|
+
return [...rules.entries()].map(([lockedField, rule]) => ({
|
|
9060
|
+
field: { fieldId: lockedField },
|
|
9061
|
+
conditions: rule.conditions,
|
|
9062
|
+
...rule.messageBeforeLock !== void 0 ? { messageBeforeLock: rule.messageBeforeLock } : {},
|
|
9063
|
+
...rule.messageAfterLock !== void 0 ? { messageAfterLock: rule.messageAfterLock } : {}
|
|
9064
|
+
}));
|
|
9065
|
+
}
|
|
8862
9066
|
function readRawObject2(raw, rawFile) {
|
|
8863
9067
|
if (raw && rawFile) {
|
|
8864
9068
|
throw new CliError("invalid_cli_usage", 'Use either "--raw" or "--raw-file", not both.');
|
|
@@ -9189,6 +9393,13 @@ function buildWidgetBody(options) {
|
|
|
9189
9393
|
hideSeeButton: options.hideSeeButton
|
|
9190
9394
|
};
|
|
9191
9395
|
}
|
|
9396
|
+
const lockedFields = buildLockedFields(options);
|
|
9397
|
+
if (lockedFields !== void 0) {
|
|
9398
|
+
body.behavior = {
|
|
9399
|
+
...typeof body.behavior === "object" && body.behavior ? body.behavior : {},
|
|
9400
|
+
lockedFields
|
|
9401
|
+
};
|
|
9402
|
+
}
|
|
9192
9403
|
if (options.calc || options.calcField || options.kpiConfig) {
|
|
9193
9404
|
body.kpi = {
|
|
9194
9405
|
...typeof body.kpi === "object" && body.kpi ? body.kpi : {},
|
|
@@ -9226,20 +9437,54 @@ function buildNavigationTargetBody(options) {
|
|
|
9226
9437
|
}
|
|
9227
9438
|
return body;
|
|
9228
9439
|
}
|
|
9440
|
+
function readRawArray(raw, rawFile) {
|
|
9441
|
+
if (raw && rawFile) {
|
|
9442
|
+
throw new CliError("invalid_cli_usage", 'Use either "--raw" or "--raw-file", not both.');
|
|
9443
|
+
}
|
|
9444
|
+
const source = rawFile ? readFileSync4(rawFile, "utf8") : raw;
|
|
9445
|
+
if (!source) {
|
|
9446
|
+
return [];
|
|
9447
|
+
}
|
|
9448
|
+
try {
|
|
9449
|
+
const parsed = JSON.parse(source);
|
|
9450
|
+
if (!Array.isArray(parsed)) {
|
|
9451
|
+
throw new CliError("invalid_cli_usage", "Raw cross widget filter payload must be a JSON array of configurations.");
|
|
9452
|
+
}
|
|
9453
|
+
return parsed;
|
|
9454
|
+
} catch (error) {
|
|
9455
|
+
if (error instanceof CliError) throw error;
|
|
9456
|
+
throw new CliError("invalid_cli_usage", "Raw JSON payload is invalid.");
|
|
9457
|
+
}
|
|
9458
|
+
}
|
|
9229
9459
|
function buildCrossWidgetFilterBody(options) {
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9460
|
+
if (options.raw !== void 0 || options.rawFile !== void 0) {
|
|
9461
|
+
return readRawArray(options.raw, options.rawFile);
|
|
9462
|
+
}
|
|
9463
|
+
if (!options.sourceWidget || !options.sourceField) {
|
|
9464
|
+
throw new CliError(
|
|
9465
|
+
"invalid_cli_usage",
|
|
9466
|
+
"Provide --source-widget and --source-field, or --raw/--raw-file with the full configuration array."
|
|
9467
|
+
);
|
|
9468
|
+
}
|
|
9469
|
+
const source = {
|
|
9470
|
+
widgetId: options.sourceWidget,
|
|
9471
|
+
fieldId: options.sourceField
|
|
9242
9472
|
};
|
|
9473
|
+
if (options.sourceSchema) {
|
|
9474
|
+
source.schemaId = options.sourceSchema;
|
|
9475
|
+
}
|
|
9476
|
+
return [
|
|
9477
|
+
{
|
|
9478
|
+
source,
|
|
9479
|
+
targets: (options.target ?? []).map((entry) => {
|
|
9480
|
+
const [widgetId, fieldId] = entry.split(":");
|
|
9481
|
+
if (!widgetId || !fieldId) {
|
|
9482
|
+
throw new CliError("invalid_cli_usage", `Invalid --target entry "${entry}". Use <widgetId:fieldId>.`);
|
|
9483
|
+
}
|
|
9484
|
+
return { widgetId, fieldId };
|
|
9485
|
+
})
|
|
9486
|
+
}
|
|
9487
|
+
];
|
|
9243
9488
|
}
|
|
9244
9489
|
function registerDisplaysCommands(program, options = {}) {
|
|
9245
9490
|
const filePath = options.configPath ?? defaultConfigPath;
|
|
@@ -9506,7 +9751,7 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
9506
9751
|
}
|
|
9507
9752
|
printTable([data], void 0, stdout);
|
|
9508
9753
|
});
|
|
9509
|
-
widgets.command("create").description("Create widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--type <type>", "widget type").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>", "chart type (lines, bars, stackedBars, combined)").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>", "time granularity (DATE, MONTH_YEAR, QUARTER_YEAR, YEAR)").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--column-field <uuid>", "KANBAN column field (UUID, not slug)").option("--swimlane-field <uuid>", "KANBAN swimlane field (UUID, not slug)").option("--start-date-field <uuid>", "TIMELINE start date field (UUID, not slug)").option("--end-date-field <uuid>", "TIMELINE end date field (UUID, not slug)").option("--label-field <uuid>", "TIMELINE label field (UUID, not slug)").option("--group-field <uuid>", "TIMELINE group field (UUID, not slug)").option("--subgroup-field <uuid>", "TIMELINE subgroup field (UUID, not slug)").option("--progress-field <uuid>", "TIMELINE progress field (UUID, not slug)").option("--card-title-field <uuid>", "KANBAN/TIMELINE card title field (UUID, not slug)").option("--card-category1-field <uuid>", "KANBAN/TIMELINE card category 1 field (UUID, not slug)").option("--card-category2-field <uuid>", "KANBAN/TIMELINE card category 2 field (UUID, not slug)").option("--card-category3-field <uuid>", "KANBAN card category 3 field (UUID, not slug)").option("--card-category4-field <uuid>", "TIMELINE card category 4 field (UUID, not slug)").option("--card-description-field <uuid>", "KANBAN/TIMELINE card description field (UUID, not slug)").option("--aggregation-method <method>", "KANBAN card aggregation method (e.g. sum)").option("--aggregation-field <uuid>", "KANBAN card aggregation field (UUID, not slug)").option("--view-state <json>", "KANBAN/TIMELINE viewState UI scalars (columnOrder, granularity, sidebarWidth, show* ...)").option("--view-state-file <path>", "read --view-state JSON from a file").option("--hierarchy-label <name>", "HIERARCHY label column name").option("--hierarchy-info <id:name>", "HIERARCHY info column; id is a unique opaque column id you choose (e.g. col_1779975207355_zfndknslw), not the name, and is reused as columnId in mappings (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-number <id:name:aggregation>", "HIERARCHY number column; id is a unique opaque column id (e.g. col_1779975207355_zfndknslw), not the name; aggregation SUM|AVERAGE|MINIMUM|MAXIMUM|COUNT|COUNT_DISTINCT (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-name <name>", "HIERARCHY root level name").option("--hierarchy-root-label-field <uuid>", "HIERARCHY root level label field (UUID, not slug)").option("--hierarchy-root-info <columnId:fieldId>", "HIERARCHY root info mapping; columnId must match a --hierarchy-info id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-number <columnId:fieldId>", "HIERARCHY root number mapping; columnId must match a --hierarchy-number id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-child <json>", "HIERARCHY child level JSON {name,schemaId,childFieldId,labelFieldId,infoMappings,numberMappings} (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-config <json>", "HIERARCHY full hierarchyConfig JSON (escape hatch)").option("--hierarchy-config-file <path>", "read --hierarchy-config JSON from a file").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
|
|
9754
|
+
widgets.command("create").description("Create widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).requiredOption("--type <type>", "widget type").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--lock-field <field>", "lock a field permanently in this widget (repeatable; field UUID or slug)", (v, p = []) => [...p, v], []).option("--lock-condition <expr>", 'conditional lock "<lockedField>:<field>:<comparator>[:<value>]"; locks <lockedField> when all its conditions match (AND, repeatable)', collectLockConditions, []).option("--lock-message-before <field:text>", "message shown while a conditional lock is not met yet (repeatable)", collectLockMessages, []).option("--lock-message-after <field:text>", "message shown once the field is locked (repeatable)", collectLockMessages, []).option("--clear-locked-fields", "clear the widget locked-fields configuration").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>", "chart type (lines, bars, stackedBars, combined)").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>", "time granularity (DATE, MONTH_YEAR, QUARTER_YEAR, YEAR)").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--column-field <uuid>", "KANBAN column field (UUID, not slug)").option("--swimlane-field <uuid>", "KANBAN swimlane field (UUID, not slug)").option("--start-date-field <uuid>", "TIMELINE start date field (UUID, not slug)").option("--end-date-field <uuid>", "TIMELINE end date field (UUID, not slug)").option("--label-field <uuid>", "TIMELINE label field (UUID, not slug)").option("--group-field <uuid>", "TIMELINE group field (UUID, not slug)").option("--subgroup-field <uuid>", "TIMELINE subgroup field (UUID, not slug)").option("--progress-field <uuid>", "TIMELINE progress field (UUID, not slug)").option("--card-title-field <uuid>", "KANBAN/TIMELINE card title field (UUID, not slug)").option("--card-category1-field <uuid>", "KANBAN/TIMELINE card category 1 field (UUID, not slug)").option("--card-category2-field <uuid>", "KANBAN/TIMELINE card category 2 field (UUID, not slug)").option("--card-category3-field <uuid>", "KANBAN card category 3 field (UUID, not slug)").option("--card-category4-field <uuid>", "TIMELINE card category 4 field (UUID, not slug)").option("--card-description-field <uuid>", "KANBAN/TIMELINE card description field (UUID, not slug)").option("--aggregation-method <method>", "KANBAN card aggregation method (e.g. sum)").option("--aggregation-field <uuid>", "KANBAN card aggregation field (UUID, not slug)").option("--view-state <json>", "KANBAN/TIMELINE viewState UI scalars (columnOrder, granularity, sidebarWidth, show* ...)").option("--view-state-file <path>", "read --view-state JSON from a file").option("--hierarchy-label <name>", "HIERARCHY label column name").option("--hierarchy-info <id:name>", "HIERARCHY info column; id is a unique opaque column id you choose (e.g. col_1779975207355_zfndknslw), not the name, and is reused as columnId in mappings (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-number <id:name:aggregation>", "HIERARCHY number column; id is a unique opaque column id (e.g. col_1779975207355_zfndknslw), not the name; aggregation SUM|AVERAGE|MINIMUM|MAXIMUM|COUNT|COUNT_DISTINCT (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-name <name>", "HIERARCHY root level name").option("--hierarchy-root-label-field <uuid>", "HIERARCHY root level label field (UUID, not slug)").option("--hierarchy-root-info <columnId:fieldId>", "HIERARCHY root info mapping; columnId must match a --hierarchy-info id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-number <columnId:fieldId>", "HIERARCHY root number mapping; columnId must match a --hierarchy-number id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-child <json>", "HIERARCHY child level JSON {name,schemaId,childFieldId,labelFieldId,infoMappings,numberMappings} (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-config <json>", "HIERARCHY full hierarchyConfig JSON (escape hatch)").option("--hierarchy-config-file <path>", "read --hierarchy-config JSON from a file").option("--raw <json>").option("--raw-file <path>").action(async function action(display, commandOptions) {
|
|
9510
9755
|
const { api, json, readOnly } = createDisplaysApi(this, filePath);
|
|
9511
9756
|
ensureWritable3("displays widgets create", readOnly, "write");
|
|
9512
9757
|
const data = await api.createWidget(display, buildWidgetBody(commandOptions));
|
|
@@ -9516,7 +9761,7 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
9516
9761
|
}
|
|
9517
9762
|
writeLine4(stdout, `Created widget on display "${display.value}".`);
|
|
9518
9763
|
});
|
|
9519
|
-
widgets.command("update").description("Update widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").option("--type <type>").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>", "chart type (lines, bars, stackedBars, combined)").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>", "time granularity (DATE, MONTH_YEAR, QUARTER_YEAR, YEAR)").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--column-field <uuid>", "KANBAN column field (UUID, not slug)").option("--swimlane-field <uuid>", "KANBAN swimlane field (UUID, not slug)").option("--start-date-field <uuid>", "TIMELINE start date field (UUID, not slug)").option("--end-date-field <uuid>", "TIMELINE end date field (UUID, not slug)").option("--label-field <uuid>", "TIMELINE label field (UUID, not slug)").option("--group-field <uuid>", "TIMELINE group field (UUID, not slug)").option("--subgroup-field <uuid>", "TIMELINE subgroup field (UUID, not slug)").option("--progress-field <uuid>", "TIMELINE progress field (UUID, not slug)").option("--card-title-field <uuid>", "KANBAN/TIMELINE card title field (UUID, not slug)").option("--card-category1-field <uuid>", "KANBAN/TIMELINE card category 1 field (UUID, not slug)").option("--card-category2-field <uuid>", "KANBAN/TIMELINE card category 2 field (UUID, not slug)").option("--card-category3-field <uuid>", "KANBAN card category 3 field (UUID, not slug)").option("--card-category4-field <uuid>", "TIMELINE card category 4 field (UUID, not slug)").option("--card-description-field <uuid>", "KANBAN/TIMELINE card description field (UUID, not slug)").option("--aggregation-method <method>", "KANBAN card aggregation method (e.g. sum)").option("--aggregation-field <uuid>", "KANBAN card aggregation field (UUID, not slug)").option("--view-state <json>", "KANBAN/TIMELINE viewState UI scalars (columnOrder, granularity, sidebarWidth, show* ...)").option("--view-state-file <path>", "read --view-state JSON from a file").option("--hierarchy-label <name>", "HIERARCHY label column name").option("--hierarchy-info <id:name>", "HIERARCHY info column; id is a unique opaque column id you choose (e.g. col_1779975207355_zfndknslw), not the name, and is reused as columnId in mappings (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-number <id:name:aggregation>", "HIERARCHY number column; id is a unique opaque column id (e.g. col_1779975207355_zfndknslw), not the name; aggregation SUM|AVERAGE|MINIMUM|MAXIMUM|COUNT|COUNT_DISTINCT (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-name <name>", "HIERARCHY root level name").option("--hierarchy-root-label-field <uuid>", "HIERARCHY root level label field (UUID, not slug)").option("--hierarchy-root-info <columnId:fieldId>", "HIERARCHY root info mapping; columnId must match a --hierarchy-info id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-number <columnId:fieldId>", "HIERARCHY root number mapping; columnId must match a --hierarchy-number id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-child <json>", "HIERARCHY child level JSON {name,schemaId,childFieldId,labelFieldId,infoMappings,numberMappings} (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-config <json>", "HIERARCHY full hierarchyConfig JSON (escape hatch)").option("--hierarchy-config-file <path>", "read --hierarchy-config JSON from a file").option("--raw <json>").option("--raw-file <path>").action(async function action(display, widgetId, commandOptions) {
|
|
9764
|
+
widgets.command("update").description("Update widget").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).argument("<widget>", "widget id").option("--type <type>").option("--name <name>").option("--description <text>").option("--icon <icon>").option("--color <color>").option("--origin-resource <uuid>").option("--origin-subschema").option("--tab <tabId>").option("--slot <slotId>").option("--layout <json>").option("--layout-file <path>").option("--field <field>", "repeatable field", (v, p = []) => [...p, v], []).option("--sort <sort>", "repeatable sort", (v, p = []) => [...p, v], []).option("--filter <expr>", "repeatable filter", collectFilters, []).option("--widget-read-only").option("--hide-add-button").option("--hide-delete-button").option("--hide-sort-button").option("--hide-filter-button").option("--hide-see-button").option("--lock-field <field>", "lock a field permanently in this widget (repeatable; field UUID or slug)", (v, p = []) => [...p, v], []).option("--lock-condition <expr>", 'conditional lock "<lockedField>:<field>:<comparator>[:<value>]"; locks <lockedField> when all its conditions match (AND, repeatable)', collectLockConditions, []).option("--lock-message-before <field:text>", "message shown while a conditional lock is not met yet (repeatable)", collectLockMessages, []).option("--lock-message-after <field:text>", "message shown once the field is locked (repeatable)", collectLockMessages, []).option("--clear-locked-fields", "clear the widget locked-fields configuration").option("--calc <type>").option("--calc-field <uuid>").option("--kpi-config <json>").option("--chart-type <type>", "chart type (lines, bars, stackedBars, combined)").option("--x-axis-field <uuid>").option("--x-axis-date-format <granularity>", "time granularity (DATE, MONTH_YEAR, QUARTER_YEAR, YEAR)").option("--x-axis-time-series").option("--y-axis-serie <json>", "repeatable y axis serie", (v, p = []) => [...p, v], []).option("--legend <json>").option("--column-field <uuid>", "KANBAN column field (UUID, not slug)").option("--swimlane-field <uuid>", "KANBAN swimlane field (UUID, not slug)").option("--start-date-field <uuid>", "TIMELINE start date field (UUID, not slug)").option("--end-date-field <uuid>", "TIMELINE end date field (UUID, not slug)").option("--label-field <uuid>", "TIMELINE label field (UUID, not slug)").option("--group-field <uuid>", "TIMELINE group field (UUID, not slug)").option("--subgroup-field <uuid>", "TIMELINE subgroup field (UUID, not slug)").option("--progress-field <uuid>", "TIMELINE progress field (UUID, not slug)").option("--card-title-field <uuid>", "KANBAN/TIMELINE card title field (UUID, not slug)").option("--card-category1-field <uuid>", "KANBAN/TIMELINE card category 1 field (UUID, not slug)").option("--card-category2-field <uuid>", "KANBAN/TIMELINE card category 2 field (UUID, not slug)").option("--card-category3-field <uuid>", "KANBAN card category 3 field (UUID, not slug)").option("--card-category4-field <uuid>", "TIMELINE card category 4 field (UUID, not slug)").option("--card-description-field <uuid>", "KANBAN/TIMELINE card description field (UUID, not slug)").option("--aggregation-method <method>", "KANBAN card aggregation method (e.g. sum)").option("--aggregation-field <uuid>", "KANBAN card aggregation field (UUID, not slug)").option("--view-state <json>", "KANBAN/TIMELINE viewState UI scalars (columnOrder, granularity, sidebarWidth, show* ...)").option("--view-state-file <path>", "read --view-state JSON from a file").option("--hierarchy-label <name>", "HIERARCHY label column name").option("--hierarchy-info <id:name>", "HIERARCHY info column; id is a unique opaque column id you choose (e.g. col_1779975207355_zfndknslw), not the name, and is reused as columnId in mappings (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-number <id:name:aggregation>", "HIERARCHY number column; id is a unique opaque column id (e.g. col_1779975207355_zfndknslw), not the name; aggregation SUM|AVERAGE|MINIMUM|MAXIMUM|COUNT|COUNT_DISTINCT (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-name <name>", "HIERARCHY root level name").option("--hierarchy-root-label-field <uuid>", "HIERARCHY root level label field (UUID, not slug)").option("--hierarchy-root-info <columnId:fieldId>", "HIERARCHY root info mapping; columnId must match a --hierarchy-info id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-root-number <columnId:fieldId>", "HIERARCHY root number mapping; columnId must match a --hierarchy-number id (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-child <json>", "HIERARCHY child level JSON {name,schemaId,childFieldId,labelFieldId,infoMappings,numberMappings} (repeatable)", (v, p = []) => [...p, v], []).option("--hierarchy-config <json>", "HIERARCHY full hierarchyConfig JSON (escape hatch)").option("--hierarchy-config-file <path>", "read --hierarchy-config JSON from a file").option("--raw <json>").option("--raw-file <path>").action(async function action(display, widgetId, commandOptions) {
|
|
9520
9765
|
const { api, json, readOnly } = createDisplaysApi(this, filePath);
|
|
9521
9766
|
ensureWritable3("displays widgets update", readOnly, "write");
|
|
9522
9767
|
const data = await api.updateWidget(display, widgetId, buildWidgetBody(commandOptions));
|
|
@@ -9586,9 +9831,9 @@ function registerDisplaysCommands(program, options = {}) {
|
|
|
9586
9831
|
emitJson4(stdout, "displays cross-widget-filter get", item);
|
|
9587
9832
|
return;
|
|
9588
9833
|
}
|
|
9589
|
-
printTable(item
|
|
9834
|
+
printTable(item ?? [], void 0, stdout);
|
|
9590
9835
|
});
|
|
9591
|
-
cross.command("set").description("Set cross widget filter").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).
|
|
9836
|
+
cross.command("set").description("Set cross widget filter (replaces the whole array of configurations)").argument("<display>", "display uuid or slug", parseUuidOrSlugSelector3).option("--source-widget <id>", "source widget id (single-configuration mode)").option("--source-field <id>", "source field id (single-configuration mode)").option("--source-schema <id>", "source schema id, only for HIERARCHY source widgets").option("--target <widget:field>", "repeatable target (single-configuration mode)", (v, p = []) => [...p, v], []).option("--raw <json>", "full array of configurations as JSON (for several configurations)").option("--raw-file <path>", "path to a JSON file with the full array of configurations").action(async function action(display, commandOptions) {
|
|
9592
9837
|
const { api, json, readOnly } = createDisplaysApi(this, filePath);
|
|
9593
9838
|
ensureWritable3("displays cross-widget-filter set", readOnly, "write");
|
|
9594
9839
|
const data = await api.setCrossWidgetFilter(display, buildCrossWidgetFilterBody(commandOptions));
|