@explorer02/cfm-survey-sdk 0.2.7 → 0.2.9
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 +38 -41
- package/dist/cli/index.mjs +36 -39
- package/package.json +1 -1
- package/templates/docs/00-integration/agent-execution-flow.md +7 -9
- package/templates/docs/00-integration/agent-operating-contract.md +2 -1
- package/templates/docs/00-integration/component-checklist.md +1 -0
- package/templates/docs/00-integration/ui-customization-wizard.md +29 -7
- package/templates/docs/00-integration/wizard-chrome-contract.md +11 -1
- package/templates/docs/00-integration/wizard-config-handoff.md +11 -9
- package/templates/docs/00-integration/wizard-post-customize-build.md +8 -2
- package/templates/docs/00-integration/wizard-preview-build-guide.md +0 -2
- package/templates/docs/00-integration/wizard-question-type-styling.md +7 -6
- package/templates/docs/00-integration/wizard-troubleshooting.md +20 -4
- package/templates/docs/03-ui-specs/README.md +1 -1
- package/templates/docs/MANIFEST.json +0 -3
- package/templates/docs/index.md +4 -6
- package/templates/docs/templates/CsatMatrixScale.tsx +56 -53
- package/templates/docs/templates/CustomSliderTrack.tsx +17 -8
- package/templates/docs/templates/FileUploadScale.tsx +11 -3
- package/templates/docs/templates/Header.tsx +32 -48
- package/templates/docs/templates/HeatmapScale.tsx +7 -4
- package/templates/docs/templates/LikertMatrixScale.tsx +58 -43
- package/templates/docs/templates/MatrixDropdown.tsx +12 -11
- package/templates/docs/templates/Question.tsx +4 -23
- package/templates/docs/templates/RankOrderScale.tsx +46 -6
- package/templates/docs/templates/RatingScale.tsx +38 -0
- package/templates/docs/templates/SliderMatrixScale.tsx +4 -3
- package/templates/docs/templates/verify-agent-build.sh +17 -0
- package/templates/preview-harness/preview-bridge.inline.js +10 -0
- package/templates/preview-harness/previewPages.ts +5 -7
- package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +11 -6
- package/templates/preview-harness/vite-app/src/fixtures/questions.ts +2 -10
- package/templates/preview-harness/vite-app/src/preview-live-overrides.css +11 -6
- package/templates/previewBridge.ts +13 -0
- package/templates/survey-theme.css +17 -17
- package/templates/wizard-dist/assets/{PreviewMock-tTMvwuEy.js → PreviewMock-CysRws9G.js} +1 -1
- package/templates/wizard-dist/assets/TypePanel-DWX1LL3q.js +1 -0
- package/templates/wizard-dist/assets/index-DIuc7AU2.js +34 -0
- package/templates/wizard-dist/assets/index-pCM8n0FT.css +1 -0
- package/templates/wizard-dist/index.html +2 -2
- package/templates/docs/00-integration/wizard-customization-catalog.md +0 -391
- package/templates/wizard-dist/assets/TypePanel-B9Ls7BSq.js +0 -1
- package/templates/wizard-dist/assets/index-CjkIlOUI.css +0 -1
- package/templates/wizard-dist/assets/index-CoCrnnxt.js +0 -34
|
@@ -25,10 +25,21 @@ Pipeline: [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) · [`
|
|
|
25
25
|
| Text placeholder color not updating | `data-cfm-textfield` + `--cfm-input-placeholder` in CSS / `::placeholder` rule |
|
|
26
26
|
| Matrix 2nd row toggle | Harness adds row via `previewMultiStatement` — `LikertMatrixScale` must render all `statementRows` from props |
|
|
27
27
|
| Blank page after deploy | **Never** reference `UI_CONFIG` without import — use `src/lib/uiConfig.ts` |
|
|
28
|
+
| Logo pink/colored box behind image | Logo `background: var(--cfm-header-logo-bg, transparent)` — never `var(--cfm-primary)` on logo img |
|
|
29
|
+
| Logo + company placement | Locked: logo left, company name after logo in `.cfm-header-brand` flex row — no grid placement templates |
|
|
30
|
+
| Logo size | `--cfm-header-logo-width/height` from wizard max-dimension slider; `object-contain` |
|
|
28
31
|
| Logo wrong on CloudFront | Never change `global.logo.fileName` manually — use wizard file in `public/` |
|
|
29
32
|
|
|
30
33
|
---
|
|
31
34
|
|
|
35
|
+
## Agent link handoff (strict)
|
|
36
|
+
|
|
37
|
+
1. Run `npx cfm-sdk customize` — read **`WIZARD_LINK=http://localhost:{port}`** from CLI (port may be 4201+)
|
|
38
|
+
2. Share **only** that URL with the client
|
|
39
|
+
3. On quit without save: CLI exits 0, GC `out/previews/` — **do not** re-run wizard; ask deploy (Phase 7)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
32
43
|
## Copy order (agents — do this in Phase 5)
|
|
33
44
|
|
|
34
45
|
```bash
|
|
@@ -276,7 +287,6 @@ EC2 `customize-ui`: quit/save uses disk session GC — see [`wizard-config-hando
|
|
|
276
287
|
|
|
277
288
|
| Doc | Role |
|
|
278
289
|
|-----|------|
|
|
279
|
-
| [`wizard-customization-catalog.md`](wizard-customization-catalog.md) | Every wizard control → CSS var → component |
|
|
280
290
|
| [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) | Full build guide |
|
|
281
291
|
| [`wizard-post-customize-build.md`](wizard-post-customize-build.md) | After wizard save — apply all formats + build |
|
|
282
292
|
| [`wizard-question-type-styling.md`](wizard-question-type-styling.md) | Per-type CSS vars |
|
|
@@ -30,16 +30,18 @@ npx cfm-sdk customize
|
|
|
30
30
|
The CLI will:
|
|
31
31
|
|
|
32
32
|
1. Export all 11 component preview pages (`export-previews` if needed)
|
|
33
|
-
2.
|
|
34
|
-
3.
|
|
35
|
-
4.
|
|
36
|
-
5.
|
|
33
|
+
2. Probe first free port from 4200 and start wizard — CLI prints **`WIZARD_LINK=http://localhost:{port}`**
|
|
34
|
+
3. **Block** until client clicks **Review & Build** or quits
|
|
35
|
+
4. On save: write `survey-ui-config.json` and exit 0
|
|
36
|
+
5. On quit: GC `out/previews/` and exit 0 — **do not re-run wizard**; ask deploy (Phase 7)
|
|
37
37
|
|
|
38
38
|
**Agent must:**
|
|
39
39
|
|
|
40
|
-
- [ ]
|
|
41
|
-
- [ ]
|
|
42
|
-
- [ ]
|
|
40
|
+
- [ ] Parse `WIZARD_LINK` from CLI and share **only** that URL with client (never hardcode `:4200`)
|
|
41
|
+
- [ ] Client is on the **same machine** (can open the `WIZARD_LINK` host)
|
|
42
|
+
- [ ] Keep terminal open until CLI exits
|
|
43
|
+
- [ ] On save: apply per Phase 6c from `survey-ui-config.json`
|
|
44
|
+
- [ ] On quit: skip Phase 6c; ask deploy confirmation with existing Phase 6 build
|
|
43
45
|
|
|
44
46
|
**Artifact:** `survey-ui-config.json` — apply source for local flow.
|
|
45
47
|
|
|
@@ -54,7 +56,7 @@ When the client uploads a logo in the local wizard:
|
|
|
54
56
|
|
|
55
57
|
The logo is **not** fetched from wizard memory on CloudFront — only from the deploy bundle. See [`aws-deploy.md`](aws-deploy.md) § Logos & local assets.
|
|
56
58
|
|
|
57
|
-
**Local quit (preview GC):** If the client exits without saving, wizard calls `POST /api/wizard-quit` → CLI deletes `out/previews
|
|
59
|
+
**Local quit (preview GC):** If the client exits without saving, wizard calls `POST /api/wizard-quit` → CLI deletes `out/previews/` → exits 0. Agent **must not** re-run the wizard or share a new link — proceed to Phase 7 deploy ask.
|
|
58
60
|
|
|
59
61
|
**After save:** `POST /api/save-config` also deletes `out/previews/` before CLI exits (preview garbage collection).
|
|
60
62
|
|
|
@@ -276,7 +278,7 @@ npx cfm-sdk fetch-ui-config
|
|
|
276
278
|
| Mistake | Fix |
|
|
277
279
|
|---------|-----|
|
|
278
280
|
| Killed CLI during poll | Re-run `customize-ui`; client re-submits |
|
|
279
|
-
| Client quit wizard | CLI exits
|
|
281
|
+
| Client quit wizard | CLI exits 0; GC done (`out/previews/` removed). **Do not** re-run wizard — skip Phase 6c, ask deploy (INPUT 4) |
|
|
280
282
|
| Applied from `survey-ui-config.json` while still seed | Use `.final.json` or run `confirm-ui-config` after apply |
|
|
281
283
|
| Only changed primary color, ignored diff | Read **entire** `diff.md` — clients often change 20+ paths |
|
|
282
284
|
| Skipped `confirm-ui-config` | Active JSON stays seed; next wizard re-open is wrong |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Wizard Post-Customize Build — All Types, All Formats
|
|
2
2
|
|
|
3
3
|
> **Phase 6c runbook.** Read after the client clicks **Review & Build** and the CLI prints `✅ UI configuration saved`.
|
|
4
|
-
> Companion docs: [`wizard-config-handoff.md`](wizard-config-handoff.md) · [`
|
|
4
|
+
> Companion docs: [`wizard-config-handoff.md`](wizard-config-handoff.md) · [`apply-ui-config.md`](apply-ui-config.md) · [`wizard-question-type-styling.md`](wizard-question-type-styling.md) · [`wizard-chrome-contract.md`](wizard-chrome-contract.md)
|
|
5
5
|
|
|
6
6
|
This doc answers two agent questions:
|
|
7
7
|
|
|
@@ -87,7 +87,7 @@ test -z "$LOGO" || test -f "public/$LOGO" || echo "FAIL: logo file missing in pu
|
|
|
87
87
|
|
|
88
88
|
The wizard **Question Types** step exposes **10 type families** (TEXT_AND_MEDIA uses theme vars only). Apply `questionTypes.{TYPE}` for **every** type block in saved JSON — even if the client's survey does not include that type. This keeps `survey-theme.css` complete and future-proof.
|
|
89
89
|
|
|
90
|
-
Use the format matrix below. Detailed token tables: [`wizard-
|
|
90
|
+
Use the format matrix below. Detailed token tables: [`wizard-question-type-styling.md`](wizard-question-type-styling.md) and [`apply-ui-config.md`](apply-ui-config.md).
|
|
91
91
|
|
|
92
92
|
### F. Hook options
|
|
93
93
|
|
|
@@ -121,6 +121,12 @@ Only after H passes → ask deploy (INPUT 4). See [`aws-deploy.md`](aws-deploy.m
|
|
|
121
121
|
|
|
122
122
|
**Do not re-run** `npx cfm-sdk export-previews` after wizard save — previews were deleted on purpose (GC). Export-previews is for **component structure changes** during Phase 5, not for applying saved config.
|
|
123
123
|
|
|
124
|
+
### If client quit without saving
|
|
125
|
+
|
|
126
|
+
CLI exits 0 with GC message. **Do not** re-run wizard or share a new link. Skip this runbook (no Phase 6c). Ask deploy (INPUT 4) with the Phase 6 build.
|
|
127
|
+
|
|
128
|
+
**Wizard link:** parse `WIZARD_LINK=…` from CLI — never hardcode `localhost:4200`.
|
|
129
|
+
|
|
124
130
|
---
|
|
125
131
|
|
|
126
132
|
## Wizard steps → live preview keys
|
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
> **Read during Phase 5 (BUILD) — mandatory.** Wizard **run** (Phase 6b) is optional; this contract is required for every agent build.
|
|
4
4
|
> **Authoritative hook list + copy commands:** [`wizard-chrome-contract.md`](wizard-chrome-contract.md) — fixes logo, layout toggles, question card, and per-type preview issues.
|
|
5
|
-
> **Complete control catalog (all steps, types, formats):** [`wizard-customization-catalog.md`](wizard-customization-catalog.md).
|
|
6
5
|
> Pipeline: [`agent-execution-flow.md`](agent-execution-flow.md).
|
|
7
6
|
|
|
8
7
|
Related docs:
|
|
9
8
|
|
|
10
|
-
- [`wizard-customization-catalog.md`](wizard-customization-catalog.md) — **every wizard control** → config → CSS var → component
|
|
11
9
|
- [`wizard-chrome-contract.md`](wizard-chrome-contract.md) — **copy-first templates** + symptom → fix table
|
|
12
10
|
|
|
13
11
|
- [`ui-customization-wizard.md`](ui-customization-wizard.md) — wizard flow and CLI
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Wizard Question-Type Styling
|
|
2
2
|
|
|
3
|
-
> **Read during Phase 5 (BUILD) — mandatory.** Alongside [`wizard-
|
|
3
|
+
> **Read during Phase 5 (BUILD) — mandatory.** Alongside [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) and [`apply-ui-config.md`](apply-ui-config.md).
|
|
4
4
|
> **After client saves wizard:** [`wizard-post-customize-build.md`](wizard-post-customize-build.md) — full format matrix + Phase 6c runbook.
|
|
5
5
|
> Pipeline: [`agent-execution-flow.md`](agent-execution-flow.md).
|
|
6
6
|
|
|
@@ -102,11 +102,12 @@ Single-select radio: outer border = focus ring, annulus = cell selected, inner d
|
|
|
102
102
|
| Wizard section | Config path | CSS vars | Apply in |
|
|
103
103
|
|----------------|-------------|----------|----------|
|
|
104
104
|
| Button style | `questionTypes.NPS_SCALE.buttonStyle` | `data-cfm-nps-button-style` | `RatingScale.tsx` |
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
| Number labels | `numberLabelMode`, per-index colors | `--cfm-number-label-mode`, `--cfm-number-mono-color`, `--cfm-number-color-{n}` | Badge row above radios |
|
|
105
|
+
| Hint labels | `hintMinText`, `hintMaxText` + hint colors | `--cfm-hint-label-*` | `hintLabelStyle()` on min/max anchor row |
|
|
106
|
+
| Number labels | `numberLabelMode`, per-index colors | `--cfm-number-label-mode`, `--cfm-number-mono-color`, `--cfm-number-color-{n}` | Badge row; `resolveNumberLabelColor()` for unselected |
|
|
108
107
|
| Selection | Theme → Question card | `--cfm-matrix-selected`, `--cfm-input-focus-ring` | `scaleRadioRingStyle` + `scaleRadioDotStyle` |
|
|
109
108
|
|
|
109
|
+
**Removed from wizard UI:** NPS selected fill, track bar, track highlight — use Theme → Question card tokens only.
|
|
110
|
+
|
|
110
111
|
### CFM_MATRIX (`CFM_matrix`, `CFM_dropdown`, `CFM_transpose`, `CFM_likert`, `CFM_bipolar`)
|
|
111
112
|
|
|
112
113
|
| Wizard section | CSS vars | Apply in |
|
|
@@ -193,9 +194,9 @@ For production builds after wizard: syncing `survey-theme.css` is sufficient —
|
|
|
193
194
|
|
|
194
195
|
**Wizard customization** exposes all **11** question types regardless of fetched survey inventory. Wire styling for every scale template you copy — not only types in the client's mockup image.
|
|
195
196
|
|
|
196
|
-
**Format-level matrix** (every preview tab → component): [`wizard-
|
|
197
|
+
**Format-level matrix** (every preview tab → component): [`wizard-post-customize-build.md`](wizard-post-customize-build.md) § Question type format matrix.
|
|
197
198
|
|
|
198
|
-
For each type in **wizard export** (all
|
|
199
|
+
For each type in **wizard export** (all 11):
|
|
199
200
|
|
|
200
201
|
- [ ] Scale component uses `var(--cfm-*)` for every wizard-exposed token (no hardcoded brand hex)
|
|
201
202
|
- [ ] Radio/scale selections use `selectionStyles.ts` pattern (focus ring + cell selected)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| | `customize` **(default)** | `customize-ui` **(fallback)** |
|
|
10
10
|
|--|---------------------------|--------------------------------|
|
|
11
11
|
| **When** | Client on same machine as agent | Client on different device |
|
|
12
|
-
| **URL** | `
|
|
12
|
+
| **URL** | `WIZARD_LINK` from CLI (dynamic port) | `WIZARD_LINK` from CLI (EC2) |
|
|
13
13
|
| **Previews** | `out/previews/` on agent disk | EC2 disk |
|
|
14
14
|
| **Save** | `survey-ui-config.json` immediately | Poll → `.final.json` + `.diff.md` |
|
|
15
15
|
| **GC** | CLI stops → no orphans | Session cleanup on EC2 |
|
|
@@ -34,7 +34,7 @@ Footer: **"Mock preview — run customize or customize-ui…"**
|
|
|
34
34
|
|
|
35
35
|
### Symptom
|
|
36
36
|
|
|
37
|
-
Client cannot open `
|
|
37
|
+
Client cannot open the `WIZARD_LINK` URL (wrong port or wrong machine)
|
|
38
38
|
|
|
39
39
|
### Fix
|
|
40
40
|
|
|
@@ -46,7 +46,7 @@ export CFM_DEPLOY_API_BASE="http://43.204.26.213:3000"
|
|
|
46
46
|
npx cfm-sdk customize-ui
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Send **only the
|
|
49
|
+
Send **only** the `WIZARD_LINK=…` line from CLI output.
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
@@ -72,9 +72,25 @@ Wire `data-cfm-*` hooks and `--cfm-*` CSS vars per [`wizard-preview-build-guide.
|
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
75
|
+
## Client quit without saving
|
|
76
|
+
|
|
77
|
+
### Symptom
|
|
78
|
+
|
|
79
|
+
CLI prints quit message and exits 0; `out/previews/` removed.
|
|
80
|
+
|
|
81
|
+
### Agent action (mandatory)
|
|
82
|
+
|
|
83
|
+
- **Do not** re-run `customize` or `customize-ui`
|
|
84
|
+
- **Do not** share a new wizard link
|
|
85
|
+
- Skip Phase 6c (no `survey-ui-config.json` update)
|
|
86
|
+
- Ask deploy confirmation (Phase 7 / INPUT 4) with the existing Phase 6 build
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
75
90
|
## Checklist
|
|
76
91
|
|
|
77
92
|
- [ ] Used **`customize`** as default when client said yes
|
|
78
|
-
- [ ]
|
|
93
|
+
- [ ] Parsed **`WIZARD_LINK`** from CLI and shared only that URL
|
|
94
|
+
- [ ] Waited for CLI exit 0 (save or quit)
|
|
79
95
|
- [ ] Phase 5 preview bridge + `data-cfm-*` wired
|
|
80
96
|
- [ ] EC2 only if client on remote device
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
> **Shared lib utilities** (NPS colors, custom fields, heatmap coords) live in [`templates/`](../templates/) — see [`client-lib-folder.md`](../00-integration/client-lib-folder.md), not in this ui-specs folder.
|
|
5
5
|
> Read the spec for **each question type present** in the fetched survey before writing React code.
|
|
6
6
|
|
|
7
|
-
> **Wizard customization:** When the client may use the web wizard, hardcoded hex in § Design Tokens below are **defaults only**. Wire `var(--cfm-*)` per [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md) — especially `--cfm-matrix-selected` (cell annulus) and `--cfm-input-focus-ring` (outer border + inner dot).
|
|
7
|
+
> **Wizard customization:** When the client may use the web wizard, hardcoded hex in § Design Tokens below are **defaults only**. Wire `var(--cfm-*)` per [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md) — especially `--cfm-matrix-selected` (cell annulus) and `--cfm-input-focus-ring` (outer border + inner dot). Portable scale templates in `docs/templates/` are layout/logic baselines; extend with `selectionStyles.ts` + `labelStyles.ts` for live preview.
|
|
8
8
|
|
|
9
9
|
## How to Read a UI Spec
|
|
10
10
|
|
|
@@ -368,10 +368,8 @@
|
|
|
368
368
|
"00-integration/agent-execution-flow.md",
|
|
369
369
|
"00-integration/constraints.md",
|
|
370
370
|
"00-integration/component-checklist.md",
|
|
371
|
-
"00-integration/wizard-customization-catalog.md",
|
|
372
371
|
"00-integration/wizard-preview-build-guide.md",
|
|
373
372
|
"00-integration/wizard-question-type-styling.md",
|
|
374
|
-
"00-integration/wizard-chrome-contract.md",
|
|
375
373
|
"02-reference/routing-table.md"
|
|
376
374
|
],
|
|
377
375
|
"onDemand": "02-reference/question-types"
|
|
@@ -428,7 +426,6 @@
|
|
|
428
426
|
"read": [
|
|
429
427
|
"00-integration/wizard-config-handoff.md",
|
|
430
428
|
"00-integration/ui-customization-wizard.md",
|
|
431
|
-
"00-integration/wizard-customization-catalog.md",
|
|
432
429
|
"00-integration/wizard-preview-build-guide.md",
|
|
433
430
|
"00-integration/wizard-question-type-styling.md",
|
|
434
431
|
"00-integration/wizard-api.md",
|
package/templates/docs/index.md
CHANGED
|
@@ -29,7 +29,6 @@ templates/docs/
|
|
|
29
29
|
├── 00-integration/
|
|
30
30
|
│ ├── agent-operating-contract.md ← Anti-hallucination — read FIRST
|
|
31
31
|
│ ├── agent-execution-flow.md ← Pipeline, client questions, wizard-ready build
|
|
32
|
-
│ ├── wizard-customization-catalog.md ← All wizard controls × types × formats
|
|
33
32
|
│ ├── wizard-preview-build-guide.md
|
|
34
33
|
│ ├── wizard-post-customize-build.md ← Phase 6c — all formats + build gate
|
|
35
34
|
│ ├── wizard-question-type-styling.md
|
|
@@ -79,7 +78,7 @@ Full step-by-step: [`agent-execution-flow.md`](00-integration/agent-execution-fl
|
|
|
79
78
|
| **2** Instance ID | Get JWT | `useSurveySDK.md` — **ask client** |
|
|
80
79
|
| **3** SDK contract | Hook + logic | `client-integration-guide.md`, `question-type-sdk-matrix.md`, logic docs |
|
|
81
80
|
| **4** Plan | Inventory + copy dispatcher | `component-checklist.md`, per-type MANIFEST docs, `implementation_plan.md` |
|
|
82
|
-
| **5** Build | Wizard-ready UI | `wizard-
|
|
81
|
+
| **5** Build | Wizard-ready UI | `wizard-chrome-contract.md`, `wizard-preview-build-guide.md`, `wizard-question-type-styling.md`, `03-ui-specs/*` |
|
|
83
82
|
| **6** Verify | Build + dev server | `verify-agent-build.sh` — **ask wizard** |
|
|
84
83
|
| **6b** Wizard | Optional customize | **`npx cfm-sdk customize`** (default) — EC2 `customize-ui` only if client on another device |
|
|
85
84
|
| **6c** Apply | Sync final config | `wizard-post-customize-build.md`, `apply-ui-config.md` |
|
|
@@ -132,7 +131,7 @@ Run: `bash templates/verify-agent-build.sh`
|
|
|
132
131
|
Only after Phase 6 passes and client says **yes**:
|
|
133
132
|
|
|
134
133
|
```bash
|
|
135
|
-
npx cfm-sdk customize # DEFAULT —
|
|
134
|
+
npx cfm-sdk customize # DEFAULT — prints WIZARD_LINK, WAITS until client saves or quits
|
|
136
135
|
```
|
|
137
136
|
|
|
138
137
|
**Phase 6b:** Client on same machine → `customize` writes `survey-ui-config.json` directly. Remote client → `customize-ui` + EC2 link (see `wizard-troubleshooting.md`). Apply per `apply-ui-config.md`.
|
|
@@ -154,7 +153,7 @@ Re-run verify script + build.
|
|
|
154
153
|
| **Always first** | `agent-operating-contract.md`, `agent-execution-flow.md`, `mockup-ui-extraction.md`, `index.md`, `client-integration-guide.md`, `constraints.md` |
|
|
155
154
|
| Phase 3 contract | `question-type-sdk-matrix.md`, `useSurveySDK.md`, logic/navigation docs |
|
|
156
155
|
| Phase 4 plan | `mockup-ui-extraction.md`, `placeholders-and-custom-fields-wiring.md`, `component-checklist.md`, per-type `03-ui-specs/*` |
|
|
157
|
-
| Wizard build | `wizard-
|
|
156
|
+
| Wizard build | `wizard-preview-build-guide.md`, `wizard-question-type-styling.md` |
|
|
158
157
|
| Wizard run | `wizard-config-handoff.md`, `ui-customization-wizard.md`, `wizard-api.md`, `wizard-troubleshooting.md` |
|
|
159
158
|
| After wizard (apply all paths) | `wizard-post-customize-build.md`, `apply-ui-config.md` |
|
|
160
159
|
| Deploy | `aws-deploy.md` (§ Logos & local assets), `setup.md#nextjs-aws-static-export` |
|
|
@@ -172,5 +171,4 @@ Re-run verify script + build.
|
|
|
172
171
|
| Hint labels | `--cfm-hint-label-*` | NPS min/max anchors |
|
|
173
172
|
| Number labels | `--cfm-number-*` | Traffic-light badges (unselected only) |
|
|
174
173
|
|
|
175
|
-
Full per-type table: [`wizard-question-type-styling.md`](00-integration/wizard-question-type-styling.md).
|
|
176
|
-
Complete wizard control index (all formats): [`wizard-customization-catalog.md`](00-integration/wizard-customization-catalog.md).
|
|
174
|
+
Full per-type table: [`wizard-question-type-styling.md`](00-integration/wizard-question-type-styling.md).
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import type { CsatQuestion, RatingMatrixQuestion, MatrixRowAnswers, ScaleColumn } from '@explorer02/cfm-survey-sdk';
|
|
3
|
+
import { csatColumnLabelStyle } from '@/lib/surveyUi/labelStyles';
|
|
4
|
+
import {
|
|
5
|
+
cellSelectedVar,
|
|
6
|
+
focusRingVar,
|
|
7
|
+
matrixRadioDotStyle,
|
|
8
|
+
matrixRadioRingStyle,
|
|
9
|
+
scaleCellSelectedStyle,
|
|
10
|
+
emojiSizeStyle,
|
|
11
|
+
unselectedOpacityStyle,
|
|
12
|
+
} from '@/lib/surveyUi/selectionStyles';
|
|
3
13
|
import { getEmojiForIndex, CsatStarIcons } from './surveyUiIcons';
|
|
4
14
|
import { columnSubmitValue, nonNaScaleColumns } from './surveyUiScaleUtils';
|
|
5
15
|
import { CustomSliderTrack } from './CustomSliderTrack';
|
|
@@ -26,10 +36,10 @@ function GraphicsColumnLabelsRow({ scaleColumns }: { scaleColumns: ScaleColumn[]
|
|
|
26
36
|
textAlign: 'center',
|
|
27
37
|
fontSize: '12px',
|
|
28
38
|
fontWeight: 500,
|
|
29
|
-
color: '#4b5563',
|
|
30
39
|
lineHeight: 1.2,
|
|
31
40
|
wordBreak: 'break-word',
|
|
32
41
|
width: '64px',
|
|
42
|
+
...csatColumnLabelStyle(),
|
|
33
43
|
}}
|
|
34
44
|
>
|
|
35
45
|
<span dangerouslySetInnerHTML={{ __html: col.label }} />
|
|
@@ -94,10 +104,10 @@ function CsatMatrixVerticalList({
|
|
|
94
104
|
onClick={() => onCellSelect(row.id, submitValue)}
|
|
95
105
|
style={{
|
|
96
106
|
width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'flex-start',
|
|
97
|
-
borderRadius: '8px', border: `1px solid ${isSelected ?
|
|
107
|
+
borderRadius: '8px', border: `1px solid ${isSelected ? focusRingVar : '#d1d5db'}`,
|
|
98
108
|
padding: '14px 18px', cursor: 'pointer', transition: 'all 0.15s',
|
|
99
|
-
backgroundColor: isSelected ? '
|
|
100
|
-
color: isSelected ?
|
|
109
|
+
backgroundColor: isSelected ? 'var(--cfm-mcq-selected-bg)' : '#fff',
|
|
110
|
+
color: isSelected ? focusRingVar : '#374151',
|
|
101
111
|
fontWeight: isSelected ? 600 : 400, fontSize: '15px', textAlign: 'left',
|
|
102
112
|
}}
|
|
103
113
|
>
|
|
@@ -116,9 +126,9 @@ function CsatMatrixVerticalList({
|
|
|
116
126
|
key={col.id}
|
|
117
127
|
style={{
|
|
118
128
|
display: 'flex', alignItems: 'center', gap: '16px',
|
|
119
|
-
borderRadius: '8px', border: `1px solid ${isSelected ?
|
|
129
|
+
borderRadius: '8px', border: `1px solid ${isSelected ? focusRingVar : '#e5e5e5'}`,
|
|
120
130
|
padding: '16px 20px', cursor: 'pointer', transition: 'all 0.15s',
|
|
121
|
-
backgroundColor: isSelected ? '
|
|
131
|
+
backgroundColor: isSelected ? 'var(--cfm-mcq-selected-bg)' : '#fff',
|
|
122
132
|
}}
|
|
123
133
|
>
|
|
124
134
|
<input type="radio" name={`${question.id}-${row.id}`}
|
|
@@ -126,13 +136,8 @@ function CsatMatrixVerticalList({
|
|
|
126
136
|
onChange={() => onCellSelect(row.id, submitValue)}
|
|
127
137
|
style={{ position: 'absolute', width: 0, height: 0, opacity: 0 }}
|
|
128
138
|
/>
|
|
129
|
-
<div style={
|
|
130
|
-
|
|
131
|
-
alignItems: 'center', justifyContent: 'center', borderRadius: '50%',
|
|
132
|
-
border: isSelected ? '2px solid #e20074' : '1.5px solid #9ca3af',
|
|
133
|
-
backgroundColor: '#fff',
|
|
134
|
-
}}>
|
|
135
|
-
{isSelected && <div style={{ width: '10px', height: '10px', borderRadius: '50%', backgroundColor: '#e20074' }} />}
|
|
139
|
+
<div style={matrixRadioRingStyle(isSelected, '20px')}>
|
|
140
|
+
{isSelected && <div style={matrixRadioDotStyle(isSelected, '10px')} />}
|
|
136
141
|
</div>
|
|
137
142
|
<span style={{ fontSize: '15px', fontWeight: 500, color: '#111827' }}
|
|
138
143
|
dangerouslySetInnerHTML={{ __html: col.label }} />
|
|
@@ -146,7 +151,7 @@ function CsatMatrixVerticalList({
|
|
|
146
151
|
<label style={{ display: 'flex', alignItems: 'center', gap: '12px', cursor: 'pointer', userSelect: 'none' }}>
|
|
147
152
|
<input type="checkbox" checked={selectedValue[row.id] === null}
|
|
148
153
|
onChange={() => onCellSelect(row.id, selectedValue[row.id] === null ? undefined : null)}
|
|
149
|
-
style={{ width: '16px', height: '16px', accentColor:
|
|
154
|
+
style={{ width: '16px', height: '16px', accentColor: focusRingVar, cursor: 'pointer' }} />
|
|
150
155
|
<span style={{ fontSize: '14px', fontWeight: 500, color: '#4b5563' }}>Not Applicable</span>
|
|
151
156
|
</label>
|
|
152
157
|
)}
|
|
@@ -190,7 +195,7 @@ function CsatMatrixGrid({
|
|
|
190
195
|
{/* Labels row */}
|
|
191
196
|
{showLabels && m > 0 && (
|
|
192
197
|
<div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: '12px' }}>
|
|
193
|
-
<div style={{ width: '180px', flexShrink: 0 }} />
|
|
198
|
+
<div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0 }} />
|
|
194
199
|
<div style={{ flex: 1, display: 'flex' }}>
|
|
195
200
|
<div style={{ flex: 1, position: 'relative', height: '24px', ...(isGraphics ? { margin: '0 40px' } : {}) }}>
|
|
196
201
|
{labels!.map((label, i) => {
|
|
@@ -204,7 +209,8 @@ function CsatMatrixGrid({
|
|
|
204
209
|
left: `${percent}%`,
|
|
205
210
|
transform: 'translateX(-50%)',
|
|
206
211
|
textAlign: 'center', fontSize: '13px', fontWeight: 600,
|
|
207
|
-
|
|
212
|
+
lineHeight: 1.3, whiteSpace: 'nowrap',
|
|
213
|
+
...csatColumnLabelStyle(),
|
|
208
214
|
}}>
|
|
209
215
|
{label}
|
|
210
216
|
</div>
|
|
@@ -219,7 +225,7 @@ function CsatMatrixGrid({
|
|
|
219
225
|
{/* Column tick labels for graphics sliders (shown with or without anchor labels) */}
|
|
220
226
|
{isGraphics && m > 0 && (
|
|
221
227
|
<div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: showLabels ? '8px' : '16px', minHeight: '24px' }}>
|
|
222
|
-
<div style={{ width: '180px', flexShrink: 0 }} />
|
|
228
|
+
<div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0 }} />
|
|
223
229
|
<div style={{ flex: 1, display: 'flex' }}>
|
|
224
230
|
<GraphicsColumnLabelsRow scaleColumns={scaleColumns} />
|
|
225
231
|
{hasNotApplicable && (
|
|
@@ -232,13 +238,13 @@ function CsatMatrixGrid({
|
|
|
232
238
|
{/* Column Headers for non-graphics */}
|
|
233
239
|
{!isGraphics && !showLabels && m > 0 && (
|
|
234
240
|
<div style={{ display: 'flex', alignItems: 'flex-end', marginBottom: '16px', minHeight: '24px' }}>
|
|
235
|
-
<div style={{ width: '180px', flexShrink: 0 }} />
|
|
241
|
+
<div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0 }} />
|
|
236
242
|
<div style={{ flex: 1, display: 'flex' }}>
|
|
237
243
|
<div style={{ flex: 1, display: 'grid', gridTemplateColumns: `repeat(${scaleColumns.length}, 1fr)` }}>
|
|
238
244
|
{scaleColumns.map(col => (
|
|
239
245
|
<div key={col.id} style={{ display: 'flex', justifyContent: 'center', padding: '0 4px' }}>
|
|
240
246
|
<span
|
|
241
|
-
style={{ fontSize: '13px', fontWeight: 500,
|
|
247
|
+
style={{ fontSize: '13px', fontWeight: 500, textAlign: 'center', lineHeight: 1.2, ...csatColumnLabelStyle() }}
|
|
242
248
|
dangerouslySetInnerHTML={{ __html: col.label }}
|
|
243
249
|
/>
|
|
244
250
|
</div>
|
|
@@ -258,9 +264,9 @@ function CsatMatrixGrid({
|
|
|
258
264
|
{statementRows.map((row, rowIdx) => (
|
|
259
265
|
<div key={row.id} style={{
|
|
260
266
|
display: 'flex', alignItems: 'center', borderRadius: '8px',
|
|
261
|
-
padding: '12px 0', backgroundColor: rowIdx % 2 === 0 ? '
|
|
267
|
+
padding: '12px 0', backgroundColor: rowIdx % 2 === 0 ? 'var(--cfm-zebra-row)' : '#fff',
|
|
262
268
|
}}>
|
|
263
|
-
<div style={{ width: '180px', flexShrink: 0, padding: '0 16px' }}>
|
|
269
|
+
<div style={{ width: 'var(--cfm-matrix-row-width, 180px)', flexShrink: 0, padding: '0 16px' }}>
|
|
264
270
|
<span style={{ fontSize: '14px', fontWeight: 500, color: '#111827', lineHeight: 1.4 }}
|
|
265
271
|
dangerouslySetInnerHTML={{ __html: row.statementText }} />
|
|
266
272
|
</div>
|
|
@@ -298,13 +304,8 @@ function CsatMatrixGrid({
|
|
|
298
304
|
const StarNode = isStar ? (isSelected ? (CsatStarIcons.filled as any) : (CsatStarIcons.empty as any)) : null;
|
|
299
305
|
|
|
300
306
|
const RadioNode = (
|
|
301
|
-
<div style={
|
|
302
|
-
|
|
303
|
-
alignItems: 'center', justifyContent: 'center', borderRadius: '50%',
|
|
304
|
-
margin: '0 auto', border: isSelected ? '2px solid #e20074' : '2px solid #d1d5db',
|
|
305
|
-
backgroundColor: '#fff',
|
|
306
|
-
}}>
|
|
307
|
-
{isSelected && <div style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: '#e20074' }} />}
|
|
307
|
+
<div style={matrixRadioRingStyle(isSelected)}>
|
|
308
|
+
{isSelected && <div style={matrixRadioDotStyle(isSelected)} />}
|
|
308
309
|
</div>
|
|
309
310
|
);
|
|
310
311
|
|
|
@@ -324,23 +325,26 @@ function CsatMatrixGrid({
|
|
|
324
325
|
cursor: 'pointer', borderRadius: '6px',
|
|
325
326
|
...(isEmoji ? {
|
|
326
327
|
padding: '6px',
|
|
328
|
+
...emojiSizeStyle(),
|
|
327
329
|
transform: isSelected ? 'scale(1.1)' : isHovered ? 'scale(1.25)' : 'scale(1)',
|
|
328
330
|
...(isSelected ? {
|
|
329
|
-
backgroundColor: '
|
|
330
|
-
boxShadow:
|
|
331
|
+
backgroundColor: 'var(--cfm-mcq-selected-bg)',
|
|
332
|
+
boxShadow: `0 0 0 2px ${focusRingVar}`,
|
|
331
333
|
borderRadius: '50%',
|
|
332
334
|
} : {}),
|
|
333
335
|
} : isStar ? {
|
|
334
336
|
padding: '4px',
|
|
335
337
|
transform: isHovered ? 'scale(1.1)' : 'scale(1)',
|
|
336
|
-
|
|
338
|
+
...(isSelected ? {} : unselectedOpacityStyle()),
|
|
339
|
+
opacity: isSelected ? 1 : isHovered ? 1 : undefined,
|
|
337
340
|
} : isNumbered ? {
|
|
338
|
-
width: '40px', height: '40px', borderRadius: '8px',
|
|
341
|
+
width: 'var(--cfm-csat-cell-size, 40px)', height: 'var(--cfm-csat-cell-size, 40px)', borderRadius: '8px',
|
|
339
342
|
fontSize: '14px', fontWeight: 500,
|
|
340
|
-
border: `1px solid ${isSelected ?
|
|
341
|
-
backgroundColor: isSelected ?
|
|
343
|
+
border: `1px solid ${isSelected ? focusRingVar : '#d1d5db'}`,
|
|
344
|
+
backgroundColor: isSelected ? cellSelectedVar : '#fff',
|
|
342
345
|
color: isSelected ? '#fff' : '#111827',
|
|
343
346
|
transition: 'all 0.15s',
|
|
347
|
+
...(isSelected ? scaleCellSelectedStyle(true) : {}),
|
|
344
348
|
} : { padding: '8px' }),
|
|
345
349
|
}}
|
|
346
350
|
>
|
|
@@ -378,7 +382,7 @@ function CsatMatrixGrid({
|
|
|
378
382
|
<label style={{ cursor: 'pointer' }}>
|
|
379
383
|
<input type="checkbox" checked={selectedValue[row.id] === null}
|
|
380
384
|
onChange={() => onCellSelect(row.id, selectedValue[row.id] === null ? undefined : null)}
|
|
381
|
-
style={{ width: '16px', height: '16px', accentColor:
|
|
385
|
+
style={{ width: '16px', height: '16px', accentColor: focusRingVar, cursor: 'pointer' }} />
|
|
382
386
|
</label>
|
|
383
387
|
</div>
|
|
384
388
|
)}
|
|
@@ -437,7 +441,8 @@ function CsatMatrixCarousel({
|
|
|
437
441
|
<div key={i} style={{
|
|
438
442
|
position: 'absolute', left: `${percent}%`, transform: 'translateX(-50%)',
|
|
439
443
|
textAlign: 'center', fontSize: '13px', fontWeight: 600,
|
|
440
|
-
|
|
444
|
+
lineHeight: 1.3, whiteSpace: 'nowrap',
|
|
445
|
+
...csatColumnLabelStyle(),
|
|
441
446
|
}}>
|
|
442
447
|
{label}
|
|
443
448
|
</div>
|
|
@@ -481,13 +486,8 @@ function CsatMatrixCarousel({
|
|
|
481
486
|
const EmojiNode = isEmoji ? getEmojiForIndex(displayIdx, scaleColumns.length) : null;
|
|
482
487
|
const StarNode = isStar ? (isSelected ? (CsatStarIcons.filled as any) : (CsatStarIcons.empty as any)) : null;
|
|
483
488
|
const RadioNode = (
|
|
484
|
-
<div style={
|
|
485
|
-
|
|
486
|
-
alignItems: 'center', justifyContent: 'center', borderRadius: '50%',
|
|
487
|
-
margin: '0 auto', border: isSelected ? '2px solid #e20074' : '2px solid #d1d5db',
|
|
488
|
-
backgroundColor: '#fff',
|
|
489
|
-
}}>
|
|
490
|
-
{isSelected && <div style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: '#e20074' }} />}
|
|
489
|
+
<div style={matrixRadioRingStyle(isSelected)}>
|
|
490
|
+
{isSelected && <div style={matrixRadioDotStyle(isSelected)} />}
|
|
491
491
|
</div>
|
|
492
492
|
);
|
|
493
493
|
|
|
@@ -503,19 +503,23 @@ function CsatMatrixCarousel({
|
|
|
503
503
|
transition: 'all 0.15s', border: 'none', backgroundColor: 'transparent',
|
|
504
504
|
cursor: 'pointer', borderRadius: '6px', pointerEvents: 'none',
|
|
505
505
|
...(isEmoji ? {
|
|
506
|
-
padding: '6px',
|
|
507
|
-
|
|
506
|
+
padding: '6px', ...emojiSizeStyle(),
|
|
507
|
+
transform: isSelected ? 'scale(1.1)' : isHovered ? 'scale(1.25)' : 'scale(1)',
|
|
508
|
+
...(isSelected ? { backgroundColor: 'var(--cfm-mcq-selected-bg)', boxShadow: `0 0 0 2px ${focusRingVar}`, borderRadius: '50%' } : {}),
|
|
508
509
|
} : isStar ? {
|
|
509
|
-
padding: '4px', transform: isHovered ? 'scale(1.1)' : 'scale(1)',
|
|
510
|
+
padding: '4px', transform: isHovered ? 'scale(1.1)' : 'scale(1)',
|
|
511
|
+
...(isSelected ? {} : unselectedOpacityStyle()),
|
|
512
|
+
opacity: isSelected ? 1 : isHovered ? 1 : undefined,
|
|
510
513
|
} : isNumbered ? {
|
|
511
|
-
width: '40px', height: '40px', borderRadius: '8px', fontSize: '14px', fontWeight: 500,
|
|
512
|
-
border: `1px solid ${isSelected ?
|
|
514
|
+
width: 'var(--cfm-csat-cell-size, 40px)', height: 'var(--cfm-csat-cell-size, 40px)', borderRadius: '8px', fontSize: '14px', fontWeight: 500,
|
|
515
|
+
border: `1px solid ${isSelected ? focusRingVar : '#d1d5db'}`, backgroundColor: isSelected ? cellSelectedVar : '#fff', color: isSelected ? '#fff' : '#111827',
|
|
516
|
+
...(isSelected ? scaleCellSelectedStyle(true) : {}),
|
|
513
517
|
} : { padding: '8px' }),
|
|
514
518
|
}}
|
|
515
519
|
>
|
|
516
520
|
{(EmojiNode ?? StarNode ?? (isNumbered ? (displayIdx + 1) : RadioNode)) as React.ReactNode}
|
|
517
521
|
</button>
|
|
518
|
-
<span style={{ fontSize: '13px', fontWeight: 500,
|
|
522
|
+
<span style={{ fontSize: '13px', fontWeight: 500, textAlign: 'center', lineHeight: 1.2, ...csatColumnLabelStyle() }}
|
|
519
523
|
dangerouslySetInnerHTML={{ __html: col.label }} />
|
|
520
524
|
</div>
|
|
521
525
|
);
|
|
@@ -528,7 +532,7 @@ function CsatMatrixCarousel({
|
|
|
528
532
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
|
529
533
|
<input type="checkbox" checked={selectedValue[row.id] === null}
|
|
530
534
|
onChange={() => onCellSelect(row.id, selectedValue[row.id] === null ? undefined : null)}
|
|
531
|
-
style={{ width: '16px', height: '16px', accentColor:
|
|
535
|
+
style={{ width: '16px', height: '16px', accentColor: focusRingVar, cursor: 'pointer' }} />
|
|
532
536
|
<span style={{ fontSize: '14px', fontWeight: 500, color: '#4b5563' }}>N/A</span>
|
|
533
537
|
</label>
|
|
534
538
|
</div>
|
|
@@ -550,7 +554,7 @@ function CsatMatrixCarousel({
|
|
|
550
554
|
<button key={i} type="button" onClick={() => setActiveCarouselIndex(i)}
|
|
551
555
|
style={{
|
|
552
556
|
width: i === activeCarouselIndex ? '20px' : '8px', height: '8px', borderRadius: '4px',
|
|
553
|
-
backgroundColor: i === activeCarouselIndex ?
|
|
557
|
+
backgroundColor: i === activeCarouselIndex ? cellSelectedVar : '#d1d5db', border: 'none', padding: 0,
|
|
554
558
|
cursor: 'pointer', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
|
|
555
559
|
}} />
|
|
556
560
|
))}
|
|
@@ -590,7 +594,6 @@ export function CsatMatrixScale({ question, selectedValue = {}, onSelect }: Csat
|
|
|
590
594
|
const isStar = displayStyle === 'star';
|
|
591
595
|
const isNumbered = displayStyle === 'numbered';
|
|
592
596
|
const isGraphics = displayStyle === 'graphics';
|
|
593
|
-
const isStandardRadio = displayStyle === 'standard';
|
|
594
597
|
|
|
595
598
|
const scaleColumns = nonNaScaleColumns(question.scaleColumns);
|
|
596
599
|
|
|
@@ -609,7 +612,7 @@ export function CsatMatrixScale({ question, selectedValue = {}, onSelect }: Csat
|
|
|
609
612
|
);
|
|
610
613
|
}
|
|
611
614
|
|
|
612
|
-
if (isDropdown || isSelectBox
|
|
615
|
+
if (isDropdown || isSelectBox) {
|
|
613
616
|
return (
|
|
614
617
|
<CsatMatrixVerticalList
|
|
615
618
|
question={question}
|