@explorer02/cfm-survey-sdk 0.2.1 → 0.2.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/dist/cli/index.js +18 -18
- package/dist/cli/index.mjs +14 -14
- package/dist/index.d.mts +465 -580
- package/dist/index.d.ts +465 -580
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +6 -4
- package/postinstall.js +96 -69
- package/templates/AGENT.md +18 -8
- package/templates/docs/00-integration/analytics-events-catalog.md +54 -0
- package/templates/docs/00-integration/answer-logic-and-navigation.md +50 -0
- package/templates/docs/00-integration/aws-deploy.md +281 -0
- package/templates/docs/00-integration/client-integration-guide.md +351 -0
- package/templates/docs/00-integration/component-checklist.md +55 -22
- package/templates/docs/00-integration/constraints.md +49 -4
- package/templates/docs/00-integration/custom-field-logic-and-navigation.md +174 -0
- package/templates/docs/00-integration/display-logic-and-navigation.md +64 -0
- package/templates/docs/00-integration/end-page-logic.md +64 -0
- package/templates/docs/00-integration/file-upload-aws.md +116 -0
- package/templates/docs/00-integration/logic-fields-catalog.md +105 -0
- package/templates/docs/00-integration/partial-save-and-recovery.md +98 -0
- package/templates/docs/00-integration/placeholders-and-tokens.md +116 -0
- package/templates/docs/00-integration/progress.md +61 -0
- package/templates/docs/00-integration/question-display-variants.md +65 -0
- package/templates/docs/00-integration/question-numbering.md +51 -0
- package/templates/docs/00-integration/question-type-sdk-matrix.md +244 -0
- package/templates/docs/00-integration/setup.md +84 -0
- package/templates/docs/00-integration/skip-logic-and-navigation.md +80 -0
- package/templates/docs/00-integration/survey-lifecycle-analytics.md +94 -0
- package/templates/docs/00-integration/useSurveySDK.md +151 -23
- package/templates/docs/01-components/01-survey-page.md +76 -54
- package/templates/docs/01-components/02-question.md +61 -58
- package/templates/docs/01-components/03-rating-scale.md +11 -1
- package/templates/docs/01-components/04-star-rating-scale.md +45 -0
- package/templates/docs/01-components/05-csat-matrix-scale.md +20 -11
- package/templates/docs/01-components/06-likert-matrix-scale.md +14 -4
- package/templates/docs/01-components/07-slider-matrix-scale.md +11 -2
- package/templates/docs/01-components/08-file-upload-scale.md +23 -51
- package/templates/docs/01-components/09-custom-slider-track.md +2 -2
- package/templates/docs/01-components/11-progress-bar.md +10 -4
- package/templates/docs/01-components/13-matrix-dropdown.md +29 -32
- package/templates/docs/01-components/14-intro-page.md +34 -0
- package/templates/docs/01-components/15-end-page.md +31 -0
- package/templates/docs/01-components/16-paused-page.md +27 -0
- package/templates/docs/01-components/17-heatmap-scale.md +75 -0
- package/templates/docs/01-components/18-rank-order-scale.md +93 -0
- package/templates/docs/01-components/README.md +42 -36
- package/templates/docs/02-reference/config-field-index.md +141 -139
- package/templates/docs/02-reference/exports.md +149 -0
- package/templates/docs/02-reference/question-types/01-rating.md +31 -32
- package/templates/docs/02-reference/question-types/04-csat.md +51 -42
- package/templates/docs/02-reference/question-types/05-rating-scale.md +16 -11
- package/templates/docs/02-reference/question-types/06-slider.md +2 -2
- package/templates/docs/02-reference/question-types/07-matrix-cfm.md +44 -30
- package/templates/docs/02-reference/question-types/08-matrix-csat.md +4 -26
- package/templates/docs/02-reference/question-types/09-matrix-rating.md +29 -13
- package/templates/docs/02-reference/question-types/10-slider-matrix.md +15 -4
- package/templates/docs/02-reference/question-types/11-file-upload.md +43 -17
- package/templates/docs/02-reference/question-types/12-heatmap.md +59 -0
- package/templates/docs/02-reference/question-types/13-rank-order.md +101 -0
- package/templates/docs/02-reference/question-types/README.md +96 -47
- package/templates/docs/02-reference/routing-table.md +33 -18
- package/templates/docs/02-reference/value-derivation.md +63 -0
- package/templates/docs/03-ui-specs/00-question-shell.md +19 -88
- package/templates/docs/03-ui-specs/01-rating.md +18 -52
- package/templates/docs/03-ui-specs/02-radio.md +80 -37
- package/templates/docs/03-ui-specs/03-text.md +2 -2
- package/templates/docs/03-ui-specs/04-csat.md +68 -33
- package/templates/docs/03-ui-specs/05-rating-scale.md +21 -18
- package/templates/docs/03-ui-specs/06-slider.md +3 -1
- package/templates/docs/03-ui-specs/07-matrix-cfm.md +44 -25
- package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +19 -43
- package/templates/docs/03-ui-specs/09-slider-matrix.md +18 -1
- package/templates/docs/03-ui-specs/10-file-upload.md +42 -21
- package/templates/docs/03-ui-specs/11-text-and-media.md +29 -29
- package/templates/docs/03-ui-specs/12-survey-chrome.md +58 -14
- package/templates/docs/03-ui-specs/13-heatmap.md +80 -0
- package/templates/docs/03-ui-specs/14-rank-order.md +128 -0
- package/templates/docs/03-ui-specs/README.md +16 -14
- package/templates/docs/03-ui-specs/shared/custom-slider-track.md +28 -35
- package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +5 -0
- package/templates/docs/MANIFEST.json +476 -69
- package/templates/docs/index.md +82 -30
- package/templates/docs/templates/CustomSliderTrack.tsx +144 -0
- package/templates/docs/templates/MatrixDropdown.tsx +216 -0
- package/templates/docs/templates/Question.tsx +221 -154
- package/templates/docs/templates/RankOrderScale.tsx +353 -0
- package/templates/docs/templates/deploy-to-aws.sh +114 -0
- package/templates/docs/templates/heatmapCoords.ts +58 -0
- package/templates/docs/templates/implementation_plan.md +126 -67
- package/templates/docs/templates/survey-inventory.schema.json +77 -0
- package/templates/docs/templates/surveyUiIcons.tsx +52 -0
- package/templates/docs/templates/verify-agent-build.sh +85 -0
- package/templates/docs/01-components/04-csat-scale.md +0 -40
- package/templates/docs/02-question-types/01-rating.md +0 -44
|
@@ -1,34 +1,60 @@
|
|
|
1
1
|
# File Upload Question
|
|
2
2
|
|
|
3
|
-
> **Type**: `'
|
|
3
|
+
> **Type**: `'FILE_UPLOAD'` | **API Source**: `FILE_UPLOAD`
|
|
4
4
|
> **Mapper**: `fileUploadMapper.ts` | **Component**: `FileUploadScale`
|
|
5
|
+
> **Upload flow**: [`00-integration/file-upload-aws.md`](../../00-integration/file-upload-aws.md)
|
|
5
6
|
|
|
6
7
|
## TypeScript Type
|
|
7
8
|
|
|
8
9
|
```typescript
|
|
9
10
|
type FileUploadQuestion = QuestionBase & {
|
|
10
|
-
type: '
|
|
11
|
-
uploadMessage?: string;
|
|
12
|
-
supportedFileFormats?: string[];
|
|
13
|
-
maxFileCount?: number;
|
|
14
|
-
fileSizeLimit?: number;
|
|
11
|
+
type: 'FILE_UPLOAD';
|
|
12
|
+
uploadMessage?: string;
|
|
13
|
+
supportedFileFormats?: string[];
|
|
14
|
+
maxFileCount?: number;
|
|
15
|
+
fileSizeLimit?: number;
|
|
15
16
|
fileSizeLimitType?: 'PER_FILE' | 'IN_TOTAL';
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
/** Stored in state.answers — required for submit */
|
|
20
|
+
type UploadedFileAnswer = {
|
|
21
|
+
id: string;
|
|
22
|
+
mediaUrl: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
mimeType?: string;
|
|
25
|
+
size?: number;
|
|
21
26
|
};
|
|
22
27
|
```
|
|
23
28
|
|
|
24
|
-
## Answer Shape: `
|
|
25
|
-
|
|
29
|
+
## Answer Shape: `UploadedFileAnswer[]`
|
|
30
|
+
|
|
31
|
+
Submitted via `quesIdVsAttachmentDetails` (NOT `questionToAnswers`). SDK submit formatter only accepts objects with `id` and `mediaUrl` — **raw browser `File[]` is dropped at submit**.
|
|
32
|
+
|
|
33
|
+
## Two-phase client flow
|
|
34
|
+
|
|
35
|
+
1. **Pick + validate locally** — extension, count, size (`PER_FILE` / `IN_TOTAL`)
|
|
36
|
+
2. **Upload each file** — `POST /get-upload-url` → presigned PUT → collect `{ id, mediaUrl, name, mimeType, size }`
|
|
37
|
+
3. **`onAction(CHANGE)`** — pass `UploadedFileAnswer[]` to SDK
|
|
38
|
+
|
|
39
|
+
See [`file-upload-aws.md`](../../00-integration/file-upload-aws.md) for API contract.
|
|
26
40
|
|
|
27
41
|
## Rendering Guidance
|
|
28
|
-
|
|
42
|
+
|
|
43
|
+
- Drag-and-drop zone + click-to-upload
|
|
29
44
|
- Show accepted formats from `supportedFileFormats`
|
|
30
|
-
- Set `<input type="file" accept=".pdf,.png
|
|
31
|
-
- Enforce `maxFileCount`
|
|
32
|
-
- Show file list with name, size,
|
|
33
|
-
-
|
|
34
|
-
|
|
45
|
+
- Set `<input type="file" accept=".pdf,.png,...">` from formats
|
|
46
|
+
- Enforce `maxFileCount` and `fileSizeLimit` before upload
|
|
47
|
+
- Show file list with name, size, remove button, uploading spinner per file
|
|
48
|
+
- `onSelect(undefined)` when all files removed
|
|
49
|
+
|
|
50
|
+
## Skip logic
|
|
51
|
+
|
|
52
|
+
Presence-only (`EXISTS` / `MISSING`) — evaluated on page entry; no discrete value-change events required.
|
|
53
|
+
|
|
54
|
+
## SDK Integration Checklist (agent)
|
|
55
|
+
|
|
56
|
+
- [ ] Type literal: `QUESTION_TYPE.FILE_UPLOAD`
|
|
57
|
+
- [ ] Answer stored as: `UploadedFileAnswer[]` after upload completes
|
|
58
|
+
- [ ] onAction CHANGE: `{ questionId, answerValue: UploadedFileAnswer[] }`
|
|
59
|
+
- [ ] Do **not** store raw `File[]` as final answer
|
|
60
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#file_upload`](../../00-integration/question-type-sdk-matrix.md#file_upload)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Heatmap Question (`HEATMAP`)
|
|
2
|
+
|
|
3
|
+
> **SDK type:** `HeatmapQuestion` in `fetchSurvey/types/heatmap.ts`
|
|
4
|
+
> **UI spec:** `03-ui-specs/13-heatmap.md`
|
|
5
|
+
|
|
6
|
+
## API Source
|
|
7
|
+
|
|
8
|
+
- `answerType: "HEATMAP"`
|
|
9
|
+
- `questionAssets[0]` → image URL
|
|
10
|
+
- `surveyHeatmapMetadata` → regions, max clicks, canvas dimensions
|
|
11
|
+
|
|
12
|
+
## Question Fields
|
|
13
|
+
|
|
14
|
+
| Field | Type | Description |
|
|
15
|
+
|-------|------|-------------|
|
|
16
|
+
| `type` | `'HEATMAP'` | Dispatch literal |
|
|
17
|
+
| `imageUrl` | `string` | Required image for click map |
|
|
18
|
+
| `regions` | `HeatmapRegion[]` | Normalized 0–1 rects for logic |
|
|
19
|
+
| `maxClicksAllowed` | `number` | 1–20, from `totalClickAllowed` |
|
|
20
|
+
| `referenceWidth` | `number` | Canvas width used for region normalization |
|
|
21
|
+
| `referenceHeight` | `number` | Canvas height used for region normalization |
|
|
22
|
+
|
|
23
|
+
## Answer Shape
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
type HeatmapClickPoint = { id: string; x: number; y: number };
|
|
27
|
+
type HeatmapAnswer = HeatmapClickPoint[];
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Stored in `state.answers[questionId]`. Submit serializes to:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
questionToAnswers[questionId] = [JSON.stringify(clicks)];
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Logic Fields
|
|
37
|
+
|
|
38
|
+
| Field | Operators | Semantics |
|
|
39
|
+
|-------|-----------|-----------|
|
|
40
|
+
| `HEATMAP` | EXISTS / MISSING | Any click points present |
|
|
41
|
+
| `HEATMAP_REGION` | CONTAINS / DOES_NOT_CONTAIN | Any listed region label has ≥1 click inside |
|
|
42
|
+
|
|
43
|
+
Logic values use region **labels** (e.g. `"Region 1"`), not shape ids.
|
|
44
|
+
|
|
45
|
+
## Component
|
|
46
|
+
|
|
47
|
+
`HeatmapScale` — see `01-components/17-heatmap-scale.md`
|
|
48
|
+
|
|
49
|
+
## SDK Integration Checklist (agent)
|
|
50
|
+
|
|
51
|
+
- [ ] Type literal: `QUESTION_TYPE.HEATMAP`
|
|
52
|
+
- [ ] Answer stored as: `HeatmapAnswer` (`HeatmapClickPoint[]`)
|
|
53
|
+
- [ ] onAction CHANGE payload: `{ questionId, answerValue: HeatmapClickPoint[] }` on click add/remove
|
|
54
|
+
- [ ] Visibility helper: N/A
|
|
55
|
+
- [ ] Custom fields: skip/display rules only
|
|
56
|
+
- [ ] Logic features: skip ✓ display ✓ answer ✗ variants ✓ numbering ✓
|
|
57
|
+
- [ ] Performance: normalize coords 0–1; copy [`templates/heatmapCoords.ts`](../../templates/heatmapCoords.ts)
|
|
58
|
+
- [ ] Respect `maxClicksAllowed`; use `findSpotNearClick` for spot hit testing
|
|
59
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#heatmap`](../../00-integration/question-type-sdk-matrix.md#heatmap)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Rank Order Question (`RANK_ORDER`)
|
|
2
|
+
|
|
3
|
+
> **SDK type:** `RankOrderQuestion` in `fetchSurvey/types/rankOrder.ts`
|
|
4
|
+
> **UI spec:** `03-ui-specs/14-rank-order.md`
|
|
5
|
+
|
|
6
|
+
## API Source
|
|
7
|
+
|
|
8
|
+
- `answerType: "RANK_ORDER"`
|
|
9
|
+
- `answerDetails[]` → options (text + optional `answerAsset`)
|
|
10
|
+
- `questionConfigs`:
|
|
11
|
+
- `RANK_ORDER_ANSWER_TYPE` → `DROPDOWN` | `DRAG_AND_DROP`
|
|
12
|
+
- `OPTION_TYPE` → `TEXT_ONLY` | `IMAGE_ONLY` | `TEXT_AND_IMAGE`
|
|
13
|
+
- `REQUIRE_RANK_ALL` → boolean
|
|
14
|
+
- `SHUFFLE_ORDER` → boolean
|
|
15
|
+
- `additionalInfo.hasOptionLevelAnswers: true` → submit uses `optionLevelAnswers`
|
|
16
|
+
|
|
17
|
+
## Question Fields
|
|
18
|
+
|
|
19
|
+
| Field | Type | API config |
|
|
20
|
+
|-------|------|------------|
|
|
21
|
+
| `type` | `'RANK_ORDER'` | `answerType` |
|
|
22
|
+
| `options` | `RankOrderOption[]` | `answerDetails[]` |
|
|
23
|
+
| `interactionMode` | `'dropdown' \| 'dragAndDrop'` | `RANK_ORDER_ANSWER_TYPE` |
|
|
24
|
+
| `optionDisplay` | `'textOnly' \| 'imageOnly' \| 'textAndImage'` | `OPTION_TYPE` |
|
|
25
|
+
| `requireRankAll` | `boolean` | `REQUIRE_RANK_ALL` |
|
|
26
|
+
| `shuffleOptions` | `boolean` | `SHUFFLE_ORDER` |
|
|
27
|
+
|
|
28
|
+
### RankOrderOption
|
|
29
|
+
|
|
30
|
+
| Field | Source |
|
|
31
|
+
|-------|--------|
|
|
32
|
+
| `id` | `answerDetails[].id` |
|
|
33
|
+
| `optionLabel` | `answerDetails[].answerText` |
|
|
34
|
+
| `imageUrl` | `answerAsset.source` |
|
|
35
|
+
| `previewImageUrl` | `answerAsset.previewImageUrl` |
|
|
36
|
+
| `answerLogic` | option `controlConfig.displayLogic` |
|
|
37
|
+
|
|
38
|
+
## Answer Shape
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
/** optionId → 1-based rank. Partial map allowed when requireRankAll is false. */
|
|
42
|
+
type RankOrderAnswers = Record<string, number>;
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Stored in `state.answers[questionId]`.
|
|
46
|
+
|
|
47
|
+
## Submit Shape
|
|
48
|
+
|
|
49
|
+
Both fields are populated for rank-order questions:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Ordered option ids from rank 1 → N
|
|
53
|
+
questionToAnswers[questionId] = ['optionIdRank1', 'optionIdRank2', ...];
|
|
54
|
+
|
|
55
|
+
// Per-option rank (API key uses -:- separator)
|
|
56
|
+
optionLevelAnswers[`${questionId}-:-${optionId}`] = [String(rank)];
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Logic Field: `RANK_ORDER`
|
|
60
|
+
|
|
61
|
+
Conditions target a **specific option** via `details.subConditionEntity` (`entityType: 'OPTION'`).
|
|
62
|
+
|
|
63
|
+
| UI label | API operator | Values | Semantics |
|
|
64
|
+
|----------|--------------|--------|-----------|
|
|
65
|
+
| Ranked as | `EQUALS` | rank number | Subject option rank equals value |
|
|
66
|
+
| Ranked more than | `GT` | rank number | Subject rank > value |
|
|
67
|
+
| Ranked less than | `LT` | rank number | Subject rank < value |
|
|
68
|
+
| Ranked above | `BEFORE` | reference option id | Subject rank < reference rank |
|
|
69
|
+
| Ranked below | `AFTER` | reference option id | Subject rank > reference rank |
|
|
70
|
+
|
|
71
|
+
Also supports `EXISTS` / `MISSING` for the whole question answer.
|
|
72
|
+
|
|
73
|
+
Works in skip, display, answer, question variant, and end-page logic via the shared `evaluateCondition` pipeline.
|
|
74
|
+
|
|
75
|
+
## SDK Helpers (client-safe)
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import {
|
|
79
|
+
assignRankWithoutDuplicates,
|
|
80
|
+
buildRankOrderFromOrderedOptionIds,
|
|
81
|
+
getOrderedOptionIdsFromRanks,
|
|
82
|
+
getVisibleRankOrderOptionsForAnswers,
|
|
83
|
+
normalizeRankOrderAnswers,
|
|
84
|
+
} from '@explorer02/cfm-survey-sdk';
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Component
|
|
88
|
+
|
|
89
|
+
`RankOrderScale` — see `01-components/18-rank-order-scale.md`
|
|
90
|
+
|
|
91
|
+
## SDK Integration Checklist (agent)
|
|
92
|
+
|
|
93
|
+
- [ ] Type literal: `QUESTION_TYPE.RANK_ORDER`
|
|
94
|
+
- [ ] Answer stored as: `RankOrderAnswers` (`Record<optionId, rank>`)
|
|
95
|
+
- [ ] onAction CHANGE payload: `{ questionId, answerValue: RankOrderAnswers }` after reorder
|
|
96
|
+
- [ ] Visibility helper: `getVisibleRankOrderOptionsForAnswers(question, answers, allQuestions, customFieldValues?)`
|
|
97
|
+
- [ ] Custom fields: 4th arg on rank visibility helper when logic references CRM fields
|
|
98
|
+
- [ ] Logic features: skip ✓ display ✓ answer ✓ (options) variants ✓ numbering ✓
|
|
99
|
+
- [ ] Performance: prefer `option.imageUrl` over `previewImageUrl`; install `@dnd-kit/*` for drag mode
|
|
100
|
+
- [ ] Value utils: `assignRankWithoutDuplicates`, `buildRankOrderFromOrderedOptionIds`, `getOrderedOptionIdsFromRanks`, `normalizeRankOrderAnswers`
|
|
101
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#rank_order`](../../00-integration/question-type-sdk-matrix.md#rank_order)
|
|
@@ -1,71 +1,120 @@
|
|
|
1
1
|
# Question Types Reference
|
|
2
2
|
|
|
3
3
|
> Read **on demand** — only for types present in the fetched survey.
|
|
4
|
-
> UI implementation: `01-components/`. Routing table: `02-reference/routing-table.md`.
|
|
5
4
|
|
|
6
|
-
## Discriminated Union
|
|
5
|
+
## Discriminated Union (11 types)
|
|
7
6
|
|
|
8
7
|
```typescript
|
|
9
8
|
type SurveyQuestion =
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
| CsatQuestion // '
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
9
|
+
| McqQuestion // 'MCQ'
|
|
10
|
+
| NpsQuestion // 'NPS_SCALE'
|
|
11
|
+
| TextInputQuestion // 'TEXTFIELD'
|
|
12
|
+
| CsatQuestion // 'CSAT_MATRIX'
|
|
13
|
+
| CfmMatrixQuestion // 'CFM_MATRIX'
|
|
14
|
+
| RatingMatrixQuestion // 'RATING_MATRIX'
|
|
15
|
+
| SliderMatrixQuestion // 'SLIDER_MATRIX'
|
|
16
|
+
| FileUploadQuestion // 'FILE_UPLOAD'
|
|
17
|
+
| TextAndMediaQuestion // 'TEXT_AND_MEDIA'
|
|
18
|
+
| HeatmapQuestion // 'HEATMAP'
|
|
19
|
+
| RankOrderQuestion; // 'RANK_ORDER'
|
|
21
20
|
```
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
Use `QUESTION_TYPE` from the SDK for dispatch comparisons.
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
24
|
+
Each type is **standalone** — open its file in exported SDK types for the complete field list.
|
|
25
|
+
|
|
26
|
+
> **Legacy:** `StarRatingQuestion` / `SliderQuestion` mapper types are not in the `SurveyQuestion` union. API RATING/SLIDER pages map into `NPS_SCALE`, matrix display styles, or `SLIDER_MATRIX`.
|
|
27
|
+
|
|
28
|
+
## Shared field names (duplicated per type file)
|
|
29
|
+
|
|
30
|
+
| Property | Meaning |
|
|
31
|
+
|----------|---------|
|
|
32
|
+
| `id` | DOM `id` on wrapper |
|
|
33
|
+
| `questionText` | Main prompt (HTML) |
|
|
34
|
+
| `questionDescription?` | Supplementary HTML |
|
|
35
|
+
| `questionHint?` | Hint text |
|
|
36
|
+
| `required` | Mandatory flag |
|
|
37
|
+
| `requiredErrorMessage?` | Custom validation message |
|
|
38
|
+
| `questionNumber?` | 1-indexed position |
|
|
39
|
+
| `skipLogics?` | Forward skip rules evaluated on page leave |
|
|
40
|
+
| `displayLogic?` | Question-level show/hide (dynamic or page-level) |
|
|
41
|
+
| `displayVariants?` | Alternate `questionText` prompts selected by condition (SDK resolves on `currentQuestions`; see [`question-display-variants.md`](../../00-integration/question-display-variants.md)) |
|
|
42
|
+
| `answerLogic?` | On `McqOption`, `StatementRow`, or `ScaleColumn` — sub-entity show/hide (see [`answer-logic-and-navigation.md`](../../00-integration/answer-logic-and-navigation.md)) |
|
|
33
43
|
|
|
34
44
|
---
|
|
35
45
|
|
|
36
|
-
##
|
|
46
|
+
## MCQ {#mcq}
|
|
47
|
+
|
|
48
|
+
- **Type:** `McqQuestion` — `type: 'MCQ'`
|
|
49
|
+
- **Answer:** `string` or `string[]` (`option.id` values)
|
|
50
|
+
- **Options:** `McqOption[]` with `id`, `optionLabel`
|
|
51
|
+
|
|
52
|
+
| Field | Type | API source |
|
|
53
|
+
|-------|------|------------|
|
|
54
|
+
| `selectionMode` | `'single' \| 'multiple'` | multi-select config |
|
|
55
|
+
| `defaultOptionIds?` | `string[]` | `DEFAULT_ANSWER` when enabled |
|
|
56
|
+
| `minSelections?` | `number` | `ANSWER_CONDITION` / `MIN_ANSWER` |
|
|
57
|
+
| `maxSelections?` | `number` | `ANSWER_CONDITION` / `MAX_ANSWER` |
|
|
58
|
+
| `answerConditionErrorMessage?` | `string` | `ANSWER_CONDITION` error message |
|
|
59
|
+
|
|
60
|
+
**Default answers:** On survey load, SDK `buildInitialAnswers` / `applyMcqDefaults` pre-populates `state.answers[questionId]` from `defaultOptionIds` (single: first id; multi: full array). Client binds UI checked state to `state.answers` — do not maintain separate default state. SDK re-applies defaults on validation/submit if answer is still empty.
|
|
37
61
|
|
|
38
|
-
|
|
39
|
-
- **Answer:** `string | number` (option id or value)
|
|
40
|
-
- **UI:** Inline MCQ radio cards in `Question.tsx` — see `01-components/02-question.md`
|
|
41
|
-
- **No separate reference doc needed**
|
|
62
|
+
### SDK Integration Checklist (agent)
|
|
42
63
|
|
|
43
|
-
|
|
64
|
+
- [ ] Type literal: `QUESTION_TYPE.MCQ`
|
|
65
|
+
- [ ] Answer stored as: `option.id` (string/number) or array when `selectionMode === 'multiple'`
|
|
66
|
+
- [ ] onAction CHANGE payload: `{ questionId, answerValue: optionId | optionId[] }`
|
|
67
|
+
- [ ] Visibility helper: `getVisibleMcqOptionsForAnswers(question, answers, allQuestions, customFieldValues?)`
|
|
68
|
+
- [ ] Custom fields: 4th arg when logic references CRM fields
|
|
69
|
+
- [ ] Logic features: skip ✓ display ✓ answer ✓ (options) variants ✓ numbering ✓
|
|
70
|
+
- [ ] Performance: filter options via helper before render
|
|
71
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#mcq`](../../00-integration/question-type-sdk-matrix.md#mcq)
|
|
44
72
|
|
|
45
|
-
|
|
73
|
+
## TEXTFIELD {#textfield}
|
|
74
|
+
|
|
75
|
+
- **Type:** `TextInputQuestion` — `type: 'TEXTFIELD'`
|
|
46
76
|
- **Answer:** `string`
|
|
47
|
-
- **UI:** Inline `<textarea>` or `<input>` in `Question.tsx`
|
|
48
|
-
- **No separate reference doc needed**
|
|
49
77
|
|
|
50
|
-
|
|
78
|
+
### SDK Integration Checklist (agent)
|
|
79
|
+
|
|
80
|
+
- [ ] Type literal: `QUESTION_TYPE.TEXTFIELD`
|
|
81
|
+
- [ ] Answer stored as: `string`
|
|
82
|
+
- [ ] onAction CHANGE payload: `{ questionId, answerValue: string }`
|
|
83
|
+
- [ ] Visibility helper: N/A
|
|
84
|
+
- [ ] Custom fields: skip/display only
|
|
85
|
+
- [ ] Logic features: skip ✓ display ✓ answer ✗ variants ✓ numbering ✓
|
|
86
|
+
- [ ] Performance: debounce long textarea onChange if partial save enabled
|
|
87
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#textfield`](../../00-integration/question-type-sdk-matrix.md#textfield)
|
|
88
|
+
|
|
89
|
+
## TEXT_AND_MEDIA {#text_and_media}
|
|
51
90
|
|
|
52
|
-
- **Type:** `TextAndMediaQuestion` — `type: '
|
|
53
|
-
- **Answer:** none — display-only
|
|
54
|
-
|
|
55
|
-
|
|
91
|
+
- **Type:** `TextAndMediaQuestion` — `type: 'TEXT_AND_MEDIA'`
|
|
92
|
+
- **Answer:** none — display-only
|
|
93
|
+
|
|
94
|
+
### SDK Integration Checklist (agent)
|
|
95
|
+
|
|
96
|
+
- [ ] Type literal: `QUESTION_TYPE.TEXT_AND_MEDIA`
|
|
97
|
+
- [ ] Answer stored as: none — no CHANGE action
|
|
98
|
+
- [ ] onAction: user advances with NEXT only
|
|
99
|
+
- [ ] Visibility helper: N/A
|
|
100
|
+
- [ ] Custom fields: skip/display only
|
|
101
|
+
- [ ] Logic features: skip ✓ display ✓ answer ✗ variants ✓ numbering ✓
|
|
102
|
+
- [ ] Performance: lazy-load media; respect `media.alignment`
|
|
103
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#text_and_media`](../../00-integration/question-type-sdk-matrix.md#text_and_media)
|
|
56
104
|
|
|
57
105
|
---
|
|
58
106
|
|
|
59
107
|
## Detailed Reference Docs
|
|
60
108
|
|
|
61
|
-
| File | Type | When to read |
|
|
62
|
-
|
|
63
|
-
| `01-rating.md` | `
|
|
64
|
-
| `04-csat.md` | `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
|
|
109
|
+
| File | Type literal | When to read |
|
|
110
|
+
|------|--------------|--------------|
|
|
111
|
+
| `01-rating.md` | `NPS_SCALE` | NPS 0–10 scales |
|
|
112
|
+
| `04-csat.md` | `CSAT_MATRIX` | CSAT matrix grid |
|
|
113
|
+
| `07-matrix-cfm.md` | `CFM_MATRIX` | CFM matrix grid |
|
|
114
|
+
| `09-matrix-rating.md` | `RATING_MATRIX` | NPS-style rating matrix |
|
|
115
|
+
| `10-slider-matrix.md` | `SLIDER_MATRIX` | Slider grid |
|
|
116
|
+
| `11-file-upload.md` | `FILE_UPLOAD` | File attachments |
|
|
117
|
+
| `12-heatmap.md` | `HEATMAP` | Click-map heatmap |
|
|
118
|
+
| `13-rank-order.md` | `RANK_ORDER` | Rank order options |
|
|
119
|
+
|
|
120
|
+
Legacy docs (deprecated API mappings): `05-rating-scale.md`, `06-slider.md`
|
|
@@ -1,35 +1,50 @@
|
|
|
1
1
|
# Question Type → Component Routing
|
|
2
2
|
|
|
3
|
-
> Canonical mapping.
|
|
4
|
-
|
|
5
|
-
| `question.type` |
|
|
6
|
-
|
|
7
|
-
| `
|
|
8
|
-
| `
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
|
|
19
|
-
|
|
3
|
+
> Canonical mapping. Dispatch on `question.type` only — each matrix variant is its own type (no `subType`).
|
|
4
|
+
|
|
5
|
+
| `question.type` | SDK Type | Component | Answer Shape | Reference Doc | UI Spec |
|
|
6
|
+
|-----------------|----------|-----------|--------------|---------------|---------|
|
|
7
|
+
| `MCQ` | `McqQuestion` | Inline in `Question` | `string \| string[]` | `question-types/README.md#mcq` | `03-ui-specs/02-radio.md` |
|
|
8
|
+
| `TEXTFIELD` | `TextInputQuestion` | Inline in `Question` | `string` | `question-types/README.md#textfield` | `03-ui-specs/03-text.md` |
|
|
9
|
+
| `NPS_SCALE` | `NpsQuestion` | `RatingScale` | `number` | `question-types/01-rating.md` | `03-ui-specs/01-rating.md` |
|
|
10
|
+
| `CFM_MATRIX` | `CfmMatrixQuestion` | `LikertMatrixScale` | `MatrixRowAnswers` | `question-types/07-matrix-cfm.md` | `03-ui-specs/07-matrix-cfm.md` |
|
|
11
|
+
| `CSAT_MATRIX` | `CsatQuestion` | `CsatMatrixScale` | `MatrixRowAnswers` | `question-types/04-csat.md` | `03-ui-specs/04-csat.md` |
|
|
12
|
+
| `RATING_MATRIX` | `RatingMatrixQuestion` | `CsatMatrixScale` | `MatrixRowAnswers` | `question-types/09-matrix-rating.md` | `03-ui-specs/08-matrix-csat-rating.md` |
|
|
13
|
+
| `SLIDER_MATRIX` | `SliderMatrixQuestion` | `SliderMatrixScale` | `MatrixRowAnswers` | `question-types/10-slider-matrix.md` | `03-ui-specs/09-slider-matrix.md` |
|
|
14
|
+
| `FILE_UPLOAD` | `FileUploadQuestion` | `FileUploadScale` | `UploadedFileAnswer[]` | `question-types/11-file-upload.md` | `03-ui-specs/10-file-upload.md` |
|
|
15
|
+
| `TEXT_AND_MEDIA` | `TextAndMediaQuestion` | Inline in `Question` | (no answer) | `question-types/README.md#text_and_media` | `03-ui-specs/11-text-and-media.md` |
|
|
16
|
+
| `HEATMAP` | `HeatmapQuestion` | `HeatmapScale` | `HeatmapAnswer` | `question-types/12-heatmap.md` | `03-ui-specs/13-heatmap.md` |
|
|
17
|
+
| `RANK_ORDER` | `RankOrderQuestion` | `RankOrderScale` | `RankOrderAnswers` | `question-types/13-rank-order.md` | `03-ui-specs/14-rank-order.md` |
|
|
18
|
+
|
|
19
|
+
Use `QUESTION_TYPE` from the SDK for dispatch:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { QUESTION_TYPE } from '@explorer02/cfm-survey-sdk';
|
|
23
|
+
|
|
24
|
+
{question.type === QUESTION_TYPE.NPS_SCALE && <RatingScale ... />}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **Legacy note:** API RATING/SLIDER page types are mapped into `NPS_SCALE`, matrix display styles, or `SLIDER_MATRIX` — not separate `question.type` values.
|
|
28
|
+
|
|
29
|
+
## SDK hook contract
|
|
30
|
+
|
|
31
|
+
- Read answers from `state.answers` (`SurveyAnswers`)
|
|
32
|
+
- Dispatch `{ type: 'CHANGE', payload: { questionId, answerValue } }`
|
|
33
|
+
- Pass `allAnswers` and `allQuestions` to `Question` for placeholder resolution
|
|
20
34
|
|
|
21
35
|
## Component Blueprint Docs
|
|
22
36
|
|
|
23
37
|
| Component | Blueprint |
|
|
24
38
|
|-----------|-----------|
|
|
25
39
|
| `SurveyPage` | `01-components/01-survey-page.md` |
|
|
40
|
+
| `IntroPage` / `EndPage` / `PausedPage` | `01-components/14-intro-page.md`, `15-end-page.md`, `16-paused-page.md` |
|
|
26
41
|
| `Question` | `01-components/02-question.md` |
|
|
27
42
|
| `RatingScale` | `01-components/03-rating-scale.md` |
|
|
28
|
-
| `CsatScale` | `01-components/04-csat-scale.md` |
|
|
29
43
|
| `CsatMatrixScale` | `01-components/05-csat-matrix-scale.md` |
|
|
30
44
|
| `LikertMatrixScale` | `01-components/06-likert-matrix-scale.md` |
|
|
31
45
|
| `SliderMatrixScale` | `01-components/07-slider-matrix-scale.md` |
|
|
32
46
|
| `FileUploadScale` | `01-components/08-file-upload-scale.md` |
|
|
47
|
+
| `HeatmapScale` | `01-components/17-heatmap-scale.md` |
|
|
33
48
|
| `CustomSliderTrack` | `01-components/09-custom-slider-track.md` |
|
|
34
49
|
| `Header` / `Footer` | `01-components/10-header-footer.md` |
|
|
35
50
|
| `ProgressBar` | `01-components/11-progress-bar.md` |
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Client-Side Value Derivation
|
|
2
|
+
|
|
3
|
+
> SDK types expose **display** fields only for scale options and matrix columns. Submit values are derived at submit/partial-save time.
|
|
4
|
+
|
|
5
|
+
## Matrix columns (`ScaleColumn`)
|
|
6
|
+
|
|
7
|
+
**Import from SDK** (preferred):
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { matrixColumnStoredValue } from '@explorer02/cfm-survey-sdk';
|
|
11
|
+
// monorepo: import { matrixColumnStoredValue } from '@repo/sdk';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
type ScaleColumn = { id: string; label: string };
|
|
16
|
+
|
|
17
|
+
// matrixColumnStoredValue(column) → column.id, except N/A → null
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Stored matrix answers** use `matrixColumnStoredValue(col)` → always the column **`id`** (string), except N/A → `null`
|
|
21
|
+
- Matrix answers: `MatrixRowAnswers = Record<statementRowId, columnId | columnId[] | null>`
|
|
22
|
+
- **Submit / partial save**: SDK `resolveSubmitValues` maps stored column ids → API label or numeric string (id-only lookup)
|
|
23
|
+
- N/A column: SDK adds `{ id: 'na', label: 'N/A' }` when `hasNotApplicableOption` is true
|
|
24
|
+
- Filter N/A from scale pickers: `scaleColumns.filter(col => col.id !== 'na')`
|
|
25
|
+
- Slider matrix rows still store numeric slider positions (unchanged)
|
|
26
|
+
|
|
27
|
+
Monorepo client mirror: `apps/client/src/lib/surveyUi/scaleValues.ts` exports the same helper for matrix UI components.
|
|
28
|
+
|
|
29
|
+
## NPS / star rating options
|
|
30
|
+
|
|
31
|
+
Options are `{ id: string; optionLabel: string }` — no `optionValue` on the type.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
function npsValueFromOption(option: NpsScalePoint, index: number): number {
|
|
35
|
+
const numeric = Number(option.optionLabel);
|
|
36
|
+
return Number.isFinite(numeric) && option.optionLabel.trim() !== '' ? numeric : index;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
NPS questions use `question.type === 'NPS_SCALE'`. Store a **number** in `SurveyAnswers`. Badge colors: traffic-light table keyed by numeric value (0–10).
|
|
41
|
+
|
|
42
|
+
## MCQ
|
|
43
|
+
|
|
44
|
+
- Selected answer: **`option.id`** (string), not a separate `optionValue` field
|
|
45
|
+
|
|
46
|
+
## Rank order
|
|
47
|
+
|
|
48
|
+
- Stored answer: **`RankOrderAnswers`** — `Record<optionId, rankNumber>` (1-based)
|
|
49
|
+
- Dropdown: use `assignRankWithoutDuplicates(current, optionId, rank)` before `onSelect`
|
|
50
|
+
- Drag-and-drop: use `buildRankOrderFromOrderedOptionIds(orderedOptionIds)` after reorder
|
|
51
|
+
- Submit / partial save:
|
|
52
|
+
- `questionToAnswers[questionId]` → ordered **option id** array (rank 1 → N)
|
|
53
|
+
- `optionLevelAnswers[`${questionId}-:-${optionId}`]` → `[String(rank)]`
|
|
54
|
+
- Filter visible options: `getVisibleRankOrderOptionsForAnswers(question, answers, allQuestions, customFieldValues?)`
|
|
55
|
+
|
|
56
|
+
## Media
|
|
57
|
+
|
|
58
|
+
- Shell side media: `question.containerMedia?.url`, `containerMedia?.mimeType`, etc.
|
|
59
|
+
- Text & media block: `question.media?.url`, `question.media?.title`, etc.
|
|
60
|
+
|
|
61
|
+
## Shared types (exported)
|
|
62
|
+
|
|
63
|
+
`StatementRow`, `ScaleColumn`, `ContainerMedia`, `QuestionMedia`, `SurveyAnswers`, `matrixColumnStoredValue` — see [`exports.md`](exports.md).
|