Skip to content

Commit bdc1666

Browse files
timedin-demickenordin
authored andcommitted
fix: selectors for linear scale
Signed-off-by: TimedIn <git@timedin.net>
1 parent b7603cd commit bdc1666

4 files changed

Lines changed: 117 additions & 84 deletions

File tree

docs/DataStructure.md

Lines changed: 77 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ Currently supported Question-Types are:
238238
| `linearscale` | A linear or Likert scale question where you choose an option that best fits your opinion |
239239
| `color` | A color answer, hex string representation (e. g. `#123456`) |
240240
| `ranking` | Using pre-defined options, the user ranks them from most to least preferred. Needs at least one option available. Answers are stored in ranked order (one answer row per option). |
241-
| `conditional` | A conditional branching question with a trigger question and multiple branches containing subquestions |
241+
| `conditional` | A conditional branching question with a trigger question and multiple branches containing subquestions |
242242

243243
## Extra Settings
244244

@@ -281,8 +281,8 @@ Supported option types and their intended usage.
281281
| `column` | Represents a column header when rendering options in a grid-style question. |
282282

283283
`row` and `column` option types are primarily used together with `grid` question types to build a two-dimensional selection matrix. `choice` is the default for normal option lists.
284-
| `triggerType` | `conditional` | string | [See trigger types](#conditional-trigger-types) | The type of trigger question (dropdown, multiple_unique, etc.) |
285-
| `branches` | `conditional` | Array | Array of [Branch objects](#branch-object) | The branches with conditions and subquestions |
284+
| `triggerType` | `conditional` | string | [See trigger types](#conditional-trigger-types) | The type of trigger question (dropdown, multiple_unique, etc.) |
285+
| `branches` | `conditional` | Array | Array of [Branch objects](#branch-object) | The branches with conditions and subquestions |
286286

287287
## Conditional Questions
288288

@@ -292,55 +292,53 @@ Conditional questions enable branching logic in forms. A trigger question determ
292292

293293
Subquestions (questions belonging to a conditional question's branch) have additional properties:
294294

295-
| Property | Type | Description |
296-
| ---------------- | ------- | -------------------------------------------------------- |
295+
| Property | Type | Description |
296+
| ---------------- | ------- | ---------------------------------------------------------------------- |
297297
| parentQuestionId | Integer | The ID of the parent conditional question (null for regular questions) |
298-
| branchId | String | The ID of the branch this subquestion belongs to |
298+
| branchId | String | The ID of the branch this subquestion belongs to |
299299

300300
### Conditional Trigger Types
301301

302302
Supported trigger types for conditional questions:
303303

304-
| Trigger Type | Condition Type | Description |
305-
| ----------------- | -------------------- | ------------------------------------------------ |
306-
| `multiple_unique` | `option_selected` | Radio buttons - single option selection |
307-
| `dropdown` | `option_selected` | Dropdown - single option selection |
308-
| `multiple` | `options_combination`| Checkboxes - all specified options must be selected |
309-
| `short` | `string_equals`, `string_contains`, `regex` | Short text with string/regex matching |
310-
| `long` | `string_contains`, `regex` | Long text with string/regex matching |
311-
| `linearscale` | `value_equals`, `value_range` | Linear scale with value matching |
312-
| `date` | `date_range` | Date with date range matching (YYYY-MM-DD) |
313-
| `time` | `time_range` | Time with time range matching (HH:mm) |
314-
| `color` | `value_equals` | Color with exact value matching |
315-
| `file` | `file_uploaded` | File with upload status matching |
304+
| Trigger Type | Condition Type | Description |
305+
| ----------------- | ------------------------------------------------------------------------------ | --------------------------------------------------- |
306+
| `multiple_unique` | `option_selected` | Radio buttons - single option selection |
307+
| `dropdown` | `option_selected` | Dropdown - single option selection |
308+
| `multiple` | `options_combination` | Checkboxes - all specified options must be selected |
309+
| `short` | `string_equals`, `string_contains`, `regex` | Short text with string/regex matching |
310+
| `long` | `string_contains`, `regex` | Long text with string/regex matching |
311+
| `linearscale` | `value_equals`, `value_not_equals`, `value_range`, `value_min`, `value_max` \* | Linear scale with value matching |
312+
| `date` | `date_range` | Date with date range matching (YYYY-MM-DD) |
313+
| `time` | `time_range` | Time with time range matching (HH:mm) |
314+
| `color` | `value_equals` | Color with exact value matching |
315+
| `file` | `file_uploaded` | File with upload status matching |
316316

317317
### Branch Object
318318

319319
A branch defines conditions and subquestions that appear when those conditions are met.
320320

321-
| Property | Type | Description |
322-
| ------------ | ------------------------------------------- | --------------------------------------------- |
323-
| id | String | Unique identifier for the branch |
324-
| conditions | Array of [Conditions](#condition-object) | Conditions that must be met to show the branch|
325-
| subQuestions | Array of [Questions](#question) | Questions shown when conditions are met |
321+
| Property | Type | Description |
322+
| ------------ | ---------------------------------------- | ---------------------------------------------- |
323+
| id | String | Unique identifier for the branch |
324+
| conditions | Array of [Conditions](#condition-object) | Conditions that must be met to show the branch |
325+
| subQuestions | Array of [Questions](#question) | Questions shown when conditions are met |
326326

327327
```json
328328
{
329-
"id": "branch-1705587600000",
330-
"conditions": [
331-
{ "type": "option_selected", "optionId": 42 }
332-
],
333-
"subQuestions": [
334-
{
335-
"id": 101,
336-
"formId": 3,
337-
"order": 1,
338-
"type": "short",
339-
"text": "Please provide details",
340-
"parentQuestionId": 100,
341-
"branchId": "branch-1705587600000"
342-
}
343-
]
329+
"id": "branch-1705587600000",
330+
"conditions": [{ "type": "option_selected", "optionId": 42 }],
331+
"subQuestions": [
332+
{
333+
"id": 101,
334+
"formId": 3,
335+
"order": 1,
336+
"type": "short",
337+
"text": "Please provide details",
338+
"parentQuestionId": 100,
339+
"branchId": "branch-1705587600000"
340+
}
341+
]
344342
}
345343
```
346344

@@ -359,6 +357,7 @@ Conditions determine when a branch is activated. The structure depends on the tr
359357
```json
360358
{ "type": "options_combination", "optionIds": [42, 43] }
361359
```
360+
362361
All options in `optionIds` must be selected for the branch to activate (AND logic).
363362

364363
#### string_equals (for short text)
@@ -415,36 +414,36 @@ A complete conditional question structure:
415414

416415
```json
417416
{
418-
"id": 100,
419-
"formId": 3,
420-
"order": 1,
421-
"type": "conditional",
422-
"isRequired": true,
423-
"text": "Do you have any dietary restrictions?",
424-
"options": [
425-
{ "id": 42, "questionId": 100, "order": 1, "text": "Yes" },
426-
{ "id": 43, "questionId": 100, "order": 2, "text": "No" }
427-
],
428-
"extraSettings": {
429-
"triggerType": "dropdown",
430-
"branches": [
431-
{
432-
"id": "branch-yes",
433-
"conditions": [{ "type": "option_selected", "optionId": 42 }],
434-
"subQuestions": [
435-
{
436-
"id": 101,
437-
"formId": 3,
438-
"order": 1,
439-
"type": "long",
440-
"text": "Please describe your dietary restrictions",
441-
"parentQuestionId": 100,
442-
"branchId": "branch-yes"
443-
}
444-
]
445-
}
446-
]
447-
}
417+
"id": 100,
418+
"formId": 3,
419+
"order": 1,
420+
"type": "conditional",
421+
"isRequired": true,
422+
"text": "Do you have any dietary restrictions?",
423+
"options": [
424+
{ "id": 42, "questionId": 100, "order": 1, "text": "Yes" },
425+
{ "id": 43, "questionId": 100, "order": 2, "text": "No" }
426+
],
427+
"extraSettings": {
428+
"triggerType": "dropdown",
429+
"branches": [
430+
{
431+
"id": "branch-yes",
432+
"conditions": [{ "type": "option_selected", "optionId": 42 }],
433+
"subQuestions": [
434+
{
435+
"id": 101,
436+
"formId": 3,
437+
"order": 1,
438+
"type": "long",
439+
"text": "Please describe your dietary restrictions",
440+
"parentQuestionId": 100,
441+
"branchId": "branch-yes"
442+
}
443+
]
444+
}
445+
]
446+
}
448447
}
449448
```
450449

@@ -454,16 +453,16 @@ When submitting or storing conditional question answers, the structure differs f
454453

455454
```json
456455
{
457-
"100": {
458-
"trigger": ["42"],
459-
"subQuestions": {
460-
"101": ["Vegetarian, no nuts"]
461-
}
462-
}
456+
"100": {
457+
"trigger": ["42"],
458+
"subQuestions": {
459+
"101": ["Vegetarian, no nuts"]
460+
}
461+
}
463462
}
464463
```
465464

466-
| Property | Type | Description |
467-
| ------------ | --------------------------- | ----------------------------------------------------- |
468-
| trigger | Array of strings | Answer values for the trigger question |
469-
| subQuestions | Object (questionId → Array) | Map of subquestion IDs to their answer value arrays |
465+
| Property | Type | Description |
466+
| ------------ | --------------------------- | --------------------------------------------------- |
467+
| trigger | Array of strings | Answer values for the trigger question |
468+
| subQuestions | Object (questionId → Array) | Map of subquestion IDs to their answer value arrays |

lib/Constants.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,10 @@ class Constants {
248248
public const CONDITION_TYPE_STRING_CONTAINS = 'string_contains';
249249
public const CONDITION_TYPE_REGEX = 'regex';
250250
public const CONDITION_TYPE_VALUE_EQUALS = 'value_equals';
251+
public const CONDITION_TYPE_VALUE_NOT_EQUALS = 'value_not_equals';
251252
public const CONDITION_TYPE_VALUE_RANGE = 'value_range';
253+
public const CONDITION_TYPE_VALUE_MIN = 'value_min';
254+
public const CONDITION_TYPE_VALUE_MAX = 'value_max';
252255
public const CONDITION_TYPE_DATE_RANGE = 'date_range';
253256
public const CONDITION_TYPE_FILE_UPLOADED = 'file_uploaded';
254257

@@ -259,7 +262,10 @@ class Constants {
259262
self::CONDITION_TYPE_STRING_CONTAINS,
260263
self::CONDITION_TYPE_REGEX,
261264
self::CONDITION_TYPE_VALUE_EQUALS,
265+
self::CONDITION_TYPE_VALUE_NOT_EQUALS,
262266
self::CONDITION_TYPE_VALUE_RANGE,
267+
self::CONDITION_TYPE_VALUE_MIN,
268+
self::CONDITION_TYPE_VALUE_MAX,
263269
self::CONDITION_TYPE_DATE_RANGE,
264270
self::CONDITION_TYPE_FILE_UPLOADED,
265271
];
@@ -274,7 +280,7 @@ class Constants {
274280
self::ANSWER_TYPE_MULTIPLE => [self::CONDITION_TYPE_OPTIONS_COMBINATION],
275281
self::ANSWER_TYPE_SHORT => [self::CONDITION_TYPE_STRING_EQUALS, self::CONDITION_TYPE_STRING_CONTAINS, self::CONDITION_TYPE_REGEX],
276282
self::ANSWER_TYPE_LONG => [self::CONDITION_TYPE_STRING_CONTAINS, self::CONDITION_TYPE_REGEX],
277-
self::ANSWER_TYPE_LINEARSCALE => [self::CONDITION_TYPE_VALUE_EQUALS, self::CONDITION_TYPE_VALUE_RANGE],
283+
self::ANSWER_TYPE_LINEARSCALE => [self::CONDITION_TYPE_VALUE_EQUALS, self::CONDITION_TYPE_VALUE_NOT_EQUALS, self::CONDITION_TYPE_VALUE_RANGE, self::CONDITION_TYPE_VALUE_MIN, self::CONDITION_TYPE_VALUE_MAX],
278284
self::ANSWER_TYPE_DATE => [self::CONDITION_TYPE_DATE_RANGE],
279285
self::ANSWER_TYPE_DATETIME => [self::CONDITION_TYPE_DATE_RANGE],
280286
self::ANSWER_TYPE_TIME => [self::CONDITION_TYPE_VALUE_RANGE],

src/components/Questions/BranchConditionEditor.vue

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@
6161
label="label"
6262
:reduce="(opt) => opt.value"
6363
class="condition-type-select" />
64-
<template v-if="conditionType === 'value_equals'">
64+
<template
65+
v-if="
66+
conditionType === 'value_equals'
67+
|| conditionType === 'value_not_equals'
68+
">
6569
<NcTextField
6670
v-model.number="conditionValue"
6771
type="number"
@@ -81,6 +85,20 @@
8185
:placeholder="t('forms', 'Max')"
8286
class="condition-range-input" />
8387
</template>
88+
<template v-else-if="conditionType === 'value_min'">
89+
<NcTextField
90+
v-model.number="conditionMin"
91+
type="number"
92+
:placeholder="t('forms', 'Min')"
93+
class="condition-value-input" />
94+
</template>
95+
<template v-else-if="conditionType === 'value_max'">
96+
<NcTextField
97+
v-model.number="conditionMax"
98+
type="number"
99+
:placeholder="t('forms', 'Max')"
100+
class="condition-value-input" />
101+
</template>
84102
</template>
85103
<template v-else-if="triggerType === 'color'">
86104
<NcColorPicker v-model="conditionValue">
@@ -280,7 +298,10 @@ export default {
280298
valueConditionTypes() {
281299
return [
282300
{ value: 'value_equals', label: t('forms', 'Equals') },
301+
{ value: 'value_not_equals', label: t('forms', 'Not Equals') },
283302
{ value: 'value_range', label: t('forms', 'In range') },
303+
{ value: 'value_min', label: t('forms', 'Higher than') },
304+
{ value: 'value_max', label: t('forms', 'Lower than') },
284305
]
285306
},
286307
@@ -325,7 +346,7 @@ export default {
325346
*/
326347
conditionMin: {
327348
get() {
328-
return this.branch.conditions?.[0]?.min || null
349+
return this.branch.conditions?.[0]?.min || ''
329350
},
330351
331352
set(value) {
@@ -338,7 +359,7 @@ export default {
338359
*/
339360
conditionMax: {
340361
get() {
341-
return this.branch.conditions?.[0]?.max || null
362+
return this.branch.conditions?.[0]?.max || ''
342363
},
343364
344365
set(value) {

src/components/Questions/QuestionConditional.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,8 +1038,15 @@ export default {
10381038
return branch.conditions.some((c) => {
10391039
if (c.type === 'value_equals') {
10401040
return numValue === parseFloat(c.value)
1041-
}
1042-
if (c.type === 'value_range') {
1041+
} else if (c.type === 'value_not_equals') {
1042+
return numValue !== parseFloat(c.value)
1043+
} else if (c.type === 'value_min') {
1044+
const min = c.min ?? Number.MIN_SAFE_INTEGER
1045+
return numValue >= min
1046+
} else if (c.type === 'value_max') {
1047+
const max = c.max ?? Number.MAX_SAFE_INTEGER
1048+
return numValue <= max
1049+
} else if (c.type === 'value_range') {
10431050
const min = c.min ?? Number.MIN_SAFE_INTEGER
10441051
const max = c.max ?? Number.MAX_SAFE_INTEGER
10451052
return numValue >= min && numValue <= max

0 commit comments

Comments
 (0)