@explorer02/cfm-survey-sdk 0.1.7 → 0.1.9

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 (61) 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 -26
  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/01-components/01-survey-page.md +91 -0
  12. package/templates/docs/01-components/02-question.md +105 -0
  13. package/templates/docs/01-components/08-file-upload-scale.md +56 -0
  14. package/templates/docs/{03-client-components → 01-components}/README.md +1 -1
  15. package/templates/docs/02-question-types/01-rating.md +0 -8
  16. package/templates/docs/02-reference/question-types/01-rating.md +52 -0
  17. package/templates/docs/02-reference/question-types/README.md +71 -0
  18. package/templates/docs/02-reference/routing-table.md +37 -0
  19. package/templates/docs/MANIFEST.json +129 -0
  20. package/templates/docs/index.md +100 -140
  21. package/templates/docs/templates/implementation_plan.md +78 -0
  22. package/templates/docs/01-sdk-core/01-fetch-survey.md +0 -68
  23. package/templates/docs/01-sdk-core/02-survey-mapper.md +0 -85
  24. package/templates/docs/01-sdk-core/03-question-mappers.md +0 -114
  25. package/templates/docs/01-sdk-core/04-pagination.md +0 -72
  26. package/templates/docs/01-sdk-core/05-validation.md +0 -66
  27. package/templates/docs/01-sdk-core/06-submit-survey.md +0 -90
  28. package/templates/docs/01-sdk-core/07-language-handling.md +0 -111
  29. package/templates/docs/01-sdk-core/08-icons-and-emojis.md +0 -56
  30. package/templates/docs/01-sdk-core/README.md +0 -53
  31. package/templates/docs/02-question-types/02-radio.md +0 -26
  32. package/templates/docs/02-question-types/03-text.md +0 -26
  33. package/templates/docs/02-question-types/12-text-and-media.md +0 -35
  34. package/templates/docs/02-question-types/README.md +0 -74
  35. package/templates/docs/03-client-components/01-survey-page.md +0 -113
  36. package/templates/docs/03-client-components/02-question.md +0 -74
  37. package/templates/docs/03-client-components/08-file-upload-scale.md +0 -37
  38. package/templates/docs/04-critical-rules/01-import-rules.md +0 -51
  39. package/templates/docs/04-critical-rules/02-action-dispatching.md +0 -56
  40. package/templates/docs/04-critical-rules/03-scroll-navigation.md +0 -37
  41. package/templates/docs/04-critical-rules/04-logo-branding.md +0 -42
  42. package/templates/docs/04-critical-rules/05-troubleshooting.md +0 -46
  43. package/templates/docs/04-critical-rules/README.md +0 -29
  44. /package/templates/docs/{03-client-components → 01-components}/03-rating-scale.md +0 -0
  45. /package/templates/docs/{03-client-components → 01-components}/04-csat-scale.md +0 -0
  46. /package/templates/docs/{03-client-components → 01-components}/05-csat-matrix-scale.md +0 -0
  47. /package/templates/docs/{03-client-components → 01-components}/06-likert-matrix-scale.md +0 -0
  48. /package/templates/docs/{03-client-components → 01-components}/07-slider-matrix-scale.md +0 -0
  49. /package/templates/docs/{03-client-components → 01-components}/09-custom-slider-track.md +0 -0
  50. /package/templates/docs/{03-client-components → 01-components}/10-header-footer.md +0 -0
  51. /package/templates/docs/{03-client-components → 01-components}/11-progress-bar.md +0 -0
  52. /package/templates/docs/{03-client-components → 01-components}/12-language-selector.md +0 -0
  53. /package/templates/docs/{03-client-components → 01-components}/13-matrix-dropdown.md +0 -0
  54. /package/templates/docs/{02-question-types → 02-reference/question-types}/04-csat.md +0 -0
  55. /package/templates/docs/{02-question-types → 02-reference/question-types}/05-rating-scale.md +0 -0
  56. /package/templates/docs/{02-question-types → 02-reference/question-types}/06-slider.md +0 -0
  57. /package/templates/docs/{02-question-types → 02-reference/question-types}/07-matrix-cfm.md +0 -0
  58. /package/templates/docs/{02-question-types → 02-reference/question-types}/08-matrix-csat.md +0 -0
  59. /package/templates/docs/{02-question-types → 02-reference/question-types}/09-matrix-rating.md +0 -0
  60. /package/templates/docs/{02-question-types → 02-reference/question-types}/10-slider-matrix.md +0 -0
  61. /package/templates/docs/{02-question-types → 02-reference/question-types}/11-file-upload.md +0 -0
@@ -0,0 +1,91 @@
1
+ # Survey Page Orchestrator Architectural Blueprint
2
+
3
+ > **Target Component**: `SurveyPage`
4
+ > **Role**: The main entry point that wires the UI to the `useSurveySDK` hook, manages loading/error states, orchestrates pagination, and controls submission.
5
+
6
+ ## Core Responsibility
7
+ This component MUST act as the state machine for the entire survey flow. It should never render questions directly—it delegates that to `Question.tsx`.
8
+
9
+ ## Setup & Wiring (CRITICAL)
10
+
11
+ The component MUST invoke `useSurveySDK`, passing the **instanceId** from the client prompt.
12
+
13
+ ```tsx
14
+ const SURVEY_PLACEHOLDERS = { FIRST_NAME: 'Customer' }; // Must be outside component!
15
+
16
+ export default function SurveyPage() {
17
+ const [selectedLanguage, setSelectedLanguage] = useState<string | undefined>("");
18
+
19
+ const { surveyQueryResults, submitSurveyResults, state, onAction } = useSurveySDK({
20
+ options: {
21
+ instanceId: 'YOUR_INSTANCE_ID_HERE', // From client prompt
22
+ language: selectedLanguage,
23
+ placeholders: SURVEY_PLACEHOLDERS,
24
+ debug: true,
25
+ }
26
+ });
27
+
28
+ const survey = surveyQueryResults.data;
29
+
30
+ // ... state machine rendering
31
+ }
32
+ ```
33
+
34
+ ## The 5 State Machine Renders (MANDATORY)
35
+
36
+ You MUST structure your render logic to strictly handle these 5 states in order to avoid UI crashes when data is not yet available.
37
+
38
+ ### 1. Loading State
39
+ If `surveyQueryResults.isLoading` is true, render a Skeleton or Spinner.
40
+ *Include your CSS-only Header and Footer around the spinner.*
41
+
42
+ ### 2. Error State
43
+ If `surveyQueryResults.error` is true, render a red Error banner containing `surveyQueryResults.error.message`.
44
+
45
+ ### 3. Empty State
46
+ If `!survey`, render a generic "Survey not found" message.
47
+
48
+ ### 4. Submitted State
49
+ If `submitSurveyResults.data` exists, the survey is complete.
50
+ *Render the Thank You Screen (e.g., "Thank you for your feedback!"). Do not render the question list.*
51
+
52
+ ### 5. Active Survey State
53
+ If the above 4 conditions are met, render the active survey page:
54
+ 1. `Header` (Logo)
55
+ 2. `ProgressBar` (Pass `state.progressPercentage`)
56
+ 3. `LanguageSelector` (Pass `survey.languages`, `survey.language`, `setSelectedLanguage`)
57
+ 4. **The Question Map** (See below)
58
+ 5. **Navigation Buttons** (See below)
59
+ 6. `Footer`
60
+
61
+ ## The Question Map
62
+
63
+ You MUST iterate over `state.currentQuestions` and dispatch to `Question.tsx`.
64
+
65
+ ```tsx
66
+ <div className="space-y-12">
67
+ {state.currentQuestions.map(question => (
68
+ <Question
69
+ key={question.id}
70
+ question={question}
71
+ selectedValue={state.values[question.id]}
72
+ validationError={state.validationErrors[question.id]}
73
+ onSelect={value => onAction({
74
+ type: 'CHANGE',
75
+ payload: { questionId: question.id, value }
76
+ })}
77
+ />
78
+ ))}
79
+ </div>
80
+ ```
81
+
82
+ ## Navigation Buttons Logic
83
+
84
+ You MUST conditionally render "Back" and "Next/Submit" buttons.
85
+
86
+ - **Back Button**: Hide if `state.currentPageIndex === 0`.
87
+ - `onClick={() => onAction({ type: 'PREVIOUS' })}`
88
+ - **Next/Submit Button**:
89
+ - `disabled={submitSurveyResults.isLoading}`
90
+ - If `state.currentPageIndex < survey.pages.length - 1`, label is "Next", action is `type: 'NEXT'`.
91
+ - If on the last page, label is "Submit", action is `type: 'NEXT'` (the SDK intercepts NEXT on the last page to trigger submission automatically!).
@@ -0,0 +1,105 @@
1
+ # Question Dispatcher Architectural Blueprint
2
+
3
+ > **Target Component**: `Question`
4
+ > **Role**: Wrapper card component that routes to the correct scale component based on `question.type`.
5
+
6
+ ## Core Responsibility
7
+ 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
+
9
+ ## Exhaustive Dispatcher Logic (CRITICAL)
10
+
11
+ The agent MUST handle EVERY SINGLE ONE of these `question.type` values. Failure to handle them will result in blank UI or broken surveys.
12
+
13
+ 1. **`rating`**: Renders `RatingScale`. This covers NPS, Stars, Emoji, and generic numeric scales.
14
+ 2. **`csat`**: Renders `CsatScale`. This covers CSAT specific faces/icons.
15
+ 3. **`radio`**: Renders inline MCQ radio buttons. (Can be implemented inside this file or separately).
16
+ 4. **`text`**: Renders an inline `<textarea>` or `<input>`.
17
+ 5. **`matrix`**:
18
+ - If `subType === 'CFM_MATRIX'`, render `LikertMatrixScale`
19
+ - If `subType === 'CSAT_MATRIX'` or `'RATING_MATRIX'`, render `CsatMatrixScale`
20
+ 6. **`slider_matrix`**: Renders `SliderMatrixScale`
21
+ 7. **`file_upload`**: Renders `FileUploadScale`
22
+ 8. **`text_and_media`**: Renders inline `<video>` or `<img>` based on `question.mediaUrl`.
23
+ - *Note: `text_and_media` questions may NOT have user input. Do not render a scale for them.*
24
+
25
+ ## Container UI Structure
26
+
27
+ Every question MUST be wrapped in a standard HTML `<section>` to visually separate it.
28
+
29
+ 1. **Wrapper styling**:
30
+ - Border: `border border-gray-200`
31
+ - Radius: `rounded-xl`
32
+ - Background: `bg-white`
33
+ - Padding: `p-6 md:p-8`
34
+ - Shadow & Hover: `shadow-sm transition-all hover:shadow-md`
35
+ - **Crucial**: ID must be set to `question.id` (`id={question.id}`) so scroll-to-error works!
36
+
37
+ 2. **Heading (`question.text`)**:
38
+ - MUST use `dangerouslySetInnerHTML={{ __html: question.text }}` because text from Sprinklr contains rich HTML (bold, links, italics).
39
+ - If `question.required === true`, append a red asterisk `*`.
40
+
41
+ 3. **Description (`question.description`)**:
42
+ - Render conditionally if `question.description` exists.
43
+ - MUST use `dangerouslySetInnerHTML`.
44
+ - Style with slightly smaller text, greyish color (e.g., `text-sm text-gray-600 mt-2`).
45
+
46
+ 4. **Validation Error Banner**:
47
+ - If `validationError` prop is passed, render a highly visible banner below the scale.
48
+ - Example: Red border-left, red background, bold text.
49
+
50
+ ## Hover Tooltips & Micro-interactions
51
+ - Focus states on text inputs MUST use the brand primary color (e.g., magenta `#e20074` for Telekom).
52
+ - Radio button wrappers should have a subtle background hover effect (e.g., `hover:bg-pink-50`).
53
+
54
+ ## Example Architectural Skeleton
55
+
56
+ ```tsx
57
+ <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">
58
+
59
+ {/* HEADING */}
60
+ <h2 className="text-lg md:text-xl font-semibold leading-relaxed text-gray-900 flex items-start gap-1">
61
+ <span dangerouslySetInnerHTML={{ __html: question.text }} className="prose prose-sm max-w-none" />
62
+ {question.required && <span className="text-[#e20074] shrink-0">*</span>}
63
+ </h2>
64
+
65
+ {/* DESCRIPTION */}
66
+ {question.description && (
67
+ <div className="text-sm text-gray-600 mt-2 leading-relaxed prose prose-sm max-w-none" dangerouslySetInnerHTML={{ __html: question.description }} />
68
+ )}
69
+
70
+ {/* DISPATCHER */}
71
+ <div className="pt-4">
72
+ {question.type === 'rating' && <RatingScale ... />}
73
+ {question.type === 'csat' && <CsatScale ... />}
74
+ {question.type === 'slider_matrix' && <SliderMatrixScale ... />}
75
+ {question.type === 'file_upload' && <FileUploadScale ... />}
76
+ {question.type === 'matrix' && question.subType === 'CFM_MATRIX' && <LikertMatrixScale ... />}
77
+ {question.type === 'matrix' && (question.subType === 'CSAT_MATRIX' || question.subType === 'RATING_MATRIX') && <CsatMatrixScale ... />}
78
+
79
+ {question.type === 'radio' && (
80
+ // Inline Radio implementation
81
+ // Make sure to match brand hover colors!
82
+ )}
83
+
84
+ {question.type === 'text' && (
85
+ // Inline Textarea implementation
86
+ // Handle character limit `question.maxCharacterCount`
87
+ )}
88
+
89
+ {question.type === 'text_and_media' && (
90
+ // Display media (image/video) based on question properties
91
+ // This handles CONTENT blocks.
92
+ )}
93
+ </div>
94
+
95
+ {/* ERROR BANNER */}
96
+ {validationError && (
97
+ <div className="mt-6 rounded-lg border-l-4 border-l-[#e20074] bg-red-50 p-4">
98
+ <p className="text-sm font-bold text-gray-900 leading-tight flex items-center gap-2">
99
+ <span className="text-[#e20074]">⚠</span> {validationError}
100
+ </p>
101
+ </div>
102
+ )}
103
+
104
+ </section>
105
+ ```
@@ -0,0 +1,56 @@
1
+ # File Upload Architectural Blueprint
2
+
3
+ > **Target Component**: `FileUploadScale`
4
+ > **Handles**: `type: 'file_upload'`
5
+
6
+ ## Core Responsibility
7
+ Render a robust drag-and-drop zone and enforce complex file size, type limits, and maximum file count constraints BEFORE updating the state.
8
+
9
+ ## State Management
10
+ - `selectedValue`: Expects an array of native `File` objects (or serialized equivalents).
11
+ - **Mutation Pattern**:
12
+ 1. The user selects files.
13
+ 2. The component intercepts the files.
14
+ 3. The component validates them against ALL limits.
15
+ 4. If valid, append the new files to `selectedValue` (handling it as an array).
16
+ 5. Call `onSelect(updatedFilesArray)`.
17
+
18
+ ## Configuration Matrix (CRITICAL FOR PLANNING)
19
+
20
+ Before calling `onSelect`, your component MUST validate against these properties:
21
+
22
+ ### 1. `supportedFileFormats`
23
+ - Extracted from `question.supportedFileFormats` (e.g. `['PDF', 'PNG', 'JPG']`).
24
+ - Use this to build the `accept` attribute string for the hidden input: `question.supportedFileFormats?.map(f => \`.\${f.toLowerCase()}\`).join(',')`.
25
+ - You MUST reject files with extensions that are not in this list.
26
+
27
+ ### 2. `maxFileCount`
28
+ - Prevent uploads if `currentFiles.length + newFiles.length > maxFileCount`.
29
+ - Provide a clear local error message indicating the max limit.
30
+
31
+ ### 3. `fileSizeLimit` & `fileSizeLimitType`
32
+ - `fileSizeLimit` is a number representing megabytes (MB).
33
+ - If `fileSizeLimitType === 'PER_FILE'`, validate that EVERY incoming file's `size` (in bytes) is `<= fileSizeLimit * 1024 * 1024`.
34
+ - If `fileSizeLimitType === 'IN_TOTAL'`, validate that the combined sum of all existing `selectedValue` sizes plus the incoming files' sizes is `<= fileSizeLimit * 1024 * 1024`.
35
+
36
+ ## UI/UX Specifications
37
+
38
+ 1. **Dropzone**:
39
+ - Wrap the main area with `onDragEnter`, `onDragOver`, `onDragLeave`, `onDrop`.
40
+ - **Inactive State**: `border-2 border-dashed border-gray-300 rounded-xl bg-gray-50 p-8 text-center cursor-pointer transition-all`.
41
+ - **Drag/Hover State**: `border-[#e20074] bg-[#fdf2f8]` (Using the brand primary color).
42
+ - Display a Cloud SVG icon (use gray, switch to magenta on hover).
43
+ - Render `question.uploadMessage` (which can contain HTML, so use `dangerouslySetInnerHTML`).
44
+ - Dynamically render the limits text (e.g., "Max 3 files, 10MB per file").
45
+
46
+ 2. **File List**:
47
+ - Render existing files as distinct cards underneath the dropzone.
48
+ - Styling: `flex items-center justify-between p-3 mt-3 border border-gray-200 rounded-lg bg-white shadow-sm`.
49
+ - Display the filename (truncated if long) and the formatted file size (e.g., "1.2 MB").
50
+ - Include a distinct, circular Trash/Remove button that removes the file from the state and calls `onSelect` with the updated array.
51
+
52
+ 3. **Validation Errors**:
53
+ - Local validation errors (e.g. "File too large") must be displayed in a highly visible red banner (`bg-red-50 text-red-700 p-3 rounded-md`) right below the dropzone.
54
+
55
+ ## Hidden Input
56
+ - Include a hidden `<input type="file" multiple={question.maxFileCount > 1} accept={...} />` that is triggered via a `useRef` when the dropzone is clicked.
@@ -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,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 details, read `01-components/`. For data shapes, read `02-reference/question-types/`.
4
+
5
+ | `question.type` | `question.subType` | Component | Answer Shape | Reference Doc |
6
+ |-----------------|-------------------|-----------|--------------|---------------|
7
+ | `'rating'` | — | `RatingScale` | `number` | `question-types/01-rating.md` |
8
+ | `'radio'` | — | Inline in `Question` | `string \| number` | `question-types/README.md#radio` |
9
+ | `'text'` | — | Inline in `Question` | `string` | `question-types/README.md#text` |
10
+ | `'csat'` | — | `CsatScale` | `number \| null` | `question-types/04-csat.md` |
11
+ | `'rating_scale'` | — | `RatingScale` | `number` | `question-types/05-rating-scale.md` |
12
+ | `'slider'` | — | `CustomSliderTrack` | `number` | `question-types/06-slider.md` |
13
+ | `'matrix'` | `'CFM_MATRIX'` | `LikertMatrixScale` | `MatrixAnswerMap` | `question-types/07-matrix-cfm.md` |
14
+ | `'matrix'` | `'CSAT_MATRIX'` | `CsatMatrixScale` | `MatrixAnswerMap` | `question-types/08-matrix-csat.md` |
15
+ | `'matrix'` | `'RATING_MATRIX'` | `CsatMatrixScale` | `MatrixAnswerMap` | `question-types/08-matrix-csat.md` |
16
+ | `'rating_matrix'` | — | `CsatMatrixScale` | `MatrixAnswerMap` | `question-types/09-matrix-rating.md` |
17
+ | `'slider_matrix'` | — | `SliderMatrixScale` | `MatrixAnswerMap` | `question-types/10-slider-matrix.md` |
18
+ | `'file_upload'` | — | `FileUploadScale` | `UploadedFile[]` | `question-types/11-file-upload.md` |
19
+ | `'text_and_media'` | — | Inline in `Question` | (no answer) | `question-types/README.md#text_and_media` |
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,129 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "packageNames": {
4
+ "npm": "@explorer02/cfm-survey-sdk",
5
+ "monorepo": "@repo/sdk"
6
+ },
7
+ "entryPoint": "index.md",
8
+ "clientInputs": ["initialPrompt", "instanceIdJwt", "deployConfirmation"],
9
+ "phases": [
10
+ {
11
+ "id": 0,
12
+ "name": "orient",
13
+ "read": ["index.md", "../AGENT.md"],
14
+ "actions": ["detectFramework", "extractBrandingFromClientPrompt"]
15
+ },
16
+ {
17
+ "id": 1,
18
+ "name": "scaffold",
19
+ "read": ["00-integration/setup.md"],
20
+ "frameworks": ["nextjs", "vite", "cra"]
21
+ },
22
+ {
23
+ "id": 2,
24
+ "name": "instanceId",
25
+ "read": ["00-integration/useSurveySDK.md"]
26
+ },
27
+ {
28
+ "id": 3,
29
+ "name": "sdkContract",
30
+ "read": ["00-integration/useSurveySDK.md"]
31
+ },
32
+ {
33
+ "id": 4,
34
+ "name": "planAndIngest",
35
+ "read": [
36
+ "01-components/README.md",
37
+ "01-components/01-survey-page.md",
38
+ "01-components/02-question.md",
39
+ "01-components/03-rating-scale.md",
40
+ "01-components/04-csat-scale.md",
41
+ "01-components/05-csat-matrix-scale.md",
42
+ "01-components/06-likert-matrix-scale.md",
43
+ "01-components/07-slider-matrix-scale.md",
44
+ "01-components/08-file-upload-scale.md",
45
+ "01-components/09-custom-slider-track.md",
46
+ "01-components/10-header-footer.md",
47
+ "01-components/11-progress-bar.md",
48
+ "01-components/12-language-selector.md",
49
+ "01-components/13-matrix-dropdown.md"
50
+ ],
51
+ "template": "templates/implementation_plan.md",
52
+ "output": "implementation_plan.md"
53
+ },
54
+ {
55
+ "id": 5,
56
+ "name": "build",
57
+ "read": ["00-integration/constraints.md", "02-reference/routing-table.md"],
58
+ "onDemand": "02-reference/question-types"
59
+ },
60
+ {
61
+ "id": 6,
62
+ "name": "verify",
63
+ "read": ["00-integration/constraints.md"],
64
+ "checklist": [
65
+ "questionDispatcherCoversAll12Types",
66
+ "everyQuestionWrapperHasId",
67
+ "noInternalSdkImports",
68
+ "surveyPageFiveStates",
69
+ "npmRunBuildPasses"
70
+ ]
71
+ },
72
+ {
73
+ "id": 7,
74
+ "name": "deploy",
75
+ "command": "npx cfm-sdk deploy"
76
+ }
77
+ ],
78
+ "questionTypes": {
79
+ "rating": {
80
+ "component": "01-components/03-rating-scale.md",
81
+ "reference": "02-reference/question-types/01-rating.md"
82
+ },
83
+ "radio": {
84
+ "component": "01-components/02-question.md",
85
+ "reference": "02-reference/question-types/README.md#radio"
86
+ },
87
+ "text": {
88
+ "component": "01-components/02-question.md",
89
+ "reference": "02-reference/question-types/README.md#text"
90
+ },
91
+ "csat": {
92
+ "component": "01-components/04-csat-scale.md",
93
+ "reference": "02-reference/question-types/04-csat.md"
94
+ },
95
+ "rating_scale": {
96
+ "component": "01-components/03-rating-scale.md",
97
+ "reference": "02-reference/question-types/05-rating-scale.md"
98
+ },
99
+ "slider": {
100
+ "component": "01-components/09-custom-slider-track.md",
101
+ "reference": "02-reference/question-types/06-slider.md"
102
+ },
103
+ "matrix": {
104
+ "component": "01-components/06-likert-matrix-scale.md",
105
+ "reference": "02-reference/question-types/07-matrix-cfm.md",
106
+ "subTypes": {
107
+ "CFM_MATRIX": "01-components/06-likert-matrix-scale.md",
108
+ "CSAT_MATRIX": "01-components/05-csat-matrix-scale.md",
109
+ "RATING_MATRIX": "01-components/05-csat-matrix-scale.md"
110
+ }
111
+ },
112
+ "rating_matrix": {
113
+ "component": "01-components/05-csat-matrix-scale.md",
114
+ "reference": "02-reference/question-types/09-matrix-rating.md"
115
+ },
116
+ "slider_matrix": {
117
+ "component": "01-components/07-slider-matrix-scale.md",
118
+ "reference": "02-reference/question-types/10-slider-matrix.md"
119
+ },
120
+ "file_upload": {
121
+ "component": "01-components/08-file-upload-scale.md",
122
+ "reference": "02-reference/question-types/11-file-upload.md"
123
+ },
124
+ "text_and_media": {
125
+ "component": "01-components/02-question.md",
126
+ "reference": "02-reference/question-types/README.md#text_and_media"
127
+ }
128
+ }
129
+ }