@duffcloudservices/site-forms 0.4.2 → 0.7.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,305 +1,790 @@
1
- import { reactive as X, ref as V, computed as p, defineComponent as I, createElementBlock as m, openBlock as u, normalizeClass as Z, renderSlot as P, createCommentVNode as k, createTextVNode as K, toDisplayString as F, createBlock as A, withCtx as j, createElementVNode as b, Fragment as T, renderList as z, watch as W, onBeforeUnmount as ee, onMounted as te, unref as f, resolveDynamicComponent as ie } from "vue";
2
- import re from "ajv";
3
- import ne from "ajv-formats";
4
- import Y from "js-yaml";
5
- function U(e, t) {
1
+ import { ref as E, watch as ee, computed as m, reactive as Ee, defineComponent as M, createElementBlock as f, openBlock as c, normalizeClass as Oe, renderSlot as V, createCommentVNode as T, createTextVNode as W, toDisplayString as w, createBlock as _, withCtx as j, createElementVNode as v, Fragment as K, renderList as G, onBeforeUnmount as Re, onMounted as pe, unref as g, resolveDynamicComponent as De } from "vue";
2
+ import je from "ajv";
3
+ import _e from "ajv-formats";
4
+ import $e from "js-yaml";
5
+ const ce = "dcs.sound", xe = {
6
+ enabled: !1,
7
+ userSet: !1,
8
+ prmOverride: !1,
9
+ volume: 0.3,
10
+ categories: {
11
+ interaction: !0,
12
+ overlay: !0,
13
+ navigation: !0,
14
+ notification: !0,
15
+ hero: !1
16
+ },
17
+ hintShown: !1
18
+ };
19
+ function Be(e) {
20
+ const t = typeof e == "number" && Number.isFinite(e) ? e : xe.volume;
21
+ return Math.min(0.8, Math.max(0, t));
22
+ }
23
+ function me(e) {
24
+ const t = xe, n = e?.categories ?? {};
25
+ return {
26
+ enabled: typeof e?.enabled == "boolean" ? e.enabled : t.enabled,
27
+ userSet: typeof e?.userSet == "boolean" ? e.userSet : t.userSet,
28
+ prmOverride: typeof e?.prmOverride == "boolean" ? e.prmOverride : t.prmOverride,
29
+ volume: Be(e?.volume),
30
+ categories: {
31
+ interaction: n.interaction ?? t.categories.interaction,
32
+ overlay: n.overlay ?? t.categories.overlay,
33
+ navigation: n.navigation ?? t.categories.navigation,
34
+ notification: n.notification ?? t.categories.notification,
35
+ hero: n.hero ?? t.categories.hero
36
+ },
37
+ hintShown: typeof e?.hintShown == "boolean" ? e.hintShown : t.hintShown
38
+ };
39
+ }
40
+ function Ne() {
41
+ if (typeof window > "u" || !window.localStorage)
42
+ return null;
43
+ try {
44
+ const e = window.localStorage.getItem(ce);
45
+ return e ? me(JSON.parse(e)) : null;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ const ke = Ne(), I = E(ke ?? me(null));
51
+ let Pe = ke !== null;
52
+ function ze(e) {
53
+ if (!(typeof window > "u" || !window.localStorage))
54
+ try {
55
+ window.localStorage.setItem(ce, JSON.stringify(e)), Pe = !0;
56
+ } catch {
57
+ }
58
+ }
59
+ ee(I, (e) => ze(e), { deep: !0 });
60
+ typeof window < "u" && window.addEventListener && window.addEventListener("storage", (e) => {
61
+ if (e.key === ce && e.newValue)
62
+ try {
63
+ I.value = me(JSON.parse(e.newValue)), Pe = !0;
64
+ } catch {
65
+ }
66
+ });
67
+ const te = 0.02, qe = 1e-4;
68
+ function k(e, t, n, i) {
69
+ const { freq: o, type: a = "sine", dur: d = 0.15, gain: s = 0.2, attack: l = 0.01, glideTo: u } = n, r = e.createOscillator(), b = e.createGain();
70
+ r.type = a, r.frequency.setValueAtTime(o, i), u && r.frequency.exponentialRampToValueAtTime(u, i + d), b.gain.setValueAtTime(qe, i), b.gain.exponentialRampToValueAtTime(s, i + l), b.gain.exponentialRampToValueAtTime(1e-3, i + d), r.connect(b).connect(t), r.start(i), r.stop(i + d + te), r.onended = () => {
71
+ try {
72
+ r.disconnect(), b.disconnect();
73
+ } catch {
74
+ }
75
+ };
76
+ }
77
+ function Ke(e, t, n, i) {
78
+ const { freq: o, ratio: a = 2, index: d = 120, dur: s = 0.4, gain: l = 0.2 } = n, u = e.createOscillator(), r = e.createOscillator(), b = e.createGain(), F = e.createGain();
79
+ u.type = "sine", r.type = "sine", u.frequency.setValueAtTime(o, i), r.frequency.setValueAtTime(o * a, i), b.gain.setValueAtTime(d, i), b.gain.exponentialRampToValueAtTime(1, i + s), F.gain.setValueAtTime(qe, i), F.gain.exponentialRampToValueAtTime(l, i + 0.012), F.gain.exponentialRampToValueAtTime(1e-3, i + s), r.connect(b).connect(u.frequency), u.connect(F).connect(t), r.start(i), u.start(i), r.stop(i + s + te), u.stop(i + s + te), u.onended = () => {
80
+ try {
81
+ u.disconnect(), r.disconnect(), b.disconnect(), F.disconnect();
82
+ } catch {
83
+ }
84
+ };
85
+ }
86
+ function Q(e, t, n, i) {
87
+ const { dur: o = 0.08, gain: a = 0.2, cutoff: d = 2e3, q: s = 1, sweepTo: l } = n, u = Math.max(1, Math.floor(e.sampleRate * o)), r = e.createBuffer(1, u, e.sampleRate), b = r.getChannelData(0);
88
+ for (let P = 0; P < u; P++)
89
+ b[P] = Math.random() * 2 - 1;
90
+ const F = e.createBufferSource();
91
+ F.buffer = r;
92
+ const L = e.createBiquadFilter();
93
+ L.type = "bandpass", L.frequency.setValueAtTime(d, i), L.Q.value = Math.min(s, 10), l && L.frequency.exponentialRampToValueAtTime(l, i + o);
94
+ const S = e.createGain();
95
+ S.gain.setValueAtTime(a, i), S.gain.exponentialRampToValueAtTime(1e-3, i + o), F.connect(L).connect(S).connect(t), F.start(i), F.stop(i + o + te), F.onended = () => {
96
+ try {
97
+ F.disconnect(), L.disconnect(), S.disconnect();
98
+ } catch {
99
+ }
100
+ };
101
+ }
102
+ const U = 523.25, he = 587.33, z = 659.25, X = 783.99, Ue = 1046.5, He = 392, oe = 440, We = 293.66, Ie = {
103
+ // ── interaction ────────────────────────────────────────────────────────
104
+ "interaction.tap": {
105
+ category: "interaction",
106
+ gain: 0.25,
107
+ duration: 0.06,
108
+ render: (e, t, n, i) => Q(e, t, { dur: 0.05, gain: i, cutoff: 1800, q: 1 }, n)
109
+ },
110
+ "interaction.subtle": {
111
+ category: "interaction",
112
+ gain: 0.12,
113
+ duration: 0.04,
114
+ render: (e, t, n, i) => Q(e, t, { dur: 0.035, gain: i, cutoff: 2400, q: 0.7 }, n)
115
+ },
116
+ "interaction.toggle": {
117
+ category: "interaction",
118
+ gain: 0.2,
119
+ duration: 0.07,
120
+ render: (e, t, n, i) => k(e, t, { freq: z, type: "triangle", dur: 0.07, gain: i, attack: 5e-3 }, n)
121
+ },
122
+ "interaction.confirm": {
123
+ category: "interaction",
124
+ gain: 0.3,
125
+ duration: 0.09,
126
+ render: (e, t, n, i) => k(e, t, { freq: U, type: "sine", dur: 0.09, gain: i, attack: 8e-3 }, n)
127
+ },
128
+ // ── overlay ────────────────────────────────────────────────────────────
129
+ "overlay.open": {
130
+ category: "overlay",
131
+ gain: 0.18,
132
+ duration: 0.18,
133
+ render: (e, t, n, i) => Q(e, t, { dur: 0.18, gain: i, cutoff: 700, q: 0.8, sweepTo: 2200 }, n)
134
+ },
135
+ "overlay.close": {
136
+ category: "overlay",
137
+ gain: 0.18,
138
+ duration: 0.18,
139
+ render: (e, t, n, i) => Q(e, t, { dur: 0.18, gain: i, cutoff: 2200, q: 0.8, sweepTo: 600 }, n)
140
+ },
141
+ "overlay.expand": {
142
+ category: "overlay",
143
+ gain: 0.16,
144
+ duration: 0.16,
145
+ render: (e, t, n, i) => k(e, t, { freq: U, type: "sine", dur: 0.16, gain: i, glideTo: z }, n)
146
+ },
147
+ "overlay.collapse": {
148
+ category: "overlay",
149
+ gain: 0.16,
150
+ duration: 0.16,
151
+ render: (e, t, n, i) => k(e, t, { freq: z, type: "sine", dur: 0.16, gain: i, glideTo: U }, n)
152
+ },
153
+ // ── navigation ─────────────────────────────────────────────────────────
154
+ "navigation.forward": {
155
+ category: "navigation",
156
+ gain: 0.2,
157
+ duration: 0.14,
158
+ render: (e, t, n, i) => k(e, t, { freq: he, type: "sine", dur: 0.13, gain: i, glideTo: X }, n)
159
+ },
160
+ "navigation.backward": {
161
+ category: "navigation",
162
+ gain: 0.2,
163
+ duration: 0.14,
164
+ render: (e, t, n, i) => k(e, t, { freq: X, type: "sine", dur: 0.13, gain: i, glideTo: he }, n)
165
+ },
166
+ "navigation.tab": {
167
+ category: "navigation",
168
+ gain: 0.18,
169
+ duration: 0.1,
170
+ render: (e, t, n, i) => k(e, t, { freq: oe, type: "triangle", dur: 0.09, gain: i, attack: 5e-3 }, n)
171
+ },
172
+ // ── notification ─────────────────────────────────────────────────────────
173
+ "notification.info": {
174
+ category: "notification",
175
+ gain: 0.15,
176
+ duration: 0.4,
177
+ render: (e, t, n, i) => Ke(e, t, { freq: z, ratio: 2, index: 90, dur: 0.42, gain: i }, n)
178
+ },
179
+ "notification.success": {
180
+ // Ascending major third (C5 → E5) — unmistakably "up" vs the error contour.
181
+ category: "notification",
182
+ gain: 0.2,
183
+ duration: 0.34,
184
+ render: (e, t, n, i) => {
185
+ k(e, t, { freq: U, type: "sine", dur: 0.14, gain: i, attack: 8e-3 }, n), k(e, t, { freq: z, type: "sine", dur: 0.2, gain: i, attack: 8e-3 }, n + 0.11);
186
+ }
187
+ },
188
+ "notification.warning": {
189
+ // Double pulse on one note — attention without alarm.
190
+ category: "notification",
191
+ gain: 0.24,
192
+ duration: 0.3,
193
+ render: (e, t, n, i) => {
194
+ k(e, t, { freq: oe, type: "triangle", dur: 0.1, gain: i, attack: 6e-3 }, n), k(e, t, { freq: oe, type: "triangle", dur: 0.1, gain: i, attack: 6e-3 }, n + 0.16);
195
+ }
196
+ },
197
+ "notification.error": {
198
+ // Descending, darker (G4 → D4, triangle) — never a harsh klaxon.
199
+ category: "notification",
200
+ gain: 0.28,
201
+ duration: 0.42,
202
+ render: (e, t, n, i) => {
203
+ k(e, t, { freq: He, type: "triangle", dur: 0.16, gain: i, attack: 8e-3 }, n), k(e, t, { freq: We, type: "triangle", dur: 0.26, gain: i, attack: 8e-3 }, n + 0.13);
204
+ }
205
+ },
206
+ // ── hero (disabled by default even when sound is on) ──────────────────────
207
+ "hero.complete": {
208
+ category: "hero",
209
+ gain: 0.4,
210
+ duration: 0.9,
211
+ render: (e, t, n, i) => {
212
+ k(e, t, { freq: U, type: "sine", dur: 0.2, gain: i, attack: 0.01 }, n), k(e, t, { freq: z, type: "sine", dur: 0.2, gain: i, attack: 0.01 }, n + 0.16), k(e, t, { freq: X, type: "sine", dur: 0.4, gain: i, attack: 0.01 }, n + 0.32);
213
+ }
214
+ },
215
+ "hero.milestone": {
216
+ category: "hero",
217
+ gain: 0.5,
218
+ duration: 1.3,
219
+ render: (e, t, n, i) => {
220
+ k(e, t, { freq: U, type: "sine", dur: 0.22, gain: i, attack: 0.01 }, n), k(e, t, { freq: z, type: "sine", dur: 0.22, gain: i, attack: 0.01 }, n + 0.18), k(e, t, { freq: X, type: "sine", dur: 0.22, gain: i, attack: 0.01 }, n + 0.36), k(e, t, { freq: Ue, type: "sine", dur: 0.55, gain: i, attack: 0.01 }, n + 0.54);
221
+ }
222
+ }
223
+ }, Ge = 120;
224
+ let B = null, H = null, ye = !1, Le = !1;
225
+ const Te = /* @__PURE__ */ new Map(), se = /* @__PURE__ */ new Set();
226
+ function Je() {
227
+ if (typeof window > "u")
228
+ return !0;
229
+ try {
230
+ if (window.frameElement !== null)
231
+ return !0;
232
+ } catch {
233
+ return !0;
234
+ }
235
+ try {
236
+ return window.self !== window.top;
237
+ } catch {
238
+ return !0;
239
+ }
240
+ }
241
+ function Ce() {
242
+ return typeof window > "u" || !window.matchMedia ? !1 : window.matchMedia("(prefers-reduced-motion: reduce)").matches;
243
+ }
244
+ function Ae() {
245
+ if (typeof window > "u")
246
+ return null;
247
+ if (B)
248
+ return B;
249
+ const e = window.AudioContext ?? window.webkitAudioContext;
250
+ if (!e)
251
+ return null;
252
+ try {
253
+ B = new e(), H = B.createGain(), H.gain.value = 1, H.connect(B.destination);
254
+ } catch {
255
+ B = null, H = null;
256
+ }
257
+ return B;
258
+ }
259
+ function Ye() {
260
+ if (ye || typeof window > "u")
261
+ return;
262
+ ye = !0;
263
+ const e = ["pointerdown", "keydown", "touchstart"], t = () => {
264
+ Le = !0;
265
+ const n = Ae();
266
+ n && n.state === "suspended" && n.resume().catch(() => {
267
+ }), n && n.state !== "suspended" && e.forEach((i) => window.removeEventListener(i, t));
268
+ };
269
+ e.forEach((n) => window.addEventListener(n, t, { passive: !0 }));
270
+ }
271
+ function Qe(e) {
272
+ return se.add(e), () => se.delete(e);
273
+ }
274
+ function Xe(e, t) {
275
+ if (typeof document > "u")
276
+ return !1;
277
+ const n = Ie[e];
278
+ if (!n || Je())
279
+ return !1;
280
+ const i = I.value;
281
+ if (!i.enabled || Ce() && !i.prmOverride)
282
+ return !1;
283
+ const o = n.category;
284
+ if (!i.categories[o] || document.hidden && o !== "notification")
285
+ return !1;
286
+ if (!t?.force) {
287
+ const a = Te.get(e) ?? 0;
288
+ if (performance.now() - a < Ge)
289
+ return !1;
290
+ }
291
+ return !0;
292
+ }
293
+ function Ze(e, t) {
294
+ if (!Xe(e, t) || !B && !Le)
295
+ return !1;
296
+ const n = Ae();
297
+ if (!n || !H)
298
+ return !1;
299
+ if (n.state === "suspended")
300
+ return n.resume().catch(() => {
301
+ }), !1;
302
+ const i = Ie[e];
303
+ Te.set(e, performance.now());
304
+ const o = n.currentTime, a = Math.min(0.8, i.gain * I.value.volume);
305
+ try {
306
+ i.render(n, H, o, a);
307
+ } catch {
308
+ return !1;
309
+ }
310
+ return se.forEach((d) => {
311
+ try {
312
+ d();
313
+ } catch {
314
+ }
315
+ }), !0;
316
+ }
317
+ const le = E(!1);
318
+ if (typeof window < "u" && typeof window.matchMedia == "function")
319
+ try {
320
+ const e = window.matchMedia("(prefers-reduced-motion: reduce)");
321
+ le.value = e.matches, e.addEventListener?.("change", (t) => {
322
+ le.value = t.matches;
323
+ });
324
+ } catch {
325
+ }
326
+ function ae(e) {
327
+ I.value.enabled = e, I.value.userSet = !0, e ? Ce() && (I.value.prmOverride = !0) : I.value.prmOverride = !1;
328
+ }
329
+ function et() {
330
+ Ye();
331
+ const e = m({
332
+ get: () => I.value.enabled,
333
+ set: (o) => ae(o)
334
+ }), t = m({
335
+ get: () => I.value.volume,
336
+ set: (o) => {
337
+ I.value.volume = Math.min(0.8, Math.max(0, o));
338
+ }
339
+ }), n = m(() => I.value.hintShown), i = m(
340
+ () => le.value && !I.value.prmOverride
341
+ );
342
+ return {
343
+ play: Ze,
344
+ enabled: e,
345
+ volume: t,
346
+ hintShown: n,
347
+ mutedByReducedMotion: i,
348
+ toggleMute: () => ae(!I.value.enabled),
349
+ setEnabled: (o) => ae(o),
350
+ setVolume: (o) => {
351
+ I.value.volume = Math.min(0.8, Math.max(0, o));
352
+ },
353
+ isCategoryEnabled: (o) => I.value.categories[o],
354
+ setCategory: (o, a) => {
355
+ I.value.categories[o] = a, I.value.userSet = !0;
356
+ },
357
+ markHintShown: () => {
358
+ I.value.hintShown = !0;
359
+ },
360
+ onDidPlay: Qe
361
+ };
362
+ }
363
+ function de(e, t) {
6
364
  return e.visibleIf ? t[e.visibleIf.fieldId] === e.visibleIf.equals : !0;
7
365
  }
8
- function oe(e, t, n) {
366
+ function tt(e, t, n) {
9
367
  if (e.type === "section-heading" || e.type === "html-block" || e.type === "hidden")
10
368
  return;
11
- const i = typeof t == "string" ? t.trim() : t, s = t == null || i === "" || Array.isArray(t) && t.length === 0;
12
- if (e.required && s)
369
+ const i = typeof t == "string" ? t.trim() : t, o = t == null || i === "" || Array.isArray(t) && t.length === 0;
370
+ if (e.required && o)
13
371
  return `${e.label} is required`;
14
- if (s) return;
372
+ if (o) return;
15
373
  if (e.type === "email" && typeof i == "string" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(i))
16
374
  return `${e.label} must be a valid email address`;
17
- const o = e.validation ?? {};
375
+ const a = e.validation ?? {};
18
376
  if (typeof i == "string") {
19
- if (o.minLength != null && i.length < o.minLength)
20
- return `${e.label} must be at least ${o.minLength} characters`;
21
- if (o.maxLength != null && i.length > o.maxLength)
22
- return `${e.label} must be at most ${o.maxLength} characters`;
23
- if (o.regex)
377
+ if (a.minLength != null && i.length < a.minLength)
378
+ return `${e.label} must be at least ${a.minLength} characters`;
379
+ if (a.maxLength != null && i.length > a.maxLength)
380
+ return `${e.label} must be at most ${a.maxLength} characters`;
381
+ if (a.regex)
24
382
  try {
25
- if (!new RegExp(o.regex).test(i))
383
+ if (!new RegExp(a.regex).test(i))
26
384
  return `${e.label} is invalid`;
27
385
  } catch {
28
386
  }
29
387
  }
30
388
  if (typeof t == "number") {
31
- if (o.min != null && t < o.min)
32
- return `${e.label} must be at least ${o.min}`;
33
- if (o.max != null && t > o.max)
34
- return `${e.label} must be at most ${o.max}`;
389
+ if (a.min != null && t < a.min)
390
+ return `${e.label} must be at least ${a.min}`;
391
+ if (a.max != null && t > a.max)
392
+ return `${e.label} must be at most ${a.max}`;
35
393
  }
36
394
  if (e.type === "file") {
37
- const c = le(t), a = n?.maxFiles, l = n?.maxFileSizeBytes, h = o.accept && o.accept.length > 0 ? o.accept : n?.accept;
38
- if (a != null && c.length > a)
39
- return `${e.label} accepts up to ${a} file${a === 1 ? "" : "s"}`;
395
+ const d = rt(t), s = n?.maxFiles, l = n?.maxFileSizeBytes, u = a.accept && a.accept.length > 0 ? a.accept : n?.accept;
396
+ if (s != null && d.length > s)
397
+ return `${e.label} accepts up to ${s} file${s === 1 ? "" : "s"}`;
40
398
  if (l != null) {
41
- const r = c.find((v) => v.size > l);
399
+ const r = d.find((b) => b.size > l);
42
400
  if (r)
43
- return `${r.name} exceeds the ${ue(l)} limit`;
401
+ return `${r.name} exceeds the ${at(l)} limit`;
44
402
  }
45
- if (h && h.length > 0) {
46
- const r = c.find((v) => !de(v, h));
403
+ if (u && u.length > 0) {
404
+ const r = d.find((b) => !ot(b, u));
47
405
  if (r)
48
- return `${r.name} must be one of: ${h.join(", ")}`;
406
+ return `${r.name} must be one of: ${u.join(", ")}`;
49
407
  }
50
408
  }
51
409
  }
52
- function se(e, t, n) {
53
- const i = {}, s = n ? new Set(n) : null;
54
- for (const o of e.fields) {
55
- if (s && !s.has(o.id) || !U(o, t)) continue;
56
- const c = oe(o, t[o.id], e.attachmentPolicy);
57
- c && (i[o.id] = c);
410
+ function it(e, t, n) {
411
+ const i = {}, o = n ? new Set(n) : null;
412
+ for (const a of e.fields) {
413
+ if (o && !o.has(a.id) || !de(a, t)) continue;
414
+ const d = tt(a, t[a.id], e.attachmentPolicy);
415
+ d && (i[a.id] = d);
58
416
  }
59
417
  return i;
60
418
  }
61
- function ae(e) {
419
+ function nt(e) {
62
420
  return Object.values(e).some((t) => !!t);
63
421
  }
64
- function le(e) {
422
+ function rt(e) {
65
423
  return typeof File > "u" ? [] : e instanceof File ? [e] : Array.isArray(e) ? e.filter((t) => t instanceof File) : [];
66
424
  }
67
- function de(e, t) {
425
+ function ot(e, t) {
68
426
  const n = e.name.toLowerCase(), i = e.type.toLowerCase();
69
- return t.some((s) => {
70
- const o = s.trim().toLowerCase();
71
- return o === "" ? !1 : o.startsWith(".") ? n.endsWith(o) : o.endsWith("/*") ? i.startsWith(o.slice(0, -1)) : i === o;
427
+ return t.some((o) => {
428
+ const a = o.trim().toLowerCase();
429
+ return a === "" ? !1 : a.startsWith(".") ? n.endsWith(a) : a.endsWith("/*") ? i.startsWith(a.slice(0, -1)) : i === a;
72
430
  });
73
431
  }
74
- function ue(e) {
432
+ function at(e) {
75
433
  return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${Math.round(e / 1024)} KB` : `${Math.round(e / (1024 * 1024))} MB`;
76
434
  }
77
- function H(e) {
435
+ function ge(e) {
78
436
  const t = {};
79
437
  for (const n of e.fields)
80
438
  n.defaultValue !== void 0 ? t[n.id] = n.defaultValue : n.type === "checkbox" ? t[n.id] = !1 : n.type === "multiselect" || n.type === "checkbox-group" ? t[n.id] = [] : t[n.id] = "";
81
439
  return t;
82
440
  }
83
- function ce(e) {
84
- const { definition: t } = e, n = X(H(t)), i = V({}), s = V({}), o = V(!1), c = V(!1), a = V(null), l = V(0), h = p(() => t.fields), r = p(
441
+ function st(e) {
442
+ const { definition: t } = e, n = Ee(ge(t)), i = E({}), o = E({}), a = E(!1), d = E(!1), s = E(null), l = E(0), u = m(() => t.fields), r = m(
85
443
  () => t.steps && t.steps.length > 0 ? t.steps : null
86
- ), v = p(() => {
444
+ ), b = m(() => {
87
445
  const y = r.value;
88
446
  return y ? y[l.value] ?? null : null;
89
- }), $ = p(() => {
90
- const y = v.value;
447
+ }), F = m(() => {
448
+ const y = b.value;
91
449
  if (!y) return t.fields;
92
- const S = new Set(y.fieldIds);
93
- return t.fields.filter((_) => S.has(_.id));
94
- }), q = p(() => l.value === 0), M = p(() => {
450
+ const x = new Set(y.fieldIds);
451
+ return t.fields.filter((Y) => x.has(Y.id));
452
+ }), L = m(() => l.value === 0), S = m(() => {
95
453
  const y = r.value;
96
454
  return !y || l.value >= y.length - 1;
97
- }), O = p(
98
- () => $.value.filter((y) => U(y, n))
455
+ }), P = m(
456
+ () => F.value.filter((y) => de(y, n))
99
457
  );
100
- function R(y, S) {
101
- n[y] = S, i.value[y] && (i.value = { ...i.value, [y]: void 0 });
458
+ function O(y, x) {
459
+ n[y] = x, i.value[y] && (i.value = { ...i.value, [y]: void 0 });
102
460
  }
103
- function w(y) {
104
- s.value = { ...s.value, [y]: !0 };
461
+ function R(y) {
462
+ o.value = { ...o.value, [y]: !0 };
105
463
  }
106
- function L(y) {
107
- const S = se(t, n, y);
464
+ function D(y) {
465
+ const x = it(t, n, y);
108
466
  if (y) {
109
- const _ = { ...i.value };
110
- for (const N of y)
111
- _[N] = S[N];
112
- i.value = _;
467
+ const Y = { ...i.value };
468
+ for (const fe of y)
469
+ Y[fe] = x[fe];
470
+ i.value = Y;
113
471
  } else
114
- i.value = S;
115
- return !ae(i.value);
472
+ i.value = x;
473
+ return !nt(i.value);
116
474
  }
117
- function d() {
118
- const y = v.value?.fieldIds;
119
- return L(y);
475
+ function p() {
476
+ const y = b.value?.fieldIds;
477
+ return D(y);
120
478
  }
121
- function g() {
122
- return L();
479
+ function $() {
480
+ return D();
123
481
  }
124
- function x() {
125
- return !r.value || !d() ? !1 : l.value < r.value.length - 1 ? (l.value++, !0) : !1;
482
+ function q() {
483
+ return !r.value || !p() ? !1 : l.value < r.value.length - 1 ? (l.value++, !0) : !1;
126
484
  }
127
- function C() {
485
+ function h() {
128
486
  l.value > 0 && l.value--;
129
487
  }
130
- function E() {
131
- const y = H(t);
132
- for (const S of Object.keys(n))
133
- delete n[S];
134
- Object.assign(n, y), i.value = {}, s.value = {}, o.value = !1, c.value = !1, a.value = null, l.value = 0;
488
+ function C() {
489
+ const y = ge(t);
490
+ for (const x of Object.keys(n))
491
+ delete n[x];
492
+ Object.assign(n, y), i.value = {}, o.value = {}, a.value = !1, d.value = !1, s.value = null, l.value = 0;
135
493
  }
136
- function Q() {
494
+ function A() {
137
495
  const y = {};
138
- for (const S of t.fields)
139
- S.type === "section-heading" || S.type === "html-block" || U(S, n) && (y[S.id] = n[S.id]);
496
+ for (const x of t.fields)
497
+ x.type === "section-heading" || x.type === "html-block" || de(x, n) && (y[x.id] = n[x.id]);
140
498
  return y;
141
499
  }
142
500
  return {
143
501
  values: n,
144
502
  errors: i,
145
- touched: s,
146
- submitting: o,
147
- submitted: c,
148
- submitError: a,
149
- fields: h,
503
+ touched: o,
504
+ submitting: a,
505
+ submitted: d,
506
+ submitError: s,
507
+ fields: u,
150
508
  steps: r,
151
509
  currentStepIndex: l,
152
- currentStep: v,
153
- currentStepFields: $,
154
- isFirstStep: q,
155
- isLastStep: M,
156
- visibleFields: O,
157
- setValue: R,
158
- touch: w,
159
- validateCurrentScope: d,
160
- validateAll: g,
161
- next: x,
162
- prev: C,
163
- reset: E,
164
- collectSubmissionValues: Q
510
+ currentStep: b,
511
+ currentStepFields: F,
512
+ isFirstStep: L,
513
+ isLastStep: S,
514
+ visibleFields: P,
515
+ setValue: O,
516
+ touch: R,
517
+ validateCurrentScope: p,
518
+ validateAll: $,
519
+ next: q,
520
+ prev: h,
521
+ reset: C,
522
+ collectSubmissionValues: A
165
523
  };
166
524
  }
167
- async function me(e) {
168
- const { apiBase: t, siteSlug: n, payload: i } = e, s = e.retries ?? 1, o = e.fetchImpl ?? fetch, c = `${t.replace(/\/$/, "")}/sites/${encodeURIComponent(
169
- n
170
- )}/forms/${encodeURIComponent(i.formId)}/submissions`, a = Object.values(i.values).some(pe);
171
- let l, h;
172
- for (let r = 0; r <= s; r++)
525
+ async function lt(e, t = Ve) {
526
+ if (typeof e.text != "function")
173
527
  try {
174
- const v = a ? { method: "POST", body: fe(i) } : {
528
+ return await e.json();
529
+ } catch {
530
+ return null;
531
+ }
532
+ const n = ft(e);
533
+ let i;
534
+ try {
535
+ i = (await e.text()).trim();
536
+ } catch (o) {
537
+ throw ht(t, e.status, o);
538
+ }
539
+ if (i === "")
540
+ throw yt(t, e.status);
541
+ if (n && !n.includes("json"))
542
+ throw be(t, e.status, n, i);
543
+ try {
544
+ return JSON.parse(i);
545
+ } catch {
546
+ throw be(t, e.status, n, i);
547
+ }
548
+ }
549
+ async function dt(e, t, n = {}) {
550
+ const i = n.label ?? Ve, o = await lt(e, i);
551
+ if (t(o)) return o;
552
+ throw gt(i, e.status, n.receiptName ?? "submission", o);
553
+ }
554
+ const ut = ["submitted", "accepted", "queued"];
555
+ function ct(e) {
556
+ return J(e) && ne(e.id) && Me(e.status, ut) && pt(e.submittedAt);
557
+ }
558
+ const mt = ["submitted", "processing", "responded"];
559
+ function Wi(e) {
560
+ return J(e) && ne(e.id) && Me(e.status, mt);
561
+ }
562
+ function Gi(e) {
563
+ return J(e) && ne(e.id);
564
+ }
565
+ function Ji(e) {
566
+ return J(e) && e.success === !0;
567
+ }
568
+ function Yi(e) {
569
+ return J(e) && e.success === !0;
570
+ }
571
+ const Ve = "[@duffcloudservices/site-forms]", ie = "We could not confirm your message was received. Please try again, or contact us directly.";
572
+ function ft(e) {
573
+ return typeof e.headers?.get != "function" ? "" : (e.headers.get("content-type") ?? "").toLowerCase();
574
+ }
575
+ function J(e) {
576
+ return typeof e == "object" && e !== null && !Array.isArray(e);
577
+ }
578
+ function ne(e) {
579
+ return typeof e == "string" && e.trim().length > 0;
580
+ }
581
+ function Me(e, t) {
582
+ return typeof e == "string" && t.includes(e);
583
+ }
584
+ function pt(e) {
585
+ return ne(e) && Number.isFinite(Date.parse(e));
586
+ }
587
+ function re(e, t) {
588
+ const n = new Error(e);
589
+ return n.receiptRejected = t, n;
590
+ }
591
+ function ht(e, t, n) {
592
+ const i = n instanceof Error ? n.message : String(n);
593
+ return console.error(
594
+ `${e} Submission could not be confirmed: the server answered ${t} but the response body could not be read (${i}). The submission may or may not have been stored, so it is reported as a failure rather than risk telling the visitor it was sent.`
595
+ ), re(ie, !1);
596
+ }
597
+ function yt(e, t) {
598
+ return console.error(
599
+ `${e} Submission NOT confirmed: the server answered ${t} with an empty body. EVERY DCS submission endpoint returns a JSON receipt — including on 204, which is why the old 204 exemption was removed (C-310) — so an empty body means the request was answered by something other than the DCS API (a misrouted POST).`
600
+ ), re(ie, !1);
601
+ }
602
+ function be(e, t, n, i) {
603
+ const o = n.includes("html") || /^\s*<(?:!doctype|html)/i.test(i);
604
+ return console.error(
605
+ `${e} Submission NOT stored: the server answered ${t} with a ${n || "bodyless/unknown"} body instead of JSON` + (o ? " — this is an HTML page, so the POST did not reach the DCS API (check the resolved api-base and the host routing for /api/v1/*)." : ".") + ` First 200 bytes: ${i.slice(0, 200)}`
606
+ ), re(ie, !1);
607
+ }
608
+ function gt(e, t, n, i) {
609
+ return console.error(
610
+ `${e} Submission NOT confirmed: the server answered ${t} with JSON that is not a valid ${n} receipt. A 2xx and parseable JSON prove only that SOMETHING answered; only the endpoint's receipt proves a row was stored. Received: ${bt(i).slice(0, 200)}`
611
+ ), re(ie, !0);
612
+ }
613
+ function bt(e) {
614
+ try {
615
+ return JSON.stringify(e) ?? String(e);
616
+ } catch {
617
+ return String(e);
618
+ }
619
+ }
620
+ async function vt(e) {
621
+ const { apiBase: t, siteSlug: n, payload: i } = e, o = e.retries ?? 1, a = e.fetchImpl ?? fetch, d = t.replace(/\/$/, ""), s = `forms/${encodeURIComponent(i.formId)}/submissions`, l = n.trim(), u = l ? `${d}/sites/${encodeURIComponent(l)}/${s}` : `${d}/${s}`, r = Object.values(i.values).some(Ft);
622
+ let b, F;
623
+ for (let L = 0; L <= o; L++)
624
+ try {
625
+ const S = r ? { method: "POST", redirect: ve, body: wt(i) } : {
175
626
  method: "POST",
627
+ redirect: ve,
176
628
  headers: { "Content-Type": "application/json" },
177
629
  body: JSON.stringify(i)
178
- }, $ = await o(c, v);
179
- if (h = $.status, $.ok) {
180
- const q = await he($);
181
- return { payload: i, response: q };
630
+ }, P = await a(u, S);
631
+ if (F = P.status, P.ok) {
632
+ let O;
633
+ try {
634
+ O = await dt(P, ct, {
635
+ receiptName: "managed-form submission"
636
+ });
637
+ } catch (R) {
638
+ const D = !!R?.receiptRejected;
639
+ throw {
640
+ payload: i,
641
+ status: P.status,
642
+ error: R,
643
+ // `nonJsonResponse` keeps its original literal meaning (the body was
644
+ // not JSON at all); a JSON body that failed the receipt predicate is
645
+ // reported through the additive `unconfirmedReceipt` flag instead.
646
+ nonJsonResponse: !D,
647
+ unconfirmedReceipt: D
648
+ };
649
+ }
650
+ return { payload: i, response: O };
182
651
  }
183
- if ($.status < 500) {
184
- const q = await ye($);
652
+ if (P.status < 500) {
653
+ const O = await St(P);
185
654
  throw {
186
655
  payload: i,
187
- status: $.status,
188
- error: new Error(`Submission failed (${$.status}): ${q}`)
656
+ status: P.status,
657
+ error: new Error(`Submission failed (${P.status}): ${O}`)
189
658
  };
190
659
  }
191
- l = new Error(`Server error ${$.status}`);
192
- } catch (v) {
193
- if (v && typeof v == "object" && "payload" in v && "error" in v)
194
- throw v;
195
- l = v;
660
+ b = new Error(`Server error ${P.status}`);
661
+ } catch (S) {
662
+ if (S && typeof S == "object" && "payload" in S && "error" in S)
663
+ throw S;
664
+ b = S;
196
665
  }
197
666
  throw {
198
667
  payload: i,
199
- status: h,
200
- error: l ?? new Error("Submission failed")
668
+ status: F,
669
+ error: b ?? new Error("Submission failed")
201
670
  };
202
671
  }
203
- function fe(e) {
672
+ function wt(e) {
204
673
  const t = new FormData();
205
674
  t.append("formId", e.formId), e.captchaToken && t.append("captchaToken", e.captchaToken);
206
675
  for (const [n, i] of Object.entries(e.values))
207
676
  if (i != null)
208
677
  if (typeof File < "u" && i instanceof File)
209
678
  t.append(`values[${n}]`, i);
210
- else if (Array.isArray(i) && i.some((s) => typeof File < "u" && s instanceof File))
211
- for (const s of i)
212
- typeof File < "u" && s instanceof File && t.append(`values[${n}][]`, s);
679
+ else if (Array.isArray(i) && i.some((o) => typeof File < "u" && o instanceof File))
680
+ for (const o of i)
681
+ typeof File < "u" && o instanceof File && t.append(`values[${n}][]`, o);
213
682
  else if (Array.isArray(i))
214
- for (const s of i) t.append(`values[${n}][]`, String(s));
683
+ for (const o of i) t.append(`values[${n}][]`, String(o));
215
684
  else
216
685
  t.append(`values[${n}]`, String(i));
217
686
  return t;
218
687
  }
219
- function pe(e) {
688
+ function Ft(e) {
220
689
  return typeof File > "u" ? !1 : e instanceof File ? !0 : Array.isArray(e) && e.some((t) => t instanceof File);
221
690
  }
222
- async function he(e) {
223
- try {
224
- return await e.json();
225
- } catch {
226
- return null;
227
- }
228
- }
229
- async function ye(e) {
691
+ const ve = "error";
692
+ async function St(e) {
230
693
  try {
231
694
  return await e.text();
232
695
  } catch {
233
696
  return "";
234
697
  }
235
698
  }
236
- const be = "http://json-schema.org/draft-07/schema#", ge = "https://duffcloudservices.com/schemas/form-definition.schema.json", ve = "PortalFormDefinition", Fe = "object", xe = "Portable form definition. This schema is the source of truth for\nboth the portal CRUD APIs and the `.dcs/forms/<formId>.yaml`\nfiles consumed by customer-site builds via `<DcsForm/>`.\n", $e = ["formId", "submission", "fields"], we = { formId: { type: "string", description: "Kebab-case identifier, unique per site.", pattern: "^[a-z0-9][a-z0-9-]*$", minLength: 1, maxLength: 80, example: "contact" }, formKind: { $ref: "#/definitions/PortalFormKind" }, submitLabel: { type: "string", description: 'Label for the submit button. Defaults to "Send" client-side.', maxLength: 80, example: "Send message" }, successMessage: { type: "string", description: "Confirmation message shown after a successful submission.", maxLength: 2e3, example: "Thanks — we'll be in touch shortly." }, submission: { $ref: "#/definitions/PortalFormSubmissionConfig" }, attachmentPolicy: { $ref: "#/definitions/PortalFormAttachmentPolicy" }, steps: { type: "array", description: "Optional multi-step grouping. When omitted, fields render as a single page.", items: { $ref: "#/definitions/PortalFormStep" } }, fields: { type: "array", description: "Flat list of all fields in the form. When `steps` is present,\nevery field referenced from a step's `fieldIds` must exist here.\n", items: { $ref: "#/definitions/PortalFormField" } }, version: { type: "integer", minimum: 1, description: "Monotonically increasing version, bumped on each save." }, createdAt: { type: "string", format: "date-time", description: "Form creation timestamp." }, updatedAt: { type: "string", format: "date-time", description: "Last modification timestamp." } }, ke = /* @__PURE__ */ JSON.parse('{"PortalFormKind":{"type":"string","description":"High-level form kind used by the visual page editor and submission\\npipeline. `freeform` preserves fully editable questionnaires; the\\nstandard values provide predictable field roles and routing for common\\nsite forms.\\n","enum":["freeform","contact","revenue-contractor","resume-submission"],"default":"freeform"},"PortalFormFieldRole":{"type":"string","description":"Semantic role for a field inside a standard form. Free-form forms may\\nomit roles or use `custom`; standard forms use roles so submissions can\\nbe surfaced consistently as contact messages, notifications, revenue\\ncontractor inquiries, or resume submissions without locking the editor\\nout of label/help-text changes.\\n","enum":["contact-name","contact-email","contact-phone","contact-company","subject","message","summary","image-attachments","resume","consent","custom"]},"PortalFormAttachmentPolicy":{"type":"object","description":"Attachment expectations for standard forms. For\\n`revenue-contractor`, image attachments are expected so contractors can\\ninclude project photos. For `resume-submission`, the resume field\\nshould accept document uploads. Free-form questionnaires can omit this.\\n","properties":{"expected":{"type":"boolean","default":false,"description":"Whether the standard flow should prompt for attachments."},"required":{"type":"boolean","default":false,"description":"Whether at least one attachment is required."},"maxFiles":{"type":"integer","minimum":1,"maximum":20,"description":"Maximum number of files accepted.","example":5},"maxFileSizeBytes":{"type":"integer","minimum":1,"description":"Maximum size per file in bytes.","example":10485760},"accept":{"type":"array","description":"Accepted MIME types or extensions (for example `image/*`).","items":{"type":"string"}}}},"PublicSiteFormContact":{"type":"object","description":"Normalized contact identity supplied with a public form submission.","properties":{"name":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":255},"phone":{"type":"string","maxLength":32},"company":{"type":"string","maxLength":160}}},"PublicSiteFormAttachment":{"type":"object","description":"Metadata for an attachment associated with a public form submission.","required":["fileName"],"properties":{"fileName":{"type":"string","maxLength":255},"contentType":{"type":"string","maxLength":120},"sizeBytes":{"type":"integer","minimum":0},"storageUrl":{"type":"string","format":"uri","description":"Internal or pre-uploaded storage URL when the binary was uploaded separately."}}},"PublicSiteFormSubmissionRequest":{"type":"object","description":"JSON request for public managed-form submissions.","required":["values"],"properties":{"formKind":{"$ref":"#/definitions/PortalFormKind"},"pageSlug":{"type":"string","description":"Optional visual-editor page slug where the form was rendered."},"contact":{"$ref":"#/definitions/PublicSiteFormContact"},"subject":{"type":"string","maxLength":200,"description":"Contact subject or inquiry title."},"summary":{"type":"string","maxLength":4000,"description":"Project, request, or applicant summary for standard flows."},"message":{"type":"string","maxLength":4000,"description":"Free-text message supplied by the submitter."},"values":{"type":"object","additionalProperties":true,"description":"Field id to submitted value map for the attached PortalFormDefinition."},"attachments":{"type":"array","description":"Attachment metadata for JSON submissions.","items":{"$ref":"#/definitions/PublicSiteFormAttachment"}},"source":{"type":"string","maxLength":120,"description":"Capture source such as `site-contact-page` or `visual-page-editor`."},"consent":{"type":"boolean","description":"Whether the submitter consented to follow-up."}}},"PublicSiteFormMultipartSubmissionRequest":{"type":"object","description":"Multipart request for public managed-form submissions with binary uploads.","properties":{"formKind":{"$ref":"#/definitions/PortalFormKind"},"pageSlug":{"type":"string"},"values":{"type":"string","description":"JSON-encoded field id to submitted value map."},"name":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":255},"phone":{"type":"string","maxLength":32},"company":{"type":"string","maxLength":160},"subject":{"type":"string","maxLength":200},"summary":{"type":"string","maxLength":4000},"message":{"type":"string","maxLength":4000},"attachments":{"type":"array","description":"Image or supporting files for standard form submissions.","items":{"type":"string","format":"binary"}},"resume":{"type":"string","format":"binary","description":"Resume document for `resume-submission` standard forms."},"source":{"type":"string","maxLength":120},"consent":{"type":"boolean"}}},"PublicSiteFormSubmissionResponse":{"type":"object","required":["id","status","submittedAt"],"properties":{"id":{"type":"string","description":"Unique submission identifier."},"status":{"type":"string","enum":["submitted","accepted","queued"]},"submittedAt":{"type":"string","format":"date-time"},"message":{"type":"string","description":"Friendly acknowledgement shown to the submitter."},"contactMessageId":{"type":"string","nullable":true,"description":"Future portal contact-message identifier when surfaced in the portal."},"notificationQueued":{"type":"boolean","description":"Whether a portal notification/contact-message handoff was queued."}}},"PortalFormFieldType":{"type":"string","description":"Field type controlling input rendering and validation. Includes\\nlayout-only types (`section-heading`, `html-block`) that render\\ndecorative content rather than capturing values, and `hidden`\\nfor prefilled values not displayed to the user.\\n","enum":["text","email","tel","textarea","select","multiselect","radio","checkbox","checkbox-group","date","file","hidden","section-heading","html-block"]},"PortalFormFieldWidth":{"type":"string","description":"Layout hint for the field within its row.","enum":["full","half","third"]},"PortalFormFieldOption":{"type":"object","description":"A single option for select / radio / checkbox-group fields.","required":["value","label"],"properties":{"value":{"type":"string","description":"Submitted value for this option."},"label":{"type":"string","description":"Human-readable label shown to the user."}}},"PortalFormFieldValidation":{"type":"object","description":"Optional validation rules applied to a field\'s value.","properties":{"regex":{"type":"string","description":"ECMAScript-compatible pattern the value must match."},"minLength":{"type":"integer","minimum":0,"description":"Minimum string length (text-like fields only)."},"maxLength":{"type":"integer","minimum":0,"description":"Maximum string length (text-like fields only)."},"min":{"type":"number","description":"Minimum numeric / date value."},"max":{"type":"number","description":"Maximum numeric / date value."},"accept":{"type":"array","description":"Accepted MIME types or file extensions for `file` fields.","items":{"type":"string"}}}},"PortalFormFieldVisibleIf":{"type":"object","description":"Single-predicate visibility rule. The field is shown only when\\nthe referenced sibling field\'s value equals `equals`. Future\\nrevisions may extend this with AND / OR composition; clients\\nshould treat unknown extra properties as opaque.\\n","required":["fieldId","equals"],"properties":{"fieldId":{"type":"string","description":"ID of the sibling field whose value gates visibility."},"equals":{"description":"Value (string, number, or boolean) the sibling field must equal.","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}},"PortalFormField":{"type":"object","description":"A single field within a form definition.","required":["id","type","label"],"properties":{"id":{"type":"string","description":"Kebab-case identifier, unique within the form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"type":{"$ref":"#/definitions/PortalFormFieldType"},"role":{"$ref":"#/definitions/PortalFormFieldRole"},"label":{"type":"string","description":"Human-readable label rendered above the input.","minLength":1,"maxLength":200},"helpText":{"type":"string","description":"Optional helper / description text shown beneath the label.","maxLength":500},"placeholder":{"type":"string","description":"Optional placeholder shown inside empty inputs.","maxLength":200},"defaultValue":{"description":"Optional default value (string, number, boolean, or array of strings).","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}}]},"required":{"type":"boolean","default":false,"description":"Whether the field must be supplied to submit the form."},"width":{"allOf":[{"$ref":"#/definitions/PortalFormFieldWidth"}],"default":"full"},"options":{"type":"array","description":"Options for `select`, `multiselect`, `radio`, `checkbox-group` fields.","items":{"$ref":"#/definitions/PortalFormFieldOption"}},"validation":{"$ref":"#/definitions/PortalFormFieldValidation"},"visibleIf":{"$ref":"#/definitions/PortalFormFieldVisibleIf"},"phi":{"type":"boolean","default":false,"description":"Marks the field as collecting Protected Health Information.\\nWhen true the value must never appear in notification emails\\nor logs and the owning site must be in the Medical category\\n(see `compliance.instructions.md`).\\n"},"html":{"type":"string","description":"Sanitized HTML body for `html-block` fields. Ignored for\\nother field types.\\n","maxLength":10000}}},"PortalFormStep":{"type":"object","description":"Optional grouping for multi-step (wizard-style) forms such as\\nthe KT Braun estate-planning questionnaires. When `steps` is\\nomitted on a form, all fields render as a single page.\\n","required":["id","title","fieldIds"],"properties":{"id":{"type":"string","description":"Kebab-case step identifier, unique within the form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"title":{"type":"string","description":"Step title shown in the wizard header.","minLength":1,"maxLength":200},"description":{"type":"string","description":"Optional descriptive text shown below the step title.","maxLength":1000},"fieldIds":{"type":"array","description":"Ordered list of field IDs that belong to this step.","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"}}}},"PortalFormSubmissionLeadConfig":{"type":"object","required":["kind"],"description":"Routes submissions into the existing PortalLeads pipeline.","properties":{"kind":{"type":"string","enum":["lead"]},"category":{"type":"string","description":"Service category id used to route the lead."}}},"PortalFormSubmissionEmailConfig":{"type":"object","required":["kind","to"],"description":"Routes submissions as a notification email to the listed recipients.","properties":{"kind":{"type":"string","enum":["email"]},"to":{"type":"array","minItems":1,"items":{"type":"string","format":"email"},"description":"Recipient email addresses."},"subjectTemplate":{"type":"string","description":"Optional subject-line template. Submitted values are intentionally\\nnot interpolated into the subject line for compliance reasons.\\n","maxLength":200}}},"PortalFormSubmissionWebhookConfig":{"type":"object","required":["kind","url"],"description":"Posts the submission JSON to a third-party webhook.","properties":{"kind":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","description":"HTTPS endpoint receiving the signed POST."},"signingSecretRef":{"type":"string","description":"Name of the Key Vault secret used to HMAC-sign the request.\\nThe secret value is never returned in API responses.\\n"}}},"PortalFormSubmissionConfig":{"description":"Discriminated union of submission destinations. Exactly one of\\n`lead`, `email`, or `webhook` shapes is used based on `kind`.\\n","oneOf":[{"$ref":"#/definitions/PortalFormSubmissionLeadConfig"},{"$ref":"#/definitions/PortalFormSubmissionEmailConfig"},{"$ref":"#/definitions/PortalFormSubmissionWebhookConfig"}],"discriminator":{"propertyName":"kind","mapping":{"lead":"#/definitions/PortalFormSubmissionLeadConfig","email":"#/definitions/PortalFormSubmissionEmailConfig","webhook":"#/definitions/PortalFormSubmissionWebhookConfig"}}},"PortalFormSummary":{"type":"object","description":"List-row projection of a form definition.","required":["formId","fieldCount"],"properties":{"formId":{"type":"string","description":"Kebab-case identifier.","pattern":"^[a-z0-9][a-z0-9-]*$","example":"contact"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"fieldCount":{"type":"integer","minimum":0,"description":"Number of input-bearing fields in the form.","example":4},"attachedPageSlugs":{"type":"array","description":"Slugs of pages that currently reference this form via `formId`.","items":{"type":"string"}},"submissionKind":{"type":"string","enum":["lead","email","webhook"],"description":"Submission destination kind."},"lastUpdated":{"type":"string","format":"date-time","description":"Last modification timestamp."}}},"PortalCreateFormRequest":{"type":"object","description":"Payload for creating a new form definition.","required":["formId","submission","fields"],"properties":{"formId":{"type":"string","description":"Kebab-case identifier, unique per site.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submitLabel":{"type":"string","maxLength":80},"successMessage":{"type":"string","maxLength":2000},"submission":{"$ref":"#/definitions/PortalFormSubmissionConfig"},"attachmentPolicy":{"$ref":"#/definitions/PortalFormAttachmentPolicy"},"steps":{"type":"array","items":{"$ref":"#/definitions/PortalFormStep"}},"fields":{"type":"array","items":{"$ref":"#/definitions/PortalFormField"}}}},"PortalUpdateFormRequest":{"type":"object","description":"Payload for updating an existing form definition. The `formId`\\nis taken from the URL; supplying it in the body is optional and,\\nif present, must match the path.\\n","properties":{"formId":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80,"description":"Optional echo of the path formId; must match if provided."},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submitLabel":{"type":"string","maxLength":80},"successMessage":{"type":"string","maxLength":2000},"submission":{"$ref":"#/definitions/PortalFormSubmissionConfig"},"attachmentPolicy":{"$ref":"#/definitions/PortalFormAttachmentPolicy"},"steps":{"type":"array","items":{"$ref":"#/definitions/PortalFormStep"}},"fields":{"type":"array","items":{"$ref":"#/definitions/PortalFormField"}},"expectedVersion":{"type":"integer","minimum":1,"description":"Optional optimistic-concurrency token. When supplied, the\\nupdate is rejected with 409 if the stored form\'s `version`\\nno longer matches.\\n"}}},"PortalDuplicateFormRequest":{"type":"object","description":"Payload for cloning an existing form under a new formId.","required":["formId"],"properties":{"formId":{"type":"string","description":"Target kebab-case formId for the cloned form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80}}},"PortalFormResponse":{"type":"object","description":"Full form definition response.","required":["form"],"properties":{"form":{"$ref":"#/definitions/PortalFormDefinition"},"attachedPageSlugs":{"type":"array","description":"Slugs of pages that currently reference this form via `formId`.","items":{"type":"string"}}}},"PortalFormListResponse":{"type":"object","required":["forms","totalCount"],"properties":{"forms":{"type":"array","items":{"$ref":"#/definitions/PortalFormSummary"}},"totalCount":{"type":"integer","minimum":0,"description":"Total number of forms matching the query.","example":3}}},"PortalFormSubmission":{"type":"object","description":"A single recorded submission against a managed form. PHI fields are\\nreplaced with `[REDACTED]` for Medical-category sites, in which case\\n`redactedForPhi` is true.\\n","required":["id","formId","siteSlug","submittedAt","source","values","redactedForPhi"],"properties":{"id":{"type":"string","description":"Unique submission row key (reverse-chronological)."},"formId":{"type":"string"},"siteSlug":{"type":"string"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submittedAt":{"type":"string","format":"date-time"},"source":{"type":"string","description":"Capture channel (e.g. `web`, `import`)."},"values":{"type":"object","additionalProperties":true,"description":"Field id → submitted value map. PHI values are redacted."},"attachments":{"type":"array","description":"Attachment metadata captured with the submission.","items":{"$ref":"#/definitions/PublicSiteFormAttachment"}},"leadId":{"type":"string","nullable":true,"description":"Cross-reference to the lead row created by the legacy capture flow."},"contactMessageId":{"type":"string","nullable":true,"description":"Future portal contact-message identifier when surfaced in the portal."},"redactedForPhi":{"type":"boolean","description":"True when one or more values were replaced with the PHI placeholder."},"remoteIp":{"type":"string","nullable":true},"userAgent":{"type":"string","nullable":true}}},"PortalFormSubmissionSummary":{"type":"object","description":"List-view representation of a submission.","required":["id","formId","submittedAt","redactedForPhi","summary"],"properties":{"id":{"type":"string"},"formId":{"type":"string"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submittedAt":{"type":"string","format":"date-time"},"redactedForPhi":{"type":"boolean"},"summary":{"type":"string","description":"Short single-line preview suitable for the portal list view."}}},"PortalFormSubmissionListResponse":{"type":"object","required":["submissions"],"properties":{"submissions":{"type":"array","items":{"$ref":"#/definitions/PortalFormSubmissionSummary"}}}}}'), Se = {
237
- $schema: be,
238
- $id: ge,
239
- title: ve,
240
- type: Fe,
241
- description: xe,
242
- required: $e,
243
- properties: we,
244
- definitions: ke
699
+ const ue = /* @__PURE__ */ new Map();
700
+ function Qi() {
701
+ ue.clear();
702
+ }
703
+ async function $t(e) {
704
+ const t = (e.apiBase ?? "").replace(/\/$/, ""), n = (e.siteSlug ?? "").trim(), i = n ? `${t}/sites/${encodeURIComponent(n)}/forms/compliance` : `${t}/forms/compliance`, o = `${t}|${n}`, a = ue.get(o);
705
+ if (a) return a;
706
+ const d = e.fetchImpl ?? (typeof fetch == "function" ? fetch : void 0);
707
+ if (!d) return !1;
708
+ const s = (async () => {
709
+ try {
710
+ const l = await d(i, { method: "GET" });
711
+ return l.ok ? (await l.json())?.siteHandlesPhi === !0 : !1;
712
+ } catch {
713
+ return !1;
714
+ }
715
+ })();
716
+ return ue.set(o, s), s;
717
+ }
718
+ const we = {
719
+ lead: "Please don't include personal health details.",
720
+ detail: "A general description of what you need is enough — we'll go over anything specific with you directly."
721
+ }, xt = "http://json-schema.org/draft-07/schema#", kt = "https://duffcloudservices.com/schemas/form-definition.schema.json", Pt = "PortalFormDefinition", qt = "object", It = "Portable form definition. This schema is the source of truth for\nboth the portal CRUD APIs and the `.dcs/forms/<formId>.yaml`\nfiles consumed by customer-site builds via `<DcsForm/>`.\n", Lt = ["formId", "submission", "fields"], Tt = { formId: { type: "string", description: "Kebab-case identifier, unique per site.", pattern: "^[a-z0-9][a-z0-9-]*$", minLength: 1, maxLength: 80, example: "contact" }, formKind: { $ref: "#/definitions/PortalFormKind" }, submitLabel: { type: "string", description: 'Label for the submit button. Defaults to "Send" client-side.', maxLength: 80, example: "Send message" }, successMessage: { type: "string", description: "Confirmation message shown after a successful submission.", maxLength: 2e3, example: "Thanks — we'll be in touch shortly." }, submission: { $ref: "#/definitions/PortalFormSubmissionConfig" }, attachmentPolicy: { $ref: "#/definitions/PortalFormAttachmentPolicy" }, steps: { type: "array", description: "Optional multi-step grouping. When omitted, fields render as a single page.", items: { $ref: "#/definitions/PortalFormStep" } }, fields: { type: "array", description: "Flat list of all fields in the form. When `steps` is present,\nevery field referenced from a step's `fieldIds` must exist here.\n", items: { $ref: "#/definitions/PortalFormField" } }, version: { type: "integer", minimum: 1, description: "Monotonically increasing version, bumped on each save." }, createdAt: { type: "string", format: "date-time", description: "Form creation timestamp." }, updatedAt: { type: "string", format: "date-time", description: "Last modification timestamp." } }, Ct = /* @__PURE__ */ JSON.parse('{"PortalFormKind":{"type":"string","description":"High-level form kind used by the visual page editor and submission\\npipeline. `freeform` preserves fully editable questionnaires; the\\nstandard values provide predictable field roles and routing for common\\nsite forms.\\n","enum":["freeform","contact","revenue-contractor","resume-submission"],"default":"freeform"},"PortalFormFieldRole":{"type":"string","description":"Semantic role for a field inside a standard form. Free-form forms may\\nomit roles or use `custom`; standard forms use roles so submissions can\\nbe surfaced consistently as contact messages, notifications, revenue\\ncontractor inquiries, or resume submissions without locking the editor\\nout of label/help-text changes.\\n","enum":["contact-name","contact-email","contact-phone","contact-company","subject","message","summary","image-attachments","resume","consent","custom"]},"PortalFormAttachmentPolicy":{"type":"object","description":"Attachment expectations for standard forms. For\\n`revenue-contractor`, image attachments are expected so contractors can\\ninclude project photos. For `resume-submission`, the resume field\\nshould accept document uploads. Free-form questionnaires can omit this.\\n","properties":{"expected":{"type":"boolean","default":false,"description":"Whether the standard flow should prompt for attachments."},"required":{"type":"boolean","default":false,"description":"Whether at least one attachment is required."},"maxFiles":{"type":"integer","minimum":1,"maximum":20,"description":"Maximum number of files accepted.","example":5},"maxFileSizeBytes":{"type":"integer","minimum":1,"description":"Maximum size per file in bytes.","example":10485760},"accept":{"type":"array","description":"Accepted MIME types or extensions (for example `image/*`).","items":{"type":"string"}}}},"PublicSiteFormContact":{"type":"object","description":"Normalized contact identity supplied with a public form submission.","properties":{"name":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":255},"phone":{"type":"string","maxLength":32},"company":{"type":"string","maxLength":160}}},"PublicSiteFormAttachment":{"type":"object","description":"Metadata for an attachment associated with a public form submission.","required":["fileName"],"properties":{"fileName":{"type":"string","maxLength":255},"contentType":{"type":"string","maxLength":120},"sizeBytes":{"type":"integer","minimum":0},"storageUrl":{"type":"string","format":"uri","description":"Internal or pre-uploaded storage URL when the binary was uploaded separately."}}},"PublicSiteFormSubmissionRequest":{"type":"object","description":"JSON request for public managed-form submissions.","required":["values"],"properties":{"formKind":{"$ref":"#/definitions/PortalFormKind"},"pageSlug":{"type":"string","description":"Optional visual-editor page slug where the form was rendered."},"contact":{"$ref":"#/definitions/PublicSiteFormContact"},"subject":{"type":"string","maxLength":200,"description":"Contact subject or inquiry title."},"summary":{"type":"string","maxLength":4000,"description":"Project, request, or applicant summary for standard flows."},"message":{"type":"string","maxLength":4000,"description":"Free-text message supplied by the submitter."},"values":{"type":"object","additionalProperties":true,"description":"Field id to submitted value map for the attached PortalFormDefinition."},"attachments":{"type":"array","description":"Attachment metadata for JSON submissions.","items":{"$ref":"#/definitions/PublicSiteFormAttachment"}},"source":{"type":"string","maxLength":120,"description":"Capture source such as `site-contact-page` or `visual-page-editor`."},"consent":{"type":"boolean","description":"Whether the submitter consented to follow-up."}}},"PublicSiteFormMultipartSubmissionRequest":{"type":"object","description":"Multipart request for public managed-form submissions with binary uploads.","properties":{"formKind":{"$ref":"#/definitions/PortalFormKind"},"pageSlug":{"type":"string"},"values":{"type":"string","description":"JSON-encoded field id to submitted value map."},"name":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":255},"phone":{"type":"string","maxLength":32},"company":{"type":"string","maxLength":160},"subject":{"type":"string","maxLength":200},"summary":{"type":"string","maxLength":4000},"message":{"type":"string","maxLength":4000},"attachments":{"type":"array","description":"Image or supporting files for standard form submissions.","items":{"type":"string","format":"binary"}},"resume":{"type":"string","format":"binary","description":"Resume document for `resume-submission` standard forms."},"source":{"type":"string","maxLength":120},"consent":{"type":"boolean"}}},"PublicSiteFormSubmissionResponse":{"type":"object","required":["id","status","submittedAt"],"properties":{"id":{"type":"string","description":"Unique submission identifier."},"status":{"type":"string","enum":["submitted","accepted","queued"]},"submittedAt":{"type":"string","format":"date-time"},"message":{"type":"string","description":"Friendly acknowledgement shown to the submitter."},"contactMessageId":{"type":"string","nullable":true,"description":"Future portal contact-message identifier when surfaced in the portal."},"notificationQueued":{"type":"boolean","description":"Whether a portal notification/contact-message handoff was queued."}}},"PortalFormFieldType":{"type":"string","description":"Field type controlling input rendering and validation. Includes\\nlayout-only types (`section-heading`, `html-block`) that render\\ndecorative content rather than capturing values, and `hidden`\\nfor prefilled values not displayed to the user.\\n","enum":["text","email","tel","textarea","select","multiselect","radio","checkbox","checkbox-group","date","file","hidden","section-heading","html-block"]},"PortalFormFieldWidth":{"type":"string","description":"Layout hint for the field within its row.","enum":["full","half","third"]},"PortalFormFieldOption":{"type":"object","description":"A single option for select / radio / checkbox-group fields.","required":["value","label"],"properties":{"value":{"type":"string","description":"Submitted value for this option."},"label":{"type":"string","description":"Human-readable label shown to the user."}}},"PortalFormFieldValidation":{"type":"object","description":"Optional validation rules applied to a field\'s value.","properties":{"regex":{"type":"string","description":"ECMAScript-compatible pattern the value must match."},"minLength":{"type":"integer","minimum":0,"description":"Minimum string length (text-like fields only)."},"maxLength":{"type":"integer","minimum":0,"description":"Maximum string length (text-like fields only)."},"min":{"type":"number","description":"Minimum numeric / date value."},"max":{"type":"number","description":"Maximum numeric / date value."},"accept":{"type":"array","description":"Accepted MIME types or file extensions for `file` fields.","items":{"type":"string"}}}},"PortalFormFieldVisibleIf":{"type":"object","description":"Single-predicate visibility rule. The field is shown only when\\nthe referenced sibling field\'s value equals `equals`. Future\\nrevisions may extend this with AND / OR composition; clients\\nshould treat unknown extra properties as opaque.\\n","required":["fieldId","equals"],"properties":{"fieldId":{"type":"string","description":"ID of the sibling field whose value gates visibility."},"equals":{"description":"Value (string, number, or boolean) the sibling field must equal.","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}},"PortalFormField":{"type":"object","description":"A single field within a form definition.","required":["id","type","label"],"properties":{"id":{"type":"string","description":"Kebab-case identifier, unique within the form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"type":{"$ref":"#/definitions/PortalFormFieldType"},"role":{"$ref":"#/definitions/PortalFormFieldRole"},"label":{"type":"string","description":"Human-readable label rendered above the input.","minLength":1,"maxLength":200},"helpText":{"type":"string","description":"Optional helper / description text shown beneath the label.","maxLength":500},"placeholder":{"type":"string","description":"Optional placeholder shown inside empty inputs.","maxLength":200},"defaultValue":{"description":"Optional default value (string, number, boolean, or array of strings).","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}}]},"required":{"type":"boolean","default":false,"description":"Whether the field must be supplied to submit the form."},"width":{"allOf":[{"$ref":"#/definitions/PortalFormFieldWidth"}],"default":"full"},"options":{"type":"array","description":"Options for `select`, `multiselect`, `radio`, `checkbox-group` fields.","items":{"$ref":"#/definitions/PortalFormFieldOption"}},"validation":{"$ref":"#/definitions/PortalFormFieldValidation"},"visibleIf":{"$ref":"#/definitions/PortalFormFieldVisibleIf"},"phi":{"type":"boolean","default":false,"description":"Marks the field as collecting Protected Health Information.\\nWhen true the value must never appear in notification emails\\nor logs and the owning site must be in the Medical category\\n(see `compliance.instructions.md`).\\n"},"html":{"type":"string","description":"Sanitized HTML body for `html-block` fields. Ignored for\\nother field types.\\n","maxLength":10000}}},"PortalFormStep":{"type":"object","description":"Optional grouping for multi-step (wizard-style) forms such as\\nthe KT Braun estate-planning questionnaires. When `steps` is\\nomitted on a form, all fields render as a single page.\\n","required":["id","title","fieldIds"],"properties":{"id":{"type":"string","description":"Kebab-case step identifier, unique within the form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"title":{"type":"string","description":"Step title shown in the wizard header.","minLength":1,"maxLength":200},"description":{"type":"string","description":"Optional descriptive text shown below the step title.","maxLength":1000},"fieldIds":{"type":"array","description":"Ordered list of field IDs that belong to this step.","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"}}}},"PortalFormSubmissionLeadConfig":{"type":"object","required":["kind"],"description":"Routes submissions into the existing PortalLeads pipeline.","properties":{"kind":{"type":"string","enum":["lead"]},"category":{"type":"string","description":"Service category id used to route the lead."}}},"PortalFormSubmissionEmailConfig":{"type":"object","required":["kind","to"],"description":"Routes submissions as a notification email to the listed recipients.","properties":{"kind":{"type":"string","enum":["email"]},"to":{"type":"array","minItems":1,"items":{"type":"string","format":"email"},"description":"Recipient email addresses."},"subjectTemplate":{"type":"string","description":"Optional subject-line template. Submitted values are intentionally\\nnot interpolated into the subject line for compliance reasons.\\n","maxLength":200}}},"PortalFormSubmissionWebhookConfig":{"type":"object","required":["kind","url"],"description":"Posts the submission JSON to a third-party webhook.","properties":{"kind":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","description":"HTTPS endpoint receiving the signed POST."},"signingSecretRef":{"type":"string","description":"Name of the Key Vault secret used to HMAC-sign the request.\\nThe secret value is never returned in API responses.\\n"}}},"PortalFormSubmissionConfig":{"description":"Discriminated union of submission destinations. Exactly one of\\n`lead`, `email`, or `webhook` shapes is used based on `kind`.\\n","oneOf":[{"$ref":"#/definitions/PortalFormSubmissionLeadConfig"},{"$ref":"#/definitions/PortalFormSubmissionEmailConfig"},{"$ref":"#/definitions/PortalFormSubmissionWebhookConfig"}],"discriminator":{"propertyName":"kind","mapping":{"lead":"#/definitions/PortalFormSubmissionLeadConfig","email":"#/definitions/PortalFormSubmissionEmailConfig","webhook":"#/definitions/PortalFormSubmissionWebhookConfig"}}},"PortalFormSummary":{"type":"object","description":"List-row projection of a form definition.","required":["formId","fieldCount"],"properties":{"formId":{"type":"string","description":"Kebab-case identifier.","pattern":"^[a-z0-9][a-z0-9-]*$","example":"contact"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"fieldCount":{"type":"integer","minimum":0,"description":"Number of input-bearing fields in the form.","example":4},"attachedPageSlugs":{"type":"array","description":"Slugs of pages that currently reference this form via `formId`.","items":{"type":"string"}},"submissionKind":{"type":"string","enum":["lead","email","webhook"],"description":"Submission destination kind."},"lastUpdated":{"type":"string","format":"date-time","description":"Last modification timestamp."}}},"PortalCreateFormRequest":{"type":"object","description":"Payload for creating a new form definition.","required":["formId","submission","fields"],"properties":{"formId":{"type":"string","description":"Kebab-case identifier, unique per site.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submitLabel":{"type":"string","maxLength":80},"successMessage":{"type":"string","maxLength":2000},"submission":{"$ref":"#/definitions/PortalFormSubmissionConfig"},"attachmentPolicy":{"$ref":"#/definitions/PortalFormAttachmentPolicy"},"steps":{"type":"array","items":{"$ref":"#/definitions/PortalFormStep"}},"fields":{"type":"array","items":{"$ref":"#/definitions/PortalFormField"}}}},"PortalUpdateFormRequest":{"type":"object","description":"Payload for updating an existing form definition. The `formId`\\nis taken from the URL; supplying it in the body is optional and,\\nif present, must match the path.\\n","properties":{"formId":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80,"description":"Optional echo of the path formId; must match if provided."},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submitLabel":{"type":"string","maxLength":80},"successMessage":{"type":"string","maxLength":2000},"submission":{"$ref":"#/definitions/PortalFormSubmissionConfig"},"attachmentPolicy":{"$ref":"#/definitions/PortalFormAttachmentPolicy"},"steps":{"type":"array","items":{"$ref":"#/definitions/PortalFormStep"}},"fields":{"type":"array","items":{"$ref":"#/definitions/PortalFormField"}},"expectedVersion":{"type":"integer","minimum":1,"description":"Optional optimistic-concurrency token. When supplied, the\\nupdate is rejected with 409 if the stored form\'s `version`\\nno longer matches.\\n"}}},"PortalDuplicateFormRequest":{"type":"object","description":"Payload for cloning an existing form under a new formId.","required":["formId"],"properties":{"formId":{"type":"string","description":"Target kebab-case formId for the cloned form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80}}},"PortalFormResponse":{"type":"object","description":"Full form definition response.","required":["form"],"properties":{"form":{"$ref":"#/definitions/PortalFormDefinition"},"attachedPageSlugs":{"type":"array","description":"Slugs of pages that currently reference this form via `formId`.","items":{"type":"string"}}}},"PortalFormListResponse":{"type":"object","required":["forms","totalCount"],"properties":{"forms":{"type":"array","items":{"$ref":"#/definitions/PortalFormSummary"}},"totalCount":{"type":"integer","minimum":0,"description":"Total number of forms matching the query.","example":3}}},"PortalFormSubmission":{"type":"object","description":"A single recorded submission against a managed form. PHI fields are\\nreplaced with `[REDACTED]` for Medical-category sites, in which case\\n`redactedForPhi` is true.\\n","required":["id","formId","siteSlug","submittedAt","source","values","redactedForPhi"],"properties":{"id":{"type":"string","description":"Unique submission row key (reverse-chronological)."},"formId":{"type":"string"},"siteSlug":{"type":"string"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submittedAt":{"type":"string","format":"date-time"},"source":{"type":"string","description":"Capture channel (e.g. `web`, `import`)."},"values":{"type":"object","additionalProperties":true,"description":"Field id → submitted value map. PHI values are redacted."},"attachments":{"type":"array","description":"Attachment metadata captured with the submission.","items":{"$ref":"#/definitions/PublicSiteFormAttachment"}},"leadId":{"type":"string","nullable":true,"description":"Cross-reference to the lead row created by the legacy capture flow."},"contactMessageId":{"type":"string","nullable":true,"description":"Future portal contact-message identifier when surfaced in the portal."},"redactedForPhi":{"type":"boolean","description":"True when one or more values were replaced with the PHI placeholder."},"remoteIp":{"type":"string","nullable":true},"userAgent":{"type":"string","nullable":true}}},"PortalFormSubmissionSummary":{"type":"object","description":"List-view representation of a submission.","required":["id","formId","submittedAt","redactedForPhi","summary"],"properties":{"id":{"type":"string"},"formId":{"type":"string"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submittedAt":{"type":"string","format":"date-time"},"redactedForPhi":{"type":"boolean"},"summary":{"type":"string","description":"Short single-line preview suitable for the portal list view."}}},"PortalFormSubmissionListResponse":{"type":"object","required":["submissions"],"properties":{"submissions":{"type":"array","items":{"$ref":"#/definitions/PortalFormSubmissionSummary"}}}}}'), At = {
722
+ $schema: xt,
723
+ $id: kt,
724
+ title: Pt,
725
+ type: qt,
726
+ description: It,
727
+ required: Lt,
728
+ properties: Tt,
729
+ definitions: Ct
245
730
  };
246
- let B = null;
247
- function Pe() {
248
- if (B) return B;
249
- const e = new re({
731
+ let Z = null;
732
+ function Vt() {
733
+ if (Z) return Z;
734
+ const e = new je({
250
735
  allErrors: !0,
251
736
  strict: !1,
252
737
  discriminator: !1
253
738
  });
254
- return ne(e), B = e.compile(Se), B;
739
+ return _e(e), Z = e.compile(At), Z;
255
740
  }
256
- function Ie(e) {
257
- const t = Pe(), n = t(e);
741
+ function Mt(e) {
742
+ const t = Vt(), n = t(e);
258
743
  return { valid: n, errors: n ? [] : t.errors ?? [] };
259
744
  }
260
- function Le(e, t, n) {
261
- const i = Ie(t);
745
+ function Et(e, t, n) {
746
+ const i = Mt(t);
262
747
  return !i.valid && n && console.warn(
263
748
  `[@duffcloudservices/site-forms] Form "${e}" failed schema validation:`,
264
749
  i.errors
265
750
  ), i;
266
751
  }
267
- function qe(e) {
752
+ function Ot(e) {
268
753
  const t = {};
269
754
  for (const [n, i] of Object.entries(e)) {
270
- const s = Ce(n), o = Ve(i);
271
- o && (t[o.formId ?? s] = o);
755
+ const o = Rt(n), a = Dt(i);
756
+ a && (t[a.formId ?? o] = a);
272
757
  }
273
758
  return t;
274
759
  }
275
- function Ce(e) {
760
+ function Rt(e) {
276
761
  return (e.split("/").pop() ?? e).replace(/\.ya?ml$/i, "");
277
762
  }
278
- function Ve(e) {
763
+ function Dt(e) {
279
764
  if (!e) return null;
280
765
  if (typeof e == "string")
281
- return Y.load(e);
766
+ return $e.load(e);
282
767
  if (typeof e == "object") {
283
768
  const t = e.default;
284
769
  return t && typeof t == "object" ? t : e;
285
770
  }
286
771
  return null;
287
772
  }
288
- function Et(e) {
289
- return Y.load(e);
773
+ function Xi(e) {
774
+ return $e.load(e);
290
775
  }
291
- const je = ["data-form-field-key"], Ae = ["for"], De = {
776
+ const jt = ["data-form-field-key"], _t = ["for"], Bt = {
292
777
  key: 0,
293
778
  "aria-hidden": "true",
294
779
  class: "dcs-form-field__required"
295
- }, Me = {
780
+ }, Nt = {
296
781
  key: 0,
297
782
  class: "dcs-form-field__help"
298
- }, Te = {
783
+ }, zt = {
299
784
  key: 0,
300
785
  class: "dcs-form-field__error",
301
786
  role: "alert"
302
- }, D = /* @__PURE__ */ I({
787
+ }, N = /* @__PURE__ */ M({
303
788
  __name: "DcsFormFieldWrapper",
304
789
  props: {
305
790
  field: {},
@@ -307,30 +792,30 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
307
792
  inputId: {}
308
793
  },
309
794
  setup(e) {
310
- return (t, n) => (u(), m("div", {
311
- class: Z(["dcs-form-field", [`dcs-form-field--${e.field.type}`, `dcs-form-field--width-${e.field.width ?? "full"}`]]),
795
+ return (t, n) => (c(), f("div", {
796
+ class: Oe(["dcs-form-field", [`dcs-form-field--${e.field.type}`, `dcs-form-field--width-${e.field.width ?? "full"}`]]),
312
797
  "data-form-field-key": e.field.id
313
798
  }, [
314
- P(t.$slots, "label", {}, () => [
315
- e.field.type !== "checkbox" && e.field.type !== "hidden" && e.field.type !== "section-heading" && e.field.type !== "html-block" ? (u(), m("label", {
799
+ V(t.$slots, "label", {}, () => [
800
+ e.field.type !== "checkbox" && e.field.type !== "hidden" && e.field.type !== "section-heading" && e.field.type !== "html-block" ? (c(), f("label", {
316
801
  key: 0,
317
802
  for: e.inputId ?? `field-${e.field.id}`,
318
803
  class: "dcs-form-field__label"
319
804
  }, [
320
- K(F(e.field.label) + " ", 1),
321
- e.field.required ? (u(), m("span", De, "*")) : k("", !0)
322
- ], 8, Ae)) : k("", !0)
805
+ W(w(e.field.label) + " ", 1),
806
+ e.field.required ? (c(), f("span", Bt, "*")) : T("", !0)
807
+ ], 8, _t)) : T("", !0)
323
808
  ]),
324
- P(t.$slots, "default"),
325
- P(t.$slots, "help", {}, () => [
326
- e.field.helpText ? (u(), m("p", Me, F(e.field.helpText), 1)) : k("", !0)
809
+ V(t.$slots, "default"),
810
+ V(t.$slots, "help", {}, () => [
811
+ e.field.helpText ? (c(), f("p", Nt, w(e.field.helpText), 1)) : T("", !0)
327
812
  ]),
328
- P(t.$slots, "error", {}, () => [
329
- e.error ? (u(), m("p", Te, F(e.error), 1)) : k("", !0)
813
+ V(t.$slots, "error", {}, () => [
814
+ e.error ? (c(), f("p", zt, w(e.error), 1)) : T("", !0)
330
815
  ])
331
- ], 10, je));
816
+ ], 10, jt));
332
817
  }
333
- }), Oe = ["id", "type", "name", "value", "placeholder", "required", "aria-invalid", "aria-describedby"], J = /* @__PURE__ */ I({
818
+ }), Kt = ["id", "type", "name", "value", "placeholder", "required", "aria-invalid", "aria-describedby"], Fe = /* @__PURE__ */ M({
334
819
  __name: "DcsFormText",
335
820
  props: {
336
821
  field: {},
@@ -339,7 +824,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
339
824
  },
340
825
  emits: ["update:modelValue", "blur"],
341
826
  setup(e, { emit: t }) {
342
- const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => {
827
+ const n = e, i = t, o = m(() => `field-${n.field.id}`), a = m(() => {
343
828
  switch (n.field.type) {
344
829
  case "email":
345
830
  return "email";
@@ -349,37 +834,37 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
349
834
  return "text";
350
835
  }
351
836
  });
352
- return (c, a) => (u(), A(D, {
837
+ return (d, s) => (c(), _(N, {
353
838
  field: e.field,
354
839
  error: e.error,
355
- "input-id": s.value
840
+ "input-id": o.value
356
841
  }, {
357
842
  default: j(() => [
358
- P(c.$slots, "input", {
359
- id: s.value,
843
+ V(d.$slots, "input", {
844
+ id: o.value,
360
845
  value: e.modelValue,
361
846
  onInput: (l) => i("update:modelValue", l.target.value),
362
847
  onBlur: () => i("blur")
363
848
  }, () => [
364
- b("input", {
365
- id: s.value,
849
+ v("input", {
850
+ id: o.value,
366
851
  class: "dcs-form-input",
367
- type: o.value,
852
+ type: a.value,
368
853
  name: e.field.id,
369
854
  value: e.modelValue ?? "",
370
855
  placeholder: e.field.placeholder,
371
856
  required: e.field.required,
372
857
  "aria-invalid": !!e.error,
373
- "aria-describedby": e.error ? `${s.value}-error` : void 0,
374
- onInput: a[0] || (a[0] = (l) => i("update:modelValue", l.target.value)),
375
- onBlur: a[1] || (a[1] = (l) => i("blur"))
376
- }, null, 40, Oe)
858
+ "aria-describedby": e.error ? `${o.value}-error` : void 0,
859
+ onInput: s[0] || (s[0] = (l) => i("update:modelValue", l.target.value)),
860
+ onBlur: s[1] || (s[1] = (l) => i("blur"))
861
+ }, null, 40, Kt)
377
862
  ])
378
863
  ]),
379
864
  _: 3
380
865
  }, 8, ["field", "error", "input-id"]));
381
866
  }
382
- }), Re = ["id", "name", "placeholder", "required", "aria-invalid", "value"], Ee = /* @__PURE__ */ I({
867
+ }), Ut = ["id", "name", "placeholder", "required", "aria-invalid", "value"], Ht = /* @__PURE__ */ M({
383
868
  __name: "DcsFormTextarea",
384
869
  props: {
385
870
  field: {},
@@ -388,21 +873,21 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
388
873
  },
389
874
  emits: ["update:modelValue", "blur"],
390
875
  setup(e, { emit: t }) {
391
- const n = e, i = t, s = p(() => `field-${n.field.id}`);
392
- return (o, c) => (u(), A(D, {
876
+ const n = e, i = t, o = m(() => `field-${n.field.id}`);
877
+ return (a, d) => (c(), _(N, {
393
878
  field: e.field,
394
879
  error: e.error,
395
- "input-id": s.value
880
+ "input-id": o.value
396
881
  }, {
397
882
  default: j(() => [
398
- P(o.$slots, "input", {
399
- id: s.value,
883
+ V(a.$slots, "input", {
884
+ id: o.value,
400
885
  value: e.modelValue,
401
- onInput: (a) => i("update:modelValue", a.target.value),
886
+ onInput: (s) => i("update:modelValue", s.target.value),
402
887
  onBlur: () => i("blur")
403
888
  }, () => [
404
- b("textarea", {
405
- id: s.value,
889
+ v("textarea", {
890
+ id: o.value,
406
891
  class: "dcs-form-input dcs-form-textarea",
407
892
  name: e.field.id,
408
893
  placeholder: e.field.placeholder,
@@ -410,19 +895,19 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
410
895
  "aria-invalid": !!e.error,
411
896
  rows: "5",
412
897
  value: e.modelValue ?? "",
413
- onInput: c[0] || (c[0] = (a) => i("update:modelValue", a.target.value)),
414
- onBlur: c[1] || (c[1] = (a) => i("blur"))
415
- }, null, 40, Re)
898
+ onInput: d[0] || (d[0] = (s) => i("update:modelValue", s.target.value)),
899
+ onBlur: d[1] || (d[1] = (s) => i("blur"))
900
+ }, null, 40, Ut)
416
901
  ])
417
902
  ]),
418
903
  _: 3
419
904
  }, 8, ["field", "error", "input-id"]));
420
905
  }
421
- }), ze = ["id", "name", "required", "multiple", "aria-invalid", "value"], _e = {
906
+ }), Wt = ["id", "name", "required", "multiple", "aria-invalid", "value"], Gt = {
422
907
  key: 0,
423
908
  value: "",
424
909
  disabled: ""
425
- }, Be = ["value"], Ke = /* @__PURE__ */ I({
910
+ }, Jt = ["value"], Yt = /* @__PURE__ */ M({
426
911
  __name: "DcsFormSelect",
427
912
  props: {
428
913
  field: {},
@@ -431,51 +916,51 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
431
916
  },
432
917
  emits: ["update:modelValue", "blur"],
433
918
  setup(e, { emit: t }) {
434
- const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => n.field.type === "multiselect"), c = p(() => n.field.options ?? []);
435
- function a(l) {
436
- const h = l.target;
437
- if (o.value) {
919
+ const n = e, i = t, o = m(() => `field-${n.field.id}`), a = m(() => n.field.type === "multiselect"), d = m(() => n.field.options ?? []);
920
+ function s(l) {
921
+ const u = l.target;
922
+ if (a.value) {
438
923
  const r = [];
439
- for (const v of Array.from(h.selectedOptions)) r.push(v.value);
924
+ for (const b of Array.from(u.selectedOptions)) r.push(b.value);
440
925
  i("update:modelValue", r);
441
926
  } else
442
- i("update:modelValue", h.value);
927
+ i("update:modelValue", u.value);
443
928
  }
444
- return (l, h) => (u(), A(D, {
929
+ return (l, u) => (c(), _(N, {
445
930
  field: e.field,
446
931
  error: e.error,
447
- "input-id": s.value
932
+ "input-id": o.value
448
933
  }, {
449
934
  default: j(() => [
450
- P(l.$slots, "input", {
451
- id: s.value,
935
+ V(l.$slots, "input", {
936
+ id: o.value,
452
937
  value: e.modelValue,
453
- options: c.value,
454
- onChange: a
938
+ options: d.value,
939
+ onChange: s
455
940
  }, () => [
456
- b("select", {
457
- id: s.value,
941
+ v("select", {
942
+ id: o.value,
458
943
  class: "dcs-form-input dcs-form-select",
459
944
  name: e.field.id,
460
945
  required: e.field.required,
461
- multiple: o.value,
946
+ multiple: a.value,
462
947
  "aria-invalid": !!e.error,
463
- value: e.modelValue ?? (o.value ? [] : ""),
464
- onChange: a,
465
- onBlur: h[0] || (h[0] = (r) => i("blur"))
948
+ value: e.modelValue ?? (a.value ? [] : ""),
949
+ onChange: s,
950
+ onBlur: u[0] || (u[0] = (r) => i("blur"))
466
951
  }, [
467
- o.value ? k("", !0) : (u(), m("option", _e, F(e.field.placeholder ?? "Select…"), 1)),
468
- (u(!0), m(T, null, z(c.value, (r) => (u(), m("option", {
952
+ a.value ? T("", !0) : (c(), f("option", Gt, w(e.field.placeholder ?? "Select…"), 1)),
953
+ (c(!0), f(K, null, G(d.value, (r) => (c(), f("option", {
469
954
  key: r.value,
470
955
  value: r.value
471
- }, F(r.label), 9, Be))), 128))
472
- ], 40, ze)
956
+ }, w(r.label), 9, Jt))), 128))
957
+ ], 40, Wt)
473
958
  ])
474
959
  ]),
475
960
  _: 3
476
961
  }, 8, ["field", "error", "input-id"]));
477
962
  }
478
- }), We = ["aria-required", "aria-invalid"], Ue = { class: "sr-only" }, Ne = ["name", "value", "checked", "onChange"], He = /* @__PURE__ */ I({
963
+ }), Qt = ["aria-required", "aria-invalid"], Xt = { class: "sr-only" }, Zt = ["name", "value", "checked", "onChange"], ei = /* @__PURE__ */ M({
479
964
  __name: "DcsFormRadio",
480
965
  props: {
481
966
  field: {},
@@ -484,39 +969,39 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
484
969
  },
485
970
  emits: ["update:modelValue"],
486
971
  setup(e, { emit: t }) {
487
- const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => n.field.options ?? []);
488
- return (c, a) => (u(), A(D, {
972
+ const n = e, i = t, o = m(() => `field-${n.field.id}`), a = m(() => n.field.options ?? []);
973
+ return (d, s) => (c(), _(N, {
489
974
  field: e.field,
490
975
  error: e.error,
491
- "input-id": s.value
976
+ "input-id": o.value
492
977
  }, {
493
978
  default: j(() => [
494
- b("fieldset", {
979
+ v("fieldset", {
495
980
  class: "dcs-form-radio-group",
496
981
  role: "radiogroup",
497
982
  "aria-required": e.field.required,
498
983
  "aria-invalid": !!e.error
499
984
  }, [
500
- b("legend", Ue, F(e.field.label), 1),
501
- (u(!0), m(T, null, z(o.value, (l) => (u(), m("label", {
985
+ v("legend", Xt, w(e.field.label), 1),
986
+ (c(!0), f(K, null, G(a.value, (l) => (c(), f("label", {
502
987
  key: l.value,
503
988
  class: "dcs-form-radio"
504
989
  }, [
505
- b("input", {
990
+ v("input", {
506
991
  type: "radio",
507
992
  name: e.field.id,
508
993
  value: l.value,
509
994
  checked: e.modelValue === l.value,
510
- onChange: (h) => i("update:modelValue", l.value)
511
- }, null, 40, Ne),
512
- b("span", null, F(l.label), 1)
995
+ onChange: (u) => i("update:modelValue", l.value)
996
+ }, null, 40, Zt),
997
+ v("span", null, w(l.label), 1)
513
998
  ]))), 128))
514
- ], 8, We)
999
+ ], 8, Qt)
515
1000
  ]),
516
1001
  _: 1
517
1002
  }, 8, ["field", "error", "input-id"]));
518
1003
  }
519
- }), Je = ["aria-required", "aria-invalid"], Ge = { class: "sr-only" }, Ye = ["name", "value", "checked", "onChange"], Qe = /* @__PURE__ */ I({
1004
+ }), ti = ["aria-required", "aria-invalid"], ii = { class: "sr-only" }, ni = ["name", "value", "checked", "onChange"], ri = /* @__PURE__ */ M({
520
1005
  __name: "DcsFormCheckboxGroup",
521
1006
  props: {
522
1007
  field: {},
@@ -525,45 +1010,45 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
525
1010
  },
526
1011
  emits: ["update:modelValue"],
527
1012
  setup(e, { emit: t }) {
528
- const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => n.field.options ?? []), c = p(() => n.modelValue ?? []);
529
- function a(l, h) {
530
- const r = new Set(c.value);
531
- h ? r.add(l) : r.delete(l), i("update:modelValue", Array.from(r));
1013
+ const n = e, i = t, o = m(() => `field-${n.field.id}`), a = m(() => n.field.options ?? []), d = m(() => n.modelValue ?? []);
1014
+ function s(l, u) {
1015
+ const r = new Set(d.value);
1016
+ u ? r.add(l) : r.delete(l), i("update:modelValue", Array.from(r));
532
1017
  }
533
- return (l, h) => (u(), A(D, {
1018
+ return (l, u) => (c(), _(N, {
534
1019
  field: e.field,
535
1020
  error: e.error,
536
- "input-id": s.value
1021
+ "input-id": o.value
537
1022
  }, {
538
1023
  default: j(() => [
539
- b("fieldset", {
1024
+ v("fieldset", {
540
1025
  class: "dcs-form-checkbox-group",
541
1026
  "aria-required": e.field.required,
542
1027
  "aria-invalid": !!e.error
543
1028
  }, [
544
- b("legend", Ge, F(e.field.label), 1),
545
- (u(!0), m(T, null, z(o.value, (r) => (u(), m("label", {
1029
+ v("legend", ii, w(e.field.label), 1),
1030
+ (c(!0), f(K, null, G(a.value, (r) => (c(), f("label", {
546
1031
  key: r.value,
547
1032
  class: "dcs-form-checkbox"
548
1033
  }, [
549
- b("input", {
1034
+ v("input", {
550
1035
  type: "checkbox",
551
1036
  name: `${e.field.id}[]`,
552
1037
  value: r.value,
553
- checked: c.value.includes(r.value),
554
- onChange: (v) => a(r.value, v.target.checked)
555
- }, null, 40, Ye),
556
- b("span", null, F(r.label), 1)
1038
+ checked: d.value.includes(r.value),
1039
+ onChange: (b) => s(r.value, b.target.checked)
1040
+ }, null, 40, ni),
1041
+ v("span", null, w(r.label), 1)
557
1042
  ]))), 128))
558
- ], 8, Je)
1043
+ ], 8, ti)
559
1044
  ]),
560
1045
  _: 1
561
1046
  }, 8, ["field", "error", "input-id"]));
562
1047
  }
563
- }), Xe = { class: "sr-only" }, Ze = ["for"], et = ["id", "name", "checked", "required", "aria-invalid"], tt = {
1048
+ }), oi = { class: "sr-only" }, ai = ["for"], si = ["id", "name", "checked", "required", "aria-invalid"], li = {
564
1049
  key: 0,
565
1050
  "aria-hidden": "true"
566
- }, it = /* @__PURE__ */ I({
1051
+ }, di = /* @__PURE__ */ M({
567
1052
  __name: "DcsFormCheckbox",
568
1053
  props: {
569
1054
  field: {},
@@ -572,39 +1057,39 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
572
1057
  },
573
1058
  emits: ["update:modelValue"],
574
1059
  setup(e, { emit: t }) {
575
- const n = e, i = t, s = p(() => `field-${n.field.id}`);
576
- return (o, c) => (u(), A(D, {
1060
+ const n = e, i = t, o = m(() => `field-${n.field.id}`);
1061
+ return (a, d) => (c(), _(N, {
577
1062
  field: e.field,
578
1063
  error: e.error,
579
- "input-id": s.value
1064
+ "input-id": o.value
580
1065
  }, {
581
1066
  label: j(() => [
582
- b("span", Xe, F(e.field.label), 1)
1067
+ v("span", oi, w(e.field.label), 1)
583
1068
  ]),
584
1069
  default: j(() => [
585
- b("label", {
1070
+ v("label", {
586
1071
  class: "dcs-form-checkbox-single",
587
- for: s.value
1072
+ for: o.value
588
1073
  }, [
589
- b("input", {
590
- id: s.value,
1074
+ v("input", {
1075
+ id: o.value,
591
1076
  type: "checkbox",
592
1077
  name: e.field.id,
593
1078
  checked: !!e.modelValue,
594
1079
  required: e.field.required,
595
1080
  "aria-invalid": !!e.error,
596
- onChange: c[0] || (c[0] = (a) => i("update:modelValue", a.target.checked))
597
- }, null, 40, et),
598
- b("span", null, [
599
- K(F(e.field.label), 1),
600
- e.field.required ? (u(), m("span", tt, " *")) : k("", !0)
1081
+ onChange: d[0] || (d[0] = (s) => i("update:modelValue", s.target.checked))
1082
+ }, null, 40, si),
1083
+ v("span", null, [
1084
+ W(w(e.field.label), 1),
1085
+ e.field.required ? (c(), f("span", li, " *")) : T("", !0)
601
1086
  ])
602
- ], 8, Ze)
1087
+ ], 8, ai)
603
1088
  ]),
604
1089
  _: 1
605
1090
  }, 8, ["field", "error", "input-id"]));
606
1091
  }
607
- }), rt = ["id", "name", "required", "aria-invalid", "value"], nt = /* @__PURE__ */ I({
1092
+ }), ui = ["id", "name", "required", "aria-invalid", "value"], ci = /* @__PURE__ */ M({
608
1093
  __name: "DcsFormDate",
609
1094
  props: {
610
1095
  field: {},
@@ -613,40 +1098,40 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
613
1098
  },
614
1099
  emits: ["update:modelValue", "blur"],
615
1100
  setup(e, { emit: t }) {
616
- const n = e, i = t, s = p(() => `field-${n.field.id}`);
617
- return (o, c) => (u(), A(D, {
1101
+ const n = e, i = t, o = m(() => `field-${n.field.id}`);
1102
+ return (a, d) => (c(), _(N, {
618
1103
  field: e.field,
619
1104
  error: e.error,
620
- "input-id": s.value
1105
+ "input-id": o.value
621
1106
  }, {
622
1107
  default: j(() => [
623
- b("input", {
624
- id: s.value,
1108
+ v("input", {
1109
+ id: o.value,
625
1110
  class: "dcs-form-input dcs-form-date",
626
1111
  type: "date",
627
1112
  name: e.field.id,
628
1113
  required: e.field.required,
629
1114
  "aria-invalid": !!e.error,
630
1115
  value: e.modelValue ?? "",
631
- onInput: c[0] || (c[0] = (a) => i("update:modelValue", a.target.value)),
632
- onBlur: c[1] || (c[1] = (a) => i("blur"))
633
- }, null, 40, rt)
1116
+ onInput: d[0] || (d[0] = (s) => i("update:modelValue", s.target.value)),
1117
+ onBlur: d[1] || (d[1] = (s) => i("blur"))
1118
+ }, null, 40, ui)
634
1119
  ]),
635
1120
  _: 1
636
1121
  }, 8, ["field", "error", "input-id"]));
637
1122
  }
638
- }), ot = ["id", "name", "multiple", "required", "aria-invalid", "accept"], st = {
1123
+ }), mi = ["id", "name", "multiple", "required", "aria-invalid", "accept"], fi = {
639
1124
  key: 0,
640
1125
  class: "dcs-form-file__status"
641
- }, at = {
1126
+ }, pi = {
642
1127
  key: 1,
643
1128
  class: "dcs-form-file-preview-grid",
644
1129
  "aria-live": "polite"
645
- }, lt = ["src", "alt"], dt = {
1130
+ }, hi = ["src", "alt"], yi = {
646
1131
  key: 1,
647
1132
  class: "dcs-form-file-preview__placeholder",
648
1133
  "aria-hidden": "true"
649
- }, ut = { class: "dcs-form-file-preview__meta" }, ct = { class: "dcs-form-file-preview__name" }, mt = { class: "dcs-form-file-preview__size" }, ft = ["aria-label", "onClick"], pt = /* @__PURE__ */ I({
1134
+ }, gi = { class: "dcs-form-file-preview__meta" }, bi = { class: "dcs-form-file-preview__name" }, vi = { class: "dcs-form-file-preview__size" }, wi = ["aria-label", "onClick"], Fi = /* @__PURE__ */ M({
650
1135
  __name: "DcsFormFile",
651
1136
  props: {
652
1137
  field: {},
@@ -656,152 +1141,157 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
656
1141
  },
657
1142
  emits: ["update:modelValue"],
658
1143
  setup(e, { emit: t }) {
659
- const n = e, i = t, s = p(() => `field-${n.field.id}`), o = V(null), c = p(() => (n.field.validation?.accept ?? n.attachmentPolicy?.accept ?? []).join(",")), a = p(() => Math.max(1, Math.floor(n.attachmentPolicy?.maxFiles ?? 1))), l = p(() => a.value > 1), h = p(() => v(n.modelValue)), r = V([]);
660
- function v(d) {
661
- return Array.isArray(d) ? d.filter(Boolean) : d ? [d] : [];
1144
+ const n = e, i = t, o = m(() => `field-${n.field.id}`), a = E(null), d = m(() => (n.field.validation?.accept ?? n.attachmentPolicy?.accept ?? []).join(",")), s = m(() => Math.max(1, Math.floor(n.attachmentPolicy?.maxFiles ?? 1))), l = m(() => s.value > 1), u = m(() => b(n.modelValue)), r = E([]);
1145
+ function b(p) {
1146
+ return Array.isArray(p) ? p.filter(Boolean) : p ? [p] : [];
662
1147
  }
663
- function $(d) {
664
- const g = d.slice(0, a.value);
665
- i("update:modelValue", l.value ? g : g[0]);
1148
+ function F(p) {
1149
+ const $ = p.slice(0, s.value);
1150
+ i("update:modelValue", l.value ? $ : $[0]);
666
1151
  }
667
- function q(d) {
668
- return [d.name, d.type, d.size, d.lastModified].join(":");
1152
+ function L(p) {
1153
+ return [p.name, p.type, p.size, p.lastModified].join(":");
669
1154
  }
670
- function M(d) {
671
- const g = /* @__PURE__ */ new Set(), x = [];
672
- for (const C of d) {
673
- const E = q(C);
674
- g.has(E) || (g.add(E), x.push(C));
1155
+ function S(p) {
1156
+ const $ = /* @__PURE__ */ new Set(), q = [];
1157
+ for (const h of p) {
1158
+ const C = L(h);
1159
+ $.has(C) || ($.add(C), q.push(h));
675
1160
  }
676
- return x;
1161
+ return q;
677
1162
  }
678
- function O(d) {
679
- const g = d.target, x = Array.from(g.files ?? []).filter((C) => C.size > 0);
680
- x.length !== 0 && ($(l.value ? M([...h.value, ...x]) : x), g.value = "");
1163
+ function P(p) {
1164
+ const $ = p.target, q = Array.from($.files ?? []).filter((h) => h.size > 0);
1165
+ q.length !== 0 && (F(l.value ? S([...u.value, ...q]) : q), $.value = "");
681
1166
  }
682
- function R(d) {
683
- const g = h.value.filter((x, C) => C !== d);
684
- $(g), g.length === 0 && o.value && (o.value.value = "");
1167
+ function O(p) {
1168
+ const $ = u.value.filter((q, h) => h !== p);
1169
+ F($), $.length === 0 && a.value && (a.value.value = "");
685
1170
  }
686
- function w(d) {
687
- return d < 1024 ? `${d} B` : d < 1024 * 1024 ? `${(d / 1024).toFixed(1)} KB` : `${(d / (1024 * 1024)).toFixed(1)} MB`;
1171
+ function R(p) {
1172
+ return p < 1024 ? `${p} B` : p < 1024 * 1024 ? `${(p / 1024).toFixed(1)} KB` : `${(p / (1024 * 1024)).toFixed(1)} MB`;
688
1173
  }
689
- function L() {
690
- for (const d of r.value)
691
- URL.revokeObjectURL(d.url);
1174
+ function D() {
1175
+ for (const p of r.value)
1176
+ URL.revokeObjectURL(p.url);
692
1177
  r.value = [];
693
1178
  }
694
- return W(
695
- h,
696
- (d) => {
697
- L(), r.value = d.map((g) => ({
698
- file: g,
699
- url: URL.createObjectURL(g),
700
- canPreview: g.type.toLowerCase().startsWith("image/")
1179
+ return ee(
1180
+ u,
1181
+ (p) => {
1182
+ D(), r.value = p.map(($) => ({
1183
+ file: $,
1184
+ url: URL.createObjectURL($),
1185
+ canPreview: $.type.toLowerCase().startsWith("image/")
701
1186
  }));
702
1187
  },
703
1188
  { immediate: !0 }
704
- ), ee(L), (d, g) => (u(), A(D, {
1189
+ ), Re(D), (p, $) => (c(), _(N, {
705
1190
  field: e.field,
706
1191
  error: e.error,
707
- "input-id": s.value
1192
+ "input-id": o.value
708
1193
  }, {
709
1194
  default: j(() => [
710
- b("input", {
1195
+ v("input", {
711
1196
  ref_key: "inputEl",
712
- ref: o,
713
- id: s.value,
1197
+ ref: a,
1198
+ id: o.value,
714
1199
  class: "dcs-form-input dcs-form-file",
715
1200
  type: "file",
716
1201
  name: e.field.id,
717
1202
  multiple: l.value,
718
1203
  required: e.field.required,
719
1204
  "aria-invalid": !!e.error,
720
- accept: c.value || void 0,
721
- onChange: O
722
- }, null, 40, ot),
723
- l.value ? (u(), m("p", st, F(h.value.length) + " of " + F(a.value) + " files selected ", 1)) : k("", !0),
724
- r.value.length > 0 ? (u(), m("div", at, [
725
- (u(!0), m(T, null, z(r.value, (x, C) => (u(), m("div", {
726
- key: q(x.file),
1205
+ accept: d.value || void 0,
1206
+ onChange: P
1207
+ }, null, 40, mi),
1208
+ l.value ? (c(), f("p", fi, w(u.value.length) + " of " + w(s.value) + " files selected ", 1)) : T("", !0),
1209
+ r.value.length > 0 ? (c(), f("div", pi, [
1210
+ (c(!0), f(K, null, G(r.value, (q, h) => (c(), f("div", {
1211
+ key: L(q.file),
727
1212
  class: "dcs-form-file-preview"
728
1213
  }, [
729
- x.canPreview ? (u(), m("img", {
1214
+ q.canPreview ? (c(), f("img", {
730
1215
  key: 0,
731
1216
  class: "dcs-form-file-preview__image",
732
- src: x.url,
733
- alt: `${x.file.name} preview`
734
- }, null, 8, lt)) : (u(), m("div", dt, " File ")),
735
- b("div", ut, [
736
- b("span", ct, F(x.file.name), 1),
737
- b("span", mt, F(w(x.file.size)), 1)
1217
+ src: q.url,
1218
+ alt: `${q.file.name} preview`
1219
+ }, null, 8, hi)) : (c(), f("div", yi, " File ")),
1220
+ v("div", gi, [
1221
+ v("span", bi, w(q.file.name), 1),
1222
+ v("span", vi, w(R(q.file.size)), 1)
738
1223
  ]),
739
- b("button", {
1224
+ v("button", {
740
1225
  type: "button",
741
1226
  class: "dcs-form-file-preview__remove",
742
- "aria-label": `Remove ${x.file.name}`,
743
- onClick: (E) => R(C)
744
- }, " Remove ", 8, ft)
1227
+ "aria-label": `Remove ${q.file.name}`,
1228
+ onClick: (C) => O(h)
1229
+ }, " Remove ", 8, wi)
745
1230
  ]))), 128))
746
- ])) : k("", !0)
1231
+ ])) : T("", !0)
747
1232
  ]),
748
1233
  _: 1
749
1234
  }, 8, ["field", "error", "input-id"]));
750
1235
  }
751
- }), ht = ["name", "value", "data-form-field-key"], yt = /* @__PURE__ */ I({
1236
+ }), Si = ["name", "value", "data-form-field-key"], $i = /* @__PURE__ */ M({
752
1237
  __name: "DcsFormHidden",
753
1238
  props: {
754
1239
  field: {},
755
1240
  modelValue: {}
756
1241
  },
757
1242
  setup(e) {
758
- return (t, n) => (u(), m("input", {
1243
+ return (t, n) => (c(), f("input", {
759
1244
  type: "hidden",
760
1245
  name: e.field.id,
761
1246
  value: e.modelValue ?? e.field.defaultValue ?? "",
762
1247
  "data-form-field-key": e.field.id
763
- }, null, 8, ht));
1248
+ }, null, 8, Si));
764
1249
  }
765
- }), bt = ["data-form-field-key"], gt = { class: "dcs-form-section__heading" }, vt = {
1250
+ }), xi = ["data-form-field-key"], ki = { class: "dcs-form-section__heading" }, Pi = {
766
1251
  key: 0,
767
1252
  class: "dcs-form-section__help"
768
- }, Ft = /* @__PURE__ */ I({
1253
+ }, qi = /* @__PURE__ */ M({
769
1254
  __name: "DcsFormSection",
770
1255
  props: {
771
1256
  field: {}
772
1257
  },
773
1258
  setup(e) {
774
- return (t, n) => (u(), m("div", {
1259
+ return (t, n) => (c(), f("div", {
775
1260
  class: "dcs-form-section",
776
1261
  "data-form-field-key": e.field.id
777
1262
  }, [
778
- P(t.$slots, "default", {}, () => [
779
- b("h3", gt, F(e.field.label), 1),
780
- e.field.helpText ? (u(), m("p", vt, F(e.field.helpText), 1)) : k("", !0)
1263
+ V(t.$slots, "default", {}, () => [
1264
+ v("h3", ki, w(e.field.label), 1),
1265
+ e.field.helpText ? (c(), f("p", Pi, w(e.field.helpText), 1)) : T("", !0)
781
1266
  ])
782
- ], 8, bt));
1267
+ ], 8, xi));
783
1268
  }
784
- }), xt = ["data-form-field-key", "innerHTML"], $t = /* @__PURE__ */ I({
1269
+ }), Ii = ["data-form-field-key", "innerHTML"], Li = /* @__PURE__ */ M({
785
1270
  __name: "DcsFormHtmlBlock",
786
1271
  props: {
787
1272
  field: {}
788
1273
  },
789
1274
  setup(e) {
790
- return (t, n) => (u(), m("div", {
1275
+ return (t, n) => (c(), f("div", {
791
1276
  class: "dcs-form-html-block",
792
1277
  "data-form-field-key": e.field.id,
793
1278
  innerHTML: e.field.html ?? ""
794
- }, null, 8, xt));
1279
+ }, null, 8, Ii));
795
1280
  }
796
- }), G = {}, wt = ["data-form-key"], kt = ["data-form-key"], St = ["data-form-key"], Pt = {
1281
+ }), Se = {}, Ti = ["data-form-key"], Ci = ["data-form-key"], Ai = ["data-form-key"], Vi = {
797
1282
  key: 0,
798
1283
  class: "dcs-form__progress",
799
1284
  "aria-live": "polite"
800
- }, It = { class: "dcs-form__fields" }, Lt = {
1285
+ }, Mi = { class: "dcs-form__fields" }, Ei = {
1286
+ key: 0,
1287
+ class: "dcs-form__phi-guidance",
1288
+ "data-form-phi-guidance": "",
1289
+ role: "note"
1290
+ }, Oi = {
801
1291
  key: 1,
802
1292
  class: "dcs-form__submit-error",
803
1293
  role: "alert"
804
- }, qt = { class: "dcs-form__actions" }, Ct = ["disabled"], zt = /* @__PURE__ */ I({
1294
+ }, Ri = { class: "dcs-form__actions" }, Di = ["disabled"], Zi = /* @__PURE__ */ M({
805
1295
  __name: "DcsForm",
806
1296
  props: {
807
1297
  formId: {},
@@ -813,66 +1303,87 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
813
1303
  },
814
1304
  emits: ["submit-success", "submit-error", "validation-error"],
815
1305
  setup(e, { expose: t, emit: n }) {
816
- const i = e, s = n, o = /* @__PURE__ */ Object.assign({}), c = p(
817
- () => qe(i.formsModules ?? o)
818
- ), a = p(() => i.definitionOverride ? i.definitionOverride : c.value[i.formId] ?? null), l = !1;
819
- W(
820
- a,
821
- (w) => {
822
- w && Le(i.formId, w, l);
1306
+ const i = e, o = n, a = /* @__PURE__ */ Object.assign({}), d = m(
1307
+ () => Ot(i.formsModules ?? a)
1308
+ ), s = m(() => i.definitionOverride ? i.definitionOverride : d.value[i.formId] ?? null), l = !1;
1309
+ ee(
1310
+ s,
1311
+ (h) => {
1312
+ h && Et(i.formId, h, l);
823
1313
  },
824
1314
  { immediate: !0 }
825
1315
  );
826
- const h = p(
827
- () => a.value ?? {
1316
+ const u = m(
1317
+ () => s.value ?? {
828
1318
  formId: i.formId,
829
1319
  submission: { kind: "lead" },
830
1320
  fields: []
831
1321
  }
832
- ), r = ce({ definition: h.value });
833
- W(
834
- () => h.value,
1322
+ ), r = st({ definition: u.value }), { play: b } = et();
1323
+ ee(
1324
+ () => u.value,
835
1325
  () => r.reset()
836
1326
  );
837
- const v = p(
838
- () => i.apiBase ?? G?.VITE_DCS_PUBLIC_API ?? ""
839
- ), $ = p(
840
- () => i.siteSlug ?? G?.VITE_DCS_SITE_SLUG ?? ""
841
- ), q = p(
842
- () => h.value.submitLabel ?? "Send"
1327
+ const F = m(() => {
1328
+ const h = i.apiBase ?? Se?.VITE_DCS_PUBLIC_API ?? "";
1329
+ return h || L();
1330
+ });
1331
+ function L() {
1332
+ if (typeof window > "u") return "";
1333
+ const h = window.location.hostname.toLowerCase();
1334
+ return h === "localhost" || h === "127.0.0.1" || h === "::1" ? "/api/v1" : "https://api.duffcloudservices.com/api/v1";
1335
+ }
1336
+ const S = m(
1337
+ () => i.siteSlug ?? Se?.VITE_DCS_SITE_SLUG ?? ""
1338
+ ), P = m(
1339
+ () => u.value.submitLabel ?? "Send"
843
1340
  );
844
- function M(w) {
845
- switch (w.type) {
1341
+ function O(h) {
1342
+ switch (h.type) {
846
1343
  case "text":
847
1344
  case "email":
848
1345
  case "tel":
849
- return J;
1346
+ return Fe;
850
1347
  case "textarea":
851
- return Ee;
1348
+ return Ht;
852
1349
  case "select":
853
1350
  case "multiselect":
854
- return Ke;
1351
+ return Yt;
855
1352
  case "radio":
856
- return He;
1353
+ return ei;
857
1354
  case "checkbox-group":
858
- return Qe;
1355
+ return ri;
859
1356
  case "checkbox":
860
- return it;
1357
+ return di;
861
1358
  case "date":
862
- return nt;
1359
+ return ci;
863
1360
  case "file":
864
- return pt;
1361
+ return Fi;
865
1362
  case "hidden":
866
- return yt;
1363
+ return $i;
867
1364
  case "section-heading":
868
- return Ft;
1365
+ return qi;
869
1366
  case "html-block":
870
- return $t;
1367
+ return Li;
871
1368
  default:
872
- return J;
1369
+ return Fe;
873
1370
  }
874
1371
  }
875
- const O = V(null);
1372
+ const R = E(!1), D = m(
1373
+ () => R.value && !!s.value
1374
+ ), p = m(() => {
1375
+ const h = r.visibleFields.value.findIndex(
1376
+ (C) => C.type === "textarea" || C.role === "message" || C.role === "summary"
1377
+ );
1378
+ return h >= 0 ? h : 0;
1379
+ });
1380
+ pe(async () => {
1381
+ R.value = await $t({
1382
+ apiBase: F.value,
1383
+ siteSlug: S.value
1384
+ });
1385
+ });
1386
+ const $ = E(null);
876
1387
  t({
877
1388
  values: r.values,
878
1389
  errors: r.errors,
@@ -880,119 +1391,126 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
880
1391
  collectSubmissionValues: r.collectSubmissionValues,
881
1392
  reset: r.reset
882
1393
  });
883
- async function R(w) {
884
- if (w.preventDefault(), !a.value) return;
1394
+ async function q(h) {
1395
+ if (h.preventDefault(), !s.value) return;
885
1396
  if (!r.validateAll()) {
886
- s("validation-error", r.errors.value);
1397
+ o("validation-error", r.errors.value);
887
1398
  return;
888
1399
  }
889
1400
  r.submitting.value = !0, r.submitError.value = null;
890
- const d = {
1401
+ const A = {
891
1402
  formId: i.formId,
892
1403
  values: r.collectSubmissionValues(),
893
1404
  captchaToken: i.captchaToken
894
1405
  };
895
1406
  try {
896
- const g = await me({
897
- apiBase: v.value,
898
- siteSlug: $.value,
899
- payload: d
1407
+ const y = await vt({
1408
+ apiBase: F.value,
1409
+ siteSlug: S.value,
1410
+ payload: A
900
1411
  });
901
- r.submitted.value = !0, s("submit-success", g);
902
- } catch (g) {
903
- const x = g;
904
- r.submitError.value = x.error?.message ?? "Submission failed", s("submit-error", x);
1412
+ r.submitted.value = !0, b("notification.success"), o("submit-success", y);
1413
+ } catch (y) {
1414
+ const x = y;
1415
+ r.submitError.value = x.error?.message ?? "Submission failed", o("submit-error", x);
905
1416
  } finally {
906
1417
  r.submitting.value = !1;
907
1418
  }
908
1419
  }
909
- return te(() => {
910
- a.value || console.warn(
1420
+ return pe(() => {
1421
+ s.value || console.warn(
911
1422
  `[@duffcloudservices/site-forms] No form definition found for "${i.formId}". Expected a YAML at .dcs/forms/${i.formId}.yaml. In customer-site repos the YAML lives above the Vite root, so the internal auto-glob will not find it — pass formsModules explicitly: <DcsForm form-id="${i.formId}" :forms-modules="dcsFormsModules" />. See @duffcloudservices/site-forms README "Vite setup" section.`
912
1423
  );
913
- }), (w, L) => a.value ? f(r).submitted.value ? (u(), m("div", {
1424
+ }), (h, C) => s.value ? g(r).submitted.value ? (c(), f("div", {
914
1425
  key: 1,
915
1426
  class: "dcs-form dcs-form--success",
916
1427
  "data-form-key": e.formId
917
1428
  }, [
918
- P(w.$slots, "success", { definition: a.value }, () => [
919
- b("p", null, F(a.value.successMessage ?? "Thanks — we received your message."), 1)
1429
+ V(h.$slots, "success", { definition: s.value }, () => [
1430
+ v("p", null, w(s.value.successMessage ?? "Thanks — we received your message."), 1)
920
1431
  ])
921
- ], 8, kt)) : (u(), m("form", {
1432
+ ], 8, Ci)) : (c(), f("form", {
922
1433
  key: 2,
923
1434
  ref_key: "formEl",
924
- ref: O,
1435
+ ref: $,
925
1436
  class: "dcs-form",
926
1437
  "data-form-key": e.formId,
927
1438
  novalidate: "",
928
- onSubmit: R
1439
+ onSubmit: q
929
1440
  }, [
930
- P(w.$slots, "header", { definition: a.value }),
931
- f(r).steps.value ? (u(), m("div", Pt, [
932
- P(w.$slots, "progress", {
933
- current: f(r).currentStepIndex.value,
934
- total: f(r).steps.value.length,
935
- step: f(r).currentStep.value
1441
+ V(h.$slots, "header", { definition: s.value }),
1442
+ g(r).steps.value ? (c(), f("div", Vi, [
1443
+ V(h.$slots, "progress", {
1444
+ current: g(r).currentStepIndex.value,
1445
+ total: g(r).steps.value.length,
1446
+ step: g(r).currentStep.value
936
1447
  }, () => [
937
- b("p", null, [
938
- K(" Step " + F(f(r).currentStepIndex.value + 1) + " of " + F(f(r).steps.value.length) + " ", 1),
939
- f(r).currentStep.value ? (u(), m(T, { key: 0 }, [
940
- K(" — " + F(f(r).currentStep.value.title), 1)
941
- ], 64)) : k("", !0)
1448
+ v("p", null, [
1449
+ W(" Step " + w(g(r).currentStepIndex.value + 1) + " of " + w(g(r).steps.value.length) + " ", 1),
1450
+ g(r).currentStep.value ? (c(), f(K, { key: 0 }, [
1451
+ W(" — " + w(g(r).currentStep.value.title), 1)
1452
+ ], 64)) : T("", !0)
942
1453
  ])
943
1454
  ])
944
- ])) : k("", !0),
945
- b("div", It, [
946
- (u(!0), m(T, null, z(f(r).visibleFields.value, (d) => (u(), A(ie(M(d)), {
947
- key: d.id,
948
- field: d,
949
- "attachment-policy": h.value.attachmentPolicy,
950
- "model-value": f(r).values[d.id],
951
- error: f(r).errors.value[d.id],
952
- "onUpdate:modelValue": (g) => f(r).setValue(d.id, g),
953
- onBlur: (g) => f(r).touch(d.id)
954
- }, null, 40, ["field", "attachment-policy", "model-value", "error", "onUpdate:modelValue", "onBlur"]))), 128))
1455
+ ])) : T("", !0),
1456
+ v("div", Mi, [
1457
+ (c(!0), f(K, null, G(g(r).visibleFields.value, (A, y) => (c(), f(K, {
1458
+ key: A.id
1459
+ }, [
1460
+ D.value && y === p.value ? (c(), f("p", Ei, [
1461
+ v("strong", null, w(g(we).lead), 1),
1462
+ W(" " + w(g(we).detail), 1)
1463
+ ])) : T("", !0),
1464
+ (c(), _(De(O(A)), {
1465
+ field: A,
1466
+ "attachment-policy": u.value.attachmentPolicy,
1467
+ "model-value": g(r).values[A.id],
1468
+ error: g(r).errors.value[A.id],
1469
+ "onUpdate:modelValue": (x) => g(r).setValue(A.id, x),
1470
+ onBlur: (x) => g(r).touch(A.id)
1471
+ }, null, 40, ["field", "attachment-policy", "model-value", "error", "onUpdate:modelValue", "onBlur"]))
1472
+ ], 64))), 128))
955
1473
  ]),
956
- f(r).submitError.value ? (u(), m("div", Lt, F(f(r).submitError.value), 1)) : k("", !0),
957
- b("div", qt, [
958
- P(w.$slots, "actions", {
959
- isFirstStep: f(r).isFirstStep.value,
960
- isLastStep: f(r).isLastStep.value,
961
- submitting: f(r).submitting.value,
962
- prev: f(r).prev,
963
- next: f(r).next
1474
+ g(r).submitError.value ? (c(), f("div", Oi, w(g(r).submitError.value), 1)) : T("", !0),
1475
+ v("div", Ri, [
1476
+ V(h.$slots, "actions", {
1477
+ isFirstStep: g(r).isFirstStep.value,
1478
+ isLastStep: g(r).isLastStep.value,
1479
+ submitting: g(r).submitting.value,
1480
+ prev: g(r).prev,
1481
+ next: g(r).next
964
1482
  }, () => [
965
- f(r).steps.value && !f(r).isFirstStep.value ? (u(), m("button", {
1483
+ g(r).steps.value && !g(r).isFirstStep.value ? (c(), f("button", {
966
1484
  key: 0,
967
1485
  type: "button",
968
1486
  class: "dcs-form__btn dcs-form__btn--prev",
969
- onClick: L[0] || (L[0] = (d) => f(r).prev())
970
- }, " Previous ")) : k("", !0),
971
- f(r).steps.value && !f(r).isLastStep.value ? (u(), m("button", {
1487
+ onClick: C[0] || (C[0] = (A) => g(r).prev())
1488
+ }, " Previous ")) : T("", !0),
1489
+ g(r).steps.value && !g(r).isLastStep.value ? (c(), f("button", {
972
1490
  key: 1,
973
1491
  type: "button",
974
1492
  class: "dcs-form__btn dcs-form__btn--next",
975
- onClick: L[1] || (L[1] = (d) => f(r).next())
976
- }, " Next ")) : k("", !0),
977
- !f(r).steps.value || f(r).isLastStep.value ? (u(), m("button", {
1493
+ onClick: C[1] || (C[1] = (A) => g(r).next())
1494
+ }, " Next ")) : T("", !0),
1495
+ !g(r).steps.value || g(r).isLastStep.value ? (c(), f("button", {
978
1496
  key: 2,
979
1497
  type: "submit",
980
1498
  class: "dcs-form__btn dcs-form__btn--submit",
981
- disabled: f(r).submitting.value
982
- }, F(f(r).submitting.value ? "Sending…" : q.value), 9, Ct)) : k("", !0)
1499
+ disabled: g(r).submitting.value
1500
+ }, w(g(r).submitting.value ? "Sending…" : P.value), 9, Di)) : T("", !0)
983
1501
  ])
984
1502
  ])
985
- ], 40, St)) : (u(), m("div", {
1503
+ ], 40, Ai)) : (c(), f("div", {
986
1504
  key: 0,
987
1505
  class: "dcs-form dcs-form--missing",
988
1506
  "data-form-key": e.formId
989
1507
  }, [
990
- P(w.$slots, "missing", { formId: e.formId }, () => [
991
- b("p", null, "Form “" + F(e.formId) + "” is not configured.", 1)
1508
+ V(h.$slots, "missing", { formId: e.formId }, () => [
1509
+ v("p", null, "Form “" + w(e.formId) + "” is not configured.", 1)
992
1510
  ])
993
- ], 8, wt));
1511
+ ], 8, Ti));
994
1512
  }
995
- }), _t = {
1513
+ }), en = {
996
1514
  contact: {
997
1515
  label: "Contact",
998
1516
  description: "Canonical contact form with standard contact fields."
@@ -1009,7 +1527,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
1009
1527
  label: "Custom / free-form",
1010
1528
  description: "Flexible starter that stays fully editable for questionnaires and bespoke intake."
1011
1529
  }
1012
- }, Vt = () => [
1530
+ }, ji = () => [
1013
1531
  { id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
1014
1532
  { id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
1015
1533
  { id: "phone", type: "tel", role: "contact-phone", label: "Phone", width: "half" },
@@ -1023,7 +1541,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
1023
1541
  width: "full",
1024
1542
  validation: { minLength: 10, maxLength: 2e3 }
1025
1543
  }
1026
- ], jt = () => [
1544
+ ], _i = () => [
1027
1545
  { id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
1028
1546
  { id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
1029
1547
  { id: "phone", type: "tel", role: "contact-phone", label: "Phone", required: !0, width: "half" },
@@ -1047,7 +1565,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
1047
1565
  width: "full",
1048
1566
  validation: { accept: ["image/*"] }
1049
1567
  }
1050
- ], At = () => [
1568
+ ], Bi = () => [
1051
1569
  { id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
1052
1570
  { id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
1053
1571
  { id: "phone", type: "tel", role: "contact-phone", label: "Phone", width: "half" },
@@ -1075,7 +1593,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
1075
1593
  ]
1076
1594
  }
1077
1595
  }
1078
- ], Dt = () => [
1596
+ ], Ni = () => [
1079
1597
  { id: "name", type: "text", label: "Full name", required: !0, width: "full" },
1080
1598
  { id: "email", type: "email", label: "Email", required: !0, width: "half" },
1081
1599
  { id: "phone", type: "tel", label: "Phone", width: "half" },
@@ -1088,7 +1606,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
1088
1606
  validation: { minLength: 10, maxLength: 2e3 }
1089
1607
  }
1090
1608
  ];
1091
- function Bt(e, t = {}) {
1609
+ function tn(e, t = {}) {
1092
1610
  switch (e ?? "custom") {
1093
1611
  case "contact":
1094
1612
  return {
@@ -1100,7 +1618,7 @@ function Bt(e, t = {}) {
1100
1618
  kind: "lead",
1101
1619
  ...t.leadCategory ? { category: t.leadCategory } : {}
1102
1620
  },
1103
- fields: Vt()
1621
+ fields: ji()
1104
1622
  };
1105
1623
  case "revenue-contractor":
1106
1624
  return {
@@ -1118,7 +1636,7 @@ function Bt(e, t = {}) {
1118
1636
  maxFiles: 5,
1119
1637
  accept: ["image/*"]
1120
1638
  },
1121
- fields: jt()
1639
+ fields: _i()
1122
1640
  };
1123
1641
  case "resume-submission":
1124
1642
  return {
@@ -1140,7 +1658,7 @@ function Bt(e, t = {}) {
1140
1658
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
1141
1659
  ]
1142
1660
  },
1143
- fields: At()
1661
+ fields: Bi()
1144
1662
  };
1145
1663
  default:
1146
1664
  return {
@@ -1152,35 +1670,45 @@ function Bt(e, t = {}) {
1152
1670
  kind: "lead",
1153
1671
  ...t.leadCategory ? { category: t.leadCategory } : {}
1154
1672
  },
1155
- fields: Dt()
1673
+ fields: Ni()
1156
1674
  };
1157
1675
  }
1158
1676
  }
1159
1677
  export {
1160
- zt as DcsForm,
1161
- it as DcsFormCheckbox,
1162
- Qe as DcsFormCheckboxGroup,
1163
- nt as DcsFormDate,
1164
- D as DcsFormFieldWrapper,
1165
- pt as DcsFormFile,
1166
- yt as DcsFormHidden,
1167
- $t as DcsFormHtmlBlock,
1168
- He as DcsFormRadio,
1169
- Ft as DcsFormSection,
1170
- Ke as DcsFormSelect,
1171
- J as DcsFormText,
1172
- Ee as DcsFormTextarea,
1173
- _t as STANDARD_FORM_PRESET_META,
1174
- Bt as buildStandardFormDefinition,
1175
- ae as hasErrors,
1176
- U as isFieldVisible,
1177
- qe as loadFormDefinitions,
1178
- Et as parseFormYaml,
1179
- me as submitFormValues,
1180
- ce as useDcsForm,
1181
- oe as validateField,
1182
- se as validateForm,
1183
- Ie as validateFormDefinition,
1184
- Le as warnIfInvalid
1678
+ Zi as DcsForm,
1679
+ di as DcsFormCheckbox,
1680
+ ri as DcsFormCheckboxGroup,
1681
+ ci as DcsFormDate,
1682
+ N as DcsFormFieldWrapper,
1683
+ Fi as DcsFormFile,
1684
+ $i as DcsFormHidden,
1685
+ Li as DcsFormHtmlBlock,
1686
+ ei as DcsFormRadio,
1687
+ qi as DcsFormSection,
1688
+ Yt as DcsFormSelect,
1689
+ Fe as DcsFormText,
1690
+ Ht as DcsFormTextarea,
1691
+ we as PHI_GUIDANCE_COPY,
1692
+ en as STANDARD_FORM_PRESET_META,
1693
+ tn as buildStandardFormDefinition,
1694
+ $t as fetchSiteHandlesPhi,
1695
+ nt as hasErrors,
1696
+ Yi as isBookingCancellationReceipt,
1697
+ Gi as isEstimateReceipt,
1698
+ de as isFieldVisible,
1699
+ ct as isManagedFormReceipt,
1700
+ Ji as isPasswordlessVerifyReceipt,
1701
+ Wi as isSiteContactReceipt,
1702
+ Ot as loadFormDefinitions,
1703
+ Xi as parseFormYaml,
1704
+ dt as readExpectedJson,
1705
+ lt as readOkBody,
1706
+ Qi as resetSitePhiPostureCache,
1707
+ vt as submitFormValues,
1708
+ st as useDcsForm,
1709
+ tt as validateField,
1710
+ it as validateForm,
1711
+ Mt as validateFormDefinition,
1712
+ Et as warnIfInvalid
1185
1713
  };
1186
1714
  //# sourceMappingURL=index.js.map