@squaredr/fieldcraft-pro 0.0.5 → 1.0.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-APHGEYG4.mjs → chunk-DDQDYTMB.mjs} +155 -129
- package/dist/chunk-LOFGBNJV.mjs +976 -0
- package/dist/chunk-QQ4JZGTD.mjs +9 -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 +150 -119
- package/dist/form-builder/index.mjs +2 -1
- package/dist/index.js +983 -443
- package/dist/index.mjs +4 -3
- package/dist/response-viewer/index.d.mts +4 -1
- package/dist/response-viewer/index.d.ts +4 -1
- package/dist/response-viewer/index.js +839 -324
- package/dist/response-viewer/index.mjs +2 -1
- package/dist/styles.css +1 -1
- package/package.json +13 -12
- package/dist/chunk-NIKQEWPG.mjs +0 -467
- /package/{dist/theme-editor-styles.css → theme-editor/styles.css} +0 -0
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var react = require('react');
|
|
|
5
5
|
var core = require('@dnd-kit/core');
|
|
6
6
|
var lucideReact = require('lucide-react');
|
|
7
7
|
var fieldcraftReact = require('@squaredr/fieldcraft-react');
|
|
8
|
+
var fieldcraftCore = require('@squaredr/fieldcraft-core');
|
|
8
9
|
var clsx = require('clsx');
|
|
9
10
|
var tailwindMerge = require('tailwind-merge');
|
|
10
11
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -80,6 +81,39 @@ function generateOptionId() {
|
|
|
80
81
|
function deepClone(obj) {
|
|
81
82
|
return structuredClone(obj);
|
|
82
83
|
}
|
|
84
|
+
function cleanCondition(expr, deletedFieldId) {
|
|
85
|
+
if (expr.field === deletedFieldId) return void 0;
|
|
86
|
+
if (expr.field && expr.field !== deletedFieldId) return expr;
|
|
87
|
+
if (expr.conditions) {
|
|
88
|
+
const cleaned = expr.conditions.map((c) => cleanCondition(c, deletedFieldId)).filter((c) => c !== void 0);
|
|
89
|
+
if (cleaned.length === 0) return void 0;
|
|
90
|
+
if (cleaned.length === 1) return cleaned[0];
|
|
91
|
+
return { ...expr, conditions: cleaned };
|
|
92
|
+
}
|
|
93
|
+
return expr;
|
|
94
|
+
}
|
|
95
|
+
function cleanupOrphanConditions(schema, deletedFieldId) {
|
|
96
|
+
for (const section of schema.sections) {
|
|
97
|
+
if (section.showIf) {
|
|
98
|
+
const cleaned = cleanCondition(section.showIf, deletedFieldId);
|
|
99
|
+
if (cleaned) {
|
|
100
|
+
section.showIf = cleaned;
|
|
101
|
+
} else {
|
|
102
|
+
delete section.showIf;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const question of section.questions) {
|
|
106
|
+
if (question.showIf) {
|
|
107
|
+
const cleaned = cleanCondition(question.showIf, deletedFieldId);
|
|
108
|
+
if (cleaned) {
|
|
109
|
+
question.showIf = cleaned;
|
|
110
|
+
} else {
|
|
111
|
+
delete question.showIf;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
83
117
|
function addSection(schema, section, index) {
|
|
84
118
|
const newSchema = deepClone(schema);
|
|
85
119
|
newSchema.sections.splice(index, 0, section);
|
|
@@ -87,7 +121,12 @@ function addSection(schema, section, index) {
|
|
|
87
121
|
}
|
|
88
122
|
function removeSection(schema, sectionId) {
|
|
89
123
|
const newSchema = deepClone(schema);
|
|
124
|
+
const removedSection = newSchema.sections.find((s) => s.id === sectionId);
|
|
125
|
+
const removedFieldIds = removedSection ? removedSection.questions.map((q) => q.id) : [];
|
|
90
126
|
newSchema.sections = newSchema.sections.filter((s) => s.id !== sectionId);
|
|
127
|
+
for (const fieldId of removedFieldIds) {
|
|
128
|
+
cleanupOrphanConditions(newSchema, fieldId);
|
|
129
|
+
}
|
|
91
130
|
return newSchema;
|
|
92
131
|
}
|
|
93
132
|
function updateSection(schema, sectionId, updates) {
|
|
@@ -137,6 +176,7 @@ function removeQuestion(schema, sectionId, questionId) {
|
|
|
137
176
|
const section = newSchema.sections.find((s) => s.id === sectionId);
|
|
138
177
|
if (!section) return schema;
|
|
139
178
|
section.questions = section.questions.filter((q) => q.id !== questionId);
|
|
179
|
+
cleanupOrphanConditions(newSchema, questionId);
|
|
140
180
|
return newSchema;
|
|
141
181
|
}
|
|
142
182
|
function updateQuestion(schema, sectionId, questionId, updates) {
|
|
@@ -975,11 +1015,13 @@ function QuestionPalette({ questionTypes, palette }) {
|
|
|
975
1015
|
const isCollapsed = collapsed[category.category] && !search;
|
|
976
1016
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
|
|
977
1017
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
978
|
-
|
|
1018
|
+
fieldcraftReact.Button,
|
|
979
1019
|
{
|
|
980
1020
|
type: "button",
|
|
1021
|
+
variant: "ghost",
|
|
1022
|
+
size: "xs",
|
|
981
1023
|
onClick: () => toggleCategory(category.category),
|
|
982
|
-
className: "
|
|
1024
|
+
className: "w-full justify-start px-1 text-[11px] font-semibold uppercase tracking-widest text-muted-foreground",
|
|
983
1025
|
"aria-expanded": !isCollapsed,
|
|
984
1026
|
children: [
|
|
985
1027
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1343,11 +1385,12 @@ function FormCanvas({ builderState }) {
|
|
|
1343
1385
|
section.id
|
|
1344
1386
|
)),
|
|
1345
1387
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1346
|
-
|
|
1388
|
+
fieldcraftReact.Button,
|
|
1347
1389
|
{
|
|
1348
1390
|
type: "button",
|
|
1391
|
+
variant: "outline",
|
|
1349
1392
|
onClick: handleAddSection,
|
|
1350
|
-
className: "w-full py-4 text-sm font-medium
|
|
1393
|
+
className: "w-full py-4 h-auto text-sm font-medium border-dashed border-fcb-border-strong text-muted-foreground hover:border-primary hover:text-primary hover:bg-primary/5",
|
|
1351
1394
|
children: [
|
|
1352
1395
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 14, strokeWidth: 2 }),
|
|
1353
1396
|
"Add Section"
|
|
@@ -1357,6 +1400,26 @@ function FormCanvas({ builderState }) {
|
|
|
1357
1400
|
] })
|
|
1358
1401
|
] }) });
|
|
1359
1402
|
}
|
|
1403
|
+
var NativeSelect = react.forwardRef(
|
|
1404
|
+
({ className, wrapperClassName, children, ...props }, ref) => {
|
|
1405
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", wrapperClassName), children: [
|
|
1406
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1407
|
+
"select",
|
|
1408
|
+
{
|
|
1409
|
+
ref,
|
|
1410
|
+
className: cn(
|
|
1411
|
+
"flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
1412
|
+
className
|
|
1413
|
+
),
|
|
1414
|
+
...props,
|
|
1415
|
+
children
|
|
1416
|
+
}
|
|
1417
|
+
),
|
|
1418
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" })
|
|
1419
|
+
] });
|
|
1420
|
+
}
|
|
1421
|
+
);
|
|
1422
|
+
NativeSelect.displayName = "NativeSelect";
|
|
1360
1423
|
function useConfigUpdater(question, onUpdate) {
|
|
1361
1424
|
return (field, value) => {
|
|
1362
1425
|
const current = question.config ?? {};
|
|
@@ -1751,18 +1814,7 @@ function SelectField({
|
|
|
1751
1814
|
}) {
|
|
1752
1815
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1753
1816
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
|
|
1754
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1755
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1756
|
-
"select",
|
|
1757
|
-
{
|
|
1758
|
-
value,
|
|
1759
|
-
onChange: (e) => onChange(e.target.value),
|
|
1760
|
-
className: "flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
1761
|
-
children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1762
|
-
}
|
|
1763
|
-
),
|
|
1764
|
-
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
1765
|
-
] })
|
|
1817
|
+
/* @__PURE__ */ jsxRuntime.jsx(NativeSelect, { value, onChange: (e) => onChange(e.target.value), children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value)) })
|
|
1766
1818
|
] });
|
|
1767
1819
|
}
|
|
1768
1820
|
function LikertLabelsEditor({
|
|
@@ -1783,15 +1835,7 @@ function LikertLabelsEditor({
|
|
|
1783
1835
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1784
1836
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1785
1837
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Scale Labels" }),
|
|
1786
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1787
|
-
"button",
|
|
1788
|
-
{
|
|
1789
|
-
type: "button",
|
|
1790
|
-
onClick: handleAdd,
|
|
1791
|
-
className: "text-xs text-primary hover:underline",
|
|
1792
|
-
children: "+ Add"
|
|
1793
|
-
}
|
|
1794
|
-
)
|
|
1838
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1795
1839
|
] }),
|
|
1796
1840
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: labels.map((label, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 group", children: [
|
|
1797
1841
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground w-4 text-right shrink-0", children: index + 1 }),
|
|
@@ -1804,11 +1848,13 @@ function LikertLabelsEditor({
|
|
|
1804
1848
|
}
|
|
1805
1849
|
),
|
|
1806
1850
|
labels.length > 2 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1807
|
-
|
|
1851
|
+
fieldcraftReact.Button,
|
|
1808
1852
|
{
|
|
1809
1853
|
type: "button",
|
|
1854
|
+
variant: "ghost",
|
|
1855
|
+
size: "icon-xs",
|
|
1810
1856
|
onClick: () => handleRemove(index),
|
|
1811
|
-
className: "text-
|
|
1857
|
+
className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1812
1858
|
children: "x"
|
|
1813
1859
|
}
|
|
1814
1860
|
)
|
|
@@ -1837,15 +1883,7 @@ function MatrixItemsEditor({
|
|
|
1837
1883
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1838
1884
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1839
1885
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: label }),
|
|
1840
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1841
|
-
"button",
|
|
1842
|
-
{
|
|
1843
|
-
type: "button",
|
|
1844
|
-
onClick: handleAdd,
|
|
1845
|
-
className: "text-xs text-primary hover:underline",
|
|
1846
|
-
children: "+ Add"
|
|
1847
|
-
}
|
|
1848
|
-
)
|
|
1886
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1849
1887
|
] }),
|
|
1850
1888
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 group", children: [
|
|
1851
1889
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1857,11 +1895,13 @@ function MatrixItemsEditor({
|
|
|
1857
1895
|
}
|
|
1858
1896
|
),
|
|
1859
1897
|
items.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1860
|
-
|
|
1898
|
+
fieldcraftReact.Button,
|
|
1861
1899
|
{
|
|
1862
1900
|
type: "button",
|
|
1901
|
+
variant: "ghost",
|
|
1902
|
+
size: "icon-xs",
|
|
1863
1903
|
onClick: () => handleRemove(index),
|
|
1864
|
-
className: "text-
|
|
1904
|
+
className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1865
1905
|
children: "x"
|
|
1866
1906
|
}
|
|
1867
1907
|
)
|
|
@@ -1894,7 +1934,7 @@ function ScoringOptionsEditor({
|
|
|
1894
1934
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1895
1935
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1896
1936
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Score Options" }),
|
|
1897
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1937
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1898
1938
|
] }),
|
|
1899
1939
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: options.map((opt, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 group", children: [
|
|
1900
1940
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1916,7 +1956,7 @@ function ScoringOptionsEditor({
|
|
|
1916
1956
|
placeholder: "Score"
|
|
1917
1957
|
}
|
|
1918
1958
|
),
|
|
1919
|
-
options.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1959
|
+
options.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: () => handleRemove(index), className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity", children: "x" })
|
|
1920
1960
|
] }, index)) })
|
|
1921
1961
|
] });
|
|
1922
1962
|
}
|
|
@@ -1938,7 +1978,7 @@ function ScoreRangesEditor({
|
|
|
1938
1978
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1939
1979
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1940
1980
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Score Ranges" }),
|
|
1941
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1981
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1942
1982
|
] }),
|
|
1943
1983
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: ranges.map((range, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 rounded-md border border-border space-y-1.5 group", children: [
|
|
1944
1984
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
@@ -1946,7 +1986,7 @@ function ScoreRangesEditor({
|
|
|
1946
1986
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
1947
1987
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Input, { type: "number", value: range.max, onChange: (e) => handleUpdate(index, { max: Number(e.target.value) }), className: "h-7 text-xs w-16", placeholder: "Max" }),
|
|
1948
1988
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Input, { value: range.label, onChange: (e) => handleUpdate(index, { label: e.target.value }), className: "h-7 text-xs flex-1", placeholder: "Label" }),
|
|
1949
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1989
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: () => handleRemove(index), className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity", children: "x" })
|
|
1950
1990
|
] }),
|
|
1951
1991
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Input, { value: range.description ?? "", onChange: (e) => handleUpdate(index, { description: e.target.value || void 0 }), className: "h-7 text-xs", placeholder: "Description (optional)" })
|
|
1952
1992
|
] }, index)) })
|
|
@@ -1996,26 +2036,20 @@ function ValidationRulesEditor({ question, onUpdate }) {
|
|
|
1996
2036
|
},
|
|
1997
2037
|
`${rule.type}-${index}`
|
|
1998
2038
|
)) }),
|
|
1999
|
-
availableTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
children:
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
availableTypes.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t.value, children: t.label }, t.value))
|
|
2014
|
-
]
|
|
2015
|
-
}
|
|
2016
|
-
),
|
|
2017
|
-
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
2018
|
-
] }) })
|
|
2039
|
+
availableTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2040
|
+
NativeSelect,
|
|
2041
|
+
{
|
|
2042
|
+
value: "",
|
|
2043
|
+
onChange: (e) => {
|
|
2044
|
+
if (e.target.value) addRule(e.target.value);
|
|
2045
|
+
},
|
|
2046
|
+
className: "h-8 text-xs text-muted-foreground",
|
|
2047
|
+
children: [
|
|
2048
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Add validation rule..." }),
|
|
2049
|
+
availableTypes.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t.value, children: t.label }, t.value))
|
|
2050
|
+
]
|
|
2051
|
+
}
|
|
2052
|
+
) })
|
|
2019
2053
|
] });
|
|
2020
2054
|
}
|
|
2021
2055
|
function RuleRow({
|
|
@@ -2029,13 +2063,15 @@ function RuleRow({
|
|
|
2029
2063
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2030
2064
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground", children: label }),
|
|
2031
2065
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2032
|
-
|
|
2066
|
+
fieldcraftReact.Button,
|
|
2033
2067
|
{
|
|
2034
2068
|
type: "button",
|
|
2069
|
+
variant: "ghost",
|
|
2070
|
+
size: "icon-xs",
|
|
2035
2071
|
onClick: onRemove,
|
|
2036
|
-
className: "opacity-0 group-hover:opacity-100 transition-opacity",
|
|
2072
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity hover:bg-destructive/10 hover:text-destructive",
|
|
2037
2073
|
title: "Remove rule",
|
|
2038
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12,
|
|
2074
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, strokeWidth: 1.75 })
|
|
2039
2075
|
}
|
|
2040
2076
|
)
|
|
2041
2077
|
] }),
|
|
@@ -2270,11 +2306,13 @@ function ConditionEditor({ question, schema, onUpdate }) {
|
|
|
2270
2306
|
!hasConditions && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mb-3", children: "Always visible. Add a rule to show this field conditionally." }),
|
|
2271
2307
|
hasConditions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 mb-3", children: showIf.conditions.map((cond, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2272
2308
|
index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2273
|
-
|
|
2309
|
+
fieldcraftReact.Button,
|
|
2274
2310
|
{
|
|
2275
2311
|
type: "button",
|
|
2312
|
+
variant: "link",
|
|
2313
|
+
size: "xs",
|
|
2276
2314
|
onClick: toggleCombine,
|
|
2277
|
-
className: "text-[10px] font-semibold uppercase tracking-wider
|
|
2315
|
+
className: "text-[10px] font-semibold uppercase tracking-wider px-0 mb-1.5",
|
|
2278
2316
|
children: showIf.combine ?? "AND"
|
|
2279
2317
|
}
|
|
2280
2318
|
),
|
|
@@ -2282,43 +2320,39 @@ function ConditionEditor({ question, schema, onUpdate }) {
|
|
|
2282
2320
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2283
2321
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground uppercase tracking-wider", children: "When" }),
|
|
2284
2322
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2285
|
-
|
|
2323
|
+
fieldcraftReact.Button,
|
|
2286
2324
|
{
|
|
2287
2325
|
type: "button",
|
|
2326
|
+
variant: "ghost",
|
|
2327
|
+
size: "icon-xs",
|
|
2288
2328
|
onClick: () => removeCondition(index),
|
|
2289
|
-
className: "opacity-0 group-hover:opacity-100 transition-opacity",
|
|
2329
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity hover:bg-destructive/10 hover:text-destructive",
|
|
2290
2330
|
title: "Remove condition",
|
|
2291
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12,
|
|
2331
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, strokeWidth: 1.75 })
|
|
2292
2332
|
}
|
|
2293
2333
|
)
|
|
2294
2334
|
] }),
|
|
2295
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
children:
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
"
|
|
2313
|
-
{
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
className: "flex h-7 w-full appearance-none rounded-md border border-input bg-card px-2 py-1 pr-7 text-xs shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
2317
|
-
children: OPERATORS.map((op) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: op.value, children: op.label }, op.value))
|
|
2318
|
-
}
|
|
2319
|
-
),
|
|
2320
|
-
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 size-3 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
2321
|
-
] }),
|
|
2335
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2336
|
+
NativeSelect,
|
|
2337
|
+
{
|
|
2338
|
+
value: cond.field ?? "",
|
|
2339
|
+
onChange: (e) => updateCondition(index, { field: e.target.value }),
|
|
2340
|
+
className: "h-7 text-xs",
|
|
2341
|
+
children: [
|
|
2342
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select field..." }),
|
|
2343
|
+
fieldOptions.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f.id, children: f.label }, f.id))
|
|
2344
|
+
]
|
|
2345
|
+
}
|
|
2346
|
+
),
|
|
2347
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2348
|
+
NativeSelect,
|
|
2349
|
+
{
|
|
2350
|
+
value: cond.operator ?? "eq",
|
|
2351
|
+
onChange: (e) => updateCondition(index, { operator: e.target.value }),
|
|
2352
|
+
className: "h-7 text-xs",
|
|
2353
|
+
children: OPERATORS.map((op) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: op.value, children: op.label }, op.value))
|
|
2354
|
+
}
|
|
2355
|
+
),
|
|
2322
2356
|
cond.operator !== "exists" && cond.operator !== "notExists" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2323
2357
|
fieldcraftReact.Input,
|
|
2324
2358
|
{
|
|
@@ -2331,11 +2365,13 @@ function ConditionEditor({ question, schema, onUpdate }) {
|
|
|
2331
2365
|
] })
|
|
2332
2366
|
] }, index)) }),
|
|
2333
2367
|
fieldOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2334
|
-
|
|
2368
|
+
fieldcraftReact.Button,
|
|
2335
2369
|
{
|
|
2336
2370
|
type: "button",
|
|
2371
|
+
variant: "link",
|
|
2372
|
+
size: "xs",
|
|
2337
2373
|
onClick: addCondition,
|
|
2338
|
-
className: "
|
|
2374
|
+
className: "px-0 gap-1.5",
|
|
2339
2375
|
children: [
|
|
2340
2376
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 12, strokeWidth: 2 }),
|
|
2341
2377
|
"Add condition"
|
|
@@ -2497,18 +2533,7 @@ function SettingsSelect({
|
|
|
2497
2533
|
}) {
|
|
2498
2534
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2499
2535
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
|
|
2500
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2501
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2502
|
-
"select",
|
|
2503
|
-
{
|
|
2504
|
-
value,
|
|
2505
|
-
onChange: (e) => onChange(e.target.value),
|
|
2506
|
-
className: "flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
2507
|
-
children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
2508
|
-
}
|
|
2509
|
-
),
|
|
2510
|
-
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
2511
|
-
] })
|
|
2536
|
+
/* @__PURE__ */ jsxRuntime.jsx(NativeSelect, { value, onChange: (e) => onChange(e.target.value), children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value)) })
|
|
2512
2537
|
] });
|
|
2513
2538
|
}
|
|
2514
2539
|
function PropertiesPanel({ builderState }) {
|
|
@@ -2610,13 +2635,14 @@ function QuestionProperties({ question, sectionId, builderState, onClose, onOpen
|
|
|
2610
2635
|
typeInfo?.label
|
|
2611
2636
|
] }) }),
|
|
2612
2637
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 px-4 pt-3 flex gap-0.5", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2613
|
-
|
|
2638
|
+
fieldcraftReact.Button,
|
|
2614
2639
|
{
|
|
2615
2640
|
type: "button",
|
|
2641
|
+
variant: activeTab === tab.key ? "default" : "ghost",
|
|
2642
|
+
size: "xs",
|
|
2616
2643
|
onClick: () => setActiveTab(tab.key),
|
|
2617
2644
|
className: cn(
|
|
2618
|
-
|
|
2619
|
-
activeTab === tab.key ? "bg-primary text-primary-foreground" : "bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
2645
|
+
activeTab !== tab.key && "text-muted-foreground"
|
|
2620
2646
|
),
|
|
2621
2647
|
children: tab.label
|
|
2622
2648
|
},
|
|
@@ -2875,13 +2901,18 @@ function FormBuilderCore(props) {
|
|
|
2875
2901
|
reader.onload = (event) => {
|
|
2876
2902
|
try {
|
|
2877
2903
|
const parsed = JSON.parse(event.target?.result);
|
|
2878
|
-
|
|
2879
|
-
|
|
2904
|
+
const validated = fieldcraftCore.validateSchema(parsed);
|
|
2905
|
+
builderState.resetSchema(validated);
|
|
2906
|
+
} catch (err) {
|
|
2907
|
+
if (err instanceof fieldcraftCore.FormEngineSchemaError) {
|
|
2908
|
+
const details = err.issues.slice(0, 3).map((i) => `\u2022 ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
2909
|
+
alert(`Invalid schema:
|
|
2910
|
+
${details}`);
|
|
2911
|
+
} else if (err instanceof SyntaxError) {
|
|
2912
|
+
alert("Failed to parse JSON file.");
|
|
2880
2913
|
} else {
|
|
2881
|
-
alert("Invalid schema
|
|
2914
|
+
alert("Invalid schema file.");
|
|
2882
2915
|
}
|
|
2883
|
-
} catch {
|
|
2884
|
-
alert("Failed to parse JSON file.");
|
|
2885
2916
|
}
|
|
2886
2917
|
};
|
|
2887
2918
|
reader.readAsText(file);
|
|
@@ -3166,74 +3197,190 @@ var cleanPreset = {
|
|
|
3166
3197
|
borderStrong: "#d1d5db",
|
|
3167
3198
|
textDim: "#9ca3af"
|
|
3168
3199
|
};
|
|
3200
|
+
|
|
3201
|
+
// src/response-viewer/clinical-display-data.ts
|
|
3202
|
+
var BODY_REGION_LABELS = {
|
|
3203
|
+
head: "Head",
|
|
3204
|
+
neck: "Neck",
|
|
3205
|
+
"left-shoulder": "Left Shoulder",
|
|
3206
|
+
"right-shoulder": "Right Shoulder",
|
|
3207
|
+
chest: "Chest",
|
|
3208
|
+
abdomen: "Abdomen",
|
|
3209
|
+
"left-upper-arm": "Left Upper Arm",
|
|
3210
|
+
"right-upper-arm": "Right Upper Arm",
|
|
3211
|
+
"left-forearm": "Left Forearm",
|
|
3212
|
+
"right-forearm": "Right Forearm",
|
|
3213
|
+
"left-hand": "Left Hand",
|
|
3214
|
+
"right-hand": "Right Hand",
|
|
3215
|
+
pelvis: "Pelvis",
|
|
3216
|
+
"left-thigh": "Left Thigh",
|
|
3217
|
+
"right-thigh": "Right Thigh",
|
|
3218
|
+
"left-knee": "Left Knee",
|
|
3219
|
+
"right-knee": "Right Knee",
|
|
3220
|
+
"left-shin": "Left Shin",
|
|
3221
|
+
"right-shin": "Right Shin",
|
|
3222
|
+
"left-foot": "Left Foot",
|
|
3223
|
+
"right-foot": "Right Foot"
|
|
3224
|
+
};
|
|
3225
|
+
var PAIN_FACE_LABELS = [
|
|
3226
|
+
{ score: 0, label: "No Hurt", color: "#22c55e" },
|
|
3227
|
+
{ score: 2, label: "Hurts Little Bit", color: "#84cc16" },
|
|
3228
|
+
{ score: 4, label: "Hurts Little More", color: "#eab308" },
|
|
3229
|
+
{ score: 6, label: "Hurts Even More", color: "#f97316" },
|
|
3230
|
+
{ score: 8, label: "Hurts Whole Lot", color: "#ef4444" },
|
|
3231
|
+
{ score: 10, label: "Hurts Worst", color: "#dc2626" }
|
|
3232
|
+
];
|
|
3233
|
+
function getPainLabel(score) {
|
|
3234
|
+
const face = PAIN_FACE_LABELS.reduce(
|
|
3235
|
+
(closest, f) => closest == null || Math.abs(f.score - score) < Math.abs(closest.score - score) ? f : closest,
|
|
3236
|
+
void 0
|
|
3237
|
+
);
|
|
3238
|
+
return face;
|
|
3239
|
+
}
|
|
3240
|
+
var SEVERITY_COLORS = {
|
|
3241
|
+
mild: "#eab308",
|
|
3242
|
+
moderate: "#f97316",
|
|
3243
|
+
severe: "#ef4444",
|
|
3244
|
+
"life-threatening": "#991b1b"
|
|
3245
|
+
};
|
|
3246
|
+
var BMI_CATEGORIES = [
|
|
3247
|
+
{ label: "Underweight", max: 18.5, color: "#3b82f6" },
|
|
3248
|
+
{ label: "Normal", max: 25, color: "#22c55e" },
|
|
3249
|
+
{ label: "Overweight", max: 30, color: "#eab308" },
|
|
3250
|
+
{ label: "Obese", max: Infinity, color: "#ef4444" }
|
|
3251
|
+
];
|
|
3252
|
+
function getBmiCategory(bmi) {
|
|
3253
|
+
return BMI_CATEGORIES.find((c) => bmi < c.max) ?? BMI_CATEGORIES[BMI_CATEGORIES.length - 1];
|
|
3254
|
+
}
|
|
3255
|
+
var INSTRUMENT_DISPLAY_NAMES = {
|
|
3256
|
+
phq9: "PHQ-9",
|
|
3257
|
+
phq2: "PHQ-2",
|
|
3258
|
+
gad7: "GAD-7",
|
|
3259
|
+
gad2: "GAD-2",
|
|
3260
|
+
"audit-c": "AUDIT-C",
|
|
3261
|
+
dast10: "DAST-10",
|
|
3262
|
+
cssrs: "C-SSRS",
|
|
3263
|
+
epds: "EPDS",
|
|
3264
|
+
isi: "ISI",
|
|
3265
|
+
pcl5: "PCL-5",
|
|
3266
|
+
psc17: "PSC-17"
|
|
3267
|
+
};
|
|
3268
|
+
var SCORE_THRESHOLDS = {
|
|
3269
|
+
phq9: [
|
|
3270
|
+
{ min: 0, max: 4, label: "Minimal", color: "#22c55e" },
|
|
3271
|
+
{ min: 5, max: 9, label: "Mild", color: "#eab308" },
|
|
3272
|
+
{ min: 10, max: 14, label: "Moderate", color: "#f97316" },
|
|
3273
|
+
{ min: 15, max: 19, label: "Moderately Severe", color: "#ef4444" },
|
|
3274
|
+
{ min: 20, max: 27, label: "Severe", color: "#dc2626" }
|
|
3275
|
+
],
|
|
3276
|
+
phq2: [
|
|
3277
|
+
{ min: 0, max: 2, label: "Negative Screen", color: "#22c55e" },
|
|
3278
|
+
{ min: 3, max: 6, label: "Positive Screen", color: "#ef4444" }
|
|
3279
|
+
],
|
|
3280
|
+
gad7: [
|
|
3281
|
+
{ min: 0, max: 4, label: "Minimal", color: "#22c55e" },
|
|
3282
|
+
{ min: 5, max: 9, label: "Mild", color: "#eab308" },
|
|
3283
|
+
{ min: 10, max: 14, label: "Moderate", color: "#f97316" },
|
|
3284
|
+
{ min: 15, max: 21, label: "Severe", color: "#ef4444" }
|
|
3285
|
+
],
|
|
3286
|
+
gad2: [
|
|
3287
|
+
{ min: 0, max: 2, label: "Negative Screen", color: "#22c55e" },
|
|
3288
|
+
{ min: 3, max: 6, label: "Positive Screen", color: "#ef4444" }
|
|
3289
|
+
],
|
|
3290
|
+
"audit-c": [
|
|
3291
|
+
{ min: 0, max: 3, label: "Low Risk", color: "#22c55e" },
|
|
3292
|
+
{ min: 4, max: 7, label: "At Risk", color: "#eab308" },
|
|
3293
|
+
{ min: 8, max: 12, label: "High Risk", color: "#ef4444" }
|
|
3294
|
+
],
|
|
3295
|
+
dast10: [
|
|
3296
|
+
{ min: 0, max: 0, label: "No Problems", color: "#22c55e" },
|
|
3297
|
+
{ min: 1, max: 2, label: "Low Level", color: "#eab308" },
|
|
3298
|
+
{ min: 3, max: 5, label: "Moderate Level", color: "#f97316" },
|
|
3299
|
+
{ min: 6, max: 8, label: "Substantial Level", color: "#ef4444" },
|
|
3300
|
+
{ min: 9, max: 10, label: "Severe Level", color: "#dc2626" }
|
|
3301
|
+
],
|
|
3302
|
+
cssrs: [
|
|
3303
|
+
{ min: 0, max: 0, label: "No Risk Identified", color: "#22c55e" },
|
|
3304
|
+
{ min: 1, max: 1, label: "Wish to be Dead", color: "#eab308" },
|
|
3305
|
+
{ min: 2, max: 2, label: "Non-specific Active Suicidal Thoughts", color: "#f97316" },
|
|
3306
|
+
{ min: 3, max: 3, label: "Active Suicidal Ideation", color: "#ef4444" },
|
|
3307
|
+
{ min: 4, max: 4, label: "Active Ideation with Intent", color: "#dc2626" },
|
|
3308
|
+
{ min: 5, max: 5, label: "Active Ideation with Plan", color: "#991b1b" },
|
|
3309
|
+
{ min: 6, max: 6, label: "Imminent Risk", color: "#7f1d1d" }
|
|
3310
|
+
],
|
|
3311
|
+
epds: [
|
|
3312
|
+
{ min: 0, max: 8, label: "Low Likelihood", color: "#22c55e" },
|
|
3313
|
+
{ min: 9, max: 11, label: "Possible Depression", color: "#eab308" },
|
|
3314
|
+
{ min: 12, max: 13, label: "Fairly High Possibility", color: "#f97316" },
|
|
3315
|
+
{ min: 14, max: 30, label: "Probable Depression", color: "#ef4444" }
|
|
3316
|
+
],
|
|
3317
|
+
isi: [
|
|
3318
|
+
{ min: 0, max: 7, label: "No Clinically Significant Insomnia", color: "#22c55e" },
|
|
3319
|
+
{ min: 8, max: 14, label: "Subthreshold Insomnia", color: "#eab308" },
|
|
3320
|
+
{ min: 15, max: 21, label: "Clinical Insomnia (Moderate)", color: "#f97316" },
|
|
3321
|
+
{ min: 22, max: 28, label: "Clinical Insomnia (Severe)", color: "#ef4444" }
|
|
3322
|
+
],
|
|
3323
|
+
pcl5: [
|
|
3324
|
+
{ min: 0, max: 30, label: "Below Threshold", color: "#22c55e" },
|
|
3325
|
+
{ min: 31, max: 80, label: "Probable PTSD", color: "#ef4444" }
|
|
3326
|
+
],
|
|
3327
|
+
psc17: [
|
|
3328
|
+
{ min: 0, max: 14, label: "No Significant Concerns", color: "#22c55e" },
|
|
3329
|
+
{ min: 15, max: 34, label: "Significant Concerns", color: "#ef4444" }
|
|
3330
|
+
]
|
|
3331
|
+
};
|
|
3332
|
+
function getScoreSeverity(instrumentKey, score) {
|
|
3333
|
+
const thresholds = SCORE_THRESHOLDS[instrumentKey];
|
|
3334
|
+
if (!thresholds) return void 0;
|
|
3335
|
+
return thresholds.find((t) => score >= t.min && score <= t.max);
|
|
3336
|
+
}
|
|
3169
3337
|
function ResponseTable({ schema, responses, onRowClick }) {
|
|
3170
3338
|
const questions = getAllQuestions(schema);
|
|
3171
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
3172
|
-
"
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
onMouseLeave: (e) => {
|
|
3198
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
3339
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-[13px]", children: [
|
|
3340
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "bg-muted", children: [
|
|
3341
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Submitted" }),
|
|
3342
|
+
questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3343
|
+
"th",
|
|
3344
|
+
{
|
|
3345
|
+
className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap",
|
|
3346
|
+
children: q.label
|
|
3347
|
+
},
|
|
3348
|
+
q.id
|
|
3349
|
+
)),
|
|
3350
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Score" })
|
|
3351
|
+
] }) }),
|
|
3352
|
+
/* @__PURE__ */ jsxRuntime.jsxs("tbody", { children: [
|
|
3353
|
+
responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3354
|
+
"tr",
|
|
3355
|
+
{
|
|
3356
|
+
onClick: () => onRowClick?.(response),
|
|
3357
|
+
className: onRowClick ? "cursor-pointer border-b border-border hover:bg-accent transition-colors" : "border-b border-border",
|
|
3358
|
+
children: [
|
|
3359
|
+
/* @__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() }),
|
|
3360
|
+
questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3361
|
+
"td",
|
|
3362
|
+
{
|
|
3363
|
+
className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap",
|
|
3364
|
+
children: formatCellValue(response.values[q.id], q.type)
|
|
3199
3365
|
},
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
]
|
|
3218
|
-
}
|
|
3219
|
-
) });
|
|
3366
|
+
q.id
|
|
3367
|
+
)),
|
|
3368
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap", children: response.totalScore ?? "\u2014" })
|
|
3369
|
+
]
|
|
3370
|
+
},
|
|
3371
|
+
response.sessionToken || idx
|
|
3372
|
+
)),
|
|
3373
|
+
responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3374
|
+
"td",
|
|
3375
|
+
{
|
|
3376
|
+
colSpan: questions.length + 2,
|
|
3377
|
+
className: "px-3 py-8 text-center text-muted-foreground",
|
|
3378
|
+
children: "No responses yet"
|
|
3379
|
+
}
|
|
3380
|
+
) })
|
|
3381
|
+
] })
|
|
3382
|
+
] }) });
|
|
3220
3383
|
}
|
|
3221
|
-
var thStyle = {
|
|
3222
|
-
padding: "8px 12px",
|
|
3223
|
-
textAlign: "left",
|
|
3224
|
-
fontWeight: 600,
|
|
3225
|
-
color: "var(--foreground, #e8e8ea)",
|
|
3226
|
-
borderBottom: "2px solid var(--border, #1c1c20)",
|
|
3227
|
-
whiteSpace: "nowrap"
|
|
3228
|
-
};
|
|
3229
|
-
var tdStyle = {
|
|
3230
|
-
padding: "8px 12px",
|
|
3231
|
-
color: "var(--foreground, #e8e8ea)",
|
|
3232
|
-
maxWidth: "200px",
|
|
3233
|
-
overflow: "hidden",
|
|
3234
|
-
textOverflow: "ellipsis",
|
|
3235
|
-
whiteSpace: "nowrap"
|
|
3236
|
-
};
|
|
3237
3384
|
function getAllQuestions(schema) {
|
|
3238
3385
|
const questions = [];
|
|
3239
3386
|
for (const section of schema.sections) {
|
|
@@ -3246,8 +3393,69 @@ function getAllQuestions(schema) {
|
|
|
3246
3393
|
}
|
|
3247
3394
|
return questions;
|
|
3248
3395
|
}
|
|
3249
|
-
function formatCellValue(value) {
|
|
3396
|
+
function formatCellValue(value, type) {
|
|
3250
3397
|
if (value == null) return "\u2014";
|
|
3398
|
+
if (type) {
|
|
3399
|
+
switch (type) {
|
|
3400
|
+
case "vitals_entry": {
|
|
3401
|
+
if (typeof value !== "object" || value === null) break;
|
|
3402
|
+
const v = value;
|
|
3403
|
+
const parts = [];
|
|
3404
|
+
if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
|
|
3405
|
+
if (v.heartRate) parts.push(`HR ${v.heartRate}`);
|
|
3406
|
+
if (v.temperature) parts.push(`${v.temperature}\xB0F`);
|
|
3407
|
+
if (v.oxygenSaturation) parts.push(`SpO\u2082 ${v.oxygenSaturation}%`);
|
|
3408
|
+
return parts.length > 0 ? parts.join(", ") : "\u2014";
|
|
3409
|
+
}
|
|
3410
|
+
case "medication_list":
|
|
3411
|
+
if (Array.isArray(value)) return `${value.length} medication${value.length !== 1 ? "s" : ""}`;
|
|
3412
|
+
break;
|
|
3413
|
+
case "allergy_list":
|
|
3414
|
+
if (Array.isArray(value)) return `${value.length} allerg${value.length !== 1 ? "ies" : "y"}`;
|
|
3415
|
+
break;
|
|
3416
|
+
case "body_diagram":
|
|
3417
|
+
if (Array.isArray(value)) {
|
|
3418
|
+
const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
|
|
3419
|
+
return labels.join(", ");
|
|
3420
|
+
}
|
|
3421
|
+
break;
|
|
3422
|
+
case "pain_scale":
|
|
3423
|
+
if (typeof value === "number") return `${value}/10`;
|
|
3424
|
+
break;
|
|
3425
|
+
case "bmi_calculator": {
|
|
3426
|
+
if (typeof value !== "object" || value === null) break;
|
|
3427
|
+
const d = value;
|
|
3428
|
+
if (d.bmi != null) return `BMI ${d.bmi}`;
|
|
3429
|
+
break;
|
|
3430
|
+
}
|
|
3431
|
+
case "payment": {
|
|
3432
|
+
if (typeof value !== "object" || value === null) break;
|
|
3433
|
+
const p = value;
|
|
3434
|
+
const status = p.status;
|
|
3435
|
+
return status ? status.charAt(0).toUpperCase() + status.slice(1) : "\u2014";
|
|
3436
|
+
}
|
|
3437
|
+
case "insurance_card": {
|
|
3438
|
+
if (typeof value !== "object" || value === null) break;
|
|
3439
|
+
const ins = value;
|
|
3440
|
+
const parts = [ins.carrierId, ins.planName].filter(Boolean);
|
|
3441
|
+
return parts.length > 0 ? parts.join(" \u2014 ") : "Card uploaded";
|
|
3442
|
+
}
|
|
3443
|
+
case "legal_name": {
|
|
3444
|
+
if (typeof value !== "object" || value === null) break;
|
|
3445
|
+
const n = value;
|
|
3446
|
+
return [n.first, n.last].filter(Boolean).join(" ") || "\u2014";
|
|
3447
|
+
}
|
|
3448
|
+
case "address": {
|
|
3449
|
+
if (typeof value !== "object" || value === null) break;
|
|
3450
|
+
const a = value;
|
|
3451
|
+
return [a.city, a.state].filter(Boolean).join(", ") || "\u2014";
|
|
3452
|
+
}
|
|
3453
|
+
case "consent":
|
|
3454
|
+
return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
|
|
3455
|
+
case "signature":
|
|
3456
|
+
return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "\u2014";
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3251
3459
|
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
3252
3460
|
if (Array.isArray(value)) return value.join(", ");
|
|
3253
3461
|
if (typeof value === "object") return JSON.stringify(value);
|
|
@@ -3258,238 +3466,570 @@ function ResponseCard({ response, fields, onClick }) {
|
|
|
3258
3466
|
"div",
|
|
3259
3467
|
{
|
|
3260
3468
|
onClick,
|
|
3261
|
-
|
|
3262
|
-
border: "1px solid var(--border, #1c1c20)",
|
|
3263
|
-
borderRadius: "8px",
|
|
3264
|
-
padding: "16px",
|
|
3265
|
-
backgroundColor: "var(--card, #111113)",
|
|
3266
|
-
cursor: onClick ? "pointer" : "default",
|
|
3267
|
-
fontFamily: "inherit",
|
|
3268
|
-
transition: "box-shadow 0.15s"
|
|
3269
|
-
},
|
|
3270
|
-
onMouseEnter: (e) => {
|
|
3271
|
-
if (onClick) e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.3)";
|
|
3272
|
-
},
|
|
3273
|
-
onMouseLeave: (e) => {
|
|
3274
|
-
e.currentTarget.style.boxShadow = "none";
|
|
3275
|
-
},
|
|
3469
|
+
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",
|
|
3276
3470
|
children: [
|
|
3277
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3278
|
-
"
|
|
3279
|
-
{
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
children: [
|
|
3288
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(response.submittedAt).toLocaleString() }),
|
|
3289
|
-
response.completionTimeMs != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3290
|
-
Math.round(response.completionTimeMs / 1e3),
|
|
3291
|
-
"s"
|
|
3292
|
-
] })
|
|
3293
|
-
]
|
|
3294
|
-
}
|
|
3295
|
-
),
|
|
3296
|
-
fields.slice(0, 4).map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "8px" }, children: [
|
|
3297
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "11px", color: "var(--muted-foreground, #8a8a95)", marginBottom: "2px" }, children: field.label }),
|
|
3298
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "13px", color: "var(--foreground, #e8e8ea)" }, children: formatValue(field.value) })
|
|
3471
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between mb-3 text-xs text-muted-foreground", children: [
|
|
3472
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(response.submittedAt).toLocaleString() }),
|
|
3473
|
+
response.completionTimeMs != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3474
|
+
Math.round(response.completionTimeMs / 1e3),
|
|
3475
|
+
"s"
|
|
3476
|
+
] })
|
|
3477
|
+
] }),
|
|
3478
|
+
fields.slice(0, 4).map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2", children: [
|
|
3479
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-muted-foreground mb-0.5", children: field.label }),
|
|
3480
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[13px] text-foreground", children: formatCardValue(field.value, field.type) })
|
|
3299
3481
|
] }, field.questionId)),
|
|
3300
|
-
fields.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
3482
|
+
fields.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mt-2", children: [
|
|
3301
3483
|
"+",
|
|
3302
3484
|
fields.length - 4,
|
|
3303
3485
|
" more fields"
|
|
3304
3486
|
] }),
|
|
3305
|
-
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3306
|
-
"
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
marginTop: "12px",
|
|
3310
|
-
paddingTop: "8px",
|
|
3311
|
-
borderTop: "1px solid var(--border, #1c1c20)",
|
|
3312
|
-
fontSize: "13px",
|
|
3313
|
-
fontWeight: 600,
|
|
3314
|
-
color: "var(--foreground, #e8e8ea)"
|
|
3315
|
-
},
|
|
3316
|
-
children: [
|
|
3317
|
-
"Score: ",
|
|
3318
|
-
response.totalScore
|
|
3319
|
-
]
|
|
3320
|
-
}
|
|
3321
|
-
)
|
|
3487
|
+
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 pt-2 border-t border-border text-[13px] font-semibold text-foreground", children: [
|
|
3488
|
+
"Score: ",
|
|
3489
|
+
response.totalScore
|
|
3490
|
+
] })
|
|
3322
3491
|
]
|
|
3323
3492
|
}
|
|
3324
3493
|
);
|
|
3325
3494
|
}
|
|
3326
|
-
function
|
|
3495
|
+
function formatCardValue(value, type) {
|
|
3327
3496
|
if (value == null) return "\u2014";
|
|
3497
|
+
if (type) {
|
|
3498
|
+
switch (type) {
|
|
3499
|
+
case "vitals_entry": {
|
|
3500
|
+
if (typeof value !== "object" || value === null) break;
|
|
3501
|
+
const v = value;
|
|
3502
|
+
const parts = [];
|
|
3503
|
+
if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
|
|
3504
|
+
if (v.heartRate) parts.push(`HR ${v.heartRate}`);
|
|
3505
|
+
if (v.temperature) parts.push(`${v.temperature}\xB0F`);
|
|
3506
|
+
if (v.oxygenSaturation) parts.push(`SpO\u2082 ${v.oxygenSaturation}%`);
|
|
3507
|
+
return parts.length > 0 ? parts.join(", ") : "\u2014";
|
|
3508
|
+
}
|
|
3509
|
+
case "medication_list":
|
|
3510
|
+
if (Array.isArray(value)) return `${value.length} medication${value.length !== 1 ? "s" : ""}`;
|
|
3511
|
+
break;
|
|
3512
|
+
case "allergy_list":
|
|
3513
|
+
if (Array.isArray(value)) return `${value.length} allerg${value.length !== 1 ? "ies" : "y"}`;
|
|
3514
|
+
break;
|
|
3515
|
+
case "body_diagram":
|
|
3516
|
+
if (Array.isArray(value)) {
|
|
3517
|
+
const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
|
|
3518
|
+
return labels.join(", ");
|
|
3519
|
+
}
|
|
3520
|
+
break;
|
|
3521
|
+
case "pain_scale":
|
|
3522
|
+
if (typeof value === "number") return `${value}/10`;
|
|
3523
|
+
break;
|
|
3524
|
+
case "bmi_calculator": {
|
|
3525
|
+
if (typeof value !== "object" || value === null) break;
|
|
3526
|
+
const d = value;
|
|
3527
|
+
if (d.bmi != null) return `BMI ${d.bmi}`;
|
|
3528
|
+
break;
|
|
3529
|
+
}
|
|
3530
|
+
case "payment": {
|
|
3531
|
+
if (typeof value !== "object" || value === null) break;
|
|
3532
|
+
const p = value;
|
|
3533
|
+
const status = p.status;
|
|
3534
|
+
return status ? status.charAt(0).toUpperCase() + status.slice(1) : "\u2014";
|
|
3535
|
+
}
|
|
3536
|
+
case "insurance_card": {
|
|
3537
|
+
if (typeof value !== "object" || value === null) break;
|
|
3538
|
+
const ins = value;
|
|
3539
|
+
const parts = [ins.carrierId, ins.planName].filter(Boolean);
|
|
3540
|
+
return parts.length > 0 ? parts.join(" \u2014 ") : "Card uploaded";
|
|
3541
|
+
}
|
|
3542
|
+
case "legal_name": {
|
|
3543
|
+
if (typeof value !== "object" || value === null) break;
|
|
3544
|
+
const n = value;
|
|
3545
|
+
return [n.first, n.last].filter(Boolean).join(" ") || "\u2014";
|
|
3546
|
+
}
|
|
3547
|
+
case "address": {
|
|
3548
|
+
if (typeof value !== "object" || value === null) break;
|
|
3549
|
+
const a = value;
|
|
3550
|
+
return [a.city, a.state].filter(Boolean).join(", ") || "\u2014";
|
|
3551
|
+
}
|
|
3552
|
+
case "consent":
|
|
3553
|
+
return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
|
|
3554
|
+
case "signature":
|
|
3555
|
+
return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "\u2014";
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3328
3558
|
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
3329
|
-
if (Array.isArray(value)) return value.map(
|
|
3559
|
+
if (Array.isArray(value)) return value.map(String).join(", ");
|
|
3330
3560
|
if (typeof value === "object") return JSON.stringify(value);
|
|
3331
3561
|
return String(value);
|
|
3332
3562
|
}
|
|
3333
3563
|
function ResponseDetail({ response, fields, onBack }) {
|
|
3334
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
3335
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3336
|
-
"
|
|
3337
|
-
{
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
onBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3348
|
-
"button",
|
|
3349
|
-
{
|
|
3350
|
-
type: "button",
|
|
3351
|
-
onClick: onBack,
|
|
3352
|
-
style: {
|
|
3353
|
-
padding: "4px 10px",
|
|
3354
|
-
fontSize: "13px",
|
|
3355
|
-
color: "var(--secondary-foreground, #e8e8ea)",
|
|
3356
|
-
backgroundColor: "var(--secondary, #17171a)",
|
|
3357
|
-
border: "1px solid var(--border, #1c1c20)",
|
|
3358
|
-
borderRadius: "6px",
|
|
3359
|
-
cursor: "pointer"
|
|
3360
|
-
},
|
|
3361
|
-
children: "Back"
|
|
3362
|
-
}
|
|
3363
|
-
),
|
|
3364
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3365
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "14px", fontWeight: 600, color: "var(--foreground, #e8e8ea)" }, children: "Response Detail" }),
|
|
3366
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "var(--muted-foreground, #8a8a95)" }, children: [
|
|
3367
|
-
"Submitted ",
|
|
3368
|
-
new Date(response.submittedAt).toLocaleString(),
|
|
3369
|
-
response.completionTimeMs != null && ` \u2014 ${Math.round(response.completionTimeMs / 1e3)}s`
|
|
3370
|
-
] })
|
|
3371
|
-
] })
|
|
3372
|
-
]
|
|
3373
|
-
}
|
|
3374
|
-
),
|
|
3375
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3564
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3565
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-5 pb-3 border-b border-border", children: [
|
|
3566
|
+
onBack && /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "outline", size: "sm", onClick: onBack, children: "Back" }),
|
|
3567
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3568
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-foreground", children: "Response Detail" }),
|
|
3569
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
3570
|
+
"Submitted ",
|
|
3571
|
+
new Date(response.submittedAt).toLocaleString(),
|
|
3572
|
+
response.completionTimeMs != null && ` \u2014 ${Math.round(response.completionTimeMs / 1e3)}s`
|
|
3573
|
+
] })
|
|
3574
|
+
] })
|
|
3575
|
+
] }),
|
|
3576
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-4", children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3376
3577
|
"div",
|
|
3377
3578
|
{
|
|
3378
|
-
|
|
3379
|
-
padding: "12px",
|
|
3380
|
-
backgroundColor: "var(--muted, #111113)",
|
|
3381
|
-
borderRadius: "6px"
|
|
3382
|
-
},
|
|
3579
|
+
className: "p-3 bg-muted rounded-md",
|
|
3383
3580
|
children: [
|
|
3384
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
3581
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mb-1", children: [
|
|
3385
3582
|
field.label,
|
|
3386
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", {
|
|
3583
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-2 text-[11px] text-muted-foreground", children: [
|
|
3387
3584
|
"(",
|
|
3388
3585
|
field.type,
|
|
3389
3586
|
")"
|
|
3390
3587
|
] })
|
|
3391
3588
|
] }),
|
|
3392
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
3589
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-foreground", children: renderFieldValue(field) })
|
|
3393
3590
|
]
|
|
3394
3591
|
},
|
|
3395
3592
|
field.questionId
|
|
3396
3593
|
)) }),
|
|
3397
|
-
response.scores && Object.keys(response.scores).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
3398
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3399
|
-
|
|
3400
|
-
{
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
key,
|
|
3423
|
-
": "
|
|
3424
|
-
] }),
|
|
3425
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "var(--primary, oklch(0.82 0.14 210))" }, children: value })
|
|
3426
|
-
]
|
|
3427
|
-
},
|
|
3428
|
-
key
|
|
3429
|
-
)),
|
|
3430
|
-
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3431
|
-
"div",
|
|
3432
|
-
{
|
|
3433
|
-
style: {
|
|
3434
|
-
padding: "8px 16px",
|
|
3435
|
-
backgroundColor: "var(--accent, #17171a)",
|
|
3436
|
-
borderRadius: "6px",
|
|
3437
|
-
fontSize: "13px"
|
|
3594
|
+
response.scores && Object.keys(response.scores).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5", children: [
|
|
3595
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-foreground mb-3", children: "Scores" }),
|
|
3596
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 flex-wrap", children: [
|
|
3597
|
+
Object.entries(response.scores).map(([key, value]) => {
|
|
3598
|
+
const displayName = INSTRUMENT_DISPLAY_NAMES[key] ?? key;
|
|
3599
|
+
const severity = typeof value === "number" ? getScoreSeverity(key, value) : void 0;
|
|
3600
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3601
|
+
"div",
|
|
3602
|
+
{
|
|
3603
|
+
className: "px-4 py-2 bg-accent rounded-md text-[13px] flex items-center gap-2",
|
|
3604
|
+
children: [
|
|
3605
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
3606
|
+
displayName,
|
|
3607
|
+
": "
|
|
3608
|
+
] }),
|
|
3609
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-primary", children: value }),
|
|
3610
|
+
severity && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3611
|
+
"span",
|
|
3612
|
+
{
|
|
3613
|
+
className: "text-[11px] font-semibold px-2 py-0.5 rounded-full text-white",
|
|
3614
|
+
style: { backgroundColor: severity.color },
|
|
3615
|
+
children: severity.label
|
|
3616
|
+
}
|
|
3617
|
+
)
|
|
3618
|
+
]
|
|
3438
3619
|
},
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
}
|
|
3444
|
-
|
|
3620
|
+
key
|
|
3621
|
+
);
|
|
3622
|
+
}),
|
|
3623
|
+
response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 bg-accent rounded-md text-[13px]", children: [
|
|
3624
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Total: " }),
|
|
3625
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-primary", children: response.totalScore })
|
|
3626
|
+
] })
|
|
3445
3627
|
] })
|
|
3446
3628
|
] }),
|
|
3447
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3629
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 pt-3 border-t border-border text-xs text-muted-foreground", children: [
|
|
3630
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3631
|
+
"Schema: ",
|
|
3632
|
+
response.schemaId,
|
|
3633
|
+
" (v",
|
|
3634
|
+
response.schemaVersion,
|
|
3635
|
+
")"
|
|
3636
|
+
] }),
|
|
3637
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3638
|
+
"Session: ",
|
|
3639
|
+
response.sessionToken
|
|
3640
|
+
] })
|
|
3641
|
+
] })
|
|
3642
|
+
] });
|
|
3643
|
+
}
|
|
3644
|
+
function renderFieldValue(field) {
|
|
3645
|
+
const { type, value } = field;
|
|
3646
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
3647
|
+
switch (type) {
|
|
3648
|
+
case "vitals_entry":
|
|
3649
|
+
return renderVitals(value);
|
|
3650
|
+
case "medication_list":
|
|
3651
|
+
return renderMedications(value);
|
|
3652
|
+
case "allergy_list":
|
|
3653
|
+
return renderAllergies(value);
|
|
3654
|
+
case "bmi_calculator":
|
|
3655
|
+
return renderBmi(value);
|
|
3656
|
+
case "body_diagram":
|
|
3657
|
+
return renderBodyDiagram(value);
|
|
3658
|
+
case "pain_scale":
|
|
3659
|
+
return renderPainScale(value);
|
|
3660
|
+
case "insurance_card":
|
|
3661
|
+
return renderInsurance(value);
|
|
3662
|
+
case "payment":
|
|
3663
|
+
return renderPayment(value);
|
|
3664
|
+
case "legal_name":
|
|
3665
|
+
return renderLegalName(value);
|
|
3666
|
+
case "address":
|
|
3667
|
+
return renderAddress(value);
|
|
3668
|
+
case "consent":
|
|
3669
|
+
return renderConsent(value);
|
|
3670
|
+
case "signature":
|
|
3671
|
+
return renderSignature(value);
|
|
3672
|
+
default:
|
|
3673
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-pre-wrap", children: formatFallbackValue(value) });
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
var VITALS_LABELS = {
|
|
3677
|
+
systolicBp: { label: "Systolic BP", unit: "mmHg", normalRange: "90-140" },
|
|
3678
|
+
diastolicBp: { label: "Diastolic BP", unit: "mmHg", normalRange: "60-90" },
|
|
3679
|
+
heartRate: { label: "Heart Rate", unit: "bpm", normalRange: "60-100" },
|
|
3680
|
+
temperature: { label: "Temperature", unit: "\xB0F", normalRange: "97.0-99.5" },
|
|
3681
|
+
respiratoryRate: { label: "Respiratory Rate", unit: "/min", normalRange: "12-20" },
|
|
3682
|
+
oxygenSaturation: { label: "SpO\u2082", unit: "%", normalRange: "95-100" }
|
|
3683
|
+
};
|
|
3684
|
+
function renderVitals(value) {
|
|
3685
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
3686
|
+
const vitals = value;
|
|
3687
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: Object.entries(VITALS_LABELS).map(([key, meta]) => {
|
|
3688
|
+
const val = vitals[key];
|
|
3689
|
+
if (val == null || val === 0) return null;
|
|
3690
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[13px]", children: [
|
|
3691
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
3692
|
+
meta.label,
|
|
3693
|
+
": "
|
|
3694
|
+
] }),
|
|
3695
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
3696
|
+
String(val),
|
|
3697
|
+
" ",
|
|
3698
|
+
meta.unit
|
|
3699
|
+
] }),
|
|
3700
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-muted-foreground ml-1", children: [
|
|
3701
|
+
"(",
|
|
3702
|
+
meta.normalRange,
|
|
3703
|
+
")"
|
|
3704
|
+
] })
|
|
3705
|
+
] }, key);
|
|
3706
|
+
}) });
|
|
3707
|
+
}
|
|
3708
|
+
function renderMedications(value) {
|
|
3709
|
+
if (!Array.isArray(value) || value.length === 0) return "No medications";
|
|
3710
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: value.map((med, idx) => {
|
|
3711
|
+
const m = med;
|
|
3712
|
+
const parts = [m.dosage, m.frequency, m.route].filter(Boolean);
|
|
3713
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3448
3714
|
"div",
|
|
3449
3715
|
{
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3716
|
+
className: "px-3 py-2 border border-border rounded-md text-[13px]",
|
|
3717
|
+
children: [
|
|
3718
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: m.name || "Unnamed" }),
|
|
3719
|
+
parts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
3720
|
+
" \u2014 ",
|
|
3721
|
+
parts.join(" \xB7 ")
|
|
3722
|
+
] }),
|
|
3723
|
+
m.notes && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground mt-0.5", children: m.notes })
|
|
3724
|
+
]
|
|
3725
|
+
},
|
|
3726
|
+
idx
|
|
3727
|
+
);
|
|
3728
|
+
}) });
|
|
3729
|
+
}
|
|
3730
|
+
function renderAllergies(value) {
|
|
3731
|
+
if (!Array.isArray(value) || value.length === 0) return "No allergies";
|
|
3732
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: value.map((allergy, idx) => {
|
|
3733
|
+
const a = allergy;
|
|
3734
|
+
const sevColor = SEVERITY_COLORS[a.severity];
|
|
3735
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3736
|
+
"div",
|
|
3737
|
+
{
|
|
3738
|
+
className: "px-3 py-2 border border-border rounded-md text-[13px] flex items-center gap-2 flex-wrap",
|
|
3457
3739
|
children: [
|
|
3458
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
response.schemaVersion,
|
|
3740
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: a.allergen || "Unknown" }),
|
|
3741
|
+
a.type && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-muted-foreground", children: [
|
|
3742
|
+
"(",
|
|
3743
|
+
a.type,
|
|
3463
3744
|
")"
|
|
3464
3745
|
] }),
|
|
3465
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3466
|
-
"
|
|
3467
|
-
|
|
3746
|
+
a.severity && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3747
|
+
"span",
|
|
3748
|
+
{
|
|
3749
|
+
className: "text-[11px] font-semibold px-2 py-0.5 rounded-full",
|
|
3750
|
+
style: {
|
|
3751
|
+
backgroundColor: sevColor ?? "var(--accent)",
|
|
3752
|
+
color: sevColor ? "#fff" : "var(--foreground)"
|
|
3753
|
+
},
|
|
3754
|
+
children: a.severity
|
|
3755
|
+
}
|
|
3756
|
+
),
|
|
3757
|
+
a.reaction && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
3758
|
+
"\u2014 ",
|
|
3759
|
+
a.reaction
|
|
3468
3760
|
] })
|
|
3469
3761
|
]
|
|
3762
|
+
},
|
|
3763
|
+
idx
|
|
3764
|
+
);
|
|
3765
|
+
}) });
|
|
3766
|
+
}
|
|
3767
|
+
function renderBmi(value) {
|
|
3768
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
3769
|
+
const data = value;
|
|
3770
|
+
const bmi = data.bmi;
|
|
3771
|
+
const unit = data.unit ?? "imperial";
|
|
3772
|
+
const height = data.height;
|
|
3773
|
+
const weight = data.weight;
|
|
3774
|
+
const heightUnit = unit === "metric" ? "cm" : "in";
|
|
3775
|
+
const weightUnit = unit === "metric" ? "kg" : "lbs";
|
|
3776
|
+
const category = bmi != null ? getBmiCategory(bmi) : void 0;
|
|
3777
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[13px] flex items-center gap-2 flex-wrap", children: [
|
|
3778
|
+
height != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3779
|
+
"Height: ",
|
|
3780
|
+
height,
|
|
3781
|
+
" ",
|
|
3782
|
+
heightUnit
|
|
3783
|
+
] }),
|
|
3784
|
+
height != null && weight != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
3785
|
+
weight != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3786
|
+
"Weight: ",
|
|
3787
|
+
weight,
|
|
3788
|
+
" ",
|
|
3789
|
+
weightUnit
|
|
3790
|
+
] }),
|
|
3791
|
+
bmi != null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3792
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2014" }),
|
|
3793
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
|
|
3794
|
+
"BMI: ",
|
|
3795
|
+
bmi
|
|
3796
|
+
] }),
|
|
3797
|
+
category && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3798
|
+
"span",
|
|
3799
|
+
{
|
|
3800
|
+
className: "text-[11px] font-semibold px-2 py-0.5 rounded-full text-white",
|
|
3801
|
+
style: { backgroundColor: category.color },
|
|
3802
|
+
children: category.label
|
|
3803
|
+
}
|
|
3804
|
+
)
|
|
3805
|
+
] })
|
|
3806
|
+
] });
|
|
3807
|
+
}
|
|
3808
|
+
function renderBodyDiagram(value) {
|
|
3809
|
+
if (!Array.isArray(value) || value.length === 0) return "No regions selected";
|
|
3810
|
+
const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
|
|
3811
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.join(", ") });
|
|
3812
|
+
}
|
|
3813
|
+
function renderPainScale(value) {
|
|
3814
|
+
if (typeof value !== "number") return formatFallbackValue(value);
|
|
3815
|
+
const face = getPainLabel(value);
|
|
3816
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", style: { color: face?.color }, children: [
|
|
3817
|
+
value,
|
|
3818
|
+
"/10",
|
|
3819
|
+
face ? ` \u2014 ${face.label}` : ""
|
|
3820
|
+
] });
|
|
3821
|
+
}
|
|
3822
|
+
function renderInsurance(value) {
|
|
3823
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
3824
|
+
const ins = value;
|
|
3825
|
+
const textFields = [
|
|
3826
|
+
{ key: "carrierId", label: "Carrier" },
|
|
3827
|
+
{ key: "planName", label: "Plan" },
|
|
3828
|
+
{ key: "memberId", label: "Member ID" },
|
|
3829
|
+
{ key: "groupNumber", label: "Group #" },
|
|
3830
|
+
{ key: "subscriberName", label: "Subscriber" },
|
|
3831
|
+
{ key: "relationship", label: "Relationship" }
|
|
3832
|
+
];
|
|
3833
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
3834
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-3 gap-y-1 text-[13px]", children: textFields.map(({ key, label }) => {
|
|
3835
|
+
const val = ins[key];
|
|
3836
|
+
if (!val) return null;
|
|
3837
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3838
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
3839
|
+
label,
|
|
3840
|
+
": "
|
|
3841
|
+
] }),
|
|
3842
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: String(val) })
|
|
3843
|
+
] }, key);
|
|
3844
|
+
}) }),
|
|
3845
|
+
!!(ins.frontImage || ins.backImage) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 mt-1", children: [
|
|
3846
|
+
typeof ins.frontImage === "string" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3847
|
+
"img",
|
|
3848
|
+
{
|
|
3849
|
+
src: ins.frontImage,
|
|
3850
|
+
alt: "Front of card",
|
|
3851
|
+
className: "h-12 rounded border border-border"
|
|
3852
|
+
}
|
|
3853
|
+
),
|
|
3854
|
+
typeof ins.backImage === "string" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3855
|
+
"img",
|
|
3856
|
+
{
|
|
3857
|
+
src: ins.backImage,
|
|
3858
|
+
alt: "Back of card",
|
|
3859
|
+
className: "h-12 rounded border border-border"
|
|
3860
|
+
}
|
|
3861
|
+
)
|
|
3862
|
+
] })
|
|
3863
|
+
] });
|
|
3864
|
+
}
|
|
3865
|
+
function renderPayment(value) {
|
|
3866
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
3867
|
+
const payment = value;
|
|
3868
|
+
const status = payment.status;
|
|
3869
|
+
const statusConfig = {
|
|
3870
|
+
succeeded: { bg: "#22c55e", label: "Succeeded" },
|
|
3871
|
+
failed: { bg: "#ef4444", label: "Failed" },
|
|
3872
|
+
processing: { bg: "#eab308", label: "Processing" }
|
|
3873
|
+
};
|
|
3874
|
+
const cfg = statusConfig[status];
|
|
3875
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-[13px]", children: [
|
|
3876
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3877
|
+
"span",
|
|
3878
|
+
{
|
|
3879
|
+
className: "text-[11px] font-semibold px-2.5 py-0.5 rounded-full text-white",
|
|
3880
|
+
style: { backgroundColor: cfg?.bg ?? "var(--accent)" },
|
|
3881
|
+
children: cfg?.label ?? status
|
|
3470
3882
|
}
|
|
3471
|
-
)
|
|
3883
|
+
),
|
|
3884
|
+
!!payment.chargeId && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: String(payment.chargeId) }),
|
|
3885
|
+
!!payment.error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive", children: String(payment.error) })
|
|
3472
3886
|
] });
|
|
3473
3887
|
}
|
|
3474
|
-
function
|
|
3888
|
+
function renderLegalName(value) {
|
|
3889
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
3890
|
+
const name = value;
|
|
3891
|
+
const parts = [name.first, name.middle, name.last].filter(Boolean);
|
|
3892
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: parts.join(" ") || "\u2014" });
|
|
3893
|
+
}
|
|
3894
|
+
function renderAddress(value) {
|
|
3895
|
+
if (typeof value !== "object" || value === null) return formatFallbackValue(value);
|
|
3896
|
+
const addr = value;
|
|
3897
|
+
const line1 = [addr.street, addr.street2].filter(Boolean).join(", ");
|
|
3898
|
+
const line2 = [addr.city, addr.state, addr.zip].filter(Boolean).join(", ");
|
|
3899
|
+
const full = [line1, line2].filter(Boolean).join(", ");
|
|
3900
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: full || "\u2014" });
|
|
3901
|
+
}
|
|
3902
|
+
function renderConsent(value) {
|
|
3903
|
+
const agreed = value === true || value === "true" || value === "agreed";
|
|
3904
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: agreed ? "font-semibold text-green-500" : "font-semibold text-muted-foreground", children: agreed ? "Agreed" : "Not agreed" });
|
|
3905
|
+
}
|
|
3906
|
+
function renderSignature(value) {
|
|
3907
|
+
if (typeof value !== "string") return formatFallbackValue(value);
|
|
3908
|
+
if (value.startsWith("data:image")) {
|
|
3909
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3910
|
+
"img",
|
|
3911
|
+
{
|
|
3912
|
+
src: value,
|
|
3913
|
+
alt: "Signature",
|
|
3914
|
+
className: "h-15 border border-border rounded"
|
|
3915
|
+
}
|
|
3916
|
+
);
|
|
3917
|
+
}
|
|
3918
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: value });
|
|
3919
|
+
}
|
|
3920
|
+
function formatFallbackValue(value) {
|
|
3475
3921
|
if (value == null) return "\u2014";
|
|
3476
3922
|
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
3477
|
-
if (Array.isArray(value)) return value.map(
|
|
3923
|
+
if (Array.isArray(value)) return value.map(formatFallbackValue).join(", ");
|
|
3478
3924
|
if (typeof value === "object") return JSON.stringify(value, null, 2);
|
|
3479
3925
|
return String(value);
|
|
3480
3926
|
}
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3927
|
+
|
|
3928
|
+
// src/response-viewer/export-utils.ts
|
|
3929
|
+
function exportToCsv(schema, responses, filename = "responses.csv") {
|
|
3930
|
+
const questions = getExportableQuestions(schema);
|
|
3931
|
+
const headers = ["Submitted", ...questions.map((q) => q.label), "Score"];
|
|
3932
|
+
const rows = responses.map((r) => {
|
|
3933
|
+
const submitted = new Date(r.submittedAt).toLocaleString();
|
|
3934
|
+
const values = questions.map((q) => formatExportValue(r.values[q.id], q.type));
|
|
3935
|
+
const score = r.totalScore != null ? String(r.totalScore) : "";
|
|
3936
|
+
return [submitted, ...values, score];
|
|
3937
|
+
});
|
|
3938
|
+
const csvContent = [headers, ...rows].map((row) => row.map(escapeCsvField).join(",")).join("\n");
|
|
3939
|
+
downloadBlob(csvContent, filename, "text/csv;charset=utf-8;");
|
|
3940
|
+
}
|
|
3941
|
+
function exportToJson(responses, filename = "responses.json") {
|
|
3942
|
+
const content = JSON.stringify(responses, null, 2);
|
|
3943
|
+
downloadBlob(content, filename, "application/json;charset=utf-8;");
|
|
3944
|
+
}
|
|
3945
|
+
function getExportableQuestions(schema) {
|
|
3946
|
+
const questions = [];
|
|
3947
|
+
for (const section of schema.sections) {
|
|
3948
|
+
for (const q of section.questions) {
|
|
3949
|
+
if (q.type === "info-block" || q.type === "section-header" || q.type === "page-break") {
|
|
3950
|
+
continue;
|
|
3951
|
+
}
|
|
3952
|
+
questions.push(q);
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
return questions;
|
|
3956
|
+
}
|
|
3957
|
+
function formatExportValue(value, type) {
|
|
3958
|
+
if (value == null) return "";
|
|
3959
|
+
switch (type) {
|
|
3960
|
+
case "vitals_entry": {
|
|
3961
|
+
if (typeof value !== "object" || value === null) break;
|
|
3962
|
+
const v = value;
|
|
3963
|
+
const parts = [];
|
|
3964
|
+
if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
|
|
3965
|
+
if (v.heartRate) parts.push(`HR ${v.heartRate}`);
|
|
3966
|
+
if (v.temperature) parts.push(`${v.temperature}\xB0F`);
|
|
3967
|
+
if (v.oxygenSaturation) parts.push(`SpO2 ${v.oxygenSaturation}%`);
|
|
3968
|
+
return parts.join("; ");
|
|
3969
|
+
}
|
|
3970
|
+
case "medication_list":
|
|
3971
|
+
if (Array.isArray(value)) {
|
|
3972
|
+
return value.map((m) => [m.name, m.dosage, m.frequency].filter(Boolean).join(" ")).join("; ");
|
|
3973
|
+
}
|
|
3974
|
+
break;
|
|
3975
|
+
case "allergy_list":
|
|
3976
|
+
if (Array.isArray(value)) {
|
|
3977
|
+
return value.map((a) => [a.allergen, a.severity].filter(Boolean).join(" - ")).join("; ");
|
|
3978
|
+
}
|
|
3979
|
+
break;
|
|
3980
|
+
case "body_diagram":
|
|
3981
|
+
if (Array.isArray(value)) {
|
|
3982
|
+
return value.map((id) => BODY_REGION_LABELS[id] ?? id).join("; ");
|
|
3983
|
+
}
|
|
3984
|
+
break;
|
|
3985
|
+
case "pain_scale":
|
|
3986
|
+
return typeof value === "number" ? `${value}/10` : String(value);
|
|
3987
|
+
case "bmi_calculator": {
|
|
3988
|
+
if (typeof value !== "object" || value === null) break;
|
|
3989
|
+
const d = value;
|
|
3990
|
+
return d.bmi != null ? `BMI ${d.bmi}` : "";
|
|
3991
|
+
}
|
|
3992
|
+
case "payment": {
|
|
3993
|
+
if (typeof value !== "object" || value === null) break;
|
|
3994
|
+
return value.status ?? "";
|
|
3995
|
+
}
|
|
3996
|
+
case "legal_name": {
|
|
3997
|
+
if (typeof value !== "object" || value === null) break;
|
|
3998
|
+
const n = value;
|
|
3999
|
+
return [n.first, n.middle, n.last].filter(Boolean).join(" ");
|
|
4000
|
+
}
|
|
4001
|
+
case "address": {
|
|
4002
|
+
if (typeof value !== "object" || value === null) break;
|
|
4003
|
+
const a = value;
|
|
4004
|
+
return [a.street, a.city, a.state, a.zip].filter(Boolean).join(", ");
|
|
4005
|
+
}
|
|
4006
|
+
case "consent":
|
|
4007
|
+
return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
|
|
4008
|
+
case "signature":
|
|
4009
|
+
return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "";
|
|
4010
|
+
}
|
|
4011
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
4012
|
+
if (Array.isArray(value)) return value.join("; ");
|
|
4013
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
4014
|
+
return String(value);
|
|
4015
|
+
}
|
|
4016
|
+
function escapeCsvField(field) {
|
|
4017
|
+
if (field.includes(",") || field.includes('"') || field.includes("\n")) {
|
|
4018
|
+
return `"${field.replace(/"/g, '""')}"`;
|
|
4019
|
+
}
|
|
4020
|
+
return field;
|
|
4021
|
+
}
|
|
4022
|
+
function downloadBlob(content, filename, mimeType) {
|
|
4023
|
+
const blob = new Blob([content], { type: mimeType });
|
|
4024
|
+
const url = URL.createObjectURL(blob);
|
|
4025
|
+
const link = document.createElement("a");
|
|
4026
|
+
link.href = url;
|
|
4027
|
+
link.download = filename;
|
|
4028
|
+
document.body.appendChild(link);
|
|
4029
|
+
link.click();
|
|
4030
|
+
document.body.removeChild(link);
|
|
4031
|
+
URL.revokeObjectURL(url);
|
|
4032
|
+
}
|
|
3493
4033
|
function ResponseViewerInner({
|
|
3494
4034
|
schema,
|
|
3495
4035
|
responses,
|
|
@@ -3515,62 +4055,73 @@ function ResponseViewerInner({
|
|
|
3515
4055
|
value: response.values[q.id]
|
|
3516
4056
|
}));
|
|
3517
4057
|
}
|
|
4058
|
+
const heightValue = typeof height === "number" ? `${height}px` : height;
|
|
4059
|
+
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
3518
4060
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3519
4061
|
"div",
|
|
3520
4062
|
{
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
flexDirection: "column",
|
|
3524
|
-
height: typeof height === "number" ? `${height}px` : height,
|
|
3525
|
-
width: typeof width === "number" ? `${width}px` : width,
|
|
3526
|
-
border: "1px solid var(--border, #1c1c20)",
|
|
3527
|
-
borderRadius: "8px",
|
|
3528
|
-
overflow: "hidden",
|
|
3529
|
-
fontFamily: "inherit",
|
|
3530
|
-
backgroundColor: "var(--background, #0a0a0b)",
|
|
3531
|
-
color: "var(--foreground, #e8e8ea)"
|
|
3532
|
-
},
|
|
4063
|
+
className: "flex flex-col border border-border rounded-lg overflow-hidden bg-background text-foreground",
|
|
4064
|
+
style: { height: heightValue, width: widthValue },
|
|
3533
4065
|
children: [
|
|
3534
|
-
!selectedResponse && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3535
|
-
"div",
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
] }),
|
|
3550
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
|
|
3551
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3552
|
-
"button",
|
|
3553
|
-
{
|
|
3554
|
-
type: "button",
|
|
3555
|
-
style: viewButtonStyle(viewMode === "table"),
|
|
3556
|
-
onClick: () => setViewMode("table"),
|
|
3557
|
-
children: "Table"
|
|
3558
|
-
}
|
|
4066
|
+
!selectedResponse && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center px-3 py-2 border-b border-border", children: [
|
|
4067
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
4068
|
+
responses.length,
|
|
4069
|
+
" response",
|
|
4070
|
+
responses.length !== 1 ? "s" : ""
|
|
4071
|
+
] }),
|
|
4072
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
4073
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4074
|
+
fieldcraftReact.Button,
|
|
4075
|
+
{
|
|
4076
|
+
variant: viewMode === "table" ? "secondary" : "ghost",
|
|
4077
|
+
size: "sm",
|
|
4078
|
+
className: cn(
|
|
4079
|
+
"h-7 text-xs",
|
|
4080
|
+
viewMode === "table" && "font-semibold border border-ring"
|
|
3559
4081
|
),
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
4082
|
+
onClick: () => setViewMode("table"),
|
|
4083
|
+
children: "Table"
|
|
4084
|
+
}
|
|
4085
|
+
),
|
|
4086
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4087
|
+
fieldcraftReact.Button,
|
|
4088
|
+
{
|
|
4089
|
+
variant: viewMode === "card" ? "secondary" : "ghost",
|
|
4090
|
+
size: "sm",
|
|
4091
|
+
className: cn(
|
|
4092
|
+
"h-7 text-xs",
|
|
4093
|
+
viewMode === "card" && "font-semibold border border-ring"
|
|
4094
|
+
),
|
|
4095
|
+
onClick: () => setViewMode("card"),
|
|
4096
|
+
children: "Cards"
|
|
4097
|
+
}
|
|
4098
|
+
),
|
|
4099
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { orientation: "vertical", className: "h-5 mx-1" }),
|
|
4100
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4101
|
+
fieldcraftReact.Button,
|
|
4102
|
+
{
|
|
4103
|
+
variant: "outline",
|
|
4104
|
+
size: "sm",
|
|
4105
|
+
className: "h-7 text-xs",
|
|
4106
|
+
onClick: () => exportToCsv(schema, responses),
|
|
4107
|
+
disabled: responses.length === 0,
|
|
4108
|
+
children: "Export CSV"
|
|
4109
|
+
}
|
|
4110
|
+
),
|
|
4111
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4112
|
+
fieldcraftReact.Button,
|
|
4113
|
+
{
|
|
4114
|
+
variant: "outline",
|
|
4115
|
+
size: "sm",
|
|
4116
|
+
className: "h-7 text-xs",
|
|
4117
|
+
onClick: () => exportToJson(responses),
|
|
4118
|
+
disabled: responses.length === 0,
|
|
4119
|
+
children: "Export JSON"
|
|
4120
|
+
}
|
|
4121
|
+
)
|
|
4122
|
+
] })
|
|
4123
|
+
] }),
|
|
4124
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex-1 overflow-auto", selectedResponse && "p-4"), children: selectedResponse ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3574
4125
|
ResponseDetail,
|
|
3575
4126
|
{
|
|
3576
4127
|
response: selectedResponse,
|
|
@@ -3584,29 +4135,18 @@ function ResponseViewerInner({
|
|
|
3584
4135
|
responses,
|
|
3585
4136
|
onRowClick: handleSelect
|
|
3586
4137
|
}
|
|
3587
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
padding: "12px"
|
|
4138
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 p-3 grid-cols-[repeat(auto-fill,minmax(280px,1fr))]", children: [
|
|
4139
|
+
responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4140
|
+
ResponseCard,
|
|
4141
|
+
{
|
|
4142
|
+
response,
|
|
4143
|
+
fields: getFields(response),
|
|
4144
|
+
onClick: () => handleSelect(response)
|
|
3595
4145
|
},
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
response,
|
|
3601
|
-
fields: getFields(response),
|
|
3602
|
-
onClick: () => handleSelect(response)
|
|
3603
|
-
},
|
|
3604
|
-
response.sessionToken || idx
|
|
3605
|
-
)),
|
|
3606
|
-
responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "32px", textAlign: "center", color: "var(--muted-foreground, #8a8a95)" }, children: "No responses yet" })
|
|
3607
|
-
]
|
|
3608
|
-
}
|
|
3609
|
-
) })
|
|
4146
|
+
response.sessionToken || idx
|
|
4147
|
+
)),
|
|
4148
|
+
responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center text-muted-foreground", children: "No responses yet" })
|
|
4149
|
+
] }) })
|
|
3610
4150
|
]
|
|
3611
4151
|
}
|
|
3612
4152
|
);
|
|
@@ -4031,7 +4571,7 @@ var ThemeEditor = fieldcraftProLicense.requireLicense(ThemeEditorInner, "ThemeEd
|
|
|
4031
4571
|
// src/index.ts
|
|
4032
4572
|
if (typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" && globalThis.process.env?.NODE_ENV !== "production") {
|
|
4033
4573
|
const _fc_banner = `
|
|
4034
|
-
%c FieldCraft Pro %c
|
|
4574
|
+
%c FieldCraft Pro %c v1.0.1
|
|
4035
4575
|
|
|
4036
4576
|
%cForm Builder \xB7 Response Viewer \xB7 Theme Editor
|
|
4037
4577
|
|