@syntrologie/adapt-faq 0.0.0-semantically-released → 2.0.1
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/FAQWidget.d.ts +22 -1
- package/dist/FAQWidget.d.ts.map +1 -1
- package/dist/FAQWidget.js +182 -49
- package/dist/cdn.d.ts +20 -2
- package/dist/cdn.d.ts.map +1 -1
- package/dist/cdn.js +7 -6
- package/dist/editor.d.ts +12 -2
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +427 -162
- package/dist/executors.d.ts +41 -0
- package/dist/executors.d.ts.map +1 -0
- package/dist/executors.js +151 -0
- package/dist/runtime.d.ts +16 -8
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +10 -10
- package/dist/schema.d.ts +2188 -143
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +121 -2
- package/dist/state.d.ts +28 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +132 -0
- package/dist/summarize.d.ts +14 -0
- package/dist/summarize.d.ts.map +1 -0
- package/dist/summarize.js +62 -0
- package/dist/types.d.ts +109 -56
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -1
- package/package.json +13 -5
package/dist/editor.js
CHANGED
|
@@ -1,6 +1,147 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Adaptive FAQ - Editor Component
|
|
4
|
+
*
|
|
5
|
+
* Review & tweak editor for AI-generated FAQ question decisions.
|
|
6
|
+
* Displays a scannable list of Q&A cards with trigger, rationale,
|
|
7
|
+
* and inline editing. Includes detection badges and hover-to-highlight.
|
|
8
|
+
*/
|
|
9
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
10
|
+
import { summarizeFAQItem, describeTrigger } from './summarize';
|
|
11
|
+
import { isOwnAction, } from './types';
|
|
12
|
+
function isRuleStrategy(s) {
|
|
13
|
+
return (typeof s === 'object' &&
|
|
14
|
+
s !== null &&
|
|
15
|
+
s.type === 'rules' &&
|
|
16
|
+
Array.isArray(s.rules));
|
|
17
|
+
}
|
|
18
|
+
function extractTargetingInfo(showWhen) {
|
|
19
|
+
if (!showWhen || !isRuleStrategy(showWhen)) {
|
|
20
|
+
return { pagePatterns: [], anchorSelectors: [], hasTargeting: false };
|
|
21
|
+
}
|
|
22
|
+
const pagePatterns = new Set();
|
|
23
|
+
const anchorSelectors = new Set();
|
|
24
|
+
for (const rule of showWhen.rules) {
|
|
25
|
+
for (const cond of rule.conditions) {
|
|
26
|
+
const c = cond;
|
|
27
|
+
if (c.type === 'page_url' && typeof c.url === 'string') {
|
|
28
|
+
pagePatterns.add(c.url);
|
|
29
|
+
}
|
|
30
|
+
else if (c.type === 'anchor_visible' && typeof c.anchorId === 'string') {
|
|
31
|
+
anchorSelectors.add(c.anchorId);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const hasTargeting = pagePatterns.size > 0 || anchorSelectors.size > 0;
|
|
36
|
+
return {
|
|
37
|
+
pagePatterns: [...pagePatterns],
|
|
38
|
+
anchorSelectors: [...anchorSelectors],
|
|
39
|
+
hasTargeting,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function extractFirstPage(showWhen) {
|
|
43
|
+
const info = extractTargetingInfo(showWhen);
|
|
44
|
+
return info.pagePatterns[0] || null;
|
|
45
|
+
}
|
|
46
|
+
function extractFirstAnchor(showWhen) {
|
|
47
|
+
const info = extractTargetingInfo(showWhen);
|
|
48
|
+
return info.anchorSelectors[0] || null;
|
|
49
|
+
}
|
|
2
50
|
// ============================================================================
|
|
3
|
-
//
|
|
51
|
+
// Helpers
|
|
52
|
+
// ============================================================================
|
|
53
|
+
function getAnswerText(answer) {
|
|
54
|
+
if (typeof answer === 'string')
|
|
55
|
+
return answer;
|
|
56
|
+
if (answer.type === 'rich')
|
|
57
|
+
return answer.html;
|
|
58
|
+
return answer.content;
|
|
59
|
+
}
|
|
60
|
+
function flattenItems(config) {
|
|
61
|
+
const actions = (config.actions || []).filter(isOwnAction);
|
|
62
|
+
return actions.map((q, i) => ({
|
|
63
|
+
key: String(i),
|
|
64
|
+
index: i,
|
|
65
|
+
summary: summarizeFAQItem(q),
|
|
66
|
+
trigger: describeTrigger(q.showWhen),
|
|
67
|
+
rationale: q.rationale,
|
|
68
|
+
firstAnchor: extractFirstAnchor(q.showWhen),
|
|
69
|
+
question: q,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
function filterConfig(config, dismissedKeys) {
|
|
73
|
+
const ownActions = (config.actions || []).filter(isOwnAction);
|
|
74
|
+
return {
|
|
75
|
+
...config,
|
|
76
|
+
actions: ownActions.filter((_, i) => !dismissedKeys.has(String(i))),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function useDetection(items, getCurrentRoute) {
|
|
80
|
+
const [detectionMap, setDetectionMap] = useState(new Map());
|
|
81
|
+
const itemsRef = useRef(items);
|
|
82
|
+
itemsRef.current = items;
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
const runDetection = () => {
|
|
85
|
+
const map = new Map();
|
|
86
|
+
const currentPath = getCurrentRoute();
|
|
87
|
+
for (const item of itemsRef.current) {
|
|
88
|
+
const targeting = extractTargetingInfo(item.question.showWhen);
|
|
89
|
+
// Check page match
|
|
90
|
+
let pageMatch = true;
|
|
91
|
+
if (targeting.pagePatterns.length > 0) {
|
|
92
|
+
pageMatch = targeting.pagePatterns.some((pattern) => {
|
|
93
|
+
const regex = new RegExp(`^${pattern.replace(/\*\*/g, '.*').replace(/(?<!\.)(\*)/g, '[^/]*')}$`);
|
|
94
|
+
return regex.test(currentPath);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Check anchor presence
|
|
98
|
+
let anchorFound = false;
|
|
99
|
+
let element = null;
|
|
100
|
+
if (item.firstAnchor) {
|
|
101
|
+
try {
|
|
102
|
+
element = document.querySelector(item.firstAnchor);
|
|
103
|
+
anchorFound = element !== null;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Invalid selector
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// No anchor to check — if page matches, consider found
|
|
111
|
+
anchorFound = pageMatch;
|
|
112
|
+
}
|
|
113
|
+
map.set(item.key, {
|
|
114
|
+
found: pageMatch && anchorFound,
|
|
115
|
+
element,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
setDetectionMap(map);
|
|
119
|
+
};
|
|
120
|
+
runDetection();
|
|
121
|
+
const interval = setInterval(runDetection, 2000);
|
|
122
|
+
window.addEventListener('popstate', runDetection);
|
|
123
|
+
return () => {
|
|
124
|
+
clearInterval(interval);
|
|
125
|
+
window.removeEventListener('popstate', runDetection);
|
|
126
|
+
};
|
|
127
|
+
}, [getCurrentRoute]);
|
|
128
|
+
return detectionMap;
|
|
129
|
+
}
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Detection Badge
|
|
132
|
+
// ============================================================================
|
|
133
|
+
function DetectionBadge({ found }) {
|
|
134
|
+
return (_jsx("span", { style: {
|
|
135
|
+
width: '8px',
|
|
136
|
+
height: '8px',
|
|
137
|
+
borderRadius: '50%',
|
|
138
|
+
backgroundColor: found ? '#22c55e' : '#475569',
|
|
139
|
+
flexShrink: 0,
|
|
140
|
+
display: 'inline-block',
|
|
141
|
+
}, title: found ? 'Found on this page' : 'Not found on this page' }));
|
|
142
|
+
}
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// Styles
|
|
4
145
|
// ============================================================================
|
|
5
146
|
const styles = {
|
|
6
147
|
container: {
|
|
@@ -36,121 +177,101 @@ const styles = {
|
|
|
36
177
|
fontSize: '11px',
|
|
37
178
|
color: '#64748b',
|
|
38
179
|
},
|
|
39
|
-
|
|
180
|
+
body: {
|
|
40
181
|
flex: 1,
|
|
41
182
|
overflow: 'auto',
|
|
42
183
|
padding: '16px',
|
|
43
184
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fontSize: '12px',
|
|
49
|
-
fontWeight: 600,
|
|
50
|
-
color: '#94a3b8',
|
|
185
|
+
groupHeader: {
|
|
186
|
+
fontSize: '11px',
|
|
187
|
+
fontWeight: 700,
|
|
188
|
+
color: '#64748b',
|
|
51
189
|
textTransform: 'uppercase',
|
|
52
|
-
letterSpacing: '0.
|
|
53
|
-
|
|
190
|
+
letterSpacing: '0.06em',
|
|
191
|
+
padding: '4px 0 8px 0',
|
|
192
|
+
display: 'flex',
|
|
193
|
+
alignItems: 'center',
|
|
194
|
+
justifyContent: 'space-between',
|
|
54
195
|
},
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
196
|
+
groupCount: {
|
|
197
|
+
fontSize: '10px',
|
|
198
|
+
color: '#475569',
|
|
199
|
+
backgroundColor: 'rgba(255,255,255,0.06)',
|
|
200
|
+
padding: '2px 6px',
|
|
201
|
+
borderRadius: '8px',
|
|
202
|
+
},
|
|
203
|
+
card: {
|
|
204
|
+
padding: '8px 10px',
|
|
58
205
|
borderRadius: '6px',
|
|
59
|
-
border: '1px solid rgba(255,255,255,0.
|
|
60
|
-
|
|
61
|
-
|
|
206
|
+
border: '1px solid rgba(255,255,255,0.06)',
|
|
207
|
+
background: 'rgba(255,255,255,0.02)',
|
|
208
|
+
marginBottom: '4px',
|
|
62
209
|
fontSize: '13px',
|
|
63
|
-
|
|
210
|
+
color: '#e2e8f0',
|
|
64
211
|
},
|
|
65
|
-
|
|
212
|
+
triggerLine: {
|
|
66
213
|
display: 'flex',
|
|
67
214
|
alignItems: 'center',
|
|
68
|
-
gap: '
|
|
69
|
-
fontSize: '
|
|
70
|
-
color: '#
|
|
71
|
-
marginTop: '8px',
|
|
72
|
-
},
|
|
73
|
-
addButton: {
|
|
74
|
-
width: '100%',
|
|
75
|
-
padding: '12px',
|
|
76
|
-
borderRadius: '8px',
|
|
77
|
-
border: '1px dashed rgba(139, 92, 246, 0.3)',
|
|
78
|
-
background: 'rgba(139, 92, 246, 0.05)',
|
|
79
|
-
color: '#8b5cf6',
|
|
80
|
-
fontSize: '13px',
|
|
81
|
-
fontWeight: 600,
|
|
215
|
+
gap: '4px',
|
|
216
|
+
fontSize: '11px',
|
|
217
|
+
color: '#94a3b8',
|
|
82
218
|
cursor: 'pointer',
|
|
83
|
-
marginBottom: '
|
|
84
|
-
},
|
|
85
|
-
questionCard: {
|
|
86
|
-
padding: '12px',
|
|
87
|
-
borderRadius: '8px',
|
|
88
|
-
border: '1px solid rgba(255,255,255,0.08)',
|
|
89
|
-
background: 'rgba(255,255,255,0.02)',
|
|
90
|
-
marginBottom: '8px',
|
|
219
|
+
marginBottom: '4px',
|
|
91
220
|
},
|
|
92
|
-
|
|
221
|
+
cardBody: {
|
|
93
222
|
display: 'flex',
|
|
94
|
-
alignItems: '
|
|
95
|
-
|
|
96
|
-
|
|
223
|
+
alignItems: 'center',
|
|
224
|
+
gap: '8px',
|
|
225
|
+
cursor: 'pointer',
|
|
97
226
|
},
|
|
98
|
-
|
|
227
|
+
cardText: {
|
|
99
228
|
flex: 1,
|
|
100
|
-
},
|
|
101
|
-
questionText: {
|
|
102
|
-
fontSize: '14px',
|
|
103
|
-
fontWeight: 500,
|
|
104
|
-
color: '#f8fafc',
|
|
105
|
-
marginBottom: '4px',
|
|
106
|
-
},
|
|
107
|
-
answerPreview: {
|
|
108
|
-
fontSize: '12px',
|
|
109
|
-
color: '#64748b',
|
|
110
229
|
overflow: 'hidden',
|
|
111
230
|
textOverflow: 'ellipsis',
|
|
112
231
|
whiteSpace: 'nowrap',
|
|
113
|
-
maxWidth: '200px',
|
|
114
232
|
},
|
|
115
|
-
|
|
116
|
-
|
|
233
|
+
rationaleLine: {
|
|
234
|
+
fontSize: '10px',
|
|
235
|
+
color: '#64748b',
|
|
236
|
+
marginTop: '4px',
|
|
237
|
+
},
|
|
238
|
+
dismissButton: {
|
|
239
|
+
padding: '2px 6px',
|
|
117
240
|
borderRadius: '4px',
|
|
118
241
|
border: 'none',
|
|
119
|
-
background: '
|
|
120
|
-
color: '#
|
|
121
|
-
fontSize: '
|
|
242
|
+
background: 'transparent',
|
|
243
|
+
color: '#64748b',
|
|
244
|
+
fontSize: '14px',
|
|
122
245
|
cursor: 'pointer',
|
|
123
246
|
flexShrink: 0,
|
|
247
|
+
lineHeight: 1,
|
|
124
248
|
},
|
|
125
|
-
|
|
126
|
-
|
|
249
|
+
dismissedSection: {
|
|
250
|
+
marginTop: '16px',
|
|
251
|
+
cursor: 'pointer',
|
|
252
|
+
userSelect: 'none',
|
|
127
253
|
},
|
|
128
|
-
|
|
129
|
-
display: 'block',
|
|
254
|
+
dismissedHeader: {
|
|
130
255
|
fontSize: '11px',
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
padding: '8px 10px',
|
|
137
|
-
borderRadius: '4px',
|
|
138
|
-
border: '1px solid rgba(255,255,255,0.1)',
|
|
139
|
-
backgroundColor: 'rgba(255,255,255,0.05)',
|
|
140
|
-
color: '#f8fafc',
|
|
141
|
-
fontSize: '13px',
|
|
256
|
+
fontWeight: 600,
|
|
257
|
+
color: '#475569',
|
|
258
|
+
display: 'flex',
|
|
259
|
+
alignItems: 'center',
|
|
260
|
+
gap: '6px',
|
|
142
261
|
},
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
262
|
+
dismissedCard: {
|
|
263
|
+
display: 'flex',
|
|
264
|
+
alignItems: 'center',
|
|
265
|
+
gap: '8px',
|
|
266
|
+
padding: '6px 10px',
|
|
267
|
+
borderRadius: '6px',
|
|
268
|
+
border: '1px solid rgba(255,255,255,0.03)',
|
|
269
|
+
background: 'transparent',
|
|
270
|
+
marginBottom: '3px',
|
|
271
|
+
cursor: 'pointer',
|
|
272
|
+
fontSize: '12px',
|
|
273
|
+
color: '#475569',
|
|
274
|
+
opacity: 0.6,
|
|
154
275
|
},
|
|
155
276
|
emptyState: {
|
|
156
277
|
textAlign: 'center',
|
|
@@ -186,95 +307,239 @@ const styles = {
|
|
|
186
307
|
fontWeight: 600,
|
|
187
308
|
cursor: 'pointer',
|
|
188
309
|
},
|
|
310
|
+
// Edit form styles
|
|
311
|
+
editForm: {
|
|
312
|
+
padding: '4px 0',
|
|
313
|
+
},
|
|
314
|
+
editHeader: {
|
|
315
|
+
display: 'flex',
|
|
316
|
+
alignItems: 'center',
|
|
317
|
+
gap: '8px',
|
|
318
|
+
marginBottom: '12px',
|
|
319
|
+
fontSize: '13px',
|
|
320
|
+
fontWeight: 600,
|
|
321
|
+
color: '#e2e8f0',
|
|
322
|
+
},
|
|
323
|
+
editLabel: {
|
|
324
|
+
fontSize: '11px',
|
|
325
|
+
fontWeight: 600,
|
|
326
|
+
color: '#64748b',
|
|
327
|
+
marginBottom: '4px',
|
|
328
|
+
display: 'block',
|
|
329
|
+
},
|
|
330
|
+
editInput: {
|
|
331
|
+
width: '100%',
|
|
332
|
+
padding: '6px 8px',
|
|
333
|
+
borderRadius: '4px',
|
|
334
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
335
|
+
background: 'rgba(255,255,255,0.04)',
|
|
336
|
+
color: '#e2e8f0',
|
|
337
|
+
fontSize: '12px',
|
|
338
|
+
fontFamily: 'inherit',
|
|
339
|
+
marginBottom: '8px',
|
|
340
|
+
boxSizing: 'border-box',
|
|
341
|
+
},
|
|
342
|
+
editTextarea: {
|
|
343
|
+
width: '100%',
|
|
344
|
+
padding: '6px 8px',
|
|
345
|
+
borderRadius: '4px',
|
|
346
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
347
|
+
background: 'rgba(255,255,255,0.04)',
|
|
348
|
+
color: '#e2e8f0',
|
|
349
|
+
fontSize: '12px',
|
|
350
|
+
fontFamily: 'inherit',
|
|
351
|
+
marginBottom: '8px',
|
|
352
|
+
resize: 'vertical',
|
|
353
|
+
minHeight: '60px',
|
|
354
|
+
boxSizing: 'border-box',
|
|
355
|
+
},
|
|
356
|
+
editRationale: {
|
|
357
|
+
padding: '8px',
|
|
358
|
+
borderRadius: '4px',
|
|
359
|
+
border: '1px dashed rgba(255,255,255,0.15)',
|
|
360
|
+
background: 'rgba(255,255,255,0.02)',
|
|
361
|
+
color: '#94a3b8',
|
|
362
|
+
fontSize: '12px',
|
|
363
|
+
marginBottom: '8px',
|
|
364
|
+
},
|
|
365
|
+
editBackButton: {
|
|
366
|
+
padding: '6px 12px',
|
|
367
|
+
borderRadius: '6px',
|
|
368
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
369
|
+
background: 'transparent',
|
|
370
|
+
color: '#94a3b8',
|
|
371
|
+
fontSize: '12px',
|
|
372
|
+
cursor: 'pointer',
|
|
373
|
+
marginTop: '8px',
|
|
374
|
+
},
|
|
189
375
|
};
|
|
190
376
|
// ============================================================================
|
|
191
377
|
// FAQEditor Component
|
|
192
378
|
// ============================================================================
|
|
193
379
|
export function FAQEditor({ config, onChange, editor }) {
|
|
194
380
|
const typedConfig = config;
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
...config,
|
|
221
|
-
actions: newQuestions,
|
|
222
|
-
});
|
|
223
|
-
editor.setDirty(true);
|
|
224
|
-
};
|
|
225
|
-
// Update a question
|
|
226
|
-
const handleUpdateQuestion = (index, updates) => {
|
|
227
|
-
const newQuestions = questions.map((q, i) => {
|
|
228
|
-
if (i !== index)
|
|
229
|
-
return q;
|
|
230
|
-
return {
|
|
231
|
-
...q,
|
|
232
|
-
config: {
|
|
233
|
-
...q.config,
|
|
234
|
-
...updates,
|
|
235
|
-
},
|
|
236
|
-
};
|
|
381
|
+
const [dismissedKeys, setDismissedKeys] = useState(new Set());
|
|
382
|
+
const [dismissedOpen, setDismissedOpen] = useState(false);
|
|
383
|
+
const [editingKey, setEditingKey] = useState(null);
|
|
384
|
+
const [previewMode, setPreviewMode] = useState('after');
|
|
385
|
+
const [_hoveredKey, setHoveredKey] = useState(null);
|
|
386
|
+
// Consume initial navigation payload on mount (FAQ is a widget mount —
|
|
387
|
+
// initialEditKey points to the mount action, no sub-item to pre-select)
|
|
388
|
+
const initialConsumed = useRef(false);
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
if (!initialConsumed.current && (editor.initialEditKey != null || editor.initialCreate)) {
|
|
391
|
+
initialConsumed.current = true;
|
|
392
|
+
editor.clearInitialState?.();
|
|
393
|
+
}
|
|
394
|
+
}, [editor]);
|
|
395
|
+
const allItems = flattenItems(typedConfig);
|
|
396
|
+
const activeItems = allItems.filter((item) => !dismissedKeys.has(item.key));
|
|
397
|
+
const dismissedItems = allItems.filter((item) => dismissedKeys.has(item.key));
|
|
398
|
+
const totalQuestions = activeItems.length;
|
|
399
|
+
const detectionMap = useDetection(allItems, editor.getCurrentRoute);
|
|
400
|
+
const foundCount = activeItems.filter((item) => detectionMap.get(item.key)?.found).length;
|
|
401
|
+
const handleDismiss = useCallback((key) => {
|
|
402
|
+
setDismissedKeys((prev) => {
|
|
403
|
+
const next = new Set(prev);
|
|
404
|
+
next.add(key);
|
|
405
|
+
return next;
|
|
237
406
|
});
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
407
|
+
if (editingKey === key)
|
|
408
|
+
setEditingKey(null);
|
|
409
|
+
}, [editingKey]);
|
|
410
|
+
const handleRestore = useCallback((key) => {
|
|
411
|
+
setDismissedKeys((prev) => {
|
|
412
|
+
const next = new Set(prev);
|
|
413
|
+
next.delete(key);
|
|
414
|
+
return next;
|
|
241
415
|
});
|
|
416
|
+
}, []);
|
|
417
|
+
const handleCardBodyClick = useCallback((item) => {
|
|
418
|
+
setEditingKey(item.key);
|
|
419
|
+
}, []);
|
|
420
|
+
const handleTriggerClick = useCallback((item) => {
|
|
421
|
+
const pageUrl = extractFirstPage(item.question.showWhen);
|
|
422
|
+
if (pageUrl) {
|
|
423
|
+
editor.navigateTo(pageUrl);
|
|
424
|
+
}
|
|
425
|
+
if (item.firstAnchor) {
|
|
426
|
+
editor.highlightElement(item.firstAnchor);
|
|
427
|
+
}
|
|
428
|
+
}, [editor]);
|
|
429
|
+
const handleBackToList = useCallback(() => {
|
|
430
|
+
setEditingKey(null);
|
|
431
|
+
setPreviewMode('after');
|
|
432
|
+
editor.previewConfig(config);
|
|
433
|
+
editor.clearHighlight();
|
|
434
|
+
}, [editor, config]);
|
|
435
|
+
const handleBeforeAfter = useCallback((mode) => {
|
|
436
|
+
setPreviewMode(mode);
|
|
437
|
+
if (mode === 'before') {
|
|
438
|
+
const filtered = filterConfig(typedConfig, new Set([editingKey]));
|
|
439
|
+
editor.previewConfig(filtered);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
editor.previewConfig(config);
|
|
443
|
+
}
|
|
444
|
+
}, [typedConfig, editingKey, editor, config]);
|
|
445
|
+
const handleFieldChange = useCallback((index, field, value) => {
|
|
446
|
+
const ownActions = (typedConfig.actions || []).filter(isOwnAction).slice();
|
|
447
|
+
const q = { ...ownActions[index], config: { ...ownActions[index].config } };
|
|
448
|
+
q.config[field] = value;
|
|
449
|
+
ownActions[index] = q;
|
|
450
|
+
const otherActions = (typedConfig.actions || []).filter((a) => !isOwnAction(a));
|
|
451
|
+
const updated = { ...typedConfig, actions: [...otherActions, ...ownActions] };
|
|
452
|
+
onChange(updated);
|
|
242
453
|
editor.setDirty(true);
|
|
454
|
+
}, [typedConfig, onChange, editor]);
|
|
455
|
+
const handlePublish = useCallback(() => {
|
|
456
|
+
if (dismissedKeys.size > 0) {
|
|
457
|
+
const filtered = filterConfig(typedConfig, dismissedKeys);
|
|
458
|
+
onChange(filtered);
|
|
459
|
+
}
|
|
460
|
+
editor.publish();
|
|
461
|
+
}, [dismissedKeys, typedConfig, onChange, editor]);
|
|
462
|
+
const handleCardHover = useCallback((item) => {
|
|
463
|
+
setHoveredKey(item.key);
|
|
464
|
+
if (item.firstAnchor) {
|
|
465
|
+
editor.highlightElement(item.firstAnchor);
|
|
466
|
+
}
|
|
467
|
+
}, [editor]);
|
|
468
|
+
const handleCardLeave = useCallback(() => {
|
|
469
|
+
setHoveredKey(null);
|
|
470
|
+
editor.clearHighlight();
|
|
471
|
+
}, [editor]);
|
|
472
|
+
// ---- Edit form renderer ----
|
|
473
|
+
const renderEditFields = (index) => {
|
|
474
|
+
const actions = (typedConfig.actions || []).filter(isOwnAction);
|
|
475
|
+
const q = actions[index];
|
|
476
|
+
if (!q)
|
|
477
|
+
return null;
|
|
478
|
+
const item = allItems.find((it) => it.key === String(index));
|
|
479
|
+
return (_jsxs("div", { style: styles.editForm, children: [item && item.trigger !== 'All pages' && (_jsxs("div", { "data-trigger": true, style: styles.triggerLine, onClick: () => handleTriggerClick(item), children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsx("label", { style: styles.editLabel, children: "Question" }), _jsx("input", { style: styles.editInput, value: q.config.question, onChange: (e) => handleFieldChange(index, 'question', e.target.value) }), _jsx("label", { style: styles.editLabel, children: "Answer" }), _jsx("textarea", { style: styles.editTextarea, value: getAnswerText(q.config.answer), onChange: (e) => handleFieldChange(index, 'answer', e.target.value) }), _jsx("label", { style: styles.editLabel, children: "Category" }), _jsx("input", { style: styles.editInput, value: q.config.category || '', onChange: (e) => handleFieldChange(index, 'category', e.target.value || undefined), placeholder: "e.g., Billing, Account" }), _jsx("label", { style: styles.editLabel, children: "AI Rationale" }), _jsx("div", { style: styles.editRationale, children: q.rationale ? q.rationale.why : 'N/A' })] }));
|
|
243
480
|
};
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
481
|
+
return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("button", { onClick: () => editor.navigateHome(), style: styles.backButton, children: "\u2190 Back" }), _jsxs("div", { children: [_jsx("h2", { style: styles.title, children: "Review Questions" }), _jsxs("p", { style: styles.subtitle, children: [totalQuestions, " question", totalQuestions !== 1 ? 's' : '', totalQuestions > 0 && ` (${foundCount} found on this page)`] })] })] }), _jsx("div", { style: styles.body, children: editingKey !== null ? (
|
|
482
|
+
/* ---- Edit mode ---- */
|
|
483
|
+
(() => {
|
|
484
|
+
const editIndex = Number(editingKey);
|
|
485
|
+
const editItem = allItems.find((it) => it.key === editingKey);
|
|
486
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.editHeader, children: [_jsx("span", { children: '\u2753' }), _jsx("span", { children: editItem?.summary })] }), _jsxs("div", { style: {
|
|
487
|
+
display: 'flex',
|
|
488
|
+
gap: '0',
|
|
489
|
+
marginBottom: '12px',
|
|
490
|
+
borderRadius: '6px',
|
|
491
|
+
overflow: 'hidden',
|
|
492
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
493
|
+
}, children: [_jsx("button", { onClick: () => handleBeforeAfter('before'), style: {
|
|
494
|
+
flex: 1,
|
|
495
|
+
padding: '6px 12px',
|
|
496
|
+
border: 'none',
|
|
497
|
+
fontSize: '12px',
|
|
498
|
+
fontWeight: 600,
|
|
499
|
+
cursor: 'pointer',
|
|
500
|
+
background: previewMode === 'before' ? 'rgba(59,130,246,0.2)' : 'transparent',
|
|
501
|
+
color: previewMode === 'before' ? '#3b82f6' : '#64748b',
|
|
502
|
+
}, children: "Before" }), _jsx("button", { onClick: () => handleBeforeAfter('after'), style: {
|
|
503
|
+
flex: 1,
|
|
504
|
+
padding: '6px 12px',
|
|
505
|
+
border: 'none',
|
|
506
|
+
borderLeft: '1px solid rgba(255,255,255,0.1)',
|
|
507
|
+
fontSize: '12px',
|
|
508
|
+
fontWeight: 600,
|
|
509
|
+
cursor: 'pointer',
|
|
510
|
+
background: previewMode === 'after' ? 'rgba(59,130,246,0.2)' : 'transparent',
|
|
511
|
+
color: previewMode === 'after' ? '#3b82f6' : '#64748b',
|
|
512
|
+
}, children: "After" })] }), renderEditFields(editIndex), _jsx("button", { style: styles.editBackButton, onClick: handleBackToList, children: "\u2190 List" })] }));
|
|
513
|
+
})()) : (
|
|
514
|
+
/* ---- List mode ---- */
|
|
515
|
+
_jsxs(_Fragment, { children: [allItems.length === 0 && (_jsx("div", { style: styles.emptyState, children: "No FAQ questions configured." })), activeItems.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.groupHeader, children: [_jsx("span", { children: "FAQ" }), _jsx("span", { style: styles.groupCount, children: activeItems.length })] }), activeItems.map((item) => {
|
|
516
|
+
const detection = detectionMap.get(item.key);
|
|
517
|
+
return (_jsxs("div", { style: styles.card, "data-item-key": item.key, onMouseEnter: () => handleCardHover(item), onMouseLeave: handleCardLeave, children: [item.trigger !== 'All pages' && (_jsxs("div", { "data-trigger": true, style: styles.triggerLine, onClick: (e) => {
|
|
518
|
+
e.stopPropagation();
|
|
519
|
+
handleTriggerClick(item);
|
|
520
|
+
}, children: [_jsx("span", { children: '\u{1f4cd}' }), _jsx("span", { children: item.trigger })] })), _jsxs("div", { "data-card-body": true, style: styles.cardBody, onClick: () => handleCardBodyClick(item), children: [_jsx(DetectionBadge, { found: detection?.found ?? false }), _jsx("span", { style: styles.cardText, children: item.summary }), _jsx("button", { style: styles.dismissButton, onClick: (e) => {
|
|
521
|
+
e.stopPropagation();
|
|
522
|
+
handleDismiss(item.key);
|
|
523
|
+
}, title: "Dismiss this question", children: "\u00D7" })] }), _jsxs("div", { style: styles.rationaleLine, children: ["WHY: ", item.rationale ? item.rationale.why : 'N/A'] })] }, item.key));
|
|
524
|
+
})] })), dismissedItems.length > 0 && (_jsxs("div", { style: styles.dismissedSection, children: [_jsxs("div", { style: { ...styles.dismissedHeader, cursor: 'pointer' }, onClick: () => setDismissedOpen(!dismissedOpen), children: [_jsx("span", { children: dismissedOpen ? '\u25be' : '\u25b8' }), _jsxs("span", { children: ["Dismissed (", dismissedItems.length, ")"] })] }), dismissedOpen && (_jsx("div", { style: { marginTop: '6px' }, children: dismissedItems.map((item) => (_jsxs("div", { style: styles.dismissedCard, children: [_jsx("span", { style: { ...styles.cardText, textDecoration: 'line-through' }, children: item.summary }), _jsx("button", { style: {
|
|
525
|
+
...styles.dismissButton,
|
|
526
|
+
color: '#3b82f6',
|
|
527
|
+
fontSize: '11px',
|
|
528
|
+
}, onClick: (e) => {
|
|
529
|
+
e.stopPropagation();
|
|
530
|
+
handleRestore(item.key);
|
|
531
|
+
}, children: "Restore" })] }, item.key))) }))] }))] })) }), _jsxs("div", { style: styles.footer, children: [_jsx("button", { onClick: () => editor.save(), style: styles.saveButton, children: "Save Draft" }), _jsx("button", { onClick: handlePublish, style: styles.publishButton, children: "Publish" })] })] }));
|
|
271
532
|
}
|
|
272
533
|
/**
|
|
273
534
|
* Editor panel configuration for the app registry.
|
|
274
535
|
*/
|
|
275
536
|
export const editorPanel = {
|
|
276
537
|
title: 'FAQ',
|
|
277
|
-
icon: '
|
|
538
|
+
icon: '\u2753',
|
|
278
539
|
description: 'FAQ accordion with per-item visibility',
|
|
279
540
|
};
|
|
541
|
+
export const editor = {
|
|
542
|
+
panel: editorPanel,
|
|
543
|
+
component: FAQEditor,
|
|
544
|
+
};
|
|
280
545
|
export default FAQEditor;
|