@explorer02/cfm-survey-sdk 0.2.1 → 0.2.3

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 (95) 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 -69
  9. package/templates/AGENT.md +18 -8
  10. package/templates/docs/00-integration/analytics-events-catalog.md +54 -0
  11. package/templates/docs/00-integration/answer-logic-and-navigation.md +50 -0
  12. package/templates/docs/00-integration/aws-deploy.md +281 -0
  13. package/templates/docs/00-integration/client-integration-guide.md +351 -0
  14. package/templates/docs/00-integration/component-checklist.md +55 -22
  15. package/templates/docs/00-integration/constraints.md +49 -4
  16. package/templates/docs/00-integration/custom-field-logic-and-navigation.md +174 -0
  17. package/templates/docs/00-integration/display-logic-and-navigation.md +64 -0
  18. package/templates/docs/00-integration/end-page-logic.md +64 -0
  19. package/templates/docs/00-integration/file-upload-aws.md +116 -0
  20. package/templates/docs/00-integration/logic-fields-catalog.md +105 -0
  21. package/templates/docs/00-integration/partial-save-and-recovery.md +98 -0
  22. package/templates/docs/00-integration/placeholders-and-tokens.md +116 -0
  23. package/templates/docs/00-integration/progress.md +61 -0
  24. package/templates/docs/00-integration/question-display-variants.md +65 -0
  25. package/templates/docs/00-integration/question-numbering.md +51 -0
  26. package/templates/docs/00-integration/question-type-sdk-matrix.md +244 -0
  27. package/templates/docs/00-integration/setup.md +84 -0
  28. package/templates/docs/00-integration/skip-logic-and-navigation.md +80 -0
  29. package/templates/docs/00-integration/survey-lifecycle-analytics.md +94 -0
  30. package/templates/docs/00-integration/useSurveySDK.md +151 -23
  31. package/templates/docs/01-components/01-survey-page.md +76 -54
  32. package/templates/docs/01-components/02-question.md +61 -58
  33. package/templates/docs/01-components/03-rating-scale.md +11 -1
  34. package/templates/docs/01-components/04-star-rating-scale.md +45 -0
  35. package/templates/docs/01-components/05-csat-matrix-scale.md +20 -11
  36. package/templates/docs/01-components/06-likert-matrix-scale.md +14 -4
  37. package/templates/docs/01-components/07-slider-matrix-scale.md +11 -2
  38. package/templates/docs/01-components/08-file-upload-scale.md +23 -51
  39. package/templates/docs/01-components/09-custom-slider-track.md +2 -2
  40. package/templates/docs/01-components/11-progress-bar.md +10 -4
  41. package/templates/docs/01-components/13-matrix-dropdown.md +29 -32
  42. package/templates/docs/01-components/14-intro-page.md +34 -0
  43. package/templates/docs/01-components/15-end-page.md +31 -0
  44. package/templates/docs/01-components/16-paused-page.md +27 -0
  45. package/templates/docs/01-components/17-heatmap-scale.md +75 -0
  46. package/templates/docs/01-components/18-rank-order-scale.md +93 -0
  47. package/templates/docs/01-components/README.md +42 -36
  48. package/templates/docs/02-reference/config-field-index.md +141 -139
  49. package/templates/docs/02-reference/exports.md +149 -0
  50. package/templates/docs/02-reference/question-types/01-rating.md +31 -32
  51. package/templates/docs/02-reference/question-types/04-csat.md +51 -42
  52. package/templates/docs/02-reference/question-types/05-rating-scale.md +16 -11
  53. package/templates/docs/02-reference/question-types/06-slider.md +2 -2
  54. package/templates/docs/02-reference/question-types/07-matrix-cfm.md +44 -30
  55. package/templates/docs/02-reference/question-types/08-matrix-csat.md +4 -26
  56. package/templates/docs/02-reference/question-types/09-matrix-rating.md +29 -13
  57. package/templates/docs/02-reference/question-types/10-slider-matrix.md +15 -4
  58. package/templates/docs/02-reference/question-types/11-file-upload.md +43 -17
  59. package/templates/docs/02-reference/question-types/12-heatmap.md +59 -0
  60. package/templates/docs/02-reference/question-types/13-rank-order.md +101 -0
  61. package/templates/docs/02-reference/question-types/README.md +96 -47
  62. package/templates/docs/02-reference/routing-table.md +33 -18
  63. package/templates/docs/02-reference/value-derivation.md +63 -0
  64. package/templates/docs/03-ui-specs/00-question-shell.md +19 -88
  65. package/templates/docs/03-ui-specs/01-rating.md +18 -52
  66. package/templates/docs/03-ui-specs/02-radio.md +80 -37
  67. package/templates/docs/03-ui-specs/03-text.md +2 -2
  68. package/templates/docs/03-ui-specs/04-csat.md +68 -33
  69. package/templates/docs/03-ui-specs/05-rating-scale.md +21 -18
  70. package/templates/docs/03-ui-specs/06-slider.md +3 -1
  71. package/templates/docs/03-ui-specs/07-matrix-cfm.md +44 -25
  72. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +19 -43
  73. package/templates/docs/03-ui-specs/09-slider-matrix.md +18 -1
  74. package/templates/docs/03-ui-specs/10-file-upload.md +42 -21
  75. package/templates/docs/03-ui-specs/11-text-and-media.md +29 -29
  76. package/templates/docs/03-ui-specs/12-survey-chrome.md +58 -14
  77. package/templates/docs/03-ui-specs/13-heatmap.md +80 -0
  78. package/templates/docs/03-ui-specs/14-rank-order.md +128 -0
  79. package/templates/docs/03-ui-specs/README.md +16 -14
  80. package/templates/docs/03-ui-specs/shared/custom-slider-track.md +28 -35
  81. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +5 -0
  82. package/templates/docs/MANIFEST.json +476 -69
  83. package/templates/docs/index.md +82 -30
  84. package/templates/docs/templates/CustomSliderTrack.tsx +144 -0
  85. package/templates/docs/templates/MatrixDropdown.tsx +216 -0
  86. package/templates/docs/templates/Question.tsx +221 -154
  87. package/templates/docs/templates/RankOrderScale.tsx +353 -0
  88. package/templates/docs/templates/deploy-to-aws.sh +114 -0
  89. package/templates/docs/templates/heatmapCoords.ts +58 -0
  90. package/templates/docs/templates/implementation_plan.md +126 -67
  91. package/templates/docs/templates/survey-inventory.schema.json +77 -0
  92. package/templates/docs/templates/surveyUiIcons.tsx +52 -0
  93. package/templates/docs/templates/verify-agent-build.sh +85 -0
  94. package/templates/docs/01-components/04-csat-scale.md +0 -40
  95. package/templates/docs/02-question-types/01-rating.md +0 -44
@@ -12,107 +12,166 @@
12
12
  ## 1. Data Fetching & State
13
13
 
14
14
  - [ ] `useSurveySDK` options: instanceId, language, placeholders, debug
15
- - [ ] `SurveyPage` 5-state machine: loading / error / empty / submitted / active
15
+ - [ ] `surveyOptions` wrapped in `useMemo` (stable refs for placeholders / customFieldValues)
16
+ - [ ] `customFieldValues` map when survey logic references `CUSTOM_FIELD` entities (see `custom-field-logic-and-navigation.md`)
17
+ - [ ] `savePartialResponse` enabled if required (`true` or `{ debounceMs }`)
18
+ - [ ] Monorepo: `packages/sdk` built (`npm run build`) before client dev
19
+ - [ ] `SurveyPage` phase router: loading / error / empty / intro / paused / end / terminal / content
16
20
  - [ ] `surveyQueryResults.isLoading`, `.error`, `.data` handling
17
21
  - [ ] `submitSurveyResults.isLoading`, `.error`, `.data` handling
22
+ - [ ] Answers read from `state.answers` (`SurveyAnswers`); changes via `payload.answerValue`
18
23
 
19
24
  ## 2. Pagination, Actions & Flow
20
25
 
21
- - [ ] `onAction` NEXT / PREVIOUS / SUBMIT wiring
22
- - [ ] `ProgressBar` bound to `state.progressPercentage`
23
- - [ ] Back/Next button visibility per layout config
24
- - [ ] Last-page NEXT auto SUBMIT behavior
26
+ - [ ] `onAction` START / NEXT / PREVIOUS / SUBMIT wiring
27
+ - [ ] `onAction(SCROLL)` once on first scroll (mark as started)
28
+ - [ ] `onAction(RECORD_QUESTION_VIEW)` via IntersectionObserver on question wrappers
29
+ - [ ] `ProgressBar` bound to `state.progressPercentage` (path-scoped — see `00-integration/progress.md`)
30
+ - [ ] Back via `state.canGoBack`; primary label via `state.primaryButtonType`
31
+ - [ ] Read `00-integration/skip-logic-and-navigation.md` for skip/Back behavior
32
+ - [ ] Read `00-integration/client-integration-guide.md` for full wiring map
33
+
34
+ ## 1b. Lifecycle & Analytics
35
+
36
+ - [ ] `onAction(SCROLL)` once on first scroll (see `survey-lifecycle-analytics.md`)
37
+ - [ ] First `CHANGE` auto mark-as-started (SDK — no extra client call)
38
+ - [ ] `onAction(RECORD_QUESTION_VIEW)` via IntersectionObserver on `#question-{id}` wrappers
39
+ - [ ] Effect re-runs when `currentQuestions` changes (multi-page / display logic)
40
+ - [ ] Partial save: `savePartialResponse` + monitor `partialSaveResults` if enabled (see `partial-save-and-recovery.md`)
41
+ - [ ] Placeholders: stable `SURVEY_PLACEHOLDERS` ref + `useMemo` on hook options (see `placeholders-and-tokens.md`)
25
42
 
26
43
  ## 3. Question Dispatching (`Question.tsx`)
27
44
 
28
- Exhaustive switch — check each:
29
-
30
- - [ ] `rating` → `RatingScale`
31
- - [ ] `csat` → `CsatScale`
32
- - [ ] `radio` → inline MCQ
33
- - [ ] `text` → inline textarea/input
34
- - [ ] `rating_scale` → `RatingScale` (star/emoji)
35
- - [ ] `slider` → `CustomSliderTrack`
36
- - [ ] `matrix` + `CFM_MATRIX` → `LikertMatrixScale`
37
- - [ ] `matrix` + `CSAT_MATRIX` / `RATING_MATRIX` → `CsatMatrixScale`
38
- - [ ] `rating_matrix` → `CsatMatrixScale`
39
- - [ ] `slider_matrix` → `SliderMatrixScale`
40
- - [ ] `file_upload` `FileUploadScale`
41
- - [ ] `text_and_media` inline media (no scale)
42
- - [ ] `dangerouslySetInnerHTML` for `question.text` / `description`
45
+ Exhaustive switch — check each (**11** `QUESTION_TYPE` literals):
46
+
47
+ - [ ] `NPS_SCALE` → `RatingScale`
48
+ - [ ] `MCQ` → inline MCQ
49
+ - [ ] `TEXTFIELD` → inline textarea/input
50
+ - [ ] `CFM_MATRIX` → `LikertMatrixScale`
51
+ - [ ] `CSAT_MATRIX`, `RATING_MATRIX` → `CsatMatrixScale`
52
+ - [ ] `SLIDER_MATRIX` → `SliderMatrixScale`
53
+ - [ ] `FILE_UPLOAD` → `FileUploadScale`
54
+ - [ ] `TEXT_AND_MEDIA` inline media (no scale)
55
+ - [ ] `HEATMAP` → `HeatmapScale`
56
+ - [ ] `RANK_ORDER` → `RankOrderScale`
57
+ - [ ] `allAnswers` + `allQuestions` passed for placeholder resolution
58
+ - [ ] `customFieldValues` passed from SurveyPage when survey uses custom-field or answer logic on CRM data
59
+ - [ ] Answer logic: `getVisibleMcqOptionsForAnswers` / `getVisibleMatrixItemsForAnswers` with `customFieldValues` 4th arg
60
+ - [ ] `dangerouslySetInnerHTML` for `question.questionText` / `questionDescription`
43
61
  - [ ] `validationError` banner per question
44
62
  - [ ] `id={question.id}` on every question wrapper
45
63
  - [ ] Copied from `docs/templates/Question.tsx` — no stub/default fallback branches
46
64
 
65
+ ## 3a. Value Derivation (client-side)
66
+
67
+ Read `02-reference/value-derivation.md`:
68
+
69
+ - [ ] Import `matrixColumnStoredValue` from SDK for matrix cells
70
+ - [ ] Matrix keys = `statementRows[].id`; values = column **`id`** or `null`
71
+ - [ ] NPS: derive numeric value from `optionLabel` or index
72
+ - [ ] MCQ: store `option.id`
73
+
47
74
  ## 3b. Required Component Files (from fetched survey)
48
75
 
49
- Fill from `00-integration/component-checklist.md` — every file below that applies to your survey **must exist** before Phase 5:
76
+ Fill from `00-integration/component-checklist.md`:
50
77
 
51
78
  | Component file | Required if survey contains | Created? |
52
79
  |----------------|----------------------------|----------|
53
- | `LikertMatrixScale.tsx` | `matrix` + `CFM_MATRIX` | |
54
- | `CsatMatrixScale.tsx` | `matrix` + `CSAT_MATRIX` or `RATING_MATRIX` | |
55
- | `SliderMatrixScale.tsx` | `slider_matrix` | **blocking** |
56
- | `FileUploadScale.tsx` | `file_upload` | **blocking** |
57
- | `MatrixDropdown.tsx` | matrix with dropdown/selectbox modes | |
58
- | `CustomSliderTrack.tsx` | `slider_matrix` or CSAT matrix `graphics` | |
80
+ | `IntroPage.tsx` | intro page | |
81
+ | `EndPage.tsx` | end pages | |
82
+ | `PausedPage.tsx` | paused flag | |
83
+ | `LikertMatrixScale.tsx` | `CFM_MATRIX` | |
84
+ | `CsatMatrixScale.tsx` | `CSAT_MATRIX` or `RATING_MATRIX` | |
85
+ | `SliderMatrixScale.tsx` | `SLIDER_MATRIX` | **blocking** |
86
+ | `FileUploadScale.tsx` | `FILE_UPLOAD` | **blocking** |
87
+ | `MatrixDropdown.tsx` | matrix dropdown/selectbox | |
88
+ | `CustomSliderTrack.tsx` | `SLIDER_MATRIX` or matrix graphics | |
89
+ | `RatingScale.tsx` | `NPS_SCALE` | |
90
+ | `HeatmapScale.tsx` | `HEATMAP` | |
91
+ | `RankOrderScale.tsx` | `RANK_ORDER` | |
92
+ | `lib/heatmapCoords.ts` | `HEATMAP` | copy from `templates/heatmapCoords.ts` |
93
+
94
+ ## 3c. Logic inventory (from fetched survey)
95
+
96
+ Scan `survey.pages` for logic flags. Fill after fetch:
97
+
98
+ | questionId | skipLogics | displayLogic | answerLogic (options/rows/cols) | customField refs (`_c_…`) |
99
+ |------------|------------|--------------|----------------------------------|---------------------------|
100
+ | | | | | |
101
+
102
+ Custom field ids found → populate `CUSTOM_FIELD_VALUES` map. See `00-integration/custom-field-logic-and-navigation.md`.
103
+
104
+ ## 3d. Per-Type SDK Integration (from fetched survey)
105
+
106
+ Fill from `00-integration/question-type-sdk-matrix.md`:
107
+
108
+ | questionId | type | answer shape | visibility helper | customField refs | component files |
109
+ |------------|------|--------------|-------------------|------------------|-----------------|
110
+ | | | | | | |
59
111
 
60
112
  ## 4. Component Architecture Checklist
61
113
 
62
- | Component | In survey? | Blueprint doc | Key constraints verified |
63
- |-----------|------------|---------------|--------------------------|
64
- | RatingScale | | `01-components/03-rating-scale.md` | |
65
- | CsatScale | | `01-components/04-csat-scale.md` | |
66
- | CsatMatrixScale | | `01-components/05-csat-matrix-scale.md` | |
67
- | LikertMatrixScale | | `01-components/06-likert-matrix-scale.md` | |
68
- | SliderMatrixScale | | `01-components/07-slider-matrix-scale.md` | |
69
- | FileUploadScale | | `01-components/08-file-upload-scale.md` | |
70
- | CustomSliderTrack | | `01-components/09-custom-slider-track.md` | |
71
- | Header / Footer | | `01-components/10-header-footer.md` | |
72
- | ProgressBar | | `01-components/11-progress-bar.md` | |
73
- | LanguageSelector | | `01-components/12-language-selector.md` | |
74
- | MatrixDropdown | | `01-components/13-matrix-dropdown.md` | |
114
+ | Component | In survey? | Blueprint doc |
115
+ |-----------|------------|---------------|
116
+ | IntroPage / EndPage / PausedPage | | `14` / `15` / `16` |
117
+ | RatingScale | | `03-rating-scale.md` |
118
+ | CsatMatrixScale | | `05-csat-matrix-scale.md` |
119
+ | LikertMatrixScale | | `06-likert-matrix-scale.md` |
120
+ | SliderMatrixScale | | `07-slider-matrix-scale.md` |
121
+ | FileUploadScale | | `08-file-upload-scale.md` |
122
+ | Header / Footer / ProgressBar / LanguageSelector | | `10`–`12` |
123
+
124
+ ## 3e. Interaction dependencies (from survey inventory)
125
+
126
+ | Package | Install when |
127
+ |---------|--------------|
128
+ | `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities` | Survey contains `RANK_ORDER` |
129
+ | `react-icons` | Always (shared setup) + CSAT emoji/star matrix |
130
+
131
+ Copy portable templates from `docs/templates/` — see `component-checklist.md`.
75
132
 
76
133
  ## 4b. Per-Question Config & UI Coverage
77
134
 
78
- Fill one row per question in the fetched survey:
135
+ Inventory required config fields per type:
79
136
 
80
- | questionId | type | subType | configs detected | uiSpec read | ref component | branches implemented |
81
- |------------|------|---------|------------------|-------------|---------------|---------------------|
82
- | | | | | | | |
137
+ | type | must inventory |
138
+ |------|----------------|
139
+ | MCQ | `selectionMode`, `minSelections`, `maxSelections`, `defaultOptionIds`, answer-logic on options |
140
+ | RANK_ORDER | `interactionMode`, `optionDisplay`, `requireRankAll`, `shuffleOptions` |
141
+ | CFM/CSAT/RATING | `gridLayout`, `displayStyle`, `selectionMode`, `hasNotApplicableOption`, `statementLayout` |
142
+ | SLIDER_MATRIX | `enableInputBox`, `enableNotApplicable`, `sliderStyle` |
143
+ | FILE_UPLOAD | `maxFileCount`, `fileSizeLimit`, `fileSizeLimitType`, upload API configured? |
144
+ | HEATMAP | `maxClicksAllowed`, image URL |
83
145
 
84
- - **configs detected:** non-default fields from `02-reference/config-field-index.md` (e.g. `matrixFormat: carousel`, `buttonType: emoji`)
85
- - **uiSpec read:** path from `MANIFEST.json` `uiSpecs` (matrix: use `matrix_{subType}`)
86
- - **ref component:** monorepo: `apps/client/src/components/{Component}.tsx`; npm: implement from ui-spec only
87
- - **branches implemented:** list decision-tree branches from the ui-spec (e.g. dropdown vs grid vs carousel)
88
- - **blocking types:** `matrix`, `slider_matrix`, `file_upload` must show interactive UI, not placeholder text
146
+ | questionId | type | configs detected | uiSpec read | component files | branches implemented |
147
+ |------------|------|------------------|-------------|-----------------|---------------------|
148
+ | | | | | | |
89
149
 
90
150
  ## 5. Theme, Brand & Logo
91
151
 
92
152
  - [ ] CSS-only logo (no `<img>`, no `public/` uploads)
93
153
  - [ ] Primary / hover colors from client prompt
94
- - [ ] Header / Footer / ProgressBar / LanguageSelector per layout
95
154
 
96
155
  ## 6. Submit & Thank You
97
156
 
98
- - [ ] `onAction({ type: 'SUBMIT' })` on final page
99
- - [ ] Thank-you screen when `submitSurveyResults.data` is set
100
- - [ ] Custom thank-you message from client prompt if provided
157
+ - [ ] `onAction({ type: 'NEXT' })` when `primaryButtonType === 'submit'`
158
+ - [ ] End UI when `state.phase.kind === 'end'` with `EndPage`
101
159
 
102
160
  ## 7. Pre-Build Verification
103
161
 
104
162
  - [ ] No imports from `.../src/` internal paths
105
- - [ ] All mutations via `onAction` — no direct state mutation
106
- - [ ] `npm run build` passes
107
-
108
- ## 7b. Stub-Free Verification
109
-
110
- Per question type in the fetched survey, confirm interactive UI renders (not grey placeholder text):
111
-
112
- | type | subType | Renders real component? | No "not yet implemented" text? |
113
- |------|---------|---------------------------|--------------------------------|
114
- | | | | |
115
-
116
- - [ ] `grep -r "not yet implemented" src/ components/ app/` returns **zero matches**
117
- - [ ] Matrix questions route by `subType` in `Question.tsx`
118
- - [ ] `slider_matrix` shows sliders; `file_upload` shows dropzone
163
+ - [ ] All mutations via `onAction`
164
+ - [ ] Stable `customFieldValues` ref; passed to hook and Question visibility helpers
165
+ - [ ] `bash templates/verify-agent-build.sh` passes
166
+ - [ ] `grep -r "not yet implemented"` → zero matches
167
+
168
+ ## 8. AWS Deploy Readiness (fill if client prompt mentions AWS / CloudFront / S3 / EC2 / Sprinklr)
169
+
170
+ - [ ] Next.js `output: "export"` + `assetPrefix: "./"` configured (see `setup.md#nextjs-aws-static-export`)
171
+ - [ ] `images.unoptimized: true` in `next.config.js`
172
+ - [ ] Build produces `out/index.html` (+ `index.html.txt` copy step in build script)
173
+ - [ ] `SURVEY_INSTANCE_ID` matches deploy JWT (`CFM_INSTANCE_ID`)
174
+ - [ ] Logo/media paths are relative (no leading `/`)
175
+ - [ ] Rank-order uses `option.imageUrl` before `option.previewImageUrl`
176
+ - [ ] `CFM_DEPLOY_API_BASE=http://43.204.26.213:3000` noted in plan
177
+ - [ ] Static file count ≤ 500 (`find out -type f | wc -l`)
@@ -0,0 +1,77 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://cfm-survey-sdk/templates/survey-inventory.schema.json",
4
+ "title": "Survey Question Inventory",
5
+ "description": "Post-fetch inventory agents fill before Phase 5 BUILD",
6
+ "type": "object",
7
+ "required": ["questions", "uniqueTypes"],
8
+ "properties": {
9
+ "instanceId": { "type": "string", "description": "JWT used to fetch survey" },
10
+ "interactionDependencies": {
11
+ "type": "array",
12
+ "items": { "type": "string" },
13
+ "description": "npm packages e.g. @dnd-kit/core, react-icons"
14
+ },
15
+ "uniqueTypes": {
16
+ "type": "array",
17
+ "items": {
18
+ "type": "string",
19
+ "enum": [
20
+ "MCQ",
21
+ "TEXTFIELD",
22
+ "NPS_SCALE",
23
+ "CFM_MATRIX",
24
+ "CSAT_MATRIX",
25
+ "RATING_MATRIX",
26
+ "SLIDER_MATRIX",
27
+ "FILE_UPLOAD",
28
+ "TEXT_AND_MEDIA",
29
+ "HEATMAP",
30
+ "RANK_ORDER"
31
+ ]
32
+ },
33
+ "uniqueItems": true
34
+ },
35
+ "questions": {
36
+ "type": "array",
37
+ "items": {
38
+ "type": "object",
39
+ "required": ["id", "type", "uiSpecRead", "implemented"],
40
+ "properties": {
41
+ "id": { "type": "string" },
42
+ "type": {
43
+ "type": "string",
44
+ "enum": [
45
+ "MCQ",
46
+ "TEXTFIELD",
47
+ "NPS_SCALE",
48
+ "CFM_MATRIX",
49
+ "CSAT_MATRIX",
50
+ "RATING_MATRIX",
51
+ "SLIDER_MATRIX",
52
+ "FILE_UPLOAD",
53
+ "TEXT_AND_MEDIA",
54
+ "HEATMAP",
55
+ "RANK_ORDER"
56
+ ]
57
+ },
58
+ "configFlags": {
59
+ "type": "array",
60
+ "items": { "type": "string" },
61
+ "description": "e.g. interactionMode:dragAndDrop, gridLayout:carousel, selectionMode:multiple, defaultOptionIds"
62
+ },
63
+ "uiSpecRead": { "type": "string" },
64
+ "componentFiles": {
65
+ "type": "array",
66
+ "items": { "type": "string" }
67
+ },
68
+ "branchesImplemented": {
69
+ "type": "array",
70
+ "items": { "type": "string" }
71
+ },
72
+ "implemented": { "type": "boolean" }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import {
5
+ FaFaceAngry,
6
+ FaFaceSadTear,
7
+ FaFaceFrown,
8
+ FaFaceFrownOpen,
9
+ FaFaceMeh,
10
+ FaFaceSmile,
11
+ FaFaceSmileBeam,
12
+ FaFaceGrin,
13
+ FaFaceGrinWide,
14
+ FaFaceGrinStars,
15
+ } from 'react-icons/fa6';
16
+
17
+ const EMOJI_COLORS = [
18
+ '#dc2626', '#e04832', '#ea580c', '#f59e0b', '#eab308',
19
+ '#a3a323', '#84cc16', '#65a30d', '#22c55e', '#16a34a', '#059669',
20
+ ] as const;
21
+
22
+ const FACE_ICONS = [
23
+ FaFaceAngry, FaFaceSadTear, FaFaceFrown, FaFaceFrownOpen, FaFaceMeh, FaFaceMeh,
24
+ FaFaceSmile, FaFaceSmileBeam, FaFaceGrin, FaFaceGrinWide, FaFaceGrinStars,
25
+ ] as const;
26
+
27
+ const CsatEmojiSet: Record<number, React.ReactNode> = {};
28
+ for (let i = 0; i < 11; i++) {
29
+ const Icon = FACE_ICONS[i];
30
+ CsatEmojiSet[i + 1] = React.createElement(Icon as React.ElementType, {
31
+ className: 'transition-transform',
32
+ style: { color: EMOJI_COLORS[i], fontSize: '28px' },
33
+ });
34
+ }
35
+
36
+ const EMOJI_SCALES: Record<number, number[]> = {
37
+ 3: [3, 6, 8],
38
+ 4: [3, 4, 7, 9],
39
+ 5: [3, 4, 6, 7, 9],
40
+ 6: [3, 4, 6, 7, 9, 11],
41
+ };
42
+
43
+ export function getEmojiForIndex(index: number, total: number): React.ReactNode {
44
+ if (total <= 1) return CsatEmojiSet[6];
45
+ if (EMOJI_SCALES[total]) {
46
+ const key = EMOJI_SCALES[total][index];
47
+ if (key) return CsatEmojiSet[key];
48
+ }
49
+ const percent = index / (total - 1);
50
+ const key = Math.min(11, Math.max(1, Math.round(percent * 10) + 1));
51
+ return CsatEmojiSet[key];
52
+ }
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env bash
2
+ # Agent build verification — run from project root after Phase 5 BUILD
3
+ set -euo pipefail
4
+
5
+ echo "=== CFM Survey SDK — Agent Build Verification ==="
6
+
7
+ STUB_MATCHES=$(grep -r "not yet implemented" src/ components/ app/ 2>/dev/null || true)
8
+ if [ -n "$STUB_MATCHES" ]; then
9
+ echo "FAIL: Stub placeholder text found:"
10
+ echo "$STUB_MATCHES"
11
+ exit 1
12
+ fi
13
+ echo "OK: No stub placeholder text"
14
+
15
+ INTERNAL_IMPORTS=$(grep -rE "@explorer02/cfm-survey-sdk/src|@repo/sdk/src" src/ components/ app/ 2>/dev/null || true)
16
+ if [ -n "$INTERNAL_IMPORTS" ]; then
17
+ echo "FAIL: Internal SDK imports found:"
18
+ echo "$INTERNAL_IMPORTS"
19
+ exit 1
20
+ fi
21
+ echo "OK: No internal SDK imports"
22
+
23
+ REQUIRED_TYPES=(MCQ TEXTFIELD NPS_SCALE CFM_MATRIX CSAT_MATRIX RATING_MATRIX SLIDER_MATRIX FILE_UPLOAD TEXT_AND_MEDIA HEATMAP RANK_ORDER)
24
+ QUESTION_FILE=""
25
+ for candidate in src/components/Question.tsx components/Question.tsx app/components/Question.tsx; do
26
+ if [ -f "$candidate" ]; then
27
+ QUESTION_FILE="$candidate"
28
+ break
29
+ fi
30
+ done
31
+
32
+ if [ -z "$QUESTION_FILE" ]; then
33
+ echo "WARN: Question.tsx not found in expected paths — skip type coverage check"
34
+ else
35
+ echo "Checking dispatcher types in $QUESTION_FILE"
36
+ for t in "${REQUIRED_TYPES[@]}"; do
37
+ if ! grep -q "QUESTION_TYPE.${t}" "$QUESTION_FILE" && \
38
+ ! grep -q "type === '${t}'" "$QUESTION_FILE" && \
39
+ ! grep -q "type === \"${t}\"" "$QUESTION_FILE"; then
40
+ echo "FAIL: Missing dispatcher branch for type: ${t}"
41
+ exit 1
42
+ fi
43
+ done
44
+ if grep -q "subType" "$QUESTION_FILE"; then
45
+ echo "FAIL: Legacy subType routing found in Question.tsx — use flat question.type literals"
46
+ exit 1
47
+ fi
48
+ echo "OK: All 11 question.type branches present"
49
+
50
+ if grep -q "RANK_ORDER" "$QUESTION_FILE" || grep -q "RankOrderScale" "$QUESTION_FILE"; then
51
+ RANK_FILE=""
52
+ for candidate in src/components/RankOrderScale.tsx components/RankOrderScale.tsx app/components/RankOrderScale.tsx; do
53
+ if [ -f "$candidate" ]; then RANK_FILE="$candidate"; break; fi
54
+ done
55
+ if [ -z "$RANK_FILE" ]; then
56
+ echo "FAIL: RankOrderScale.tsx required when RANK_ORDER is wired in Question.tsx"
57
+ exit 1
58
+ fi
59
+ echo "OK: RankOrderScale.tsx exists ($RANK_FILE)"
60
+ if ! grep -q "@dnd-kit/core" package.json 2>/dev/null; then
61
+ echo "FAIL: @dnd-kit/core missing from package.json (required for RANK_ORDER)"
62
+ exit 1
63
+ fi
64
+ echo "OK: @dnd-kit in package.json"
65
+ if ! grep -q "getVisibleRankOrderOptionsForAnswers" "$QUESTION_FILE"; then
66
+ echo "WARN: getVisibleRankOrderOptionsForAnswers not found in Question.tsx"
67
+ fi
68
+ fi
69
+
70
+ if grep -q "FILE_UPLOAD" "$QUESTION_FILE" || grep -q "FileUploadScale" "$QUESTION_FILE"; then
71
+ UPLOAD_FILE=""
72
+ for candidate in src/components/FileUploadScale.tsx components/FileUploadScale.tsx app/components/FileUploadScale.tsx; do
73
+ if [ -f "$candidate" ]; then UPLOAD_FILE="$candidate"; break; fi
74
+ done
75
+ if [ -z "$UPLOAD_FILE" ]; then
76
+ echo "FAIL: FileUploadScale.tsx required when FILE_UPLOAD is wired"
77
+ exit 1
78
+ fi
79
+ echo "OK: FileUploadScale.tsx exists ($UPLOAD_FILE)"
80
+ fi
81
+ fi
82
+
83
+ echo "Running npm run build..."
84
+ npm run build
85
+ echo "=== All checks passed ==="
@@ -1,40 +0,0 @@
1
- # CSAT & Rating Architectural Blueprints
2
-
3
- > **UI spec (authoritative):** `03-ui-specs/04-csat.md`
4
- > **Reference implementation:** `apps/client/src/components/CsatScale.tsx`
5
- > **Target Components**: `CsatScale`
6
- > **Handles**: `type: 'csat'`, `'rating'`, `'rating_scale'`, CSAT matrices.
7
-
8
- ## Core Responsibility
9
- Render sentiment arrays (Faces, Stars, Numbers) ensuring that active selections are unmistakably highlighted using bounding boxes or strong border accents, NEVER just by changing the SVG fill color.
10
-
11
- ## State Management & Mapping
12
- - **Emojis**: `buttonType === 'emoji'`
13
- - Map values using `getEmojiForIndex(index, totalLength)` from your icons utility.
14
- - **Stars**: `buttonType === 'star'`
15
- - Active stars (index <= selectedValue) get `CsatStarIcons.filled`.
16
- - **Numbers**: Default for rating scales.
17
- - Apply `option.color` to the background if provided by the SDK (NPS tracking).
18
-
19
- ## UX Bounding Box Rules (CRITICAL)
20
-
21
- The UI must exactly match the "Active Box" standard.
22
-
23
- ### For Emojis & Stars:
24
- - **Unselected**: `inline-flex p-2 border-2 border-transparent opacity-40 hover:opacity-100 hover:scale-110 transition-all cursor-pointer`.
25
- - **Selected**: The specific selected icon MUST be wrapped in a prominent square box with rounded corners.
26
- - **CSS**: `border-2 border-[#e20074] rounded-lg p-2 bg-pink-50 shadow-sm scale-110 opacity-100`.
27
-
28
- ### For Numbered Badges (Rating):
29
- - **Shape**: MUST be square with rounded corners (`rounded-lg w-12 h-12`). Do not use full circles.
30
- - **Selected**: `border-2 border-[#e20074] opacity-100 font-bold scale-105 shadow-md`.
31
- - **Unselected**: `opacity-30 border-transparent`.
32
-
33
- ## Bounding Boxes inside Matrices
34
- When emojis or stars are rendered inside `CsatMatrixScale`, the exact same bounding box logic applies. The table cell `<td>` should center the icon, and the icon wrapper `<div>` or `<button>` should toggle between `border-transparent` and `border-[#e20074]` based on the active state.
35
-
36
- ## Tooltips & Label Positions
37
- - **Tooltips**: Every single emoji, star, and badge MUST have `title={label}` applied so the user sees the semantic text string on hover.
38
- - **Labels (`minLabel`, `midLabel`, `maxLabel`)**:
39
- - Must be rendered absolutely below the track.
40
- - `midLabel` must use inline styles to calculate its exact centered position: `style={{ left: \`\${(midLabelIndex / (options.length - 1)) * 100}%\` }}`.
@@ -1,44 +0,0 @@
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
- ## NPS Traffic-Light Colors
25
- ```
26
- 0: #e2001a (red) 3: #f18b00 (orange) 6: #fcd900 (yellow)
27
- 1: #e4251b 4: #f7b200 7: #9fca00 (lime)
28
- 2: #ec610a 5: #fcd900 8: #6bb300 (green)
29
- 9: #339a00
30
- 10: #008000
31
- ```
32
-
33
- ## Mid-Label Positioning
34
- ```typescript
35
- // CSS left percentage for mid-label
36
- const left = (midLabelIndex / (options.length - 1)) * 100;
37
- // For 11-point scale: midLabelIndex defaults to 7.5
38
- ```
39
-
40
- ## Rendering Guidance
41
- - Display as horizontal row of numbered badge buttons
42
- - Each badge uses `option.color` as background
43
- - Selected badge: emphasized border/scale, all others dimmed
44
- - Labels positioned: minLabel (left), midLabel (calculated %), maxLabel (right)