strapi-plugin-hubspot 0.13.1 → 0.15.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 +33 -0
- package/README.md +58 -32
- package/dist/_chunks/{Forms-CHB-sKem.mjs → Forms-B7eIm0Fi.mjs} +38 -3
- package/dist/_chunks/{Forms-lrzuWzoB.js → Forms-ClyI4MJ1.js} +38 -3
- package/dist/_chunks/{HubspotFormInput-Bb6it0nP.js → HubspotFormInput-Do7F83V6.js} +1 -1
- package/dist/_chunks/{HubspotFormInput-YI09keTC.mjs → HubspotFormInput-cZAiaAaq.mjs} +1 -1
- package/dist/_chunks/{HubspotObjectInput-D3A6xzb3.js → HubspotObjectInput-DWqk2C26.js} +3 -3
- package/dist/_chunks/{HubspotObjectInput-CzLKT9oj.mjs → HubspotObjectInput-ZZk_7hWq.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-DicskLcN.mjs → HubspotPropertyInput-5bkDGmGo.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-Dss3eUlL.js → HubspotPropertyInput-v9B3wb1a.js} +3 -3
- package/dist/_chunks/{Settings-4CbX3SPK.mjs → Settings-QcFbm3s5.mjs} +132 -36
- package/dist/_chunks/{Settings-Ch8tPZAV.js → Settings-X3EHnTaO.js} +131 -35
- package/dist/_chunks/{en-D8DJiSxd.mjs → en-DMJeF7EN.mjs} +19 -6
- package/dist/_chunks/{en-PYFibnDc.js → en-ZHvaZLi7.js} +19 -6
- package/dist/_chunks/{fr-BMjixfXn.mjs → fr-CVAntLmc.mjs} +18 -5
- package/dist/_chunks/{fr-yHk4sSEl.js → fr-Ds_2EAzZ.js} +18 -5
- package/dist/_chunks/{index-BE2ribUL.mjs → index-BSJAhj8r.mjs} +6 -6
- package/dist/_chunks/{index-BAvedd2m.js → index-BpcilDy1.js} +6 -6
- package/dist/_chunks/{objectLabels-CycoU0zY.js → objectLabels-CYXXFpNB.js} +1 -1
- package/dist/_chunks/{objectLabels-DGJxYjKz.mjs → objectLabels-CrYRzsqe.mjs} +1 -1
- package/dist/_chunks/{useHubspotSchema-DRfaI6mF.js → useHubspotSchema-Ba6xZUCL.js} +1 -1
- package/dist/_chunks/{useHubspotSchema-e31UsYb2.mjs → useHubspotSchema-CBF4GkmK.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/builder/types.d.ts +2 -0
- package/dist/server/index.js +502 -197
- package/dist/server/index.mjs +502 -197
- package/dist/server/src/__tests__/hsforms.test.d.ts +1 -0
- package/dist/server/src/__tests__/settings.test.d.ts +1 -0
- package/dist/server/src/content-types.d.ts +8 -0
- package/dist/server/src/forms.d.ts +10 -6
- package/dist/server/src/hsforms.d.ts +75 -0
- package/dist/server/src/importHubspot.d.ts +1 -0
- package/dist/server/src/index.d.ts +17 -2
- package/dist/server/src/settings.d.ts +36 -5
- package/package.json +1 -1
|
@@ -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 } from "@strapi/design-system";
|
|
4
|
+
import { Flex, Divider, Typography, Button, Box, Link, Badge, Loader, Field, SingleSelect, SingleSelectOption } 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-BSJAhj8r.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-CrYRzsqe.mjs";
|
|
9
9
|
const AuditSection = ({ configured }) => {
|
|
10
10
|
const intl = useIntl();
|
|
11
11
|
const { formatMessage } = intl;
|
|
@@ -172,19 +172,31 @@ const FailuresSection = ({ configured }) => {
|
|
|
172
172
|
) : null
|
|
173
173
|
] });
|
|
174
174
|
};
|
|
175
|
+
const REGIONS = ["eu1", "na1"];
|
|
175
176
|
const HubspotSettings = () => {
|
|
176
177
|
const { formatMessage } = useIntl();
|
|
177
178
|
const { get, put, del } = useFetchClient();
|
|
178
179
|
const [settings, setSettings] = React.useState(null);
|
|
179
180
|
const [apiKey, setApiKey] = React.useState("");
|
|
181
|
+
const [portalId, setPortalId] = React.useState("");
|
|
182
|
+
const [region, setRegion] = React.useState("eu1");
|
|
183
|
+
const [defaultFormId, setDefaultFormId] = React.useState("");
|
|
184
|
+
const [forms, setForms] = React.useState([]);
|
|
180
185
|
const [busy, setBusy] = React.useState(false);
|
|
181
186
|
const [feedback, setFeedback] = React.useState(
|
|
182
187
|
null
|
|
183
188
|
);
|
|
184
189
|
const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
|
|
190
|
+
const apply = (data) => {
|
|
191
|
+
setSettings(data);
|
|
192
|
+
setPortalId(data.portalId ?? "");
|
|
193
|
+
setRegion(data.region || "eu1");
|
|
194
|
+
setDefaultFormId(data.defaultFormId ?? "");
|
|
195
|
+
setForms(Array.isArray(data.forms) ? data.forms : []);
|
|
196
|
+
};
|
|
185
197
|
const load = React.useCallback(async () => {
|
|
186
198
|
const { data } = await get(`/${PLUGIN_ID}/settings`);
|
|
187
|
-
|
|
199
|
+
apply(data);
|
|
188
200
|
}, [get]);
|
|
189
201
|
React.useEffect(() => {
|
|
190
202
|
load().catch(
|
|
@@ -195,12 +207,17 @@ const HubspotSettings = () => {
|
|
|
195
207
|
setBusy(true);
|
|
196
208
|
setFeedback(null);
|
|
197
209
|
try {
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
await put(`/${PLUGIN_ID}/settings`, {
|
|
211
|
+
...apiKey.trim() ? { apiKey: apiKey.trim() } : {},
|
|
212
|
+
portalId,
|
|
213
|
+
region,
|
|
214
|
+
defaultFormId
|
|
215
|
+
});
|
|
200
216
|
setApiKey("");
|
|
201
|
-
|
|
217
|
+
await load();
|
|
218
|
+
setFeedback({ tone: "success", text: t("settings.saved", "Settings saved.") });
|
|
202
219
|
} catch {
|
|
203
|
-
setFeedback({ tone: "danger", text: t("settings.save-error", "Could not save
|
|
220
|
+
setFeedback({ tone: "danger", text: t("settings.save-error", "Could not save settings.") });
|
|
204
221
|
} finally {
|
|
205
222
|
setBusy(false);
|
|
206
223
|
}
|
|
@@ -209,9 +226,9 @@ const HubspotSettings = () => {
|
|
|
209
226
|
setBusy(true);
|
|
210
227
|
setFeedback(null);
|
|
211
228
|
try {
|
|
212
|
-
|
|
213
|
-
setSettings(data);
|
|
229
|
+
await del(`/${PLUGIN_ID}/settings`);
|
|
214
230
|
setApiKey("");
|
|
231
|
+
await load();
|
|
215
232
|
setFeedback({ tone: "success", text: t("settings.removed", "Key removed.") });
|
|
216
233
|
} catch {
|
|
217
234
|
setFeedback({ tone: "danger", text: t("settings.remove-error", "Could not remove the key.") });
|
|
@@ -234,6 +251,7 @@ const HubspotSettings = () => {
|
|
|
234
251
|
})
|
|
235
252
|
} : { tone: "danger", text: t("settings.test-no-key", "No key configured.") }
|
|
236
253
|
);
|
|
254
|
+
if (data.configured) await load();
|
|
237
255
|
} catch {
|
|
238
256
|
setFeedback({
|
|
239
257
|
tone: "danger",
|
|
@@ -251,44 +269,122 @@ const HubspotSettings = () => {
|
|
|
251
269
|
config: t("settings.source.config", "config/plugins.ts"),
|
|
252
270
|
env: t("settings.source.env", "environment variable")
|
|
253
271
|
};
|
|
272
|
+
const sourceHint = (source) => source && source !== "settings" ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t("settings.source", "source: {source}", { source: sourceLabel[source] }) }) : null;
|
|
273
|
+
const regionOptions = REGIONS.includes(region) ? [...REGIONS] : [region, ...REGIONS];
|
|
254
274
|
return /* @__PURE__ */ jsx(Box, { padding: 8, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
|
|
255
275
|
/* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
|
|
256
276
|
/* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "HubSpot" }),
|
|
257
277
|
/* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: t(
|
|
258
278
|
"settings.subtitle",
|
|
259
|
-
"
|
|
279
|
+
"Private app token and the HubSpot account that receives form conversions. Switch test → production here — nothing is hardcoded."
|
|
260
280
|
) })
|
|
261
281
|
] }),
|
|
262
282
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
263
283
|
/* @__PURE__ */ jsx(Badge, { active: settings.configured, children: settings.configured ? t("settings.configured", "Configured {hint}", { hint: settings.hint }) : t("settings.not-configured", "Not configured") }),
|
|
264
284
|
settings.keySource ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t("settings.source", "source: {source}", { source: sourceLabel[settings.keySource] }) }) : null
|
|
265
285
|
] }),
|
|
266
|
-
/* @__PURE__ */ jsx(Box, { maxWidth: "32rem", children: /* @__PURE__ */ jsxs(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
286
|
+
/* @__PURE__ */ jsx(Box, { maxWidth: "32rem", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
287
|
+
/* @__PURE__ */ jsxs(
|
|
288
|
+
Field.Root,
|
|
289
|
+
{
|
|
290
|
+
name: "apiKey",
|
|
291
|
+
hint: t(
|
|
292
|
+
"settings.key.hint",
|
|
293
|
+
"A key entered here overrides config/plugins.ts and HUBSPOT_API_KEY."
|
|
294
|
+
),
|
|
295
|
+
children: [
|
|
296
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("settings.key.label", "Private app token") }),
|
|
297
|
+
/* @__PURE__ */ jsx(
|
|
298
|
+
Field.Input,
|
|
299
|
+
{
|
|
300
|
+
type: "password",
|
|
301
|
+
value: apiKey,
|
|
302
|
+
autoComplete: "off",
|
|
303
|
+
placeholder: settings.configured ? t("settings.key.placeholder.set", "•••••••• (leave empty to keep)") : t("settings.key.placeholder.empty", "pat-eu1-…"),
|
|
304
|
+
onChange: (e) => setApiKey(e.target.value)
|
|
305
|
+
}
|
|
306
|
+
),
|
|
307
|
+
/* @__PURE__ */ jsx(Field.Hint, {})
|
|
308
|
+
]
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
/* @__PURE__ */ jsxs(
|
|
312
|
+
Field.Root,
|
|
313
|
+
{
|
|
314
|
+
name: "portalId",
|
|
315
|
+
hint: t(
|
|
316
|
+
"settings.portal.hint",
|
|
317
|
+
"HubSpot account id (Settings → Account → Account setup). Test portal today, production later."
|
|
318
|
+
),
|
|
319
|
+
children: [
|
|
320
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("settings.portal.label", "Portal ID") }),
|
|
321
|
+
/* @__PURE__ */ jsx(
|
|
322
|
+
Field.Input,
|
|
323
|
+
{
|
|
324
|
+
type: "text",
|
|
325
|
+
value: portalId,
|
|
326
|
+
autoComplete: "off",
|
|
327
|
+
placeholder: "148991818",
|
|
328
|
+
onChange: (e) => setPortalId(e.target.value)
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
/* @__PURE__ */ jsx(Field.Hint, {}),
|
|
332
|
+
sourceHint(settings.portalSource)
|
|
333
|
+
]
|
|
334
|
+
}
|
|
335
|
+
),
|
|
336
|
+
/* @__PURE__ */ jsxs(Field.Root, { name: "region", children: [
|
|
337
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("settings.region.label", "Region") }),
|
|
338
|
+
/* @__PURE__ */ jsx(
|
|
339
|
+
SingleSelect,
|
|
340
|
+
{
|
|
341
|
+
value: region,
|
|
342
|
+
onChange: (value) => setRegion(String(value)),
|
|
343
|
+
children: regionOptions.map((r) => /* @__PURE__ */ jsx(SingleSelectOption, { value: r, children: r === "eu1" ? t("settings.region.eu1", "Europe (eu1)") : r === "na1" ? t("settings.region.na1", "North America (na1)") : r }, r))
|
|
344
|
+
}
|
|
273
345
|
),
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
onChange: (e) => setApiKey(e.target.value)
|
|
284
|
-
}
|
|
346
|
+
sourceHint(settings.regionSource)
|
|
347
|
+
] }),
|
|
348
|
+
/* @__PURE__ */ jsxs(
|
|
349
|
+
Field.Root,
|
|
350
|
+
{
|
|
351
|
+
name: "defaultFormId",
|
|
352
|
+
hint: t(
|
|
353
|
+
"settings.form.hint",
|
|
354
|
+
"Marketing form that receives submissions when a builder form doesn't pick its own. Create the form in HubSpot, then pick it here."
|
|
285
355
|
),
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
356
|
+
children: [
|
|
357
|
+
/* @__PURE__ */ jsx(Field.Label, { children: t("settings.form.label", "Default marketing form") }),
|
|
358
|
+
forms.length ? /* @__PURE__ */ jsxs(
|
|
359
|
+
SingleSelect,
|
|
360
|
+
{
|
|
361
|
+
value: defaultFormId,
|
|
362
|
+
placeholder: t("settings.form.placeholder", "Pick a form of the connected portal"),
|
|
363
|
+
onChange: (value) => setDefaultFormId(String(value)),
|
|
364
|
+
children: [
|
|
365
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "", children: t("settings.form.none", "None — CRM upsert only") }),
|
|
366
|
+
defaultFormId && !forms.some((f) => f.id === defaultFormId) && /* @__PURE__ */ jsx(SingleSelectOption, { value: defaultFormId, children: defaultFormId }),
|
|
367
|
+
forms.map((f) => /* @__PURE__ */ jsx(SingleSelectOption, { value: f.id, children: f.name }, f.id))
|
|
368
|
+
]
|
|
369
|
+
}
|
|
370
|
+
) : /* @__PURE__ */ jsx(
|
|
371
|
+
Field.Input,
|
|
372
|
+
{
|
|
373
|
+
type: "text",
|
|
374
|
+
value: defaultFormId,
|
|
375
|
+
autoComplete: "off",
|
|
376
|
+
placeholder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
377
|
+
onChange: (e) => setDefaultFormId(e.target.value.trim())
|
|
378
|
+
}
|
|
379
|
+
),
|
|
380
|
+
/* @__PURE__ */ jsx(Field.Hint, {}),
|
|
381
|
+
sourceHint(settings.formSource)
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
] }) }),
|
|
290
386
|
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
291
|
-
/* @__PURE__ */ jsx(Button, { onClick: save, loading: busy,
|
|
387
|
+
/* @__PURE__ */ jsx(Button, { onClick: save, loading: busy, children: t("settings.save", "Save") }),
|
|
292
388
|
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: test, loading: busy, disabled: !settings.configured, children: t("settings.test", "Test connection") }),
|
|
293
389
|
/* @__PURE__ */ jsx(
|
|
294
390
|
Button,
|
|
@@ -297,7 +393,7 @@ const HubspotSettings = () => {
|
|
|
297
393
|
onClick: remove,
|
|
298
394
|
loading: busy,
|
|
299
395
|
disabled: settings.keySource !== "settings",
|
|
300
|
-
children: t("settings.remove", "Remove")
|
|
396
|
+
children: t("settings.remove", "Remove key")
|
|
301
397
|
}
|
|
302
398
|
)
|
|
303
399
|
] }),
|
|
@@ -311,7 +407,7 @@ const HubspotSettings = () => {
|
|
|
311
407
|
) : null,
|
|
312
408
|
/* @__PURE__ */ jsx(Box, { paddingTop: 4, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
313
409
|
"settings.scopes",
|
|
314
|
-
"The token needs
|
|
410
|
+
"The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list and submit marketing forms). The token is never returned to the browser."
|
|
315
411
|
) }) }),
|
|
316
412
|
/* @__PURE__ */ jsx(AuditSection, { configured: settings.configured }),
|
|
317
413
|
/* @__PURE__ */ jsx(FailuresSection, { configured: settings.configured })
|
|
@@ -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-BpcilDy1.js");
|
|
9
9
|
const reactRouterDom = require("react-router-dom");
|
|
10
|
-
const objectLabels = require("./objectLabels-
|
|
10
|
+
const objectLabels = require("./objectLabels-CYXXFpNB.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" } });
|
|
@@ -192,19 +192,31 @@ const FailuresSection = ({ configured }) => {
|
|
|
192
192
|
) : null
|
|
193
193
|
] });
|
|
194
194
|
};
|
|
195
|
+
const REGIONS = ["eu1", "na1"];
|
|
195
196
|
const HubspotSettings = () => {
|
|
196
197
|
const { formatMessage } = index.useIntl();
|
|
197
198
|
const { get, put, del } = admin.useFetchClient();
|
|
198
199
|
const [settings, setSettings] = React__namespace.useState(null);
|
|
199
200
|
const [apiKey, setApiKey] = React__namespace.useState("");
|
|
201
|
+
const [portalId, setPortalId] = React__namespace.useState("");
|
|
202
|
+
const [region, setRegion] = React__namespace.useState("eu1");
|
|
203
|
+
const [defaultFormId, setDefaultFormId] = React__namespace.useState("");
|
|
204
|
+
const [forms, setForms] = React__namespace.useState([]);
|
|
200
205
|
const [busy, setBusy] = React__namespace.useState(false);
|
|
201
206
|
const [feedback, setFeedback] = React__namespace.useState(
|
|
202
207
|
null
|
|
203
208
|
);
|
|
204
209
|
const t = (id, defaultMessage, values) => formatMessage({ id: index$1.getTranslation(id), defaultMessage }, values);
|
|
210
|
+
const apply = (data) => {
|
|
211
|
+
setSettings(data);
|
|
212
|
+
setPortalId(data.portalId ?? "");
|
|
213
|
+
setRegion(data.region || "eu1");
|
|
214
|
+
setDefaultFormId(data.defaultFormId ?? "");
|
|
215
|
+
setForms(Array.isArray(data.forms) ? data.forms : []);
|
|
216
|
+
};
|
|
205
217
|
const load = React__namespace.useCallback(async () => {
|
|
206
218
|
const { data } = await get(`/${index$1.PLUGIN_ID}/settings`);
|
|
207
|
-
|
|
219
|
+
apply(data);
|
|
208
220
|
}, [get]);
|
|
209
221
|
React__namespace.useEffect(() => {
|
|
210
222
|
load().catch(
|
|
@@ -215,12 +227,17 @@ const HubspotSettings = () => {
|
|
|
215
227
|
setBusy(true);
|
|
216
228
|
setFeedback(null);
|
|
217
229
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
230
|
+
await put(`/${index$1.PLUGIN_ID}/settings`, {
|
|
231
|
+
...apiKey.trim() ? { apiKey: apiKey.trim() } : {},
|
|
232
|
+
portalId,
|
|
233
|
+
region,
|
|
234
|
+
defaultFormId
|
|
235
|
+
});
|
|
220
236
|
setApiKey("");
|
|
221
|
-
|
|
237
|
+
await load();
|
|
238
|
+
setFeedback({ tone: "success", text: t("settings.saved", "Settings saved.") });
|
|
222
239
|
} catch {
|
|
223
|
-
setFeedback({ tone: "danger", text: t("settings.save-error", "Could not save
|
|
240
|
+
setFeedback({ tone: "danger", text: t("settings.save-error", "Could not save settings.") });
|
|
224
241
|
} finally {
|
|
225
242
|
setBusy(false);
|
|
226
243
|
}
|
|
@@ -229,9 +246,9 @@ const HubspotSettings = () => {
|
|
|
229
246
|
setBusy(true);
|
|
230
247
|
setFeedback(null);
|
|
231
248
|
try {
|
|
232
|
-
|
|
233
|
-
setSettings(data);
|
|
249
|
+
await del(`/${index$1.PLUGIN_ID}/settings`);
|
|
234
250
|
setApiKey("");
|
|
251
|
+
await load();
|
|
235
252
|
setFeedback({ tone: "success", text: t("settings.removed", "Key removed.") });
|
|
236
253
|
} catch {
|
|
237
254
|
setFeedback({ tone: "danger", text: t("settings.remove-error", "Could not remove the key.") });
|
|
@@ -254,6 +271,7 @@ const HubspotSettings = () => {
|
|
|
254
271
|
})
|
|
255
272
|
} : { tone: "danger", text: t("settings.test-no-key", "No key configured.") }
|
|
256
273
|
);
|
|
274
|
+
if (data.configured) await load();
|
|
257
275
|
} catch {
|
|
258
276
|
setFeedback({
|
|
259
277
|
tone: "danger",
|
|
@@ -271,44 +289,122 @@ const HubspotSettings = () => {
|
|
|
271
289
|
config: t("settings.source.config", "config/plugins.ts"),
|
|
272
290
|
env: t("settings.source.env", "environment variable")
|
|
273
291
|
};
|
|
292
|
+
const sourceHint = (source) => source && source !== "settings" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t("settings.source", "source: {source}", { source: sourceLabel[source] }) }) : null;
|
|
293
|
+
const regionOptions = REGIONS.includes(region) ? [...REGIONS] : [region, ...REGIONS];
|
|
274
294
|
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 8, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
|
|
275
295
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
|
|
276
296
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", children: "HubSpot" }),
|
|
277
297
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: t(
|
|
278
298
|
"settings.subtitle",
|
|
279
|
-
"
|
|
299
|
+
"Private app token and the HubSpot account that receives form conversions. Switch test → production here — nothing is hardcoded."
|
|
280
300
|
) })
|
|
281
301
|
] }),
|
|
282
302
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
283
303
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { active: settings.configured, children: settings.configured ? t("settings.configured", "Configured {hint}", { hint: settings.hint }) : t("settings.not-configured", "Not configured") }),
|
|
284
304
|
settings.keySource ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t("settings.source", "source: {source}", { source: sourceLabel[settings.keySource] }) }) : null
|
|
285
305
|
] }),
|
|
286
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { maxWidth: "32rem", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
306
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { maxWidth: "32rem", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
307
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
308
|
+
designSystem.Field.Root,
|
|
309
|
+
{
|
|
310
|
+
name: "apiKey",
|
|
311
|
+
hint: t(
|
|
312
|
+
"settings.key.hint",
|
|
313
|
+
"A key entered here overrides config/plugins.ts and HUBSPOT_API_KEY."
|
|
314
|
+
),
|
|
315
|
+
children: [
|
|
316
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("settings.key.label", "Private app token") }),
|
|
317
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
318
|
+
designSystem.Field.Input,
|
|
319
|
+
{
|
|
320
|
+
type: "password",
|
|
321
|
+
value: apiKey,
|
|
322
|
+
autoComplete: "off",
|
|
323
|
+
placeholder: settings.configured ? t("settings.key.placeholder.set", "•••••••• (leave empty to keep)") : t("settings.key.placeholder.empty", "pat-eu1-…"),
|
|
324
|
+
onChange: (e) => setApiKey(e.target.value)
|
|
325
|
+
}
|
|
326
|
+
),
|
|
327
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
332
|
+
designSystem.Field.Root,
|
|
333
|
+
{
|
|
334
|
+
name: "portalId",
|
|
335
|
+
hint: t(
|
|
336
|
+
"settings.portal.hint",
|
|
337
|
+
"HubSpot account id (Settings → Account → Account setup). Test portal today, production later."
|
|
338
|
+
),
|
|
339
|
+
children: [
|
|
340
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("settings.portal.label", "Portal ID") }),
|
|
341
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
342
|
+
designSystem.Field.Input,
|
|
343
|
+
{
|
|
344
|
+
type: "text",
|
|
345
|
+
value: portalId,
|
|
346
|
+
autoComplete: "off",
|
|
347
|
+
placeholder: "148991818",
|
|
348
|
+
onChange: (e) => setPortalId(e.target.value)
|
|
349
|
+
}
|
|
350
|
+
),
|
|
351
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
|
|
352
|
+
sourceHint(settings.portalSource)
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
),
|
|
356
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name: "region", children: [
|
|
357
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("settings.region.label", "Region") }),
|
|
358
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
359
|
+
designSystem.SingleSelect,
|
|
360
|
+
{
|
|
361
|
+
value: region,
|
|
362
|
+
onChange: (value) => setRegion(String(value)),
|
|
363
|
+
children: regionOptions.map((r) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: r, children: r === "eu1" ? t("settings.region.eu1", "Europe (eu1)") : r === "na1" ? t("settings.region.na1", "North America (na1)") : r }, r))
|
|
364
|
+
}
|
|
293
365
|
),
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
onChange: (e) => setApiKey(e.target.value)
|
|
304
|
-
}
|
|
366
|
+
sourceHint(settings.regionSource)
|
|
367
|
+
] }),
|
|
368
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
369
|
+
designSystem.Field.Root,
|
|
370
|
+
{
|
|
371
|
+
name: "defaultFormId",
|
|
372
|
+
hint: t(
|
|
373
|
+
"settings.form.hint",
|
|
374
|
+
"Marketing form that receives submissions when a builder form doesn't pick its own. Create the form in HubSpot, then pick it here."
|
|
305
375
|
),
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
376
|
+
children: [
|
|
377
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("settings.form.label", "Default marketing form") }),
|
|
378
|
+
forms.length ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
379
|
+
designSystem.SingleSelect,
|
|
380
|
+
{
|
|
381
|
+
value: defaultFormId,
|
|
382
|
+
placeholder: t("settings.form.placeholder", "Pick a form of the connected portal"),
|
|
383
|
+
onChange: (value) => setDefaultFormId(String(value)),
|
|
384
|
+
children: [
|
|
385
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "", children: t("settings.form.none", "None — CRM upsert only") }),
|
|
386
|
+
defaultFormId && !forms.some((f) => f.id === defaultFormId) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: defaultFormId, children: defaultFormId }),
|
|
387
|
+
forms.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: f.id, children: f.name }, f.id))
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
391
|
+
designSystem.Field.Input,
|
|
392
|
+
{
|
|
393
|
+
type: "text",
|
|
394
|
+
value: defaultFormId,
|
|
395
|
+
autoComplete: "off",
|
|
396
|
+
placeholder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
397
|
+
onChange: (e) => setDefaultFormId(e.target.value.trim())
|
|
398
|
+
}
|
|
399
|
+
),
|
|
400
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
|
|
401
|
+
sourceHint(settings.formSource)
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
)
|
|
405
|
+
] }) }),
|
|
310
406
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
311
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: save, loading: busy,
|
|
407
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: save, loading: busy, children: t("settings.save", "Save") }),
|
|
312
408
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: test, loading: busy, disabled: !settings.configured, children: t("settings.test", "Test connection") }),
|
|
313
409
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
314
410
|
designSystem.Button,
|
|
@@ -317,7 +413,7 @@ const HubspotSettings = () => {
|
|
|
317
413
|
onClick: remove,
|
|
318
414
|
loading: busy,
|
|
319
415
|
disabled: settings.keySource !== "settings",
|
|
320
|
-
children: t("settings.remove", "Remove")
|
|
416
|
+
children: t("settings.remove", "Remove key")
|
|
321
417
|
}
|
|
322
418
|
)
|
|
323
419
|
] }),
|
|
@@ -331,7 +427,7 @@ const HubspotSettings = () => {
|
|
|
331
427
|
) : null,
|
|
332
428
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
|
|
333
429
|
"settings.scopes",
|
|
334
|
-
"The token needs
|
|
430
|
+
"The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list and submit marketing forms). The token is never returned to the browser."
|
|
335
431
|
) }) }),
|
|
336
432
|
/* @__PURE__ */ jsxRuntime.jsx(AuditSection, { configured: settings.configured }),
|
|
337
433
|
/* @__PURE__ */ jsxRuntime.jsx(FailuresSection, { configured: settings.configured })
|
|
@@ -28,7 +28,7 @@ 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": "
|
|
31
|
+
"settings.subtitle": "Private app token and the HubSpot account that receives form conversions. Switch test → production here — nothing is hardcoded.",
|
|
32
32
|
"settings.configured": "Configured {hint}",
|
|
33
33
|
"settings.not-configured": "Not configured",
|
|
34
34
|
"settings.source": "source: {source}",
|
|
@@ -36,22 +36,31 @@ const en = {
|
|
|
36
36
|
"settings.source.config": "config/plugins.ts",
|
|
37
37
|
"settings.source.env": "environment variable",
|
|
38
38
|
"settings.key.label": "Private app token",
|
|
39
|
-
"settings.key.hint": "A key entered here overrides
|
|
39
|
+
"settings.key.hint": "A key entered here overrides config/plugins.ts and HUBSPOT_API_KEY.",
|
|
40
40
|
"settings.key.placeholder.set": "•••••••• (leave empty to keep)",
|
|
41
41
|
"settings.key.placeholder.empty": "pat-eu1-…",
|
|
42
|
+
"settings.portal.label": "Portal ID",
|
|
43
|
+
"settings.portal.hint": "HubSpot account id (Settings → Account → Account setup). Test portal today, production later.",
|
|
44
|
+
"settings.region.label": "Region",
|
|
45
|
+
"settings.region.eu1": "Europe (eu1)",
|
|
46
|
+
"settings.region.na1": "North America (na1)",
|
|
47
|
+
"settings.form.label": "Default marketing form",
|
|
48
|
+
"settings.form.hint": "Marketing form that receives submissions when a builder form doesn't pick its own. Create the form in HubSpot, then pick it here.",
|
|
49
|
+
"settings.form.placeholder": "Pick a form of the connected portal",
|
|
50
|
+
"settings.form.none": "None — CRM upsert only",
|
|
42
51
|
"settings.save": "Save",
|
|
43
52
|
"settings.test": "Test connection",
|
|
44
|
-
"settings.remove": "Remove",
|
|
53
|
+
"settings.remove": "Remove key",
|
|
45
54
|
"settings.loading": "Loading…",
|
|
46
55
|
"settings.load-error": "Could not load settings.",
|
|
47
|
-
"settings.saved": "
|
|
48
|
-
"settings.save-error": "Could not save
|
|
56
|
+
"settings.saved": "Settings saved.",
|
|
57
|
+
"settings.save-error": "Could not save settings.",
|
|
49
58
|
"settings.removed": "Key removed.",
|
|
50
59
|
"settings.remove-error": "Could not remove the key.",
|
|
51
60
|
"settings.test-ok": "Connection established — {count} readable properties.",
|
|
52
61
|
"settings.test-no-key": "No key configured.",
|
|
53
62
|
"settings.test-error": "HubSpot unreachable — invalid or revoked key?",
|
|
54
|
-
"settings.scopes": "The token needs
|
|
63
|
+
"settings.scopes": "The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list and submit marketing forms). The token is never returned to the browser.",
|
|
55
64
|
"audit.title": "Mapping audit",
|
|
56
65
|
"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.",
|
|
57
66
|
"audit.run": "Run the audit",
|
|
@@ -139,6 +148,10 @@ const en = {
|
|
|
139
148
|
"editor.form-next": "Next label",
|
|
140
149
|
"editor.form-submit": "Submit label",
|
|
141
150
|
"editor.form-success": "Success message",
|
|
151
|
+
"editor.hubspot-form": "HubSpot marketing form",
|
|
152
|
+
"editor.hubspot-form-placeholder": "Pick a form of the connected portal",
|
|
153
|
+
"editor.hubspot-form-none": "None — CRM upsert only",
|
|
154
|
+
"editor.hubspot-form-hint": "Submissions go through HubSpot’s Forms API so they count as conversions. Switch the connected portal (test → production) and pick the matching form — nothing is hardcoded.",
|
|
142
155
|
"editor.form-hint": "Select a step or a field to edit it. The public endpoint serves this form at /api/hubspot/forms/{slug}.",
|
|
143
156
|
"panel.label": "Label",
|
|
144
157
|
"panel.name": "Payload key",
|
|
@@ -30,7 +30,7 @@ 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": "
|
|
33
|
+
"settings.subtitle": "Private app token and the HubSpot account that receives form conversions. Switch test → production here — nothing is hardcoded.",
|
|
34
34
|
"settings.configured": "Configured {hint}",
|
|
35
35
|
"settings.not-configured": "Not configured",
|
|
36
36
|
"settings.source": "source: {source}",
|
|
@@ -38,22 +38,31 @@ const en = {
|
|
|
38
38
|
"settings.source.config": "config/plugins.ts",
|
|
39
39
|
"settings.source.env": "environment variable",
|
|
40
40
|
"settings.key.label": "Private app token",
|
|
41
|
-
"settings.key.hint": "A key entered here overrides
|
|
41
|
+
"settings.key.hint": "A key entered here overrides config/plugins.ts and HUBSPOT_API_KEY.",
|
|
42
42
|
"settings.key.placeholder.set": "•••••••• (leave empty to keep)",
|
|
43
43
|
"settings.key.placeholder.empty": "pat-eu1-…",
|
|
44
|
+
"settings.portal.label": "Portal ID",
|
|
45
|
+
"settings.portal.hint": "HubSpot account id (Settings → Account → Account setup). Test portal today, production later.",
|
|
46
|
+
"settings.region.label": "Region",
|
|
47
|
+
"settings.region.eu1": "Europe (eu1)",
|
|
48
|
+
"settings.region.na1": "North America (na1)",
|
|
49
|
+
"settings.form.label": "Default marketing form",
|
|
50
|
+
"settings.form.hint": "Marketing form that receives submissions when a builder form doesn't pick its own. Create the form in HubSpot, then pick it here.",
|
|
51
|
+
"settings.form.placeholder": "Pick a form of the connected portal",
|
|
52
|
+
"settings.form.none": "None — CRM upsert only",
|
|
44
53
|
"settings.save": "Save",
|
|
45
54
|
"settings.test": "Test connection",
|
|
46
|
-
"settings.remove": "Remove",
|
|
55
|
+
"settings.remove": "Remove key",
|
|
47
56
|
"settings.loading": "Loading…",
|
|
48
57
|
"settings.load-error": "Could not load settings.",
|
|
49
|
-
"settings.saved": "
|
|
50
|
-
"settings.save-error": "Could not save
|
|
58
|
+
"settings.saved": "Settings saved.",
|
|
59
|
+
"settings.save-error": "Could not save settings.",
|
|
51
60
|
"settings.removed": "Key removed.",
|
|
52
61
|
"settings.remove-error": "Could not remove the key.",
|
|
53
62
|
"settings.test-ok": "Connection established — {count} readable properties.",
|
|
54
63
|
"settings.test-no-key": "No key configured.",
|
|
55
64
|
"settings.test-error": "HubSpot unreachable — invalid or revoked key?",
|
|
56
|
-
"settings.scopes": "The token needs
|
|
65
|
+
"settings.scopes": "The token needs crm.schemas.contacts.read, crm.schemas.companies.read and forms (to list and submit marketing forms). The token is never returned to the browser.",
|
|
57
66
|
"audit.title": "Mapping audit",
|
|
58
67
|
"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.",
|
|
59
68
|
"audit.run": "Run the audit",
|
|
@@ -141,6 +150,10 @@ const en = {
|
|
|
141
150
|
"editor.form-next": "Next label",
|
|
142
151
|
"editor.form-submit": "Submit label",
|
|
143
152
|
"editor.form-success": "Success message",
|
|
153
|
+
"editor.hubspot-form": "HubSpot marketing form",
|
|
154
|
+
"editor.hubspot-form-placeholder": "Pick a form of the connected portal",
|
|
155
|
+
"editor.hubspot-form-none": "None — CRM upsert only",
|
|
156
|
+
"editor.hubspot-form-hint": "Submissions go through HubSpot’s Forms API so they count as conversions. Switch the connected portal (test → production) and pick the matching form — nothing is hardcoded.",
|
|
144
157
|
"editor.form-hint": "Select a step or a field to edit it. The public endpoint serves this form at /api/hubspot/forms/{slug}.",
|
|
145
158
|
"panel.label": "Label",
|
|
146
159
|
"panel.name": "Payload key",
|