@explorer02/cfm-survey-sdk 0.2.0 → 0.2.2

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 (88) hide show
  1. package/dist/cli/index.js +18 -18
  2. package/dist/cli/index.mjs +14 -14
  3. package/dist/index.d.mts +465 -580
  4. package/dist/index.d.ts +465 -580
  5. package/dist/index.js +1 -1
  6. package/dist/index.mjs +1 -1
  7. package/package.json +6 -4
  8. package/postinstall.js +96 -64
  9. package/templates/AGENT.md +15 -7
  10. package/templates/docs/00-integration/answer-logic-and-navigation.md +50 -0
  11. package/templates/docs/00-integration/aws-deploy.md +281 -0
  12. package/templates/docs/00-integration/client-integration-guide.md +351 -0
  13. package/templates/docs/00-integration/component-checklist.md +84 -0
  14. package/templates/docs/00-integration/constraints.md +65 -3
  15. package/templates/docs/00-integration/custom-field-logic-and-navigation.md +174 -0
  16. package/templates/docs/00-integration/display-logic-and-navigation.md +49 -0
  17. package/templates/docs/00-integration/end-page-logic.md +64 -0
  18. package/templates/docs/00-integration/partial-save-and-recovery.md +74 -0
  19. package/templates/docs/00-integration/placeholders-and-tokens.md +116 -0
  20. package/templates/docs/00-integration/progress.md +61 -0
  21. package/templates/docs/00-integration/question-display-variants.md +65 -0
  22. package/templates/docs/00-integration/question-numbering.md +51 -0
  23. package/templates/docs/00-integration/question-type-sdk-matrix.md +242 -0
  24. package/templates/docs/00-integration/setup.md +84 -0
  25. package/templates/docs/00-integration/skip-logic-and-navigation.md +80 -0
  26. package/templates/docs/00-integration/survey-lifecycle-analytics.md +94 -0
  27. package/templates/docs/00-integration/useSurveySDK.md +151 -23
  28. package/templates/docs/01-components/01-survey-page.md +76 -54
  29. package/templates/docs/01-components/02-question.md +77 -89
  30. package/templates/docs/01-components/03-rating-scale.md +11 -1
  31. package/templates/docs/01-components/04-star-rating-scale.md +45 -0
  32. package/templates/docs/01-components/05-csat-matrix-scale.md +20 -11
  33. package/templates/docs/01-components/06-likert-matrix-scale.md +12 -3
  34. package/templates/docs/01-components/07-slider-matrix-scale.md +11 -2
  35. package/templates/docs/01-components/08-file-upload-scale.md +10 -1
  36. package/templates/docs/01-components/09-custom-slider-track.md +2 -2
  37. package/templates/docs/01-components/11-progress-bar.md +10 -4
  38. package/templates/docs/01-components/13-matrix-dropdown.md +3 -1
  39. package/templates/docs/01-components/14-intro-page.md +34 -0
  40. package/templates/docs/01-components/15-end-page.md +31 -0
  41. package/templates/docs/01-components/16-paused-page.md +27 -0
  42. package/templates/docs/01-components/17-heatmap-scale.md +75 -0
  43. package/templates/docs/01-components/18-rank-order-scale.md +100 -0
  44. package/templates/docs/01-components/README.md +44 -36
  45. package/templates/docs/02-reference/config-field-index.md +141 -139
  46. package/templates/docs/02-reference/exports.md +149 -0
  47. package/templates/docs/02-reference/question-types/01-rating.md +31 -32
  48. package/templates/docs/02-reference/question-types/04-csat.md +51 -42
  49. package/templates/docs/02-reference/question-types/05-rating-scale.md +16 -11
  50. package/templates/docs/02-reference/question-types/06-slider.md +2 -2
  51. package/templates/docs/02-reference/question-types/07-matrix-cfm.md +44 -30
  52. package/templates/docs/02-reference/question-types/08-matrix-csat.md +4 -26
  53. package/templates/docs/02-reference/question-types/09-matrix-rating.md +29 -13
  54. package/templates/docs/02-reference/question-types/10-slider-matrix.md +15 -4
  55. package/templates/docs/02-reference/question-types/11-file-upload.md +13 -2
  56. package/templates/docs/02-reference/question-types/12-heatmap.md +59 -0
  57. package/templates/docs/02-reference/question-types/13-rank-order.md +101 -0
  58. package/templates/docs/02-reference/question-types/README.md +86 -47
  59. package/templates/docs/02-reference/routing-table.md +33 -18
  60. package/templates/docs/02-reference/value-derivation.md +63 -0
  61. package/templates/docs/03-ui-specs/00-question-shell.md +24 -80
  62. package/templates/docs/03-ui-specs/01-rating.md +12 -54
  63. package/templates/docs/03-ui-specs/02-radio.md +21 -33
  64. package/templates/docs/03-ui-specs/03-text.md +2 -2
  65. package/templates/docs/03-ui-specs/04-csat.md +38 -33
  66. package/templates/docs/03-ui-specs/05-rating-scale.md +21 -18
  67. package/templates/docs/03-ui-specs/06-slider.md +3 -1
  68. package/templates/docs/03-ui-specs/07-matrix-cfm.md +25 -22
  69. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +18 -46
  70. package/templates/docs/03-ui-specs/09-slider-matrix.md +2 -2
  71. package/templates/docs/03-ui-specs/10-file-upload.md +2 -2
  72. package/templates/docs/03-ui-specs/11-text-and-media.md +29 -29
  73. package/templates/docs/03-ui-specs/12-survey-chrome.md +20 -14
  74. package/templates/docs/03-ui-specs/13-heatmap.md +73 -0
  75. package/templates/docs/03-ui-specs/14-rank-order.md +59 -0
  76. package/templates/docs/03-ui-specs/README.md +14 -13
  77. package/templates/docs/03-ui-specs/shared/custom-slider-track.md +3 -3
  78. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +6 -1
  79. package/templates/docs/MANIFEST.json +454 -67
  80. package/templates/docs/index.md +84 -29
  81. package/templates/docs/templates/Question.tsx +310 -0
  82. package/templates/docs/templates/deploy-to-aws.sh +114 -0
  83. package/templates/docs/templates/heatmapCoords.ts +58 -0
  84. package/templates/docs/templates/implementation_plan.md +115 -49
  85. package/templates/docs/templates/survey-inventory.schema.json +72 -0
  86. package/templates/docs/templates/verify-agent-build.sh +53 -0
  87. package/templates/docs/01-components/04-csat-scale.md +0 -40
  88. package/templates/docs/02-question-types/01-rating.md +0 -44
@@ -1,107 +1,95 @@
1
1
  # Question Dispatcher Architectural Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/00-question-shell.md`
4
- > **Reference implementation:** `apps/client/src/components/Question.tsx`
4
+ > **Canonical template:** [`templates/Question.tsx`](../templates/Question.tsx) — copy verbatim, then create imported components
5
+ > **Monorepo reference:** `apps/client/src/components/Question.tsx`
5
6
  > **Target Component**: `Question`
6
- > **Role**: Wrapper card component that routes to the correct scale component based on `question.type`.
7
7
 
8
- ## Core Responsibility
9
- Render a visually separated container (card) for each question. Handle rich text parsing, required asterisks, description text, and error validation messages. Most importantly, it MUST act as an exhaustive switch statement dispatching to ALL supported question types.
8
+ ## Start Here (CRITICAL)
10
9
 
11
- ## Exhaustive Dispatcher Logic (CRITICAL)
10
+ 1. Copy [`docs/templates/Question.tsx`](../templates/Question.tsx) into your project.
11
+ 2. Create every imported scale component — see [`component-checklist.md`](../00-integration/component-checklist.md).
12
+ 3. Read `03-ui-specs/` for each type in your fetched survey **before** editing the dispatcher.
12
13
 
13
- The agent MUST handle EVERY SINGLE ONE of these `question.type` values. Failure to handle them will result in blank UI or broken surveys.
14
+ **Do NOT** write partial skeletons with `default` fallbacks or `"not yet implemented"` messages.
14
15
 
15
- 1. **`rating`**: Renders `RatingScale`. This covers NPS, Stars, Emoji, and generic numeric scales.
16
- 2. **`csat`**: Renders `CsatScale`. This covers CSAT specific faces/icons.
17
- 3. **`radio`**: Renders inline MCQ radio buttons. (Can be implemented inside this file or separately).
18
- 4. **`text`**: Renders an inline `<textarea>` or `<input>`.
19
- 5. **`matrix`**:
20
- - If `subType === 'CFM_MATRIX'`, render `LikertMatrixScale`
21
- - If `subType === 'CSAT_MATRIX'` or `'RATING_MATRIX'`, render `CsatMatrixScale`
22
- 6. **`slider_matrix`**: Renders `SliderMatrixScale`
23
- 7. **`file_upload`**: Renders `FileUploadScale`
24
- 8. **`text_and_media`**: Renders inline `<video>` or `<img>` based on `question.mediaUrl`.
25
- - *Note: `text_and_media` questions may NOT have user input. Do not render a scale for them.*
16
+ ## Dispatcher (11 type literals)
26
17
 
27
- ## Container UI Structure
28
-
29
- Every question MUST be wrapped in a standard HTML `<section>` to visually separate it.
18
+ Use `QUESTION_TYPE` from the SDK:
30
19
 
31
- 1. **Wrapper styling**:
32
- - Border: `border border-gray-200`
33
- - Radius: `rounded-xl`
34
- - Background: `bg-white`
35
- - Padding: `p-6 md:p-8`
36
- - Shadow & Hover: `shadow-sm transition-all hover:shadow-md`
37
- - **Crucial**: ID must be set to `question.id` (`id={question.id}`) so scroll-to-error works!
20
+ | `question.type` | Renders |
21
+ |-----------------|---------|
22
+ | `NPS_SCALE` | `RatingScale` (NPS) |
23
+ | `MCQ` | Inline radio/checkbox |
24
+ | `TEXTFIELD` | Inline textarea/input |
25
+ | `CFM_MATRIX` | `LikertMatrixScale` |
26
+ | `CSAT_MATRIX`, `RATING_MATRIX` | `CsatMatrixScale` |
27
+ | `SLIDER_MATRIX` | `SliderMatrixScale` |
28
+ | `FILE_UPLOAD` | `FileUploadScale` |
29
+ | `TEXT_AND_MEDIA` | Media only (no answer) |
30
+ | `HEATMAP` | `HeatmapScale` |
31
+ | `RANK_ORDER` | `RankOrderScale` |
38
32
 
39
- 2. **Heading (`question.text`)**:
40
- - MUST use `dangerouslySetInnerHTML={{ __html: question.text }}` because text from Sprinklr contains rich HTML (bold, links, italics).
41
- - If `question.required === true`, append a red asterisk `*`.
33
+ **Never** branch on `subType`.
42
34
 
43
- 3. **Description (`question.description`)**:
44
- - Render conditionally if `question.description` exists.
45
- - MUST use `dangerouslySetInnerHTML`.
46
- - Style with slightly smaller text, greyish color (e.g., `text-sm text-gray-600 mt-2`).
35
+ ```tsx
36
+ import { QUESTION_TYPE } from '@explorer02/cfm-survey-sdk';
47
37
 
48
- 4. **Validation Error Banner**:
49
- - If `validationError` prop is passed, render a highly visible banner below the scale.
50
- - Example: Red border-left, red background, bold text.
38
+ {question.type === QUESTION_TYPE.CFM_MATRIX && <LikertMatrixScale ... />}
39
+ ```
51
40
 
52
- ## Hover Tooltips & Micro-interactions
53
- - Focus states on text inputs MUST use the brand primary color (e.g., magenta `#e20074` for Telekom).
54
- - Radio button wrappers should have a subtle background hover effect (e.g., `hover:bg-pink-50`).
41
+ ## Placeholder resolution
55
42
 
56
- ## Example Architectural Skeleton
43
+ Pass `allAnswers` and `allQuestions` so question text can reference prior answers:
57
44
 
58
45
  ```tsx
59
- <section id={question.id} className="space-y-6 rounded-xl border border-gray-200 bg-white p-6 md:p-8 shadow-sm transition-all hover:shadow-md">
60
-
61
- {/* HEADING */}
62
- <h2 className="text-lg md:text-xl font-semibold leading-relaxed text-gray-900 flex items-start gap-1">
63
- <span dangerouslySetInnerHTML={{ __html: question.text }} className="prose prose-sm max-w-none" />
64
- {question.required && <span className="text-[#e20074] shrink-0">*</span>}
65
- </h2>
66
-
67
- {/* DESCRIPTION */}
68
- {question.description && (
69
- <div className="text-sm text-gray-600 mt-2 leading-relaxed prose prose-sm max-w-none" dangerouslySetInnerHTML={{ __html: question.description }} />
70
- )}
71
-
72
- {/* DISPATCHER */}
73
- <div className="pt-4">
74
- {question.type === 'rating' && <RatingScale ... />}
75
- {question.type === 'csat' && <CsatScale ... />}
76
- {question.type === 'slider_matrix' && <SliderMatrixScale ... />}
77
- {question.type === 'file_upload' && <FileUploadScale ... />}
78
- {question.type === 'matrix' && question.subType === 'CFM_MATRIX' && <LikertMatrixScale ... />}
79
- {question.type === 'matrix' && (question.subType === 'CSAT_MATRIX' || question.subType === 'RATING_MATRIX') && <CsatMatrixScale ... />}
80
-
81
- {question.type === 'radio' && (
82
- // Inline Radio implementation
83
- // Make sure to match brand hover colors!
84
- )}
85
-
86
- {question.type === 'text' && (
87
- // Inline Textarea implementation
88
- // Handle character limit `question.maxCharacterCount`
89
- )}
90
-
91
- {question.type === 'text_and_media' && (
92
- // Display media (image/video) based on question properties
93
- // This handles CONTENT blocks.
94
- )}
95
- </div>
96
-
97
- {/* ERROR BANNER */}
98
- {validationError && (
99
- <div className="mt-6 rounded-lg border-l-4 border-l-[#e20074] bg-red-50 p-4">
100
- <p className="text-sm font-bold text-gray-900 leading-tight flex items-center gap-2">
101
- <span className="text-[#e20074]">⚠</span> {validationError}
102
- </p>
103
- </div>
104
- )}
105
-
106
- </section>
46
+ resolveSurveyQuestionPlaceholders(question.questionText, allAnswers, questionsById)
47
+ ```
48
+
49
+ ## Props Contract
50
+
51
+ ```typescript
52
+ type QuestionProps = {
53
+ question: SurveyQuestion;
54
+ selectedValue?: AnswerValue;
55
+ validationError?: string;
56
+ allAnswers: SurveyAnswers;
57
+ allQuestions: SurveyQuestion[];
58
+ onSelect: (answerValue: AnswerValue) => void;
59
+ };
107
60
  ```
61
+
62
+ Wire `onSelect` → `onAction({ type: 'CHANGE', payload: { questionId, answerValue } })`.
63
+
64
+ ## Container UI Structure
65
+
66
+ - Wrapper: `<section id={question.id} ...>` — **required** for scroll-to-error
67
+ - `questionText` / `questionDescription`: `dangerouslySetInnerHTML`
68
+ - Required asterisk when `question.required`
69
+ - Validation error banner when `validationError` is set
70
+
71
+ ## Matrix value storage
72
+
73
+ Store matrix cell values as column **`id`** via `matrixColumnStoredValue(col)` — see [`value-derivation.md`](../02-reference/value-derivation.md).
74
+
75
+ ## SDK Integration Checklist — inline types (agent)
76
+
77
+ ### MCQ (`QUESTION_TYPE.MCQ`)
78
+
79
+ - [ ] Filter options with `getVisibleMcqOptionsForAnswers` before render
80
+ - [ ] Store `option.id`; support `isMultiSelect` array shape when configured
81
+ - [ ] onAction CHANGE: `{ questionId, answerValue: optionId | optionId[] }`
82
+
83
+ ### TEXTFIELD (`QUESTION_TYPE.TEXTFIELD`)
84
+
85
+ - [ ] Render `inputVariant` (`short` single-line vs `long` textarea)
86
+ - [ ] onAction CHANGE: `{ questionId, answerValue: string }`
87
+ - [ ] Debounce long text if partial save enabled
88
+
89
+ ### TEXT_AND_MEDIA (`QUESTION_TYPE.TEXT_AND_MEDIA`)
90
+
91
+ - [ ] No answer input — no CHANGE action
92
+ - [ ] Render media block when `question.media?.url` present
93
+ - [ ] User advances with NEXT
94
+
95
+ See [`question-type-sdk-matrix.md`](../00-integration/question-type-sdk-matrix.md) for all 11 types.
@@ -3,7 +3,7 @@
3
3
  > **UI spec (authoritative):** `03-ui-specs/01-rating.md` (NPS) · `03-ui-specs/05-rating-scale.md` (star/emoji gap)
4
4
  > **Reference implementation:** `apps/client/src/components/RatingScale.tsx`
5
5
  > **Target Component**: `RatingScale`
6
- > **Handles**: `type: 'rating'`, `type: 'rating_scale'`
6
+ > **Handles**: `type: 'NPS_SCALE'` (via `QUESTION_TYPE.NPS_SCALE`)
7
7
 
8
8
  ## Core Responsibility
9
9
  Render a horizontal sequence of numbered badges.
@@ -28,3 +28,13 @@ The UI must exactly match enterprise standards:
28
28
  3. **Selected State**: The selected badge MUST pop out prominently: `border-2 border-[#e20074] shadow-md scale-105 opacity-100 font-bold`.
29
29
  4. **Hover State**: `hover:scale-105 hover:border-[#e20074] transition-all cursor-pointer`.
30
30
  5. **Tooltips**: Every badge MUST have `title={option.label}` applied for accessibility and semantic context (e.g. hovering '10' shows 'Extremely Likely').
31
+
32
+ ## SDK Integration Checklist (agent)
33
+
34
+ - [ ] Type literal: `QUESTION_TYPE.NPS_SCALE`
35
+ - [ ] Answer stored as: `number`
36
+ - [ ] onAction CHANGE: `{ questionId, answerValue: number }`
37
+ - [ ] Visibility helper: `getVisibleScaleColumnsForAnswers` when scale points have answer logic
38
+ - [ ] Custom fields: 4th arg on scale visibility helper when needed
39
+ - [ ] Logic features: skip ✓ display ✓ answer ✓ variants ✓ numbering ✓
40
+ - [ ] Matrix row: [`question-type-sdk-matrix.md`](../00-integration/question-type-sdk-matrix.md#nps_scale)
@@ -0,0 +1,45 @@
1
+ # Star Rating Scale Architectural Blueprint
2
+
3
+ > **DEPRECATED:** API RATING pages map to `NPS_SCALE` or matrix display styles — not a separate `question.type`. Keep for historical reference only.
4
+ >
5
+ > **UI spec (authoritative):** `03-ui-specs/05-rating-scale.md`
6
+ > **SDK type:** `StarRatingQuestion` — `type: 'star_rating'`
7
+ > **Target Component**: `RatingScale` (star/emoji variant)
8
+ > **Handles**: `type: 'star_rating'` only — not `CSAT_MATRIX` (that is a matrix grid)
9
+
10
+ ## Core Responsibility
11
+
12
+ Render star or emoji rating scales ensuring that active selections are unmistakably highlighted using bounding boxes or strong border accents, NEVER just by changing the SVG fill color.
13
+
14
+ ## State Management & Mapping
15
+
16
+ - **Emojis**: `scaleStyle === 'emoji'`
17
+ - Map values using client-side emoji icons (react-icons) proportional to scale length.
18
+ - **Stars**: `scaleStyle === 'star'`
19
+ - Active stars (index <= selectedValue) use filled star icons from react-icons.
20
+ - **Numbers**: Derive display/submit value from `optionLabel` (see `02-reference/value-derivation.md`).
21
+
22
+ ## UX Bounding Box Rules (CRITICAL)
23
+
24
+ ### For Emojis & Stars:
25
+
26
+ - **Unselected**: `inline-flex p-2 border-2 border-transparent opacity-40 hover:opacity-100 hover:scale-110 transition-all cursor-pointer`.
27
+ - **Selected**: The specific selected icon MUST be wrapped in a prominent square box with rounded corners.
28
+ - **CSS**: `border-2 border-[#e20074] rounded-lg p-2 bg-pink-50 shadow-sm scale-110 opacity-100`.
29
+
30
+ ### For Numbered Badges:
31
+
32
+ - **Shape**: MUST be square with rounded corners (`rounded-lg w-12 h-12`). Do not use full circles.
33
+ - **Selected**: `border-2 border-[#e20074] opacity-100 font-bold scale-105 shadow-md`.
34
+ - **Unselected**: `opacity-30 border-transparent`.
35
+
36
+ ## Bounding Boxes inside Matrices
37
+
38
+ When emojis or stars are rendered inside `CsatMatrixScale`, the exact same bounding box logic applies. See `05-csat-matrix-scale.md`.
39
+
40
+ ## Tooltips & Label Positions
41
+
42
+ - **Tooltips**: Every emoji, star, and badge MUST have `title={optionLabel}` on hover.
43
+ - **Labels (`minLabel`, `midLabel`, `maxLabel`)**:
44
+ - Render in `Question.tsx` above the scale (same pattern as `nps`).
45
+ - `midLabel` position: `style={{ left: \`\${(midLabelIndex / (options.length - 1)) * 100}%\` }}`.
@@ -1,22 +1,27 @@
1
1
  # CSAT Matrix Architectural Blueprint
2
2
 
3
- > **UI spec (authoritative):** `03-ui-specs/08-matrix-csat-rating.md`
3
+ > **UI spec (authoritative):** `03-ui-specs/04-csat.md` (for `CSAT_MATRIX`) and `03-ui-specs/08-matrix-csat-rating.md` (for `RATING_MATRIX`)
4
4
  > **Reference implementation:** `apps/client/src/components/CsatMatrixScale.tsx`
5
5
  > **Target Component**: `CsatMatrixScale`
6
- > **Handles**: `type: 'matrix'` (CSAT_MATRIX and RATING_MATRIX subTypes)
6
+ > **Handles**: `type: 'CSAT_MATRIX'` and `type: 'RATING_MATRIX'`
7
7
 
8
8
  ## Core Responsibility
9
+
9
10
  Render a grid where columns represent satisfaction scales (Emojis, Stars, or Rating Badges).
10
11
 
11
12
  ## Data Mapping & State Management
12
- - `selectedValue`: Expects `MatrixAnswerMap` shape.
13
- - Iterate over `question.rows`. Within each row, iterate over `question.columns`.
14
- - Render the correct icon/badge in the `<td>` based on `question.buttonType`.
13
+
14
+ - `selectedValue`: `MatrixRowAnswers` — keys are `statementRows[].id`, values are column **`id`** (via `matrixColumnStoredValue`)
15
+ - Iterate over `question.statementRows`. Within each row, iterate over `question.scaleColumns`.
16
+ - Render the correct icon/badge in the `<td>` based on `question.displayStyle`.
15
17
 
16
18
  ## Configuration Matrix
19
+
17
20
  You MUST handle:
18
- - **`hasNotApplicable`**: If true, the last column might be `null`. Render a distinct UI (e.g., standard radio or gray badge) for the N/A column.
19
- - **`reverseScaleOrder`**: Ensure your column mapping respects this logic if implemented in the UI.
21
+
22
+ - **`hasNotApplicableOption`**: If true, the last column might be `null`. Render a distinct UI for the N/A column.
23
+ - **`reverseScaleOrder`**: Reverse column order before render when true.
24
+ - **`gridLayout`**: `'standard'`, `'carousel'`, or `'dropdown'` — see ui-spec decision tree.
20
25
 
21
26
  ## UI/UX Bounding Box Rules (CRITICAL)
22
27
 
@@ -28,8 +33,12 @@ Matrices are difficult to read and click accurately. You MUST implement these st
28
33
  2. **The "Active Bounding Box"**:
29
34
  - For Emojis and Stars inside the matrix, do NOT just change the SVG fill.
30
35
  - The interactive wrapper inside the `<td>` MUST toggle a pink bounding box.
31
- - **Selected Cell**: `border-2 border-[#e20074] rounded-lg p-2 bg-pink-50 shadow-sm scale-110 opacity-100 cursor-pointer`.
32
- - **Unselected Cell**: `border-2 border-transparent p-2 opacity-40 hover:opacity-100 hover:scale-110 cursor-pointer`.
33
36
 
34
- 3. **Tooltips**:
35
- - Every single interactive cell MUST have `title={col.label}` so the user sees exactly what column they are in without scrolling up to the table header.
37
+ ## SDK Integration Checklist (agent)
38
+
39
+ - [ ] Type literals: `QUESTION_TYPE.CSAT_MATRIX` and `QUESTION_TYPE.RATING_MATRIX`
40
+ - [ ] Answer stored as: `MatrixRowAnswers` via `matrixColumnStoredValue`
41
+ - [ ] onAction CHANGE: `{ questionId, answerValue: MatrixRowAnswers }`
42
+ - [ ] Visibility helper: `getVisibleMatrixItemsForAnswers(..., customFieldValues?)`
43
+ - [ ] Emoji/star UI: client-side `react-icons` — not SDK exports
44
+ - [ ] Matrix row: [`question-type-sdk-matrix.md`](../00-integration/question-type-sdk-matrix.md)
@@ -3,14 +3,14 @@
3
3
  > **UI spec (authoritative):** `03-ui-specs/07-matrix-cfm.md`
4
4
  > **Reference implementation:** `apps/client/src/components/LikertMatrixScale.tsx`
5
5
  > **Target Component**: `LikertMatrixScale`
6
- > **Handles**: `type: 'matrix'` (CFM_MATRIX subType)
6
+ > **Handles**: `type: 'CFM_MATRIX'`
7
7
 
8
8
  ## Core Responsibility
9
9
  Render a grid with text-label columns. However, this component must be highly dynamic to support complex layouts, transpositions, and long-form matrix readability.
10
10
 
11
11
  ## Data Mapping & State Management
12
- - `selectedValue`: Expects `MatrixAnswerMap` shape (`Record<string, string | number | null>`).
13
- - **Mutation**: When radio button is clicked for `row.id` and `col.value`, dispatch `onSelect({ ...selectedValue, [row.id]: col.value })`.
12
+ - `selectedValue`: `MatrixRowAnswers` store column **`id`** via `matrixColumnStoredValue(col)`
13
+ - **Mutation**: When a cell is selected for `row.id`, dispatch `onSelect({ ...selectedValue, [row.id]: columnId })`
14
14
 
15
15
  ## Configuration Matrix (CRITICAL FOR PLANNING)
16
16
 
@@ -44,3 +44,12 @@ To match enterprise standards, implement the following UI strictly:
44
44
  - Every input MUST have `title={col.label}` so the user sees the semantic meaning when hovering the radio button.
45
45
  4. **Header Typography**:
46
46
  - `th` tags should be `text-xs font-semibold text-gray-500 uppercase tracking-wider p-4 border-b`.
47
+
48
+ ## SDK Integration Checklist (agent)
49
+
50
+ - [ ] Type literal: `QUESTION_TYPE.CFM_MATRIX`
51
+ - [ ] Answer stored as: `MatrixRowAnswers` via `matrixColumnStoredValue`
52
+ - [ ] onAction CHANGE: `{ questionId, answerValue: MatrixRowAnswers }`
53
+ - [ ] Visibility helper: `getVisibleMatrixItemsForAnswers(..., customFieldValues?)`
54
+ - [ ] Handle `gridLayout`: standard / carousel / dropdown
55
+ - [ ] Matrix row: [`question-type-sdk-matrix.md#cfm_matrix`](../00-integration/question-type-sdk-matrix.md#cfm_matrix)
@@ -3,13 +3,13 @@
3
3
  > **UI spec (authoritative):** `03-ui-specs/09-slider-matrix.md`
4
4
  > **Reference implementation:** `apps/client/src/components/SliderMatrixScale.tsx`
5
5
  > **Target Component**: `SliderMatrixScale`
6
- > **Handles**: `type: 'slider_matrix'`
6
+ > **Handles**: `type: 'SLIDER_MATRIX'`
7
7
 
8
8
  ## Core Responsibility
9
9
  Render an independent range slider for every row in the matrix. Sliders must be highly visible, utilizing a "thick track" UI, dynamic fill based on the current value, and exact value badges.
10
10
 
11
11
  ## State Management
12
- - `selectedValue`: Expects `MatrixAnswerMap` shape.
12
+ - `selectedValue`: Expects `MatrixRowAnswers` shape.
13
13
  - `row.min`, `row.max`, and `row.step` must dictate the boundaries of the slider logic.
14
14
  - **Mutation**: `onChange` of the input must parse the value as a Number before dispatching `onSelect`.
15
15
 
@@ -45,3 +45,12 @@ Your component must account for:
45
45
  - If `true`, render a checkbox at the end of the row.
46
46
  - Checking it sets the value in the map to `null`.
47
47
  - When `value === null`, gray out the slider track and disable the input.
48
+
49
+ ## SDK Integration Checklist (agent)
50
+
51
+ - [ ] Type literal: `QUESTION_TYPE.SLIDER_MATRIX`
52
+ - [ ] Answer stored as: `MatrixRowAnswers` — number or `null` per row
53
+ - [ ] onAction CHANGE: `{ questionId, answerValue: MatrixRowAnswers }`
54
+ - [ ] Visibility helper: `getVisibleStatementRowsForAnswers(..., customFieldValues?)`
55
+ - [ ] Use `CustomSliderTrack` when `sliderType === 'graphics'`
56
+ - [ ] Matrix row: [`question-type-sdk-matrix.md#slider_matrix`](../00-integration/question-type-sdk-matrix.md#slider_matrix)
@@ -3,7 +3,7 @@
3
3
  > **UI spec (authoritative):** `03-ui-specs/10-file-upload.md`
4
4
  > **Reference implementation:** `apps/client/src/components/FileUploadScale.tsx`
5
5
  > **Target Component**: `FileUploadScale`
6
- > **Handles**: `type: 'file_upload'`
6
+ > **Handles**: `type: 'FILE_UPLOAD'`
7
7
 
8
8
  ## Core Responsibility
9
9
  Render a robust drag-and-drop zone and enforce complex file size, type limits, and maximum file count constraints BEFORE updating the state.
@@ -56,3 +56,12 @@ Before calling `onSelect`, your component MUST validate against these properties
56
56
 
57
57
  ## Hidden Input
58
58
  - Include a hidden `<input type="file" multiple={question.maxFileCount > 1} accept={...} />` that is triggered via a `useRef` when the dropzone is clicked.
59
+
60
+ ## SDK Integration Checklist (agent)
61
+
62
+ - [ ] Type literal: `QUESTION_TYPE.FILE_UPLOAD`
63
+ - [ ] Answer stored as: `UploadedFile[]`
64
+ - [ ] onAction CHANGE: `{ questionId, answerValue: UploadedFile[] }` after upload
65
+ - [ ] Visibility helper: N/A
66
+ - [ ] Enforce `maxFileCount` and `fileSizeLimit` client-side
67
+ - [ ] Matrix row: [`question-type-sdk-matrix.md#file_upload`](../00-integration/question-type-sdk-matrix.md#file_upload)
@@ -3,7 +3,7 @@
3
3
  > **UI spec (authoritative):** `03-ui-specs/shared/custom-slider-track.md`
4
4
  > **Reference implementation:** `apps/client/src/components/CustomSliderTrack.tsx`
5
5
  > **Target Component**: `CustomSliderTrack`
6
- > **Used By**: `SliderMatrixScale`, `CsatMatrixScale`, `CsatScale`
6
+ > **Used By**: `SliderMatrixScale`, `CsatMatrixScale`, standalone `slider`
7
7
 
8
8
  ## Core Responsibility
9
9
  Native browser `<input type="range">` elements are impossible to style consistently across browsers, especially when trying to render dynamic emoji handles. This component MUST implement an invisible overlay architecture to provide total CSS control over the slider's appearance.
@@ -24,7 +24,7 @@ A dynamic `<div>` representing the portion of the slider up to the current value
24
24
  ### Layer 3: The Custom Thumb
25
25
  A visual element that floats along the track.
26
26
  - **For Standard Sliders**: A pink circle. `w-5 h-5 bg-[#e20074] border-2 border-white rounded-full shadow-md pointer-events-none`.
27
- - **For Graphics Sliders (`sliderType === 'graphics'`)**: A larger white circle containing an emoji mapped to the current value using `getEmojiForIndex`. `w-10 h-10 bg-white shadow-md border flex items-center justify-center text-2xl`.
27
+ - **For Graphics Sliders (`sliderType === 'graphics'`)**: A larger white circle containing an emoji mapped to the current value using client-side react-icons. `w-10 h-10 bg-white shadow-md border flex items-center justify-center text-2xl`.
28
28
  - Inline Style: `style={{ left: \`\${percentage}%\` }}`.
29
29
 
30
30
  ### Layer 4: The Native Input (The Engine)
@@ -1,14 +1,15 @@
1
1
  # Progress Bar
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
4
+ > **Progress calculation:** `00-integration/progress.md`
4
5
  > **Reference implementation:** `apps/client/src/components/ProgressBar.tsx`
5
6
  > **Component**: `ProgressBar`
6
- > **Role**: Displays completion status at the top of the survey.
7
7
 
8
8
  ## Props Interface
9
+
9
10
  ```typescript
10
11
  type ProgressBarProps = {
11
- progressPercentage: number; // 0 to 100
12
+ progressPercentage: number; // 0 to 100, path-scoped
12
13
  };
13
14
  ```
14
15
 
@@ -16,7 +17,6 @@ type ProgressBarProps = {
16
17
 
17
18
  ```tsx
18
19
  export default function ProgressBar({ progressPercentage }: ProgressBarProps) {
19
- // Ensure it's bounded between 0 and 100
20
20
  const width = Math.min(100, Math.max(0, progressPercentage));
21
21
 
22
22
  return (
@@ -30,4 +30,10 @@ export default function ProgressBar({ progressPercentage }: ProgressBarProps) {
30
30
  }
31
31
  ```
32
32
 
33
- The `progressPercentage` value is provided directly by `state.progressPercentage` from the `useSurveySDK` hook.
33
+ ## Wiring
34
+
35
+ ```tsx
36
+ <ProgressBar progressPercentage={state.progressPercentage} />
37
+ ```
38
+
39
+ The SDK computes percentage from **questions on the active skip path**, not total survey pages. Skip logic and answer changes update the bar automatically.
@@ -6,7 +6,9 @@
6
6
  > **Used by**: `CsatMatrixScale`, `LikertMatrixScale`
7
7
 
8
8
  ## Role
9
- Renders a dropdown selector for a single row in a matrix question. Used when `matrixFormat === 'dropdown'` or `buttonType === 'dropdown'`.
9
+ Renders a dropdown selector for a single row in a matrix question. Used when `gridLayout === 'dropdown'` or `displayStyle === 'dropdown'`.
10
+
11
+ Accepts `ScaleColumn[]`; emits stored column ids via `matrixColumnStoredValue(col)` — see `02-reference/value-derivation.md`.
10
12
 
11
13
  ## Props Interface
12
14
  ```typescript
@@ -0,0 +1,34 @@
1
+ # Intro Page
2
+
3
+ > **UI spec:** `03-ui-specs/12-survey-chrome.md`
4
+ > **Reference:** `apps/client/src/components/IntroPage.tsx`
5
+
6
+ ## When to render
7
+
8
+ `state.phase.kind === 'intro'` and `state.specialPage` is set.
9
+
10
+ ## Props
11
+
12
+ ```typescript
13
+ type IntroPageProps = {
14
+ page: SpecialPage;
15
+ language?: string;
16
+ onStart: () => void;
17
+ };
18
+ ```
19
+
20
+ ## Wiring
21
+
22
+ ```tsx
23
+ <IntroPage
24
+ page={state.specialPage}
25
+ language={survey.language}
26
+ onStart={() => onAction({ type: 'START' })}
27
+ />
28
+ ```
29
+
30
+ ## Content
31
+
32
+ - Render `page.header` and optional `page.description` as HTML
33
+ - CTA label: `page.ctaLabel` or default "Start Survey"
34
+ - Header + Footer chrome on full-page layout
@@ -0,0 +1,31 @@
1
+ # End Page
2
+
3
+ > **UI spec:** `03-ui-specs/12-survey-chrome.md`
4
+ > **Reference:** `apps/client/src/components/EndPage.tsx`
5
+ > **Logic:** `00-integration/end-page-logic.md`
6
+
7
+ ## When to render
8
+
9
+ `state.phase.kind === 'end'` after successful submit, with `state.specialPage` from `survey.endPages`.
10
+
11
+ When the survey has **multiple end pages**, the SDK picks the page at submit using display logic (see [`end-page-logic.md`](../00-integration/end-page-logic.md)). The client only renders the resolved `state.specialPage`.
12
+
13
+ ## Props
14
+
15
+ ```typescript
16
+ type EndPageProps = {
17
+ page: SpecialPage;
18
+ };
19
+ ```
20
+
21
+ ## Wiring
22
+
23
+ ```tsx
24
+ <EndPage page={state.specialPage} />
25
+ ```
26
+
27
+ ## Content
28
+
29
+ - Render `page.header` and optional `page.description` as HTML
30
+ - No navigation buttons — terminal thank-you screen
31
+ - Header + Footer chrome
@@ -0,0 +1,27 @@
1
+ # Paused Page
2
+
3
+ > **UI spec:** `03-ui-specs/12-survey-chrome.md`
4
+ > **Reference:** `apps/client/src/components/PausedPage.tsx`
5
+
6
+ ## When to render
7
+
8
+ `state.phase.kind === 'paused'` when survey `phaseFlags.paused` is true.
9
+
10
+ ## Props
11
+
12
+ ```typescript
13
+ type PausedPageProps = {
14
+ page: SpecialPage;
15
+ };
16
+ ```
17
+
18
+ ## Wiring
19
+
20
+ ```tsx
21
+ <PausedPage page={state.specialPage} />
22
+ ```
23
+
24
+ ## Content
25
+
26
+ - Read-only message from `page.header` / `page.description`
27
+ - No start or navigation actions
@@ -0,0 +1,75 @@
1
+ # Heatmap Architectural Blueprint
2
+
3
+ > **UI spec (authoritative):** `03-ui-specs/13-heatmap.md`
4
+ > **Reference implementation:** `apps/client/src/components/HeatmapScale.tsx`
5
+ > **Coordinate helpers:** `apps/client/src/lib/surveyUi/heatmapCoords.ts`
6
+ > **Target Component**: `HeatmapScale`
7
+ > **Handles**: `type: 'HEATMAP'`
8
+
9
+ ## Core Responsibility
10
+
11
+ Render the heatmap image, capture click spots in normalized 0–1 coordinates, toggle spots on repeat click, enforce `maxClicksAllowed` client-side, and bubble answers via `onSelect`.
12
+
13
+ ## Props Contract
14
+
15
+ ```typescript
16
+ type HeatmapScaleProps = {
17
+ question: HeatmapQuestion;
18
+ selectedValue?: HeatmapAnswer;
19
+ onSelect: (answer: HeatmapAnswer | undefined) => void;
20
+ };
21
+ ```
22
+
23
+ ## State Management
24
+
25
+ - `selectedValue` from SDK is source of truth — do not duplicate answer state
26
+ - Local UI state: `isImageReady` only
27
+
28
+ ## Click Interaction
29
+
30
+ 1. User clicks image → compute normalized `{ x, y }` from `getBoundingClientRect()`
31
+ 2. If click within 14px of existing spot → remove that spot
32
+ 3. Else if under `maxClicksAllowed` → append `{ id: crypto.randomUUID(), x, y }`
33
+ 4. Else → no-op
34
+
35
+ ## Coordinate Helpers
36
+
37
+ Use `apps/client/src/lib/surveyUi/heatmapCoords.ts`:
38
+
39
+ - `getNormalizedClickFromImage(event, imageElement)`
40
+ - `findSpotNearClick(clickX, clickY, spots, imageRect)`
41
+ - `createNewSpot(x, y)`
42
+
43
+ ## DOM Structure
44
+
45
+ ```tsx
46
+ <div className="relative inline-block max-w-full">
47
+ <img onClick={handleImageClick} ... />
48
+ <div className="absolute inset-0 pointer-events-none">
49
+ {spots.map(spot => <Spot />)}
50
+ </div>
51
+ </div>
52
+ ```
53
+
54
+ ## Wiring in Question.tsx
55
+
56
+ ```typescript
57
+ {question.type === QUESTION_TYPE.HEATMAP && (
58
+ <HeatmapScale
59
+ question={question as HeatmapQuestion}
60
+ selectedValue={Array.isArray(selectedValue) ? selectedValue : undefined}
61
+ onSelect={onSelect}
62
+ />
63
+ )}
64
+ ```
65
+
66
+ Question shell (title, description, validation) stays in `Question.tsx`.
67
+
68
+ ## SDK Integration Checklist (agent)
69
+
70
+ - [ ] Type literal: `QUESTION_TYPE.HEATMAP`
71
+ - [ ] Answer stored as: `HeatmapClickPoint[]`
72
+ - [ ] onAction CHANGE: `{ questionId, answerValue: HeatmapClickPoint[] }`
73
+ - [ ] Copy coord helper from [`templates/heatmapCoords.ts`](../templates/heatmapCoords.ts)
74
+ - [ ] Respect `maxClicksAllowed`; normalize coords 0–1
75
+ - [ ] Matrix row: [`question-type-sdk-matrix.md#heatmap`](../00-integration/question-type-sdk-matrix.md#heatmap)