@explorer02/cfm-survey-sdk 0.1.8 → 0.2.0

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 (75) hide show
  1. package/dist/index.d.mts +24 -26
  2. package/dist/index.d.ts +24 -26
  3. package/dist/index.js +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/package.json +2 -2
  6. package/postinstall.js +21 -30
  7. package/templates/AGENT.md +24 -10
  8. package/templates/docs/00-integration/constraints.md +54 -0
  9. package/templates/docs/00-integration/setup.md +80 -0
  10. package/templates/docs/00-integration/useSurveySDK.md +107 -0
  11. package/templates/docs/{03-client-components → 01-components}/01-survey-page.md +2 -0
  12. package/templates/docs/{03-client-components → 01-components}/02-question.md +2 -0
  13. package/templates/docs/{03-client-components → 01-components}/03-rating-scale.md +2 -0
  14. package/templates/docs/{03-client-components → 01-components}/04-csat-scale.md +3 -1
  15. package/templates/docs/{03-client-components → 01-components}/05-csat-matrix-scale.md +2 -0
  16. package/templates/docs/{03-client-components → 01-components}/06-likert-matrix-scale.md +2 -0
  17. package/templates/docs/{03-client-components → 01-components}/07-slider-matrix-scale.md +2 -0
  18. package/templates/docs/{03-client-components → 01-components}/08-file-upload-scale.md +2 -0
  19. package/templates/docs/{03-client-components → 01-components}/09-custom-slider-track.md +2 -0
  20. package/templates/docs/{03-client-components → 01-components}/10-header-footer.md +3 -1
  21. package/templates/docs/{03-client-components → 01-components}/11-progress-bar.md +2 -0
  22. package/templates/docs/{03-client-components → 01-components}/12-language-selector.md +2 -0
  23. package/templates/docs/{03-client-components → 01-components}/13-matrix-dropdown.md +2 -0
  24. package/templates/docs/{03-client-components → 01-components}/README.md +3 -3
  25. package/templates/docs/02-question-types/01-rating.md +0 -8
  26. package/templates/docs/02-reference/config-field-index.md +158 -0
  27. package/templates/docs/02-reference/question-types/01-rating.md +52 -0
  28. package/templates/docs/02-reference/question-types/README.md +71 -0
  29. package/templates/docs/02-reference/routing-table.md +37 -0
  30. package/templates/docs/03-ui-specs/00-question-shell.md +89 -0
  31. package/templates/docs/03-ui-specs/01-rating.md +80 -0
  32. package/templates/docs/03-ui-specs/02-radio.md +57 -0
  33. package/templates/docs/03-ui-specs/03-text.md +51 -0
  34. package/templates/docs/03-ui-specs/04-csat.md +57 -0
  35. package/templates/docs/03-ui-specs/05-rating-scale.md +49 -0
  36. package/templates/docs/03-ui-specs/06-slider.md +42 -0
  37. package/templates/docs/03-ui-specs/07-matrix-cfm.md +66 -0
  38. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +65 -0
  39. package/templates/docs/03-ui-specs/09-slider-matrix.md +61 -0
  40. package/templates/docs/03-ui-specs/10-file-upload.md +54 -0
  41. package/templates/docs/03-ui-specs/11-text-and-media.md +49 -0
  42. package/templates/docs/03-ui-specs/12-survey-chrome.md +60 -0
  43. package/templates/docs/03-ui-specs/README.md +79 -0
  44. package/templates/docs/03-ui-specs/shared/custom-slider-track.md +52 -0
  45. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +38 -0
  46. package/templates/docs/MANIFEST.json +150 -0
  47. package/templates/docs/index.md +111 -142
  48. package/templates/docs/templates/implementation_plan.md +91 -0
  49. package/templates/docs/01-sdk-core/01-fetch-survey.md +0 -68
  50. package/templates/docs/01-sdk-core/02-survey-mapper.md +0 -85
  51. package/templates/docs/01-sdk-core/03-question-mappers.md +0 -114
  52. package/templates/docs/01-sdk-core/04-pagination.md +0 -72
  53. package/templates/docs/01-sdk-core/05-validation.md +0 -66
  54. package/templates/docs/01-sdk-core/06-submit-survey.md +0 -90
  55. package/templates/docs/01-sdk-core/07-language-handling.md +0 -111
  56. package/templates/docs/01-sdk-core/08-icons-and-emojis.md +0 -56
  57. package/templates/docs/01-sdk-core/README.md +0 -53
  58. package/templates/docs/02-question-types/02-radio.md +0 -26
  59. package/templates/docs/02-question-types/03-text.md +0 -26
  60. package/templates/docs/02-question-types/12-text-and-media.md +0 -35
  61. package/templates/docs/02-question-types/README.md +0 -74
  62. package/templates/docs/04-critical-rules/01-import-rules.md +0 -51
  63. package/templates/docs/04-critical-rules/02-action-dispatching.md +0 -56
  64. package/templates/docs/04-critical-rules/03-scroll-navigation.md +0 -37
  65. package/templates/docs/04-critical-rules/04-logo-branding.md +0 -42
  66. package/templates/docs/04-critical-rules/05-troubleshooting.md +0 -46
  67. package/templates/docs/04-critical-rules/README.md +0 -29
  68. /package/templates/docs/{02-question-types → 02-reference/question-types}/04-csat.md +0 -0
  69. /package/templates/docs/{02-question-types → 02-reference/question-types}/05-rating-scale.md +0 -0
  70. /package/templates/docs/{02-question-types → 02-reference/question-types}/06-slider.md +0 -0
  71. /package/templates/docs/{02-question-types → 02-reference/question-types}/07-matrix-cfm.md +0 -0
  72. /package/templates/docs/{02-question-types → 02-reference/question-types}/08-matrix-csat.md +0 -0
  73. /package/templates/docs/{02-question-types → 02-reference/question-types}/09-matrix-rating.md +0 -0
  74. /package/templates/docs/{02-question-types → 02-reference/question-types}/10-slider-matrix.md +0 -0
  75. /package/templates/docs/{02-question-types → 02-reference/question-types}/11-file-upload.md +0 -0
@@ -0,0 +1,57 @@
1
+ # UI Spec: Radio MCQ (`type: 'radio'`)
2
+
3
+ > **Reference:** `apps/client/src/components/Question.tsx` (inline)
4
+ > **SDK type:** `RadioQuestion` — `fetchSurvey/types/radio.ts`
5
+ > **Component blueprint:** `01-components/02-question.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `options[]` | ✅ | One card per option |
12
+ | `options[].label` | ✅ | HTML via `dangerouslySetInnerHTML` |
13
+ | `options[].value` | ✅ | Selection value; `null` → empty string on input |
14
+ | `isMultiSelect` | ❌ | SDK supports; client is single-select only |
15
+
16
+ ## Layout Anatomy
17
+
18
+ ```
19
+ ┌─ label (full-width card) ─────────────────────────────────────┐
20
+ │ (●) Option label HTML │ selected
21
+ └───────────────────────────────────────────────────────────────┘
22
+ ┌─ label ───────────────────────────────────────────────────────┐
23
+ │ (○) Option label HTML │ unselected
24
+ └───────────────────────────────────────────────────────────────┘
25
+ ```
26
+
27
+ ## Interactive States
28
+
29
+ | State | Border | Background | Radio |
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
+ ```
44
+
45
+ ## Answer Mutation
46
+
47
+ ```typescript
48
+ onSelect(option.value) // string | number
49
+ ```
50
+
51
+ ## Agent Checklist
52
+
53
+ - [ ] Full-width card options with gap-3 vertical stack
54
+ - [ ] Custom circular radio indicator (not browser default)
55
+ - [ ] Selected: magenta border + pink background
56
+ - [ ] HTML labels
57
+ - [ ] If `isMultiSelect`: implement checkbox cards (SDK gap in reference)
@@ -0,0 +1,51 @@
1
+ # UI Spec: Text Field (`type: 'text'`)
2
+
3
+ > **Reference:** `apps/client/src/components/Question.tsx` (inline)
4
+ > **SDK type:** `TextQuestion` — `fetchSurvey/types/text.ts`
5
+
6
+ ## Config Inventory
7
+
8
+ | Field | Implemented | UI impact |
9
+ |-------|-------------|-----------|
10
+ | `placeholder` | ✅ | Textarea placeholder; default `'Type your response here...'` |
11
+ | `maxCharacterCount` | ✅ | Counter uses this; default 1000 |
12
+ | `inputVariant: 'long'` | ✅ | Always textarea (4 rows) in reference |
13
+ | `inputVariant: 'short'` | ❌ | Should render single-line `<input>` when SDK sends `'short'` |
14
+
15
+ ## Layout Anatomy
16
+
17
+ ```
18
+ ┌─ textarea (w-full rounded-[4px] border border-gray-300 p-4) ──┐
19
+ │ placeholder text │
20
+ │ │
21
+ └─────────────────────────────────────────────────────────────────┘
22
+ N Zeichen verbleiben. ← counter
23
+ ```
24
+
25
+ ## Interactive States
26
+
27
+ | State | Style |
28
+ |-------|-------|
29
+ | Default | `border-gray-300` |
30
+ | Focus | `focus:border-[#e20074] focus:ring-1 focus:ring-[#e20074]` |
31
+
32
+ ## Character Counter
33
+
34
+ ```typescript
35
+ remaining = maxCharacterCount ?? 1000 - selectedValue.length
36
+ // Display: "{remaining} Zeichen verbleiben." (German in reference client)
37
+ // Agents: use survey language for i18n
38
+ ```
39
+
40
+ ## Answer Mutation
41
+
42
+ ```typescript
43
+ onSelect(string) // onChange from textarea
44
+ ```
45
+
46
+ ## Agent Checklist
47
+
48
+ - [ ] Textarea with 4 rows for long variant
49
+ - [ ] Character counter bottom-right
50
+ - [ ] Magenta focus ring
51
+ - [ ] Implement `inputVariant: 'short'` as single-line input when present
@@ -0,0 +1,57 @@
1
+ # UI Spec: CSAT (`type: 'csat'`)
2
+
3
+ > **Reference:** `apps/client/src/components/CsatScale.tsx`
4
+ > **SDK type:** `CsatQuestion` — `fetchSurvey/types/csat.ts`
5
+ > **Component blueprint:** `01-components/04-csat-scale.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `options[]` | ✅ | Scale buttons or dropdown options |
12
+ | `buttonType` | ✅ partial | Branches below |
13
+ | `labelPosition` | ✅ | `'top'` → flex-col-reverse; `'hidden'` → no label |
14
+ | `minLabel` / `maxLabel` | ✅ | Rendered in Question.tsx above scale |
15
+ | `hasNotApplicable` | ❌ | N/A option in options array still renders |
16
+ | `reverseScaleOrder` | ❌ | Document: reverse options before map |
17
+
18
+ ## Config Decision Tree
19
+
20
+ ```
21
+ csat
22
+ ├── buttonType === 'dropdown'
23
+ │ └── Native <select>, placeholder "Select an option"
24
+ │ └── Parse value 'null' → null
25
+ └── else (emoji | star | numbered | number | default)
26
+ └── flex flex-wrap gap-3 buttons
27
+ ├── emoji → getEmojiForIndex(index, scaleLength)
28
+ ├── star → CsatStarIcons filled/empty; labels hidden
29
+ ├── numbered/number → mono font value
30
+ └── default → bold value or 'N/A' if null
31
+ ```
32
+
33
+ ## Interactive States
34
+
35
+ | State | Border | Background | Transform |
36
+ |-------|--------|------------|-----------|
37
+ | Default | `border-gray-200` | `white` | scale-100 |
38
+ | Hover | `border-gray-300` | `hover:bg-gray-50` | — |
39
+ | Selected | `border-[#e20074]` | `#fdf2f8` | `scale-110` |
40
+ | Star mode | transparent | transparent | no border/bg on button |
41
+
42
+ ## Tooltips
43
+
44
+ Recommended: `title={option.label}` on each button. Not in reference client.
45
+
46
+ ## Answer Mutation
47
+
48
+ ```typescript
49
+ onSelect(string | number | null)
50
+ ```
51
+
52
+ ## Agent Checklist
53
+
54
+ - [ ] Dropdown branch with magenta focus ring
55
+ - [ ] Emoji/star/numbered branches per decision tree
56
+ - [ ] `labelPosition` controls label above/below/hidden
57
+ - [ ] Implement `reverseScaleOrder` and standalone `hasNotApplicable` (SDK gaps)
@@ -0,0 +1,49 @@
1
+ # UI Spec: Rating Scale (`type: 'rating_scale'`)
2
+
3
+ > **Reference:** ❌ Not routed in `Question.tsx` — reuse `CsatScale.tsx` patterns
4
+ > **SDK type:** `RatingScaleQuestion` — `fetchSurvey/types/ratingScale.ts`
5
+ > **Component blueprint:** `01-components/03-rating-scale.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `options[]` | ❌ gap | Star/emoji buttons (no NPS colors) |
12
+ | `scaleStyle` | ❌ gap | `'star'` or `'emoji'` |
13
+ | `minLabel` / `midLabel` / `maxLabel` | ❌ gap | Same formulas as NPS in Question shell |
14
+ | `midLabelIndex` | ❌ gap | `(midLabelIndex / (options.length - 1)) * 100`% |
15
+ | `reverseScaleOrder` | ❌ gap | Reverse options before render |
16
+
17
+ ## Expected UI (from CsatScale + rating labels)
18
+
19
+ ```
20
+ [minLabel] [midLabel @ %] [maxLabel]
21
+ ┌────┬────┬────┬────┬────┐
22
+ │ ★ │ ★ │ ★ │ ★ │ ★ │ scaleStyle: 'star'
23
+ └────┴────┴────┴────┴────┘
24
+ OR emoji row from getEmojiForIndex when scaleStyle: 'emoji'
25
+ ```
26
+
27
+ Reuse CsatScale selected styling: `border-[#e20074] bg-[#fdf2f8] scale-110`.
28
+
29
+ ## Config Decision Tree
30
+
31
+ ```
32
+ rating_scale
33
+ ├── scaleStyle === 'star' → CsatStarIcons per option
34
+ ├── scaleStyle === 'emoji' → getEmojiForIndex
35
+ └── else → numbered badges (no option.color)
36
+ ```
37
+
38
+ ## Answer Mutation
39
+
40
+ ```typescript
41
+ onSelect(number)
42
+ ```
43
+
44
+ ## Agent Checklist
45
+
46
+ - [ ] Add dispatcher branch in Question for `rating_scale`
47
+ - [ ] No traffic-light NPS colors on options
48
+ - [ ] min/mid/max labels with midLabelIndex formula
49
+ - [ ] Match CsatScale interactive states
@@ -0,0 +1,42 @@
1
+ # UI Spec: Standalone Slider (`type: 'slider'`)
2
+
3
+ > **Reference:** ❌ Not routed in `Question.tsx` — reuse `CustomSliderTrack.tsx`
4
+ > **SDK type:** `SliderQuestion` — `fetchSurvey/types/slider.ts`
5
+ > **Component blueprint:** `01-components/09-custom-slider-track.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `min` / `max` / `step` | ❌ gap | Track range |
12
+ | `defaultValue` | ❌ gap | Initial thumb position |
13
+ | `minLabel` / `maxLabel` | ❌ gap | End labels above track |
14
+ | `enableInputBox` | ❌ gap | See SliderMatrixScale input pattern |
15
+ | `displayValues` | ❌ gap | Pass to CustomSliderTrack |
16
+
17
+ ## Layout Anatomy
18
+
19
+ ```
20
+ [minLabel] [maxLabel]
21
+ ┌──────────────────────────────────────────────────┐
22
+ │ ───●──────────────── (CustomSliderTrack) │
23
+ └──────────────────────────────────────────────────┘
24
+ [optional number input + steppers if enableInputBox]
25
+ ```
26
+
27
+ ## Reference Patterns
28
+
29
+ Copy from [`09-slider-matrix.md`](09-slider-matrix.md) single-row layout and [`shared/custom-slider-track.md`](shared/custom-slider-track.md).
30
+
31
+ ## Answer Mutation
32
+
33
+ ```typescript
34
+ onSelect(number)
35
+ ```
36
+
37
+ ## Agent Checklist
38
+
39
+ - [ ] Add Question dispatcher for `slider`
40
+ - [ ] CustomSliderTrack with min/max/step
41
+ - [ ] Optional input box with clamp min/max
42
+ - [ ] End labels flex justify-between
@@ -0,0 +1,66 @@
1
+ # UI Spec: CFM Matrix (`type: 'matrix'`, `subType: 'CFM_MATRIX'`)
2
+
3
+ > **Reference:** `apps/client/src/components/LikertMatrixScale.tsx`
4
+ > **SDK type:** `MatrixQuestion` — `fetchSurvey/types/matrix.ts`
5
+ > **Component blueprint:** `01-components/06-likert-matrix-scale.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `rows[]`, `columns[]` | ✅ | Grid / carousel / dropdown data |
12
+ | `matrixFormat` | ✅ | Orchestrator routing |
13
+ | `answerType` | ✅ | `'multiple'` → checkboxes |
14
+ | `matrixType: 'bipolar'` | ✅ | 25% left + 25% right columns |
15
+ | `labels[]` | ✅ | Anchor labels above scale |
16
+ | `transposeTable` | ✅ | Swap row/col iteration |
17
+ | `repeatScale` | ✅ | Re-render header per row |
18
+ | `enableScale` | ✅ | Show/hide column header row |
19
+ | `hasNotApplicable` | ✅ | N/A column with mutex |
20
+
21
+ ## Config Decision Tree
22
+
23
+ ```
24
+ CFM_MATRIX
25
+ ├── matrixFormat === 'dropdown' → LikertMatrixDropdownLayout (MatrixDropdown per row)
26
+ ├── matrixFormat === 'carousel' → LikertMatrixCarouselLayout (one row, prev/next, blue dots)
27
+ └── else → LikertMatrixGridLayout
28
+ ├── transposeTable ? columns×rows : rows×columns
29
+ ├── repeatScale ? header per row : single header
30
+ ├── enableScale ? column headers : hidden
31
+ └── matrixType === 'bipolar' ? 25% | scale | 25% layout
32
+ ```
33
+
34
+ ## Interactive Behaviors
35
+
36
+ | Behavior | Condition | Action |
37
+ |----------|-----------|--------|
38
+ | Click selected radio again | single-select grid/carousel | Clear row (`undefined`) |
39
+ | N/A selected | any | Other cols opacity 0.4, disabled |
40
+ | Multi N/A | answerType multiple | Sets `[null]` exclusively |
41
+ | Multi toggle | answerType multiple | Add/remove from array |
42
+
43
+ ## Grid Layout
44
+
45
+ - Row label column: `180px` (likert) or `25%` (bipolar)
46
+ - Zebra rows: even rows `rgba(249,250,251,0.8)`
47
+ - Native inputs: `accentColor: #e20074`, 18×18px
48
+
49
+ ## Carousel
50
+
51
+ - Blue active dot: `#2563eb` (Likert only)
52
+ - Card: white, border, shadow, centered row text
53
+
54
+ ## Answer Mutation
55
+
56
+ ```typescript
57
+ onSelect(MatrixAnswerMap) // Record<rowId, value | value[] | null>
58
+ ```
59
+
60
+ ## Agent Checklist
61
+
62
+ - [ ] All three matrixFormat branches
63
+ - [ ] transposeTable + repeatScale + enableScale
64
+ - [ ] Bipolar 25/50/25 layout with rightText
65
+ - [ ] Single-select deselect on re-click
66
+ - [ ] N/A mutex with opacity 0.4
@@ -0,0 +1,65 @@
1
+ # UI Spec: CSAT / Rating Matrix (`subType: 'CSAT_MATRIX' | 'RATING_MATRIX'`)
2
+
3
+ > **Reference:** `apps/client/src/components/CsatMatrixScale.tsx`
4
+ > **SDK type:** `MatrixQuestion` — `fetchSurvey/types/matrix.ts`
5
+ > **Component blueprint:** `01-components/05-csat-matrix-scale.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `buttonType` | ✅ | Grid vs vertical list routing |
12
+ | `matrixFormat` | ✅ | carousel vs standard |
13
+ | `hasNotApplicable` | ✅ | Checkbox below row (vertical) or N/A column (grid) |
14
+ | `reverseScaleOrder` | ✅ | Flip emoji/star/number index |
15
+ | `labels[]` | ✅ | Anchor row above columns |
16
+ | `columns[].color` | ✅ | RATING_MATRIX badge colors |
17
+
18
+ ## Config Decision Tree
19
+
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).
51
+
52
+ ## Answer Mutation
53
+
54
+ ```typescript
55
+ onSelect(MatrixAnswerMap)
56
+ // hasNotApplicable: null for N/A; undefined clears
57
+ ```
58
+
59
+ ## Agent Checklist
60
+
61
+ - [ ] scaleColumns = columns.filter(c => c.value !== null)
62
+ - [ ] All buttonType × matrixFormat branches
63
+ - [ ] reverseScaleOrder on emoji/star/numbered
64
+ - [ ] Carousel pink dot indicators
65
+ - [ ] Vertical selectbox cards match standalone radio styling
@@ -0,0 +1,61 @@
1
+ # UI Spec: Slider Matrix (`type: 'slider_matrix'`)
2
+
3
+ > **Reference:** `apps/client/src/components/SliderMatrixScale.tsx`
4
+ > **SDK type:** `SliderMatrixQuestion` — `fetchSurvey/types/sliderMatrix.ts`
5
+ > **Component blueprint:** `01-components/07-slider-matrix-scale.md`
6
+ > **Shared:** [`shared/custom-slider-track.md`](shared/custom-slider-track.md)
7
+
8
+ ## Config Inventory
9
+
10
+ | Field | Implemented | UI impact |
11
+ |-------|-------------|-----------|
12
+ | `rows[]` (min/max/step) | ✅ | Per-row slider range |
13
+ | `labels[]` | ✅ | Header anchor labels |
14
+ | `tickValues[]` | ✅ | Colored badges above track |
15
+ | `ticks` | ✅ | Discrete marks + htmlStep |
16
+ | `sliderType: 'graphics'` | ✅ | Plain text tick labels; emoji thumb |
17
+ | `enableInputBox` | ✅ | 80px input + stepper buttons |
18
+ | `displayValues` | ✅ | Passed to CustomSliderTrack |
19
+ | `enableNotApplicable` | ✅ | Checkbox; value `'N/A'` string |
20
+
21
+ ## Layout Anatomy
22
+
23
+ ```
24
+ [header: labels[] + tickValues badges OR computed marks]
25
+ ┌─ row band (#f3f4f6, rounded-xl) ─────────────────────────────┐
26
+ │ 25% statement │ CustomSliderTrack │ 25% rightText? │ input? │ N/A? │
27
+ └───────────────────────────────────────────────────────────────┘
28
+ ```
29
+
30
+ ## Header Tick Positioning
31
+
32
+ ```typescript
33
+ percentage = ((tv.value - min) / (max - min)) * 100
34
+ sliceWidth = tickValues.length > 1 ? 100 / (tickValues.length - 1) : 100
35
+ // Colored badge: tv.color bg, white text, rounded 4px
36
+ // graphics: plain text label instead of colored badge
37
+ ```
38
+
39
+ ## N/A Behavior
40
+
41
+ - Checked → `rowVal === 'N/A'`; slider disabled, input grayed
42
+ - Unchecked → reset to `defaultValue ?? min`
43
+
44
+ ## Input Box
45
+
46
+ - Clamp to row.min / row.max on change
47
+ - Steppers use `htmlStep` increment/decrement
48
+
49
+ ## Answer Mutation
50
+
51
+ ```typescript
52
+ onSelect(Record<string, number | null | 'N/A'>)
53
+ ```
54
+
55
+ ## Agent Checklist
56
+
57
+ - [ ] Shared header from first row min/max/step
58
+ - [ ] Gray row bands with 25% statement columns
59
+ - [ ] CustomSliderTrack per row with correct htmlStep
60
+ - [ ] tickValues colored badges (non-graphics)
61
+ - [ ] N/A mutex disables slider + input
@@ -0,0 +1,54 @@
1
+ # UI Spec: File Upload (`type: 'file_upload'`)
2
+
3
+ > **Reference:** `apps/client/src/components/FileUploadScale.tsx`
4
+ > **SDK type:** `FileUploadQuestion` — `fetchSurvey/types/fileUpload.ts`
5
+ > **Component blueprint:** `01-components/08-file-upload-scale.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `uploadMessage` | ✅ | HTML in dropzone (bold 16px) |
12
+ | `supportedFileFormats` | ✅ | Validation + accept attribute |
13
+ | `maxFileCount` | ✅ | Total file limit |
14
+ | `fileSizeLimit` | ✅ | MB limit |
15
+ | `fileSizeLimitType` | ✅ | `PER_FILE` or `IN_TOTAL` |
16
+
17
+ ## Layout Anatomy
18
+
19
+ ```
20
+ [uploadMessage HTML in dashed dropzone]
21
+ File limit: N; File size limit: XMB/file|in total; Supports: ...
22
+ [local error banner — red if validation fails]
23
+ [file list cards: icon, name, size, remove ×]
24
+ ```
25
+
26
+ ## Interactive States
27
+
28
+ | State | Border | Background |
29
+ |-------|--------|------------|
30
+ | Default | `#d1d5db` dashed | `#f9fafb` |
31
+ | Hover | `#e20074` | `#fdf2f8` |
32
+ | Drag active | `#e20074` | `#fdf2f8` |
33
+
34
+ ## Local Validation (before SDK)
35
+
36
+ 1. Max file count exceeded
37
+ 2. Unsupported extension
38
+ 3. PER_FILE size exceeded
39
+ 4. IN_TOTAL cumulative size exceeded
40
+
41
+ Error banner: `#fee2e2` bg, `#b91c1c` text.
42
+
43
+ ## Answer Mutation
44
+
45
+ ```typescript
46
+ onSelect(File[] | undefined) // undefined when all removed
47
+ ```
48
+
49
+ ## Agent Checklist
50
+
51
+ - [ ] Hidden `<input type="file">` triggered by dropzone click
52
+ - [ ] Drag enter/over/leave/drop handlers
53
+ - [ ] Dynamic constraints line in dropzone
54
+ - [ ] File list with remove button (hover red)
@@ -0,0 +1,49 @@
1
+ # UI Spec: Text and Media (`type: 'text_and_media'`)
2
+
3
+ > **Reference:** `apps/client/src/components/Question.tsx` (inline)
4
+ > **SDK type:** `TextAndMediaQuestion` — `fetchSurvey/types/textAndMedia.ts`
5
+ > **Component blueprint:** `01-components/02-question.md`
6
+
7
+ ## Config Inventory
8
+
9
+ | Field | Implemented | UI impact |
10
+ |-------|-------------|-----------|
11
+ | `mediaUrl` | ✅ | Required to render media block |
12
+ | `mediaMimeType` | ✅ | `video/` or `VIDEO` → `<video>`; else `<img>` |
13
+ | `mediaTitle` | ✅ | Caption below media, centered gray text |
14
+ | `mediaAlignment` | ✅ | Flex justify: CENTER / RIGHT / default left |
15
+ | `mediaSize` | ✅ | Width `%` on media container; default 100% |
16
+
17
+ No answer input — display-only question.
18
+
19
+ ## Layout Anatomy
20
+
21
+ ```
22
+ [question title + description — standard shell]
23
+
24
+ ┌─ flex container (justify per mediaAlignment) ──────────────┐
25
+ │ ┌─ media block (width: mediaSize%) ──────────────────┐ │
26
+ │ │ <video controls> or <img> │ │
27
+ │ │ [optional mediaTitle caption] │ │
28
+ │ └─────────────────────────────────────────────────────┘ │
29
+ └────────────────────────────────────────────────────────────┘
30
+ ```
31
+
32
+ ## Alignment Mapping
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.
43
+
44
+ ## Agent Checklist
45
+
46
+ - [ ] Render only when `mediaUrl` is present
47
+ - [ ] Video vs image from mime type
48
+ - [ ] `max-w-full rounded-md shadow-sm` on media element
49
+ - [ ] Optional centered caption for `mediaTitle`
@@ -0,0 +1,60 @@
1
+ # UI Spec: Survey Chrome
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`
5
+
6
+ ## SurveyPage — 5-State Machine
7
+
8
+ | State | Condition | UI |
9
+ |-------|-----------|-----|
10
+ | Loading | `surveyQueryResults.isLoading` | Centered "Loading..." |
11
+ | Error | `surveyQueryResults.error` | Red error message |
12
+ | Empty | `!survey` | "No survey data" |
13
+ | Submitted | `submitSurveyResults.data` | Thank you message |
14
+ | Active | else | Questions + nav |
15
+
16
+ ## Active Layout
17
+
18
+ ```
19
+ Header
20
+ main (max-w-4xl)
21
+ ProgressBar(progressPercentage)
22
+ LanguageSelector (hidden if ≤1 language)
23
+ Questions (space-y-12)
24
+ Submit error (if any)
25
+ Nav: Back (if page > 0) + Next/Submit
26
+ Footer
27
+ ```
28
+
29
+ ## Navigation Labels
30
+
31
+ German when `survey.language.startsWith('de')`:
32
+
33
+ | Action | EN | DE |
34
+ |--------|----|----|
35
+ | Back | Zurück | Back |
36
+ | Next | Weiter | Next |
37
+ | Submit | Absenden | Submit |
38
+ | Submitting | Wird gesendet... | Submitting... |
39
+
40
+ ## Button Tokens
41
+
42
+ - Back: `border-[#e20074] text-[#e20074] hover:bg-[#fdf2f8]`
43
+ - Primary: `bg-[#e20074] text-white hover:opacity-90`
44
+
45
+ ## ProgressBar
46
+
47
+ Pink fill bar, `300ms` width transition on `progressPercentage`.
48
+
49
+ ## LanguageSelector
50
+
51
+ - Hidden when `languages.length <= 1`
52
+ - Focus ring: `#2563eb`
53
+
54
+ ## Agent Checklist
55
+
56
+ - [ ] All 5 states implemented
57
+ - [ ] `onAction({ type: 'CHANGE' })` wired per question
58
+ - [ ] PREVIOUS / NEXT actions on nav buttons
59
+ - [ ] ProgressBar receives `state.progressPercentage`
60
+ - [ ] Header/Footer on every state branch