@explorer02/cfm-survey-sdk 0.1.8 → 0.2.0
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.d.mts +24 -26
- package/dist/index.d.ts +24 -26
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/postinstall.js +21 -30
- package/templates/AGENT.md +24 -10
- package/templates/docs/00-integration/constraints.md +54 -0
- package/templates/docs/00-integration/setup.md +80 -0
- package/templates/docs/00-integration/useSurveySDK.md +107 -0
- package/templates/docs/{03-client-components → 01-components}/01-survey-page.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/02-question.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/03-rating-scale.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/04-csat-scale.md +3 -1
- package/templates/docs/{03-client-components → 01-components}/05-csat-matrix-scale.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/06-likert-matrix-scale.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/07-slider-matrix-scale.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/08-file-upload-scale.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/09-custom-slider-track.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/10-header-footer.md +3 -1
- package/templates/docs/{03-client-components → 01-components}/11-progress-bar.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/12-language-selector.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/13-matrix-dropdown.md +2 -0
- package/templates/docs/{03-client-components → 01-components}/README.md +3 -3
- package/templates/docs/02-question-types/01-rating.md +0 -8
- package/templates/docs/02-reference/config-field-index.md +158 -0
- package/templates/docs/02-reference/question-types/01-rating.md +52 -0
- package/templates/docs/02-reference/question-types/README.md +71 -0
- package/templates/docs/02-reference/routing-table.md +37 -0
- package/templates/docs/03-ui-specs/00-question-shell.md +89 -0
- package/templates/docs/03-ui-specs/01-rating.md +80 -0
- package/templates/docs/03-ui-specs/02-radio.md +57 -0
- package/templates/docs/03-ui-specs/03-text.md +51 -0
- package/templates/docs/03-ui-specs/04-csat.md +57 -0
- package/templates/docs/03-ui-specs/05-rating-scale.md +49 -0
- package/templates/docs/03-ui-specs/06-slider.md +42 -0
- package/templates/docs/03-ui-specs/07-matrix-cfm.md +66 -0
- package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +65 -0
- package/templates/docs/03-ui-specs/09-slider-matrix.md +61 -0
- package/templates/docs/03-ui-specs/10-file-upload.md +54 -0
- package/templates/docs/03-ui-specs/11-text-and-media.md +49 -0
- package/templates/docs/03-ui-specs/12-survey-chrome.md +60 -0
- package/templates/docs/03-ui-specs/README.md +79 -0
- package/templates/docs/03-ui-specs/shared/custom-slider-track.md +52 -0
- package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +38 -0
- package/templates/docs/MANIFEST.json +150 -0
- package/templates/docs/index.md +111 -142
- package/templates/docs/templates/implementation_plan.md +91 -0
- package/templates/docs/01-sdk-core/01-fetch-survey.md +0 -68
- package/templates/docs/01-sdk-core/02-survey-mapper.md +0 -85
- package/templates/docs/01-sdk-core/03-question-mappers.md +0 -114
- package/templates/docs/01-sdk-core/04-pagination.md +0 -72
- package/templates/docs/01-sdk-core/05-validation.md +0 -66
- package/templates/docs/01-sdk-core/06-submit-survey.md +0 -90
- package/templates/docs/01-sdk-core/07-language-handling.md +0 -111
- package/templates/docs/01-sdk-core/08-icons-and-emojis.md +0 -56
- package/templates/docs/01-sdk-core/README.md +0 -53
- package/templates/docs/02-question-types/02-radio.md +0 -26
- package/templates/docs/02-question-types/03-text.md +0 -26
- package/templates/docs/02-question-types/12-text-and-media.md +0 -35
- package/templates/docs/02-question-types/README.md +0 -74
- package/templates/docs/04-critical-rules/01-import-rules.md +0 -51
- package/templates/docs/04-critical-rules/02-action-dispatching.md +0 -56
- package/templates/docs/04-critical-rules/03-scroll-navigation.md +0 -37
- package/templates/docs/04-critical-rules/04-logo-branding.md +0 -42
- package/templates/docs/04-critical-rules/05-troubleshooting.md +0 -46
- package/templates/docs/04-critical-rules/README.md +0 -29
- /package/templates/docs/{02-question-types → 02-reference/question-types}/04-csat.md +0 -0
- /package/templates/docs/{02-question-types → 02-reference/question-types}/05-rating-scale.md +0 -0
- /package/templates/docs/{02-question-types → 02-reference/question-types}/06-slider.md +0 -0
- /package/templates/docs/{02-question-types → 02-reference/question-types}/07-matrix-cfm.md +0 -0
- /package/templates/docs/{02-question-types → 02-reference/question-types}/08-matrix-csat.md +0 -0
- /package/templates/docs/{02-question-types → 02-reference/question-types}/09-matrix-rating.md +0 -0
- /package/templates/docs/{02-question-types → 02-reference/question-types}/10-slider-matrix.md +0 -0
- /package/templates/docs/{02-question-types → 02-reference/question-types}/11-file-upload.md +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Rating Scale Architectural Blueprint
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/01-rating.md` (NPS) · `03-ui-specs/05-rating-scale.md` (star/emoji gap)
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/RatingScale.tsx`
|
|
3
5
|
> **Target Component**: `RatingScale`
|
|
4
6
|
> **Handles**: `type: 'rating'`, `type: 'rating_scale'`
|
|
5
7
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# CSAT & Rating Architectural Blueprints
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/04-csat.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/CsatScale.tsx`
|
|
5
|
+
> **Target Components**: `CsatScale`
|
|
4
6
|
> **Handles**: `type: 'csat'`, `'rating'`, `'rating_scale'`, CSAT matrices.
|
|
5
7
|
|
|
6
8
|
## Core Responsibility
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# CSAT Matrix Architectural Blueprint
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/08-matrix-csat-rating.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/CsatMatrixScale.tsx`
|
|
3
5
|
> **Target Component**: `CsatMatrixScale`
|
|
4
6
|
> **Handles**: `type: 'matrix'` (CSAT_MATRIX and RATING_MATRIX subTypes)
|
|
5
7
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Likert Matrix Architectural Blueprint
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/07-matrix-cfm.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/LikertMatrixScale.tsx`
|
|
3
5
|
> **Target Component**: `LikertMatrixScale`
|
|
4
6
|
> **Handles**: `type: 'matrix'` (CFM_MATRIX subType)
|
|
5
7
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Slider Matrix Architectural Blueprint
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/09-slider-matrix.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/SliderMatrixScale.tsx`
|
|
3
5
|
> **Target Component**: `SliderMatrixScale`
|
|
4
6
|
> **Handles**: `type: 'slider_matrix'`
|
|
5
7
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# File Upload Architectural Blueprint
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/10-file-upload.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/FileUploadScale.tsx`
|
|
3
5
|
> **Target Component**: `FileUploadScale`
|
|
4
6
|
> **Handles**: `type: 'file_upload'`
|
|
5
7
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Custom Slider Track Architectural Blueprint
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/shared/custom-slider-track.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/CustomSliderTrack.tsx`
|
|
3
5
|
> **Target Component**: `CustomSliderTrack`
|
|
4
6
|
> **Used By**: `SliderMatrixScale`, `CsatMatrixScale`, `CsatScale`
|
|
5
7
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Theme & Logo Engineering Blueprint
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/Header.tsx`, `Footer.tsx`
|
|
5
|
+
> **Target Components**: `Header`, `Footer`
|
|
4
6
|
> **Handles**: Branding, Mockup Replication
|
|
5
7
|
|
|
6
8
|
## Core Responsibility
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Language Selector
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/LanguageSelector.tsx`
|
|
3
5
|
> **Component**: `LanguageSelector`
|
|
4
6
|
> **Role**: Allows the user to switch the survey language.
|
|
5
7
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Matrix Dropdown
|
|
2
2
|
|
|
3
|
+
> **UI spec (authoritative):** `03-ui-specs/shared/matrix-dropdown.md`
|
|
4
|
+
> **Reference implementation:** `apps/client/src/components/MatrixDropdown.tsx`
|
|
3
5
|
> **Component**: `MatrixDropdown`
|
|
4
6
|
> **Used by**: `CsatMatrixScale`, `LikertMatrixScale`
|
|
5
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Client Components Overview
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
3
|
+
> Wiring blueprints for React components. **Authoritative UI details** are in [`03-ui-specs/`](../03-ui-specs/README.md) (reference-client driven).
|
|
4
|
+
> This folder documents props, `onSelect` wiring, and dispatcher architecture.
|
|
5
5
|
|
|
6
6
|
## Component Architecture
|
|
7
7
|
|
|
@@ -40,7 +40,7 @@ SurveyPage (Top-level orchestrator)
|
|
|
40
40
|
|
|
41
41
|
## The `Question` Dispatcher
|
|
42
42
|
|
|
43
|
-
The `Question` component
|
|
43
|
+
The `Question` component is the heart of the rendering engine. It receives a `SurveyQuestion` union type and switches on `question.type` (and `question.subType` for matrices) to render the correct specialized scale component.
|
|
44
44
|
|
|
45
45
|
See `02-question.md` for details on the dispatcher logic.
|
|
46
46
|
|
|
@@ -21,14 +21,6 @@ type RatingQuestion = QuestionBase & {
|
|
|
21
21
|
## Answer Shape: `number`
|
|
22
22
|
Example: `7` (the selected badge value)
|
|
23
23
|
|
|
24
|
-
## Options Auto-Generation
|
|
25
|
-
If the API provides no explicit options, the SDK generates 0-10:
|
|
26
|
-
```typescript
|
|
27
|
-
Array.from({ length: 11 }, (_, i) => ({
|
|
28
|
-
id: String(i), label: String(i), value: i, color: getColor(i)
|
|
29
|
-
}));
|
|
30
|
-
```
|
|
31
|
-
|
|
32
24
|
## NPS Traffic-Light Colors
|
|
33
25
|
```
|
|
34
26
|
0: #e2001a (red) 3: #f18b00 (orange) 6: #fcd900 (yellow)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Config Field Index
|
|
2
|
+
|
|
3
|
+
> Master map: every SDK config field → reference client file → implemented? → UI spec doc.
|
|
4
|
+
> Cross-check this when building any question with non-default config.
|
|
5
|
+
|
|
6
|
+
## QuestionBase (all types)
|
|
7
|
+
|
|
8
|
+
| Field | Types | Client file | Implemented? | Spec doc |
|
|
9
|
+
|-------|-------|-------------|--------------|----------|
|
|
10
|
+
| `id` | all | Question.tsx | ✅ | 00-question-shell |
|
|
11
|
+
| `text` | all | Question.tsx | ✅ | 00-question-shell |
|
|
12
|
+
| `description` | all | Question.tsx | ✅ | 00-question-shell |
|
|
13
|
+
| `required` | all | Question.tsx | ✅ | 00-question-shell |
|
|
14
|
+
| `requiredErrorMessage` | all | Question.tsx | ✅ | 00-question-shell |
|
|
15
|
+
| `questionNumber` | all | — | ❌ | 00-question-shell |
|
|
16
|
+
| `containerMediaUrl` | all | — | ❌ | 00-question-shell |
|
|
17
|
+
| `containerMediaMimeType` | all | — | ❌ | 00-question-shell |
|
|
18
|
+
| `containerMediaAlignment` | all | — | ❌ | 00-question-shell |
|
|
19
|
+
| `containerMediaSize` | all | — | ❌ | 00-question-shell |
|
|
20
|
+
|
|
21
|
+
## rating
|
|
22
|
+
|
|
23
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
24
|
+
|-------|--------|-------------|--------------|----------|
|
|
25
|
+
| `options[]` | SurveyOption[] | RatingScale.tsx | ✅ | 01-rating |
|
|
26
|
+
| `options[].color` | hex | RatingScale.tsx | ✅ | 01-rating |
|
|
27
|
+
| `minLabel` | string | Question.tsx | ✅ | 01-rating |
|
|
28
|
+
| `midLabel` | string | Question.tsx | ✅ | 01-rating |
|
|
29
|
+
| `maxLabel` | string | Question.tsx | ✅ | 01-rating |
|
|
30
|
+
| `midLabelIndex` | number | Question.tsx | ✅ | 01-rating |
|
|
31
|
+
| `reverseScaleOrder` | boolean | — | ❌ | 01-rating |
|
|
32
|
+
| `buttonVariant` | radio/numbered/emoji | — | ❌ | 01-rating |
|
|
33
|
+
|
|
34
|
+
## radio
|
|
35
|
+
|
|
36
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
37
|
+
|-------|--------|-------------|--------------|----------|
|
|
38
|
+
| `options[]` | SurveyOption[] | Question.tsx | ✅ | 02-radio |
|
|
39
|
+
| `isMultiSelect` | boolean | — | ❌ | 02-radio |
|
|
40
|
+
|
|
41
|
+
## text
|
|
42
|
+
|
|
43
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
44
|
+
|-------|--------|-------------|--------------|----------|
|
|
45
|
+
| `placeholder` | string | Question.tsx | ✅ | 03-text |
|
|
46
|
+
| `maxCharacterCount` | number | Question.tsx | ✅ | 03-text |
|
|
47
|
+
| `inputVariant` | long/short | Question.tsx | partial | 03-text |
|
|
48
|
+
|
|
49
|
+
## csat
|
|
50
|
+
|
|
51
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
52
|
+
|-------|--------|-------------|--------------|----------|
|
|
53
|
+
| `options[]` | SurveyOption[] | CsatScale.tsx | ✅ | 04-csat |
|
|
54
|
+
| `buttonType` | emoji/star/numbered/... | CsatScale.tsx | ✅ | 04-csat |
|
|
55
|
+
| `labelPosition` | top/bottom/hidden | CsatScale.tsx | ✅ | 04-csat |
|
|
56
|
+
| `minLabel` / `maxLabel` | string | Question.tsx | ✅ | 04-csat |
|
|
57
|
+
| `hasNotApplicable` | boolean | — | ❌ | 04-csat |
|
|
58
|
+
| `reverseScaleOrder` | boolean | — | ❌ | 04-csat |
|
|
59
|
+
|
|
60
|
+
## rating_scale
|
|
61
|
+
|
|
62
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
63
|
+
|-------|--------|-------------|--------------|----------|
|
|
64
|
+
| `options[]` | SurveyOption[] | — | ❌ | 05-rating-scale |
|
|
65
|
+
| `scaleStyle` | star/emoji | — | ❌ | 05-rating-scale |
|
|
66
|
+
| `minLabel` / `midLabel` / `maxLabel` | string | — | ❌ | 05-rating-scale |
|
|
67
|
+
| `midLabelIndex` | number | — | ❌ | 05-rating-scale |
|
|
68
|
+
| `reverseScaleOrder` | boolean | — | ❌ | 05-rating-scale |
|
|
69
|
+
|
|
70
|
+
## slider
|
|
71
|
+
|
|
72
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
73
|
+
|-------|--------|-------------|--------------|----------|
|
|
74
|
+
| `min` / `max` / `step` | number | — | ❌ | 06-slider |
|
|
75
|
+
| `defaultValue` | number | — | ❌ | 06-slider |
|
|
76
|
+
| `minLabel` / `maxLabel` | string | — | ❌ | 06-slider |
|
|
77
|
+
| `enableInputBox` | boolean | SliderMatrixScale.tsx | partial | 06-slider |
|
|
78
|
+
| `displayValues` | boolean | CustomSliderTrack.tsx | partial | 06-slider, shared/custom-slider-track |
|
|
79
|
+
|
|
80
|
+
## matrix (shared)
|
|
81
|
+
|
|
82
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
83
|
+
|-------|--------|-------------|--------------|----------|
|
|
84
|
+
| `subType` | CFM/CSAT/RATING | Question.tsx | ✅ | 07, 08 |
|
|
85
|
+
| `rows[]` | MatrixRow[] | Likert/CsatMatrix | ✅ | 07, 08 |
|
|
86
|
+
| `columns[]` | MatrixColumn[] | Likert/CsatMatrix | ✅ | 07, 08 |
|
|
87
|
+
| `matrixFormat` | standard/carousel/dropdown | Likert/CsatMatrix | ✅ | 07, 08 |
|
|
88
|
+
| `hasNotApplicable` | boolean | Likert/CsatMatrix | ✅ | 07, 08 |
|
|
89
|
+
| `reverseScaleOrder` | boolean | CsatMatrixScale | ✅ | 08 |
|
|
90
|
+
| `labels[]` | string[] | Likert/CsatMatrix | ✅ | 07, 08 |
|
|
91
|
+
| `buttonType` | SurveyButtonType | CsatMatrixScale | ✅ | 08 |
|
|
92
|
+
|
|
93
|
+
## matrix — CFM only
|
|
94
|
+
|
|
95
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
96
|
+
|-------|--------|-------------|--------------|----------|
|
|
97
|
+
| `matrixType` | likert/bipolar | LikertMatrixScale | ✅ | 07-matrix-cfm |
|
|
98
|
+
| `answerType` | single/multiple | LikertMatrixScale | ✅ | 07-matrix-cfm |
|
|
99
|
+
| `transposeTable` | boolean | LikertMatrixScale | ✅ | 07-matrix-cfm |
|
|
100
|
+
| `repeatScale` | boolean | LikertMatrixScale | ✅ | 07-matrix-cfm |
|
|
101
|
+
| `enableScale` | boolean | LikertMatrixScale | ✅ | 07-matrix-cfm |
|
|
102
|
+
|
|
103
|
+
## slider_matrix
|
|
104
|
+
|
|
105
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
106
|
+
|-------|--------|-------------|--------------|----------|
|
|
107
|
+
| `rows[].min/max/step` | number | SliderMatrixScale | ✅ | 09-slider-matrix |
|
|
108
|
+
| `labels[]` | string[] | SliderMatrixScale | ✅ | 09-slider-matrix |
|
|
109
|
+
| `ticks` | number | SliderMatrixScale | ✅ | 09-slider-matrix |
|
|
110
|
+
| `tickValues[]` | TickValue[] | SliderMatrixScale | ✅ | 09-slider-matrix |
|
|
111
|
+
| `enableInputBox` | boolean | SliderMatrixScale | ✅ | 09-slider-matrix |
|
|
112
|
+
| `displayValues` | boolean | CustomSliderTrack | ✅ | 09-slider-matrix |
|
|
113
|
+
| `enableNotApplicable` | boolean | SliderMatrixScale | ✅ | 09-slider-matrix |
|
|
114
|
+
| `sliderType` | graphics | SliderMatrixScale | ✅ | 09-slider-matrix |
|
|
115
|
+
|
|
116
|
+
## file_upload
|
|
117
|
+
|
|
118
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
119
|
+
|-------|--------|-------------|--------------|----------|
|
|
120
|
+
| `uploadMessage` | HTML string | FileUploadScale | ✅ | 10-file-upload |
|
|
121
|
+
| `supportedFileFormats` | string[] | FileUploadScale | ✅ | 10-file-upload |
|
|
122
|
+
| `maxFileCount` | number | FileUploadScale | ✅ | 10-file-upload |
|
|
123
|
+
| `fileSizeLimit` | number (MB) | FileUploadScale | ✅ | 10-file-upload |
|
|
124
|
+
| `fileSizeLimitType` | PER_FILE/IN_TOTAL | FileUploadScale | ✅ | 10-file-upload |
|
|
125
|
+
|
|
126
|
+
## text_and_media
|
|
127
|
+
|
|
128
|
+
| Field | Values | Client file | Implemented? | Spec doc |
|
|
129
|
+
|-------|--------|-------------|--------------|----------|
|
|
130
|
+
| `mediaUrl` | string | Question.tsx | ✅ | 11-text-and-media |
|
|
131
|
+
| `mediaMimeType` | string | Question.tsx | ✅ | 11-text-and-media |
|
|
132
|
+
| `mediaTitle` | string | Question.tsx | ✅ | 11-text-and-media |
|
|
133
|
+
| `mediaAlignment` | string | Question.tsx | ✅ | 11-text-and-media |
|
|
134
|
+
| `mediaSize` | number (%) | Question.tsx | ✅ | 11-text-and-media |
|
|
135
|
+
|
|
136
|
+
## Implementation Gaps (no client branch yet)
|
|
137
|
+
|
|
138
|
+
| Gap | SDK type/field | Extend from |
|
|
139
|
+
|-----|----------------|-------------|
|
|
140
|
+
| `rating_scale` dispatcher | `type: 'rating_scale'` | CsatScale star/emoji patterns |
|
|
141
|
+
| `slider` dispatcher | `type: 'slider'` | CustomSliderTrack + SliderMatrixScale row |
|
|
142
|
+
| `radio.isMultiSelect` | checkbox cards | 02-radio card styling |
|
|
143
|
+
| `text.inputVariant: 'short'` | single-line input | 03-text focus tokens |
|
|
144
|
+
| `rating.reverseScaleOrder` | reverse options array | 01-rating |
|
|
145
|
+
| `rating.buttonVariant` | numbered clickable badges | 01-rating |
|
|
146
|
+
| `csat.hasNotApplicable` / `reverseScaleOrder` | standalone CSAT | 04-csat, 08-matrix N/A |
|
|
147
|
+
| `containerMedia*` on any type | container asset | 11-text-and-media media block |
|
|
148
|
+
|
|
149
|
+
## buttonType × matrixFormat Quick Reference
|
|
150
|
+
|
|
151
|
+
| buttonType | matrixFormat | Layout component | Spec |
|
|
152
|
+
|------------|--------------|------------------|------|
|
|
153
|
+
| (CFM default) | standard | LikertMatrixGrid | 07 |
|
|
154
|
+
| (CFM default) | carousel | LikertMatrixCarousel | 07 |
|
|
155
|
+
| (CFM default) | dropdown | LikertMatrixDropdown | 07 |
|
|
156
|
+
| emoji/star/numbered | standard | CsatMatrixGrid | 08 |
|
|
157
|
+
| emoji/star/numbered/graphics | carousel | CsatMatrixCarousel | 08 |
|
|
158
|
+
| dropdown/selectbox/radio | any | CsatMatrixVerticalList | 08 |
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Rating Question (NPS 0-10)
|
|
2
|
+
|
|
3
|
+
> **Type**: `'rating'` | **API Source**: `SCALE` with `SCALE_TYPE=TEN_POINT` or `NPS_SCALE`
|
|
4
|
+
> **Mapper**: `ratingMapper.ts` | **Component**: `RatingScale`
|
|
5
|
+
|
|
6
|
+
## TypeScript Type
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
type RatingQuestion = QuestionBase & {
|
|
10
|
+
type: 'rating';
|
|
11
|
+
options: SurveyOption[]; // 0-10 with NPS traffic-light colors
|
|
12
|
+
minLabel?: string; // Left label (e.g. "Not Likely")
|
|
13
|
+
midLabel?: string; // Middle label (e.g. "Neutral")
|
|
14
|
+
maxLabel?: string; // Right label (e.g. "Extremely Likely")
|
|
15
|
+
midLabelIndex?: number; // Fractional position for mid-label CSS
|
|
16
|
+
reverseScaleOrder?: boolean; // Render high→low
|
|
17
|
+
buttonVariant?: 'radio' | 'numbered' | 'emoji';
|
|
18
|
+
};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Answer Shape: `number`
|
|
22
|
+
Example: `7` (the selected badge value)
|
|
23
|
+
|
|
24
|
+
## Options Auto-Generation
|
|
25
|
+
If the API provides no explicit options, the SDK generates 0-10:
|
|
26
|
+
```typescript
|
|
27
|
+
Array.from({ length: 11 }, (_, i) => ({
|
|
28
|
+
id: String(i), label: String(i), value: i, color: getColor(i)
|
|
29
|
+
}));
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## NPS Traffic-Light Colors
|
|
33
|
+
```
|
|
34
|
+
0: #e2001a (red) 3: #f18b00 (orange) 6: #fcd900 (yellow)
|
|
35
|
+
1: #e4251b 4: #f7b200 7: #9fca00 (lime)
|
|
36
|
+
2: #ec610a 5: #fcd900 8: #6bb300 (green)
|
|
37
|
+
9: #339a00
|
|
38
|
+
10: #008000
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Mid-Label Positioning
|
|
42
|
+
```typescript
|
|
43
|
+
// CSS left percentage for mid-label
|
|
44
|
+
const left = (midLabelIndex / (options.length - 1)) * 100;
|
|
45
|
+
// For 11-point scale: midLabelIndex defaults to 7.5
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Rendering Guidance
|
|
49
|
+
- Display as horizontal row of numbered badge buttons
|
|
50
|
+
- Each badge uses `option.color` as background
|
|
51
|
+
- Selected badge: emphasized border/scale, all others dimmed
|
|
52
|
+
- Labels positioned: minLabel (left), midLabel (calculated %), maxLabel (right)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Question Types Reference
|
|
2
|
+
|
|
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
|
+
|
|
6
|
+
## Discriminated Union
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
type SurveyQuestion =
|
|
10
|
+
| RatingQuestion // 'rating'
|
|
11
|
+
| RadioQuestion // 'radio'
|
|
12
|
+
| TextQuestion // 'text'
|
|
13
|
+
| CsatQuestion // 'csat'
|
|
14
|
+
| RatingScaleQuestion // 'rating_scale'
|
|
15
|
+
| SliderQuestion // 'slider'
|
|
16
|
+
| MatrixQuestion // 'matrix' (check subType)
|
|
17
|
+
| RatingMatrixQuestion // 'rating_matrix'
|
|
18
|
+
| SliderMatrixQuestion // 'slider_matrix'
|
|
19
|
+
| FileUploadQuestion // 'file_upload'
|
|
20
|
+
| TextAndMediaQuestion; // 'text_and_media'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Shared `QuestionBase` Properties
|
|
24
|
+
|
|
25
|
+
| Property | Type | Notes |
|
|
26
|
+
|----------|------|-------|
|
|
27
|
+
| `id` | `string` | **MUST** be DOM `id` on wrapper |
|
|
28
|
+
| `text` | `string` | HTML — use `dangerouslySetInnerHTML` |
|
|
29
|
+
| `description` | `string?` | HTML |
|
|
30
|
+
| `required` | `boolean?` | |
|
|
31
|
+
| `requiredErrorMessage` | `string?` | Custom validation message |
|
|
32
|
+
| `questionNumber` | `number?` | 1-indexed |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## radio {#radio}
|
|
37
|
+
|
|
38
|
+
- **Type:** `RadioQuestion` — `type: 'radio'`
|
|
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**
|
|
42
|
+
|
|
43
|
+
## text {#text}
|
|
44
|
+
|
|
45
|
+
- **Type:** `TextQuestion` — `type: 'text'`
|
|
46
|
+
- **Answer:** `string`
|
|
47
|
+
- **UI:** Inline `<textarea>` or `<input>` in `Question.tsx`
|
|
48
|
+
- **No separate reference doc needed**
|
|
49
|
+
|
|
50
|
+
## text_and_media {#text_and_media}
|
|
51
|
+
|
|
52
|
+
- **Type:** `TextAndMediaQuestion` — `type: 'text_and_media'`
|
|
53
|
+
- **Answer:** none — display-only, skipped in validation and submission
|
|
54
|
+
- **UI:** Inline `<video>` or `<img>` from `question.mediaUrl` — no scale component
|
|
55
|
+
- **No separate reference doc needed**
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Detailed Reference Docs
|
|
60
|
+
|
|
61
|
+
| File | Type | When to read |
|
|
62
|
+
|------|------|--------------|
|
|
63
|
+
| `01-rating.md` | `rating` | NPS / numeric scales |
|
|
64
|
+
| `04-csat.md` | `csat` | CSAT emoji/star (≤5 options) |
|
|
65
|
+
| `05-rating-scale.md` | `rating_scale` | Star/emoji rating |
|
|
66
|
+
| `06-slider.md` | `slider` | Single range slider |
|
|
67
|
+
| `07-matrix-cfm.md` | `matrix` + `CFM_MATRIX` | Likert text grid |
|
|
68
|
+
| `08-matrix-csat.md` | `matrix` + CSAT/RATING | Satisfaction grid |
|
|
69
|
+
| `09-matrix-rating.md` | `rating_matrix` | NPS-style grid |
|
|
70
|
+
| `10-slider-matrix.md` | `slider_matrix` | Slider grid |
|
|
71
|
+
| `11-file-upload.md` | `file_upload` | File attachments |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Question Type → Component Routing
|
|
2
|
+
|
|
3
|
+
> Canonical mapping. For **UI implementation** (config branches, tokens, states), read `03-ui-specs/`. For wiring, read `01-components/`. For data shapes, read `02-reference/question-types/`. For field audit, read `config-field-index.md`.
|
|
4
|
+
|
|
5
|
+
| `question.type` | `question.subType` | Component | Answer Shape | Reference Doc | UI Spec |
|
|
6
|
+
|-----------------|-------------------|-----------|--------------|---------------|---------|
|
|
7
|
+
| `'rating'` | — | `RatingScale` | `number` | `question-types/01-rating.md` | `03-ui-specs/01-rating.md` |
|
|
8
|
+
| `'radio'` | — | Inline in `Question` | `string \| number` | `question-types/README.md#radio` | `03-ui-specs/02-radio.md` |
|
|
9
|
+
| `'text'` | — | Inline in `Question` | `string` | `question-types/README.md#text` | `03-ui-specs/03-text.md` |
|
|
10
|
+
| `'csat'` | — | `CsatScale` | `number \| null` | `question-types/04-csat.md` | `03-ui-specs/04-csat.md` |
|
|
11
|
+
| `'rating_scale'` | — | `RatingScale` | `number` | `question-types/05-rating-scale.md` | `03-ui-specs/05-rating-scale.md` |
|
|
12
|
+
| `'slider'` | — | `CustomSliderTrack` | `number` | `question-types/06-slider.md` | `03-ui-specs/06-slider.md` |
|
|
13
|
+
| `'matrix'` | `'CFM_MATRIX'` | `LikertMatrixScale` | `MatrixAnswerMap` | `question-types/07-matrix-cfm.md` | `03-ui-specs/07-matrix-cfm.md` |
|
|
14
|
+
| `'matrix'` | `'CSAT_MATRIX'` | `CsatMatrixScale` | `MatrixAnswerMap` | `question-types/08-matrix-csat.md` | `03-ui-specs/08-matrix-csat-rating.md` |
|
|
15
|
+
| `'matrix'` | `'RATING_MATRIX'` | `CsatMatrixScale` | `MatrixAnswerMap` | `question-types/08-matrix-csat.md` | `03-ui-specs/08-matrix-csat-rating.md` |
|
|
16
|
+
| `'rating_matrix'` | — | `CsatMatrixScale` | `MatrixAnswerMap` | `question-types/09-matrix-rating.md` | `03-ui-specs/08-matrix-csat-rating.md` |
|
|
17
|
+
| `'slider_matrix'` | — | `SliderMatrixScale` | `MatrixAnswerMap` | `question-types/10-slider-matrix.md` | `03-ui-specs/09-slider-matrix.md` |
|
|
18
|
+
| `'file_upload'` | — | `FileUploadScale` | `UploadedFile[]` | `question-types/11-file-upload.md` | `03-ui-specs/10-file-upload.md` |
|
|
19
|
+
| `'text_and_media'` | — | Inline in `Question` | (no answer) | `question-types/README.md#text_and_media` | `03-ui-specs/11-text-and-media.md` |
|
|
20
|
+
|
|
21
|
+
## Component Blueprint Docs
|
|
22
|
+
|
|
23
|
+
| Component | Blueprint |
|
|
24
|
+
|-----------|-----------|
|
|
25
|
+
| `SurveyPage` | `01-components/01-survey-page.md` |
|
|
26
|
+
| `Question` | `01-components/02-question.md` |
|
|
27
|
+
| `RatingScale` | `01-components/03-rating-scale.md` |
|
|
28
|
+
| `CsatScale` | `01-components/04-csat-scale.md` |
|
|
29
|
+
| `CsatMatrixScale` | `01-components/05-csat-matrix-scale.md` |
|
|
30
|
+
| `LikertMatrixScale` | `01-components/06-likert-matrix-scale.md` |
|
|
31
|
+
| `SliderMatrixScale` | `01-components/07-slider-matrix-scale.md` |
|
|
32
|
+
| `FileUploadScale` | `01-components/08-file-upload-scale.md` |
|
|
33
|
+
| `CustomSliderTrack` | `01-components/09-custom-slider-track.md` |
|
|
34
|
+
| `Header` / `Footer` | `01-components/10-header-footer.md` |
|
|
35
|
+
| `ProgressBar` | `01-components/11-progress-bar.md` |
|
|
36
|
+
| `LanguageSelector` | `01-components/12-language-selector.md` |
|
|
37
|
+
| `MatrixDropdown` | `01-components/13-matrix-dropdown.md` |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# UI Spec: Question Shell (`Question.tsx`)
|
|
2
|
+
|
|
3
|
+
> **Reference:** `apps/client/src/components/Question.tsx`
|
|
4
|
+
> **SDK:** All types extend `QuestionBase` from `fetchSurvey/types/base.ts`
|
|
5
|
+
> **Component blueprint:** `01-components/02-question.md`
|
|
6
|
+
|
|
7
|
+
## Config Inventory (QuestionBase)
|
|
8
|
+
|
|
9
|
+
| Field | Implemented | UI impact |
|
|
10
|
+
|-------|-------------|-----------|
|
|
11
|
+
| `id` | ✅ | `section id={question.id}` — required for scroll-to-error |
|
|
12
|
+
| `text` | ✅ | Heading via `dangerouslySetInnerHTML` |
|
|
13
|
+
| `description` | ✅ | Optional HTML block below title |
|
|
14
|
+
| `required` | ✅ | Red `*` after title (`text-red-500`) |
|
|
15
|
+
| `requiredErrorMessage` | ✅ | Shown via SDK validation in error banner |
|
|
16
|
+
| `questionNumber` | ❌ | Not rendered in reference client |
|
|
17
|
+
| `containerMediaUrl` | ❌ | Not rendered on generic questions |
|
|
18
|
+
|
|
19
|
+
## Layout Anatomy
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─ section (rounded-xl border border-gray-200 bg-white p-6 shadow-sm) ─────┐
|
|
23
|
+
│ h2: question.text (HTML) + red * if required │
|
|
24
|
+
│ [optional description HTML] │
|
|
25
|
+
│ ┌─ type-specific scale (see per-type specs) ─────────────────────┐ │
|
|
26
|
+
│ └─────────────────────────────────────────────────────────────────┘ │
|
|
27
|
+
│ [validation error banner if validationError prop] │
|
|
28
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Dispatcher Routing
|
|
32
|
+
|
|
33
|
+
| `question.type` | Condition | Component |
|
|
34
|
+
|-----------------|-----------|-----------|
|
|
35
|
+
| `rating` | — | `RatingScale` + label row in Question |
|
|
36
|
+
| `radio` | — | Inline card radios in Question |
|
|
37
|
+
| `text` | — | Inline textarea in Question |
|
|
38
|
+
| `csat` | — | `CsatScale` + min/max labels in Question |
|
|
39
|
+
| `matrix` | `CSAT_MATRIX` \| `RATING_MATRIX` | `CsatMatrixScale` |
|
|
40
|
+
| `matrix` | `CFM_MATRIX` | `LikertMatrixScale` |
|
|
41
|
+
| `slider_matrix` | — | `SliderMatrixScale` |
|
|
42
|
+
| `file_upload` | — | `FileUploadScale` |
|
|
43
|
+
| `text_and_media` | `mediaUrl` present | Inline media in Question |
|
|
44
|
+
| `slider` | — | ❌ Not routed — see `06-slider.md` |
|
|
45
|
+
| `rating_scale` | — | ❌ Not routed — see `05-rating-scale.md` |
|
|
46
|
+
|
|
47
|
+
## Label Positioning (rating)
|
|
48
|
+
|
|
49
|
+
Rendered in Question before `RatingScale`:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
// midLabel CSS left:
|
|
53
|
+
left: `${(midLabelIndex / (options.length - 1)) * 100}%`
|
|
54
|
+
// with -translate-x-1/2 for centering
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
minLabel: `absolute left-0`, maxLabel: `absolute right-0`, container `relative h-6 text-xs font-semibold text-gray-500`.
|
|
58
|
+
|
|
59
|
+
## Label Positioning (csat)
|
|
60
|
+
|
|
61
|
+
minLabel / maxLabel: `flex justify-between text-xs font-semibold text-gray-500` above `CsatScale`.
|
|
62
|
+
|
|
63
|
+
## Validation Error Banner
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
className="mt-4 rounded-[4px] border border-[#333333] bg-[#d9d9d9] py-3.5 px-4"
|
|
67
|
+
// text: text-[13px] font-bold text-gray-900
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Props Contract
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
type QuestionProps = {
|
|
74
|
+
question: SurveyQuestion;
|
|
75
|
+
selectedValue?: AnswerValue;
|
|
76
|
+
validationError?: string;
|
|
77
|
+
onSelect: (value: AnswerValue) => void;
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Wire `onSelect` → `onAction({ type: 'CHANGE', payload: { questionId, value } })`.
|
|
82
|
+
|
|
83
|
+
## Agent Checklist
|
|
84
|
+
|
|
85
|
+
- [ ] `id={question.id}` on outermost `<section>`
|
|
86
|
+
- [ ] HTML rendering for text and description
|
|
87
|
+
- [ ] Exhaustive type switch — all 12 types handled or explicitly noted as gap
|
|
88
|
+
- [ ] Validation error banner below scale
|
|
89
|
+
- [ ] Rating/csat endpoint labels positioned per formulas above
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# UI Spec: NPS Rating (`type: 'rating'`)
|
|
2
|
+
|
|
3
|
+
> **Reference:** `apps/client/src/components/RatingScale.tsx` + label row in `Question.tsx`
|
|
4
|
+
> **SDK type:** `RatingQuestion` — `fetchSurvey/types/rating.ts`
|
|
5
|
+
> **Component blueprint:** `01-components/03-rating-scale.md`
|
|
6
|
+
|
|
7
|
+
## Config Inventory
|
|
8
|
+
|
|
9
|
+
| Field | Implemented | UI impact |
|
|
10
|
+
|-------|-------------|-----------|
|
|
11
|
+
| `options[]` | ✅ | Badge row + radio row (dual layer) |
|
|
12
|
+
| `options[].color` | ✅ | Badge background; fallback `#e20074` |
|
|
13
|
+
| `options[].label` | ✅ | Displayed in badge |
|
|
14
|
+
| `options[].value` | ✅ | Selection value |
|
|
15
|
+
| `minLabel` | ✅ | Rendered in Question.tsx above scale |
|
|
16
|
+
| `midLabel` | ✅ | Absolute positioned in Question.tsx |
|
|
17
|
+
| `maxLabel` | ✅ | Absolute positioned in Question.tsx |
|
|
18
|
+
| `midLabelIndex` | ✅ | Mid label `left` percentage |
|
|
19
|
+
| `reverseScaleOrder` | ❌ | Document for agents when SDK sends true |
|
|
20
|
+
| `buttonVariant` | ❌ | Client always uses dual-layer radio layout |
|
|
21
|
+
|
|
22
|
+
## Layout Anatomy
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
[minLabel] [midLabel @ %] [maxLabel]
|
|
26
|
+
┌──┬──┬──┬── ... ──┬──┐ ← Row 1: colored badges (NOT clickable)
|
|
27
|
+
│0 │1 │2 │3 │ ... │10│ grid repeat(options.length), h-5 w-5 sm:h-6 w-6
|
|
28
|
+
└──┴──┴──┴── ... ──┴──┘ rounded-[3px], white bold text, option.color bg
|
|
29
|
+
┌──────────────────────────────────────────────┐
|
|
30
|
+
│ ○ ○ ● ○ ... ○ │ ← Row 2: gray track bg-[#f2f2f2]
|
|
31
|
+
└──────────────────────────────────────────────┘ h-11 sm:h-12, rounded-md
|
|
32
|
+
↑ selected cell: inner bg-[#fbe8f3]
|
|
33
|
+
radio: border-[4px] border-[#e20074] bg-white
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Config Decision Tree
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
rating
|
|
40
|
+
└── RatingScale (always dual-layer in reference client)
|
|
41
|
+
├── Row 1: display-only colored badges
|
|
42
|
+
└── Row 2: sr-only radio + custom circle per cell
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If `buttonVariant === 'numbered'` (SDK gap): badges become clickable buttons instead — not in reference client.
|
|
46
|
+
|
|
47
|
+
If `reverseScaleOrder`: reverse `options` array before mapping both rows.
|
|
48
|
+
|
|
49
|
+
## Interactive States
|
|
50
|
+
|
|
51
|
+
| State | Badge row | Track cell | Radio circle |
|
|
52
|
+
|-------|-----------|------------|--------------|
|
|
53
|
+
| Default | Full color | Transparent inner | `border-2 border-gray-300` |
|
|
54
|
+
| Hover | — | `hover:bg-gray-200/40` | — |
|
|
55
|
+
| Selected | Unchanged | `bg-[#fbe8f3]` | `border-[4px] border-[#e20074]` |
|
|
56
|
+
|
|
57
|
+
## Tooltips
|
|
58
|
+
|
|
59
|
+
Recommended: `title={option.label}` on each track `<label>`. Not in reference client yet.
|
|
60
|
+
|
|
61
|
+
## Answer Mutation
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
onSelect(option.value) // number
|
|
65
|
+
// → onAction CHANGE { questionId, value: number }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Accessibility
|
|
69
|
+
|
|
70
|
+
- `role="radiogroup"` on wrapper
|
|
71
|
+
- Native `<input type="radio" className="sr-only">` per cell
|
|
72
|
+
- `name={questionId}` groups radios
|
|
73
|
+
|
|
74
|
+
## Agent Checklist
|
|
75
|
+
|
|
76
|
+
- [ ] Two-row grid with `gridTemplateColumns: repeat(options.length, 1fr)`
|
|
77
|
+
- [ ] Badges use `option.color`, not hardcoded NPS colors unless option lacks color
|
|
78
|
+
- [ ] Only track row is clickable
|
|
79
|
+
- [ ] min/mid/max labels in Question wrapper with midLabelIndex formula
|
|
80
|
+
- [ ] Selected state: pink inner track + thick magenta radio ring
|