@explorer02/cfm-survey-sdk 0.2.3 → 0.2.5

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 (100) hide show
  1. package/dist/cli/index.js +141 -33
  2. package/dist/cli/index.mjs +141 -33
  3. package/dist/index.js +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/package.json +3 -1
  6. package/postinstall.js +9 -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 +293 -0
  10. package/templates/docs/00-integration/agent-operating-contract.md +268 -0
  11. package/templates/docs/00-integration/apply-ui-config.md +390 -0
  12. package/templates/docs/00-integration/aws-deploy.md +117 -3
  13. package/templates/docs/00-integration/canonical-survey-fixtures.md +94 -0
  14. package/templates/docs/00-integration/client-integration-guide.md +9 -7
  15. package/templates/docs/00-integration/client-lib-folder.md +115 -0
  16. package/templates/docs/00-integration/component-checklist.md +90 -62
  17. package/templates/docs/00-integration/constraints.md +14 -5
  18. package/templates/docs/00-integration/custom-field-logic-and-navigation.md +56 -0
  19. package/templates/docs/00-integration/mockup-ui-extraction.md +113 -0
  20. package/templates/docs/00-integration/placeholders-and-custom-fields-wiring.md +192 -0
  21. package/templates/docs/00-integration/placeholders-and-tokens.md +2 -1
  22. package/templates/docs/00-integration/progress.md +4 -2
  23. package/templates/docs/00-integration/setup.md +12 -0
  24. package/templates/docs/00-integration/ui-customization-wizard.md +123 -0
  25. package/templates/docs/00-integration/useSurveySDK.md +35 -1
  26. package/templates/docs/00-integration/wizard-api.md +126 -0
  27. package/templates/docs/00-integration/wizard-config-handoff.md +292 -0
  28. package/templates/docs/00-integration/wizard-preview-build-guide.md +405 -0
  29. package/templates/docs/00-integration/wizard-question-type-styling.md +197 -0
  30. package/templates/docs/00-integration/wizard-troubleshooting.md +80 -0
  31. package/templates/docs/01-components/01-survey-page.md +18 -8
  32. package/templates/docs/01-components/02-question.md +2 -2
  33. package/templates/docs/01-components/03-rating-scale.md +12 -18
  34. package/templates/docs/01-components/05-csat-matrix-scale.md +18 -26
  35. package/templates/docs/01-components/06-likert-matrix-scale.md +19 -39
  36. package/templates/docs/01-components/07-slider-matrix-scale.md +48 -33
  37. package/templates/docs/01-components/09-custom-slider-track.md +15 -25
  38. package/templates/docs/01-components/10-header-footer.md +23 -4
  39. package/templates/docs/01-components/11-progress-bar.md +14 -6
  40. package/templates/docs/01-components/19-survey-sticky-chrome.md +79 -0
  41. package/templates/docs/01-components/README.md +2 -2
  42. package/templates/docs/02-reference/question-types/10-slider-matrix.md +38 -26
  43. package/templates/docs/02-reference/routing-table.md +1 -0
  44. package/templates/docs/02-reference/value-derivation.md +1 -1
  45. package/templates/docs/03-ui-specs/00-question-shell.md +10 -0
  46. package/templates/docs/03-ui-specs/01-rating.md +18 -0
  47. package/templates/docs/03-ui-specs/02-radio.md +16 -1
  48. package/templates/docs/03-ui-specs/03-text.md +12 -1
  49. package/templates/docs/03-ui-specs/04-csat.md +84 -50
  50. package/templates/docs/03-ui-specs/06-slider.md +3 -1
  51. package/templates/docs/03-ui-specs/07-matrix-cfm.md +71 -48
  52. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +48 -18
  53. package/templates/docs/03-ui-specs/09-slider-matrix.md +59 -32
  54. package/templates/docs/03-ui-specs/10-file-upload.md +11 -0
  55. package/templates/docs/03-ui-specs/11-text-and-media.md +4 -0
  56. package/templates/docs/03-ui-specs/12-survey-chrome.md +54 -5
  57. package/templates/docs/03-ui-specs/13-heatmap.md +12 -1
  58. package/templates/docs/03-ui-specs/14-rank-order.md +11 -0
  59. package/templates/docs/03-ui-specs/README.md +4 -2
  60. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +1 -1
  61. package/templates/docs/MANIFEST.json +180 -7
  62. package/templates/docs/index.md +114 -117
  63. package/templates/docs/templates/CsatMatrixScale.tsx +637 -0
  64. package/templates/docs/templates/FileUploadScale.tsx +262 -0
  65. package/templates/docs/templates/HeatmapScale.tsx +114 -0
  66. package/templates/docs/templates/LikertMatrixScale.tsx +414 -0
  67. package/templates/docs/templates/Question.tsx +3 -0
  68. package/templates/docs/templates/RatingScale.tsx +129 -0
  69. package/templates/docs/templates/SliderMatrixScale.tsx +248 -0
  70. package/templates/docs/templates/SurveyStickyChrome.tsx +24 -0
  71. package/templates/docs/templates/customFieldValues.ts +36 -0
  72. package/templates/docs/templates/deploy-to-aws.sh +74 -0
  73. package/templates/docs/templates/implementation_plan.md +64 -10
  74. package/templates/docs/templates/labelStyles.ts +33 -0
  75. package/templates/docs/templates/selectionStyles.ts +100 -0
  76. package/templates/docs/templates/surveyUiIcons.tsx +11 -0
  77. package/templates/docs/templates/surveyUiScaleUtils.ts +51 -0
  78. package/templates/docs/templates/verify-agent-build.sh +119 -0
  79. package/templates/preview-harness/preview-bridge.inline.js +201 -0
  80. package/templates/preview-harness/previewPages.js +108 -0
  81. package/templates/preview-harness/previewPages.ts +298 -0
  82. package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +67 -0
  83. package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +122 -0
  84. package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +75 -0
  85. package/templates/preview-harness/vite-app/src/fixtures/questions.ts +311 -0
  86. package/templates/preview-harness/vite-app/src/globals.css +16 -0
  87. package/templates/preview-harness/vite-app/src/mount.tsx +9 -0
  88. package/templates/preview-harness/vite-app/src/preview-live-overrides.css +101 -0
  89. package/templates/preview-harness/vite-app/stubs/next-dynamic.tsx +25 -0
  90. package/templates/preview-harness/vite-app/stubs/next-image.tsx +29 -0
  91. package/templates/previewBridge.ts +153 -0
  92. package/templates/survey-theme.css +295 -0
  93. package/templates/survey-ui-config.schema.json +213 -0
  94. package/templates/wizard-dist/assets/PreviewMock-AoZdAEPZ.js +1 -0
  95. package/templates/wizard-dist/assets/TypePanel-39FnbtvG.js +1 -0
  96. package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
  97. package/templates/wizard-dist/assets/index-CWKdsJOp.js +34 -0
  98. package/templates/wizard-dist/assets/vendor-BwkXDkd3.js +17 -0
  99. package/templates/wizard-dist/index.html +20 -0
  100. package/templates/wizard.html +13 -1129
@@ -1,7 +1,8 @@
1
1
  # Survey Page Orchestrator Architectural Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
4
- > **Reference implementation:** `apps/client/src/components/SurveyPage.tsx`
4
+ > **Portable template:** implement from `docs/templates/` + [`agent-execution-flow.md`](../00-integration/agent-execution-flow.md) Phase 5
5
+ > **Monorepo snapshot (do not copy):** `apps/client/src/components/SurveyPage.tsx`
5
6
  > **Target Component**: `SurveyPage`
6
7
  > **Role**: Wires `useSurveySDK` to the UI — loading/error states, phase routing, pagination, submission.
7
8
 
@@ -13,9 +14,9 @@ Act as the **phase state machine** for the survey. Never render questions direct
13
14
 
14
15
  ```tsx
15
16
  import type { CustomFieldValueMap } from '@explorer02/cfm-survey-sdk';
17
+ import { CUSTOM_FIELD_VALUES } from '@/lib/customFieldValues';
16
18
 
17
19
  const SURVEY_PLACEHOLDERS = { FIRST_NAME: 'Customer' }; // Must be outside component!
18
- const CUSTOM_FIELD_VALUES: CustomFieldValueMap = {}; // Populate when survey uses custom-field logic
19
20
 
20
21
  export default function SurveyPage() {
21
22
  const [selectedLanguage, setSelectedLanguage] = useState<string | undefined>();
@@ -46,6 +47,14 @@ export default function SurveyPage() {
46
47
 
47
48
  See [`client-integration-guide.md`](../00-integration/client-integration-guide.md) for lifecycle effects (`SCROLL`, `RECORD_QUESTION_VIEW`).
48
49
 
50
+ When fetched survey contains any `_c_…` custom field id in logic rules:
51
+
52
+ - [ ] Copy [`templates/customFieldValues.ts`](../templates/customFieldValues.ts) → `src/lib/customFieldValues.ts`
53
+ - [ ] Populate map from CRM/transaction payload
54
+ - [ ] Import `CUSTOM_FIELD_VALUES` in `SurveyPage` (do not define inline empty `{}`)
55
+
56
+ See [`client-lib-folder.md`](../00-integration/client-lib-folder.md) and [`custom-field-logic-and-navigation.md`](../00-integration/custom-field-logic-and-navigation.md).
57
+
49
58
  ## Render Order (MANDATORY)
50
59
 
51
60
  Handle in this order:
@@ -73,12 +82,13 @@ See `14-intro-page.md`, `15-end-page.md`, `16-paused-page.md`.
73
82
 
74
83
  ### 3. Active content (`phase.kind === 'content'`)
75
84
 
76
- 1. `Header`
77
- 2. `ProgressBar` (`state.progressPercentage`) — see `00-integration/progress.md`
78
- 3. `LanguageSelector`
79
- 4. Question map (below)
80
- 5. Navigation buttons (below)
81
- 6. `Footer`
85
+ 1. `SurveyStickyChrome` — sticky `Header` + `ProgressBar` (`state.progressPercentage`); see `19-survey-sticky-chrome.md` and `00-integration/progress.md`
86
+ 2. `main`: `LanguageSelector`
87
+ 3. Question map (below)
88
+ 4. Navigation buttons (below)
89
+ 5. `Footer`
90
+
91
+ Use standalone `Header` (no sticky chrome) on intro, end, paused, loading, and terminal phases.
82
92
 
83
93
  ## The Question Map
84
94
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/00-question-shell.md`
4
4
  > **Canonical template:** [`templates/Question.tsx`](../templates/Question.tsx) — copy verbatim, then create imported components
5
- > **Monorepo reference:** `apps/client/src/components/Question.tsx`
5
+ > **Monorepo snapshot (illustrative only):** `apps/client/src/components/Question.tsx`
6
6
  > **Target Component**: `Question`
7
7
 
8
8
  ## Start Here (CRITICAL)
@@ -77,7 +77,7 @@ Store matrix cell values as column **`id`** via `matrixColumnStoredValue(col)`
77
77
  ### MCQ (`QUESTION_TYPE.MCQ`)
78
78
 
79
79
  - [ ] Filter options with `getVisibleMcqOptionsForAnswers` before render
80
- - [ ] Store `option.id`; support `isMultiSelect` array shape when configured
80
+ - [ ] Store `option.id`; use `selectionMode` (`single` | `multiple`) — not `isMultiSelect`
81
81
  - [ ] onAction CHANGE: `{ questionId, answerValue: optionId | optionId[] }`
82
82
 
83
83
  ### TEXTFIELD (`QUESTION_TYPE.TEXTFIELD`)
@@ -1,40 +1,34 @@
1
1
  # Rating Scale Architectural Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/01-rating.md` (NPS) · `03-ui-specs/05-rating-scale.md` (star/emoji gap)
4
- > **Reference implementation:** `apps/client/src/components/RatingScale.tsx`
4
+ > **Portable template:** copy [`templates/RatingScale.tsx`](../templates/RatingScale.tsx) + [`surveyUiScaleUtils.ts`](../templates/surveyUiScaleUtils.ts) + [`surveyUiIcons.tsx`](../templates/surveyUiIcons.tsx)
5
5
  > **Target Component**: `RatingScale`
6
6
  > **Handles**: `type: 'NPS_SCALE'` (via `QUESTION_TYPE.NPS_SCALE`)
7
7
 
8
8
  ## Core Responsibility
9
- Render a horizontal sequence of numbered badges.
9
+
10
+ Render a horizontal sequence of numbered badges with traffic-light colors (NPS 0–10).
10
11
 
11
12
  ## Configuration Matrix
12
- You MUST handle the following configurations dynamically:
13
13
 
14
14
  ### 1. Color Grading (NPS)
15
- - Check if `option.color` exists. If so, apply it to the badge background. This is crucial for NPS scales (Red 0-6, Yellow 7-8, Green 9-10).
15
+ - Use `getAccentColor(value)` from `surveyUiScaleUtils.ts` for badge backgrounds
16
16
 
17
17
  ### 2. Label Positioning (`minLabel`, `midLabel`, `maxLabel`)
18
- - **Architectural Requirement**: Wrap the badge sequence and the labels in a relative container.
19
- - Place labels underneath the badges using absolute positioning.
20
- - **Mid Label Calculation**: You must calculate the exact center position: `style={{ left: \`\${(midLabelIndex / (options.length - 1)) * 100}%\` }}`.
18
+ - Render in `Question.tsx` above `RatingScale` with absolute positioning
19
+ - Mid label: `left: (midLabelIndex / (options.length - 1)) * 100%`
21
20
 
22
21
  ## UI/UX Rules (CRITICAL)
23
22
 
24
- The UI must exactly match enterprise standards:
25
-
26
- 1. **Shape**: Badges MUST be square with rounded corners (`rounded-lg w-10 h-10 sm:w-12 sm:h-12`). Do NOT use full circles (`rounded-full`).
27
- 2. **Unselected State**: If any item is selected, unselected items must dim (`opacity-30`).
28
- 3. **Selected State**: The selected badge MUST pop out prominently: `border-2 border-[#e20074] shadow-md scale-105 opacity-100 font-bold`.
29
- 4. **Hover State**: `hover:scale-105 hover:border-[#e20074] transition-all cursor-pointer`.
30
- 5. **Tooltips**: Every badge MUST have `title={option.label}` applied for accessibility and semantic context (e.g. hovering '10' shows 'Extremely Likely').
23
+ 1. **Shape**: Square badges with rounded corners — not full circles
24
+ 2. **Unselected State**: Dim unselected when any item selected (`opacity-30`)
25
+ 3. **Selected State**: `border-2 border-[#e20074] shadow-md scale-105`
26
+ 4. **Tooltips**: `title={option.label}` on every badge
31
27
 
32
28
  ## SDK Integration Checklist (agent)
33
29
 
30
+ - [ ] Copied portable `RatingScale.tsx`
34
31
  - [ ] Type literal: `QUESTION_TYPE.NPS_SCALE`
35
32
  - [ ] Answer stored as: `number`
36
33
  - [ ] onAction CHANGE: `{ questionId, answerValue: number }`
37
- - [ ] Visibility helper: `getVisibleScaleColumnsForAnswers` when scale points have answer logic
38
- - [ ] Custom fields: 4th arg on scale visibility helper when needed
39
- - [ ] Logic features: skip ✓ display ✓ answer ✓ variants ✓ numbering ✓
40
- - [ ] Matrix row: [`question-type-sdk-matrix.md`](../00-integration/question-type-sdk-matrix.md#nps_scale)
34
+ - [ ] minLabel/maxLabel rendered in Question.tsx when present
@@ -1,44 +1,36 @@
1
1
  # CSAT Matrix Architectural Blueprint
2
2
 
3
- > **UI spec (authoritative):** `03-ui-specs/04-csat.md` (for `CSAT_MATRIX`) and `03-ui-specs/08-matrix-csat-rating.md` (for `RATING_MATRIX`)
4
- > **Reference implementation:** `apps/client/src/components/CsatMatrixScale.tsx`
3
+ > **UI spec (authoritative):** `03-ui-specs/04-csat.md` (CSAT) · `03-ui-specs/08-matrix-csat-rating.md` (RATING)
4
+ > **Portable template:** copy [`templates/CsatMatrixScale.tsx`](../templates/CsatMatrixScale.tsx) + shared deps (see ui-spec)
5
5
  > **Target Component**: `CsatMatrixScale`
6
6
  > **Handles**: `type: 'CSAT_MATRIX'` and `type: 'RATING_MATRIX'`
7
7
 
8
8
  ## Core Responsibility
9
9
 
10
- Render a grid where columns represent satisfaction scales (Emojis, Stars, or Rating Badges).
10
+ Render a matrix grid where rows are `statementRows` and columns are `scaleColumns`. Route on `displayStyle` and `gridLayout`.
11
11
 
12
- ## Data Mapping & State Management
12
+ ## Statement & labels (CRITICAL)
13
13
 
14
- - `selectedValue`: `MatrixRowAnswers` keys are `statementRows[].id`, values are column **`id`** (via `matrixColumnStoredValue`)
15
- - Iterate over `question.statementRows`. Within each row, iterate over `question.scaleColumns`.
16
- - Render the correct icon/badge in the `<td>` based on `question.displayStyle`.
14
+ Every grid row MUST show `statementRows[].statementText` in a **180px** left column.
17
15
 
18
- ## Configuration Matrix
19
-
20
- You MUST handle:
21
-
22
- - **`hasNotApplicableOption`**: If true, the last column might be `null`. Render a distinct UI for the N/A column.
23
- - **`reverseScaleOrder`**: Reverse column order before render when true.
24
- - **`gridLayout`**: `'standard'`, `'carousel'`, or `'dropdown'` — see ui-spec decision tree.
16
+ Every grid MUST show either:
17
+ - A **column header row** from `scaleColumns[].label`, OR
18
+ - An **anchor label row** from `scaleAnchorLabels[]`
25
19
 
26
- ## UI/UX Bounding Box Rules (CRITICAL)
20
+ Never render a bare grid of inputs with no row/column labels.
27
21
 
28
- Matrices are difficult to read and click accurately. You MUST implement these strict UI rules:
29
-
30
- 1. **Row Tracking**:
31
- - Every data `<tr>` MUST have `hover:bg-gray-50 transition-colors duration-150`.
22
+ ## Configuration Matrix
32
23
 
33
- 2. **The "Active Bounding Box"**:
34
- - For Emojis and Stars inside the matrix, do NOT just change the SVG fill.
35
- - The interactive wrapper inside the `<td>` MUST toggle a pink bounding box.
24
+ - **`displayStyle`**: emoji | star | numbered | graphics | standard — each has distinct UI (see ui-spec)
25
+ - **`hasNotApplicableOption`**: 56px N/A column
26
+ - **`reverseScaleOrder`**: flip column index before icon/badge lookup
27
+ - **`gridLayout`**: standard | carousel | dropdown
36
28
 
37
29
  ## SDK Integration Checklist (agent)
38
30
 
31
+ - [ ] Copied portable template + `surveyUiIcons.tsx` + `surveyUiScaleUtils.ts`
39
32
  - [ ] Type literals: `QUESTION_TYPE.CSAT_MATRIX` and `QUESTION_TYPE.RATING_MATRIX`
40
- - [ ] Answer stored as: `MatrixRowAnswers` via `matrixColumnStoredValue`
41
- - [ ] onAction CHANGE: `{ questionId, answerValue: MatrixRowAnswers }`
42
- - [ ] Visibility helper: `getVisibleMatrixItemsForAnswers(..., customFieldValues?)`
43
- - [ ] Emoji/star UI: client-side `react-icons` — not SDK exports
33
+ - [ ] Answer: `MatrixRowAnswers` via `matrixColumnStoredValue(col)`
34
+ - [ ] Visibility: `getVisibleMatrixItemsForAnswers(..., customFieldValues?)`
35
+ - [ ] Row labels + column/anchor labels visible
44
36
  - [ ] Matrix row: [`question-type-sdk-matrix.md`](../00-integration/question-type-sdk-matrix.md)
@@ -1,56 +1,36 @@
1
1
  # Likert Matrix Architectural Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/07-matrix-cfm.md`
4
- > **Reference implementation:** `apps/client/src/components/LikertMatrixScale.tsx`
4
+ > **Portable template:** copy [`templates/LikertMatrixScale.tsx`](../templates/LikertMatrixScale.tsx) + [`MatrixDropdown.tsx`](../templates/MatrixDropdown.tsx)
5
5
  > **Target Component**: `LikertMatrixScale`
6
6
  > **Handles**: `type: 'CFM_MATRIX'`
7
7
 
8
8
  ## Core Responsibility
9
- Render a grid with text-label columns. However, this component must be highly dynamic to support complex layouts, transpositions, and long-form matrix readability.
10
9
 
11
- ## Data Mapping & State Management
12
- - `selectedValue`: `MatrixRowAnswers` — store column **`id`** via `matrixColumnStoredValue(col)`
13
- - **Mutation**: When a cell is selected for `row.id`, dispatch `onSelect({ ...selectedValue, [row.id]: columnId })`
10
+ Render CFM matrix grids with support for bipolar layout, transposed tables, multi-select checkboxes, and anchor labels.
14
11
 
15
- ## Configuration Matrix (CRITICAL FOR PLANNING)
12
+ ## Statement & labels (CRITICAL)
16
13
 
17
- Your component architecture MUST account for the following boolean flags from `question`:
14
+ | Mode | Left label | Right label | Column headers |
15
+ |------|------------|-------------|----------------|
16
+ | Likert | `statementText` (180px) | — | `scaleColumns[].label` when `showColumnHeaders` |
17
+ | Bipolar | `statementText` (25%) | `oppositeStatementText` (25%) | anchor labels OR column headers |
18
+ | Transposed | `scaleColumns[].label` as row | — | `statementRows[].statementText` as columns |
18
19
 
19
- ### 1. `transposeTable`
20
- If `true`, the standard Row-by-Column matrix is inverted:
21
- - `question.columns` become the row labels on the left.
22
- - `question.rows` become the top column headers.
23
- - **Architectural Requirement**: Your `<tbody>` map must iterate over `question.columns` first, and render `<td>` cells mapped from `question.rows`.
24
- - Ensure `onSelect` still correctly pairs the original `row.id` with the original `col.value`.
20
+ Use `dangerouslySetInnerHTML` for all statement/label HTML.
25
21
 
26
- ### 2. `repeatColumnHeaders`
27
- If `true`, the `<thead>` containing column labels must be re-injected periodically to prevent users from losing track of columns on long matrices.
28
- - **Implementation Strategy**: In your row `map()`, if `index > 0 && index % 5 === 0`, return a React Fragment containing a cloned `<tr>` header row followed by the actual data `<tr>`.
29
- - **Note:** API/config field is `repeatColumnHeaders` (not `repeatScale`).
22
+ ## Configuration Matrix
30
23
 
31
- ### 3. `matrixType === 'bipolar'`
32
- Standard matrices (`'likert'`) only show statement text on the left. Bipolar scales show text on BOTH sides of the radio buttons.
33
- - **Implementation Strategy**: Render an extra `<td>` at the end of the row containing `row.rightText`.
34
-
35
- ## UI/UX & Styling Constraints
36
-
37
- To match enterprise standards, implement the following UI strictly:
38
-
39
- 1. **Row Tracking (Hover Effects)**:
40
- - Every data `<tr>` MUST have `hover:bg-gray-50 transition-colors duration-150 group`. This is mandatory for UX on wide matrices.
41
- 2. **Radio Buttons**:
42
- - Use `accent-[#e20074] w-5 h-5 cursor-pointer` or build custom CSS circles that fill with the brand color.
43
- - Do NOT use standard unstyled blue browser radios.
44
- 3. **Tooltips (Accessibility)**:
45
- - Every input MUST have `title={col.label}` so the user sees the semantic meaning when hovering the radio button.
46
- 4. **Header Typography**:
47
- - `th` tags should be `text-xs font-semibold text-gray-500 uppercase tracking-wider p-4 border-b`.
24
+ - **`transposeTable`**: swaps which array becomes grid rows vs columns
25
+ - **`selectionMode: 'multiple'`**: checkboxes; answers are `string[]` per row
26
+ - **`statementLayout: 'bipolar'`**: 25% | scale | 25% layout
27
+ - **`scaleAnchorLabels`**: positioned above scale when bipolar or likert
48
28
 
49
29
  ## SDK Integration Checklist (agent)
50
30
 
31
+ - [ ] Copied portable template
51
32
  - [ ] Type literal: `QUESTION_TYPE.CFM_MATRIX`
52
- - [ ] Answer stored as: `MatrixRowAnswers` via `matrixColumnStoredValue`
53
- - [ ] onAction CHANGE: `{ questionId, answerValue: MatrixRowAnswers }`
54
- - [ ] Visibility helper: `getVisibleMatrixItemsForAnswers(..., customFieldValues?)`
55
- - [ ] Handle `gridLayout`: standard / carousel / dropdown
56
- - [ ] Matrix row: [`question-type-sdk-matrix.md#cfm_matrix`](../00-integration/question-type-sdk-matrix.md#cfm_matrix)
33
+ - [ ] Answer: `MatrixRowAnswers` via `matrixColumnStoredValue(col)`
34
+ - [ ] Visibility: `getVisibleMatrixItemsForAnswers(..., customFieldValues?)`
35
+ - [ ] All row labels and header labels visible per ui-spec
36
+ - [ ] Matrix row: [`question-type-sdk-matrix.md`](../00-integration/question-type-sdk-matrix.md)
@@ -1,56 +1,71 @@
1
1
  # Slider Matrix Architectural Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/09-slider-matrix.md`
4
- > **Reference implementation:** `apps/client/src/components/SliderMatrixScale.tsx`
4
+ > **Portable template:** copy [`templates/SliderMatrixScale.tsx`](../templates/SliderMatrixScale.tsx) + [`CustomSliderTrack.tsx`](../templates/CustomSliderTrack.tsx) + [`surveyUiScaleUtils.ts`](../templates/surveyUiScaleUtils.ts)
5
5
  > **Target Component**: `SliderMatrixScale`
6
6
  > **Handles**: `type: 'SLIDER_MATRIX'`
7
7
 
8
8
  ## Core Responsibility
9
- Render an independent range slider for every row in the matrix. Sliders must be highly visible, utilizing a "thick track" UI, dynamic fill based on the current value, and exact value badges.
10
9
 
11
- ## State Management
12
- - `selectedValue`: Expects `MatrixRowAnswers` shape.
13
- - `row.min`, `row.max`, and `row.step` must dictate the boundaries of the slider logic.
14
- - **Mutation**: `onChange` of the input must parse the value as a Number before dispatching `onSelect`.
10
+ Render one `CustomSliderTrack` per `statementRows[]` entry, with a shared header row of colored tick badges and optional anchor labels.
15
11
 
16
- ## The "Thick Slider" UX Implementation (CRITICAL)
12
+ ## CRITICAL Do NOT improvise
17
13
 
18
- Native `<input type="range">` elements are notoriously difficult to style consistently across browsers. You MUST implement the "Invisible Overlay" pattern to achieve the expected thick-track UI.
14
+ - **Do NOT** build custom “thick slider” / invisible overlay range inputs
15
+ - **Do NOT** use legacy field names (`rows`, `labels`, `tickValues`, `enableNotApplicable`, `sliderType`)
16
+ - **Copy the portable template first**, then customize branding colors only
19
17
 
20
- ### The Invisible Overlay Pattern
21
- For every slider row, build a relative container:
22
- 1. **Base Track**: `absolute w-full h-2 bg-gray-200 rounded-full`.
23
- 2. **Dynamic Fill Track**: `absolute h-2 bg-[#e20074] rounded-l-full`.
24
- - Calculate width dynamically: `((value - min) / (max - min)) * 100 + '%'`.
25
- 3. **The Thumb**: `absolute w-5 h-5 bg-[#e20074] border-2 border-white rounded-full shadow-md pointer-events-none transition-transform group-hover:scale-125`.
26
- - Position it dynamically: `left: calc(${percentage}% - 10px)`.
27
- 4. **The Input Control**: Layer a native input on top, completely invisible but clickable:
28
- - `<input type="range" className="absolute w-full h-full opacity-0 cursor-pointer z-10" />`
18
+ ## SDK Fields (authoritative)
29
19
 
30
- ## Configuration Matrix (CRITICAL FOR PLANNING)
20
+ From `fetchSurvey/types/sliderMatrix.ts`:
31
21
 
32
- Your component must account for:
22
+ | Field | UI role |
23
+ |-------|---------|
24
+ | `statementRows[]` | Row labels + per-row min/max/step |
25
+ | `ticks.count` + `ticks.marks[]` | Header colored badges (`tickPosition`, `tickLabel`) |
26
+ | `scaleAnchorLabels?` | Optional text labels above tick row |
27
+ | `enableInputBox` | 80px numeric input + steppers |
28
+ | `displayValues` | Pass to `CustomSliderTrack` |
29
+ | `hasNotApplicableOption` | N/A checkbox column; answer value **`'N/A'`** string |
30
+ | `sliderStyle?: 'graphics'` | Plain text ticks + emoji thumb via `CustomSliderTrack` |
33
31
 
34
- ### 1. Exact Value Display
35
- - Users must see the exact number they are selecting.
36
- - Render a badge at the far right of the slider track: `shrink-0 w-8 text-center font-bold text-[#e20074]`.
32
+ ## Layout (matches reference)
37
33
 
38
- ### 2. `row.ticks` & `tickValues`
39
- - If `question.ticks` or `row.tickValues` exists, you must render tiny vertical tick marks below the track at appropriate percentage intervals to guide the user.
34
+ ```
35
+ [25% spacer] [scaleAnchorLabels?] [colored tick badges 0–10] [input spacer?] [N/A header?]
36
+ ┌─ gray row band #f3f4f6 ─────────────────────────────────────────────────────────────┐
37
+ │ 25% statementText │ CustomSliderTrack │ 80px input? │ N/A checkbox? │
38
+ └─────────────────────────────────────────────────────────────────────────────────────┘
39
+ ```
40
40
 
41
- ### 3. Label Positioning
42
- - `row.minLabel` and `row.maxLabel` must be displayed directly underneath the far-left and far-right of the slider track using flexbox `justify-between text-xs text-gray-500`.
41
+ ## Statement text visibility
43
42
 
44
- ### 4. `enableNotApplicable`
45
- - If `true`, render a checkbox at the end of the row.
46
- - Checking it sets the value in the map to `null`.
47
- - When `value === null`, gray out the slider track and disable the input.
43
+ - Render `row.statementText` with `dangerouslySetInnerHTML` in the **25% left column**
44
+ - Skip rendering when text is empty or literal `"Question"` (placeholder from API)
45
+ - **Never** omit the statement column — agents that put statement text in the question title only will fail multi-row surveys
46
+
47
+ ## Header tick badges
48
+
49
+ ```typescript
50
+ const tickValues = ticks.marks.map((mark, index) => ({
51
+ value: mark.tickPosition,
52
+ label: mark.tickLabel,
53
+ color: tickColorFromIndex(index, ticks.count),
54
+ }));
55
+ const htmlStep = ticks.count > 1 ? (max - min) / (ticks.count - 1) : row.step;
56
+ ```
57
+
58
+ ## N/A behavior
59
+
60
+ - Checked → store `'N/A'` string (not `null`)
61
+ - Unchecked → reset to `row.defaultValue ?? row.min`
48
62
 
49
63
  ## SDK Integration Checklist (agent)
50
64
 
65
+ - [ ] Copied `SliderMatrixScale.tsx`, `CustomSliderTrack.tsx`, `surveyUiScaleUtils.ts`
51
66
  - [ ] Type literal: `QUESTION_TYPE.SLIDER_MATRIX`
52
- - [ ] Answer stored as: `MatrixRowAnswers` — number or `null` per row
67
+ - [ ] Answer: `MatrixRowAnswers` — `{ [rowId]: number | 'N/A' }`
53
68
  - [ ] onAction CHANGE: `{ questionId, answerValue: MatrixRowAnswers }`
54
- - [ ] Visibility helper: `getVisibleStatementRowsForAnswers(..., customFieldValues?)`
55
- - [ ] Use `CustomSliderTrack` when `sliderType === 'graphics'`
69
+ - [ ] Visibility: `getVisibleStatementRowsForAnswers(..., customFieldValues?)`
70
+ - [ ] Header row renders even for single-row questions
56
71
  - [ ] Matrix row: [`question-type-sdk-matrix.md#slider_matrix`](../00-integration/question-type-sdk-matrix.md#slider_matrix)
@@ -1,38 +1,28 @@
1
1
  # Custom Slider Track Architectural Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/shared/custom-slider-track.md`
4
- > **Reference implementation:** `apps/client/src/components/CustomSliderTrack.tsx`
4
+ > **Portable template:** copy [`templates/CustomSliderTrack.tsx`](../templates/CustomSliderTrack.tsx) + [`surveyUiIcons.tsx`](../templates/surveyUiIcons.tsx)
5
5
  > **Target Component**: `CustomSliderTrack`
6
- > **Used By**: `SliderMatrixScale`, `CsatMatrixScale`, standalone `slider`
6
+ > **Used By**: `SliderMatrixScale`, `CsatMatrixScale` (`displayStyle: graphics`)
7
7
 
8
8
  ## Core Responsibility
9
- Native browser `<input type="range">` elements are impossible to style consistently across browsers, especially when trying to render dynamic emoji handles. This component MUST implement an invisible overlay architecture to provide total CSS control over the slider's appearance.
10
9
 
11
- ## The Invisible Overlay Architecture (CRITICAL)
10
+ Provide a styled range slider with optional emoji thumb (`sliderType: 'graphics'`). Used **inside** `SliderMatrixScale` and matrix graphics rows — not as a standalone question type.
12
11
 
13
- Instead of trying to style the `::-webkit-slider-thumb` pseudo-elements, you MUST build the slider using layered DOM elements.
12
+ ## CRITICAL Copy template
14
13
 
15
- ### Layer 1: The Background Track
16
- A static `<div>` representing the entire length of the slider.
17
- - CSS: `absolute w-full h-2 bg-gray-200 rounded-full`.
14
+ Do not reimplement from this blueprint alone. Copy `templates/CustomSliderTrack.tsx` which implements the layered track + invisible range input pattern.
18
15
 
19
- ### Layer 2: The Active Fill
20
- A dynamic `<div>` representing the portion of the slider up to the current value.
21
- - CSS: `absolute h-2 bg-[#e20074] rounded-l-full`.
22
- - Inline Style: `style={{ width: \`\${percentage}%\` }}`.
16
+ ## Pass tickLabels for matrix graphics
23
17
 
24
- ### Layer 3: The Custom Thumb
25
- A visual element that floats along the track.
26
- - **For Standard Sliders**: A pink circle. `w-5 h-5 bg-[#e20074] border-2 border-white rounded-full shadow-md pointer-events-none`.
27
- - **For Graphics Sliders (`sliderType === 'graphics'`)**: A larger white circle containing an emoji mapped to the current value using client-side react-icons. `w-10 h-10 bg-white shadow-md border flex items-center justify-center text-2xl`.
28
- - Inline Style: `style={{ left: \`\${percentage}%\` }}`.
18
+ ```typescript
19
+ tickLabels={scaleColumns.map(col => col.label)}
20
+ ```
29
21
 
30
- ### Layer 4: The Native Input (The Engine)
31
- An invisible native input overlaid exactly on top to capture all mouse/touch events automatically without needing manual math for drag interactions.
32
- - CSS: `<input type="range" className="absolute w-full h-full opacity-0 cursor-pointer z-10" />`.
33
- - This ensures perfect accessibility, keyboard navigation, and mobile touch support.
22
+ Required for correct tooltip text on graphics sliders (RATING_MATRIX Page 11 fixture).
34
23
 
35
- ## Configuration Logic
36
- - Calculate percentage carefully: `((value - min) / (max - min)) * 100`.
37
- - Ensure the custom thumb is negatively margined (e.g. `-ml-2.5` or `-ml-5`) so that its *center* aligns with the percentage point, not its left edge.
38
- - If `question.ticks` is provided, map out small `w-0.5 h-3 bg-gray-300` vertical lines below the track at the correct percentage intervals.
24
+ ## SDK Integration Checklist
25
+
26
+ - [ ] Copied portable `CustomSliderTrack.tsx`
27
+ - [ ] Used by `SliderMatrixScale` or `CsatMatrixScale` not wired directly in `Question.tsx` for SLIDER_MATRIX
28
+ - [ ] `hasSelectedValue`, `disabled`, `htmlStep` props wired correctly
@@ -1,17 +1,36 @@
1
1
  # Theme & Logo Engineering Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
4
- > **Reference implementation:** `apps/client/src/components/Header.tsx`, `Footer.tsx`
4
+ > **Wizard contract:** [`wizard-preview-build-guide.md`](../00-integration/wizard-preview-build-guide.md) `data-cfm-*` + `var(--cfm-*)`
5
+ > **Monorepo snapshot (do not copy):** `apps/client/src/components/Header.tsx`, `Footer.tsx`
5
6
  > **Target Components**: `Header`, `Footer`
6
7
  > **Handles**: Branding, Mockup Replication
7
8
 
8
9
  ## Core Responsibility
9
10
  When building the survey layout, the client will provide a text prompt or an image mockup. You MUST dynamically build the Theme and Logo using purely CSS and Tailwind to match the requested brand.
10
11
 
12
+ ## Sticky content chrome
13
+
14
+ On the **content** phase, render `Header` inside `SurveyStickyChrome` with `embedded` so the wrapper owns the shadow:
15
+
16
+ ```tsx
17
+ <Header embedded />
18
+ ```
19
+
20
+ Use standalone `Header` (default, with shadow) on intro, end, paused, loading, and terminal pages. See `19-survey-sticky-chrome.md`.
21
+
11
22
  ## Logo Engineering Rules (CRITICAL)
12
- - **NO EXTERNAL IMAGES**: You are strictly forbidden from writing `<img src="url" />` for logos, because you do not have a CDN to host them.
13
- - **CSS-Only Logos**: You MUST construct the company logo using HTML and CSS primitives.
14
- - Example: If the prompt shows the Telekom logo (a white 'T' inside a pink square, or distinct abstract shapes), you must build it using Flexbox, CSS shapes (`rounded-full`, `rounded-tr-3xl`, absolute positioning), and the exact brand colors (`bg-[#e20074] text-white`).
23
+
24
+ Agents must implement **one** of these wizard-compatible patterns in `Header.tsx`:
25
+
26
+ | Mode | When | Required markup |
27
+ |------|------|-----------------|
28
+ | **Image** | Brand asset in `public/` | `<img data-cfm-logo src="./exact-file.svg" />` for AWS deploy (`assetPrefix: './'`). Use `/exact-file.svg` only for local dev. Include hidden `<span data-cfm-logo-text>` fallback. |
29
+ | **Text** | CSS-only logo from prompt (no image file) | `<span data-cfm-logo-text>Company</span>` in logo slot — no `data-cfm-logo` img |
30
+
31
+ - **NO EXTERNAL URLS** for logos (`https://…`, wizard `/assets/` URLs, or `data:image/…`).
32
+ - **CSS-only logos** use `data-cfm-logo-text` (and optional CSS shapes) — wizard shows text until client uploads an image.
33
+ - After wizard upload, set `src="./{global.logo.fileName}"` from saved config — see [`aws-deploy.md`](../00-integration/aws-deploy.md) § Logos & local assets.
15
34
 
16
35
  ## Theme Engineering
17
36
  - Map the primary brand color to the accent and border colors across the entire survey.
@@ -20,20 +20,28 @@ export default function ProgressBar({ progressPercentage }: ProgressBarProps) {
20
20
  const width = Math.min(100, Math.max(0, progressPercentage));
21
21
 
22
22
  return (
23
- <div className="mb-8 h-2 w-full overflow-hidden rounded-full bg-gray-100">
24
- <div
25
- className="h-full bg-[#e20074] transition-all duration-500 ease-out"
26
- style={{ width: `${width}%` }}
27
- />
23
+ <div className="w-full">
24
+ <div className="h-3 w-full rounded-full bg-gray-100">
25
+ <div
26
+ className="h-3 rounded-full bg-[#e20074] transition-all duration-300 ease-out"
27
+ style={{ width: `${width}%` }}
28
+ />
29
+ </div>
28
30
  </div>
29
31
  );
30
32
  }
31
33
  ```
32
34
 
35
+ Vertical spacing (`pt-4` above, `pb-5` below) lives on the **`SurveyStickyChrome`** wrapper — not on `ProgressBar` itself.
36
+
33
37
  ## Wiring
34
38
 
39
+ Place inside **`SurveyStickyChrome`** on the content phase (see `19-survey-sticky-chrome.md`):
40
+
35
41
  ```tsx
36
- <ProgressBar progressPercentage={state.progressPercentage} />
42
+ <SurveyStickyChrome progressPercentage={state.progressPercentage} />
37
43
  ```
38
44
 
45
+ Do not duplicate `ProgressBar` inside scrollable `<main>`.
46
+
39
47
  The SDK computes percentage from **questions on the active skip path**, not total survey pages. Skip logic and answer changes update the bar automatically.
@@ -0,0 +1,79 @@
1
+ # Survey Sticky Chrome
2
+
3
+ > **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
4
+ > **Reference implementation:** `apps/client/src/components/SurveyStickyChrome.tsx`
5
+ > **Template:** [`templates/SurveyStickyChrome.tsx`](../templates/SurveyStickyChrome.tsx)
6
+ > **Component**: `SurveyStickyChrome`
7
+
8
+ ## Purpose
9
+
10
+ Wrap **Header + ProgressBar** in a single `sticky top-0` container so the progress bar stays visible when the respondent scrolls to answer questions lower on the page (especially the last question on a multi-question page).
11
+
12
+ ## Props
13
+
14
+ ```typescript
15
+ type SurveyStickyChromeProps = {
16
+ progressPercentage: number; // from state.progressPercentage
17
+ };
18
+ ```
19
+
20
+ ## Implementation
21
+
22
+ ```tsx
23
+ export default function SurveyStickyChrome({ progressPercentage }: SurveyStickyChromeProps) {
24
+ return (
25
+ <div className="sticky top-0 z-30 bg-white shadow-[0_2px_8px_rgba(0,0,0,0.06)]">
26
+ <Header embedded />
27
+ <div className="mx-auto w-full max-w-4xl px-4 pb-5 pt-4 sm:px-6 lg:px-8">
28
+ <ProgressBar progressPercentage={progressPercentage} />
29
+ </div>
30
+ </div>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ## Spacing (logo → progress bar)
36
+
37
+ Keep a visible gap between the logo block and the progress track — do **not** flush them together.
38
+
39
+ | Token | Tailwind | Purpose |
40
+ |-------|----------|---------|
41
+ | Top inset | `pt-4` | 16px gap below logo / header row |
42
+ | Bottom inset | `pb-5` | 20px gap above scrollable question content |
43
+ | Horizontal | `px-4 sm:px-6 lg:px-8` | Align track with `main` content column (`max-w-4xl`) |
44
+
45
+ ## Header `embedded` prop
46
+
47
+ When `Header` is inside sticky chrome, pass `embedded` to avoid a double shadow:
48
+
49
+ ```tsx
50
+ <header className={`relative h-[120px] w-full bg-white ${embedded ? '' : 'z-10 shadow-[0_2px_8px_rgba(0,0,0,0.06)]'}`}>
51
+ ```
52
+
53
+ Use the standalone `Header` (no `embedded`) on intro, end, loading, and terminal pages.
54
+
55
+ ## SurveyPage wiring (content phase only)
56
+
57
+ ```tsx
58
+ <div className="flex min-h-screen flex-col bg-white">
59
+ <SurveyStickyChrome progressPercentage={state.progressPercentage} />
60
+
61
+ <main className="mx-auto w-full max-w-4xl flex-1 px-4 py-10 sm:px-6 lg:px-8">
62
+ <LanguageSelector ... />
63
+ {/* questions + nav */}
64
+ </main>
65
+
66
+ <Footer />
67
+ </div>
68
+ ```
69
+
70
+ Do **not** render a separate `ProgressBar` inside `<main>` — it belongs in sticky chrome only.
71
+
72
+ ## Agent checklist
73
+
74
+ - [ ] `SurveyStickyChrome` (or equivalent sticky wrapper) on `phase.kind === 'content'`
75
+ - [ ] `ProgressBar` bound to `state.progressPercentage`
76
+ - [ ] `sticky top-0 z-30 bg-white` on wrapper; single shadow on wrapper, not Header
77
+ - [ ] `pt-4` gap between logo and progress track; `pb-5` below track
78
+ - [ ] Progress track aligned to same `max-w-4xl` as question content
79
+ - [ ] Standalone `Header` on intro / end / loading / terminal branches
@@ -15,8 +15,7 @@ SurveyPage (phase router)
15
15
  ├── IntroPage / PausedPage / EndPage (special phases)
16
16
 
17
17
  └── SurveyContent (phase.kind === 'content')
18
- ├── Header
19
- ├── ProgressBar
18
+ ├── SurveyStickyChrome (sticky Header + ProgressBar)
20
19
  ├── LanguageSelector
21
20
  ├── Question × N (dispatcher)
22
21
  ├── Back / Next|Submit nav
@@ -62,6 +61,7 @@ Shared: `CustomSliderTrack`, `MatrixDropdown`
62
61
  | `09-custom-slider-track.md` | `CustomSliderTrack` |
63
62
  | `10-header-footer.md` | `Header` & `Footer` |
64
63
  | `11-progress-bar.md` | `ProgressBar` |
64
+ | `19-survey-sticky-chrome.md` | `SurveyStickyChrome` (sticky Header + ProgressBar) |
65
65
  | `12-language-selector.md` | `LanguageSelector` |
66
66
  | `13-matrix-dropdown.md` | `MatrixDropdown` |
67
67
  | `14-intro-page.md` | `IntroPage` |