@duffcloudservices/site-forms 0.4.2 → 0.5.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/composables/useFormSubmission.d.ts +5 -1
- package/dist/fields/DcsFormDate.vue.d.ts +2 -2
- package/dist/fields/DcsFormSelect.vue.d.ts +2 -2
- package/dist/fields/DcsFormText.vue.d.ts +2 -2
- package/dist/fields/DcsFormTextarea.vue.d.ts +2 -2
- package/dist/index.js +899 -536
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/DcsForm.vue +37 -4
- package/src/__tests__/missing-form-error-surfacing.test.ts +119 -0
- package/src/__tests__/submission.test.ts +24 -0
- package/src/composables/useFormSubmission.ts +11 -4
package/dist/index.js
CHANGED
|
@@ -1,305 +1,661 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
1
|
+
import { ref as A, watch as Q, computed as f, reactive as Se, defineComponent as C, createElementBlock as p, openBlock as m, normalizeClass as ke, renderSlot as V, createCommentVNode as I, createTextVNode as X, toDisplayString as w, createBlock as j, withCtx as D, createElementVNode as v, Fragment as U, renderList as W, onBeforeUnmount as qe, onMounted as Pe, unref as y, resolveDynamicComponent as Le } from "vue";
|
|
2
|
+
import Ie from "ajv";
|
|
3
|
+
import Te from "ajv-formats";
|
|
4
|
+
import fe from "js-yaml";
|
|
5
|
+
const oe = "dcs.sound", pe = {
|
|
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 Ve(e) {
|
|
20
|
+
const t = typeof e == "number" && Number.isFinite(e) ? e : pe.volume;
|
|
21
|
+
return Math.min(0.8, Math.max(0, t));
|
|
22
|
+
}
|
|
23
|
+
function ae(e) {
|
|
24
|
+
const t = pe, r = 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: Ve(e?.volume),
|
|
30
|
+
categories: {
|
|
31
|
+
interaction: r.interaction ?? t.categories.interaction,
|
|
32
|
+
overlay: r.overlay ?? t.categories.overlay,
|
|
33
|
+
navigation: r.navigation ?? t.categories.navigation,
|
|
34
|
+
notification: r.notification ?? t.categories.notification,
|
|
35
|
+
hero: r.hero ?? t.categories.hero
|
|
36
|
+
},
|
|
37
|
+
hintShown: typeof e?.hintShown == "boolean" ? e.hintShown : t.hintShown
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function Ce() {
|
|
41
|
+
if (typeof window > "u" || !window.localStorage)
|
|
42
|
+
return null;
|
|
43
|
+
try {
|
|
44
|
+
const e = window.localStorage.getItem(oe);
|
|
45
|
+
return e ? ae(JSON.parse(e)) : null;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const he = Ce(), k = A(he ?? ae(null));
|
|
51
|
+
let ye = he !== null;
|
|
52
|
+
function Ae(e) {
|
|
53
|
+
if (!(typeof window > "u" || !window.localStorage))
|
|
54
|
+
try {
|
|
55
|
+
window.localStorage.setItem(oe, JSON.stringify(e)), ye = !0;
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
Q(k, (e) => Ae(e), { deep: !0 });
|
|
60
|
+
typeof window < "u" && window.addEventListener && window.addEventListener("storage", (e) => {
|
|
61
|
+
if (e.key === oe && e.newValue)
|
|
62
|
+
try {
|
|
63
|
+
k.value = ae(JSON.parse(e.newValue)), ye = !0;
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const Z = 0.02, ge = 1e-4;
|
|
68
|
+
function $(e, t, r, i) {
|
|
69
|
+
const { freq: o, type: a = "sine", dur: u = 0.15, gain: s = 0.2, attack: d = 0.01, glideTo: c } = r, n = e.createOscillator(), h = e.createGain();
|
|
70
|
+
n.type = a, n.frequency.setValueAtTime(o, i), c && n.frequency.exponentialRampToValueAtTime(c, i + u), h.gain.setValueAtTime(ge, i), h.gain.exponentialRampToValueAtTime(s, i + d), h.gain.exponentialRampToValueAtTime(1e-3, i + u), n.connect(h).connect(t), n.start(i), n.stop(i + u + Z), n.onended = () => {
|
|
71
|
+
try {
|
|
72
|
+
n.disconnect(), h.disconnect();
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function Me(e, t, r, i) {
|
|
78
|
+
const { freq: o, ratio: a = 2, index: u = 120, dur: s = 0.4, gain: d = 0.2 } = r, c = e.createOscillator(), n = e.createOscillator(), h = e.createGain(), x = e.createGain();
|
|
79
|
+
c.type = "sine", n.type = "sine", c.frequency.setValueAtTime(o, i), n.frequency.setValueAtTime(o * a, i), h.gain.setValueAtTime(u, i), h.gain.exponentialRampToValueAtTime(1, i + s), x.gain.setValueAtTime(ge, i), x.gain.exponentialRampToValueAtTime(d, i + 0.012), x.gain.exponentialRampToValueAtTime(1e-3, i + s), n.connect(h).connect(c.frequency), c.connect(x).connect(t), n.start(i), c.start(i), n.stop(i + s + Z), c.stop(i + s + Z), c.onended = () => {
|
|
80
|
+
try {
|
|
81
|
+
c.disconnect(), n.disconnect(), h.disconnect(), x.disconnect();
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function G(e, t, r, i) {
|
|
87
|
+
const { dur: o = 0.08, gain: a = 0.2, cutoff: u = 2e3, q: s = 1, sweepTo: d } = r, c = Math.max(1, Math.floor(e.sampleRate * o)), n = e.createBuffer(1, c, e.sampleRate), h = n.getChannelData(0);
|
|
88
|
+
for (let q = 0; q < c; q++)
|
|
89
|
+
h[q] = Math.random() * 2 - 1;
|
|
90
|
+
const x = e.createBufferSource();
|
|
91
|
+
x.buffer = n;
|
|
92
|
+
const L = e.createBiquadFilter();
|
|
93
|
+
L.type = "bandpass", L.frequency.setValueAtTime(u, i), L.Q.value = Math.min(s, 10), d && L.frequency.exponentialRampToValueAtTime(d, i + o);
|
|
94
|
+
const S = e.createGain();
|
|
95
|
+
S.gain.setValueAtTime(a, i), S.gain.exponentialRampToValueAtTime(1e-3, i + o), x.connect(L).connect(S).connect(t), x.start(i), x.stop(i + o + Z), x.onended = () => {
|
|
96
|
+
try {
|
|
97
|
+
x.disconnect(), L.disconnect(), S.disconnect();
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const K = 523.25, le = 587.33, _ = 659.25, J = 783.99, De = 1046.5, je = 392, ee = 440, Oe = 293.66, be = {
|
|
103
|
+
// ── interaction ────────────────────────────────────────────────────────
|
|
104
|
+
"interaction.tap": {
|
|
105
|
+
category: "interaction",
|
|
106
|
+
gain: 0.25,
|
|
107
|
+
duration: 0.06,
|
|
108
|
+
render: (e, t, r, i) => G(e, t, { dur: 0.05, gain: i, cutoff: 1800, q: 1 }, r)
|
|
109
|
+
},
|
|
110
|
+
"interaction.subtle": {
|
|
111
|
+
category: "interaction",
|
|
112
|
+
gain: 0.12,
|
|
113
|
+
duration: 0.04,
|
|
114
|
+
render: (e, t, r, i) => G(e, t, { dur: 0.035, gain: i, cutoff: 2400, q: 0.7 }, r)
|
|
115
|
+
},
|
|
116
|
+
"interaction.toggle": {
|
|
117
|
+
category: "interaction",
|
|
118
|
+
gain: 0.2,
|
|
119
|
+
duration: 0.07,
|
|
120
|
+
render: (e, t, r, i) => $(e, t, { freq: _, type: "triangle", dur: 0.07, gain: i, attack: 5e-3 }, r)
|
|
121
|
+
},
|
|
122
|
+
"interaction.confirm": {
|
|
123
|
+
category: "interaction",
|
|
124
|
+
gain: 0.3,
|
|
125
|
+
duration: 0.09,
|
|
126
|
+
render: (e, t, r, i) => $(e, t, { freq: K, type: "sine", dur: 0.09, gain: i, attack: 8e-3 }, r)
|
|
127
|
+
},
|
|
128
|
+
// ── overlay ────────────────────────────────────────────────────────────
|
|
129
|
+
"overlay.open": {
|
|
130
|
+
category: "overlay",
|
|
131
|
+
gain: 0.18,
|
|
132
|
+
duration: 0.18,
|
|
133
|
+
render: (e, t, r, i) => G(e, t, { dur: 0.18, gain: i, cutoff: 700, q: 0.8, sweepTo: 2200 }, r)
|
|
134
|
+
},
|
|
135
|
+
"overlay.close": {
|
|
136
|
+
category: "overlay",
|
|
137
|
+
gain: 0.18,
|
|
138
|
+
duration: 0.18,
|
|
139
|
+
render: (e, t, r, i) => G(e, t, { dur: 0.18, gain: i, cutoff: 2200, q: 0.8, sweepTo: 600 }, r)
|
|
140
|
+
},
|
|
141
|
+
"overlay.expand": {
|
|
142
|
+
category: "overlay",
|
|
143
|
+
gain: 0.16,
|
|
144
|
+
duration: 0.16,
|
|
145
|
+
render: (e, t, r, i) => $(e, t, { freq: K, type: "sine", dur: 0.16, gain: i, glideTo: _ }, r)
|
|
146
|
+
},
|
|
147
|
+
"overlay.collapse": {
|
|
148
|
+
category: "overlay",
|
|
149
|
+
gain: 0.16,
|
|
150
|
+
duration: 0.16,
|
|
151
|
+
render: (e, t, r, i) => $(e, t, { freq: _, type: "sine", dur: 0.16, gain: i, glideTo: K }, r)
|
|
152
|
+
},
|
|
153
|
+
// ── navigation ─────────────────────────────────────────────────────────
|
|
154
|
+
"navigation.forward": {
|
|
155
|
+
category: "navigation",
|
|
156
|
+
gain: 0.2,
|
|
157
|
+
duration: 0.14,
|
|
158
|
+
render: (e, t, r, i) => $(e, t, { freq: le, type: "sine", dur: 0.13, gain: i, glideTo: J }, r)
|
|
159
|
+
},
|
|
160
|
+
"navigation.backward": {
|
|
161
|
+
category: "navigation",
|
|
162
|
+
gain: 0.2,
|
|
163
|
+
duration: 0.14,
|
|
164
|
+
render: (e, t, r, i) => $(e, t, { freq: J, type: "sine", dur: 0.13, gain: i, glideTo: le }, r)
|
|
165
|
+
},
|
|
166
|
+
"navigation.tab": {
|
|
167
|
+
category: "navigation",
|
|
168
|
+
gain: 0.18,
|
|
169
|
+
duration: 0.1,
|
|
170
|
+
render: (e, t, r, i) => $(e, t, { freq: ee, type: "triangle", dur: 0.09, gain: i, attack: 5e-3 }, r)
|
|
171
|
+
},
|
|
172
|
+
// ── notification ─────────────────────────────────────────────────────────
|
|
173
|
+
"notification.info": {
|
|
174
|
+
category: "notification",
|
|
175
|
+
gain: 0.15,
|
|
176
|
+
duration: 0.4,
|
|
177
|
+
render: (e, t, r, i) => Me(e, t, { freq: _, ratio: 2, index: 90, dur: 0.42, gain: i }, r)
|
|
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, r, i) => {
|
|
185
|
+
$(e, t, { freq: K, type: "sine", dur: 0.14, gain: i, attack: 8e-3 }, r), $(e, t, { freq: _, type: "sine", dur: 0.2, gain: i, attack: 8e-3 }, r + 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, r, i) => {
|
|
194
|
+
$(e, t, { freq: ee, type: "triangle", dur: 0.1, gain: i, attack: 6e-3 }, r), $(e, t, { freq: ee, type: "triangle", dur: 0.1, gain: i, attack: 6e-3 }, r + 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, r, i) => {
|
|
203
|
+
$(e, t, { freq: je, type: "triangle", dur: 0.16, gain: i, attack: 8e-3 }, r), $(e, t, { freq: Oe, type: "triangle", dur: 0.26, gain: i, attack: 8e-3 }, r + 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, r, i) => {
|
|
212
|
+
$(e, t, { freq: K, type: "sine", dur: 0.2, gain: i, attack: 0.01 }, r), $(e, t, { freq: _, type: "sine", dur: 0.2, gain: i, attack: 0.01 }, r + 0.16), $(e, t, { freq: J, type: "sine", dur: 0.4, gain: i, attack: 0.01 }, r + 0.32);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"hero.milestone": {
|
|
216
|
+
category: "hero",
|
|
217
|
+
gain: 0.5,
|
|
218
|
+
duration: 1.3,
|
|
219
|
+
render: (e, t, r, i) => {
|
|
220
|
+
$(e, t, { freq: K, type: "sine", dur: 0.22, gain: i, attack: 0.01 }, r), $(e, t, { freq: _, type: "sine", dur: 0.22, gain: i, attack: 0.01 }, r + 0.18), $(e, t, { freq: J, type: "sine", dur: 0.22, gain: i, attack: 0.01 }, r + 0.36), $(e, t, { freq: De, type: "sine", dur: 0.55, gain: i, attack: 0.01 }, r + 0.54);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}, Re = 120;
|
|
224
|
+
let R = null, N = null, de = !1, ve = !1;
|
|
225
|
+
const Fe = /* @__PURE__ */ new Map(), ie = /* @__PURE__ */ new Set();
|
|
226
|
+
function Ee() {
|
|
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 we() {
|
|
242
|
+
return typeof window > "u" || !window.matchMedia ? !1 : window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
243
|
+
}
|
|
244
|
+
function xe() {
|
|
245
|
+
if (typeof window > "u")
|
|
246
|
+
return null;
|
|
247
|
+
if (R)
|
|
248
|
+
return R;
|
|
249
|
+
const e = window.AudioContext ?? window.webkitAudioContext;
|
|
250
|
+
if (!e)
|
|
251
|
+
return null;
|
|
252
|
+
try {
|
|
253
|
+
R = new e(), N = R.createGain(), N.gain.value = 1, N.connect(R.destination);
|
|
254
|
+
} catch {
|
|
255
|
+
R = null, N = null;
|
|
256
|
+
}
|
|
257
|
+
return R;
|
|
258
|
+
}
|
|
259
|
+
function Be() {
|
|
260
|
+
if (de || typeof window > "u")
|
|
261
|
+
return;
|
|
262
|
+
de = !0;
|
|
263
|
+
const e = ["pointerdown", "keydown", "touchstart"], t = () => {
|
|
264
|
+
ve = !0;
|
|
265
|
+
const r = xe();
|
|
266
|
+
r && r.state === "suspended" && r.resume().catch(() => {
|
|
267
|
+
}), r && r.state !== "suspended" && e.forEach((i) => window.removeEventListener(i, t));
|
|
268
|
+
};
|
|
269
|
+
e.forEach((r) => window.addEventListener(r, t, { passive: !0 }));
|
|
270
|
+
}
|
|
271
|
+
function _e(e) {
|
|
272
|
+
return ie.add(e), () => ie.delete(e);
|
|
273
|
+
}
|
|
274
|
+
function ze(e, t) {
|
|
275
|
+
if (typeof document > "u")
|
|
276
|
+
return !1;
|
|
277
|
+
const r = be[e];
|
|
278
|
+
if (!r || Ee())
|
|
279
|
+
return !1;
|
|
280
|
+
const i = k.value;
|
|
281
|
+
if (!i.enabled || we() && !i.prmOverride)
|
|
282
|
+
return !1;
|
|
283
|
+
const o = r.category;
|
|
284
|
+
if (!i.categories[o] || document.hidden && o !== "notification")
|
|
285
|
+
return !1;
|
|
286
|
+
if (!t?.force) {
|
|
287
|
+
const a = Fe.get(e) ?? 0;
|
|
288
|
+
if (performance.now() - a < Re)
|
|
289
|
+
return !1;
|
|
290
|
+
}
|
|
291
|
+
return !0;
|
|
292
|
+
}
|
|
293
|
+
function Ke(e, t) {
|
|
294
|
+
if (!ze(e, t) || !R && !ve)
|
|
295
|
+
return !1;
|
|
296
|
+
const r = xe();
|
|
297
|
+
if (!r || !N)
|
|
298
|
+
return !1;
|
|
299
|
+
if (r.state === "suspended")
|
|
300
|
+
return r.resume().catch(() => {
|
|
301
|
+
}), !1;
|
|
302
|
+
const i = be[e];
|
|
303
|
+
Fe.set(e, performance.now());
|
|
304
|
+
const o = r.currentTime, a = Math.min(0.8, i.gain * k.value.volume);
|
|
305
|
+
try {
|
|
306
|
+
i.render(r, N, o, a);
|
|
307
|
+
} catch {
|
|
308
|
+
return !1;
|
|
309
|
+
}
|
|
310
|
+
return ie.forEach((u) => {
|
|
311
|
+
try {
|
|
312
|
+
u();
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
}), !0;
|
|
316
|
+
}
|
|
317
|
+
const re = A(!1);
|
|
318
|
+
if (typeof window < "u" && typeof window.matchMedia == "function")
|
|
319
|
+
try {
|
|
320
|
+
const e = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
321
|
+
re.value = e.matches, e.addEventListener?.("change", (t) => {
|
|
322
|
+
re.value = t.matches;
|
|
323
|
+
});
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
function te(e) {
|
|
327
|
+
k.value.enabled = e, k.value.userSet = !0, e ? we() && (k.value.prmOverride = !0) : k.value.prmOverride = !1;
|
|
328
|
+
}
|
|
329
|
+
function Ne() {
|
|
330
|
+
Be();
|
|
331
|
+
const e = f({
|
|
332
|
+
get: () => k.value.enabled,
|
|
333
|
+
set: (o) => te(o)
|
|
334
|
+
}), t = f({
|
|
335
|
+
get: () => k.value.volume,
|
|
336
|
+
set: (o) => {
|
|
337
|
+
k.value.volume = Math.min(0.8, Math.max(0, o));
|
|
338
|
+
}
|
|
339
|
+
}), r = f(() => k.value.hintShown), i = f(
|
|
340
|
+
() => re.value && !k.value.prmOverride
|
|
341
|
+
);
|
|
342
|
+
return {
|
|
343
|
+
play: Ke,
|
|
344
|
+
enabled: e,
|
|
345
|
+
volume: t,
|
|
346
|
+
hintShown: r,
|
|
347
|
+
mutedByReducedMotion: i,
|
|
348
|
+
toggleMute: () => te(!k.value.enabled),
|
|
349
|
+
setEnabled: (o) => te(o),
|
|
350
|
+
setVolume: (o) => {
|
|
351
|
+
k.value.volume = Math.min(0.8, Math.max(0, o));
|
|
352
|
+
},
|
|
353
|
+
isCategoryEnabled: (o) => k.value.categories[o],
|
|
354
|
+
setCategory: (o, a) => {
|
|
355
|
+
k.value.categories[o] = a, k.value.userSet = !0;
|
|
356
|
+
},
|
|
357
|
+
markHintShown: () => {
|
|
358
|
+
k.value.hintShown = !0;
|
|
359
|
+
},
|
|
360
|
+
onDidPlay: _e
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function ne(e, t) {
|
|
6
364
|
return e.visibleIf ? t[e.visibleIf.fieldId] === e.visibleIf.equals : !0;
|
|
7
365
|
}
|
|
8
|
-
function
|
|
366
|
+
function Ue(e, t, r) {
|
|
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,
|
|
12
|
-
if (e.required &&
|
|
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 (
|
|
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
|
|
375
|
+
const a = e.validation ?? {};
|
|
18
376
|
if (typeof i == "string") {
|
|
19
|
-
if (
|
|
20
|
-
return `${e.label} must be at least ${
|
|
21
|
-
if (
|
|
22
|
-
return `${e.label} must be at most ${
|
|
23
|
-
if (
|
|
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(
|
|
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 (
|
|
32
|
-
return `${e.label} must be at least ${
|
|
33
|
-
if (
|
|
34
|
-
return `${e.label} must be at most ${
|
|
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
|
|
38
|
-
if (
|
|
39
|
-
return `${e.label} accepts up to ${
|
|
40
|
-
if (
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
return `${
|
|
395
|
+
const u = Ge(t), s = r?.maxFiles, d = r?.maxFileSizeBytes, c = a.accept && a.accept.length > 0 ? a.accept : r?.accept;
|
|
396
|
+
if (s != null && u.length > s)
|
|
397
|
+
return `${e.label} accepts up to ${s} file${s === 1 ? "" : "s"}`;
|
|
398
|
+
if (d != null) {
|
|
399
|
+
const n = u.find((h) => h.size > d);
|
|
400
|
+
if (n)
|
|
401
|
+
return `${n.name} exceeds the ${Ye(d)} limit`;
|
|
44
402
|
}
|
|
45
|
-
if (
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
return `${
|
|
403
|
+
if (c && c.length > 0) {
|
|
404
|
+
const n = u.find((h) => !Je(h, c));
|
|
405
|
+
if (n)
|
|
406
|
+
return `${n.name} must be one of: ${c.join(", ")}`;
|
|
49
407
|
}
|
|
50
408
|
}
|
|
51
409
|
}
|
|
52
|
-
function
|
|
53
|
-
const i = {},
|
|
54
|
-
for (const
|
|
55
|
-
if (
|
|
56
|
-
const
|
|
57
|
-
|
|
410
|
+
function We(e, t, r) {
|
|
411
|
+
const i = {}, o = r ? new Set(r) : null;
|
|
412
|
+
for (const a of e.fields) {
|
|
413
|
+
if (o && !o.has(a.id) || !ne(a, t)) continue;
|
|
414
|
+
const u = Ue(a, t[a.id], e.attachmentPolicy);
|
|
415
|
+
u && (i[a.id] = u);
|
|
58
416
|
}
|
|
59
417
|
return i;
|
|
60
418
|
}
|
|
61
|
-
function
|
|
419
|
+
function He(e) {
|
|
62
420
|
return Object.values(e).some((t) => !!t);
|
|
63
421
|
}
|
|
64
|
-
function
|
|
422
|
+
function Ge(e) {
|
|
65
423
|
return typeof File > "u" ? [] : e instanceof File ? [e] : Array.isArray(e) ? e.filter((t) => t instanceof File) : [];
|
|
66
424
|
}
|
|
67
|
-
function
|
|
68
|
-
const
|
|
69
|
-
return t.some((
|
|
70
|
-
const
|
|
71
|
-
return
|
|
425
|
+
function Je(e, t) {
|
|
426
|
+
const r = e.name.toLowerCase(), i = e.type.toLowerCase();
|
|
427
|
+
return t.some((o) => {
|
|
428
|
+
const a = o.trim().toLowerCase();
|
|
429
|
+
return a === "" ? !1 : a.startsWith(".") ? r.endsWith(a) : a.endsWith("/*") ? i.startsWith(a.slice(0, -1)) : i === a;
|
|
72
430
|
});
|
|
73
431
|
}
|
|
74
|
-
function
|
|
432
|
+
function Ye(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
|
|
435
|
+
function ue(e) {
|
|
78
436
|
const t = {};
|
|
79
|
-
for (const
|
|
80
|
-
|
|
437
|
+
for (const r of e.fields)
|
|
438
|
+
r.defaultValue !== void 0 ? t[r.id] = r.defaultValue : r.type === "checkbox" ? t[r.id] = !1 : r.type === "multiselect" || r.type === "checkbox-group" ? t[r.id] = [] : t[r.id] = "";
|
|
81
439
|
return t;
|
|
82
440
|
}
|
|
83
|
-
function
|
|
84
|
-
const { definition: t } = e,
|
|
441
|
+
function Qe(e) {
|
|
442
|
+
const { definition: t } = e, r = Se(ue(t)), i = A({}), o = A({}), a = A(!1), u = A(!1), s = A(null), d = A(0), c = f(() => t.fields), n = f(
|
|
85
443
|
() => t.steps && t.steps.length > 0 ? t.steps : null
|
|
86
|
-
),
|
|
87
|
-
const
|
|
88
|
-
return
|
|
89
|
-
}),
|
|
90
|
-
const
|
|
91
|
-
if (!
|
|
92
|
-
const
|
|
93
|
-
return t.fields.filter((
|
|
94
|
-
}),
|
|
95
|
-
const
|
|
96
|
-
return !
|
|
97
|
-
}),
|
|
98
|
-
() =>
|
|
444
|
+
), h = f(() => {
|
|
445
|
+
const b = n.value;
|
|
446
|
+
return b ? b[d.value] ?? null : null;
|
|
447
|
+
}), x = f(() => {
|
|
448
|
+
const b = h.value;
|
|
449
|
+
if (!b) return t.fields;
|
|
450
|
+
const T = new Set(b.fieldIds);
|
|
451
|
+
return t.fields.filter((H) => T.has(H.id));
|
|
452
|
+
}), L = f(() => d.value === 0), S = f(() => {
|
|
453
|
+
const b = n.value;
|
|
454
|
+
return !b || d.value >= b.length - 1;
|
|
455
|
+
}), q = f(
|
|
456
|
+
() => x.value.filter((b) => ne(b, r))
|
|
99
457
|
);
|
|
100
|
-
function
|
|
101
|
-
|
|
458
|
+
function M(b, T) {
|
|
459
|
+
r[b] = T, i.value[b] && (i.value = { ...i.value, [b]: void 0 });
|
|
102
460
|
}
|
|
103
|
-
function
|
|
104
|
-
|
|
461
|
+
function z(b) {
|
|
462
|
+
o.value = { ...o.value, [b]: !0 };
|
|
105
463
|
}
|
|
106
|
-
function
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
109
|
-
const
|
|
110
|
-
for (const
|
|
111
|
-
|
|
112
|
-
i.value =
|
|
464
|
+
function B(b) {
|
|
465
|
+
const T = We(t, r, b);
|
|
466
|
+
if (b) {
|
|
467
|
+
const H = { ...i.value };
|
|
468
|
+
for (const se of b)
|
|
469
|
+
H[se] = T[se];
|
|
470
|
+
i.value = H;
|
|
113
471
|
} else
|
|
114
|
-
i.value =
|
|
115
|
-
return !
|
|
472
|
+
i.value = T;
|
|
473
|
+
return !He(i.value);
|
|
116
474
|
}
|
|
117
|
-
function
|
|
118
|
-
const
|
|
119
|
-
return
|
|
475
|
+
function l() {
|
|
476
|
+
const b = h.value?.fieldIds;
|
|
477
|
+
return B(b);
|
|
120
478
|
}
|
|
121
|
-
function
|
|
122
|
-
return
|
|
479
|
+
function F() {
|
|
480
|
+
return B();
|
|
123
481
|
}
|
|
124
|
-
function
|
|
125
|
-
return !
|
|
482
|
+
function g() {
|
|
483
|
+
return !n.value || !l() ? !1 : d.value < n.value.length - 1 ? (d.value++, !0) : !1;
|
|
126
484
|
}
|
|
127
|
-
function
|
|
128
|
-
|
|
485
|
+
function P() {
|
|
486
|
+
d.value > 0 && d.value--;
|
|
129
487
|
}
|
|
130
|
-
function
|
|
131
|
-
const
|
|
132
|
-
for (const
|
|
133
|
-
delete
|
|
134
|
-
Object.assign(
|
|
488
|
+
function O() {
|
|
489
|
+
const b = ue(t);
|
|
490
|
+
for (const T of Object.keys(r))
|
|
491
|
+
delete r[T];
|
|
492
|
+
Object.assign(r, b), i.value = {}, o.value = {}, a.value = !1, u.value = !1, s.value = null, d.value = 0;
|
|
135
493
|
}
|
|
136
|
-
function
|
|
137
|
-
const
|
|
138
|
-
for (const
|
|
139
|
-
|
|
140
|
-
return
|
|
494
|
+
function $e() {
|
|
495
|
+
const b = {};
|
|
496
|
+
for (const T of t.fields)
|
|
497
|
+
T.type === "section-heading" || T.type === "html-block" || ne(T, r) && (b[T.id] = r[T.id]);
|
|
498
|
+
return b;
|
|
141
499
|
}
|
|
142
500
|
return {
|
|
143
|
-
values:
|
|
501
|
+
values: r,
|
|
144
502
|
errors: i,
|
|
145
|
-
touched:
|
|
146
|
-
submitting:
|
|
147
|
-
submitted:
|
|
148
|
-
submitError:
|
|
149
|
-
fields:
|
|
150
|
-
steps:
|
|
151
|
-
currentStepIndex:
|
|
152
|
-
currentStep:
|
|
153
|
-
currentStepFields:
|
|
154
|
-
isFirstStep:
|
|
155
|
-
isLastStep:
|
|
156
|
-
visibleFields:
|
|
157
|
-
setValue:
|
|
158
|
-
touch:
|
|
159
|
-
validateCurrentScope:
|
|
160
|
-
validateAll:
|
|
161
|
-
next:
|
|
162
|
-
prev:
|
|
163
|
-
reset:
|
|
164
|
-
collectSubmissionValues:
|
|
503
|
+
touched: o,
|
|
504
|
+
submitting: a,
|
|
505
|
+
submitted: u,
|
|
506
|
+
submitError: s,
|
|
507
|
+
fields: c,
|
|
508
|
+
steps: n,
|
|
509
|
+
currentStepIndex: d,
|
|
510
|
+
currentStep: h,
|
|
511
|
+
currentStepFields: x,
|
|
512
|
+
isFirstStep: L,
|
|
513
|
+
isLastStep: S,
|
|
514
|
+
visibleFields: q,
|
|
515
|
+
setValue: M,
|
|
516
|
+
touch: z,
|
|
517
|
+
validateCurrentScope: l,
|
|
518
|
+
validateAll: F,
|
|
519
|
+
next: g,
|
|
520
|
+
prev: P,
|
|
521
|
+
reset: O,
|
|
522
|
+
collectSubmissionValues: $e
|
|
165
523
|
};
|
|
166
524
|
}
|
|
167
|
-
async function
|
|
168
|
-
const { apiBase: t, siteSlug:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
let l, h;
|
|
172
|
-
for (let r = 0; r <= s; r++)
|
|
525
|
+
async function Xe(e) {
|
|
526
|
+
const { apiBase: t, siteSlug: r, payload: i } = e, o = e.retries ?? 1, a = e.fetchImpl ?? fetch, u = t.replace(/\/$/, ""), s = `forms/${encodeURIComponent(i.formId)}/submissions`, d = r.trim(), c = d ? `${u}/sites/${encodeURIComponent(d)}/${s}` : `${u}/${s}`, n = Object.values(i.values).some(et);
|
|
527
|
+
let h, x;
|
|
528
|
+
for (let L = 0; L <= o; L++)
|
|
173
529
|
try {
|
|
174
|
-
const
|
|
530
|
+
const S = n ? { method: "POST", body: Ze(i) } : {
|
|
175
531
|
method: "POST",
|
|
176
532
|
headers: { "Content-Type": "application/json" },
|
|
177
533
|
body: JSON.stringify(i)
|
|
178
|
-
},
|
|
179
|
-
if (
|
|
180
|
-
const
|
|
181
|
-
return { payload: i, response:
|
|
534
|
+
}, q = await a(c, S);
|
|
535
|
+
if (x = q.status, q.ok) {
|
|
536
|
+
const M = await tt(q);
|
|
537
|
+
return { payload: i, response: M };
|
|
182
538
|
}
|
|
183
|
-
if (
|
|
184
|
-
const
|
|
539
|
+
if (q.status < 500) {
|
|
540
|
+
const M = await it(q);
|
|
185
541
|
throw {
|
|
186
542
|
payload: i,
|
|
187
|
-
status:
|
|
188
|
-
error: new Error(`Submission failed (${
|
|
543
|
+
status: q.status,
|
|
544
|
+
error: new Error(`Submission failed (${q.status}): ${M}`)
|
|
189
545
|
};
|
|
190
546
|
}
|
|
191
|
-
|
|
192
|
-
} catch (
|
|
193
|
-
if (
|
|
194
|
-
throw
|
|
195
|
-
|
|
547
|
+
h = new Error(`Server error ${q.status}`);
|
|
548
|
+
} catch (S) {
|
|
549
|
+
if (S && typeof S == "object" && "payload" in S && "error" in S)
|
|
550
|
+
throw S;
|
|
551
|
+
h = S;
|
|
196
552
|
}
|
|
197
553
|
throw {
|
|
198
554
|
payload: i,
|
|
199
|
-
status:
|
|
200
|
-
error:
|
|
555
|
+
status: x,
|
|
556
|
+
error: h ?? new Error("Submission failed")
|
|
201
557
|
};
|
|
202
558
|
}
|
|
203
|
-
function
|
|
559
|
+
function Ze(e) {
|
|
204
560
|
const t = new FormData();
|
|
205
561
|
t.append("formId", e.formId), e.captchaToken && t.append("captchaToken", e.captchaToken);
|
|
206
|
-
for (const [
|
|
562
|
+
for (const [r, i] of Object.entries(e.values))
|
|
207
563
|
if (i != null)
|
|
208
564
|
if (typeof File < "u" && i instanceof File)
|
|
209
|
-
t.append(`values[${
|
|
210
|
-
else if (Array.isArray(i) && i.some((
|
|
211
|
-
for (const
|
|
212
|
-
typeof File < "u" &&
|
|
565
|
+
t.append(`values[${r}]`, i);
|
|
566
|
+
else if (Array.isArray(i) && i.some((o) => typeof File < "u" && o instanceof File))
|
|
567
|
+
for (const o of i)
|
|
568
|
+
typeof File < "u" && o instanceof File && t.append(`values[${r}][]`, o);
|
|
213
569
|
else if (Array.isArray(i))
|
|
214
|
-
for (const
|
|
570
|
+
for (const o of i) t.append(`values[${r}][]`, String(o));
|
|
215
571
|
else
|
|
216
|
-
t.append(`values[${
|
|
572
|
+
t.append(`values[${r}]`, String(i));
|
|
217
573
|
return t;
|
|
218
574
|
}
|
|
219
|
-
function
|
|
575
|
+
function et(e) {
|
|
220
576
|
return typeof File > "u" ? !1 : e instanceof File ? !0 : Array.isArray(e) && e.some((t) => t instanceof File);
|
|
221
577
|
}
|
|
222
|
-
async function
|
|
578
|
+
async function tt(e) {
|
|
223
579
|
try {
|
|
224
580
|
return await e.json();
|
|
225
581
|
} catch {
|
|
226
582
|
return null;
|
|
227
583
|
}
|
|
228
584
|
}
|
|
229
|
-
async function
|
|
585
|
+
async function it(e) {
|
|
230
586
|
try {
|
|
231
587
|
return await e.text();
|
|
232
588
|
} catch {
|
|
233
589
|
return "";
|
|
234
590
|
}
|
|
235
591
|
}
|
|
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:
|
|
238
|
-
$id:
|
|
239
|
-
title:
|
|
240
|
-
type:
|
|
241
|
-
description:
|
|
242
|
-
required:
|
|
243
|
-
properties:
|
|
244
|
-
definitions:
|
|
592
|
+
const rt = "http://json-schema.org/draft-07/schema#", nt = "https://duffcloudservices.com/schemas/form-definition.schema.json", ot = "PortalFormDefinition", at = "object", st = "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"], dt = { 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." } }, ut = /* @__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"}}}}}'), ct = {
|
|
593
|
+
$schema: rt,
|
|
594
|
+
$id: nt,
|
|
595
|
+
title: ot,
|
|
596
|
+
type: at,
|
|
597
|
+
description: st,
|
|
598
|
+
required: lt,
|
|
599
|
+
properties: dt,
|
|
600
|
+
definitions: ut
|
|
245
601
|
};
|
|
246
|
-
let
|
|
247
|
-
function
|
|
248
|
-
if (
|
|
249
|
-
const e = new
|
|
602
|
+
let Y = null;
|
|
603
|
+
function mt() {
|
|
604
|
+
if (Y) return Y;
|
|
605
|
+
const e = new Ie({
|
|
250
606
|
allErrors: !0,
|
|
251
607
|
strict: !1,
|
|
252
608
|
discriminator: !1
|
|
253
609
|
});
|
|
254
|
-
return
|
|
610
|
+
return Te(e), Y = e.compile(ct), Y;
|
|
255
611
|
}
|
|
256
|
-
function
|
|
257
|
-
const t =
|
|
258
|
-
return { valid:
|
|
612
|
+
function ft(e) {
|
|
613
|
+
const t = mt(), r = t(e);
|
|
614
|
+
return { valid: r, errors: r ? [] : t.errors ?? [] };
|
|
259
615
|
}
|
|
260
|
-
function
|
|
261
|
-
const i =
|
|
262
|
-
return !i.valid &&
|
|
616
|
+
function pt(e, t, r) {
|
|
617
|
+
const i = ft(t);
|
|
618
|
+
return !i.valid && r && console.warn(
|
|
263
619
|
`[@duffcloudservices/site-forms] Form "${e}" failed schema validation:`,
|
|
264
620
|
i.errors
|
|
265
621
|
), i;
|
|
266
622
|
}
|
|
267
|
-
function
|
|
623
|
+
function ht(e) {
|
|
268
624
|
const t = {};
|
|
269
|
-
for (const [
|
|
270
|
-
const
|
|
271
|
-
|
|
625
|
+
for (const [r, i] of Object.entries(e)) {
|
|
626
|
+
const o = yt(r), a = gt(i);
|
|
627
|
+
a && (t[a.formId ?? o] = a);
|
|
272
628
|
}
|
|
273
629
|
return t;
|
|
274
630
|
}
|
|
275
|
-
function
|
|
631
|
+
function yt(e) {
|
|
276
632
|
return (e.split("/").pop() ?? e).replace(/\.ya?ml$/i, "");
|
|
277
633
|
}
|
|
278
|
-
function
|
|
634
|
+
function gt(e) {
|
|
279
635
|
if (!e) return null;
|
|
280
636
|
if (typeof e == "string")
|
|
281
|
-
return
|
|
637
|
+
return fe.load(e);
|
|
282
638
|
if (typeof e == "object") {
|
|
283
639
|
const t = e.default;
|
|
284
640
|
return t && typeof t == "object" ? t : e;
|
|
285
641
|
}
|
|
286
642
|
return null;
|
|
287
643
|
}
|
|
288
|
-
function
|
|
289
|
-
return
|
|
644
|
+
function ki(e) {
|
|
645
|
+
return fe.load(e);
|
|
290
646
|
}
|
|
291
|
-
const
|
|
647
|
+
const bt = ["data-form-field-key"], vt = ["for"], Ft = {
|
|
292
648
|
key: 0,
|
|
293
649
|
"aria-hidden": "true",
|
|
294
650
|
class: "dcs-form-field__required"
|
|
295
|
-
},
|
|
651
|
+
}, wt = {
|
|
296
652
|
key: 0,
|
|
297
653
|
class: "dcs-form-field__help"
|
|
298
|
-
},
|
|
654
|
+
}, xt = {
|
|
299
655
|
key: 0,
|
|
300
656
|
class: "dcs-form-field__error",
|
|
301
657
|
role: "alert"
|
|
302
|
-
},
|
|
658
|
+
}, E = /* @__PURE__ */ C({
|
|
303
659
|
__name: "DcsFormFieldWrapper",
|
|
304
660
|
props: {
|
|
305
661
|
field: {},
|
|
@@ -307,30 +663,30 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
307
663
|
inputId: {}
|
|
308
664
|
},
|
|
309
665
|
setup(e) {
|
|
310
|
-
return (t,
|
|
311
|
-
class:
|
|
666
|
+
return (t, r) => (m(), p("div", {
|
|
667
|
+
class: ke(["dcs-form-field", [`dcs-form-field--${e.field.type}`, `dcs-form-field--width-${e.field.width ?? "full"}`]]),
|
|
312
668
|
"data-form-field-key": e.field.id
|
|
313
669
|
}, [
|
|
314
|
-
|
|
315
|
-
e.field.type !== "checkbox" && e.field.type !== "hidden" && e.field.type !== "section-heading" && e.field.type !== "html-block" ? (
|
|
670
|
+
V(t.$slots, "label", {}, () => [
|
|
671
|
+
e.field.type !== "checkbox" && e.field.type !== "hidden" && e.field.type !== "section-heading" && e.field.type !== "html-block" ? (m(), p("label", {
|
|
316
672
|
key: 0,
|
|
317
673
|
for: e.inputId ?? `field-${e.field.id}`,
|
|
318
674
|
class: "dcs-form-field__label"
|
|
319
675
|
}, [
|
|
320
|
-
|
|
321
|
-
e.field.required ? (
|
|
322
|
-
], 8,
|
|
676
|
+
X(w(e.field.label) + " ", 1),
|
|
677
|
+
e.field.required ? (m(), p("span", Ft, "*")) : I("", !0)
|
|
678
|
+
], 8, vt)) : I("", !0)
|
|
323
679
|
]),
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
e.field.helpText ? (
|
|
680
|
+
V(t.$slots, "default"),
|
|
681
|
+
V(t.$slots, "help", {}, () => [
|
|
682
|
+
e.field.helpText ? (m(), p("p", wt, w(e.field.helpText), 1)) : I("", !0)
|
|
327
683
|
]),
|
|
328
|
-
|
|
329
|
-
e.error ? (
|
|
684
|
+
V(t.$slots, "error", {}, () => [
|
|
685
|
+
e.error ? (m(), p("p", xt, w(e.error), 1)) : I("", !0)
|
|
330
686
|
])
|
|
331
|
-
], 10,
|
|
687
|
+
], 10, bt));
|
|
332
688
|
}
|
|
333
|
-
}),
|
|
689
|
+
}), $t = ["id", "type", "name", "value", "placeholder", "required", "aria-invalid", "aria-describedby"], ce = /* @__PURE__ */ C({
|
|
334
690
|
__name: "DcsFormText",
|
|
335
691
|
props: {
|
|
336
692
|
field: {},
|
|
@@ -339,8 +695,8 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
339
695
|
},
|
|
340
696
|
emits: ["update:modelValue", "blur"],
|
|
341
697
|
setup(e, { emit: t }) {
|
|
342
|
-
const
|
|
343
|
-
switch (
|
|
698
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`), a = f(() => {
|
|
699
|
+
switch (r.field.type) {
|
|
344
700
|
case "email":
|
|
345
701
|
return "email";
|
|
346
702
|
case "tel":
|
|
@@ -349,37 +705,37 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
349
705
|
return "text";
|
|
350
706
|
}
|
|
351
707
|
});
|
|
352
|
-
return (
|
|
708
|
+
return (u, s) => (m(), j(E, {
|
|
353
709
|
field: e.field,
|
|
354
710
|
error: e.error,
|
|
355
|
-
"input-id":
|
|
711
|
+
"input-id": o.value
|
|
356
712
|
}, {
|
|
357
|
-
default:
|
|
358
|
-
|
|
359
|
-
id:
|
|
713
|
+
default: D(() => [
|
|
714
|
+
V(u.$slots, "input", {
|
|
715
|
+
id: o.value,
|
|
360
716
|
value: e.modelValue,
|
|
361
|
-
onInput: (
|
|
717
|
+
onInput: (d) => i("update:modelValue", d.target.value),
|
|
362
718
|
onBlur: () => i("blur")
|
|
363
719
|
}, () => [
|
|
364
|
-
|
|
365
|
-
id:
|
|
720
|
+
v("input", {
|
|
721
|
+
id: o.value,
|
|
366
722
|
class: "dcs-form-input",
|
|
367
|
-
type:
|
|
723
|
+
type: a.value,
|
|
368
724
|
name: e.field.id,
|
|
369
725
|
value: e.modelValue ?? "",
|
|
370
726
|
placeholder: e.field.placeholder,
|
|
371
727
|
required: e.field.required,
|
|
372
728
|
"aria-invalid": !!e.error,
|
|
373
|
-
"aria-describedby": e.error ? `${
|
|
374
|
-
onInput:
|
|
375
|
-
onBlur:
|
|
376
|
-
}, null, 40,
|
|
729
|
+
"aria-describedby": e.error ? `${o.value}-error` : void 0,
|
|
730
|
+
onInput: s[0] || (s[0] = (d) => i("update:modelValue", d.target.value)),
|
|
731
|
+
onBlur: s[1] || (s[1] = (d) => i("blur"))
|
|
732
|
+
}, null, 40, $t)
|
|
377
733
|
])
|
|
378
734
|
]),
|
|
379
735
|
_: 3
|
|
380
736
|
}, 8, ["field", "error", "input-id"]));
|
|
381
737
|
}
|
|
382
|
-
}),
|
|
738
|
+
}), St = ["id", "name", "placeholder", "required", "aria-invalid", "value"], kt = /* @__PURE__ */ C({
|
|
383
739
|
__name: "DcsFormTextarea",
|
|
384
740
|
props: {
|
|
385
741
|
field: {},
|
|
@@ -388,21 +744,21 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
388
744
|
},
|
|
389
745
|
emits: ["update:modelValue", "blur"],
|
|
390
746
|
setup(e, { emit: t }) {
|
|
391
|
-
const
|
|
392
|
-
return (
|
|
747
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`);
|
|
748
|
+
return (a, u) => (m(), j(E, {
|
|
393
749
|
field: e.field,
|
|
394
750
|
error: e.error,
|
|
395
|
-
"input-id":
|
|
751
|
+
"input-id": o.value
|
|
396
752
|
}, {
|
|
397
|
-
default:
|
|
398
|
-
|
|
399
|
-
id:
|
|
753
|
+
default: D(() => [
|
|
754
|
+
V(a.$slots, "input", {
|
|
755
|
+
id: o.value,
|
|
400
756
|
value: e.modelValue,
|
|
401
|
-
onInput: (
|
|
757
|
+
onInput: (s) => i("update:modelValue", s.target.value),
|
|
402
758
|
onBlur: () => i("blur")
|
|
403
759
|
}, () => [
|
|
404
|
-
|
|
405
|
-
id:
|
|
760
|
+
v("textarea", {
|
|
761
|
+
id: o.value,
|
|
406
762
|
class: "dcs-form-input dcs-form-textarea",
|
|
407
763
|
name: e.field.id,
|
|
408
764
|
placeholder: e.field.placeholder,
|
|
@@ -410,19 +766,19 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
410
766
|
"aria-invalid": !!e.error,
|
|
411
767
|
rows: "5",
|
|
412
768
|
value: e.modelValue ?? "",
|
|
413
|
-
onInput:
|
|
414
|
-
onBlur:
|
|
415
|
-
}, null, 40,
|
|
769
|
+
onInput: u[0] || (u[0] = (s) => i("update:modelValue", s.target.value)),
|
|
770
|
+
onBlur: u[1] || (u[1] = (s) => i("blur"))
|
|
771
|
+
}, null, 40, St)
|
|
416
772
|
])
|
|
417
773
|
]),
|
|
418
774
|
_: 3
|
|
419
775
|
}, 8, ["field", "error", "input-id"]));
|
|
420
776
|
}
|
|
421
|
-
}),
|
|
777
|
+
}), qt = ["id", "name", "required", "multiple", "aria-invalid", "value"], Pt = {
|
|
422
778
|
key: 0,
|
|
423
779
|
value: "",
|
|
424
780
|
disabled: ""
|
|
425
|
-
},
|
|
781
|
+
}, Lt = ["value"], It = /* @__PURE__ */ C({
|
|
426
782
|
__name: "DcsFormSelect",
|
|
427
783
|
props: {
|
|
428
784
|
field: {},
|
|
@@ -431,51 +787,51 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
431
787
|
},
|
|
432
788
|
emits: ["update:modelValue", "blur"],
|
|
433
789
|
setup(e, { emit: t }) {
|
|
434
|
-
const
|
|
435
|
-
function
|
|
436
|
-
const
|
|
437
|
-
if (
|
|
438
|
-
const
|
|
439
|
-
for (const
|
|
440
|
-
i("update:modelValue",
|
|
790
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`), a = f(() => r.field.type === "multiselect"), u = f(() => r.field.options ?? []);
|
|
791
|
+
function s(d) {
|
|
792
|
+
const c = d.target;
|
|
793
|
+
if (a.value) {
|
|
794
|
+
const n = [];
|
|
795
|
+
for (const h of Array.from(c.selectedOptions)) n.push(h.value);
|
|
796
|
+
i("update:modelValue", n);
|
|
441
797
|
} else
|
|
442
|
-
i("update:modelValue",
|
|
798
|
+
i("update:modelValue", c.value);
|
|
443
799
|
}
|
|
444
|
-
return (
|
|
800
|
+
return (d, c) => (m(), j(E, {
|
|
445
801
|
field: e.field,
|
|
446
802
|
error: e.error,
|
|
447
|
-
"input-id":
|
|
803
|
+
"input-id": o.value
|
|
448
804
|
}, {
|
|
449
|
-
default:
|
|
450
|
-
|
|
451
|
-
id:
|
|
805
|
+
default: D(() => [
|
|
806
|
+
V(d.$slots, "input", {
|
|
807
|
+
id: o.value,
|
|
452
808
|
value: e.modelValue,
|
|
453
|
-
options:
|
|
454
|
-
onChange:
|
|
809
|
+
options: u.value,
|
|
810
|
+
onChange: s
|
|
455
811
|
}, () => [
|
|
456
|
-
|
|
457
|
-
id:
|
|
812
|
+
v("select", {
|
|
813
|
+
id: o.value,
|
|
458
814
|
class: "dcs-form-input dcs-form-select",
|
|
459
815
|
name: e.field.id,
|
|
460
816
|
required: e.field.required,
|
|
461
|
-
multiple:
|
|
817
|
+
multiple: a.value,
|
|
462
818
|
"aria-invalid": !!e.error,
|
|
463
|
-
value: e.modelValue ?? (
|
|
464
|
-
onChange:
|
|
465
|
-
onBlur:
|
|
819
|
+
value: e.modelValue ?? (a.value ? [] : ""),
|
|
820
|
+
onChange: s,
|
|
821
|
+
onBlur: c[0] || (c[0] = (n) => i("blur"))
|
|
466
822
|
}, [
|
|
467
|
-
|
|
468
|
-
(
|
|
469
|
-
key:
|
|
470
|
-
value:
|
|
471
|
-
},
|
|
472
|
-
], 40,
|
|
823
|
+
a.value ? I("", !0) : (m(), p("option", Pt, w(e.field.placeholder ?? "Select…"), 1)),
|
|
824
|
+
(m(!0), p(U, null, W(u.value, (n) => (m(), p("option", {
|
|
825
|
+
key: n.value,
|
|
826
|
+
value: n.value
|
|
827
|
+
}, w(n.label), 9, Lt))), 128))
|
|
828
|
+
], 40, qt)
|
|
473
829
|
])
|
|
474
830
|
]),
|
|
475
831
|
_: 3
|
|
476
832
|
}, 8, ["field", "error", "input-id"]));
|
|
477
833
|
}
|
|
478
|
-
}),
|
|
834
|
+
}), Tt = ["aria-required", "aria-invalid"], Vt = { class: "sr-only" }, Ct = ["name", "value", "checked", "onChange"], At = /* @__PURE__ */ C({
|
|
479
835
|
__name: "DcsFormRadio",
|
|
480
836
|
props: {
|
|
481
837
|
field: {},
|
|
@@ -484,39 +840,39 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
484
840
|
},
|
|
485
841
|
emits: ["update:modelValue"],
|
|
486
842
|
setup(e, { emit: t }) {
|
|
487
|
-
const
|
|
488
|
-
return (
|
|
843
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`), a = f(() => r.field.options ?? []);
|
|
844
|
+
return (u, s) => (m(), j(E, {
|
|
489
845
|
field: e.field,
|
|
490
846
|
error: e.error,
|
|
491
|
-
"input-id":
|
|
847
|
+
"input-id": o.value
|
|
492
848
|
}, {
|
|
493
|
-
default:
|
|
494
|
-
|
|
849
|
+
default: D(() => [
|
|
850
|
+
v("fieldset", {
|
|
495
851
|
class: "dcs-form-radio-group",
|
|
496
852
|
role: "radiogroup",
|
|
497
853
|
"aria-required": e.field.required,
|
|
498
854
|
"aria-invalid": !!e.error
|
|
499
855
|
}, [
|
|
500
|
-
|
|
501
|
-
(
|
|
502
|
-
key:
|
|
856
|
+
v("legend", Vt, w(e.field.label), 1),
|
|
857
|
+
(m(!0), p(U, null, W(a.value, (d) => (m(), p("label", {
|
|
858
|
+
key: d.value,
|
|
503
859
|
class: "dcs-form-radio"
|
|
504
860
|
}, [
|
|
505
|
-
|
|
861
|
+
v("input", {
|
|
506
862
|
type: "radio",
|
|
507
863
|
name: e.field.id,
|
|
508
|
-
value:
|
|
509
|
-
checked: e.modelValue ===
|
|
510
|
-
onChange: (
|
|
511
|
-
}, null, 40,
|
|
512
|
-
|
|
864
|
+
value: d.value,
|
|
865
|
+
checked: e.modelValue === d.value,
|
|
866
|
+
onChange: (c) => i("update:modelValue", d.value)
|
|
867
|
+
}, null, 40, Ct),
|
|
868
|
+
v("span", null, w(d.label), 1)
|
|
513
869
|
]))), 128))
|
|
514
|
-
], 8,
|
|
870
|
+
], 8, Tt)
|
|
515
871
|
]),
|
|
516
872
|
_: 1
|
|
517
873
|
}, 8, ["field", "error", "input-id"]));
|
|
518
874
|
}
|
|
519
|
-
}),
|
|
875
|
+
}), Mt = ["aria-required", "aria-invalid"], Dt = { class: "sr-only" }, jt = ["name", "value", "checked", "onChange"], Ot = /* @__PURE__ */ C({
|
|
520
876
|
__name: "DcsFormCheckboxGroup",
|
|
521
877
|
props: {
|
|
522
878
|
field: {},
|
|
@@ -525,45 +881,45 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
525
881
|
},
|
|
526
882
|
emits: ["update:modelValue"],
|
|
527
883
|
setup(e, { emit: t }) {
|
|
528
|
-
const
|
|
529
|
-
function
|
|
530
|
-
const
|
|
531
|
-
|
|
884
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`), a = f(() => r.field.options ?? []), u = f(() => r.modelValue ?? []);
|
|
885
|
+
function s(d, c) {
|
|
886
|
+
const n = new Set(u.value);
|
|
887
|
+
c ? n.add(d) : n.delete(d), i("update:modelValue", Array.from(n));
|
|
532
888
|
}
|
|
533
|
-
return (
|
|
889
|
+
return (d, c) => (m(), j(E, {
|
|
534
890
|
field: e.field,
|
|
535
891
|
error: e.error,
|
|
536
|
-
"input-id":
|
|
892
|
+
"input-id": o.value
|
|
537
893
|
}, {
|
|
538
|
-
default:
|
|
539
|
-
|
|
894
|
+
default: D(() => [
|
|
895
|
+
v("fieldset", {
|
|
540
896
|
class: "dcs-form-checkbox-group",
|
|
541
897
|
"aria-required": e.field.required,
|
|
542
898
|
"aria-invalid": !!e.error
|
|
543
899
|
}, [
|
|
544
|
-
|
|
545
|
-
(
|
|
546
|
-
key:
|
|
900
|
+
v("legend", Dt, w(e.field.label), 1),
|
|
901
|
+
(m(!0), p(U, null, W(a.value, (n) => (m(), p("label", {
|
|
902
|
+
key: n.value,
|
|
547
903
|
class: "dcs-form-checkbox"
|
|
548
904
|
}, [
|
|
549
|
-
|
|
905
|
+
v("input", {
|
|
550
906
|
type: "checkbox",
|
|
551
907
|
name: `${e.field.id}[]`,
|
|
552
|
-
value:
|
|
553
|
-
checked:
|
|
554
|
-
onChange: (
|
|
555
|
-
}, null, 40,
|
|
556
|
-
|
|
908
|
+
value: n.value,
|
|
909
|
+
checked: u.value.includes(n.value),
|
|
910
|
+
onChange: (h) => s(n.value, h.target.checked)
|
|
911
|
+
}, null, 40, jt),
|
|
912
|
+
v("span", null, w(n.label), 1)
|
|
557
913
|
]))), 128))
|
|
558
|
-
], 8,
|
|
914
|
+
], 8, Mt)
|
|
559
915
|
]),
|
|
560
916
|
_: 1
|
|
561
917
|
}, 8, ["field", "error", "input-id"]));
|
|
562
918
|
}
|
|
563
|
-
}),
|
|
919
|
+
}), Rt = { class: "sr-only" }, Et = ["for"], Bt = ["id", "name", "checked", "required", "aria-invalid"], _t = {
|
|
564
920
|
key: 0,
|
|
565
921
|
"aria-hidden": "true"
|
|
566
|
-
},
|
|
922
|
+
}, zt = /* @__PURE__ */ C({
|
|
567
923
|
__name: "DcsFormCheckbox",
|
|
568
924
|
props: {
|
|
569
925
|
field: {},
|
|
@@ -572,39 +928,39 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
572
928
|
},
|
|
573
929
|
emits: ["update:modelValue"],
|
|
574
930
|
setup(e, { emit: t }) {
|
|
575
|
-
const
|
|
576
|
-
return (
|
|
931
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`);
|
|
932
|
+
return (a, u) => (m(), j(E, {
|
|
577
933
|
field: e.field,
|
|
578
934
|
error: e.error,
|
|
579
|
-
"input-id":
|
|
935
|
+
"input-id": o.value
|
|
580
936
|
}, {
|
|
581
|
-
label:
|
|
582
|
-
|
|
937
|
+
label: D(() => [
|
|
938
|
+
v("span", Rt, w(e.field.label), 1)
|
|
583
939
|
]),
|
|
584
|
-
default:
|
|
585
|
-
|
|
940
|
+
default: D(() => [
|
|
941
|
+
v("label", {
|
|
586
942
|
class: "dcs-form-checkbox-single",
|
|
587
|
-
for:
|
|
943
|
+
for: o.value
|
|
588
944
|
}, [
|
|
589
|
-
|
|
590
|
-
id:
|
|
945
|
+
v("input", {
|
|
946
|
+
id: o.value,
|
|
591
947
|
type: "checkbox",
|
|
592
948
|
name: e.field.id,
|
|
593
949
|
checked: !!e.modelValue,
|
|
594
950
|
required: e.field.required,
|
|
595
951
|
"aria-invalid": !!e.error,
|
|
596
|
-
onChange:
|
|
597
|
-
}, null, 40,
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
e.field.required ? (
|
|
952
|
+
onChange: u[0] || (u[0] = (s) => i("update:modelValue", s.target.checked))
|
|
953
|
+
}, null, 40, Bt),
|
|
954
|
+
v("span", null, [
|
|
955
|
+
X(w(e.field.label), 1),
|
|
956
|
+
e.field.required ? (m(), p("span", _t, " *")) : I("", !0)
|
|
601
957
|
])
|
|
602
|
-
], 8,
|
|
958
|
+
], 8, Et)
|
|
603
959
|
]),
|
|
604
960
|
_: 1
|
|
605
961
|
}, 8, ["field", "error", "input-id"]));
|
|
606
962
|
}
|
|
607
|
-
}),
|
|
963
|
+
}), Kt = ["id", "name", "required", "aria-invalid", "value"], Nt = /* @__PURE__ */ C({
|
|
608
964
|
__name: "DcsFormDate",
|
|
609
965
|
props: {
|
|
610
966
|
field: {},
|
|
@@ -613,40 +969,40 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
613
969
|
},
|
|
614
970
|
emits: ["update:modelValue", "blur"],
|
|
615
971
|
setup(e, { emit: t }) {
|
|
616
|
-
const
|
|
617
|
-
return (
|
|
972
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`);
|
|
973
|
+
return (a, u) => (m(), j(E, {
|
|
618
974
|
field: e.field,
|
|
619
975
|
error: e.error,
|
|
620
|
-
"input-id":
|
|
976
|
+
"input-id": o.value
|
|
621
977
|
}, {
|
|
622
|
-
default:
|
|
623
|
-
|
|
624
|
-
id:
|
|
978
|
+
default: D(() => [
|
|
979
|
+
v("input", {
|
|
980
|
+
id: o.value,
|
|
625
981
|
class: "dcs-form-input dcs-form-date",
|
|
626
982
|
type: "date",
|
|
627
983
|
name: e.field.id,
|
|
628
984
|
required: e.field.required,
|
|
629
985
|
"aria-invalid": !!e.error,
|
|
630
986
|
value: e.modelValue ?? "",
|
|
631
|
-
onInput:
|
|
632
|
-
onBlur:
|
|
633
|
-
}, null, 40,
|
|
987
|
+
onInput: u[0] || (u[0] = (s) => i("update:modelValue", s.target.value)),
|
|
988
|
+
onBlur: u[1] || (u[1] = (s) => i("blur"))
|
|
989
|
+
}, null, 40, Kt)
|
|
634
990
|
]),
|
|
635
991
|
_: 1
|
|
636
992
|
}, 8, ["field", "error", "input-id"]));
|
|
637
993
|
}
|
|
638
|
-
}),
|
|
994
|
+
}), Ut = ["id", "name", "multiple", "required", "aria-invalid", "accept"], Wt = {
|
|
639
995
|
key: 0,
|
|
640
996
|
class: "dcs-form-file__status"
|
|
641
|
-
},
|
|
997
|
+
}, Ht = {
|
|
642
998
|
key: 1,
|
|
643
999
|
class: "dcs-form-file-preview-grid",
|
|
644
1000
|
"aria-live": "polite"
|
|
645
|
-
},
|
|
1001
|
+
}, Gt = ["src", "alt"], Jt = {
|
|
646
1002
|
key: 1,
|
|
647
1003
|
class: "dcs-form-file-preview__placeholder",
|
|
648
1004
|
"aria-hidden": "true"
|
|
649
|
-
},
|
|
1005
|
+
}, Yt = { class: "dcs-form-file-preview__meta" }, Qt = { class: "dcs-form-file-preview__name" }, Xt = { class: "dcs-form-file-preview__size" }, Zt = ["aria-label", "onClick"], ei = /* @__PURE__ */ C({
|
|
650
1006
|
__name: "DcsFormFile",
|
|
651
1007
|
props: {
|
|
652
1008
|
field: {},
|
|
@@ -656,152 +1012,152 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
656
1012
|
},
|
|
657
1013
|
emits: ["update:modelValue"],
|
|
658
1014
|
setup(e, { emit: t }) {
|
|
659
|
-
const
|
|
660
|
-
function
|
|
661
|
-
return Array.isArray(
|
|
1015
|
+
const r = e, i = t, o = f(() => `field-${r.field.id}`), a = A(null), u = f(() => (r.field.validation?.accept ?? r.attachmentPolicy?.accept ?? []).join(",")), s = f(() => Math.max(1, Math.floor(r.attachmentPolicy?.maxFiles ?? 1))), d = f(() => s.value > 1), c = f(() => h(r.modelValue)), n = A([]);
|
|
1016
|
+
function h(l) {
|
|
1017
|
+
return Array.isArray(l) ? l.filter(Boolean) : l ? [l] : [];
|
|
662
1018
|
}
|
|
663
|
-
function
|
|
664
|
-
const
|
|
665
|
-
i("update:modelValue",
|
|
1019
|
+
function x(l) {
|
|
1020
|
+
const F = l.slice(0, s.value);
|
|
1021
|
+
i("update:modelValue", d.value ? F : F[0]);
|
|
666
1022
|
}
|
|
667
|
-
function
|
|
668
|
-
return [
|
|
1023
|
+
function L(l) {
|
|
1024
|
+
return [l.name, l.type, l.size, l.lastModified].join(":");
|
|
669
1025
|
}
|
|
670
|
-
function
|
|
671
|
-
const
|
|
672
|
-
for (const
|
|
673
|
-
const
|
|
674
|
-
|
|
1026
|
+
function S(l) {
|
|
1027
|
+
const F = /* @__PURE__ */ new Set(), g = [];
|
|
1028
|
+
for (const P of l) {
|
|
1029
|
+
const O = L(P);
|
|
1030
|
+
F.has(O) || (F.add(O), g.push(P));
|
|
675
1031
|
}
|
|
676
|
-
return
|
|
1032
|
+
return g;
|
|
677
1033
|
}
|
|
678
|
-
function
|
|
679
|
-
const
|
|
680
|
-
|
|
1034
|
+
function q(l) {
|
|
1035
|
+
const F = l.target, g = Array.from(F.files ?? []).filter((P) => P.size > 0);
|
|
1036
|
+
g.length !== 0 && (x(d.value ? S([...c.value, ...g]) : g), F.value = "");
|
|
681
1037
|
}
|
|
682
|
-
function
|
|
683
|
-
const
|
|
684
|
-
|
|
1038
|
+
function M(l) {
|
|
1039
|
+
const F = c.value.filter((g, P) => P !== l);
|
|
1040
|
+
x(F), F.length === 0 && a.value && (a.value.value = "");
|
|
685
1041
|
}
|
|
686
|
-
function
|
|
687
|
-
return
|
|
1042
|
+
function z(l) {
|
|
1043
|
+
return l < 1024 ? `${l} B` : l < 1024 * 1024 ? `${(l / 1024).toFixed(1)} KB` : `${(l / (1024 * 1024)).toFixed(1)} MB`;
|
|
688
1044
|
}
|
|
689
|
-
function
|
|
690
|
-
for (const
|
|
691
|
-
URL.revokeObjectURL(
|
|
692
|
-
|
|
1045
|
+
function B() {
|
|
1046
|
+
for (const l of n.value)
|
|
1047
|
+
URL.revokeObjectURL(l.url);
|
|
1048
|
+
n.value = [];
|
|
693
1049
|
}
|
|
694
|
-
return
|
|
695
|
-
|
|
696
|
-
(
|
|
697
|
-
|
|
698
|
-
file:
|
|
699
|
-
url: URL.createObjectURL(
|
|
700
|
-
canPreview:
|
|
1050
|
+
return Q(
|
|
1051
|
+
c,
|
|
1052
|
+
(l) => {
|
|
1053
|
+
B(), n.value = l.map((F) => ({
|
|
1054
|
+
file: F,
|
|
1055
|
+
url: URL.createObjectURL(F),
|
|
1056
|
+
canPreview: F.type.toLowerCase().startsWith("image/")
|
|
701
1057
|
}));
|
|
702
1058
|
},
|
|
703
1059
|
{ immediate: !0 }
|
|
704
|
-
),
|
|
1060
|
+
), qe(B), (l, F) => (m(), j(E, {
|
|
705
1061
|
field: e.field,
|
|
706
1062
|
error: e.error,
|
|
707
|
-
"input-id":
|
|
1063
|
+
"input-id": o.value
|
|
708
1064
|
}, {
|
|
709
|
-
default:
|
|
710
|
-
|
|
1065
|
+
default: D(() => [
|
|
1066
|
+
v("input", {
|
|
711
1067
|
ref_key: "inputEl",
|
|
712
|
-
ref:
|
|
713
|
-
id:
|
|
1068
|
+
ref: a,
|
|
1069
|
+
id: o.value,
|
|
714
1070
|
class: "dcs-form-input dcs-form-file",
|
|
715
1071
|
type: "file",
|
|
716
1072
|
name: e.field.id,
|
|
717
|
-
multiple:
|
|
1073
|
+
multiple: d.value,
|
|
718
1074
|
required: e.field.required,
|
|
719
1075
|
"aria-invalid": !!e.error,
|
|
720
|
-
accept:
|
|
721
|
-
onChange:
|
|
722
|
-
}, null, 40,
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
(
|
|
726
|
-
key:
|
|
1076
|
+
accept: u.value || void 0,
|
|
1077
|
+
onChange: q
|
|
1078
|
+
}, null, 40, Ut),
|
|
1079
|
+
d.value ? (m(), p("p", Wt, w(c.value.length) + " of " + w(s.value) + " files selected ", 1)) : I("", !0),
|
|
1080
|
+
n.value.length > 0 ? (m(), p("div", Ht, [
|
|
1081
|
+
(m(!0), p(U, null, W(n.value, (g, P) => (m(), p("div", {
|
|
1082
|
+
key: L(g.file),
|
|
727
1083
|
class: "dcs-form-file-preview"
|
|
728
1084
|
}, [
|
|
729
|
-
|
|
1085
|
+
g.canPreview ? (m(), p("img", {
|
|
730
1086
|
key: 0,
|
|
731
1087
|
class: "dcs-form-file-preview__image",
|
|
732
|
-
src:
|
|
733
|
-
alt: `${
|
|
734
|
-
}, null, 8,
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1088
|
+
src: g.url,
|
|
1089
|
+
alt: `${g.file.name} preview`
|
|
1090
|
+
}, null, 8, Gt)) : (m(), p("div", Jt, " File ")),
|
|
1091
|
+
v("div", Yt, [
|
|
1092
|
+
v("span", Qt, w(g.file.name), 1),
|
|
1093
|
+
v("span", Xt, w(z(g.file.size)), 1)
|
|
738
1094
|
]),
|
|
739
|
-
|
|
1095
|
+
v("button", {
|
|
740
1096
|
type: "button",
|
|
741
1097
|
class: "dcs-form-file-preview__remove",
|
|
742
|
-
"aria-label": `Remove ${
|
|
743
|
-
onClick: (
|
|
744
|
-
}, " Remove ", 8,
|
|
1098
|
+
"aria-label": `Remove ${g.file.name}`,
|
|
1099
|
+
onClick: (O) => M(P)
|
|
1100
|
+
}, " Remove ", 8, Zt)
|
|
745
1101
|
]))), 128))
|
|
746
|
-
])) :
|
|
1102
|
+
])) : I("", !0)
|
|
747
1103
|
]),
|
|
748
1104
|
_: 1
|
|
749
1105
|
}, 8, ["field", "error", "input-id"]));
|
|
750
1106
|
}
|
|
751
|
-
}),
|
|
1107
|
+
}), ti = ["name", "value", "data-form-field-key"], ii = /* @__PURE__ */ C({
|
|
752
1108
|
__name: "DcsFormHidden",
|
|
753
1109
|
props: {
|
|
754
1110
|
field: {},
|
|
755
1111
|
modelValue: {}
|
|
756
1112
|
},
|
|
757
1113
|
setup(e) {
|
|
758
|
-
return (t,
|
|
1114
|
+
return (t, r) => (m(), p("input", {
|
|
759
1115
|
type: "hidden",
|
|
760
1116
|
name: e.field.id,
|
|
761
1117
|
value: e.modelValue ?? e.field.defaultValue ?? "",
|
|
762
1118
|
"data-form-field-key": e.field.id
|
|
763
|
-
}, null, 8,
|
|
1119
|
+
}, null, 8, ti));
|
|
764
1120
|
}
|
|
765
|
-
}),
|
|
1121
|
+
}), ri = ["data-form-field-key"], ni = { class: "dcs-form-section__heading" }, oi = {
|
|
766
1122
|
key: 0,
|
|
767
1123
|
class: "dcs-form-section__help"
|
|
768
|
-
},
|
|
1124
|
+
}, ai = /* @__PURE__ */ C({
|
|
769
1125
|
__name: "DcsFormSection",
|
|
770
1126
|
props: {
|
|
771
1127
|
field: {}
|
|
772
1128
|
},
|
|
773
1129
|
setup(e) {
|
|
774
|
-
return (t,
|
|
1130
|
+
return (t, r) => (m(), p("div", {
|
|
775
1131
|
class: "dcs-form-section",
|
|
776
1132
|
"data-form-field-key": e.field.id
|
|
777
1133
|
}, [
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
e.field.helpText ? (
|
|
1134
|
+
V(t.$slots, "default", {}, () => [
|
|
1135
|
+
v("h3", ni, w(e.field.label), 1),
|
|
1136
|
+
e.field.helpText ? (m(), p("p", oi, w(e.field.helpText), 1)) : I("", !0)
|
|
781
1137
|
])
|
|
782
|
-
], 8,
|
|
1138
|
+
], 8, ri));
|
|
783
1139
|
}
|
|
784
|
-
}),
|
|
1140
|
+
}), si = ["data-form-field-key", "innerHTML"], li = /* @__PURE__ */ C({
|
|
785
1141
|
__name: "DcsFormHtmlBlock",
|
|
786
1142
|
props: {
|
|
787
1143
|
field: {}
|
|
788
1144
|
},
|
|
789
1145
|
setup(e) {
|
|
790
|
-
return (t,
|
|
1146
|
+
return (t, r) => (m(), p("div", {
|
|
791
1147
|
class: "dcs-form-html-block",
|
|
792
1148
|
"data-form-field-key": e.field.id,
|
|
793
1149
|
innerHTML: e.field.html ?? ""
|
|
794
|
-
}, null, 8,
|
|
1150
|
+
}, null, 8, si));
|
|
795
1151
|
}
|
|
796
|
-
}),
|
|
1152
|
+
}), me = {}, di = ["data-form-key"], ui = ["data-form-key"], ci = ["data-form-key"], mi = {
|
|
797
1153
|
key: 0,
|
|
798
1154
|
class: "dcs-form__progress",
|
|
799
1155
|
"aria-live": "polite"
|
|
800
|
-
},
|
|
1156
|
+
}, fi = { class: "dcs-form__fields" }, pi = {
|
|
801
1157
|
key: 1,
|
|
802
1158
|
class: "dcs-form__submit-error",
|
|
803
1159
|
role: "alert"
|
|
804
|
-
},
|
|
1160
|
+
}, hi = { class: "dcs-form__actions" }, yi = ["disabled"], qi = /* @__PURE__ */ C({
|
|
805
1161
|
__name: "DcsForm",
|
|
806
1162
|
props: {
|
|
807
1163
|
formId: {},
|
|
@@ -812,187 +1168,194 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
812
1168
|
formsModules: {}
|
|
813
1169
|
},
|
|
814
1170
|
emits: ["submit-success", "submit-error", "validation-error"],
|
|
815
|
-
setup(e, { expose: t, emit:
|
|
816
|
-
const i = e,
|
|
817
|
-
() =>
|
|
818
|
-
),
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
(
|
|
822
|
-
|
|
1171
|
+
setup(e, { expose: t, emit: r }) {
|
|
1172
|
+
const i = e, o = r, a = /* @__PURE__ */ Object.assign({}), u = f(
|
|
1173
|
+
() => ht(i.formsModules ?? a)
|
|
1174
|
+
), s = f(() => i.definitionOverride ? i.definitionOverride : u.value[i.formId] ?? null), d = !1;
|
|
1175
|
+
Q(
|
|
1176
|
+
s,
|
|
1177
|
+
(l) => {
|
|
1178
|
+
l && pt(i.formId, l, d);
|
|
823
1179
|
},
|
|
824
1180
|
{ immediate: !0 }
|
|
825
1181
|
);
|
|
826
|
-
const
|
|
827
|
-
() =>
|
|
1182
|
+
const c = f(
|
|
1183
|
+
() => s.value ?? {
|
|
828
1184
|
formId: i.formId,
|
|
829
1185
|
submission: { kind: "lead" },
|
|
830
1186
|
fields: []
|
|
831
1187
|
}
|
|
832
|
-
),
|
|
833
|
-
|
|
834
|
-
() =>
|
|
835
|
-
() =>
|
|
1188
|
+
), n = Qe({ definition: c.value }), { play: h } = Ne();
|
|
1189
|
+
Q(
|
|
1190
|
+
() => c.value,
|
|
1191
|
+
() => n.reset()
|
|
836
1192
|
);
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
)
|
|
842
|
-
(
|
|
1193
|
+
const x = f(() => {
|
|
1194
|
+
const l = i.apiBase ?? me?.VITE_DCS_PUBLIC_API ?? "";
|
|
1195
|
+
return l || L();
|
|
1196
|
+
});
|
|
1197
|
+
function L() {
|
|
1198
|
+
if (typeof window > "u") return "";
|
|
1199
|
+
const l = window.location.hostname.toLowerCase();
|
|
1200
|
+
return l === "localhost" || l === "127.0.0.1" || l === "::1" ? "/api/v1" : "https://api.duffcloudservices.com/api/v1";
|
|
1201
|
+
}
|
|
1202
|
+
const S = f(
|
|
1203
|
+
() => i.siteSlug ?? me?.VITE_DCS_SITE_SLUG ?? ""
|
|
1204
|
+
), q = f(
|
|
1205
|
+
() => c.value.submitLabel ?? "Send"
|
|
843
1206
|
);
|
|
844
|
-
function M(
|
|
845
|
-
switch (
|
|
1207
|
+
function M(l) {
|
|
1208
|
+
switch (l.type) {
|
|
846
1209
|
case "text":
|
|
847
1210
|
case "email":
|
|
848
1211
|
case "tel":
|
|
849
|
-
return
|
|
1212
|
+
return ce;
|
|
850
1213
|
case "textarea":
|
|
851
|
-
return
|
|
1214
|
+
return kt;
|
|
852
1215
|
case "select":
|
|
853
1216
|
case "multiselect":
|
|
854
|
-
return
|
|
1217
|
+
return It;
|
|
855
1218
|
case "radio":
|
|
856
|
-
return
|
|
1219
|
+
return At;
|
|
857
1220
|
case "checkbox-group":
|
|
858
|
-
return
|
|
1221
|
+
return Ot;
|
|
859
1222
|
case "checkbox":
|
|
860
|
-
return
|
|
1223
|
+
return zt;
|
|
861
1224
|
case "date":
|
|
862
|
-
return
|
|
1225
|
+
return Nt;
|
|
863
1226
|
case "file":
|
|
864
|
-
return
|
|
1227
|
+
return ei;
|
|
865
1228
|
case "hidden":
|
|
866
|
-
return
|
|
1229
|
+
return ii;
|
|
867
1230
|
case "section-heading":
|
|
868
|
-
return
|
|
1231
|
+
return ai;
|
|
869
1232
|
case "html-block":
|
|
870
|
-
return
|
|
1233
|
+
return li;
|
|
871
1234
|
default:
|
|
872
|
-
return
|
|
1235
|
+
return ce;
|
|
873
1236
|
}
|
|
874
1237
|
}
|
|
875
|
-
const
|
|
1238
|
+
const z = A(null);
|
|
876
1239
|
t({
|
|
877
|
-
values:
|
|
878
|
-
errors:
|
|
879
|
-
validateAll:
|
|
880
|
-
collectSubmissionValues:
|
|
881
|
-
reset:
|
|
1240
|
+
values: n.values,
|
|
1241
|
+
errors: n.errors,
|
|
1242
|
+
validateAll: n.validateAll,
|
|
1243
|
+
collectSubmissionValues: n.collectSubmissionValues,
|
|
1244
|
+
reset: n.reset
|
|
882
1245
|
});
|
|
883
|
-
async function
|
|
884
|
-
if (
|
|
885
|
-
if (!
|
|
886
|
-
|
|
1246
|
+
async function B(l) {
|
|
1247
|
+
if (l.preventDefault(), !s.value) return;
|
|
1248
|
+
if (!n.validateAll()) {
|
|
1249
|
+
o("validation-error", n.errors.value);
|
|
887
1250
|
return;
|
|
888
1251
|
}
|
|
889
|
-
|
|
890
|
-
const
|
|
1252
|
+
n.submitting.value = !0, n.submitError.value = null;
|
|
1253
|
+
const g = {
|
|
891
1254
|
formId: i.formId,
|
|
892
|
-
values:
|
|
1255
|
+
values: n.collectSubmissionValues(),
|
|
893
1256
|
captchaToken: i.captchaToken
|
|
894
1257
|
};
|
|
895
1258
|
try {
|
|
896
|
-
const
|
|
897
|
-
apiBase:
|
|
898
|
-
siteSlug:
|
|
899
|
-
payload:
|
|
1259
|
+
const P = await Xe({
|
|
1260
|
+
apiBase: x.value,
|
|
1261
|
+
siteSlug: S.value,
|
|
1262
|
+
payload: g
|
|
900
1263
|
});
|
|
901
|
-
|
|
902
|
-
} catch (
|
|
903
|
-
const
|
|
904
|
-
|
|
1264
|
+
n.submitted.value = !0, h("notification.success"), o("submit-success", P);
|
|
1265
|
+
} catch (P) {
|
|
1266
|
+
const O = P;
|
|
1267
|
+
n.submitError.value = O.error?.message ?? "Submission failed", o("submit-error", O);
|
|
905
1268
|
} finally {
|
|
906
|
-
|
|
1269
|
+
n.submitting.value = !1;
|
|
907
1270
|
}
|
|
908
1271
|
}
|
|
909
|
-
return
|
|
910
|
-
|
|
1272
|
+
return Pe(() => {
|
|
1273
|
+
s.value || console.warn(
|
|
911
1274
|
`[@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
1275
|
);
|
|
913
|
-
}), (
|
|
1276
|
+
}), (l, F) => s.value ? y(n).submitted.value ? (m(), p("div", {
|
|
914
1277
|
key: 1,
|
|
915
1278
|
class: "dcs-form dcs-form--success",
|
|
916
1279
|
"data-form-key": e.formId
|
|
917
1280
|
}, [
|
|
918
|
-
|
|
919
|
-
|
|
1281
|
+
V(l.$slots, "success", { definition: s.value }, () => [
|
|
1282
|
+
v("p", null, w(s.value.successMessage ?? "Thanks — we received your message."), 1)
|
|
920
1283
|
])
|
|
921
|
-
], 8,
|
|
1284
|
+
], 8, ui)) : (m(), p("form", {
|
|
922
1285
|
key: 2,
|
|
923
1286
|
ref_key: "formEl",
|
|
924
|
-
ref:
|
|
1287
|
+
ref: z,
|
|
925
1288
|
class: "dcs-form",
|
|
926
1289
|
"data-form-key": e.formId,
|
|
927
1290
|
novalidate: "",
|
|
928
|
-
onSubmit:
|
|
1291
|
+
onSubmit: B
|
|
929
1292
|
}, [
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
current:
|
|
934
|
-
total:
|
|
935
|
-
step:
|
|
1293
|
+
V(l.$slots, "header", { definition: s.value }),
|
|
1294
|
+
y(n).steps.value ? (m(), p("div", mi, [
|
|
1295
|
+
V(l.$slots, "progress", {
|
|
1296
|
+
current: y(n).currentStepIndex.value,
|
|
1297
|
+
total: y(n).steps.value.length,
|
|
1298
|
+
step: y(n).currentStep.value
|
|
936
1299
|
}, () => [
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
], 64)) :
|
|
1300
|
+
v("p", null, [
|
|
1301
|
+
X(" Step " + w(y(n).currentStepIndex.value + 1) + " of " + w(y(n).steps.value.length) + " ", 1),
|
|
1302
|
+
y(n).currentStep.value ? (m(), p(U, { key: 0 }, [
|
|
1303
|
+
X(" — " + w(y(n).currentStep.value.title), 1)
|
|
1304
|
+
], 64)) : I("", !0)
|
|
942
1305
|
])
|
|
943
1306
|
])
|
|
944
|
-
])) :
|
|
945
|
-
|
|
946
|
-
(
|
|
947
|
-
key:
|
|
948
|
-
field:
|
|
949
|
-
"attachment-policy":
|
|
950
|
-
"model-value":
|
|
951
|
-
error:
|
|
952
|
-
"onUpdate:modelValue": (
|
|
953
|
-
onBlur: (
|
|
1307
|
+
])) : I("", !0),
|
|
1308
|
+
v("div", fi, [
|
|
1309
|
+
(m(!0), p(U, null, W(y(n).visibleFields.value, (g) => (m(), j(Le(M(g)), {
|
|
1310
|
+
key: g.id,
|
|
1311
|
+
field: g,
|
|
1312
|
+
"attachment-policy": c.value.attachmentPolicy,
|
|
1313
|
+
"model-value": y(n).values[g.id],
|
|
1314
|
+
error: y(n).errors.value[g.id],
|
|
1315
|
+
"onUpdate:modelValue": (P) => y(n).setValue(g.id, P),
|
|
1316
|
+
onBlur: (P) => y(n).touch(g.id)
|
|
954
1317
|
}, null, 40, ["field", "attachment-policy", "model-value", "error", "onUpdate:modelValue", "onBlur"]))), 128))
|
|
955
1318
|
]),
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
isFirstStep:
|
|
960
|
-
isLastStep:
|
|
961
|
-
submitting:
|
|
962
|
-
prev:
|
|
963
|
-
next:
|
|
1319
|
+
y(n).submitError.value ? (m(), p("div", pi, w(y(n).submitError.value), 1)) : I("", !0),
|
|
1320
|
+
v("div", hi, [
|
|
1321
|
+
V(l.$slots, "actions", {
|
|
1322
|
+
isFirstStep: y(n).isFirstStep.value,
|
|
1323
|
+
isLastStep: y(n).isLastStep.value,
|
|
1324
|
+
submitting: y(n).submitting.value,
|
|
1325
|
+
prev: y(n).prev,
|
|
1326
|
+
next: y(n).next
|
|
964
1327
|
}, () => [
|
|
965
|
-
|
|
1328
|
+
y(n).steps.value && !y(n).isFirstStep.value ? (m(), p("button", {
|
|
966
1329
|
key: 0,
|
|
967
1330
|
type: "button",
|
|
968
1331
|
class: "dcs-form__btn dcs-form__btn--prev",
|
|
969
|
-
onClick:
|
|
970
|
-
}, " Previous ")) :
|
|
971
|
-
|
|
1332
|
+
onClick: F[0] || (F[0] = (g) => y(n).prev())
|
|
1333
|
+
}, " Previous ")) : I("", !0),
|
|
1334
|
+
y(n).steps.value && !y(n).isLastStep.value ? (m(), p("button", {
|
|
972
1335
|
key: 1,
|
|
973
1336
|
type: "button",
|
|
974
1337
|
class: "dcs-form__btn dcs-form__btn--next",
|
|
975
|
-
onClick:
|
|
976
|
-
}, " Next ")) :
|
|
977
|
-
!
|
|
1338
|
+
onClick: F[1] || (F[1] = (g) => y(n).next())
|
|
1339
|
+
}, " Next ")) : I("", !0),
|
|
1340
|
+
!y(n).steps.value || y(n).isLastStep.value ? (m(), p("button", {
|
|
978
1341
|
key: 2,
|
|
979
1342
|
type: "submit",
|
|
980
1343
|
class: "dcs-form__btn dcs-form__btn--submit",
|
|
981
|
-
disabled:
|
|
982
|
-
},
|
|
1344
|
+
disabled: y(n).submitting.value
|
|
1345
|
+
}, w(y(n).submitting.value ? "Sending…" : q.value), 9, yi)) : I("", !0)
|
|
983
1346
|
])
|
|
984
1347
|
])
|
|
985
|
-
], 40,
|
|
1348
|
+
], 40, ci)) : (m(), p("div", {
|
|
986
1349
|
key: 0,
|
|
987
1350
|
class: "dcs-form dcs-form--missing",
|
|
988
1351
|
"data-form-key": e.formId
|
|
989
1352
|
}, [
|
|
990
|
-
|
|
991
|
-
|
|
1353
|
+
V(l.$slots, "missing", { formId: e.formId }, () => [
|
|
1354
|
+
v("p", null, "Form “" + w(e.formId) + "” is not configured.", 1)
|
|
992
1355
|
])
|
|
993
|
-
], 8,
|
|
1356
|
+
], 8, di));
|
|
994
1357
|
}
|
|
995
|
-
}),
|
|
1358
|
+
}), Pi = {
|
|
996
1359
|
contact: {
|
|
997
1360
|
label: "Contact",
|
|
998
1361
|
description: "Canonical contact form with standard contact fields."
|
|
@@ -1009,7 +1372,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
1009
1372
|
label: "Custom / free-form",
|
|
1010
1373
|
description: "Flexible starter that stays fully editable for questionnaires and bespoke intake."
|
|
1011
1374
|
}
|
|
1012
|
-
},
|
|
1375
|
+
}, gi = () => [
|
|
1013
1376
|
{ id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
|
|
1014
1377
|
{ id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
|
|
1015
1378
|
{ id: "phone", type: "tel", role: "contact-phone", label: "Phone", width: "half" },
|
|
@@ -1023,7 +1386,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
1023
1386
|
width: "full",
|
|
1024
1387
|
validation: { minLength: 10, maxLength: 2e3 }
|
|
1025
1388
|
}
|
|
1026
|
-
],
|
|
1389
|
+
], bi = () => [
|
|
1027
1390
|
{ id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
|
|
1028
1391
|
{ id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
|
|
1029
1392
|
{ id: "phone", type: "tel", role: "contact-phone", label: "Phone", required: !0, width: "half" },
|
|
@@ -1047,7 +1410,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
1047
1410
|
width: "full",
|
|
1048
1411
|
validation: { accept: ["image/*"] }
|
|
1049
1412
|
}
|
|
1050
|
-
],
|
|
1413
|
+
], vi = () => [
|
|
1051
1414
|
{ id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
|
|
1052
1415
|
{ id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
|
|
1053
1416
|
{ id: "phone", type: "tel", role: "contact-phone", label: "Phone", width: "half" },
|
|
@@ -1075,7 +1438,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
1075
1438
|
]
|
|
1076
1439
|
}
|
|
1077
1440
|
}
|
|
1078
|
-
],
|
|
1441
|
+
], Fi = () => [
|
|
1079
1442
|
{ id: "name", type: "text", label: "Full name", required: !0, width: "full" },
|
|
1080
1443
|
{ id: "email", type: "email", label: "Email", required: !0, width: "half" },
|
|
1081
1444
|
{ id: "phone", type: "tel", label: "Phone", width: "half" },
|
|
@@ -1088,7 +1451,7 @@ const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
|
1088
1451
|
validation: { minLength: 10, maxLength: 2e3 }
|
|
1089
1452
|
}
|
|
1090
1453
|
];
|
|
1091
|
-
function
|
|
1454
|
+
function Li(e, t = {}) {
|
|
1092
1455
|
switch (e ?? "custom") {
|
|
1093
1456
|
case "contact":
|
|
1094
1457
|
return {
|
|
@@ -1100,7 +1463,7 @@ function Bt(e, t = {}) {
|
|
|
1100
1463
|
kind: "lead",
|
|
1101
1464
|
...t.leadCategory ? { category: t.leadCategory } : {}
|
|
1102
1465
|
},
|
|
1103
|
-
fields:
|
|
1466
|
+
fields: gi()
|
|
1104
1467
|
};
|
|
1105
1468
|
case "revenue-contractor":
|
|
1106
1469
|
return {
|
|
@@ -1118,7 +1481,7 @@ function Bt(e, t = {}) {
|
|
|
1118
1481
|
maxFiles: 5,
|
|
1119
1482
|
accept: ["image/*"]
|
|
1120
1483
|
},
|
|
1121
|
-
fields:
|
|
1484
|
+
fields: bi()
|
|
1122
1485
|
};
|
|
1123
1486
|
case "resume-submission":
|
|
1124
1487
|
return {
|
|
@@ -1140,7 +1503,7 @@ function Bt(e, t = {}) {
|
|
|
1140
1503
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
1141
1504
|
]
|
|
1142
1505
|
},
|
|
1143
|
-
fields:
|
|
1506
|
+
fields: vi()
|
|
1144
1507
|
};
|
|
1145
1508
|
default:
|
|
1146
1509
|
return {
|
|
@@ -1152,35 +1515,35 @@ function Bt(e, t = {}) {
|
|
|
1152
1515
|
kind: "lead",
|
|
1153
1516
|
...t.leadCategory ? { category: t.leadCategory } : {}
|
|
1154
1517
|
},
|
|
1155
|
-
fields:
|
|
1518
|
+
fields: Fi()
|
|
1156
1519
|
};
|
|
1157
1520
|
}
|
|
1158
1521
|
}
|
|
1159
1522
|
export {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1523
|
+
qi as DcsForm,
|
|
1524
|
+
zt as DcsFormCheckbox,
|
|
1525
|
+
Ot as DcsFormCheckboxGroup,
|
|
1526
|
+
Nt as DcsFormDate,
|
|
1527
|
+
E as DcsFormFieldWrapper,
|
|
1528
|
+
ei as DcsFormFile,
|
|
1529
|
+
ii as DcsFormHidden,
|
|
1530
|
+
li as DcsFormHtmlBlock,
|
|
1531
|
+
At as DcsFormRadio,
|
|
1532
|
+
ai as DcsFormSection,
|
|
1533
|
+
It as DcsFormSelect,
|
|
1534
|
+
ce as DcsFormText,
|
|
1535
|
+
kt as DcsFormTextarea,
|
|
1536
|
+
Pi as STANDARD_FORM_PRESET_META,
|
|
1537
|
+
Li as buildStandardFormDefinition,
|
|
1538
|
+
He as hasErrors,
|
|
1539
|
+
ne as isFieldVisible,
|
|
1540
|
+
ht as loadFormDefinitions,
|
|
1541
|
+
ki as parseFormYaml,
|
|
1542
|
+
Xe as submitFormValues,
|
|
1543
|
+
Qe as useDcsForm,
|
|
1544
|
+
Ue as validateField,
|
|
1545
|
+
We as validateForm,
|
|
1546
|
+
ft as validateFormDefinition,
|
|
1547
|
+
pt as warnIfInvalid
|
|
1185
1548
|
};
|
|
1186
1549
|
//# sourceMappingURL=index.js.map
|