@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/dist/index.js CHANGED
@@ -1,15 +1,591 @@
1
1
  'use strict';
2
2
 
3
- var fieldcraftProLicense = require('@squaredr/fieldcraft-pro-license');
4
3
  var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
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
- var jsxRuntime = require('react/jsx-runtime');
11
11
 
12
- // src/index.ts
12
+ // ../license/dist/index.mjs
13
+ var LEGACY_TIER_MAP = {
14
+ "01": "starter",
15
+ "02": "pro",
16
+ "03": "business",
17
+ "04": "enterprise"
18
+ };
19
+ var TIER_MAP_NUMERIC = {
20
+ 1: "starter",
21
+ 2: "pro",
22
+ 3: "business",
23
+ 4: "enterprise"
24
+ };
25
+ var PRO_FEATURES = [
26
+ "SchemaEditor",
27
+ "ResponseViewer",
28
+ "ThemeEditor",
29
+ "FormBuilder",
30
+ "ApiManager",
31
+ "Analytics"
32
+ ];
33
+ var ALL_FEATURES = [
34
+ ...PRO_FEATURES,
35
+ "Telehealth"
36
+ ];
37
+ var TIER_FEATURES = {
38
+ starter: ["SchemaEditor", "ResponseViewer"],
39
+ pro: PRO_FEATURES,
40
+ business: ALL_FEATURES,
41
+ enterprise: ALL_FEATURES
42
+ };
43
+ var HMAC_SECRET = "e50a657813a5ab9563050728a605b9ab58b911d5c1a9f48c6825f379a533ccd7685d11d01937d9f135776a0db3efeefe29b3e5282b27ede8cee93758f94f2d75";
44
+ var B32_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
45
+ function base32hexDecode(str) {
46
+ const upper = str.toUpperCase();
47
+ let bits = 0;
48
+ let value = 0;
49
+ const bytes = [];
50
+ for (let i = 0; i < upper.length; i++) {
51
+ const idx = B32_CHARS.indexOf(upper[i]);
52
+ if (idx === -1) return null;
53
+ value = value << 5 | idx;
54
+ bits += 5;
55
+ if (bits >= 8) {
56
+ bits -= 8;
57
+ bytes.push(value >>> bits & 255);
58
+ }
59
+ }
60
+ return new Uint8Array(bytes);
61
+ }
62
+ function parseLegacyKey(key) {
63
+ const parts = key.split("-");
64
+ if (parts.length !== 5) return null;
65
+ const [p1, p2, tierHex, expiryHex, hash] = parts;
66
+ if (p1 !== "FC" || p2 !== "PRO") return null;
67
+ if (!(tierHex in LEGACY_TIER_MAP)) return null;
68
+ if (!/^[0-9A-Fa-f]{8}$/.test(expiryHex)) return null;
69
+ if (!/^[0-9A-Fa-f]{8}$/.test(hash)) return null;
70
+ return { format: "legacy", tierHex, expiryHex, hash };
71
+ }
72
+ function parseOpaqueKey(key) {
73
+ const stripped = key.replace(/-/g, "");
74
+ if (!stripped.startsWith("FCPRO")) return null;
75
+ const encoded = stripped.slice(5);
76
+ const decoded = base32hexDecode(encoded);
77
+ if (!decoded || decoded.length < 17) return null;
78
+ const tier = decoded[0];
79
+ if (!(tier in TIER_MAP_NUMERIC)) return null;
80
+ const expiryTimestamp = decoded[1] << 24 | decoded[2] << 16 | decoded[3] << 8 | decoded[4];
81
+ const expiryUnsigned = expiryTimestamp >>> 0;
82
+ const nonce = decoded.slice(5, 9);
83
+ const signature = decoded.slice(9, 17);
84
+ return { format: "opaque", tier, expiryTimestamp: expiryUnsigned, nonce, signature };
85
+ }
86
+ function parseKey(key) {
87
+ if (!key || typeof key !== "string") return null;
88
+ if (key.startsWith("FCPRO-")) {
89
+ return parseOpaqueKey(key);
90
+ }
91
+ return parseLegacyKey(key);
92
+ }
93
+ async function computeHmacLegacy(tierHex, expiryHex) {
94
+ const encoder = new TextEncoder();
95
+ const keyData = encoder.encode(HMAC_SECRET);
96
+ const message = encoder.encode(`FC-PRO-${tierHex}-${expiryHex}`);
97
+ const cryptoKey = await crypto.subtle.importKey(
98
+ "raw",
99
+ keyData,
100
+ { name: "HMAC", hash: "SHA-256" },
101
+ false,
102
+ ["sign"]
103
+ );
104
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, message);
105
+ const hashBytes = new Uint8Array(signature).slice(0, 4);
106
+ return Array.from(hashBytes).map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
107
+ }
108
+ async function computeHmacOpaque(payload) {
109
+ const encoder = new TextEncoder();
110
+ const keyData = encoder.encode(HMAC_SECRET);
111
+ const cryptoKey = await crypto.subtle.importKey(
112
+ "raw",
113
+ keyData,
114
+ { name: "HMAC", hash: "SHA-256" },
115
+ false,
116
+ ["sign"]
117
+ );
118
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, payload);
119
+ return new Uint8Array(signature).slice(0, 8);
120
+ }
121
+ function isExpiredLegacy(expiryHex) {
122
+ const expiryTimestamp = parseInt(expiryHex, 16);
123
+ if (expiryTimestamp === 4294967295) return false;
124
+ return Date.now() / 1e3 > expiryTimestamp;
125
+ }
126
+ function isExpiredOpaque(expiryTimestamp) {
127
+ if (expiryTimestamp === 4294967295) return false;
128
+ return Date.now() / 1e3 > expiryTimestamp;
129
+ }
130
+ async function validateLicense(key) {
131
+ if (!key || typeof key !== "string") {
132
+ return { status: "invalid", tier: null };
133
+ }
134
+ const parsed = parseKey(key);
135
+ if (!parsed) {
136
+ return { status: "invalid", tier: null };
137
+ }
138
+ if (parsed.format === "opaque") {
139
+ const payload = new Uint8Array(9);
140
+ payload[0] = parsed.tier;
141
+ payload[1] = parsed.expiryTimestamp >>> 24 & 255;
142
+ payload[2] = parsed.expiryTimestamp >>> 16 & 255;
143
+ payload[3] = parsed.expiryTimestamp >>> 8 & 255;
144
+ payload[4] = parsed.expiryTimestamp & 255;
145
+ payload.set(parsed.nonce, 5);
146
+ const expectedSig = await computeHmacOpaque(payload);
147
+ let match = true;
148
+ for (let i = 0; i < 8; i++) {
149
+ if (parsed.signature[i] !== expectedSig[i]) {
150
+ match = false;
151
+ break;
152
+ }
153
+ }
154
+ if (!match) {
155
+ return { status: "invalid", tier: null };
156
+ }
157
+ const tier2 = TIER_MAP_NUMERIC[parsed.tier];
158
+ if (!tier2) {
159
+ return { status: "invalid", tier: null };
160
+ }
161
+ if (isExpiredOpaque(parsed.expiryTimestamp)) {
162
+ return { status: "expired", tier: tier2 };
163
+ }
164
+ return { status: "valid", tier: tier2 };
165
+ }
166
+ const expectedHash = await computeHmacLegacy(parsed.tierHex, parsed.expiryHex);
167
+ if (parsed.hash.toUpperCase() !== expectedHash) {
168
+ return { status: "invalid", tier: null };
169
+ }
170
+ const tier = LEGACY_TIER_MAP[parsed.tierHex];
171
+ if (isExpiredLegacy(parsed.expiryHex)) {
172
+ return { status: "expired", tier };
173
+ }
174
+ return { status: "valid", tier };
175
+ }
176
+ function tierHasFeature(tier, featureName) {
177
+ return TIER_FEATURES[tier]?.includes(featureName) ?? false;
178
+ }
179
+ var DEV_HOSTNAMES = /* @__PURE__ */ new Set([
180
+ "localhost",
181
+ "127.0.0.1",
182
+ "0.0.0.0",
183
+ "[::1]",
184
+ "::1"
185
+ ]);
186
+ var DEV_HOSTNAME_PATTERNS = [
187
+ /^192\.168\.\d+\.\d+$/,
188
+ // local network
189
+ /^10\.\d+\.\d+\.\d+$/,
190
+ // local network
191
+ /^172\.(1[6-9]|2\d|3[01])\.\d+\.\d+$/
192
+ // local network
193
+ ];
194
+ var PRODUCTION_HOST_SUFFIXES = [
195
+ ".vercel.app",
196
+ ".netlify.app",
197
+ ".netlify.com",
198
+ ".herokuapp.com",
199
+ ".fly.dev",
200
+ ".railway.app",
201
+ ".render.com",
202
+ ".onrender.com",
203
+ ".pages.dev",
204
+ // Cloudflare Pages
205
+ ".workers.dev",
206
+ // Cloudflare Workers
207
+ ".azurewebsites.net",
208
+ ".web.app",
209
+ // Firebase
210
+ ".firebaseapp.com",
211
+ ".amplifyapp.com",
212
+ ".surge.sh",
213
+ ".github.io",
214
+ ".gitlab.io"
215
+ ];
216
+ function isDevHostname(hostname) {
217
+ if (DEV_HOSTNAMES.has(hostname)) return true;
218
+ for (const pattern of DEV_HOSTNAME_PATTERNS) {
219
+ if (pattern.test(hostname)) return true;
220
+ }
221
+ return false;
222
+ }
223
+ function isKnownProductionHost(hostname) {
224
+ for (const suffix of PRODUCTION_HOST_SUFFIXES) {
225
+ if (hostname.endsWith(suffix)) return true;
226
+ }
227
+ return false;
228
+ }
229
+ function hasRealTLD(hostname) {
230
+ return /\.[a-z]{2,}$/i.test(hostname) && !isDevHostname(hostname);
231
+ }
232
+ function isProductionEnvironment() {
233
+ const _proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
234
+ if (_proc?.env) {
235
+ const nodeEnv = _proc.env.NODE_ENV;
236
+ if (nodeEnv === "production") return true;
237
+ if (nodeEnv === "development" || nodeEnv === "test") return false;
238
+ }
239
+ if (typeof window !== "undefined" && window.location) {
240
+ const { hostname, protocol, port } = window.location;
241
+ if (protocol === "file:") return false;
242
+ if (isDevHostname(hostname)) return false;
243
+ if (port && port !== "443" && port !== "80") return false;
244
+ if (isKnownProductionHost(hostname)) return true;
245
+ if (protocol === "https:" && hasRealTLD(hostname)) return true;
246
+ if (hasRealTLD(hostname)) return true;
247
+ }
248
+ return false;
249
+ }
250
+ var PING_URL = "https://squaredr.tech/api/license/ping";
251
+ var CACHE_KEY = "fcpro_lp";
252
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
253
+ function hashKey(key) {
254
+ let hash = 0;
255
+ for (let i = 0; i < key.length; i++) {
256
+ hash = (hash << 5) - hash + key.charCodeAt(i) | 0;
257
+ }
258
+ return Math.abs(hash).toString(36);
259
+ }
260
+ function cacheId(key) {
261
+ return `${CACHE_KEY}_${hashKey(key)}`;
262
+ }
263
+ function readCache(key) {
264
+ if (typeof window === "undefined" || !window.localStorage) return null;
265
+ try {
266
+ const raw = localStorage.getItem(cacheId(key));
267
+ if (!raw) return null;
268
+ const entry = JSON.parse(raw);
269
+ if (Date.now() - entry.ts > CACHE_TTL_MS) {
270
+ localStorage.removeItem(cacheId(key));
271
+ return null;
272
+ }
273
+ return entry;
274
+ } catch {
275
+ return null;
276
+ }
277
+ }
278
+ function writeCache(key, status, tier, reason = null) {
279
+ if (typeof window === "undefined" || !window.localStorage) return;
280
+ try {
281
+ const entry = { s: status, t: tier, r: reason, ts: Date.now() };
282
+ localStorage.setItem(cacheId(key), JSON.stringify(entry));
283
+ } catch {
284
+ }
285
+ }
286
+ function getCachedPingResult(key) {
287
+ const cached = readCache(key);
288
+ if (!cached) return null;
289
+ return { status: cached.s, tier: cached.t, reason: cached.r || void 0 };
290
+ }
291
+ async function performPing(key) {
292
+ const cached = readCache(key);
293
+ if (cached) {
294
+ return { status: cached.s, tier: cached.t };
295
+ }
296
+ try {
297
+ const controller = new AbortController();
298
+ const timeout = setTimeout(() => controller.abort(), 1e4);
299
+ const res = await fetch(PING_URL, {
300
+ method: "POST",
301
+ headers: { "Content-Type": "application/json" },
302
+ body: JSON.stringify({ key }),
303
+ signal: controller.signal
304
+ });
305
+ clearTimeout(timeout);
306
+ if (!res.ok) return null;
307
+ const data = await res.json();
308
+ const tier = data.tier || null;
309
+ if (data.status === "active") {
310
+ writeCache(key, "valid", tier);
311
+ return { status: "valid", tier };
312
+ }
313
+ if (data.status === "revoked") {
314
+ writeCache(key, "revoked", null, data.reason);
315
+ return { status: "revoked", tier: null, reason: data.reason };
316
+ }
317
+ if (data.status === "expired") {
318
+ writeCache(key, "expired", tier);
319
+ return { status: "expired", tier };
320
+ }
321
+ if (data.status === "domain_mismatch") {
322
+ writeCache(key, "domain_mismatch", null, data.reason);
323
+ return { status: "domain_mismatch", tier: null, reason: data.reason };
324
+ }
325
+ return null;
326
+ } catch {
327
+ return null;
328
+ }
329
+ }
330
+ var defaultContext = { status: "validating", tier: null };
331
+ var LicenseCtx = react.createContext(defaultContext);
332
+ function FieldCraftProProvider({ licenseKey, children }) {
333
+ const [license, setLicense] = react.useState(defaultContext);
334
+ react.useEffect(() => {
335
+ let cancelled = false;
336
+ setLicense(defaultContext);
337
+ validateLicense(licenseKey).then((clientResult) => {
338
+ if (cancelled) return;
339
+ setLicense(clientResult);
340
+ if (clientResult.status !== "valid" || !isProductionEnvironment()) return;
341
+ const cached = getCachedPingResult(licenseKey);
342
+ if (cached) {
343
+ if (cached.status === "revoked" || cached.status === "domain_mismatch") {
344
+ setLicense({ status: cached.status, tier: null });
345
+ }
346
+ return;
347
+ }
348
+ performPing(licenseKey).then((pingResult) => {
349
+ if (cancelled) return;
350
+ if (!pingResult) return;
351
+ if (pingResult.status === "revoked" || pingResult.status === "domain_mismatch") {
352
+ setLicense({ status: pingResult.status, tier: null });
353
+ }
354
+ });
355
+ });
356
+ return () => {
357
+ cancelled = true;
358
+ };
359
+ }, [licenseKey]);
360
+ return /* @__PURE__ */ jsxRuntime.jsx(LicenseCtx.Provider, { value: license, children });
361
+ }
362
+ function useLicense() {
363
+ return react.useContext(LicenseCtx);
364
+ }
365
+ function UnlicensedOverlay({ featureName, reason, children }) {
366
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", minHeight: "200px" }, children: [
367
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { pointerEvents: "none", userSelect: "none", filter: "blur(2px)" }, children }),
368
+ /* @__PURE__ */ jsxRuntime.jsxs(
369
+ "div",
370
+ {
371
+ style: {
372
+ position: "absolute",
373
+ inset: 0,
374
+ zIndex: 2147483647,
375
+ display: "flex",
376
+ flexDirection: "column",
377
+ alignItems: "center",
378
+ justifyContent: "center",
379
+ backgroundColor: "rgba(0, 0, 0, 0.75)",
380
+ backdropFilter: "blur(4px)",
381
+ fontFamily: "system-ui, -apple-system, sans-serif",
382
+ padding: "32px"
383
+ },
384
+ children: [
385
+ /* @__PURE__ */ jsxRuntime.jsx(
386
+ "div",
387
+ {
388
+ style: {
389
+ position: "absolute",
390
+ inset: 0,
391
+ overflow: "hidden",
392
+ pointerEvents: "none",
393
+ zIndex: 0
394
+ },
395
+ children: Array.from({ length: 12 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
396
+ "div",
397
+ {
398
+ style: {
399
+ position: "absolute",
400
+ top: `${i * 80}px`,
401
+ left: "-50%",
402
+ right: "-50%",
403
+ textAlign: "center",
404
+ transform: "rotate(-30deg)",
405
+ fontSize: "18px",
406
+ fontWeight: 700,
407
+ color: "rgba(255, 255, 255, 0.06)",
408
+ letterSpacing: "8px",
409
+ textTransform: "uppercase",
410
+ whiteSpace: "nowrap",
411
+ userSelect: "none"
412
+ },
413
+ children: "UNLICENSED \u2022 FIELDCRAFT PRO \u2022 UNLICENSED \u2022 FIELDCRAFT PRO \u2022 UNLICENSED \u2022 FIELDCRAFT PRO"
414
+ },
415
+ i
416
+ ))
417
+ }
418
+ ),
419
+ /* @__PURE__ */ jsxRuntime.jsxs(
420
+ "div",
421
+ {
422
+ style: {
423
+ position: "relative",
424
+ zIndex: 1,
425
+ textAlign: "center",
426
+ maxWidth: "420px"
427
+ },
428
+ children: [
429
+ /* @__PURE__ */ jsxRuntime.jsx(
430
+ "div",
431
+ {
432
+ style: {
433
+ width: "64px",
434
+ height: "64px",
435
+ margin: "0 auto 16px",
436
+ borderRadius: "50%",
437
+ backgroundColor: "rgba(239, 68, 68, 0.2)",
438
+ border: "2px solid rgba(239, 68, 68, 0.5)",
439
+ display: "flex",
440
+ alignItems: "center",
441
+ justifyContent: "center",
442
+ fontSize: "28px"
443
+ },
444
+ children: "\u{1F6E1}"
445
+ }
446
+ ),
447
+ /* @__PURE__ */ jsxRuntime.jsx(
448
+ "div",
449
+ {
450
+ style: {
451
+ fontSize: "22px",
452
+ fontWeight: 700,
453
+ color: "#ffffff",
454
+ marginBottom: "8px",
455
+ letterSpacing: "-0.02em"
456
+ },
457
+ children: "License Required"
458
+ }
459
+ ),
460
+ /* @__PURE__ */ jsxRuntime.jsx(
461
+ "div",
462
+ {
463
+ style: {
464
+ fontSize: "15px",
465
+ fontWeight: 600,
466
+ color: "#f87171",
467
+ marginBottom: "6px"
468
+ },
469
+ children: featureName
470
+ }
471
+ ),
472
+ /* @__PURE__ */ jsxRuntime.jsx(
473
+ "div",
474
+ {
475
+ style: {
476
+ fontSize: "14px",
477
+ color: "#d1d5db",
478
+ marginBottom: "24px",
479
+ lineHeight: "1.5"
480
+ },
481
+ children: reason
482
+ }
483
+ ),
484
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "10px", justifyContent: "center", flexWrap: "wrap" }, children: [
485
+ /* @__PURE__ */ jsxRuntime.jsx(
486
+ "a",
487
+ {
488
+ href: "https://squaredr.tech/products/fieldcraft/admin-pro#pricing",
489
+ target: "_blank",
490
+ rel: "noopener noreferrer",
491
+ style: {
492
+ display: "inline-block",
493
+ padding: "12px 24px",
494
+ fontSize: "14px",
495
+ fontWeight: 600,
496
+ color: "#ffffff",
497
+ backgroundColor: "#2563eb",
498
+ borderRadius: "8px",
499
+ textDecoration: "none",
500
+ boxShadow: "0 4px 14px rgba(37, 99, 235, 0.4)"
501
+ },
502
+ children: "Get a License"
503
+ }
504
+ ),
505
+ /* @__PURE__ */ jsxRuntime.jsx(
506
+ "a",
507
+ {
508
+ href: "https://discord.gg/zMxdu5UVW",
509
+ target: "_blank",
510
+ rel: "noopener noreferrer",
511
+ style: {
512
+ display: "inline-block",
513
+ padding: "12px 24px",
514
+ fontSize: "14px",
515
+ fontWeight: 600,
516
+ color: "#93c5fd",
517
+ backgroundColor: "rgba(37, 99, 235, 0.15)",
518
+ border: "1px solid rgba(37, 99, 235, 0.4)",
519
+ borderRadius: "8px",
520
+ textDecoration: "none"
521
+ },
522
+ children: "Join Discord"
523
+ }
524
+ )
525
+ ] }),
526
+ /* @__PURE__ */ jsxRuntime.jsxs(
527
+ "div",
528
+ {
529
+ style: {
530
+ fontSize: "12px",
531
+ color: "#6b7280",
532
+ marginTop: "16px"
533
+ },
534
+ children: [
535
+ "This component works freely in development.",
536
+ " ",
537
+ /* @__PURE__ */ jsxRuntime.jsx(
538
+ "a",
539
+ {
540
+ href: "https://squaredr.tech/products/fieldcraft/docs/pro/license",
541
+ target: "_blank",
542
+ rel: "noopener noreferrer",
543
+ style: { color: "#9ca3af", textDecoration: "underline" },
544
+ children: "Learn more"
545
+ }
546
+ )
547
+ ]
548
+ }
549
+ )
550
+ ]
551
+ }
552
+ )
553
+ ]
554
+ }
555
+ )
556
+ ] });
557
+ }
558
+ function requireLicense(Component2, featureName) {
559
+ function LicenseGated(props) {
560
+ const { status, tier } = useLicense();
561
+ const isProduction = isProductionEnvironment();
562
+ if (!isProduction) {
563
+ return /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props });
564
+ }
565
+ if (status === "validating") {
566
+ return null;
567
+ }
568
+ const isLicensed = status === "valid" && tier != null && tierHasFeature(tier, featureName);
569
+ if (isLicensed) {
570
+ return /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props });
571
+ }
572
+ let reason = "A valid license is required for production use.";
573
+ if (status === "invalid") {
574
+ reason = "Invalid license key. Please check your license key and try again.";
575
+ } else if (status === "expired") {
576
+ reason = "Your license has expired. Please renew to continue using this component in production.";
577
+ } else if (status === "revoked") {
578
+ reason = "This license key has been revoked. Please contact support or purchase a new license.";
579
+ } else if (status === "domain_mismatch") {
580
+ 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.";
581
+ } else if (!tier || !tierHasFeature(tier, featureName)) {
582
+ reason = "Your license does not include this feature. Please upgrade your plan.";
583
+ }
584
+ return /* @__PURE__ */ jsxRuntime.jsx(UnlicensedOverlay, { featureName, reason, children: /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props }) });
585
+ }
586
+ LicenseGated.displayName = `requireLicense(${Component2.displayName || Component2.name || "Component"})`;
587
+ return LicenseGated;
588
+ }
13
589
  var MAX_HISTORY = 50;
14
590
  function useUndoRedo(currentSchema, setSchema) {
15
591
  const historyRef = react.useRef([currentSchema]);
@@ -80,6 +656,39 @@ function generateOptionId() {
80
656
  function deepClone(obj) {
81
657
  return structuredClone(obj);
82
658
  }
659
+ function cleanCondition(expr, deletedFieldId) {
660
+ if (expr.field === deletedFieldId) return void 0;
661
+ if (expr.field && expr.field !== deletedFieldId) return expr;
662
+ if (expr.conditions) {
663
+ const cleaned = expr.conditions.map((c) => cleanCondition(c, deletedFieldId)).filter((c) => c !== void 0);
664
+ if (cleaned.length === 0) return void 0;
665
+ if (cleaned.length === 1) return cleaned[0];
666
+ return { ...expr, conditions: cleaned };
667
+ }
668
+ return expr;
669
+ }
670
+ function cleanupOrphanConditions(schema, deletedFieldId) {
671
+ for (const section of schema.sections) {
672
+ if (section.showIf) {
673
+ const cleaned = cleanCondition(section.showIf, deletedFieldId);
674
+ if (cleaned) {
675
+ section.showIf = cleaned;
676
+ } else {
677
+ delete section.showIf;
678
+ }
679
+ }
680
+ for (const question of section.questions) {
681
+ if (question.showIf) {
682
+ const cleaned = cleanCondition(question.showIf, deletedFieldId);
683
+ if (cleaned) {
684
+ question.showIf = cleaned;
685
+ } else {
686
+ delete question.showIf;
687
+ }
688
+ }
689
+ }
690
+ }
691
+ }
83
692
  function addSection(schema, section, index) {
84
693
  const newSchema = deepClone(schema);
85
694
  newSchema.sections.splice(index, 0, section);
@@ -87,7 +696,12 @@ function addSection(schema, section, index) {
87
696
  }
88
697
  function removeSection(schema, sectionId) {
89
698
  const newSchema = deepClone(schema);
699
+ const removedSection = newSchema.sections.find((s) => s.id === sectionId);
700
+ const removedFieldIds = removedSection ? removedSection.questions.map((q) => q.id) : [];
90
701
  newSchema.sections = newSchema.sections.filter((s) => s.id !== sectionId);
702
+ for (const fieldId of removedFieldIds) {
703
+ cleanupOrphanConditions(newSchema, fieldId);
704
+ }
91
705
  return newSchema;
92
706
  }
93
707
  function updateSection(schema, sectionId, updates) {
@@ -137,6 +751,7 @@ function removeQuestion(schema, sectionId, questionId) {
137
751
  const section = newSchema.sections.find((s) => s.id === sectionId);
138
752
  if (!section) return schema;
139
753
  section.questions = section.questions.filter((q) => q.id !== questionId);
754
+ cleanupOrphanConditions(newSchema, questionId);
140
755
  return newSchema;
141
756
  }
142
757
  function updateQuestion(schema, sectionId, questionId, updates) {
@@ -975,11 +1590,13 @@ function QuestionPalette({ questionTypes, palette }) {
975
1590
  const isCollapsed = collapsed[category.category] && !search;
976
1591
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
977
1592
  /* @__PURE__ */ jsxRuntime.jsxs(
978
- "button",
1593
+ fieldcraftReact.Button,
979
1594
  {
980
1595
  type: "button",
1596
+ variant: "ghost",
1597
+ size: "xs",
981
1598
  onClick: () => toggleCategory(category.category),
982
- className: "flex items-center gap-1.5 w-full px-1 py-1 text-left text-[11px] font-semibold uppercase tracking-widest text-muted-foreground bg-transparent border-0 cursor-pointer",
1599
+ className: "w-full justify-start px-1 text-[11px] font-semibold uppercase tracking-widest text-muted-foreground",
983
1600
  "aria-expanded": !isCollapsed,
984
1601
  children: [
985
1602
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1343,11 +1960,12 @@ function FormCanvas({ builderState }) {
1343
1960
  section.id
1344
1961
  )),
1345
1962
  /* @__PURE__ */ jsxRuntime.jsxs(
1346
- "button",
1963
+ fieldcraftReact.Button,
1347
1964
  {
1348
1965
  type: "button",
1966
+ variant: "outline",
1349
1967
  onClick: handleAddSection,
1350
- className: "w-full py-4 text-sm font-medium rounded-lg border border-dashed border-fcb-border-strong text-muted-foreground bg-transparent flex items-center justify-center gap-2 cursor-pointer transition-colors hover:border-primary hover:text-primary hover:bg-primary/5",
1968
+ 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
1969
  children: [
1352
1970
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 14, strokeWidth: 2 }),
1353
1971
  "Add Section"
@@ -1357,6 +1975,26 @@ function FormCanvas({ builderState }) {
1357
1975
  ] })
1358
1976
  ] }) });
1359
1977
  }
1978
+ var NativeSelect = react.forwardRef(
1979
+ ({ className, wrapperClassName, children, ...props }, ref) => {
1980
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", wrapperClassName), children: [
1981
+ /* @__PURE__ */ jsxRuntime.jsx(
1982
+ "select",
1983
+ {
1984
+ ref,
1985
+ className: cn(
1986
+ "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",
1987
+ className
1988
+ ),
1989
+ ...props,
1990
+ children
1991
+ }
1992
+ ),
1993
+ /* @__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" })
1994
+ ] });
1995
+ }
1996
+ );
1997
+ NativeSelect.displayName = "NativeSelect";
1360
1998
  function useConfigUpdater(question, onUpdate) {
1361
1999
  return (field, value) => {
1362
2000
  const current = question.config ?? {};
@@ -1751,18 +2389,7 @@ function SelectField({
1751
2389
  }) {
1752
2390
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1753
2391
  /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
1754
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
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
- ] })
2392
+ /* @__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
2393
  ] });
1767
2394
  }
1768
2395
  function LikertLabelsEditor({
@@ -1783,15 +2410,7 @@ function LikertLabelsEditor({
1783
2410
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1784
2411
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1785
2412
  /* @__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
- )
2413
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
1795
2414
  ] }),
1796
2415
  /* @__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
2416
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground w-4 text-right shrink-0", children: index + 1 }),
@@ -1804,11 +2423,13 @@ function LikertLabelsEditor({
1804
2423
  }
1805
2424
  ),
1806
2425
  labels.length > 2 && /* @__PURE__ */ jsxRuntime.jsx(
1807
- "button",
2426
+ fieldcraftReact.Button,
1808
2427
  {
1809
2428
  type: "button",
2429
+ variant: "ghost",
2430
+ size: "icon-xs",
1810
2431
  onClick: () => handleRemove(index),
1811
- className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1",
2432
+ className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity",
1812
2433
  children: "x"
1813
2434
  }
1814
2435
  )
@@ -1837,15 +2458,7 @@ function MatrixItemsEditor({
1837
2458
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1838
2459
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1839
2460
  /* @__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
- )
2461
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
1849
2462
  ] }),
1850
2463
  /* @__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
2464
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1857,11 +2470,13 @@ function MatrixItemsEditor({
1857
2470
  }
1858
2471
  ),
1859
2472
  items.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
1860
- "button",
2473
+ fieldcraftReact.Button,
1861
2474
  {
1862
2475
  type: "button",
2476
+ variant: "ghost",
2477
+ size: "icon-xs",
1863
2478
  onClick: () => handleRemove(index),
1864
- className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1",
2479
+ className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity",
1865
2480
  children: "x"
1866
2481
  }
1867
2482
  )
@@ -1894,7 +2509,7 @@ function ScoringOptionsEditor({
1894
2509
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1895
2510
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1896
2511
  /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Score Options" }),
1897
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: handleAdd, className: "text-xs text-primary hover:underline", children: "+ Add" })
2512
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
1898
2513
  ] }),
1899
2514
  /* @__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
2515
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1916,7 +2531,7 @@ function ScoringOptionsEditor({
1916
2531
  placeholder: "Score"
1917
2532
  }
1918
2533
  ),
1919
- options.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => handleRemove(index), className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1", children: "x" })
2534
+ 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
2535
  ] }, index)) })
1921
2536
  ] });
1922
2537
  }
@@ -1938,7 +2553,7 @@ function ScoreRangesEditor({
1938
2553
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1939
2554
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
1940
2555
  /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground", children: "Score Ranges" }),
1941
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: handleAdd, className: "text-xs text-primary hover:underline", children: "+ Add" })
2556
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
1942
2557
  ] }),
1943
2558
  /* @__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
2559
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
@@ -1946,7 +2561,7 @@ function ScoreRangesEditor({
1946
2561
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: "to" }),
1947
2562
  /* @__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
2563
  /* @__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("button", { type: "button", onClick: () => handleRemove(index), className: "text-xs text-destructive opacity-0 group-hover:opacity-100 transition-opacity px-1", children: "x" })
2564
+ /* @__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
2565
  ] }),
1951
2566
  /* @__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
2567
  ] }, index)) })
@@ -1996,26 +2611,20 @@ function ValidationRulesEditor({ question, onUpdate }) {
1996
2611
  },
1997
2612
  `${rule.type}-${index}`
1998
2613
  )) }),
1999
- availableTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2000
- /* @__PURE__ */ jsxRuntime.jsxs(
2001
- "select",
2002
- {
2003
- value: "",
2004
- onChange: (e) => {
2005
- if (e.target.value) addRule(e.target.value);
2006
- },
2007
- className: "flex h-8 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-xs shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-muted-foreground cursor-pointer",
2008
- children: [
2009
- /* @__PURE__ */ jsxRuntime.jsxs("option", { value: "", children: [
2010
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 12 }),
2011
- " Add validation rule..."
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
- ] }) })
2614
+ availableTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
2615
+ NativeSelect,
2616
+ {
2617
+ value: "",
2618
+ onChange: (e) => {
2619
+ if (e.target.value) addRule(e.target.value);
2620
+ },
2621
+ className: "h-8 text-xs text-muted-foreground",
2622
+ children: [
2623
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Add validation rule..." }),
2624
+ availableTypes.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t.value, children: t.label }, t.value))
2625
+ ]
2626
+ }
2627
+ ) })
2019
2628
  ] });
2020
2629
  }
2021
2630
  function RuleRow({
@@ -2029,13 +2638,15 @@ function RuleRow({
2029
2638
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2030
2639
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground", children: label }),
2031
2640
  /* @__PURE__ */ jsxRuntime.jsx(
2032
- "button",
2641
+ fieldcraftReact.Button,
2033
2642
  {
2034
2643
  type: "button",
2644
+ variant: "ghost",
2645
+ size: "icon-xs",
2035
2646
  onClick: onRemove,
2036
- className: "opacity-0 group-hover:opacity-100 transition-opacity",
2647
+ className: "opacity-0 group-hover:opacity-100 transition-opacity hover:bg-destructive/10 hover:text-destructive",
2037
2648
  title: "Remove rule",
2038
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, className: "text-destructive", strokeWidth: 1.75 })
2649
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, strokeWidth: 1.75 })
2039
2650
  }
2040
2651
  )
2041
2652
  ] }),
@@ -2270,11 +2881,13 @@ function ConditionEditor({ question, schema, onUpdate }) {
2270
2881
  !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
2882
  hasConditions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 mb-3", children: showIf.conditions.map((cond, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2272
2883
  index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2273
- "button",
2884
+ fieldcraftReact.Button,
2274
2885
  {
2275
2886
  type: "button",
2887
+ variant: "link",
2888
+ size: "xs",
2276
2889
  onClick: toggleCombine,
2277
- className: "text-[10px] font-semibold uppercase tracking-wider text-primary mb-1.5 block cursor-pointer hover:underline",
2890
+ className: "text-[10px] font-semibold uppercase tracking-wider px-0 mb-1.5",
2278
2891
  children: showIf.combine ?? "AND"
2279
2892
  }
2280
2893
  ),
@@ -2282,43 +2895,39 @@ function ConditionEditor({ question, schema, onUpdate }) {
2282
2895
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2283
2896
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground uppercase tracking-wider", children: "When" }),
2284
2897
  /* @__PURE__ */ jsxRuntime.jsx(
2285
- "button",
2898
+ fieldcraftReact.Button,
2286
2899
  {
2287
2900
  type: "button",
2901
+ variant: "ghost",
2902
+ size: "icon-xs",
2288
2903
  onClick: () => removeCondition(index),
2289
- className: "opacity-0 group-hover:opacity-100 transition-opacity",
2904
+ className: "opacity-0 group-hover:opacity-100 transition-opacity hover:bg-destructive/10 hover:text-destructive",
2290
2905
  title: "Remove condition",
2291
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, className: "text-destructive", strokeWidth: 1.75 })
2906
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 12, strokeWidth: 1.75 })
2292
2907
  }
2293
2908
  )
2294
2909
  ] }),
2295
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2296
- /* @__PURE__ */ jsxRuntime.jsxs(
2297
- "select",
2298
- {
2299
- value: cond.field ?? "",
2300
- onChange: (e) => updateCondition(index, { field: e.target.value }),
2301
- 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",
2302
- children: [
2303
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select field..." }),
2304
- fieldOptions.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f.id, children: f.label }, f.id))
2305
- ]
2306
- }
2307
- ),
2308
- /* @__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" }) })
2309
- ] }),
2310
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
2311
- /* @__PURE__ */ jsxRuntime.jsx(
2312
- "select",
2313
- {
2314
- value: cond.operator ?? "eq",
2315
- onChange: (e) => updateCondition(index, { operator: e.target.value }),
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
- ] }),
2910
+ /* @__PURE__ */ jsxRuntime.jsxs(
2911
+ NativeSelect,
2912
+ {
2913
+ value: cond.field ?? "",
2914
+ onChange: (e) => updateCondition(index, { field: e.target.value }),
2915
+ className: "h-7 text-xs",
2916
+ children: [
2917
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select field..." }),
2918
+ fieldOptions.map((f) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: f.id, children: f.label }, f.id))
2919
+ ]
2920
+ }
2921
+ ),
2922
+ /* @__PURE__ */ jsxRuntime.jsx(
2923
+ NativeSelect,
2924
+ {
2925
+ value: cond.operator ?? "eq",
2926
+ onChange: (e) => updateCondition(index, { operator: e.target.value }),
2927
+ className: "h-7 text-xs",
2928
+ children: OPERATORS.map((op) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: op.value, children: op.label }, op.value))
2929
+ }
2930
+ ),
2322
2931
  cond.operator !== "exists" && cond.operator !== "notExists" && /* @__PURE__ */ jsxRuntime.jsx(
2323
2932
  fieldcraftReact.Input,
2324
2933
  {
@@ -2331,11 +2940,13 @@ function ConditionEditor({ question, schema, onUpdate }) {
2331
2940
  ] })
2332
2941
  ] }, index)) }),
2333
2942
  fieldOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
2334
- "button",
2943
+ fieldcraftReact.Button,
2335
2944
  {
2336
2945
  type: "button",
2946
+ variant: "link",
2947
+ size: "xs",
2337
2948
  onClick: addCondition,
2338
- className: "flex items-center gap-1.5 text-xs text-primary hover:underline",
2949
+ className: "px-0 gap-1.5",
2339
2950
  children: [
2340
2951
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 12, strokeWidth: 2 }),
2341
2952
  "Add condition"
@@ -2497,18 +3108,7 @@ function SettingsSelect({
2497
3108
  }) {
2498
3109
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2499
3110
  /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
2500
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
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
- ] })
3111
+ /* @__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
3112
  ] });
2513
3113
  }
2514
3114
  function PropertiesPanel({ builderState }) {
@@ -2610,13 +3210,14 @@ function QuestionProperties({ question, sectionId, builderState, onClose, onOpen
2610
3210
  typeInfo?.label
2611
3211
  ] }) }),
2612
3212
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 px-4 pt-3 flex gap-0.5", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
2613
- "button",
3213
+ fieldcraftReact.Button,
2614
3214
  {
2615
3215
  type: "button",
3216
+ variant: activeTab === tab.key ? "default" : "ghost",
3217
+ size: "xs",
2616
3218
  onClick: () => setActiveTab(tab.key),
2617
3219
  className: cn(
2618
- "px-3 py-1.5 text-xs font-medium rounded-md border-0 cursor-pointer transition-colors",
2619
- activeTab === tab.key ? "bg-primary text-primary-foreground" : "bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground"
3220
+ activeTab !== tab.key && "text-muted-foreground"
2620
3221
  ),
2621
3222
  children: tab.label
2622
3223
  },
@@ -2875,13 +3476,18 @@ function FormBuilderCore(props) {
2875
3476
  reader.onload = (event) => {
2876
3477
  try {
2877
3478
  const parsed = JSON.parse(event.target?.result);
2878
- if (parsed && parsed.sections && Array.isArray(parsed.sections)) {
2879
- builderState.resetSchema(parsed);
3479
+ const validated = fieldcraftCore.validateSchema(parsed);
3480
+ builderState.resetSchema(validated);
3481
+ } catch (err) {
3482
+ if (err instanceof fieldcraftCore.FormEngineSchemaError) {
3483
+ const details = err.issues.slice(0, 3).map((i) => `\u2022 ${i.path.join(".")}: ${i.message}`).join("\n");
3484
+ alert(`Invalid schema:
3485
+ ${details}`);
3486
+ } else if (err instanceof SyntaxError) {
3487
+ alert("Failed to parse JSON file.");
2880
3488
  } else {
2881
- alert("Invalid schema: must contain a 'sections' array.");
3489
+ alert("Invalid schema file.");
2882
3490
  }
2883
- } catch {
2884
- alert("Failed to parse JSON file.");
2885
3491
  }
2886
3492
  };
2887
3493
  reader.readAsText(file);
@@ -3091,7 +3697,7 @@ function FormBuilderInner(props) {
3091
3697
  }
3092
3698
 
3093
3699
  // src/form-builder/components/FormBuilderGated.tsx
3094
- var FormBuilder = fieldcraftProLicense.requireLicense(FormBuilderInner, "FormBuilder");
3700
+ var FormBuilder = requireLicense(FormBuilderInner, "FormBuilder");
3095
3701
 
3096
3702
  // src/form-builder/theme/presets.ts
3097
3703
  var squaredrDarkPreset = {
@@ -3166,74 +3772,190 @@ var cleanPreset = {
3166
3772
  borderStrong: "#d1d5db",
3167
3773
  textDim: "#9ca3af"
3168
3774
  };
3169
- function ResponseTable({ schema, responses, onRowClick }) {
3170
- const questions = getAllQuestions(schema);
3171
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflow: "auto", fontFamily: "inherit" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3172
- "table",
3173
- {
3174
- style: {
3175
- width: "100%",
3176
- borderCollapse: "collapse",
3177
- fontSize: "13px"
3178
- },
3179
- children: [
3180
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { style: { backgroundColor: "var(--muted, #111113)" }, children: [
3181
- /* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle, children: "Submitted" }),
3182
- questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle, children: q.label }, q.id)),
3183
- /* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle, children: "Score" })
3184
- ] }) }),
3185
- /* @__PURE__ */ jsxRuntime.jsxs("tbody", { children: [
3186
- responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
3187
- "tr",
3188
- {
3189
- onClick: () => onRowClick?.(response),
3190
- style: {
3191
- cursor: onRowClick ? "pointer" : "default",
3192
- borderBottom: "1px solid var(--border, #1c1c20)"
3193
- },
3194
- onMouseEnter: (e) => {
3195
- if (onRowClick) e.currentTarget.style.backgroundColor = "var(--accent, #17171a)";
3196
- },
3197
- onMouseLeave: (e) => {
3198
- e.currentTarget.style.backgroundColor = "transparent";
3199
- },
3200
- children: [
3201
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: new Date(response.submittedAt).toLocaleString() }),
3202
- questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: formatCellValue(response.values[q.id]) }, q.id)),
3203
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: tdStyle, children: response.totalScore ?? "\u2014" })
3204
- ]
3205
- },
3206
- response.sessionToken || idx
3207
- )),
3208
- responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
3209
- "td",
3210
- {
3211
- colSpan: questions.length + 2,
3212
- style: { ...tdStyle, textAlign: "center", color: "var(--muted-foreground, #8a8a95)", padding: "32px" },
3213
- children: "No responses yet"
3214
- }
3215
- ) })
3216
- ] })
3217
- ]
3218
- }
3219
- ) });
3775
+
3776
+ // src/response-viewer/clinical-display-data.ts
3777
+ var BODY_REGION_LABELS = {
3778
+ head: "Head",
3779
+ neck: "Neck",
3780
+ "left-shoulder": "Left Shoulder",
3781
+ "right-shoulder": "Right Shoulder",
3782
+ chest: "Chest",
3783
+ abdomen: "Abdomen",
3784
+ "left-upper-arm": "Left Upper Arm",
3785
+ "right-upper-arm": "Right Upper Arm",
3786
+ "left-forearm": "Left Forearm",
3787
+ "right-forearm": "Right Forearm",
3788
+ "left-hand": "Left Hand",
3789
+ "right-hand": "Right Hand",
3790
+ pelvis: "Pelvis",
3791
+ "left-thigh": "Left Thigh",
3792
+ "right-thigh": "Right Thigh",
3793
+ "left-knee": "Left Knee",
3794
+ "right-knee": "Right Knee",
3795
+ "left-shin": "Left Shin",
3796
+ "right-shin": "Right Shin",
3797
+ "left-foot": "Left Foot",
3798
+ "right-foot": "Right Foot"
3799
+ };
3800
+ var PAIN_FACE_LABELS = [
3801
+ { score: 0, label: "No Hurt", color: "#22c55e" },
3802
+ { score: 2, label: "Hurts Little Bit", color: "#84cc16" },
3803
+ { score: 4, label: "Hurts Little More", color: "#eab308" },
3804
+ { score: 6, label: "Hurts Even More", color: "#f97316" },
3805
+ { score: 8, label: "Hurts Whole Lot", color: "#ef4444" },
3806
+ { score: 10, label: "Hurts Worst", color: "#dc2626" }
3807
+ ];
3808
+ function getPainLabel(score) {
3809
+ const face = PAIN_FACE_LABELS.reduce(
3810
+ (closest, f) => closest == null || Math.abs(f.score - score) < Math.abs(closest.score - score) ? f : closest,
3811
+ void 0
3812
+ );
3813
+ return face;
3814
+ }
3815
+ var SEVERITY_COLORS = {
3816
+ mild: "#eab308",
3817
+ moderate: "#f97316",
3818
+ severe: "#ef4444",
3819
+ "life-threatening": "#991b1b"
3820
+ };
3821
+ var BMI_CATEGORIES = [
3822
+ { label: "Underweight", max: 18.5, color: "#3b82f6" },
3823
+ { label: "Normal", max: 25, color: "#22c55e" },
3824
+ { label: "Overweight", max: 30, color: "#eab308" },
3825
+ { label: "Obese", max: Infinity, color: "#ef4444" }
3826
+ ];
3827
+ function getBmiCategory(bmi) {
3828
+ return BMI_CATEGORIES.find((c) => bmi < c.max) ?? BMI_CATEGORIES[BMI_CATEGORIES.length - 1];
3220
3829
  }
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"
3830
+ var INSTRUMENT_DISPLAY_NAMES = {
3831
+ phq9: "PHQ-9",
3832
+ phq2: "PHQ-2",
3833
+ gad7: "GAD-7",
3834
+ gad2: "GAD-2",
3835
+ "audit-c": "AUDIT-C",
3836
+ dast10: "DAST-10",
3837
+ cssrs: "C-SSRS",
3838
+ epds: "EPDS",
3839
+ isi: "ISI",
3840
+ pcl5: "PCL-5",
3841
+ psc17: "PSC-17"
3228
3842
  };
3229
- var tdStyle = {
3230
- padding: "8px 12px",
3231
- color: "var(--foreground, #e8e8ea)",
3232
- maxWidth: "200px",
3233
- overflow: "hidden",
3234
- textOverflow: "ellipsis",
3235
- whiteSpace: "nowrap"
3843
+ var SCORE_THRESHOLDS = {
3844
+ phq9: [
3845
+ { min: 0, max: 4, label: "Minimal", color: "#22c55e" },
3846
+ { min: 5, max: 9, label: "Mild", color: "#eab308" },
3847
+ { min: 10, max: 14, label: "Moderate", color: "#f97316" },
3848
+ { min: 15, max: 19, label: "Moderately Severe", color: "#ef4444" },
3849
+ { min: 20, max: 27, label: "Severe", color: "#dc2626" }
3850
+ ],
3851
+ phq2: [
3852
+ { min: 0, max: 2, label: "Negative Screen", color: "#22c55e" },
3853
+ { min: 3, max: 6, label: "Positive Screen", color: "#ef4444" }
3854
+ ],
3855
+ gad7: [
3856
+ { min: 0, max: 4, label: "Minimal", color: "#22c55e" },
3857
+ { min: 5, max: 9, label: "Mild", color: "#eab308" },
3858
+ { min: 10, max: 14, label: "Moderate", color: "#f97316" },
3859
+ { min: 15, max: 21, label: "Severe", color: "#ef4444" }
3860
+ ],
3861
+ gad2: [
3862
+ { min: 0, max: 2, label: "Negative Screen", color: "#22c55e" },
3863
+ { min: 3, max: 6, label: "Positive Screen", color: "#ef4444" }
3864
+ ],
3865
+ "audit-c": [
3866
+ { min: 0, max: 3, label: "Low Risk", color: "#22c55e" },
3867
+ { min: 4, max: 7, label: "At Risk", color: "#eab308" },
3868
+ { min: 8, max: 12, label: "High Risk", color: "#ef4444" }
3869
+ ],
3870
+ dast10: [
3871
+ { min: 0, max: 0, label: "No Problems", color: "#22c55e" },
3872
+ { min: 1, max: 2, label: "Low Level", color: "#eab308" },
3873
+ { min: 3, max: 5, label: "Moderate Level", color: "#f97316" },
3874
+ { min: 6, max: 8, label: "Substantial Level", color: "#ef4444" },
3875
+ { min: 9, max: 10, label: "Severe Level", color: "#dc2626" }
3876
+ ],
3877
+ cssrs: [
3878
+ { min: 0, max: 0, label: "No Risk Identified", color: "#22c55e" },
3879
+ { min: 1, max: 1, label: "Wish to be Dead", color: "#eab308" },
3880
+ { min: 2, max: 2, label: "Non-specific Active Suicidal Thoughts", color: "#f97316" },
3881
+ { min: 3, max: 3, label: "Active Suicidal Ideation", color: "#ef4444" },
3882
+ { min: 4, max: 4, label: "Active Ideation with Intent", color: "#dc2626" },
3883
+ { min: 5, max: 5, label: "Active Ideation with Plan", color: "#991b1b" },
3884
+ { min: 6, max: 6, label: "Imminent Risk", color: "#7f1d1d" }
3885
+ ],
3886
+ epds: [
3887
+ { min: 0, max: 8, label: "Low Likelihood", color: "#22c55e" },
3888
+ { min: 9, max: 11, label: "Possible Depression", color: "#eab308" },
3889
+ { min: 12, max: 13, label: "Fairly High Possibility", color: "#f97316" },
3890
+ { min: 14, max: 30, label: "Probable Depression", color: "#ef4444" }
3891
+ ],
3892
+ isi: [
3893
+ { min: 0, max: 7, label: "No Clinically Significant Insomnia", color: "#22c55e" },
3894
+ { min: 8, max: 14, label: "Subthreshold Insomnia", color: "#eab308" },
3895
+ { min: 15, max: 21, label: "Clinical Insomnia (Moderate)", color: "#f97316" },
3896
+ { min: 22, max: 28, label: "Clinical Insomnia (Severe)", color: "#ef4444" }
3897
+ ],
3898
+ pcl5: [
3899
+ { min: 0, max: 30, label: "Below Threshold", color: "#22c55e" },
3900
+ { min: 31, max: 80, label: "Probable PTSD", color: "#ef4444" }
3901
+ ],
3902
+ psc17: [
3903
+ { min: 0, max: 14, label: "No Significant Concerns", color: "#22c55e" },
3904
+ { min: 15, max: 34, label: "Significant Concerns", color: "#ef4444" }
3905
+ ]
3236
3906
  };
3907
+ function getScoreSeverity(instrumentKey, score) {
3908
+ const thresholds = SCORE_THRESHOLDS[instrumentKey];
3909
+ if (!thresholds) return void 0;
3910
+ return thresholds.find((t) => score >= t.min && score <= t.max);
3911
+ }
3912
+ function ResponseTable({ schema, responses, onRowClick }) {
3913
+ const questions = getAllQuestions(schema);
3914
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-[13px]", children: [
3915
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "bg-muted", children: [
3916
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Submitted" }),
3917
+ questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx(
3918
+ "th",
3919
+ {
3920
+ className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap",
3921
+ children: q.label
3922
+ },
3923
+ q.id
3924
+ )),
3925
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2 text-left font-semibold text-foreground border-b-2 border-border whitespace-nowrap", children: "Score" })
3926
+ ] }) }),
3927
+ /* @__PURE__ */ jsxRuntime.jsxs("tbody", { children: [
3928
+ responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
3929
+ "tr",
3930
+ {
3931
+ onClick: () => onRowClick?.(response),
3932
+ className: onRowClick ? "cursor-pointer border-b border-border hover:bg-accent transition-colors" : "border-b border-border",
3933
+ children: [
3934
+ /* @__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() }),
3935
+ questions.map((q) => /* @__PURE__ */ jsxRuntime.jsx(
3936
+ "td",
3937
+ {
3938
+ className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap",
3939
+ children: formatCellValue(response.values[q.id], q.type)
3940
+ },
3941
+ q.id
3942
+ )),
3943
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2 text-foreground max-w-50 overflow-hidden text-ellipsis whitespace-nowrap", children: response.totalScore ?? "\u2014" })
3944
+ ]
3945
+ },
3946
+ response.sessionToken || idx
3947
+ )),
3948
+ responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
3949
+ "td",
3950
+ {
3951
+ colSpan: questions.length + 2,
3952
+ className: "px-3 py-8 text-center text-muted-foreground",
3953
+ children: "No responses yet"
3954
+ }
3955
+ ) })
3956
+ ] })
3957
+ ] }) });
3958
+ }
3237
3959
  function getAllQuestions(schema) {
3238
3960
  const questions = [];
3239
3961
  for (const section of schema.sections) {
@@ -3246,8 +3968,69 @@ function getAllQuestions(schema) {
3246
3968
  }
3247
3969
  return questions;
3248
3970
  }
3249
- function formatCellValue(value) {
3971
+ function formatCellValue(value, type) {
3250
3972
  if (value == null) return "\u2014";
3973
+ if (type) {
3974
+ switch (type) {
3975
+ case "vitals_entry": {
3976
+ if (typeof value !== "object" || value === null) break;
3977
+ const v = value;
3978
+ const parts = [];
3979
+ if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
3980
+ if (v.heartRate) parts.push(`HR ${v.heartRate}`);
3981
+ if (v.temperature) parts.push(`${v.temperature}\xB0F`);
3982
+ if (v.oxygenSaturation) parts.push(`SpO\u2082 ${v.oxygenSaturation}%`);
3983
+ return parts.length > 0 ? parts.join(", ") : "\u2014";
3984
+ }
3985
+ case "medication_list":
3986
+ if (Array.isArray(value)) return `${value.length} medication${value.length !== 1 ? "s" : ""}`;
3987
+ break;
3988
+ case "allergy_list":
3989
+ if (Array.isArray(value)) return `${value.length} allerg${value.length !== 1 ? "ies" : "y"}`;
3990
+ break;
3991
+ case "body_diagram":
3992
+ if (Array.isArray(value)) {
3993
+ const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
3994
+ return labels.join(", ");
3995
+ }
3996
+ break;
3997
+ case "pain_scale":
3998
+ if (typeof value === "number") return `${value}/10`;
3999
+ break;
4000
+ case "bmi_calculator": {
4001
+ if (typeof value !== "object" || value === null) break;
4002
+ const d = value;
4003
+ if (d.bmi != null) return `BMI ${d.bmi}`;
4004
+ break;
4005
+ }
4006
+ case "payment": {
4007
+ if (typeof value !== "object" || value === null) break;
4008
+ const p = value;
4009
+ const status = p.status;
4010
+ return status ? status.charAt(0).toUpperCase() + status.slice(1) : "\u2014";
4011
+ }
4012
+ case "insurance_card": {
4013
+ if (typeof value !== "object" || value === null) break;
4014
+ const ins = value;
4015
+ const parts = [ins.carrierId, ins.planName].filter(Boolean);
4016
+ return parts.length > 0 ? parts.join(" \u2014 ") : "Card uploaded";
4017
+ }
4018
+ case "legal_name": {
4019
+ if (typeof value !== "object" || value === null) break;
4020
+ const n = value;
4021
+ return [n.first, n.last].filter(Boolean).join(" ") || "\u2014";
4022
+ }
4023
+ case "address": {
4024
+ if (typeof value !== "object" || value === null) break;
4025
+ const a = value;
4026
+ return [a.city, a.state].filter(Boolean).join(", ") || "\u2014";
4027
+ }
4028
+ case "consent":
4029
+ return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
4030
+ case "signature":
4031
+ return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "\u2014";
4032
+ }
4033
+ }
3251
4034
  if (typeof value === "boolean") return value ? "Yes" : "No";
3252
4035
  if (Array.isArray(value)) return value.join(", ");
3253
4036
  if (typeof value === "object") return JSON.stringify(value);
@@ -3258,238 +4041,570 @@ function ResponseCard({ response, fields, onClick }) {
3258
4041
  "div",
3259
4042
  {
3260
4043
  onClick,
3261
- style: {
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
- },
4044
+ 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
4045
  children: [
3277
- /* @__PURE__ */ jsxRuntime.jsxs(
3278
- "div",
3279
- {
3280
- style: {
3281
- display: "flex",
3282
- justifyContent: "space-between",
3283
- marginBottom: "12px",
3284
- fontSize: "12px",
3285
- color: "var(--muted-foreground, #8a8a95)"
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) })
4046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between mb-3 text-xs text-muted-foreground", children: [
4047
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(response.submittedAt).toLocaleString() }),
4048
+ response.completionTimeMs != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4049
+ Math.round(response.completionTimeMs / 1e3),
4050
+ "s"
4051
+ ] })
4052
+ ] }),
4053
+ fields.slice(0, 4).map((field) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2", children: [
4054
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-muted-foreground mb-0.5", children: field.label }),
4055
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[13px] text-foreground", children: formatCardValue(field.value, field.type) })
3299
4056
  ] }, field.questionId)),
3300
- fields.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "var(--muted-foreground, #8a8a95)", marginTop: "8px" }, children: [
4057
+ fields.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mt-2", children: [
3301
4058
  "+",
3302
4059
  fields.length - 4,
3303
4060
  " more fields"
3304
4061
  ] }),
3305
- response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs(
3306
- "div",
3307
- {
3308
- style: {
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
- )
4062
+ response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 pt-2 border-t border-border text-[13px] font-semibold text-foreground", children: [
4063
+ "Score: ",
4064
+ response.totalScore
4065
+ ] })
3322
4066
  ]
3323
4067
  }
3324
4068
  );
3325
4069
  }
3326
- function formatValue(value) {
4070
+ function formatCardValue(value, type) {
3327
4071
  if (value == null) return "\u2014";
4072
+ if (type) {
4073
+ switch (type) {
4074
+ case "vitals_entry": {
4075
+ if (typeof value !== "object" || value === null) break;
4076
+ const v = value;
4077
+ const parts = [];
4078
+ if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
4079
+ if (v.heartRate) parts.push(`HR ${v.heartRate}`);
4080
+ if (v.temperature) parts.push(`${v.temperature}\xB0F`);
4081
+ if (v.oxygenSaturation) parts.push(`SpO\u2082 ${v.oxygenSaturation}%`);
4082
+ return parts.length > 0 ? parts.join(", ") : "\u2014";
4083
+ }
4084
+ case "medication_list":
4085
+ if (Array.isArray(value)) return `${value.length} medication${value.length !== 1 ? "s" : ""}`;
4086
+ break;
4087
+ case "allergy_list":
4088
+ if (Array.isArray(value)) return `${value.length} allerg${value.length !== 1 ? "ies" : "y"}`;
4089
+ break;
4090
+ case "body_diagram":
4091
+ if (Array.isArray(value)) {
4092
+ const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
4093
+ return labels.join(", ");
4094
+ }
4095
+ break;
4096
+ case "pain_scale":
4097
+ if (typeof value === "number") return `${value}/10`;
4098
+ break;
4099
+ case "bmi_calculator": {
4100
+ if (typeof value !== "object" || value === null) break;
4101
+ const d = value;
4102
+ if (d.bmi != null) return `BMI ${d.bmi}`;
4103
+ break;
4104
+ }
4105
+ case "payment": {
4106
+ if (typeof value !== "object" || value === null) break;
4107
+ const p = value;
4108
+ const status = p.status;
4109
+ return status ? status.charAt(0).toUpperCase() + status.slice(1) : "\u2014";
4110
+ }
4111
+ case "insurance_card": {
4112
+ if (typeof value !== "object" || value === null) break;
4113
+ const ins = value;
4114
+ const parts = [ins.carrierId, ins.planName].filter(Boolean);
4115
+ return parts.length > 0 ? parts.join(" \u2014 ") : "Card uploaded";
4116
+ }
4117
+ case "legal_name": {
4118
+ if (typeof value !== "object" || value === null) break;
4119
+ const n = value;
4120
+ return [n.first, n.last].filter(Boolean).join(" ") || "\u2014";
4121
+ }
4122
+ case "address": {
4123
+ if (typeof value !== "object" || value === null) break;
4124
+ const a = value;
4125
+ return [a.city, a.state].filter(Boolean).join(", ") || "\u2014";
4126
+ }
4127
+ case "consent":
4128
+ return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
4129
+ case "signature":
4130
+ return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "\u2014";
4131
+ }
4132
+ }
3328
4133
  if (typeof value === "boolean") return value ? "Yes" : "No";
3329
- if (Array.isArray(value)) return value.map(formatValue).join(", ");
4134
+ if (Array.isArray(value)) return value.map(String).join(", ");
3330
4135
  if (typeof value === "object") return JSON.stringify(value);
3331
4136
  return String(value);
3332
4137
  }
3333
4138
  function ResponseDetail({ response, fields, onBack }) {
3334
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontFamily: "inherit" }, children: [
3335
- /* @__PURE__ */ jsxRuntime.jsxs(
3336
- "div",
3337
- {
3338
- style: {
3339
- display: "flex",
3340
- alignItems: "center",
3341
- gap: "12px",
3342
- marginBottom: "20px",
3343
- paddingBottom: "12px",
3344
- borderBottom: "1px solid var(--border, #1c1c20)"
3345
- },
3346
- children: [
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(
4139
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4140
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-5 pb-3 border-b border-border", children: [
4141
+ onBack && /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "outline", size: "sm", onClick: onBack, children: "Back" }),
4142
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4143
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-foreground", children: "Response Detail" }),
4144
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
4145
+ "Submitted ",
4146
+ new Date(response.submittedAt).toLocaleString(),
4147
+ response.completionTimeMs != null && ` \u2014 ${Math.round(response.completionTimeMs / 1e3)}s`
4148
+ ] })
4149
+ ] })
4150
+ ] }),
4151
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-4", children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(
3376
4152
  "div",
3377
4153
  {
3378
- style: {
3379
- padding: "12px",
3380
- backgroundColor: "var(--muted, #111113)",
3381
- borderRadius: "6px"
3382
- },
4154
+ className: "p-3 bg-muted rounded-md",
3383
4155
  children: [
3384
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "var(--muted-foreground, #8a8a95)", marginBottom: "4px" }, children: [
4156
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground mb-1", children: [
3385
4157
  field.label,
3386
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { marginLeft: "8px", fontSize: "11px", color: "var(--muted-foreground, #8a8a95)" }, children: [
4158
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-2 text-[11px] text-muted-foreground", children: [
3387
4159
  "(",
3388
4160
  field.type,
3389
4161
  ")"
3390
4162
  ] })
3391
4163
  ] }),
3392
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "14px", color: "var(--foreground, #e8e8ea)", whiteSpace: "pre-wrap" }, children: formatDetailValue(field.value) })
4164
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-foreground", children: renderFieldValue(field) })
3393
4165
  ]
3394
4166
  },
3395
4167
  field.questionId
3396
4168
  )) }),
3397
- response.scores && Object.keys(response.scores).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "20px" }, children: [
3398
- /* @__PURE__ */ jsxRuntime.jsx(
3399
- "div",
3400
- {
3401
- style: {
3402
- fontSize: "14px",
3403
- fontWeight: 600,
3404
- color: "var(--foreground, #e8e8ea)",
3405
- marginBottom: "12px"
3406
- },
3407
- children: "Scores"
3408
- }
3409
- ),
3410
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "12px", flexWrap: "wrap" }, children: [
3411
- Object.entries(response.scores).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs(
3412
- "div",
3413
- {
3414
- style: {
3415
- padding: "8px 16px",
3416
- backgroundColor: "var(--accent, #17171a)",
3417
- borderRadius: "6px",
3418
- fontSize: "13px"
3419
- },
3420
- children: [
3421
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "var(--muted-foreground, #8a8a95)" }, children: [
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"
4169
+ response.scores && Object.keys(response.scores).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5", children: [
4170
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-foreground mb-3", children: "Scores" }),
4171
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 flex-wrap", children: [
4172
+ Object.entries(response.scores).map(([key, value]) => {
4173
+ const displayName = INSTRUMENT_DISPLAY_NAMES[key] ?? key;
4174
+ const severity = typeof value === "number" ? getScoreSeverity(key, value) : void 0;
4175
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4176
+ "div",
4177
+ {
4178
+ className: "px-4 py-2 bg-accent rounded-md text-[13px] flex items-center gap-2",
4179
+ children: [
4180
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
4181
+ displayName,
4182
+ ": "
4183
+ ] }),
4184
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-primary", children: value }),
4185
+ severity && /* @__PURE__ */ jsxRuntime.jsx(
4186
+ "span",
4187
+ {
4188
+ className: "text-[11px] font-semibold px-2 py-0.5 rounded-full text-white",
4189
+ style: { backgroundColor: severity.color },
4190
+ children: severity.label
4191
+ }
4192
+ )
4193
+ ]
3438
4194
  },
3439
- children: [
3440
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--muted-foreground, #8a8a95)" }, children: "Total: " }),
3441
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "var(--primary, oklch(0.82 0.14 210))" }, children: response.totalScore })
3442
- ]
3443
- }
3444
- )
4195
+ key
4196
+ );
4197
+ }),
4198
+ response.totalScore != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 bg-accent rounded-md text-[13px]", children: [
4199
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Total: " }),
4200
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-primary", children: response.totalScore })
4201
+ ] })
3445
4202
  ] })
3446
4203
  ] }),
3447
- /* @__PURE__ */ jsxRuntime.jsxs(
4204
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 pt-3 border-t border-border text-xs text-muted-foreground", children: [
4205
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4206
+ "Schema: ",
4207
+ response.schemaId,
4208
+ " (v",
4209
+ response.schemaVersion,
4210
+ ")"
4211
+ ] }),
4212
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4213
+ "Session: ",
4214
+ response.sessionToken
4215
+ ] })
4216
+ ] })
4217
+ ] });
4218
+ }
4219
+ function renderFieldValue(field) {
4220
+ const { type, value } = field;
4221
+ if (value == null) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2014" });
4222
+ switch (type) {
4223
+ case "vitals_entry":
4224
+ return renderVitals(value);
4225
+ case "medication_list":
4226
+ return renderMedications(value);
4227
+ case "allergy_list":
4228
+ return renderAllergies(value);
4229
+ case "bmi_calculator":
4230
+ return renderBmi(value);
4231
+ case "body_diagram":
4232
+ return renderBodyDiagram(value);
4233
+ case "pain_scale":
4234
+ return renderPainScale(value);
4235
+ case "insurance_card":
4236
+ return renderInsurance(value);
4237
+ case "payment":
4238
+ return renderPayment(value);
4239
+ case "legal_name":
4240
+ return renderLegalName(value);
4241
+ case "address":
4242
+ return renderAddress(value);
4243
+ case "consent":
4244
+ return renderConsent(value);
4245
+ case "signature":
4246
+ return renderSignature(value);
4247
+ default:
4248
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-pre-wrap", children: formatFallbackValue(value) });
4249
+ }
4250
+ }
4251
+ var VITALS_LABELS = {
4252
+ systolicBp: { label: "Systolic BP", unit: "mmHg", normalRange: "90-140" },
4253
+ diastolicBp: { label: "Diastolic BP", unit: "mmHg", normalRange: "60-90" },
4254
+ heartRate: { label: "Heart Rate", unit: "bpm", normalRange: "60-100" },
4255
+ temperature: { label: "Temperature", unit: "\xB0F", normalRange: "97.0-99.5" },
4256
+ respiratoryRate: { label: "Respiratory Rate", unit: "/min", normalRange: "12-20" },
4257
+ oxygenSaturation: { label: "SpO\u2082", unit: "%", normalRange: "95-100" }
4258
+ };
4259
+ function renderVitals(value) {
4260
+ if (typeof value !== "object" || value === null) return formatFallbackValue(value);
4261
+ const vitals = value;
4262
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: Object.entries(VITALS_LABELS).map(([key, meta]) => {
4263
+ const val = vitals[key];
4264
+ if (val == null || val === 0) return null;
4265
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[13px]", children: [
4266
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
4267
+ meta.label,
4268
+ ": "
4269
+ ] }),
4270
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
4271
+ String(val),
4272
+ " ",
4273
+ meta.unit
4274
+ ] }),
4275
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-muted-foreground ml-1", children: [
4276
+ "(",
4277
+ meta.normalRange,
4278
+ ")"
4279
+ ] })
4280
+ ] }, key);
4281
+ }) });
4282
+ }
4283
+ function renderMedications(value) {
4284
+ if (!Array.isArray(value) || value.length === 0) return "No medications";
4285
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: value.map((med, idx) => {
4286
+ const m = med;
4287
+ const parts = [m.dosage, m.frequency, m.route].filter(Boolean);
4288
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3448
4289
  "div",
3449
4290
  {
3450
- style: {
3451
- marginTop: "20px",
3452
- paddingTop: "12px",
3453
- borderTop: "1px solid var(--border, #1c1c20)",
3454
- fontSize: "12px",
3455
- color: "var(--muted-foreground, #8a8a95)"
3456
- },
4291
+ className: "px-3 py-2 border border-border rounded-md text-[13px]",
3457
4292
  children: [
3458
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3459
- "Schema: ",
3460
- response.schemaId,
3461
- " (v",
3462
- response.schemaVersion,
4293
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: m.name || "Unnamed" }),
4294
+ parts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
4295
+ " \u2014 ",
4296
+ parts.join(" \xB7 ")
4297
+ ] }),
4298
+ m.notes && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground mt-0.5", children: m.notes })
4299
+ ]
4300
+ },
4301
+ idx
4302
+ );
4303
+ }) });
4304
+ }
4305
+ function renderAllergies(value) {
4306
+ if (!Array.isArray(value) || value.length === 0) return "No allergies";
4307
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: value.map((allergy, idx) => {
4308
+ const a = allergy;
4309
+ const sevColor = SEVERITY_COLORS[a.severity];
4310
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4311
+ "div",
4312
+ {
4313
+ className: "px-3 py-2 border border-border rounded-md text-[13px] flex items-center gap-2 flex-wrap",
4314
+ children: [
4315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: a.allergen || "Unknown" }),
4316
+ a.type && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-muted-foreground", children: [
4317
+ "(",
4318
+ a.type,
3463
4319
  ")"
3464
4320
  ] }),
3465
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3466
- "Session: ",
3467
- response.sessionToken
4321
+ a.severity && /* @__PURE__ */ jsxRuntime.jsx(
4322
+ "span",
4323
+ {
4324
+ className: "text-[11px] font-semibold px-2 py-0.5 rounded-full",
4325
+ style: {
4326
+ backgroundColor: sevColor ?? "var(--accent)",
4327
+ color: sevColor ? "#fff" : "var(--foreground)"
4328
+ },
4329
+ children: a.severity
4330
+ }
4331
+ ),
4332
+ a.reaction && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
4333
+ "\u2014 ",
4334
+ a.reaction
3468
4335
  ] })
3469
4336
  ]
4337
+ },
4338
+ idx
4339
+ );
4340
+ }) });
4341
+ }
4342
+ function renderBmi(value) {
4343
+ if (typeof value !== "object" || value === null) return formatFallbackValue(value);
4344
+ const data = value;
4345
+ const bmi = data.bmi;
4346
+ const unit = data.unit ?? "imperial";
4347
+ const height = data.height;
4348
+ const weight = data.weight;
4349
+ const heightUnit = unit === "metric" ? "cm" : "in";
4350
+ const weightUnit = unit === "metric" ? "kg" : "lbs";
4351
+ const category = bmi != null ? getBmiCategory(bmi) : void 0;
4352
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[13px] flex items-center gap-2 flex-wrap", children: [
4353
+ height != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4354
+ "Height: ",
4355
+ height,
4356
+ " ",
4357
+ heightUnit
4358
+ ] }),
4359
+ height != null && weight != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
4360
+ weight != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4361
+ "Weight: ",
4362
+ weight,
4363
+ " ",
4364
+ weightUnit
4365
+ ] }),
4366
+ bmi != null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4367
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2014" }),
4368
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
4369
+ "BMI: ",
4370
+ bmi
4371
+ ] }),
4372
+ category && /* @__PURE__ */ jsxRuntime.jsx(
4373
+ "span",
4374
+ {
4375
+ className: "text-[11px] font-semibold px-2 py-0.5 rounded-full text-white",
4376
+ style: { backgroundColor: category.color },
4377
+ children: category.label
4378
+ }
4379
+ )
4380
+ ] })
4381
+ ] });
4382
+ }
4383
+ function renderBodyDiagram(value) {
4384
+ if (!Array.isArray(value) || value.length === 0) return "No regions selected";
4385
+ const labels = value.map((id) => BODY_REGION_LABELS[id] ?? id);
4386
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.join(", ") });
4387
+ }
4388
+ function renderPainScale(value) {
4389
+ if (typeof value !== "number") return formatFallbackValue(value);
4390
+ const face = getPainLabel(value);
4391
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", style: { color: face?.color }, children: [
4392
+ value,
4393
+ "/10",
4394
+ face ? ` \u2014 ${face.label}` : ""
4395
+ ] });
4396
+ }
4397
+ function renderInsurance(value) {
4398
+ if (typeof value !== "object" || value === null) return formatFallbackValue(value);
4399
+ const ins = value;
4400
+ const textFields = [
4401
+ { key: "carrierId", label: "Carrier" },
4402
+ { key: "planName", label: "Plan" },
4403
+ { key: "memberId", label: "Member ID" },
4404
+ { key: "groupNumber", label: "Group #" },
4405
+ { key: "subscriberName", label: "Subscriber" },
4406
+ { key: "relationship", label: "Relationship" }
4407
+ ];
4408
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
4409
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-3 gap-y-1 text-[13px]", children: textFields.map(({ key, label }) => {
4410
+ const val = ins[key];
4411
+ if (!val) return null;
4412
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4413
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
4414
+ label,
4415
+ ": "
4416
+ ] }),
4417
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: String(val) })
4418
+ ] }, key);
4419
+ }) }),
4420
+ !!(ins.frontImage || ins.backImage) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 mt-1", children: [
4421
+ typeof ins.frontImage === "string" && /* @__PURE__ */ jsxRuntime.jsx(
4422
+ "img",
4423
+ {
4424
+ src: ins.frontImage,
4425
+ alt: "Front of card",
4426
+ className: "h-12 rounded border border-border"
4427
+ }
4428
+ ),
4429
+ typeof ins.backImage === "string" && /* @__PURE__ */ jsxRuntime.jsx(
4430
+ "img",
4431
+ {
4432
+ src: ins.backImage,
4433
+ alt: "Back of card",
4434
+ className: "h-12 rounded border border-border"
4435
+ }
4436
+ )
4437
+ ] })
4438
+ ] });
4439
+ }
4440
+ function renderPayment(value) {
4441
+ if (typeof value !== "object" || value === null) return formatFallbackValue(value);
4442
+ const payment = value;
4443
+ const status = payment.status;
4444
+ const statusConfig = {
4445
+ succeeded: { bg: "#22c55e", label: "Succeeded" },
4446
+ failed: { bg: "#ef4444", label: "Failed" },
4447
+ processing: { bg: "#eab308", label: "Processing" }
4448
+ };
4449
+ const cfg = statusConfig[status];
4450
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-[13px]", children: [
4451
+ /* @__PURE__ */ jsxRuntime.jsx(
4452
+ "span",
4453
+ {
4454
+ className: "text-[11px] font-semibold px-2.5 py-0.5 rounded-full text-white",
4455
+ style: { backgroundColor: cfg?.bg ?? "var(--accent)" },
4456
+ children: cfg?.label ?? status
3470
4457
  }
3471
- )
4458
+ ),
4459
+ !!payment.chargeId && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: String(payment.chargeId) }),
4460
+ !!payment.error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive", children: String(payment.error) })
3472
4461
  ] });
3473
4462
  }
3474
- function formatDetailValue(value) {
4463
+ function renderLegalName(value) {
4464
+ if (typeof value !== "object" || value === null) return formatFallbackValue(value);
4465
+ const name = value;
4466
+ const parts = [name.first, name.middle, name.last].filter(Boolean);
4467
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: parts.join(" ") || "\u2014" });
4468
+ }
4469
+ function renderAddress(value) {
4470
+ if (typeof value !== "object" || value === null) return formatFallbackValue(value);
4471
+ const addr = value;
4472
+ const line1 = [addr.street, addr.street2].filter(Boolean).join(", ");
4473
+ const line2 = [addr.city, addr.state, addr.zip].filter(Boolean).join(", ");
4474
+ const full = [line1, line2].filter(Boolean).join(", ");
4475
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: full || "\u2014" });
4476
+ }
4477
+ function renderConsent(value) {
4478
+ const agreed = value === true || value === "true" || value === "agreed";
4479
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: agreed ? "font-semibold text-green-500" : "font-semibold text-muted-foreground", children: agreed ? "Agreed" : "Not agreed" });
4480
+ }
4481
+ function renderSignature(value) {
4482
+ if (typeof value !== "string") return formatFallbackValue(value);
4483
+ if (value.startsWith("data:image")) {
4484
+ return /* @__PURE__ */ jsxRuntime.jsx(
4485
+ "img",
4486
+ {
4487
+ src: value,
4488
+ alt: "Signature",
4489
+ className: "h-15 border border-border rounded"
4490
+ }
4491
+ );
4492
+ }
4493
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: value });
4494
+ }
4495
+ function formatFallbackValue(value) {
3475
4496
  if (value == null) return "\u2014";
3476
4497
  if (typeof value === "boolean") return value ? "Yes" : "No";
3477
- if (Array.isArray(value)) return value.map(formatDetailValue).join("\n");
4498
+ if (Array.isArray(value)) return value.map(formatFallbackValue).join(", ");
3478
4499
  if (typeof value === "object") return JSON.stringify(value, null, 2);
3479
4500
  return String(value);
3480
4501
  }
3481
- var viewButtonStyle = (active) => ({
3482
- padding: "4px 10px",
3483
- fontSize: "12px",
3484
- fontWeight: active ? 600 : 400,
3485
- color: active ? "var(--primary, oklch(0.82 0.14 210))" : "var(--muted-foreground, #8a8a95)",
3486
- backgroundColor: active ? "var(--accent, #17171a)" : "transparent",
3487
- border: "1px solid",
3488
- borderColor: active ? "var(--ring, oklch(0.82 0.14 210))" : "var(--border, #1c1c20)",
3489
- borderRadius: "6px",
3490
- cursor: "pointer",
3491
- fontFamily: "inherit"
3492
- });
4502
+
4503
+ // src/response-viewer/export-utils.ts
4504
+ function exportToCsv(schema, responses, filename = "responses.csv") {
4505
+ const questions = getExportableQuestions(schema);
4506
+ const headers = ["Submitted", ...questions.map((q) => q.label), "Score"];
4507
+ const rows = responses.map((r) => {
4508
+ const submitted = new Date(r.submittedAt).toLocaleString();
4509
+ const values = questions.map((q) => formatExportValue(r.values[q.id], q.type));
4510
+ const score = r.totalScore != null ? String(r.totalScore) : "";
4511
+ return [submitted, ...values, score];
4512
+ });
4513
+ const csvContent = [headers, ...rows].map((row) => row.map(escapeCsvField).join(",")).join("\n");
4514
+ downloadBlob(csvContent, filename, "text/csv;charset=utf-8;");
4515
+ }
4516
+ function exportToJson(responses, filename = "responses.json") {
4517
+ const content = JSON.stringify(responses, null, 2);
4518
+ downloadBlob(content, filename, "application/json;charset=utf-8;");
4519
+ }
4520
+ function getExportableQuestions(schema) {
4521
+ const questions = [];
4522
+ for (const section of schema.sections) {
4523
+ for (const q of section.questions) {
4524
+ if (q.type === "info-block" || q.type === "section-header" || q.type === "page-break") {
4525
+ continue;
4526
+ }
4527
+ questions.push(q);
4528
+ }
4529
+ }
4530
+ return questions;
4531
+ }
4532
+ function formatExportValue(value, type) {
4533
+ if (value == null) return "";
4534
+ switch (type) {
4535
+ case "vitals_entry": {
4536
+ if (typeof value !== "object" || value === null) break;
4537
+ const v = value;
4538
+ const parts = [];
4539
+ if (v.systolicBp && v.diastolicBp) parts.push(`BP ${v.systolicBp}/${v.diastolicBp}`);
4540
+ if (v.heartRate) parts.push(`HR ${v.heartRate}`);
4541
+ if (v.temperature) parts.push(`${v.temperature}\xB0F`);
4542
+ if (v.oxygenSaturation) parts.push(`SpO2 ${v.oxygenSaturation}%`);
4543
+ return parts.join("; ");
4544
+ }
4545
+ case "medication_list":
4546
+ if (Array.isArray(value)) {
4547
+ return value.map((m) => [m.name, m.dosage, m.frequency].filter(Boolean).join(" ")).join("; ");
4548
+ }
4549
+ break;
4550
+ case "allergy_list":
4551
+ if (Array.isArray(value)) {
4552
+ return value.map((a) => [a.allergen, a.severity].filter(Boolean).join(" - ")).join("; ");
4553
+ }
4554
+ break;
4555
+ case "body_diagram":
4556
+ if (Array.isArray(value)) {
4557
+ return value.map((id) => BODY_REGION_LABELS[id] ?? id).join("; ");
4558
+ }
4559
+ break;
4560
+ case "pain_scale":
4561
+ return typeof value === "number" ? `${value}/10` : String(value);
4562
+ case "bmi_calculator": {
4563
+ if (typeof value !== "object" || value === null) break;
4564
+ const d = value;
4565
+ return d.bmi != null ? `BMI ${d.bmi}` : "";
4566
+ }
4567
+ case "payment": {
4568
+ if (typeof value !== "object" || value === null) break;
4569
+ return value.status ?? "";
4570
+ }
4571
+ case "legal_name": {
4572
+ if (typeof value !== "object" || value === null) break;
4573
+ const n = value;
4574
+ return [n.first, n.middle, n.last].filter(Boolean).join(" ");
4575
+ }
4576
+ case "address": {
4577
+ if (typeof value !== "object" || value === null) break;
4578
+ const a = value;
4579
+ return [a.street, a.city, a.state, a.zip].filter(Boolean).join(", ");
4580
+ }
4581
+ case "consent":
4582
+ return value === true || value === "true" || value === "agreed" ? "Agreed" : "Not agreed";
4583
+ case "signature":
4584
+ return typeof value === "string" && value.startsWith("data:image") ? "Signed" : "";
4585
+ }
4586
+ if (typeof value === "boolean") return value ? "Yes" : "No";
4587
+ if (Array.isArray(value)) return value.join("; ");
4588
+ if (typeof value === "object") return JSON.stringify(value);
4589
+ return String(value);
4590
+ }
4591
+ function escapeCsvField(field) {
4592
+ if (field.includes(",") || field.includes('"') || field.includes("\n")) {
4593
+ return `"${field.replace(/"/g, '""')}"`;
4594
+ }
4595
+ return field;
4596
+ }
4597
+ function downloadBlob(content, filename, mimeType) {
4598
+ const blob = new Blob([content], { type: mimeType });
4599
+ const url = URL.createObjectURL(blob);
4600
+ const link = document.createElement("a");
4601
+ link.href = url;
4602
+ link.download = filename;
4603
+ document.body.appendChild(link);
4604
+ link.click();
4605
+ document.body.removeChild(link);
4606
+ URL.revokeObjectURL(url);
4607
+ }
3493
4608
  function ResponseViewerInner({
3494
4609
  schema,
3495
4610
  responses,
@@ -3515,62 +4630,73 @@ function ResponseViewerInner({
3515
4630
  value: response.values[q.id]
3516
4631
  }));
3517
4632
  }
4633
+ const heightValue = typeof height === "number" ? `${height}px` : height;
4634
+ const widthValue = typeof width === "number" ? `${width}px` : width;
3518
4635
  return /* @__PURE__ */ jsxRuntime.jsxs(
3519
4636
  "div",
3520
4637
  {
3521
- style: {
3522
- display: "flex",
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
- },
4638
+ className: "flex flex-col border border-border rounded-lg overflow-hidden bg-background text-foreground",
4639
+ style: { height: heightValue, width: widthValue },
3533
4640
  children: [
3534
- !selectedResponse && /* @__PURE__ */ jsxRuntime.jsxs(
3535
- "div",
3536
- {
3537
- style: {
3538
- display: "flex",
3539
- justifyContent: "space-between",
3540
- alignItems: "center",
3541
- padding: "8px 12px",
3542
- borderBottom: "1px solid var(--border, #1c1c20)"
3543
- },
3544
- children: [
3545
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "13px", color: "var(--muted-foreground, #8a8a95)" }, children: [
3546
- responses.length,
3547
- " response",
3548
- responses.length !== 1 ? "s" : ""
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
- }
4641
+ !selectedResponse && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center px-3 py-2 border-b border-border", children: [
4642
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground", children: [
4643
+ responses.length,
4644
+ " response",
4645
+ responses.length !== 1 ? "s" : ""
4646
+ ] }),
4647
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
4648
+ /* @__PURE__ */ jsxRuntime.jsx(
4649
+ fieldcraftReact.Button,
4650
+ {
4651
+ variant: viewMode === "table" ? "secondary" : "ghost",
4652
+ size: "sm",
4653
+ className: cn(
4654
+ "h-7 text-xs",
4655
+ viewMode === "table" && "font-semibold border border-ring"
3559
4656
  ),
3560
- /* @__PURE__ */ jsxRuntime.jsx(
3561
- "button",
3562
- {
3563
- type: "button",
3564
- style: viewButtonStyle(viewMode === "card"),
3565
- onClick: () => setViewMode("card"),
3566
- children: "Cards"
3567
- }
3568
- )
3569
- ] })
3570
- ]
3571
- }
3572
- ),
3573
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "auto", padding: selectedResponse ? "16px" : void 0 }, children: selectedResponse ? /* @__PURE__ */ jsxRuntime.jsx(
4657
+ onClick: () => setViewMode("table"),
4658
+ children: "Table"
4659
+ }
4660
+ ),
4661
+ /* @__PURE__ */ jsxRuntime.jsx(
4662
+ fieldcraftReact.Button,
4663
+ {
4664
+ variant: viewMode === "card" ? "secondary" : "ghost",
4665
+ size: "sm",
4666
+ className: cn(
4667
+ "h-7 text-xs",
4668
+ viewMode === "card" && "font-semibold border border-ring"
4669
+ ),
4670
+ onClick: () => setViewMode("card"),
4671
+ children: "Cards"
4672
+ }
4673
+ ),
4674
+ /* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { orientation: "vertical", className: "h-5 mx-1" }),
4675
+ /* @__PURE__ */ jsxRuntime.jsx(
4676
+ fieldcraftReact.Button,
4677
+ {
4678
+ variant: "outline",
4679
+ size: "sm",
4680
+ className: "h-7 text-xs",
4681
+ onClick: () => exportToCsv(schema, responses),
4682
+ disabled: responses.length === 0,
4683
+ children: "Export CSV"
4684
+ }
4685
+ ),
4686
+ /* @__PURE__ */ jsxRuntime.jsx(
4687
+ fieldcraftReact.Button,
4688
+ {
4689
+ variant: "outline",
4690
+ size: "sm",
4691
+ className: "h-7 text-xs",
4692
+ onClick: () => exportToJson(responses),
4693
+ disabled: responses.length === 0,
4694
+ children: "Export JSON"
4695
+ }
4696
+ )
4697
+ ] })
4698
+ ] }),
4699
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex-1 overflow-auto", selectedResponse && "p-4"), children: selectedResponse ? /* @__PURE__ */ jsxRuntime.jsx(
3574
4700
  ResponseDetail,
3575
4701
  {
3576
4702
  response: selectedResponse,
@@ -3584,29 +4710,18 @@ function ResponseViewerInner({
3584
4710
  responses,
3585
4711
  onRowClick: handleSelect
3586
4712
  }
3587
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
3588
- "div",
3589
- {
3590
- style: {
3591
- display: "grid",
3592
- gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
3593
- gap: "12px",
3594
- padding: "12px"
4713
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 p-3 grid-cols-[repeat(auto-fill,minmax(280px,1fr))]", children: [
4714
+ responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsx(
4715
+ ResponseCard,
4716
+ {
4717
+ response,
4718
+ fields: getFields(response),
4719
+ onClick: () => handleSelect(response)
3595
4720
  },
3596
- children: [
3597
- responses.map((response, idx) => /* @__PURE__ */ jsxRuntime.jsx(
3598
- ResponseCard,
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
- ) })
4721
+ response.sessionToken || idx
4722
+ )),
4723
+ responses.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center text-muted-foreground", children: "No responses yet" })
4724
+ ] }) })
3610
4725
  ]
3611
4726
  }
3612
4727
  );
@@ -3625,7 +4740,7 @@ function getAllQuestions2(schema) {
3625
4740
  }
3626
4741
 
3627
4742
  // src/response-viewer/ResponseViewer.tsx
3628
- var ResponseViewer = fieldcraftProLicense.requireLicense(ResponseViewerInner, "ResponseViewer");
4743
+ var ResponseViewer = requireLicense(ResponseViewerInner, "ResponseViewer");
3629
4744
 
3630
4745
  // src/theme-editor/preview-schema.ts
3631
4746
  var PREVIEW_SCHEMA = {
@@ -4026,12 +5141,12 @@ function ThemeEditorInner({
4026
5141
  }
4027
5142
 
4028
5143
  // src/theme-editor/ThemeEditor.tsx
4029
- var ThemeEditor = fieldcraftProLicense.requireLicense(ThemeEditorInner, "ThemeEditor");
5144
+ var ThemeEditor = requireLicense(ThemeEditorInner, "ThemeEditor");
4030
5145
 
4031
5146
  // src/index.ts
4032
5147
  if (typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" && globalThis.process.env?.NODE_ENV !== "production") {
4033
5148
  const _fc_banner = `
4034
- %c FieldCraft Pro %c v0.0.0
5149
+ %c FieldCraft Pro %c v1.0.1
4035
5150
 
4036
5151
  %cForm Builder \xB7 Response Viewer \xB7 Theme Editor
4037
5152
 
@@ -4049,32 +5164,9 @@ Need a license? \u2192 https://squaredr.tech/products/fieldcraft/admin-pro#prici
4049
5164
  );
4050
5165
  }
4051
5166
 
4052
- Object.defineProperty(exports, "FieldCraftProProvider", {
4053
- enumerable: true,
4054
- get: function () { return fieldcraftProLicense.FieldCraftProProvider; }
4055
- });
4056
- Object.defineProperty(exports, "UnlicensedOverlay", {
4057
- enumerable: true,
4058
- get: function () { return fieldcraftProLicense.UnlicensedOverlay; }
4059
- });
4060
- Object.defineProperty(exports, "isProductionEnvironment", {
4061
- enumerable: true,
4062
- get: function () { return fieldcraftProLicense.isProductionEnvironment; }
4063
- });
4064
- Object.defineProperty(exports, "requireLicense", {
4065
- enumerable: true,
4066
- get: function () { return fieldcraftProLicense.requireLicense; }
4067
- });
4068
- Object.defineProperty(exports, "useLicense", {
4069
- enumerable: true,
4070
- get: function () { return fieldcraftProLicense.useLicense; }
4071
- });
4072
- Object.defineProperty(exports, "validateLicense", {
4073
- enumerable: true,
4074
- get: function () { return fieldcraftProLicense.validateLicense; }
4075
- });
4076
5167
  exports.DEFAULT_PALETTE = DEFAULT_PALETTE;
4077
5168
  exports.DEFAULT_SCHEMA = DEFAULT_SCHEMA;
5169
+ exports.FieldCraftProProvider = FieldCraftProProvider;
4078
5170
  exports.FormBuilder = FormBuilder;
4079
5171
  exports.FormBuilderThemeProvider = FormBuilderThemeProvider;
4080
5172
  exports.PREVIEW_SCHEMA = PREVIEW_SCHEMA;
@@ -4082,6 +5174,7 @@ exports.QUESTION_TYPE_INFO = QUESTION_TYPE_INFO;
4082
5174
  exports.ResponseViewer = ResponseViewer;
4083
5175
  exports.ThemeEditor = ThemeEditor;
4084
5176
  exports.ThemeEditorInner = ThemeEditorInner;
5177
+ exports.UnlicensedOverlay = UnlicensedOverlay;
4085
5178
  exports.addOption = addOption;
4086
5179
  exports.addQuestion = addQuestion;
4087
5180
  exports.addSection = addSection;
@@ -4095,12 +5188,14 @@ exports.generateId = generateId;
4095
5188
  exports.generateOptionId = generateOptionId;
4096
5189
  exports.generateQuestionId = generateQuestionId;
4097
5190
  exports.generateSectionId = generateSectionId;
5191
+ exports.isProductionEnvironment = isProductionEnvironment;
4098
5192
  exports.moveOption = moveOption;
4099
5193
  exports.moveQuestion = moveQuestion;
4100
5194
  exports.moveSection = moveSection;
4101
5195
  exports.removeOption = removeOption;
4102
5196
  exports.removeQuestion = removeQuestion;
4103
5197
  exports.removeSection = removeSection;
5198
+ exports.requireLicense = requireLicense;
4104
5199
  exports.squaredrDarkPreset = squaredrDarkPreset;
4105
5200
  exports.updateOption = updateOption;
4106
5201
  exports.updateQuestion = updateQuestion;
@@ -4108,4 +5203,6 @@ exports.updateSection = updateSection;
4108
5203
  exports.useBuilderState = useBuilderState;
4109
5204
  exports.useBuilderTheme = useBuilderTheme;
4110
5205
  exports.useDragDrop = useDragDrop;
5206
+ exports.useLicense = useLicense;
4111
5207
  exports.useUndoRedo = useUndoRedo;
5208
+ exports.validateLicense = validateLicense;