@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,94 @@
1
+ # Canonical Survey Fixtures
2
+
3
+ > Ground-truth UI expectations for integration-test surveys. When your fetched survey matches these patterns, **copy portable templates** from [`templates/`](../templates/) — do not rebuild matrix/slider UI from prose.
4
+
5
+ Related: [`component-checklist.md`](component-checklist.md) · [`question-type-sdk-matrix.md`](question-type-sdk-matrix.md)
6
+
7
+ ---
8
+
9
+ ## Template copy order (all matrix types)
10
+
11
+ 0. `customFieldValues.ts` → `src/lib/` when survey logic references `_c_…` ids
12
+ 1. `surveyUiScaleUtils.ts` + `surveyUiIcons.tsx`
13
+ 2. `CustomSliderTrack.tsx` + `MatrixDropdown.tsx`
14
+ 3. Scale component for each type in survey
15
+ 4. `Question.tsx` from template (includes `questionNumber`, matrix visibility helpers, `customFieldValues` prop)
16
+
17
+ ---
18
+
19
+ ## Page-by-page fixture map
20
+
21
+ | Page | Type | Key config | Expected UI | Copy |
22
+ |------|------|------------|-------------|------|
23
+ | 1 | `NPS_SCALE` | 11 options 0–10, `minLabel`/`maxLabel` | Traffic-light badges + anchor labels | `RatingScale.tsx` |
24
+ | 2 | `SLIDER_MATRIX` | 1 row, `ticks.count: 11`; skip uses `_c_67199096d294be011a2b4773` | Colored 0–10 header + gray row + track; CRM field in `customFieldValues.ts` | `SliderMatrixScale.tsx` + `customFieldValues.ts` |
25
+ | 3 | `FILE_UPLOAD` | `maxFileCount: 1` | Dropzone → presigned upload → `UploadedFileAnswer[]` | `FileUploadScale.tsx` |
26
+ | 4 | `MCQ` | `selectionMode: single`, `defaultOptionIds` | Radio; pre-select default | `Question.tsx` inline |
27
+ | 6 | `MCQ` | `minSelections`/`maxSelections` | Checkbox + validation message | `Question.tsx` inline |
28
+ | 7 | `CFM_MATRIX` | `statementLayout: bipolar`, `showColumnHeaders: false`, 3 anchor labels, 10 cols | 25% left stmt \| radios \| 25% right stmt | `LikertMatrixScale.tsx` |
29
+ | 9 | `CSAT_MATRIX` | `displayStyle: emoji`, 5 anchors, N/A | 180px stmt column + emoji row + anchors | `CsatMatrixScale.tsx` |
30
+ | 10 | `CFM_MATRIX` | `transposeTable: true`, `selectionMode: multiple` | Rows=Scale Items, cols=Statement Items, **checkboxes** | `LikertMatrixScale.tsx` |
31
+ | 11 | `RATING_MATRIX` | `displayStyle: graphics` | Per-row `CustomSliderTrack` (NOT SliderMatrixScale) | `CsatMatrixScale.tsx` |
32
+ | 12 | `SLIDER_MATRIX` | 2 rows + 2 anchor labels | Shared header + 2 row bands with visible statements | `SliderMatrixScale.tsx` |
33
+ | 14 | multi | star + slider + numbered rating | Three separate components; all labels visible | all three templates |
34
+
35
+ ---
36
+
37
+ ## Common agent failures
38
+
39
+ | Symptom | Cause | Fix |
40
+ |---------|-------|-----|
41
+ | Lone pink circle, no slider track | Built stub thumb instead of `SliderMatrixScale` | Copy template |
42
+ | Matrix with no row labels | Omitted 180px / 25% statement column | See ui-spec statement visibility |
43
+ | Matrix with no column labels | Ignored `scaleAnchorLabels` and `showColumnHeaders` | Render header row |
44
+ | CSAT shows radios instead of emojis | Skipped `displayStyle: emoji` branch | Copy `CsatMatrixScale` |
45
+ | RATING numbered shows native radios | Skipped `displayStyle: numbered` branch | 40×40 badges |
46
+ | Bipolar missing right text | Ignored `oppositeStatementText` | 25% right column |
47
+ | Transposed matrix wrong orientation | Ignored `transposeTable` | Swap gridRows/gridCols |
48
+ | Custom-field skip never fires | `customFieldValues` omitted from hook or wrong `_c_` id | Copy `customFieldValues.ts`; wire hook + Question |
49
+
50
+ ---
51
+
52
+ ## Custom field fixture (Page 2)
53
+
54
+ SLIDER_MATRIX skip when CRM field `_c_67199096d294be011a2b4773` `CASE_SENSITIVE_TEXT` equals `"abc"`:
55
+
56
+ ```typescript
57
+ // src/lib/customFieldValues.ts
58
+ export const CUSTOM_FIELD_VALUES = {
59
+ '_c_67199096d294be011a2b4773': 'abc',
60
+ };
61
+ ```
62
+
63
+ Import in `SurveyPage` and pass to `useSurveySDK` + `Question`. See [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md).
64
+
65
+ ## Statement text rules (all matrix types)
66
+
67
+ ```tsx
68
+ // Always render — never hide statementRows[].statementText
69
+ <span dangerouslySetInnerHTML={{ __html: row.statementText }} />
70
+ ```
71
+
72
+ For CFM bipolar, also render `row.oppositeStatementText` on the right.
73
+
74
+ For CFM transposed, row labels come from `scaleColumns[].label`; column headers from `statementRows[].statementText`.
75
+
76
+ ---
77
+
78
+ ## Label header rules
79
+
80
+ At least one header row MUST be visible when configured:
81
+
82
+ - `scaleAnchorLabels.length > 0` → anchor row (positioning formulas in ui-specs)
83
+ - `showColumnHeaders === true` → column label row from `scaleColumns[].label` or transposed `statementText`
84
+ - CSAT without anchors → column header row from `scaleColumns[].label`
85
+
86
+ ---
87
+
88
+ ## Agent checklist
89
+
90
+ - [ ] Read this doc when survey contains matrix or slider types
91
+ - [ ] Copied all required templates before Phase 5 BUILD
92
+ - [ ] Verified row labels + column/anchor labels render on every matrix question
93
+ - [ ] Verified slider has header badges + track (not orphan circle)
94
+ - [ ] `customFieldValues.ts` populated and wired to hook + Question when survey uses CRM logic
@@ -1,6 +1,6 @@
1
1
  # Client Integration Guide
2
2
 
3
- > **Start here** after Phase 2 (instanceId). Single map of everything the client must wire to integrate all SDK features correctly.
3
+ > **Start here** after Phase 2 (instanceId). Full pipeline order: [`agent-execution-flow.md`](agent-execution-flow.md).
4
4
 
5
5
  Import only from the package root (`@explorer02/cfm-survey-sdk` or `@repo/sdk`). Never from `/src/` paths.
6
6
 
@@ -34,7 +34,7 @@ flowchart TB
34
34
  | Dispatcher | `Question.tsx` | Route on `question.type`; filter options/rows via visibility helpers; placeholder text |
35
35
  | Scales | `*Scale.tsx` | Render one question type; store answers via `onSelect` callback |
36
36
 
37
- Reference implementation: `apps/client/src/components/SurveyPage.tsx`.
37
+ Reference implementation: portable templates in `docs/templates/` — not `apps/client` (monorepo snapshot only).
38
38
 
39
39
  ---
40
40
 
@@ -51,7 +51,7 @@ Reference implementation: `apps/client/src/components/SurveyPage.tsx`.
51
51
  | Question display variants | Render `question.questionText` from `currentQuestions` (no client variant logic) | Conditional variant text via same logic engine |
52
52
  | Answer logic UI | `getVisible*ForAnswers` + pass `customFieldValues` | Condition evaluation engine |
53
53
  | Custom fields | Stable `customFieldValues` → hook **and** `Question` | Skip/display/validation/progress evaluation |
54
- | Progress | Bind `state.progressPercentage` | Active-path denominator + earned credits |
54
+ | Progress | `SurveyStickyChrome` with `state.progressPercentage` | Sticky header + bar; active-path denominator + earned credits |
55
55
  | Question numbering | Render `question.questionNumber` from `currentQuestions` | Active-path ordinals (same denominator as progress); see [`question-numbering.md`](question-numbering.md) |
56
56
  | Placeholders | Stable `placeholders` ref + `language` option | Token replacement at map time |
57
57
  | Mark as started | `onAction(SCROLL)` once on first scroll | One-time API call (also fires on CHANGE) |
@@ -61,7 +61,7 @@ Reference implementation: `apps/client/src/components/SurveyPage.tsx`.
61
61
  | Per-type integration | Read [`question-type-sdk-matrix.md`](question-type-sdk-matrix.md) before building scales | Answer shapes, visibility helpers, performance notes per type |
62
62
  | Validation scroll | `id={question.id}` on question wrapper | Scroll-to-first-error on failed NEXT/SUBMIT |
63
63
 
64
- Deep dives: [`useSurveySDK.md`](useSurveySDK.md) · [`question-type-sdk-matrix.md`](question-type-sdk-matrix.md) · [`placeholders-and-tokens.md`](placeholders-and-tokens.md) · [`partial-save-and-recovery.md`](partial-save-and-recovery.md) · [`survey-lifecycle-analytics.md`](survey-lifecycle-analytics.md) · [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md) · [`display-logic-and-navigation.md`](display-logic-and-navigation.md) · [`question-display-variants.md`](question-display-variants.md) · [`question-numbering.md`](question-numbering.md) · [`end-page-logic.md`](end-page-logic.md) · [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md) · [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md) · [`progress.md`](progress.md) · [`constraints.md`](constraints.md)
64
+ Deep dives: [`useSurveySDK.md`](useSurveySDK.md) · [`question-type-sdk-matrix.md`](question-type-sdk-matrix.md) · [`placeholders-and-tokens.md`](placeholders-and-tokens.md) · [`partial-save-and-recovery.md`](partial-save-and-recovery.md) · [`survey-lifecycle-analytics.md`](survey-lifecycle-analytics.md) · [`analytics-events-catalog.md`](analytics-events-catalog.md) · [`logic-fields-catalog.md`](logic-fields-catalog.md) · [`file-upload-aws.md`](file-upload-aws.md) · [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md) · [`display-logic-and-navigation.md`](display-logic-and-navigation.md) · [`question-display-variants.md`](question-display-variants.md) · [`question-numbering.md`](question-numbering.md) · [`end-page-logic.md`](end-page-logic.md) · [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md) · [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md) · [`progress.md`](progress.md) · [`constraints.md`](constraints.md)
65
65
 
66
66
  ---
67
67
 
@@ -161,10 +161,11 @@ function SurveyContent({
161
161
  }, [state.currentQuestions, onAction]);
162
162
 
163
163
  return (
164
- <main>
165
- <ProgressBar progressPercentage={state.progressPercentage} />
164
+ <>
165
+ <SurveyStickyChrome progressPercentage={state.progressPercentage} />
166
166
 
167
- {state.currentQuestions.map(question => (
167
+ <main className="mx-auto w-full max-w-4xl flex-1 px-4 py-10">
168
+ {state.currentQuestions.map(question => (
168
169
  <Question
169
170
  key={question.id}
170
171
  question={question}
@@ -189,7 +190,8 @@ function SurveyContent({
189
190
  >
190
191
  {state.primaryButtonType === 'submit' ? 'Submit' : 'Next'}
191
192
  </button>
192
- </main>
193
+ </main>
194
+ </>
193
195
  );
194
196
  }
195
197
 
@@ -0,0 +1,115 @@
1
+ # Client Lib Folder — Portable Templates
2
+
3
+ > Maps the reference monorepo [`apps/client/src/lib/`](../../../../apps/client/src/lib/) layout to portable templates agents copy into their project. **Do not import from `apps/client/` in npm installs.**
4
+
5
+ Related: [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md) · [`component-checklist.md`](component-checklist.md) · [`constraints.md`](constraints.md)
6
+
7
+ ---
8
+
9
+ ## Reference → portable mapping
10
+
11
+ | Reference file (monorepo) | Portable template | Used by | Agent action |
12
+ |---------------------------|-------------------|---------|--------------|
13
+ | `lib/customFieldValues.ts` | [`templates/customFieldValues.ts`](../templates/customFieldValues.ts) | `SurveyPage` → `useSurveySDK` + `Question` | Copy to `src/lib/`; populate `_c_…` ids from fetched survey |
14
+ | `lib/surveyUi/getAccentColor.ts` + `scaleValues.ts` | [`templates/surveyUiScaleUtils.ts`](../templates/surveyUiScaleUtils.ts) | NPS colors, slider tick colors, matrix column helpers | Copy to `src/lib/` or co-locate with scale components |
15
+ | `lib/surveyUi/icons.tsx` | [`templates/surveyUiIcons.tsx`](../templates/surveyUiIcons.tsx) | CSAT emoji/star (`CsatMatrixScale`) | Copy; add `react-icons` to `package.json` |
16
+ | `lib/surveyUi/heatmapCoords.ts` | [`templates/heatmapCoords.ts`](../templates/heatmapCoords.ts) | `HeatmapScale` | Copy to `src/lib/heatmapCoords.ts` |
17
+ | — (wizard styling) | [`templates/selectionStyles.ts`](../templates/selectionStyles.ts) | All scale radio/numbered selections | Copy to `src/lib/surveyUi/selectionStyles.ts` |
18
+ | — (wizard styling) | [`templates/labelStyles.ts`](../templates/labelStyles.ts) | Matrix/slider label items | Copy to `src/lib/surveyUi/labelStyles.ts` |
19
+
20
+ Scale **components** live in `src/components/` — see [`component-checklist.md`](component-checklist.md). Lib files are shared utilities and CRM data, not React components.
21
+
22
+ ---
23
+
24
+ ## Recommended project layout
25
+
26
+ ```
27
+ src/
28
+ ├── lib/
29
+ │ ├── customFieldValues.ts ← CRM map for useSurveySDK (copy template)
30
+ │ ├── surveyUiScaleUtils.ts ← NPS/tick/matrix helpers
31
+ │ ├── surveyUiIcons.tsx ← emoji/star icons (optional co-locate)
32
+ │ ├── surveyUi/selectionStyles.ts ← wizard: focus ring + cell selected (copy template)
33
+ │ ├── surveyUi/labelStyles.ts ← wizard: label item colors (copy template)
34
+ │ └── heatmapCoords.ts ← when survey has HEATMAP
35
+ └── components/
36
+ ├── SurveyPage.tsx
37
+ ├── Question.tsx
38
+ ├── RatingScale.tsx
39
+ └── ...scale components from templates/
40
+ ```
41
+
42
+ Placeholders follow the same stable-ref rule — define **`SURVEY_PLACEHOLDERS`** outside the component (often in `SurveyPage.tsx` module scope or a separate `lib/placeholders.ts`).
43
+
44
+ ---
45
+
46
+ ## `customFieldValues.ts` — CRM input for SDK logic
47
+
48
+ Custom field values are **not** survey UI inputs. They come from CRM/transaction context (API payload, JWT claims, host app state) and are passed to the SDK so skip, display, progress, end-page, and answer logic can evaluate rules referencing `entityType: 'CUSTOM_FIELD'`.
49
+
50
+ ```typescript
51
+ import type { CustomFieldValueMap } from '@explorer02/cfm-survey-sdk';
52
+ import { CUSTOM_FIELD_VALUES } from '@/lib/customFieldValues';
53
+
54
+ const surveyOptions = useMemo(
55
+ () => ({
56
+ instanceId,
57
+ placeholders: SURVEY_PLACEHOLDERS,
58
+ customFieldValues: CUSTOM_FIELD_VALUES,
59
+ }),
60
+ [instanceId, selectedLanguage]
61
+ );
62
+ ```
63
+
64
+ Pass the **same map** to `Question`:
65
+
66
+ ```tsx
67
+ <Question customFieldValues={CUSTOM_FIELD_VALUES} ... />
68
+ ```
69
+
70
+ See [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md) for discovery, value shapes, and the two wiring points.
71
+
72
+ ---
73
+
74
+ ## `surveyUiScaleUtils.ts` — scale UI helpers
75
+
76
+ Exports used by portable scale templates:
77
+
78
+ | Export | Purpose |
79
+ |--------|---------|
80
+ | `getAccentColor(value)` | NPS 0–10 traffic-light badge colors |
81
+ | `tickColorFromIndex(index, total)` | SLIDER_MATRIX header tick badge colors |
82
+ | `matrixColumnStoredValue(col)` | Matrix answer storage — column `id` or `null` for N/A |
83
+ | `npsValueFromOption` / `starValueFromOption` | NPS badge numeric values |
84
+ | `nonNaScaleColumns` | Filter N/A column from scale columns |
85
+
86
+ Prefer SDK `matrixColumnStoredValue` when importing from package; template re-exports the same contract for co-located components.
87
+
88
+ ---
89
+
90
+ ## `surveyUiIcons.tsx` — CSAT emoji and star icons
91
+
92
+ - `getEmojiForIndex(index, totalColumns)` — CSAT emoji matrix cells
93
+ - `CsatStarIcons` — filled/empty star nodes for `displayStyle: 'star'`
94
+
95
+ Requires `react-icons` (`fa6`, `fa`).
96
+
97
+ ---
98
+
99
+ ## `heatmapCoords.ts` — normalized click coordinates
100
+
101
+ Used by `HeatmapScale`:
102
+
103
+ - `getNormalizedClickFromImage(event, imageElement)`
104
+ - `findSpotNearClick(clickX, clickY, spots, imageRect)`
105
+ - `createNewSpot(x, y)`
106
+
107
+ ---
108
+
109
+ ## Agent checklist
110
+
111
+ - [ ] Copied lib templates before Phase 5 BUILD — do not read `apps/client/src/lib` at runtime
112
+ - [ ] `customFieldValues.ts` created when survey logic references any `_c_…` custom field id
113
+ - [ ] `CUSTOM_FIELD_VALUES` defined **outside** React components (stable ref)
114
+ - [ ] Same map passed to `useSurveySDK` options **and** `Question` prop
115
+ - [ ] Scale utils/icons copied when survey contains NPS, matrix emoji/star, SLIDER_MATRIX, or HEATMAP
@@ -1,84 +1,120 @@
1
1
  # Required Component Files Checklist
2
2
 
3
- > **Blocking gate before Phase 5 (BUILD).** After fetching the survey with `instanceId`, list every `question.type` and ensure all required files exist.
3
+ > **Blocking gate before Phase 5 (BUILD).** Read [`agent-operating-contract.md`](agent-operating-contract.md) §2–§3 first do not invent files or paths from memory.
4
+ >
5
+ > **Wizard-ready build (Phase 5 — mandatory):** Every agent build wires live-preview contract per [`agent-execution-flow.md`](agent-execution-flow.md), [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md), and [`wizard-question-type-styling.md`](wizard-question-type-styling.md) — `var(--cfm-*)`, `data-cfm-*` hooks, `selectionStyles.ts`, `labelStyles.ts`, draft `survey-ui-config.json`. Running the wizard (Phase 6b) is optional; wizard-ready artifacts are not.
6
+ >
7
+ > Read [`canonical-survey-fixtures.md`](canonical-survey-fixtures.md) when matrix/slider labels are missing.
4
8
 
5
9
  ## Step 1: Inventory Question Types
6
10
 
7
11
  From `survey.pages`, collect unique `question.type` values — see `MANIFEST.json` `typeLiterals`:
8
12
 
9
- | questionId | type |
10
- |------------|------|
11
- | | |
13
+ | questionId | type | displayStyle / statementLayout / transposeTable |
14
+ |------------|------|--------------------------------------------------|
15
+ | | | |
12
16
 
13
- ## Step 2: Create Required Files
17
+ Note matrix variants: `displayStyle`, `statementLayout`, `transposeTable`, `selectionMode`.
14
18
 
15
- | If survey contains | Agent MUST create before `npm run build` | UI spec |
16
- |--------------------|------------------------------------------|---------|
17
- | `NPS_SCALE` | `RatingScale.tsx` | `03-ui-specs/01-rating.md` |
18
- | `CFM_MATRIX` | `LikertMatrixScale.tsx`, `MatrixDropdown.tsx` | `03-ui-specs/07-matrix-cfm.md` |
19
- | `CSAT_MATRIX` | `CsatMatrixScale.tsx`, `MatrixDropdown.tsx`, `CustomSliderTrack.tsx` | `03-ui-specs/04-csat.md` |
20
- | `RATING_MATRIX` | `CsatMatrixScale.tsx`, `MatrixDropdown.tsx`, `CustomSliderTrack.tsx` | `03-ui-specs/08-matrix-csat-rating.md` |
21
- | `SLIDER_MATRIX` | `SliderMatrixScale.tsx`, `CustomSliderTrack.tsx` | `03-ui-specs/09-slider-matrix.md` |
22
- | `FILE_UPLOAD` | `FileUploadScale.tsx` | `03-ui-specs/10-file-upload.md` |
23
- | `MCQ`, `TEXTFIELD`, `TEXT_AND_MEDIA` | Inline in `Question.tsx` | `03-ui-specs/02-radio.md`, `03-text.md`, `11-text-and-media.md` |
24
- | `RANK_ORDER` | `RankOrderScale.tsx` (+ `@dnd-kit/*`) | `03-ui-specs/14-rank-order.md` |
25
- | `HEATMAP` | `HeatmapScale.tsx`, `lib/heatmapCoords.ts` (copy from `templates/heatmapCoords.ts`) | `03-ui-specs/13-heatmap.md` |
19
+ ## Step 2: Copy Portable Templates (CRITICAL)
26
20
 
27
- **Survey chrome (when applicable):**
21
+ **Copy from [`templates/`](../templates/) before customizing branding.** Do not implement matrix/slider UI from scratch.
28
22
 
29
- | Component | When |
30
- |-----------|------|
31
- | `IntroPage.tsx` | `survey.introPage` |
32
- | `EndPage.tsx` | `survey.endPages` |
33
- | `PausedPage.tsx` | `survey.phaseFlags.paused` |
23
+ ### Copy order
34
24
 
35
- Always create **`Question.tsx`** from [`templates/Question.tsx`](../templates/Question.tsx).
25
+ 0. **`customFieldValues.ts`** `src/lib/` when any `_c_…` id in survey logic (see [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md))
26
+ 0b. **`selectionStyles.ts`**, **`labelStyles.ts`** → `src/lib/surveyUi/` (**required** — see [`wizard-question-type-styling.md`](wizard-question-type-styling.md))
27
+ 1. `surveyUiScaleUtils.ts` → `src/lib/` (or co-locate with components)
28
+ 2. `surveyUiIcons.tsx` → same folder as scale components
29
+ 3. `CustomSliderTrack.tsx`, `MatrixDropdown.tsx`, `heatmapCoords.ts`
30
+ 4. Scale components per survey types
31
+ 5. `Question.tsx` from template
36
32
 
37
- ## Flat Type Routing (CRITICAL)
33
+ | `templates/customFieldValues.ts` | `src/lib/customFieldValues.ts` | Any `CUSTOM_FIELD` in skip/display/answer/end logic |
34
+ | `templates/selectionStyles.ts` | `src/lib/surveyUi/selectionStyles.ts` | **Required** — radio/scale selection colors |
35
+ | `templates/labelStyles.ts` | `src/lib/surveyUi/labelStyles.ts` | **Required** — matrix/slider label items |
36
+ | `templates/survey-theme.css` | `src/styles/survey-theme.css` | **Required** — full `--cfm-*` defaults |
37
+ | `templates/previewBridge.ts` | `src/lib/previewBridge.ts` | **Required** — local `customize` live preview |
38
38
 
39
- Use `QUESTION_TYPE` **no `subType`**:
39
+ See [`client-lib-folder.md`](client-lib-folder.md) for full lib layout.
40
40
 
41
- ```tsx
42
- import { QUESTION_TYPE } from '@explorer02/cfm-survey-sdk';
41
+ | Portable template | Copy to | When survey contains |
42
+ |-------------------|---------|----------------------|
43
+ | `templates/Question.tsx` | `src/components/Question.tsx` | Always |
44
+ | `templates/RatingScale.tsx` | `src/components/RatingScale.tsx` | `NPS_SCALE` |
45
+ | `templates/LikertMatrixScale.tsx` | `src/components/LikertMatrixScale.tsx` | `CFM_MATRIX` |
46
+ | `templates/CsatMatrixScale.tsx` | `src/components/CsatMatrixScale.tsx` | `CSAT_MATRIX`, `RATING_MATRIX` |
47
+ | `templates/SliderMatrixScale.tsx` | `src/components/SliderMatrixScale.tsx` | `SLIDER_MATRIX` |
48
+ | `templates/FileUploadScale.tsx` | `src/components/FileUploadScale.tsx` | `FILE_UPLOAD` |
49
+ | `templates/HeatmapScale.tsx` | `src/components/HeatmapScale.tsx` | `HEATMAP` |
50
+ | `templates/RankOrderScale.tsx` | `src/components/RankOrderScale.tsx` | `RANK_ORDER` |
43
51
 
44
- {question.type === QUESTION_TYPE.CFM_MATRIX && (
45
- <LikertMatrixScale question={question} selectedValue={...} onSelect={onSelect} />
46
- )}
52
+ ### Matrix label visibility gate
47
53
 
48
- {(question.type === QUESTION_TYPE.CSAT_MATRIX ||
49
- question.type === QUESTION_TYPE.RATING_MATRIX) && (
50
- <CsatMatrixScale question={question} selectedValue={...} onSelect={onSelect} />
51
- )}
52
- ```
54
+ Before `npm run build`, confirm every matrix question in your inventory will render:
55
+
56
+ - [ ] **Row labels**: `statementRows[].statementText` in 180px (or 25% bipolar) column
57
+ - [ ] **Column labels**: `scaleColumns[].label` header row OR `scaleAnchorLabels` anchor row
58
+ - [ ] **Bipolar**: `oppositeStatementText` in 25% right column
59
+ - [ ] **Transposed**: row labels from `scaleColumns[].label`; column headers from `statementRows[].statementText`
60
+
61
+ ### Slider visibility gate
62
+
63
+ - [ ] Colored tick header row (0–10 badges from `ticks.marks`)
64
+ - [ ] Gray row band with statement text + `CustomSliderTrack` (not orphan circle)
65
+
66
+ | If survey contains | Also copy | UI spec |
67
+ |--------------------|-----------|---------|
68
+ | `SLIDER_MATRIX` | `CustomSliderTrack.tsx`, `surveyUiScaleUtils.ts` | `03-ui-specs/09-slider-matrix.md` |
69
+ | `CFM_MATRIX` | `MatrixDropdown.tsx` (if dropdown layout) | `03-ui-specs/07-matrix-cfm.md` |
70
+ | `CSAT_MATRIX` / `RATING_MATRIX` | `surveyUiIcons.tsx`, `CustomSliderTrack.tsx` (graphics) | `03-ui-specs/04-csat.md`, `08-matrix-csat-rating.md` |
71
+ | `RANK_ORDER` | `@dnd-kit/*` in package.json | `03-ui-specs/14-rank-order.md` |
72
+ | `HEATMAP` | `heatmapCoords.ts` | `03-ui-specs/13-heatmap.md` |
73
+ | `MCQ`, `TEXTFIELD`, `TEXT_AND_MEDIA` | inline in `Question.tsx` | `03-ui-specs/02-radio.md`, etc. |
74
+
75
+ ## Flat Type Routing (CRITICAL)
76
+
77
+ Use `QUESTION_TYPE` — **no `subType`**. Pass filtered matrix questions from `getVisibleMatrixItemsForAnswers`.
53
78
 
54
79
  ## Shared Primitives
55
80
 
56
81
  | File | Used by |
57
82
  |------|---------|
58
- | `CustomSliderTrack.tsx` | `SLIDER_MATRIX`, matrix `displayStyle: graphics` |
59
- | `MatrixDropdown.tsx` | `CFM_MATRIX`, `CSAT_MATRIX`, `RATING_MATRIX` dropdown/selectbox modes |
83
+ | `CustomSliderTrack.tsx` | `SLIDER_MATRIX`, RATING/CSAT `displayStyle: graphics` |
84
+ | `MatrixDropdown.tsx` | Matrix dropdown/selectbox modes |
85
+ | `surveyUiScaleUtils.ts` | NPS colors, tick colors, matrix column values |
86
+ | `surveyUiIcons.tsx` | CSAT emoji/star icons |
87
+ | `selectionStyles.ts` | Focus ring + cell selected pattern (all scale radios) |
88
+ | `labelStyles.ts` | Hint / column / tick label item colors |
60
89
 
61
90
  Store matrix values as column **`id`** — `matrixColumnStoredValue(col)` from SDK.
62
91
 
63
- ## Answer logic (when survey has gated options / rows / columns)
64
-
65
- - [ ] MCQ: filter options with `getVisibleMcqOptionsForAnswers(question, answers, allQuestions, customFieldValues)`
66
- - [ ] Matrix (CSAT/CFM/RATING/SLIDER): filter rows (and CSAT/CFM columns) with `getVisibleMatrixItemsForAnswers(question, answers, allQuestions, customFieldValues)`
67
- - [ ] Pass `customFieldValues` from `SurveyPage` → `Question` when survey logic references `CUSTOM_FIELD` entities
68
- - [ ] Wire `onAction({ type: 'CHANGE' })` so answer logic re-evaluates when prior answers change
69
-
70
- See [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md) and [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md).
71
-
72
92
  ## Step 3: Verify Before Build
73
93
 
74
- - [ ] Every type in the survey inventory has a matching component file
75
- - [ ] `Question.tsx` copied from `docs/templates/Question.tsx` with all **11** type branches wired
76
- - [ ] `SurveyPage` handles all `state.phase` values
77
- - [ ] No `default` fallback rendering stub text (see `constraints.md` §7)
78
- - [ ] RANK_ORDER: `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities` in `package.json`
79
- - [ ] Per-type wiring verified against `question-type-sdk-matrix.md`
80
- - [ ] Grep project: `not yet implemented` **zero matches**
94
+ - [ ] Every type in survey inventory has matching component file(s)
95
+ - [ ] **`src/lib/customFieldValues.ts`** when survey logic references `_c_…` ids
96
+ - [ ] `CUSTOM_FIELD_VALUES` wired in `SurveyPage` hook options **and** `Question` prop
97
+ - [ ] Matrix row + column/anchor labels visible (see canonical fixtures)
98
+ - [ ] Slider has header + track (see canonical fixtures)
99
+ - [ ] `Question.tsx` has `questionNumber` prefix and all 11 type branches
100
+ - [ ] `react-icons` in package.json when CSAT/RATING/NPS emoji modes present
101
+ - [ ] Grep: `not yet implemented` → zero matches
81
102
 
82
103
  ```bash
83
- grep -r "not yet implemented" src/ components/ app/ 2>/dev/null || true
104
+ bash templates/verify-agent-build.sh
105
+ # or from SDK package root: node_modules/@explorer02/cfm-survey-sdk/templates/docs/templates/verify-agent-build.sh
84
106
  ```
107
+
108
+ ## Step 4: Wizard-ready build (Phase 5 — mandatory)
109
+
110
+ Read: [`agent-execution-flow.md`](agent-execution-flow.md), [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md), [`wizard-question-type-styling.md`](wizard-question-type-styling.md)
111
+
112
+ - [ ] `src/styles/survey-theme.css` with full `--cfm-*` set (copy from `templates/survey-theme.css`)
113
+ - [ ] `selectionStyles.ts` + `labelStyles.ts` in `src/lib/surveyUi/`
114
+ - [ ] Draft `./survey-ui-config.json` synced with mockup branding
115
+ - [ ] `previewBridge.ts` + `PreviewBridgeInit` in root layout
116
+ - [ ] Header/Footer/ProgressBar/SurveyPage use `var(--cfm-*)` — no hardcoded brand hex for wizard-controlled tokens
117
+ - [ ] `data-cfm-logo` / `data-cfm-logo-text`, `data-cfm-company`, `data-cfm-survey-title`, `data-cfm-btn-next`, `data-cfm-btn-back`, `data-cfm-thank-you` on chrome
118
+ - [ ] Per-type scale vars per `wizard-question-type-styling.md` for types in survey inventory
119
+ - [ ] Selection pattern: focus ring = outer border + inner dot; cell selected = annulus fill
120
+ - [ ] `npx cfm-sdk export-previews` succeeds from project root (when previews needed for AWS wizard)
@@ -1,6 +1,7 @@
1
1
  # Critical Constraints
2
2
 
3
- > Non-negotiable rules. Violating any of these breaks the build or survey UX.
3
+ > Non-negotiable rules. Violating any of these breaks the build or survey UX.
4
+ > Agent enforcement: [`agent-operating-contract.md`](agent-operating-contract.md) §4 hallucination blocklist.
4
5
 
5
6
  ## 1. Top-Level Imports Only
6
7
 
@@ -26,11 +27,18 @@ Every question card **must** have `id={question.id}` on its outermost element:
26
27
 
27
28
  Required for scroll-to-error when validation fails.
28
29
 
29
- ## 4. CSS-Only Logo
30
+ ## 4. Logo (wizard-compatible)
30
31
 
31
- - **Never** use `<img>` with external URLs for the brand logo
32
- - **Never** save logo files to `public/`
33
- - Build logos with Tailwind CSS divs — derive design from **client prompt** (INPUT 1)
32
+ Choose **one** pattern in `Header.tsx` see [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) and [`10-header-footer.md`](../01-components/10-header-footer.md):
33
+
34
+ | Mode | Rules |
35
+ |------|--------|
36
+ | **Text / CSS-only** | Build logo with HTML + CSS; use `<span data-cfm-logo-text>`. No `public/` logo file required. |
37
+ | **Local image** | Put asset in `public/`; reference with `<img data-cfm-logo src="/your-logo.svg" />`. **Never** use external URLs (`https://…`) in the built survey. |
38
+
39
+ - Do **not** drop unrelated `public/logo_*` files during agent build — CLI seeds from `Header.tsx`, not newest `logo_*`.
40
+ - After wizard upload, `survey-ui-config.json` `global.logo.fileName` is authoritative on re-open.
41
+ - After wizard complete, poll/final JSON includes `global.logo.url` (public CloudFront URL) for agent handoff; the agent still copies the file to `public/` and uses a local `src` in `Header.tsx`.
34
42
 
35
43
  ## 5. Client Components
36
44
 
@@ -18,6 +18,61 @@ Survey logic rules may reference **custom fields** instead of survey questions:
18
18
 
19
19
  Inspect fetched survey data: any `skipLogics`, `displayLogic`, or `answerLogic` with `entityType: 'CUSTOM_FIELD'` requires client-supplied values.
20
20
 
21
+ Copy portable template: [`templates/customFieldValues.ts`](../templates/customFieldValues.ts) → `src/lib/customFieldValues.ts`. See [`client-lib-folder.md`](client-lib-folder.md).
22
+
23
+ ---
24
+
25
+ ## Agent implementation recipe
26
+
27
+ 1. **After fetch** — scan `survey.pages` (and `endPages` logic) for `conditionEntity.entityType === 'CUSTOM_FIELD'`. Collect every `conditionEntity.id` (`_c_…`).
28
+ 2. **Record** each id in `implementation_plan.md` §3d with `fieldType`, logic kind (skip/display/answer/end), and expected CRM value shape.
29
+ 3. **Copy** [`templates/customFieldValues.ts`](../templates/customFieldValues.ts) → `src/lib/customFieldValues.ts`. Populate from CRM/transaction API — **not** inline in `SurveyPage`.
30
+ 4. **Wire hook** — import map and pass in stable `useMemo` options:
31
+
32
+ ```typescript
33
+ import { CUSTOM_FIELD_VALUES } from '@/lib/customFieldValues';
34
+
35
+ const surveyOptions = useMemo(
36
+ () => ({
37
+ instanceId,
38
+ placeholders: SURVEY_PLACEHOLDERS,
39
+ customFieldValues: CUSTOM_FIELD_VALUES,
40
+ }),
41
+ [instanceId, selectedLanguage]
42
+ );
43
+ ```
44
+
45
+ 5. **Wire Question** — pass the **same** map as `customFieldValues` prop (template `Question.tsx` already forwards it to `getVisible*ForAnswers`).
46
+ 6. **Verify** — trigger a custom-field skip rule on Next; no per-question client evaluator code needed.
47
+
48
+ **Blocking gate:** if any `_c_…` id exists in fetched survey → `src/lib/customFieldValues.ts` must exist before Phase 5 BUILD.
49
+
50
+ ### Fixture example (integration-test survey)
51
+
52
+ Page 2 `SLIDER_MATRIX` has a skip rule:
53
+
54
+ ```json
55
+ {
56
+ "conditionEntity": {
57
+ "id": "_c_67199096d294be011a2b4773",
58
+ "entityType": "CUSTOM_FIELD",
59
+ "fieldType": "TEXT"
60
+ },
61
+ "field": "CASE_SENSITIVE_TEXT",
62
+ "values": ["abc"]
63
+ }
64
+ ```
65
+
66
+ Client map:
67
+
68
+ ```typescript
69
+ export const CUSTOM_FIELD_VALUES: CustomFieldValueMap = {
70
+ '_c_67199096d294be011a2b4773': 'abc',
71
+ };
72
+ ```
73
+
74
+ When this value matches, SDK skip logic runs on Next — values must be in **hook options**, not typed by the respondent.
75
+
21
76
  ---
22
77
 
23
78
  ## Client value map
@@ -172,3 +227,4 @@ When a question has several `skipLogics[]` entries (including custom-field rules
172
227
  - [`display-logic-and-navigation.md`](display-logic-and-navigation.md) — question-level gating
173
228
  - [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md) — option/row/column gating + visibility helpers
174
229
  - [`client-integration-guide.md`](client-integration-guide.md) — full wiring map
230
+ - [`client-lib-folder.md`](client-lib-folder.md) — lib folder layout + portable templates
@@ -46,4 +46,19 @@ Display logic may gate questions on **custom field** values (`conditionEntity.en
46
46
 
47
47
  Alternate question wording (`questionDisplayVariants`) uses the same condition engine but **does not hide** the question — the SDK resolves `questionText` on `state.currentQuestions`. See [`question-display-variants.md`](question-display-variants.md).
48
48
 
49
- See also: [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md), [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md), [`progress.md`](progress.md).
49
+ See also: [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md), [`skip-logic-and-navigation.md`](skip-logic-and-navigation.md), [`progress.md`](progress.md), [`logic-fields-catalog.md`](logic-fields-catalog.md).
50
+
51
+ ---
52
+
53
+ ## Worked example — dynamic display on same page
54
+
55
+ **Setup:** Page 1 has Q1 (MCQ trigger) and Q2 (display-gated, `isDynamicAppearanceEnabled: true`). Q2 hidden until Q1 selects option A.
56
+
57
+ 1. User loads page → `currentQuestions` = [Q1] only; progress denominator excludes Q2.
58
+ 2. User selects A → SDK evaluates display logic → `currentQuestions` = [Q1, Q2]; Q2 animates in; progress total increases.
59
+ 3. User answers Q2 → normal CHANGE flow.
60
+ 4. User changes Q1 away from A → SDK sanitizes Q2 answer; Q2 hidden again; progress credits adjust.
61
+
62
+ **Client:** render `state.currentQuestions` only — no manual show/hide. Re-run IntersectionObserver effect when `currentQuestions` changes.
63
+
64
+ **Back navigation:** if display logic hides all questions on a page, SDK skips empty pages — user never sees a blank screen (see skip/display integration tests).