@explorer02/cfm-survey-sdk 0.2.2 → 0.2.3

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 (41) hide show
  1. package/package.json +1 -1
  2. package/templates/AGENT.md +8 -4
  3. package/templates/docs/00-integration/analytics-events-catalog.md +54 -0
  4. package/templates/docs/00-integration/client-integration-guide.md +1 -1
  5. package/templates/docs/00-integration/component-checklist.md +10 -0
  6. package/templates/docs/00-integration/display-logic-and-navigation.md +16 -1
  7. package/templates/docs/00-integration/file-upload-aws.md +116 -0
  8. package/templates/docs/00-integration/logic-fields-catalog.md +105 -0
  9. package/templates/docs/00-integration/partial-save-and-recovery.md +24 -0
  10. package/templates/docs/00-integration/question-type-sdk-matrix.md +11 -9
  11. package/templates/docs/00-integration/setup.md +3 -3
  12. package/templates/docs/00-integration/skip-logic-and-navigation.md +1 -1
  13. package/templates/docs/00-integration/survey-lifecycle-analytics.md +1 -1
  14. package/templates/docs/01-components/06-likert-matrix-scale.md +2 -1
  15. package/templates/docs/01-components/08-file-upload-scale.md +15 -52
  16. package/templates/docs/01-components/13-matrix-dropdown.md +28 -33
  17. package/templates/docs/01-components/17-heatmap-scale.md +4 -4
  18. package/templates/docs/01-components/18-rank-order-scale.md +24 -31
  19. package/templates/docs/02-reference/question-types/11-file-upload.md +36 -21
  20. package/templates/docs/02-reference/question-types/README.md +11 -1
  21. package/templates/docs/03-ui-specs/01-rating.md +10 -2
  22. package/templates/docs/03-ui-specs/02-radio.md +80 -25
  23. package/templates/docs/03-ui-specs/04-csat.md +35 -5
  24. package/templates/docs/03-ui-specs/07-matrix-cfm.md +20 -4
  25. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +5 -1
  26. package/templates/docs/03-ui-specs/09-slider-matrix.md +17 -0
  27. package/templates/docs/03-ui-specs/10-file-upload.md +41 -20
  28. package/templates/docs/03-ui-specs/12-survey-chrome.md +38 -0
  29. package/templates/docs/03-ui-specs/13-heatmap.md +9 -2
  30. package/templates/docs/03-ui-specs/14-rank-order.md +98 -29
  31. package/templates/docs/03-ui-specs/README.md +7 -5
  32. package/templates/docs/03-ui-specs/shared/custom-slider-track.md +28 -35
  33. package/templates/docs/MANIFEST.json +28 -3
  34. package/templates/docs/index.md +5 -4
  35. package/templates/docs/templates/CustomSliderTrack.tsx +144 -0
  36. package/templates/docs/templates/MatrixDropdown.tsx +216 -0
  37. package/templates/docs/templates/RankOrderScale.tsx +353 -0
  38. package/templates/docs/templates/implementation_plan.md +20 -0
  39. package/templates/docs/templates/survey-inventory.schema.json +28 -23
  40. package/templates/docs/templates/surveyUiIcons.tsx +52 -0
  41. package/templates/docs/templates/verify-agent-build.sh +32 -0
@@ -1,18 +1,18 @@
1
1
  # UI Spec: File Upload (`type: 'FILE_UPLOAD'`)
2
2
 
3
- > **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/FileUploadScale.tsx`
4
3
  > **SDK type:** `FileUploadQuestion` — `fetchSurvey/types/fileUpload.ts`
5
4
  > **Component blueprint:** `01-components/08-file-upload-scale.md`
5
+ > **Upload API:** [`00-integration/file-upload-aws.md`](../00-integration/file-upload-aws.md)
6
6
 
7
7
  ## Config Inventory
8
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` |
9
+ | Field | 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
16
 
17
17
  ## Layout Anatomy
18
18
 
@@ -20,7 +20,7 @@
20
20
  [uploadMessage HTML in dashed dropzone]
21
21
  File limit: N; File size limit: XMB/file|in total; Supports: ...
22
22
  [local error banner — red if validation fails]
23
- [file list cards: icon, name, size, remove ×]
23
+ [file list cards: icon, name, size, uploading…, remove ×]
24
24
  ```
25
25
 
26
26
  ## Interactive States
@@ -31,24 +31,45 @@
31
31
  | Hover | `#e20074` | `#fdf2f8` |
32
32
  | Drag active | `#e20074` | `#fdf2f8` |
33
33
 
34
- ## Local Validation (before SDK)
34
+ ## Implementation Recipe
35
35
 
36
- 1. Max file count exceeded
37
- 2. Unsupported extension
38
- 3. PER_FILE size exceeded
39
- 4. IN_TOTAL cumulative size exceeded
36
+ ### Phase 1 Local pick + validate
40
37
 
41
- Error banner: `#fee2e2` bg, `#b91c1c` text.
38
+ 1. Hidden `<input type="file" multiple={maxFileCount > 1}>`
39
+ 2. Drag enter/over/leave/drop on dropzone
40
+ 3. Reject: wrong extension, count exceeded, size exceeded
41
+
42
+ ### Phase 2 — Upload (required for submit)
43
+
44
+ ```
45
+ User picks File
46
+ → POST {uploadApi}/get-upload-url { fileName, contentType, questionId }
47
+ ← { uploadUrl, fileId, mediaUrl }
48
+ → PUT uploadUrl (binary body, file Content-Type)
49
+ → onSelect([...existing, { id: fileId, mediaUrl, name, mimeType, size }])
50
+ → onAction(CHANGE)
51
+ ```
52
+
53
+ See [`file-upload-aws.md`](../00-integration/file-upload-aws.md).
54
+
55
+ ### Phase 3 — Remove
56
+
57
+ Remove from list → `onSelect(undefined)` when empty, else updated `UploadedFileAnswer[]`.
42
58
 
43
59
  ## Answer Mutation
44
60
 
45
61
  ```typescript
46
- onSelect(File[] | undefined) // undefined when all removed
62
+ // After upload completes NOT raw File[]
63
+ onSelect: (value: UploadedFileAnswer[] | undefined) => void
64
+ onAction({ type: 'CHANGE', payload: { questionId, answerValue: UploadedFileAnswer[] } })
47
65
  ```
48
66
 
67
+ **Non-submittable:** storing `File[]` only — SDK submit drops attachments.
68
+
49
69
  ## Agent Checklist
50
70
 
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)
71
+ - [ ] Local validation before upload
72
+ - [ ] Presigned upload wired; progress/spinner per file
73
+ - [ ] Final answer shape: `UploadedFileAnswer[]` with `id` + `mediaUrl`
74
+ - [ ] Remove clears answer or passes updated array
75
+ - [ ] Matrix row: [`question-type-sdk-matrix.md#file_upload`](../00-integration/question-type-sdk-matrix.md#file_upload)
@@ -55,6 +55,41 @@ Pink fill bar. Bound to `state.progressPercentage` (path-scoped — see `00-inte
55
55
 
56
56
  - Hidden when `languages.length <= 1`
57
57
  - Focus ring: `#2563eb`
58
+ - Pass `language` in `useMemo` hook options — SDK remaps survey client-side (no refetch)
59
+
60
+ ## Question numbering
61
+
62
+ Render ordinals from SDK on content pages:
63
+
64
+ ```tsx
65
+ {question.questionNumber != null && (
66
+ <span className="mr-1 font-semibold">{question.questionNumber}.</span>
67
+ )}
68
+ ```
69
+
70
+ See [`question-numbering.md`](../00-integration/question-numbering.md).
71
+
72
+ ## Special pages
73
+
74
+ | Phase | Component | Action |
75
+ |-------|-----------|--------|
76
+ | `intro` | `IntroPage` | `page={state.specialPage}` · `onStart={() => onAction({ type: 'START' })}` |
77
+ | `paused` | `PausedPage` | `page={state.specialPage}` · read-only, no nav |
78
+ | `end` | `EndPage` | `page={state.specialPage}` · terminal thank-you |
79
+
80
+ ## Terminal phases
81
+
82
+ Render a simple message (no question nav) for:
83
+
84
+ - `submitted` — already completed
85
+ - `expired` — survey expired
86
+ - `quota_fulfilled` — quota reached
87
+
88
+ Keep Header/Footer chrome consistent across all phases.
89
+
90
+ ## Submit loading
91
+
92
+ Disable primary Next/Submit when `submitSurveyResults.isLoading`; label "Submitting…" / "Wird gesendet…".
58
93
 
59
94
  ## Agent Checklist
60
95
 
@@ -64,3 +99,6 @@ Pink fill bar. Bound to `state.progressPercentage` (path-scoped — see `00-inte
64
99
  - [ ] Back uses `state.canGoBack`; primary uses `state.primaryButtonType`
65
100
  - [ ] ProgressBar receives `state.progressPercentage`
66
101
  - [ ] Header/Footer on every state branch
102
+ - [ ] `questionNumber` prefix when present on content questions
103
+ - [ ] Intro / Paused / End / terminal phase branches
104
+ - [ ] Submit button disabled while `submitSurveyResults.isLoading`
@@ -58,11 +58,18 @@ Use singular "spot" when `maxClicksAllowed === 1`.
58
58
  ## Answer Mutation
59
59
 
60
60
  ```typescript
61
- onSelect(HeatmapAnswer | undefined)
62
- // HeatmapAnswer = Array<{ id: string; x: number; y: number }>
61
+ onSelect(HeatmapClickPoint[] | undefined)
62
+ // HeatmapClickPoint = { id: string; x: number; y: number }
63
63
  // x, y normalized 0–1 relative to rendered image bounds
64
+ onAction({ type: 'CHANGE', payload: { questionId, answerValue: spots } })
64
65
  ```
65
66
 
67
+ Copy coord helpers from [`templates/heatmapCoords.ts`](../templates/heatmapCoords.ts): `getNormalizedClickFromImage`, `findSpotNearClick` (14px remove radius), `createNewSpot`.
68
+
69
+ ## Logic fields
70
+
71
+ `HEATMAP` and `HEATMAP_REGION` — see [`logic-fields-catalog.md`](../00-integration/logic-fields-catalog.md).
72
+
66
73
  ## Agent Checklist
67
74
 
68
75
  - [ ] Click handler on `<img>` element (not overlay) for precise coordinates
@@ -2,58 +2,127 @@
2
2
 
3
3
  > **Reference:** `02-reference/question-types/13-rank-order.md`
4
4
  > **Blueprint:** `01-components/18-rank-order-scale.md`
5
-
6
- ## Interaction Modes
5
+ > **Portable template:** copy [`templates/RankOrderScale.tsx`](../templates/RankOrderScale.tsx)
6
+
7
+ ## Config gate
8
+
9
+ | Field | Values | UI branch |
10
+ |-------|--------|-----------|
11
+ | `interactionMode` | `'dropdown'` | Rank `<select>` per row |
12
+ | `interactionMode` | `'dragAndDrop'` | `@dnd-kit` sortable list |
13
+ | `optionDisplay` | `textOnly` / `imageOnly` / `textAndImage` | Option content layout |
14
+ | `requireRankAll` | boolean | Validation (SDK) |
15
+ | `shuffleOptions` | boolean | **SDK shuffles at fetch** — render `question.options` as-is; do not re-shuffle client-side |
16
+
17
+ ## Dependencies
18
+
19
+ ```bash
20
+ npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
21
+ ```
22
+
23
+ Next.js: template includes `'use client'`. Optional in `Question.tsx`:
24
+
25
+ ```tsx
26
+ const RankOrderScale = dynamic(() => import('./RankOrderScale').then(m => ({ default: m.RankOrderScale })), {
27
+ ssr: false,
28
+ loading: () => <p>Loading rank order…</p>,
29
+ });
30
+ ```
31
+
32
+ ## SDK wiring
33
+
34
+ ```typescript
35
+ import {
36
+ getVisibleRankOrderOptionsForAnswers,
37
+ normalizeRankOrderAnswers,
38
+ assignRankWithoutDuplicates,
39
+ buildRankOrderFromOrderedOptionIds,
40
+ getOrderedOptionIdsFromRanks,
41
+ } from '@explorer02/cfm-survey-sdk';
42
+
43
+ // Question.tsx — filter options + normalize answer
44
+ const rankOrderQuestion = {
45
+ ...question,
46
+ options: getVisibleRankOrderOptionsForAnswers(question, allAnswers, allQuestions, customFieldValues),
47
+ };
48
+ <RankOrderScale
49
+ question={rankOrderQuestion}
50
+ selectedValue={normalizeRankOrderAnswers(selectedValue)}
51
+ onSelect={onSelect}
52
+ />
53
+ ```
54
+
55
+ Answer shape: `RankOrderAnswers` = `Record<optionId, rank>` (1-based).
56
+
57
+ ## Interaction modes
7
58
 
8
59
  ### Dropdown (`interactionMode: 'dropdown'`)
9
60
 
10
61
  | Element | Behavior |
11
62
  |---------|----------|
12
63
  | Rank select | `-` (unassigned) or `1..N` per option row |
13
- | Duplicate ranks | Selecting rank R on option A clears R from any other option |
14
- | Layout | Rank select on left, option content on right |
64
+ | Duplicate ranks | `assignRankWithoutDuplicates` clears rank R from other options |
65
+ | Layout | Rank select left, option content right |
15
66
 
16
- ### Drag and Drop (`interactionMode: 'dragAndDrop'`)
67
+ ### Drag and drop (`interactionMode: 'dragAndDrop'`)
17
68
 
18
69
  | Element | Behavior |
19
70
  |---------|----------|
20
- | Drag handle | Six-dot grip on left; pointer + keyboard sensors |
71
+ | Drag handle | Six-dot grip; `PointerSensor` + `KeyboardSensor` |
21
72
  | Rank badge | Circular badge showing 1-based position |
22
- | Reorder | List order determines rank; smooth transform during drag |
23
- | Up/Down buttons | Keyboard fallback on right side of each row |
73
+ | Reorder | List order `buildRankOrderFromOrderedOptionIds(orderedIds)` |
74
+ | Up/Down | Keyboard fallback; disabled on first/last |
75
+
76
+ ## Implementation Recipe — dnd-kit
77
+
78
+ ```typescript
79
+ const sensors = useSensors(
80
+ useSensor(PointerSensor),
81
+ useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
82
+ );
83
+
84
+ const handleDragEnd = (event: DragEndEvent) => {
85
+ const { active, over } = event;
86
+ if (!over || active.id === over.id) return;
87
+ const oldIndex = orderedOptionIds.indexOf(String(active.id));
88
+ const newIndex = orderedOptionIds.indexOf(String(over.id));
89
+ if (oldIndex === -1 || newIndex === -1) return;
90
+ onSelect(buildRankOrderFromOrderedOptionIds(arrayMove(orderedOptionIds, oldIndex, newIndex)));
91
+ };
92
+ ```
24
93
 
25
- ## Option Display Variants
94
+ Wrap list in `<DndContext collisionDetection={closestCenter}>` + `<SortableContext strategy={verticalListSortingStrategy}>`.
95
+
96
+ Copy full implementation from [`templates/RankOrderScale.tsx`](../templates/RankOrderScale.tsx).
97
+
98
+ ## Option display
26
99
 
27
100
  | `optionDisplay` | Render |
28
101
  |-----------------|--------|
29
- | `textOnly` | Full-width text label only |
30
- | `imageOnly` | Large square thumbnail only (no label) |
31
- | `textAndImage` | Thumbnail + text label side by side |
32
-
33
- Use `dangerouslySetInnerHTML` for option labels (HTML from builder).
102
+ | `textOnly` | Label via `dangerouslySetInnerHTML` |
103
+ | `imageOnly` | Square thumbnail; prefer `option.imageUrl` over `previewImageUrl` |
104
+ | `textAndImage` | Thumbnail + label side by side |
34
105
 
35
106
  ## Validation UX
36
107
 
37
- - Required + `requireRankAll`: every visible option must have a unique rank from 1..N
108
+ - Required + `requireRankAll`: every visible option unique rank 1..N
38
109
  - Required + not `requireRankAll`: at least one visible option ranked
39
- - SDK surfaces `validationError` on the question shell — show the standard error banner
110
+ - Show `validationError` from question shell
40
111
 
41
112
  ## Styling
42
113
 
43
- Match MCQ card styling:
44
-
45
- - Border: `#e5e5e5` default, `#e20074` when actively ranked (optional highlight)
46
- - Background hover: `hover:bg-gray-50/50`
47
- - Rank badge: `#fdf2f8` background, `#e20074` text
114
+ - Border `#e5e5e5`, optional `#e20074` when ranked
115
+ - Rank badge: `#fdf2f8` bg, `#e20074` text
48
116
 
49
- ## Accessibility
117
+ ## Logic
50
118
 
51
- - Each rank select: `aria-label="Rank for {option label}"`
52
- - Drag handle: `aria-label="Drag to reorder {option label}"`
53
- - Up/Down: disabled on first/last item
119
+ RANK_ORDER operators (ranked as/above/below) see [`logic-fields-catalog.md`](../00-integration/logic-fields-catalog.md).
54
120
 
55
- ## Do Not
121
+ ## Agent checklist
56
122
 
57
- - Store ranks in local component state without syncing to `onSelect`
58
- - Submit option labels submit rank numbers via SDK formatter only
59
- - Hardcode logic operators in the client — all conditions are SDK-evaluated
123
+ - [ ] Copied `templates/RankOrderScale.tsx` (or equivalent dnd-kit + dropdown)
124
+ - [ ] `@dnd-kit/*` in package.json when survey has RANK_ORDER
125
+ - [ ] `getVisibleRankOrderOptionsForAnswers` + `normalizeRankOrderAnswers`
126
+ - [ ] `option.imageUrl` before `previewImageUrl`
127
+ - [ ] No client-side re-shuffle when `shuffleOptions` is true
128
+ - [ ] No stub placeholder text
@@ -38,17 +38,19 @@ Each file in this folder follows the same structure:
38
38
  | Spec file | Reference component | `question.type` |
39
39
  |-----------|---------------------|-----------------|
40
40
  | [00-question-shell.md](00-question-shell.md) | `Question.tsx` | All (wrapper) |
41
- | [01-rating.md](01-rating.md) | `RatingScale.tsx` | `nps` |
42
- | [02-radio.md](02-radio.md) | `Question.tsx` inline | `mcq` |
43
- | [03-text.md](03-text.md) | `Question.tsx` inline | `text_input` |
44
- | [04-csat.md](04-csat.md) | `CsatMatrixScale.tsx` | `csat` |
41
+ | [01-rating.md](01-rating.md) | `RatingScale.tsx` | `NPS_SCALE` |
42
+ | [02-radio.md](02-radio.md) | `Question.tsx` inline | `MCQ` |
43
+ | [03-text.md](03-text.md) | `Question.tsx` inline | `TEXTFIELD` |
44
+ | [04-csat.md](04-csat.md) | `CsatMatrixScale.tsx` | `CSAT_MATRIX` |
45
45
  | [05-rating-scale.md](05-rating-scale.md) | `RatingScale.tsx` | `star_rating` |
46
46
  | [06-slider.md](06-slider.md) | `CustomSliderTrack.tsx` | `slider` |
47
47
  | [07-matrix-cfm.md](07-matrix-cfm.md) | `LikertMatrixScale.tsx` | `cfm_matrix` |
48
48
  | [08-matrix-csat-rating.md](08-matrix-csat-rating.md) | `CsatMatrixScale.tsx` | `rating_matrix` |
49
49
  | [09-slider-matrix.md](09-slider-matrix.md) | `SliderMatrixScale.tsx` | `slider_matrix` |
50
50
  | [10-file-upload.md](10-file-upload.md) | `FileUploadScale.tsx` | `file_upload` |
51
- | [11-text-and-media.md](11-text-and-media.md) | `Question.tsx` inline | `text_and_media` |
51
+ | [11-text-and-media.md](11-text-and-media.md) | `Question.tsx` inline | `TEXT_AND_MEDIA` |
52
+ | [13-heatmap.md](13-heatmap.md) | `HeatmapScale.tsx` | `HEATMAP` |
53
+ | [14-rank-order.md](14-rank-order.md) | `RankOrderScale.tsx` | `RANK_ORDER` |
52
54
  | [12-survey-chrome.md](12-survey-chrome.md) | `SurveyPage.tsx` + chrome | Shell |
53
55
  | [shared/custom-slider-track.md](shared/custom-slider-track.md) | `CustomSliderTrack.tsx` | Primitive |
54
56
  | [shared/matrix-dropdown.md](shared/matrix-dropdown.md) | `MatrixDropdown.tsx` | Primitive |
@@ -1,52 +1,45 @@
1
1
  # UI Spec: Custom Slider Track (shared primitive)
2
2
 
3
- > **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/CustomSliderTrack.tsx`
4
- > **Used by:** `SliderMatrixScale.tsx`, `CsatMatrixScale` (graphics), standalone `slider` (gap)
3
+ > **Portable template:** copy [`templates/CustomSliderTrack.tsx`](../templates/CustomSliderTrack.tsx) + [`templates/surveyUiIcons.tsx`](../templates/surveyUiIcons.tsx)
4
+ > **Used by:** `SliderMatrixScale`, `CsatMatrixScale` (graphics), matrix `displayStyle: graphics`
5
+
6
+ ## Scale column labels (Day 19 — CRITICAL)
7
+
8
+ Matrix rows must pass **`tickLabels` from `scaleColumns[].label`** (filtered to visible columns):
9
+
10
+ ```typescript
11
+ const tickLabels = visibleScaleColumns.map(col => col.label);
12
+ <CustomSliderTrack tickLabels={tickLabels} ticks={tickLabels.length} ... />
13
+ ```
14
+
15
+ Tooltip shows stripped HTML from `tickLabels[tickIndex]`; without labels, tooltips fall back to numeric value only.
5
16
 
6
17
  ## Layer Architecture
7
18
 
8
19
  ```
9
- 1. Gray inactive track (#e5e7eb, 4px height)
10
- 2. Magenta fill bar (width = percentage%, #e20074 or #9ca3af if disabled)
11
- 3. Tick marks (1px × 8px at ticks positions)
12
- 4. Invisible <input type="range"> overlay (opacity 0, full hit area)
13
- 5. Visual thumb: circle OR emoji (sliderType === 'graphics')
14
- 6. Tooltip bubble on hover/drag (#1f2937 bg, rounded value)
20
+ 1. Gray inactive track (#e5e7eb, 4px)
21
+ 2. Magenta fill bar (percentage%, #e20074 or #9ca3af if disabled)
22
+ 3. Tick marks at column positions
23
+ 4. Invisible <input type="range"> overlay
24
+ 5. Thumb: circle OR emoji (sliderType === 'graphics')
25
+ 6. Tooltip on hover/drag (#1f2937)
15
26
  ```
16
27
 
17
28
  ## Props
18
29
 
19
30
  | Prop | Role |
20
31
  |------|------|
21
- | `min`, `max`, `htmlStep` | Range input bounds |
32
+ | `min`, `max`, `htmlStep` | Range bounds |
22
33
  | `value` | Current position |
23
- | `disabled` | Gray theme, opacity 0.5, no interaction |
24
- | `displayValues` | Reserved tooltip always shows on hover/drag in client |
25
- | `hasSelectedValue` | Reserved |
26
- | `sliderType: 'graphics'` | Emoji thumb via client-side react-icons |
27
- | `ticks` | Discrete tick count + emoji index scale |
28
- | `reverseScaleOrder` | Flip emoji index calculation |
29
-
30
- ## Interactive States
31
-
32
- | State | Thumb | Tooltip |
33
- |-------|-------|---------|
34
- | Default | White circle, 2.5px magenta border | Hidden |
35
- | Hover/drag | Pink glow ring `rgba(226,0,116,0.2)` | Dark bubble with `Math.round(value)` |
36
- | Disabled | Gray border, opacity 0.5 | Hidden |
37
- | Graphics | Emoji at percentage position | Same tooltip |
38
-
39
- ## Emoji Index Formula
40
-
41
- ```typescript
42
- const idx = Math.round(((value - min) / (max - min)) * ((ticks || 10) - 1));
43
- const displayIdx = reverseScaleOrder ? (ticks || 10) - 1 - idx : idx;
44
- // Map displayIdx to react-icons emoji — proportional mapping is client-implemented (not an SDK export)
45
- ```
34
+ | `disabled` | Gray theme, opacity 0.5 |
35
+ | `sliderType: 'graphics'` | Emoji thumb via `surveyUiIcons.getEmojiForIndex` |
36
+ | `ticks` | Discrete tick count |
37
+ | **`tickLabels`** | **Column labels for tooltip + graphics index** |
38
+ | `reverseScaleOrder` | Flip emoji index |
46
39
 
47
40
  ## Agent Checklist
48
41
 
49
- - [ ] 4-layer overlay with invisible range input
42
+ - [ ] Copy portable template or match layer stack
43
+ - [ ] Always pass `tickLabels` from matrix `scaleColumns` when available
50
44
  - [ ] Tooltip on hover AND drag
51
- - [ ] Graphics mode emoji thumb
52
- - [ ] Transition disabled while dragging
45
+ - [ ] Graphics emoji thumb with `reverseScaleOrder` support
@@ -1,5 +1,5 @@
1
1
  {
2
- "docsSchemaVersion": "2.3.0",
2
+ "docsSchemaVersion": "2.4.0",
3
3
  "sdkTypesVersion": "0.2.1",
4
4
  "packageNames": {
5
5
  "npm": "@explorer02/cfm-survey-sdk",
@@ -107,8 +107,27 @@
107
107
  {
108
108
  "path": "00-integration/survey-lifecycle-analytics.md",
109
109
  "purpose": "Mark-as-started and question activity"
110
+ },
111
+ {
112
+ "path": "00-integration/logic-fields-catalog.md",
113
+ "purpose": "Logic field reference by question type for skip/display/answer/end"
114
+ },
115
+ {
116
+ "path": "00-integration/analytics-events-catalog.md",
117
+ "purpose": "Consolidated SDK analytics and client lifecycle events"
118
+ },
119
+ {
120
+ "path": "00-integration/file-upload-aws.md",
121
+ "purpose": "Presigned S3 upload flow for FILE_UPLOAD submit contract"
110
122
  }
111
123
  ],
124
+ "templateScaleFiles": [
125
+ "templates/RankOrderScale.tsx",
126
+ "templates/MatrixDropdown.tsx",
127
+ "templates/CustomSliderTrack.tsx",
128
+ "templates/surveyUiIcons.tsx",
129
+ "templates/heatmapCoords.ts"
130
+ ],
112
131
  "deployTargets": {
113
132
  "aws": {
114
133
  "doc": "00-integration/aws-deploy.md",
@@ -216,7 +235,9 @@
216
235
  "00-integration/partial-save-and-recovery.md",
217
236
  "00-integration/survey-lifecycle-analytics.md",
218
237
  "00-integration/end-page-logic.md",
219
- "00-integration/progress.md"
238
+ "00-integration/progress.md",
239
+ "00-integration/logic-fields-catalog.md",
240
+ "00-integration/analytics-events-catalog.md"
220
241
  ]
221
242
  },
222
243
  {
@@ -225,6 +246,7 @@
225
246
  "readAlways": [
226
247
  "00-integration/component-checklist.md",
227
248
  "00-integration/question-type-sdk-matrix.md",
249
+ "00-integration/logic-fields-catalog.md",
228
250
  "03-ui-specs/00-question-shell.md",
229
251
  "03-ui-specs/12-survey-chrome.md",
230
252
  "01-components/01-survey-page.md",
@@ -271,7 +293,10 @@
271
293
  "npmRunBuildPasses",
272
294
  "nextStaticExportConfigured",
273
295
  "outIndexHtmlExists",
274
- "outFileCountUnder500"
296
+ "outFileCountUnder500",
297
+ "rankOrderScaleTemplateCopied",
298
+ "dndKitWhenRankOrder",
299
+ "fileUploadPresignedFlowWhenFileUpload"
275
300
  ]
276
301
  },
277
302
  {
@@ -71,7 +71,7 @@ Ask: *"Please provide your survey instance ID (JWT token)."*
71
71
 
72
72
  ### PHASE 3: SDK CONTRACT
73
73
 
74
- Read: `00-integration/client-integration-guide.md` (start here), `00-integration/question-type-sdk-matrix.md`, `00-integration/useSurveySDK.md`, `02-reference/exports.md`, `00-integration/skip-logic-and-navigation.md`, `00-integration/display-logic-and-navigation.md`, `00-integration/question-display-variants.md`, `00-integration/question-numbering.md`, `00-integration/placeholders-and-tokens.md`, `00-integration/partial-save-and-recovery.md`, `00-integration/survey-lifecycle-analytics.md`, `00-integration/end-page-logic.md`, `00-integration/answer-logic-and-navigation.md`, `00-integration/custom-field-logic-and-navigation.md`, `00-integration/progress.md`, `02-reference/value-derivation.md`
74
+ Read: `00-integration/client-integration-guide.md` (start here), `00-integration/question-type-sdk-matrix.md`, `00-integration/useSurveySDK.md`, `02-reference/exports.md`, `00-integration/skip-logic-and-navigation.md`, `00-integration/display-logic-and-navigation.md`, `00-integration/question-display-variants.md`, `00-integration/question-numbering.md`, `00-integration/placeholders-and-tokens.md`, `00-integration/partial-save-and-recovery.md`, `00-integration/survey-lifecycle-analytics.md`, `00-integration/end-page-logic.md`, `00-integration/answer-logic-and-navigation.md`, `00-integration/custom-field-logic-and-navigation.md`, `00-integration/progress.md`, `02-reference/value-derivation.md`, `00-integration/logic-fields-catalog.md`, `00-integration/analytics-events-catalog.md`
75
75
 
76
76
  Understand `useSurveySDK`, `onAction`, `state.answers`. Do **not** read internal mapper/source paths.
77
77
 
@@ -94,7 +94,7 @@ Optional: read `02-reference/routing-table.md` for type overview.
94
94
 
95
95
  Read: `00-integration/constraints.md`, `00-integration/component-checklist.md`, `00-integration/question-type-sdk-matrix.md`
96
96
 
97
- Create all required scale component files **before** wiring `Question.tsx`. For each question in the survey:
97
+ Create all required scale component files **before** wiring `Question.tsx`. **Copy portable templates** from `docs/templates/` when listed in `component-checklist.md` (`RankOrderScale.tsx`, `MatrixDropdown.tsx`, `CustomSliderTrack.tsx`, `surveyUiIcons.tsx`, `heatmapCoords.ts`). For each question in the survey:
98
98
 
99
99
  1. Read the matching row in `question-type-sdk-matrix.md` (answer shape, visibility helper, performance)
100
100
  2. Read `02-reference/question-types/` doc for data shape (MANIFEST `reference`)
@@ -166,8 +166,9 @@ export OUT_DIR="<absolute path to out/ or dist/>"
166
166
  | When | Read |
167
167
  |------|------|
168
168
  | Always first | `index.md`, `00-integration/client-integration-guide.md`, `00-integration/question-type-sdk-matrix.md`, `00-integration/setup.md`, `00-integration/useSurveySDK.md`, `00-integration/constraints.md` |
169
- | Lifecycle & placeholders | `00-integration/survey-lifecycle-analytics.md`, `00-integration/placeholders-and-tokens.md`, `00-integration/partial-save-and-recovery.md` |
170
- | Skip logic & progress | `00-integration/skip-logic-and-navigation.md`, `00-integration/display-logic-and-navigation.md`, `00-integration/question-display-variants.md`, `00-integration/question-numbering.md`, `00-integration/end-page-logic.md`, `00-integration/answer-logic-and-navigation.md`, `00-integration/custom-field-logic-and-navigation.md`, `00-integration/progress.md` |
169
+ | Lifecycle & placeholders | `survey-lifecycle-analytics.md`, `placeholders-and-tokens.md`, `partial-save-and-recovery.md`, `analytics-events-catalog.md` |
170
+ | Skip logic & progress | `skip-logic-and-navigation.md`, `display-logic-and-navigation.md`, `question-display-variants.md`, `question-numbering.md`, `end-page-logic.md`, `answer-logic-and-navigation.md`, `custom-field-logic-and-navigation.md`, `progress.md`, `logic-fields-catalog.md` |
171
+ | File upload submit | `file-upload-aws.md`, `03-ui-specs/10-file-upload.md` |
171
172
  | Before any React code | `component-checklist.md`, `templates/Question.tsx`, MANIFEST `readAlways` docs |
172
173
  | After instanceId fetch | Per-type docs from MANIFEST `questionTypes` (blocking: `CFM_MATRIX`, `CSAT_MATRIX`, `RATING_MATRIX`, `SLIDER_MATRIX`, `FILE_UPLOAD`) |
173
174
  | Config audit | `02-reference/config-field-index.md` |