@duffcloudservices/site-forms 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,280 +1,305 @@
1
- import { reactive as X, ref as j, computed as f, defineComponent as S, createElementBlock as h, openBlock as d, normalizeClass as Z, renderSlot as k, createCommentVNode as w, createTextVNode as E, toDisplayString as b, createBlock as L, withCtx as I, createElementVNode as y, Fragment as D, renderList as R, watch as N, onMounted as ee, unref as c, resolveDynamicComponent as te } from "vue";
2
- import ie from "ajv";
3
- import re from "ajv-formats";
4
- import J from "js-yaml";
5
- function z(e, t) {
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 ne(e, t) {
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, r = t == null || i === "" || Array.isArray(t) && t.length === 0;
12
- if (e.required && r)
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 (r) return;
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 n = e.validation;
18
- if (n) {
19
- if (typeof i == "string") {
20
- if (n.minLength != null && i.length < n.minLength)
21
- return `${e.label} must be at least ${n.minLength} characters`;
22
- if (n.maxLength != null && i.length > n.maxLength)
23
- return `${e.label} must be at most ${n.maxLength} characters`;
24
- if (n.regex)
25
- try {
26
- if (!new RegExp(n.regex).test(i))
27
- return `${e.label} is invalid`;
28
- } catch {
29
- }
30
- }
31
- if (typeof t == "number") {
32
- if (n.min != null && t < n.min)
33
- return `${e.label} must be at least ${n.min}`;
34
- if (n.max != null && t > n.max)
35
- return `${e.label} must be at most ${n.max}`;
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 (e.type === "file" && n.accept && n.accept.length > 0) {
38
- const l = t;
39
- if (l && typeof File < "u" && l instanceof File && !n.accept.some((u) => u.startsWith(".") ? l.name.toLowerCase().endsWith(u.toLowerCase()) : l.type === u))
40
- return `${e.label} must be one of: ${n.accept.join(", ")}`;
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 oe(e, t, i) {
45
- const r = {}, n = i ? new Set(i) : null;
46
- for (const l of e.fields) {
47
- if (n && !n.has(l.id) || !z(l, t)) continue;
48
- const s = ne(l, t[l.id]);
49
- s && (r[l.id] = s);
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 r;
59
+ return i;
52
60
  }
53
- function se(e) {
61
+ function ae(e) {
54
62
  return Object.values(e).some((t) => !!t);
55
63
  }
56
- function W(e) {
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 i of e.fields)
59
- i.defaultValue !== void 0 ? t[i.id] = i.defaultValue : i.type === "checkbox" ? t[i.id] = !1 : i.type === "multiselect" || i.type === "checkbox-group" ? t[i.id] = [] : t[i.id] = "";
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 ae(e) {
63
- const { definition: t } = e, i = X(W(t)), r = j({}), n = j({}), l = j(!1), s = j(!1), u = j(null), a = j(0), o = f(() => t.fields), m = f(
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
- ), g = f(() => {
66
- const p = m.value;
67
- return p ? p[a.value] ?? null : null;
68
- }), $ = f(() => {
69
- const p = g.value;
70
- if (!p) return t.fields;
71
- const x = new Set(p.fieldIds);
72
- return t.fields.filter((A) => x.has(A.id));
73
- }), C = f(() => a.value === 0), T = f(() => {
74
- const p = m.value;
75
- return !p || a.value >= p.length - 1;
76
- }), K = f(
77
- () => $.value.filter((p) => z(p, i))
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 v(p, x) {
80
- i[p] = x, r.value[p] && (r.value = { ...r.value, [p]: void 0 });
100
+ function R(y, S) {
101
+ n[y] = S, i.value[y] && (i.value = { ...i.value, [y]: void 0 });
81
102
  }
82
- function V(p) {
83
- n.value = { ...n.value, [p]: !0 };
103
+ function w(y) {
104
+ s.value = { ...s.value, [y]: !0 };
84
105
  }
85
- function F(p) {
86
- const x = oe(t, i, p);
87
- if (p) {
88
- const A = { ...r.value };
89
- for (const B of p)
90
- A[B] = x[B];
91
- r.value = A;
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
- r.value = x;
94
- return !se(r.value);
114
+ i.value = S;
115
+ return !ae(i.value);
95
116
  }
96
- function P() {
97
- const p = g.value?.fieldIds;
98
- return F(p);
117
+ function d() {
118
+ const y = v.value?.fieldIds;
119
+ return L(y);
99
120
  }
100
- function M() {
101
- return F();
121
+ function g() {
122
+ return L();
102
123
  }
103
- function _() {
104
- return !m.value || !P() ? !1 : a.value < m.value.length - 1 ? (a.value++, !0) : !1;
124
+ function x() {
125
+ return !r.value || !d() ? !1 : l.value < r.value.length - 1 ? (l.value++, !0) : !1;
105
126
  }
106
- function G() {
107
- a.value > 0 && a.value--;
127
+ function C() {
128
+ l.value > 0 && l.value--;
108
129
  }
109
- function Y() {
110
- const p = W(t);
111
- for (const x of Object.keys(i))
112
- delete i[x];
113
- Object.assign(i, p), r.value = {}, n.value = {}, l.value = !1, s.value = !1, u.value = null, a.value = 0;
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 p = {};
117
- for (const x of t.fields)
118
- x.type === "section-heading" || x.type === "html-block" || z(x, i) && (p[x.id] = i[x.id]);
119
- return p;
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: i,
123
- errors: r,
124
- touched: n,
125
- submitting: l,
126
- submitted: s,
127
- submitError: u,
128
- fields: o,
129
- steps: m,
130
- currentStepIndex: a,
131
- currentStep: g,
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: C,
134
- isLastStep: T,
135
- visibleFields: K,
136
- setValue: v,
137
- touch: V,
138
- validateCurrentScope: P,
139
- validateAll: M,
140
- next: _,
141
- prev: G,
142
- reset: Y,
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 le(e) {
147
- const { apiBase: t, siteSlug: i, payload: r } = e, n = e.retries ?? 1, l = e.fetchImpl ?? fetch, s = `${t.replace(/\/$/, "")}/sites/${encodeURIComponent(
148
- i
149
- )}/forms/${encodeURIComponent(r.formId)}/submissions`, u = Object.values(r.values).some(
150
- (m) => typeof File < "u" && m instanceof File
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 g = u ? { method: "POST", body: de(r) } : {
174
+ const v = a ? { method: "POST", body: fe(i) } : {
156
175
  method: "POST",
157
176
  headers: { "Content-Type": "application/json" },
158
- body: JSON.stringify(r)
159
- }, $ = await l(s, g);
160
- if (o = $.status, $.ok) {
161
- const C = await ue($);
162
- return { payload: r, response: C };
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 C = await ce($);
184
+ const q = await ye($);
166
185
  throw {
167
- payload: r,
186
+ payload: i,
168
187
  status: $.status,
169
- error: new Error(`Submission failed (${$.status}): ${C}`)
188
+ error: new Error(`Submission failed (${$.status}): ${q}`)
170
189
  };
171
190
  }
172
- a = new Error(`Server error ${$.status}`);
173
- } catch (g) {
174
- if (g && typeof g == "object" && "payload" in g && "error" in g)
175
- throw g;
176
- a = g;
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: r,
180
- status: o,
181
- error: a ?? new Error("Submission failed")
198
+ payload: i,
199
+ status: h,
200
+ error: l ?? new Error("Submission failed")
182
201
  };
183
202
  }
184
- function de(e) {
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 [i, r] of Object.entries(e.values))
188
- if (r != null)
189
- if (r instanceof File)
190
- t.append(`values[${i}]`, r);
191
- else if (Array.isArray(r))
192
- for (const n of r) t.append(`values[${i}][]`, String(n));
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[${i}]`, String(r));
216
+ t.append(`values[${n}]`, String(i));
195
217
  return t;
196
218
  }
197
- async function ue(e) {
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 ce(e) {
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: me,
213
- $id: fe,
214
- title: pe,
215
- type: he,
216
- description: ye,
217
- required: be,
218
- properties: ge,
219
- definitions: ve
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 O = null;
222
- function xe() {
223
- if (O) return O;
224
- const e = new ie({
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 re(e), O = e.compile(Fe), O;
254
+ return ne(e), B = e.compile(Se), B;
230
255
  }
231
- function $e(e) {
232
- const t = xe(), i = t(e);
233
- return { valid: i, errors: i ? [] : t.errors ?? [] };
256
+ function Ie(e) {
257
+ const t = Pe(), n = t(e);
258
+ return { valid: n, errors: n ? [] : t.errors ?? [] };
234
259
  }
235
- function we(e, t, i) {
236
- const r = $e(t);
237
- return !r.valid && i && console.warn(
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
- r.errors
240
- ), r;
264
+ i.errors
265
+ ), i;
241
266
  }
242
- function ke(e) {
267
+ function qe(e) {
243
268
  const t = {};
244
- for (const [i, r] of Object.entries(e)) {
245
- const n = Se(i), l = Pe(r);
246
- l && (t[l.formId ?? n] = l);
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 Se(e) {
275
+ function Ce(e) {
251
276
  return (e.split("/").pop() ?? e).replace(/\.ya?ml$/i, "");
252
277
  }
253
- function Pe(e) {
278
+ function Ve(e) {
254
279
  if (!e) return null;
255
280
  if (typeof e == "string")
256
- return J.load(e);
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 Pt(e) {
264
- return J.load(e);
288
+ function Et(e) {
289
+ return Y.load(e);
265
290
  }
266
- const Ie = ["data-form-field-key"], Le = ["for"], qe = {
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
- }, Ce = {
295
+ }, Me = {
271
296
  key: 0,
272
297
  class: "dcs-form-field__help"
273
- }, Ve = {
298
+ }, Te = {
274
299
  key: 0,
275
300
  class: "dcs-form-field__error",
276
301
  role: "alert"
277
- }, q = /* @__PURE__ */ S({
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, i) => (d(), h("div", {
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
- k(t.$slots, "label", {}, () => [
290
- e.field.type !== "checkbox" && e.field.type !== "hidden" && e.field.type !== "section-heading" && e.field.type !== "html-block" ? (d(), h("label", {
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
- E(b(e.field.label) + " ", 1),
296
- e.field.required ? (d(), h("span", qe, "*")) : w("", !0)
297
- ], 8, Le)) : w("", !0)
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
- k(t.$slots, "default"),
300
- k(t.$slots, "help", {}, () => [
301
- e.field.helpText ? (d(), h("p", Ce, b(e.field.helpText), 1)) : w("", !0)
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
- k(t.$slots, "error", {}, () => [
304
- e.error ? (d(), h("p", Ve, b(e.error), 1)) : w("", !0)
328
+ P(t.$slots, "error", {}, () => [
329
+ e.error ? (u(), m("p", Te, F(e.error), 1)) : k("", !0)
305
330
  ])
306
- ], 10, Ie));
331
+ ], 10, je));
307
332
  }
308
- }), je = ["id", "type", "name", "value", "placeholder", "required", "aria-invalid", "aria-describedby"], H = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`), l = f(() => {
318
- switch (i.field.type) {
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 (s, u) => (d(), L(q, {
352
+ return (c, a) => (u(), A(D, {
328
353
  field: e.field,
329
354
  error: e.error,
330
- "input-id": n.value
355
+ "input-id": s.value
331
356
  }, {
332
- default: I(() => [
333
- k(s.$slots, "input", {
334
- id: n.value,
357
+ default: j(() => [
358
+ P(c.$slots, "input", {
359
+ id: s.value,
335
360
  value: e.modelValue,
336
- onInput: (a) => r("update:modelValue", a.target.value),
337
- onBlur: () => r("blur")
361
+ onInput: (l) => i("update:modelValue", l.target.value),
362
+ onBlur: () => i("blur")
338
363
  }, () => [
339
- y("input", {
340
- id: n.value,
364
+ b("input", {
365
+ id: s.value,
341
366
  class: "dcs-form-input",
342
- type: l.value,
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 ? `${n.value}-error` : void 0,
349
- onInput: u[0] || (u[0] = (a) => r("update:modelValue", a.target.value)),
350
- onBlur: u[1] || (u[1] = (a) => r("blur"))
351
- }, null, 40, je)
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
- }), De = ["id", "name", "placeholder", "required", "aria-invalid", "value"], Te = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`);
367
- return (l, s) => (d(), L(q, {
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": n.value
395
+ "input-id": s.value
371
396
  }, {
372
- default: I(() => [
373
- k(l.$slots, "input", {
374
- id: n.value,
397
+ default: j(() => [
398
+ P(o.$slots, "input", {
399
+ id: s.value,
375
400
  value: e.modelValue,
376
- onInput: (u) => r("update:modelValue", u.target.value),
377
- onBlur: () => r("blur")
401
+ onInput: (a) => i("update:modelValue", a.target.value),
402
+ onBlur: () => i("blur")
378
403
  }, () => [
379
- y("textarea", {
380
- id: n.value,
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: s[0] || (s[0] = (u) => r("update:modelValue", u.target.value)),
389
- onBlur: s[1] || (s[1] = (u) => r("blur"))
390
- }, null, 40, De)
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
- }), Me = ["id", "name", "required", "multiple", "aria-invalid", "value"], Ae = {
421
+ }), ze = ["id", "name", "required", "multiple", "aria-invalid", "value"], _e = {
397
422
  key: 0,
398
423
  value: "",
399
424
  disabled: ""
400
- }, Oe = ["value"], Ee = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`), l = f(() => i.field.type === "multiselect"), s = f(() => i.field.options ?? []);
410
- function u(a) {
411
- const o = a.target;
412
- if (l.value) {
413
- const m = [];
414
- for (const g of Array.from(o.selectedOptions)) m.push(g.value);
415
- r("update:modelValue", m);
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
- r("update:modelValue", o.value);
442
+ i("update:modelValue", h.value);
418
443
  }
419
- return (a, o) => (d(), L(q, {
444
+ return (l, h) => (u(), A(D, {
420
445
  field: e.field,
421
446
  error: e.error,
422
- "input-id": n.value
447
+ "input-id": s.value
423
448
  }, {
424
- default: I(() => [
425
- k(a.$slots, "input", {
426
- id: n.value,
449
+ default: j(() => [
450
+ P(l.$slots, "input", {
451
+ id: s.value,
427
452
  value: e.modelValue,
428
- options: s.value,
429
- onChange: u
453
+ options: c.value,
454
+ onChange: a
430
455
  }, () => [
431
- y("select", {
432
- id: n.value,
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: l.value,
461
+ multiple: o.value,
437
462
  "aria-invalid": !!e.error,
438
- value: e.modelValue ?? (l.value ? [] : ""),
439
- onChange: u,
440
- onBlur: o[0] || (o[0] = (m) => r("blur"))
463
+ value: e.modelValue ?? (o.value ? [] : ""),
464
+ onChange: a,
465
+ onBlur: h[0] || (h[0] = (r) => i("blur"))
441
466
  }, [
442
- l.value ? w("", !0) : (d(), h("option", Ae, b(e.field.placeholder ?? "Select…"), 1)),
443
- (d(!0), h(D, null, R(s.value, (m) => (d(), h("option", {
444
- key: m.value,
445
- value: m.value
446
- }, b(m.label), 9, Oe))), 128))
447
- ], 40, Me)
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
- }), Re = ["aria-required", "aria-invalid"], Ke = { class: "sr-only" }, ze = ["name", "value", "checked", "onChange"], Be = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`), l = f(() => i.field.options ?? []);
463
- return (s, u) => (d(), L(q, {
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": n.value
491
+ "input-id": s.value
467
492
  }, {
468
- default: I(() => [
469
- y("fieldset", {
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
- y("legend", Ke, b(e.field.label), 1),
476
- (d(!0), h(D, null, R(l.value, (a) => (d(), h("label", {
477
- key: a.value,
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
- y("input", {
505
+ b("input", {
481
506
  type: "radio",
482
507
  name: e.field.id,
483
- value: a.value,
484
- checked: e.modelValue === a.value,
485
- onChange: (o) => r("update:modelValue", a.value)
486
- }, null, 40, ze),
487
- y("span", null, b(a.label), 1)
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, Re)
514
+ ], 8, We)
490
515
  ]),
491
516
  _: 1
492
517
  }, 8, ["field", "error", "input-id"]));
493
518
  }
494
- }), Ne = ["aria-required", "aria-invalid"], We = { class: "sr-only" }, He = ["name", "value", "checked", "onChange"], Ue = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`), l = f(() => i.field.options ?? []), s = f(() => i.modelValue ?? []);
504
- function u(a, o) {
505
- const m = new Set(s.value);
506
- o ? m.add(a) : m.delete(a), r("update:modelValue", Array.from(m));
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 (a, o) => (d(), L(q, {
533
+ return (l, h) => (u(), A(D, {
509
534
  field: e.field,
510
535
  error: e.error,
511
- "input-id": n.value
536
+ "input-id": s.value
512
537
  }, {
513
- default: I(() => [
514
- y("fieldset", {
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
- y("legend", We, b(e.field.label), 1),
520
- (d(!0), h(D, null, R(l.value, (m) => (d(), h("label", {
521
- key: m.value,
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
- y("input", {
549
+ b("input", {
525
550
  type: "checkbox",
526
551
  name: `${e.field.id}[]`,
527
- value: m.value,
528
- checked: s.value.includes(m.value),
529
- onChange: (g) => u(m.value, g.target.checked)
530
- }, null, 40, He),
531
- y("span", null, b(m.label), 1)
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, Ne)
558
+ ], 8, Je)
534
559
  ]),
535
560
  _: 1
536
561
  }, 8, ["field", "error", "input-id"]));
537
562
  }
538
- }), Je = { class: "sr-only" }, _e = ["for"], Ge = ["id", "name", "checked", "required", "aria-invalid"], Ye = {
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
- }, Qe = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`);
551
- return (l, s) => (d(), L(q, {
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": n.value
579
+ "input-id": s.value
555
580
  }, {
556
- label: I(() => [
557
- y("span", Je, b(e.field.label), 1)
581
+ label: j(() => [
582
+ b("span", Xe, F(e.field.label), 1)
558
583
  ]),
559
- default: I(() => [
560
- y("label", {
584
+ default: j(() => [
585
+ b("label", {
561
586
  class: "dcs-form-checkbox-single",
562
- for: n.value
587
+ for: s.value
563
588
  }, [
564
- y("input", {
565
- id: n.value,
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: s[0] || (s[0] = (u) => r("update:modelValue", u.target.checked))
572
- }, null, 40, Ge),
573
- y("span", null, [
574
- E(b(e.field.label), 1),
575
- e.field.required ? (d(), h("span", Ye, " *")) : w("", !0)
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, _e)
602
+ ], 8, Ze)
578
603
  ]),
579
604
  _: 1
580
605
  }, 8, ["field", "error", "input-id"]));
581
606
  }
582
- }), Xe = ["id", "name", "required", "aria-invalid", "value"], Ze = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`);
592
- return (l, s) => (d(), L(q, {
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": n.value
620
+ "input-id": s.value
596
621
  }, {
597
- default: I(() => [
598
- y("input", {
599
- id: n.value,
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: s[0] || (s[0] = (u) => r("update:modelValue", u.target.value)),
607
- onBlur: s[1] || (s[1] = (u) => r("blur"))
608
- }, null, 40, Xe)
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
- }), et = ["id", "name", "required", "aria-invalid", "accept"], tt = /* @__PURE__ */ S({
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 i = e, r = t, n = f(() => `field-${i.field.id}`), l = f(() => (i.field.validation?.accept ?? []).join(","));
623
- function s(u) {
624
- const a = u.target;
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
- return (u, a) => (d(), L(q, {
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": n.value
707
+ "input-id": s.value
631
708
  }, {
632
- default: I(() => [
633
- y("input", {
634
- id: n.value,
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: l.value || void 0,
641
- onChange: s
642
- }, null, 40, et)
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
- }), it = ["name", "value", "data-form-field-key"], rt = /* @__PURE__ */ S({
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, i) => (d(), h("input", {
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, it));
763
+ }, null, 8, ht));
660
764
  }
661
- }), nt = ["data-form-field-key"], ot = { class: "dcs-form-section__heading" }, st = {
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
- }, at = /* @__PURE__ */ S({
768
+ }, Ft = /* @__PURE__ */ I({
665
769
  __name: "DcsFormSection",
666
770
  props: {
667
771
  field: {}
668
772
  },
669
773
  setup(e) {
670
- return (t, i) => (d(), h("div", {
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
- k(t.$slots, "default", {}, () => [
675
- y("h3", ot, b(e.field.label), 1),
676
- e.field.helpText ? (d(), h("p", st, b(e.field.helpText), 1)) : w("", !0)
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, nt));
782
+ ], 8, bt));
679
783
  }
680
- }), lt = ["data-form-field-key", "innerHTML"], dt = /* @__PURE__ */ S({
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, i) => (d(), h("div", {
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, lt));
794
+ }, null, 8, xt));
691
795
  }
692
- }), U = {}, ut = ["data-form-key"], ct = ["data-form-key"], mt = ["data-form-key"], ft = {
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
- }, pt = { class: "dcs-form__fields" }, ht = {
800
+ }, It = { class: "dcs-form__fields" }, Lt = {
697
801
  key: 1,
698
802
  class: "dcs-form__submit-error",
699
803
  role: "alert"
700
- }, yt = { class: "dcs-form__actions" }, bt = ["disabled"], It = /* @__PURE__ */ S({
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, { emit: t }) {
712
- const i = e, r = t, n = /* @__PURE__ */ Object.assign({}), l = f(
713
- () => ke(i.formsModules ?? n)
714
- ), s = f(() => i.definitionOverride ? i.definitionOverride : l.value[i.formId] ?? null), u = !1;
715
- N(
716
- s,
717
- (v) => {
718
- v && we(i.formId, v, u);
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 a = f(
723
- () => s.value ?? {
826
+ const h = p(
827
+ () => a.value ?? {
724
828
  formId: i.formId,
725
829
  submission: { kind: "lead" },
726
830
  fields: []
727
831
  }
728
- ), o = ae({ definition: a.value });
729
- N(
730
- () => a.value,
731
- () => o.reset()
832
+ ), r = ce({ definition: h.value });
833
+ W(
834
+ () => h.value,
835
+ () => r.reset()
732
836
  );
733
- const m = f(
734
- () => i.apiBase ?? U?.VITE_DCS_PUBLIC_API ?? ""
735
- ), g = f(
736
- () => i.siteSlug ?? U?.VITE_DCS_SITE_SLUG ?? ""
737
- ), $ = f(
738
- () => a.value.submitLabel ?? "Send"
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 C(v) {
741
- switch (v.type) {
844
+ function M(w) {
845
+ switch (w.type) {
742
846
  case "text":
743
847
  case "email":
744
848
  case "tel":
745
- return H;
849
+ return J;
746
850
  case "textarea":
747
- return Te;
851
+ return Ee;
748
852
  case "select":
749
853
  case "multiselect":
750
- return Ee;
854
+ return Ke;
751
855
  case "radio":
752
- return Be;
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 Ze;
862
+ return nt;
759
863
  case "file":
760
- return tt;
864
+ return pt;
761
865
  case "hidden":
762
- return rt;
866
+ return yt;
763
867
  case "section-heading":
764
- return at;
868
+ return Ft;
765
869
  case "html-block":
766
- return dt;
870
+ return $t;
767
871
  default:
768
- return H;
872
+ return J;
769
873
  }
770
874
  }
771
- const T = j(null);
772
- async function K(v) {
773
- if (v.preventDefault(), !s.value) return;
774
- if (!o.validateAll()) {
775
- r("validation-error", o.errors.value);
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
- o.submitting.value = !0, o.submitError.value = null;
779
- const F = {
889
+ r.submitting.value = !0, r.submitError.value = null;
890
+ const d = {
780
891
  formId: i.formId,
781
- values: o.collectSubmissionValues(),
892
+ values: r.collectSubmissionValues(),
782
893
  captchaToken: i.captchaToken
783
894
  };
784
895
  try {
785
- const P = await le({
786
- apiBase: m.value,
787
- siteSlug: g.value,
788
- payload: F
896
+ const g = await me({
897
+ apiBase: v.value,
898
+ siteSlug: $.value,
899
+ payload: d
789
900
  });
790
- o.submitted.value = !0, r("submit-success", P);
791
- } catch (P) {
792
- const M = P;
793
- o.submitError.value = M.error?.message ?? "Submission failed", r("submit-error", M);
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
- o.submitting.value = !1;
906
+ r.submitting.value = !1;
796
907
  }
797
908
  }
798
- return ee(() => {
799
- s.value || console.warn(
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
- }), (v, V) => s.value ? c(o).submitted.value ? (d(), h("div", {
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
- k(v.$slots, "success", { definition: s.value }, () => [
808
- y("p", null, b(s.value.successMessage ?? "Thanks — we received your message."), 1)
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, ct)) : (d(), h("form", {
921
+ ], 8, kt)) : (u(), m("form", {
811
922
  key: 2,
812
923
  ref_key: "formEl",
813
- ref: T,
924
+ ref: O,
814
925
  class: "dcs-form",
815
926
  "data-form-key": e.formId,
816
927
  novalidate: "",
817
- onSubmit: K
928
+ onSubmit: R
818
929
  }, [
819
- k(v.$slots, "header", { definition: s.value }),
820
- c(o).steps.value ? (d(), h("div", ft, [
821
- k(v.$slots, "progress", {
822
- current: c(o).currentStepIndex.value,
823
- total: c(o).steps.value.length,
824
- step: c(o).currentStep.value
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
- y("p", null, [
827
- E(" Step " + b(c(o).currentStepIndex.value + 1) + " of " + b(c(o).steps.value.length) + " ", 1),
828
- c(o).currentStep.value ? (d(), h(D, { key: 0 }, [
829
- E(" — " + b(c(o).currentStep.value.title), 1)
830
- ], 64)) : w("", !0)
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
- ])) : w("", !0),
834
- y("div", pt, [
835
- (d(!0), h(D, null, R(c(o).visibleFields.value, (F) => (d(), L(te(C(F)), {
836
- key: F.id,
837
- field: F,
838
- "model-value": c(o).values[F.id],
839
- error: c(o).errors.value[F.id],
840
- "onUpdate:modelValue": (P) => c(o).setValue(F.id, P),
841
- onBlur: (P) => c(o).touch(F.id)
842
- }, null, 40, ["field", "model-value", "error", "onUpdate:modelValue", "onBlur"]))), 128))
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
- c(o).submitError.value ? (d(), h("div", ht, b(c(o).submitError.value), 1)) : w("", !0),
845
- y("div", yt, [
846
- k(v.$slots, "actions", {
847
- isFirstStep: c(o).isFirstStep.value,
848
- isLastStep: c(o).isLastStep.value,
849
- submitting: c(o).submitting.value,
850
- prev: c(o).prev,
851
- next: c(o).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
- c(o).steps.value && !c(o).isFirstStep.value ? (d(), h("button", {
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: V[0] || (V[0] = (F) => c(o).prev())
858
- }, " Previous ")) : w("", !0),
859
- c(o).steps.value && !c(o).isLastStep.value ? (d(), h("button", {
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: V[1] || (V[1] = (F) => c(o).next())
864
- }, " Next ")) : w("", !0),
865
- !c(o).steps.value || c(o).isLastStep.value ? (d(), h("button", {
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: c(o).submitting.value
870
- }, b(c(o).submitting.value ? "Sending…" : $.value), 9, bt)) : w("", !0)
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, mt)) : (d(), h("div", {
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
- k(v.$slots, "missing", { formId: e.formId }, () => [
879
- y("p", null, "Form “" + b(e.formId) + "” is not configured.", 1)
990
+ P(w.$slots, "missing", { formId: e.formId }, () => [
991
+ b("p", null, "Form “" + F(e.formId) + "” is not configured.", 1)
880
992
  ])
881
- ], 8, ut));
993
+ ], 8, wt));
882
994
  }
883
- }), Lt = {
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
- }, gt = () => [
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
- ], vt = () => [
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
- ], Ft = () => [
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
- ], xt = () => [
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 qt(e, t = {}) {
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: gt()
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: vt()
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: Ft()
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: xt()
1155
+ fields: Dt()
1044
1156
  };
1045
1157
  }
1046
1158
  }
1047
1159
  export {
1048
- It as DcsForm,
1049
- Qe as DcsFormCheckbox,
1050
- Ue as DcsFormCheckboxGroup,
1051
- Ze as DcsFormDate,
1052
- q as DcsFormFieldWrapper,
1053
- tt as DcsFormFile,
1054
- rt as DcsFormHidden,
1055
- dt as DcsFormHtmlBlock,
1056
- Be as DcsFormRadio,
1057
- at as DcsFormSection,
1058
- Ee as DcsFormSelect,
1059
- H as DcsFormText,
1060
- Te as DcsFormTextarea,
1061
- Lt as STANDARD_FORM_PRESET_META,
1062
- qt as buildStandardFormDefinition,
1063
- se as hasErrors,
1064
- z as isFieldVisible,
1065
- ke as loadFormDefinitions,
1066
- Pt as parseFormYaml,
1067
- le as submitFormValues,
1068
- ae as useDcsForm,
1069
- ne as validateField,
1070
- oe as validateForm,
1071
- $e as validateFormDefinition,
1072
- we as warnIfInvalid
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