strapi-plugin-hubspot 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +28 -13
- package/dist/_chunks/{Forms-B7eIm0Fi.mjs → Forms-6EoEYzAc.mjs} +26 -5
- package/dist/_chunks/{Forms-ClyI4MJ1.js → Forms-BW_zGc1P.js} +26 -5
- package/dist/_chunks/{HubspotFormInput-cZAiaAaq.mjs → HubspotFormInput-V5w4y1fb.mjs} +1 -1
- package/dist/_chunks/{HubspotFormInput-Do7F83V6.js → HubspotFormInput-oMhm1UJm.js} +1 -1
- package/dist/_chunks/{HubspotObjectInput-DWqk2C26.js → HubspotObjectInput-Bmo6YQgs.js} +3 -3
- package/dist/_chunks/{HubspotObjectInput-ZZk_7hWq.mjs → HubspotObjectInput-DhsnSguG.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-5bkDGmGo.mjs → HubspotPropertyInput-CrYP4pc0.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-v9B3wb1a.js → HubspotPropertyInput-luFfnxqy.js} +3 -3
- package/dist/_chunks/{Settings-X3EHnTaO.js → Settings-B3A22i7Z.js} +77 -4
- package/dist/_chunks/{Settings-QcFbm3s5.mjs → Settings-CjcWj9I6.mjs} +78 -5
- package/dist/_chunks/{en-ZHvaZLi7.js → en-C7ls2EUl.js} +15 -2
- package/dist/_chunks/{en-DMJeF7EN.mjs → en-D6GHgMOt.mjs} +15 -2
- package/dist/_chunks/{fr-CVAntLmc.mjs → fr-CfohMY8n.mjs} +15 -2
- package/dist/_chunks/{fr-Ds_2EAzZ.js → fr-pOP8ZXqM.js} +15 -2
- package/dist/_chunks/{index-BSJAhj8r.mjs → index-7cL9PRh1.mjs} +6 -6
- package/dist/_chunks/{index-BpcilDy1.js → index-CodpLKZx.js} +6 -6
- package/dist/_chunks/{objectLabels-CYXXFpNB.js → objectLabels-CSWGIgNb.js} +1 -1
- package/dist/_chunks/{objectLabels-CrYRzsqe.mjs → objectLabels-Dnj14fsp.mjs} +1 -1
- package/dist/_chunks/{useHubspotSchema-CBF4GkmK.mjs → useHubspotSchema-BsmQgJgx.mjs} +1 -1
- package/dist/_chunks/{useHubspotSchema-Ba6xZUCL.js → useHubspotSchema-DiRN3HrN.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +216 -7
- package/dist/server/index.mjs +216 -7
- package/dist/server/src/__tests__/formSync.test.d.ts +1 -0
- package/dist/server/src/formSync.d.ts +37 -0
- package/dist/server/src/forms.d.ts +6 -8
- package/dist/server/src/hsforms.d.ts +8 -0
- package/dist/server/src/importHubspot.d.ts +4 -0
- package/dist/server/src/index.d.ts +6 -0
- package/dist/server/src/settings.d.ts +32 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.16.0 — 2026-09-22
|
|
6
|
+
|
|
7
|
+
### Added — submission policies
|
|
8
|
+
The plugin no longer assumes one HubSpot workflow. **Settings → HubSpot**
|
|
9
|
+
(and `forms.*` in `config/plugins.ts`) now expose three independent choices:
|
|
10
|
+
|
|
11
|
+
- **`submissionMode`** (`auto` / `forms` / `crm`): native marketing-form
|
|
12
|
+
conversions when a GUID is linked, CRM upsert only, or automatic.
|
|
13
|
+
- **`writeExtraProperties`** (default `true`): after a Forms API conversion,
|
|
14
|
+
leftover mapped contact fields still land on the contact via CRM upsert.
|
|
15
|
+
- **`syncFieldsOnPublish`** (default `false`): on publish, PATCH missing
|
|
16
|
+
mapped contact fields onto the linked HubSpot form. Off so HubSpot-first
|
|
17
|
+
installs aren't mutated. CRM properties are never created.
|
|
18
|
+
|
|
5
19
|
## 0.15.0 — 2026-09-21
|
|
6
20
|
|
|
7
21
|
### Added — account settings in the admin
|
package/README.md
CHANGED
|
@@ -44,14 +44,25 @@ Permissions**):
|
|
|
44
44
|
| `GET` | `/api/hubspot/forms/:slug?locale=` | The published form — meta, steps, fields, conditions. The CRM mapping is stripped: the browser never learns your property names |
|
|
45
45
|
| `POST` | `/api/hubspot/forms/:slug/submit` | Validates (bounds, conditions, required), maps server-side, syncs HubSpot, stores the submission |
|
|
46
46
|
|
|
47
|
-
The submit pipeline
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
The submit pipeline is a **policy**, not a single path. Settings → HubSpot
|
|
48
|
+
(or `forms.*` in `config/plugins.ts`) pick how this install talks to HubSpot —
|
|
49
|
+
so a portal that wants native conversions and a portal that only wants CRM
|
|
50
|
+
upserts can share the same plugin:
|
|
51
|
+
|
|
52
|
+
| Setting | Default | What it does |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `forms.submissionMode` | `auto` | `auto`: Forms API when a marketing GUID is linked, else CRM upsert. `forms`: always try the Forms API. `crm`: never count as a conversion |
|
|
55
|
+
| `forms.writeExtraProperties` | `true` | After a conversion, CRM-write mapped contact fields the HubSpot form dropped (they 400 the Forms API) |
|
|
56
|
+
| `forms.syncFieldsOnPublish` | `false` | On publish, PATCH missing mapped **contact** fields onto the HubSpot form. Off by default so HubSpot-first installs aren't mutated |
|
|
57
|
+
|
|
58
|
+
The visitor's `hutk` is forwarded so attribution sticks. Then the contact is
|
|
59
|
+
looked up by email and, when the address is on a corporate domain, the company
|
|
60
|
+
is found-or-created and associated. Timeline notes are opt-in and only used
|
|
61
|
+
on the CRM-upsert path.
|
|
62
|
+
|
|
63
|
+
CRM properties are **never created**. Map to properties that already exist in
|
|
64
|
+
the portal; opting into field sync only adds form fields that point at those
|
|
65
|
+
properties.
|
|
55
66
|
|
|
56
67
|
Portal id, region and form GUIDs are configuration. A test portal today,
|
|
57
68
|
production tomorrow: swap `portalId` / `region` / the GUID, no code change.
|
|
@@ -66,6 +77,9 @@ hubspot: {
|
|
|
66
77
|
companyFromDomain: true, // Company by corporate domain + association
|
|
67
78
|
timelineNote: false, // recap note on the CRM-upsert fallback only
|
|
68
79
|
defaultFormId: env("HUBSPOT_DEFAULT_FORM_ID", ""),
|
|
80
|
+
submissionMode: "auto", // auto | forms | crm
|
|
81
|
+
writeExtraProperties: true,
|
|
82
|
+
syncFieldsOnPublish: false,
|
|
69
83
|
},
|
|
70
84
|
},
|
|
71
85
|
}
|
|
@@ -365,11 +379,12 @@ its equivalent per object you send to).
|
|
|
365
379
|
|
|
366
380
|
### A settings screen
|
|
367
381
|
|
|
368
|
-
**Settings → HubSpot** holds the private app token
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
`config/plugins.ts` and env
|
|
372
|
-
|
|
382
|
+
**Settings → HubSpot** holds the private app token, the HubSpot account that
|
|
383
|
+
receives conversions (portal id, region, default marketing form), **and** the
|
|
384
|
+
submission policy: conversion vs CRM upsert, leftover contact writes, form
|
|
385
|
+
field sync on publish. Values saved here override `config/plugins.ts` and env
|
|
386
|
+
— switching test → production, or switching HubSpot-first ↔ Strapi-first, is
|
|
387
|
+
an admin change, not a deploy.
|
|
373
388
|
|
|
374
389
|
The token is stored server-side and never returned to the browser: the UI only
|
|
375
390
|
receives whether a key exists, where it comes from, and its last four
|
|
@@ -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-7cL9PRh1.mjs";
|
|
9
9
|
import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from "@dnd-kit/core";
|
|
10
10
|
import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable, arrayMove } 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-Dnj14fsp.mjs";
|
|
13
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-BsmQgJgx.mjs";
|
|
14
14
|
const OPERATORS = [
|
|
15
15
|
"eq",
|
|
16
16
|
"neq",
|
|
@@ -774,11 +774,32 @@ const FormEditor = () => {
|
|
|
774
774
|
setBusy(true);
|
|
775
775
|
setFeedback(null);
|
|
776
776
|
try {
|
|
777
|
-
await post(`/${PLUGIN_ID}/builder/forms/${documentId}/publish${query}`);
|
|
777
|
+
const { data } = await post(`/${PLUGIN_ID}/builder/forms/${documentId}/publish${query}`);
|
|
778
778
|
setPublished(true);
|
|
779
779
|
setErrors([]);
|
|
780
780
|
setProblems([]);
|
|
781
|
-
|
|
781
|
+
const added = Array.isArray(data?.addedFields) ? data.addedFields.length : 0;
|
|
782
|
+
if (data?.syncWarning) {
|
|
783
|
+
setFeedback({
|
|
784
|
+
tone: "success",
|
|
785
|
+
text: t(
|
|
786
|
+
"editor.published-sync-warn",
|
|
787
|
+
"Form published. HubSpot field sync failed: {error}",
|
|
788
|
+
{ error: data.syncWarning }
|
|
789
|
+
)
|
|
790
|
+
});
|
|
791
|
+
} else if (added) {
|
|
792
|
+
setFeedback({
|
|
793
|
+
tone: "success",
|
|
794
|
+
text: t(
|
|
795
|
+
"editor.published-synced",
|
|
796
|
+
"Form published. {count, plural, one {# field added} other {# fields added}} to the HubSpot form.",
|
|
797
|
+
{ count: added }
|
|
798
|
+
)
|
|
799
|
+
});
|
|
800
|
+
} else {
|
|
801
|
+
setFeedback({ tone: "success", text: t("editor.published", "Form published.") });
|
|
802
|
+
}
|
|
782
803
|
} catch (err) {
|
|
783
804
|
const data = err.response?.data;
|
|
784
805
|
setErrors(data?.errors ?? []);
|
|
@@ -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-CodpLKZx.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-CSWGIgNb.js");
|
|
15
|
+
const useHubspotSchema = require("./useHubspotSchema-DiRN3HrN.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" } });
|
|
@@ -794,11 +794,32 @@ const FormEditor = () => {
|
|
|
794
794
|
setBusy(true);
|
|
795
795
|
setFeedback(null);
|
|
796
796
|
try {
|
|
797
|
-
await post(`/${index$1.PLUGIN_ID}/builder/forms/${documentId}/publish${query}`);
|
|
797
|
+
const { data } = await post(`/${index$1.PLUGIN_ID}/builder/forms/${documentId}/publish${query}`);
|
|
798
798
|
setPublished(true);
|
|
799
799
|
setErrors([]);
|
|
800
800
|
setProblems([]);
|
|
801
|
-
|
|
801
|
+
const added = Array.isArray(data?.addedFields) ? data.addedFields.length : 0;
|
|
802
|
+
if (data?.syncWarning) {
|
|
803
|
+
setFeedback({
|
|
804
|
+
tone: "success",
|
|
805
|
+
text: t(
|
|
806
|
+
"editor.published-sync-warn",
|
|
807
|
+
"Form published. HubSpot field sync failed: {error}",
|
|
808
|
+
{ error: data.syncWarning }
|
|
809
|
+
)
|
|
810
|
+
});
|
|
811
|
+
} else if (added) {
|
|
812
|
+
setFeedback({
|
|
813
|
+
tone: "success",
|
|
814
|
+
text: t(
|
|
815
|
+
"editor.published-synced",
|
|
816
|
+
"Form published. {count, plural, one {# field added} other {# fields added}} to the HubSpot form.",
|
|
817
|
+
{ count: added }
|
|
818
|
+
)
|
|
819
|
+
});
|
|
820
|
+
} else {
|
|
821
|
+
setFeedback({ tone: "success", text: t("editor.published", "Form published.") });
|
|
822
|
+
}
|
|
802
823
|
} catch (err) {
|
|
803
824
|
const data = err.response?.data;
|
|
804
825
|
setErrors(data?.errors ?? []);
|
|
@@ -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-7cL9PRh1.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-CodpLKZx.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" } });
|
|
@@ -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-CodpLKZx.js");
|
|
8
|
+
const objectLabels = require("./objectLabels-CSWGIgNb.js");
|
|
9
|
+
const useHubspotSchema = require("./useHubspotSchema-DiRN3HrN.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-7cL9PRh1.mjs";
|
|
6
|
+
import { o as objectLabel } from "./objectLabels-Dnj14fsp.mjs";
|
|
7
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-BsmQgJgx.mjs";
|
|
8
8
|
const HubspotObjectInput = React.forwardRef(
|
|
9
9
|
({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
|
|
10
10
|
const intl = useIntl();
|
package/dist/_chunks/{HubspotPropertyInput-5bkDGmGo.mjs → HubspotPropertyInput-CrYP4pc0.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-7cL9PRh1.mjs";
|
|
8
|
+
import { o as objectLabel } from "./objectLabels-Dnj14fsp.mjs";
|
|
9
|
+
import { u as useHubspotSchema } from "./useHubspotSchema-BsmQgJgx.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-CodpLKZx.js");
|
|
10
|
+
const objectLabels = require("./objectLabels-CSWGIgNb.js");
|
|
11
|
+
const useHubspotSchema = require("./useHubspotSchema-DiRN3HrN.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-CodpLKZx.js");
|
|
9
9
|
const reactRouterDom = require("react-router-dom");
|
|
10
|
-
const objectLabels = require("./objectLabels-
|
|
10
|
+
const objectLabels = require("./objectLabels-CSWGIgNb.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" } });
|
|
@@ -201,6 +201,9 @@ const HubspotSettings = () => {
|
|
|
201
201
|
const [portalId, setPortalId] = React__namespace.useState("");
|
|
202
202
|
const [region, setRegion] = React__namespace.useState("eu1");
|
|
203
203
|
const [defaultFormId, setDefaultFormId] = React__namespace.useState("");
|
|
204
|
+
const [submissionMode, setSubmissionMode] = React__namespace.useState("auto");
|
|
205
|
+
const [writeExtraProperties, setWriteExtraProperties] = React__namespace.useState(true);
|
|
206
|
+
const [syncFieldsOnPublish, setSyncFieldsOnPublish] = React__namespace.useState(false);
|
|
204
207
|
const [forms, setForms] = React__namespace.useState([]);
|
|
205
208
|
const [busy, setBusy] = React__namespace.useState(false);
|
|
206
209
|
const [feedback, setFeedback] = React__namespace.useState(
|
|
@@ -212,6 +215,9 @@ const HubspotSettings = () => {
|
|
|
212
215
|
setPortalId(data.portalId ?? "");
|
|
213
216
|
setRegion(data.region || "eu1");
|
|
214
217
|
setDefaultFormId(data.defaultFormId ?? "");
|
|
218
|
+
setSubmissionMode(data.submissionMode === "crm" || data.submissionMode === "forms" ? data.submissionMode : "auto");
|
|
219
|
+
setWriteExtraProperties(data.writeExtraProperties !== false);
|
|
220
|
+
setSyncFieldsOnPublish(data.syncFieldsOnPublish === true);
|
|
215
221
|
setForms(Array.isArray(data.forms) ? data.forms : []);
|
|
216
222
|
};
|
|
217
223
|
const load = React__namespace.useCallback(async () => {
|
|
@@ -231,7 +237,10 @@ const HubspotSettings = () => {
|
|
|
231
237
|
...apiKey.trim() ? { apiKey: apiKey.trim() } : {},
|
|
232
238
|
portalId,
|
|
233
239
|
region,
|
|
234
|
-
defaultFormId
|
|
240
|
+
defaultFormId,
|
|
241
|
+
submissionMode,
|
|
242
|
+
writeExtraProperties,
|
|
243
|
+
syncFieldsOnPublish
|
|
235
244
|
});
|
|
236
245
|
setApiKey("");
|
|
237
246
|
await load();
|
|
@@ -401,7 +410,71 @@ const HubspotSettings = () => {
|
|
|
401
410
|
sourceHint(settings.formSource)
|
|
402
411
|
]
|
|
403
412
|
}
|
|
404
|
-
)
|
|
413
|
+
),
|
|
414
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", children: t("settings.policy.title", "How submissions reach HubSpot") }),
|
|
415
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
416
|
+
"settings.policy.hint",
|
|
417
|
+
"Pick the policy that matches this portal. Defaults stay safe for every install: conversions when a form is linked, leftover answers still written on the contact, HubSpot forms never mutated unless you ask."
|
|
418
|
+
) }),
|
|
419
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
420
|
+
designSystem.Field.Root,
|
|
421
|
+
{
|
|
422
|
+
name: "submissionMode",
|
|
423
|
+
hint: t(
|
|
424
|
+
"settings.mode.hint",
|
|
425
|
+
"Automatic uses the marketing form when one is linked, otherwise a CRM upsert. CRM-only never counts as a conversion. Forms-only prefers the Forms API and falls back to the CRM if HubSpot refuses the submit."
|
|
426
|
+
),
|
|
427
|
+
children: [
|
|
428
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("settings.mode.label", "Submission path") }),
|
|
429
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
430
|
+
designSystem.SingleSelect,
|
|
431
|
+
{
|
|
432
|
+
value: submissionMode,
|
|
433
|
+
onChange: (value) => {
|
|
434
|
+
const next = String(value);
|
|
435
|
+
if (next === "crm" || next === "forms" || next === "auto") setSubmissionMode(next);
|
|
436
|
+
},
|
|
437
|
+
children: [
|
|
438
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "auto", children: t("settings.mode.auto", "Automatic — form conversion if linked, else CRM") }),
|
|
439
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "forms", children: t("settings.mode.forms", "Marketing form — native HubSpot conversions") }),
|
|
440
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "crm", children: t("settings.mode.crm", "CRM only — upsert the contact, no form conversion") })
|
|
441
|
+
]
|
|
442
|
+
}
|
|
443
|
+
),
|
|
444
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
|
|
445
|
+
]
|
|
446
|
+
}
|
|
447
|
+
),
|
|
448
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
449
|
+
designSystem.Checkbox,
|
|
450
|
+
{
|
|
451
|
+
checked: writeExtraProperties,
|
|
452
|
+
onCheckedChange: (checked) => setWriteExtraProperties(checked === true),
|
|
453
|
+
children: t(
|
|
454
|
+
"settings.extra.label",
|
|
455
|
+
"After a conversion, also write mapped contact fields that the HubSpot form doesn't have"
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
),
|
|
459
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
460
|
+
"settings.extra.hint",
|
|
461
|
+
"The Forms API drops unknown fields (they 400 the submit). With this on, leftovers still land on the contact. Turn it off if HubSpot must only receive what the marketing form declares."
|
|
462
|
+
) }),
|
|
463
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
464
|
+
designSystem.Checkbox,
|
|
465
|
+
{
|
|
466
|
+
checked: syncFieldsOnPublish,
|
|
467
|
+
onCheckedChange: (checked) => setSyncFieldsOnPublish(checked === true),
|
|
468
|
+
children: t(
|
|
469
|
+
"settings.sync.label",
|
|
470
|
+
"On publish, add mapped contact fields that are missing on the HubSpot form"
|
|
471
|
+
)
|
|
472
|
+
}
|
|
473
|
+
),
|
|
474
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
475
|
+
"settings.sync.hint",
|
|
476
|
+
"Strapi-first installs: a field added here appears on the HubSpot form next publish. HubSpot-first installs should leave this off — importing a form already carries its fields. Properties themselves are never created; map to ones that exist in the portal."
|
|
477
|
+
) })
|
|
405
478
|
] }) }),
|
|
406
479
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
407
480
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: save, loading: busy, children: t("settings.save", "Save") }),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
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
|
-
import { Flex, Divider, Typography, Button, Box, Link, Badge, Loader, Field, SingleSelect, SingleSelectOption } from "@strapi/design-system";
|
|
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-7cL9PRh1.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-Dnj14fsp.mjs";
|
|
9
9
|
const AuditSection = ({ configured }) => {
|
|
10
10
|
const intl = useIntl();
|
|
11
11
|
const { formatMessage } = intl;
|
|
@@ -181,6 +181,9 @@ const HubspotSettings = () => {
|
|
|
181
181
|
const [portalId, setPortalId] = React.useState("");
|
|
182
182
|
const [region, setRegion] = React.useState("eu1");
|
|
183
183
|
const [defaultFormId, setDefaultFormId] = React.useState("");
|
|
184
|
+
const [submissionMode, setSubmissionMode] = React.useState("auto");
|
|
185
|
+
const [writeExtraProperties, setWriteExtraProperties] = React.useState(true);
|
|
186
|
+
const [syncFieldsOnPublish, setSyncFieldsOnPublish] = React.useState(false);
|
|
184
187
|
const [forms, setForms] = React.useState([]);
|
|
185
188
|
const [busy, setBusy] = React.useState(false);
|
|
186
189
|
const [feedback, setFeedback] = React.useState(
|
|
@@ -192,6 +195,9 @@ const HubspotSettings = () => {
|
|
|
192
195
|
setPortalId(data.portalId ?? "");
|
|
193
196
|
setRegion(data.region || "eu1");
|
|
194
197
|
setDefaultFormId(data.defaultFormId ?? "");
|
|
198
|
+
setSubmissionMode(data.submissionMode === "crm" || data.submissionMode === "forms" ? data.submissionMode : "auto");
|
|
199
|
+
setWriteExtraProperties(data.writeExtraProperties !== false);
|
|
200
|
+
setSyncFieldsOnPublish(data.syncFieldsOnPublish === true);
|
|
195
201
|
setForms(Array.isArray(data.forms) ? data.forms : []);
|
|
196
202
|
};
|
|
197
203
|
const load = React.useCallback(async () => {
|
|
@@ -211,7 +217,10 @@ const HubspotSettings = () => {
|
|
|
211
217
|
...apiKey.trim() ? { apiKey: apiKey.trim() } : {},
|
|
212
218
|
portalId,
|
|
213
219
|
region,
|
|
214
|
-
defaultFormId
|
|
220
|
+
defaultFormId,
|
|
221
|
+
submissionMode,
|
|
222
|
+
writeExtraProperties,
|
|
223
|
+
syncFieldsOnPublish
|
|
215
224
|
});
|
|
216
225
|
setApiKey("");
|
|
217
226
|
await load();
|
|
@@ -381,7 +390,71 @@ const HubspotSettings = () => {
|
|
|
381
390
|
sourceHint(settings.formSource)
|
|
382
391
|
]
|
|
383
392
|
}
|
|
384
|
-
)
|
|
393
|
+
),
|
|
394
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", children: t("settings.policy.title", "How submissions reach HubSpot") }),
|
|
395
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
396
|
+
"settings.policy.hint",
|
|
397
|
+
"Pick the policy that matches this portal. Defaults stay safe for every install: conversions when a form is linked, leftover answers still written on the contact, HubSpot forms never mutated unless you ask."
|
|
398
|
+
) }),
|
|
399
|
+
/* @__PURE__ */ jsxs(
|
|
400
|
+
Field.Root,
|
|
401
|
+
{
|
|
402
|
+
name: "submissionMode",
|
|
403
|
+
hint: t(
|
|
404
|
+
"settings.mode.hint",
|
|
405
|
+
"Automatic uses the marketing form when one is linked, otherwise a CRM upsert. CRM-only never counts as a conversion. Forms-only prefers the Forms API and falls back to the CRM if HubSpot refuses the submit."
|
|
406
|
+
),
|
|
407
|
+
children: [
|
|
408
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("settings.mode.label", "Submission path") }),
|
|
409
|
+
/* @__PURE__ */ jsxs(
|
|
410
|
+
SingleSelect,
|
|
411
|
+
{
|
|
412
|
+
value: submissionMode,
|
|
413
|
+
onChange: (value) => {
|
|
414
|
+
const next = String(value);
|
|
415
|
+
if (next === "crm" || next === "forms" || next === "auto") setSubmissionMode(next);
|
|
416
|
+
},
|
|
417
|
+
children: [
|
|
418
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "auto", children: t("settings.mode.auto", "Automatic — form conversion if linked, else CRM") }),
|
|
419
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "forms", children: t("settings.mode.forms", "Marketing form — native HubSpot conversions") }),
|
|
420
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "crm", children: t("settings.mode.crm", "CRM only — upsert the contact, no form conversion") })
|
|
421
|
+
]
|
|
422
|
+
}
|
|
423
|
+
),
|
|
424
|
+
/* @__PURE__ */ jsx(Field.Hint, {})
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
),
|
|
428
|
+
/* @__PURE__ */ jsx(
|
|
429
|
+
Checkbox,
|
|
430
|
+
{
|
|
431
|
+
checked: writeExtraProperties,
|
|
432
|
+
onCheckedChange: (checked) => setWriteExtraProperties(checked === true),
|
|
433
|
+
children: t(
|
|
434
|
+
"settings.extra.label",
|
|
435
|
+
"After a conversion, also write mapped contact fields that the HubSpot form doesn't have"
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
),
|
|
439
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
440
|
+
"settings.extra.hint",
|
|
441
|
+
"The Forms API drops unknown fields (they 400 the submit). With this on, leftovers still land on the contact. Turn it off if HubSpot must only receive what the marketing form declares."
|
|
442
|
+
) }),
|
|
443
|
+
/* @__PURE__ */ jsx(
|
|
444
|
+
Checkbox,
|
|
445
|
+
{
|
|
446
|
+
checked: syncFieldsOnPublish,
|
|
447
|
+
onCheckedChange: (checked) => setSyncFieldsOnPublish(checked === true),
|
|
448
|
+
children: t(
|
|
449
|
+
"settings.sync.label",
|
|
450
|
+
"On publish, add mapped contact fields that are missing on the HubSpot form"
|
|
451
|
+
)
|
|
452
|
+
}
|
|
453
|
+
),
|
|
454
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
455
|
+
"settings.sync.hint",
|
|
456
|
+
"Strapi-first installs: a field added here appears on the HubSpot form next publish. HubSpot-first installs should leave this off — importing a form already carries its fields. Properties themselves are never created; map to ones that exist in the portal."
|
|
457
|
+
) })
|
|
385
458
|
] }) }),
|
|
386
459
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
387
460
|
/* @__PURE__ */ jsx(Button, { onClick: save, loading: busy, children: t("settings.save", "Save") }),
|
|
@@ -30,7 +30,18 @@ const en = {
|
|
|
30
30
|
"picker.refresh": "Refresh the properties from HubSpot",
|
|
31
31
|
"picker.import": "Import the {count} options from HubSpot",
|
|
32
32
|
"picker.imported": "Options imported — save to keep them.",
|
|
33
|
-
"settings.subtitle": "Private app token
|
|
33
|
+
"settings.subtitle": "Private app token, HubSpot account, and how submissions reach the CRM. Switch test → production here — nothing is hardcoded.",
|
|
34
|
+
"settings.policy.title": "How submissions reach HubSpot",
|
|
35
|
+
"settings.policy.hint": "Pick the policy that matches this portal. Defaults stay safe for every install: conversions when a form is linked, leftover answers still written on the contact, HubSpot forms never mutated unless you ask.",
|
|
36
|
+
"settings.mode.label": "Submission path",
|
|
37
|
+
"settings.mode.hint": "Automatic uses the marketing form when one is linked, otherwise a CRM upsert. CRM-only never counts as a conversion. Forms-only prefers the Forms API and falls back to the CRM if HubSpot refuses the submit.",
|
|
38
|
+
"settings.mode.auto": "Automatic — form conversion if linked, else CRM",
|
|
39
|
+
"settings.mode.forms": "Marketing form — native HubSpot conversions",
|
|
40
|
+
"settings.mode.crm": "CRM only — upsert the contact, no form conversion",
|
|
41
|
+
"settings.extra.label": "After a conversion, also write mapped contact fields that the HubSpot form doesn't have",
|
|
42
|
+
"settings.extra.hint": "The Forms API drops unknown fields (they 400 the submit). With this on, leftovers still land on the contact. Turn it off if HubSpot must only receive what the marketing form declares.",
|
|
43
|
+
"settings.sync.label": "On publish, add mapped contact fields that are missing on the HubSpot form",
|
|
44
|
+
"settings.sync.hint": "Strapi-first installs: a field added here appears on the HubSpot form next publish. HubSpot-first installs should leave this off — importing a form already carries its fields. Properties themselves are never created; map to ones that exist in the portal.",
|
|
34
45
|
"settings.configured": "Configured {hint}",
|
|
35
46
|
"settings.not-configured": "Not configured",
|
|
36
47
|
"settings.source": "source: {source}",
|
|
@@ -62,7 +73,7 @@ const en = {
|
|
|
62
73
|
"settings.test-ok": "Connection established — {count} readable properties.",
|
|
63
74
|
"settings.test-no-key": "No key configured.",
|
|
64
75
|
"settings.test-error": "HubSpot unreachable — invalid or revoked key?",
|
|
65
|
-
"settings.scopes": "The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list and
|
|
76
|
+
"settings.scopes": "The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list, submit, and optionally update marketing forms). The token is never returned to the browser.",
|
|
66
77
|
"audit.title": "Mapping audit",
|
|
67
78
|
"audit.subtitle": "Scans every entry of the validated content types and lists the mappings the portal would reject today — including properties deleted in HubSpot since they were saved.",
|
|
68
79
|
"audit.run": "Run the audit",
|
|
@@ -122,6 +133,8 @@ const en = {
|
|
|
122
133
|
"editor.invalid": "The structure has problems — see the flagged cards.",
|
|
123
134
|
"editor.publish": "Publish",
|
|
124
135
|
"editor.published": "Form published.",
|
|
136
|
+
"editor.published-synced": "Form published. {count, plural, one {# field added} other {# fields added}} to the HubSpot form.",
|
|
137
|
+
"editor.published-sync-warn": "Form published. HubSpot field sync failed: {error}",
|
|
125
138
|
"editor.publish-blocked": "Publishing is blocked — fix the flagged mappings first.",
|
|
126
139
|
"editor.unpublish": "Unpublish",
|
|
127
140
|
"editor.unpublished": "Form unpublished.",
|
|
@@ -28,7 +28,18 @@ const en = {
|
|
|
28
28
|
"picker.refresh": "Refresh the properties from HubSpot",
|
|
29
29
|
"picker.import": "Import the {count} options from HubSpot",
|
|
30
30
|
"picker.imported": "Options imported — save to keep them.",
|
|
31
|
-
"settings.subtitle": "Private app token
|
|
31
|
+
"settings.subtitle": "Private app token, HubSpot account, and how submissions reach the CRM. Switch test → production here — nothing is hardcoded.",
|
|
32
|
+
"settings.policy.title": "How submissions reach HubSpot",
|
|
33
|
+
"settings.policy.hint": "Pick the policy that matches this portal. Defaults stay safe for every install: conversions when a form is linked, leftover answers still written on the contact, HubSpot forms never mutated unless you ask.",
|
|
34
|
+
"settings.mode.label": "Submission path",
|
|
35
|
+
"settings.mode.hint": "Automatic uses the marketing form when one is linked, otherwise a CRM upsert. CRM-only never counts as a conversion. Forms-only prefers the Forms API and falls back to the CRM if HubSpot refuses the submit.",
|
|
36
|
+
"settings.mode.auto": "Automatic — form conversion if linked, else CRM",
|
|
37
|
+
"settings.mode.forms": "Marketing form — native HubSpot conversions",
|
|
38
|
+
"settings.mode.crm": "CRM only — upsert the contact, no form conversion",
|
|
39
|
+
"settings.extra.label": "After a conversion, also write mapped contact fields that the HubSpot form doesn't have",
|
|
40
|
+
"settings.extra.hint": "The Forms API drops unknown fields (they 400 the submit). With this on, leftovers still land on the contact. Turn it off if HubSpot must only receive what the marketing form declares.",
|
|
41
|
+
"settings.sync.label": "On publish, add mapped contact fields that are missing on the HubSpot form",
|
|
42
|
+
"settings.sync.hint": "Strapi-first installs: a field added here appears on the HubSpot form next publish. HubSpot-first installs should leave this off — importing a form already carries its fields. Properties themselves are never created; map to ones that exist in the portal.",
|
|
32
43
|
"settings.configured": "Configured {hint}",
|
|
33
44
|
"settings.not-configured": "Not configured",
|
|
34
45
|
"settings.source": "source: {source}",
|
|
@@ -60,7 +71,7 @@ const en = {
|
|
|
60
71
|
"settings.test-ok": "Connection established — {count} readable properties.",
|
|
61
72
|
"settings.test-no-key": "No key configured.",
|
|
62
73
|
"settings.test-error": "HubSpot unreachable — invalid or revoked key?",
|
|
63
|
-
"settings.scopes": "The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list and
|
|
74
|
+
"settings.scopes": "The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list, submit, and optionally update marketing forms). The token is never returned to the browser.",
|
|
64
75
|
"audit.title": "Mapping audit",
|
|
65
76
|
"audit.subtitle": "Scans every entry of the validated content types and lists the mappings the portal would reject today — including properties deleted in HubSpot since they were saved.",
|
|
66
77
|
"audit.run": "Run the audit",
|
|
@@ -120,6 +131,8 @@ const en = {
|
|
|
120
131
|
"editor.invalid": "The structure has problems — see the flagged cards.",
|
|
121
132
|
"editor.publish": "Publish",
|
|
122
133
|
"editor.published": "Form published.",
|
|
134
|
+
"editor.published-synced": "Form published. {count, plural, one {# field added} other {# fields added}} to the HubSpot form.",
|
|
135
|
+
"editor.published-sync-warn": "Form published. HubSpot field sync failed: {error}",
|
|
123
136
|
"editor.publish-blocked": "Publishing is blocked — fix the flagged mappings first.",
|
|
124
137
|
"editor.unpublish": "Unpublish",
|
|
125
138
|
"editor.unpublished": "Form unpublished.",
|
|
@@ -28,7 +28,18 @@ const fr = {
|
|
|
28
28
|
"picker.refresh": "Rafraîchir les propriétés depuis HubSpot",
|
|
29
29
|
"picker.import": "Importer les {count} options depuis HubSpot",
|
|
30
30
|
"picker.imported": "Options importées — enregistrez pour les conserver.",
|
|
31
|
-
"settings.subtitle": "Jeton d'application privée
|
|
31
|
+
"settings.subtitle": "Jeton d'application privée, compte HubSpot, et la façon dont les envois atteignent le CRM. Passez du test à la prod ici — rien n'est figé dans le code.",
|
|
32
|
+
"settings.policy.title": "Comment les envois atteignent HubSpot",
|
|
33
|
+
"settings.policy.hint": "Choisissez la politique de ce portail. Les défauts restent sûrs pour toute install : conversion si un formulaire est lié, réponses en trop écrites sur le contact, formulaires HubSpot jamais mutés sauf si vous le demandez.",
|
|
34
|
+
"settings.mode.label": "Chemin d'envoi",
|
|
35
|
+
"settings.mode.hint": "Automatique utilise le formulaire marketing s'il est lié, sinon un upsert CRM. CRM seul ne compte jamais comme conversion. Formulaire marketing privilégie l'API Forms et retombe sur le CRM si HubSpot refuse l'envoi.",
|
|
36
|
+
"settings.mode.auto": "Automatique — conversion si un formulaire est lié, sinon CRM",
|
|
37
|
+
"settings.mode.forms": "Formulaire marketing — conversions HubSpot natives",
|
|
38
|
+
"settings.mode.crm": "CRM uniquement — upsert du contact, pas de conversion",
|
|
39
|
+
"settings.extra.label": "Après une conversion, écrire aussi sur le contact les champs mappés absents du formulaire HubSpot",
|
|
40
|
+
"settings.extra.hint": "L'API Forms écarte les champs inconnus (ils font échouer tout l'envoi). Activé, les champs restants atterrissent quand même sur le contact. Désactivez si HubSpot ne doit recevoir que ce que le formulaire marketing déclare.",
|
|
41
|
+
"settings.sync.label": "À la publication, ajouter au formulaire HubSpot les champs mappés manquants",
|
|
42
|
+
"settings.sync.hint": "Install Strapi-first : un champ ajouté ici apparaît sur le formulaire HubSpot à la publication suivante. Install HubSpot-first : laissez désactivé — l'import porte déjà les champs. Les propriétés CRM ne sont jamais créées ; mappez vers des propriétés déjà présentes dans le portail.",
|
|
32
43
|
"settings.configured": "Configurée {hint}",
|
|
33
44
|
"settings.not-configured": "Non configurée",
|
|
34
45
|
"settings.source": "source : {source}",
|
|
@@ -60,7 +71,7 @@ const fr = {
|
|
|
60
71
|
"settings.test-ok": "Connexion établie — {count} propriétés lisibles.",
|
|
61
72
|
"settings.test-no-key": "Aucune clé configurée.",
|
|
62
73
|
"settings.test-error": "HubSpot injoignable — clé invalide ou révoquée ?",
|
|
63
|
-
"settings.scopes": "Le jeton a besoin de crm.schemas.contacts.read, crm.schemas.companies.read et forms (lister et
|
|
74
|
+
"settings.scopes": "Le jeton a besoin de crm.schemas.contacts.read, crm.schemas.companies.read et forms (lister, soumettre et éventuellement mettre à jour les formulaires marketing). Il n'est jamais renvoyé au navigateur.",
|
|
64
75
|
"audit.title": "Audit des mappings",
|
|
65
76
|
"audit.subtitle": "Parcourt toutes les entrées des content-types validés et liste les mappings que le portail refuserait aujourd'hui — y compris les propriétés supprimées dans HubSpot depuis leur enregistrement.",
|
|
66
77
|
"audit.run": "Lancer l'audit",
|
|
@@ -120,6 +131,8 @@ const fr = {
|
|
|
120
131
|
"editor.invalid": "La structure a des problèmes — voir les cartes signalées.",
|
|
121
132
|
"editor.publish": "Publier",
|
|
122
133
|
"editor.published": "Formulaire publié.",
|
|
134
|
+
"editor.published-synced": "Formulaire publié. {count, plural, one {# champ ajouté} other {# champs ajoutés}} au formulaire HubSpot.",
|
|
135
|
+
"editor.published-sync-warn": "Formulaire publié. La sync des champs HubSpot a échoué : {error}",
|
|
123
136
|
"editor.publish-blocked": "Publication bloquée — corrigez d'abord les mappings signalés.",
|
|
124
137
|
"editor.unpublish": "Dépublier",
|
|
125
138
|
"editor.unpublished": "Formulaire dépublié.",
|