@dataimago/interview 0.2.0-alpha.0 → 0.2.0-alpha.2
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.cjs +513 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -74
- package/dist/index.d.ts +152 -74
- package/dist/index.js +509 -117
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -40,6 +40,28 @@ function ProgressBar({ current, total, estimatedMinutesRemaining }) {
|
|
|
40
40
|
// src/QuestionCard.tsx
|
|
41
41
|
import { useState, useEffect } from "react";
|
|
42
42
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
43
|
+
function defaultValueFor(question) {
|
|
44
|
+
switch (question.inputType) {
|
|
45
|
+
case "multi-select":
|
|
46
|
+
case "list":
|
|
47
|
+
return [];
|
|
48
|
+
case "composite":
|
|
49
|
+
return {};
|
|
50
|
+
case "repeater":
|
|
51
|
+
case "file-upload":
|
|
52
|
+
return [];
|
|
53
|
+
default:
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function canAdvanceWith(question, value) {
|
|
58
|
+
if (!question.required) return true;
|
|
59
|
+
if (value === void 0 || value === null) return false;
|
|
60
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
61
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
62
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
43
65
|
function QuestionCard({
|
|
44
66
|
question,
|
|
45
67
|
existingAnswer,
|
|
@@ -50,113 +72,28 @@ function QuestionCard({
|
|
|
50
72
|
isLast
|
|
51
73
|
}) {
|
|
52
74
|
const [localValue, setLocalValue] = useState(
|
|
53
|
-
existingAnswer ?? (question
|
|
75
|
+
existingAnswer ?? defaultValueFor(question)
|
|
54
76
|
);
|
|
55
77
|
useEffect(() => {
|
|
56
|
-
|
|
57
|
-
setLocalValue(current);
|
|
78
|
+
setLocalValue(existingAnswer ?? defaultValueFor(question));
|
|
58
79
|
}, [question.id, existingAnswer, question.inputType]);
|
|
59
80
|
const handleSubmit = (e) => {
|
|
60
81
|
e.preventDefault();
|
|
61
82
|
onAnswerSubmit(question.id, localValue);
|
|
62
83
|
onNext();
|
|
63
84
|
};
|
|
64
|
-
const canAdvance = question
|
|
85
|
+
const canAdvance = canAdvanceWith(question, localValue);
|
|
65
86
|
return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "animate-slide-up", children: [
|
|
66
87
|
/* @__PURE__ */ jsx2("h2", { className: "font-display text-3xl font-medium text-stone-900 sm:text-4xl", children: question.prompt }),
|
|
67
88
|
question.subprompt && /* @__PURE__ */ jsx2("p", { className: "mt-4 text-lg text-stone-700", children: question.subprompt }),
|
|
68
|
-
/* @__PURE__ */
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
className: "w-full rounded-lg border-2 border-stone-200 bg-stone-50 px-4 py-3 text-lg text-stone-900 focus:border-forest-700 focus:outline-none",
|
|
77
|
-
placeholder: "Type your answer\u2026"
|
|
78
|
-
}
|
|
79
|
-
),
|
|
80
|
-
question.inputType === "long-text" && /* @__PURE__ */ jsx2(
|
|
81
|
-
"textarea",
|
|
82
|
-
{
|
|
83
|
-
value: localValue,
|
|
84
|
-
onChange: (e) => setLocalValue(e.target.value),
|
|
85
|
-
autoFocus: true,
|
|
86
|
-
rows: 5,
|
|
87
|
-
className: "w-full rounded-lg border-2 border-stone-200 bg-stone-50 px-4 py-3 text-lg text-stone-900 focus:border-forest-700 focus:outline-none",
|
|
88
|
-
placeholder: "Type your answer\u2026"
|
|
89
|
-
}
|
|
90
|
-
),
|
|
91
|
-
question.inputType === "select" && question.options && /* @__PURE__ */ jsx2("div", { className: "space-y-3", role: "radiogroup", "aria-labelledby": `q-${question.id}`, children: question.options.map((opt) => /* @__PURE__ */ jsxs2(
|
|
92
|
-
"label",
|
|
93
|
-
{
|
|
94
|
-
className: `flex cursor-pointer items-start gap-3 rounded-lg border-2 p-4 transition-colors
|
|
95
|
-
${localValue === opt.value ? "border-forest-700 bg-forest-50" : "border-stone-200 bg-stone-50 hover:border-stone-400"}`,
|
|
96
|
-
children: [
|
|
97
|
-
/* @__PURE__ */ jsx2(
|
|
98
|
-
"input",
|
|
99
|
-
{
|
|
100
|
-
type: "radio",
|
|
101
|
-
name: question.id,
|
|
102
|
-
value: opt.value,
|
|
103
|
-
checked: localValue === opt.value,
|
|
104
|
-
onChange: (e) => setLocalValue(e.target.value),
|
|
105
|
-
className: "mt-1 h-4 w-4 text-forest-700"
|
|
106
|
-
}
|
|
107
|
-
),
|
|
108
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
109
|
-
/* @__PURE__ */ jsx2("div", { className: "font-medium text-stone-900", children: opt.label }),
|
|
110
|
-
opt.description && /* @__PURE__ */ jsx2("div", { className: "mt-1 text-sm text-stone-700", children: opt.description })
|
|
111
|
-
] })
|
|
112
|
-
]
|
|
113
|
-
},
|
|
114
|
-
opt.value
|
|
115
|
-
)) }),
|
|
116
|
-
question.inputType === "multi-select" && question.options && /* @__PURE__ */ jsx2("div", { className: "space-y-2", role: "group", "aria-labelledby": `q-${question.id}`, children: question.options.map((opt) => {
|
|
117
|
-
const values = localValue || [];
|
|
118
|
-
const checked = values.includes(opt.value);
|
|
119
|
-
return /* @__PURE__ */ jsxs2(
|
|
120
|
-
"label",
|
|
121
|
-
{
|
|
122
|
-
className: `flex cursor-pointer items-start gap-3 rounded-lg border-2 p-4 transition-colors
|
|
123
|
-
${checked ? "border-forest-700 bg-forest-50" : "border-stone-200 bg-stone-50 hover:border-stone-400"}`,
|
|
124
|
-
children: [
|
|
125
|
-
/* @__PURE__ */ jsx2(
|
|
126
|
-
"input",
|
|
127
|
-
{
|
|
128
|
-
type: "checkbox",
|
|
129
|
-
value: opt.value,
|
|
130
|
-
checked,
|
|
131
|
-
onChange: (e) => {
|
|
132
|
-
if (e.target.checked) {
|
|
133
|
-
setLocalValue([...values, opt.value]);
|
|
134
|
-
} else {
|
|
135
|
-
setLocalValue(values.filter((v) => v !== opt.value));
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
className: "mt-1 h-4 w-4 text-forest-700"
|
|
139
|
-
}
|
|
140
|
-
),
|
|
141
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
142
|
-
/* @__PURE__ */ jsx2("div", { className: "font-medium text-stone-900", children: opt.label }),
|
|
143
|
-
opt.description && /* @__PURE__ */ jsx2("div", { className: "mt-1 text-sm text-stone-700", children: opt.description })
|
|
144
|
-
] })
|
|
145
|
-
]
|
|
146
|
-
},
|
|
147
|
-
opt.value
|
|
148
|
-
);
|
|
149
|
-
}) }),
|
|
150
|
-
question.inputType === "list" && question.listRange && /* @__PURE__ */ jsx2(
|
|
151
|
-
ListInput,
|
|
152
|
-
{
|
|
153
|
-
values: localValue || [],
|
|
154
|
-
onChange: setLocalValue,
|
|
155
|
-
min: question.listRange.min,
|
|
156
|
-
max: question.listRange.max
|
|
157
|
-
}
|
|
158
|
-
)
|
|
159
|
-
] }),
|
|
89
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-8", children: /* @__PURE__ */ jsx2(
|
|
90
|
+
InputForType,
|
|
91
|
+
{
|
|
92
|
+
question,
|
|
93
|
+
value: localValue,
|
|
94
|
+
onChange: setLocalValue
|
|
95
|
+
}
|
|
96
|
+
) }),
|
|
160
97
|
question.examples && question.examples.length > 0 && /* @__PURE__ */ jsxs2("details", { className: "mt-8", children: [
|
|
161
98
|
/* @__PURE__ */ jsx2("summary", { className: "cursor-pointer text-sm font-medium text-forest-700 hover:text-forest-800", children: "Show examples from different fields" }),
|
|
162
99
|
/* @__PURE__ */ jsx2("ul", { className: "mt-4 space-y-3 border-l-2 border-copper-200 pl-5", children: question.examples.map((ex, i) => /* @__PURE__ */ jsxs2("li", { className: "text-sm italic text-stone-700", children: [
|
|
@@ -188,13 +125,174 @@ function QuestionCard({
|
|
|
188
125
|
] })
|
|
189
126
|
] });
|
|
190
127
|
}
|
|
128
|
+
function InputForType({ question, value, onChange }) {
|
|
129
|
+
switch (question.inputType) {
|
|
130
|
+
case "short-text":
|
|
131
|
+
return /* @__PURE__ */ jsx2(ShortTextInput, { value, onChange });
|
|
132
|
+
case "long-text":
|
|
133
|
+
return /* @__PURE__ */ jsx2(LongTextInput, { value, onChange });
|
|
134
|
+
case "single-select":
|
|
135
|
+
return /* @__PURE__ */ jsx2(SingleSelectInput, { question, value, onChange });
|
|
136
|
+
case "multi-select":
|
|
137
|
+
return /* @__PURE__ */ jsx2(MultiSelectInput, { question, value: value ?? [], onChange });
|
|
138
|
+
case "list":
|
|
139
|
+
return /* @__PURE__ */ jsx2(ListInput, { value: value ?? [], onChange, listRange: question.listRange });
|
|
140
|
+
case "scale":
|
|
141
|
+
return /* @__PURE__ */ jsx2(ScaleInput, { question, value, onChange });
|
|
142
|
+
case "date":
|
|
143
|
+
return /* @__PURE__ */ jsx2(DateInput, { value, onChange });
|
|
144
|
+
case "composite":
|
|
145
|
+
return /* @__PURE__ */ jsx2(CompositeInput, { question, value: value ?? {}, onChange });
|
|
146
|
+
case "repeater":
|
|
147
|
+
return /* @__PURE__ */ jsx2(RepeaterInput, { question, value: value ?? [], onChange });
|
|
148
|
+
case "file-upload":
|
|
149
|
+
return /* @__PURE__ */ jsx2(FileUploadInput, { question, value: value ?? [], onChange });
|
|
150
|
+
default: {
|
|
151
|
+
const _exhaustive = question.inputType;
|
|
152
|
+
return /* @__PURE__ */ jsx2(UnknownTypeFallback, { inputType: _exhaustive });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function ShortTextInput({ value, onChange }) {
|
|
157
|
+
return /* @__PURE__ */ jsx2(
|
|
158
|
+
"input",
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
value: value ?? "",
|
|
162
|
+
onChange: (e) => onChange(e.target.value),
|
|
163
|
+
autoFocus: true,
|
|
164
|
+
className: "w-full rounded-lg border-2 border-stone-200 bg-stone-50 px-4 py-3 text-lg text-stone-900 focus:border-forest-700 focus:outline-none",
|
|
165
|
+
placeholder: "Type your answer\u2026"
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
function LongTextInput({ value, onChange }) {
|
|
170
|
+
return /* @__PURE__ */ jsx2(
|
|
171
|
+
"textarea",
|
|
172
|
+
{
|
|
173
|
+
value: value ?? "",
|
|
174
|
+
onChange: (e) => onChange(e.target.value),
|
|
175
|
+
autoFocus: true,
|
|
176
|
+
rows: 5,
|
|
177
|
+
className: "w-full rounded-lg border-2 border-stone-200 bg-stone-50 px-4 py-3 text-lg text-stone-900 focus:border-forest-700 focus:outline-none",
|
|
178
|
+
placeholder: "Type your answer\u2026"
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
function SingleSelectInput({
|
|
183
|
+
question,
|
|
184
|
+
value,
|
|
185
|
+
onChange
|
|
186
|
+
}) {
|
|
187
|
+
return /* @__PURE__ */ jsx2("div", { className: "space-y-3", role: "radiogroup", "aria-labelledby": `q-${question.id}`, children: (question.options ?? []).map((opt) => /* @__PURE__ */ jsxs2(
|
|
188
|
+
"label",
|
|
189
|
+
{
|
|
190
|
+
className: `flex cursor-pointer items-start gap-3 rounded-lg border-2 p-4 transition-colors ${value === opt.value ? "border-forest-700 bg-forest-50" : "border-stone-200 bg-stone-50 hover:border-stone-400"}`,
|
|
191
|
+
children: [
|
|
192
|
+
/* @__PURE__ */ jsx2(
|
|
193
|
+
"input",
|
|
194
|
+
{
|
|
195
|
+
type: "radio",
|
|
196
|
+
name: question.id,
|
|
197
|
+
value: opt.value,
|
|
198
|
+
checked: value === opt.value,
|
|
199
|
+
onChange: (e) => onChange(e.target.value),
|
|
200
|
+
className: "mt-1 h-4 w-4 text-forest-700"
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
204
|
+
/* @__PURE__ */ jsx2("div", { className: "font-medium text-stone-900", children: opt.label }),
|
|
205
|
+
opt.description && /* @__PURE__ */ jsx2("div", { className: "mt-1 text-sm text-stone-700", children: opt.description })
|
|
206
|
+
] })
|
|
207
|
+
]
|
|
208
|
+
},
|
|
209
|
+
opt.value
|
|
210
|
+
)) });
|
|
211
|
+
}
|
|
212
|
+
function MultiSelectInput({
|
|
213
|
+
question,
|
|
214
|
+
value,
|
|
215
|
+
onChange
|
|
216
|
+
}) {
|
|
217
|
+
return /* @__PURE__ */ jsx2("div", { className: "space-y-2", role: "group", "aria-labelledby": `q-${question.id}`, children: (question.options ?? []).map((opt) => {
|
|
218
|
+
const checked = value.includes(opt.value);
|
|
219
|
+
return /* @__PURE__ */ jsxs2(
|
|
220
|
+
"label",
|
|
221
|
+
{
|
|
222
|
+
className: `flex cursor-pointer items-start gap-3 rounded-lg border-2 p-4 transition-colors ${checked ? "border-forest-700 bg-forest-50" : "border-stone-200 bg-stone-50 hover:border-stone-400"}`,
|
|
223
|
+
children: [
|
|
224
|
+
/* @__PURE__ */ jsx2(
|
|
225
|
+
"input",
|
|
226
|
+
{
|
|
227
|
+
type: "checkbox",
|
|
228
|
+
value: opt.value,
|
|
229
|
+
checked,
|
|
230
|
+
onChange: (e) => {
|
|
231
|
+
if (e.target.checked) onChange([...value, opt.value]);
|
|
232
|
+
else onChange(value.filter((v) => v !== opt.value));
|
|
233
|
+
},
|
|
234
|
+
className: "mt-1 h-4 w-4 text-forest-700"
|
|
235
|
+
}
|
|
236
|
+
),
|
|
237
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
238
|
+
/* @__PURE__ */ jsx2("div", { className: "font-medium text-stone-900", children: opt.label }),
|
|
239
|
+
opt.description && /* @__PURE__ */ jsx2("div", { className: "mt-1 text-sm text-stone-700", children: opt.description })
|
|
240
|
+
] })
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
opt.value
|
|
244
|
+
);
|
|
245
|
+
}) });
|
|
246
|
+
}
|
|
247
|
+
function ScaleInput({
|
|
248
|
+
question,
|
|
249
|
+
value,
|
|
250
|
+
onChange
|
|
251
|
+
}) {
|
|
252
|
+
const options = question.options ?? [];
|
|
253
|
+
return /* @__PURE__ */ jsx2("div", { className: "space-y-3", role: "radiogroup", "aria-labelledby": `q-${question.id}`, children: options.map((opt) => /* @__PURE__ */ jsxs2(
|
|
254
|
+
"label",
|
|
255
|
+
{
|
|
256
|
+
className: `flex cursor-pointer items-center gap-3 rounded-lg border-2 p-3 transition-colors ${value === opt.value ? "border-forest-700 bg-forest-50" : "border-stone-200 bg-stone-50 hover:border-stone-400"}`,
|
|
257
|
+
children: [
|
|
258
|
+
/* @__PURE__ */ jsx2(
|
|
259
|
+
"input",
|
|
260
|
+
{
|
|
261
|
+
type: "radio",
|
|
262
|
+
name: question.id,
|
|
263
|
+
value: opt.value,
|
|
264
|
+
checked: value === opt.value,
|
|
265
|
+
onChange: (e) => onChange(e.target.value),
|
|
266
|
+
className: "h-4 w-4 text-forest-700"
|
|
267
|
+
}
|
|
268
|
+
),
|
|
269
|
+
/* @__PURE__ */ jsx2("span", { className: "font-mono text-sm text-stone-500", children: opt.value }),
|
|
270
|
+
/* @__PURE__ */ jsx2("span", { className: "text-stone-900", children: opt.label })
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
opt.value
|
|
274
|
+
)) });
|
|
275
|
+
}
|
|
276
|
+
function DateInput({ value, onChange }) {
|
|
277
|
+
return /* @__PURE__ */ jsx2(
|
|
278
|
+
"input",
|
|
279
|
+
{
|
|
280
|
+
type: "date",
|
|
281
|
+
value: value ?? "",
|
|
282
|
+
onChange: (e) => onChange(e.target.value),
|
|
283
|
+
autoFocus: true,
|
|
284
|
+
className: "rounded-lg border-2 border-stone-200 bg-stone-50 px-4 py-3 text-lg text-stone-900 focus:border-forest-700 focus:outline-none"
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
}
|
|
191
288
|
function ListInput({
|
|
192
|
-
|
|
289
|
+
value,
|
|
193
290
|
onChange,
|
|
194
|
-
|
|
195
|
-
max
|
|
291
|
+
listRange
|
|
196
292
|
}) {
|
|
197
|
-
const items =
|
|
293
|
+
const items = value.length > 0 ? value : [""];
|
|
294
|
+
const min = listRange?.min ?? 0;
|
|
295
|
+
const max = listRange?.max ?? Infinity;
|
|
198
296
|
const setItem = (i, v) => {
|
|
199
297
|
const next = [...items];
|
|
200
298
|
next[i] = v;
|
|
@@ -221,7 +319,7 @@ function ListInput({
|
|
|
221
319
|
value: item,
|
|
222
320
|
onChange: (e) => setItem(i, e.target.value),
|
|
223
321
|
className: "flex-grow rounded-lg border-2 border-stone-200 bg-stone-50 px-3 py-2 text-stone-900 focus:border-forest-700 focus:outline-none",
|
|
224
|
-
placeholder: `
|
|
322
|
+
placeholder: `Item ${i + 1}`
|
|
225
323
|
}
|
|
226
324
|
),
|
|
227
325
|
/* @__PURE__ */ jsx2(
|
|
@@ -231,7 +329,7 @@ function ListInput({
|
|
|
231
329
|
onClick: () => removeItem(i),
|
|
232
330
|
disabled: items.length <= 1,
|
|
233
331
|
className: "btn-ghost text-stone-500 disabled:opacity-30",
|
|
234
|
-
"aria-label": `Remove
|
|
332
|
+
"aria-label": `Remove item ${i + 1}`,
|
|
235
333
|
children: "\xD7"
|
|
236
334
|
}
|
|
237
335
|
)
|
|
@@ -244,19 +342,312 @@ function ListInput({
|
|
|
244
342
|
onClick: addItem,
|
|
245
343
|
disabled: items.length >= max,
|
|
246
344
|
className: "text-sm font-medium text-forest-700 hover:text-forest-800 disabled:opacity-40",
|
|
247
|
-
children: "+ Add
|
|
345
|
+
children: "+ Add item"
|
|
248
346
|
}
|
|
249
347
|
),
|
|
250
|
-
/* @__PURE__ */ jsxs2("span", { className: "font-mono text-xs text-stone-500", children: [
|
|
348
|
+
listRange && /* @__PURE__ */ jsxs2("span", { className: "font-mono text-xs text-stone-500", children: [
|
|
251
349
|
validCount,
|
|
252
350
|
" / ",
|
|
253
351
|
min,
|
|
254
352
|
"\u2013",
|
|
255
|
-
max
|
|
353
|
+
max === Infinity ? "\u221E" : max
|
|
256
354
|
] })
|
|
257
355
|
] })
|
|
258
356
|
] });
|
|
259
357
|
}
|
|
358
|
+
function CompositeInput({
|
|
359
|
+
question,
|
|
360
|
+
value,
|
|
361
|
+
onChange
|
|
362
|
+
}) {
|
|
363
|
+
const subQuestions = question.subQuestions ?? [];
|
|
364
|
+
return /* @__PURE__ */ jsx2("div", { className: "space-y-6 rounded-lg border-2 border-stone-200 bg-stone-50 p-4", children: subQuestions.map((sub) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
365
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm font-medium text-stone-900", children: sub.prompt }),
|
|
366
|
+
sub.subprompt && /* @__PURE__ */ jsx2("div", { className: "mt-1 text-xs text-stone-700", children: sub.subprompt }),
|
|
367
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-2", children: /* @__PURE__ */ jsx2(
|
|
368
|
+
InputForType,
|
|
369
|
+
{
|
|
370
|
+
question: sub,
|
|
371
|
+
value: value[sub.id],
|
|
372
|
+
onChange: (next) => onChange({ ...value, [sub.id]: next })
|
|
373
|
+
}
|
|
374
|
+
) })
|
|
375
|
+
] }, sub.id)) });
|
|
376
|
+
}
|
|
377
|
+
function RepeaterInput({
|
|
378
|
+
question,
|
|
379
|
+
value,
|
|
380
|
+
onChange
|
|
381
|
+
}) {
|
|
382
|
+
const rows = value.length > 0 ? value : [{}];
|
|
383
|
+
const itemSchema = question.itemSchema ?? [];
|
|
384
|
+
const min = question.listRange?.min ?? 1;
|
|
385
|
+
const max = question.listRange?.max ?? Infinity;
|
|
386
|
+
const updateRow = (i, patch) => {
|
|
387
|
+
const next = [...rows];
|
|
388
|
+
next[i] = { ...next[i], ...patch };
|
|
389
|
+
onChange(next);
|
|
390
|
+
};
|
|
391
|
+
const addRow = () => {
|
|
392
|
+
if (rows.length < max) onChange([...rows, {}]);
|
|
393
|
+
};
|
|
394
|
+
const removeRow = (i) => {
|
|
395
|
+
if (rows.length <= 1) return;
|
|
396
|
+
onChange(rows.filter((_, idx) => idx !== i));
|
|
397
|
+
};
|
|
398
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-4", children: [
|
|
399
|
+
rows.map((row, i) => /* @__PURE__ */ jsxs2("div", { className: "rounded-lg border-2 border-stone-200 bg-stone-50 p-4", children: [
|
|
400
|
+
/* @__PURE__ */ jsxs2("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
401
|
+
/* @__PURE__ */ jsxs2("span", { className: "font-mono text-xs uppercase tracking-wider text-stone-500", children: [
|
|
402
|
+
"Item ",
|
|
403
|
+
i + 1
|
|
404
|
+
] }),
|
|
405
|
+
/* @__PURE__ */ jsx2(
|
|
406
|
+
"button",
|
|
407
|
+
{
|
|
408
|
+
type: "button",
|
|
409
|
+
onClick: () => removeRow(i),
|
|
410
|
+
disabled: rows.length <= min,
|
|
411
|
+
className: "btn-ghost text-xs text-stone-500 disabled:opacity-30",
|
|
412
|
+
"aria-label": `Remove item ${i + 1}`,
|
|
413
|
+
children: "Remove"
|
|
414
|
+
}
|
|
415
|
+
)
|
|
416
|
+
] }),
|
|
417
|
+
/* @__PURE__ */ jsx2("div", { className: "space-y-4", children: itemSchema.map((sub) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
418
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm font-medium text-stone-900", children: sub.prompt }),
|
|
419
|
+
sub.subprompt && /* @__PURE__ */ jsx2("div", { className: "mt-1 text-xs text-stone-700", children: sub.subprompt }),
|
|
420
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-2", children: /* @__PURE__ */ jsx2(
|
|
421
|
+
InputForType,
|
|
422
|
+
{
|
|
423
|
+
question: sub,
|
|
424
|
+
value: row[sub.id],
|
|
425
|
+
onChange: (next) => updateRow(i, { [sub.id]: next })
|
|
426
|
+
}
|
|
427
|
+
) })
|
|
428
|
+
] }, sub.id)) })
|
|
429
|
+
] }, i)),
|
|
430
|
+
/* @__PURE__ */ jsx2(
|
|
431
|
+
"button",
|
|
432
|
+
{
|
|
433
|
+
type: "button",
|
|
434
|
+
onClick: addRow,
|
|
435
|
+
disabled: rows.length >= max,
|
|
436
|
+
className: "text-sm font-medium text-forest-700 hover:text-forest-800 disabled:opacity-40",
|
|
437
|
+
children: "+ Add another"
|
|
438
|
+
}
|
|
439
|
+
)
|
|
440
|
+
] });
|
|
441
|
+
}
|
|
442
|
+
function FileUploadInput({
|
|
443
|
+
question,
|
|
444
|
+
value,
|
|
445
|
+
onChange
|
|
446
|
+
}) {
|
|
447
|
+
const itemSchema = question.itemSchema ?? [];
|
|
448
|
+
const onFiles = (files) => {
|
|
449
|
+
if (!files || files.length === 0) return;
|
|
450
|
+
const entries = Array.from(files).map((f) => ({
|
|
451
|
+
file: f,
|
|
452
|
+
metadata: {}
|
|
453
|
+
}));
|
|
454
|
+
onChange([...value, ...entries]);
|
|
455
|
+
};
|
|
456
|
+
const updateMetadata = (i, patch) => {
|
|
457
|
+
const next = [...value];
|
|
458
|
+
next[i] = { ...next[i], metadata: { ...next[i].metadata, ...patch } };
|
|
459
|
+
onChange(next);
|
|
460
|
+
};
|
|
461
|
+
const removeEntry = (i) => onChange(value.filter((_, idx) => idx !== i));
|
|
462
|
+
const onDrop = (e) => {
|
|
463
|
+
e.preventDefault();
|
|
464
|
+
onFiles(e.dataTransfer.files);
|
|
465
|
+
};
|
|
466
|
+
const onDragOver = (e) => e.preventDefault();
|
|
467
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-4", children: [
|
|
468
|
+
/* @__PURE__ */ jsxs2(
|
|
469
|
+
"div",
|
|
470
|
+
{
|
|
471
|
+
onDrop,
|
|
472
|
+
onDragOver,
|
|
473
|
+
className: "rounded-lg border-2 border-dashed border-stone-300 bg-stone-50 p-6 text-center",
|
|
474
|
+
children: [
|
|
475
|
+
/* @__PURE__ */ jsxs2("p", { className: "text-sm text-stone-700", children: [
|
|
476
|
+
"Drag PDFs or other files here, or",
|
|
477
|
+
" ",
|
|
478
|
+
/* @__PURE__ */ jsxs2("label", { className: "cursor-pointer font-medium text-forest-700 hover:text-forest-800", children: [
|
|
479
|
+
"browse",
|
|
480
|
+
/* @__PURE__ */ jsx2(
|
|
481
|
+
"input",
|
|
482
|
+
{
|
|
483
|
+
type: "file",
|
|
484
|
+
multiple: true,
|
|
485
|
+
className: "sr-only",
|
|
486
|
+
onChange: (e) => onFiles(e.target.files)
|
|
487
|
+
}
|
|
488
|
+
)
|
|
489
|
+
] })
|
|
490
|
+
] }),
|
|
491
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-xs text-stone-500", children: "You'll add a title, author, and description for each file below." })
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
),
|
|
495
|
+
value.length > 0 && /* @__PURE__ */ jsx2("ul", { className: "space-y-4", children: value.map((entry, i) => {
|
|
496
|
+
const name = entry.file.name;
|
|
497
|
+
const size = "size" in entry.file ? entry.file.size : void 0;
|
|
498
|
+
return /* @__PURE__ */ jsxs2("li", { className: "rounded-lg border-2 border-stone-200 bg-stone-50 p-4", children: [
|
|
499
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between", children: [
|
|
500
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
501
|
+
/* @__PURE__ */ jsx2("div", { className: "font-medium text-stone-900", children: name }),
|
|
502
|
+
typeof size === "number" && /* @__PURE__ */ jsxs2("div", { className: "font-mono text-xs text-stone-500", children: [
|
|
503
|
+
(size / 1024).toFixed(1),
|
|
504
|
+
" KB"
|
|
505
|
+
] })
|
|
506
|
+
] }),
|
|
507
|
+
/* @__PURE__ */ jsx2(
|
|
508
|
+
"button",
|
|
509
|
+
{
|
|
510
|
+
type: "button",
|
|
511
|
+
onClick: () => removeEntry(i),
|
|
512
|
+
className: "btn-ghost text-xs text-stone-500",
|
|
513
|
+
"aria-label": `Remove ${name}`,
|
|
514
|
+
children: "Remove"
|
|
515
|
+
}
|
|
516
|
+
)
|
|
517
|
+
] }),
|
|
518
|
+
itemSchema.length > 0 && /* @__PURE__ */ jsx2("div", { className: "mt-4 space-y-3", children: itemSchema.map((sub) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
519
|
+
/* @__PURE__ */ jsx2("div", { className: "text-xs font-medium text-stone-900", children: sub.prompt }),
|
|
520
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-1", children: /* @__PURE__ */ jsx2(
|
|
521
|
+
InputForType,
|
|
522
|
+
{
|
|
523
|
+
question: sub,
|
|
524
|
+
value: entry.metadata[sub.id],
|
|
525
|
+
onChange: (next) => updateMetadata(i, { [sub.id]: next })
|
|
526
|
+
}
|
|
527
|
+
) })
|
|
528
|
+
] }, sub.id)) })
|
|
529
|
+
] }, i);
|
|
530
|
+
}) })
|
|
531
|
+
] });
|
|
532
|
+
}
|
|
533
|
+
function UnknownTypeFallback({ inputType }) {
|
|
534
|
+
return /* @__PURE__ */ jsxs2("div", { className: "rounded-lg border-2 border-stone-300 bg-stone-50 p-4 text-sm text-stone-500", children: [
|
|
535
|
+
"Unsupported question input type:",
|
|
536
|
+
" ",
|
|
537
|
+
/* @__PURE__ */ jsx2("code", { className: "font-mono", children: String(inputType) })
|
|
538
|
+
] });
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/summarize.ts
|
|
542
|
+
function isAnswered(value) {
|
|
543
|
+
if (value === void 0 || value === null) return false;
|
|
544
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
545
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
546
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
function summarizeShort(question, value, maxLen = 80) {
|
|
550
|
+
if (!isAnswered(value)) return "";
|
|
551
|
+
switch (question.inputType) {
|
|
552
|
+
case "short-text":
|
|
553
|
+
case "long-text":
|
|
554
|
+
case "date":
|
|
555
|
+
case "scale":
|
|
556
|
+
return truncate(String(value), maxLen);
|
|
557
|
+
case "single-select": {
|
|
558
|
+
const v = String(value);
|
|
559
|
+
const label = question.options?.find((o) => o.value === v)?.label;
|
|
560
|
+
return truncate(label ?? v, maxLen);
|
|
561
|
+
}
|
|
562
|
+
case "multi-select":
|
|
563
|
+
case "list": {
|
|
564
|
+
const arr = value;
|
|
565
|
+
const labels = arr.map((v) => {
|
|
566
|
+
const found = question.options?.find((o) => o.value === v)?.label;
|
|
567
|
+
return found ?? v;
|
|
568
|
+
});
|
|
569
|
+
return truncate(labels.join(", "), maxLen);
|
|
570
|
+
}
|
|
571
|
+
case "composite": {
|
|
572
|
+
const obj = value;
|
|
573
|
+
const subs = question.subQuestions ?? [];
|
|
574
|
+
for (const sub of subs) {
|
|
575
|
+
const subVal = obj[sub.id];
|
|
576
|
+
if (isAnswered(subVal)) {
|
|
577
|
+
return truncate(summarizeShort(sub, subVal, maxLen), maxLen);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return "";
|
|
581
|
+
}
|
|
582
|
+
case "repeater": {
|
|
583
|
+
const rows = value;
|
|
584
|
+
return `${rows.length} item${rows.length === 1 ? "" : "s"}`;
|
|
585
|
+
}
|
|
586
|
+
case "file-upload": {
|
|
587
|
+
const files = value;
|
|
588
|
+
return `${files.length} file${files.length === 1 ? "" : "s"}`;
|
|
589
|
+
}
|
|
590
|
+
default:
|
|
591
|
+
return "";
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
function summarizeLong(question, value) {
|
|
595
|
+
if (!isAnswered(value)) return "";
|
|
596
|
+
switch (question.inputType) {
|
|
597
|
+
case "short-text":
|
|
598
|
+
case "long-text":
|
|
599
|
+
case "date":
|
|
600
|
+
case "scale":
|
|
601
|
+
return String(value);
|
|
602
|
+
case "single-select": {
|
|
603
|
+
const v = String(value);
|
|
604
|
+
const label = question.options?.find((o) => o.value === v)?.label;
|
|
605
|
+
return label ?? v;
|
|
606
|
+
}
|
|
607
|
+
case "multi-select":
|
|
608
|
+
case "list": {
|
|
609
|
+
const arr = value;
|
|
610
|
+
const labels = arr.map((v) => question.options?.find((o) => o.value === v)?.label ?? v);
|
|
611
|
+
return labels.join("\n\u2022 ").replace(/^/, "\u2022 ");
|
|
612
|
+
}
|
|
613
|
+
case "composite": {
|
|
614
|
+
const obj = value;
|
|
615
|
+
const subs = question.subQuestions ?? [];
|
|
616
|
+
const lines = [];
|
|
617
|
+
for (const sub of subs) {
|
|
618
|
+
const subVal = obj[sub.id];
|
|
619
|
+
if (isAnswered(subVal)) {
|
|
620
|
+
lines.push(`${sub.prompt}: ${summarizeShort(sub, subVal, 200)}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return lines.join("\n");
|
|
624
|
+
}
|
|
625
|
+
case "repeater": {
|
|
626
|
+
const rows = value;
|
|
627
|
+
const itemSchema = question.itemSchema ?? [];
|
|
628
|
+
const summary = rows.map((row, i) => {
|
|
629
|
+
const parts = [`Item ${i + 1}:`];
|
|
630
|
+
for (const sub of itemSchema) {
|
|
631
|
+
const subVal = row[sub.id];
|
|
632
|
+
if (isAnswered(subVal)) {
|
|
633
|
+
parts.push(` ${sub.prompt}: ${summarizeShort(sub, subVal, 100)}`);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return parts.join("\n");
|
|
637
|
+
});
|
|
638
|
+
return summary.join("\n");
|
|
639
|
+
}
|
|
640
|
+
case "file-upload": {
|
|
641
|
+
const files = value;
|
|
642
|
+
return files.map((f, i) => `${i + 1}. ${f.file.name}`).join("\n");
|
|
643
|
+
}
|
|
644
|
+
default:
|
|
645
|
+
return "";
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function truncate(s, maxLen) {
|
|
649
|
+
return s.length > maxLen ? s.slice(0, maxLen - 1) + "\u2026" : s;
|
|
650
|
+
}
|
|
260
651
|
|
|
261
652
|
// src/AnswerSidebar.tsx
|
|
262
653
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -270,21 +661,20 @@ function AnswerSidebar({ questions, currentIndex, answers, onSelectQuestion }) {
|
|
|
270
661
|
/* @__PURE__ */ jsx3("h3", { className: "mb-3 font-mono text-xs uppercase tracking-wider text-stone-500", children: "Your answers" }),
|
|
271
662
|
/* @__PURE__ */ jsx3("ol", { className: "space-y-3", children: questions.map((q, i) => {
|
|
272
663
|
const answer = answers[q.id];
|
|
273
|
-
const answered =
|
|
664
|
+
const answered = isAnswered(answer);
|
|
274
665
|
const isCurrent = i === currentIndex;
|
|
666
|
+
const preview = answered ? summarizeShort(q, answer) : "";
|
|
275
667
|
return /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(
|
|
276
668
|
"button",
|
|
277
669
|
{
|
|
278
670
|
type: "button",
|
|
279
671
|
onClick: () => onSelectQuestion(i),
|
|
280
|
-
className: `w-full rounded-lg border p-3 text-left transition-colors
|
|
281
|
-
${isCurrent ? "border-forest-700 bg-forest-50" : answered ? "border-stone-200 bg-stone-50 hover:border-stone-400" : "border-stone-200/50 bg-stone-50/50 hover:border-stone-300"}`,
|
|
672
|
+
className: `w-full rounded-lg border p-3 text-left transition-colors ${isCurrent ? "border-forest-700 bg-forest-50" : answered ? "border-stone-200 bg-stone-50 hover:border-stone-400" : "border-stone-200/50 bg-stone-50/50 hover:border-stone-300"}`,
|
|
282
673
|
children: /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-2", children: [
|
|
283
674
|
/* @__PURE__ */ jsxs3(
|
|
284
675
|
"span",
|
|
285
676
|
{
|
|
286
|
-
className: `mt-0.5 font-mono text-xs
|
|
287
|
-
${isCurrent ? "text-forest-700" : answered ? "text-stone-500" : "text-stone-400"}`,
|
|
677
|
+
className: `mt-0.5 font-mono text-xs ${isCurrent ? "text-forest-700" : answered ? "text-stone-500" : "text-stone-400"}`,
|
|
288
678
|
children: [
|
|
289
679
|
String(i + 1).padStart(2, "0"),
|
|
290
680
|
"."
|
|
@@ -295,12 +685,11 @@ function AnswerSidebar({ questions, currentIndex, answers, onSelectQuestion }) {
|
|
|
295
685
|
/* @__PURE__ */ jsx3(
|
|
296
686
|
"div",
|
|
297
687
|
{
|
|
298
|
-
className: `text-xs font-medium
|
|
299
|
-
${answered ? "text-stone-900" : "text-stone-500"}`,
|
|
688
|
+
className: `text-xs font-medium ${answered ? "text-stone-900" : "text-stone-500"}`,
|
|
300
689
|
children: q.prompt.length > 50 ? q.prompt.slice(0, 50) + "\u2026" : q.prompt
|
|
301
690
|
}
|
|
302
691
|
),
|
|
303
|
-
answered && /* @__PURE__ */ jsx3("div", { className: "mt-1 text-xs italic text-stone-500 line-clamp-1", children:
|
|
692
|
+
answered && /* @__PURE__ */ jsx3("div", { className: "mt-1 text-xs italic text-stone-500 line-clamp-1", children: preview })
|
|
304
693
|
] })
|
|
305
694
|
] })
|
|
306
695
|
}
|
|
@@ -319,13 +708,13 @@ function ReviewSummary({ questions, answers, onGenerate, onEdit, generating }) {
|
|
|
319
708
|
/* @__PURE__ */ jsx4("p", { className: "mt-4 text-lg text-stone-700", children: "These become the seed content of your project. You can edit anything now, or continue and edit later through the platform." }),
|
|
320
709
|
/* @__PURE__ */ jsx4("dl", { className: "mt-10 space-y-6 border-t border-stone-200 pt-6", children: questions.map((q, i) => {
|
|
321
710
|
const answer = answers[q.id];
|
|
322
|
-
const
|
|
323
|
-
const
|
|
711
|
+
const answered = isAnswered(answer);
|
|
712
|
+
const display = answered ? summarizeLong(q, answer) : "";
|
|
324
713
|
return /* @__PURE__ */ jsxs4("div", { className: "flex gap-4 border-b border-stone-200 pb-6", children: [
|
|
325
714
|
/* @__PURE__ */ jsx4("span", { className: "font-mono text-sm text-stone-400 pt-1", children: String(i + 1).padStart(2, "0") }),
|
|
326
715
|
/* @__PURE__ */ jsxs4("div", { className: "flex-grow", children: [
|
|
327
716
|
/* @__PURE__ */ jsx4("dt", { className: "text-sm font-medium text-stone-900", children: q.prompt }),
|
|
328
|
-
/* @__PURE__ */ jsx4("dd", { className: "mt-2 text-stone-900 whitespace-pre-wrap", children:
|
|
717
|
+
/* @__PURE__ */ jsx4("dd", { className: "mt-2 text-stone-900 whitespace-pre-wrap", children: answered ? display : /* @__PURE__ */ jsx4("span", { className: "italic text-stone-400", children: "Not answered" }) })
|
|
329
718
|
] }),
|
|
330
719
|
/* @__PURE__ */ jsx4(
|
|
331
720
|
"button",
|
|
@@ -362,6 +751,9 @@ export {
|
|
|
362
751
|
AnswerSidebar,
|
|
363
752
|
ProgressBar,
|
|
364
753
|
QuestionCard,
|
|
365
|
-
ReviewSummary
|
|
754
|
+
ReviewSummary,
|
|
755
|
+
isAnswered,
|
|
756
|
+
summarizeLong,
|
|
757
|
+
summarizeShort
|
|
366
758
|
};
|
|
367
759
|
//# sourceMappingURL=index.js.map
|