@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.
Files changed (75) hide show
  1. package/dist/index.d.mts +24 -26
  2. package/dist/index.d.ts +24 -26
  3. package/dist/index.js +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/package.json +2 -2
  6. package/postinstall.js +21 -30
  7. package/templates/AGENT.md +24 -10
  8. package/templates/docs/00-integration/constraints.md +54 -0
  9. package/templates/docs/00-integration/setup.md +80 -0
  10. package/templates/docs/00-integration/useSurveySDK.md +107 -0
  11. package/templates/docs/{03-client-components → 01-components}/01-survey-page.md +2 -0
  12. package/templates/docs/{03-client-components → 01-components}/02-question.md +2 -0
  13. package/templates/docs/{03-client-components → 01-components}/03-rating-scale.md +2 -0
  14. package/templates/docs/{03-client-components → 01-components}/04-csat-scale.md +3 -1
  15. package/templates/docs/{03-client-components → 01-components}/05-csat-matrix-scale.md +2 -0
  16. package/templates/docs/{03-client-components → 01-components}/06-likert-matrix-scale.md +2 -0
  17. package/templates/docs/{03-client-components → 01-components}/07-slider-matrix-scale.md +2 -0
  18. package/templates/docs/{03-client-components → 01-components}/08-file-upload-scale.md +2 -0
  19. package/templates/docs/{03-client-components → 01-components}/09-custom-slider-track.md +2 -0
  20. package/templates/docs/{03-client-components → 01-components}/10-header-footer.md +3 -1
  21. package/templates/docs/{03-client-components → 01-components}/11-progress-bar.md +2 -0
  22. package/templates/docs/{03-client-components → 01-components}/12-language-selector.md +2 -0
  23. package/templates/docs/{03-client-components → 01-components}/13-matrix-dropdown.md +2 -0
  24. package/templates/docs/{03-client-components → 01-components}/README.md +3 -3
  25. package/templates/docs/02-question-types/01-rating.md +0 -8
  26. package/templates/docs/02-reference/config-field-index.md +158 -0
  27. package/templates/docs/02-reference/question-types/01-rating.md +52 -0
  28. package/templates/docs/02-reference/question-types/README.md +71 -0
  29. package/templates/docs/02-reference/routing-table.md +37 -0
  30. package/templates/docs/03-ui-specs/00-question-shell.md +89 -0
  31. package/templates/docs/03-ui-specs/01-rating.md +80 -0
  32. package/templates/docs/03-ui-specs/02-radio.md +57 -0
  33. package/templates/docs/03-ui-specs/03-text.md +51 -0
  34. package/templates/docs/03-ui-specs/04-csat.md +57 -0
  35. package/templates/docs/03-ui-specs/05-rating-scale.md +49 -0
  36. package/templates/docs/03-ui-specs/06-slider.md +42 -0
  37. package/templates/docs/03-ui-specs/07-matrix-cfm.md +66 -0
  38. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +65 -0
  39. package/templates/docs/03-ui-specs/09-slider-matrix.md +61 -0
  40. package/templates/docs/03-ui-specs/10-file-upload.md +54 -0
  41. package/templates/docs/03-ui-specs/11-text-and-media.md +49 -0
  42. package/templates/docs/03-ui-specs/12-survey-chrome.md +60 -0
  43. package/templates/docs/03-ui-specs/README.md +79 -0
  44. package/templates/docs/03-ui-specs/shared/custom-slider-track.md +52 -0
  45. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +38 -0
  46. package/templates/docs/MANIFEST.json +150 -0
  47. package/templates/docs/index.md +111 -142
  48. package/templates/docs/templates/implementation_plan.md +91 -0
  49. package/templates/docs/01-sdk-core/01-fetch-survey.md +0 -68
  50. package/templates/docs/01-sdk-core/02-survey-mapper.md +0 -85
  51. package/templates/docs/01-sdk-core/03-question-mappers.md +0 -114
  52. package/templates/docs/01-sdk-core/04-pagination.md +0 -72
  53. package/templates/docs/01-sdk-core/05-validation.md +0 -66
  54. package/templates/docs/01-sdk-core/06-submit-survey.md +0 -90
  55. package/templates/docs/01-sdk-core/07-language-handling.md +0 -111
  56. package/templates/docs/01-sdk-core/08-icons-and-emojis.md +0 -56
  57. package/templates/docs/01-sdk-core/README.md +0 -53
  58. package/templates/docs/02-question-types/02-radio.md +0 -26
  59. package/templates/docs/02-question-types/03-text.md +0 -26
  60. package/templates/docs/02-question-types/12-text-and-media.md +0 -35
  61. package/templates/docs/02-question-types/README.md +0 -74
  62. package/templates/docs/04-critical-rules/01-import-rules.md +0 -51
  63. package/templates/docs/04-critical-rules/02-action-dispatching.md +0 -56
  64. package/templates/docs/04-critical-rules/03-scroll-navigation.md +0 -37
  65. package/templates/docs/04-critical-rules/04-logo-branding.md +0 -42
  66. package/templates/docs/04-critical-rules/05-troubleshooting.md +0 -46
  67. package/templates/docs/04-critical-rules/README.md +0 -29
  68. /package/templates/docs/{02-question-types → 02-reference/question-types}/04-csat.md +0 -0
  69. /package/templates/docs/{02-question-types → 02-reference/question-types}/05-rating-scale.md +0 -0
  70. /package/templates/docs/{02-question-types → 02-reference/question-types}/06-slider.md +0 -0
  71. /package/templates/docs/{02-question-types → 02-reference/question-types}/07-matrix-cfm.md +0 -0
  72. /package/templates/docs/{02-question-types → 02-reference/question-types}/08-matrix-csat.md +0 -0
  73. /package/templates/docs/{02-question-types → 02-reference/question-types}/09-matrix-rating.md +0 -0
  74. /package/templates/docs/{02-question-types → 02-reference/question-types}/10-slider-matrix.md +0 -0
  75. /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
- > **Target Components**: `CsatScale`, `RatingScale`, `CsatMatrixScale`
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
- > **Target Components**: `Header`, Global Styles
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
  # Progress Bar
2
2
 
3
+ > **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
4
+ > **Reference implementation:** `apps/client/src/components/ProgressBar.tsx`
3
5
  > **Component**: `ProgressBar`
4
6
  > **Role**: Displays completion status at the top of the survey.
5
7
 
@@ -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
- > This folder documents the React client components that render the survey UI.
4
- > It explains the architecture, the dispatcher (`Question.tsx`), and all 11 individual question scale components.
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 (`apps/client/src/components/Question.tsx`) 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.
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