@explorer02/cfm-survey-sdk 0.1.6 → 0.1.8
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/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/postinstall.js +18 -4
- package/templates/docs/03-client-components/01-survey-page.md +35 -57
- package/templates/docs/03-client-components/02-question.md +78 -47
- package/templates/docs/03-client-components/03-rating-scale.md +20 -83
- package/templates/docs/03-client-components/04-csat-scale.md +29 -70
- package/templates/docs/03-client-components/05-csat-matrix-scale.md +24 -68
- package/templates/docs/03-client-components/06-likert-matrix-scale.md +41 -40
- package/templates/docs/03-client-components/07-slider-matrix-scale.md +33 -81
- package/templates/docs/03-client-components/08-file-upload-scale.md +53 -84
- package/templates/docs/03-client-components/09-custom-slider-track.md +27 -63
- package/templates/docs/03-client-components/10-header-footer.md +18 -41
- package/templates/docs/index.md +31 -9
|
@@ -1,43 +1,44 @@
|
|
|
1
|
-
# Likert Matrix
|
|
1
|
+
# Likert Matrix Architectural Blueprint
|
|
2
2
|
|
|
3
|
-
> **Component**: `LikertMatrixScale`
|
|
3
|
+
> **Target Component**: `LikertMatrixScale`
|
|
4
4
|
> **Handles**: `type: 'matrix'` (CFM_MATRIX subType)
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
6
|
+
## Core Responsibility
|
|
7
|
+
Render a grid with text-label columns. However, this component must be highly dynamic to support complex layouts, transpositions, and long-form matrix readability.
|
|
8
|
+
|
|
9
|
+
## Data Mapping & State Management
|
|
10
|
+
- `selectedValue`: Expects `MatrixAnswerMap` shape (`Record<string, string | number | null>`).
|
|
11
|
+
- **Mutation**: When radio button is clicked for `row.id` and `col.value`, dispatch `onSelect({ ...selectedValue, [row.id]: col.value })`.
|
|
12
|
+
|
|
13
|
+
## Configuration Matrix (CRITICAL FOR PLANNING)
|
|
14
|
+
|
|
15
|
+
Your component architecture MUST account for the following boolean flags from `question`:
|
|
16
|
+
|
|
17
|
+
### 1. `transposeTable`
|
|
18
|
+
If `true`, the standard Row-by-Column matrix is inverted:
|
|
19
|
+
- `question.columns` become the row labels on the left.
|
|
20
|
+
- `question.rows` become the top column headers.
|
|
21
|
+
- **Architectural Requirement**: Your `<tbody>` map must iterate over `question.columns` first, and render `<td>` cells mapped from `question.rows`.
|
|
22
|
+
- Ensure `onSelect` still correctly pairs the original `row.id` with the original `col.value`.
|
|
23
|
+
|
|
24
|
+
### 2. `repeatScale`
|
|
25
|
+
If `true`, the `<thead>` containing column labels must be re-injected periodically to prevent users from losing track of columns on long matrices.
|
|
26
|
+
- **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>`.
|
|
27
|
+
|
|
28
|
+
### 3. `matrixType === 'bipolar'`
|
|
29
|
+
Standard matrices (`'likert'`) only show statement text on the left. Bipolar scales show text on BOTH sides of the radio buttons.
|
|
30
|
+
- **Implementation Strategy**: Render an extra `<td>` at the end of the row containing `row.rightText`.
|
|
31
|
+
|
|
32
|
+
## UI/UX & Styling Constraints
|
|
33
|
+
|
|
34
|
+
To match enterprise standards, implement the following UI strictly:
|
|
35
|
+
|
|
36
|
+
1. **Row Tracking (Hover Effects)**:
|
|
37
|
+
- Every data `<tr>` MUST have `hover:bg-gray-50 transition-colors duration-150 group`. This is mandatory for UX on wide matrices.
|
|
38
|
+
2. **Radio Buttons**:
|
|
39
|
+
- Use `accent-[#e20074] w-5 h-5 cursor-pointer` or build custom CSS circles that fill with the brand color.
|
|
40
|
+
- Do NOT use standard unstyled blue browser radios.
|
|
41
|
+
3. **Tooltips (Accessibility)**:
|
|
42
|
+
- Every input MUST have `title={col.label}` so the user sees the semantic meaning when hovering the radio button.
|
|
43
|
+
4. **Header Typography**:
|
|
44
|
+
- `th` tags should be `text-xs font-semibold text-gray-500 uppercase tracking-wider p-4 border-b`.
|
|
@@ -1,93 +1,45 @@
|
|
|
1
|
-
# Slider Matrix
|
|
1
|
+
# Slider Matrix Architectural Blueprint
|
|
2
2
|
|
|
3
|
-
> **Component**: `SliderMatrixScale`
|
|
3
|
+
> **Target Component**: `SliderMatrixScale`
|
|
4
4
|
> **Handles**: `type: 'slider_matrix'`
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
|
|
8
|
-
type SliderMatrixScaleProps = {
|
|
9
|
-
question: SurveyQuestion & { type: 'slider_matrix' };
|
|
10
|
-
selectedValue: MatrixAnswerMap;
|
|
11
|
-
onSelect: (value: MatrixAnswerMap) => void;
|
|
12
|
-
};
|
|
13
|
-
```
|
|
6
|
+
## Core Responsibility
|
|
7
|
+
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.
|
|
14
8
|
|
|
15
|
-
##
|
|
9
|
+
## State Management
|
|
10
|
+
- `selectedValue`: Expects `MatrixAnswerMap` shape.
|
|
11
|
+
- `row.min`, `row.max`, and `row.step` must dictate the boundaries of the slider logic.
|
|
12
|
+
- **Mutation**: `onChange` of the input must parse the value as a Number before dispatching `onSelect`.
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
## The "Thick Slider" UX Implementation (CRITICAL)
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
The slider track must be visually prominent:
|
|
21
|
-
- Height: `h-2` or `h-3`.
|
|
22
|
-
- Rounded corners: `rounded-full`.
|
|
23
|
-
- **Dynamic Fill**: The track up to the thumb MUST be pink (`bg-[#e20074]`). The track after the thumb MUST be light gray (`bg-gray-200`).
|
|
24
|
-
- You can achieve this using CSS linear-gradients tied to the value percentage, or by using a custom overlay div.
|
|
16
|
+
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.
|
|
25
17
|
|
|
26
|
-
###
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
### The Invisible Overlay Pattern
|
|
19
|
+
For every slider row, build a relative container:
|
|
20
|
+
1. **Base Track**: `absolute w-full h-2 bg-gray-200 rounded-full`.
|
|
21
|
+
2. **Dynamic Fill Track**: `absolute h-2 bg-[#e20074] rounded-l-full`.
|
|
22
|
+
- Calculate width dynamically: `((value - min) / (max - min)) * 100 + '%'`.
|
|
23
|
+
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`.
|
|
24
|
+
- Position it dynamically: `left: calc(${percentage}% - 10px)`.
|
|
25
|
+
4. **The Input Control**: Layer a native input on top, completely invisible but clickable:
|
|
26
|
+
- `<input type="range" className="absolute w-full h-full opacity-0 cursor-pointer z-10" />`
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
- The exact selected value (e.g., `7`) **MUST** be displayed visibly.
|
|
32
|
-
- Typical layout: Place it at the far right of the slider track in a small bold badge.
|
|
28
|
+
## Configuration Matrix (CRITICAL FOR PLANNING)
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
- `row.ticks`: If provided (e.g., `10`), render tiny tick marks beneath the track at intervals.
|
|
36
|
-
- `row.minLabel` & `row.maxLabel`: Render underneath the far left and far right of the track.
|
|
30
|
+
Your component must account for:
|
|
37
31
|
|
|
38
|
-
|
|
32
|
+
### 1. Exact Value Display
|
|
33
|
+
- Users must see the exact number they are selecting.
|
|
34
|
+
- Render a badge at the far right of the slider track: `shrink-0 w-8 text-center font-bold text-[#e20074]`.
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const val = selectedValue[row.id] ?? row.min;
|
|
43
|
-
const percentage = ((val - row.min) / (row.max - row.min)) * 100;
|
|
36
|
+
### 2. `row.ticks` & `tickValues`
|
|
37
|
+
- 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.
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<div className="absolute w-full h-2 bg-gray-200 rounded-full" />
|
|
53
|
-
|
|
54
|
-
{/* Track Fill (Pink) */}
|
|
55
|
-
<div
|
|
56
|
-
className="absolute h-2 bg-[#e20074] rounded-l-full"
|
|
57
|
-
style={{ width: `${percentage}%` }}
|
|
58
|
-
/>
|
|
59
|
-
|
|
60
|
-
{/* Native Input (Invisible overlay for mechanics) */}
|
|
61
|
-
<input
|
|
62
|
-
type="range"
|
|
63
|
-
min={row.min}
|
|
64
|
-
max={row.max}
|
|
65
|
-
step={row.step}
|
|
66
|
-
value={val}
|
|
67
|
-
onChange={e => onSelect({ ...selectedValue, [row.id]: Number(e.target.value) })}
|
|
68
|
-
className="absolute w-full h-full opacity-0 cursor-pointer z-10"
|
|
69
|
-
title={`Value: ${val}`}
|
|
70
|
-
/>
|
|
71
|
-
|
|
72
|
-
{/* Visual Thumb */}
|
|
73
|
-
<div
|
|
74
|
-
className="absolute w-5 h-5 bg-[#e20074] border-2 border-white rounded-full shadow-md pointer-events-none transition-transform"
|
|
75
|
-
style={{ left: `calc(${percentage}% - 10px)` }}
|
|
76
|
-
/>
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
{/* Value Display Badge */}
|
|
80
|
-
<div className="shrink-0 w-8 text-center font-bold text-[#e20074]">
|
|
81
|
-
{val}
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
{/* Min/Max Labels */}
|
|
86
|
-
<div className="flex justify-between text-xs text-gray-500 font-medium px-1">
|
|
87
|
-
<span>{row.minLabel}</span>
|
|
88
|
-
<span>{row.maxLabel}</span>
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
);
|
|
92
|
-
})}
|
|
93
|
-
```
|
|
39
|
+
### 3. Label Positioning
|
|
40
|
+
- `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
|
+
|
|
42
|
+
### 4. `enableNotApplicable`
|
|
43
|
+
- If `true`, render a checkbox at the end of the row.
|
|
44
|
+
- Checking it sets the value in the map to `null`.
|
|
45
|
+
- When `value === null`, gray out the slider track and disable the input.
|
|
@@ -1,87 +1,56 @@
|
|
|
1
|
-
# File Upload
|
|
1
|
+
# File Upload Architectural Blueprint
|
|
2
2
|
|
|
3
|
-
> **Component**: `FileUploadScale`
|
|
3
|
+
> **Target Component**: `FileUploadScale`
|
|
4
4
|
> **Handles**: `type: 'file_upload'`
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<p className="text-sm font-semibold text-gray-700 group-hover:text-[#e20074]">
|
|
58
|
-
{question.uploadMessage || "Click or drag files here to upload"}
|
|
59
|
-
</p>
|
|
60
|
-
<p className="text-xs text-gray-500">
|
|
61
|
-
Supported formats: {question.supportedFileFormats?.join(', ')} (Max {question.fileSizeLimit}MB)
|
|
62
|
-
</p>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
{/* Uploaded Files List */}
|
|
66
|
-
{selectedValue && selectedValue.length > 0 && (
|
|
67
|
-
<ul className="space-y-2 mt-4">
|
|
68
|
-
{selectedValue.map((file, i) => (
|
|
69
|
-
<li key={i} className="flex items-center justify-between p-3 border border-gray-200 rounded-lg bg-white shadow-sm">
|
|
70
|
-
<div className="flex items-center gap-3 overflow-hidden">
|
|
71
|
-
<span className="text-gray-400">📄</span>
|
|
72
|
-
<span className="text-sm font-medium text-gray-700 truncate">{file.name}</span>
|
|
73
|
-
{file.size && <span className="text-xs text-gray-400">({(file.size / 1024 / 1024).toFixed(2)} MB)</span>}
|
|
74
|
-
</div>
|
|
75
|
-
<button
|
|
76
|
-
onClick={() => onSelect(selectedValue.filter((_, index) => index !== i))}
|
|
77
|
-
className="text-red-500 hover:bg-red-50 p-2 rounded-md transition-colors"
|
|
78
|
-
title="Remove file"
|
|
79
|
-
>
|
|
80
|
-
🗑️
|
|
81
|
-
</button>
|
|
82
|
-
</li>
|
|
83
|
-
))}
|
|
84
|
-
</ul>
|
|
85
|
-
)}
|
|
86
|
-
</div>
|
|
87
|
-
```
|
|
6
|
+
## Core Responsibility
|
|
7
|
+
Render a robust drag-and-drop zone and enforce complex file size, type limits, and maximum file count constraints BEFORE updating the state.
|
|
8
|
+
|
|
9
|
+
## State Management
|
|
10
|
+
- `selectedValue`: Expects an array of native `File` objects (or serialized equivalents).
|
|
11
|
+
- **Mutation Pattern**:
|
|
12
|
+
1. The user selects files.
|
|
13
|
+
2. The component intercepts the files.
|
|
14
|
+
3. The component validates them against ALL limits.
|
|
15
|
+
4. If valid, append the new files to `selectedValue` (handling it as an array).
|
|
16
|
+
5. Call `onSelect(updatedFilesArray)`.
|
|
17
|
+
|
|
18
|
+
## Configuration Matrix (CRITICAL FOR PLANNING)
|
|
19
|
+
|
|
20
|
+
Before calling `onSelect`, your component MUST validate against these properties:
|
|
21
|
+
|
|
22
|
+
### 1. `supportedFileFormats`
|
|
23
|
+
- Extracted from `question.supportedFileFormats` (e.g. `['PDF', 'PNG', 'JPG']`).
|
|
24
|
+
- Use this to build the `accept` attribute string for the hidden input: `question.supportedFileFormats?.map(f => \`.\${f.toLowerCase()}\`).join(',')`.
|
|
25
|
+
- You MUST reject files with extensions that are not in this list.
|
|
26
|
+
|
|
27
|
+
### 2. `maxFileCount`
|
|
28
|
+
- Prevent uploads if `currentFiles.length + newFiles.length > maxFileCount`.
|
|
29
|
+
- Provide a clear local error message indicating the max limit.
|
|
30
|
+
|
|
31
|
+
### 3. `fileSizeLimit` & `fileSizeLimitType`
|
|
32
|
+
- `fileSizeLimit` is a number representing megabytes (MB).
|
|
33
|
+
- If `fileSizeLimitType === 'PER_FILE'`, validate that EVERY incoming file's `size` (in bytes) is `<= fileSizeLimit * 1024 * 1024`.
|
|
34
|
+
- If `fileSizeLimitType === 'IN_TOTAL'`, validate that the combined sum of all existing `selectedValue` sizes plus the incoming files' sizes is `<= fileSizeLimit * 1024 * 1024`.
|
|
35
|
+
|
|
36
|
+
## UI/UX Specifications
|
|
37
|
+
|
|
38
|
+
1. **Dropzone**:
|
|
39
|
+
- Wrap the main area with `onDragEnter`, `onDragOver`, `onDragLeave`, `onDrop`.
|
|
40
|
+
- **Inactive State**: `border-2 border-dashed border-gray-300 rounded-xl bg-gray-50 p-8 text-center cursor-pointer transition-all`.
|
|
41
|
+
- **Drag/Hover State**: `border-[#e20074] bg-[#fdf2f8]` (Using the brand primary color).
|
|
42
|
+
- Display a Cloud SVG icon (use gray, switch to magenta on hover).
|
|
43
|
+
- Render `question.uploadMessage` (which can contain HTML, so use `dangerouslySetInnerHTML`).
|
|
44
|
+
- Dynamically render the limits text (e.g., "Max 3 files, 10MB per file").
|
|
45
|
+
|
|
46
|
+
2. **File List**:
|
|
47
|
+
- Render existing files as distinct cards underneath the dropzone.
|
|
48
|
+
- Styling: `flex items-center justify-between p-3 mt-3 border border-gray-200 rounded-lg bg-white shadow-sm`.
|
|
49
|
+
- Display the filename (truncated if long) and the formatted file size (e.g., "1.2 MB").
|
|
50
|
+
- Include a distinct, circular Trash/Remove button that removes the file from the state and calls `onSelect` with the updated array.
|
|
51
|
+
|
|
52
|
+
3. **Validation Errors**:
|
|
53
|
+
- Local validation errors (e.g. "File too large") must be displayed in a highly visible red banner (`bg-red-50 text-red-700 p-3 rounded-md`) right below the dropzone.
|
|
54
|
+
|
|
55
|
+
## Hidden Input
|
|
56
|
+
- Include a hidden `<input type="file" multiple={question.maxFileCount > 1} accept={...} />` that is triggered via a `useRef` when the dropzone is clicked.
|
|
@@ -1,72 +1,36 @@
|
|
|
1
|
-
# Custom Slider Track
|
|
1
|
+
# Custom Slider Track Architectural Blueprint
|
|
2
2
|
|
|
3
|
-
> **Component**: `CustomSliderTrack`
|
|
4
|
-
> **Used
|
|
3
|
+
> **Target Component**: `CustomSliderTrack`
|
|
4
|
+
> **Used By**: `SliderMatrixScale`, `CsatMatrixScale`, `CsatScale`
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
|
|
6
|
+
## Core Responsibility
|
|
7
|
+
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.
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
If the API specifies `graphics`, the slider thumb **MUST** dynamically display an emoji corresponding to the current value.
|
|
11
|
-
- The thumb must be large enough to hold an emoji (`w-8 h-8` or `w-10 h-10`).
|
|
12
|
-
- Use `getEmojiForIndex` from the SDK to map the current percentage/value to the correct sentiment face.
|
|
13
|
-
- The thumb must have a shadow and white background to pop out from the track: `bg-white rounded-full shadow-md border border-gray-200 flex items-center justify-center text-xl`.
|
|
9
|
+
## The Invisible Overlay Architecture (CRITICAL)
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
Native `<input type="range">` cannot reliably render an emoji inside the draggable thumb handle cross-browser.
|
|
11
|
+
Instead of trying to style the `::-webkit-slider-thumb` pseudo-elements, you MUST build the slider using layered DOM elements.
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
3. Render a visual thumb `div` (the pink circle or the emoji, left bound to `percentage`).
|
|
22
|
-
4. **Overlay** a completely invisible native `<input type="range" className="absolute opacity-0 w-full h-full cursor-pointer z-10" />`.
|
|
13
|
+
### Layer 1: The Background Track
|
|
14
|
+
A static `<div>` representing the entire length of the slider.
|
|
15
|
+
- CSS: `absolute w-full h-2 bg-gray-200 rounded-full`.
|
|
23
16
|
|
|
24
|
-
|
|
17
|
+
### Layer 2: The Active Fill
|
|
18
|
+
A dynamic `<div>` representing the portion of the slider up to the current value.
|
|
19
|
+
- CSS: `absolute h-2 bg-[#e20074] rounded-l-full`.
|
|
20
|
+
- Inline Style: `style={{ width: \`\${percentage}%\` }}`.
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
### Layer 3: The Custom Thumb
|
|
23
|
+
A visual element that floats along the track.
|
|
24
|
+
- **For Standard Sliders**: A pink circle. `w-5 h-5 bg-[#e20074] border-2 border-white rounded-full shadow-md pointer-events-none`.
|
|
25
|
+
- **For Graphics Sliders (`sliderType === 'graphics'`)**: A larger white circle containing an emoji mapped to the current value using `getEmojiForIndex`. `w-10 h-10 bg-white shadow-md border flex items-center justify-center text-2xl`.
|
|
26
|
+
- Inline Style: `style={{ left: \`\${percentage}%\` }}`.
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
### Layer 4: The Native Input (The Engine)
|
|
29
|
+
An invisible native input overlaid exactly on top to capture all mouse/touch events automatically without needing manual math for drag interactions.
|
|
30
|
+
- CSS: `<input type="range" className="absolute w-full h-full opacity-0 cursor-pointer z-10" />`.
|
|
31
|
+
- This ensures perfect accessibility, keyboard navigation, and mobile touch support.
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{/* Filled Track */}
|
|
41
|
-
<div
|
|
42
|
-
className="absolute h-2 bg-[#e20074] rounded-l-full"
|
|
43
|
-
style={{ width: `${percentage}%` }}
|
|
44
|
-
/>
|
|
45
|
-
|
|
46
|
-
{/* Invisible Native Input */}
|
|
47
|
-
<input
|
|
48
|
-
type="range"
|
|
49
|
-
min={min}
|
|
50
|
-
max={max}
|
|
51
|
-
step={step}
|
|
52
|
-
value={value}
|
|
53
|
-
onChange={e => onChange(Number(e.target.value))}
|
|
54
|
-
className="absolute w-full h-full opacity-0 cursor-pointer z-10"
|
|
55
|
-
title={`Value: ${value}`}
|
|
56
|
-
/>
|
|
57
|
-
|
|
58
|
-
{/* Custom Thumb */}
|
|
59
|
-
<div
|
|
60
|
-
className={`absolute pointer-events-none transition-transform flex items-center justify-center ${
|
|
61
|
-
sliderType === 'graphics'
|
|
62
|
-
? 'w-10 h-10 bg-white rounded-full shadow-md border border-gray-200 text-2xl -ml-5'
|
|
63
|
-
: 'w-5 h-5 bg-[#e20074] border-2 border-white rounded-full shadow-md -ml-2.5 hover:scale-125'
|
|
64
|
-
}`}
|
|
65
|
-
style={{ left: `${percentage}%` }}
|
|
66
|
-
>
|
|
67
|
-
{sliderType === 'graphics' && emoji}
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
```
|
|
33
|
+
## Configuration Logic
|
|
34
|
+
- Calculate percentage carefully: `((value - min) / (max - min)) * 100`.
|
|
35
|
+
- 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.
|
|
36
|
+
- 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.
|
|
@@ -1,46 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Theme & Logo Engineering Blueprint
|
|
2
2
|
|
|
3
|
-
> **Components**: `Header`,
|
|
4
|
-
> **
|
|
3
|
+
> **Target Components**: `Header`, Global Styles
|
|
4
|
+
> **Handles**: Branding, Mockup Replication
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Core Responsibility
|
|
7
|
+
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.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
## Logo Engineering Rules (CRITICAL)
|
|
10
|
+
- **NO EXTERNAL IMAGES**: You are strictly forbidden from writing `<img src="url" />` for logos, because you do not have a CDN to host them.
|
|
11
|
+
- **CSS-Only Logos**: You MUST construct the company logo using HTML and CSS primitives.
|
|
12
|
+
- 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`).
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
## Theme Engineering
|
|
15
|
+
- Map the primary brand color to the accent and border colors across the entire survey.
|
|
16
|
+
- If the mockup shows a distinct font, update the Next.js `layout.tsx` to pull that font from `next/font/google` (e.g. `Inter`, `Outfit`, `Roboto`).
|
|
17
|
+
- Ensure the header, footer, and progress bar strictly align with the color palette extracted from the mockup.
|
|
11
18
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<header className="flex h-16 items-center justify-between border-b px-6">
|
|
18
|
-
{/* CSS-only Abstract Logo */}
|
|
19
|
-
<div className="flex items-center gap-2">
|
|
20
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-[#e20074]">
|
|
21
|
-
<div className="h-4 w-4 rounded-full bg-white" />
|
|
22
|
-
</div>
|
|
23
|
-
<span className="text-xl font-bold tracking-tight text-gray-900">
|
|
24
|
-
YourBrand
|
|
25
|
-
</span>
|
|
26
|
-
</div>
|
|
27
|
-
</header>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## The Footer
|
|
33
|
-
|
|
34
|
-
```tsx
|
|
35
|
-
export default function Footer() {
|
|
36
|
-
return (
|
|
37
|
-
<footer className="border-t py-6 text-center text-sm text-gray-500">
|
|
38
|
-
<p>Powered by Sprinklr CFM SDK</p>
|
|
39
|
-
</footer>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Customization
|
|
45
|
-
|
|
46
|
-
The client may provide a prompt asking for specific colors ("make it dark mode", "use blue"). Update the CSS classes in `SurveyPage`, `Header`, and `Footer` to match their request. Do not try to reverse-engineer their brand logo exactly — stick to clean abstract CSS geometry unless you have an explicit SVG path.
|
|
19
|
+
## Planning Requirement
|
|
20
|
+
In your `implementation_plan.md`, explicitly document:
|
|
21
|
+
1. What CSS techniques you will use to build the abstract/geometric logo.
|
|
22
|
+
2. What primary HEX color you will use for the theme.
|
|
23
|
+
3. What Google font you will import.
|
package/templates/docs/index.md
CHANGED
|
@@ -76,15 +76,37 @@ Then understand what question types exist:
|
|
|
76
76
|
|
|
77
77
|
*You do NOT need to read every question type doc upfront. Read them ON DEMAND when you encounter each type during component building.*
|
|
78
78
|
|
|
79
|
-
### PHASE 4:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
79
|
+
### PHASE 4: UI ARCHITECTURE & WORKFLOW PLANNING (CRITICAL)
|
|
80
|
+
Before writing ANY React code, you MUST enter Planning Mode and generate a deeply comprehensive \`implementation_plan.md\`.
|
|
81
|
+
|
|
82
|
+
**DEEP CONTEXT INGESTION REQUIRED**:
|
|
83
|
+
Before you even start writing the plan, you MUST read EVERY SINGLE markdown file inside \`03-client-components/\`. You cannot plan accurately without knowing the exact architectures required for CSAT bounds, file validations, and matrix transpositions.
|
|
84
|
+
|
|
85
|
+
Read ALL the component architectural blueprints IN ORDER:
|
|
86
|
+
1. \`03-client-components/README.md\`
|
|
87
|
+
2. \`03-client-components/01-survey-page.md\`
|
|
88
|
+
3. \`03-client-components/02-question.md\`
|
|
89
|
+
4. \`03-client-components/03-rating-scale.md\`
|
|
90
|
+
5. \`03-client-components/04-csat-scale.md\`
|
|
91
|
+
6. \`03-client-components/05-csat-matrix-scale.md\`
|
|
92
|
+
7. \`03-client-components/06-likert-matrix-scale.md\`
|
|
93
|
+
8. \`03-client-components/07-slider-matrix-scale.md\`
|
|
94
|
+
9. \`03-client-components/08-file-upload-scale.md\`
|
|
95
|
+
10. \`03-client-components/09-custom-slider-track.md\`
|
|
96
|
+
11. \`03-client-components/10-header-footer.md\`
|
|
97
|
+
12. \`03-client-components/11-progress-bar.md\`
|
|
98
|
+
13. \`03-client-components/12-language-selector.md\`
|
|
99
|
+
14. \`03-client-components/13-matrix-dropdown.md\`
|
|
100
|
+
|
|
101
|
+
Your \`implementation_plan.md\` MUST be structured professionally and precisely cover:
|
|
102
|
+
1. **Data Fetching & State**: Precisely how \`useSurveySDK(instanceId)\` will be integrated. How to handle \`isLoading\`, \`error\`, \`empty\`, and \`submitted\` states properly.
|
|
103
|
+
2. **Pagination, Actions, & Flow**: How you will iterate through pages, manage \`onAction({ type: 'NEXT' / 'PREVIOUS' })\`, and precisely wire up the \`ProgressBar\`.
|
|
104
|
+
3. **Question Dispatching**: How \`Question.tsx\` will comprehensively route all 12 \`question.type\` variants (including \`text_and_media\` for \`CONTENT\` questions). How it will safely handle rich HTML parsing and validation error display.
|
|
105
|
+
4. **Question Architectures & Component Checklist**: You must create a checklist inside your plan for EACH complex component. You must verify mappings, step sizes, ticks, hover effects, scale items positioning, and matrix transposition logic EXACTLY as detailed in the blueprints.
|
|
106
|
+
5. **Theme, Brand & Logos**: How you will engineer CSS-only logos based on the client's mockup prompt and map specific brand colors to hover states.
|
|
107
|
+
6. **Submit Survey**: How you will trigger the final \`submitSurvey\` dispatch and elegantly transition the UI to the Thank You screen.
|
|
108
|
+
|
|
109
|
+
**ACTION**: Generate the \`implementation_plan.md\`. Once the plan is written, **DO NOT WAIT FOR APPROVAL**. Automatically proceed to trigger Phase 5 and start implementing the code!
|
|
88
110
|
|
|
89
111
|
Then for EACH question type present in the fetched survey, read the matching question-type doc AND component doc:
|
|
90
112
|
|