@duffcloudservices/site-forms 0.1.3 → 0.2.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/README.md +4 -0
- package/dist/DcsForm.vue.d.ts +7 -1
- package/dist/composables/useFormValidation.d.ts +2 -2
- package/dist/fields/DcsFormFile.vue.d.ts +8 -5
- package/dist/index.js +591 -479
- package/dist/index.js.map +1 -1
- package/dist/site-forms.css +1 -1
- package/package.json +1 -1
- package/src/DcsForm.vue +9 -0
- package/src/__tests__/fields.test.ts +18 -0
- package/src/__tests__/submission.test.ts +26 -0
- package/src/__tests__/validation.test.ts +21 -1
- package/src/composables/useFormSubmission.ts +18 -4
- package/src/composables/useFormValidation.ts +65 -14
- package/src/fields/DcsFormFile.vue +127 -6
- package/src/style.css +96 -0
package/dist/index.js
CHANGED
|
@@ -1,280 +1,305 @@
|
|
|
1
|
-
import { reactive as X, ref as
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
function
|
|
1
|
+
import { reactive as X, ref as V, computed as p, defineComponent as I, createElementBlock as m, openBlock as u, normalizeClass as Z, renderSlot as P, createCommentVNode as k, createTextVNode as K, toDisplayString as F, createBlock as A, withCtx as j, createElementVNode as b, Fragment as T, renderList as z, watch as W, onBeforeUnmount as ee, onMounted as te, unref as f, resolveDynamicComponent as ie } from "vue";
|
|
2
|
+
import re from "ajv";
|
|
3
|
+
import ne from "ajv-formats";
|
|
4
|
+
import Y from "js-yaml";
|
|
5
|
+
function U(e, t) {
|
|
6
6
|
return e.visibleIf ? t[e.visibleIf.fieldId] === e.visibleIf.equals : !0;
|
|
7
7
|
}
|
|
8
|
-
function
|
|
8
|
+
function oe(e, t, n) {
|
|
9
9
|
if (e.type === "section-heading" || e.type === "html-block" || e.type === "hidden")
|
|
10
10
|
return;
|
|
11
|
-
const i = typeof t == "string" ? t.trim() : t,
|
|
12
|
-
if (e.required &&
|
|
11
|
+
const i = typeof t == "string" ? t.trim() : t, s = t == null || i === "" || Array.isArray(t) && t.length === 0;
|
|
12
|
+
if (e.required && s)
|
|
13
13
|
return `${e.label} is required`;
|
|
14
|
-
if (
|
|
14
|
+
if (s) return;
|
|
15
15
|
if (e.type === "email" && typeof i == "string" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(i))
|
|
16
16
|
return `${e.label} must be a valid email address`;
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
17
|
+
const o = e.validation ?? {};
|
|
18
|
+
if (typeof i == "string") {
|
|
19
|
+
if (o.minLength != null && i.length < o.minLength)
|
|
20
|
+
return `${e.label} must be at least ${o.minLength} characters`;
|
|
21
|
+
if (o.maxLength != null && i.length > o.maxLength)
|
|
22
|
+
return `${e.label} must be at most ${o.maxLength} characters`;
|
|
23
|
+
if (o.regex)
|
|
24
|
+
try {
|
|
25
|
+
if (!new RegExp(o.regex).test(i))
|
|
26
|
+
return `${e.label} is invalid`;
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (typeof t == "number") {
|
|
31
|
+
if (o.min != null && t < o.min)
|
|
32
|
+
return `${e.label} must be at least ${o.min}`;
|
|
33
|
+
if (o.max != null && t > o.max)
|
|
34
|
+
return `${e.label} must be at most ${o.max}`;
|
|
35
|
+
}
|
|
36
|
+
if (e.type === "file") {
|
|
37
|
+
const c = le(t), a = n?.maxFiles, l = n?.maxFileSizeBytes, h = o.accept && o.accept.length > 0 ? o.accept : n?.accept;
|
|
38
|
+
if (a != null && c.length > a)
|
|
39
|
+
return `${e.label} accepts up to ${a} file${a === 1 ? "" : "s"}`;
|
|
40
|
+
if (l != null) {
|
|
41
|
+
const r = c.find((v) => v.size > l);
|
|
42
|
+
if (r)
|
|
43
|
+
return `${r.name} exceeds the ${ue(l)} limit`;
|
|
36
44
|
}
|
|
37
|
-
if (
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
return `${
|
|
45
|
+
if (h && h.length > 0) {
|
|
46
|
+
const r = c.find((v) => !de(v, h));
|
|
47
|
+
if (r)
|
|
48
|
+
return `${r.name} must be one of: ${h.join(", ")}`;
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
|
-
function
|
|
45
|
-
const
|
|
46
|
-
for (const
|
|
47
|
-
if (
|
|
48
|
-
const
|
|
49
|
-
|
|
52
|
+
function se(e, t, n) {
|
|
53
|
+
const i = {}, s = n ? new Set(n) : null;
|
|
54
|
+
for (const o of e.fields) {
|
|
55
|
+
if (s && !s.has(o.id) || !U(o, t)) continue;
|
|
56
|
+
const c = oe(o, t[o.id], e.attachmentPolicy);
|
|
57
|
+
c && (i[o.id] = c);
|
|
50
58
|
}
|
|
51
|
-
return
|
|
59
|
+
return i;
|
|
52
60
|
}
|
|
53
|
-
function
|
|
61
|
+
function ae(e) {
|
|
54
62
|
return Object.values(e).some((t) => !!t);
|
|
55
63
|
}
|
|
56
|
-
function
|
|
64
|
+
function le(e) {
|
|
65
|
+
return typeof File > "u" ? [] : e instanceof File ? [e] : Array.isArray(e) ? e.filter((t) => t instanceof File) : [];
|
|
66
|
+
}
|
|
67
|
+
function de(e, t) {
|
|
68
|
+
const n = e.name.toLowerCase(), i = e.type.toLowerCase();
|
|
69
|
+
return t.some((s) => {
|
|
70
|
+
const o = s.trim().toLowerCase();
|
|
71
|
+
return o === "" ? !1 : o.startsWith(".") ? n.endsWith(o) : o.endsWith("/*") ? i.startsWith(o.slice(0, -1)) : i === o;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function ue(e) {
|
|
75
|
+
return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${Math.round(e / 1024)} KB` : `${Math.round(e / (1024 * 1024))} MB`;
|
|
76
|
+
}
|
|
77
|
+
function H(e) {
|
|
57
78
|
const t = {};
|
|
58
|
-
for (const
|
|
59
|
-
|
|
79
|
+
for (const n of e.fields)
|
|
80
|
+
n.defaultValue !== void 0 ? t[n.id] = n.defaultValue : n.type === "checkbox" ? t[n.id] = !1 : n.type === "multiselect" || n.type === "checkbox-group" ? t[n.id] = [] : t[n.id] = "";
|
|
60
81
|
return t;
|
|
61
82
|
}
|
|
62
|
-
function
|
|
63
|
-
const { definition: t } = e,
|
|
83
|
+
function ce(e) {
|
|
84
|
+
const { definition: t } = e, n = X(H(t)), i = V({}), s = V({}), o = V(!1), c = V(!1), a = V(null), l = V(0), h = p(() => t.fields), r = p(
|
|
64
85
|
() => t.steps && t.steps.length > 0 ? t.steps : null
|
|
65
|
-
),
|
|
66
|
-
const
|
|
67
|
-
return
|
|
68
|
-
}), $ =
|
|
69
|
-
const
|
|
70
|
-
if (!
|
|
71
|
-
const
|
|
72
|
-
return t.fields.filter((
|
|
73
|
-
}),
|
|
74
|
-
const
|
|
75
|
-
return !
|
|
76
|
-
}),
|
|
77
|
-
() => $.value.filter((
|
|
86
|
+
), v = p(() => {
|
|
87
|
+
const y = r.value;
|
|
88
|
+
return y ? y[l.value] ?? null : null;
|
|
89
|
+
}), $ = p(() => {
|
|
90
|
+
const y = v.value;
|
|
91
|
+
if (!y) return t.fields;
|
|
92
|
+
const S = new Set(y.fieldIds);
|
|
93
|
+
return t.fields.filter((_) => S.has(_.id));
|
|
94
|
+
}), q = p(() => l.value === 0), M = p(() => {
|
|
95
|
+
const y = r.value;
|
|
96
|
+
return !y || l.value >= y.length - 1;
|
|
97
|
+
}), O = p(
|
|
98
|
+
() => $.value.filter((y) => U(y, n))
|
|
78
99
|
);
|
|
79
|
-
function
|
|
80
|
-
|
|
100
|
+
function R(y, S) {
|
|
101
|
+
n[y] = S, i.value[y] && (i.value = { ...i.value, [y]: void 0 });
|
|
81
102
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
103
|
+
function w(y) {
|
|
104
|
+
s.value = { ...s.value, [y]: !0 };
|
|
84
105
|
}
|
|
85
|
-
function
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
const
|
|
89
|
-
for (const
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
function L(y) {
|
|
107
|
+
const S = se(t, n, y);
|
|
108
|
+
if (y) {
|
|
109
|
+
const _ = { ...i.value };
|
|
110
|
+
for (const N of y)
|
|
111
|
+
_[N] = S[N];
|
|
112
|
+
i.value = _;
|
|
92
113
|
} else
|
|
93
|
-
|
|
94
|
-
return !
|
|
114
|
+
i.value = S;
|
|
115
|
+
return !ae(i.value);
|
|
95
116
|
}
|
|
96
|
-
function
|
|
97
|
-
const
|
|
98
|
-
return
|
|
117
|
+
function d() {
|
|
118
|
+
const y = v.value?.fieldIds;
|
|
119
|
+
return L(y);
|
|
99
120
|
}
|
|
100
|
-
function
|
|
101
|
-
return
|
|
121
|
+
function g() {
|
|
122
|
+
return L();
|
|
102
123
|
}
|
|
103
|
-
function
|
|
104
|
-
return !
|
|
124
|
+
function x() {
|
|
125
|
+
return !r.value || !d() ? !1 : l.value < r.value.length - 1 ? (l.value++, !0) : !1;
|
|
105
126
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
127
|
+
function C() {
|
|
128
|
+
l.value > 0 && l.value--;
|
|
108
129
|
}
|
|
109
|
-
function
|
|
110
|
-
const
|
|
111
|
-
for (const
|
|
112
|
-
delete
|
|
113
|
-
Object.assign(
|
|
130
|
+
function E() {
|
|
131
|
+
const y = H(t);
|
|
132
|
+
for (const S of Object.keys(n))
|
|
133
|
+
delete n[S];
|
|
134
|
+
Object.assign(n, y), i.value = {}, s.value = {}, o.value = !1, c.value = !1, a.value = null, l.value = 0;
|
|
114
135
|
}
|
|
115
136
|
function Q() {
|
|
116
|
-
const
|
|
117
|
-
for (const
|
|
118
|
-
|
|
119
|
-
return
|
|
137
|
+
const y = {};
|
|
138
|
+
for (const S of t.fields)
|
|
139
|
+
S.type === "section-heading" || S.type === "html-block" || U(S, n) && (y[S.id] = n[S.id]);
|
|
140
|
+
return y;
|
|
120
141
|
}
|
|
121
142
|
return {
|
|
122
|
-
values:
|
|
123
|
-
errors:
|
|
124
|
-
touched:
|
|
125
|
-
submitting:
|
|
126
|
-
submitted:
|
|
127
|
-
submitError:
|
|
128
|
-
fields:
|
|
129
|
-
steps:
|
|
130
|
-
currentStepIndex:
|
|
131
|
-
currentStep:
|
|
143
|
+
values: n,
|
|
144
|
+
errors: i,
|
|
145
|
+
touched: s,
|
|
146
|
+
submitting: o,
|
|
147
|
+
submitted: c,
|
|
148
|
+
submitError: a,
|
|
149
|
+
fields: h,
|
|
150
|
+
steps: r,
|
|
151
|
+
currentStepIndex: l,
|
|
152
|
+
currentStep: v,
|
|
132
153
|
currentStepFields: $,
|
|
133
|
-
isFirstStep:
|
|
134
|
-
isLastStep:
|
|
135
|
-
visibleFields:
|
|
136
|
-
setValue:
|
|
137
|
-
touch:
|
|
138
|
-
validateCurrentScope:
|
|
139
|
-
validateAll:
|
|
140
|
-
next:
|
|
141
|
-
prev:
|
|
142
|
-
reset:
|
|
154
|
+
isFirstStep: q,
|
|
155
|
+
isLastStep: M,
|
|
156
|
+
visibleFields: O,
|
|
157
|
+
setValue: R,
|
|
158
|
+
touch: w,
|
|
159
|
+
validateCurrentScope: d,
|
|
160
|
+
validateAll: g,
|
|
161
|
+
next: x,
|
|
162
|
+
prev: C,
|
|
163
|
+
reset: E,
|
|
143
164
|
collectSubmissionValues: Q
|
|
144
165
|
};
|
|
145
166
|
}
|
|
146
|
-
async function
|
|
147
|
-
const { apiBase: t, siteSlug:
|
|
148
|
-
|
|
149
|
-
)}/forms/${encodeURIComponent(
|
|
150
|
-
|
|
151
|
-
)
|
|
152
|
-
let a, o;
|
|
153
|
-
for (let m = 0; m <= n; m++)
|
|
167
|
+
async function me(e) {
|
|
168
|
+
const { apiBase: t, siteSlug: n, payload: i } = e, s = e.retries ?? 1, o = e.fetchImpl ?? fetch, c = `${t.replace(/\/$/, "")}/sites/${encodeURIComponent(
|
|
169
|
+
n
|
|
170
|
+
)}/forms/${encodeURIComponent(i.formId)}/submissions`, a = Object.values(i.values).some(pe);
|
|
171
|
+
let l, h;
|
|
172
|
+
for (let r = 0; r <= s; r++)
|
|
154
173
|
try {
|
|
155
|
-
const
|
|
174
|
+
const v = a ? { method: "POST", body: fe(i) } : {
|
|
156
175
|
method: "POST",
|
|
157
176
|
headers: { "Content-Type": "application/json" },
|
|
158
|
-
body: JSON.stringify(
|
|
159
|
-
}, $ = await
|
|
160
|
-
if (
|
|
161
|
-
const
|
|
162
|
-
return { payload:
|
|
177
|
+
body: JSON.stringify(i)
|
|
178
|
+
}, $ = await o(c, v);
|
|
179
|
+
if (h = $.status, $.ok) {
|
|
180
|
+
const q = await he($);
|
|
181
|
+
return { payload: i, response: q };
|
|
163
182
|
}
|
|
164
183
|
if ($.status < 500) {
|
|
165
|
-
const
|
|
184
|
+
const q = await ye($);
|
|
166
185
|
throw {
|
|
167
|
-
payload:
|
|
186
|
+
payload: i,
|
|
168
187
|
status: $.status,
|
|
169
|
-
error: new Error(`Submission failed (${$.status}): ${
|
|
188
|
+
error: new Error(`Submission failed (${$.status}): ${q}`)
|
|
170
189
|
};
|
|
171
190
|
}
|
|
172
|
-
|
|
173
|
-
} catch (
|
|
174
|
-
if (
|
|
175
|
-
throw
|
|
176
|
-
|
|
191
|
+
l = new Error(`Server error ${$.status}`);
|
|
192
|
+
} catch (v) {
|
|
193
|
+
if (v && typeof v == "object" && "payload" in v && "error" in v)
|
|
194
|
+
throw v;
|
|
195
|
+
l = v;
|
|
177
196
|
}
|
|
178
197
|
throw {
|
|
179
|
-
payload:
|
|
180
|
-
status:
|
|
181
|
-
error:
|
|
198
|
+
payload: i,
|
|
199
|
+
status: h,
|
|
200
|
+
error: l ?? new Error("Submission failed")
|
|
182
201
|
};
|
|
183
202
|
}
|
|
184
|
-
function
|
|
203
|
+
function fe(e) {
|
|
185
204
|
const t = new FormData();
|
|
186
205
|
t.append("formId", e.formId), e.captchaToken && t.append("captchaToken", e.captchaToken);
|
|
187
|
-
for (const [
|
|
188
|
-
if (
|
|
189
|
-
if (
|
|
190
|
-
t.append(`values[${
|
|
191
|
-
else if (Array.isArray(
|
|
192
|
-
for (const
|
|
206
|
+
for (const [n, i] of Object.entries(e.values))
|
|
207
|
+
if (i != null)
|
|
208
|
+
if (typeof File < "u" && i instanceof File)
|
|
209
|
+
t.append(`values[${n}]`, i);
|
|
210
|
+
else if (Array.isArray(i) && i.some((s) => typeof File < "u" && s instanceof File))
|
|
211
|
+
for (const s of i)
|
|
212
|
+
typeof File < "u" && s instanceof File && t.append(`values[${n}][]`, s);
|
|
213
|
+
else if (Array.isArray(i))
|
|
214
|
+
for (const s of i) t.append(`values[${n}][]`, String(s));
|
|
193
215
|
else
|
|
194
|
-
t.append(`values[${
|
|
216
|
+
t.append(`values[${n}]`, String(i));
|
|
195
217
|
return t;
|
|
196
218
|
}
|
|
197
|
-
|
|
219
|
+
function pe(e) {
|
|
220
|
+
return typeof File > "u" ? !1 : e instanceof File ? !0 : Array.isArray(e) && e.some((t) => t instanceof File);
|
|
221
|
+
}
|
|
222
|
+
async function he(e) {
|
|
198
223
|
try {
|
|
199
224
|
return await e.json();
|
|
200
225
|
} catch {
|
|
201
226
|
return null;
|
|
202
227
|
}
|
|
203
228
|
}
|
|
204
|
-
async function
|
|
229
|
+
async function ye(e) {
|
|
205
230
|
try {
|
|
206
231
|
return await e.text();
|
|
207
232
|
} catch {
|
|
208
233
|
return "";
|
|
209
234
|
}
|
|
210
235
|
}
|
|
211
|
-
const me = "http://json-schema.org/draft-07/schema#", fe = "https://duffcloudservices.com/schemas/form-definition.schema.json", pe = "PortalFormDefinition", he = "object", ye = "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", be = ["formId", "submission", "fields"], ge = { 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." } }, ve = /* @__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"}}}}}'), Fe = {
|
|
212
|
-
$schema:
|
|
213
|
-
$id:
|
|
214
|
-
title:
|
|
215
|
-
type:
|
|
216
|
-
description:
|
|
217
|
-
required:
|
|
218
|
-
properties:
|
|
219
|
-
definitions:
|
|
236
|
+
const be = "http://json-schema.org/draft-07/schema#", ge = "https://duffcloudservices.com/schemas/form-definition.schema.json", ve = "PortalFormDefinition", Fe = "object", xe = "Portable form definition. This schema is the source of truth for\nboth the portal CRUD APIs and the `.dcs/forms/<formId>.yaml`\nfiles consumed by customer-site builds via `<DcsForm/>`.\n", $e = ["formId", "submission", "fields"], we = { formId: { type: "string", description: "Kebab-case identifier, unique per site.", pattern: "^[a-z0-9][a-z0-9-]*$", minLength: 1, maxLength: 80, example: "contact" }, formKind: { $ref: "#/definitions/PortalFormKind" }, submitLabel: { type: "string", description: 'Label for the submit button. Defaults to "Send" client-side.', maxLength: 80, example: "Send message" }, successMessage: { type: "string", description: "Confirmation message shown after a successful submission.", maxLength: 2e3, example: "Thanks — we'll be in touch shortly." }, submission: { $ref: "#/definitions/PortalFormSubmissionConfig" }, attachmentPolicy: { $ref: "#/definitions/PortalFormAttachmentPolicy" }, steps: { type: "array", description: "Optional multi-step grouping. When omitted, fields render as a single page.", items: { $ref: "#/definitions/PortalFormStep" } }, fields: { type: "array", description: "Flat list of all fields in the form. When `steps` is present,\nevery field referenced from a step's `fieldIds` must exist here.\n", items: { $ref: "#/definitions/PortalFormField" } }, version: { type: "integer", minimum: 1, description: "Monotonically increasing version, bumped on each save." }, createdAt: { type: "string", format: "date-time", description: "Form creation timestamp." }, updatedAt: { type: "string", format: "date-time", description: "Last modification timestamp." } }, ke = /* @__PURE__ */ JSON.parse('{"PortalFormKind":{"type":"string","description":"High-level form kind used by the visual page editor and submission\\npipeline. `freeform` preserves fully editable questionnaires; the\\nstandard values provide predictable field roles and routing for common\\nsite forms.\\n","enum":["freeform","contact","revenue-contractor","resume-submission"],"default":"freeform"},"PortalFormFieldRole":{"type":"string","description":"Semantic role for a field inside a standard form. Free-form forms may\\nomit roles or use `custom`; standard forms use roles so submissions can\\nbe surfaced consistently as contact messages, notifications, revenue\\ncontractor inquiries, or resume submissions without locking the editor\\nout of label/help-text changes.\\n","enum":["contact-name","contact-email","contact-phone","contact-company","subject","message","summary","image-attachments","resume","consent","custom"]},"PortalFormAttachmentPolicy":{"type":"object","description":"Attachment expectations for standard forms. For\\n`revenue-contractor`, image attachments are expected so contractors can\\ninclude project photos. For `resume-submission`, the resume field\\nshould accept document uploads. Free-form questionnaires can omit this.\\n","properties":{"expected":{"type":"boolean","default":false,"description":"Whether the standard flow should prompt for attachments."},"required":{"type":"boolean","default":false,"description":"Whether at least one attachment is required."},"maxFiles":{"type":"integer","minimum":1,"maximum":20,"description":"Maximum number of files accepted.","example":5},"maxFileSizeBytes":{"type":"integer","minimum":1,"description":"Maximum size per file in bytes.","example":10485760},"accept":{"type":"array","description":"Accepted MIME types or extensions (for example `image/*`).","items":{"type":"string"}}}},"PublicSiteFormContact":{"type":"object","description":"Normalized contact identity supplied with a public form submission.","properties":{"name":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":255},"phone":{"type":"string","maxLength":32},"company":{"type":"string","maxLength":160}}},"PublicSiteFormAttachment":{"type":"object","description":"Metadata for an attachment associated with a public form submission.","required":["fileName"],"properties":{"fileName":{"type":"string","maxLength":255},"contentType":{"type":"string","maxLength":120},"sizeBytes":{"type":"integer","minimum":0},"storageUrl":{"type":"string","format":"uri","description":"Internal or pre-uploaded storage URL when the binary was uploaded separately."}}},"PublicSiteFormSubmissionRequest":{"type":"object","description":"JSON request for public managed-form submissions.","required":["values"],"properties":{"formKind":{"$ref":"#/definitions/PortalFormKind"},"pageSlug":{"type":"string","description":"Optional visual-editor page slug where the form was rendered."},"contact":{"$ref":"#/definitions/PublicSiteFormContact"},"subject":{"type":"string","maxLength":200,"description":"Contact subject or inquiry title."},"summary":{"type":"string","maxLength":4000,"description":"Project, request, or applicant summary for standard flows."},"message":{"type":"string","maxLength":4000,"description":"Free-text message supplied by the submitter."},"values":{"type":"object","additionalProperties":true,"description":"Field id to submitted value map for the attached PortalFormDefinition."},"attachments":{"type":"array","description":"Attachment metadata for JSON submissions.","items":{"$ref":"#/definitions/PublicSiteFormAttachment"}},"source":{"type":"string","maxLength":120,"description":"Capture source such as `site-contact-page` or `visual-page-editor`."},"consent":{"type":"boolean","description":"Whether the submitter consented to follow-up."}}},"PublicSiteFormMultipartSubmissionRequest":{"type":"object","description":"Multipart request for public managed-form submissions with binary uploads.","properties":{"formKind":{"$ref":"#/definitions/PortalFormKind"},"pageSlug":{"type":"string"},"values":{"type":"string","description":"JSON-encoded field id to submitted value map."},"name":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":255},"phone":{"type":"string","maxLength":32},"company":{"type":"string","maxLength":160},"subject":{"type":"string","maxLength":200},"summary":{"type":"string","maxLength":4000},"message":{"type":"string","maxLength":4000},"attachments":{"type":"array","description":"Image or supporting files for standard form submissions.","items":{"type":"string","format":"binary"}},"resume":{"type":"string","format":"binary","description":"Resume document for `resume-submission` standard forms."},"source":{"type":"string","maxLength":120},"consent":{"type":"boolean"}}},"PublicSiteFormSubmissionResponse":{"type":"object","required":["id","status","submittedAt"],"properties":{"id":{"type":"string","description":"Unique submission identifier."},"status":{"type":"string","enum":["submitted","accepted","queued"]},"submittedAt":{"type":"string","format":"date-time"},"message":{"type":"string","description":"Friendly acknowledgement shown to the submitter."},"contactMessageId":{"type":"string","nullable":true,"description":"Future portal contact-message identifier when surfaced in the portal."},"notificationQueued":{"type":"boolean","description":"Whether a portal notification/contact-message handoff was queued."}}},"PortalFormFieldType":{"type":"string","description":"Field type controlling input rendering and validation. Includes\\nlayout-only types (`section-heading`, `html-block`) that render\\ndecorative content rather than capturing values, and `hidden`\\nfor prefilled values not displayed to the user.\\n","enum":["text","email","tel","textarea","select","multiselect","radio","checkbox","checkbox-group","date","file","hidden","section-heading","html-block"]},"PortalFormFieldWidth":{"type":"string","description":"Layout hint for the field within its row.","enum":["full","half","third"]},"PortalFormFieldOption":{"type":"object","description":"A single option for select / radio / checkbox-group fields.","required":["value","label"],"properties":{"value":{"type":"string","description":"Submitted value for this option."},"label":{"type":"string","description":"Human-readable label shown to the user."}}},"PortalFormFieldValidation":{"type":"object","description":"Optional validation rules applied to a field\'s value.","properties":{"regex":{"type":"string","description":"ECMAScript-compatible pattern the value must match."},"minLength":{"type":"integer","minimum":0,"description":"Minimum string length (text-like fields only)."},"maxLength":{"type":"integer","minimum":0,"description":"Maximum string length (text-like fields only)."},"min":{"type":"number","description":"Minimum numeric / date value."},"max":{"type":"number","description":"Maximum numeric / date value."},"accept":{"type":"array","description":"Accepted MIME types or file extensions for `file` fields.","items":{"type":"string"}}}},"PortalFormFieldVisibleIf":{"type":"object","description":"Single-predicate visibility rule. The field is shown only when\\nthe referenced sibling field\'s value equals `equals`. Future\\nrevisions may extend this with AND / OR composition; clients\\nshould treat unknown extra properties as opaque.\\n","required":["fieldId","equals"],"properties":{"fieldId":{"type":"string","description":"ID of the sibling field whose value gates visibility."},"equals":{"description":"Value (string, number, or boolean) the sibling field must equal.","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}},"PortalFormField":{"type":"object","description":"A single field within a form definition.","required":["id","type","label"],"properties":{"id":{"type":"string","description":"Kebab-case identifier, unique within the form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"type":{"$ref":"#/definitions/PortalFormFieldType"},"role":{"$ref":"#/definitions/PortalFormFieldRole"},"label":{"type":"string","description":"Human-readable label rendered above the input.","minLength":1,"maxLength":200},"helpText":{"type":"string","description":"Optional helper / description text shown beneath the label.","maxLength":500},"placeholder":{"type":"string","description":"Optional placeholder shown inside empty inputs.","maxLength":200},"defaultValue":{"description":"Optional default value (string, number, boolean, or array of strings).","oneOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"array","items":{"type":"string"}}]},"required":{"type":"boolean","default":false,"description":"Whether the field must be supplied to submit the form."},"width":{"allOf":[{"$ref":"#/definitions/PortalFormFieldWidth"}],"default":"full"},"options":{"type":"array","description":"Options for `select`, `multiselect`, `radio`, `checkbox-group` fields.","items":{"$ref":"#/definitions/PortalFormFieldOption"}},"validation":{"$ref":"#/definitions/PortalFormFieldValidation"},"visibleIf":{"$ref":"#/definitions/PortalFormFieldVisibleIf"},"phi":{"type":"boolean","default":false,"description":"Marks the field as collecting Protected Health Information.\\nWhen true the value must never appear in notification emails\\nor logs and the owning site must be in the Medical category\\n(see `compliance.instructions.md`).\\n"},"html":{"type":"string","description":"Sanitized HTML body for `html-block` fields. Ignored for\\nother field types.\\n","maxLength":10000}}},"PortalFormStep":{"type":"object","description":"Optional grouping for multi-step (wizard-style) forms such as\\nthe KT Braun estate-planning questionnaires. When `steps` is\\nomitted on a form, all fields render as a single page.\\n","required":["id","title","fieldIds"],"properties":{"id":{"type":"string","description":"Kebab-case step identifier, unique within the form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"title":{"type":"string","description":"Step title shown in the wizard header.","minLength":1,"maxLength":200},"description":{"type":"string","description":"Optional descriptive text shown below the step title.","maxLength":1000},"fieldIds":{"type":"array","description":"Ordered list of field IDs that belong to this step.","items":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"}}}},"PortalFormSubmissionLeadConfig":{"type":"object","required":["kind"],"description":"Routes submissions into the existing PortalLeads pipeline.","properties":{"kind":{"type":"string","enum":["lead"]},"category":{"type":"string","description":"Service category id used to route the lead."}}},"PortalFormSubmissionEmailConfig":{"type":"object","required":["kind","to"],"description":"Routes submissions as a notification email to the listed recipients.","properties":{"kind":{"type":"string","enum":["email"]},"to":{"type":"array","minItems":1,"items":{"type":"string","format":"email"},"description":"Recipient email addresses."},"subjectTemplate":{"type":"string","description":"Optional subject-line template. Submitted values are intentionally\\nnot interpolated into the subject line for compliance reasons.\\n","maxLength":200}}},"PortalFormSubmissionWebhookConfig":{"type":"object","required":["kind","url"],"description":"Posts the submission JSON to a third-party webhook.","properties":{"kind":{"type":"string","enum":["webhook"]},"url":{"type":"string","format":"uri","description":"HTTPS endpoint receiving the signed POST."},"signingSecretRef":{"type":"string","description":"Name of the Key Vault secret used to HMAC-sign the request.\\nThe secret value is never returned in API responses.\\n"}}},"PortalFormSubmissionConfig":{"description":"Discriminated union of submission destinations. Exactly one of\\n`lead`, `email`, or `webhook` shapes is used based on `kind`.\\n","oneOf":[{"$ref":"#/definitions/PortalFormSubmissionLeadConfig"},{"$ref":"#/definitions/PortalFormSubmissionEmailConfig"},{"$ref":"#/definitions/PortalFormSubmissionWebhookConfig"}],"discriminator":{"propertyName":"kind","mapping":{"lead":"#/definitions/PortalFormSubmissionLeadConfig","email":"#/definitions/PortalFormSubmissionEmailConfig","webhook":"#/definitions/PortalFormSubmissionWebhookConfig"}}},"PortalFormSummary":{"type":"object","description":"List-row projection of a form definition.","required":["formId","fieldCount"],"properties":{"formId":{"type":"string","description":"Kebab-case identifier.","pattern":"^[a-z0-9][a-z0-9-]*$","example":"contact"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"fieldCount":{"type":"integer","minimum":0,"description":"Number of input-bearing fields in the form.","example":4},"attachedPageSlugs":{"type":"array","description":"Slugs of pages that currently reference this form via `formId`.","items":{"type":"string"}},"submissionKind":{"type":"string","enum":["lead","email","webhook"],"description":"Submission destination kind."},"lastUpdated":{"type":"string","format":"date-time","description":"Last modification timestamp."}}},"PortalCreateFormRequest":{"type":"object","description":"Payload for creating a new form definition.","required":["formId","submission","fields"],"properties":{"formId":{"type":"string","description":"Kebab-case identifier, unique per site.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submitLabel":{"type":"string","maxLength":80},"successMessage":{"type":"string","maxLength":2000},"submission":{"$ref":"#/definitions/PortalFormSubmissionConfig"},"attachmentPolicy":{"$ref":"#/definitions/PortalFormAttachmentPolicy"},"steps":{"type":"array","items":{"$ref":"#/definitions/PortalFormStep"}},"fields":{"type":"array","items":{"$ref":"#/definitions/PortalFormField"}}}},"PortalUpdateFormRequest":{"type":"object","description":"Payload for updating an existing form definition. The `formId`\\nis taken from the URL; supplying it in the body is optional and,\\nif present, must match the path.\\n","properties":{"formId":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80,"description":"Optional echo of the path formId; must match if provided."},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submitLabel":{"type":"string","maxLength":80},"successMessage":{"type":"string","maxLength":2000},"submission":{"$ref":"#/definitions/PortalFormSubmissionConfig"},"attachmentPolicy":{"$ref":"#/definitions/PortalFormAttachmentPolicy"},"steps":{"type":"array","items":{"$ref":"#/definitions/PortalFormStep"}},"fields":{"type":"array","items":{"$ref":"#/definitions/PortalFormField"}},"expectedVersion":{"type":"integer","minimum":1,"description":"Optional optimistic-concurrency token. When supplied, the\\nupdate is rejected with 409 if the stored form\'s `version`\\nno longer matches.\\n"}}},"PortalDuplicateFormRequest":{"type":"object","description":"Payload for cloning an existing form under a new formId.","required":["formId"],"properties":{"formId":{"type":"string","description":"Target kebab-case formId for the cloned form.","pattern":"^[a-z0-9][a-z0-9-]*$","minLength":1,"maxLength":80}}},"PortalFormResponse":{"type":"object","description":"Full form definition response.","required":["form"],"properties":{"form":{"$ref":"#/definitions/PortalFormDefinition"},"attachedPageSlugs":{"type":"array","description":"Slugs of pages that currently reference this form via `formId`.","items":{"type":"string"}}}},"PortalFormListResponse":{"type":"object","required":["forms","totalCount"],"properties":{"forms":{"type":"array","items":{"$ref":"#/definitions/PortalFormSummary"}},"totalCount":{"type":"integer","minimum":0,"description":"Total number of forms matching the query.","example":3}}},"PortalFormSubmission":{"type":"object","description":"A single recorded submission against a managed form. PHI fields are\\nreplaced with `[REDACTED]` for Medical-category sites, in which case\\n`redactedForPhi` is true.\\n","required":["id","formId","siteSlug","submittedAt","source","values","redactedForPhi"],"properties":{"id":{"type":"string","description":"Unique submission row key (reverse-chronological)."},"formId":{"type":"string"},"siteSlug":{"type":"string"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submittedAt":{"type":"string","format":"date-time"},"source":{"type":"string","description":"Capture channel (e.g. `web`, `import`)."},"values":{"type":"object","additionalProperties":true,"description":"Field id → submitted value map. PHI values are redacted."},"attachments":{"type":"array","description":"Attachment metadata captured with the submission.","items":{"$ref":"#/definitions/PublicSiteFormAttachment"}},"leadId":{"type":"string","nullable":true,"description":"Cross-reference to the lead row created by the legacy capture flow."},"contactMessageId":{"type":"string","nullable":true,"description":"Future portal contact-message identifier when surfaced in the portal."},"redactedForPhi":{"type":"boolean","description":"True when one or more values were replaced with the PHI placeholder."},"remoteIp":{"type":"string","nullable":true},"userAgent":{"type":"string","nullable":true}}},"PortalFormSubmissionSummary":{"type":"object","description":"List-view representation of a submission.","required":["id","formId","submittedAt","redactedForPhi","summary"],"properties":{"id":{"type":"string"},"formId":{"type":"string"},"formKind":{"$ref":"#/definitions/PortalFormKind"},"submittedAt":{"type":"string","format":"date-time"},"redactedForPhi":{"type":"boolean"},"summary":{"type":"string","description":"Short single-line preview suitable for the portal list view."}}},"PortalFormSubmissionListResponse":{"type":"object","required":["submissions"],"properties":{"submissions":{"type":"array","items":{"$ref":"#/definitions/PortalFormSubmissionSummary"}}}}}'), Se = {
|
|
237
|
+
$schema: be,
|
|
238
|
+
$id: ge,
|
|
239
|
+
title: ve,
|
|
240
|
+
type: Fe,
|
|
241
|
+
description: xe,
|
|
242
|
+
required: $e,
|
|
243
|
+
properties: we,
|
|
244
|
+
definitions: ke
|
|
220
245
|
};
|
|
221
|
-
let
|
|
222
|
-
function
|
|
223
|
-
if (
|
|
224
|
-
const e = new
|
|
246
|
+
let B = null;
|
|
247
|
+
function Pe() {
|
|
248
|
+
if (B) return B;
|
|
249
|
+
const e = new re({
|
|
225
250
|
allErrors: !0,
|
|
226
251
|
strict: !1,
|
|
227
252
|
discriminator: !1
|
|
228
253
|
});
|
|
229
|
-
return
|
|
254
|
+
return ne(e), B = e.compile(Se), B;
|
|
230
255
|
}
|
|
231
|
-
function
|
|
232
|
-
const t =
|
|
233
|
-
return { valid:
|
|
256
|
+
function Ie(e) {
|
|
257
|
+
const t = Pe(), n = t(e);
|
|
258
|
+
return { valid: n, errors: n ? [] : t.errors ?? [] };
|
|
234
259
|
}
|
|
235
|
-
function
|
|
236
|
-
const
|
|
237
|
-
return !
|
|
260
|
+
function Le(e, t, n) {
|
|
261
|
+
const i = Ie(t);
|
|
262
|
+
return !i.valid && n && console.warn(
|
|
238
263
|
`[@duffcloudservices/site-forms] Form "${e}" failed schema validation:`,
|
|
239
|
-
|
|
240
|
-
),
|
|
264
|
+
i.errors
|
|
265
|
+
), i;
|
|
241
266
|
}
|
|
242
|
-
function
|
|
267
|
+
function qe(e) {
|
|
243
268
|
const t = {};
|
|
244
|
-
for (const [
|
|
245
|
-
const
|
|
246
|
-
|
|
269
|
+
for (const [n, i] of Object.entries(e)) {
|
|
270
|
+
const s = Ce(n), o = Ve(i);
|
|
271
|
+
o && (t[o.formId ?? s] = o);
|
|
247
272
|
}
|
|
248
273
|
return t;
|
|
249
274
|
}
|
|
250
|
-
function
|
|
275
|
+
function Ce(e) {
|
|
251
276
|
return (e.split("/").pop() ?? e).replace(/\.ya?ml$/i, "");
|
|
252
277
|
}
|
|
253
|
-
function
|
|
278
|
+
function Ve(e) {
|
|
254
279
|
if (!e) return null;
|
|
255
280
|
if (typeof e == "string")
|
|
256
|
-
return
|
|
281
|
+
return Y.load(e);
|
|
257
282
|
if (typeof e == "object") {
|
|
258
283
|
const t = e.default;
|
|
259
284
|
return t && typeof t == "object" ? t : e;
|
|
260
285
|
}
|
|
261
286
|
return null;
|
|
262
287
|
}
|
|
263
|
-
function
|
|
264
|
-
return
|
|
288
|
+
function Et(e) {
|
|
289
|
+
return Y.load(e);
|
|
265
290
|
}
|
|
266
|
-
const
|
|
291
|
+
const je = ["data-form-field-key"], Ae = ["for"], De = {
|
|
267
292
|
key: 0,
|
|
268
293
|
"aria-hidden": "true",
|
|
269
294
|
class: "dcs-form-field__required"
|
|
270
|
-
},
|
|
295
|
+
}, Me = {
|
|
271
296
|
key: 0,
|
|
272
297
|
class: "dcs-form-field__help"
|
|
273
|
-
},
|
|
298
|
+
}, Te = {
|
|
274
299
|
key: 0,
|
|
275
300
|
class: "dcs-form-field__error",
|
|
276
301
|
role: "alert"
|
|
277
|
-
},
|
|
302
|
+
}, D = /* @__PURE__ */ I({
|
|
278
303
|
__name: "DcsFormFieldWrapper",
|
|
279
304
|
props: {
|
|
280
305
|
field: {},
|
|
@@ -282,30 +307,30 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
282
307
|
inputId: {}
|
|
283
308
|
},
|
|
284
309
|
setup(e) {
|
|
285
|
-
return (t,
|
|
310
|
+
return (t, n) => (u(), m("div", {
|
|
286
311
|
class: Z(["dcs-form-field", [`dcs-form-field--${e.field.type}`, `dcs-form-field--width-${e.field.width ?? "full"}`]]),
|
|
287
312
|
"data-form-field-key": e.field.id
|
|
288
313
|
}, [
|
|
289
|
-
|
|
290
|
-
e.field.type !== "checkbox" && e.field.type !== "hidden" && e.field.type !== "section-heading" && e.field.type !== "html-block" ? (
|
|
314
|
+
P(t.$slots, "label", {}, () => [
|
|
315
|
+
e.field.type !== "checkbox" && e.field.type !== "hidden" && e.field.type !== "section-heading" && e.field.type !== "html-block" ? (u(), m("label", {
|
|
291
316
|
key: 0,
|
|
292
317
|
for: e.inputId ?? `field-${e.field.id}`,
|
|
293
318
|
class: "dcs-form-field__label"
|
|
294
319
|
}, [
|
|
295
|
-
|
|
296
|
-
e.field.required ? (
|
|
297
|
-
], 8,
|
|
320
|
+
K(F(e.field.label) + " ", 1),
|
|
321
|
+
e.field.required ? (u(), m("span", De, "*")) : k("", !0)
|
|
322
|
+
], 8, Ae)) : k("", !0)
|
|
298
323
|
]),
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
e.field.helpText ? (
|
|
324
|
+
P(t.$slots, "default"),
|
|
325
|
+
P(t.$slots, "help", {}, () => [
|
|
326
|
+
e.field.helpText ? (u(), m("p", Me, F(e.field.helpText), 1)) : k("", !0)
|
|
302
327
|
]),
|
|
303
|
-
|
|
304
|
-
e.error ? (
|
|
328
|
+
P(t.$slots, "error", {}, () => [
|
|
329
|
+
e.error ? (u(), m("p", Te, F(e.error), 1)) : k("", !0)
|
|
305
330
|
])
|
|
306
|
-
], 10,
|
|
331
|
+
], 10, je));
|
|
307
332
|
}
|
|
308
|
-
}),
|
|
333
|
+
}), Oe = ["id", "type", "name", "value", "placeholder", "required", "aria-invalid", "aria-describedby"], J = /* @__PURE__ */ I({
|
|
309
334
|
__name: "DcsFormText",
|
|
310
335
|
props: {
|
|
311
336
|
field: {},
|
|
@@ -314,8 +339,8 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
314
339
|
},
|
|
315
340
|
emits: ["update:modelValue", "blur"],
|
|
316
341
|
setup(e, { emit: t }) {
|
|
317
|
-
const
|
|
318
|
-
switch (
|
|
342
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => {
|
|
343
|
+
switch (n.field.type) {
|
|
319
344
|
case "email":
|
|
320
345
|
return "email";
|
|
321
346
|
case "tel":
|
|
@@ -324,37 +349,37 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
324
349
|
return "text";
|
|
325
350
|
}
|
|
326
351
|
});
|
|
327
|
-
return (
|
|
352
|
+
return (c, a) => (u(), A(D, {
|
|
328
353
|
field: e.field,
|
|
329
354
|
error: e.error,
|
|
330
|
-
"input-id":
|
|
355
|
+
"input-id": s.value
|
|
331
356
|
}, {
|
|
332
|
-
default:
|
|
333
|
-
|
|
334
|
-
id:
|
|
357
|
+
default: j(() => [
|
|
358
|
+
P(c.$slots, "input", {
|
|
359
|
+
id: s.value,
|
|
335
360
|
value: e.modelValue,
|
|
336
|
-
onInput: (
|
|
337
|
-
onBlur: () =>
|
|
361
|
+
onInput: (l) => i("update:modelValue", l.target.value),
|
|
362
|
+
onBlur: () => i("blur")
|
|
338
363
|
}, () => [
|
|
339
|
-
|
|
340
|
-
id:
|
|
364
|
+
b("input", {
|
|
365
|
+
id: s.value,
|
|
341
366
|
class: "dcs-form-input",
|
|
342
|
-
type:
|
|
367
|
+
type: o.value,
|
|
343
368
|
name: e.field.id,
|
|
344
369
|
value: e.modelValue ?? "",
|
|
345
370
|
placeholder: e.field.placeholder,
|
|
346
371
|
required: e.field.required,
|
|
347
372
|
"aria-invalid": !!e.error,
|
|
348
|
-
"aria-describedby": e.error ? `${
|
|
349
|
-
onInput:
|
|
350
|
-
onBlur:
|
|
351
|
-
}, null, 40,
|
|
373
|
+
"aria-describedby": e.error ? `${s.value}-error` : void 0,
|
|
374
|
+
onInput: a[0] || (a[0] = (l) => i("update:modelValue", l.target.value)),
|
|
375
|
+
onBlur: a[1] || (a[1] = (l) => i("blur"))
|
|
376
|
+
}, null, 40, Oe)
|
|
352
377
|
])
|
|
353
378
|
]),
|
|
354
379
|
_: 3
|
|
355
380
|
}, 8, ["field", "error", "input-id"]));
|
|
356
381
|
}
|
|
357
|
-
}),
|
|
382
|
+
}), Re = ["id", "name", "placeholder", "required", "aria-invalid", "value"], Ee = /* @__PURE__ */ I({
|
|
358
383
|
__name: "DcsFormTextarea",
|
|
359
384
|
props: {
|
|
360
385
|
field: {},
|
|
@@ -363,21 +388,21 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
363
388
|
},
|
|
364
389
|
emits: ["update:modelValue", "blur"],
|
|
365
390
|
setup(e, { emit: t }) {
|
|
366
|
-
const
|
|
367
|
-
return (
|
|
391
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`);
|
|
392
|
+
return (o, c) => (u(), A(D, {
|
|
368
393
|
field: e.field,
|
|
369
394
|
error: e.error,
|
|
370
|
-
"input-id":
|
|
395
|
+
"input-id": s.value
|
|
371
396
|
}, {
|
|
372
|
-
default:
|
|
373
|
-
|
|
374
|
-
id:
|
|
397
|
+
default: j(() => [
|
|
398
|
+
P(o.$slots, "input", {
|
|
399
|
+
id: s.value,
|
|
375
400
|
value: e.modelValue,
|
|
376
|
-
onInput: (
|
|
377
|
-
onBlur: () =>
|
|
401
|
+
onInput: (a) => i("update:modelValue", a.target.value),
|
|
402
|
+
onBlur: () => i("blur")
|
|
378
403
|
}, () => [
|
|
379
|
-
|
|
380
|
-
id:
|
|
404
|
+
b("textarea", {
|
|
405
|
+
id: s.value,
|
|
381
406
|
class: "dcs-form-input dcs-form-textarea",
|
|
382
407
|
name: e.field.id,
|
|
383
408
|
placeholder: e.field.placeholder,
|
|
@@ -385,19 +410,19 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
385
410
|
"aria-invalid": !!e.error,
|
|
386
411
|
rows: "5",
|
|
387
412
|
value: e.modelValue ?? "",
|
|
388
|
-
onInput:
|
|
389
|
-
onBlur:
|
|
390
|
-
}, null, 40,
|
|
413
|
+
onInput: c[0] || (c[0] = (a) => i("update:modelValue", a.target.value)),
|
|
414
|
+
onBlur: c[1] || (c[1] = (a) => i("blur"))
|
|
415
|
+
}, null, 40, Re)
|
|
391
416
|
])
|
|
392
417
|
]),
|
|
393
418
|
_: 3
|
|
394
419
|
}, 8, ["field", "error", "input-id"]));
|
|
395
420
|
}
|
|
396
|
-
}),
|
|
421
|
+
}), ze = ["id", "name", "required", "multiple", "aria-invalid", "value"], _e = {
|
|
397
422
|
key: 0,
|
|
398
423
|
value: "",
|
|
399
424
|
disabled: ""
|
|
400
|
-
},
|
|
425
|
+
}, Be = ["value"], Ke = /* @__PURE__ */ I({
|
|
401
426
|
__name: "DcsFormSelect",
|
|
402
427
|
props: {
|
|
403
428
|
field: {},
|
|
@@ -406,51 +431,51 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
406
431
|
},
|
|
407
432
|
emits: ["update:modelValue", "blur"],
|
|
408
433
|
setup(e, { emit: t }) {
|
|
409
|
-
const
|
|
410
|
-
function
|
|
411
|
-
const
|
|
412
|
-
if (
|
|
413
|
-
const
|
|
414
|
-
for (const
|
|
415
|
-
|
|
434
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => n.field.type === "multiselect"), c = p(() => n.field.options ?? []);
|
|
435
|
+
function a(l) {
|
|
436
|
+
const h = l.target;
|
|
437
|
+
if (o.value) {
|
|
438
|
+
const r = [];
|
|
439
|
+
for (const v of Array.from(h.selectedOptions)) r.push(v.value);
|
|
440
|
+
i("update:modelValue", r);
|
|
416
441
|
} else
|
|
417
|
-
|
|
442
|
+
i("update:modelValue", h.value);
|
|
418
443
|
}
|
|
419
|
-
return (
|
|
444
|
+
return (l, h) => (u(), A(D, {
|
|
420
445
|
field: e.field,
|
|
421
446
|
error: e.error,
|
|
422
|
-
"input-id":
|
|
447
|
+
"input-id": s.value
|
|
423
448
|
}, {
|
|
424
|
-
default:
|
|
425
|
-
|
|
426
|
-
id:
|
|
449
|
+
default: j(() => [
|
|
450
|
+
P(l.$slots, "input", {
|
|
451
|
+
id: s.value,
|
|
427
452
|
value: e.modelValue,
|
|
428
|
-
options:
|
|
429
|
-
onChange:
|
|
453
|
+
options: c.value,
|
|
454
|
+
onChange: a
|
|
430
455
|
}, () => [
|
|
431
|
-
|
|
432
|
-
id:
|
|
456
|
+
b("select", {
|
|
457
|
+
id: s.value,
|
|
433
458
|
class: "dcs-form-input dcs-form-select",
|
|
434
459
|
name: e.field.id,
|
|
435
460
|
required: e.field.required,
|
|
436
|
-
multiple:
|
|
461
|
+
multiple: o.value,
|
|
437
462
|
"aria-invalid": !!e.error,
|
|
438
|
-
value: e.modelValue ?? (
|
|
439
|
-
onChange:
|
|
440
|
-
onBlur:
|
|
463
|
+
value: e.modelValue ?? (o.value ? [] : ""),
|
|
464
|
+
onChange: a,
|
|
465
|
+
onBlur: h[0] || (h[0] = (r) => i("blur"))
|
|
441
466
|
}, [
|
|
442
|
-
|
|
443
|
-
(
|
|
444
|
-
key:
|
|
445
|
-
value:
|
|
446
|
-
},
|
|
447
|
-
], 40,
|
|
467
|
+
o.value ? k("", !0) : (u(), m("option", _e, F(e.field.placeholder ?? "Select…"), 1)),
|
|
468
|
+
(u(!0), m(T, null, z(c.value, (r) => (u(), m("option", {
|
|
469
|
+
key: r.value,
|
|
470
|
+
value: r.value
|
|
471
|
+
}, F(r.label), 9, Be))), 128))
|
|
472
|
+
], 40, ze)
|
|
448
473
|
])
|
|
449
474
|
]),
|
|
450
475
|
_: 3
|
|
451
476
|
}, 8, ["field", "error", "input-id"]));
|
|
452
477
|
}
|
|
453
|
-
}),
|
|
478
|
+
}), We = ["aria-required", "aria-invalid"], Ue = { class: "sr-only" }, Ne = ["name", "value", "checked", "onChange"], He = /* @__PURE__ */ I({
|
|
454
479
|
__name: "DcsFormRadio",
|
|
455
480
|
props: {
|
|
456
481
|
field: {},
|
|
@@ -459,39 +484,39 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
459
484
|
},
|
|
460
485
|
emits: ["update:modelValue"],
|
|
461
486
|
setup(e, { emit: t }) {
|
|
462
|
-
const
|
|
463
|
-
return (
|
|
487
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => n.field.options ?? []);
|
|
488
|
+
return (c, a) => (u(), A(D, {
|
|
464
489
|
field: e.field,
|
|
465
490
|
error: e.error,
|
|
466
|
-
"input-id":
|
|
491
|
+
"input-id": s.value
|
|
467
492
|
}, {
|
|
468
|
-
default:
|
|
469
|
-
|
|
493
|
+
default: j(() => [
|
|
494
|
+
b("fieldset", {
|
|
470
495
|
class: "dcs-form-radio-group",
|
|
471
496
|
role: "radiogroup",
|
|
472
497
|
"aria-required": e.field.required,
|
|
473
498
|
"aria-invalid": !!e.error
|
|
474
499
|
}, [
|
|
475
|
-
|
|
476
|
-
(
|
|
477
|
-
key:
|
|
500
|
+
b("legend", Ue, F(e.field.label), 1),
|
|
501
|
+
(u(!0), m(T, null, z(o.value, (l) => (u(), m("label", {
|
|
502
|
+
key: l.value,
|
|
478
503
|
class: "dcs-form-radio"
|
|
479
504
|
}, [
|
|
480
|
-
|
|
505
|
+
b("input", {
|
|
481
506
|
type: "radio",
|
|
482
507
|
name: e.field.id,
|
|
483
|
-
value:
|
|
484
|
-
checked: e.modelValue ===
|
|
485
|
-
onChange: (
|
|
486
|
-
}, null, 40,
|
|
487
|
-
|
|
508
|
+
value: l.value,
|
|
509
|
+
checked: e.modelValue === l.value,
|
|
510
|
+
onChange: (h) => i("update:modelValue", l.value)
|
|
511
|
+
}, null, 40, Ne),
|
|
512
|
+
b("span", null, F(l.label), 1)
|
|
488
513
|
]))), 128))
|
|
489
|
-
], 8,
|
|
514
|
+
], 8, We)
|
|
490
515
|
]),
|
|
491
516
|
_: 1
|
|
492
517
|
}, 8, ["field", "error", "input-id"]));
|
|
493
518
|
}
|
|
494
|
-
}),
|
|
519
|
+
}), Je = ["aria-required", "aria-invalid"], Ge = { class: "sr-only" }, Ye = ["name", "value", "checked", "onChange"], Qe = /* @__PURE__ */ I({
|
|
495
520
|
__name: "DcsFormCheckboxGroup",
|
|
496
521
|
props: {
|
|
497
522
|
field: {},
|
|
@@ -500,45 +525,45 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
500
525
|
},
|
|
501
526
|
emits: ["update:modelValue"],
|
|
502
527
|
setup(e, { emit: t }) {
|
|
503
|
-
const
|
|
504
|
-
function
|
|
505
|
-
const
|
|
506
|
-
|
|
528
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`), o = p(() => n.field.options ?? []), c = p(() => n.modelValue ?? []);
|
|
529
|
+
function a(l, h) {
|
|
530
|
+
const r = new Set(c.value);
|
|
531
|
+
h ? r.add(l) : r.delete(l), i("update:modelValue", Array.from(r));
|
|
507
532
|
}
|
|
508
|
-
return (
|
|
533
|
+
return (l, h) => (u(), A(D, {
|
|
509
534
|
field: e.field,
|
|
510
535
|
error: e.error,
|
|
511
|
-
"input-id":
|
|
536
|
+
"input-id": s.value
|
|
512
537
|
}, {
|
|
513
|
-
default:
|
|
514
|
-
|
|
538
|
+
default: j(() => [
|
|
539
|
+
b("fieldset", {
|
|
515
540
|
class: "dcs-form-checkbox-group",
|
|
516
541
|
"aria-required": e.field.required,
|
|
517
542
|
"aria-invalid": !!e.error
|
|
518
543
|
}, [
|
|
519
|
-
|
|
520
|
-
(
|
|
521
|
-
key:
|
|
544
|
+
b("legend", Ge, F(e.field.label), 1),
|
|
545
|
+
(u(!0), m(T, null, z(o.value, (r) => (u(), m("label", {
|
|
546
|
+
key: r.value,
|
|
522
547
|
class: "dcs-form-checkbox"
|
|
523
548
|
}, [
|
|
524
|
-
|
|
549
|
+
b("input", {
|
|
525
550
|
type: "checkbox",
|
|
526
551
|
name: `${e.field.id}[]`,
|
|
527
|
-
value:
|
|
528
|
-
checked:
|
|
529
|
-
onChange: (
|
|
530
|
-
}, null, 40,
|
|
531
|
-
|
|
552
|
+
value: r.value,
|
|
553
|
+
checked: c.value.includes(r.value),
|
|
554
|
+
onChange: (v) => a(r.value, v.target.checked)
|
|
555
|
+
}, null, 40, Ye),
|
|
556
|
+
b("span", null, F(r.label), 1)
|
|
532
557
|
]))), 128))
|
|
533
|
-
], 8,
|
|
558
|
+
], 8, Je)
|
|
534
559
|
]),
|
|
535
560
|
_: 1
|
|
536
561
|
}, 8, ["field", "error", "input-id"]));
|
|
537
562
|
}
|
|
538
|
-
}),
|
|
563
|
+
}), Xe = { class: "sr-only" }, Ze = ["for"], et = ["id", "name", "checked", "required", "aria-invalid"], tt = {
|
|
539
564
|
key: 0,
|
|
540
565
|
"aria-hidden": "true"
|
|
541
|
-
},
|
|
566
|
+
}, it = /* @__PURE__ */ I({
|
|
542
567
|
__name: "DcsFormCheckbox",
|
|
543
568
|
props: {
|
|
544
569
|
field: {},
|
|
@@ -547,39 +572,39 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
547
572
|
},
|
|
548
573
|
emits: ["update:modelValue"],
|
|
549
574
|
setup(e, { emit: t }) {
|
|
550
|
-
const
|
|
551
|
-
return (
|
|
575
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`);
|
|
576
|
+
return (o, c) => (u(), A(D, {
|
|
552
577
|
field: e.field,
|
|
553
578
|
error: e.error,
|
|
554
|
-
"input-id":
|
|
579
|
+
"input-id": s.value
|
|
555
580
|
}, {
|
|
556
|
-
label:
|
|
557
|
-
|
|
581
|
+
label: j(() => [
|
|
582
|
+
b("span", Xe, F(e.field.label), 1)
|
|
558
583
|
]),
|
|
559
|
-
default:
|
|
560
|
-
|
|
584
|
+
default: j(() => [
|
|
585
|
+
b("label", {
|
|
561
586
|
class: "dcs-form-checkbox-single",
|
|
562
|
-
for:
|
|
587
|
+
for: s.value
|
|
563
588
|
}, [
|
|
564
|
-
|
|
565
|
-
id:
|
|
589
|
+
b("input", {
|
|
590
|
+
id: s.value,
|
|
566
591
|
type: "checkbox",
|
|
567
592
|
name: e.field.id,
|
|
568
593
|
checked: !!e.modelValue,
|
|
569
594
|
required: e.field.required,
|
|
570
595
|
"aria-invalid": !!e.error,
|
|
571
|
-
onChange:
|
|
572
|
-
}, null, 40,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
e.field.required ? (
|
|
596
|
+
onChange: c[0] || (c[0] = (a) => i("update:modelValue", a.target.checked))
|
|
597
|
+
}, null, 40, et),
|
|
598
|
+
b("span", null, [
|
|
599
|
+
K(F(e.field.label), 1),
|
|
600
|
+
e.field.required ? (u(), m("span", tt, " *")) : k("", !0)
|
|
576
601
|
])
|
|
577
|
-
], 8,
|
|
602
|
+
], 8, Ze)
|
|
578
603
|
]),
|
|
579
604
|
_: 1
|
|
580
605
|
}, 8, ["field", "error", "input-id"]));
|
|
581
606
|
}
|
|
582
|
-
}),
|
|
607
|
+
}), rt = ["id", "name", "required", "aria-invalid", "value"], nt = /* @__PURE__ */ I({
|
|
583
608
|
__name: "DcsFormDate",
|
|
584
609
|
props: {
|
|
585
610
|
field: {},
|
|
@@ -588,116 +613,195 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
588
613
|
},
|
|
589
614
|
emits: ["update:modelValue", "blur"],
|
|
590
615
|
setup(e, { emit: t }) {
|
|
591
|
-
const
|
|
592
|
-
return (
|
|
616
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`);
|
|
617
|
+
return (o, c) => (u(), A(D, {
|
|
593
618
|
field: e.field,
|
|
594
619
|
error: e.error,
|
|
595
|
-
"input-id":
|
|
620
|
+
"input-id": s.value
|
|
596
621
|
}, {
|
|
597
|
-
default:
|
|
598
|
-
|
|
599
|
-
id:
|
|
622
|
+
default: j(() => [
|
|
623
|
+
b("input", {
|
|
624
|
+
id: s.value,
|
|
600
625
|
class: "dcs-form-input dcs-form-date",
|
|
601
626
|
type: "date",
|
|
602
627
|
name: e.field.id,
|
|
603
628
|
required: e.field.required,
|
|
604
629
|
"aria-invalid": !!e.error,
|
|
605
630
|
value: e.modelValue ?? "",
|
|
606
|
-
onInput:
|
|
607
|
-
onBlur:
|
|
608
|
-
}, null, 40,
|
|
631
|
+
onInput: c[0] || (c[0] = (a) => i("update:modelValue", a.target.value)),
|
|
632
|
+
onBlur: c[1] || (c[1] = (a) => i("blur"))
|
|
633
|
+
}, null, 40, rt)
|
|
609
634
|
]),
|
|
610
635
|
_: 1
|
|
611
636
|
}, 8, ["field", "error", "input-id"]));
|
|
612
637
|
}
|
|
613
|
-
}),
|
|
638
|
+
}), ot = ["id", "name", "multiple", "required", "aria-invalid", "accept"], st = {
|
|
639
|
+
key: 0,
|
|
640
|
+
class: "dcs-form-file__status"
|
|
641
|
+
}, at = {
|
|
642
|
+
key: 1,
|
|
643
|
+
class: "dcs-form-file-preview-grid",
|
|
644
|
+
"aria-live": "polite"
|
|
645
|
+
}, lt = ["src", "alt"], dt = {
|
|
646
|
+
key: 1,
|
|
647
|
+
class: "dcs-form-file-preview__placeholder",
|
|
648
|
+
"aria-hidden": "true"
|
|
649
|
+
}, ut = { class: "dcs-form-file-preview__meta" }, ct = { class: "dcs-form-file-preview__name" }, mt = { class: "dcs-form-file-preview__size" }, ft = ["aria-label", "onClick"], pt = /* @__PURE__ */ I({
|
|
614
650
|
__name: "DcsFormFile",
|
|
615
651
|
props: {
|
|
616
652
|
field: {},
|
|
653
|
+
attachmentPolicy: {},
|
|
617
654
|
modelValue: {},
|
|
618
655
|
error: {}
|
|
619
656
|
},
|
|
620
657
|
emits: ["update:modelValue"],
|
|
621
658
|
setup(e, { emit: t }) {
|
|
622
|
-
const
|
|
623
|
-
function
|
|
624
|
-
|
|
625
|
-
r("update:modelValue", a.files?.[0]);
|
|
659
|
+
const n = e, i = t, s = p(() => `field-${n.field.id}`), o = V(null), c = p(() => (n.field.validation?.accept ?? n.attachmentPolicy?.accept ?? []).join(",")), a = p(() => Math.max(1, Math.floor(n.attachmentPolicy?.maxFiles ?? 1))), l = p(() => a.value > 1), h = p(() => v(n.modelValue)), r = V([]);
|
|
660
|
+
function v(d) {
|
|
661
|
+
return Array.isArray(d) ? d.filter(Boolean) : d ? [d] : [];
|
|
626
662
|
}
|
|
627
|
-
|
|
663
|
+
function $(d) {
|
|
664
|
+
const g = d.slice(0, a.value);
|
|
665
|
+
i("update:modelValue", l.value ? g : g[0]);
|
|
666
|
+
}
|
|
667
|
+
function q(d) {
|
|
668
|
+
return [d.name, d.type, d.size, d.lastModified].join(":");
|
|
669
|
+
}
|
|
670
|
+
function M(d) {
|
|
671
|
+
const g = /* @__PURE__ */ new Set(), x = [];
|
|
672
|
+
for (const C of d) {
|
|
673
|
+
const E = q(C);
|
|
674
|
+
g.has(E) || (g.add(E), x.push(C));
|
|
675
|
+
}
|
|
676
|
+
return x;
|
|
677
|
+
}
|
|
678
|
+
function O(d) {
|
|
679
|
+
const g = d.target, x = Array.from(g.files ?? []).filter((C) => C.size > 0);
|
|
680
|
+
x.length !== 0 && ($(l.value ? M([...h.value, ...x]) : x), g.value = "");
|
|
681
|
+
}
|
|
682
|
+
function R(d) {
|
|
683
|
+
const g = h.value.filter((x, C) => C !== d);
|
|
684
|
+
$(g), g.length === 0 && o.value && (o.value.value = "");
|
|
685
|
+
}
|
|
686
|
+
function w(d) {
|
|
687
|
+
return d < 1024 ? `${d} B` : d < 1024 * 1024 ? `${(d / 1024).toFixed(1)} KB` : `${(d / (1024 * 1024)).toFixed(1)} MB`;
|
|
688
|
+
}
|
|
689
|
+
function L() {
|
|
690
|
+
for (const d of r.value)
|
|
691
|
+
URL.revokeObjectURL(d.url);
|
|
692
|
+
r.value = [];
|
|
693
|
+
}
|
|
694
|
+
return W(
|
|
695
|
+
h,
|
|
696
|
+
(d) => {
|
|
697
|
+
L(), r.value = d.map((g) => ({
|
|
698
|
+
file: g,
|
|
699
|
+
url: URL.createObjectURL(g),
|
|
700
|
+
canPreview: g.type.toLowerCase().startsWith("image/")
|
|
701
|
+
}));
|
|
702
|
+
},
|
|
703
|
+
{ immediate: !0 }
|
|
704
|
+
), ee(L), (d, g) => (u(), A(D, {
|
|
628
705
|
field: e.field,
|
|
629
706
|
error: e.error,
|
|
630
|
-
"input-id":
|
|
707
|
+
"input-id": s.value
|
|
631
708
|
}, {
|
|
632
|
-
default:
|
|
633
|
-
|
|
634
|
-
|
|
709
|
+
default: j(() => [
|
|
710
|
+
b("input", {
|
|
711
|
+
ref_key: "inputEl",
|
|
712
|
+
ref: o,
|
|
713
|
+
id: s.value,
|
|
635
714
|
class: "dcs-form-input dcs-form-file",
|
|
636
715
|
type: "file",
|
|
637
716
|
name: e.field.id,
|
|
717
|
+
multiple: l.value,
|
|
638
718
|
required: e.field.required,
|
|
639
719
|
"aria-invalid": !!e.error,
|
|
640
|
-
accept:
|
|
641
|
-
onChange:
|
|
642
|
-
}, null, 40,
|
|
720
|
+
accept: c.value || void 0,
|
|
721
|
+
onChange: O
|
|
722
|
+
}, null, 40, ot),
|
|
723
|
+
l.value ? (u(), m("p", st, F(h.value.length) + " of " + F(a.value) + " files selected ", 1)) : k("", !0),
|
|
724
|
+
r.value.length > 0 ? (u(), m("div", at, [
|
|
725
|
+
(u(!0), m(T, null, z(r.value, (x, C) => (u(), m("div", {
|
|
726
|
+
key: q(x.file),
|
|
727
|
+
class: "dcs-form-file-preview"
|
|
728
|
+
}, [
|
|
729
|
+
x.canPreview ? (u(), m("img", {
|
|
730
|
+
key: 0,
|
|
731
|
+
class: "dcs-form-file-preview__image",
|
|
732
|
+
src: x.url,
|
|
733
|
+
alt: `${x.file.name} preview`
|
|
734
|
+
}, null, 8, lt)) : (u(), m("div", dt, " File ")),
|
|
735
|
+
b("div", ut, [
|
|
736
|
+
b("span", ct, F(x.file.name), 1),
|
|
737
|
+
b("span", mt, F(w(x.file.size)), 1)
|
|
738
|
+
]),
|
|
739
|
+
b("button", {
|
|
740
|
+
type: "button",
|
|
741
|
+
class: "dcs-form-file-preview__remove",
|
|
742
|
+
"aria-label": `Remove ${x.file.name}`,
|
|
743
|
+
onClick: (E) => R(C)
|
|
744
|
+
}, " Remove ", 8, ft)
|
|
745
|
+
]))), 128))
|
|
746
|
+
])) : k("", !0)
|
|
643
747
|
]),
|
|
644
748
|
_: 1
|
|
645
749
|
}, 8, ["field", "error", "input-id"]));
|
|
646
750
|
}
|
|
647
|
-
}),
|
|
751
|
+
}), ht = ["name", "value", "data-form-field-key"], yt = /* @__PURE__ */ I({
|
|
648
752
|
__name: "DcsFormHidden",
|
|
649
753
|
props: {
|
|
650
754
|
field: {},
|
|
651
755
|
modelValue: {}
|
|
652
756
|
},
|
|
653
757
|
setup(e) {
|
|
654
|
-
return (t,
|
|
758
|
+
return (t, n) => (u(), m("input", {
|
|
655
759
|
type: "hidden",
|
|
656
760
|
name: e.field.id,
|
|
657
761
|
value: e.modelValue ?? e.field.defaultValue ?? "",
|
|
658
762
|
"data-form-field-key": e.field.id
|
|
659
|
-
}, null, 8,
|
|
763
|
+
}, null, 8, ht));
|
|
660
764
|
}
|
|
661
|
-
}),
|
|
765
|
+
}), bt = ["data-form-field-key"], gt = { class: "dcs-form-section__heading" }, vt = {
|
|
662
766
|
key: 0,
|
|
663
767
|
class: "dcs-form-section__help"
|
|
664
|
-
},
|
|
768
|
+
}, Ft = /* @__PURE__ */ I({
|
|
665
769
|
__name: "DcsFormSection",
|
|
666
770
|
props: {
|
|
667
771
|
field: {}
|
|
668
772
|
},
|
|
669
773
|
setup(e) {
|
|
670
|
-
return (t,
|
|
774
|
+
return (t, n) => (u(), m("div", {
|
|
671
775
|
class: "dcs-form-section",
|
|
672
776
|
"data-form-field-key": e.field.id
|
|
673
777
|
}, [
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
e.field.helpText ? (
|
|
778
|
+
P(t.$slots, "default", {}, () => [
|
|
779
|
+
b("h3", gt, F(e.field.label), 1),
|
|
780
|
+
e.field.helpText ? (u(), m("p", vt, F(e.field.helpText), 1)) : k("", !0)
|
|
677
781
|
])
|
|
678
|
-
], 8,
|
|
782
|
+
], 8, bt));
|
|
679
783
|
}
|
|
680
|
-
}),
|
|
784
|
+
}), xt = ["data-form-field-key", "innerHTML"], $t = /* @__PURE__ */ I({
|
|
681
785
|
__name: "DcsFormHtmlBlock",
|
|
682
786
|
props: {
|
|
683
787
|
field: {}
|
|
684
788
|
},
|
|
685
789
|
setup(e) {
|
|
686
|
-
return (t,
|
|
790
|
+
return (t, n) => (u(), m("div", {
|
|
687
791
|
class: "dcs-form-html-block",
|
|
688
792
|
"data-form-field-key": e.field.id,
|
|
689
793
|
innerHTML: e.field.html ?? ""
|
|
690
|
-
}, null, 8,
|
|
794
|
+
}, null, 8, xt));
|
|
691
795
|
}
|
|
692
|
-
}),
|
|
796
|
+
}), G = {}, wt = ["data-form-key"], kt = ["data-form-key"], St = ["data-form-key"], Pt = {
|
|
693
797
|
key: 0,
|
|
694
798
|
class: "dcs-form__progress",
|
|
695
799
|
"aria-live": "polite"
|
|
696
|
-
},
|
|
800
|
+
}, It = { class: "dcs-form__fields" }, Lt = {
|
|
697
801
|
key: 1,
|
|
698
802
|
class: "dcs-form__submit-error",
|
|
699
803
|
role: "alert"
|
|
700
|
-
},
|
|
804
|
+
}, qt = { class: "dcs-form__actions" }, Ct = ["disabled"], zt = /* @__PURE__ */ I({
|
|
701
805
|
__name: "DcsForm",
|
|
702
806
|
props: {
|
|
703
807
|
formId: {},
|
|
@@ -708,179 +812,187 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
708
812
|
formsModules: {}
|
|
709
813
|
},
|
|
710
814
|
emits: ["submit-success", "submit-error", "validation-error"],
|
|
711
|
-
setup(e, {
|
|
712
|
-
const i = e,
|
|
713
|
-
() =>
|
|
714
|
-
),
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
(
|
|
718
|
-
|
|
815
|
+
setup(e, { expose: t, emit: n }) {
|
|
816
|
+
const i = e, s = n, o = /* @__PURE__ */ Object.assign({}), c = p(
|
|
817
|
+
() => qe(i.formsModules ?? o)
|
|
818
|
+
), a = p(() => i.definitionOverride ? i.definitionOverride : c.value[i.formId] ?? null), l = !1;
|
|
819
|
+
W(
|
|
820
|
+
a,
|
|
821
|
+
(w) => {
|
|
822
|
+
w && Le(i.formId, w, l);
|
|
719
823
|
},
|
|
720
824
|
{ immediate: !0 }
|
|
721
825
|
);
|
|
722
|
-
const
|
|
723
|
-
() =>
|
|
826
|
+
const h = p(
|
|
827
|
+
() => a.value ?? {
|
|
724
828
|
formId: i.formId,
|
|
725
829
|
submission: { kind: "lead" },
|
|
726
830
|
fields: []
|
|
727
831
|
}
|
|
728
|
-
),
|
|
729
|
-
|
|
730
|
-
() =>
|
|
731
|
-
() =>
|
|
832
|
+
), r = ce({ definition: h.value });
|
|
833
|
+
W(
|
|
834
|
+
() => h.value,
|
|
835
|
+
() => r.reset()
|
|
732
836
|
);
|
|
733
|
-
const
|
|
734
|
-
() => i.apiBase ??
|
|
735
|
-
),
|
|
736
|
-
() => i.siteSlug ??
|
|
737
|
-
),
|
|
738
|
-
() =>
|
|
837
|
+
const v = p(
|
|
838
|
+
() => i.apiBase ?? G?.VITE_DCS_PUBLIC_API ?? ""
|
|
839
|
+
), $ = p(
|
|
840
|
+
() => i.siteSlug ?? G?.VITE_DCS_SITE_SLUG ?? ""
|
|
841
|
+
), q = p(
|
|
842
|
+
() => h.value.submitLabel ?? "Send"
|
|
739
843
|
);
|
|
740
|
-
function
|
|
741
|
-
switch (
|
|
844
|
+
function M(w) {
|
|
845
|
+
switch (w.type) {
|
|
742
846
|
case "text":
|
|
743
847
|
case "email":
|
|
744
848
|
case "tel":
|
|
745
|
-
return
|
|
849
|
+
return J;
|
|
746
850
|
case "textarea":
|
|
747
|
-
return
|
|
851
|
+
return Ee;
|
|
748
852
|
case "select":
|
|
749
853
|
case "multiselect":
|
|
750
|
-
return
|
|
854
|
+
return Ke;
|
|
751
855
|
case "radio":
|
|
752
|
-
return
|
|
856
|
+
return He;
|
|
753
857
|
case "checkbox-group":
|
|
754
|
-
return Ue;
|
|
755
|
-
case "checkbox":
|
|
756
858
|
return Qe;
|
|
859
|
+
case "checkbox":
|
|
860
|
+
return it;
|
|
757
861
|
case "date":
|
|
758
|
-
return
|
|
862
|
+
return nt;
|
|
759
863
|
case "file":
|
|
760
|
-
return
|
|
864
|
+
return pt;
|
|
761
865
|
case "hidden":
|
|
762
|
-
return
|
|
866
|
+
return yt;
|
|
763
867
|
case "section-heading":
|
|
764
|
-
return
|
|
868
|
+
return Ft;
|
|
765
869
|
case "html-block":
|
|
766
|
-
return
|
|
870
|
+
return $t;
|
|
767
871
|
default:
|
|
768
|
-
return
|
|
872
|
+
return J;
|
|
769
873
|
}
|
|
770
874
|
}
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
875
|
+
const O = V(null);
|
|
876
|
+
t({
|
|
877
|
+
values: r.values,
|
|
878
|
+
errors: r.errors,
|
|
879
|
+
validateAll: r.validateAll,
|
|
880
|
+
collectSubmissionValues: r.collectSubmissionValues,
|
|
881
|
+
reset: r.reset
|
|
882
|
+
});
|
|
883
|
+
async function R(w) {
|
|
884
|
+
if (w.preventDefault(), !a.value) return;
|
|
885
|
+
if (!r.validateAll()) {
|
|
886
|
+
s("validation-error", r.errors.value);
|
|
776
887
|
return;
|
|
777
888
|
}
|
|
778
|
-
|
|
779
|
-
const
|
|
889
|
+
r.submitting.value = !0, r.submitError.value = null;
|
|
890
|
+
const d = {
|
|
780
891
|
formId: i.formId,
|
|
781
|
-
values:
|
|
892
|
+
values: r.collectSubmissionValues(),
|
|
782
893
|
captchaToken: i.captchaToken
|
|
783
894
|
};
|
|
784
895
|
try {
|
|
785
|
-
const
|
|
786
|
-
apiBase:
|
|
787
|
-
siteSlug:
|
|
788
|
-
payload:
|
|
896
|
+
const g = await me({
|
|
897
|
+
apiBase: v.value,
|
|
898
|
+
siteSlug: $.value,
|
|
899
|
+
payload: d
|
|
789
900
|
});
|
|
790
|
-
|
|
791
|
-
} catch (
|
|
792
|
-
const
|
|
793
|
-
|
|
901
|
+
r.submitted.value = !0, s("submit-success", g);
|
|
902
|
+
} catch (g) {
|
|
903
|
+
const x = g;
|
|
904
|
+
r.submitError.value = x.error?.message ?? "Submission failed", s("submit-error", x);
|
|
794
905
|
} finally {
|
|
795
|
-
|
|
906
|
+
r.submitting.value = !1;
|
|
796
907
|
}
|
|
797
908
|
}
|
|
798
|
-
return
|
|
799
|
-
|
|
909
|
+
return te(() => {
|
|
910
|
+
a.value || console.warn(
|
|
800
911
|
`[@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.`
|
|
801
912
|
);
|
|
802
|
-
}), (
|
|
913
|
+
}), (w, L) => a.value ? f(r).submitted.value ? (u(), m("div", {
|
|
803
914
|
key: 1,
|
|
804
915
|
class: "dcs-form dcs-form--success",
|
|
805
916
|
"data-form-key": e.formId
|
|
806
917
|
}, [
|
|
807
|
-
|
|
808
|
-
|
|
918
|
+
P(w.$slots, "success", { definition: a.value }, () => [
|
|
919
|
+
b("p", null, F(a.value.successMessage ?? "Thanks — we received your message."), 1)
|
|
809
920
|
])
|
|
810
|
-
], 8,
|
|
921
|
+
], 8, kt)) : (u(), m("form", {
|
|
811
922
|
key: 2,
|
|
812
923
|
ref_key: "formEl",
|
|
813
|
-
ref:
|
|
924
|
+
ref: O,
|
|
814
925
|
class: "dcs-form",
|
|
815
926
|
"data-form-key": e.formId,
|
|
816
927
|
novalidate: "",
|
|
817
|
-
onSubmit:
|
|
928
|
+
onSubmit: R
|
|
818
929
|
}, [
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
current:
|
|
823
|
-
total:
|
|
824
|
-
step:
|
|
930
|
+
P(w.$slots, "header", { definition: a.value }),
|
|
931
|
+
f(r).steps.value ? (u(), m("div", Pt, [
|
|
932
|
+
P(w.$slots, "progress", {
|
|
933
|
+
current: f(r).currentStepIndex.value,
|
|
934
|
+
total: f(r).steps.value.length,
|
|
935
|
+
step: f(r).currentStep.value
|
|
825
936
|
}, () => [
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
], 64)) :
|
|
937
|
+
b("p", null, [
|
|
938
|
+
K(" Step " + F(f(r).currentStepIndex.value + 1) + " of " + F(f(r).steps.value.length) + " ", 1),
|
|
939
|
+
f(r).currentStep.value ? (u(), m(T, { key: 0 }, [
|
|
940
|
+
K(" — " + F(f(r).currentStep.value.title), 1)
|
|
941
|
+
], 64)) : k("", !0)
|
|
831
942
|
])
|
|
832
943
|
])
|
|
833
|
-
])) :
|
|
834
|
-
|
|
835
|
-
(
|
|
836
|
-
key:
|
|
837
|
-
field:
|
|
838
|
-
"
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
944
|
+
])) : k("", !0),
|
|
945
|
+
b("div", It, [
|
|
946
|
+
(u(!0), m(T, null, z(f(r).visibleFields.value, (d) => (u(), A(ie(M(d)), {
|
|
947
|
+
key: d.id,
|
|
948
|
+
field: d,
|
|
949
|
+
"attachment-policy": h.value.attachmentPolicy,
|
|
950
|
+
"model-value": f(r).values[d.id],
|
|
951
|
+
error: f(r).errors.value[d.id],
|
|
952
|
+
"onUpdate:modelValue": (g) => f(r).setValue(d.id, g),
|
|
953
|
+
onBlur: (g) => f(r).touch(d.id)
|
|
954
|
+
}, null, 40, ["field", "attachment-policy", "model-value", "error", "onUpdate:modelValue", "onBlur"]))), 128))
|
|
843
955
|
]),
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
isFirstStep:
|
|
848
|
-
isLastStep:
|
|
849
|
-
submitting:
|
|
850
|
-
prev:
|
|
851
|
-
next:
|
|
956
|
+
f(r).submitError.value ? (u(), m("div", Lt, F(f(r).submitError.value), 1)) : k("", !0),
|
|
957
|
+
b("div", qt, [
|
|
958
|
+
P(w.$slots, "actions", {
|
|
959
|
+
isFirstStep: f(r).isFirstStep.value,
|
|
960
|
+
isLastStep: f(r).isLastStep.value,
|
|
961
|
+
submitting: f(r).submitting.value,
|
|
962
|
+
prev: f(r).prev,
|
|
963
|
+
next: f(r).next
|
|
852
964
|
}, () => [
|
|
853
|
-
|
|
965
|
+
f(r).steps.value && !f(r).isFirstStep.value ? (u(), m("button", {
|
|
854
966
|
key: 0,
|
|
855
967
|
type: "button",
|
|
856
968
|
class: "dcs-form__btn dcs-form__btn--prev",
|
|
857
|
-
onClick:
|
|
858
|
-
}, " Previous ")) :
|
|
859
|
-
|
|
969
|
+
onClick: L[0] || (L[0] = (d) => f(r).prev())
|
|
970
|
+
}, " Previous ")) : k("", !0),
|
|
971
|
+
f(r).steps.value && !f(r).isLastStep.value ? (u(), m("button", {
|
|
860
972
|
key: 1,
|
|
861
973
|
type: "button",
|
|
862
974
|
class: "dcs-form__btn dcs-form__btn--next",
|
|
863
|
-
onClick:
|
|
864
|
-
}, " Next ")) :
|
|
865
|
-
!
|
|
975
|
+
onClick: L[1] || (L[1] = (d) => f(r).next())
|
|
976
|
+
}, " Next ")) : k("", !0),
|
|
977
|
+
!f(r).steps.value || f(r).isLastStep.value ? (u(), m("button", {
|
|
866
978
|
key: 2,
|
|
867
979
|
type: "submit",
|
|
868
980
|
class: "dcs-form__btn dcs-form__btn--submit",
|
|
869
|
-
disabled:
|
|
870
|
-
},
|
|
981
|
+
disabled: f(r).submitting.value
|
|
982
|
+
}, F(f(r).submitting.value ? "Sending…" : q.value), 9, Ct)) : k("", !0)
|
|
871
983
|
])
|
|
872
984
|
])
|
|
873
|
-
], 40,
|
|
985
|
+
], 40, St)) : (u(), m("div", {
|
|
874
986
|
key: 0,
|
|
875
987
|
class: "dcs-form dcs-form--missing",
|
|
876
988
|
"data-form-key": e.formId
|
|
877
989
|
}, [
|
|
878
|
-
|
|
879
|
-
|
|
990
|
+
P(w.$slots, "missing", { formId: e.formId }, () => [
|
|
991
|
+
b("p", null, "Form “" + F(e.formId) + "” is not configured.", 1)
|
|
880
992
|
])
|
|
881
|
-
], 8,
|
|
993
|
+
], 8, wt));
|
|
882
994
|
}
|
|
883
|
-
}),
|
|
995
|
+
}), _t = {
|
|
884
996
|
contact: {
|
|
885
997
|
label: "Contact",
|
|
886
998
|
description: "Canonical contact form with standard contact fields."
|
|
@@ -897,7 +1009,7 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
897
1009
|
label: "Custom / free-form",
|
|
898
1010
|
description: "Flexible starter that stays fully editable for questionnaires and bespoke intake."
|
|
899
1011
|
}
|
|
900
|
-
},
|
|
1012
|
+
}, Vt = () => [
|
|
901
1013
|
{ id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
|
|
902
1014
|
{ id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
|
|
903
1015
|
{ id: "phone", type: "tel", role: "contact-phone", label: "Phone", width: "half" },
|
|
@@ -911,7 +1023,7 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
911
1023
|
width: "full",
|
|
912
1024
|
validation: { minLength: 10, maxLength: 2e3 }
|
|
913
1025
|
}
|
|
914
|
-
],
|
|
1026
|
+
], jt = () => [
|
|
915
1027
|
{ id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
|
|
916
1028
|
{ id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
|
|
917
1029
|
{ id: "phone", type: "tel", role: "contact-phone", label: "Phone", required: !0, width: "half" },
|
|
@@ -935,7 +1047,7 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
935
1047
|
width: "full",
|
|
936
1048
|
validation: { accept: ["image/*"] }
|
|
937
1049
|
}
|
|
938
|
-
],
|
|
1050
|
+
], At = () => [
|
|
939
1051
|
{ id: "name", type: "text", role: "contact-name", label: "Full name", required: !0, width: "full" },
|
|
940
1052
|
{ id: "email", type: "email", role: "contact-email", label: "Email", required: !0, width: "half" },
|
|
941
1053
|
{ id: "phone", type: "tel", role: "contact-phone", label: "Phone", width: "half" },
|
|
@@ -963,7 +1075,7 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
963
1075
|
]
|
|
964
1076
|
}
|
|
965
1077
|
}
|
|
966
|
-
],
|
|
1078
|
+
], Dt = () => [
|
|
967
1079
|
{ id: "name", type: "text", label: "Full name", required: !0, width: "full" },
|
|
968
1080
|
{ id: "email", type: "email", label: "Email", required: !0, width: "half" },
|
|
969
1081
|
{ id: "phone", type: "tel", label: "Phone", width: "half" },
|
|
@@ -976,7 +1088,7 @@ const Ie = ["data-form-field-key"], Le = ["for"], qe = {
|
|
|
976
1088
|
validation: { minLength: 10, maxLength: 2e3 }
|
|
977
1089
|
}
|
|
978
1090
|
];
|
|
979
|
-
function
|
|
1091
|
+
function Bt(e, t = {}) {
|
|
980
1092
|
switch (e ?? "custom") {
|
|
981
1093
|
case "contact":
|
|
982
1094
|
return {
|
|
@@ -988,7 +1100,7 @@ function qt(e, t = {}) {
|
|
|
988
1100
|
kind: "lead",
|
|
989
1101
|
...t.leadCategory ? { category: t.leadCategory } : {}
|
|
990
1102
|
},
|
|
991
|
-
fields:
|
|
1103
|
+
fields: Vt()
|
|
992
1104
|
};
|
|
993
1105
|
case "revenue-contractor":
|
|
994
1106
|
return {
|
|
@@ -1006,7 +1118,7 @@ function qt(e, t = {}) {
|
|
|
1006
1118
|
maxFiles: 5,
|
|
1007
1119
|
accept: ["image/*"]
|
|
1008
1120
|
},
|
|
1009
|
-
fields:
|
|
1121
|
+
fields: jt()
|
|
1010
1122
|
};
|
|
1011
1123
|
case "resume-submission":
|
|
1012
1124
|
return {
|
|
@@ -1028,7 +1140,7 @@ function qt(e, t = {}) {
|
|
|
1028
1140
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
1029
1141
|
]
|
|
1030
1142
|
},
|
|
1031
|
-
fields:
|
|
1143
|
+
fields: At()
|
|
1032
1144
|
};
|
|
1033
1145
|
default:
|
|
1034
1146
|
return {
|
|
@@ -1040,35 +1152,35 @@ function qt(e, t = {}) {
|
|
|
1040
1152
|
kind: "lead",
|
|
1041
1153
|
...t.leadCategory ? { category: t.leadCategory } : {}
|
|
1042
1154
|
},
|
|
1043
|
-
fields:
|
|
1155
|
+
fields: Dt()
|
|
1044
1156
|
};
|
|
1045
1157
|
}
|
|
1046
1158
|
}
|
|
1047
1159
|
export {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1160
|
+
zt as DcsForm,
|
|
1161
|
+
it as DcsFormCheckbox,
|
|
1162
|
+
Qe as DcsFormCheckboxGroup,
|
|
1163
|
+
nt as DcsFormDate,
|
|
1164
|
+
D as DcsFormFieldWrapper,
|
|
1165
|
+
pt as DcsFormFile,
|
|
1166
|
+
yt as DcsFormHidden,
|
|
1167
|
+
$t as DcsFormHtmlBlock,
|
|
1168
|
+
He as DcsFormRadio,
|
|
1169
|
+
Ft as DcsFormSection,
|
|
1170
|
+
Ke as DcsFormSelect,
|
|
1171
|
+
J as DcsFormText,
|
|
1172
|
+
Ee as DcsFormTextarea,
|
|
1173
|
+
_t as STANDARD_FORM_PRESET_META,
|
|
1174
|
+
Bt as buildStandardFormDefinition,
|
|
1175
|
+
ae as hasErrors,
|
|
1176
|
+
U as isFieldVisible,
|
|
1177
|
+
qe as loadFormDefinitions,
|
|
1178
|
+
Et as parseFormYaml,
|
|
1179
|
+
me as submitFormValues,
|
|
1180
|
+
ce as useDcsForm,
|
|
1181
|
+
oe as validateField,
|
|
1182
|
+
se as validateForm,
|
|
1183
|
+
Ie as validateFormDefinition,
|
|
1184
|
+
Le as warnIfInvalid
|
|
1073
1185
|
};
|
|
1074
1186
|
//# sourceMappingURL=index.js.map
|