strapi-plugin-hubspot 0.16.7 → 0.16.8
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 +12 -0
- package/dist/_chunks/{Forms-DyV6KyiO.mjs → Forms-CUNHDvhI.mjs} +52 -6
- package/dist/_chunks/{Forms-uUN4XjhW.js → Forms-CylJ0nD3.js} +52 -6
- package/dist/_chunks/{HubspotFormInput-BkWat_wu.js → HubspotFormInput-D2H2u9fZ.js} +1 -1
- package/dist/_chunks/{HubspotFormInput-DEXPX-6j.mjs → HubspotFormInput-Dppgr_rd.mjs} +1 -1
- package/dist/_chunks/{HubspotObjectInput-CFDYW-TM.js → HubspotObjectInput-BjRCaqXr.js} +3 -3
- package/dist/_chunks/{HubspotObjectInput-DL8fYK2x.mjs → HubspotObjectInput-CWFddNNw.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-CFMkbQeT.js → HubspotPropertyInput-BlDarkcj.js} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-WU5FMJj3.mjs → HubspotPropertyInput-TGG9ozTa.mjs} +3 -3
- package/dist/_chunks/{Settings-B_hhdpHa.js → Settings-DOx7YSqA.js} +2 -2
- package/dist/_chunks/{Settings-CiXEps_C.mjs → Settings-tct49GYU.mjs} +2 -2
- package/dist/_chunks/{index-BeBn7bJa.mjs → index-Au3W85GX.mjs} +5 -5
- package/dist/_chunks/{index-CG_WQzSa.js → index-DhGVA3ZL.js} +5 -5
- package/dist/_chunks/{objectLabels-CS_0QCNz.mjs → objectLabels-3rnJ0WUc.mjs} +1 -1
- package/dist/_chunks/{objectLabels-BGq-Ypgn.js → objectLabels-Bx7x8fFO.js} +1 -1
- package/dist/_chunks/{useHubspotSchema-CP7LRdzO.mjs → useHubspotSchema-BlVXfwju.mjs} +1 -1
- package/dist/_chunks/{useHubspotSchema-BZP2qkAu.js → useHubspotSchema-DrGeidXC.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/builder/types.d.ts +2 -1
- package/dist/server/index.js +229 -23
- package/dist/server/index.mjs +229 -23
- package/dist/server/src/conditions.d.ts +1 -0
- package/dist/server/src/formSync.d.ts +20 -0
- package/dist/server/src/formsAdmin.d.ts +1 -0
- package/dist/server/src/hsforms.d.ts +2 -0
- package/dist/server/src/importHubspot.d.ts +2 -1
- package/dist/server/src/index.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.16.8 — 2026-09-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Publishing a form linked to HubSpot is blocked when a required marketing
|
|
9
|
+
field is not collected. Submissions show the conversion error next to
|
|
10
|
+
**Contact only**.
|
|
11
|
+
- **Create in HubSpot** builds a marketing form from the Strapi definition
|
|
12
|
+
and stores its GUID.
|
|
13
|
+
- Resync updates conditions that came from HubSpot and leaves editor-written
|
|
14
|
+
ones. With field sync on, `visibleIf` is pushed back as HubSpot dependent
|
|
15
|
+
fields. Progressive groups are reported on import.
|
|
16
|
+
|
|
5
17
|
## 0.16.7 — 2026-09-23
|
|
6
18
|
|
|
7
19
|
### Fixed
|
|
@@ -5,12 +5,12 @@ import { u as useIntl } from "./index-CEh8vkxY.mjs";
|
|
|
5
5
|
import { Typography, Flex, Box, SingleSelect, SingleSelectOption, MultiSelect, MultiSelectOption, TextInput, IconButton, Button, Combobox, ComboboxOption, Field, Textarea, Checkbox, Divider, Loader, Badge, Table, Thead, Tr, Th, Tbody, Td, Modal } from "@strapi/design-system";
|
|
6
6
|
import { Trash, Plus, ArrowClockwise, ArrowLeft, Drag, ArrowUp, ArrowDown, List, Duplicate, Download } from "@strapi/icons";
|
|
7
7
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
8
|
-
import { g as getTranslation, P as PLUGIN_ID } from "./index-
|
|
8
|
+
import { g as getTranslation, P as PLUGIN_ID } from "./index-Au3W85GX.mjs";
|
|
9
9
|
import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, useDroppable, closestCorners } from "@dnd-kit/core";
|
|
10
10
|
import { arrayMove, sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
|
|
11
11
|
import { CSS } from "@dnd-kit/utilities";
|
|
12
|
-
import { o as objectLabel } from "./objectLabels-
|
|
13
|
-
import { u as useHubspotSchema } from "./useHubspotSchema-
|
|
12
|
+
import { o as objectLabel } from "./objectLabels-3rnJ0WUc.mjs";
|
|
13
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-BlVXfwju.mjs";
|
|
14
14
|
const OPERATORS = [
|
|
15
15
|
"eq",
|
|
16
16
|
"neq",
|
|
@@ -682,7 +682,7 @@ const FieldPanel = ({ field, candidates, problem, onChange }) => {
|
|
|
682
682
|
{
|
|
683
683
|
condition: field.visibleIf,
|
|
684
684
|
candidates,
|
|
685
|
-
onChange: (visibleIf) => onChange({ visibleIf })
|
|
685
|
+
onChange: (visibleIf) => onChange({ visibleIf, visibleIfSource: "editor" })
|
|
686
686
|
}
|
|
687
687
|
)
|
|
688
688
|
] })
|
|
@@ -882,9 +882,14 @@ const FormEditor = () => {
|
|
|
882
882
|
const data = err.response?.data;
|
|
883
883
|
setErrors(data?.errors ?? []);
|
|
884
884
|
setProblems(data?.problems ?? []);
|
|
885
|
+
const missing = data?.missingHubspot ?? [];
|
|
885
886
|
setFeedback({
|
|
886
887
|
tone: "danger",
|
|
887
|
-
text:
|
|
888
|
+
text: missing.length ? t(
|
|
889
|
+
"editor.publish-missing-hubspot",
|
|
890
|
+
"Publishing is blocked — the linked HubSpot form requires {fields}, which this form does not collect.",
|
|
891
|
+
{ fields: missing.map((field) => `${field.object}.${field.name}`).join(", ") }
|
|
892
|
+
) : t("editor.publish-blocked", "Publishing is blocked — fix the flagged mappings first.")
|
|
888
893
|
});
|
|
889
894
|
} finally {
|
|
890
895
|
setBusy(false);
|
|
@@ -902,6 +907,29 @@ const FormEditor = () => {
|
|
|
902
907
|
setBusy(false);
|
|
903
908
|
}
|
|
904
909
|
};
|
|
910
|
+
const createInHubspot = async () => {
|
|
911
|
+
if (dirty && !await save()) return;
|
|
912
|
+
setBusy(true);
|
|
913
|
+
setFeedback(null);
|
|
914
|
+
try {
|
|
915
|
+
const { data } = await post(
|
|
916
|
+
`/${PLUGIN_ID}/builder/forms/${documentId}/create-hubspot${query}`
|
|
917
|
+
);
|
|
918
|
+
if (data.form) setEntry(data.form);
|
|
919
|
+
setFeedback({
|
|
920
|
+
tone: "success",
|
|
921
|
+
text: t("editor.created-hubspot", "HubSpot form created and linked.")
|
|
922
|
+
});
|
|
923
|
+
} catch (err) {
|
|
924
|
+
const message = err.response?.data?.error?.message;
|
|
925
|
+
setFeedback({
|
|
926
|
+
tone: "danger",
|
|
927
|
+
text: message || t("editor.create-hubspot-error", "Could not create the HubSpot form.")
|
|
928
|
+
});
|
|
929
|
+
} finally {
|
|
930
|
+
setBusy(false);
|
|
931
|
+
}
|
|
932
|
+
};
|
|
905
933
|
const resync = async () => {
|
|
906
934
|
if (dirty && !await save()) return;
|
|
907
935
|
setBusy(true);
|
|
@@ -982,6 +1010,15 @@ const FormEditor = () => {
|
|
|
982
1010
|
children: t("editor.resync", "Resync from HubSpot")
|
|
983
1011
|
}
|
|
984
1012
|
),
|
|
1013
|
+
/* @__PURE__ */ jsx(
|
|
1014
|
+
Button,
|
|
1015
|
+
{
|
|
1016
|
+
variant: "tertiary",
|
|
1017
|
+
onClick: createInHubspot,
|
|
1018
|
+
disabled: busy || Boolean(entry.hubspotFormId),
|
|
1019
|
+
children: t("editor.create-hubspot", "Create in HubSpot")
|
|
1020
|
+
}
|
|
1021
|
+
),
|
|
985
1022
|
/* @__PURE__ */ jsx(Button, { onClick: publish, disabled: busy, children: t("editor.publish", "Publish") }),
|
|
986
1023
|
published && /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: unpublish, disabled: busy, children: t("editor.unpublish", "Unpublish") })
|
|
987
1024
|
] })
|
|
@@ -1468,6 +1505,12 @@ const FormsList = () => {
|
|
|
1468
1505
|
return t("forms.skipped-rich-text", "A content block was skipped — the builder has no rich-text element");
|
|
1469
1506
|
case "legal-consent":
|
|
1470
1507
|
return t("forms.skipped-consent", "The GDPR consent block was skipped — rebuild it as a field if needed");
|
|
1508
|
+
case "progressive":
|
|
1509
|
+
return t(
|
|
1510
|
+
"forms.skipped-progressive",
|
|
1511
|
+
"A progressive field group was skipped — it is not a visibility condition ({detail})",
|
|
1512
|
+
{ detail: item.detail ?? "" }
|
|
1513
|
+
);
|
|
1471
1514
|
}
|
|
1472
1515
|
};
|
|
1473
1516
|
const create = async () => {
|
|
@@ -1774,7 +1817,10 @@ const Submissions = () => {
|
|
|
1774
1817
|
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { children: row.formTitle || row.form }) }),
|
|
1775
1818
|
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: row.email || "—" }) }),
|
|
1776
1819
|
/* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", ellipsis: true, children: preview(row.values) }) }),
|
|
1777
|
-
/* @__PURE__ */ jsx(Td, { children: row.meta?.hubspotConversion === true || row.meta?.hubspotConversion === "true" ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: t("submissions.converted", "Conversion") }) : row.hubspotSynced ? /* @__PURE__ */
|
|
1820
|
+
/* @__PURE__ */ jsx(Td, { children: row.meta?.hubspotConversion === true || row.meta?.hubspotConversion === "true" ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success100", textColor: "success700", children: t("submissions.converted", "Conversion") }) : row.hubspotSynced ? /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
|
|
1821
|
+
/* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: t("submissions.contact-only", "Contact only") }),
|
|
1822
|
+
typeof row.meta?.hubspotError === "string" && row.meta.hubspotError && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger600", children: row.meta.hubspotError })
|
|
1823
|
+
] }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "warning100", textColor: "warning700", children: t("submissions.not-synced", "Not synced") }) })
|
|
1778
1824
|
]
|
|
1779
1825
|
},
|
|
1780
1826
|
row.documentId
|
|
@@ -7,12 +7,12 @@ 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-
|
|
10
|
+
const index$1 = require("./index-DhGVA3ZL.js");
|
|
11
11
|
const core = require("@dnd-kit/core");
|
|
12
12
|
const sortable = require("@dnd-kit/sortable");
|
|
13
13
|
const utilities = require("@dnd-kit/utilities");
|
|
14
|
-
const objectLabels = require("./objectLabels-
|
|
15
|
-
const useHubspotSchema = require("./useHubspotSchema-
|
|
14
|
+
const objectLabels = require("./objectLabels-Bx7x8fFO.js");
|
|
15
|
+
const useHubspotSchema = require("./useHubspotSchema-DrGeidXC.js");
|
|
16
16
|
function _interopNamespace(e) {
|
|
17
17
|
if (e && e.__esModule) return e;
|
|
18
18
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -702,7 +702,7 @@ const FieldPanel = ({ field, candidates, problem, onChange }) => {
|
|
|
702
702
|
{
|
|
703
703
|
condition: field.visibleIf,
|
|
704
704
|
candidates,
|
|
705
|
-
onChange: (visibleIf) => onChange({ visibleIf })
|
|
705
|
+
onChange: (visibleIf) => onChange({ visibleIf, visibleIfSource: "editor" })
|
|
706
706
|
}
|
|
707
707
|
)
|
|
708
708
|
] })
|
|
@@ -902,9 +902,14 @@ const FormEditor = () => {
|
|
|
902
902
|
const data = err.response?.data;
|
|
903
903
|
setErrors(data?.errors ?? []);
|
|
904
904
|
setProblems(data?.problems ?? []);
|
|
905
|
+
const missing = data?.missingHubspot ?? [];
|
|
905
906
|
setFeedback({
|
|
906
907
|
tone: "danger",
|
|
907
|
-
text:
|
|
908
|
+
text: missing.length ? t(
|
|
909
|
+
"editor.publish-missing-hubspot",
|
|
910
|
+
"Publishing is blocked — the linked HubSpot form requires {fields}, which this form does not collect.",
|
|
911
|
+
{ fields: missing.map((field) => `${field.object}.${field.name}`).join(", ") }
|
|
912
|
+
) : t("editor.publish-blocked", "Publishing is blocked — fix the flagged mappings first.")
|
|
908
913
|
});
|
|
909
914
|
} finally {
|
|
910
915
|
setBusy(false);
|
|
@@ -922,6 +927,29 @@ const FormEditor = () => {
|
|
|
922
927
|
setBusy(false);
|
|
923
928
|
}
|
|
924
929
|
};
|
|
930
|
+
const createInHubspot = async () => {
|
|
931
|
+
if (dirty && !await save()) return;
|
|
932
|
+
setBusy(true);
|
|
933
|
+
setFeedback(null);
|
|
934
|
+
try {
|
|
935
|
+
const { data } = await post(
|
|
936
|
+
`/${index$1.PLUGIN_ID}/builder/forms/${documentId}/create-hubspot${query}`
|
|
937
|
+
);
|
|
938
|
+
if (data.form) setEntry(data.form);
|
|
939
|
+
setFeedback({
|
|
940
|
+
tone: "success",
|
|
941
|
+
text: t("editor.created-hubspot", "HubSpot form created and linked.")
|
|
942
|
+
});
|
|
943
|
+
} catch (err) {
|
|
944
|
+
const message = err.response?.data?.error?.message;
|
|
945
|
+
setFeedback({
|
|
946
|
+
tone: "danger",
|
|
947
|
+
text: message || t("editor.create-hubspot-error", "Could not create the HubSpot form.")
|
|
948
|
+
});
|
|
949
|
+
} finally {
|
|
950
|
+
setBusy(false);
|
|
951
|
+
}
|
|
952
|
+
};
|
|
925
953
|
const resync = async () => {
|
|
926
954
|
if (dirty && !await save()) return;
|
|
927
955
|
setBusy(true);
|
|
@@ -1002,6 +1030,15 @@ const FormEditor = () => {
|
|
|
1002
1030
|
children: t("editor.resync", "Resync from HubSpot")
|
|
1003
1031
|
}
|
|
1004
1032
|
),
|
|
1033
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1034
|
+
designSystem.Button,
|
|
1035
|
+
{
|
|
1036
|
+
variant: "tertiary",
|
|
1037
|
+
onClick: createInHubspot,
|
|
1038
|
+
disabled: busy || Boolean(entry.hubspotFormId),
|
|
1039
|
+
children: t("editor.create-hubspot", "Create in HubSpot")
|
|
1040
|
+
}
|
|
1041
|
+
),
|
|
1005
1042
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: publish, disabled: busy, children: t("editor.publish", "Publish") }),
|
|
1006
1043
|
published && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: unpublish, disabled: busy, children: t("editor.unpublish", "Unpublish") })
|
|
1007
1044
|
] })
|
|
@@ -1488,6 +1525,12 @@ const FormsList = () => {
|
|
|
1488
1525
|
return t("forms.skipped-rich-text", "A content block was skipped — the builder has no rich-text element");
|
|
1489
1526
|
case "legal-consent":
|
|
1490
1527
|
return t("forms.skipped-consent", "The GDPR consent block was skipped — rebuild it as a field if needed");
|
|
1528
|
+
case "progressive":
|
|
1529
|
+
return t(
|
|
1530
|
+
"forms.skipped-progressive",
|
|
1531
|
+
"A progressive field group was skipped — it is not a visibility condition ({detail})",
|
|
1532
|
+
{ detail: item.detail ?? "" }
|
|
1533
|
+
);
|
|
1491
1534
|
}
|
|
1492
1535
|
};
|
|
1493
1536
|
const create = async () => {
|
|
@@ -1794,7 +1837,10 @@ const Submissions = () => {
|
|
|
1794
1837
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: row.formTitle || row.form }) }),
|
|
1795
1838
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: row.email || "—" }) }),
|
|
1796
1839
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", ellipsis: true, children: preview(row.values) }) }),
|
|
1797
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: row.meta?.hubspotConversion === true || row.meta?.hubspotConversion === "true" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: t("submissions.converted", "Conversion") }) : row.hubspotSynced ? /* @__PURE__ */ jsxRuntime.
|
|
1840
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: row.meta?.hubspotConversion === true || row.meta?.hubspotConversion === "true" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: t("submissions.converted", "Conversion") }) : row.hubspotSynced ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 1, children: [
|
|
1841
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: t("submissions.contact-only", "Contact only") }),
|
|
1842
|
+
typeof row.meta?.hubspotError === "string" && row.meta.hubspotError && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger600", children: row.meta.hubspotError })
|
|
1843
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: t("submissions.not-synced", "Not synced") }) })
|
|
1798
1844
|
]
|
|
1799
1845
|
},
|
|
1800
1846
|
row.documentId
|
|
@@ -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-DhGVA3ZL.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" } });
|
|
@@ -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-Au3W85GX.mjs";
|
|
7
7
|
const HubspotFormInput = React.forwardRef(
|
|
8
8
|
({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
|
|
9
9
|
const { formatMessage } = 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-DhGVA3ZL.js");
|
|
8
|
+
const objectLabels = require("./objectLabels-Bx7x8fFO.js");
|
|
9
|
+
const useHubspotSchema = require("./useHubspotSchema-DrGeidXC.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" } });
|
|
@@ -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-Au3W85GX.mjs";
|
|
6
|
+
import { o as objectLabel } from "./objectLabels-3rnJ0WUc.mjs";
|
|
7
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-BlVXfwju.mjs";
|
|
8
8
|
const HubspotObjectInput = React.forwardRef(
|
|
9
9
|
({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
|
|
10
10
|
const intl = useIntl();
|
|
@@ -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-DhGVA3ZL.js");
|
|
10
|
+
const objectLabels = require("./objectLabels-Bx7x8fFO.js");
|
|
11
|
+
const useHubspotSchema = require("./useHubspotSchema-DrGeidXC.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" } });
|
package/dist/_chunks/{HubspotPropertyInput-WU5FMJj3.mjs → HubspotPropertyInput-TGG9ozTa.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-Au3W85GX.mjs";
|
|
8
|
+
import { o as objectLabel } from "./objectLabels-3rnJ0WUc.mjs";
|
|
9
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-BlVXfwju.mjs";
|
|
10
10
|
const oneLine = {
|
|
11
11
|
display: "block",
|
|
12
12
|
whiteSpace: "nowrap",
|
|
@@ -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-DhGVA3ZL.js");
|
|
9
9
|
const reactRouterDom = require("react-router-dom");
|
|
10
|
-
const objectLabels = require("./objectLabels-
|
|
10
|
+
const objectLabels = require("./objectLabels-Bx7x8fFO.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, SingleSelect, SingleSelectOption, Checkbox } 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-Au3W85GX.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-3rnJ0WUc.mjs";
|
|
9
9
|
const AuditSection = ({ configured }) => {
|
|
10
10
|
const intl = useIntl();
|
|
11
11
|
const { formatMessage } = intl;
|
|
@@ -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-CUNHDvhI.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-TGG9ozTa.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-CWFddNNw.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-Dppgr_rd.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-tct49GYU.mjs")).default
|
|
139
139
|
}
|
|
140
140
|
]
|
|
141
141
|
);
|
|
@@ -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-CylJ0nD3.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-BlDarkcj.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-BjRCaqXr.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-D2H2u9fZ.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-DOx7YSqA.js"))).default
|
|
140
140
|
}
|
|
141
141
|
]
|
|
142
142
|
);
|
|
@@ -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-Au3W85GX.mjs";
|
|
4
4
|
let cached = null;
|
|
5
5
|
let inFlight = null;
|
|
6
6
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -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-DhGVA3ZL.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" } });
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
|
@@ -45,6 +45,7 @@ export interface FormField {
|
|
|
45
45
|
/** Company fields: rotating placeholder examples, one per line. */
|
|
46
46
|
placeholderExamples?: string[];
|
|
47
47
|
visibleIf?: Condition | null;
|
|
48
|
+
visibleIfSource?: "hubspot" | "editor" | null;
|
|
48
49
|
}
|
|
49
50
|
export interface FormStep {
|
|
50
51
|
id: string;
|
|
@@ -92,7 +93,7 @@ export interface HubspotSource {
|
|
|
92
93
|
}
|
|
93
94
|
/** What a HubSpot import couldn't carry over (mirrors server SkippedItem). */
|
|
94
95
|
export interface SkippedItem {
|
|
95
|
-
code: "field-type" | "hidden-field" | "object" | "duplicate" | "condition" | "rich-text" | "legal-consent";
|
|
96
|
+
code: "field-type" | "hidden-field" | "object" | "duplicate" | "condition" | "rich-text" | "legal-consent" | "progressive";
|
|
96
97
|
label?: string;
|
|
97
98
|
detail?: string;
|
|
98
99
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -1600,6 +1600,48 @@ function missingMappedFields(definition, existingNames) {
|
|
|
1600
1600
|
const existing = new Set(existingNames);
|
|
1601
1601
|
return mappedContactFields(definition).filter((field) => !existing.has(field.property));
|
|
1602
1602
|
}
|
|
1603
|
+
const OBJECT_TYPE_BY_NAME = {
|
|
1604
|
+
contact: "0-1",
|
|
1605
|
+
company: "0-2"
|
|
1606
|
+
};
|
|
1607
|
+
function collectedHubspotKeys(definition) {
|
|
1608
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1609
|
+
const add = (object, property) => {
|
|
1610
|
+
const typeId = OBJECT_TYPE_BY_NAME[object] || object;
|
|
1611
|
+
if (property) keys.add(`${typeId}:${property}`);
|
|
1612
|
+
};
|
|
1613
|
+
for (const step of definition.steps ?? []) {
|
|
1614
|
+
for (const fld of step.fields ?? []) {
|
|
1615
|
+
if (fld.type === "company") {
|
|
1616
|
+
const map = fld.companyMap ?? {};
|
|
1617
|
+
for (const slot of Object.values(map)) {
|
|
1618
|
+
if (slot?.property?.trim()) add(slot.object?.trim() || "company", slot.property.trim());
|
|
1619
|
+
}
|
|
1620
|
+
continue;
|
|
1621
|
+
}
|
|
1622
|
+
const mapping = fld.hubspot ?? {};
|
|
1623
|
+
const object = mapping.object?.trim() || "contact";
|
|
1624
|
+
const property = mapping.property?.trim() || fld.name;
|
|
1625
|
+
if (property) add(object, property);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
return keys;
|
|
1629
|
+
}
|
|
1630
|
+
function missingRequiredHubspotFields(definition, required) {
|
|
1631
|
+
const collected = collectedHubspotKeys(definition);
|
|
1632
|
+
const missing = [];
|
|
1633
|
+
for (const field of required) {
|
|
1634
|
+
if (!field.name) continue;
|
|
1635
|
+
const objectTypeId = field.objectTypeId?.trim() || "0-1";
|
|
1636
|
+
if (collected.has(`${objectTypeId}:${field.name}`)) continue;
|
|
1637
|
+
missing.push({
|
|
1638
|
+
name: field.name,
|
|
1639
|
+
objectTypeId,
|
|
1640
|
+
object: objectTypeId === "0-2" ? "company" : "contact"
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
return missing;
|
|
1644
|
+
}
|
|
1603
1645
|
function hsFieldType(type, options) {
|
|
1604
1646
|
switch (type) {
|
|
1605
1647
|
case "email":
|
|
@@ -1650,6 +1692,74 @@ function appendFieldGroups(existing, extra) {
|
|
|
1650
1692
|
}))
|
|
1651
1693
|
];
|
|
1652
1694
|
}
|
|
1695
|
+
function conditionToHubspot(condition, parentId) {
|
|
1696
|
+
const rules = (condition.rules ?? []).filter((rule) => rule.field === parentId);
|
|
1697
|
+
if (!rules.length || rules.length !== (condition.rules ?? []).length) return null;
|
|
1698
|
+
const op = rules[0]?.operator;
|
|
1699
|
+
if (rules.some((rule) => rule.operator !== op)) return null;
|
|
1700
|
+
const values = rules.map((rule) => rule.value).filter((value) => Boolean(value));
|
|
1701
|
+
switch (op) {
|
|
1702
|
+
case "eq":
|
|
1703
|
+
return { operator: values.length > 1 ? "SET_ANY" : "EQ", values };
|
|
1704
|
+
case "neq":
|
|
1705
|
+
return { operator: values.length > 1 ? "NOT_SET_ANY" : "NEQ", values };
|
|
1706
|
+
case "contains":
|
|
1707
|
+
return { operator: "CONTAINS", values };
|
|
1708
|
+
case "gt":
|
|
1709
|
+
return { operator: "GT", values };
|
|
1710
|
+
case "lt":
|
|
1711
|
+
return { operator: "LT", values };
|
|
1712
|
+
case "notEmpty":
|
|
1713
|
+
return { operator: "SET", values: [] };
|
|
1714
|
+
case "empty":
|
|
1715
|
+
return { operator: "NOT_SET", values: [] };
|
|
1716
|
+
default:
|
|
1717
|
+
return null;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
function applyVisibleIfToFieldGroups(groups, definition) {
|
|
1721
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1722
|
+
for (const step of definition.steps ?? []) {
|
|
1723
|
+
for (const fld of step.fields ?? []) {
|
|
1724
|
+
const mapping = fld.hubspot ?? {};
|
|
1725
|
+
byId.set(fld.id, { name: fld.name, property: mapping.property?.trim() || fld.name });
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
const moves = /* @__PURE__ */ new Map();
|
|
1729
|
+
for (const step of definition.steps ?? []) {
|
|
1730
|
+
for (const fld of step.fields ?? []) {
|
|
1731
|
+
const visibleIf = fld.visibleIf;
|
|
1732
|
+
const parentId = visibleIf?.rules?.[0]?.field;
|
|
1733
|
+
if (!visibleIf || !parentId) continue;
|
|
1734
|
+
const converted = conditionToHubspot(visibleIf, parentId);
|
|
1735
|
+
const parent = byId.get(parentId);
|
|
1736
|
+
const mapping = fld.hubspot ?? {};
|
|
1737
|
+
const property = mapping.property?.trim() || fld.name;
|
|
1738
|
+
if (!converted || !parent) continue;
|
|
1739
|
+
moves.set(property, { parent: parent.property, ...converted });
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
if (!moves.size) return groups;
|
|
1743
|
+
const cloned = JSON.parse(JSON.stringify(groups));
|
|
1744
|
+
const flat = [];
|
|
1745
|
+
for (const group of cloned) {
|
|
1746
|
+
(group.fields ?? []).forEach((field, index2) => flat.push({ group, index: index2, field }));
|
|
1747
|
+
}
|
|
1748
|
+
for (const [property, move] of moves) {
|
|
1749
|
+
const child = flat.find((item) => item.field.name === property);
|
|
1750
|
+
const parent = flat.find((item) => item.field.name === move.parent);
|
|
1751
|
+
if (!child || !parent || child === parent) continue;
|
|
1752
|
+
parent.field.dependentFields = [
|
|
1753
|
+
...parent.field.dependentFields ?? [],
|
|
1754
|
+
{
|
|
1755
|
+
dependentCondition: { operator: move.operator, values: move.values },
|
|
1756
|
+
field: child.field
|
|
1757
|
+
}
|
|
1758
|
+
];
|
|
1759
|
+
child.group.fields?.splice(child.index, 1);
|
|
1760
|
+
}
|
|
1761
|
+
return cloned;
|
|
1762
|
+
}
|
|
1653
1763
|
const HS_BASE$2 = "https://api.hubapi.com";
|
|
1654
1764
|
const OBJECT_BY_TYPE_ID = {
|
|
1655
1765
|
"0-1": "contact",
|
|
@@ -1731,7 +1841,7 @@ function convertField(raw, ctx) {
|
|
|
1731
1841
|
helpText: raw.description || void 0,
|
|
1732
1842
|
...options.length ? { options } : {},
|
|
1733
1843
|
hubspot: { object, property: name },
|
|
1734
|
-
...ctx.parentCondition ? { visibleIf: ctx.parentCondition } : {}
|
|
1844
|
+
...ctx.parentCondition ? { visibleIf: ctx.parentCondition, visibleIfSource: "hubspot" } : {}
|
|
1735
1845
|
};
|
|
1736
1846
|
const out = [field];
|
|
1737
1847
|
for (const dependent of raw.dependentFields ?? []) {
|
|
@@ -1754,6 +1864,12 @@ function convertHubspotForm(raw) {
|
|
|
1754
1864
|
const usedNames = /* @__PURE__ */ new Set();
|
|
1755
1865
|
const fields = [];
|
|
1756
1866
|
for (const group of raw.fieldGroups ?? []) {
|
|
1867
|
+
if (group.groupType === "progressive" || group.groupType === "queued") {
|
|
1868
|
+
skipped.push({
|
|
1869
|
+
code: "progressive",
|
|
1870
|
+
detail: group.groupType
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1757
1873
|
if (group.richText && !(group.fields ?? []).length) {
|
|
1758
1874
|
skipped.push({ code: "rich-text", detail: group.richText.slice(0, 80) });
|
|
1759
1875
|
continue;
|
|
@@ -1825,7 +1941,10 @@ function mergeHubspotImport(current, incoming) {
|
|
|
1825
1941
|
existing.helpText = next.helpText;
|
|
1826
1942
|
if (next.options) existing.options = next.options;
|
|
1827
1943
|
else delete existing.options;
|
|
1828
|
-
if (
|
|
1944
|
+
if (next.visibleIf && (existing.visibleIfSource === "hubspot" || !existing.visibleIf)) {
|
|
1945
|
+
existing.visibleIf = next.visibleIf;
|
|
1946
|
+
if (existing.visibleIfSource !== "editor") existing.visibleIfSource = "hubspot";
|
|
1947
|
+
}
|
|
1829
1948
|
updated.push(key);
|
|
1830
1949
|
continue;
|
|
1831
1950
|
}
|
|
@@ -2146,13 +2265,62 @@ async function submitMarketingForm(input) {
|
|
|
2146
2265
|
async function syncFieldsToHubspotForm(apiKey, formGuid, definition) {
|
|
2147
2266
|
const raw = await fetchHubspotForm(apiKey, formGuid);
|
|
2148
2267
|
const extra = missingMappedFields(definition, formFieldNames(raw));
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2268
|
+
const fieldGroups = applyVisibleIfToFieldGroups(
|
|
2269
|
+
appendFieldGroups(raw.fieldGroups ?? [], extra),
|
|
2270
|
+
definition
|
|
2271
|
+
);
|
|
2272
|
+
if (!extra.length && JSON.stringify(fieldGroups) === JSON.stringify(appendFieldGroups(raw.fieldGroups ?? [], []))) {
|
|
2273
|
+
return [];
|
|
2274
|
+
}
|
|
2275
|
+
await patchHubspotForm(apiKey, formGuid, { fieldGroups });
|
|
2153
2276
|
clearFormShapeCache(formGuid);
|
|
2154
2277
|
return extra.map((field) => field.property);
|
|
2155
2278
|
}
|
|
2279
|
+
async function createMarketingForm(apiKey, name, definition) {
|
|
2280
|
+
const fields = mappedContactFields(definition).map((field) => toHsFormField(field));
|
|
2281
|
+
if (!fields.length) {
|
|
2282
|
+
throw new Error("Add at least one mapped contact field before creating a HubSpot form");
|
|
2283
|
+
}
|
|
2284
|
+
const res = await fetch("https://api.hubapi.com/marketing/v3/forms", {
|
|
2285
|
+
method: "POST",
|
|
2286
|
+
headers: {
|
|
2287
|
+
Authorization: `Bearer ${apiKey}`,
|
|
2288
|
+
"Content-Type": "application/json"
|
|
2289
|
+
},
|
|
2290
|
+
body: JSON.stringify({
|
|
2291
|
+
name,
|
|
2292
|
+
formType: "hubspot",
|
|
2293
|
+
fieldGroups: applyVisibleIfToFieldGroups(
|
|
2294
|
+
fields.map((field) => ({
|
|
2295
|
+
groupType: "default_group",
|
|
2296
|
+
richTextType: "text",
|
|
2297
|
+
fields: [field]
|
|
2298
|
+
})),
|
|
2299
|
+
definition
|
|
2300
|
+
),
|
|
2301
|
+
configuration: {
|
|
2302
|
+
language: "fr",
|
|
2303
|
+
recaptchaEnabled: false,
|
|
2304
|
+
createNewContactForNewEmail: true,
|
|
2305
|
+
prePopulateKnownValues: true,
|
|
2306
|
+
notifyContactOwner: false,
|
|
2307
|
+
notifyRecipients: [],
|
|
2308
|
+
editable: true,
|
|
2309
|
+
archivable: true,
|
|
2310
|
+
cloneable: true,
|
|
2311
|
+
allowLinkToResetKnownValues: false,
|
|
2312
|
+
postSubmitAction: { type: "thank_you", value: "Merci." }
|
|
2313
|
+
},
|
|
2314
|
+
displayOptions: { submitButtonText: "Envoyer", style: { submitButtonText: "Envoyer" } },
|
|
2315
|
+
legalConsentOptions: { type: "none" }
|
|
2316
|
+
})
|
|
2317
|
+
});
|
|
2318
|
+
const body = await res.json().catch(() => ({}));
|
|
2319
|
+
if (!res.ok || !body.id) {
|
|
2320
|
+
throw new Error(body.message || `HubSpot ${res.status}`);
|
|
2321
|
+
}
|
|
2322
|
+
return body.id;
|
|
2323
|
+
}
|
|
2156
2324
|
function publicForm(entry) {
|
|
2157
2325
|
return {
|
|
2158
2326
|
slug: entry.slug,
|
|
@@ -2166,7 +2334,9 @@ function publicForm(entry) {
|
|
|
2166
2334
|
steps: (entry.definition?.steps ?? []).map((step) => ({
|
|
2167
2335
|
...step,
|
|
2168
2336
|
// The CRM mappings are the server's business — never the browser's.
|
|
2169
|
-
fields: (step.fields ?? []).map(
|
|
2337
|
+
fields: (step.fields ?? []).map(
|
|
2338
|
+
({ hubspot: _hubspot, companyMap: _companyMap, visibleIfSource: _source, ...fld }) => fld
|
|
2339
|
+
)
|
|
2170
2340
|
}))
|
|
2171
2341
|
};
|
|
2172
2342
|
}
|
|
@@ -2910,6 +3080,22 @@ function createFormsAdminController(strapi) {
|
|
|
2910
3080
|
ctx.body = { errors: structural, problems: mappings };
|
|
2911
3081
|
return;
|
|
2912
3082
|
}
|
|
3083
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3084
|
+
const account = await resolveAccount(strapi);
|
|
3085
|
+
const ownGuid = typeof entry.hubspotFormId === "string" ? entry.hubspotFormId.trim() : "";
|
|
3086
|
+
const formGuid = isFormGuid(ownGuid) ? ownGuid : isFormGuid(account.defaultFormId) ? account.defaultFormId.trim() : "";
|
|
3087
|
+
if (apiKey && formGuid) {
|
|
3088
|
+
const shape = await loadFormShape(apiKey, formGuid);
|
|
3089
|
+
const missingHubspot = shape ? missingRequiredHubspotFields(
|
|
3090
|
+
entry.definition ?? EMPTY_DEFINITION,
|
|
3091
|
+
[...shape.fields.filter((field) => field.required)]
|
|
3092
|
+
) : [];
|
|
3093
|
+
if (missingHubspot.length) {
|
|
3094
|
+
ctx.status = 400;
|
|
3095
|
+
ctx.body = { errors: structural, problems: mappings, missingHubspot };
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
2913
3099
|
await documents().publish({
|
|
2914
3100
|
documentId: ctx.params.documentId,
|
|
2915
3101
|
locale: ctx.query.locale
|
|
@@ -2917,22 +3103,16 @@ function createFormsAdminController(strapi) {
|
|
|
2917
3103
|
const policy = await resolvePolicy(strapi);
|
|
2918
3104
|
let addedFields = [];
|
|
2919
3105
|
let syncWarning;
|
|
2920
|
-
if (policy.syncFieldsOnPublish) {
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
entry.definition ?? EMPTY_DEFINITION
|
|
2931
|
-
);
|
|
2932
|
-
} catch (err) {
|
|
2933
|
-
syncWarning = err.message;
|
|
2934
|
-
strapi.log.warn(`[hubspot] field sync on publish failed — ${syncWarning}`);
|
|
2935
|
-
}
|
|
3106
|
+
if (policy.syncFieldsOnPublish && apiKey && formGuid) {
|
|
3107
|
+
try {
|
|
3108
|
+
addedFields = await syncFieldsToHubspotForm(
|
|
3109
|
+
apiKey,
|
|
3110
|
+
formGuid,
|
|
3111
|
+
entry.definition ?? EMPTY_DEFINITION
|
|
3112
|
+
);
|
|
3113
|
+
} catch (err) {
|
|
3114
|
+
syncWarning = err.message;
|
|
3115
|
+
strapi.log.warn(`[hubspot] field sync on publish failed — ${syncWarning}`);
|
|
2936
3116
|
}
|
|
2937
3117
|
}
|
|
2938
3118
|
ctx.body = {
|
|
@@ -2941,6 +3121,31 @@ function createFormsAdminController(strapi) {
|
|
|
2941
3121
|
...syncWarning ? { syncWarning } : {}
|
|
2942
3122
|
};
|
|
2943
3123
|
},
|
|
3124
|
+
async createInHubspot(ctx) {
|
|
3125
|
+
const entry = await documents().findOne({
|
|
3126
|
+
documentId: ctx.params.documentId,
|
|
3127
|
+
locale: ctx.query.locale,
|
|
3128
|
+
status: "draft"
|
|
3129
|
+
});
|
|
3130
|
+
if (!entry) ctx.throw(404, "Form not found");
|
|
3131
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3132
|
+
if (!apiKey) ctx.throw(400, "HubSpot API key is missing");
|
|
3133
|
+
try {
|
|
3134
|
+
const id = await createMarketingForm(
|
|
3135
|
+
apiKey,
|
|
3136
|
+
entry.name || entry.slug || "Formulaire",
|
|
3137
|
+
entry.definition ?? EMPTY_DEFINITION
|
|
3138
|
+
);
|
|
3139
|
+
const updated = await documents().update({
|
|
3140
|
+
documentId: ctx.params.documentId,
|
|
3141
|
+
locale: ctx.query.locale,
|
|
3142
|
+
data: { hubspotFormId: id }
|
|
3143
|
+
});
|
|
3144
|
+
ctx.body = { form: updated, hubspotFormId: id };
|
|
3145
|
+
} catch (err) {
|
|
3146
|
+
ctx.throw(400, err.message);
|
|
3147
|
+
}
|
|
3148
|
+
},
|
|
2944
3149
|
async unpublish(ctx) {
|
|
2945
3150
|
await documents().unpublish({
|
|
2946
3151
|
documentId: ctx.params.documentId,
|
|
@@ -3707,6 +3912,7 @@ const routes = {
|
|
|
3707
3912
|
adminRoute("GET", "/builder/forms/:documentId", "formsAdmin.findOne", [FORMS_ACTION]),
|
|
3708
3913
|
adminRoute("PUT", "/builder/forms/:documentId", "formsAdmin.update", [FORMS_ACTION]),
|
|
3709
3914
|
adminRoute("POST", "/builder/forms/:documentId/publish", "formsAdmin.publish", [FORMS_ACTION]),
|
|
3915
|
+
adminRoute("POST", "/builder/forms/:documentId/create-hubspot", "formsAdmin.createInHubspot", [FORMS_ACTION]),
|
|
3710
3916
|
adminRoute("POST", "/builder/forms/:documentId/unpublish", "formsAdmin.unpublish", [FORMS_ACTION]),
|
|
3711
3917
|
adminRoute("DELETE", "/builder/forms/:documentId", "formsAdmin.remove", [FORMS_ACTION]),
|
|
3712
3918
|
adminRoute("POST", "/builder/forms/:documentId/duplicate", "formsAdmin.duplicate", [FORMS_ACTION]),
|
package/dist/server/index.mjs
CHANGED
|
@@ -1599,6 +1599,48 @@ function missingMappedFields(definition, existingNames) {
|
|
|
1599
1599
|
const existing = new Set(existingNames);
|
|
1600
1600
|
return mappedContactFields(definition).filter((field) => !existing.has(field.property));
|
|
1601
1601
|
}
|
|
1602
|
+
const OBJECT_TYPE_BY_NAME = {
|
|
1603
|
+
contact: "0-1",
|
|
1604
|
+
company: "0-2"
|
|
1605
|
+
};
|
|
1606
|
+
function collectedHubspotKeys(definition) {
|
|
1607
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1608
|
+
const add = (object, property) => {
|
|
1609
|
+
const typeId = OBJECT_TYPE_BY_NAME[object] || object;
|
|
1610
|
+
if (property) keys.add(`${typeId}:${property}`);
|
|
1611
|
+
};
|
|
1612
|
+
for (const step of definition.steps ?? []) {
|
|
1613
|
+
for (const fld of step.fields ?? []) {
|
|
1614
|
+
if (fld.type === "company") {
|
|
1615
|
+
const map = fld.companyMap ?? {};
|
|
1616
|
+
for (const slot of Object.values(map)) {
|
|
1617
|
+
if (slot?.property?.trim()) add(slot.object?.trim() || "company", slot.property.trim());
|
|
1618
|
+
}
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
const mapping = fld.hubspot ?? {};
|
|
1622
|
+
const object = mapping.object?.trim() || "contact";
|
|
1623
|
+
const property = mapping.property?.trim() || fld.name;
|
|
1624
|
+
if (property) add(object, property);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
return keys;
|
|
1628
|
+
}
|
|
1629
|
+
function missingRequiredHubspotFields(definition, required) {
|
|
1630
|
+
const collected = collectedHubspotKeys(definition);
|
|
1631
|
+
const missing = [];
|
|
1632
|
+
for (const field of required) {
|
|
1633
|
+
if (!field.name) continue;
|
|
1634
|
+
const objectTypeId = field.objectTypeId?.trim() || "0-1";
|
|
1635
|
+
if (collected.has(`${objectTypeId}:${field.name}`)) continue;
|
|
1636
|
+
missing.push({
|
|
1637
|
+
name: field.name,
|
|
1638
|
+
objectTypeId,
|
|
1639
|
+
object: objectTypeId === "0-2" ? "company" : "contact"
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
return missing;
|
|
1643
|
+
}
|
|
1602
1644
|
function hsFieldType(type, options) {
|
|
1603
1645
|
switch (type) {
|
|
1604
1646
|
case "email":
|
|
@@ -1649,6 +1691,74 @@ function appendFieldGroups(existing, extra) {
|
|
|
1649
1691
|
}))
|
|
1650
1692
|
];
|
|
1651
1693
|
}
|
|
1694
|
+
function conditionToHubspot(condition, parentId) {
|
|
1695
|
+
const rules = (condition.rules ?? []).filter((rule) => rule.field === parentId);
|
|
1696
|
+
if (!rules.length || rules.length !== (condition.rules ?? []).length) return null;
|
|
1697
|
+
const op = rules[0]?.operator;
|
|
1698
|
+
if (rules.some((rule) => rule.operator !== op)) return null;
|
|
1699
|
+
const values = rules.map((rule) => rule.value).filter((value) => Boolean(value));
|
|
1700
|
+
switch (op) {
|
|
1701
|
+
case "eq":
|
|
1702
|
+
return { operator: values.length > 1 ? "SET_ANY" : "EQ", values };
|
|
1703
|
+
case "neq":
|
|
1704
|
+
return { operator: values.length > 1 ? "NOT_SET_ANY" : "NEQ", values };
|
|
1705
|
+
case "contains":
|
|
1706
|
+
return { operator: "CONTAINS", values };
|
|
1707
|
+
case "gt":
|
|
1708
|
+
return { operator: "GT", values };
|
|
1709
|
+
case "lt":
|
|
1710
|
+
return { operator: "LT", values };
|
|
1711
|
+
case "notEmpty":
|
|
1712
|
+
return { operator: "SET", values: [] };
|
|
1713
|
+
case "empty":
|
|
1714
|
+
return { operator: "NOT_SET", values: [] };
|
|
1715
|
+
default:
|
|
1716
|
+
return null;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
function applyVisibleIfToFieldGroups(groups, definition) {
|
|
1720
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1721
|
+
for (const step of definition.steps ?? []) {
|
|
1722
|
+
for (const fld of step.fields ?? []) {
|
|
1723
|
+
const mapping = fld.hubspot ?? {};
|
|
1724
|
+
byId.set(fld.id, { name: fld.name, property: mapping.property?.trim() || fld.name });
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
const moves = /* @__PURE__ */ new Map();
|
|
1728
|
+
for (const step of definition.steps ?? []) {
|
|
1729
|
+
for (const fld of step.fields ?? []) {
|
|
1730
|
+
const visibleIf = fld.visibleIf;
|
|
1731
|
+
const parentId = visibleIf?.rules?.[0]?.field;
|
|
1732
|
+
if (!visibleIf || !parentId) continue;
|
|
1733
|
+
const converted = conditionToHubspot(visibleIf, parentId);
|
|
1734
|
+
const parent = byId.get(parentId);
|
|
1735
|
+
const mapping = fld.hubspot ?? {};
|
|
1736
|
+
const property = mapping.property?.trim() || fld.name;
|
|
1737
|
+
if (!converted || !parent) continue;
|
|
1738
|
+
moves.set(property, { parent: parent.property, ...converted });
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
if (!moves.size) return groups;
|
|
1742
|
+
const cloned = JSON.parse(JSON.stringify(groups));
|
|
1743
|
+
const flat = [];
|
|
1744
|
+
for (const group of cloned) {
|
|
1745
|
+
(group.fields ?? []).forEach((field, index2) => flat.push({ group, index: index2, field }));
|
|
1746
|
+
}
|
|
1747
|
+
for (const [property, move] of moves) {
|
|
1748
|
+
const child = flat.find((item) => item.field.name === property);
|
|
1749
|
+
const parent = flat.find((item) => item.field.name === move.parent);
|
|
1750
|
+
if (!child || !parent || child === parent) continue;
|
|
1751
|
+
parent.field.dependentFields = [
|
|
1752
|
+
...parent.field.dependentFields ?? [],
|
|
1753
|
+
{
|
|
1754
|
+
dependentCondition: { operator: move.operator, values: move.values },
|
|
1755
|
+
field: child.field
|
|
1756
|
+
}
|
|
1757
|
+
];
|
|
1758
|
+
child.group.fields?.splice(child.index, 1);
|
|
1759
|
+
}
|
|
1760
|
+
return cloned;
|
|
1761
|
+
}
|
|
1652
1762
|
const HS_BASE$2 = "https://api.hubapi.com";
|
|
1653
1763
|
const OBJECT_BY_TYPE_ID = {
|
|
1654
1764
|
"0-1": "contact",
|
|
@@ -1730,7 +1840,7 @@ function convertField(raw, ctx) {
|
|
|
1730
1840
|
helpText: raw.description || void 0,
|
|
1731
1841
|
...options.length ? { options } : {},
|
|
1732
1842
|
hubspot: { object, property: name },
|
|
1733
|
-
...ctx.parentCondition ? { visibleIf: ctx.parentCondition } : {}
|
|
1843
|
+
...ctx.parentCondition ? { visibleIf: ctx.parentCondition, visibleIfSource: "hubspot" } : {}
|
|
1734
1844
|
};
|
|
1735
1845
|
const out = [field];
|
|
1736
1846
|
for (const dependent of raw.dependentFields ?? []) {
|
|
@@ -1753,6 +1863,12 @@ function convertHubspotForm(raw) {
|
|
|
1753
1863
|
const usedNames = /* @__PURE__ */ new Set();
|
|
1754
1864
|
const fields = [];
|
|
1755
1865
|
for (const group of raw.fieldGroups ?? []) {
|
|
1866
|
+
if (group.groupType === "progressive" || group.groupType === "queued") {
|
|
1867
|
+
skipped.push({
|
|
1868
|
+
code: "progressive",
|
|
1869
|
+
detail: group.groupType
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1756
1872
|
if (group.richText && !(group.fields ?? []).length) {
|
|
1757
1873
|
skipped.push({ code: "rich-text", detail: group.richText.slice(0, 80) });
|
|
1758
1874
|
continue;
|
|
@@ -1824,7 +1940,10 @@ function mergeHubspotImport(current, incoming) {
|
|
|
1824
1940
|
existing.helpText = next.helpText;
|
|
1825
1941
|
if (next.options) existing.options = next.options;
|
|
1826
1942
|
else delete existing.options;
|
|
1827
|
-
if (
|
|
1943
|
+
if (next.visibleIf && (existing.visibleIfSource === "hubspot" || !existing.visibleIf)) {
|
|
1944
|
+
existing.visibleIf = next.visibleIf;
|
|
1945
|
+
if (existing.visibleIfSource !== "editor") existing.visibleIfSource = "hubspot";
|
|
1946
|
+
}
|
|
1828
1947
|
updated.push(key);
|
|
1829
1948
|
continue;
|
|
1830
1949
|
}
|
|
@@ -2145,13 +2264,62 @@ async function submitMarketingForm(input) {
|
|
|
2145
2264
|
async function syncFieldsToHubspotForm(apiKey, formGuid, definition) {
|
|
2146
2265
|
const raw = await fetchHubspotForm(apiKey, formGuid);
|
|
2147
2266
|
const extra = missingMappedFields(definition, formFieldNames(raw));
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2267
|
+
const fieldGroups = applyVisibleIfToFieldGroups(
|
|
2268
|
+
appendFieldGroups(raw.fieldGroups ?? [], extra),
|
|
2269
|
+
definition
|
|
2270
|
+
);
|
|
2271
|
+
if (!extra.length && JSON.stringify(fieldGroups) === JSON.stringify(appendFieldGroups(raw.fieldGroups ?? [], []))) {
|
|
2272
|
+
return [];
|
|
2273
|
+
}
|
|
2274
|
+
await patchHubspotForm(apiKey, formGuid, { fieldGroups });
|
|
2152
2275
|
clearFormShapeCache(formGuid);
|
|
2153
2276
|
return extra.map((field) => field.property);
|
|
2154
2277
|
}
|
|
2278
|
+
async function createMarketingForm(apiKey, name, definition) {
|
|
2279
|
+
const fields = mappedContactFields(definition).map((field) => toHsFormField(field));
|
|
2280
|
+
if (!fields.length) {
|
|
2281
|
+
throw new Error("Add at least one mapped contact field before creating a HubSpot form");
|
|
2282
|
+
}
|
|
2283
|
+
const res = await fetch("https://api.hubapi.com/marketing/v3/forms", {
|
|
2284
|
+
method: "POST",
|
|
2285
|
+
headers: {
|
|
2286
|
+
Authorization: `Bearer ${apiKey}`,
|
|
2287
|
+
"Content-Type": "application/json"
|
|
2288
|
+
},
|
|
2289
|
+
body: JSON.stringify({
|
|
2290
|
+
name,
|
|
2291
|
+
formType: "hubspot",
|
|
2292
|
+
fieldGroups: applyVisibleIfToFieldGroups(
|
|
2293
|
+
fields.map((field) => ({
|
|
2294
|
+
groupType: "default_group",
|
|
2295
|
+
richTextType: "text",
|
|
2296
|
+
fields: [field]
|
|
2297
|
+
})),
|
|
2298
|
+
definition
|
|
2299
|
+
),
|
|
2300
|
+
configuration: {
|
|
2301
|
+
language: "fr",
|
|
2302
|
+
recaptchaEnabled: false,
|
|
2303
|
+
createNewContactForNewEmail: true,
|
|
2304
|
+
prePopulateKnownValues: true,
|
|
2305
|
+
notifyContactOwner: false,
|
|
2306
|
+
notifyRecipients: [],
|
|
2307
|
+
editable: true,
|
|
2308
|
+
archivable: true,
|
|
2309
|
+
cloneable: true,
|
|
2310
|
+
allowLinkToResetKnownValues: false,
|
|
2311
|
+
postSubmitAction: { type: "thank_you", value: "Merci." }
|
|
2312
|
+
},
|
|
2313
|
+
displayOptions: { submitButtonText: "Envoyer", style: { submitButtonText: "Envoyer" } },
|
|
2314
|
+
legalConsentOptions: { type: "none" }
|
|
2315
|
+
})
|
|
2316
|
+
});
|
|
2317
|
+
const body = await res.json().catch(() => ({}));
|
|
2318
|
+
if (!res.ok || !body.id) {
|
|
2319
|
+
throw new Error(body.message || `HubSpot ${res.status}`);
|
|
2320
|
+
}
|
|
2321
|
+
return body.id;
|
|
2322
|
+
}
|
|
2155
2323
|
function publicForm(entry) {
|
|
2156
2324
|
return {
|
|
2157
2325
|
slug: entry.slug,
|
|
@@ -2165,7 +2333,9 @@ function publicForm(entry) {
|
|
|
2165
2333
|
steps: (entry.definition?.steps ?? []).map((step) => ({
|
|
2166
2334
|
...step,
|
|
2167
2335
|
// The CRM mappings are the server's business — never the browser's.
|
|
2168
|
-
fields: (step.fields ?? []).map(
|
|
2336
|
+
fields: (step.fields ?? []).map(
|
|
2337
|
+
({ hubspot: _hubspot, companyMap: _companyMap, visibleIfSource: _source, ...fld }) => fld
|
|
2338
|
+
)
|
|
2169
2339
|
}))
|
|
2170
2340
|
};
|
|
2171
2341
|
}
|
|
@@ -2909,6 +3079,22 @@ function createFormsAdminController(strapi) {
|
|
|
2909
3079
|
ctx.body = { errors: structural, problems: mappings };
|
|
2910
3080
|
return;
|
|
2911
3081
|
}
|
|
3082
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3083
|
+
const account = await resolveAccount(strapi);
|
|
3084
|
+
const ownGuid = typeof entry.hubspotFormId === "string" ? entry.hubspotFormId.trim() : "";
|
|
3085
|
+
const formGuid = isFormGuid(ownGuid) ? ownGuid : isFormGuid(account.defaultFormId) ? account.defaultFormId.trim() : "";
|
|
3086
|
+
if (apiKey && formGuid) {
|
|
3087
|
+
const shape = await loadFormShape(apiKey, formGuid);
|
|
3088
|
+
const missingHubspot = shape ? missingRequiredHubspotFields(
|
|
3089
|
+
entry.definition ?? EMPTY_DEFINITION,
|
|
3090
|
+
[...shape.fields.filter((field) => field.required)]
|
|
3091
|
+
) : [];
|
|
3092
|
+
if (missingHubspot.length) {
|
|
3093
|
+
ctx.status = 400;
|
|
3094
|
+
ctx.body = { errors: structural, problems: mappings, missingHubspot };
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
2912
3098
|
await documents().publish({
|
|
2913
3099
|
documentId: ctx.params.documentId,
|
|
2914
3100
|
locale: ctx.query.locale
|
|
@@ -2916,22 +3102,16 @@ function createFormsAdminController(strapi) {
|
|
|
2916
3102
|
const policy = await resolvePolicy(strapi);
|
|
2917
3103
|
let addedFields = [];
|
|
2918
3104
|
let syncWarning;
|
|
2919
|
-
if (policy.syncFieldsOnPublish) {
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
entry.definition ?? EMPTY_DEFINITION
|
|
2930
|
-
);
|
|
2931
|
-
} catch (err) {
|
|
2932
|
-
syncWarning = err.message;
|
|
2933
|
-
strapi.log.warn(`[hubspot] field sync on publish failed — ${syncWarning}`);
|
|
2934
|
-
}
|
|
3105
|
+
if (policy.syncFieldsOnPublish && apiKey && formGuid) {
|
|
3106
|
+
try {
|
|
3107
|
+
addedFields = await syncFieldsToHubspotForm(
|
|
3108
|
+
apiKey,
|
|
3109
|
+
formGuid,
|
|
3110
|
+
entry.definition ?? EMPTY_DEFINITION
|
|
3111
|
+
);
|
|
3112
|
+
} catch (err) {
|
|
3113
|
+
syncWarning = err.message;
|
|
3114
|
+
strapi.log.warn(`[hubspot] field sync on publish failed — ${syncWarning}`);
|
|
2935
3115
|
}
|
|
2936
3116
|
}
|
|
2937
3117
|
ctx.body = {
|
|
@@ -2940,6 +3120,31 @@ function createFormsAdminController(strapi) {
|
|
|
2940
3120
|
...syncWarning ? { syncWarning } : {}
|
|
2941
3121
|
};
|
|
2942
3122
|
},
|
|
3123
|
+
async createInHubspot(ctx) {
|
|
3124
|
+
const entry = await documents().findOne({
|
|
3125
|
+
documentId: ctx.params.documentId,
|
|
3126
|
+
locale: ctx.query.locale,
|
|
3127
|
+
status: "draft"
|
|
3128
|
+
});
|
|
3129
|
+
if (!entry) ctx.throw(404, "Form not found");
|
|
3130
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3131
|
+
if (!apiKey) ctx.throw(400, "HubSpot API key is missing");
|
|
3132
|
+
try {
|
|
3133
|
+
const id = await createMarketingForm(
|
|
3134
|
+
apiKey,
|
|
3135
|
+
entry.name || entry.slug || "Formulaire",
|
|
3136
|
+
entry.definition ?? EMPTY_DEFINITION
|
|
3137
|
+
);
|
|
3138
|
+
const updated = await documents().update({
|
|
3139
|
+
documentId: ctx.params.documentId,
|
|
3140
|
+
locale: ctx.query.locale,
|
|
3141
|
+
data: { hubspotFormId: id }
|
|
3142
|
+
});
|
|
3143
|
+
ctx.body = { form: updated, hubspotFormId: id };
|
|
3144
|
+
} catch (err) {
|
|
3145
|
+
ctx.throw(400, err.message);
|
|
3146
|
+
}
|
|
3147
|
+
},
|
|
2943
3148
|
async unpublish(ctx) {
|
|
2944
3149
|
await documents().unpublish({
|
|
2945
3150
|
documentId: ctx.params.documentId,
|
|
@@ -3706,6 +3911,7 @@ const routes = {
|
|
|
3706
3911
|
adminRoute("GET", "/builder/forms/:documentId", "formsAdmin.findOne", [FORMS_ACTION]),
|
|
3707
3912
|
adminRoute("PUT", "/builder/forms/:documentId", "formsAdmin.update", [FORMS_ACTION]),
|
|
3708
3913
|
adminRoute("POST", "/builder/forms/:documentId/publish", "formsAdmin.publish", [FORMS_ACTION]),
|
|
3914
|
+
adminRoute("POST", "/builder/forms/:documentId/create-hubspot", "formsAdmin.createInHubspot", [FORMS_ACTION]),
|
|
3709
3915
|
adminRoute("POST", "/builder/forms/:documentId/unpublish", "formsAdmin.unpublish", [FORMS_ACTION]),
|
|
3710
3916
|
adminRoute("DELETE", "/builder/forms/:documentId", "formsAdmin.remove", [FORMS_ACTION]),
|
|
3711
3917
|
adminRoute("POST", "/builder/forms/:documentId/duplicate", "formsAdmin.duplicate", [FORMS_ACTION]),
|
|
@@ -26,6 +26,21 @@ export interface MappedFormField {
|
|
|
26
26
|
export declare function leftoverContactProps(contact: Record<string, string>, sentFieldNames: Iterable<string>): Record<string, string>;
|
|
27
27
|
export declare function mappedContactFields(definition: FormDefinition): MappedFormField[];
|
|
28
28
|
export declare function missingMappedFields(definition: FormDefinition, existingNames: Iterable<string>): MappedFormField[];
|
|
29
|
+
export interface MissingHubspotRequired {
|
|
30
|
+
name: string;
|
|
31
|
+
objectTypeId: string;
|
|
32
|
+
object: string;
|
|
33
|
+
}
|
|
34
|
+
/** Properties the Strapi form can actually send (`objectTypeId:property`). */
|
|
35
|
+
export declare function collectedHubspotKeys(definition: FormDefinition): Set<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Required marketing-form fields the Strapi form never collects. HubSpot
|
|
38
|
+
* rejects the whole conversion when one of these is absent.
|
|
39
|
+
*/
|
|
40
|
+
export declare function missingRequiredHubspotFields(definition: FormDefinition, required: {
|
|
41
|
+
name: string;
|
|
42
|
+
objectTypeId?: string;
|
|
43
|
+
}[]): MissingHubspotRequired[];
|
|
29
44
|
export declare function hsFieldType(type: string, options?: unknown[]): string;
|
|
30
45
|
export declare function toHsFormField(field: MappedFormField): Record<string, unknown>;
|
|
31
46
|
export interface RawFieldGroupLike {
|
|
@@ -35,3 +50,8 @@ export interface RawFieldGroupLike {
|
|
|
35
50
|
fields?: unknown[];
|
|
36
51
|
}
|
|
37
52
|
export declare function appendFieldGroups(existing: RawFieldGroupLike[], extra: MappedFormField[]): Record<string, unknown>[];
|
|
53
|
+
/**
|
|
54
|
+
* Nest Strapi `visibleIf` fields under their HubSpot parent as dependentFields.
|
|
55
|
+
* Conditions edited in Strapi are included; unsupported multi-parent rules stay flat.
|
|
56
|
+
*/
|
|
57
|
+
export declare function applyVisibleIfToFieldGroups(groups: Record<string, unknown>[], definition: FormDefinition): Record<string, unknown>[];
|
|
@@ -26,6 +26,7 @@ export declare function createFormsAdminController(strapi: Core.Strapi): {
|
|
|
26
26
|
create(ctx: Ctx): Promise<void>;
|
|
27
27
|
update(ctx: Ctx): Promise<void>;
|
|
28
28
|
publish(ctx: Ctx): Promise<void>;
|
|
29
|
+
createInHubspot(ctx: Ctx): Promise<void>;
|
|
29
30
|
unpublish(ctx: Ctx): Promise<void>;
|
|
30
31
|
/**
|
|
31
32
|
* Feeds the `hubspot.form-picker` custom field: every form of the builder
|
|
@@ -153,3 +153,5 @@ export declare function submitMarketingForm(input: FormSubmitInput): Promise<{
|
|
|
153
153
|
* that already exist. Returns the property names that were added.
|
|
154
154
|
*/
|
|
155
155
|
export declare function syncFieldsToHubspotForm(apiKey: string, formGuid: string, definition: FormDefinition): Promise<string[]>;
|
|
156
|
+
/** Create a marketing form from the Strapi definition and return its GUID. */
|
|
157
|
+
export declare function createMarketingForm(apiKey: string, name: string, definition: FormDefinition): Promise<string>;
|
|
@@ -21,7 +21,7 @@ export interface HubspotFormSummary {
|
|
|
21
21
|
}
|
|
22
22
|
/** Something the converter could not express in the builder's model. */
|
|
23
23
|
export interface SkippedItem {
|
|
24
|
-
code: "field-type" | "hidden-field" | "object" | "duplicate" | "condition" | "rich-text" | "legal-consent";
|
|
24
|
+
code: "field-type" | "hidden-field" | "object" | "duplicate" | "condition" | "rich-text" | "legal-consent" | "progressive";
|
|
25
25
|
/** Field label or name, when the item is a field. */
|
|
26
26
|
label?: string;
|
|
27
27
|
/** Raw detail (the fieldType, the operator…) for the report. */
|
|
@@ -58,6 +58,7 @@ interface RawField {
|
|
|
58
58
|
dependentFields?: RawDependent[];
|
|
59
59
|
}
|
|
60
60
|
interface RawFieldGroup {
|
|
61
|
+
groupType?: string;
|
|
61
62
|
richText?: string;
|
|
62
63
|
fields?: RawField[];
|
|
63
64
|
}
|
|
@@ -274,6 +274,7 @@ declare const _default: {
|
|
|
274
274
|
create(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
275
275
|
update(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
276
276
|
publish(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
277
|
+
createInHubspot(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
277
278
|
unpublish(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
278
279
|
options(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
279
280
|
listSources(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-hubspot",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.8",
|
|
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",
|