@explorer02/cfm-survey-sdk 0.1.3 → 0.1.5

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 (46) hide show
  1. package/package.json +1 -1
  2. package/postinstall.js +30 -18
  3. package/templates/AGENT.md +10 -1906
  4. package/templates/docs/01-sdk-core/01-fetch-survey.md +68 -0
  5. package/templates/docs/01-sdk-core/02-survey-mapper.md +85 -0
  6. package/templates/docs/01-sdk-core/03-question-mappers.md +114 -0
  7. package/templates/docs/01-sdk-core/04-pagination.md +72 -0
  8. package/templates/docs/01-sdk-core/05-validation.md +66 -0
  9. package/templates/docs/01-sdk-core/06-submit-survey.md +90 -0
  10. package/templates/docs/01-sdk-core/07-language-handling.md +111 -0
  11. package/templates/docs/01-sdk-core/08-icons-and-emojis.md +88 -0
  12. package/templates/docs/01-sdk-core/README.md +53 -0
  13. package/templates/docs/02-question-types/01-rating.md +52 -0
  14. package/templates/docs/02-question-types/02-radio.md +26 -0
  15. package/templates/docs/02-question-types/03-text.md +26 -0
  16. package/templates/docs/02-question-types/04-csat.md +54 -0
  17. package/templates/docs/02-question-types/05-rating-scale.md +26 -0
  18. package/templates/docs/02-question-types/06-slider.md +30 -0
  19. package/templates/docs/02-question-types/07-matrix-cfm.md +43 -0
  20. package/templates/docs/02-question-types/08-matrix-csat.md +29 -0
  21. package/templates/docs/02-question-types/09-matrix-rating.md +28 -0
  22. package/templates/docs/02-question-types/10-slider-matrix.md +40 -0
  23. package/templates/docs/02-question-types/11-file-upload.md +34 -0
  24. package/templates/docs/02-question-types/12-text-and-media.md +35 -0
  25. package/templates/docs/02-question-types/README.md +74 -0
  26. package/templates/docs/03-client-components/01-survey-page.md +113 -0
  27. package/templates/docs/03-client-components/02-question.md +57 -0
  28. package/templates/docs/03-client-components/03-rating-scale.md +38 -0
  29. package/templates/docs/03-client-components/04-csat-scale.md +40 -0
  30. package/templates/docs/03-client-components/05-csat-matrix-scale.md +43 -0
  31. package/templates/docs/03-client-components/06-likert-matrix-scale.md +38 -0
  32. package/templates/docs/03-client-components/07-slider-matrix-scale.md +44 -0
  33. package/templates/docs/03-client-components/08-file-upload-scale.md +33 -0
  34. package/templates/docs/03-client-components/09-custom-slider-track.md +27 -0
  35. package/templates/docs/03-client-components/10-header-footer.md +46 -0
  36. package/templates/docs/03-client-components/11-progress-bar.md +31 -0
  37. package/templates/docs/03-client-components/12-language-selector.md +41 -0
  38. package/templates/docs/03-client-components/13-matrix-dropdown.md +42 -0
  39. package/templates/docs/03-client-components/README.md +63 -0
  40. package/templates/docs/04-critical-rules/01-import-rules.md +51 -0
  41. package/templates/docs/04-critical-rules/02-action-dispatching.md +56 -0
  42. package/templates/docs/04-critical-rules/03-scroll-navigation.md +37 -0
  43. package/templates/docs/04-critical-rules/04-logo-branding.md +42 -0
  44. package/templates/docs/04-critical-rules/05-troubleshooting.md +46 -0
  45. package/templates/docs/04-critical-rules/README.md +29 -0
  46. package/templates/docs/index.md +129 -0
@@ -0,0 +1,88 @@
1
+ # Icons & Emojis — CSAT Visual System
2
+
3
+ > **Source**: `packages/sdk/src/ui/icons.tsx`
4
+ > **Exports**: `CsatEmojiSet`, `CsatEmojiMapping`, `CsatStarIcons`, `getEmojiForIndex`
5
+
6
+ ## What It Provides
7
+
8
+ Pre-built React elements for CSAT satisfaction faces and star rating icons. These are used by `CsatScale`, `CsatMatrixScale`, and `CustomSliderTrack` components.
9
+
10
+ ## Emoji Face System (11-Level)
11
+
12
+ The SDK ships an 11-level sentiment face scale using `react-icons/fa6`:
13
+
14
+ | Index | Face | Color | Icon |
15
+ |-------|------|-------|------|
16
+ | 1 | Very Angry | `#dc2626` | FaFaceAngry |
17
+ | 2 | Sad | `#e04832` | FaFaceSadTear |
18
+ | 3 | Frown | `#ea580c` | FaFaceFrown |
19
+ | 4 | Open Frown | `#f59e0b` | FaFaceFrownOpen |
20
+ | 5 | Meh | `#eab308` | FaFaceMeh |
21
+ | 6 | Neutral | `#a3a323` | FaFaceMeh |
22
+ | 7 | Smile | `#84cc16` | FaFaceSmile |
23
+ | 8 | Beam | `#65a30d` | FaFaceSmileBeam |
24
+ | 9 | Grin | `#22c55e` | FaFaceGrin |
25
+ | 10 | Wide Grin | `#16a34a` | FaFaceGrinWide |
26
+ | 11 | Star-Struck | `#059669` | FaFaceGrinStars |
27
+
28
+ ## `getEmojiForIndex(index, total)` — Adaptive Mapping
29
+
30
+ Maps a 0-based column index within a scale of `total` items to the correct face from the 11-level set.
31
+
32
+ **Curated subsets for common scale lengths:**
33
+ - 3 options → indices [3, 6, 8] from the 11-set
34
+ - 4 options → indices [3, 4, 7, 9]
35
+ - 5 options → indices [3, 4, 6, 7, 9]
36
+ - 6 options → indices [3, 4, 6, 7, 9, 11]
37
+
38
+ **For other lengths:** Interpolates linearly across the 11-set:
39
+ ```typescript
40
+ const percent = index / (total - 1);
41
+ const key = Math.min(11, Math.max(1, Math.round(percent * 10) + 1));
42
+ ```
43
+
44
+ ## `CsatEmojiMapping` — Legacy 1–5 Mapping
45
+
46
+ For standalone 5-point CSAT scales:
47
+ ```typescript
48
+ { 1: Face[1], 2: Face[4], 3: Face[6], 4: Face[8], 5: Face[11] }
49
+ ```
50
+
51
+ ## `CsatStarIcons` — Star Rating
52
+
53
+ ```typescript
54
+ {
55
+ filled: <FaStar style={{ color: '#e20074', fontSize: '28px' }} />,
56
+ empty: <FaRegStar style={{ color: '#d1d5db', fontSize: '28px' }} />,
57
+ }
58
+ ```
59
+
60
+ ## Usage in Components
61
+
62
+ ```tsx
63
+ import { getEmojiForIndex, CsatStarIcons, CsatEmojiMapping } from '@explorer02/cfm-survey-sdk';
64
+
65
+ // CSAT Matrix — dynamic emoji per column
66
+ {columns.map((col, i) => getEmojiForIndex(i, columns.length))}
67
+
68
+ // CSAT Scale — star rendering
69
+ {options.map((opt, i) => (
70
+ i < selectedIndex ? CsatStarIcons.filled : CsatStarIcons.empty
71
+ ))}
72
+
73
+ // Standalone CSAT — legacy 1-5 emoji
74
+ {CsatEmojiMapping[optionValue]}
75
+ ```
76
+
77
+ ## Important: `buttonType` Determines Rendering
78
+
79
+ The `CsatQuestion.buttonType` and `MatrixQuestion.buttonType` fields control which icon system to use:
80
+
81
+ | `buttonType` | Render Style |
82
+ |-------------|-------------|
83
+ | `'emoji'` | Sentiment face icons from `getEmojiForIndex` |
84
+ | `'star'` | Star icons from `CsatStarIcons` |
85
+ | `'numbered'` | Numeric badge buttons (no icons) |
86
+ | `'radio'` | Standard radio circles |
87
+ | `'graphics'` | Continuous slider with emoji thumb |
88
+ | `'dropdown'` | Collapsed select dropdown |
@@ -0,0 +1,53 @@
1
+ # SDK Core Architecture
2
+
3
+ > This folder documents the internal architecture of `@explorer02/cfm-survey-sdk`.
4
+ > Read these files **in order** to understand how the SDK processes a survey from network fetch to user submission.
5
+
6
+ ## Pipeline Overview
7
+
8
+ ```
9
+ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ ┌─────────────┐
10
+ │ Fetch API │ ──→ │ Map Survey │ ──→ │ Map Questions │ ──→ │ Paginate │
11
+ │ (network) │ │ (language) │ │ (per-type) │ │ (pages) │
12
+ └─────────────┘ └──────────────┘ └──────────────────┘ └─────────────┘
13
+
14
+
15
+ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ ┌─────────────┐
16
+ │ Submit │ ←── │ Format Body │ ←── │ Validate │ ←── │ Render UI │
17
+ │ (network) │ │ (payload) │ │ (required) │ │ (React) │
18
+ └─────────────┘ └──────────────┘ └──────────────────┘ └─────────────┘
19
+ ```
20
+
21
+ ## Files in This Folder
22
+
23
+ | File | Topic | When to Read |
24
+ |------|-------|-------------|
25
+ | `01-fetch-survey.md` | Network fetching, API endpoint, instanceId | **Always** — first step |
26
+ | `02-survey-mapper.md` | Raw API → Survey object transformation | **Always** — understand data shape |
27
+ | `03-question-mappers.md` | Question type dispatch, normalizeType | **Always** — core routing logic |
28
+ | `04-pagination.md` | Page navigation, currentPageIndex | **Always** — state management |
29
+ | `05-validation.md` | Required field validation, scroll-to-error | **Always** — UX critical |
30
+ | `06-submit-survey.md` | Response submission, payload formatting | **Always** — submission pipeline |
31
+ | `07-language-handling.md` | Translations, placeholders, language switching | When survey is multi-language |
32
+ | `08-icons-and-emojis.md` | CSAT emoji faces, star icons | When CSAT/rating_scale types present |
33
+
34
+ ## The Single Hook: `useSurveySDK()`
35
+
36
+ The entire SDK is consumed through **one hook**:
37
+
38
+ ```typescript
39
+ import { useSurveySDK } from '@explorer02/cfm-survey-sdk';
40
+
41
+ const { surveyQueryResults, submitSurveyResults, state, onAction } = useSurveySDK({
42
+ options: {
43
+ instanceId: string, // JWT token from client
44
+ language: string | undefined, // Active language code
45
+ placeholders: Record<string, string>, // Token replacements
46
+ debug: boolean, // Console logging
47
+ }
48
+ });
49
+ ```
50
+
51
+ This hook internally composes: `useFetchSurvey` → `useSurveyPagination` → `useSubmitResponse` → `useSurveyProgress`.
52
+
53
+ The client component **never** needs to import or call these internal hooks directly.
@@ -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,26 @@
1
+ # Radio Question (MCQ)
2
+
3
+ > **Type**: `'radio'` | **API Source**: `MCQ` (and any unrecognised answerType)
4
+ > **Mapper**: `radioMapper.ts` | **Component**: Inline in `Question.tsx`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type RadioQuestion = QuestionBase & {
10
+ type: 'radio';
11
+ options: SurveyOption[]; // Answer choices
12
+ isMultiSelect?: boolean; // If true, answer is (string|number)[]
13
+ };
14
+ ```
15
+
16
+ ## Answer Shape: `string | number`
17
+ Example: `"option_1"` or `3`
18
+ If `isMultiSelect: true` → `(string | number)[]`
19
+
20
+ ## Rendering Guidance
21
+ - Render as card-style option list with radio circles
22
+ - Each option: border card with custom radio indicator + label
23
+ - Selected state: brand-color border + light tint background + filled dot
24
+ - Use `<input type="radio" className="sr-only">` for accessibility
25
+ - Labels use `dangerouslySetInnerHTML` (may contain HTML)
26
+ - `onChange={() => onSelect(option.value)}`
@@ -0,0 +1,26 @@
1
+ # Text Question (Free Text)
2
+
3
+ > **Type**: `'text'` | **API Source**: `TEXTFIELD`
4
+ > **Mapper**: `textMapper.ts` | **Component**: Inline in `Question.tsx`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type TextQuestion = QuestionBase & {
10
+ type: 'text';
11
+ maxCharacterCount?: number; // Character limit
12
+ placeholder?: string; // Input placeholder text
13
+ inputVariant?: 'short' | 'long'; // Single-line vs textarea
14
+ };
15
+ ```
16
+
17
+ ## Answer Shape: `string`
18
+ Example: `"Great customer service, very satisfied"`
19
+
20
+ ## Rendering Guidance
21
+ - `inputVariant === 'short'` → `<input type="text">`, `inputVariant === 'long'` → `<textarea rows={4}>`
22
+ - Default to `<textarea>` if `inputVariant` is not specified
23
+ - Show character counter: `maxCharacterCount - currentLength` remaining
24
+ - `onChange={e => onSelect(e.target.value)}`
25
+ - Use `question.placeholder` or fallback: `"Type your response here..."`
26
+ - Focus state: brand-color border + ring
@@ -0,0 +1,54 @@
1
+ # CSAT Question (Satisfaction Scale)
2
+
3
+ > **Type**: `'csat'` | **API Source**: `SCALE` with `SCALE_TYPE=CSAT` or ≤5 options
4
+ > **Mapper**: `ratingMapper.ts` (CSAT branch) | **Component**: `CsatScale`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type CsatQuestion = QuestionBase & {
10
+ type: 'csat';
11
+ options: SurveyOption[]; // Usually 1-5 satisfaction levels
12
+ minLabel?: string; // "Very Dissatisfied"
13
+ maxLabel?: string; // "Very Satisfied"
14
+ buttonType?: SurveyButtonType; // 'emoji' | 'star' | 'numbered' | 'radio'
15
+ hasNotApplicable?: boolean; // N/A option available
16
+ reverseScaleOrder?: boolean; // Render high→low
17
+ labelPosition?: 'top' | 'bottom' | 'hidden';
18
+ };
19
+ ```
20
+
21
+ ## Answer Shape: `number | null`
22
+ Example: `4` or `null` (N/A selected)
23
+
24
+ ## Rendering by `buttonType`
25
+
26
+ | `buttonType` | Render As |
27
+ |-------------|-----------|
28
+ | `'emoji'` | Sentiment face icons from `CsatEmojiMapping` |
29
+ | `'star'` | Star icons from `CsatStarIcons` (filled/empty) |
30
+ | `'numbered'` | Numeric badge buttons |
31
+ | `'radio'` | Standard radio circles |
32
+ | `'graphics'` | Emoji slider (use `CustomSliderTrack`) |
33
+
34
+ ## Star Rating Pattern
35
+ ```tsx
36
+ {options.map((opt, i) => {
37
+ const numericValue = typeof opt.value === 'number' ? opt.value : i + 1;
38
+ const isActive = typeof selectedValue === 'number' && numericValue <= selectedValue;
39
+ return (
40
+ <button onClick={() => onSelect(opt.value)}>
41
+ {isActive ? CsatStarIcons.filled : CsatStarIcons.empty}
42
+ </button>
43
+ );
44
+ })}
45
+ ```
46
+
47
+ ## Emoji Pattern
48
+ ```tsx
49
+ {options.map((opt, i) => (
50
+ <button onClick={() => onSelect(opt.value)}>
51
+ {CsatEmojiMapping[typeof opt.value === 'number' ? opt.value : i + 1]}
52
+ </button>
53
+ ))}
54
+ ```
@@ -0,0 +1,26 @@
1
+ # Rating Scale Question (Stars/Emoji)
2
+
3
+ > **Type**: `'rating_scale'` | **API Source**: `SCALE` with `SCALE_TYPE=STAR/EMOJI/FIVE_POINT`
4
+ > **Mapper**: `ratingMapper.ts` (rating_scale branch) | **Component**: `RatingScale`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type RatingScaleQuestion = QuestionBase & {
10
+ type: 'rating_scale';
11
+ options: SurveyOption[]; // Typically 1-5 or 1-10 (no NPS colors)
12
+ minLabel?: string;
13
+ midLabel?: string;
14
+ maxLabel?: string;
15
+ midLabelIndex?: number;
16
+ reverseScaleOrder?: boolean;
17
+ scaleStyle?: 'star' | 'emoji';
18
+ };
19
+ ```
20
+
21
+ ## Answer Shape: `number`
22
+
23
+ ## Key Difference from `'rating'`
24
+ - No NPS traffic-light colors on options
25
+ - Rendered as stars, emojis, or plain icons — not numbered badges
26
+ - Use `scaleStyle` to determine icon type
@@ -0,0 +1,30 @@
1
+ # Slider Question (Single Range)
2
+
3
+ > **Type**: `'slider'` | **API Source**: `SCALE` with `INPUT_TYPE=SLIDER`
4
+ > **Mapper**: `ratingMapper.ts` (slider branch) | **Component**: `CustomSliderTrack` or native `<input type="range">`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type SliderQuestion = QuestionBase & {
10
+ type: 'slider';
11
+ min: number; // Left end value
12
+ max: number; // Right end value
13
+ step: number; // Increment between positions
14
+ defaultValue?: number; // Initial handle position
15
+ minLabel?: string; // Left label
16
+ maxLabel?: string; // Right label
17
+ enableInputBox?: boolean; // Show numeric input alongside
18
+ displayValues?: boolean; // Show value tooltip on thumb
19
+ };
20
+ ```
21
+
22
+ ## Answer Shape: `number`
23
+ Example: `65`
24
+
25
+ ## Rendering Guidance
26
+ - Use `<input type="range" min={min} max={max} step={step}>`
27
+ - Or use the `CustomSliderTrack` component for emoji-thumb variant
28
+ - Show labels at min/max ends
29
+ - If `enableInputBox` → show numeric `<input type="number">` alongside
30
+ - If `displayValues` → show floating tooltip showing current value
@@ -0,0 +1,43 @@
1
+ # Matrix CFM Question (Likert Grid)
2
+
3
+ > **Type**: `'matrix'` with `subType: 'CFM_MATRIX'` | **API Source**: `CFM_MATRIX`
4
+ > **Mapper**: `cfmMatrixMapper.ts` | **Component**: `LikertMatrixScale`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type MatrixQuestion = QuestionBase & {
10
+ type: 'matrix';
11
+ subType: 'CFM_MATRIX';
12
+ rows: MatrixRow[]; // Statements to rate
13
+ columns: MatrixColumn[]; // Shared answer options per row
14
+ buttonType?: SurveyButtonType;
15
+ hasNotApplicable?: boolean;
16
+ reverseScaleOrder?: boolean;
17
+ labels?: string[]; // Anchor labels above columns
18
+ matrixFormat?: 'standard' | 'carousel' | 'dropdown';
19
+ matrixType?: 'likert' | 'bipolar';
20
+ answerType?: 'single' | 'multiple';
21
+ transposeTable?: boolean;
22
+ repeatScale?: boolean;
23
+ enableScale?: boolean;
24
+ };
25
+
26
+ type MatrixRow = { id: string; text: string; rightText?: string; };
27
+ type MatrixColumn = { id: string; label: string; value: string | number | null; color?: string; };
28
+ ```
29
+
30
+ ## Answer Shape: `MatrixAnswerMap`
31
+ ```typescript
32
+ { "row1_id": "column_value", "row2_id": "column_value", "row3_id": null }
33
+ // null = N/A selected for that row
34
+ ```
35
+
36
+ ## Rendering Guidance
37
+ - **Standard format**: Grid table — rows on left, columns as headers
38
+ - **Carousel format**: Show one row at a time with prev/next
39
+ - **Dropdown format**: Each row has a dropdown selector for its column value
40
+ - Likert: unipolar text labels (e.g. "Strongly Disagree" → "Strongly Agree")
41
+ - Bipolar: `row.text` on left + `row.rightText` on right
42
+ - If `hasNotApplicable` → add N/A column with `value: null`
43
+ - `onSelect` receives the full `MatrixAnswerMap` object on each change
@@ -0,0 +1,29 @@
1
+ # Matrix CSAT Question (Satisfaction Grid)
2
+
3
+ > **Type**: `'matrix'` with `subType: 'CSAT_MATRIX'` | **API Source**: `CSAT_MATRIX`
4
+ > **Mapper**: `csatMatrixMapper.ts` | **Component**: `CsatMatrixScale`
5
+
6
+ ## TypeScript Type
7
+ Same as `MatrixQuestion` (see `07-matrix-cfm.md`) but with `subType: 'CSAT_MATRIX'`.
8
+
9
+ ## Key Difference from CFM_MATRIX
10
+ - Columns represent satisfaction levels (not text labels)
11
+ - Uses `buttonType` to determine visual: emoji faces, stars, numbered, etc.
12
+ - Uses `getEmojiForIndex(colIndex, totalCols)` for emoji rendering per cell
13
+
14
+ ## Rendering by `buttonType`
15
+
16
+ | `buttonType` | Cell Rendering |
17
+ |-------------|---------------|
18
+ | `'emoji'` | Sentiment face icons via `getEmojiForIndex` |
19
+ | `'star'` | Star icons via `CsatStarIcons` |
20
+ | `'numbered'` | Numeric badge buttons |
21
+ | `'radio'` | Standard radio circles |
22
+ | `'dropdown'` | Dropdown per row (uses `MatrixDropdown`) |
23
+ | `'graphics'` | Emoji slider per row (uses `CustomSliderTrack`) |
24
+
25
+ ## Answer Shape: `MatrixAnswerMap`
26
+ Same as CFM_MATRIX — `{ "row_id": column_value | null }`
27
+
28
+ ## N/A Column
29
+ If `hasNotApplicable` is true, the last column has `value: null`. Selecting it records `null` for that row, which is a valid answer (passes validation).
@@ -0,0 +1,28 @@
1
+ # Rating Matrix Question
2
+
3
+ > **Type**: `'rating_matrix'` | **API Source**: `RATING_MATRIX`
4
+ > **Mapper**: `ratingMatrixMapper.ts` → routed to `mapMatrixQuestion` | **Component**: `CsatMatrixScale`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type RatingMatrixQuestion = QuestionBase & {
10
+ type: 'rating_matrix';
11
+ rows: MatrixRow[];
12
+ columns: MatrixColumn[]; // Rating columns with NPS-style colors
13
+ minLabel?: string;
14
+ maxLabel?: string;
15
+ buttonType?: SurveyButtonType;
16
+ hasNotApplicable?: boolean;
17
+ reverseScaleOrder?: boolean;
18
+ labels?: string[];
19
+ };
20
+ ```
21
+
22
+ ## Key Difference from CSAT_MATRIX
23
+ - Columns carry traffic-light `color` values (like NPS rating)
24
+ - Has `minLabel`/`maxLabel` endpoint labels
25
+ - Rendered by the same `CsatMatrixScale` component
26
+
27
+ ## Answer Shape: `MatrixAnswerMap`
28
+ Same pattern: `{ "row_id": column_value | null }`
@@ -0,0 +1,40 @@
1
+ # Slider Matrix Question
2
+
3
+ > **Type**: `'slider_matrix'` | **API Source**: `SLIDER_MATRIX`
4
+ > **Mapper**: `sliderMatrixMapper.ts` | **Component**: `SliderMatrixScale`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type SliderMatrixQuestion = QuestionBase & {
10
+ type: 'slider_matrix';
11
+ rows: SliderMatrixRow[]; // Each row has independent slider config
12
+ labels?: string[]; // Anchor labels above track
13
+ ticks?: number; // Number of tick marks (default 10)
14
+ tickValues?: TickValue[]; // Pre-calculated tick values with colors
15
+ enableInputBox?: boolean;
16
+ displayValues?: boolean;
17
+ enableNotApplicable?: boolean;
18
+ sliderType?: 'graphics'; // Emoji thumb slider
19
+ };
20
+
21
+ type SliderMatrixRow = MatrixRow & {
22
+ min: number; max: number; step: number; defaultValue?: number;
23
+ };
24
+
25
+ type TickValue = { value: number; label: string; color: string; };
26
+ ```
27
+
28
+ ## Answer Shape: `MatrixAnswerMap`
29
+ ```typescript
30
+ { "row1_id": 75, "row2_id": 30, "row3_id": null }
31
+ // number = slider position, null = N/A
32
+ ```
33
+
34
+ ## Rendering Guidance
35
+ - Each row: row label + independent range slider
36
+ - If `sliderType === 'graphics'` → use `CustomSliderTrack` (emoji thumb)
37
+ - If `tickValues` available → render tick marks with labels and colors
38
+ - If `enableInputBox` → show numeric input alongside slider
39
+ - If `enableNotApplicable` → add N/A checkbox per row
40
+ - `onSelect` receives the full `MatrixAnswerMap` with all row values
@@ -0,0 +1,34 @@
1
+ # File Upload Question
2
+
3
+ > **Type**: `'file_upload'` | **API Source**: `FILE_UPLOAD`
4
+ > **Mapper**: `fileUploadMapper.ts` | **Component**: `FileUploadScale`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type FileUploadQuestion = QuestionBase & {
10
+ type: 'file_upload';
11
+ uploadMessage?: string; // Instruction text (HTML)
12
+ supportedFileFormats?: string[]; // ['PDF', 'PNG', 'DOCX'] (uppercase, no dots)
13
+ maxFileCount?: number; // Max files allowed
14
+ fileSizeLimit?: number; // Max size in MB
15
+ fileSizeLimitType?: 'PER_FILE' | 'IN_TOTAL';
16
+ };
17
+
18
+ type UploadedFile = {
19
+ id: string; name: string; mediaUrl: string;
20
+ mimeType?: string; size?: number;
21
+ };
22
+ ```
23
+
24
+ ## Answer Shape: `UploadedFile[]`
25
+ Array of uploaded file descriptors. Submitted via `quesIdVsAttachmentDetails` (NOT `questionToAnswers`).
26
+
27
+ ## Rendering Guidance
28
+ - Drag-and-drop zone + click-to-upload button
29
+ - Show accepted formats from `supportedFileFormats`
30
+ - Set `<input type="file" accept=".pdf,.png,.docx">`
31
+ - Enforce `maxFileCount` in UI
32
+ - Show file list with name, size, and remove button
33
+ - Enforce `fileSizeLimit` with appropriate error messages
34
+ - `onSelect` receives the full `UploadedFile[]` array
@@ -0,0 +1,35 @@
1
+ # Text and Media Question (Display-Only)
2
+
3
+ > **Type**: `'text_and_media'` | **API Source**: `TEXT_AND_MEDIA`
4
+ > **Mapper**: `textAndMediaMapper.ts` | **Component**: Inline in `Question.tsx`
5
+
6
+ ## TypeScript Type
7
+
8
+ ```typescript
9
+ type TextAndMediaQuestion = QuestionBase & {
10
+ type: 'text_and_media';
11
+ mediaUrl?: string; // Image or video URL
12
+ mediaMimeType?: string; // 'image/*' or 'video/*'
13
+ mediaTitle?: string; // Caption/alt text
14
+ mediaAlignment?: string; // Layout position
15
+ mediaSize?: number; // Width percentage
16
+ };
17
+ ```
18
+
19
+ ## Answer Shape: NONE
20
+ This is **display-only** — no answer is collected.
21
+
22
+ ## ⚠️ Critical Rules
23
+ - **DO NOT** dispatch a `CHANGE` action for this question type
24
+ - **Excluded** from validation (always passes)
25
+ - **Excluded** from progress percentage counting
26
+ - **Skipped** in `buildSubmitRequestBody()` — nothing is submitted
27
+
28
+ ## Rendering Guidance
29
+ - Show `question.text` as the heading
30
+ - If `mediaUrl` exists:
31
+ - `mediaMimeType` starts with `'video/'` → `<video controls src={mediaUrl}>`
32
+ - Otherwise → `<img src={mediaUrl} alt={mediaTitle}>`
33
+ - Position media according to `mediaAlignment`
34
+ - Size media according to `mediaSize` percentage
35
+ - Show `mediaTitle` as caption below media
@@ -0,0 +1,74 @@
1
+ # Question Types Overview
2
+
3
+ > This folder contains one detailed doc per question type. Each doc covers: TypeScript type, mapper logic, answer shape, properties, and rendering guidance.
4
+
5
+ ## The `SurveyQuestion` Discriminated Union
6
+
7
+ The SDK maps API questions into a **discriminated union** using the `type` field:
8
+
9
+ ```typescript
10
+ type SurveyQuestion =
11
+ | RatingQuestion // type: 'rating' — NPS 0-10 numeric scale
12
+ | RadioQuestion // type: 'radio' — MCQ single/multi select
13
+ | TextQuestion // type: 'text' — Free text input
14
+ | CsatQuestion // type: 'csat' — 1-5 satisfaction (emoji/star)
15
+ | RatingScaleQuestion // type: 'rating_scale' — Star/emoji rating (1-5/1-10)
16
+ | SliderQuestion // type: 'slider' — Single range slider
17
+ | MatrixQuestion // type: 'matrix' — Grid (CFM/CSAT/RATING subTypes)
18
+ | RatingMatrixQuestion // type: 'rating_matrix' — NPS-style rating grid
19
+ | SliderMatrixQuestion // type: 'slider_matrix' — Grid of range sliders
20
+ | FileUploadQuestion // type: 'file_upload' — File attachment
21
+ | TextAndMediaQuestion; // type: 'text_and_media' — Display-only media card
22
+ ```
23
+
24
+ ## Question → Component Routing Table
25
+
26
+ | `question.type` | `question.subType` | Client Component | Answer Shape |
27
+ |-----------------|-------------------|-----------------|-------------|
28
+ | `'rating'` | — | `RatingScale` | `number` |
29
+ | `'radio'` | — | Inline radio cards | `string \| number` |
30
+ | `'text'` | — | Inline textarea | `string` |
31
+ | `'csat'` | — | `CsatScale` | `number \| null` |
32
+ | `'rating_scale'` | — | `RatingScale` (star/emoji variant) | `number` |
33
+ | `'slider'` | — | `CustomSliderTrack` | `number` |
34
+ | `'matrix'` | `'CFM_MATRIX'` | `LikertMatrixScale` | `MatrixAnswerMap` |
35
+ | `'matrix'` | `'CSAT_MATRIX'` | `CsatMatrixScale` | `MatrixAnswerMap` |
36
+ | `'matrix'` | `'RATING_MATRIX'` | `CsatMatrixScale` | `MatrixAnswerMap` |
37
+ | `'rating_matrix'` | — | `CsatMatrixScale` | `MatrixAnswerMap` |
38
+ | `'slider_matrix'` | — | `SliderMatrixScale` | `MatrixAnswerMap` |
39
+ | `'file_upload'` | — | `FileUploadScale` | `UploadedFile[]` |
40
+ | `'text_and_media'` | — | Inline media card | (no answer) |
41
+
42
+ ## Shared Base Properties
43
+
44
+ All question types extend `QuestionBase`:
45
+
46
+ | Property | Type | Description |
47
+ |----------|------|-------------|
48
+ | `id` | `string` | ⚠️ MUST be DOM element id |
49
+ | `text` | `string` | Question text (may contain HTML) |
50
+ | `description` | `string?` | Supplementary instruction (HTML) |
51
+ | `required` | `boolean?` | Mandatory answer flag |
52
+ | `requiredErrorMessage` | `string?` | Custom validation error |
53
+ | `questionNumber` | `number?` | 1-indexed ordinal |
54
+ | `containerMediaUrl` | `string?` | Image/video alongside question |
55
+ | `containerMediaMimeType` | `string?` | MIME type for container media |
56
+ | `containerMediaAlignment` | `string?` | Media layout position |
57
+ | `containerMediaSize` | `number?` | Media width percentage |
58
+
59
+ ## Files in This Folder
60
+
61
+ | File | Question Type | API Source |
62
+ |------|-------------|-----------|
63
+ | `01-rating.md` | `RatingQuestion` | `SCALE` (NPS/TEN_POINT) |
64
+ | `02-radio.md` | `RadioQuestion` | `MCQ` |
65
+ | `03-text.md` | `TextQuestion` | `TEXTFIELD` |
66
+ | `04-csat.md` | `CsatQuestion` | `SCALE` (CSAT, ≤5 options) |
67
+ | `05-rating-scale.md` | `RatingScaleQuestion` | `SCALE` (STAR/EMOJI) |
68
+ | `06-slider.md` | `SliderQuestion` | `SCALE` (INPUT_TYPE=SLIDER) |
69
+ | `07-matrix-cfm.md` | `MatrixQuestion` CFM | `CFM_MATRIX` |
70
+ | `08-matrix-csat.md` | `MatrixQuestion` CSAT | `CSAT_MATRIX` |
71
+ | `09-matrix-rating.md` | `RatingMatrixQuestion` | `RATING_MATRIX` |
72
+ | `10-slider-matrix.md` | `SliderMatrixQuestion` | `SLIDER_MATRIX` |
73
+ | `11-file-upload.md` | `FileUploadQuestion` | `FILE_UPLOAD` |
74
+ | `12-text-and-media.md` | `TextAndMediaQuestion` | `TEXT_AND_MEDIA` |