@explorer02/cfm-survey-sdk 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +18 -18
- package/dist/cli/index.mjs +14 -14
- package/dist/index.d.mts +465 -580
- package/dist/index.d.ts +465 -580
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +6 -4
- package/postinstall.js +96 -64
- package/templates/AGENT.md +15 -7
- package/templates/docs/00-integration/answer-logic-and-navigation.md +50 -0
- package/templates/docs/00-integration/aws-deploy.md +281 -0
- package/templates/docs/00-integration/client-integration-guide.md +351 -0
- package/templates/docs/00-integration/component-checklist.md +84 -0
- package/templates/docs/00-integration/constraints.md +65 -3
- package/templates/docs/00-integration/custom-field-logic-and-navigation.md +174 -0
- package/templates/docs/00-integration/display-logic-and-navigation.md +49 -0
- package/templates/docs/00-integration/end-page-logic.md +64 -0
- package/templates/docs/00-integration/partial-save-and-recovery.md +74 -0
- package/templates/docs/00-integration/placeholders-and-tokens.md +116 -0
- package/templates/docs/00-integration/progress.md +61 -0
- package/templates/docs/00-integration/question-display-variants.md +65 -0
- package/templates/docs/00-integration/question-numbering.md +51 -0
- package/templates/docs/00-integration/question-type-sdk-matrix.md +242 -0
- package/templates/docs/00-integration/setup.md +84 -0
- package/templates/docs/00-integration/skip-logic-and-navigation.md +80 -0
- package/templates/docs/00-integration/survey-lifecycle-analytics.md +94 -0
- package/templates/docs/00-integration/useSurveySDK.md +151 -23
- package/templates/docs/01-components/01-survey-page.md +76 -54
- package/templates/docs/01-components/02-question.md +77 -89
- package/templates/docs/01-components/03-rating-scale.md +11 -1
- package/templates/docs/01-components/04-star-rating-scale.md +45 -0
- package/templates/docs/01-components/05-csat-matrix-scale.md +20 -11
- package/templates/docs/01-components/06-likert-matrix-scale.md +12 -3
- package/templates/docs/01-components/07-slider-matrix-scale.md +11 -2
- package/templates/docs/01-components/08-file-upload-scale.md +10 -1
- package/templates/docs/01-components/09-custom-slider-track.md +2 -2
- package/templates/docs/01-components/11-progress-bar.md +10 -4
- package/templates/docs/01-components/13-matrix-dropdown.md +3 -1
- package/templates/docs/01-components/14-intro-page.md +34 -0
- package/templates/docs/01-components/15-end-page.md +31 -0
- package/templates/docs/01-components/16-paused-page.md +27 -0
- package/templates/docs/01-components/17-heatmap-scale.md +75 -0
- package/templates/docs/01-components/18-rank-order-scale.md +100 -0
- package/templates/docs/01-components/README.md +44 -36
- package/templates/docs/02-reference/config-field-index.md +141 -139
- package/templates/docs/02-reference/exports.md +149 -0
- package/templates/docs/02-reference/question-types/01-rating.md +31 -32
- package/templates/docs/02-reference/question-types/04-csat.md +51 -42
- package/templates/docs/02-reference/question-types/05-rating-scale.md +16 -11
- package/templates/docs/02-reference/question-types/06-slider.md +2 -2
- package/templates/docs/02-reference/question-types/07-matrix-cfm.md +44 -30
- package/templates/docs/02-reference/question-types/08-matrix-csat.md +4 -26
- package/templates/docs/02-reference/question-types/09-matrix-rating.md +29 -13
- package/templates/docs/02-reference/question-types/10-slider-matrix.md +15 -4
- package/templates/docs/02-reference/question-types/11-file-upload.md +13 -2
- package/templates/docs/02-reference/question-types/12-heatmap.md +59 -0
- package/templates/docs/02-reference/question-types/13-rank-order.md +101 -0
- package/templates/docs/02-reference/question-types/README.md +86 -47
- package/templates/docs/02-reference/routing-table.md +33 -18
- package/templates/docs/02-reference/value-derivation.md +63 -0
- package/templates/docs/03-ui-specs/00-question-shell.md +24 -80
- package/templates/docs/03-ui-specs/01-rating.md +12 -54
- package/templates/docs/03-ui-specs/02-radio.md +21 -33
- package/templates/docs/03-ui-specs/03-text.md +2 -2
- package/templates/docs/03-ui-specs/04-csat.md +38 -33
- package/templates/docs/03-ui-specs/05-rating-scale.md +21 -18
- package/templates/docs/03-ui-specs/06-slider.md +3 -1
- package/templates/docs/03-ui-specs/07-matrix-cfm.md +25 -22
- package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +18 -46
- package/templates/docs/03-ui-specs/09-slider-matrix.md +2 -2
- package/templates/docs/03-ui-specs/10-file-upload.md +2 -2
- package/templates/docs/03-ui-specs/11-text-and-media.md +29 -29
- package/templates/docs/03-ui-specs/12-survey-chrome.md +20 -14
- package/templates/docs/03-ui-specs/13-heatmap.md +73 -0
- package/templates/docs/03-ui-specs/14-rank-order.md +59 -0
- package/templates/docs/03-ui-specs/README.md +14 -13
- package/templates/docs/03-ui-specs/shared/custom-slider-track.md +3 -3
- package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +6 -1
- package/templates/docs/MANIFEST.json +454 -67
- package/templates/docs/index.md +84 -29
- package/templates/docs/templates/Question.tsx +310 -0
- package/templates/docs/templates/deploy-to-aws.sh +114 -0
- package/templates/docs/templates/heatmapCoords.ts +58 -0
- package/templates/docs/templates/implementation_plan.md +115 -49
- package/templates/docs/templates/survey-inventory.schema.json +72 -0
- package/templates/docs/templates/verify-agent-build.sh +53 -0
- package/templates/docs/01-components/04-csat-scale.md +0 -40
- package/templates/docs/02-question-types/01-rating.md +0 -44
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
# UI Spec:
|
|
1
|
+
# UI Spec: MCQ (`type: 'MCQ'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
> **SDK type:** `RadioQuestion` — `fetchSurvey/types/radio.ts`
|
|
3
|
+
> **SDK type:** `McqQuestion` — `fetchSurvey/types/mcq.ts`
|
|
5
4
|
> **Component blueprint:** `01-components/02-question.md`
|
|
6
5
|
|
|
7
6
|
## Config Inventory
|
|
@@ -9,43 +8,26 @@
|
|
|
9
8
|
| Field | Implemented | UI impact |
|
|
10
9
|
|-------|-------------|-----------|
|
|
11
10
|
| `options[]` | ✅ | One card per option |
|
|
12
|
-
| `options[].
|
|
13
|
-
| `options[].
|
|
14
|
-
| `isMultiSelect` |
|
|
11
|
+
| `options[].optionLabel` | ✅ | HTML via `dangerouslySetInnerHTML` |
|
|
12
|
+
| `options[].id` | ✅ | **Submit value** — selected answer is `option.id` |
|
|
13
|
+
| `isMultiSelect` | ⚠️ | SDK supports multi-select; implement checkbox cards when `true` |
|
|
15
14
|
|
|
16
|
-
##
|
|
15
|
+
## Multi-select note
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
┌─ label (full-width card) ─────────────────────────────────────┐
|
|
20
|
-
│ (●) Option label HTML │ selected
|
|
21
|
-
└───────────────────────────────────────────────────────────────┘
|
|
22
|
-
┌─ label ───────────────────────────────────────────────────────┐
|
|
23
|
-
│ (○) Option label HTML │ unselected
|
|
24
|
-
└───────────────────────────────────────────────────────────────┘
|
|
25
|
-
```
|
|
17
|
+
When `question.isMultiSelect === true`:
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
- Answer shape is `string[]` (or `number[]`) of selected `option.id` values
|
|
20
|
+
- Toggle options on click; do not replace the entire array with a single id
|
|
21
|
+
- onAction CHANGE: `{ questionId, answerValue: selectedIds[] }`
|
|
22
|
+
- Still filter via `getVisibleMcqOptionsForAnswers` before render
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|-------|--------|------------|-------|
|
|
31
|
-
| Unselected | `#e5e5e5` | `white` | `border-gray-400`, empty |
|
|
32
|
-
| Unselected hover | `#e5e5e5` | `hover:bg-gray-50/50` | — |
|
|
33
|
-
| Selected | `#e20074` | `#fdf2f8` | `border-2 border-[#e20074]` + inner dot `h-2.5 w-2.5 bg-[#e20074]` |
|
|
34
|
-
|
|
35
|
-
## Structure
|
|
36
|
-
|
|
37
|
-
```tsx
|
|
38
|
-
<label className="flex items-center gap-4 rounded-lg border py-4 px-5 cursor-pointer">
|
|
39
|
-
<input type="radio" name={question.id} className="sr-only" />
|
|
40
|
-
<div className="h-5 w-5 rounded-full border ..."> {/* custom radio */} </div>
|
|
41
|
-
<span dangerouslySetInnerHTML={{ __html: option.label }} />
|
|
42
|
-
</label>
|
|
43
|
-
```
|
|
24
|
+
When `false` (default), single-select radio behavior applies.
|
|
44
25
|
|
|
45
26
|
## Answer Mutation
|
|
46
27
|
|
|
47
28
|
```typescript
|
|
48
|
-
onSelect(option.
|
|
29
|
+
onSelect(option.id) // string
|
|
30
|
+
// → onAction CHANGE { questionId, answerValue: string }
|
|
49
31
|
```
|
|
50
32
|
|
|
51
33
|
## Agent Checklist
|
|
@@ -54,4 +36,10 @@ onSelect(option.value) // string | number
|
|
|
54
36
|
- [ ] Custom circular radio indicator (not browser default)
|
|
55
37
|
- [ ] Selected: magenta border + pink background
|
|
56
38
|
- [ ] HTML labels
|
|
57
|
-
- [ ] If `isMultiSelect`: implement checkbox cards
|
|
39
|
+
- [ ] If `isMultiSelect`: implement checkbox cards with array answer shape
|
|
40
|
+
|
|
41
|
+
## SDK Integration Checklist (agent)
|
|
42
|
+
|
|
43
|
+
- [ ] Visibility helper: `getVisibleMcqOptionsForAnswers`
|
|
44
|
+
- [ ] Store `option.id` — never option label text
|
|
45
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#mcq`](../00-integration/question-type-sdk-matrix.md#mcq)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# UI Spec: Text Field (`type: '
|
|
1
|
+
# UI Spec: Text Field (`type: 'TEXTFIELD'`)
|
|
2
2
|
|
|
3
3
|
> **Reference:** `apps/client/src/components/Question.tsx` (inline)
|
|
4
|
-
> **SDK type:** `
|
|
4
|
+
> **SDK type:** `TextInputQuestion` — `fetchSurvey/types/text.ts`
|
|
5
5
|
|
|
6
6
|
## Config Inventory
|
|
7
7
|
|
|
@@ -1,57 +1,62 @@
|
|
|
1
|
-
# UI Spec: CSAT (`type: '
|
|
1
|
+
# UI Spec: CSAT Matrix Grid (`type: 'CSAT_MATRIX'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/CsatMatrixScale.tsx`
|
|
4
4
|
> **SDK type:** `CsatQuestion` — `fetchSurvey/types/csat.ts`
|
|
5
|
-
> **Component blueprint:** `01-components/
|
|
5
|
+
> **Component blueprint:** `01-components/05-csat-matrix-scale.md`
|
|
6
6
|
|
|
7
7
|
## Config Inventory
|
|
8
8
|
|
|
9
9
|
| Field | Implemented | UI impact |
|
|
10
10
|
|-------|-------------|-----------|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
11
|
+
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid / carousel / dropdown data |
|
|
12
|
+
| `displayStyle` | ✅ | Grid vs vertical list routing |
|
|
13
|
+
| `gridLayout` | ✅ | carousel vs standard vs dropdown |
|
|
14
|
+
| `hasNotApplicableOption` | ✅ | Checkbox below row (vertical) or N/A column (grid) |
|
|
15
|
+
| `reverseScaleOrder` | ✅ | Flip emoji/star/number index |
|
|
16
|
+
| `scaleAnchorLabels[]` | ✅ | Anchor row above columns |
|
|
17
17
|
|
|
18
18
|
## Config Decision Tree
|
|
19
19
|
|
|
20
20
|
```
|
|
21
|
-
|
|
22
|
-
├──
|
|
23
|
-
|
|
24
|
-
│
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
CSAT_MATRIX
|
|
22
|
+
├── gridLayout === 'carousel' → CsatMatrixCarousel
|
|
23
|
+
├── displayStyle in (dropdown, selectbox) → CsatMatrixVerticalList
|
|
24
|
+
│ ├── dropdown → MatrixDropdown placeholder "Select..."
|
|
25
|
+
│ ├── selectbox → full-width cards #fdf2f8 selected
|
|
26
|
+
│ └── else → card radio list
|
|
27
|
+
└── else → CsatMatrixGrid
|
|
28
|
+
├── displayStyle === 'emoji' → client-side emoji icons (react-icons) + hover scale + ring when selected
|
|
29
|
+
├── displayStyle === 'star' → opacity 0.5 unselected; filled/empty icons
|
|
30
|
+
├── displayStyle === 'numbered' → 40×40 badge; selected magenta fill
|
|
31
|
+
└── displayStyle === 'graphics' → CustomSliderTrack per row (carousel)
|
|
31
32
|
```
|
|
32
33
|
|
|
33
|
-
##
|
|
34
|
+
## Grid Structure
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| Selected | `border-[#e20074]` | `#fdf2f8` | `scale-110` |
|
|
40
|
-
| Star mode | transparent | transparent | no border/bg on button |
|
|
36
|
+
- Row label: `180px` fixed column
|
|
37
|
+
- N/A column: `56px` when `hasNotApplicableOption`
|
|
38
|
+
- Alternating row backgrounds
|
|
39
|
+
- Column headers OR `scaleAnchorLabels[]` anchor row with percentage positioning
|
|
41
40
|
|
|
42
|
-
##
|
|
41
|
+
## Carousel
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
- Pink active dot expands: `20px × 8px` vs `8px` inactive
|
|
44
|
+
- Column labels below cells
|
|
45
|
+
- Emoji tooltip via hover state
|
|
46
|
+
- Graphics mode: slider maps column index
|
|
45
47
|
|
|
46
48
|
## Answer Mutation
|
|
47
49
|
|
|
50
|
+
Store **`column.id`** via `matrixColumnStoredValue(col)` — see `02-reference/value-derivation.md`.
|
|
51
|
+
|
|
48
52
|
```typescript
|
|
49
|
-
onSelect(
|
|
53
|
+
onSelect(MatrixRowAnswers)
|
|
54
|
+
onAction({ type: 'CHANGE', payload: { questionId, answerValue: MatrixRowAnswers } })
|
|
50
55
|
```
|
|
51
56
|
|
|
52
57
|
## Agent Checklist
|
|
53
58
|
|
|
54
|
-
- [ ]
|
|
55
|
-
- [ ]
|
|
56
|
-
- [ ]
|
|
57
|
-
- [ ]
|
|
59
|
+
- [ ] All displayStyle × gridLayout branches
|
|
60
|
+
- [ ] reverseScaleOrder on emoji/star/numbered
|
|
61
|
+
- [ ] Carousel pink dot indicators
|
|
62
|
+
- [ ] Vertical selectbox cards match MCQ card styling
|
|
@@ -1,49 +1,52 @@
|
|
|
1
|
-
# UI Spec: Rating
|
|
1
|
+
# UI Spec: Star Rating (`type: 'star_rating'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
5
|
-
> **
|
|
3
|
+
> **DEPRECATED:** Not in the current `SurveyQuestion` union. API RATING pages map to `NPS_SCALE` or CSAT/RATING matrix display styles. See `01-rating.md` and matrix specs instead.
|
|
4
|
+
>
|
|
5
|
+
> **SDK type:** `StarRatingQuestion` — `fetchSurvey/types/starRating.ts`
|
|
6
|
+
> **Component blueprint:** `01-components/04-star-rating-scale.md`
|
|
7
|
+
> **Routed in template:** `Question.tsx` → `RatingScale` for `star_rating`
|
|
6
8
|
|
|
7
9
|
## Config Inventory
|
|
8
10
|
|
|
9
11
|
| Field | Implemented | UI impact |
|
|
10
12
|
|-------|-------------|-----------|
|
|
11
|
-
| `options[]` |
|
|
12
|
-
| `scaleStyle` |
|
|
13
|
-
| `minLabel` / `midLabel` / `maxLabel` |
|
|
14
|
-
| `midLabelIndex` |
|
|
15
|
-
| `reverseScaleOrder` |
|
|
13
|
+
| `options[]` | ✅ | Star/emoji buttons (no NPS traffic-light colors) |
|
|
14
|
+
| `scaleStyle` | ✅ | `'star'` or `'emoji'` |
|
|
15
|
+
| `minLabel` / `midLabel` / `maxLabel` | ✅ | Same formulas as NPS in Question shell |
|
|
16
|
+
| `midLabelIndex` | ✅ | `(midLabelIndex / (options.length - 1)) * 100`% |
|
|
17
|
+
| `reverseScaleOrder` | partial | Reverse options before render |
|
|
16
18
|
|
|
17
|
-
## Expected UI
|
|
19
|
+
## Expected UI
|
|
18
20
|
|
|
19
21
|
```
|
|
20
22
|
[minLabel] [midLabel @ %] [maxLabel]
|
|
21
23
|
┌────┬────┬────┬────┬────┐
|
|
22
24
|
│ ★ │ ★ │ ★ │ ★ │ ★ │ scaleStyle: 'star'
|
|
23
25
|
└────┴────┴────┴────┴────┘
|
|
24
|
-
OR emoji row from
|
|
26
|
+
OR emoji row from client-side react-icons when scaleStyle: 'emoji'
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
Selected styling: `border-[#e20074] bg-[#fdf2f8] scale-110`.
|
|
28
30
|
|
|
29
31
|
## Config Decision Tree
|
|
30
32
|
|
|
31
33
|
```
|
|
32
|
-
|
|
33
|
-
├── scaleStyle === 'star' →
|
|
34
|
-
├── scaleStyle === 'emoji' →
|
|
35
|
-
└── else → numbered badges (no
|
|
34
|
+
star_rating
|
|
35
|
+
├── scaleStyle === 'star' → client-side star icons (react-icons) per option
|
|
36
|
+
├── scaleStyle === 'emoji' → client-side emoji icons (react-icons)
|
|
37
|
+
└── else → numbered badges (no accentColor)
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
## Answer Mutation
|
|
39
41
|
|
|
40
42
|
```typescript
|
|
41
43
|
onSelect(number)
|
|
44
|
+
onAction({ type: 'CHANGE', payload: { questionId, answerValue: number } })
|
|
42
45
|
```
|
|
43
46
|
|
|
44
47
|
## Agent Checklist
|
|
45
48
|
|
|
46
|
-
- [ ]
|
|
49
|
+
- [ ] Dispatcher branch for `star_rating` in Question.tsx
|
|
47
50
|
- [ ] No traffic-light NPS colors on options
|
|
48
51
|
- [ ] min/mid/max labels with midLabelIndex formula
|
|
49
|
-
- [ ] Match
|
|
52
|
+
- [ ] Match star/emoji bounding box rules from `04-star-rating-scale.md`
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# UI Spec: Standalone Slider (`type: 'slider'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **DEPRECATED:** Not in the current `SurveyQuestion` union. API SLIDER pages map to `SLIDER_MATRIX` or matrix `displayStyle: 'graphics'`. See `10-slider-matrix.md` and `CustomSliderTrack` specs.
|
|
4
|
+
>
|
|
5
|
+
> **Reference:** Standalone slider is not routed in current `Question.tsx`
|
|
4
6
|
> **SDK type:** `SliderQuestion` — `fetchSurvey/types/slider.ts`
|
|
5
7
|
> **Component blueprint:** `01-components/09-custom-slider-track.md`
|
|
6
8
|
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
# UI Spec: CFM Matrix (`type: '
|
|
1
|
+
# UI Spec: CFM Matrix (`type: 'CFM_MATRIX'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
> **SDK type:** `
|
|
3
|
+
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/LikertMatrixScale.tsx`
|
|
4
|
+
> **SDK type:** `CfmMatrixQuestion` — `fetchSurvey/types/cfmMatrix.ts`
|
|
5
5
|
> **Component blueprint:** `01-components/06-likert-matrix-scale.md`
|
|
6
6
|
|
|
7
7
|
## Config Inventory
|
|
8
8
|
|
|
9
9
|
| Field | Implemented | UI impact |
|
|
10
10
|
|-------|-------------|-----------|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
11
|
+
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid / carousel / dropdown data |
|
|
12
|
+
| `gridLayout` | ✅ | Orchestrator routing |
|
|
13
|
+
| `selectionMode` | ✅ | `'multiple'` → checkboxes |
|
|
14
|
+
| `statementLayout: 'bipolar'` | ✅ | 25% left + 25% right columns |
|
|
15
|
+
| `scaleAnchorLabels[]` | ✅ | Anchor labels above scale |
|
|
16
16
|
| `transposeTable` | ✅ | Swap row/col iteration |
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
17
|
+
| `repeatColumnHeaders` | ✅ | Re-render header per row |
|
|
18
|
+
| `showColumnHeaders` | ✅ | Show/hide column header row |
|
|
19
|
+
| `hasNotApplicableOption` | ✅ | N/A column with mutex |
|
|
20
20
|
|
|
21
21
|
## Config Decision Tree
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
CFM_MATRIX
|
|
25
|
-
├──
|
|
26
|
-
├──
|
|
25
|
+
├── gridLayout === 'dropdown' → LikertMatrixDropdownLayout (MatrixDropdown per row)
|
|
26
|
+
├── gridLayout === 'carousel' → LikertMatrixCarouselLayout (one row, prev/next, blue dots)
|
|
27
27
|
└── else → LikertMatrixGridLayout
|
|
28
28
|
├── transposeTable ? columns×rows : rows×columns
|
|
29
|
-
├──
|
|
30
|
-
├──
|
|
31
|
-
└──
|
|
29
|
+
├── repeatColumnHeaders ? header per row : single header
|
|
30
|
+
├── showColumnHeaders ? column headers : hidden
|
|
31
|
+
└── statementLayout === 'bipolar' ? 25% | scale | 25% layout
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## Interactive Behaviors
|
|
@@ -37,8 +37,8 @@ CFM_MATRIX
|
|
|
37
37
|
|----------|-----------|--------|
|
|
38
38
|
| Click selected radio again | single-select grid/carousel | Clear row (`undefined`) |
|
|
39
39
|
| N/A selected | any | Other cols opacity 0.4, disabled |
|
|
40
|
-
| Multi N/A |
|
|
41
|
-
| Multi toggle |
|
|
40
|
+
| Multi N/A | selectionMode multiple | Sets `[null]` exclusively |
|
|
41
|
+
| Multi toggle | selectionMode multiple | Add/remove from array |
|
|
42
42
|
|
|
43
43
|
## Grid Layout
|
|
44
44
|
|
|
@@ -53,14 +53,17 @@ CFM_MATRIX
|
|
|
53
53
|
|
|
54
54
|
## Answer Mutation
|
|
55
55
|
|
|
56
|
+
Store **`column.id`** via `matrixColumnStoredValue(col)` — see `02-reference/value-derivation.md`.
|
|
57
|
+
|
|
56
58
|
```typescript
|
|
57
|
-
onSelect(
|
|
59
|
+
onSelect(MatrixRowAnswers) // Record<statementRows[].id, value | value[] | null>
|
|
60
|
+
onAction({ type: 'CHANGE', payload: { questionId, answerValue: MatrixRowAnswers } })
|
|
58
61
|
```
|
|
59
62
|
|
|
60
63
|
## Agent Checklist
|
|
61
64
|
|
|
62
|
-
- [ ] All three
|
|
63
|
-
- [ ] transposeTable +
|
|
64
|
-
- [ ] Bipolar 25/50/25 layout with
|
|
65
|
+
- [ ] All three gridLayout branches
|
|
66
|
+
- [ ] transposeTable + repeatColumnHeaders + showColumnHeaders
|
|
67
|
+
- [ ] Bipolar 25/50/25 layout with oppositeStatementText
|
|
65
68
|
- [ ] Single-select deselect on re-click
|
|
66
69
|
- [ ] N/A mutex with opacity 0.4
|
|
@@ -1,65 +1,37 @@
|
|
|
1
|
-
# UI Spec:
|
|
1
|
+
# UI Spec: Rating Matrix Grid (`type: 'RATING_MATRIX'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
> **SDK type:** `
|
|
3
|
+
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/CsatMatrixScale.tsx`
|
|
4
|
+
> **SDK type:** `RatingMatrixQuestion` — `fetchSurvey/types/ratingMatrix.ts`
|
|
5
5
|
> **Component blueprint:** `01-components/05-csat-matrix-scale.md`
|
|
6
6
|
|
|
7
7
|
## Config Inventory
|
|
8
8
|
|
|
9
9
|
| Field | Implemented | UI impact |
|
|
10
10
|
|-------|-------------|-----------|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
11
|
+
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid / carousel / dropdown data |
|
|
12
|
+
| `displayStyle` | ✅ | Grid vs vertical list routing |
|
|
13
|
+
| `gridLayout` | ✅ | carousel vs standard vs dropdown |
|
|
14
|
+
| `hasNotApplicableOption` | ✅ | N/A column or checkbox |
|
|
14
15
|
| `reverseScaleOrder` | ✅ | Flip emoji/star/number index |
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
16
|
+
| `scaleAnchorLabels[]` | ✅ | Anchor row above columns |
|
|
17
|
+
| `minLabel` / `maxLabel` | ✅ | Rendered in Question.tsx above scale |
|
|
18
|
+
| `scaleColumns[].accentColor` | ✅ | NPS-style badge colors when numbered |
|
|
17
19
|
|
|
18
20
|
## Config Decision Tree
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
CSAT_MATRIX | RATING_MATRIX
|
|
22
|
-
├── matrixFormat === 'carousel' → CsatMatrixCarousel
|
|
23
|
-
├── buttonType in (dropdown, selectbox, radio) → CsatMatrixVerticalList
|
|
24
|
-
│ ├── dropdown → MatrixDropdown placeholder "Select..."
|
|
25
|
-
│ ├── selectbox → full-width cards #fdf2f8 selected
|
|
26
|
-
│ └── else → card radio list
|
|
27
|
-
└── else → CsatMatrixGrid
|
|
28
|
-
├── emoji → getEmojiForIndex + hover scale + ring when selected
|
|
29
|
-
├── star → opacity 0.5 unselected; filled/empty icons
|
|
30
|
-
├── numbered → 40×40 badge; selected magenta fill
|
|
31
|
-
└── graphics → CustomSliderTrack per row (not in grid — carousel only)
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Grid Structure
|
|
35
|
-
|
|
36
|
-
- Row label: `180px` fixed column
|
|
37
|
-
- N/A column: `56px` when `hasNotApplicable`
|
|
38
|
-
- Alternating row backgrounds
|
|
39
|
-
- Column headers OR `labels[]` anchor row with percentage positioning
|
|
40
|
-
|
|
41
|
-
## Carousel
|
|
42
|
-
|
|
43
|
-
- Pink active dot expands: `20px × 8px` vs `8px` inactive
|
|
44
|
-
- Column labels below cells
|
|
45
|
-
- Emoji tooltip via hover state (`hoveredCell`)
|
|
46
|
-
- Graphics mode: slider maps column index
|
|
47
|
-
|
|
48
|
-
## Tooltips
|
|
49
|
-
|
|
50
|
-
`title={col.label}` recommended on grid cells (emoji hover uses scale transform).
|
|
22
|
+
Same as `CSAT_MATRIX` grid — reuse `CsatMatrixScale` component. See [04-csat.md](04-csat.md) decision tree with `type: 'RATING_MATRIX'`.
|
|
51
23
|
|
|
52
24
|
## Answer Mutation
|
|
53
25
|
|
|
26
|
+
Store **`column.id`** via `matrixColumnStoredValue(col)` — see `02-reference/value-derivation.md`.
|
|
27
|
+
|
|
54
28
|
```typescript
|
|
55
|
-
onSelect(
|
|
56
|
-
|
|
29
|
+
onSelect(MatrixRowAnswers)
|
|
30
|
+
onAction({ type: 'CHANGE', payload: { questionId, answerValue: MatrixRowAnswers } })
|
|
57
31
|
```
|
|
58
32
|
|
|
59
33
|
## Agent Checklist
|
|
60
34
|
|
|
61
|
-
- [ ]
|
|
62
|
-
- [ ]
|
|
63
|
-
- [ ]
|
|
64
|
-
- [ ] Carousel pink dot indicators
|
|
65
|
-
- [ ] Vertical selectbox cards match standalone radio styling
|
|
35
|
+
- [ ] Reuse CsatMatrixScale with `RATING_MATRIX` type
|
|
36
|
+
- [ ] Apply accentColor on numbered badges when present
|
|
37
|
+
- [ ] minLabel / maxLabel above grid when set
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# UI Spec: Slider Matrix (`type: '
|
|
1
|
+
# UI Spec: Slider Matrix (`type: 'SLIDER_MATRIX'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/SliderMatrixScale.tsx`
|
|
4
4
|
> **SDK type:** `SliderMatrixQuestion` — `fetchSurvey/types/sliderMatrix.ts`
|
|
5
5
|
> **Component blueprint:** `01-components/07-slider-matrix-scale.md`
|
|
6
6
|
> **Shared:** [`shared/custom-slider-track.md`](shared/custom-slider-track.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# UI Spec: File Upload (`type: '
|
|
1
|
+
# UI Spec: File Upload (`type: 'FILE_UPLOAD'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/FileUploadScale.tsx`
|
|
4
4
|
> **SDK type:** `FileUploadQuestion` — `fetchSurvey/types/fileUpload.ts`
|
|
5
5
|
> **Component blueprint:** `01-components/08-file-upload-scale.md`
|
|
6
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# UI Spec: Text and Media (`type: '
|
|
1
|
+
# UI Spec: Text and Media (`type: 'TEXT_AND_MEDIA'`)
|
|
2
2
|
|
|
3
3
|
> **Reference:** `apps/client/src/components/Question.tsx` (inline)
|
|
4
4
|
> **SDK type:** `TextAndMediaQuestion` — `fetchSurvey/types/textAndMedia.ts`
|
|
@@ -8,42 +8,42 @@
|
|
|
8
8
|
|
|
9
9
|
| Field | Implemented | UI impact |
|
|
10
10
|
|-------|-------------|-----------|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
11
|
+
| `media?.url` | ✅ | Required to render media block |
|
|
12
|
+
| `media?.mimeType` | ✅ | `video/` or `VIDEO` → `<video>`; else `<img>` |
|
|
13
|
+
| `media?.title` | ✅ | Caption below media, centered gray text |
|
|
14
|
+
| `media?.alignment` | ✅ | Flex justify: CENTER / RIGHT / default left |
|
|
15
|
+
| `media?.size` | ✅ | Width `%` on media container; default 100% |
|
|
16
16
|
|
|
17
17
|
No answer input — display-only question.
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Media alignment anatomy
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
│
|
|
27
|
-
│
|
|
28
|
-
│
|
|
29
|
-
|
|
22
|
+
┌─────────────────────────────────────────┐
|
|
23
|
+
│ questionText (HTML) │
|
|
24
|
+
│ questionDescription (optional) │
|
|
25
|
+
├─────────────────────────────────────────┤
|
|
26
|
+
│ [ media block — flex container ] │
|
|
27
|
+
│ alignment LEFT: justify-start │
|
|
28
|
+
│ alignment CENTER: justify-center │
|
|
29
|
+
│ alignment RIGHT: justify-end │
|
|
30
|
+
│ width: media.size % (default 100) │
|
|
31
|
+
│ <img> or <video> max-w-full │
|
|
32
|
+
│ caption: media.title (centered gray) │
|
|
33
|
+
└─────────────────────────────────────────┘
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
| `mediaAlignment` | CSS |
|
|
35
|
-
|------------------|-----|
|
|
36
|
-
| `TOP_CENTER`, `BOTTOM_CENTER` | `justify-center` |
|
|
37
|
-
| `TOP_RIGHT`, `BOTTOM_RIGHT` | `justify-end` |
|
|
38
|
-
| default | `justify-start` |
|
|
39
|
-
|
|
40
|
-
## Answer Mutation
|
|
41
|
-
|
|
42
|
-
None — no `onSelect` payload for this type.
|
|
36
|
+
Container-level media (survey chrome) is separate — see `03-ui-specs/12-survey-chrome.md`.
|
|
43
37
|
|
|
44
38
|
## Agent Checklist
|
|
45
39
|
|
|
46
|
-
- [ ] Render only when `
|
|
47
|
-
- [ ] Video vs image from
|
|
40
|
+
- [ ] Render only when `question.media?.url` is present
|
|
41
|
+
- [ ] Video vs image from `media.mimeType`
|
|
48
42
|
- [ ] `max-w-full rounded-md shadow-sm` on media element
|
|
49
|
-
- [ ] Optional centered caption for `
|
|
43
|
+
- [ ] Optional centered caption for `media.title`
|
|
44
|
+
- [ ] Respect `media.alignment` (LEFT / CENTER / RIGHT) and `media.size` width %
|
|
45
|
+
|
|
46
|
+
## SDK Integration Checklist (agent)
|
|
47
|
+
|
|
48
|
+
- [ ] No CHANGE action — display-only
|
|
49
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#text_and_media`](../00-integration/question-type-sdk-matrix.md#text_and_media)
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
# UI Spec: Survey Chrome
|
|
2
2
|
|
|
3
|
-
> **Reference:** `SurveyPage.tsx`, `Header.tsx`, `Footer.tsx`, `ProgressBar.tsx`, `LanguageSelector.tsx`
|
|
4
|
-
> **Component blueprints:** `01-components/01-survey-page.md`, `10-header-footer.md`, `11-progress-bar.md`, `12-language-selector.md`
|
|
3
|
+
> **Reference:** `SurveyPage.tsx`, `Header.tsx`, `Footer.tsx`, `ProgressBar.tsx`, `LanguageSelector.tsx`, `IntroPage.tsx`, `EndPage.tsx`, `PausedPage.tsx`
|
|
4
|
+
> **Component blueprints:** `01-components/01-survey-page.md`, `10-header-footer.md`, `11-progress-bar.md`, `12-language-selector.md`, `14-intro-page.md`, `15-end-page.md`, `16-paused-page.md`
|
|
5
5
|
|
|
6
|
-
## SurveyPage —
|
|
6
|
+
## SurveyPage — Phase Router
|
|
7
7
|
|
|
8
8
|
| State | Condition | UI |
|
|
9
9
|
|-------|-----------|-----|
|
|
10
10
|
| Loading | `surveyQueryResults.isLoading` | Centered "Loading..." |
|
|
11
11
|
| Error | `surveyQueryResults.error` | Red error message |
|
|
12
12
|
| Empty | `!survey` | "No survey data" |
|
|
13
|
-
|
|
|
14
|
-
|
|
|
13
|
+
| Intro | `state.phase.kind === 'intro'` | `IntroPage` + Start CTA |
|
|
14
|
+
| Paused | `state.phase.kind === 'paused'` | `PausedPage` |
|
|
15
|
+
| End | `state.phase.kind === 'end'` | `EndPage` |
|
|
16
|
+
| Submitted | `state.phase.kind === 'submitted'` | Terminal message |
|
|
17
|
+
| Expired | `state.phase.kind === 'expired'` | Terminal message |
|
|
18
|
+
| Quota | `state.phase.kind === 'quota_fulfilled'` | Terminal message |
|
|
19
|
+
| Active | `state.phase.kind === 'content'` | Questions + nav |
|
|
15
20
|
|
|
16
21
|
## Active Layout
|
|
17
22
|
|
|
@@ -22,7 +27,7 @@ main (max-w-4xl)
|
|
|
22
27
|
LanguageSelector (hidden if ≤1 language)
|
|
23
28
|
Questions (space-y-12)
|
|
24
29
|
Submit error (if any)
|
|
25
|
-
Nav: Back (if
|
|
30
|
+
Nav: Back (if canGoBack) + Next/Submit (primaryButtonType)
|
|
26
31
|
Footer
|
|
27
32
|
```
|
|
28
33
|
|
|
@@ -32,10 +37,10 @@ German when `survey.language.startsWith('de')`:
|
|
|
32
37
|
|
|
33
38
|
| Action | EN | DE |
|
|
34
39
|
|--------|----|----|
|
|
35
|
-
| Back |
|
|
36
|
-
| Next |
|
|
37
|
-
| Submit |
|
|
38
|
-
| Submitting |
|
|
40
|
+
| Back | Back | Zurück |
|
|
41
|
+
| Next | Next | Weiter |
|
|
42
|
+
| Submit | Submit | Absenden |
|
|
43
|
+
| Submitting | Submitting... | Wird gesendet... |
|
|
39
44
|
|
|
40
45
|
## Button Tokens
|
|
41
46
|
|
|
@@ -44,7 +49,7 @@ German when `survey.language.startsWith('de')`:
|
|
|
44
49
|
|
|
45
50
|
## ProgressBar
|
|
46
51
|
|
|
47
|
-
Pink fill bar
|
|
52
|
+
Pink fill bar. Bound to `state.progressPercentage` (path-scoped — see `00-integration/progress.md`).
|
|
48
53
|
|
|
49
54
|
## LanguageSelector
|
|
50
55
|
|
|
@@ -53,8 +58,9 @@ Pink fill bar, `300ms` width transition on `progressPercentage`.
|
|
|
53
58
|
|
|
54
59
|
## Agent Checklist
|
|
55
60
|
|
|
56
|
-
- [ ] All
|
|
57
|
-
- [ ] `onAction({ type: '
|
|
58
|
-
- [ ]
|
|
61
|
+
- [ ] All phase branches implemented
|
|
62
|
+
- [ ] `onAction({ type: 'START' })` on intro
|
|
63
|
+
- [ ] `onAction({ type: 'CHANGE' })` wired per question with `allAnswers` / `allQuestions`
|
|
64
|
+
- [ ] Back uses `state.canGoBack`; primary uses `state.primaryButtonType`
|
|
59
65
|
- [ ] ProgressBar receives `state.progressPercentage`
|
|
60
66
|
- [ ] Header/Footer on every state branch
|