strapi-plugin-hubspot 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/_chunks/{Forms-CKA6ukB6.mjs → Forms-77Mornxt.mjs} +114 -18
- package/dist/_chunks/{Forms-HMZbTk_B.js → Forms-B8kkPY0c.js} +113 -17
- package/dist/_chunks/{HubspotFormInput-CKYRcNNU.mjs → HubspotFormInput-BVknnlmR.mjs} +1 -1
- package/dist/_chunks/{HubspotFormInput-BequMb12.js → HubspotFormInput-kx3LlQYF.js} +1 -1
- package/dist/_chunks/{HubspotObjectInput-V3uhGK8k.mjs → HubspotObjectInput-B-Y1PZ6c.mjs} +3 -3
- package/dist/_chunks/{HubspotObjectInput-D-FGwsyS.js → HubspotObjectInput-BOO1PvOj.js} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-DrBsx9jQ.mjs → HubspotPropertyInput-Drxn_Ddt.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-Dn1QvOcu.js → HubspotPropertyInput-awfD1opH.js} +3 -3
- package/dist/_chunks/{Settings-Cgp7M1Cv.js → Settings-BT1NhvxO.js} +2 -2
- package/dist/_chunks/{Settings-BjkNbyna.mjs → Settings-DrujhMxS.mjs} +2 -2
- package/dist/_chunks/{en-D5Ch9YFo.js → en-B8adhJhO.js} +10 -3
- package/dist/_chunks/{en-Cun5LoUP.mjs → en-BYKKi1LK.mjs} +10 -3
- package/dist/_chunks/{fr-CqlhHltZ.js → fr-DnKtGXaB.js} +10 -3
- package/dist/_chunks/{fr-ClOWflse.mjs → fr-DzgArgi3.mjs} +10 -3
- package/dist/_chunks/{index-FKsiOEPB.mjs → index-LrCSAHoH.mjs} +6 -6
- package/dist/_chunks/{index-D2Q34BuH.js → index-P7p8ASb6.js} +6 -6
- package/dist/_chunks/{objectLabels-LAQ0M6-a.js → objectLabels-Dn-zriuZ.js} +1 -1
- package/dist/_chunks/{objectLabels-DBKevJle.mjs → objectLabels-yNrnTOBo.mjs} +1 -1
- package/dist/_chunks/{useHubspotSchema-C08qBAyy.js → useHubspotSchema-BottzdRR.js} +1 -1
- package/dist/_chunks/{useHubspotSchema-BC0Od-Wl.mjs → useHubspotSchema-DccG2KbZ.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/builder/types.d.ts +1 -0
- package/dist/server/index.js +67 -8
- package/dist/server/index.mjs +67 -8
- package/dist/server/src/content-types.d.ts +3 -0
- package/dist/server/src/forms.d.ts +2 -0
- package/dist/server/src/formsAdmin.d.ts +6 -0
- package/dist/server/src/importLegacy.d.ts +1 -0
- package/dist/server/src/index.d.ts +4 -0
- package/dist/shared/types.d.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0 — 2026-08-29
|
|
4
|
+
|
|
5
|
+
### Improved (builder UX pass, from a live session)
|
|
6
|
+
- **The whole step card now selects the step** — the click target was the tiny
|
|
7
|
+
"Step N" text, which read as "step titles can't be edited". Inner controls
|
|
8
|
+
stop propagation, so field selection and the action buttons are unaffected.
|
|
9
|
+
- Field panel gains the imported-but-invisible settings: **icon**, **answer
|
|
10
|
+
memory (persist)** and **CSS classes**; the step and form panels gain **CSS
|
|
11
|
+
classes** too.
|
|
12
|
+
- **Form-level `class`** now exists end to end: content-type attribute, save,
|
|
13
|
+
public API, legacy import (it used to be silently dropped on migration).
|
|
14
|
+
- **Duplicate** action on the forms list — every locale of the draft is
|
|
15
|
+
copied under a fresh slug.
|
|
16
|
+
- Leaving the editor with unsaved changes now warns before the tab closes.
|
|
17
|
+
- Cosmetics: the actions column no longer shows a raw translation key, and the
|
|
18
|
+
structure column pluralizes properly.
|
|
19
|
+
|
|
3
20
|
## 0.7.0 — 2026-08-29
|
|
4
21
|
|
|
5
22
|
### Added
|
|
@@ -3,11 +3,11 @@ import { useParams, useSearchParams, Link, useNavigate, Routes, Route } from "re
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { u as useIntl } from "./index-CEh8vkxY.mjs";
|
|
5
5
|
import { Typography, Flex, Box, SingleSelect, SingleSelectOption, TextInput, IconButton, Button, Field, Checkbox, Divider, Combobox, ComboboxOption, Loader, Badge, Textarea, Table, Thead, Tr, Th, Tbody, Td } from "@strapi/design-system";
|
|
6
|
-
import { Trash, Plus, ArrowClockwise, ArrowLeft, ArrowUp, ArrowDown } from "@strapi/icons";
|
|
6
|
+
import { Trash, Plus, ArrowClockwise, ArrowLeft, ArrowUp, ArrowDown, Duplicate } from "@strapi/icons";
|
|
7
7
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
8
|
-
import { g as getTranslation, P as PLUGIN_ID } from "./index-
|
|
9
|
-
import { o as objectLabel } from "./objectLabels-
|
|
10
|
-
import { u as useHubspotSchema } from "./useHubspotSchema-
|
|
8
|
+
import { g as getTranslation, P as PLUGIN_ID } from "./index-LrCSAHoH.mjs";
|
|
9
|
+
import { o as objectLabel } from "./objectLabels-yNrnTOBo.mjs";
|
|
10
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-DccG2KbZ.mjs";
|
|
11
11
|
const OPERATORS = ["eq", "neq", "contains", "empty", "notEmpty", "gt", "lt"];
|
|
12
12
|
const NEEDS_VALUE = /* @__PURE__ */ new Set(["eq", "neq", "contains", "gt", "lt"]);
|
|
13
13
|
const ConditionEditor = ({ condition, candidates, onChange }) => {
|
|
@@ -224,6 +224,43 @@ const FieldPanel = ({ field, candidates, problem, onChange }) => {
|
|
|
224
224
|
children: t("panel.required", "Required")
|
|
225
225
|
}
|
|
226
226
|
),
|
|
227
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 4, children: [
|
|
228
|
+
/* @__PURE__ */ jsx(Box, { flex: "1", children: /* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
229
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("panel.icon", "Icon") }),
|
|
230
|
+
/* @__PURE__ */ jsx(
|
|
231
|
+
TextInput,
|
|
232
|
+
{
|
|
233
|
+
value: field.icon ?? "",
|
|
234
|
+
placeholder: "i-lucide-building-2",
|
|
235
|
+
onChange: (e) => onChange({ icon: e.target.value })
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
] }) }),
|
|
239
|
+
/* @__PURE__ */ jsx(Box, { flex: "1", children: /* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
240
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("panel.persist", "Answer memory") }),
|
|
241
|
+
/* @__PURE__ */ jsxs(
|
|
242
|
+
SingleSelect,
|
|
243
|
+
{
|
|
244
|
+
value: field.persist ?? "session",
|
|
245
|
+
onChange: (v) => onChange({ persist: v }),
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "session", children: t("panel.persist-session", "This visit only") }),
|
|
248
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "days30", children: t("panel.persist-days30", "30 days (non-identifying fields only)") })
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
] }) })
|
|
253
|
+
] }),
|
|
254
|
+
/* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
255
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("panel.class", "CSS classes") }),
|
|
256
|
+
/* @__PURE__ */ jsx(
|
|
257
|
+
TextInput,
|
|
258
|
+
{
|
|
259
|
+
value: field.class ?? "",
|
|
260
|
+
onChange: (e) => onChange({ class: e.target.value })
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
] }),
|
|
227
264
|
isChoice && /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
|
|
228
265
|
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: t("panel.options", "Choices") }),
|
|
229
266
|
(field.options ?? []).map((option, index) => /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
@@ -370,6 +407,14 @@ const FormEditor = () => {
|
|
|
370
407
|
const [feedback, setFeedback] = React.useState(null);
|
|
371
408
|
const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
|
|
372
409
|
const query = locale ? `?locale=${locale}` : "";
|
|
410
|
+
React.useEffect(() => {
|
|
411
|
+
if (!dirty) return void 0;
|
|
412
|
+
const warn = (event) => {
|
|
413
|
+
event.preventDefault();
|
|
414
|
+
};
|
|
415
|
+
window.addEventListener("beforeunload", warn);
|
|
416
|
+
return () => window.removeEventListener("beforeunload", warn);
|
|
417
|
+
}, [dirty]);
|
|
373
418
|
React.useEffect(() => {
|
|
374
419
|
get("/i18n/locales").then(({ data }) => Array.isArray(data) && setLocales(data)).catch(() => setLocales([]));
|
|
375
420
|
}, [get]);
|
|
@@ -462,6 +507,7 @@ const FormEditor = () => {
|
|
|
462
507
|
nextLabel: entry.nextLabel,
|
|
463
508
|
submitLabel: entry.submitLabel,
|
|
464
509
|
successMessage: entry.successMessage,
|
|
510
|
+
class: entry.class,
|
|
465
511
|
definition
|
|
466
512
|
}
|
|
467
513
|
);
|
|
@@ -567,6 +613,8 @@ const FormEditor = () => {
|
|
|
567
613
|
hasRadius: true,
|
|
568
614
|
shadow: "tableShadow",
|
|
569
615
|
padding: 4,
|
|
616
|
+
onClick: () => setSelection({ kind: "step", stepId: step.id }),
|
|
617
|
+
style: { cursor: "pointer" },
|
|
570
618
|
borderColor: selection.kind === "step" && selection.stepId === step.id ? "primary600" : void 0,
|
|
571
619
|
borderStyle: selection.kind === "step" && selection.stepId === step.id ? "solid" : void 0,
|
|
572
620
|
borderWidth: selection.kind === "step" && selection.stepId === step.id ? "1px" : void 0,
|
|
@@ -589,7 +637,7 @@ const FormEditor = () => {
|
|
|
589
637
|
]
|
|
590
638
|
}
|
|
591
639
|
),
|
|
592
|
-
/* @__PURE__ */ jsxs(Flex, { gap: 1, children: [
|
|
640
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 1, onClick: (e) => e.stopPropagation(), children: [
|
|
593
641
|
/* @__PURE__ */ jsx(
|
|
594
642
|
IconButton,
|
|
595
643
|
{
|
|
@@ -631,7 +679,10 @@ const FormEditor = () => {
|
|
|
631
679
|
background: isSelected ? "primary100" : "neutral100",
|
|
632
680
|
hasRadius: true,
|
|
633
681
|
padding: 3,
|
|
634
|
-
onClick: () =>
|
|
682
|
+
onClick: (e) => {
|
|
683
|
+
e.stopPropagation();
|
|
684
|
+
setSelection({ kind: "field", fieldId: field.id });
|
|
685
|
+
},
|
|
635
686
|
style: { cursor: "pointer" },
|
|
636
687
|
children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
637
688
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
@@ -689,7 +740,8 @@ const FormEditor = () => {
|
|
|
689
740
|
{
|
|
690
741
|
variant: "tertiary",
|
|
691
742
|
startIcon: /* @__PURE__ */ jsx(Plus, {}),
|
|
692
|
-
onClick: () => {
|
|
743
|
+
onClick: (e) => {
|
|
744
|
+
e.stopPropagation();
|
|
693
745
|
const field = newField(t("editor.new-field", "New field"));
|
|
694
746
|
patchStep(step.id, { fields: [...step.fields, field] });
|
|
695
747
|
setSelection({ kind: "field", fieldId: field.id });
|
|
@@ -746,6 +798,16 @@ const FormEditor = () => {
|
|
|
746
798
|
}
|
|
747
799
|
)
|
|
748
800
|
] }),
|
|
801
|
+
/* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
802
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("panel.class", "CSS classes") }),
|
|
803
|
+
/* @__PURE__ */ jsx(
|
|
804
|
+
TextInput,
|
|
805
|
+
{
|
|
806
|
+
value: selectedStep.class ?? "",
|
|
807
|
+
onChange: (e) => patchStep(selectedStep.id, { class: e.target.value })
|
|
808
|
+
}
|
|
809
|
+
)
|
|
810
|
+
] }),
|
|
749
811
|
/* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: t("editor.step-condition", "Show this step only when…") }),
|
|
750
812
|
/* @__PURE__ */ jsx(
|
|
751
813
|
ConditionEditor,
|
|
@@ -819,6 +881,16 @@ const FormEditor = () => {
|
|
|
819
881
|
}
|
|
820
882
|
)
|
|
821
883
|
] }),
|
|
884
|
+
/* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
885
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("panel.class", "CSS classes") }),
|
|
886
|
+
/* @__PURE__ */ jsx(
|
|
887
|
+
TextInput,
|
|
888
|
+
{
|
|
889
|
+
value: entry.class ?? "",
|
|
890
|
+
onChange: (e) => patchEntry({ class: e.target.value })
|
|
891
|
+
}
|
|
892
|
+
)
|
|
893
|
+
] }),
|
|
822
894
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
823
895
|
"editor.form-hint",
|
|
824
896
|
"Select a step or a field to edit it. The public endpoint serves this form at /api/hubspot/forms/{slug}.",
|
|
@@ -878,6 +950,19 @@ const FormsList = () => {
|
|
|
878
950
|
setBusy(false);
|
|
879
951
|
}
|
|
880
952
|
};
|
|
953
|
+
const duplicate = async (row) => {
|
|
954
|
+
setBusy(true);
|
|
955
|
+
setError(null);
|
|
956
|
+
try {
|
|
957
|
+
const { data } = await post(
|
|
958
|
+
`/${PLUGIN_ID}/builder/forms/${row.documentId}/duplicate`
|
|
959
|
+
);
|
|
960
|
+
navigate(data.documentId);
|
|
961
|
+
} catch {
|
|
962
|
+
setError(t("forms.duplicate-error", "Could not duplicate the form."));
|
|
963
|
+
setBusy(false);
|
|
964
|
+
}
|
|
965
|
+
};
|
|
881
966
|
const remove = async (row) => {
|
|
882
967
|
if (!window.confirm(t("forms.delete-confirm", 'Delete "{name}" and all its locales?', { name: row.name }))) {
|
|
883
968
|
return;
|
|
@@ -949,7 +1034,7 @@ const FormsList = () => {
|
|
|
949
1034
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: t("forms.col-structure", "Structure") }) }),
|
|
950
1035
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: t("forms.col-status", "Status") }) }),
|
|
951
1036
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: t("forms.col-updated", "Updated") }) }),
|
|
952
|
-
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: t("forms.col-actions", "") }) })
|
|
1037
|
+
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: t("forms.col-actions", "Actions") }) })
|
|
953
1038
|
] }) }),
|
|
954
1039
|
/* @__PURE__ */ jsx(Tbody, { children: rows.map((row) => /* @__PURE__ */ jsxs(
|
|
955
1040
|
Tr,
|
|
@@ -959,21 +1044,32 @@ const FormsList = () => {
|
|
|
959
1044
|
children: [
|
|
960
1045
|
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: row.name }) }),
|
|
961
1046
|
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: row.slug }) }),
|
|
962
|
-
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: t("forms.structure", "{steps} steps · {fields} fields", {
|
|
1047
|
+
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: t("forms.structure", "{steps, plural, one {# step} other {# steps}} · {fields, plural, one {# field} other {# fields}}", {
|
|
963
1048
|
steps: row.steps,
|
|
964
1049
|
fields: row.fields
|
|
965
1050
|
}) }) }),
|
|
966
1051
|
/* @__PURE__ */ jsx(Td, { children: row.published ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: t("forms.published", "Published") }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: t("forms.draft", "Draft") }) }),
|
|
967
1052
|
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: row.updatedAt ? new Date(row.updatedAt).toLocaleString() : "—" }) }),
|
|
968
|
-
/* @__PURE__ */ jsx(Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1053
|
+
/* @__PURE__ */ jsx(Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs(Flex, { gap: 1, children: [
|
|
1054
|
+
/* @__PURE__ */ jsx(
|
|
1055
|
+
IconButton,
|
|
1056
|
+
{
|
|
1057
|
+
label: t("forms.duplicate", "Duplicate"),
|
|
1058
|
+
onClick: () => duplicate(row),
|
|
1059
|
+
disabled: busy,
|
|
1060
|
+
children: /* @__PURE__ */ jsx(Duplicate, {})
|
|
1061
|
+
}
|
|
1062
|
+
),
|
|
1063
|
+
/* @__PURE__ */ jsx(
|
|
1064
|
+
IconButton,
|
|
1065
|
+
{
|
|
1066
|
+
label: t("forms.delete", "Delete"),
|
|
1067
|
+
onClick: () => remove(row),
|
|
1068
|
+
disabled: busy,
|
|
1069
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
1070
|
+
}
|
|
1071
|
+
)
|
|
1072
|
+
] }) })
|
|
977
1073
|
]
|
|
978
1074
|
},
|
|
979
1075
|
row.documentId
|
|
@@ -7,9 +7,9 @@ const index = require("./index-DkTxsEqL.js");
|
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
9
|
const admin = require("@strapi/strapi/admin");
|
|
10
|
-
const index$1 = require("./index-
|
|
11
|
-
const objectLabels = require("./objectLabels-
|
|
12
|
-
const useHubspotSchema = require("./useHubspotSchema-
|
|
10
|
+
const index$1 = require("./index-P7p8ASb6.js");
|
|
11
|
+
const objectLabels = require("./objectLabels-Dn-zriuZ.js");
|
|
12
|
+
const useHubspotSchema = require("./useHubspotSchema-BottzdRR.js");
|
|
13
13
|
function _interopNamespace(e) {
|
|
14
14
|
if (e && e.__esModule) return e;
|
|
15
15
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -244,6 +244,43 @@ const FieldPanel = ({ field, candidates, problem, onChange }) => {
|
|
|
244
244
|
children: t("panel.required", "Required")
|
|
245
245
|
}
|
|
246
246
|
),
|
|
247
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, children: [
|
|
248
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
249
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.icon", "Icon") }),
|
|
250
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
251
|
+
designSystem.TextInput,
|
|
252
|
+
{
|
|
253
|
+
value: field.icon ?? "",
|
|
254
|
+
placeholder: "i-lucide-building-2",
|
|
255
|
+
onChange: (e) => onChange({ icon: e.target.value })
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
] }) }),
|
|
259
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: "1", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
260
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.persist", "Answer memory") }),
|
|
261
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
262
|
+
designSystem.SingleSelect,
|
|
263
|
+
{
|
|
264
|
+
value: field.persist ?? "session",
|
|
265
|
+
onChange: (v) => onChange({ persist: v }),
|
|
266
|
+
children: [
|
|
267
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "session", children: t("panel.persist-session", "This visit only") }),
|
|
268
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "days30", children: t("panel.persist-days30", "30 days (non-identifying fields only)") })
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
] }) })
|
|
273
|
+
] }),
|
|
274
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
275
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.class", "CSS classes") }),
|
|
276
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
277
|
+
designSystem.TextInput,
|
|
278
|
+
{
|
|
279
|
+
value: field.class ?? "",
|
|
280
|
+
onChange: (e) => onChange({ class: e.target.value })
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
] }),
|
|
247
284
|
isChoice && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
|
|
248
285
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("panel.options", "Choices") }),
|
|
249
286
|
(field.options ?? []).map((option, index2) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
@@ -390,6 +427,14 @@ const FormEditor = () => {
|
|
|
390
427
|
const [feedback, setFeedback] = React__namespace.useState(null);
|
|
391
428
|
const t = (id, defaultMessage, values) => formatMessage({ id: index$1.getTranslation(id), defaultMessage }, values);
|
|
392
429
|
const query = locale ? `?locale=${locale}` : "";
|
|
430
|
+
React__namespace.useEffect(() => {
|
|
431
|
+
if (!dirty) return void 0;
|
|
432
|
+
const warn = (event) => {
|
|
433
|
+
event.preventDefault();
|
|
434
|
+
};
|
|
435
|
+
window.addEventListener("beforeunload", warn);
|
|
436
|
+
return () => window.removeEventListener("beforeunload", warn);
|
|
437
|
+
}, [dirty]);
|
|
393
438
|
React__namespace.useEffect(() => {
|
|
394
439
|
get("/i18n/locales").then(({ data }) => Array.isArray(data) && setLocales(data)).catch(() => setLocales([]));
|
|
395
440
|
}, [get]);
|
|
@@ -482,6 +527,7 @@ const FormEditor = () => {
|
|
|
482
527
|
nextLabel: entry.nextLabel,
|
|
483
528
|
submitLabel: entry.submitLabel,
|
|
484
529
|
successMessage: entry.successMessage,
|
|
530
|
+
class: entry.class,
|
|
485
531
|
definition
|
|
486
532
|
}
|
|
487
533
|
);
|
|
@@ -587,6 +633,8 @@ const FormEditor = () => {
|
|
|
587
633
|
hasRadius: true,
|
|
588
634
|
shadow: "tableShadow",
|
|
589
635
|
padding: 4,
|
|
636
|
+
onClick: () => setSelection({ kind: "step", stepId: step.id }),
|
|
637
|
+
style: { cursor: "pointer" },
|
|
590
638
|
borderColor: selection.kind === "step" && selection.stepId === step.id ? "primary600" : void 0,
|
|
591
639
|
borderStyle: selection.kind === "step" && selection.stepId === step.id ? "solid" : void 0,
|
|
592
640
|
borderWidth: selection.kind === "step" && selection.stepId === step.id ? "1px" : void 0,
|
|
@@ -609,7 +657,7 @@ const FormEditor = () => {
|
|
|
609
657
|
]
|
|
610
658
|
}
|
|
611
659
|
),
|
|
612
|
-
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
|
|
660
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, onClick: (e) => e.stopPropagation(), children: [
|
|
613
661
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
614
662
|
designSystem.IconButton,
|
|
615
663
|
{
|
|
@@ -651,7 +699,10 @@ const FormEditor = () => {
|
|
|
651
699
|
background: isSelected ? "primary100" : "neutral100",
|
|
652
700
|
hasRadius: true,
|
|
653
701
|
padding: 3,
|
|
654
|
-
onClick: () =>
|
|
702
|
+
onClick: (e) => {
|
|
703
|
+
e.stopPropagation();
|
|
704
|
+
setSelection({ kind: "field", fieldId: field.id });
|
|
705
|
+
},
|
|
655
706
|
style: { cursor: "pointer" },
|
|
656
707
|
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
657
708
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
|
|
@@ -709,7 +760,8 @@ const FormEditor = () => {
|
|
|
709
760
|
{
|
|
710
761
|
variant: "tertiary",
|
|
711
762
|
startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
712
|
-
onClick: () => {
|
|
763
|
+
onClick: (e) => {
|
|
764
|
+
e.stopPropagation();
|
|
713
765
|
const field = newField(t("editor.new-field", "New field"));
|
|
714
766
|
patchStep(step.id, { fields: [...step.fields, field] });
|
|
715
767
|
setSelection({ kind: "field", fieldId: field.id });
|
|
@@ -766,6 +818,16 @@ const FormEditor = () => {
|
|
|
766
818
|
}
|
|
767
819
|
)
|
|
768
820
|
] }),
|
|
821
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
822
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.class", "CSS classes") }),
|
|
823
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
824
|
+
designSystem.TextInput,
|
|
825
|
+
{
|
|
826
|
+
value: selectedStep.class ?? "",
|
|
827
|
+
onChange: (e) => patchStep(selectedStep.id, { class: e.target.value })
|
|
828
|
+
}
|
|
829
|
+
)
|
|
830
|
+
] }),
|
|
769
831
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("editor.step-condition", "Show this step only when…") }),
|
|
770
832
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
771
833
|
ConditionEditor,
|
|
@@ -839,6 +901,16 @@ const FormEditor = () => {
|
|
|
839
901
|
}
|
|
840
902
|
)
|
|
841
903
|
] }),
|
|
904
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
905
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("panel.class", "CSS classes") }),
|
|
906
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
907
|
+
designSystem.TextInput,
|
|
908
|
+
{
|
|
909
|
+
value: entry.class ?? "",
|
|
910
|
+
onChange: (e) => patchEntry({ class: e.target.value })
|
|
911
|
+
}
|
|
912
|
+
)
|
|
913
|
+
] }),
|
|
842
914
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
843
915
|
"editor.form-hint",
|
|
844
916
|
"Select a step or a field to edit it. The public endpoint serves this form at /api/hubspot/forms/{slug}.",
|
|
@@ -898,6 +970,19 @@ const FormsList = () => {
|
|
|
898
970
|
setBusy(false);
|
|
899
971
|
}
|
|
900
972
|
};
|
|
973
|
+
const duplicate = async (row) => {
|
|
974
|
+
setBusy(true);
|
|
975
|
+
setError(null);
|
|
976
|
+
try {
|
|
977
|
+
const { data } = await post(
|
|
978
|
+
`/${index$1.PLUGIN_ID}/builder/forms/${row.documentId}/duplicate`
|
|
979
|
+
);
|
|
980
|
+
navigate(data.documentId);
|
|
981
|
+
} catch {
|
|
982
|
+
setError(t("forms.duplicate-error", "Could not duplicate the form."));
|
|
983
|
+
setBusy(false);
|
|
984
|
+
}
|
|
985
|
+
};
|
|
901
986
|
const remove = async (row) => {
|
|
902
987
|
if (!window.confirm(t("forms.delete-confirm", 'Delete "{name}" and all its locales?', { name: row.name }))) {
|
|
903
988
|
return;
|
|
@@ -969,7 +1054,7 @@ const FormsList = () => {
|
|
|
969
1054
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-structure", "Structure") }) }),
|
|
970
1055
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-status", "Status") }) }),
|
|
971
1056
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-updated", "Updated") }) }),
|
|
972
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-actions", "") }) })
|
|
1057
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-actions", "Actions") }) })
|
|
973
1058
|
] }) }),
|
|
974
1059
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
975
1060
|
designSystem.Tr,
|
|
@@ -979,21 +1064,32 @@ const FormsList = () => {
|
|
|
979
1064
|
children: [
|
|
980
1065
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: row.name }) }),
|
|
981
1066
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: row.slug }) }),
|
|
982
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("forms.structure", "{steps} steps · {fields} fields", {
|
|
1067
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("forms.structure", "{steps, plural, one {# step} other {# steps}} · {fields, plural, one {# field} other {# fields}}", {
|
|
983
1068
|
steps: row.steps,
|
|
984
1069
|
fields: row.fields
|
|
985
1070
|
}) }) }),
|
|
986
1071
|
/* @__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") }) }),
|
|
987
1072
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: row.updatedAt ? new Date(row.updatedAt).toLocaleString() : "—" }) }),
|
|
988
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1073
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
|
|
1074
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1075
|
+
designSystem.IconButton,
|
|
1076
|
+
{
|
|
1077
|
+
label: t("forms.duplicate", "Duplicate"),
|
|
1078
|
+
onClick: () => duplicate(row),
|
|
1079
|
+
disabled: busy,
|
|
1080
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Duplicate, {})
|
|
1081
|
+
}
|
|
1082
|
+
),
|
|
1083
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1084
|
+
designSystem.IconButton,
|
|
1085
|
+
{
|
|
1086
|
+
label: t("forms.delete", "Delete"),
|
|
1087
|
+
onClick: () => remove(row),
|
|
1088
|
+
disabled: busy,
|
|
1089
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
|
|
1090
|
+
}
|
|
1091
|
+
)
|
|
1092
|
+
] }) })
|
|
997
1093
|
]
|
|
998
1094
|
},
|
|
999
1095
|
row.documentId
|
|
@@ -3,7 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { u as useIntl } from "./index-CEh8vkxY.mjs";
|
|
4
4
|
import { Field, Flex, Loader, Combobox, ComboboxOption } from "@strapi/design-system";
|
|
5
5
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
6
|
-
import { P as PLUGIN_ID, g as getTranslation } from "./index-
|
|
6
|
+
import { P as PLUGIN_ID, g as getTranslation } from "./index-LrCSAHoH.mjs";
|
|
7
7
|
const HubspotFormInput = React.forwardRef(
|
|
8
8
|
({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
|
|
9
9
|
const { formatMessage } = useIntl();
|
|
@@ -5,7 +5,7 @@ const React = require("react");
|
|
|
5
5
|
const index = require("./index-DkTxsEqL.js");
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
7
|
const admin = require("@strapi/strapi/admin");
|
|
8
|
-
const index$1 = require("./index-
|
|
8
|
+
const index$1 = require("./index-P7p8ASb6.js");
|
|
9
9
|
function _interopNamespace(e) {
|
|
10
10
|
if (e && e.__esModule) return e;
|
|
11
11
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -2,9 +2,9 @@ import { jsxs, jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { u as useIntl } from "./index-CEh8vkxY.mjs";
|
|
4
4
|
import { Field, Flex, Loader, SingleSelect, SingleSelectOption } from "@strapi/design-system";
|
|
5
|
-
import { g as getTranslation } from "./index-
|
|
6
|
-
import { o as objectLabel } from "./objectLabels-
|
|
7
|
-
import { u as useHubspotSchema } from "./useHubspotSchema-
|
|
5
|
+
import { g as getTranslation } from "./index-LrCSAHoH.mjs";
|
|
6
|
+
import { o as objectLabel } from "./objectLabels-yNrnTOBo.mjs";
|
|
7
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-DccG2KbZ.mjs";
|
|
8
8
|
const HubspotObjectInput = React.forwardRef(
|
|
9
9
|
({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
|
|
10
10
|
const intl = useIntl();
|
|
@@ -4,9 +4,9 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const index = require("./index-DkTxsEqL.js");
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
|
-
const index$1 = require("./index-
|
|
8
|
-
const objectLabels = require("./objectLabels-
|
|
9
|
-
const useHubspotSchema = require("./useHubspotSchema-
|
|
7
|
+
const index$1 = require("./index-P7p8ASb6.js");
|
|
8
|
+
const objectLabels = require("./objectLabels-Dn-zriuZ.js");
|
|
9
|
+
const useHubspotSchema = require("./useHubspotSchema-BottzdRR.js");
|
|
10
10
|
function _interopNamespace(e) {
|
|
11
11
|
if (e && e.__esModule) return e;
|
|
12
12
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
package/dist/_chunks/{HubspotPropertyInput-DrBsx9jQ.mjs → HubspotPropertyInput-Drxn_Ddt.mjs}
RENAMED
|
@@ -4,9 +4,9 @@ import { u as useIntl } from "./index-CEh8vkxY.mjs";
|
|
|
4
4
|
import { Field, Flex, Loader, Combobox, ComboboxOption, IconButton, Button, Typography, Link } from "@strapi/design-system";
|
|
5
5
|
import { ArrowClockwise } from "@strapi/icons";
|
|
6
6
|
import { useField } from "@strapi/strapi/admin";
|
|
7
|
-
import { g as getTranslation } from "./index-
|
|
8
|
-
import { o as objectLabel } from "./objectLabels-
|
|
9
|
-
import { u as useHubspotSchema } from "./useHubspotSchema-
|
|
7
|
+
import { g as getTranslation } from "./index-LrCSAHoH.mjs";
|
|
8
|
+
import { o as objectLabel } from "./objectLabels-yNrnTOBo.mjs";
|
|
9
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-DccG2KbZ.mjs";
|
|
10
10
|
const oneLine = {
|
|
11
11
|
display: "block",
|
|
12
12
|
whiteSpace: "nowrap",
|
|
@@ -6,9 +6,9 @@ const index = require("./index-DkTxsEqL.js");
|
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
7
|
const icons = require("@strapi/icons");
|
|
8
8
|
const admin = require("@strapi/strapi/admin");
|
|
9
|
-
const index$1 = require("./index-
|
|
10
|
-
const objectLabels = require("./objectLabels-
|
|
11
|
-
const useHubspotSchema = require("./useHubspotSchema-
|
|
9
|
+
const index$1 = require("./index-P7p8ASb6.js");
|
|
10
|
+
const objectLabels = require("./objectLabels-Dn-zriuZ.js");
|
|
11
|
+
const useHubspotSchema = require("./useHubspotSchema-BottzdRR.js");
|
|
12
12
|
function _interopNamespace(e) {
|
|
13
13
|
if (e && e.__esModule) return e;
|
|
14
14
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -5,9 +5,9 @@ const React = require("react");
|
|
|
5
5
|
const index = require("./index-DkTxsEqL.js");
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
7
|
const admin = require("@strapi/strapi/admin");
|
|
8
|
-
const index$1 = require("./index-
|
|
8
|
+
const index$1 = require("./index-P7p8ASb6.js");
|
|
9
9
|
const reactRouterDom = require("react-router-dom");
|
|
10
|
-
const objectLabels = require("./objectLabels-
|
|
10
|
+
const objectLabels = require("./objectLabels-Dn-zriuZ.js");
|
|
11
11
|
function _interopNamespace(e) {
|
|
12
12
|
if (e && e.__esModule) return e;
|
|
13
13
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -3,9 +3,9 @@ import * as React from "react";
|
|
|
3
3
|
import { u as useIntl } from "./index-CEh8vkxY.mjs";
|
|
4
4
|
import { Flex, Divider, Typography, Button, Box, Link, Badge, Loader, Field } from "@strapi/design-system";
|
|
5
5
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
6
|
-
import { g as getTranslation, P as PLUGIN_ID } from "./index-
|
|
6
|
+
import { g as getTranslation, P as PLUGIN_ID } from "./index-LrCSAHoH.mjs";
|
|
7
7
|
import { Link as Link$1 } from "react-router-dom";
|
|
8
|
-
import { o as objectLabel } from "./objectLabels-
|
|
8
|
+
import { o as objectLabel } from "./objectLabels-yNrnTOBo.mjs";
|
|
9
9
|
const AuditSection = ({ configured }) => {
|
|
10
10
|
const intl = useIntl();
|
|
11
11
|
const { formatMessage } = intl;
|
|
@@ -94,8 +94,8 @@ const en = {
|
|
|
94
94
|
"forms.col-structure": "Structure",
|
|
95
95
|
"forms.col-status": "Status",
|
|
96
96
|
"forms.col-updated": "Updated",
|
|
97
|
-
"forms.col-actions": "",
|
|
98
|
-
"forms.structure": "{steps} steps · {fields} fields",
|
|
97
|
+
"forms.col-actions": "Actions",
|
|
98
|
+
"forms.structure": "{steps, plural, one {# step} other {# steps}} · {fields, plural, one {# field} other {# fields}}",
|
|
99
99
|
"forms.published": "Published",
|
|
100
100
|
"forms.draft": "Draft",
|
|
101
101
|
"forms.delete": "Delete",
|
|
@@ -199,6 +199,13 @@ const en = {
|
|
|
199
199
|
"form-picker.placeholder": "Choose a form…",
|
|
200
200
|
"form-picker.fallback": "Form slug",
|
|
201
201
|
"form-picker.orphan": "{slug} — unknown form",
|
|
202
|
-
"form-picker.draft": "{name} ({slug}) — draft, not served yet"
|
|
202
|
+
"form-picker.draft": "{name} ({slug}) — draft, not served yet",
|
|
203
|
+
"forms.duplicate": "Duplicate",
|
|
204
|
+
"forms.duplicate-error": "Could not duplicate the form.",
|
|
205
|
+
"panel.icon": "Icon",
|
|
206
|
+
"panel.persist": "Answer memory",
|
|
207
|
+
"panel.persist-session": "This visit only",
|
|
208
|
+
"panel.persist-days30": "30 days (non-identifying fields only)",
|
|
209
|
+
"panel.class": "CSS classes"
|
|
203
210
|
};
|
|
204
211
|
exports.default = en;
|
|
@@ -92,8 +92,8 @@ const en = {
|
|
|
92
92
|
"forms.col-structure": "Structure",
|
|
93
93
|
"forms.col-status": "Status",
|
|
94
94
|
"forms.col-updated": "Updated",
|
|
95
|
-
"forms.col-actions": "",
|
|
96
|
-
"forms.structure": "{steps} steps · {fields} fields",
|
|
95
|
+
"forms.col-actions": "Actions",
|
|
96
|
+
"forms.structure": "{steps, plural, one {# step} other {# steps}} · {fields, plural, one {# field} other {# fields}}",
|
|
97
97
|
"forms.published": "Published",
|
|
98
98
|
"forms.draft": "Draft",
|
|
99
99
|
"forms.delete": "Delete",
|
|
@@ -197,7 +197,14 @@ const en = {
|
|
|
197
197
|
"form-picker.placeholder": "Choose a form…",
|
|
198
198
|
"form-picker.fallback": "Form slug",
|
|
199
199
|
"form-picker.orphan": "{slug} — unknown form",
|
|
200
|
-
"form-picker.draft": "{name} ({slug}) — draft, not served yet"
|
|
200
|
+
"form-picker.draft": "{name} ({slug}) — draft, not served yet",
|
|
201
|
+
"forms.duplicate": "Duplicate",
|
|
202
|
+
"forms.duplicate-error": "Could not duplicate the form.",
|
|
203
|
+
"panel.icon": "Icon",
|
|
204
|
+
"panel.persist": "Answer memory",
|
|
205
|
+
"panel.persist-session": "This visit only",
|
|
206
|
+
"panel.persist-days30": "30 days (non-identifying fields only)",
|
|
207
|
+
"panel.class": "CSS classes"
|
|
201
208
|
};
|
|
202
209
|
export {
|
|
203
210
|
en as default
|
|
@@ -94,8 +94,8 @@ const fr = {
|
|
|
94
94
|
"forms.col-structure": "Structure",
|
|
95
95
|
"forms.col-status": "Statut",
|
|
96
96
|
"forms.col-updated": "Modifié",
|
|
97
|
-
"forms.col-actions": "",
|
|
98
|
-
"forms.structure": "{steps} étapes · {fields} champs",
|
|
97
|
+
"forms.col-actions": "Actions",
|
|
98
|
+
"forms.structure": "{steps, plural, one {# étape} other {# étapes}} · {fields, plural, one {# champ} other {# champs}}",
|
|
99
99
|
"forms.published": "Publié",
|
|
100
100
|
"forms.draft": "Brouillon",
|
|
101
101
|
"forms.delete": "Supprimer",
|
|
@@ -199,6 +199,13 @@ const fr = {
|
|
|
199
199
|
"form-picker.placeholder": "Choisir un formulaire…",
|
|
200
200
|
"form-picker.fallback": "Slug du formulaire",
|
|
201
201
|
"form-picker.orphan": "{slug} — formulaire inconnu",
|
|
202
|
-
"form-picker.draft": "{name} ({slug}) — brouillon, pas encore servi"
|
|
202
|
+
"form-picker.draft": "{name} ({slug}) — brouillon, pas encore servi",
|
|
203
|
+
"forms.duplicate": "Dupliquer",
|
|
204
|
+
"forms.duplicate-error": "Impossible de dupliquer le formulaire.",
|
|
205
|
+
"panel.icon": "Icône",
|
|
206
|
+
"panel.persist": "Mémoire de la réponse",
|
|
207
|
+
"panel.persist-session": "Cette visite seulement",
|
|
208
|
+
"panel.persist-days30": "30 jours (champs non identifiants uniquement)",
|
|
209
|
+
"panel.class": "Classes CSS"
|
|
203
210
|
};
|
|
204
211
|
exports.default = fr;
|
|
@@ -92,8 +92,8 @@ const fr = {
|
|
|
92
92
|
"forms.col-structure": "Structure",
|
|
93
93
|
"forms.col-status": "Statut",
|
|
94
94
|
"forms.col-updated": "Modifié",
|
|
95
|
-
"forms.col-actions": "",
|
|
96
|
-
"forms.structure": "{steps} étapes · {fields} champs",
|
|
95
|
+
"forms.col-actions": "Actions",
|
|
96
|
+
"forms.structure": "{steps, plural, one {# étape} other {# étapes}} · {fields, plural, one {# champ} other {# champs}}",
|
|
97
97
|
"forms.published": "Publié",
|
|
98
98
|
"forms.draft": "Brouillon",
|
|
99
99
|
"forms.delete": "Supprimer",
|
|
@@ -197,7 +197,14 @@ const fr = {
|
|
|
197
197
|
"form-picker.placeholder": "Choisir un formulaire…",
|
|
198
198
|
"form-picker.fallback": "Slug du formulaire",
|
|
199
199
|
"form-picker.orphan": "{slug} — formulaire inconnu",
|
|
200
|
-
"form-picker.draft": "{name} ({slug}) — brouillon, pas encore servi"
|
|
200
|
+
"form-picker.draft": "{name} ({slug}) — brouillon, pas encore servi",
|
|
201
|
+
"forms.duplicate": "Dupliquer",
|
|
202
|
+
"forms.duplicate-error": "Impossible de dupliquer le formulaire.",
|
|
203
|
+
"panel.icon": "Icône",
|
|
204
|
+
"panel.persist": "Mémoire de la réponse",
|
|
205
|
+
"panel.persist-session": "Cette visite seulement",
|
|
206
|
+
"panel.persist-days30": "30 jours (champs non identifiants uniquement)",
|
|
207
|
+
"panel.class": "Classes CSS"
|
|
201
208
|
};
|
|
202
209
|
export {
|
|
203
210
|
fr as default
|
|
@@ -25,7 +25,7 @@ const index = {
|
|
|
25
25
|
icon: PaperPlane,
|
|
26
26
|
intlLabel: { id: `${PLUGIN_ID}.menu.label`, defaultMessage: "HubSpot Forms" },
|
|
27
27
|
permissions: [{ action: `plugin::${PLUGIN_ID}.forms`, subject: null }],
|
|
28
|
-
Component: async () => (await import("./Forms-
|
|
28
|
+
Component: async () => (await import("./Forms-77Mornxt.mjs")).default
|
|
29
29
|
});
|
|
30
30
|
app.customFields.register({
|
|
31
31
|
name: "property",
|
|
@@ -40,7 +40,7 @@ const index = {
|
|
|
40
40
|
defaultMessage: "Picked from the portal's real properties"
|
|
41
41
|
},
|
|
42
42
|
components: {
|
|
43
|
-
Input: async () => import("./HubspotPropertyInput-
|
|
43
|
+
Input: async () => import("./HubspotPropertyInput-Drxn_Ddt.mjs")
|
|
44
44
|
},
|
|
45
45
|
// Exposed in the Content-Type Builder. Without this, `objectField` could
|
|
46
46
|
// only be set by hand-editing schema.json — which is not how anyone
|
|
@@ -103,7 +103,7 @@ const index = {
|
|
|
103
103
|
defaultMessage: "Picked from the portal's configured objects"
|
|
104
104
|
},
|
|
105
105
|
components: {
|
|
106
|
-
Input: async () => import("./HubspotObjectInput-
|
|
106
|
+
Input: async () => import("./HubspotObjectInput-B-Y1PZ6c.mjs")
|
|
107
107
|
}
|
|
108
108
|
});
|
|
109
109
|
app.customFields.register({
|
|
@@ -119,7 +119,7 @@ const index = {
|
|
|
119
119
|
defaultMessage: "A form built in the HubSpot form builder, stored by slug"
|
|
120
120
|
},
|
|
121
121
|
components: {
|
|
122
|
-
Input: async () => import("./HubspotFormInput-
|
|
122
|
+
Input: async () => import("./HubspotFormInput-BVknnlmR.mjs")
|
|
123
123
|
}
|
|
124
124
|
});
|
|
125
125
|
app.createSettingSection(
|
|
@@ -135,7 +135,7 @@ const index = {
|
|
|
135
135
|
// Mirrors the RBAC action registered server-side: a role without it
|
|
136
136
|
// doesn't see the link at all — the routes refuse it anyway.
|
|
137
137
|
permissions: [{ action: `plugin::${PLUGIN_ID}.settings`, subject: null }],
|
|
138
|
-
Component: async () => (await import("./Settings-
|
|
138
|
+
Component: async () => (await import("./Settings-DrujhMxS.mjs")).default
|
|
139
139
|
}
|
|
140
140
|
]
|
|
141
141
|
);
|
|
@@ -145,7 +145,7 @@ const index = {
|
|
|
145
145
|
return Promise.all(
|
|
146
146
|
locales.map(async (locale) => {
|
|
147
147
|
try {
|
|
148
|
-
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-
|
|
148
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-BYKKi1LK.mjs"), "./translations/fr.json": () => import("./fr-DzgArgi3.mjs") }), `./translations/${locale}.json`, 3);
|
|
149
149
|
return { data: prefixPluginTranslations(data), locale };
|
|
150
150
|
} catch {
|
|
151
151
|
return { data: {}, locale };
|
|
@@ -26,7 +26,7 @@ const index = {
|
|
|
26
26
|
icon: icons.PaperPlane,
|
|
27
27
|
intlLabel: { id: `${PLUGIN_ID}.menu.label`, defaultMessage: "HubSpot Forms" },
|
|
28
28
|
permissions: [{ action: `plugin::${PLUGIN_ID}.forms`, subject: null }],
|
|
29
|
-
Component: async () => (await Promise.resolve().then(() => require("./Forms-
|
|
29
|
+
Component: async () => (await Promise.resolve().then(() => require("./Forms-B8kkPY0c.js"))).default
|
|
30
30
|
});
|
|
31
31
|
app.customFields.register({
|
|
32
32
|
name: "property",
|
|
@@ -41,7 +41,7 @@ const index = {
|
|
|
41
41
|
defaultMessage: "Picked from the portal's real properties"
|
|
42
42
|
},
|
|
43
43
|
components: {
|
|
44
|
-
Input: async () => Promise.resolve().then(() => require("./HubspotPropertyInput-
|
|
44
|
+
Input: async () => Promise.resolve().then(() => require("./HubspotPropertyInput-awfD1opH.js"))
|
|
45
45
|
},
|
|
46
46
|
// Exposed in the Content-Type Builder. Without this, `objectField` could
|
|
47
47
|
// only be set by hand-editing schema.json — which is not how anyone
|
|
@@ -104,7 +104,7 @@ const index = {
|
|
|
104
104
|
defaultMessage: "Picked from the portal's configured objects"
|
|
105
105
|
},
|
|
106
106
|
components: {
|
|
107
|
-
Input: async () => Promise.resolve().then(() => require("./HubspotObjectInput-
|
|
107
|
+
Input: async () => Promise.resolve().then(() => require("./HubspotObjectInput-BOO1PvOj.js"))
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
app.customFields.register({
|
|
@@ -120,7 +120,7 @@ const index = {
|
|
|
120
120
|
defaultMessage: "A form built in the HubSpot form builder, stored by slug"
|
|
121
121
|
},
|
|
122
122
|
components: {
|
|
123
|
-
Input: async () => Promise.resolve().then(() => require("./HubspotFormInput-
|
|
123
|
+
Input: async () => Promise.resolve().then(() => require("./HubspotFormInput-kx3LlQYF.js"))
|
|
124
124
|
}
|
|
125
125
|
});
|
|
126
126
|
app.createSettingSection(
|
|
@@ -136,7 +136,7 @@ const index = {
|
|
|
136
136
|
// Mirrors the RBAC action registered server-side: a role without it
|
|
137
137
|
// doesn't see the link at all — the routes refuse it anyway.
|
|
138
138
|
permissions: [{ action: `plugin::${PLUGIN_ID}.settings`, subject: null }],
|
|
139
|
-
Component: async () => (await Promise.resolve().then(() => require("./Settings-
|
|
139
|
+
Component: async () => (await Promise.resolve().then(() => require("./Settings-BT1NhvxO.js"))).default
|
|
140
140
|
}
|
|
141
141
|
]
|
|
142
142
|
);
|
|
@@ -146,7 +146,7 @@ const index = {
|
|
|
146
146
|
return Promise.all(
|
|
147
147
|
locales.map(async (locale) => {
|
|
148
148
|
try {
|
|
149
|
-
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-
|
|
149
|
+
const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-B8adhJhO.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-DnKtGXaB.js")) }), `./translations/${locale}.json`, 3);
|
|
150
150
|
return { data: prefixPluginTranslations(data), locale };
|
|
151
151
|
} catch {
|
|
152
152
|
return { data: {}, locale };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const React = require("react");
|
|
3
3
|
const admin = require("@strapi/strapi/admin");
|
|
4
|
-
const index = require("./index-
|
|
4
|
+
const index = require("./index-P7p8ASb6.js");
|
|
5
5
|
function _interopNamespace(e) {
|
|
6
6
|
if (e && e.__esModule) return e;
|
|
7
7
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
3
|
-
import { P as PLUGIN_ID } from "./index-
|
|
3
|
+
import { P as PLUGIN_ID } from "./index-LrCSAHoH.mjs";
|
|
4
4
|
let cached = null;
|
|
5
5
|
let inFlight = null;
|
|
6
6
|
const listeners = /* @__PURE__ */ new Set();
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -397,6 +397,7 @@ const contentTypes = {
|
|
|
397
397
|
nextLabel: localizedString(),
|
|
398
398
|
submitLabel: localizedString(),
|
|
399
399
|
successMessage: localizedString("text"),
|
|
400
|
+
class: { type: "string" },
|
|
400
401
|
definition: {
|
|
401
402
|
type: "json",
|
|
402
403
|
required: true,
|
|
@@ -573,6 +574,7 @@ function publicForm(entry) {
|
|
|
573
574
|
nextLabel: entry.nextLabel ?? null,
|
|
574
575
|
submitLabel: entry.submitLabel ?? null,
|
|
575
576
|
successMessage: entry.successMessage ?? null,
|
|
577
|
+
class: entry.class ?? null,
|
|
576
578
|
locale: entry.locale ?? null,
|
|
577
579
|
steps: (entry.definition?.steps ?? []).map((step) => ({
|
|
578
580
|
...step,
|
|
@@ -932,6 +934,7 @@ function convertLegacyForm(entry, map = {}) {
|
|
|
932
934
|
nextLabel: entry.nextLabel ?? null,
|
|
933
935
|
submitLabel: entry.submitLabel ?? null,
|
|
934
936
|
successMessage: entry.successMessage ?? null,
|
|
937
|
+
class: entry.class ?? null,
|
|
935
938
|
definition: { version: 1, steps }
|
|
936
939
|
};
|
|
937
940
|
}
|
|
@@ -943,7 +946,8 @@ const WRITABLE = [
|
|
|
943
946
|
"subtitle",
|
|
944
947
|
"nextLabel",
|
|
945
948
|
"submitLabel",
|
|
946
|
-
"successMessage"
|
|
949
|
+
"successMessage",
|
|
950
|
+
"class"
|
|
947
951
|
];
|
|
948
952
|
const slugify = (value) => value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "form";
|
|
949
953
|
function createFormsAdminController(strapi) {
|
|
@@ -962,6 +966,15 @@ function createFormsAdminController(strapi) {
|
|
|
962
966
|
return null;
|
|
963
967
|
}
|
|
964
968
|
}
|
|
969
|
+
async function uniqueSlug(name) {
|
|
970
|
+
const base = slugify(name);
|
|
971
|
+
let slug = base;
|
|
972
|
+
for (let i = 2; ; i += 1) {
|
|
973
|
+
const clash = await documents().findFirst({ filters: { slug } });
|
|
974
|
+
if (!clash) return slug;
|
|
975
|
+
slug = `${base}-${i}`;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
965
978
|
function pickWritable(body) {
|
|
966
979
|
const data = {};
|
|
967
980
|
for (const key of WRITABLE) {
|
|
@@ -1027,13 +1040,7 @@ function createFormsAdminController(strapi) {
|
|
|
1027
1040
|
const body = ctx.request.body ?? {};
|
|
1028
1041
|
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
1029
1042
|
if (!name) ctx.throw(400, "A form needs a name");
|
|
1030
|
-
const
|
|
1031
|
-
let slug = base;
|
|
1032
|
-
for (let i = 2; ; i += 1) {
|
|
1033
|
-
const clash = await documents().findFirst({ filters: { slug } });
|
|
1034
|
-
if (!clash) break;
|
|
1035
|
-
slug = `${base}-${i}`;
|
|
1036
|
-
}
|
|
1043
|
+
const slug = await uniqueSlug(name);
|
|
1037
1044
|
let definition;
|
|
1038
1045
|
try {
|
|
1039
1046
|
definition = requireValidDefinition(ctx, body.definition);
|
|
@@ -1202,6 +1209,57 @@ function createFormsAdminController(strapi) {
|
|
|
1202
1209
|
if (!targetId) ctx.throw(404, "Source entry not found");
|
|
1203
1210
|
ctx.body = { documentId: targetId, locales: imported };
|
|
1204
1211
|
},
|
|
1212
|
+
/**
|
|
1213
|
+
* Copies a form (every locale of its draft) into a new draft document —
|
|
1214
|
+
* the quickest way to A/B a variant or to start from an existing form.
|
|
1215
|
+
* Internal step/field ids are kept: they only need uniqueness per form.
|
|
1216
|
+
*/
|
|
1217
|
+
async duplicate(ctx) {
|
|
1218
|
+
const sourceId = ctx.params.documentId;
|
|
1219
|
+
let locales = [void 0];
|
|
1220
|
+
try {
|
|
1221
|
+
const found = await strapi.plugin("i18n").service("locales").find();
|
|
1222
|
+
if (Array.isArray(found) && found.length) {
|
|
1223
|
+
locales = [...found].sort((a, b) => Number(b.isDefault ?? false) - Number(a.isDefault ?? false)).map((l) => l.code);
|
|
1224
|
+
}
|
|
1225
|
+
} catch {
|
|
1226
|
+
}
|
|
1227
|
+
let targetId = null;
|
|
1228
|
+
let slug = null;
|
|
1229
|
+
for (const locale of locales) {
|
|
1230
|
+
const source = await documents().findOne({
|
|
1231
|
+
documentId: sourceId,
|
|
1232
|
+
locale,
|
|
1233
|
+
status: "draft"
|
|
1234
|
+
});
|
|
1235
|
+
if (!source) continue;
|
|
1236
|
+
const name = `${source.name} (2)`;
|
|
1237
|
+
slug ??= await uniqueSlug(name);
|
|
1238
|
+
const data = {
|
|
1239
|
+
name,
|
|
1240
|
+
slug,
|
|
1241
|
+
title: source.title,
|
|
1242
|
+
subtitle: source.subtitle,
|
|
1243
|
+
nextLabel: source.nextLabel,
|
|
1244
|
+
submitLabel: source.submitLabel,
|
|
1245
|
+
successMessage: source.successMessage,
|
|
1246
|
+
class: source.class,
|
|
1247
|
+
definition: source.definition
|
|
1248
|
+
};
|
|
1249
|
+
if (targetId) {
|
|
1250
|
+
const { slug: _slug, name: _name, ...localized } = data;
|
|
1251
|
+
await documents().update({ documentId: targetId, locale, data: localized });
|
|
1252
|
+
} else {
|
|
1253
|
+
const created = await documents().create({
|
|
1254
|
+
locale,
|
|
1255
|
+
data
|
|
1256
|
+
});
|
|
1257
|
+
targetId = created.documentId;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
if (!targetId) ctx.throw(404, "Form not found");
|
|
1261
|
+
ctx.body = { documentId: targetId };
|
|
1262
|
+
},
|
|
1205
1263
|
async remove(ctx) {
|
|
1206
1264
|
await documents().delete({
|
|
1207
1265
|
documentId: ctx.params.documentId
|
|
@@ -1502,6 +1560,7 @@ const routes = {
|
|
|
1502
1560
|
adminRoute("POST", "/builder/forms/:documentId/publish", "formsAdmin.publish", [FORMS_ACTION]),
|
|
1503
1561
|
adminRoute("POST", "/builder/forms/:documentId/unpublish", "formsAdmin.unpublish", [FORMS_ACTION]),
|
|
1504
1562
|
adminRoute("DELETE", "/builder/forms/:documentId", "formsAdmin.remove", [FORMS_ACTION]),
|
|
1563
|
+
adminRoute("POST", "/builder/forms/:documentId/duplicate", "formsAdmin.duplicate", [FORMS_ACTION]),
|
|
1505
1564
|
// No FORMS_ACTION: editors pick forms from the Content Manager, like
|
|
1506
1565
|
// they pick properties — the builder itself stays gated.
|
|
1507
1566
|
adminRoute("GET", "/forms-options", "formsAdmin.options"),
|
package/dist/server/index.mjs
CHANGED
|
@@ -396,6 +396,7 @@ const contentTypes = {
|
|
|
396
396
|
nextLabel: localizedString(),
|
|
397
397
|
submitLabel: localizedString(),
|
|
398
398
|
successMessage: localizedString("text"),
|
|
399
|
+
class: { type: "string" },
|
|
399
400
|
definition: {
|
|
400
401
|
type: "json",
|
|
401
402
|
required: true,
|
|
@@ -572,6 +573,7 @@ function publicForm(entry) {
|
|
|
572
573
|
nextLabel: entry.nextLabel ?? null,
|
|
573
574
|
submitLabel: entry.submitLabel ?? null,
|
|
574
575
|
successMessage: entry.successMessage ?? null,
|
|
576
|
+
class: entry.class ?? null,
|
|
575
577
|
locale: entry.locale ?? null,
|
|
576
578
|
steps: (entry.definition?.steps ?? []).map((step) => ({
|
|
577
579
|
...step,
|
|
@@ -931,6 +933,7 @@ function convertLegacyForm(entry, map = {}) {
|
|
|
931
933
|
nextLabel: entry.nextLabel ?? null,
|
|
932
934
|
submitLabel: entry.submitLabel ?? null,
|
|
933
935
|
successMessage: entry.successMessage ?? null,
|
|
936
|
+
class: entry.class ?? null,
|
|
934
937
|
definition: { version: 1, steps }
|
|
935
938
|
};
|
|
936
939
|
}
|
|
@@ -942,7 +945,8 @@ const WRITABLE = [
|
|
|
942
945
|
"subtitle",
|
|
943
946
|
"nextLabel",
|
|
944
947
|
"submitLabel",
|
|
945
|
-
"successMessage"
|
|
948
|
+
"successMessage",
|
|
949
|
+
"class"
|
|
946
950
|
];
|
|
947
951
|
const slugify = (value) => value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "form";
|
|
948
952
|
function createFormsAdminController(strapi) {
|
|
@@ -961,6 +965,15 @@ function createFormsAdminController(strapi) {
|
|
|
961
965
|
return null;
|
|
962
966
|
}
|
|
963
967
|
}
|
|
968
|
+
async function uniqueSlug(name) {
|
|
969
|
+
const base = slugify(name);
|
|
970
|
+
let slug = base;
|
|
971
|
+
for (let i = 2; ; i += 1) {
|
|
972
|
+
const clash = await documents().findFirst({ filters: { slug } });
|
|
973
|
+
if (!clash) return slug;
|
|
974
|
+
slug = `${base}-${i}`;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
964
977
|
function pickWritable(body) {
|
|
965
978
|
const data = {};
|
|
966
979
|
for (const key of WRITABLE) {
|
|
@@ -1026,13 +1039,7 @@ function createFormsAdminController(strapi) {
|
|
|
1026
1039
|
const body = ctx.request.body ?? {};
|
|
1027
1040
|
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
1028
1041
|
if (!name) ctx.throw(400, "A form needs a name");
|
|
1029
|
-
const
|
|
1030
|
-
let slug = base;
|
|
1031
|
-
for (let i = 2; ; i += 1) {
|
|
1032
|
-
const clash = await documents().findFirst({ filters: { slug } });
|
|
1033
|
-
if (!clash) break;
|
|
1034
|
-
slug = `${base}-${i}`;
|
|
1035
|
-
}
|
|
1042
|
+
const slug = await uniqueSlug(name);
|
|
1036
1043
|
let definition;
|
|
1037
1044
|
try {
|
|
1038
1045
|
definition = requireValidDefinition(ctx, body.definition);
|
|
@@ -1201,6 +1208,57 @@ function createFormsAdminController(strapi) {
|
|
|
1201
1208
|
if (!targetId) ctx.throw(404, "Source entry not found");
|
|
1202
1209
|
ctx.body = { documentId: targetId, locales: imported };
|
|
1203
1210
|
},
|
|
1211
|
+
/**
|
|
1212
|
+
* Copies a form (every locale of its draft) into a new draft document —
|
|
1213
|
+
* the quickest way to A/B a variant or to start from an existing form.
|
|
1214
|
+
* Internal step/field ids are kept: they only need uniqueness per form.
|
|
1215
|
+
*/
|
|
1216
|
+
async duplicate(ctx) {
|
|
1217
|
+
const sourceId = ctx.params.documentId;
|
|
1218
|
+
let locales = [void 0];
|
|
1219
|
+
try {
|
|
1220
|
+
const found = await strapi.plugin("i18n").service("locales").find();
|
|
1221
|
+
if (Array.isArray(found) && found.length) {
|
|
1222
|
+
locales = [...found].sort((a, b) => Number(b.isDefault ?? false) - Number(a.isDefault ?? false)).map((l) => l.code);
|
|
1223
|
+
}
|
|
1224
|
+
} catch {
|
|
1225
|
+
}
|
|
1226
|
+
let targetId = null;
|
|
1227
|
+
let slug = null;
|
|
1228
|
+
for (const locale of locales) {
|
|
1229
|
+
const source = await documents().findOne({
|
|
1230
|
+
documentId: sourceId,
|
|
1231
|
+
locale,
|
|
1232
|
+
status: "draft"
|
|
1233
|
+
});
|
|
1234
|
+
if (!source) continue;
|
|
1235
|
+
const name = `${source.name} (2)`;
|
|
1236
|
+
slug ??= await uniqueSlug(name);
|
|
1237
|
+
const data = {
|
|
1238
|
+
name,
|
|
1239
|
+
slug,
|
|
1240
|
+
title: source.title,
|
|
1241
|
+
subtitle: source.subtitle,
|
|
1242
|
+
nextLabel: source.nextLabel,
|
|
1243
|
+
submitLabel: source.submitLabel,
|
|
1244
|
+
successMessage: source.successMessage,
|
|
1245
|
+
class: source.class,
|
|
1246
|
+
definition: source.definition
|
|
1247
|
+
};
|
|
1248
|
+
if (targetId) {
|
|
1249
|
+
const { slug: _slug, name: _name, ...localized } = data;
|
|
1250
|
+
await documents().update({ documentId: targetId, locale, data: localized });
|
|
1251
|
+
} else {
|
|
1252
|
+
const created = await documents().create({
|
|
1253
|
+
locale,
|
|
1254
|
+
data
|
|
1255
|
+
});
|
|
1256
|
+
targetId = created.documentId;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
if (!targetId) ctx.throw(404, "Form not found");
|
|
1260
|
+
ctx.body = { documentId: targetId };
|
|
1261
|
+
},
|
|
1204
1262
|
async remove(ctx) {
|
|
1205
1263
|
await documents().delete({
|
|
1206
1264
|
documentId: ctx.params.documentId
|
|
@@ -1501,6 +1559,7 @@ const routes = {
|
|
|
1501
1559
|
adminRoute("POST", "/builder/forms/:documentId/publish", "formsAdmin.publish", [FORMS_ACTION]),
|
|
1502
1560
|
adminRoute("POST", "/builder/forms/:documentId/unpublish", "formsAdmin.unpublish", [FORMS_ACTION]),
|
|
1503
1561
|
adminRoute("DELETE", "/builder/forms/:documentId", "formsAdmin.remove", [FORMS_ACTION]),
|
|
1562
|
+
adminRoute("POST", "/builder/forms/:documentId/duplicate", "formsAdmin.duplicate", [FORMS_ACTION]),
|
|
1504
1563
|
// No FORMS_ACTION: editors pick forms from the Content Manager, like
|
|
1505
1564
|
// they pick properties — the builder itself stays gated.
|
|
1506
1565
|
adminRoute("GET", "/forms-options", "formsAdmin.options"),
|
|
@@ -19,6 +19,7 @@ export interface FormEntry {
|
|
|
19
19
|
nextLabel?: string | null;
|
|
20
20
|
submitLabel?: string | null;
|
|
21
21
|
successMessage?: string | null;
|
|
22
|
+
class?: string | null;
|
|
22
23
|
locale?: string | null;
|
|
23
24
|
definition: FormDefinition;
|
|
24
25
|
[key: string]: unknown;
|
|
@@ -30,6 +31,7 @@ export interface PublicForm {
|
|
|
30
31
|
nextLabel?: string | null;
|
|
31
32
|
submitLabel?: string | null;
|
|
32
33
|
successMessage?: string | null;
|
|
34
|
+
class?: string | null;
|
|
33
35
|
locale?: string | null;
|
|
34
36
|
steps: FormDefinition["steps"];
|
|
35
37
|
}
|
|
@@ -42,5 +42,11 @@ export declare function createFormsAdminController(strapi: Core.Strapi): {
|
|
|
42
42
|
* published version, and never modifies the source.
|
|
43
43
|
*/
|
|
44
44
|
runImport(ctx: Ctx): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Copies a form (every locale of its draft) into a new draft document —
|
|
47
|
+
* the quickest way to A/B a variant or to start from an existing form.
|
|
48
|
+
* Internal step/field ids are kept: they only need uniqueness per form.
|
|
49
|
+
*/
|
|
50
|
+
duplicate(ctx: Ctx): Promise<void>;
|
|
45
51
|
remove(ctx: Ctx): Promise<void>;
|
|
46
52
|
};
|
|
@@ -20,6 +20,7 @@ export interface ConvertedForm {
|
|
|
20
20
|
nextLabel?: string | null;
|
|
21
21
|
submitLabel?: string | null;
|
|
22
22
|
successMessage?: string | null;
|
|
23
|
+
class?: string | null;
|
|
23
24
|
definition: FormDefinition;
|
|
24
25
|
}
|
|
25
26
|
export declare function convertLegacyForm(entry: Record<string, unknown>, map?: ImportMap): ConvertedForm;
|
|
@@ -96,6 +96,9 @@ declare const _default: {
|
|
|
96
96
|
};
|
|
97
97
|
};
|
|
98
98
|
};
|
|
99
|
+
class: {
|
|
100
|
+
type: string;
|
|
101
|
+
};
|
|
99
102
|
definition: {
|
|
100
103
|
type: string;
|
|
101
104
|
required: boolean;
|
|
@@ -253,6 +256,7 @@ declare const _default: {
|
|
|
253
256
|
options(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
254
257
|
listSources(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
255
258
|
runImport(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
259
|
+
duplicate(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
256
260
|
remove(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
257
261
|
};
|
|
258
262
|
forms: ({ strapi }: {
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -72,6 +72,8 @@ export interface PublicForm {
|
|
|
72
72
|
nextLabel?: string | null;
|
|
73
73
|
submitLabel?: string | null;
|
|
74
74
|
successMessage?: string | null;
|
|
75
|
+
/** Extra utility classes on the form wrapper, for the host frontend. */
|
|
76
|
+
class?: string | null;
|
|
75
77
|
locale?: string | null;
|
|
76
78
|
steps: PublicFormStep[];
|
|
77
79
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-hubspot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "The form builder for HubSpot in Strapi — multi-step forms with conditional fields mapped to real CRM properties, a public submit pipeline with retries, and save-time mapping validation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|