@explorer02/cfm-survey-sdk 0.2.3 → 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 (94) 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 +60 -19
  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/apply-ui-config.md +365 -0
  12. package/templates/docs/00-integration/canonical-survey-fixtures.md +94 -0
  13. package/templates/docs/00-integration/client-integration-guide.md +9 -7
  14. package/templates/docs/00-integration/client-lib-folder.md +115 -0
  15. package/templates/docs/00-integration/component-checklist.md +88 -62
  16. package/templates/docs/00-integration/constraints.md +13 -5
  17. package/templates/docs/00-integration/custom-field-logic-and-navigation.md +56 -0
  18. package/templates/docs/00-integration/progress.md +4 -2
  19. package/templates/docs/00-integration/setup.md +12 -0
  20. package/templates/docs/00-integration/ui-customization-wizard.md +144 -0
  21. package/templates/docs/00-integration/useSurveySDK.md +35 -1
  22. package/templates/docs/00-integration/wizard-api.md +134 -0
  23. package/templates/docs/00-integration/wizard-config-handoff.md +246 -0
  24. package/templates/docs/00-integration/wizard-preview-build-guide.md +400 -0
  25. package/templates/docs/00-integration/wizard-question-type-styling.md +195 -0
  26. package/templates/docs/01-components/01-survey-page.md +18 -8
  27. package/templates/docs/01-components/02-question.md +2 -2
  28. package/templates/docs/01-components/03-rating-scale.md +12 -18
  29. package/templates/docs/01-components/05-csat-matrix-scale.md +18 -26
  30. package/templates/docs/01-components/06-likert-matrix-scale.md +19 -39
  31. package/templates/docs/01-components/07-slider-matrix-scale.md +48 -33
  32. package/templates/docs/01-components/09-custom-slider-track.md +15 -25
  33. package/templates/docs/01-components/10-header-footer.md +23 -4
  34. package/templates/docs/01-components/11-progress-bar.md +14 -6
  35. package/templates/docs/01-components/19-survey-sticky-chrome.md +79 -0
  36. package/templates/docs/01-components/README.md +2 -2
  37. package/templates/docs/02-reference/question-types/10-slider-matrix.md +38 -26
  38. package/templates/docs/02-reference/routing-table.md +1 -0
  39. package/templates/docs/02-reference/value-derivation.md +1 -1
  40. package/templates/docs/03-ui-specs/00-question-shell.md +10 -0
  41. package/templates/docs/03-ui-specs/01-rating.md +18 -0
  42. package/templates/docs/03-ui-specs/02-radio.md +16 -1
  43. package/templates/docs/03-ui-specs/03-text.md +12 -1
  44. package/templates/docs/03-ui-specs/04-csat.md +84 -50
  45. package/templates/docs/03-ui-specs/06-slider.md +3 -1
  46. package/templates/docs/03-ui-specs/07-matrix-cfm.md +71 -48
  47. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +48 -18
  48. package/templates/docs/03-ui-specs/09-slider-matrix.md +59 -32
  49. package/templates/docs/03-ui-specs/10-file-upload.md +11 -0
  50. package/templates/docs/03-ui-specs/11-text-and-media.md +4 -0
  51. package/templates/docs/03-ui-specs/12-survey-chrome.md +54 -5
  52. package/templates/docs/03-ui-specs/13-heatmap.md +12 -1
  53. package/templates/docs/03-ui-specs/14-rank-order.md +11 -0
  54. package/templates/docs/03-ui-specs/README.md +4 -2
  55. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +1 -1
  56. package/templates/docs/MANIFEST.json +162 -6
  57. package/templates/docs/index.md +116 -117
  58. package/templates/docs/templates/CsatMatrixScale.tsx +637 -0
  59. package/templates/docs/templates/FileUploadScale.tsx +262 -0
  60. package/templates/docs/templates/HeatmapScale.tsx +114 -0
  61. package/templates/docs/templates/LikertMatrixScale.tsx +414 -0
  62. package/templates/docs/templates/Question.tsx +3 -0
  63. package/templates/docs/templates/RatingScale.tsx +129 -0
  64. package/templates/docs/templates/SliderMatrixScale.tsx +248 -0
  65. package/templates/docs/templates/SurveyStickyChrome.tsx +24 -0
  66. package/templates/docs/templates/customFieldValues.ts +36 -0
  67. package/templates/docs/templates/implementation_plan.md +56 -9
  68. package/templates/docs/templates/labelStyles.ts +33 -0
  69. package/templates/docs/templates/selectionStyles.ts +100 -0
  70. package/templates/docs/templates/surveyUiIcons.tsx +11 -0
  71. package/templates/docs/templates/surveyUiScaleUtils.ts +51 -0
  72. package/templates/docs/templates/verify-agent-build.sh +119 -0
  73. package/templates/preview-harness/preview-bridge.inline.js +201 -0
  74. package/templates/preview-harness/previewPages.js +108 -0
  75. package/templates/preview-harness/previewPages.ts +298 -0
  76. package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +67 -0
  77. package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +122 -0
  78. package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +75 -0
  79. package/templates/preview-harness/vite-app/src/fixtures/questions.ts +311 -0
  80. package/templates/preview-harness/vite-app/src/globals.css +16 -0
  81. package/templates/preview-harness/vite-app/src/mount.tsx +9 -0
  82. package/templates/preview-harness/vite-app/src/preview-live-overrides.css +101 -0
  83. package/templates/preview-harness/vite-app/stubs/next-dynamic.tsx +25 -0
  84. package/templates/preview-harness/vite-app/stubs/next-image.tsx +29 -0
  85. package/templates/previewBridge.ts +153 -0
  86. package/templates/survey-theme.css +295 -0
  87. package/templates/survey-ui-config.schema.json +213 -0
  88. package/templates/wizard-dist/assets/PreviewMock-DgHfrVeb.js +1 -0
  89. package/templates/wizard-dist/assets/TypePanel-CFVC3Ptn.js +1 -0
  90. package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
  91. package/templates/wizard-dist/assets/index-DYK3X1e5.js +34 -0
  92. package/templates/wizard-dist/assets/vendor-BwkXDkd3.js +17 -0
  93. package/templates/wizard-dist/index.html +20 -0
  94. 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) |
@@ -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,94 +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
- **Copy portable templates from [`templates/`](../templates/) when available** — then customize branding. Otherwise implement from ui-spec Implementation Recipe.
19
+ ## Step 2: Copy Portable Templates (CRITICAL)
16
20
 
17
- | Portable template | Copy to |
18
- |-------------------|---------|
19
- | `templates/Question.tsx` | `src/components/Question.tsx` |
20
- | `templates/RankOrderScale.tsx` | `src/components/RankOrderScale.tsx` |
21
- | `templates/MatrixDropdown.tsx` | `src/components/MatrixDropdown.tsx` |
22
- | `templates/CustomSliderTrack.tsx` + `surveyUiIcons.tsx` | same folder |
23
- | `templates/heatmapCoords.ts` | `src/lib/heatmapCoords.ts` |
21
+ **Copy from [`templates/`](../templates/) before customizing branding.** Do not implement matrix/slider UI from scratch.
24
22
 
25
- | If survey contains | Agent MUST create before `npm run build` | UI spec |
26
- |--------------------|------------------------------------------|---------|
27
- | `NPS_SCALE` | `RatingScale.tsx` | `03-ui-specs/01-rating.md` |
28
- | `CFM_MATRIX` | `LikertMatrixScale.tsx`, `MatrixDropdown.tsx` | `03-ui-specs/07-matrix-cfm.md` |
29
- | `CSAT_MATRIX` | `CsatMatrixScale.tsx`, `MatrixDropdown.tsx`, `CustomSliderTrack.tsx` | `03-ui-specs/04-csat.md` |
30
- | `RATING_MATRIX` | `CsatMatrixScale.tsx`, `MatrixDropdown.tsx`, `CustomSliderTrack.tsx` | `03-ui-specs/08-matrix-csat-rating.md` |
31
- | `SLIDER_MATRIX` | `SliderMatrixScale.tsx`, `CustomSliderTrack.tsx` | `03-ui-specs/09-slider-matrix.md` |
32
- | `FILE_UPLOAD` | `FileUploadScale.tsx` | `03-ui-specs/10-file-upload.md` |
33
- | `MCQ`, `TEXTFIELD`, `TEXT_AND_MEDIA` | Inline in `Question.tsx` | `03-ui-specs/02-radio.md`, `03-text.md`, `11-text-and-media.md` |
34
- | `RANK_ORDER` | `RankOrderScale.tsx` (+ `@dnd-kit/*`) | `03-ui-specs/14-rank-order.md` |
35
- | `HEATMAP` | `HeatmapScale.tsx`, `lib/heatmapCoords.ts` (copy from `templates/heatmapCoords.ts`) | `03-ui-specs/13-heatmap.md` |
23
+ ### Copy order
36
24
 
37
- **Survey chrome (when applicable):**
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
38
32
 
39
- | Component | When |
40
- |-----------|------|
41
- | `IntroPage.tsx` | `survey.introPage` |
42
- | `EndPage.tsx` | `survey.endPages` |
43
- | `PausedPage.tsx` | `survey.phaseFlags.paused` |
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 |
44
38
 
45
- Always create **`Question.tsx`** from [`templates/Question.tsx`](../templates/Question.tsx).
39
+ See [`client-lib-folder.md`](client-lib-folder.md) for full lib layout.
46
40
 
47
- ## Flat Type Routing (CRITICAL)
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` |
48
51
 
49
- Use `QUESTION_TYPE` **no `subType`**:
52
+ ### Matrix label visibility gate
50
53
 
51
- ```tsx
52
- import { QUESTION_TYPE } from '@explorer02/cfm-survey-sdk';
54
+ Before `npm run build`, confirm every matrix question in your inventory will render:
53
55
 
54
- {question.type === QUESTION_TYPE.CFM_MATRIX && (
55
- <LikertMatrixScale question={question} selectedValue={...} onSelect={onSelect} />
56
- )}
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`
57
60
 
58
- {(question.type === QUESTION_TYPE.CSAT_MATRIX ||
59
- question.type === QUESTION_TYPE.RATING_MATRIX) && (
60
- <CsatMatrixScale question={question} selectedValue={...} onSelect={onSelect} />
61
- )}
62
- ```
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`.
63
78
 
64
79
  ## Shared Primitives
65
80
 
66
81
  | File | Used by |
67
82
  |------|---------|
68
- | `CustomSliderTrack.tsx` | `SLIDER_MATRIX`, matrix `displayStyle: graphics` |
69
- | `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 |
70
89
 
71
90
  Store matrix values as column **`id`** — `matrixColumnStoredValue(col)` from SDK.
72
91
 
73
- ## Answer logic (when survey has gated options / rows / columns)
74
-
75
- - [ ] MCQ: filter options with `getVisibleMcqOptionsForAnswers(question, answers, allQuestions, customFieldValues)`
76
- - [ ] Matrix (CSAT/CFM/RATING/SLIDER): filter rows (and CSAT/CFM columns) with `getVisibleMatrixItemsForAnswers(question, answers, allQuestions, customFieldValues)`
77
- - [ ] Pass `customFieldValues` from `SurveyPage` → `Question` when survey logic references `CUSTOM_FIELD` entities
78
- - [ ] Wire `onAction({ type: 'CHANGE' })` so answer logic re-evaluates when prior answers change
79
-
80
- See [`answer-logic-and-navigation.md`](answer-logic-and-navigation.md) and [`custom-field-logic-and-navigation.md`](custom-field-logic-and-navigation.md).
81
-
82
92
  ## Step 3: Verify Before Build
83
93
 
84
- - [ ] Every type in the survey inventory has a matching component file
85
- - [ ] `Question.tsx` copied from `docs/templates/Question.tsx` with all **11** type branches wired
86
- - [ ] `SurveyPage` handles all `state.phase` values
87
- - [ ] No `default` fallback rendering stub text (see `constraints.md` §7)
88
- - [ ] RANK_ORDER: `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities` in `package.json`
89
- - [ ] Per-type wiring verified against `question-type-sdk-matrix.md`
90
- - [ ] 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
91
102
 
92
103
  ```bash
93
- 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
94
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
@@ -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).
@@ -132,6 +132,18 @@ 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