@squaredr/fieldcraft-pro 1.7.0 → 1.9.0
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/LICENSE.txt +51 -51
- package/README.md +139 -139
- package/dist/{chunk-VEP3DOCQ.mjs → chunk-5QXKDYWY.mjs} +103 -19
- package/dist/{chunk-GZIV2XAD.mjs → chunk-7GPX3YIB.mjs} +146 -183
- package/dist/{chunk-J5V6OVZO.mjs → chunk-DWRLPGX5.mjs} +690 -211
- package/dist/{chunk-4TN6YJDF.mjs → chunk-KTIU7VCP.mjs} +971 -214
- package/dist/default-schema-BGxf9Jrg.d.ts +174 -0
- package/dist/default-schema-Bwq8FATF.d.mts +174 -0
- package/dist/form-builder/index.d.mts +14 -170
- package/dist/form-builder/index.d.ts +14 -170
- package/dist/form-builder/index.js +827 -326
- package/dist/form-builder/index.mjs +1 -1
- package/dist/index.d.mts +111 -10
- package/dist/index.d.ts +111 -10
- package/dist/index.js +3525 -971
- package/dist/index.mjs +1424 -124
- package/dist/response-viewer/index.d.mts +1 -1
- package/dist/response-viewer/index.d.ts +1 -1
- package/dist/response-viewer/index.js +146 -183
- package/dist/response-viewer/index.mjs +1 -1
- package/dist/styles.css +2 -2
- package/dist/templates/index.d.mts +1 -1
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +103 -19
- package/dist/templates/index.mjs +1 -1
- package/dist/theme-editor/index.d.mts +12 -7
- package/dist/theme-editor/index.d.ts +12 -7
- package/dist/theme-editor/index.js +971 -214
- package/dist/theme-editor/index.mjs +1 -1
- package/dist/{types-bFHsZT1n.d.mts → types-BQOeI3LX.d.mts} +1 -1
- package/dist/{types-bFHsZT1n.d.ts → types-BQOeI3LX.d.ts} +1 -1
- package/package.json +29 -18
- package/theme-editor/styles.css +497 -466
|
@@ -4,6 +4,119 @@ import { useState, useEffect, useMemo } from 'react';
|
|
|
4
4
|
import { Input, Button, Separator, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@squaredr/fieldcraft-react';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
|
+
// src/response-viewer/constants.ts
|
|
8
|
+
var DISPLAY_ONLY_TYPES = /* @__PURE__ */ new Set([
|
|
9
|
+
"info-block",
|
|
10
|
+
"info_block",
|
|
11
|
+
"section-header",
|
|
12
|
+
"page-break",
|
|
13
|
+
"image",
|
|
14
|
+
"divider",
|
|
15
|
+
"spacer",
|
|
16
|
+
"video",
|
|
17
|
+
"rich-text",
|
|
18
|
+
"welcome-screen",
|
|
19
|
+
"thank-you-screen",
|
|
20
|
+
"hidden",
|
|
21
|
+
"calculated"
|
|
22
|
+
]);
|
|
23
|
+
function formatDuration(ms) {
|
|
24
|
+
if (ms == null || ms < 0) return "\u2014";
|
|
25
|
+
const totalSeconds = Math.round(ms / 1e3);
|
|
26
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
27
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
28
|
+
const seconds = totalSeconds % 60;
|
|
29
|
+
return `${minutes}m ${seconds}s`;
|
|
30
|
+
}
|
|
31
|
+
function countAnswered(values) {
|
|
32
|
+
let count = 0;
|
|
33
|
+
for (const v of Object.values(values)) {
|
|
34
|
+
if (v !== void 0 && v !== null && v !== "") count++;
|
|
35
|
+
}
|
|
36
|
+
return count;
|
|
37
|
+
}
|
|
38
|
+
function getTotalFieldCount(schema) {
|
|
39
|
+
let count = 0;
|
|
40
|
+
for (const section of schema.sections) {
|
|
41
|
+
for (const q of section.questions) {
|
|
42
|
+
if (!DISPLAY_ONLY_TYPES.has(q.type)) count++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return count;
|
|
46
|
+
}
|
|
47
|
+
function ResponseTable({
|
|
48
|
+
schema,
|
|
49
|
+
responses,
|
|
50
|
+
onRowClick,
|
|
51
|
+
selectable,
|
|
52
|
+
selectedIds,
|
|
53
|
+
onToggleSelect,
|
|
54
|
+
onSelectAll
|
|
55
|
+
}) {
|
|
56
|
+
const totalFields = getTotalFieldCount(schema);
|
|
57
|
+
const allPageSelected = selectable && responses.length > 0 && responses.every(
|
|
58
|
+
(r) => r.sessionToken && selectedIds?.has(r.sessionToken)
|
|
59
|
+
);
|
|
60
|
+
const colCount = 4 + (selectable ? 1 : 0);
|
|
61
|
+
return /* @__PURE__ */ jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-[13px]", children: [
|
|
62
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "bg-muted", children: [
|
|
63
|
+
selectable && /* @__PURE__ */ jsx("th", { className: "px-3 py-2 w-8 border-b-2 border-border", children: /* @__PURE__ */ jsx(
|
|
64
|
+
"input",
|
|
65
|
+
{
|
|
66
|
+
type: "checkbox",
|
|
67
|
+
checked: allPageSelected,
|
|
68
|
+
onChange: () => onSelectAll?.(),
|
|
69
|
+
className: "accent-primary"
|
|
70
|
+
}
|
|
71
|
+
) }),
|
|
72
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Submitted" }),
|
|
73
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Completion Time" }),
|
|
74
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Fields Answered" }),
|
|
75
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Score" })
|
|
76
|
+
] }) }),
|
|
77
|
+
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
78
|
+
responses.map((response, idx) => {
|
|
79
|
+
const answered = countAnswered(response.values);
|
|
80
|
+
return /* @__PURE__ */ jsxs(
|
|
81
|
+
"tr",
|
|
82
|
+
{
|
|
83
|
+
onClick: () => onRowClick?.(response),
|
|
84
|
+
className: onRowClick ? "cursor-pointer border-b border-border hover:bg-accent transition-colors" : "border-b border-border",
|
|
85
|
+
children: [
|
|
86
|
+
selectable && /* @__PURE__ */ jsx("td", { className: "px-3 py-2 w-8", children: /* @__PURE__ */ jsx(
|
|
87
|
+
"input",
|
|
88
|
+
{
|
|
89
|
+
type: "checkbox",
|
|
90
|
+
checked: !!(response.sessionToken && selectedIds?.has(response.sessionToken)),
|
|
91
|
+
onChange: (e) => {
|
|
92
|
+
e.stopPropagation();
|
|
93
|
+
if (response.sessionToken) onToggleSelect?.(response.sessionToken);
|
|
94
|
+
},
|
|
95
|
+
onClick: (e) => e.stopPropagation(),
|
|
96
|
+
className: "accent-primary"
|
|
97
|
+
}
|
|
98
|
+
) }),
|
|
99
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-foreground whitespace-nowrap", children: new Date(response.submittedAt).toLocaleString() }),
|
|
100
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-foreground whitespace-nowrap", children: formatDuration(response.completionTimeMs) }),
|
|
101
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-foreground whitespace-nowrap", children: totalFields > 0 ? `${answered}/${totalFields}` : "\u2014" }),
|
|
102
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-foreground whitespace-nowrap", children: response.totalScore ?? "\u2014" })
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
response.sessionToken || idx
|
|
106
|
+
);
|
|
107
|
+
}),
|
|
108
|
+
responses.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
109
|
+
"td",
|
|
110
|
+
{
|
|
111
|
+
colSpan: colCount,
|
|
112
|
+
className: "px-3 py-8 text-center text-muted-foreground",
|
|
113
|
+
children: "No responses yet"
|
|
114
|
+
}
|
|
115
|
+
) })
|
|
116
|
+
] })
|
|
117
|
+
] }) });
|
|
118
|
+
}
|
|
119
|
+
|
|
7
120
|
// src/response-viewer/clinical-display-data.ts
|
|
8
121
|
var BODY_REGION_LABELS = {
|
|
9
122
|
head: "Head",
|
|
@@ -140,166 +253,6 @@ function getScoreSeverity(instrumentKey, score) {
|
|
|
140
253
|
if (!thresholds) return void 0;
|
|
141
254
|
return thresholds.find((t) => score >= t.min && score <= t.max);
|
|
142
255
|
}
|
|
143
|
-
function ResponseTable({
|
|
144
|
-
schema,
|
|
145
|
-
responses,
|
|
146
|
-
onRowClick,
|
|
147
|
-
selectable,
|
|
148
|
-
selectedIds,
|
|
149
|
-
onToggleSelect,
|
|
150
|
-
onSelectAll
|
|
151
|
-
}) {
|
|
152
|
-
const questions = getAllQuestions(schema);
|
|
153
|
-
const allPageSelected = selectable && responses.length > 0 && responses.every(
|
|
154
|
-
(r) => r.sessionToken && selectedIds?.has(r.sessionToken)
|
|
155
|
-
);
|
|
156
|
-
return /* @__PURE__ */ jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-[13px]", children: [
|
|
157
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "bg-muted", children: [
|
|
158
|
-
selectable && /* @__PURE__ */ jsx("th", { className: "px-3 py-2 w-8 border-b-2 border-border", children: /* @__PURE__ */ jsx(
|
|
159
|
-
"input",
|
|
160
|
-
{
|
|
161
|
-
type: "checkbox",
|
|
162
|
-
checked: allPageSelected,
|
|
163
|
-
onChange: () => onSelectAll?.(),
|
|
164
|
-
className: "accent-primary"
|
|
165
|
-
}
|
|
166
|
-
) }),
|
|
167
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Submitted" }),
|
|
168
|
-
questions.map((q) => /* @__PURE__ */ jsx(
|
|
169
|
-
"th",
|
|
170
|
-
{
|
|
171
|
-
className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap",
|
|
172
|
-
children: q.label
|
|
173
|
-
},
|
|
174
|
-
q.id
|
|
175
|
-
)),
|
|
176
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Score" })
|
|
177
|
-
] }) }),
|
|
178
|
-
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
179
|
-
responses.map((response, idx) => /* @__PURE__ */ jsxs(
|
|
180
|
-
"tr",
|
|
181
|
-
{
|
|
182
|
-
onClick: () => onRowClick?.(response),
|
|
183
|
-
className: onRowClick ? "cursor-pointer border-b border-border hover:bg-accent transition-colors" : "border-b border-border",
|
|
184
|
-
children: [
|
|
185
|
-
selectable && /* @__PURE__ */ jsx("td", { className: "px-3 py-2 w-8", children: /* @__PURE__ */ jsx(
|
|
186
|
-
"input",
|
|
187
|
-
{
|
|
188
|
-
type: "checkbox",
|
|
189
|
-
checked: !!(response.sessionToken && selectedIds?.has(response.sessionToken)),
|
|
190
|
-
onChange: (e) => {
|
|
191
|
-
e.stopPropagation();
|
|
192
|
-
if (response.sessionToken) onToggleSelect?.(response.sessionToken);
|
|
193
|
-
},
|
|
194
|
-
onClick: (e) => e.stopPropagation(),
|
|
195
|
-
className: "accent-primary"
|
|
196
|
-
}
|
|
197
|
-
) }),
|
|
198
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap", children: new Date(response.submittedAt).toLocaleString() }),
|
|
199
|
-
questions.map((q) => /* @__PURE__ */ jsx(
|
|
200
|
-
"td",
|
|
201
|
-
{
|
|
202
|
-
className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap",
|
|
203
|
-
children: formatCellValue(response.values[q.id], q.type)
|
|
204
|
-
},
|
|
205
|
-
q.id
|
|
206
|
-
)),
|
|
207
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap", children: response.totalScore ?? "\u2014" })
|
|
208
|
-
]
|
|
209
|
-
},
|
|
210
|
-
response.sessionToken || idx
|
|
211
|
-
)),
|
|
212
|
-
responses.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
213
|
-
"td",
|
|
214
|
-
{
|
|
215
|
-
colSpan: questions.length + 2 + (selectable ? 1 : 0),
|
|
216
|
-
className: "px-3 py-8 text-center text-muted-foreground",
|
|
217
|
-
children: "No responses yet"
|
|
218
|
-
}
|
|
219
|
-
) })
|
|
220
|
-
] })
|
|
221
|
-
] }) });
|
|
222
|
-
}
|
|
223
|
-
function getAllQuestions(schema) {
|
|
224
|
-
const questions = [];
|
|
225
|
-
for (const section of schema.sections) {
|
|
226
|
-
for (const q of section.questions) {
|
|
227
|
-
if (q.type === "info-block" || q.type === "section-header" || q.type === "page-break") {
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
questions.push(q);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return questions;
|
|
234
|
-
}
|
|
235
|
-
function formatCellValue(value, type) {
|
|
236
|
-
if (value == null) return "\u2014";
|
|
237
|
-
if (type) {
|
|
238
|
-
switch (type) {
|
|
239
|
-
case "vitals_entry": {
|
|
240
|
-
if (typeof value !== "object" || value === null) break;
|
|
241
|
-
const v = value;
|
|
242
|
-
const parts = [];
|
|
243
|
-
if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
|
|
244
|
-
if (v.heartRate) parts.push(`HR ${v.heartRate}`);
|
|
245
|
-
if (v.temperature) parts.push(`${v.temperature}\xB0F`);
|
|
246
|
-
if (v.oxygenSaturation) parts.push(`SpO\u2082 ${v.oxygenSaturation}%`);
|
|
247
|
-
return parts.length > 0 ? parts.join(", ") : "\u2014";
|
|
248
|
-
}
|
|
249
|
-
case "medication_list":
|
|
250
|
-
if (Array.isArray(value)) return `${value.length} medication${value.length !== 1 ? "s" : ""}`;
|
|
251
|
-
break;
|
|
252
|
-
case "allergy_list":
|
|
253
|
-
if (Array.isArray(value)) return `${value.length} allerg${value.length !== 1 ? "ies" : "y"}`;
|
|
254
|
-
break;
|
|
255
|
-
case "body_diagram":
|
|
256
|
-
if (Array.isArray(value)) {
|
|
257
|
-
const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
|
|
258
|
-
return labels.join(", ");
|
|
259
|
-
}
|
|
260
|
-
break;
|
|
261
|
-
case "pain_scale":
|
|
262
|
-
if (typeof value === "number") return `${value}/10`;
|
|
263
|
-
break;
|
|
264
|
-
case "bmi_calculator": {
|
|
265
|
-
if (typeof value !== "object" || value === null) break;
|
|
266
|
-
const d = value;
|
|
267
|
-
if (d.bmi != null) return `BMI ${d.bmi}`;
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
case "payment": {
|
|
271
|
-
if (typeof value !== "object" || value === null) break;
|
|
272
|
-
const p = value;
|
|
273
|
-
const status = p.status;
|
|
274
|
-
return status ? status.charAt(0).toUpperCase() + status.slice(1) : "\u2014";
|
|
275
|
-
}
|
|
276
|
-
case "insurance_card": {
|
|
277
|
-
if (typeof value !== "object" || value === null) break;
|
|
278
|
-
const ins = value;
|
|
279
|
-
const parts = [ins.carrierId, ins.planName].filter(Boolean);
|
|
280
|
-
return parts.length > 0 ? parts.join(" \u2014 ") : "Card uploaded";
|
|
281
|
-
}
|
|
282
|
-
case "legal_name": {
|
|
283
|
-
if (typeof value !== "object" || value === null) break;
|
|
284
|
-
const n = value;
|
|
285
|
-
return [n.first, n.last].filter(Boolean).join(" ") || "\u2014";
|
|
286
|
-
}
|
|
287
|
-
case "address": {
|
|
288
|
-
if (typeof value !== "object" || value === null) break;
|
|
289
|
-
const a = value;
|
|
290
|
-
return [a.city, a.state].filter(Boolean).join(", ") || "\u2014";
|
|
291
|
-
}
|
|
292
|
-
case "consent":
|
|
293
|
-
return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
|
|
294
|
-
case "signature":
|
|
295
|
-
return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "\u2014";
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
299
|
-
if (Array.isArray(value)) return value.join(", ");
|
|
300
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
301
|
-
return String(value);
|
|
302
|
-
}
|
|
303
256
|
function ResponseCard({
|
|
304
257
|
response,
|
|
305
258
|
fields,
|
|
@@ -783,7 +736,13 @@ function formatFallbackValue(value) {
|
|
|
783
736
|
if (value == null) return "\u2014";
|
|
784
737
|
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
785
738
|
if (Array.isArray(value)) return value.map(formatFallbackValue).join(", ");
|
|
786
|
-
if (typeof value === "object")
|
|
739
|
+
if (typeof value === "object") {
|
|
740
|
+
try {
|
|
741
|
+
return JSON.stringify(value, null, 2);
|
|
742
|
+
} catch {
|
|
743
|
+
return "[Object]";
|
|
744
|
+
}
|
|
745
|
+
}
|
|
787
746
|
return String(value);
|
|
788
747
|
}
|
|
789
748
|
function TimelineView({ responses, questions, onSelect }) {
|
|
@@ -1301,9 +1260,7 @@ function getExportableQuestions(schema) {
|
|
|
1301
1260
|
const questions = [];
|
|
1302
1261
|
for (const section of schema.sections) {
|
|
1303
1262
|
for (const q of section.questions) {
|
|
1304
|
-
if (
|
|
1305
|
-
continue;
|
|
1306
|
-
}
|
|
1263
|
+
if (DISPLAY_ONLY_TYPES.has(q.type)) continue;
|
|
1307
1264
|
questions.push(q);
|
|
1308
1265
|
}
|
|
1309
1266
|
}
|
|
@@ -1369,6 +1326,10 @@ function formatExportValue(value, type) {
|
|
|
1369
1326
|
return String(value);
|
|
1370
1327
|
}
|
|
1371
1328
|
function escapeCsvField(field) {
|
|
1329
|
+
const first = field.charAt(0);
|
|
1330
|
+
if (first === "=" || first === "+" || first === "-" || first === "@" || first === " " || first === "\r") {
|
|
1331
|
+
field = `'${field}`;
|
|
1332
|
+
}
|
|
1372
1333
|
if (field.includes(",") || field.includes('"') || field.includes("\n")) {
|
|
1373
1334
|
return `"${field.replace(/"/g, '""')}"`;
|
|
1374
1335
|
}
|
|
@@ -1383,7 +1344,7 @@ function downloadBlob(content, filename, mimeType) {
|
|
|
1383
1344
|
document.body.appendChild(link);
|
|
1384
1345
|
link.click();
|
|
1385
1346
|
document.body.removeChild(link);
|
|
1386
|
-
URL.revokeObjectURL(url);
|
|
1347
|
+
setTimeout(() => URL.revokeObjectURL(url), 100);
|
|
1387
1348
|
}
|
|
1388
1349
|
|
|
1389
1350
|
// src/response-viewer/pagination-utils.ts
|
|
@@ -1503,14 +1464,15 @@ function applyFilters(responses, state) {
|
|
|
1503
1464
|
function matchesDateRange(submittedAt, range) {
|
|
1504
1465
|
if (!range.from && !range.to) return true;
|
|
1505
1466
|
const submitted = new Date(submittedAt);
|
|
1467
|
+
if (isNaN(submitted.getTime())) return false;
|
|
1506
1468
|
if (range.from) {
|
|
1507
|
-
const from = new Date(range.from);
|
|
1508
|
-
from.
|
|
1469
|
+
const from = /* @__PURE__ */ new Date(range.from + "T00:00:00");
|
|
1470
|
+
if (isNaN(from.getTime())) return false;
|
|
1509
1471
|
if (submitted < from) return false;
|
|
1510
1472
|
}
|
|
1511
1473
|
if (range.to) {
|
|
1512
|
-
const to = new Date(range.to);
|
|
1513
|
-
to.
|
|
1474
|
+
const to = /* @__PURE__ */ new Date(range.to + "T23:59:59.999");
|
|
1475
|
+
if (isNaN(to.getTime())) return false;
|
|
1514
1476
|
if (submitted > to) return false;
|
|
1515
1477
|
}
|
|
1516
1478
|
return true;
|
|
@@ -1616,9 +1578,12 @@ function ResponseViewerInner({
|
|
|
1616
1578
|
[responses, searchQuery, schema]
|
|
1617
1579
|
);
|
|
1618
1580
|
const isFiltered = hasActiveFilters(filterState);
|
|
1619
|
-
const filteredResponses =
|
|
1581
|
+
const filteredResponses = useMemo(
|
|
1582
|
+
() => applyFilters(searchedResponses, filterState),
|
|
1583
|
+
[searchedResponses, filterState]
|
|
1584
|
+
);
|
|
1620
1585
|
const pag = paginate(filteredResponses, currentPage, effectivePageSize);
|
|
1621
|
-
const questions =
|
|
1586
|
+
const questions = useMemo(() => getAllQuestions(schema), [schema]);
|
|
1622
1587
|
const isSelectable = selectable && (!!onBulkDelete || !!onBulkExport);
|
|
1623
1588
|
function handleSelect(response) {
|
|
1624
1589
|
setSelectedResponse(response);
|
|
@@ -1720,7 +1685,7 @@ function ResponseViewerInner({
|
|
|
1720
1685
|
});
|
|
1721
1686
|
}
|
|
1722
1687
|
function getFields(response) {
|
|
1723
|
-
return questions.map((q) => ({
|
|
1688
|
+
return questions.filter((q) => response.values[q.id] !== void 0).map((q) => ({
|
|
1724
1689
|
questionId: q.id,
|
|
1725
1690
|
label: q.label,
|
|
1726
1691
|
type: q.type,
|
|
@@ -1873,10 +1838,10 @@ function ResponseViewerInner({
|
|
|
1873
1838
|
className: "h-7 text-xs",
|
|
1874
1839
|
onClick: () => {
|
|
1875
1840
|
const csvFilename = filename ? filename.replace(/\.\w+$/, ".csv") : "responses.csv";
|
|
1876
|
-
exportToCsv(schema,
|
|
1877
|
-
onExport?.("csv",
|
|
1841
|
+
exportToCsv(schema, filteredResponses, csvFilename, { dateFormat, columnLabels });
|
|
1842
|
+
onExport?.("csv", filteredResponses.length);
|
|
1878
1843
|
},
|
|
1879
|
-
disabled:
|
|
1844
|
+
disabled: filteredResponses.length === 0,
|
|
1880
1845
|
children: "Export CSV"
|
|
1881
1846
|
}
|
|
1882
1847
|
),
|
|
@@ -1888,10 +1853,10 @@ function ResponseViewerInner({
|
|
|
1888
1853
|
className: "h-7 text-xs",
|
|
1889
1854
|
onClick: () => {
|
|
1890
1855
|
const jsonFilename = filename ? filename.replace(/\.\w+$/, ".json") : "responses.json";
|
|
1891
|
-
exportToJson(
|
|
1892
|
-
onExport?.("json",
|
|
1856
|
+
exportToJson(filteredResponses, jsonFilename);
|
|
1857
|
+
onExport?.("json", filteredResponses.length);
|
|
1893
1858
|
},
|
|
1894
|
-
disabled:
|
|
1859
|
+
disabled: filteredResponses.length === 0,
|
|
1895
1860
|
children: "Export JSON"
|
|
1896
1861
|
}
|
|
1897
1862
|
)
|
|
@@ -2067,7 +2032,7 @@ function ResponseViewerInner({
|
|
|
2067
2032
|
response: selectedResponse,
|
|
2068
2033
|
fields: getFields(selectedResponse),
|
|
2069
2034
|
onBack: handleBack,
|
|
2070
|
-
onDelete: onDelete && selectedResponse.sessionToken ? () => handleDeleteSingle(selectedResponse.sessionToken) : void 0
|
|
2035
|
+
onDelete: onDelete && selectedResponse.sessionToken != null ? () => handleDeleteSingle(selectedResponse.sessionToken) : void 0
|
|
2071
2036
|
}
|
|
2072
2037
|
) }) : viewMode === "timeline" ? /* @__PURE__ */ jsx(
|
|
2073
2038
|
TimelineView,
|
|
@@ -2172,13 +2137,11 @@ function ResponseViewerInner({
|
|
|
2172
2137
|
}
|
|
2173
2138
|
);
|
|
2174
2139
|
}
|
|
2175
|
-
function
|
|
2140
|
+
function getAllQuestions(schema) {
|
|
2176
2141
|
const questions = [];
|
|
2177
2142
|
for (const section of schema.sections) {
|
|
2178
2143
|
for (const q of section.questions) {
|
|
2179
|
-
if (
|
|
2180
|
-
continue;
|
|
2181
|
-
}
|
|
2144
|
+
if (DISPLAY_ONLY_TYPES.has(q.type)) continue;
|
|
2182
2145
|
questions.push(q);
|
|
2183
2146
|
}
|
|
2184
2147
|
}
|