@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.
- package/dist/cli/index.js +140 -33
- package/dist/cli/index.mjs +140 -33
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +8 -1
- package/postinstall.js +8 -5
- package/templates/AGENT.md +62 -17
- package/templates/deploy-wizard-to-s3.sh +52 -0
- package/templates/docs/00-integration/agent-execution-flow.md +281 -0
- package/templates/docs/00-integration/agent-operating-contract.md +260 -0
- package/templates/docs/00-integration/analytics-events-catalog.md +54 -0
- package/templates/docs/00-integration/apply-ui-config.md +365 -0
- package/templates/docs/00-integration/canonical-survey-fixtures.md +94 -0
- package/templates/docs/00-integration/client-integration-guide.md +10 -8
- package/templates/docs/00-integration/client-lib-folder.md +115 -0
- package/templates/docs/00-integration/component-checklist.md +90 -54
- package/templates/docs/00-integration/constraints.md +13 -5
- package/templates/docs/00-integration/custom-field-logic-and-navigation.md +56 -0
- package/templates/docs/00-integration/display-logic-and-navigation.md +16 -1
- package/templates/docs/00-integration/file-upload-aws.md +116 -0
- package/templates/docs/00-integration/logic-fields-catalog.md +105 -0
- package/templates/docs/00-integration/partial-save-and-recovery.md +24 -0
- package/templates/docs/00-integration/progress.md +4 -2
- package/templates/docs/00-integration/question-type-sdk-matrix.md +11 -9
- package/templates/docs/00-integration/setup.md +15 -3
- package/templates/docs/00-integration/skip-logic-and-navigation.md +1 -1
- package/templates/docs/00-integration/survey-lifecycle-analytics.md +1 -1
- package/templates/docs/00-integration/ui-customization-wizard.md +144 -0
- package/templates/docs/00-integration/useSurveySDK.md +35 -1
- package/templates/docs/00-integration/wizard-api.md +134 -0
- package/templates/docs/00-integration/wizard-config-handoff.md +246 -0
- package/templates/docs/00-integration/wizard-preview-build-guide.md +400 -0
- package/templates/docs/00-integration/wizard-question-type-styling.md +195 -0
- package/templates/docs/01-components/01-survey-page.md +18 -8
- package/templates/docs/01-components/02-question.md +2 -2
- package/templates/docs/01-components/03-rating-scale.md +12 -18
- package/templates/docs/01-components/05-csat-matrix-scale.md +18 -26
- package/templates/docs/01-components/06-likert-matrix-scale.md +19 -38
- package/templates/docs/01-components/07-slider-matrix-scale.md +48 -33
- package/templates/docs/01-components/08-file-upload-scale.md +15 -52
- package/templates/docs/01-components/09-custom-slider-track.md +15 -25
- package/templates/docs/01-components/10-header-footer.md +23 -4
- package/templates/docs/01-components/11-progress-bar.md +14 -6
- package/templates/docs/01-components/13-matrix-dropdown.md +28 -33
- package/templates/docs/01-components/17-heatmap-scale.md +4 -4
- package/templates/docs/01-components/18-rank-order-scale.md +24 -31
- package/templates/docs/01-components/19-survey-sticky-chrome.md +79 -0
- package/templates/docs/01-components/README.md +2 -2
- package/templates/docs/02-reference/question-types/10-slider-matrix.md +38 -26
- package/templates/docs/02-reference/question-types/11-file-upload.md +36 -21
- package/templates/docs/02-reference/question-types/README.md +11 -1
- package/templates/docs/02-reference/routing-table.md +1 -0
- package/templates/docs/02-reference/value-derivation.md +1 -1
- package/templates/docs/03-ui-specs/00-question-shell.md +10 -0
- package/templates/docs/03-ui-specs/01-rating.md +28 -2
- package/templates/docs/03-ui-specs/02-radio.md +94 -24
- package/templates/docs/03-ui-specs/03-text.md +12 -1
- package/templates/docs/03-ui-specs/04-csat.md +90 -26
- package/templates/docs/03-ui-specs/06-slider.md +3 -1
- package/templates/docs/03-ui-specs/07-matrix-cfm.md +74 -35
- package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +50 -16
- package/templates/docs/03-ui-specs/09-slider-matrix.md +70 -26
- package/templates/docs/03-ui-specs/10-file-upload.md +52 -20
- package/templates/docs/03-ui-specs/11-text-and-media.md +4 -0
- package/templates/docs/03-ui-specs/12-survey-chrome.md +92 -5
- package/templates/docs/03-ui-specs/13-heatmap.md +21 -3
- package/templates/docs/03-ui-specs/14-rank-order.md +108 -28
- package/templates/docs/03-ui-specs/README.md +11 -7
- package/templates/docs/03-ui-specs/shared/custom-slider-track.md +28 -35
- package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +1 -1
- package/templates/docs/MANIFEST.json +187 -6
- package/templates/docs/index.md +116 -116
- package/templates/docs/templates/CsatMatrixScale.tsx +637 -0
- package/templates/docs/templates/CustomSliderTrack.tsx +144 -0
- package/templates/docs/templates/FileUploadScale.tsx +262 -0
- package/templates/docs/templates/HeatmapScale.tsx +114 -0
- package/templates/docs/templates/LikertMatrixScale.tsx +414 -0
- package/templates/docs/templates/MatrixDropdown.tsx +216 -0
- package/templates/docs/templates/Question.tsx +3 -0
- package/templates/docs/templates/RankOrderScale.tsx +353 -0
- package/templates/docs/templates/RatingScale.tsx +129 -0
- package/templates/docs/templates/SliderMatrixScale.tsx +248 -0
- package/templates/docs/templates/SurveyStickyChrome.tsx +24 -0
- package/templates/docs/templates/customFieldValues.ts +36 -0
- package/templates/docs/templates/implementation_plan.md +75 -8
- package/templates/docs/templates/labelStyles.ts +33 -0
- package/templates/docs/templates/selectionStyles.ts +100 -0
- package/templates/docs/templates/survey-inventory.schema.json +28 -23
- package/templates/docs/templates/surveyUiIcons.tsx +63 -0
- package/templates/docs/templates/surveyUiScaleUtils.ts +51 -0
- package/templates/docs/templates/verify-agent-build.sh +151 -0
- package/templates/preview-harness/preview-bridge.inline.js +201 -0
- package/templates/preview-harness/previewPages.js +108 -0
- package/templates/preview-harness/previewPages.ts +298 -0
- package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +67 -0
- package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +122 -0
- package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +75 -0
- package/templates/preview-harness/vite-app/src/fixtures/questions.ts +311 -0
- package/templates/preview-harness/vite-app/src/globals.css +16 -0
- package/templates/preview-harness/vite-app/src/mount.tsx +9 -0
- package/templates/preview-harness/vite-app/src/preview-live-overrides.css +101 -0
- package/templates/preview-harness/vite-app/stubs/next-dynamic.tsx +25 -0
- package/templates/preview-harness/vite-app/stubs/next-image.tsx +29 -0
- package/templates/previewBridge.ts +153 -0
- package/templates/survey-theme.css +295 -0
- package/templates/survey-ui-config.schema.json +213 -0
- package/templates/wizard-dist/assets/PreviewMock-DgHfrVeb.js +1 -0
- package/templates/wizard-dist/assets/TypePanel-CFVC3Ptn.js +1 -0
- package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
- package/templates/wizard-dist/assets/index-DYK3X1e5.js +34 -0
- package/templates/wizard-dist/assets/vendor-BwkXDkd3.js +17 -0
- package/templates/wizard-dist/index.html +20 -0
- 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
|
-
<
|
|
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
|
-
| **
|
|
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 `
|
|
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:
|
|
171
|
-
| **Value derivation** | SDK submit maps
|
|
172
|
-
| **Performance notes** |
|
|
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** |
|
|
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 `
|
|
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:
|
|
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 —
|
|
153
|
+
### RANK_ORDER — dnd-kit (required when survey contains RANK_ORDER)
|
|
142
154
|
|
|
143
|
-
|
|
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)
|
|
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.
|
|
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
|
-
|
|
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
|
|-----------|-------|------|---------|
|