@squaredr/fieldcraft-pro 0.0.5 → 1.1.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 +48 -10
- package/README.md +37 -6
- package/dist/{chunk-MQGR25NG.mjs → chunk-7LQW5QYT.mjs} +1 -3
- package/dist/{chunk-APHGEYG4.mjs → chunk-CJBC3KWB.mjs} +156 -130
- package/dist/chunk-QQ4JZGTD.mjs +9 -0
- package/dist/chunk-RSU3X25P.mjs +976 -0
- package/dist/chunk-VECQKSWS.mjs +582 -0
- package/dist/form-builder/index.d.mts +2 -2
- package/dist/form-builder/index.d.ts +2 -2
- package/dist/form-builder/index.js +474 -123
- package/dist/form-builder/index.mjs +3 -1
- package/dist/index.d.mts +52 -3
- package/dist/index.d.ts +52 -3
- package/dist/index.js +1571 -474
- package/dist/index.mjs +7 -5
- package/dist/response-viewer/index.d.mts +4 -1
- package/dist/response-viewer/index.d.ts +4 -1
- package/dist/response-viewer/index.js +1155 -320
- package/dist/response-viewer/index.mjs +3 -1
- package/dist/styles.css +1 -1
- package/dist/theme-editor/index.js +324 -4
- package/dist/theme-editor/index.mjs +2 -1
- package/package.json +15 -14
- package/dist/chunk-NIKQEWPG.mjs +0 -467
- /package/{dist/theme-editor-styles.css → theme-editor/styles.css} +0 -0
|
@@ -1,78 +1,517 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var fieldcraftProLicense = require('@squaredr/fieldcraft-pro-license');
|
|
4
3
|
var react = require('react');
|
|
5
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var fieldcraftReact = require('@squaredr/fieldcraft-react');
|
|
6
|
+
var clsx = require('clsx');
|
|
7
|
+
var tailwindMerge = require('tailwind-merge');
|
|
6
8
|
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
9
|
+
// ../license/dist/index.mjs
|
|
10
|
+
var PRO_FEATURES = [
|
|
11
|
+
"SchemaEditor",
|
|
12
|
+
"ResponseViewer",
|
|
13
|
+
"ThemeEditor",
|
|
14
|
+
"FormBuilder",
|
|
15
|
+
"ApiManager",
|
|
16
|
+
"Analytics"
|
|
17
|
+
];
|
|
18
|
+
var ALL_FEATURES = [
|
|
19
|
+
...PRO_FEATURES,
|
|
20
|
+
"Telehealth"
|
|
21
|
+
];
|
|
22
|
+
var TIER_FEATURES = {
|
|
23
|
+
starter: ["SchemaEditor", "ResponseViewer"],
|
|
24
|
+
pro: PRO_FEATURES,
|
|
25
|
+
business: ALL_FEATURES,
|
|
26
|
+
enterprise: ALL_FEATURES
|
|
27
|
+
};
|
|
28
|
+
function tierHasFeature(tier, featureName) {
|
|
29
|
+
return TIER_FEATURES[tier]?.includes(featureName) ?? false;
|
|
30
|
+
}
|
|
31
|
+
var DEV_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
32
|
+
"localhost",
|
|
33
|
+
"127.0.0.1",
|
|
34
|
+
"0.0.0.0",
|
|
35
|
+
"[::1]",
|
|
36
|
+
"::1"
|
|
37
|
+
]);
|
|
38
|
+
var DEV_HOSTNAME_PATTERNS = [
|
|
39
|
+
/^192\.168\.\d+\.\d+$/,
|
|
40
|
+
// local network
|
|
41
|
+
/^10\.\d+\.\d+\.\d+$/,
|
|
42
|
+
// local network
|
|
43
|
+
/^172\.(1[6-9]|2\d|3[01])\.\d+\.\d+$/
|
|
44
|
+
// local network
|
|
45
|
+
];
|
|
46
|
+
var PRODUCTION_HOST_SUFFIXES = [
|
|
47
|
+
".vercel.app",
|
|
48
|
+
".netlify.app",
|
|
49
|
+
".netlify.com",
|
|
50
|
+
".herokuapp.com",
|
|
51
|
+
".fly.dev",
|
|
52
|
+
".railway.app",
|
|
53
|
+
".render.com",
|
|
54
|
+
".onrender.com",
|
|
55
|
+
".pages.dev",
|
|
56
|
+
// Cloudflare Pages
|
|
57
|
+
".workers.dev",
|
|
58
|
+
// Cloudflare Workers
|
|
59
|
+
".azurewebsites.net",
|
|
60
|
+
".web.app",
|
|
61
|
+
// Firebase
|
|
62
|
+
".firebaseapp.com",
|
|
63
|
+
".amplifyapp.com",
|
|
64
|
+
".surge.sh",
|
|
65
|
+
".github.io",
|
|
66
|
+
".gitlab.io"
|
|
67
|
+
];
|
|
68
|
+
function isDevHostname(hostname) {
|
|
69
|
+
if (DEV_HOSTNAMES.has(hostname)) return true;
|
|
70
|
+
for (const pattern of DEV_HOSTNAME_PATTERNS) {
|
|
71
|
+
if (pattern.test(hostname)) return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
function isKnownProductionHost(hostname) {
|
|
76
|
+
for (const suffix of PRODUCTION_HOST_SUFFIXES) {
|
|
77
|
+
if (hostname.endsWith(suffix)) return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
function hasRealTLD(hostname) {
|
|
82
|
+
return /\.[a-z]{2,}$/i.test(hostname) && !isDevHostname(hostname);
|
|
83
|
+
}
|
|
84
|
+
function isProductionEnvironment() {
|
|
85
|
+
const _proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
86
|
+
if (_proc?.env) {
|
|
87
|
+
const nodeEnv = _proc.env.NODE_ENV;
|
|
88
|
+
if (nodeEnv === "production") return true;
|
|
89
|
+
if (nodeEnv === "development" || nodeEnv === "test") return false;
|
|
90
|
+
}
|
|
91
|
+
if (typeof window !== "undefined" && window.location) {
|
|
92
|
+
const { hostname, protocol, port } = window.location;
|
|
93
|
+
if (protocol === "file:") return false;
|
|
94
|
+
if (isDevHostname(hostname)) return false;
|
|
95
|
+
if (port && port !== "443" && port !== "80") return false;
|
|
96
|
+
if (isKnownProductionHost(hostname)) return true;
|
|
97
|
+
if (protocol === "https:" && hasRealTLD(hostname)) return true;
|
|
98
|
+
if (hasRealTLD(hostname)) return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
var defaultContext = { status: "validating", tier: null };
|
|
103
|
+
var LicenseCtx = react.createContext(defaultContext);
|
|
104
|
+
function useLicense() {
|
|
105
|
+
return react.useContext(LicenseCtx);
|
|
106
|
+
}
|
|
107
|
+
function UnlicensedOverlay({ featureName, reason, children }) {
|
|
108
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", minHeight: "200px" }, children: [
|
|
109
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { pointerEvents: "none", userSelect: "none", filter: "blur(2px)" }, children }),
|
|
110
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
111
|
+
"div",
|
|
112
|
+
{
|
|
113
|
+
style: {
|
|
114
|
+
position: "absolute",
|
|
115
|
+
inset: 0,
|
|
116
|
+
zIndex: 2147483647,
|
|
117
|
+
display: "flex",
|
|
118
|
+
flexDirection: "column",
|
|
119
|
+
alignItems: "center",
|
|
120
|
+
justifyContent: "center",
|
|
121
|
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
122
|
+
backdropFilter: "blur(4px)",
|
|
123
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
124
|
+
padding: "32px"
|
|
125
|
+
},
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
128
|
+
"div",
|
|
27
129
|
{
|
|
28
|
-
onClick: () => onRowClick?.(response),
|
|
29
130
|
style: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
131
|
+
position: "absolute",
|
|
132
|
+
inset: 0,
|
|
133
|
+
overflow: "hidden",
|
|
134
|
+
pointerEvents: "none",
|
|
135
|
+
zIndex: 0
|
|
35
136
|
},
|
|
36
|
-
|
|
37
|
-
|
|
137
|
+
children: Array.from({ length: 12 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
138
|
+
"div",
|
|
139
|
+
{
|
|
140
|
+
style: {
|
|
141
|
+
position: "absolute",
|
|
142
|
+
top: `${i * 80}px`,
|
|
143
|
+
left: "-50%",
|
|
144
|
+
right: "-50%",
|
|
145
|
+
textAlign: "center",
|
|
146
|
+
transform: "rotate(-30deg)",
|
|
147
|
+
fontSize: "18px",
|
|
148
|
+
fontWeight: 700,
|
|
149
|
+
color: "rgba(255, 255, 255, 0.06)",
|
|
150
|
+
letterSpacing: "8px",
|
|
151
|
+
textTransform: "uppercase",
|
|
152
|
+
whiteSpace: "nowrap",
|
|
153
|
+
userSelect: "none"
|
|
154
|
+
},
|
|
155
|
+
children: "UNLICENSED \u2022 FIELDCRAFT PRO \u2022 UNLICENSED \u2022 FIELDCRAFT PRO \u2022 UNLICENSED \u2022 FIELDCRAFT PRO"
|
|
156
|
+
},
|
|
157
|
+
i
|
|
158
|
+
))
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
162
|
+
"div",
|
|
163
|
+
{
|
|
164
|
+
style: {
|
|
165
|
+
position: "relative",
|
|
166
|
+
zIndex: 1,
|
|
167
|
+
textAlign: "center",
|
|
168
|
+
maxWidth: "420px"
|
|
38
169
|
},
|
|
39
170
|
children: [
|
|
40
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41
|
-
|
|
42
|
-
|
|
171
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
172
|
+
"div",
|
|
173
|
+
{
|
|
174
|
+
style: {
|
|
175
|
+
width: "64px",
|
|
176
|
+
height: "64px",
|
|
177
|
+
margin: "0 auto 16px",
|
|
178
|
+
borderRadius: "50%",
|
|
179
|
+
backgroundColor: "rgba(239, 68, 68, 0.2)",
|
|
180
|
+
border: "2px solid rgba(239, 68, 68, 0.5)",
|
|
181
|
+
display: "flex",
|
|
182
|
+
alignItems: "center",
|
|
183
|
+
justifyContent: "center",
|
|
184
|
+
fontSize: "28px"
|
|
185
|
+
},
|
|
186
|
+
children: "\u{1F6E1}"
|
|
187
|
+
}
|
|
188
|
+
),
|
|
189
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
style: {
|
|
193
|
+
fontSize: "22px",
|
|
194
|
+
fontWeight: 700,
|
|
195
|
+
color: "#ffffff",
|
|
196
|
+
marginBottom: "8px",
|
|
197
|
+
letterSpacing: "-0.02em"
|
|
198
|
+
},
|
|
199
|
+
children: "License Required"
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
203
|
+
"div",
|
|
204
|
+
{
|
|
205
|
+
style: {
|
|
206
|
+
fontSize: "15px",
|
|
207
|
+
fontWeight: 600,
|
|
208
|
+
color: "#f87171",
|
|
209
|
+
marginBottom: "6px"
|
|
210
|
+
},
|
|
211
|
+
children: featureName
|
|
212
|
+
}
|
|
213
|
+
),
|
|
214
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
215
|
+
"div",
|
|
216
|
+
{
|
|
217
|
+
style: {
|
|
218
|
+
fontSize: "14px",
|
|
219
|
+
color: "#d1d5db",
|
|
220
|
+
marginBottom: "24px",
|
|
221
|
+
lineHeight: "1.5"
|
|
222
|
+
},
|
|
223
|
+
children: reason
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "10px", justifyContent: "center", flexWrap: "wrap" }, children: [
|
|
227
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
228
|
+
"a",
|
|
229
|
+
{
|
|
230
|
+
href: "https://squaredr.tech/products/fieldcraft/admin-pro#pricing",
|
|
231
|
+
target: "_blank",
|
|
232
|
+
rel: "noopener noreferrer",
|
|
233
|
+
style: {
|
|
234
|
+
display: "inline-block",
|
|
235
|
+
padding: "12px 24px",
|
|
236
|
+
fontSize: "14px",
|
|
237
|
+
fontWeight: 600,
|
|
238
|
+
color: "#ffffff",
|
|
239
|
+
backgroundColor: "#2563eb",
|
|
240
|
+
borderRadius: "8px",
|
|
241
|
+
textDecoration: "none",
|
|
242
|
+
boxShadow: "0 4px 14px rgba(37, 99, 235, 0.4)"
|
|
243
|
+
},
|
|
244
|
+
children: "Get a License"
|
|
245
|
+
}
|
|
246
|
+
),
|
|
247
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
248
|
+
"a",
|
|
249
|
+
{
|
|
250
|
+
href: "https://discord.gg/zMxdu5UVW",
|
|
251
|
+
target: "_blank",
|
|
252
|
+
rel: "noopener noreferrer",
|
|
253
|
+
style: {
|
|
254
|
+
display: "inline-block",
|
|
255
|
+
padding: "12px 24px",
|
|
256
|
+
fontSize: "14px",
|
|
257
|
+
fontWeight: 600,
|
|
258
|
+
color: "#93c5fd",
|
|
259
|
+
backgroundColor: "rgba(37, 99, 235, 0.15)",
|
|
260
|
+
border: "1px solid rgba(37, 99, 235, 0.4)",
|
|
261
|
+
borderRadius: "8px",
|
|
262
|
+
textDecoration: "none"
|
|
263
|
+
},
|
|
264
|
+
children: "Join Discord"
|
|
265
|
+
}
|
|
266
|
+
)
|
|
267
|
+
] }),
|
|
268
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
269
|
+
"div",
|
|
270
|
+
{
|
|
271
|
+
style: {
|
|
272
|
+
fontSize: "12px",
|
|
273
|
+
color: "#6b7280",
|
|
274
|
+
marginTop: "16px"
|
|
275
|
+
},
|
|
276
|
+
children: [
|
|
277
|
+
"This component works freely in development.",
|
|
278
|
+
" ",
|
|
279
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
280
|
+
"a",
|
|
281
|
+
{
|
|
282
|
+
href: "https://squaredr.tech/products/fieldcraft/docs/pro/license",
|
|
283
|
+
target: "_blank",
|
|
284
|
+
rel: "noopener noreferrer",
|
|
285
|
+
style: { color: "#9ca3af", textDecoration: "underline" },
|
|
286
|
+
children: "Learn more"
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
)
|
|
43
292
|
]
|
|
44
|
-
},
|
|
45
|
-
response.sessionToken || idx
|
|
46
|
-
)),
|
|
47
|
-
responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48
|
-
"td",
|
|
49
|
-
{
|
|
50
|
-
colSpan: questions.length + 2,
|
|
51
|
-
style: { ...tdStyle, textAlign: "center", color: "var(--muted-foreground, #8a8a95)", padding: "32px" },
|
|
52
|
-
children: "No responses yet"
|
|
53
293
|
}
|
|
54
|
-
)
|
|
55
|
-
]
|
|
56
|
-
|
|
294
|
+
)
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
)
|
|
298
|
+
] });
|
|
299
|
+
}
|
|
300
|
+
function requireLicense(Component, featureName) {
|
|
301
|
+
function LicenseGated(props) {
|
|
302
|
+
const { status, tier } = useLicense();
|
|
303
|
+
const isProduction = isProductionEnvironment();
|
|
304
|
+
if (!isProduction) {
|
|
305
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props });
|
|
306
|
+
}
|
|
307
|
+
if (status === "validating") {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
const isLicensed = status === "valid" && tier != null && tierHasFeature(tier, featureName);
|
|
311
|
+
if (isLicensed) {
|
|
312
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props });
|
|
57
313
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
314
|
+
let reason = "A valid license is required for production use.";
|
|
315
|
+
if (status === "invalid") {
|
|
316
|
+
reason = "Invalid license key. Please check your license key and try again.";
|
|
317
|
+
} else if (status === "expired") {
|
|
318
|
+
reason = "Your license has expired. Please renew to continue using this component in production.";
|
|
319
|
+
} else if (status === "revoked") {
|
|
320
|
+
reason = "This license key has been revoked. Please contact support or purchase a new license.";
|
|
321
|
+
} else if (status === "domain_mismatch") {
|
|
322
|
+
reason = "This license key is registered to a different domain. Each key can only be used on one production domain. Please purchase a new license for this domain.";
|
|
323
|
+
} else if (!tier || !tierHasFeature(tier, featureName)) {
|
|
324
|
+
reason = "Your license does not include this feature. Please upgrade your plan.";
|
|
325
|
+
}
|
|
326
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UnlicensedOverlay, { featureName, reason, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props }) });
|
|
327
|
+
}
|
|
328
|
+
LicenseGated.displayName = `requireLicense(${Component.displayName || Component.name || "Component"})`;
|
|
329
|
+
return LicenseGated;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/response-viewer/clinical-display-data.ts
|
|
333
|
+
var BODY_REGION_LABELS = {
|
|
334
|
+
head: "Head",
|
|
335
|
+
neck: "Neck",
|
|
336
|
+
"left-shoulder": "Left Shoulder",
|
|
337
|
+
"right-shoulder": "Right Shoulder",
|
|
338
|
+
chest: "Chest",
|
|
339
|
+
abdomen: "Abdomen",
|
|
340
|
+
"left-upper-arm": "Left Upper Arm",
|
|
341
|
+
"right-upper-arm": "Right Upper Arm",
|
|
342
|
+
"left-forearm": "Left Forearm",
|
|
343
|
+
"right-forearm": "Right Forearm",
|
|
344
|
+
"left-hand": "Left Hand",
|
|
345
|
+
"right-hand": "Right Hand",
|
|
346
|
+
pelvis: "Pelvis",
|
|
347
|
+
"left-thigh": "Left Thigh",
|
|
348
|
+
"right-thigh": "Right Thigh",
|
|
349
|
+
"left-knee": "Left Knee",
|
|
350
|
+
"right-knee": "Right Knee",
|
|
351
|
+
"left-shin": "Left Shin",
|
|
352
|
+
"right-shin": "Right Shin",
|
|
353
|
+
"left-foot": "Left Foot",
|
|
354
|
+
"right-foot": "Right Foot"
|
|
355
|
+
};
|
|
356
|
+
var PAIN_FACE_LABELS = [
|
|
357
|
+
{ score: 0, label: "No Hurt", color: "#22c55e" },
|
|
358
|
+
{ score: 2, label: "Hurts Little Bit", color: "#84cc16" },
|
|
359
|
+
{ score: 4, label: "Hurts Little More", color: "#eab308" },
|
|
360
|
+
{ score: 6, label: "Hurts Even More", color: "#f97316" },
|
|
361
|
+
{ score: 8, label: "Hurts Whole Lot", color: "#ef4444" },
|
|
362
|
+
{ score: 10, label: "Hurts Worst", color: "#dc2626" }
|
|
363
|
+
];
|
|
364
|
+
function getPainLabel(score) {
|
|
365
|
+
const face = PAIN_FACE_LABELS.reduce(
|
|
366
|
+
(closest, f) => closest == null || Math.abs(f.score - score) < Math.abs(closest.score - score) ? f : closest,
|
|
367
|
+
void 0
|
|
368
|
+
);
|
|
369
|
+
return face;
|
|
370
|
+
}
|
|
371
|
+
var SEVERITY_COLORS = {
|
|
372
|
+
mild: "#eab308",
|
|
373
|
+
moderate: "#f97316",
|
|
374
|
+
severe: "#ef4444",
|
|
375
|
+
"life-threatening": "#991b1b"
|
|
376
|
+
};
|
|
377
|
+
var BMI_CATEGORIES = [
|
|
378
|
+
{ label: "Underweight", max: 18.5, color: "#3b82f6" },
|
|
379
|
+
{ label: "Normal", max: 25, color: "#22c55e" },
|
|
380
|
+
{ label: "Overweight", max: 30, color: "#eab308" },
|
|
381
|
+
{ label: "Obese", max: Infinity, color: "#ef4444" }
|
|
382
|
+
];
|
|
383
|
+
function getBmiCategory(bmi) {
|
|
384
|
+
return BMI_CATEGORIES.find((c) => bmi < c.max) ?? BMI_CATEGORIES[BMI_CATEGORIES.length - 1];
|
|
385
|
+
}
|
|
386
|
+
var INSTRUMENT_DISPLAY_NAMES = {
|
|
387
|
+
phq9: "PHQ-9",
|
|
388
|
+
phq2: "PHQ-2",
|
|
389
|
+
gad7: "GAD-7",
|
|
390
|
+
gad2: "GAD-2",
|
|
391
|
+
"audit-c": "AUDIT-C",
|
|
392
|
+
dast10: "DAST-10",
|
|
393
|
+
cssrs: "C-SSRS",
|
|
394
|
+
epds: "EPDS",
|
|
395
|
+
isi: "ISI",
|
|
396
|
+
pcl5: "PCL-5",
|
|
397
|
+
psc17: "PSC-17"
|
|
67
398
|
};
|
|
68
|
-
var
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
399
|
+
var SCORE_THRESHOLDS = {
|
|
400
|
+
phq9: [
|
|
401
|
+
{ min: 0, max: 4, label: "Minimal", color: "#22c55e" },
|
|
402
|
+
{ min: 5, max: 9, label: "Mild", color: "#eab308" },
|
|
403
|
+
{ min: 10, max: 14, label: "Moderate", color: "#f97316" },
|
|
404
|
+
{ min: 15, max: 19, label: "Moderately Severe", color: "#ef4444" },
|
|
405
|
+
{ min: 20, max: 27, label: "Severe", color: "#dc2626" }
|
|
406
|
+
],
|
|
407
|
+
phq2: [
|
|
408
|
+
{ min: 0, max: 2, label: "Negative Screen", color: "#22c55e" },
|
|
409
|
+
{ min: 3, max: 6, label: "Positive Screen", color: "#ef4444" }
|
|
410
|
+
],
|
|
411
|
+
gad7: [
|
|
412
|
+
{ min: 0, max: 4, label: "Minimal", color: "#22c55e" },
|
|
413
|
+
{ min: 5, max: 9, label: "Mild", color: "#eab308" },
|
|
414
|
+
{ min: 10, max: 14, label: "Moderate", color: "#f97316" },
|
|
415
|
+
{ min: 15, max: 21, label: "Severe", color: "#ef4444" }
|
|
416
|
+
],
|
|
417
|
+
gad2: [
|
|
418
|
+
{ min: 0, max: 2, label: "Negative Screen", color: "#22c55e" },
|
|
419
|
+
{ min: 3, max: 6, label: "Positive Screen", color: "#ef4444" }
|
|
420
|
+
],
|
|
421
|
+
"audit-c": [
|
|
422
|
+
{ min: 0, max: 3, label: "Low Risk", color: "#22c55e" },
|
|
423
|
+
{ min: 4, max: 7, label: "At Risk", color: "#eab308" },
|
|
424
|
+
{ min: 8, max: 12, label: "High Risk", color: "#ef4444" }
|
|
425
|
+
],
|
|
426
|
+
dast10: [
|
|
427
|
+
{ min: 0, max: 0, label: "No Problems", color: "#22c55e" },
|
|
428
|
+
{ min: 1, max: 2, label: "Low Level", color: "#eab308" },
|
|
429
|
+
{ min: 3, max: 5, label: "Moderate Level", color: "#f97316" },
|
|
430
|
+
{ min: 6, max: 8, label: "Substantial Level", color: "#ef4444" },
|
|
431
|
+
{ min: 9, max: 10, label: "Severe Level", color: "#dc2626" }
|
|
432
|
+
],
|
|
433
|
+
cssrs: [
|
|
434
|
+
{ min: 0, max: 0, label: "No Risk Identified", color: "#22c55e" },
|
|
435
|
+
{ min: 1, max: 1, label: "Wish to be Dead", color: "#eab308" },
|
|
436
|
+
{ min: 2, max: 2, label: "Non-specific Active Suicidal Thoughts", color: "#f97316" },
|
|
437
|
+
{ min: 3, max: 3, label: "Active Suicidal Ideation", color: "#ef4444" },
|
|
438
|
+
{ min: 4, max: 4, label: "Active Ideation with Intent", color: "#dc2626" },
|
|
439
|
+
{ min: 5, max: 5, label: "Active Ideation with Plan", color: "#991b1b" },
|
|
440
|
+
{ min: 6, max: 6, label: "Imminent Risk", color: "#7f1d1d" }
|
|
441
|
+
],
|
|
442
|
+
epds: [
|
|
443
|
+
{ min: 0, max: 8, label: "Low Likelihood", color: "#22c55e" },
|
|
444
|
+
{ min: 9, max: 11, label: "Possible Depression", color: "#eab308" },
|
|
445
|
+
{ min: 12, max: 13, label: "Fairly High Possibility", color: "#f97316" },
|
|
446
|
+
{ min: 14, max: 30, label: "Probable Depression", color: "#ef4444" }
|
|
447
|
+
],
|
|
448
|
+
isi: [
|
|
449
|
+
{ min: 0, max: 7, label: "No Clinically Significant Insomnia", color: "#22c55e" },
|
|
450
|
+
{ min: 8, max: 14, label: "Subthreshold Insomnia", color: "#eab308" },
|
|
451
|
+
{ min: 15, max: 21, label: "Clinical Insomnia (Moderate)", color: "#f97316" },
|
|
452
|
+
{ min: 22, max: 28, label: "Clinical Insomnia (Severe)", color: "#ef4444" }
|
|
453
|
+
],
|
|
454
|
+
pcl5: [
|
|
455
|
+
{ min: 0, max: 30, label: "Below Threshold", color: "#22c55e" },
|
|
456
|
+
{ min: 31, max: 80, label: "Probable PTSD", color: "#ef4444" }
|
|
457
|
+
],
|
|
458
|
+
psc17: [
|
|
459
|
+
{ min: 0, max: 14, label: "No Significant Concerns", color: "#22c55e" },
|
|
460
|
+
{ min: 15, max: 34, label: "Significant Concerns", color: "#ef4444" }
|
|
461
|
+
]
|
|
75
462
|
};
|
|
463
|
+
function getScoreSeverity(instrumentKey, score) {
|
|
464
|
+
const thresholds = SCORE_THRESHOLDS[instrumentKey];
|
|
465
|
+
if (!thresholds) return void 0;
|
|
466
|
+
return thresholds.find((t) => score >= t.min && score <= t.max);
|
|
467
|
+
}
|
|
468
|
+
function ResponseTable({ schema, responses, onRowClick }) {
|
|
469
|
+
const questions = getAllQuestions(schema);
|
|
470
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-[13px]", children: [
|
|
471
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "bg-muted", children: [
|
|
472
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Submitted" }),
|
|
473
|
+
questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
474
|
+
"th",
|
|
475
|
+
{
|
|
476
|
+
className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap",
|
|
477
|
+
children: q.label
|
|
478
|
+
},
|
|
479
|
+
q.id
|
|
480
|
+
)),
|
|
481
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Score" })
|
|
482
|
+
] }) }),
|
|
483
|
+
/* @__PURE__ */ jsxRuntime.jsxs("tbody", { children: [
|
|
484
|
+
responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
485
|
+
"tr",
|
|
486
|
+
{
|
|
487
|
+
onClick: () => onRowClick?.(response),
|
|
488
|
+
className: onRowClick ? "cursor-pointer border-b border-border hover:bg-accent transition-colors" : "border-b border-border",
|
|
489
|
+
children: [
|
|
490
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap", children: new Date(response.submittedAt).toLocaleString() }),
|
|
491
|
+
questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
492
|
+
"td",
|
|
493
|
+
{
|
|
494
|
+
className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap",
|
|
495
|
+
children: formatCellValue(response.values[q.id], q.type)
|
|
496
|
+
},
|
|
497
|
+
q.id
|
|
498
|
+
)),
|
|
499
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap", children: response.totalScore ?? "\u2014" })
|
|
500
|
+
]
|
|
501
|
+
},
|
|
502
|
+
response.sessionToken || idx
|
|
503
|
+
)),
|
|
504
|
+
responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
505
|
+
"td",
|
|
506
|
+
{
|
|
507
|
+
colSpan: questions.length + 2,
|
|
508
|
+
className: "px-3 py-8 text-center text-muted-foreground",
|
|
509
|
+
children: "No responses yet"
|
|
510
|
+
}
|
|
511
|
+
) })
|
|
512
|
+
] })
|
|
513
|
+
] }) });
|
|
514
|
+
}
|
|
76
515
|
function getAllQuestions(schema) {
|
|
77
516
|
const questions = [];
|
|
78
517
|
for (const section of schema.sections) {
|
|
@@ -85,8 +524,69 @@ function getAllQuestions(schema) {
|
|
|
85
524
|
}
|
|
86
525
|
return questions;
|
|
87
526
|
}
|
|
88
|
-
function formatCellValue(value) {
|
|
527
|
+
function formatCellValue(value, type) {
|
|
89
528
|
if (value == null) return "\u2014";
|
|
529
|
+
if (type) {
|
|
530
|
+
switch (type) {
|
|
531
|
+
case "vitals_entry": {
|
|
532
|
+
if (typeof value !== "object" || value === null) break;
|
|
533
|
+
const v = value;
|
|
534
|
+
const parts = [];
|
|
535
|
+
if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
|
|
536
|
+
if (v.heartRate) parts.push(`HR ${v.heartRate}`);
|
|
537
|
+
if (v.temperature) parts.push(`${v.temperature}\xB0F`);
|
|
538
|
+
if (v.oxygenSaturation) parts.push(`SpO\u2082 ${v.oxygenSaturation}%`);
|
|
539
|
+
return parts.length > 0 ? parts.join(", ") : "\u2014";
|
|
540
|
+
}
|
|
541
|
+
case "medication_list":
|
|
542
|
+
if (Array.isArray(value)) return `${value.length} medication${value.length !== 1 ? "s" : ""}`;
|
|
543
|
+
break;
|
|
544
|
+
case "allergy_list":
|
|
545
|
+
if (Array.isArray(value)) return `${value.length} allerg${value.length !== 1 ? "ies" : "y"}`;
|
|
546
|
+
break;
|
|
547
|
+
case "body_diagram":
|
|
548
|
+
if (Array.isArray(value)) {
|
|
549
|
+
const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
|
|
550
|
+
return labels.join(", ");
|
|
551
|
+
}
|
|
552
|
+
break;
|
|
553
|
+
case "pain_scale":
|
|
554
|
+
if (typeof value === "number") return `${value}/10`;
|
|
555
|
+
break;
|
|
556
|
+
case "bmi_calculator": {
|
|
557
|
+
if (typeof value !== "object" || value === null) break;
|
|
558
|
+
const d = value;
|
|
559
|
+
if (d.bmi != null) return `BMI ${d.bmi}`;
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
case "payment": {
|
|
563
|
+
if (typeof value !== "object" || value === null) break;
|
|
564
|
+
const p = value;
|
|
565
|
+
const status = p.status;
|
|
566
|
+
return status ? status.charAt(0).toUpperCase() + status.slice(1) : "\u2014";
|
|
567
|
+
}
|
|
568
|
+
case "insurance_card": {
|
|
569
|
+
if (typeof value !== "object" || value === null) break;
|
|
570
|
+
const ins = value;
|
|
571
|
+
const parts = [ins.carrierId, ins.planName].filter(Boolean);
|
|
572
|
+
return parts.length > 0 ? parts.join(" \u2014 ") : "Card uploaded";
|
|
573
|
+
}
|
|
574
|
+
case "legal_name": {
|
|
575
|
+
if (typeof value !== "object" || value === null) break;
|
|
576
|
+
const n = value;
|
|
577
|
+
return [n.first, n.last].filter(Boolean).join(" ") || "\u2014";
|
|
578
|
+
}
|
|
579
|
+
case "address": {
|
|
580
|
+
if (typeof value !== "object" || value === null) break;
|
|
581
|
+
const a = value;
|
|
582
|
+
return [a.city, a.state].filter(Boolean).join(", ") || "\u2014";
|
|
583
|
+
}
|
|
584
|
+
case "consent":
|
|
585
|
+
return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
|
|
586
|
+
case "signature":
|
|
587
|
+
return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "\u2014";
|
|
588
|
+
}
|
|
589
|
+
}
|
|
90
590
|
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
91
591
|
if (Array.isArray(value)) return value.join(", ");
|
|
92
592
|
if (typeof value === "object") return JSON.stringify(value);
|
|
@@ -97,238 +597,573 @@ function ResponseCard({ response, fields, onClick }) {
|
|
|
97
597
|
"div",
|
|
98
598
|
{
|
|
99
599
|
onClick,
|
|
100
|
-
|
|
101
|
-
border: "1px solid var(--border, #1c1c20)",
|
|
102
|
-
borderRadius: "8px",
|
|
103
|
-
padding: "16px",
|
|
104
|
-
backgroundColor: "var(--card, #111113)",
|
|
105
|
-
cursor: onClick ? "pointer" : "default",
|
|
106
|
-
fontFamily: "inherit",
|
|
107
|
-
transition: "box-shadow 0.15s"
|
|
108
|
-
},
|
|
109
|
-
onMouseEnter: (e) => {
|
|
110
|
-
if (onClick) e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.3)";
|
|
111
|
-
},
|
|
112
|
-
onMouseLeave: (e) => {
|
|
113
|
-
e.currentTarget.style.boxShadow = "none";
|
|
114
|
-
},
|
|
600
|
+
className: onClick ? "border border-border rounded-lg p-4 bg-card cursor-pointer transition-shadow hover:shadow-md" : "border border-border rounded-lg p-4 bg-card",
|
|
115
601
|
children: [
|
|
116
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
117
|
-
"
|
|
118
|
-
{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
children: [
|
|
127
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(response.submittedAt).toLocaleString() }),
|
|
128
|
-
response.completionTimeMs != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
129
|
-
Math.round(response.completionTimeMs / 1e3),
|
|
130
|
-
"s"
|
|
131
|
-
] })
|
|
132
|
-
]
|
|
133
|
-
}
|
|
134
|
-
),
|
|
135
|
-
fields.slice(0, 4).map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "8px" }, children: [
|
|
136
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "11px", color: "var(--muted-foreground, #8a8a95)", marginBottom: "2px" }, children: field.label }),
|
|
137
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "13px", color: "var(--foreground, #e8e8ea)" }, children: formatValue(field.value) })
|
|
602
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between mb-3 text-xs text-muted-foreground", children: [
|
|
603
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(response.submittedAt).toLocaleString() }),
|
|
604
|
+
response.completionTimeMs != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
605
|
+
Math.round(response.completionTimeMs / 1e3),
|
|
606
|
+
"s"
|
|
607
|
+
] })
|
|
608
|
+
] }),
|
|
609
|
+
fields.slice(0, 4).map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2", children: [
|
|
610
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-muted-foreground mb-0.5", children: field.label }),
|
|
611
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[13px] text-foreground", children: formatCardValue(field.value, field.type) })
|
|
138
612
|
] }, field.questionId)),
|
|
139
|
-
fields.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
613
|
+
fields.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mt-2", children: [
|
|
140
614
|
"+",
|
|
141
615
|
fields.length - 4,
|
|
142
616
|
" more fields"
|
|
143
617
|
] }),
|
|
144
|
-
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
145
|
-
"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
marginTop: "12px",
|
|
149
|
-
paddingTop: "8px",
|
|
150
|
-
borderTop: "1px solid var(--border, #1c1c20)",
|
|
151
|
-
fontSize: "13px",
|
|
152
|
-
fontWeight: 600,
|
|
153
|
-
color: "var(--foreground, #e8e8ea)"
|
|
154
|
-
},
|
|
155
|
-
children: [
|
|
156
|
-
"Score: ",
|
|
157
|
-
response.totalScore
|
|
158
|
-
]
|
|
159
|
-
}
|
|
160
|
-
)
|
|
618
|
+
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 pt-2 border-t border-border text-[13px] font-semibold text-foreground", children: [
|
|
619
|
+
"Score: ",
|
|
620
|
+
response.totalScore
|
|
621
|
+
] })
|
|
161
622
|
]
|
|
162
623
|
}
|
|
163
624
|
);
|
|
164
625
|
}
|
|
165
|
-
function
|
|
626
|
+
function formatCardValue(value, type) {
|
|
166
627
|
if (value == null) return "\u2014";
|
|
628
|
+
if (type) {
|
|
629
|
+
switch (type) {
|
|
630
|
+
case "vitals_entry": {
|
|
631
|
+
if (typeof value !== "object" || value === null) break;
|
|
632
|
+
const v = value;
|
|
633
|
+
const parts = [];
|
|
634
|
+
if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
|
|
635
|
+
if (v.heartRate) parts.push(`HR ${v.heartRate}`);
|
|
636
|
+
if (v.temperature) parts.push(`${v.temperature}\xB0F`);
|
|
637
|
+
if (v.oxygenSaturation) parts.push(`SpO\u2082 ${v.oxygenSaturation}%`);
|
|
638
|
+
return parts.length > 0 ? parts.join(", ") : "\u2014";
|
|
639
|
+
}
|
|
640
|
+
case "medication_list":
|
|
641
|
+
if (Array.isArray(value)) return `${value.length} medication${value.length !== 1 ? "s" : ""}`;
|
|
642
|
+
break;
|
|
643
|
+
case "allergy_list":
|
|
644
|
+
if (Array.isArray(value)) return `${value.length} allerg${value.length !== 1 ? "ies" : "y"}`;
|
|
645
|
+
break;
|
|
646
|
+
case "body_diagram":
|
|
647
|
+
if (Array.isArray(value)) {
|
|
648
|
+
const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
|
|
649
|
+
return labels.join(", ");
|
|
650
|
+
}
|
|
651
|
+
break;
|
|
652
|
+
case "pain_scale":
|
|
653
|
+
if (typeof value === "number") return `${value}/10`;
|
|
654
|
+
break;
|
|
655
|
+
case "bmi_calculator": {
|
|
656
|
+
if (typeof value !== "object" || value === null) break;
|
|
657
|
+
const d = value;
|
|
658
|
+
if (d.bmi != null) return `BMI ${d.bmi}`;
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
case "payment": {
|
|
662
|
+
if (typeof value !== "object" || value === null) break;
|
|
663
|
+
const p = value;
|
|
664
|
+
const status = p.status;
|
|
665
|
+
return status ? status.charAt(0).toUpperCase() + status.slice(1) : "\u2014";
|
|
666
|
+
}
|
|
667
|
+
case "insurance_card": {
|
|
668
|
+
if (typeof value !== "object" || value === null) break;
|
|
669
|
+
const ins = value;
|
|
670
|
+
const parts = [ins.carrierId, ins.planName].filter(Boolean);
|
|
671
|
+
return parts.length > 0 ? parts.join(" \u2014 ") : "Card uploaded";
|
|
672
|
+
}
|
|
673
|
+
case "legal_name": {
|
|
674
|
+
if (typeof value !== "object" || value === null) break;
|
|
675
|
+
const n = value;
|
|
676
|
+
return [n.first, n.last].filter(Boolean).join(" ") || "\u2014";
|
|
677
|
+
}
|
|
678
|
+
case "address": {
|
|
679
|
+
if (typeof value !== "object" || value === null) break;
|
|
680
|
+
const a = value;
|
|
681
|
+
return [a.city, a.state].filter(Boolean).join(", ") || "\u2014";
|
|
682
|
+
}
|
|
683
|
+
case "consent":
|
|
684
|
+
return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
|
|
685
|
+
case "signature":
|
|
686
|
+
return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "\u2014";
|
|
687
|
+
}
|
|
688
|
+
}
|
|
167
689
|
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
168
|
-
if (Array.isArray(value)) return value.map(
|
|
690
|
+
if (Array.isArray(value)) return value.map(String).join(", ");
|
|
169
691
|
if (typeof value === "object") return JSON.stringify(value);
|
|
170
692
|
return String(value);
|
|
171
693
|
}
|
|
172
694
|
function ResponseDetail({ response, fields, onBack }) {
|
|
173
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
174
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
175
|
-
"
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
onBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
187
|
-
"button",
|
|
188
|
-
{
|
|
189
|
-
type: "button",
|
|
190
|
-
onClick: onBack,
|
|
191
|
-
style: {
|
|
192
|
-
padding: "4px 10px",
|
|
193
|
-
fontSize: "13px",
|
|
194
|
-
color: "var(--secondary-foreground, #e8e8ea)",
|
|
195
|
-
backgroundColor: "var(--secondary, #17171a)",
|
|
196
|
-
border: "1px solid var(--border, #1c1c20)",
|
|
197
|
-
borderRadius: "6px",
|
|
198
|
-
cursor: "pointer"
|
|
199
|
-
},
|
|
200
|
-
children: "Back"
|
|
201
|
-
}
|
|
202
|
-
),
|
|
203
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
204
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "14px", fontWeight: 600, color: "var(--foreground, #e8e8ea)" }, children: "Response Detail" }),
|
|
205
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "var(--muted-foreground, #8a8a95)" }, children: [
|
|
206
|
-
"Submitted ",
|
|
207
|
-
new Date(response.submittedAt).toLocaleString(),
|
|
208
|
-
response.completionTimeMs != null && ` \u2014 ${Math.round(response.completionTimeMs / 1e3)}s`
|
|
209
|
-
] })
|
|
210
|
-
] })
|
|
211
|
-
]
|
|
212
|
-
}
|
|
213
|
-
),
|
|
214
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
695
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
696
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-5 pb-3 border-b border-border", children: [
|
|
697
|
+
onBack && /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "outline", size: "sm", onClick: onBack, children: "Back" }),
|
|
698
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
699
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-foreground", children: "Response Detail" }),
|
|
700
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
701
|
+
"Submitted ",
|
|
702
|
+
new Date(response.submittedAt).toLocaleString(),
|
|
703
|
+
response.completionTimeMs != null && ` \u2014 ${Math.round(response.completionTimeMs / 1e3)}s`
|
|
704
|
+
] })
|
|
705
|
+
] })
|
|
706
|
+
] }),
|
|
707
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-4", children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
215
708
|
"div",
|
|
216
709
|
{
|
|
217
|
-
|
|
218
|
-
padding: "12px",
|
|
219
|
-
backgroundColor: "var(--muted, #111113)",
|
|
220
|
-
borderRadius: "6px"
|
|
221
|
-
},
|
|
710
|
+
className: "p-3 bg-muted rounded-md",
|
|
222
711
|
children: [
|
|
223
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
712
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mb-1", children: [
|
|
224
713
|
field.label,
|
|
225
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", {
|
|
714
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-2 text-[11px] text-muted-foreground", children: [
|
|
226
715
|
"(",
|
|
227
716
|
field.type,
|
|
228
717
|
")"
|
|
229
718
|
] })
|
|
230
719
|
] }),
|
|
231
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-foreground", children: renderFieldValue(field) })
|
|
232
721
|
]
|
|
233
722
|
},
|
|
234
723
|
field.questionId
|
|
235
724
|
)) }),
|
|
236
|
-
response.scores && Object.keys(response.scores).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
237
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
key,
|
|
262
|
-
": "
|
|
263
|
-
] }),
|
|
264
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "var(--primary, oklch(0.82 0.14 210))" }, children: value })
|
|
265
|
-
]
|
|
266
|
-
},
|
|
267
|
-
key
|
|
268
|
-
)),
|
|
269
|
-
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
270
|
-
"div",
|
|
271
|
-
{
|
|
272
|
-
style: {
|
|
273
|
-
padding: "8px 16px",
|
|
274
|
-
backgroundColor: "var(--accent, #17171a)",
|
|
275
|
-
borderRadius: "6px",
|
|
276
|
-
fontSize: "13px"
|
|
725
|
+
response.scores && Object.keys(response.scores).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5", children: [
|
|
726
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-foreground mb-3", children: "Scores" }),
|
|
727
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 flex-wrap", children: [
|
|
728
|
+
Object.entries(response.scores).map(([key, value]) => {
|
|
729
|
+
const displayName = INSTRUMENT_DISPLAY_NAMES[key] ?? key;
|
|
730
|
+
const severity = typeof value === "number" ? getScoreSeverity(key, value) : void 0;
|
|
731
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
732
|
+
"div",
|
|
733
|
+
{
|
|
734
|
+
className: "px-4 py-2 bg-accent rounded-md text-[13px] flex items-center gap-2",
|
|
735
|
+
children: [
|
|
736
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
737
|
+
displayName,
|
|
738
|
+
": "
|
|
739
|
+
] }),
|
|
740
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-primary", children: value }),
|
|
741
|
+
severity && /* @__PURE__ */ jsxRuntime.jsx(
|
|
742
|
+
"span",
|
|
743
|
+
{
|
|
744
|
+
className: "text-[11px] font-semibold px-2 py-0.5 rounded-full text-white",
|
|
745
|
+
style: { backgroundColor: severity.color },
|
|
746
|
+
children: severity.label
|
|
747
|
+
}
|
|
748
|
+
)
|
|
749
|
+
]
|
|
277
750
|
},
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
|
|
751
|
+
key
|
|
752
|
+
);
|
|
753
|
+
}),
|
|
754
|
+
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 bg-accent rounded-md text-[13px]", children: [
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Total: " }),
|
|
756
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-primary", children: response.totalScore })
|
|
757
|
+
] })
|
|
284
758
|
] })
|
|
285
759
|
] }),
|
|
286
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
760
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 pt-3 border-t border-border text-xs text-muted-foreground", children: [
|
|
761
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
762
|
+
"Schema: ",
|
|
763
|
+
response.schemaId,
|
|
764
|
+
" (v",
|
|
765
|
+
response.schemaVersion,
|
|
766
|
+
")"
|
|
767
|
+
] }),
|
|
768
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
769
|
+
"Session: ",
|
|
770
|
+
response.sessionToken
|
|
771
|
+
] })
|
|
772
|
+
] })
|
|
773
|
+
] });
|
|
774
|
+
}
|
|
775
|
+
function renderFieldValue(field) {
|
|
776
|
+
const { type, value } = field;
|
|
777
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
778
|
+
switch (type) {
|
|
779
|
+
case "vitals_entry":
|
|
780
|
+
return renderVitals(value);
|
|
781
|
+
case "medication_list":
|
|
782
|
+
return renderMedications(value);
|
|
783
|
+
case "allergy_list":
|
|
784
|
+
return renderAllergies(value);
|
|
785
|
+
case "bmi_calculator":
|
|
786
|
+
return renderBmi(value);
|
|
787
|
+
case "body_diagram":
|
|
788
|
+
return renderBodyDiagram(value);
|
|
789
|
+
case "pain_scale":
|
|
790
|
+
return renderPainScale(value);
|
|
791
|
+
case "insurance_card":
|
|
792
|
+
return renderInsurance(value);
|
|
793
|
+
case "payment":
|
|
794
|
+
return renderPayment(value);
|
|
795
|
+
case "legal_name":
|
|
796
|
+
return renderLegalName(value);
|
|
797
|
+
case "address":
|
|
798
|
+
return renderAddress(value);
|
|
799
|
+
case "consent":
|
|
800
|
+
return renderConsent(value);
|
|
801
|
+
case "signature":
|
|
802
|
+
return renderSignature(value);
|
|
803
|
+
default:
|
|
804
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-pre-wrap", children: formatFallbackValue(value) });
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
var VITALS_LABELS = {
|
|
808
|
+
systolicBp: { label: "Systolic BP", unit: "mmHg", normalRange: "90-140" },
|
|
809
|
+
diastolicBp: { label: "Diastolic BP", unit: "mmHg", normalRange: "60-90" },
|
|
810
|
+
heartRate: { label: "Heart Rate", unit: "bpm", normalRange: "60-100" },
|
|
811
|
+
temperature: { label: "Temperature", unit: "\xB0F", normalRange: "97.0-99.5" },
|
|
812
|
+
respiratoryRate: { label: "Respiratory Rate", unit: "/min", normalRange: "12-20" },
|
|
813
|
+
oxygenSaturation: { label: "SpO\u2082", unit: "%", normalRange: "95-100" }
|
|
814
|
+
};
|
|
815
|
+
function renderVitals(value) {
|
|
816
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
817
|
+
const vitals = value;
|
|
818
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: Object.entries(VITALS_LABELS).map(([key, meta]) => {
|
|
819
|
+
const val = vitals[key];
|
|
820
|
+
if (val == null || val === 0) return null;
|
|
821
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[13px]", children: [
|
|
822
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
823
|
+
meta.label,
|
|
824
|
+
": "
|
|
825
|
+
] }),
|
|
826
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
827
|
+
String(val),
|
|
828
|
+
" ",
|
|
829
|
+
meta.unit
|
|
830
|
+
] }),
|
|
831
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-muted-foreground ml-1", children: [
|
|
832
|
+
"(",
|
|
833
|
+
meta.normalRange,
|
|
834
|
+
")"
|
|
835
|
+
] })
|
|
836
|
+
] }, key);
|
|
837
|
+
}) });
|
|
838
|
+
}
|
|
839
|
+
function renderMedications(value) {
|
|
840
|
+
if (!Array.isArray(value) || value.length === 0) return "No medications";
|
|
841
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: value.map((med, idx) => {
|
|
842
|
+
const m = med;
|
|
843
|
+
const parts = [m.dosage, m.frequency, m.route].filter(Boolean);
|
|
844
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
287
845
|
"div",
|
|
288
846
|
{
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
847
|
+
className: "px-3 py-2 border border-border rounded-md text-[13px]",
|
|
848
|
+
children: [
|
|
849
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: m.name || "Unnamed" }),
|
|
850
|
+
parts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
851
|
+
" \u2014 ",
|
|
852
|
+
parts.join(" \xB7 ")
|
|
853
|
+
] }),
|
|
854
|
+
m.notes && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground mt-0.5", children: m.notes })
|
|
855
|
+
]
|
|
856
|
+
},
|
|
857
|
+
idx
|
|
858
|
+
);
|
|
859
|
+
}) });
|
|
860
|
+
}
|
|
861
|
+
function renderAllergies(value) {
|
|
862
|
+
if (!Array.isArray(value) || value.length === 0) return "No allergies";
|
|
863
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: value.map((allergy, idx) => {
|
|
864
|
+
const a = allergy;
|
|
865
|
+
const sevColor = SEVERITY_COLORS[a.severity];
|
|
866
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
867
|
+
"div",
|
|
868
|
+
{
|
|
869
|
+
className: "px-3 py-2 border border-border rounded-md text-[13px] flex items-center gap-2 flex-wrap",
|
|
296
870
|
children: [
|
|
297
|
-
/* @__PURE__ */ jsxRuntime.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
response.schemaVersion,
|
|
871
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: a.allergen || "Unknown" }),
|
|
872
|
+
a.type && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-muted-foreground", children: [
|
|
873
|
+
"(",
|
|
874
|
+
a.type,
|
|
302
875
|
")"
|
|
303
876
|
] }),
|
|
304
|
-
/* @__PURE__ */ jsxRuntime.
|
|
305
|
-
"
|
|
306
|
-
|
|
877
|
+
a.severity && /* @__PURE__ */ jsxRuntime.jsx(
|
|
878
|
+
"span",
|
|
879
|
+
{
|
|
880
|
+
className: "text-[11px] font-semibold px-2 py-0.5 rounded-full",
|
|
881
|
+
style: {
|
|
882
|
+
backgroundColor: sevColor ?? "var(--accent)",
|
|
883
|
+
color: sevColor ? "#fff" : "var(--foreground)"
|
|
884
|
+
},
|
|
885
|
+
children: a.severity
|
|
886
|
+
}
|
|
887
|
+
),
|
|
888
|
+
a.reaction && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
889
|
+
"\u2014 ",
|
|
890
|
+
a.reaction
|
|
307
891
|
] })
|
|
308
892
|
]
|
|
893
|
+
},
|
|
894
|
+
idx
|
|
895
|
+
);
|
|
896
|
+
}) });
|
|
897
|
+
}
|
|
898
|
+
function renderBmi(value) {
|
|
899
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
900
|
+
const data = value;
|
|
901
|
+
const bmi = data.bmi;
|
|
902
|
+
const unit = data.unit ?? "imperial";
|
|
903
|
+
const height = data.height;
|
|
904
|
+
const weight = data.weight;
|
|
905
|
+
const heightUnit = unit === "metric" ? "cm" : "in";
|
|
906
|
+
const weightUnit = unit === "metric" ? "kg" : "lbs";
|
|
907
|
+
const category = bmi != null ? getBmiCategory(bmi) : void 0;
|
|
908
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[13px] flex items-center gap-2 flex-wrap", children: [
|
|
909
|
+
height != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
910
|
+
"Height: ",
|
|
911
|
+
height,
|
|
912
|
+
" ",
|
|
913
|
+
heightUnit
|
|
914
|
+
] }),
|
|
915
|
+
height != null && weight != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
916
|
+
weight != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
917
|
+
"Weight: ",
|
|
918
|
+
weight,
|
|
919
|
+
" ",
|
|
920
|
+
weightUnit
|
|
921
|
+
] }),
|
|
922
|
+
bmi != null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
923
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2014" }),
|
|
924
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
|
|
925
|
+
"BMI: ",
|
|
926
|
+
bmi
|
|
927
|
+
] }),
|
|
928
|
+
category && /* @__PURE__ */ jsxRuntime.jsx(
|
|
929
|
+
"span",
|
|
930
|
+
{
|
|
931
|
+
className: "text-[11px] font-semibold px-2 py-0.5 rounded-full text-white",
|
|
932
|
+
style: { backgroundColor: category.color },
|
|
933
|
+
children: category.label
|
|
934
|
+
}
|
|
935
|
+
)
|
|
936
|
+
] })
|
|
937
|
+
] });
|
|
938
|
+
}
|
|
939
|
+
function renderBodyDiagram(value) {
|
|
940
|
+
if (!Array.isArray(value) || value.length === 0) return "No regions selected";
|
|
941
|
+
const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
|
|
942
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.join(", ") });
|
|
943
|
+
}
|
|
944
|
+
function renderPainScale(value) {
|
|
945
|
+
if (typeof value !== "number") return formatFallbackValue(value);
|
|
946
|
+
const face = getPainLabel(value);
|
|
947
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", style: { color: face?.color }, children: [
|
|
948
|
+
value,
|
|
949
|
+
"/10",
|
|
950
|
+
face ? ` \u2014 ${face.label}` : ""
|
|
951
|
+
] });
|
|
952
|
+
}
|
|
953
|
+
function renderInsurance(value) {
|
|
954
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
955
|
+
const ins = value;
|
|
956
|
+
const textFields = [
|
|
957
|
+
{ key: "carrierId", label: "Carrier" },
|
|
958
|
+
{ key: "planName", label: "Plan" },
|
|
959
|
+
{ key: "memberId", label: "Member ID" },
|
|
960
|
+
{ key: "groupNumber", label: "Group #" },
|
|
961
|
+
{ key: "subscriberName", label: "Subscriber" },
|
|
962
|
+
{ key: "relationship", label: "Relationship" }
|
|
963
|
+
];
|
|
964
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
965
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-3 gap-y-1 text-[13px]", children: textFields.map(({ key, label }) => {
|
|
966
|
+
const val = ins[key];
|
|
967
|
+
if (!val) return null;
|
|
968
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
969
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
970
|
+
label,
|
|
971
|
+
": "
|
|
972
|
+
] }),
|
|
973
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: String(val) })
|
|
974
|
+
] }, key);
|
|
975
|
+
}) }),
|
|
976
|
+
!!(ins.frontImage || ins.backImage) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 mt-1", children: [
|
|
977
|
+
typeof ins.frontImage === "string" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
978
|
+
"img",
|
|
979
|
+
{
|
|
980
|
+
src: ins.frontImage,
|
|
981
|
+
alt: "Front of card",
|
|
982
|
+
className: "h-12 rounded border border-border"
|
|
983
|
+
}
|
|
984
|
+
),
|
|
985
|
+
typeof ins.backImage === "string" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
986
|
+
"img",
|
|
987
|
+
{
|
|
988
|
+
src: ins.backImage,
|
|
989
|
+
alt: "Back of card",
|
|
990
|
+
className: "h-12 rounded border border-border"
|
|
991
|
+
}
|
|
992
|
+
)
|
|
993
|
+
] })
|
|
994
|
+
] });
|
|
995
|
+
}
|
|
996
|
+
function renderPayment(value) {
|
|
997
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
998
|
+
const payment = value;
|
|
999
|
+
const status = payment.status;
|
|
1000
|
+
const statusConfig = {
|
|
1001
|
+
succeeded: { bg: "#22c55e", label: "Succeeded" },
|
|
1002
|
+
failed: { bg: "#ef4444", label: "Failed" },
|
|
1003
|
+
processing: { bg: "#eab308", label: "Processing" }
|
|
1004
|
+
};
|
|
1005
|
+
const cfg = statusConfig[status];
|
|
1006
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-[13px]", children: [
|
|
1007
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1008
|
+
"span",
|
|
1009
|
+
{
|
|
1010
|
+
className: "text-[11px] font-semibold px-2.5 py-0.5 rounded-full text-white",
|
|
1011
|
+
style: { backgroundColor: cfg?.bg ?? "var(--accent)" },
|
|
1012
|
+
children: cfg?.label ?? status
|
|
309
1013
|
}
|
|
310
|
-
)
|
|
1014
|
+
),
|
|
1015
|
+
!!payment.chargeId && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: String(payment.chargeId) }),
|
|
1016
|
+
!!payment.error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive", children: String(payment.error) })
|
|
311
1017
|
] });
|
|
312
1018
|
}
|
|
313
|
-
function
|
|
1019
|
+
function renderLegalName(value) {
|
|
1020
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
1021
|
+
const name = value;
|
|
1022
|
+
const parts = [name.first, name.middle, name.last].filter(Boolean);
|
|
1023
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: parts.join(" ") || "\u2014" });
|
|
1024
|
+
}
|
|
1025
|
+
function renderAddress(value) {
|
|
1026
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
1027
|
+
const addr = value;
|
|
1028
|
+
const line1 = [addr.street, addr.street2].filter(Boolean).join(", ");
|
|
1029
|
+
const line2 = [addr.city, addr.state, addr.zip].filter(Boolean).join(", ");
|
|
1030
|
+
const full = [line1, line2].filter(Boolean).join(", ");
|
|
1031
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: full || "\u2014" });
|
|
1032
|
+
}
|
|
1033
|
+
function renderConsent(value) {
|
|
1034
|
+
const agreed = value === true || value === "true" || value === "agreed";
|
|
1035
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: agreed ? "font-semibold text-green-500" : "font-semibold text-muted-foreground", children: agreed ? "Agreed" : "Not agreed" });
|
|
1036
|
+
}
|
|
1037
|
+
function renderSignature(value) {
|
|
1038
|
+
if (typeof value !== "string") return formatFallbackValue(value);
|
|
1039
|
+
if (value.startsWith("data:image")) {
|
|
1040
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1041
|
+
"img",
|
|
1042
|
+
{
|
|
1043
|
+
src: value,
|
|
1044
|
+
alt: "Signature",
|
|
1045
|
+
className: "h-15 border border-border rounded"
|
|
1046
|
+
}
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: value });
|
|
1050
|
+
}
|
|
1051
|
+
function formatFallbackValue(value) {
|
|
314
1052
|
if (value == null) return "\u2014";
|
|
315
1053
|
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
316
|
-
if (Array.isArray(value)) return value.map(
|
|
1054
|
+
if (Array.isArray(value)) return value.map(formatFallbackValue).join(", ");
|
|
317
1055
|
if (typeof value === "object") return JSON.stringify(value, null, 2);
|
|
318
1056
|
return String(value);
|
|
319
1057
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
1058
|
+
|
|
1059
|
+
// src/response-viewer/export-utils.ts
|
|
1060
|
+
function exportToCsv(schema, responses, filename = "responses.csv") {
|
|
1061
|
+
const questions = getExportableQuestions(schema);
|
|
1062
|
+
const headers = ["Submitted", ...questions.map((q) => q.label), "Score"];
|
|
1063
|
+
const rows = responses.map((r) => {
|
|
1064
|
+
const submitted = new Date(r.submittedAt).toLocaleString();
|
|
1065
|
+
const values = questions.map((q) => formatExportValue(r.values[q.id], q.type));
|
|
1066
|
+
const score = r.totalScore != null ? String(r.totalScore) : "";
|
|
1067
|
+
return [submitted, ...values, score];
|
|
1068
|
+
});
|
|
1069
|
+
const csvContent = [headers, ...rows].map((row) => row.map(escapeCsvField).join(",")).join("\n");
|
|
1070
|
+
downloadBlob(csvContent, filename, "text/csv;charset=utf-8;");
|
|
1071
|
+
}
|
|
1072
|
+
function exportToJson(responses, filename = "responses.json") {
|
|
1073
|
+
const content = JSON.stringify(responses, null, 2);
|
|
1074
|
+
downloadBlob(content, filename, "application/json;charset=utf-8;");
|
|
1075
|
+
}
|
|
1076
|
+
function getExportableQuestions(schema) {
|
|
1077
|
+
const questions = [];
|
|
1078
|
+
for (const section of schema.sections) {
|
|
1079
|
+
for (const q of section.questions) {
|
|
1080
|
+
if (q.type === "info-block" || q.type === "section-header" || q.type === "page-break") {
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
questions.push(q);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return questions;
|
|
1087
|
+
}
|
|
1088
|
+
function formatExportValue(value, type) {
|
|
1089
|
+
if (value == null) return "";
|
|
1090
|
+
switch (type) {
|
|
1091
|
+
case "vitals_entry": {
|
|
1092
|
+
if (typeof value !== "object" || value === null) break;
|
|
1093
|
+
const v = value;
|
|
1094
|
+
const parts = [];
|
|
1095
|
+
if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
|
|
1096
|
+
if (v.heartRate) parts.push(`HR ${v.heartRate}`);
|
|
1097
|
+
if (v.temperature) parts.push(`${v.temperature}\xB0F`);
|
|
1098
|
+
if (v.oxygenSaturation) parts.push(`SpO2 ${v.oxygenSaturation}%`);
|
|
1099
|
+
return parts.join("; ");
|
|
1100
|
+
}
|
|
1101
|
+
case "medication_list":
|
|
1102
|
+
if (Array.isArray(value)) {
|
|
1103
|
+
return value.map((m) => [m.name, m.dosage, m.frequency].filter(Boolean).join(" ")).join("; ");
|
|
1104
|
+
}
|
|
1105
|
+
break;
|
|
1106
|
+
case "allergy_list":
|
|
1107
|
+
if (Array.isArray(value)) {
|
|
1108
|
+
return value.map((a) => [a.allergen, a.severity].filter(Boolean).join(" - ")).join("; ");
|
|
1109
|
+
}
|
|
1110
|
+
break;
|
|
1111
|
+
case "body_diagram":
|
|
1112
|
+
if (Array.isArray(value)) {
|
|
1113
|
+
return value.map((id) => BODY_REGION_LABELS[id] ?? id).join("; ");
|
|
1114
|
+
}
|
|
1115
|
+
break;
|
|
1116
|
+
case "pain_scale":
|
|
1117
|
+
return typeof value === "number" ? `${value}/10` : String(value);
|
|
1118
|
+
case "bmi_calculator": {
|
|
1119
|
+
if (typeof value !== "object" || value === null) break;
|
|
1120
|
+
const d = value;
|
|
1121
|
+
return d.bmi != null ? `BMI ${d.bmi}` : "";
|
|
1122
|
+
}
|
|
1123
|
+
case "payment": {
|
|
1124
|
+
if (typeof value !== "object" || value === null) break;
|
|
1125
|
+
return value.status ?? "";
|
|
1126
|
+
}
|
|
1127
|
+
case "legal_name": {
|
|
1128
|
+
if (typeof value !== "object" || value === null) break;
|
|
1129
|
+
const n = value;
|
|
1130
|
+
return [n.first, n.middle, n.last].filter(Boolean).join(" ");
|
|
1131
|
+
}
|
|
1132
|
+
case "address": {
|
|
1133
|
+
if (typeof value !== "object" || value === null) break;
|
|
1134
|
+
const a = value;
|
|
1135
|
+
return [a.street, a.city, a.state, a.zip].filter(Boolean).join(", ");
|
|
1136
|
+
}
|
|
1137
|
+
case "consent":
|
|
1138
|
+
return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
|
|
1139
|
+
case "signature":
|
|
1140
|
+
return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "";
|
|
1141
|
+
}
|
|
1142
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
1143
|
+
if (Array.isArray(value)) return value.join("; ");
|
|
1144
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
1145
|
+
return String(value);
|
|
1146
|
+
}
|
|
1147
|
+
function escapeCsvField(field) {
|
|
1148
|
+
if (field.includes(",") || field.includes('"') || field.includes("\n")) {
|
|
1149
|
+
return `"${field.replace(/"/g, '""')}"`;
|
|
1150
|
+
}
|
|
1151
|
+
return field;
|
|
1152
|
+
}
|
|
1153
|
+
function downloadBlob(content, filename, mimeType) {
|
|
1154
|
+
const blob = new Blob([content], { type: mimeType });
|
|
1155
|
+
const url = URL.createObjectURL(blob);
|
|
1156
|
+
const link = document.createElement("a");
|
|
1157
|
+
link.href = url;
|
|
1158
|
+
link.download = filename;
|
|
1159
|
+
document.body.appendChild(link);
|
|
1160
|
+
link.click();
|
|
1161
|
+
document.body.removeChild(link);
|
|
1162
|
+
URL.revokeObjectURL(url);
|
|
1163
|
+
}
|
|
1164
|
+
function cn(...inputs) {
|
|
1165
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
1166
|
+
}
|
|
332
1167
|
function ResponseViewerInner({
|
|
333
1168
|
schema,
|
|
334
1169
|
responses,
|
|
@@ -354,62 +1189,73 @@ function ResponseViewerInner({
|
|
|
354
1189
|
value: response.values[q.id]
|
|
355
1190
|
}));
|
|
356
1191
|
}
|
|
1192
|
+
const heightValue = typeof height === "number" ? `${height}px` : height;
|
|
1193
|
+
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
357
1194
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
358
1195
|
"div",
|
|
359
1196
|
{
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
flexDirection: "column",
|
|
363
|
-
height: typeof height === "number" ? `${height}px` : height,
|
|
364
|
-
width: typeof width === "number" ? `${width}px` : width,
|
|
365
|
-
border: "1px solid var(--border, #1c1c20)",
|
|
366
|
-
borderRadius: "8px",
|
|
367
|
-
overflow: "hidden",
|
|
368
|
-
fontFamily: "inherit",
|
|
369
|
-
backgroundColor: "var(--background, #0a0a0b)",
|
|
370
|
-
color: "var(--foreground, #e8e8ea)"
|
|
371
|
-
},
|
|
1197
|
+
className: "flex flex-col border border-border rounded-lg overflow-hidden bg-background text-foreground",
|
|
1198
|
+
style: { height: heightValue, width: widthValue },
|
|
372
1199
|
children: [
|
|
373
|
-
!selectedResponse && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
374
|
-
"div",
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
] }),
|
|
389
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
|
|
390
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
391
|
-
"button",
|
|
392
|
-
{
|
|
393
|
-
type: "button",
|
|
394
|
-
style: viewButtonStyle(viewMode === "table"),
|
|
395
|
-
onClick: () => setViewMode("table"),
|
|
396
|
-
children: "Table"
|
|
397
|
-
}
|
|
1200
|
+
!selectedResponse && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center px-3 py-2 border-b border-border", children: [
|
|
1201
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
1202
|
+
responses.length,
|
|
1203
|
+
" response",
|
|
1204
|
+
responses.length !== 1 ? "s" : ""
|
|
1205
|
+
] }),
|
|
1206
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1207
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1208
|
+
fieldcraftReact.Button,
|
|
1209
|
+
{
|
|
1210
|
+
variant: viewMode === "table" ? "secondary" : "ghost",
|
|
1211
|
+
size: "sm",
|
|
1212
|
+
className: cn(
|
|
1213
|
+
"h-7 text-xs",
|
|
1214
|
+
viewMode === "table" && "font-semibold border border-ring"
|
|
398
1215
|
),
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
1216
|
+
onClick: () => setViewMode("table"),
|
|
1217
|
+
children: "Table"
|
|
1218
|
+
}
|
|
1219
|
+
),
|
|
1220
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1221
|
+
fieldcraftReact.Button,
|
|
1222
|
+
{
|
|
1223
|
+
variant: viewMode === "card" ? "secondary" : "ghost",
|
|
1224
|
+
size: "sm",
|
|
1225
|
+
className: cn(
|
|
1226
|
+
"h-7 text-xs",
|
|
1227
|
+
viewMode === "card" && "font-semibold border border-ring"
|
|
1228
|
+
),
|
|
1229
|
+
onClick: () => setViewMode("card"),
|
|
1230
|
+
children: "Cards"
|
|
1231
|
+
}
|
|
1232
|
+
),
|
|
1233
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { orientation: "vertical", className: "h-5 mx-1" }),
|
|
1234
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1235
|
+
fieldcraftReact.Button,
|
|
1236
|
+
{
|
|
1237
|
+
variant: "outline",
|
|
1238
|
+
size: "sm",
|
|
1239
|
+
className: "h-7 text-xs",
|
|
1240
|
+
onClick: () => exportToCsv(schema, responses),
|
|
1241
|
+
disabled: responses.length === 0,
|
|
1242
|
+
children: "Export CSV"
|
|
1243
|
+
}
|
|
1244
|
+
),
|
|
1245
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1246
|
+
fieldcraftReact.Button,
|
|
1247
|
+
{
|
|
1248
|
+
variant: "outline",
|
|
1249
|
+
size: "sm",
|
|
1250
|
+
className: "h-7 text-xs",
|
|
1251
|
+
onClick: () => exportToJson(responses),
|
|
1252
|
+
disabled: responses.length === 0,
|
|
1253
|
+
children: "Export JSON"
|
|
1254
|
+
}
|
|
1255
|
+
)
|
|
1256
|
+
] })
|
|
1257
|
+
] }),
|
|
1258
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex-1 overflow-auto", selectedResponse && "p-4"), children: selectedResponse ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
413
1259
|
ResponseDetail,
|
|
414
1260
|
{
|
|
415
1261
|
response: selectedResponse,
|
|
@@ -423,29 +1269,18 @@ function ResponseViewerInner({
|
|
|
423
1269
|
responses,
|
|
424
1270
|
onRowClick: handleSelect
|
|
425
1271
|
}
|
|
426
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
padding: "12px"
|
|
1272
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 p-3 grid-cols-[repeat(auto-fill,minmax(280px,1fr))]", children: [
|
|
1273
|
+
responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1274
|
+
ResponseCard,
|
|
1275
|
+
{
|
|
1276
|
+
response,
|
|
1277
|
+
fields: getFields(response),
|
|
1278
|
+
onClick: () => handleSelect(response)
|
|
434
1279
|
},
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
response,
|
|
440
|
-
fields: getFields(response),
|
|
441
|
-
onClick: () => handleSelect(response)
|
|
442
|
-
},
|
|
443
|
-
response.sessionToken || idx
|
|
444
|
-
)),
|
|
445
|
-
responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "32px", textAlign: "center", color: "var(--muted-foreground, #8a8a95)" }, children: "No responses yet" })
|
|
446
|
-
]
|
|
447
|
-
}
|
|
448
|
-
) })
|
|
1280
|
+
response.sessionToken || idx
|
|
1281
|
+
)),
|
|
1282
|
+
responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center text-muted-foreground", children: "No responses yet" })
|
|
1283
|
+
] }) })
|
|
449
1284
|
]
|
|
450
1285
|
}
|
|
451
1286
|
);
|
|
@@ -464,6 +1299,6 @@ function getAllQuestions2(schema) {
|
|
|
464
1299
|
}
|
|
465
1300
|
|
|
466
1301
|
// src/response-viewer/ResponseViewer.tsx
|
|
467
|
-
var ResponseViewer =
|
|
1302
|
+
var ResponseViewer = requireLicense(ResponseViewerInner, "ResponseViewer");
|
|
468
1303
|
|
|
469
1304
|
exports.ResponseViewer = ResponseViewer;
|