strapi-plugin-hubspot 0.16.1 → 0.16.2
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 +8 -0
- package/README.md +4 -4
- package/dist/_chunks/{Forms-DEoZGrEt.mjs → Forms-BOQWely0.mjs} +48 -5
- package/dist/_chunks/{Forms-D0QXkYAN.js → Forms-mMNP7hc9.js} +48 -5
- package/dist/_chunks/{HubspotFormInput-CPep55GL.mjs → HubspotFormInput-DCkoavrN.mjs} +1 -1
- package/dist/_chunks/{HubspotFormInput-4ZMYXWM_.js → HubspotFormInput-DI35Aqiu.js} +1 -1
- package/dist/_chunks/{HubspotObjectInput-CQszIx1W.mjs → HubspotObjectInput-BbnvhdbD.mjs} +3 -3
- package/dist/_chunks/{HubspotObjectInput-BTqYXJIa.js → HubspotObjectInput-Cq1TuL_l.js} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-ClNph0f1.mjs → HubspotPropertyInput-BHfpBD36.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-2MgIKlR_.js → HubspotPropertyInput-BLZzsW08.js} +3 -3
- package/dist/_chunks/{Settings-DhiHQC6U.js → Settings-BAsx0LGv.js} +2 -2
- package/dist/_chunks/{Settings-CZ-qQSKT.mjs → Settings-DqRpHtnT.mjs} +2 -2
- package/dist/_chunks/{en-Dh3bMS6N.mjs → en-Djd3TID0.mjs} +6 -2
- package/dist/_chunks/{en-CSgFmKmM.js → en-sfzopB5K.js} +6 -2
- package/dist/_chunks/{fr-aWOIOlS9.mjs → fr-C7t1elmR.mjs} +6 -2
- package/dist/_chunks/{fr-D_EhZS0P.js → fr-sF5Josfh.js} +6 -2
- package/dist/_chunks/{index-gKM4jVf8.mjs → index-CLAy5dVN.mjs} +6 -6
- package/dist/_chunks/{index-ciliGv0E.js → index-R3ReZXF1.js} +6 -6
- package/dist/_chunks/{objectLabels-DQpSLUaJ.mjs → objectLabels--zOmqsfm.mjs} +1 -1
- package/dist/_chunks/{objectLabels-BjYfdhRB.js → objectLabels-CW2iBOkD.js} +1 -1
- package/dist/_chunks/{useHubspotSchema-KKVppjC2.js → useHubspotSchema-BRzzvs9t.js} +1 -1
- package/dist/_chunks/{useHubspotSchema-CE7tJgOM.mjs → useHubspotSchema-Bz2o1BU-.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +129 -12
- package/dist/server/index.mjs +129 -12
- package/dist/server/src/formsAdmin.d.ts +8 -3
- package/dist/server/src/importHubspot.d.ts +12 -0
- package/dist/server/src/index.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.16.2 — 2026-09-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Resync from HubSpot** in the form editor (and a safer list re-import):
|
|
9
|
+
mapped fields update in place (label, type, required, options), new HubSpot
|
|
10
|
+
fields append to the last step, Strapi steps / conditions / extra fields
|
|
11
|
+
stay. Matching uses the linked form GUID rather than the slug.
|
|
12
|
+
|
|
5
13
|
## 0.16.1 — 2026-09-22
|
|
6
14
|
|
|
7
15
|
### Changed
|
package/README.md
CHANGED
|
@@ -174,10 +174,10 @@ the portal. The translation carries over what the builder can express:
|
|
|
174
174
|
- dependent fields become `visibleIf` conditions (value lists are expanded
|
|
175
175
|
into OR'd/AND'd rules).
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
The HubSpot original is never modified. Re-importing (or **Resync from HubSpot**
|
|
178
|
+
in the editor) updates mapped fields in place and appends new ones — Strapi
|
|
179
|
+
steps, conditions and extra fields stay. Matching is by HubSpot form GUID,
|
|
180
|
+
then slug. The published version is never touched.
|
|
181
181
|
|
|
182
182
|
### i18n and publishing
|
|
183
183
|
|
|
@@ -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-CLAy5dVN.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--zOmqsfm.mjs";
|
|
13
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-Bz2o1BU-.mjs";
|
|
14
14
|
const OPERATORS = [
|
|
15
15
|
"eq",
|
|
16
16
|
"neq",
|
|
@@ -899,6 +899,40 @@ const FormEditor = () => {
|
|
|
899
899
|
setBusy(false);
|
|
900
900
|
}
|
|
901
901
|
};
|
|
902
|
+
const resync = async () => {
|
|
903
|
+
if (dirty && !await save()) return;
|
|
904
|
+
setBusy(true);
|
|
905
|
+
setFeedback(null);
|
|
906
|
+
try {
|
|
907
|
+
const { data } = await post(`/${PLUGIN_ID}/builder/forms/${documentId}/resync${query}`);
|
|
908
|
+
if (data.form) {
|
|
909
|
+
setEntry(data.form);
|
|
910
|
+
setDirty(false);
|
|
911
|
+
}
|
|
912
|
+
const added = data.added?.length ?? 0;
|
|
913
|
+
const updated = data.updated?.length ?? 0;
|
|
914
|
+
const skipped = data.skipped?.length ?? 0;
|
|
915
|
+
setFeedback({
|
|
916
|
+
tone: "success",
|
|
917
|
+
text: skipped > 0 ? t(
|
|
918
|
+
"editor.resynced-gaps",
|
|
919
|
+
"Resynced from HubSpot — {updated, plural, one {# updated} other {# updated}}, {added, plural, one {# added} other {# added}}, {skipped, plural, one {# skipped} other {# skipped}}.",
|
|
920
|
+
{ updated, added, skipped }
|
|
921
|
+
) : t(
|
|
922
|
+
"editor.resynced",
|
|
923
|
+
"Resynced from HubSpot — {updated, plural, one {# field updated} other {# fields updated}}, {added, plural, one {# added} other {# added}}.",
|
|
924
|
+
{ updated, added }
|
|
925
|
+
)
|
|
926
|
+
});
|
|
927
|
+
} catch {
|
|
928
|
+
setFeedback({
|
|
929
|
+
tone: "danger",
|
|
930
|
+
text: t("editor.resync-error", "Could not resync from HubSpot — is the form still linked?")
|
|
931
|
+
});
|
|
932
|
+
} finally {
|
|
933
|
+
setBusy(false);
|
|
934
|
+
}
|
|
935
|
+
};
|
|
902
936
|
const fieldFlags = (fieldId) => ({
|
|
903
937
|
error: errors.find((e) => e.fieldId === fieldId),
|
|
904
938
|
problem: problems.find((p) => p.fieldId === fieldId)
|
|
@@ -936,6 +970,15 @@ const FormEditor = () => {
|
|
|
936
970
|
}
|
|
937
971
|
),
|
|
938
972
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: save, disabled: busy || !dirty, children: t("editor.save", "Save") }),
|
|
973
|
+
/* @__PURE__ */ jsx(
|
|
974
|
+
Button,
|
|
975
|
+
{
|
|
976
|
+
variant: "tertiary",
|
|
977
|
+
onClick: resync,
|
|
978
|
+
disabled: busy || !entry.hubspotFormId,
|
|
979
|
+
children: t("editor.resync", "Resync from HubSpot")
|
|
980
|
+
}
|
|
981
|
+
),
|
|
939
982
|
/* @__PURE__ */ jsx(Button, { onClick: publish, disabled: busy, children: t("editor.publish", "Publish") }),
|
|
940
983
|
published && /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: unpublish, disabled: busy, children: t("editor.unpublish", "Unpublish") })
|
|
941
984
|
] })
|
|
@@ -1311,7 +1354,7 @@ const FormEditor = () => {
|
|
|
1311
1354
|
),
|
|
1312
1355
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
1313
1356
|
"editor.hubspot-form-hint",
|
|
1314
|
-
"Submissions go through HubSpot’s Forms API so they count as conversions.
|
|
1357
|
+
"Submissions go through HubSpot’s Forms API so they count as conversions. After you change the form in HubSpot, use Resync from HubSpot — mapped fields update in place, new ones are appended, your steps stay."
|
|
1315
1358
|
) })
|
|
1316
1359
|
] }),
|
|
1317
1360
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
@@ -1490,7 +1533,7 @@ const FormsList = () => {
|
|
|
1490
1533
|
/* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", children: t("forms.import-hubspot-title", "Import from your HubSpot portal") }),
|
|
1491
1534
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: t(
|
|
1492
1535
|
"forms.import-hubspot-hint",
|
|
1493
|
-
"Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing
|
|
1536
|
+
"Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing updates mapped fields in place and appends new ones."
|
|
1494
1537
|
) })
|
|
1495
1538
|
] }),
|
|
1496
1539
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
@@ -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-R3ReZXF1.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-CW2iBOkD.js");
|
|
15
|
+
const useHubspotSchema = require("./useHubspotSchema-BRzzvs9t.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" } });
|
|
@@ -919,6 +919,40 @@ const FormEditor = () => {
|
|
|
919
919
|
setBusy(false);
|
|
920
920
|
}
|
|
921
921
|
};
|
|
922
|
+
const resync = async () => {
|
|
923
|
+
if (dirty && !await save()) return;
|
|
924
|
+
setBusy(true);
|
|
925
|
+
setFeedback(null);
|
|
926
|
+
try {
|
|
927
|
+
const { data } = await post(`/${index$1.PLUGIN_ID}/builder/forms/${documentId}/resync${query}`);
|
|
928
|
+
if (data.form) {
|
|
929
|
+
setEntry(data.form);
|
|
930
|
+
setDirty(false);
|
|
931
|
+
}
|
|
932
|
+
const added = data.added?.length ?? 0;
|
|
933
|
+
const updated = data.updated?.length ?? 0;
|
|
934
|
+
const skipped = data.skipped?.length ?? 0;
|
|
935
|
+
setFeedback({
|
|
936
|
+
tone: "success",
|
|
937
|
+
text: skipped > 0 ? t(
|
|
938
|
+
"editor.resynced-gaps",
|
|
939
|
+
"Resynced from HubSpot — {updated, plural, one {# updated} other {# updated}}, {added, plural, one {# added} other {# added}}, {skipped, plural, one {# skipped} other {# skipped}}.",
|
|
940
|
+
{ updated, added, skipped }
|
|
941
|
+
) : t(
|
|
942
|
+
"editor.resynced",
|
|
943
|
+
"Resynced from HubSpot — {updated, plural, one {# field updated} other {# fields updated}}, {added, plural, one {# added} other {# added}}.",
|
|
944
|
+
{ updated, added }
|
|
945
|
+
)
|
|
946
|
+
});
|
|
947
|
+
} catch {
|
|
948
|
+
setFeedback({
|
|
949
|
+
tone: "danger",
|
|
950
|
+
text: t("editor.resync-error", "Could not resync from HubSpot — is the form still linked?")
|
|
951
|
+
});
|
|
952
|
+
} finally {
|
|
953
|
+
setBusy(false);
|
|
954
|
+
}
|
|
955
|
+
};
|
|
922
956
|
const fieldFlags = (fieldId) => ({
|
|
923
957
|
error: errors.find((e) => e.fieldId === fieldId),
|
|
924
958
|
problem: problems.find((p) => p.fieldId === fieldId)
|
|
@@ -956,6 +990,15 @@ const FormEditor = () => {
|
|
|
956
990
|
}
|
|
957
991
|
),
|
|
958
992
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: save, disabled: busy || !dirty, children: t("editor.save", "Save") }),
|
|
993
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
994
|
+
designSystem.Button,
|
|
995
|
+
{
|
|
996
|
+
variant: "tertiary",
|
|
997
|
+
onClick: resync,
|
|
998
|
+
disabled: busy || !entry.hubspotFormId,
|
|
999
|
+
children: t("editor.resync", "Resync from HubSpot")
|
|
1000
|
+
}
|
|
1001
|
+
),
|
|
959
1002
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: publish, disabled: busy, children: t("editor.publish", "Publish") }),
|
|
960
1003
|
published && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: unpublish, disabled: busy, children: t("editor.unpublish", "Unpublish") })
|
|
961
1004
|
] })
|
|
@@ -1331,7 +1374,7 @@ const FormEditor = () => {
|
|
|
1331
1374
|
),
|
|
1332
1375
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
1333
1376
|
"editor.hubspot-form-hint",
|
|
1334
|
-
"Submissions go through HubSpot’s Forms API so they count as conversions.
|
|
1377
|
+
"Submissions go through HubSpot’s Forms API so they count as conversions. After you change the form in HubSpot, use Resync from HubSpot — mapped fields update in place, new ones are appended, your steps stay."
|
|
1335
1378
|
) })
|
|
1336
1379
|
] }),
|
|
1337
1380
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
@@ -1510,7 +1553,7 @@ const FormsList = () => {
|
|
|
1510
1553
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: t("forms.import-hubspot-title", "Import from your HubSpot portal") }),
|
|
1511
1554
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: t(
|
|
1512
1555
|
"forms.import-hubspot-hint",
|
|
1513
|
-
"Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing
|
|
1556
|
+
"Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing updates mapped fields in place and appends new ones."
|
|
1514
1557
|
) })
|
|
1515
1558
|
] }),
|
|
1516
1559
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
@@ -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-CLAy5dVN.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-R3ReZXF1.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-CLAy5dVN.mjs";
|
|
6
|
+
import { o as objectLabel } from "./objectLabels--zOmqsfm.mjs";
|
|
7
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-Bz2o1BU-.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-R3ReZXF1.js");
|
|
8
|
+
const objectLabels = require("./objectLabels-CW2iBOkD.js");
|
|
9
|
+
const useHubspotSchema = require("./useHubspotSchema-BRzzvs9t.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-ClNph0f1.mjs → HubspotPropertyInput-BHfpBD36.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-CLAy5dVN.mjs";
|
|
8
|
+
import { o as objectLabel } from "./objectLabels--zOmqsfm.mjs";
|
|
9
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-Bz2o1BU-.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-R3ReZXF1.js");
|
|
10
|
+
const objectLabels = require("./objectLabels-CW2iBOkD.js");
|
|
11
|
+
const useHubspotSchema = require("./useHubspotSchema-BRzzvs9t.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-R3ReZXF1.js");
|
|
9
9
|
const reactRouterDom = require("react-router-dom");
|
|
10
|
-
const objectLabels = require("./objectLabels-
|
|
10
|
+
const objectLabels = require("./objectLabels-CW2iBOkD.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-CLAy5dVN.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--zOmqsfm.mjs";
|
|
9
9
|
const AuditSection = ({ configured }) => {
|
|
10
10
|
const intl = useIntl();
|
|
11
11
|
const { formatMessage } = intl;
|
|
@@ -164,7 +164,11 @@ const en = {
|
|
|
164
164
|
"editor.hubspot-form": "HubSpot marketing form",
|
|
165
165
|
"editor.hubspot-form-placeholder": "Pick a form of the connected portal",
|
|
166
166
|
"editor.hubspot-form-none": "None — CRM upsert only",
|
|
167
|
-
"editor.hubspot-form-hint": "Submissions go through HubSpot’s Forms API so they count as conversions.
|
|
167
|
+
"editor.hubspot-form-hint": "Submissions go through HubSpot’s Forms API so they count as conversions. After you change the form in HubSpot, use Resync from HubSpot — mapped fields update in place, new ones are appended, your steps stay.",
|
|
168
|
+
"editor.resync": "Resync from HubSpot",
|
|
169
|
+
"editor.resynced": "Resynced from HubSpot — {updated, plural, one {# field updated} other {# fields updated}}, {added, plural, one {# added} other {# added}}.",
|
|
170
|
+
"editor.resynced-gaps": "Resynced from HubSpot — {updated, plural, one {# updated} other {# updated}}, {added, plural, one {# added} other {# added}}, {skipped, plural, one {# skipped} other {# skipped}}.",
|
|
171
|
+
"editor.resync-error": "Could not resync from HubSpot — is the form still linked?",
|
|
168
172
|
"editor.form-hint": "Select a step or a field to edit it. The public endpoint serves this form at /api/hubspot/forms/{slug}.",
|
|
169
173
|
"panel.label": "Label",
|
|
170
174
|
"panel.name": "Payload key",
|
|
@@ -234,7 +238,7 @@ const en = {
|
|
|
234
238
|
"forms.col-submissions": "Submissions",
|
|
235
239
|
"forms.submissions": "Submissions",
|
|
236
240
|
"forms.import-hubspot-title": "Import from your HubSpot portal",
|
|
237
|
-
"forms.import-hubspot-hint": "Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing
|
|
241
|
+
"forms.import-hubspot-hint": "Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing updates mapped fields in place and appends new ones.",
|
|
238
242
|
"forms.import-hubspot-source": "HubSpot form to import",
|
|
239
243
|
"forms.hubspot-list-error": "Could not list the portal's forms — the token needs the `forms` read scope.",
|
|
240
244
|
"forms.report-title": "Imported with gaps",
|
|
@@ -166,7 +166,11 @@ const en = {
|
|
|
166
166
|
"editor.hubspot-form": "HubSpot marketing form",
|
|
167
167
|
"editor.hubspot-form-placeholder": "Pick a form of the connected portal",
|
|
168
168
|
"editor.hubspot-form-none": "None — CRM upsert only",
|
|
169
|
-
"editor.hubspot-form-hint": "Submissions go through HubSpot’s Forms API so they count as conversions.
|
|
169
|
+
"editor.hubspot-form-hint": "Submissions go through HubSpot’s Forms API so they count as conversions. After you change the form in HubSpot, use Resync from HubSpot — mapped fields update in place, new ones are appended, your steps stay.",
|
|
170
|
+
"editor.resync": "Resync from HubSpot",
|
|
171
|
+
"editor.resynced": "Resynced from HubSpot — {updated, plural, one {# field updated} other {# fields updated}}, {added, plural, one {# added} other {# added}}.",
|
|
172
|
+
"editor.resynced-gaps": "Resynced from HubSpot — {updated, plural, one {# updated} other {# updated}}, {added, plural, one {# added} other {# added}}, {skipped, plural, one {# skipped} other {# skipped}}.",
|
|
173
|
+
"editor.resync-error": "Could not resync from HubSpot — is the form still linked?",
|
|
170
174
|
"editor.form-hint": "Select a step or a field to edit it. The public endpoint serves this form at /api/hubspot/forms/{slug}.",
|
|
171
175
|
"panel.label": "Label",
|
|
172
176
|
"panel.name": "Payload key",
|
|
@@ -236,7 +240,7 @@ const en = {
|
|
|
236
240
|
"forms.col-submissions": "Submissions",
|
|
237
241
|
"forms.submissions": "Submissions",
|
|
238
242
|
"forms.import-hubspot-title": "Import from your HubSpot portal",
|
|
239
|
-
"forms.import-hubspot-hint": "Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing
|
|
243
|
+
"forms.import-hubspot-hint": "Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing updates mapped fields in place and appends new ones.",
|
|
240
244
|
"forms.import-hubspot-source": "HubSpot form to import",
|
|
241
245
|
"forms.hubspot-list-error": "Could not list the portal's forms — the token needs the `forms` read scope.",
|
|
242
246
|
"forms.report-title": "Imported with gaps",
|
|
@@ -164,7 +164,11 @@ const fr = {
|
|
|
164
164
|
"editor.hubspot-form": "Formulaire marketing HubSpot",
|
|
165
165
|
"editor.hubspot-form-placeholder": "Choisir un formulaire du portail connecté",
|
|
166
166
|
"editor.hubspot-form-none": "Aucun — upsert CRM uniquement",
|
|
167
|
-
"editor.hubspot-form-hint": "Les envois passent par l’API Forms de HubSpot pour compter comme des conversions.
|
|
167
|
+
"editor.hubspot-form-hint": "Les envois passent par l’API Forms de HubSpot pour compter comme des conversions. Après une modification dans HubSpot, utilisez Resynchroniser — les champs mappés se mettent à jour sur place, les nouveaux s’ajoutent, vos étapes restent.",
|
|
168
|
+
"editor.resync": "Resynchroniser depuis HubSpot",
|
|
169
|
+
"editor.resynced": "Resynchronisé depuis HubSpot — {updated, plural, one {# champ mis à jour} other {# champs mis à jour}}, {added, plural, one {# ajouté} other {# ajoutés}}.",
|
|
170
|
+
"editor.resynced-gaps": "Resynchronisé depuis HubSpot — {updated, plural, one {# mis à jour} other {# mis à jour}}, {added, plural, one {# ajouté} other {# ajoutés}}, {skipped, plural, one {# écarté} other {# écartés}}.",
|
|
171
|
+
"editor.resync-error": "Resynchronisation impossible — le formulaire est-il toujours lié ?",
|
|
168
172
|
"editor.form-hint": "Sélectionnez une étape ou un champ pour l'éditer. Le endpoint public sert ce formulaire sur /api/hubspot/forms/{slug}.",
|
|
169
173
|
"panel.label": "Libellé",
|
|
170
174
|
"panel.name": "Clé du payload",
|
|
@@ -234,7 +238,7 @@ const fr = {
|
|
|
234
238
|
"forms.col-submissions": "Soumissions",
|
|
235
239
|
"forms.submissions": "Soumissions",
|
|
236
240
|
"forms.import-hubspot-title": "Importer depuis votre portail HubSpot",
|
|
237
|
-
"forms.import-hubspot-hint": "Traduit un formulaire construit dans HubSpot en brouillon ici, mapping CRM inclus. Ce que le builder ne sait pas exprimer est écarté et signalé. Ré-importer
|
|
241
|
+
"forms.import-hubspot-hint": "Traduit un formulaire construit dans HubSpot en brouillon ici, mapping CRM inclus. Ce que le builder ne sait pas exprimer est écarté et signalé. Ré-importer met à jour les champs mappés et ajoute les nouveaux.",
|
|
238
242
|
"forms.import-hubspot-source": "Formulaire HubSpot à importer",
|
|
239
243
|
"forms.hubspot-list-error": "Impossible de lister les formulaires du portail — le jeton a besoin du scope de lecture `forms`.",
|
|
240
244
|
"forms.report-title": "Importé avec des manques",
|
|
@@ -166,7 +166,11 @@ const fr = {
|
|
|
166
166
|
"editor.hubspot-form": "Formulaire marketing HubSpot",
|
|
167
167
|
"editor.hubspot-form-placeholder": "Choisir un formulaire du portail connecté",
|
|
168
168
|
"editor.hubspot-form-none": "Aucun — upsert CRM uniquement",
|
|
169
|
-
"editor.hubspot-form-hint": "Les envois passent par l’API Forms de HubSpot pour compter comme des conversions.
|
|
169
|
+
"editor.hubspot-form-hint": "Les envois passent par l’API Forms de HubSpot pour compter comme des conversions. Après une modification dans HubSpot, utilisez Resynchroniser — les champs mappés se mettent à jour sur place, les nouveaux s’ajoutent, vos étapes restent.",
|
|
170
|
+
"editor.resync": "Resynchroniser depuis HubSpot",
|
|
171
|
+
"editor.resynced": "Resynchronisé depuis HubSpot — {updated, plural, one {# champ mis à jour} other {# champs mis à jour}}, {added, plural, one {# ajouté} other {# ajoutés}}.",
|
|
172
|
+
"editor.resynced-gaps": "Resynchronisé depuis HubSpot — {updated, plural, one {# mis à jour} other {# mis à jour}}, {added, plural, one {# ajouté} other {# ajoutés}}, {skipped, plural, one {# écarté} other {# écartés}}.",
|
|
173
|
+
"editor.resync-error": "Resynchronisation impossible — le formulaire est-il toujours lié ?",
|
|
170
174
|
"editor.form-hint": "Sélectionnez une étape ou un champ pour l'éditer. Le endpoint public sert ce formulaire sur /api/hubspot/forms/{slug}.",
|
|
171
175
|
"panel.label": "Libellé",
|
|
172
176
|
"panel.name": "Clé du payload",
|
|
@@ -236,7 +240,7 @@ const fr = {
|
|
|
236
240
|
"forms.col-submissions": "Soumissions",
|
|
237
241
|
"forms.submissions": "Soumissions",
|
|
238
242
|
"forms.import-hubspot-title": "Importer depuis votre portail HubSpot",
|
|
239
|
-
"forms.import-hubspot-hint": "Traduit un formulaire construit dans HubSpot en brouillon ici, mapping CRM inclus. Ce que le builder ne sait pas exprimer est écarté et signalé. Ré-importer
|
|
243
|
+
"forms.import-hubspot-hint": "Traduit un formulaire construit dans HubSpot en brouillon ici, mapping CRM inclus. Ce que le builder ne sait pas exprimer est écarté et signalé. Ré-importer met à jour les champs mappés et ajoute les nouveaux.",
|
|
240
244
|
"forms.import-hubspot-source": "Formulaire HubSpot à importer",
|
|
241
245
|
"forms.hubspot-list-error": "Impossible de lister les formulaires du portail — le jeton a besoin du scope de lecture `forms`.",
|
|
242
246
|
"forms.report-title": "Importé avec des manques",
|
|
@@ -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-BOQWely0.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-BHfpBD36.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-BbnvhdbD.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-DCkoavrN.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-DqRpHtnT.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-Djd3TID0.mjs"), "./translations/fr.json": () => import("./fr-C7t1elmR.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-mMNP7hc9.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-BLZzsW08.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-Cq1TuL_l.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-DI35Aqiu.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-BAsx0LGv.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-sfzopB5K.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-sF5Josfh.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-R3ReZXF1.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-CLAy5dVN.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
|
@@ -1778,6 +1778,65 @@ function convertHubspotForm(raw) {
|
|
|
1778
1778
|
skipped
|
|
1779
1779
|
};
|
|
1780
1780
|
}
|
|
1781
|
+
function mappingKey(field) {
|
|
1782
|
+
const mapping = field.hubspot ?? {};
|
|
1783
|
+
const property = mapping.property?.trim();
|
|
1784
|
+
if (!property) return null;
|
|
1785
|
+
return `${mapping.object?.trim() || "contact"}:${property}`;
|
|
1786
|
+
}
|
|
1787
|
+
function uniqueName(base, used) {
|
|
1788
|
+
if (!used.has(base)) return base;
|
|
1789
|
+
let n = 2;
|
|
1790
|
+
while (used.has(`${base}_${n}`)) n += 1;
|
|
1791
|
+
return `${base}_${n}`;
|
|
1792
|
+
}
|
|
1793
|
+
function mergeHubspotImport(current, incoming) {
|
|
1794
|
+
const steps = (current.steps ?? []).map((step) => ({
|
|
1795
|
+
...step,
|
|
1796
|
+
fields: [...step.fields ?? []]
|
|
1797
|
+
}));
|
|
1798
|
+
if (!steps.length) {
|
|
1799
|
+
steps.push({ id: makeId$1("stp"), fields: [] });
|
|
1800
|
+
}
|
|
1801
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1802
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
1803
|
+
for (const step of steps) {
|
|
1804
|
+
for (const field of step.fields) {
|
|
1805
|
+
usedNames.add(field.name);
|
|
1806
|
+
const key = mappingKey(field);
|
|
1807
|
+
if (key) byKey.set(key, field);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
const added = [];
|
|
1811
|
+
const updated = [];
|
|
1812
|
+
const incomingFields = (incoming.steps ?? []).flatMap((step) => step.fields ?? []);
|
|
1813
|
+
for (const next of incomingFields) {
|
|
1814
|
+
const key = mappingKey(next);
|
|
1815
|
+
const existing = key ? byKey.get(key) : void 0;
|
|
1816
|
+
if (existing) {
|
|
1817
|
+
existing.label = next.label;
|
|
1818
|
+
existing.type = next.type;
|
|
1819
|
+
existing.required = next.required;
|
|
1820
|
+
existing.placeholder = next.placeholder;
|
|
1821
|
+
existing.helpText = next.helpText;
|
|
1822
|
+
if (next.options) existing.options = next.options;
|
|
1823
|
+
else delete existing.options;
|
|
1824
|
+
if (!existing.visibleIf && next.visibleIf) existing.visibleIf = next.visibleIf;
|
|
1825
|
+
updated.push(key);
|
|
1826
|
+
continue;
|
|
1827
|
+
}
|
|
1828
|
+
const field = {
|
|
1829
|
+
...next,
|
|
1830
|
+
id: makeId$1("fld"),
|
|
1831
|
+
name: uniqueName(next.name, usedNames)
|
|
1832
|
+
};
|
|
1833
|
+
usedNames.add(field.name);
|
|
1834
|
+
if (key) byKey.set(key, field);
|
|
1835
|
+
steps[steps.length - 1].fields.push(field);
|
|
1836
|
+
added.push(key || field.name);
|
|
1837
|
+
}
|
|
1838
|
+
return { definition: { version: 1, steps }, added, updated };
|
|
1839
|
+
}
|
|
1781
1840
|
async function hsGet(apiKey, path) {
|
|
1782
1841
|
const res = await fetch(`${HS_BASE$2}${path}`, {
|
|
1783
1842
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
@@ -2895,9 +2954,9 @@ function createFormsAdminController(strapi) {
|
|
|
2895
2954
|
},
|
|
2896
2955
|
/**
|
|
2897
2956
|
* Converts one portal form into a plugin form draft, in the current
|
|
2898
|
-
* locale.
|
|
2899
|
-
*
|
|
2900
|
-
*
|
|
2957
|
+
* locale. Matching prefers the linked HubSpot GUID, then the slug.
|
|
2958
|
+
* Re-importing merges: mapped fields update in place, new ones append,
|
|
2959
|
+
* Strapi steps stay. Never the published version, never the portal.
|
|
2901
2960
|
*/
|
|
2902
2961
|
async runHubspotImport(ctx) {
|
|
2903
2962
|
const formId = ctx.request.body?.formId;
|
|
@@ -2916,24 +2975,81 @@ function createFormsAdminController(strapi) {
|
|
|
2916
2975
|
const { skipped, definition, ...meta } = converted;
|
|
2917
2976
|
const slug = slugify(name);
|
|
2918
2977
|
const existing = await documents().findFirst({
|
|
2978
|
+
filters: { hubspotFormId: formId }
|
|
2979
|
+
}) ?? await documents().findFirst({
|
|
2919
2980
|
filters: { slug }
|
|
2920
2981
|
});
|
|
2921
|
-
let documentId;
|
|
2922
2982
|
if (existing) {
|
|
2923
|
-
await documents().
|
|
2983
|
+
const current = await documents().findOne({
|
|
2924
2984
|
documentId: existing.documentId,
|
|
2925
2985
|
locale: ctx.query.locale,
|
|
2926
|
-
|
|
2986
|
+
status: "draft"
|
|
2927
2987
|
});
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2988
|
+
const merged = mergeHubspotImport(current?.definition ?? EMPTY_DEFINITION, definition);
|
|
2989
|
+
await documents().update({
|
|
2990
|
+
documentId: existing.documentId,
|
|
2931
2991
|
locale: ctx.query.locale,
|
|
2932
|
-
data: {
|
|
2992
|
+
data: {
|
|
2993
|
+
hubspotFormId: meta.hubspotFormId ?? formId,
|
|
2994
|
+
definition: merged.definition
|
|
2995
|
+
}
|
|
2933
2996
|
});
|
|
2934
|
-
|
|
2997
|
+
ctx.body = {
|
|
2998
|
+
documentId: existing.documentId,
|
|
2999
|
+
skipped,
|
|
3000
|
+
added: merged.added,
|
|
3001
|
+
updated: merged.updated
|
|
3002
|
+
};
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
const created = await documents().create({
|
|
3006
|
+
locale: ctx.query.locale,
|
|
3007
|
+
data: { ...meta, name, slug, definition }
|
|
3008
|
+
});
|
|
3009
|
+
ctx.body = { documentId: created.documentId, skipped, added: [], updated: [] };
|
|
3010
|
+
},
|
|
3011
|
+
/**
|
|
3012
|
+
* Pull the linked HubSpot marketing form back into this draft: mapped
|
|
3013
|
+
* fields update in place, new ones append, Strapi steps and extras stay.
|
|
3014
|
+
*/
|
|
3015
|
+
async resync(ctx) {
|
|
3016
|
+
const entry = await documents().findOne({
|
|
3017
|
+
documentId: ctx.params.documentId,
|
|
3018
|
+
locale: ctx.query.locale,
|
|
3019
|
+
status: "draft"
|
|
3020
|
+
});
|
|
3021
|
+
if (!entry) ctx.throw(404, "Form not found");
|
|
3022
|
+
const guid = typeof entry.hubspotFormId === "string" ? entry.hubspotFormId.trim() : "";
|
|
3023
|
+
if (!isFormGuid(guid)) {
|
|
3024
|
+
ctx.throw(400, "This form is not linked to a HubSpot marketing form.");
|
|
3025
|
+
}
|
|
3026
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3027
|
+
if (!apiKey) ctx.throw(400, "No HubSpot API key configured");
|
|
3028
|
+
let converted;
|
|
3029
|
+
try {
|
|
3030
|
+
converted = convertHubspotForm(await fetchHubspotForm(apiKey, guid));
|
|
3031
|
+
} catch (err) {
|
|
3032
|
+
strapi.log.warn(`[hubspot] form resync failed — ${err.message}`);
|
|
3033
|
+
ctx.throw(502, err.message || "Cannot reach HubSpot");
|
|
3034
|
+
return;
|
|
2935
3035
|
}
|
|
2936
|
-
|
|
3036
|
+
const merged = mergeHubspotImport(entry.definition ?? EMPTY_DEFINITION, converted.definition);
|
|
3037
|
+
await documents().update({
|
|
3038
|
+
documentId: ctx.params.documentId,
|
|
3039
|
+
locale: ctx.query.locale,
|
|
3040
|
+
data: { definition: merged.definition }
|
|
3041
|
+
});
|
|
3042
|
+
const form = await documents().findOne({
|
|
3043
|
+
documentId: ctx.params.documentId,
|
|
3044
|
+
locale: ctx.query.locale,
|
|
3045
|
+
status: "draft"
|
|
3046
|
+
});
|
|
3047
|
+
ctx.body = {
|
|
3048
|
+
form,
|
|
3049
|
+
added: merged.added,
|
|
3050
|
+
updated: merged.updated,
|
|
3051
|
+
skipped: converted.skipped
|
|
3052
|
+
};
|
|
2937
3053
|
},
|
|
2938
3054
|
/**
|
|
2939
3055
|
* Copies a form (every locale of its draft) into a new draft document —
|
|
@@ -3472,6 +3588,7 @@ const routes = {
|
|
|
3472
3588
|
adminRoute("POST", "/builder/forms/:documentId/unpublish", "formsAdmin.unpublish", [FORMS_ACTION]),
|
|
3473
3589
|
adminRoute("DELETE", "/builder/forms/:documentId", "formsAdmin.remove", [FORMS_ACTION]),
|
|
3474
3590
|
adminRoute("POST", "/builder/forms/:documentId/duplicate", "formsAdmin.duplicate", [FORMS_ACTION]),
|
|
3591
|
+
adminRoute("POST", "/builder/forms/:documentId/resync", "formsAdmin.resync", [FORMS_ACTION]),
|
|
3475
3592
|
// No FORMS_ACTION: editors pick forms from the Content Manager, like
|
|
3476
3593
|
// they pick properties — the builder itself stays gated.
|
|
3477
3594
|
adminRoute("GET", "/forms-options", "formsAdmin.options"),
|
package/dist/server/index.mjs
CHANGED
|
@@ -1777,6 +1777,65 @@ function convertHubspotForm(raw) {
|
|
|
1777
1777
|
skipped
|
|
1778
1778
|
};
|
|
1779
1779
|
}
|
|
1780
|
+
function mappingKey(field) {
|
|
1781
|
+
const mapping = field.hubspot ?? {};
|
|
1782
|
+
const property = mapping.property?.trim();
|
|
1783
|
+
if (!property) return null;
|
|
1784
|
+
return `${mapping.object?.trim() || "contact"}:${property}`;
|
|
1785
|
+
}
|
|
1786
|
+
function uniqueName(base, used) {
|
|
1787
|
+
if (!used.has(base)) return base;
|
|
1788
|
+
let n = 2;
|
|
1789
|
+
while (used.has(`${base}_${n}`)) n += 1;
|
|
1790
|
+
return `${base}_${n}`;
|
|
1791
|
+
}
|
|
1792
|
+
function mergeHubspotImport(current, incoming) {
|
|
1793
|
+
const steps = (current.steps ?? []).map((step) => ({
|
|
1794
|
+
...step,
|
|
1795
|
+
fields: [...step.fields ?? []]
|
|
1796
|
+
}));
|
|
1797
|
+
if (!steps.length) {
|
|
1798
|
+
steps.push({ id: makeId$1("stp"), fields: [] });
|
|
1799
|
+
}
|
|
1800
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1801
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
1802
|
+
for (const step of steps) {
|
|
1803
|
+
for (const field of step.fields) {
|
|
1804
|
+
usedNames.add(field.name);
|
|
1805
|
+
const key = mappingKey(field);
|
|
1806
|
+
if (key) byKey.set(key, field);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const added = [];
|
|
1810
|
+
const updated = [];
|
|
1811
|
+
const incomingFields = (incoming.steps ?? []).flatMap((step) => step.fields ?? []);
|
|
1812
|
+
for (const next of incomingFields) {
|
|
1813
|
+
const key = mappingKey(next);
|
|
1814
|
+
const existing = key ? byKey.get(key) : void 0;
|
|
1815
|
+
if (existing) {
|
|
1816
|
+
existing.label = next.label;
|
|
1817
|
+
existing.type = next.type;
|
|
1818
|
+
existing.required = next.required;
|
|
1819
|
+
existing.placeholder = next.placeholder;
|
|
1820
|
+
existing.helpText = next.helpText;
|
|
1821
|
+
if (next.options) existing.options = next.options;
|
|
1822
|
+
else delete existing.options;
|
|
1823
|
+
if (!existing.visibleIf && next.visibleIf) existing.visibleIf = next.visibleIf;
|
|
1824
|
+
updated.push(key);
|
|
1825
|
+
continue;
|
|
1826
|
+
}
|
|
1827
|
+
const field = {
|
|
1828
|
+
...next,
|
|
1829
|
+
id: makeId$1("fld"),
|
|
1830
|
+
name: uniqueName(next.name, usedNames)
|
|
1831
|
+
};
|
|
1832
|
+
usedNames.add(field.name);
|
|
1833
|
+
if (key) byKey.set(key, field);
|
|
1834
|
+
steps[steps.length - 1].fields.push(field);
|
|
1835
|
+
added.push(key || field.name);
|
|
1836
|
+
}
|
|
1837
|
+
return { definition: { version: 1, steps }, added, updated };
|
|
1838
|
+
}
|
|
1780
1839
|
async function hsGet(apiKey, path) {
|
|
1781
1840
|
const res = await fetch(`${HS_BASE$2}${path}`, {
|
|
1782
1841
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
@@ -2894,9 +2953,9 @@ function createFormsAdminController(strapi) {
|
|
|
2894
2953
|
},
|
|
2895
2954
|
/**
|
|
2896
2955
|
* Converts one portal form into a plugin form draft, in the current
|
|
2897
|
-
* locale.
|
|
2898
|
-
*
|
|
2899
|
-
*
|
|
2956
|
+
* locale. Matching prefers the linked HubSpot GUID, then the slug.
|
|
2957
|
+
* Re-importing merges: mapped fields update in place, new ones append,
|
|
2958
|
+
* Strapi steps stay. Never the published version, never the portal.
|
|
2900
2959
|
*/
|
|
2901
2960
|
async runHubspotImport(ctx) {
|
|
2902
2961
|
const formId = ctx.request.body?.formId;
|
|
@@ -2915,24 +2974,81 @@ function createFormsAdminController(strapi) {
|
|
|
2915
2974
|
const { skipped, definition, ...meta } = converted;
|
|
2916
2975
|
const slug = slugify(name);
|
|
2917
2976
|
const existing = await documents().findFirst({
|
|
2977
|
+
filters: { hubspotFormId: formId }
|
|
2978
|
+
}) ?? await documents().findFirst({
|
|
2918
2979
|
filters: { slug }
|
|
2919
2980
|
});
|
|
2920
|
-
let documentId;
|
|
2921
2981
|
if (existing) {
|
|
2922
|
-
await documents().
|
|
2982
|
+
const current = await documents().findOne({
|
|
2923
2983
|
documentId: existing.documentId,
|
|
2924
2984
|
locale: ctx.query.locale,
|
|
2925
|
-
|
|
2985
|
+
status: "draft"
|
|
2926
2986
|
});
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2987
|
+
const merged = mergeHubspotImport(current?.definition ?? EMPTY_DEFINITION, definition);
|
|
2988
|
+
await documents().update({
|
|
2989
|
+
documentId: existing.documentId,
|
|
2930
2990
|
locale: ctx.query.locale,
|
|
2931
|
-
data: {
|
|
2991
|
+
data: {
|
|
2992
|
+
hubspotFormId: meta.hubspotFormId ?? formId,
|
|
2993
|
+
definition: merged.definition
|
|
2994
|
+
}
|
|
2932
2995
|
});
|
|
2933
|
-
|
|
2996
|
+
ctx.body = {
|
|
2997
|
+
documentId: existing.documentId,
|
|
2998
|
+
skipped,
|
|
2999
|
+
added: merged.added,
|
|
3000
|
+
updated: merged.updated
|
|
3001
|
+
};
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
const created = await documents().create({
|
|
3005
|
+
locale: ctx.query.locale,
|
|
3006
|
+
data: { ...meta, name, slug, definition }
|
|
3007
|
+
});
|
|
3008
|
+
ctx.body = { documentId: created.documentId, skipped, added: [], updated: [] };
|
|
3009
|
+
},
|
|
3010
|
+
/**
|
|
3011
|
+
* Pull the linked HubSpot marketing form back into this draft: mapped
|
|
3012
|
+
* fields update in place, new ones append, Strapi steps and extras stay.
|
|
3013
|
+
*/
|
|
3014
|
+
async resync(ctx) {
|
|
3015
|
+
const entry = await documents().findOne({
|
|
3016
|
+
documentId: ctx.params.documentId,
|
|
3017
|
+
locale: ctx.query.locale,
|
|
3018
|
+
status: "draft"
|
|
3019
|
+
});
|
|
3020
|
+
if (!entry) ctx.throw(404, "Form not found");
|
|
3021
|
+
const guid = typeof entry.hubspotFormId === "string" ? entry.hubspotFormId.trim() : "";
|
|
3022
|
+
if (!isFormGuid(guid)) {
|
|
3023
|
+
ctx.throw(400, "This form is not linked to a HubSpot marketing form.");
|
|
3024
|
+
}
|
|
3025
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3026
|
+
if (!apiKey) ctx.throw(400, "No HubSpot API key configured");
|
|
3027
|
+
let converted;
|
|
3028
|
+
try {
|
|
3029
|
+
converted = convertHubspotForm(await fetchHubspotForm(apiKey, guid));
|
|
3030
|
+
} catch (err) {
|
|
3031
|
+
strapi.log.warn(`[hubspot] form resync failed — ${err.message}`);
|
|
3032
|
+
ctx.throw(502, err.message || "Cannot reach HubSpot");
|
|
3033
|
+
return;
|
|
2934
3034
|
}
|
|
2935
|
-
|
|
3035
|
+
const merged = mergeHubspotImport(entry.definition ?? EMPTY_DEFINITION, converted.definition);
|
|
3036
|
+
await documents().update({
|
|
3037
|
+
documentId: ctx.params.documentId,
|
|
3038
|
+
locale: ctx.query.locale,
|
|
3039
|
+
data: { definition: merged.definition }
|
|
3040
|
+
});
|
|
3041
|
+
const form = await documents().findOne({
|
|
3042
|
+
documentId: ctx.params.documentId,
|
|
3043
|
+
locale: ctx.query.locale,
|
|
3044
|
+
status: "draft"
|
|
3045
|
+
});
|
|
3046
|
+
ctx.body = {
|
|
3047
|
+
form,
|
|
3048
|
+
added: merged.added,
|
|
3049
|
+
updated: merged.updated,
|
|
3050
|
+
skipped: converted.skipped
|
|
3051
|
+
};
|
|
2936
3052
|
},
|
|
2937
3053
|
/**
|
|
2938
3054
|
* Copies a form (every locale of its draft) into a new draft document —
|
|
@@ -3471,6 +3587,7 @@ const routes = {
|
|
|
3471
3587
|
adminRoute("POST", "/builder/forms/:documentId/unpublish", "formsAdmin.unpublish", [FORMS_ACTION]),
|
|
3472
3588
|
adminRoute("DELETE", "/builder/forms/:documentId", "formsAdmin.remove", [FORMS_ACTION]),
|
|
3473
3589
|
adminRoute("POST", "/builder/forms/:documentId/duplicate", "formsAdmin.duplicate", [FORMS_ACTION]),
|
|
3590
|
+
adminRoute("POST", "/builder/forms/:documentId/resync", "formsAdmin.resync", [FORMS_ACTION]),
|
|
3474
3591
|
// No FORMS_ACTION: editors pick forms from the Content Manager, like
|
|
3475
3592
|
// they pick properties — the builder itself stays gated.
|
|
3476
3593
|
adminRoute("GET", "/forms-options", "formsAdmin.options"),
|
|
@@ -50,11 +50,16 @@ export declare function createFormsAdminController(strapi: Core.Strapi): {
|
|
|
50
50
|
listHubspotSources(ctx: Ctx): Promise<void>;
|
|
51
51
|
/**
|
|
52
52
|
* Converts one portal form into a plugin form draft, in the current
|
|
53
|
-
* locale.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* locale. Matching prefers the linked HubSpot GUID, then the slug.
|
|
54
|
+
* Re-importing merges: mapped fields update in place, new ones append,
|
|
55
|
+
* Strapi steps stay. Never the published version, never the portal.
|
|
56
56
|
*/
|
|
57
57
|
runHubspotImport(ctx: Ctx): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Pull the linked HubSpot marketing form back into this draft: mapped
|
|
60
|
+
* fields update in place, new ones append, Strapi steps and extras stay.
|
|
61
|
+
*/
|
|
62
|
+
resync(ctx: Ctx): Promise<void>;
|
|
58
63
|
/**
|
|
59
64
|
* Copies a form (every locale of its draft) into a new draft document —
|
|
60
65
|
* the quickest way to A/B a variant or to start from an existing form.
|
|
@@ -79,6 +79,18 @@ export interface RawHubspotForm {
|
|
|
79
79
|
} | null;
|
|
80
80
|
}
|
|
81
81
|
export declare function convertHubspotForm(raw: RawHubspotForm): ConvertedHubspotForm;
|
|
82
|
+
export interface HubspotMergeResult {
|
|
83
|
+
definition: FormDefinition;
|
|
84
|
+
added: string[];
|
|
85
|
+
updated: string[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Fold a freshly converted HubSpot form into an existing builder definition.
|
|
89
|
+
* Mapped fields are updated in place (label, type, required, options…) so
|
|
90
|
+
* Strapi steps, ids, conditions and extra fields survive. New HubSpot fields
|
|
91
|
+
* are appended to the last step. Nothing is deleted.
|
|
92
|
+
*/
|
|
93
|
+
export declare function mergeHubspotImport(current: FormDefinition, incoming: FormDefinition): HubspotMergeResult;
|
|
82
94
|
/** Every regular form of the portal, name-sorted. Needs the `forms` scope. */
|
|
83
95
|
export declare function listHubspotForms(apiKey: string): Promise<HubspotFormSummary[]>;
|
|
84
96
|
/** One form, full definition. */
|
|
@@ -280,6 +280,7 @@ declare const _default: {
|
|
|
280
280
|
runImport(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
281
281
|
listHubspotSources(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
282
282
|
runHubspotImport(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
283
|
+
resync(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
283
284
|
duplicate(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
284
285
|
remove(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
285
286
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-hubspot",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
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",
|