strapi-plugin-hubspot 0.4.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +116 -4
  3. package/dist/_chunks/Forms-BF4_zA-m.js +1007 -0
  4. package/dist/_chunks/Forms-CW1KFO9U.mjs +989 -0
  5. package/dist/_chunks/{HubspotObjectInput-DutelVp6.mjs → HubspotObjectInput-CpxeM2J0.mjs} +4 -16
  6. package/dist/_chunks/{HubspotObjectInput-Br2jTFkG.js → HubspotObjectInput-hjAYWtaU.js} +4 -16
  7. package/dist/_chunks/HubspotPropertyInput-C-52Lu24.mjs +227 -0
  8. package/dist/_chunks/HubspotPropertyInput-OSeIBaRC.js +245 -0
  9. package/dist/_chunks/Settings-2V7TH5CX.mjs +321 -0
  10. package/dist/_chunks/Settings-DcA9M5Tw.js +339 -0
  11. package/dist/_chunks/en-CJ9wYHgN.mjs +197 -0
  12. package/dist/_chunks/en-C_-N8cjv.js +197 -0
  13. package/dist/_chunks/fr-BHObPoUp.js +197 -0
  14. package/dist/_chunks/fr-DttDn-2Y.mjs +197 -0
  15. package/dist/_chunks/{Settings-CQsB0B2T.mjs → index-C1UJXE_o.mjs} +576 -317
  16. package/dist/_chunks/{index-BUlB1rK9.js → index-CMzg-x5i.js} +12 -4
  17. package/dist/_chunks/{Settings-CvbKVl-t.js → index-Dgbid2LK.js} +580 -322
  18. package/dist/_chunks/{index-CKoBcX1T.mjs → index-DkGOTp8p.mjs} +12 -4
  19. package/dist/_chunks/{objectLabels-dM4o67Fz.mjs → objectLabels-D9UEuOGr.mjs} +1 -1
  20. package/dist/_chunks/{objectLabels-B76t8qqg.js → objectLabels-Th7aYW-B.js} +1 -1
  21. package/dist/_chunks/useHubspotSchema-Bgrgg8Mt.js +75 -0
  22. package/dist/_chunks/useHubspotSchema-DQkQcA_V.mjs +58 -0
  23. package/dist/admin/index.js +2 -1
  24. package/dist/admin/index.mjs +2 -1
  25. package/dist/admin/src/builder/ConditionEditor.d.ts +17 -0
  26. package/dist/admin/src/builder/FieldPanel.d.ts +16 -0
  27. package/dist/admin/src/builder/types.d.ts +90 -0
  28. package/dist/admin/src/index.d.ts +1 -0
  29. package/dist/admin/src/pages/FormEditor.d.ts +9 -0
  30. package/dist/admin/src/pages/Forms.d.ts +4 -0
  31. package/dist/admin/src/pages/FormsList.d.ts +8 -0
  32. package/dist/admin/src/useHubspotSchema.d.ts +28 -0
  33. package/dist/server/index.js +880 -5
  34. package/dist/server/index.mjs +880 -5
  35. package/dist/server/src/__tests__/conditions.test.d.ts +1 -0
  36. package/dist/server/src/__tests__/forms.test.d.ts +1 -0
  37. package/dist/server/src/__tests__/formsSubmit.test.d.ts +1 -0
  38. package/dist/server/src/__tests__/importLegacy.test.d.ts +1 -0
  39. package/dist/server/src/conditions.d.ts +88 -0
  40. package/dist/server/src/content-types.d.ts +161 -9
  41. package/dist/server/src/forms.d.ts +81 -0
  42. package/dist/server/src/formsAdmin.d.ts +39 -0
  43. package/dist/server/src/importLegacy.d.ts +25 -0
  44. package/dist/server/src/index.d.ts +214 -16
  45. package/dist/shared/types.d.ts +103 -0
  46. package/dist/shared/types.js +2 -0
  47. package/dist/shared/types.mjs +2 -0
  48. package/package.json +23 -6
  49. package/dist/_chunks/HubspotPropertyInput-CK6Yj9rC.mjs +0 -177
  50. package/dist/_chunks/HubspotPropertyInput-DIPDPRXJ.js +0 -195
  51. package/dist/_chunks/en-D4ueeUcJ.mjs +0 -82
  52. package/dist/_chunks/en-PBG7-vMR.js +0 -82
  53. package/dist/_chunks/fr-DL4wdV8I.js +0 -82
  54. package/dist/_chunks/fr-DYnxVmIO.mjs +0 -82
@@ -0,0 +1,1007 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const objectLabels = require("./objectLabels-Th7aYW-B.js");
6
+ const index$1 = require("./index-Dgbid2LK.js");
7
+ const designSystem = require("@strapi/design-system");
8
+ const icons = require("@strapi/icons");
9
+ const admin = require("@strapi/strapi/admin");
10
+ const index = require("./index-CMzg-x5i.js");
11
+ const useHubspotSchema = require("./useHubspotSchema-Bgrgg8Mt.js");
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
15
+ if (e) {
16
+ for (const k in e) {
17
+ if (k !== "default") {
18
+ const d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: () => e[k]
22
+ });
23
+ }
24
+ }
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+ const React__namespace = /* @__PURE__ */ _interopNamespace(React);
30
+ const OPERATORS = ["eq", "neq", "contains", "empty", "notEmpty", "gt", "lt"];
31
+ const NEEDS_VALUE = /* @__PURE__ */ new Set(["eq", "neq", "contains", "gt", "lt"]);
32
+ const ConditionEditor = ({ condition, candidates, onChange }) => {
33
+ const { formatMessage } = objectLabels.useIntl();
34
+ const t = (id, defaultMessage) => formatMessage({ id: index.getTranslation(id), defaultMessage });
35
+ const opLabel = {
36
+ eq: t("condition.op.eq", "is"),
37
+ neq: t("condition.op.neq", "is not"),
38
+ contains: t("condition.op.contains", "contains"),
39
+ empty: t("condition.op.empty", "is empty"),
40
+ notEmpty: t("condition.op.notEmpty", "is filled"),
41
+ gt: t("condition.op.gt", "is greater than"),
42
+ lt: t("condition.op.lt", "is less than")
43
+ };
44
+ const rules = condition?.rules ?? [];
45
+ const logic = condition?.logic ?? "and";
46
+ const commit = (nextRules, nextLogic = logic) => onChange(nextRules.length ? { logic: nextLogic, rules: nextRules } : null);
47
+ const updateRule = (index2, patch) => commit(rules.map((rule, i) => i === index2 ? { ...rule, ...patch } : rule));
48
+ const addRule = () => {
49
+ const first = candidates[0];
50
+ if (!first) return;
51
+ commit([...rules, { field: first.id, operator: "eq", value: "" }]);
52
+ };
53
+ if (!candidates.length) {
54
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t("condition.no-candidates", "Conditions can only read fields placed earlier in the form.") });
55
+ }
56
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
57
+ rules.map((rule, index2) => {
58
+ const target = candidates.find((f) => f.id === rule.field);
59
+ const targetOptions = target?.options?.filter((o) => o.value) ?? [];
60
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral100", hasRadius: true, padding: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
61
+ index2 > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: logic === "and" ? t("condition.and", "AND") : t("condition.or", "OR") }),
62
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", minWidth: "120px", children: /* @__PURE__ */ jsxRuntime.jsx(
63
+ designSystem.SingleSelect,
64
+ {
65
+ "aria-label": t("condition.field", "Field"),
66
+ value: rule.field,
67
+ onChange: (v) => updateRule(index2, { field: String(v), value: "" }),
68
+ children: candidates.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: f.id, children: f.label || f.name }, f.id))
69
+ }
70
+ ) }),
71
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", minWidth: "110px", children: /* @__PURE__ */ jsxRuntime.jsx(
72
+ designSystem.SingleSelect,
73
+ {
74
+ "aria-label": t("condition.operator", "Operator"),
75
+ value: rule.operator,
76
+ onChange: (v) => updateRule(index2, { operator: v }),
77
+ children: OPERATORS.map((op) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: op, children: opLabel[op] }, op))
78
+ }
79
+ ) }),
80
+ NEEDS_VALUE.has(rule.operator) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", minWidth: "110px", children: targetOptions.length ? /* @__PURE__ */ jsxRuntime.jsx(
81
+ designSystem.SingleSelect,
82
+ {
83
+ "aria-label": t("condition.value", "Value"),
84
+ value: rule.value ?? "",
85
+ onChange: (v) => updateRule(index2, { value: String(v) }),
86
+ children: targetOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: o.value, children: o.label || o.value }, o.value))
87
+ }
88
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
89
+ designSystem.TextInput,
90
+ {
91
+ "aria-label": t("condition.value", "Value"),
92
+ value: rule.value ?? "",
93
+ onChange: (e) => updateRule(index2, { value: e.target.value })
94
+ }
95
+ ) }),
96
+ /* @__PURE__ */ jsxRuntime.jsx(
97
+ designSystem.IconButton,
98
+ {
99
+ label: t("condition.remove-rule", "Remove this rule"),
100
+ onClick: () => commit(rules.filter((_, i) => i !== index2)),
101
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
102
+ }
103
+ )
104
+ ] }) }, index2);
105
+ }),
106
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
107
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: addRule, children: t("condition.add-rule", "Add a rule") }),
108
+ rules.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(
109
+ designSystem.SingleSelect,
110
+ {
111
+ "aria-label": t("condition.logic", "Logic"),
112
+ value: logic,
113
+ onChange: (v) => commit(rules, v),
114
+ children: [
115
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "and", children: t("condition.logic-and", "All rules (AND)") }),
116
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "or", children: t("condition.logic-or", "At least one (OR)") })
117
+ ]
118
+ }
119
+ )
120
+ ] })
121
+ ] });
122
+ };
123
+ const FIELD_TYPES = [
124
+ "text",
125
+ "email",
126
+ "tel",
127
+ "number",
128
+ "website",
129
+ "textarea",
130
+ "select",
131
+ "checkbox",
132
+ "radio"
133
+ ];
134
+ let counter = 0;
135
+ const makeId = (prefix) => `${prefix}_${Date.now().toString(36)}${(counter += 1).toString(36)}${Math.random().toString(36).slice(2, 6)}`;
136
+ function fieldsBefore(definition, beforeId) {
137
+ const out = [];
138
+ for (const step of definition.steps) {
139
+ if (step.id === beforeId) return out;
140
+ for (const field of step.fields) {
141
+ if (field.id === beforeId) return out;
142
+ out.push(field);
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+ const slugifyName = (label) => label.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
148
+ const FieldPanel = ({ field, candidates, problem, onChange }) => {
149
+ const intl = objectLabels.useIntl();
150
+ const { formatMessage } = intl;
151
+ const { schema, refresh, refreshing } = useHubspotSchema.useHubspotSchema();
152
+ const t = (id, defaultMessage, values) => formatMessage({ id: index.getTranslation(id), defaultMessage }, values);
153
+ const object = field.hubspot?.object || "contact";
154
+ const properties = (schema?.properties ?? []).filter((p) => p.object === object);
155
+ const selected = properties.find((p) => p.name === field.hubspot?.property);
156
+ const isChoice = field.type === "select" || field.type === "radio";
157
+ const setHubspot = (patch) => onChange({ hubspot: { ...field.hubspot, ...patch } });
158
+ const importOptions = () => {
159
+ if (!selected?.options?.length) return;
160
+ onChange({
161
+ options: selected.options.map((o) => ({ value: o.value, label: o.label ?? o.value }))
162
+ });
163
+ };
164
+ const deepLink = schema?.portalId && schema?.uiDomain && selected ? `https://${schema.uiDomain}/property-settings/${schema.portalId}/properties?type=0-${{ contact: 1, company: 2, deal: 3, ticket: 5 }[object] ?? 1}&search=${encodeURIComponent(selected.name)}` : null;
165
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
166
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
167
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.label", "Label") }),
168
+ /* @__PURE__ */ jsxRuntime.jsx(
169
+ designSystem.TextInput,
170
+ {
171
+ value: field.label,
172
+ onChange: (e) => {
173
+ const label = e.target.value;
174
+ const auto = !field.name || field.name === slugifyName(field.label);
175
+ onChange({ label, ...auto ? { name: slugifyName(label) } : {} });
176
+ }
177
+ }
178
+ )
179
+ ] }),
180
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
181
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.name", "Payload key") }),
182
+ /* @__PURE__ */ jsxRuntime.jsx(
183
+ designSystem.TextInput,
184
+ {
185
+ value: field.name,
186
+ onChange: (e) => onChange({ name: slugifyName(e.target.value) || e.target.value })
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t("panel.name-hint", "Key of the answer in the submission — unique in the form.") })
190
+ ] }),
191
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
192
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.type", "Type") }),
193
+ /* @__PURE__ */ jsxRuntime.jsx(
194
+ designSystem.SingleSelect,
195
+ {
196
+ value: field.type,
197
+ onChange: (v) => onChange({ type: v }),
198
+ children: FIELD_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: type, children: type }, type))
199
+ }
200
+ )
201
+ ] }),
202
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
204
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.placeholder", "Placeholder") }),
205
+ /* @__PURE__ */ jsxRuntime.jsx(
206
+ designSystem.TextInput,
207
+ {
208
+ value: field.placeholder ?? "",
209
+ onChange: (e) => onChange({ placeholder: e.target.value })
210
+ }
211
+ )
212
+ ] }) }),
213
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
214
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.width", "Width") }),
215
+ /* @__PURE__ */ jsxRuntime.jsxs(
216
+ designSystem.SingleSelect,
217
+ {
218
+ value: field.width ?? "full",
219
+ onChange: (v) => onChange({ width: v }),
220
+ children: [
221
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "full", children: t("panel.width-full", "Full") }),
222
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "half", children: t("panel.width-half", "Half") })
223
+ ]
224
+ }
225
+ )
226
+ ] }) })
227
+ ] }),
228
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
229
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.help", "Help text") }),
230
+ /* @__PURE__ */ jsxRuntime.jsx(
231
+ designSystem.TextInput,
232
+ {
233
+ value: field.helpText ?? "",
234
+ onChange: (e) => onChange({ helpText: e.target.value })
235
+ }
236
+ )
237
+ ] }),
238
+ /* @__PURE__ */ jsxRuntime.jsx(
239
+ designSystem.Checkbox,
240
+ {
241
+ checked: Boolean(field.required),
242
+ onCheckedChange: (checked) => onChange({ required: checked === true }),
243
+ children: t("panel.required", "Required")
244
+ }
245
+ ),
246
+ isChoice && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
247
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("panel.options", "Choices") }),
248
+ (field.options ?? []).map((option, index2) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
249
+ /* @__PURE__ */ jsxRuntime.jsx(
250
+ designSystem.TextInput,
251
+ {
252
+ "aria-label": t("panel.option-value", "Value"),
253
+ placeholder: t("panel.option-value", "Value"),
254
+ value: option.value,
255
+ onChange: (e) => onChange({
256
+ options: (field.options ?? []).map(
257
+ (o, i) => i === index2 ? { ...o, value: e.target.value } : o
258
+ )
259
+ })
260
+ }
261
+ ),
262
+ /* @__PURE__ */ jsxRuntime.jsx(
263
+ designSystem.TextInput,
264
+ {
265
+ "aria-label": t("panel.option-label", "Label"),
266
+ placeholder: t("panel.option-label", "Label"),
267
+ value: option.label ?? "",
268
+ onChange: (e) => onChange({
269
+ options: (field.options ?? []).map(
270
+ (o, i) => i === index2 ? { ...o, label: e.target.value } : o
271
+ )
272
+ })
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsxRuntime.jsx(
276
+ designSystem.IconButton,
277
+ {
278
+ label: t("panel.option-remove", "Remove"),
279
+ onClick: () => onChange({ options: (field.options ?? []).filter((_, i) => i !== index2) }),
280
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
281
+ }
282
+ )
283
+ ] }, index2)),
284
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
285
+ /* @__PURE__ */ jsxRuntime.jsx(
286
+ designSystem.Button,
287
+ {
288
+ variant: "tertiary",
289
+ size: "S",
290
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
291
+ onClick: () => onChange({ options: [...field.options ?? [], { value: "", label: "" }] }),
292
+ children: t("panel.option-add", "Add a choice")
293
+ }
294
+ ),
295
+ selected?.type === "enumeration" && Boolean(selected.options?.length) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", size: "S", onClick: importOptions, children: t("panel.option-import", "Import the {count} options from HubSpot", {
296
+ count: selected.options.length
297
+ }) })
298
+ ] })
299
+ ] }),
300
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}),
301
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
302
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
303
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("panel.hubspot", "HubSpot") }),
304
+ /* @__PURE__ */ jsxRuntime.jsx(
305
+ designSystem.IconButton,
306
+ {
307
+ label: t("panel.refresh", "Refresh the portal schema"),
308
+ onClick: refresh,
309
+ disabled: refreshing,
310
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowClockwise, {})
311
+ }
312
+ )
313
+ ] }),
314
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
315
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.object", "Object") }),
316
+ /* @__PURE__ */ jsxRuntime.jsx(
317
+ designSystem.SingleSelect,
318
+ {
319
+ value: object,
320
+ onChange: (v) => {
321
+ setHubspot({ object: String(v), property: void 0 });
322
+ },
323
+ children: (schema?.objects ?? ["contact", "company"]).map((name) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: name, children: objectLabels.objectLabel(intl, name) }, name))
324
+ }
325
+ )
326
+ ] }),
327
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
328
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.property", "Property") }),
329
+ /* @__PURE__ */ jsxRuntime.jsx(
330
+ designSystem.Combobox,
331
+ {
332
+ value: field.hubspot?.property ?? "",
333
+ onChange: (v) => setHubspot({ property: v || void 0 }),
334
+ onClear: () => setHubspot({ property: void 0 }),
335
+ placeholder: t("panel.property-placeholder", "Search a property…"),
336
+ children: properties.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ComboboxOption, { value: p.name, children: [
337
+ p.group ? `${p.group} · ` : "",
338
+ p.label,
339
+ " (",
340
+ p.name,
341
+ ")"
342
+ ] }, p.name))
343
+ }
344
+ ),
345
+ problem && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger600", children: t(`panel.problem-${problem.code}`, "Invalid mapping ({code})", {
346
+ code: problem.code
347
+ }) }),
348
+ !field.hubspot?.property && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t("panel.property-hint", "Without a property, the answer is sent under the payload key.") })
349
+ ] }),
350
+ deepLink && /* @__PURE__ */ jsxRuntime.jsx("a", { href: deepLink, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "primary600", children: t("panel.view-in-hubspot", "View in HubSpot ↗") }) })
351
+ ] }),
352
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}),
353
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
354
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("panel.condition", "Show this field only when…") }),
355
+ /* @__PURE__ */ jsxRuntime.jsx(
356
+ ConditionEditor,
357
+ {
358
+ condition: field.visibleIf,
359
+ candidates,
360
+ onChange: (visibleIf) => onChange({ visibleIf })
361
+ }
362
+ )
363
+ ] })
364
+ ] });
365
+ };
366
+ const newField = (label) => ({
367
+ id: makeId("fld"),
368
+ name: "",
369
+ label,
370
+ type: "text",
371
+ width: "full",
372
+ hubspot: { object: "contact" }
373
+ });
374
+ const newStep = () => ({ id: makeId("stp"), title: "", fields: [] });
375
+ const FormEditor = () => {
376
+ const { formatMessage } = objectLabels.useIntl();
377
+ const { get, post, put } = admin.useFetchClient();
378
+ const { documentId } = index$1.useParams();
379
+ const [searchParams, setSearchParams] = index$1.useSearchParams();
380
+ const locale = searchParams.get("locale") ?? void 0;
381
+ const [entry, setEntry] = React__namespace.useState(null);
382
+ const [published, setPublished] = React__namespace.useState(false);
383
+ const [locales, setLocales] = React__namespace.useState([]);
384
+ const [selection, setSelection] = React__namespace.useState({ kind: "form" });
385
+ const [dirty, setDirty] = React__namespace.useState(false);
386
+ const [busy, setBusy] = React__namespace.useState(false);
387
+ const [errors, setErrors] = React__namespace.useState([]);
388
+ const [problems, setProblems] = React__namespace.useState([]);
389
+ const [feedback, setFeedback] = React__namespace.useState(null);
390
+ const t = (id, defaultMessage, values) => formatMessage({ id: index.getTranslation(id), defaultMessage }, values);
391
+ const query = locale ? `?locale=${locale}` : "";
392
+ React__namespace.useEffect(() => {
393
+ get("/i18n/locales").then(({ data }) => Array.isArray(data) && setLocales(data)).catch(() => setLocales([]));
394
+ }, [get]);
395
+ React__namespace.useEffect(() => {
396
+ let cancelled = false;
397
+ setEntry(null);
398
+ setSelection({ kind: "form" });
399
+ setErrors([]);
400
+ setProblems([]);
401
+ get(
402
+ `/${index.PLUGIN_ID}/builder/forms/${documentId}${query}`
403
+ ).then(({ data }) => {
404
+ if (cancelled) return;
405
+ setEntry(data.form);
406
+ setPublished(data.published);
407
+ setDirty(false);
408
+ }).catch(async () => {
409
+ if (cancelled || !locale) return;
410
+ try {
411
+ const { data } = await get(
412
+ `/${index.PLUGIN_ID}/builder/forms/${documentId}`
413
+ );
414
+ if (cancelled) return;
415
+ setEntry({ ...data.form, locale });
416
+ setPublished(false);
417
+ setDirty(true);
418
+ setFeedback({
419
+ tone: "success",
420
+ text: t("editor.locale-copied", "New locale started from the default structure — save to keep it.")
421
+ });
422
+ } catch {
423
+ if (!cancelled) setFeedback({ tone: "danger", text: t("editor.load-error", "Could not load the form.") });
424
+ }
425
+ });
426
+ return () => {
427
+ cancelled = true;
428
+ };
429
+ }, [documentId, locale]);
430
+ if (!entry) {
431
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: t("editor.loading", "Loading…") }) });
432
+ }
433
+ const definition = entry.definition ?? { version: 1, steps: [] };
434
+ const patchEntry = (patch) => {
435
+ setEntry((current) => current ? { ...current, ...patch } : current);
436
+ setDirty(true);
437
+ };
438
+ const patchDefinition = (mutate) => patchEntry({ definition: { version: 1, steps: mutate(definition.steps) } });
439
+ const patchField = (fieldId, patch) => patchDefinition(
440
+ (steps) => steps.map((step) => ({
441
+ ...step,
442
+ fields: step.fields.map((f) => f.id === fieldId ? { ...f, ...patch } : f)
443
+ }))
444
+ );
445
+ const patchStep = (stepId, patch) => patchDefinition((steps) => steps.map((s) => s.id === stepId ? { ...s, ...patch } : s));
446
+ const moveField = (stepIndex, fieldIndex, delta) => patchDefinition((steps) => {
447
+ const next = steps.map((s) => ({ ...s, fields: [...s.fields] }));
448
+ const from = next[stepIndex];
449
+ const [field] = from.fields.splice(fieldIndex, 1);
450
+ const target = fieldIndex + delta;
451
+ if (target >= 0 && target <= from.fields.length) {
452
+ from.fields.splice(target, 0, field);
453
+ } else if (delta === -1 && next[stepIndex - 1]) {
454
+ next[stepIndex - 1].fields.push(field);
455
+ } else if (delta === 1 && next[stepIndex + 1]) {
456
+ next[stepIndex + 1].fields.unshift(field);
457
+ } else {
458
+ from.fields.splice(fieldIndex, 0, field);
459
+ }
460
+ return next;
461
+ });
462
+ const moveStep = (stepIndex, delta) => patchDefinition((steps) => {
463
+ const target = stepIndex + delta;
464
+ if (target < 0 || target >= steps.length) return steps;
465
+ const next = [...steps];
466
+ const [step] = next.splice(stepIndex, 1);
467
+ next.splice(target, 0, step);
468
+ return next;
469
+ });
470
+ const save = async () => {
471
+ setBusy(true);
472
+ setFeedback(null);
473
+ setErrors([]);
474
+ try {
475
+ const { data } = await put(
476
+ `/${index.PLUGIN_ID}/builder/forms/${documentId}${query}`,
477
+ {
478
+ name: entry.name,
479
+ title: entry.title,
480
+ subtitle: entry.subtitle,
481
+ nextLabel: entry.nextLabel,
482
+ submitLabel: entry.submitLabel,
483
+ successMessage: entry.successMessage,
484
+ definition
485
+ }
486
+ );
487
+ setProblems(data.problems ?? []);
488
+ setDirty(false);
489
+ setFeedback({ tone: "success", text: t("editor.saved", "Draft saved.") });
490
+ return true;
491
+ } catch (err) {
492
+ const data = err.response?.data;
493
+ if (data?.errors) {
494
+ setErrors(data.errors);
495
+ setFeedback({ tone: "danger", text: t("editor.invalid", "The structure has problems — see the flagged cards.") });
496
+ } else {
497
+ setFeedback({ tone: "danger", text: t("editor.save-error", "Could not save the form.") });
498
+ }
499
+ return false;
500
+ } finally {
501
+ setBusy(false);
502
+ }
503
+ };
504
+ const publish = async () => {
505
+ if (dirty && !await save()) return;
506
+ setBusy(true);
507
+ setFeedback(null);
508
+ try {
509
+ await post(`/${index.PLUGIN_ID}/builder/forms/${documentId}/publish${query}`);
510
+ setPublished(true);
511
+ setErrors([]);
512
+ setProblems([]);
513
+ setFeedback({ tone: "success", text: t("editor.published", "Form published.") });
514
+ } catch (err) {
515
+ const data = err.response?.data;
516
+ setErrors(data?.errors ?? []);
517
+ setProblems(data?.problems ?? []);
518
+ setFeedback({
519
+ tone: "danger",
520
+ text: t("editor.publish-blocked", "Publishing is blocked — fix the flagged mappings first.")
521
+ });
522
+ } finally {
523
+ setBusy(false);
524
+ }
525
+ };
526
+ const unpublish = async () => {
527
+ setBusy(true);
528
+ try {
529
+ await post(`/${index.PLUGIN_ID}/builder/forms/${documentId}/unpublish${query}`);
530
+ setPublished(false);
531
+ setFeedback({ tone: "success", text: t("editor.unpublished", "Form unpublished.") });
532
+ } catch {
533
+ setFeedback({ tone: "danger", text: t("editor.unpublish-error", "Could not unpublish.") });
534
+ } finally {
535
+ setBusy(false);
536
+ }
537
+ };
538
+ const fieldFlags = (fieldId) => ({
539
+ error: errors.find((e) => e.fieldId === fieldId),
540
+ problem: problems.find((p) => p.fieldId === fieldId)
541
+ });
542
+ const selectedField = selection.kind === "field" ? definition.steps.flatMap((s) => s.fields).find((f) => f.id === selection.fieldId) : void 0;
543
+ const selectedStep = selection.kind === "step" ? definition.steps.find((s) => s.id === selection.stepId) : void 0;
544
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 6, children: [
545
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", paddingBottom: 4, children: [
546
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", children: [
547
+ /* @__PURE__ */ jsxRuntime.jsx(index$1.Link, { to: `/plugins/${index.PLUGIN_ID}`, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: t("editor.back", "Back to the list"), tag: "span", children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}) }) }),
548
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
549
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
550
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", tag: "h1", children: entry.name }),
551
+ published ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: t("forms.published", "Published") }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: t("forms.draft", "Draft") }),
552
+ dirty && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: t("editor.unsaved", "Unsaved changes") })
553
+ ] }),
554
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
555
+ "/",
556
+ entry.slug
557
+ ] })
558
+ ] })
559
+ ] }),
560
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
561
+ locales.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
562
+ designSystem.SingleSelect,
563
+ {
564
+ "aria-label": t("editor.locale", "Locale"),
565
+ value: locale ?? locales.find((l) => l.isDefault)?.code ?? "",
566
+ onChange: (v) => {
567
+ const code = String(v);
568
+ const isDefault = locales.find((l) => l.isDefault)?.code === code;
569
+ setSearchParams(isDefault ? {} : { locale: code });
570
+ },
571
+ children: locales.map((l) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: l.code, children: l.name }, l.code))
572
+ }
573
+ ),
574
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: save, disabled: busy || !dirty, children: t("editor.save", "Save") }),
575
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: publish, disabled: busy, children: t("editor.publish", "Publish") }),
576
+ published && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: unpublish, disabled: busy, children: t("editor.unpublish", "Unpublish") })
577
+ ] })
578
+ ] }),
579
+ feedback && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: feedback.tone === "success" ? "success600" : "danger600", children: feedback.text }) }),
580
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 6, alignItems: "flex-start", children: [
581
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
582
+ definition.steps.map((step, stepIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
583
+ designSystem.Box,
584
+ {
585
+ background: "neutral0",
586
+ hasRadius: true,
587
+ shadow: "tableShadow",
588
+ padding: 4,
589
+ borderColor: selection.kind === "step" && selection.stepId === step.id ? "primary600" : void 0,
590
+ borderStyle: selection.kind === "step" && selection.stepId === step.id ? "solid" : void 0,
591
+ borderWidth: selection.kind === "step" && selection.stepId === step.id ? "1px" : void 0,
592
+ children: [
593
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", paddingBottom: 2, children: [
594
+ /* @__PURE__ */ jsxRuntime.jsxs(
595
+ designSystem.Flex,
596
+ {
597
+ gap: 2,
598
+ alignItems: "center",
599
+ onClick: () => setSelection({ kind: "step", stepId: step.id }),
600
+ style: { cursor: "pointer" },
601
+ children: [
602
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "delta", children: [
603
+ t("editor.step", "Step {n}", { n: stepIndex + 1 }),
604
+ step.title ? ` — ${step.title}` : ""
605
+ ] }),
606
+ step.visibleIf?.rules?.length ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "alternative100", textColor: "alternative700", children: t("editor.conditional", "Conditional") }) : null,
607
+ errors.some((e) => e.stepId === step.id) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: t("editor.flag-error", "To fix") })
608
+ ]
609
+ }
610
+ ),
611
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
612
+ /* @__PURE__ */ jsxRuntime.jsx(
613
+ designSystem.IconButton,
614
+ {
615
+ label: t("editor.step-up", "Move the step up"),
616
+ onClick: () => moveStep(stepIndex, -1),
617
+ disabled: stepIndex === 0,
618
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUp, {})
619
+ }
620
+ ),
621
+ /* @__PURE__ */ jsxRuntime.jsx(
622
+ designSystem.IconButton,
623
+ {
624
+ label: t("editor.step-down", "Move the step down"),
625
+ onClick: () => moveStep(stepIndex, 1),
626
+ disabled: stepIndex === definition.steps.length - 1,
627
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDown, {})
628
+ }
629
+ ),
630
+ /* @__PURE__ */ jsxRuntime.jsx(
631
+ designSystem.IconButton,
632
+ {
633
+ label: t("editor.step-delete", "Delete the step and its fields"),
634
+ onClick: () => {
635
+ setSelection({ kind: "form" });
636
+ patchDefinition((steps) => steps.filter((s) => s.id !== step.id));
637
+ },
638
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
639
+ }
640
+ )
641
+ ] })
642
+ ] }),
643
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
644
+ step.fields.map((field, fieldIndex) => {
645
+ const flags = fieldFlags(field.id);
646
+ const isSelected = selection.kind === "field" && selection.fieldId === field.id;
647
+ return /* @__PURE__ */ jsxRuntime.jsx(
648
+ designSystem.Box,
649
+ {
650
+ background: isSelected ? "primary100" : "neutral100",
651
+ hasRadius: true,
652
+ padding: 3,
653
+ onClick: () => setSelection({ kind: "field", fieldId: field.id }),
654
+ style: { cursor: "pointer" },
655
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
656
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
657
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: field.label || field.name || "—" }),
658
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: field.type }),
659
+ field.required && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: t("editor.required", "required") }),
660
+ field.hubspot?.property && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
661
+ field.hubspot.object ?? "contact",
662
+ ".",
663
+ field.hubspot.property
664
+ ] }),
665
+ field.visibleIf?.rules?.length ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "alternative100", textColor: "alternative700", children: t("editor.conditional", "Conditional") }) : null,
666
+ (flags.error || flags.problem) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "danger100", textColor: "danger700", children: flags.error?.code ?? flags.problem?.code })
667
+ ] }),
668
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, onClick: (e) => e.stopPropagation(), children: [
669
+ /* @__PURE__ */ jsxRuntime.jsx(
670
+ designSystem.IconButton,
671
+ {
672
+ label: t("editor.field-up", "Move up"),
673
+ onClick: () => moveField(stepIndex, fieldIndex, -1),
674
+ disabled: stepIndex === 0 && fieldIndex === 0,
675
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUp, {})
676
+ }
677
+ ),
678
+ /* @__PURE__ */ jsxRuntime.jsx(
679
+ designSystem.IconButton,
680
+ {
681
+ label: t("editor.field-down", "Move down"),
682
+ onClick: () => moveField(stepIndex, fieldIndex, 1),
683
+ disabled: stepIndex === definition.steps.length - 1 && fieldIndex === step.fields.length - 1,
684
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDown, {})
685
+ }
686
+ ),
687
+ /* @__PURE__ */ jsxRuntime.jsx(
688
+ designSystem.IconButton,
689
+ {
690
+ label: t("editor.field-delete", "Delete the field"),
691
+ onClick: () => {
692
+ setSelection({ kind: "form" });
693
+ patchStep(step.id, {
694
+ fields: step.fields.filter((f) => f.id !== field.id)
695
+ });
696
+ },
697
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
698
+ }
699
+ )
700
+ ] })
701
+ ] })
702
+ },
703
+ field.id
704
+ );
705
+ }),
706
+ /* @__PURE__ */ jsxRuntime.jsx(
707
+ designSystem.Button,
708
+ {
709
+ variant: "tertiary",
710
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
711
+ onClick: () => {
712
+ const field = newField(t("editor.new-field", "New field"));
713
+ patchStep(step.id, { fields: [...step.fields, field] });
714
+ setSelection({ kind: "field", fieldId: field.id });
715
+ },
716
+ children: t("editor.add-field", "Add a field")
717
+ }
718
+ )
719
+ ] })
720
+ ]
721
+ },
722
+ step.id
723
+ )),
724
+ /* @__PURE__ */ jsxRuntime.jsx(
725
+ designSystem.Button,
726
+ {
727
+ variant: "secondary",
728
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
729
+ onClick: () => {
730
+ const step = newStep();
731
+ patchDefinition((steps) => [...steps, step]);
732
+ setSelection({ kind: "step", stepId: step.id });
733
+ },
734
+ children: t("editor.add-step", "Add a step")
735
+ }
736
+ )
737
+ ] }) }),
738
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 4, width: "380px", style: { flexShrink: 0 }, children: selectedField ? /* @__PURE__ */ jsxRuntime.jsx(
739
+ FieldPanel,
740
+ {
741
+ field: selectedField,
742
+ candidates: fieldsBefore(definition, selectedField.id),
743
+ problem: problems.find((p) => p.fieldId === selectedField.id),
744
+ onChange: (patch) => patchField(selectedField.id, patch)
745
+ }
746
+ ) : selectedStep ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
747
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("editor.step-panel", "Step") }),
748
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
749
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.step-title", "Title") }),
750
+ /* @__PURE__ */ jsxRuntime.jsx(
751
+ designSystem.TextInput,
752
+ {
753
+ value: selectedStep.title ?? "",
754
+ onChange: (e) => patchStep(selectedStep.id, { title: e.target.value })
755
+ }
756
+ )
757
+ ] }),
758
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
759
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.step-description", "Description") }),
760
+ /* @__PURE__ */ jsxRuntime.jsx(
761
+ designSystem.Textarea,
762
+ {
763
+ value: selectedStep.description ?? "",
764
+ onChange: (e) => patchStep(selectedStep.id, { description: e.target.value })
765
+ }
766
+ )
767
+ ] }),
768
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("editor.step-condition", "Show this step only when…") }),
769
+ /* @__PURE__ */ jsxRuntime.jsx(
770
+ ConditionEditor,
771
+ {
772
+ condition: selectedStep.visibleIf,
773
+ candidates: fieldsBefore(definition, selectedStep.id),
774
+ onChange: (visibleIf) => patchStep(selectedStep.id, { visibleIf })
775
+ }
776
+ )
777
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
778
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("editor.form-panel", "Form") }),
779
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
780
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.form-name", "Internal name") }),
781
+ /* @__PURE__ */ jsxRuntime.jsx(
782
+ designSystem.TextInput,
783
+ {
784
+ value: entry.name,
785
+ onChange: (e) => patchEntry({ name: e.target.value })
786
+ }
787
+ )
788
+ ] }),
789
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
790
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.form-title", "Title") }),
791
+ /* @__PURE__ */ jsxRuntime.jsx(
792
+ designSystem.TextInput,
793
+ {
794
+ value: entry.title ?? "",
795
+ onChange: (e) => patchEntry({ title: e.target.value })
796
+ }
797
+ )
798
+ ] }),
799
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
800
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.form-subtitle", "Subtitle") }),
801
+ /* @__PURE__ */ jsxRuntime.jsx(
802
+ designSystem.Textarea,
803
+ {
804
+ value: entry.subtitle ?? "",
805
+ onChange: (e) => patchEntry({ subtitle: e.target.value })
806
+ }
807
+ )
808
+ ] }),
809
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
810
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
811
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.form-next", "Next label") }),
812
+ /* @__PURE__ */ jsxRuntime.jsx(
813
+ designSystem.TextInput,
814
+ {
815
+ value: entry.nextLabel ?? "",
816
+ onChange: (e) => patchEntry({ nextLabel: e.target.value })
817
+ }
818
+ )
819
+ ] }) }),
820
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
821
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.form-submit", "Submit label") }),
822
+ /* @__PURE__ */ jsxRuntime.jsx(
823
+ designSystem.TextInput,
824
+ {
825
+ value: entry.submitLabel ?? "",
826
+ onChange: (e) => patchEntry({ submitLabel: e.target.value })
827
+ }
828
+ )
829
+ ] }) })
830
+ ] }),
831
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
832
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("editor.form-success", "Success message") }),
833
+ /* @__PURE__ */ jsxRuntime.jsx(
834
+ designSystem.Textarea,
835
+ {
836
+ value: entry.successMessage ?? "",
837
+ onChange: (e) => patchEntry({ successMessage: e.target.value })
838
+ }
839
+ )
840
+ ] }),
841
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
842
+ "editor.form-hint",
843
+ "Select a step or a field to edit it. The public endpoint serves this form at /api/hubspot/forms/{slug}.",
844
+ { slug: entry.slug }
845
+ ) })
846
+ ] }) })
847
+ ] })
848
+ ] });
849
+ };
850
+ const FormsList = () => {
851
+ const { formatMessage } = objectLabels.useIntl();
852
+ const { get, post, del } = admin.useFetchClient();
853
+ const navigate = index$1.useNavigate();
854
+ const [rows, setRows] = React__namespace.useState(null);
855
+ const [name, setName] = React__namespace.useState("");
856
+ const [busy, setBusy] = React__namespace.useState(false);
857
+ const [error, setError] = React__namespace.useState(null);
858
+ const [sources, setSources] = React__namespace.useState([]);
859
+ const [sourceId, setSourceId] = React__namespace.useState("");
860
+ const t = (id, defaultMessage, values) => formatMessage({ id: index.getTranslation(id), defaultMessage }, values);
861
+ const load = React__namespace.useCallback(async () => {
862
+ const { data } = await get(`/${index.PLUGIN_ID}/builder/forms`);
863
+ setRows(data.forms);
864
+ }, [get]);
865
+ React__namespace.useEffect(() => {
866
+ load().catch(() => setError(t("forms.load-error", "Could not load the forms.")));
867
+ get(
868
+ `/${index.PLUGIN_ID}/builder/import/sources`
869
+ ).then(({ data }) => setSources(data.sources ?? [])).catch(() => setSources([]));
870
+ }, [load]);
871
+ const runImport = async () => {
872
+ if (!sourceId) return;
873
+ setBusy(true);
874
+ setError(null);
875
+ try {
876
+ const { data } = await post(`/${index.PLUGIN_ID}/builder/import`, {
877
+ documentId: sourceId
878
+ });
879
+ navigate(data.documentId);
880
+ } catch {
881
+ setError(t("forms.import-error", "Could not import the form."));
882
+ setBusy(false);
883
+ }
884
+ };
885
+ const create = async () => {
886
+ if (!name.trim()) return;
887
+ setBusy(true);
888
+ setError(null);
889
+ try {
890
+ const { data } = await post(
891
+ `/${index.PLUGIN_ID}/builder/forms`,
892
+ { name: name.trim() }
893
+ );
894
+ navigate(data.form.documentId);
895
+ } catch {
896
+ setError(t("forms.create-error", "Could not create the form."));
897
+ setBusy(false);
898
+ }
899
+ };
900
+ const remove = async (row) => {
901
+ if (!window.confirm(t("forms.delete-confirm", 'Delete "{name}" and all its locales?', { name: row.name }))) {
902
+ return;
903
+ }
904
+ setBusy(true);
905
+ try {
906
+ await del(`/${index.PLUGIN_ID}/builder/forms/${row.documentId}`);
907
+ await load();
908
+ } catch {
909
+ setError(t("forms.delete-error", "Could not delete the form."));
910
+ } finally {
911
+ setBusy(false);
912
+ }
913
+ };
914
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, children: [
915
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "flex-end", paddingBottom: 6, children: [
916
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
917
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: t("forms.title", "HubSpot forms") }),
918
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", tag: "p", children: t(
919
+ "forms.subtitle",
920
+ "Multi-step forms with conditional fields, mapped to your portal's real properties."
921
+ ) })
922
+ ] }),
923
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
924
+ /* @__PURE__ */ jsxRuntime.jsx(
925
+ designSystem.TextInput,
926
+ {
927
+ "aria-label": t("forms.name-label", "Name of the new form"),
928
+ placeholder: t("forms.name-placeholder", "Name of the new form"),
929
+ value: name,
930
+ onChange: (e) => setName(e.target.value),
931
+ onKeyDown: (e) => e.key === "Enter" && create()
932
+ }
933
+ ),
934
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: create, disabled: busy || !name.trim(), children: t("forms.create", "Create") })
935
+ ] })
936
+ ] }),
937
+ error && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: error }) }),
938
+ sources.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 4, marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", justifyContent: "space-between", children: [
939
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
940
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: t("forms.import-title", "Import an existing form") }),
941
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: t(
942
+ "forms.import-hint",
943
+ "Converts a legacy content-type form (all locales) into a draft here — same slug, source untouched. Re-importing overwrites the draft."
944
+ ) })
945
+ ] }),
946
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
947
+ /* @__PURE__ */ jsxRuntime.jsx(
948
+ designSystem.SingleSelect,
949
+ {
950
+ "aria-label": t("forms.import-source", "Form to import"),
951
+ placeholder: t("forms.import-source", "Form to import"),
952
+ value: sourceId,
953
+ onChange: (v) => setSourceId(String(v)),
954
+ children: sources.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: s.documentId, children: [
955
+ s.name,
956
+ " ",
957
+ s.slug ? `(${s.slug})` : ""
958
+ ] }, s.documentId))
959
+ }
960
+ ),
961
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: runImport, disabled: busy || !sourceId, children: t("forms.import", "Import") })
962
+ ] })
963
+ ] }) }),
964
+ !rows ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: t("forms.loading", "Loading…") }) : rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("forms.empty", "No form yet — name one above to open the builder.") }) }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 6, rowCount: rows.length, children: [
965
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
966
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-name", "Name") }) }),
967
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-slug", "Slug") }) }),
968
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-structure", "Structure") }) }),
969
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-status", "Status") }) }),
970
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-updated", "Updated") }) }),
971
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-actions", "") }) })
972
+ ] }) }),
973
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
974
+ designSystem.Tr,
975
+ {
976
+ onClick: () => navigate(row.documentId),
977
+ style: { cursor: "pointer" },
978
+ children: [
979
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: row.name }) }),
980
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: row.slug }) }),
981
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("forms.structure", "{steps} steps · {fields} fields", {
982
+ steps: row.steps,
983
+ fields: row.fields
984
+ }) }) }),
985
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: row.published ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: t("forms.published", "Published") }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: t("forms.draft", "Draft") }) }),
986
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: row.updatedAt ? new Date(row.updatedAt).toLocaleString() : "—" }) }),
987
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsx(
988
+ designSystem.IconButton,
989
+ {
990
+ label: t("forms.delete", "Delete"),
991
+ onClick: () => remove(row),
992
+ disabled: busy,
993
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
994
+ }
995
+ ) })
996
+ ]
997
+ },
998
+ row.documentId
999
+ )) })
1000
+ ] })
1001
+ ] });
1002
+ };
1003
+ const Forms = () => /* @__PURE__ */ jsxRuntime.jsxs(index$1.Routes, { children: [
1004
+ /* @__PURE__ */ jsxRuntime.jsx(index$1.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(FormsList, {}) }),
1005
+ /* @__PURE__ */ jsxRuntime.jsx(index$1.Route, { path: ":documentId", element: /* @__PURE__ */ jsxRuntime.jsx(FormEditor, {}) })
1006
+ ] });
1007
+ exports.default = Forms;