@explorer02/cfm-survey-sdk 0.2.2 → 0.2.4

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 (113) hide show
  1. package/dist/cli/index.js +140 -33
  2. package/dist/cli/index.mjs +140 -33
  3. package/dist/index.js +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/package.json +8 -1
  6. package/postinstall.js +8 -5
  7. package/templates/AGENT.md +62 -17
  8. package/templates/deploy-wizard-to-s3.sh +52 -0
  9. package/templates/docs/00-integration/agent-execution-flow.md +281 -0
  10. package/templates/docs/00-integration/agent-operating-contract.md +260 -0
  11. package/templates/docs/00-integration/analytics-events-catalog.md +54 -0
  12. package/templates/docs/00-integration/apply-ui-config.md +365 -0
  13. package/templates/docs/00-integration/canonical-survey-fixtures.md +94 -0
  14. package/templates/docs/00-integration/client-integration-guide.md +10 -8
  15. package/templates/docs/00-integration/client-lib-folder.md +115 -0
  16. package/templates/docs/00-integration/component-checklist.md +90 -54
  17. package/templates/docs/00-integration/constraints.md +13 -5
  18. package/templates/docs/00-integration/custom-field-logic-and-navigation.md +56 -0
  19. package/templates/docs/00-integration/display-logic-and-navigation.md +16 -1
  20. package/templates/docs/00-integration/file-upload-aws.md +116 -0
  21. package/templates/docs/00-integration/logic-fields-catalog.md +105 -0
  22. package/templates/docs/00-integration/partial-save-and-recovery.md +24 -0
  23. package/templates/docs/00-integration/progress.md +4 -2
  24. package/templates/docs/00-integration/question-type-sdk-matrix.md +11 -9
  25. package/templates/docs/00-integration/setup.md +15 -3
  26. package/templates/docs/00-integration/skip-logic-and-navigation.md +1 -1
  27. package/templates/docs/00-integration/survey-lifecycle-analytics.md +1 -1
  28. package/templates/docs/00-integration/ui-customization-wizard.md +144 -0
  29. package/templates/docs/00-integration/useSurveySDK.md +35 -1
  30. package/templates/docs/00-integration/wizard-api.md +134 -0
  31. package/templates/docs/00-integration/wizard-config-handoff.md +246 -0
  32. package/templates/docs/00-integration/wizard-preview-build-guide.md +400 -0
  33. package/templates/docs/00-integration/wizard-question-type-styling.md +195 -0
  34. package/templates/docs/01-components/01-survey-page.md +18 -8
  35. package/templates/docs/01-components/02-question.md +2 -2
  36. package/templates/docs/01-components/03-rating-scale.md +12 -18
  37. package/templates/docs/01-components/05-csat-matrix-scale.md +18 -26
  38. package/templates/docs/01-components/06-likert-matrix-scale.md +19 -38
  39. package/templates/docs/01-components/07-slider-matrix-scale.md +48 -33
  40. package/templates/docs/01-components/08-file-upload-scale.md +15 -52
  41. package/templates/docs/01-components/09-custom-slider-track.md +15 -25
  42. package/templates/docs/01-components/10-header-footer.md +23 -4
  43. package/templates/docs/01-components/11-progress-bar.md +14 -6
  44. package/templates/docs/01-components/13-matrix-dropdown.md +28 -33
  45. package/templates/docs/01-components/17-heatmap-scale.md +4 -4
  46. package/templates/docs/01-components/18-rank-order-scale.md +24 -31
  47. package/templates/docs/01-components/19-survey-sticky-chrome.md +79 -0
  48. package/templates/docs/01-components/README.md +2 -2
  49. package/templates/docs/02-reference/question-types/10-slider-matrix.md +38 -26
  50. package/templates/docs/02-reference/question-types/11-file-upload.md +36 -21
  51. package/templates/docs/02-reference/question-types/README.md +11 -1
  52. package/templates/docs/02-reference/routing-table.md +1 -0
  53. package/templates/docs/02-reference/value-derivation.md +1 -1
  54. package/templates/docs/03-ui-specs/00-question-shell.md +10 -0
  55. package/templates/docs/03-ui-specs/01-rating.md +28 -2
  56. package/templates/docs/03-ui-specs/02-radio.md +94 -24
  57. package/templates/docs/03-ui-specs/03-text.md +12 -1
  58. package/templates/docs/03-ui-specs/04-csat.md +90 -26
  59. package/templates/docs/03-ui-specs/06-slider.md +3 -1
  60. package/templates/docs/03-ui-specs/07-matrix-cfm.md +74 -35
  61. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +50 -16
  62. package/templates/docs/03-ui-specs/09-slider-matrix.md +70 -26
  63. package/templates/docs/03-ui-specs/10-file-upload.md +52 -20
  64. package/templates/docs/03-ui-specs/11-text-and-media.md +4 -0
  65. package/templates/docs/03-ui-specs/12-survey-chrome.md +92 -5
  66. package/templates/docs/03-ui-specs/13-heatmap.md +21 -3
  67. package/templates/docs/03-ui-specs/14-rank-order.md +108 -28
  68. package/templates/docs/03-ui-specs/README.md +11 -7
  69. package/templates/docs/03-ui-specs/shared/custom-slider-track.md +28 -35
  70. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +1 -1
  71. package/templates/docs/MANIFEST.json +187 -6
  72. package/templates/docs/index.md +116 -116
  73. package/templates/docs/templates/CsatMatrixScale.tsx +637 -0
  74. package/templates/docs/templates/CustomSliderTrack.tsx +144 -0
  75. package/templates/docs/templates/FileUploadScale.tsx +262 -0
  76. package/templates/docs/templates/HeatmapScale.tsx +114 -0
  77. package/templates/docs/templates/LikertMatrixScale.tsx +414 -0
  78. package/templates/docs/templates/MatrixDropdown.tsx +216 -0
  79. package/templates/docs/templates/Question.tsx +3 -0
  80. package/templates/docs/templates/RankOrderScale.tsx +353 -0
  81. package/templates/docs/templates/RatingScale.tsx +129 -0
  82. package/templates/docs/templates/SliderMatrixScale.tsx +248 -0
  83. package/templates/docs/templates/SurveyStickyChrome.tsx +24 -0
  84. package/templates/docs/templates/customFieldValues.ts +36 -0
  85. package/templates/docs/templates/implementation_plan.md +75 -8
  86. package/templates/docs/templates/labelStyles.ts +33 -0
  87. package/templates/docs/templates/selectionStyles.ts +100 -0
  88. package/templates/docs/templates/survey-inventory.schema.json +28 -23
  89. package/templates/docs/templates/surveyUiIcons.tsx +63 -0
  90. package/templates/docs/templates/surveyUiScaleUtils.ts +51 -0
  91. package/templates/docs/templates/verify-agent-build.sh +151 -0
  92. package/templates/preview-harness/preview-bridge.inline.js +201 -0
  93. package/templates/preview-harness/previewPages.js +108 -0
  94. package/templates/preview-harness/previewPages.ts +298 -0
  95. package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +67 -0
  96. package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +122 -0
  97. package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +75 -0
  98. package/templates/preview-harness/vite-app/src/fixtures/questions.ts +311 -0
  99. package/templates/preview-harness/vite-app/src/globals.css +16 -0
  100. package/templates/preview-harness/vite-app/src/mount.tsx +9 -0
  101. package/templates/preview-harness/vite-app/src/preview-live-overrides.css +101 -0
  102. package/templates/preview-harness/vite-app/stubs/next-dynamic.tsx +25 -0
  103. package/templates/preview-harness/vite-app/stubs/next-image.tsx +29 -0
  104. package/templates/previewBridge.ts +153 -0
  105. package/templates/survey-theme.css +295 -0
  106. package/templates/survey-ui-config.schema.json +213 -0
  107. package/templates/wizard-dist/assets/PreviewMock-DgHfrVeb.js +1 -0
  108. package/templates/wizard-dist/assets/TypePanel-CFVC3Ptn.js +1 -0
  109. package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
  110. package/templates/wizard-dist/assets/index-DYK3X1e5.js +34 -0
  111. package/templates/wizard-dist/assets/vendor-BwkXDkd3.js +17 -0
  112. package/templates/wizard-dist/index.html +20 -0
  113. package/templates/wizard.html +13 -1129
@@ -0,0 +1,116 @@
1
+ # File Upload — Presigned S3 Flow
2
+
3
+ > Required for **submittable** file-upload answers. Raw `File[]` in `state.answers` is **dropped** at submit — SDK expects `UploadedFileAnswer[]` with `id` and `mediaUrl`.
4
+
5
+ Related: [`03-ui-specs/10-file-upload.md`](../03-ui-specs/10-file-upload.md) · [`02-reference/question-types/11-file-upload.md`](../02-reference/question-types/11-file-upload.md)
6
+
7
+ ---
8
+
9
+ ## Answer contract
10
+
11
+ ```typescript
12
+ type UploadedFileAnswer = {
13
+ id: string;
14
+ mediaUrl: string;
15
+ name?: string;
16
+ mimeType?: string;
17
+ size?: number;
18
+ };
19
+
20
+ onAction({
21
+ type: 'CHANGE',
22
+ payload: { questionId, answerValue: UploadedFileAnswer[] },
23
+ });
24
+ ```
25
+
26
+ Submit formatter maps to `quesIdVsAttachmentDetails[questionId]`.
27
+
28
+ ---
29
+
30
+ ## Upload API (EC2 reference — Day 22)
31
+
32
+ **Base URL:** configure via env (example: `http://43.204.26.213:3000` or project-specific upload service).
33
+
34
+ ### Step 1 — Request presigned URL
35
+
36
+ ```http
37
+ POST /get-upload-url
38
+ Content-Type: application/json
39
+
40
+ {
41
+ "fileName": "report.pdf",
42
+ "contentType": "application/pdf",
43
+ "questionId": "<question.id>"
44
+ }
45
+ ```
46
+
47
+ **Response:**
48
+
49
+ ```json
50
+ {
51
+ "uploadUrl": "https://s3.amazonaws.com/...",
52
+ "fileId": "generated-id",
53
+ "mediaUrl": "https://cdn.../path/to/file"
54
+ }
55
+ ```
56
+
57
+ ### Step 2 — PUT file to S3
58
+
59
+ ```http
60
+ PUT {uploadUrl}
61
+ Content-Type: application/pdf
62
+ Body: <file binary>
63
+ ```
64
+
65
+ ### Step 3 — Update SDK state
66
+
67
+ Append to answer array and call `onAction(CHANGE)` with `{ id: fileId, mediaUrl, name, mimeType, size }`.
68
+
69
+ ---
70
+
71
+ ## Client implementation sketch
72
+
73
+ ```typescript
74
+ async function uploadFile(file: File, questionId: string): Promise<UploadedFileAnswer> {
75
+ const res = await fetch(`${UPLOAD_API_BASE}/get-upload-url`, {
76
+ method: 'POST',
77
+ headers: { 'Content-Type': 'application/json' },
78
+ body: JSON.stringify({
79
+ fileName: file.name,
80
+ contentType: file.type,
81
+ questionId,
82
+ }),
83
+ });
84
+ const { uploadUrl, fileId, mediaUrl } = await res.json();
85
+ await fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } });
86
+ return { id: fileId, mediaUrl, name: file.name, mimeType: file.type, size: file.size };
87
+ }
88
+ ```
89
+
90
+ Wire `UPLOAD_API_BASE` from env — **never** embed AWS credentials in client code.
91
+
92
+ ---
93
+
94
+ ## UI states
95
+
96
+ | State | UX |
97
+ |-------|-----|
98
+ | Uploading | Spinner on file row; disable dropzone |
99
+ | Success | Show file in list; call `onAction(CHANGE)` |
100
+ | Failure | Red banner; keep local file uncommitted |
101
+ | Remove | Update array; `undefined` when empty |
102
+
103
+ ---
104
+
105
+ ## Partial save
106
+
107
+ Only call `onAction(CHANGE)` after upload completes — partial save should not POST raw `File` blobs.
108
+
109
+ ---
110
+
111
+ ## Agent checklist
112
+
113
+ - [ ] Local validation before upload (format, count, size)
114
+ - [ ] Presigned PUT wired per file
115
+ - [ ] Final answer is `UploadedFileAnswer[]` with `id` + `mediaUrl`
116
+ - [ ] Submit body includes attachments (verify in network tab)
@@ -0,0 +1,105 @@
1
+ # Logic Fields Catalog
2
+
3
+ > Central reference for skip, display, answer, variant, and end-page logic fields by question type. Agents do **not** implement evaluators — wire UI + hook; SDK evaluates conditions.
4
+
5
+ Cross-links: [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md) · [`display-logic-and-navigation.md`](display-logic-and-navigation.md) · [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md) · [`end-page-logic.md`](end-page-logic.md)
6
+
7
+ ---
8
+
9
+ ## MCQ
10
+
11
+ | Field | Used in | Operators | Notes |
12
+ |-------|---------|-----------|-------|
13
+ | `SELECTED_OPTIONS` / option id | skip, display, end | contains, does not contain, equals | Single or multi option ids |
14
+ | `NUM_OF_SELECTED_OPTIONS` | skip, display, end | `EQUALS`, `GT`, `LT`, etc. | Count of selected option ids; zero = missing for EXISTS |
15
+ | Option-level answer logic | answer (UI) | same engine | `getVisibleMcqOptionsForAnswers` |
16
+
17
+ ---
18
+
19
+ ## TEXTFIELD / TEXT_AND_MEDIA
20
+
21
+ | Field | Used in | Operators |
22
+ |-------|---------|-----------|
23
+ | `TEXT` / `CASE_SENSITIVE_TEXT` | skip, display, end | empty, contains, equals, etc. |
24
+ | TEXT_AND_MEDIA | — | no answer stored |
25
+
26
+ ---
27
+
28
+ ## NPS_SCALE
29
+
30
+ | Field | Used in | Operators |
31
+ |-------|---------|-----------|
32
+ | `TEN_POINT_SCALE` / scale value | skip, display, end | numeric compare `<`, `>`, `==`, `!=` |
33
+ | Scale point answer logic | answer (UI) | `getVisibleScaleColumnsForAnswers` |
34
+
35
+ ---
36
+
37
+ ## CFM_MATRIX / CSAT_MATRIX / RATING_MATRIX
38
+
39
+ | Field | Used in | Operators |
40
+ |-------|---------|-----------|
41
+ | Per-row composite keys | skip, display | per statement row + column id |
42
+ | Matrix cell values | skip, display | numeric or column id match |
43
+ | Row/column answer logic | answer (UI) | `getVisibleMatrixItemsForAnswers` |
44
+
45
+ Store answers as column **`id`** via `matrixColumnStoredValue`.
46
+
47
+ ---
48
+
49
+ ## SLIDER_MATRIX
50
+
51
+ | Field | Used in | Operators |
52
+ |-------|---------|-----------|
53
+ | Row statement values | skip, display | numeric compare |
54
+ | Row answer logic | answer (UI) | `getVisibleStatementRowsForAnswers` |
55
+
56
+ ---
57
+
58
+ ## FILE_UPLOAD
59
+
60
+ | Field | Used in | Operators |
61
+ |-------|---------|-----------|
62
+ | Presence only | skip, display | `EXISTS`, `MISSING` |
63
+ | Evaluation | on page entry | no discrete value-change events |
64
+
65
+ ---
66
+
67
+ ## HEATMAP
68
+
69
+ | Field | Used in | Operators |
70
+ |-------|---------|-----------|
71
+ | `HEATMAP` | skip, display | spot count, presence |
72
+ | `HEATMAP_REGION` | skip, display | region label match |
73
+
74
+ Regions are **logic-only** — not rendered on respondent UI.
75
+
76
+ ---
77
+
78
+ ## RANK_ORDER
79
+
80
+ | Field | Used in | Operators |
81
+ |-------|---------|-----------|
82
+ | `RANK_ORDER` (per option) | skip, display, answer | ranked as (`EQUALS`), ranked more/less than (`GT`/`LT`), ranked above/below (`BEFORE`/`AFTER`) |
83
+ | Whole question | skip, display | `EXISTS`, `MISSING` |
84
+
85
+ See [`02-reference/question-types/13-rank-order.md`](../02-reference/question-types/13-rank-order.md).
86
+
87
+ ---
88
+
89
+ ## CUSTOM_FIELD (all types)
90
+
91
+ | Field | Used in | Value shapes |
92
+ |-------|---------|--------------|
93
+ | `_c_*` entity ids | skip, display, end, answer | TEXT, NUMBER, PICKLIST, DATETIME, multiselect |
94
+
95
+ Pass stable `customFieldValues` to hook **and** visibility helpers. See [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md).
96
+
97
+ ---
98
+
99
+ ## Client wiring summary
100
+
101
+ | Logic kind | Client action |
102
+ |------------|---------------|
103
+ | Skip / display / end / variants | Render `state.currentQuestions`; no client evaluators |
104
+ | Answer logic | `getVisible*ForAnswers(..., customFieldValues?)` before render |
105
+ | Custom fields | Two wiring points: hook options + Question visibility helpers |
@@ -63,6 +63,30 @@ Do not block navigation (NEXT/PREVIOUS) on partial save failure unless product e
63
63
 
64
64
  ---
65
65
 
66
+ ## Per-type debounce guidance
67
+
68
+ | Question type | Client UI note |
69
+ |---------------|----------------|
70
+ | TEXTFIELD (long) | Debounce `onAction(CHANGE)` 300–500 ms to reduce partial-save churn |
71
+ | SLIDER_MATRIX / graphics | Debounce slider `onChange` or rely on SDK debounce |
72
+ | FILE_UPLOAD | Call `CHANGE` only **after** presigned upload completes — see [`file-upload-aws.md`](file-upload-aws.md) |
73
+ | MCQ / matrix | Immediate `CHANGE` on selection is fine |
74
+ | TEXT_AND_MEDIA | No CHANGE — excluded from partial save payload |
75
+
76
+ ---
77
+
78
+ ## Recovery (honest scope)
79
+
80
+ Reload behavior depends on **backend** + instance configuration — the SDK debounces POST to `responseV2` with `surveySubmitted: false` but does not implement client-side resume UI. If product requires resume:
81
+
82
+ - Enable `savePartialResponse`
83
+ - Optionally show `partialSaveResults` status
84
+ - Confirm with backend team whether refetch restores `state.answers`
85
+
86
+ Path-change sanitization (skip/display) clears off-path answers before partial save — see [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md).
87
+
88
+ ---
89
+
66
90
  ## Verify checklist
67
91
 
68
92
  - [ ] `savePartialResponse` only set when required
@@ -52,10 +52,12 @@ Survey: 4 questions on active path (Q1–Q4 sequential).
52
52
 
53
53
  ## UI wiring
54
54
 
55
+ On the **content** phase, render progress inside sticky chrome so it stays visible when scrolling to the last question on a page:
56
+
55
57
  ```tsx
56
- <ProgressBar progressPercentage={state.progressPercentage} />
58
+ <SurveyStickyChrome progressPercentage={state.progressPercentage} />
57
59
  ```
58
60
 
59
- See [`01-components/11-progress-bar.md`](../01-components/11-progress-bar.md) and [`03-ui-specs/12-survey-chrome.md`](../03-ui-specs/12-survey-chrome.md).
61
+ See [`01-components/19-survey-sticky-chrome.md`](../01-components/19-survey-sticky-chrome.md), [`01-components/11-progress-bar.md`](../01-components/11-progress-bar.md), and [`03-ui-specs/12-survey-chrome.md`](../03-ui-specs/12-survey-chrome.md).
60
62
 
61
63
  See also: [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md).
@@ -38,7 +38,9 @@ Legend: **opt** = option-level; **row+col** = statement rows and scale columns;
38
38
  | **Value derivation** | Store `option.id`; SDK maps to API value at submit |
39
39
  | **Performance notes** | Filter options once per render via helper; avoid re-fetch on option click |
40
40
  | **Docs to read** | [`02-question.md`](../01-components/02-question.md) · [`question-types/README.md#mcq`](../02-reference/question-types/README.md#mcq) · [`02-radio.md`](../03-ui-specs/02-radio.md) |
41
- | **Known limitations** | Multi-select array shape is client responsibility; verify `selectionMode` from survey config |
41
+ | **Default answers** | SDK `applyMcqDefaults` seeds `state.answers` from `defaultOptionIds` on load — client binds `selectedValue` only |
42
+ | **Answer conditions** | SDK validates `minSelections` / `maxSelections` on NEXT; client blocks add at max + shows `validationError` |
43
+ | **Known limitations** | Multi-select stores `string[]`; use `selectionMode` not `isMultiSelect` |
42
44
 
43
45
  ---
44
46
 
@@ -160,18 +162,18 @@ Legend: **opt** = option-level; **row+col** = statement rows and scale columns;
160
162
 
161
163
  | Column | Value |
162
164
  |--------|-------|
163
- | **SDK type + answer shape** | `FileUploadQuestion`; answer `UploadedFile[]` |
165
+ | **SDK type + answer shape** | `FileUploadQuestion`; answer `UploadedFileAnswer[]` (`{ id, mediaUrl, name?, mimeType?, size? }`) |
164
166
  | **Component file(s)** | `FileUploadScale.tsx` |
165
167
  | **Dispatcher branch** | `QUESTION_TYPE.FILE_UPLOAD` / `'FILE_UPLOAD'` |
166
168
  | **Visibility helper** | None |
167
169
  | **Answer logic scope** | None |
168
170
  | **Skip / display / variants / numbering** | SDK auto |
169
171
  | **Custom field logic** | Skip/display only |
170
- | **`onAction` pattern** | `{ type: 'CHANGE', payload: { questionId, answerValue: UploadedFile[] } }` after upload completes |
171
- | **Value derivation** | SDK submit maps file metadata to API |
172
- | **Performance notes** | Upload to presigned URL before CHANGE; show progress UI |
173
- | **Docs to read** | [`08-file-upload-scale.md`](../01-components/08-file-upload-scale.md) · [`11-file-upload.md`](../02-reference/question-types/11-file-upload.md) · [`10-file-upload.md`](../03-ui-specs/10-file-upload.md) |
174
- | **Known limitations** | Blocking question validate file count/size client-side per config |
172
+ | **`onAction` pattern** | `{ type: 'CHANGE', payload: { questionId, answerValue: UploadedFileAnswer[] } }` **after** presigned upload completes |
173
+ | **Value derivation** | SDK submit maps to `quesIdVsAttachmentDetails` raw `File[]` is **not submitted** |
174
+ | **Performance notes** | Local validate → POST presigned URL PUT file → then CHANGE; show progress UI |
175
+ | **Docs to read** | [`file-upload-aws.md`](file-upload-aws.md) · [`08-file-upload-scale.md`](../01-components/08-file-upload-scale.md) · [`11-file-upload.md`](../02-reference/question-types/11-file-upload.md) · [`10-file-upload.md`](../03-ui-specs/10-file-upload.md) |
176
+ | **Known limitations** | Must wire upload API; see [`file-upload-aws.md`](file-upload-aws.md) |
175
177
 
176
178
  ---
177
179
 
@@ -198,14 +200,14 @@ Legend: **opt** = option-level; **row+col** = statement rows and scale columns;
198
200
 
199
201
  | Column | Value |
200
202
  |--------|-------|
201
- | **SDK type + answer shape** | `HeatmapQuestion`; answer `HeatmapAnswer` (`{ clicks: HeatmapClickPoint[] }`) |
203
+ | **SDK type + answer shape** | `HeatmapQuestion`; answer `HeatmapClickPoint[]` (array of `{ id, x, y }` normalized 0–1) |
202
204
  | **Component file(s)** | `HeatmapScale.tsx` + portable coord helper (see [`templates/heatmapCoords.ts`](../templates/heatmapCoords.ts)) |
203
205
  | **Dispatcher branch** | `QUESTION_TYPE.HEATMAP` / `'HEATMAP'` |
204
206
  | **Visibility helper** | None |
205
207
  | **Answer logic scope** | None |
206
208
  | **Skip / display / variants / numbering** | SDK auto |
207
209
  | **Custom field logic** | Skip/display only |
208
- | **`onAction` pattern** | `{ type: 'CHANGE', payload: { questionId, answerValue: HeatmapAnswer } }` on each click add/remove |
210
+ | **`onAction` pattern** | `{ type: 'CHANGE', payload: { questionId, answerValue: HeatmapClickPoint[] \| undefined } }` on each click add/remove |
209
211
  | **Value derivation** | Normalized 0–1 coordinates; respect `maxClicksAllowed` |
210
212
  | **Performance notes** | Normalize coords on image load; use `findSpotNearClick` for hit testing |
211
213
  | **Docs to read** | [`17-heatmap-scale.md`](../01-components/17-heatmap-scale.md) · [`12-heatmap.md`](../02-reference/question-types/12-heatmap.md) · [`13-heatmap.md`](../03-ui-specs/13-heatmap.md) |
@@ -132,21 +132,33 @@ else → ask client which React setup to use
132
132
 
133
133
  For **existing apps**, read `package.json` and project structure — do not scaffold from scratch unless the client asks.
134
134
 
135
+ ### Recommended `src/lib/` scaffold
136
+
137
+ ```
138
+ src/lib/
139
+ ├── customFieldValues.ts ← templates/customFieldValues.ts (when CUSTOM_FIELD logic)
140
+ ├── surveyUiScaleUtils.ts ← templates/surveyUiScaleUtils.ts
141
+ ├── surveyUiIcons.tsx ← templates/surveyUiIcons.tsx (optional co-locate)
142
+ └── heatmapCoords.ts ← templates/heatmapCoords.ts (HEATMAP only)
143
+ ```
144
+
145
+ See [`client-lib-folder.md`](client-lib-folder.md).
146
+
135
147
  ---
136
148
 
137
149
  ## Survey-type conditional dependencies
138
150
 
139
151
  After fetching the survey, install extra packages only when those types appear:
140
152
 
141
- ### RANK_ORDER — drag-and-drop mode
153
+ ### RANK_ORDER — dnd-kit (required when survey contains RANK_ORDER)
142
154
 
143
- When the survey contains `RANK_ORDER` with `interactionMode: 'dragAndDrop'` (or you implement drag UI):
155
+ Install whenever the survey inventory includes **`RANK_ORDER`** the portable template supports both dropdown and drag-and-drop modes:
144
156
 
145
157
  ```bash
146
158
  npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
147
159
  ```
148
160
 
149
- See [`18-rank-order-scale.md`](../01-components/18-rank-order-scale.md) and [`question-type-sdk-matrix.md#rank_order`](question-type-sdk-matrix.md#rank_order).
161
+ Copy [`templates/RankOrderScale.tsx`](../templates/RankOrderScale.tsx) before building. See [`18-rank-order-scale.md`](../01-components/18-rank-order-scale.md).
150
162
 
151
163
  ### HEATMAP — coordinate normalization
152
164
 
@@ -77,4 +77,4 @@ Event: `optional_question_skipped` with `questionId` and `pageIndex`.
77
77
  - Use `state.canGoBack` for the Back button (not `currentPageIndex === 0`).
78
78
  - Use `state.primaryButtonType` for Next vs Submit label; SDK maps `NEXT` → `SUBMIT` on the last page when appropriate.
79
79
 
80
- See also: [`progress.md`](progress.md), [`useSurveySDK.md`](useSurveySDK.md), [`display-logic-and-navigation.md`](display-logic-and-navigation.md), [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md).
80
+ See also: [`progress.md`](progress.md), [`useSurveySDK.md`](useSurveySDK.md), [`display-logic-and-navigation.md`](display-logic-and-navigation.md), [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md), [`logic-fields-catalog.md`](logic-fields-catalog.md).
@@ -79,7 +79,7 @@ useEffect(() => {
79
79
 
80
80
  ## Debug: `logic_session_summary`
81
81
 
82
- When `debug: true` is set in hook options, the SDK emits structured console events including lifecycle transitions. Cross-reference event names in [`useSurveySDK.md`](useSurveySDK.md) during integration testing.
82
+ When `debug: true` is set in hook options, the SDK emits structured console events including lifecycle transitions. Full event catalog: [`analytics-events-catalog.md`](analytics-events-catalog.md).
83
83
 
84
84
  ---
85
85
 
@@ -0,0 +1,144 @@
1
+ # UI Customization Wizard
2
+
3
+ > Optional **run** after Phase 6 verify. **Wizard-ready build** is mandatory in Phase 5 — see [`agent-execution-flow.md`](agent-execution-flow.md).
4
+ > **Wait + apply protocol:** [`wizard-config-handoff.md`](wizard-config-handoff.md).
5
+
6
+ **Before the client opts in:** agents must already have wired [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) artifacts so AWS wizard previews update live (`--cfm-*` CSS vars + `data-cfm-*` content hooks).
7
+
8
+ ## When to Use
9
+
10
+ After **Phase 6** (verify + `npm run dev`), ask the client:
11
+
12
+ > *"Your survey is running locally. Would you like to customize the UI through the web wizard?"*
13
+
14
+ - **Yes** → Phase 6b: run `npx cfm-sdk customize-ui` (AWS) or `customize` (local)
15
+ - **No** → proceed to Phase 7 deploy
16
+
17
+ ## AWS flow (production) — agent waits for client
18
+
19
+ ```bash
20
+ export CFM_INSTANCE_ID="..." CFM_DEPLOY_API_BASE="http://43.204.26.213:3000"
21
+ npx cfm-sdk customize-ui
22
+ ```
23
+
24
+ **Default = review mode.** The CLI:
25
+
26
+ 1. Resolves seed config from project (`survey-ui-config.json` → `survey-theme.css` → `Header.tsx` → defaults)
27
+ 2. Exports component preview pages (`export-previews`) — one HTML per preview key (`header`, `footer`, `MCQ_single`, …)
28
+ 3. Uploads preview HTML to S3 via EC2 API (`POST /wizard/session/start` → presigned PUTs → `upload/complete`)
29
+ 4. Prints the **deployed wizard link** and opens it for the agent — **agent must copy this link to the client**
30
+ 5. **Polls until client submits** (`GET /wizard/config/:surveyId` → `status: ready`)
31
+ 6. Writes review artifacts — **does not auto-apply to codebase**
32
+
33
+ ### Client wizard link (after S3 upload)
34
+
35
+ Once previews are on S3, the CLI prints:
36
+
37
+ ```
38
+ ✅ Previews uploaded
39
+ 🔗 Client wizard link — share with the client:
40
+ http://43.204.26.213:3000/wizard-app/index.html?session=<uuid>&secret=<hex>
41
+ ```
42
+
43
+ | Who | Uses |
44
+ |-----|------|
45
+ | **Client** | Opens the **deployed EC2 wizard link** only — customizes branding, colors, question-type tokens; previews update live from S3-backed component pages |
46
+ | **Agent** | Keeps `customize-ui` running (polls API); does **not** ask client to use localhost |
47
+
48
+ **URL shape:** `{CFM_DEPLOY_API_BASE}/wizard-app/index.html?session={sessionId}&secret={sessionSecret}`
49
+
50
+ Default production API: `http://43.204.26.213:3000` (see `MANIFEST.json` `apiBaseDefault`).
51
+
52
+ **Agent script (after CLI prints the link):**
53
+
54
+ > *"I've uploaded your survey component previews. Open this link to customize the UI: [paste wizard URL]. When you're done, click **Review & Build** on the last step. I'll wait here until your changes are saved."*
55
+
56
+ **On success, CLI prints:**
57
+ ```
58
+ ✅ Configuration ready
59
+ Final: survey-ui-config.final.json
60
+ Diff: survey-ui-config.diff.md
61
+ Apply when ready: npx cfm-sdk confirm-ui-config
62
+ ```
63
+
64
+ **Agent must:**
65
+ - [ ] Keep terminal open until CLI exits 0
66
+ - [ ] **Copy the deployed wizard link from CLI output and send it to the client** (after previews upload to S3)
67
+ - [ ] Tell client: *"Please complete the wizard and click Review & Build."*
68
+ - [ ] Read `survey-ui-config.diff.md` before Phase 6c
69
+ - [ ] Apply codebase per [`apply-ui-config.md`](apply-ui-config.md) from **`.final.json`**
70
+ - [ ] Run `npx cfm-sdk confirm-ui-config` after codebase apply
71
+
72
+ See [`wizard-config-handoff.md`](wizard-config-handoff.md) for full handoff checklist.
73
+
74
+ ### Legacy immediate write
75
+
76
+ ```bash
77
+ npx cfm-sdk customize-ui --apply
78
+ ```
79
+
80
+ Writes `survey-ui-config.json` + logo immediately (no diff review). Phase 6c apply still required.
81
+
82
+ ### Detached session
83
+
84
+ ```bash
85
+ npx cfm-sdk customize-ui --no-wait
86
+ # Later, when client done:
87
+ export CFM_WIZARD_SESSION_ID="..."
88
+ npx cfm-sdk fetch-ui-config
89
+ ```
90
+
91
+ ## Local flow (development)
92
+
93
+ ```bash
94
+ npx cfm-sdk customize
95
+ ```
96
+
97
+ Opens browser at `http://localhost:4200`. **Blocks** until client saves → writes `survey-ui-config.json` directly. Then Phase 6c apply from that file (no `confirm-ui-config`).
98
+
99
+ ## Seed config
100
+
101
+ | Priority | Source |
102
+ |----------|--------|
103
+ | 1 | `./survey-ui-config.json` (draft) |
104
+ | 2 | `src/styles/survey-theme.css` (`--cfm-*` vars) |
105
+ | 3 | `implementation_plan.md` branding hints |
106
+ | 4 | `Header.tsx` — `data-cfm-logo` / `data-cfm-logo-text` |
107
+ | 5 | SDK defaults |
108
+
109
+ Debug: `npx cfm-sdk resolve-ui-config --write`
110
+
111
+ ## Wizard Steps
112
+
113
+ | Step | Controls |
114
+ |------|----------|
115
+ | Identity | Survey title, company name, tab title, thank-you message |
116
+ | Logo | Upload → S3 (AWS) or `public/` (local) |
117
+ | Header / Footer | Logo box, company, links, copyright, colors |
118
+ | Theme & Colors | Presets, palette, question card (cell selected + focus ring) |
119
+ | Layout & Chrome | Header/footer toggles, nav, width, font |
120
+ | Question Types | Per-type tokens + format preview tabs |
121
+ | Testing & Tokens | Placeholders, custom fields, debug |
122
+ | Deploy | AWS or Vercel target |
123
+ | Review & Build | **Client submits** — triggers CLI poll success |
124
+
125
+ ## Output artifacts
126
+
127
+ | File | When |
128
+ |------|------|
129
+ | `survey-ui-config.final.json` | AWS — client's submitted config (apply source) |
130
+ | `survey-ui-config.diff.md` | AWS — every changed path (audit before apply) |
131
+ | `survey-ui-config.json` | Active config (seed until `confirm-ui-config`; or final for local/`--apply`) |
132
+
133
+ Apply tokens per [`apply-ui-config.md`](apply-ui-config.md).
134
+
135
+ ## Agent Checklist
136
+
137
+ - [ ] Phase 5: components wired per [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md)
138
+ - [ ] Client opted in after localhost preview (INPUT 3)
139
+ - [ ] Ran `customize-ui` or `customize` and **waited** for CLI exit 0
140
+ - [ ] Read `survey-ui-config.diff.md` (AWS review mode)
141
+ - [ ] Phase 6c: applied **all** diff paths per [`apply-ui-config.md`](apply-ui-config.md)
142
+ - [ ] AWS: ran `confirm-ui-config` after codebase apply
143
+ - [ ] Re-ran verify script + build
144
+ - [ ] Did not modify SDK integration (`onAction`, dispatch logic)
@@ -88,7 +88,41 @@ Missing ids in the map are treated as empty for logic evaluation.
88
88
 
89
89
  When the survey uses custom-field logic, also pass the same map into `Question.tsx` visibility helpers — see [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md).
90
90
 
91
- ## Return Shape
91
+ ---
92
+
93
+ ## Custom field values (required when survey uses CRM logic)
94
+
95
+ Custom field values are **not** survey UI inputs — they come from CRM/transaction context (host app API, JWT claims, etc.) and enable SDK evaluation of rules where `entityType: 'CUSTOM_FIELD'`.
96
+
97
+ | Situation | Action |
98
+ |-----------|--------|
99
+ | No `_c_…` ids in fetched survey logic | `customFieldValues: {}` or omit — empty map is fine |
100
+ | Any skip/display/answer/end rule references `CUSTOM_FIELD` | **Required** — copy [`templates/customFieldValues.ts`](../templates/customFieldValues.ts) → `src/lib/customFieldValues.ts` |
101
+
102
+ ### Import and wire (two places)
103
+
104
+ ```typescript
105
+ import { CUSTOM_FIELD_VALUES } from '@/lib/customFieldValues';
106
+
107
+ // 1. Hook options (skip, display, progress, validation, end-page logic)
108
+ const surveyOptions = useMemo(
109
+ () => ({
110
+ instanceId,
111
+ placeholders: SURVEY_PLACEHOLDERS,
112
+ customFieldValues: CUSTOM_FIELD_VALUES,
113
+ }),
114
+ [instanceId, selectedLanguage]
115
+ );
116
+
117
+ // 2. Question prop (answer-logic UI — same map)
118
+ <Question customFieldValues={CUSTOM_FIELD_VALUES} ... />
119
+ ```
120
+
121
+ **Both wiring points are mandatory** when custom-field logic exists. Hook-only wiring breaks answer-logic UI; Question-only wiring breaks skip/display/progress.
122
+
123
+ Discovery steps: [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md) · lib layout: [`client-lib-folder.md`](client-lib-folder.md).
124
+
125
+ ---
92
126
 
93
127
  | Namespace | Field | Type | Purpose |
94
128
  |-----------|-------|------|---------|