@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
@@ -1,72 +0,0 @@
1
- # Pagination — `useSurveyPagination`
2
-
3
- > **Source**: `packages/sdk/src/surveyPagination/useSurveyPagination.ts`
4
- > **Called by**: `useSurveySDK()` internally.
5
-
6
- ## What It Does
7
-
8
- Manages the multi-page survey state: current page index, user answers, validation errors, page navigation, and wires into submission and progress tracking.
9
-
10
- ## State Shape
11
-
12
- ```typescript
13
- {
14
- currentPageIndex: number, // Zero-based active page
15
- values: Record<string, AnswerValue>, // All answers keyed by question ID
16
- validationErrors: Record<string, string>, // Error messages keyed by question ID
17
- currentQuestions: SurveyQuestion[], // Questions on the active page
18
- progressPercentage: number, // 0–100 completion percentage
19
- }
20
- ```
21
-
22
- ## Navigation Actions
23
-
24
- ### `onNext()` (dispatched by `onAction({ type: 'NEXT' })`)
25
- 1. Validates all questions on the current page
26
- 2. If validation fails → populates `validationErrors` → scrolls to first error
27
- 3. If validation passes → clears errors → increments `currentPageIndex`
28
- 4. If already on the last page → auto-redirects to `onSubmit()`
29
-
30
- ### `onBack()` (dispatched by `onAction({ type: 'PREVIOUS' })`)
31
- 1. Decrements `currentPageIndex` (minimum 0)
32
- 2. Scrolls to the top of the new page
33
- 3. Logs backtrack analytics
34
-
35
- ### `onChange(questionId, value)` (dispatched by `onAction({ type: 'CHANGE', payload })`)
36
- 1. Updates `values[questionId] = value`
37
- 2. If the question had a validation error, clears it immediately
38
-
39
- ### `onSubmit()` (dispatched by `onAction({ type: 'SUBMIT' })` or auto-triggered)
40
- 1. Validates ALL pages (not just current)
41
- 2. If any page has errors → navigates to the error page → scrolls to first error
42
- 3. If all valid → calls `submit(values)` → sends response to server
43
-
44
- ## Progress Tracking
45
-
46
- `useSurveyProgress` computes `progressPercentage`:
47
- - Counts total answerable questions across ALL pages (excludes `text_and_media`)
48
- - Counts questions that have a non-empty value in `values`
49
- - Formula: `(answered / total) * 100`
50
-
51
- ## Internal Refs (Analytics)
52
-
53
- The hook maintains a `useRef` for analytics data that persists across renders:
54
- - `pageStartTime` — timestamp when current page was entered
55
- - `sessionStartTime` — timestamp when survey was first loaded
56
- - `maxVisitedPageIndex` — furthest page the user has reached
57
- - `validationErrorsCount` — total validation failures during the session
58
- - `backtrackCount` — number of times user navigated backwards
59
-
60
- These are used for telemetry and do NOT affect the UI.
61
-
62
- ## Auto-Submit on Last Page
63
-
64
- When `onAction({ type: 'NEXT' })` is dispatched on the last page, the SDK's `useSurveySDK` dispatcher automatically converts it to `{ type: 'SUBMIT' }`:
65
-
66
- ```typescript
67
- if (action.type === 'NEXT' && currentPageIndex === survey.pages.length - 1) {
68
- action = { type: 'SUBMIT' };
69
- }
70
- ```
71
-
72
- This means the client's "Next" button on the last page automatically becomes "Submit" in behaviour.
@@ -1,66 +0,0 @@
1
- # Validation — `validateQuestions` & `validateAllPages`
2
-
3
- > **Source**: `packages/sdk/src/surveyPagination/utils/validation.ts`
4
- > **Called by**: `useOnNext()` and `useOnSubmit()` internally.
5
-
6
- ## What It Does
7
-
8
- Validates required questions before allowing page navigation or survey submission. Populates `state.validationErrors` with per-question error messages.
9
-
10
- ## Validation Rules
11
-
12
- ### Standard Questions (rating, radio, text, csat, rating_scale, slider)
13
- - **Required check**: `value !== undefined && value !== ''`
14
- - `null` is treated as a **valid** answer (represents the N/A option)
15
-
16
- ### Matrix Questions (matrix, rating_matrix, slider_matrix)
17
- - Every row must have a value: `question.rows.every(row => answerMap[row.id] !== undefined && answerMap[row.id] !== '')`
18
- - `null` per row is valid (N/A option)
19
-
20
- ### Display-Only Questions (text_and_media)
21
- - **Always skipped** — `isDisplayOnly()` returns `true`
22
- - Never counted in validation or progress
23
-
24
- ## Error Messages
25
-
26
- | Source | Priority |
27
- |--------|----------|
28
- | `question.requiredErrorMessage` (from API) | **Highest** — custom per-question message |
29
- | Language-aware default | Fallback |
30
-
31
- Default messages:
32
- - German (`de`): `"Beantworten Sie bitte diese Frage bevor Sie fortfahren."`
33
- - All other: `"Please answer this question before you proceed."`
34
-
35
- ## Scroll-to-Error
36
-
37
- When validation fails, the SDK:
38
- 1. Identifies the first question with an error
39
- 2. Calls `document.getElementById(questionId)` to find the DOM element
40
- 3. Calls `.scrollIntoView({ behavior: 'smooth', block: 'center' })` to scroll to it
41
-
42
- **⚠️ CRITICAL**: This is why every question wrapper element MUST have `id={question.id}`.
43
-
44
- ## `validateQuestions(questions, values, language)`
45
-
46
- Validates a single page's questions. Returns `Record<string, string>` of errors (empty if all valid).
47
-
48
- ## `validateAllPages(pages, values, language)`
49
-
50
- Validates ALL pages at once (used before submission). Returns:
51
- ```typescript
52
- {
53
- errors: Record<string, string>, // All errors across all pages
54
- errorPageIndex: number, // First page with errors
55
- firstErrorId: string, // First question ID with error
56
- } | null // null = all valid
57
- ```
58
-
59
- When this returns non-null, the SDK:
60
- 1. Navigates to `errorPageIndex`
61
- 2. Sets all errors into `validationErrors` state
62
- 3. Scrolls to `firstErrorId`
63
-
64
- ## `isQuestionAnswered(question, values)`
65
-
66
- Exported utility that checks if a single question has been answered. Used by both validation and progress tracking.
@@ -1,90 +0,0 @@
1
- # Submit Survey — `useSubmitResponse` & `buildSubmitRequestBody`
2
-
3
- > **Sources**:
4
- > - `packages/sdk/src/submitSurvey/useSubmitResponse.ts`
5
- > - `packages/sdk/src/submitSurvey/utils/formatters.ts`
6
-
7
- ## What It Does
8
-
9
- Formats user answers into the Sprinklr CFM API payload structure and sends them via POST.
10
-
11
- ## Submission Endpoint
12
-
13
- ```
14
- POST https://custom-p0.feedbook.me/api/feedback/survey/cfm/response
15
- ```
16
-
17
- ## Payload Format — `SubmitRequestBody`
18
-
19
- ```typescript
20
- {
21
- questionToAnswers: Record<string, string[]>, // Standard + matrix row answers
22
- quesIdVsAttachmentDetails: Record<string, any[]>, // File upload attachments
23
- optionLevelAnswers: Record<string, string[]>, // (reserved)
24
- conjointTasks: unknown[], // (reserved — always [])
25
- }
26
- ```
27
-
28
- ## Per-Type Serialization
29
-
30
- ### Standard Questions (rating, radio, text, csat, rating_scale, slider)
31
- ```
32
- questionToAnswers[questionId] = [String(value)]
33
- ```
34
-
35
- ### Matrix Questions (matrix, rating_matrix, slider_matrix)
36
- Each row becomes a separate entry with a combined key:
37
- ```
38
- questionToAnswers[`${questionId}_${rowId}`] = [String(rowValue)]
39
- ```
40
-
41
- ### File Upload
42
- ```
43
- quesIdVsAttachmentDetails[questionId] = [
44
- { id: "...", name: "file.pdf", mediaUrl: "https://..." },
45
- ...
46
- ]
47
- ```
48
-
49
- ### Text and Media (display-only)
50
- Completely skipped — no data submitted.
51
-
52
- ## Answer Value Types
53
-
54
- ```typescript
55
- type AnswerValue = string | number | null | MatrixAnswerMap | any[];
56
-
57
- type MatrixAnswerMap = Record<string, string | number | null | Array<string | number | null>>;
58
- ```
59
-
60
- | Question Type | Answer Shape | Example |
61
- |--------------|-------------|---------|
62
- | `rating` | `number` | `7` |
63
- | `radio` | `string \| number` | `"option_1"` |
64
- | `text` | `string` | `"Great service"` |
65
- | `csat` | `number \| null` | `4` or `null` (N/A) |
66
- | `rating_scale` | `number` | `3` |
67
- | `slider` | `number` | `65` |
68
- | `matrix` | `MatrixAnswerMap` | `{ "row1": "col2", "row2": null }` |
69
- | `slider_matrix` | `MatrixAnswerMap` | `{ "row1": 75, "row2": 30 }` |
70
- | `file_upload` | `UploadedFile[]` | `[{ id, name, mediaUrl }]` |
71
- | `text_and_media` | (none) | Not submitted |
72
-
73
- ## Submit Result
74
-
75
- ```typescript
76
- type SubmitResult = {
77
- success: boolean;
78
- responseId: string; // Generated database ID for the response
79
- };
80
- ```
81
-
82
- Accessible via `submitSurveyResults.data` in the component.
83
-
84
- ## Error Handling
85
-
86
- | Scenario | Behaviour |
87
- |----------|-----------|
88
- | Network failure | `submitSurveyResults.error` populated |
89
- | Server rejection | `submitSurveyResults.error` populated with status |
90
- | During submission | `submitSurveyResults.isLoading = true` — disable submit button |
@@ -1,111 +0,0 @@
1
- # Language Handling — Translations & Placeholders
2
-
3
- > **Source**: `packages/sdk/src/fetchSurvey/utils/extractSurveyLanguages.ts`
4
-
5
- ## How Multi-Language Works
6
-
7
- 1. The raw API response contains `translations` objects on each question and answer detail
8
- 2. `extractSurveyLanguages()` scans all questions to discover available language codes
9
- 3. `mapSurvey()` resolves text fields using the active language's translations
10
- 4. Language switching is **client-side only** — no API re-fetch
11
-
12
- ## Language Discovery
13
-
14
- The SDK traverses all questions in the survey and collects unique language codes from `question.translations` keys:
15
-
16
- ```typescript
17
- // Pseudocode
18
- languagesSet.add(baseLanguage); // e.g. 'de'
19
- for each question:
20
- for each key in question.translations:
21
- languagesSet.add(key); // e.g. 'en', 'fr'
22
- ```
23
-
24
- ## Display Names
25
-
26
- Built-in language code → display name mapping:
27
-
28
- | Code | Display Name |
29
- |------|-------------|
30
- | `en` | English |
31
- | `de` | Deutsch |
32
- | `hi` | Hindi |
33
- | `es` | Español |
34
- | `fr` | Français |
35
- | `it` | Italiano |
36
- | `ja` | 日本語 |
37
- | `ko` | 한국어 |
38
- | `zh` | 中文 |
39
- | `bn` | Bengali |
40
- | `gu` | Gujarati |
41
- | `mr` | Marathi |
42
- | `pa` | Punjabi |
43
- | `ta` | Tamil |
44
- | `te` | Telugu |
45
- | `ur` | Urdu |
46
-
47
- Unknown codes fall back to `code.toUpperCase()`.
48
-
49
- ## Translation Resolution Priority
50
-
51
- For question text:
52
- ```
53
- translations[language].questionRichText
54
- → translations[language].questionText
55
- → raw.questionRichText
56
- → raw.questionText
57
- → ''
58
- ```
59
-
60
- For answer option labels:
61
- ```
62
- translations[language].answerText → raw.answerText → ''
63
- ```
64
-
65
- For validation error messages:
66
- ```
67
- config.translations[language].value[0] → config.value[0] → default message
68
- ```
69
-
70
- ## Placeholders
71
-
72
- Tokens like `{{FIRST_NAME}}` in question text are replaced at map time:
73
-
74
- ```typescript
75
- const SURVEY_PLACEHOLDERS = {
76
- FIRST_NAME: 'Customer',
77
- LAST_NAME: 'Smith',
78
- EMAIL_ADDRESS: 'customer@example.com',
79
- };
80
- ```
81
-
82
- **⚠️ CRITICAL**: Define `SURVEY_PLACEHOLDERS` as a **constant outside the component** to prevent infinite re-render loops:
83
-
84
- ```tsx
85
- // ✅ CORRECT — stable reference
86
- const SURVEY_PLACEHOLDERS = { FIRST_NAME: 'John' };
87
- export default function SurveyPage() { ... }
88
-
89
- // ❌ WRONG — new object every render → infinite re-fetch
90
- export default function SurveyPage() {
91
- useSurveySDK({ options: { placeholders: { FIRST_NAME: 'John' } } });
92
- }
93
- ```
94
-
95
- ## Language Switching in UI
96
-
97
- ```tsx
98
- const [selectedLanguage, setSelectedLanguage] = useState<string | undefined>("");
99
-
100
- // Pass to SDK
101
- const { ... } = useSurveySDK({ options: { language: selectedLanguage } });
102
-
103
- // Render selector
104
- <LanguageSelector
105
- languages={survey.languages}
106
- selectedLanguage={survey.language}
107
- onChange={setSelectedLanguage}
108
- />
109
- ```
110
-
111
- When `setSelectedLanguage` fires, the SDK re-maps the cached survey data to the new language — no network request.
@@ -1,56 +0,0 @@
1
- # Icons & Emojis Architecture
2
-
3
- > **Source**: `apps/client/src/components/icons.tsx` (to be created by the agent)
4
-
5
- ## The CSAT Emoji System
6
-
7
- The SDK relies on a specific sequence of sentiment faces for CSAT scales. You MUST implement this mapping in your client application using a library like `react-icons/fa` or raw SVGs.
8
-
9
- ### Default 5-Point Mapping
10
-
11
- ```tsx
12
- import { FaRegSmile, FaRegMeh, FaRegFrown, FaRegSmileBeam, FaRegTired } from 'react-icons/fa';
13
-
14
- export const CsatEmojiMapping: Record<number | string, JSX.Element> = {
15
- 1: <FaRegTired className="text-red-500 w-full h-full" />,
16
- 2: <FaRegFrown className="text-orange-500 w-full h-full" />,
17
- 3: <FaRegMeh className="text-yellow-500 w-full h-full" />,
18
- 4: <FaRegSmile className="text-green-500 w-full h-full" />,
19
- 5: <FaRegSmileBeam className="text-green-600 w-full h-full" />,
20
- };
21
- ```
22
-
23
- ### The `getEmojiForIndex` Helper
24
-
25
- Because some surveys use 3-point, 7-point, or 10-point scales instead of 5, you MUST use a helper function to proportionally map any scale length to the 5 core sentiment faces.
26
-
27
- ```tsx
28
- export function getEmojiForIndex(index: number, totalOptions: number): JSX.Element {
29
- const percentage = index / (totalOptions - 1);
30
-
31
- if (percentage < 0.2) return CsatEmojiMapping[1];
32
- if (percentage < 0.4) return CsatEmojiMapping[2];
33
- if (percentage < 0.6) return CsatEmojiMapping[3];
34
- if (percentage < 0.8) return CsatEmojiMapping[4];
35
- return CsatEmojiMapping[5];
36
- }
37
- ```
38
-
39
- ## The Star Icon System
40
-
41
- For star ratings, you need filled and empty variants:
42
-
43
- ```tsx
44
- import { FaStar, FaRegStar } from 'react-icons/fa';
45
-
46
- export const CsatStarIcons = {
47
- filled: <FaStar className="text-yellow-400 w-full h-full" />,
48
- empty: <FaRegStar className="text-gray-300 w-full h-full" />
49
- };
50
- ```
51
-
52
- ## Color Mapping System
53
- For rating matrices and numbered NPS grids, colors are usually provided by the SDK in `option.color`. If not, use standard NPS coloring:
54
- - **0-6 (Detractors)**: Red/Orange
55
- - **7-8 (Passives)**: Yellow
56
- - **9-10 (Promoters)**: Green
@@ -1,53 +0,0 @@
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.
@@ -1,26 +0,0 @@
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)}`
@@ -1,26 +0,0 @@
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
@@ -1,35 +0,0 @@
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
@@ -1,74 +0,0 @@
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` |