strapi-plugin-hubspot 0.4.2 → 0.6.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +116 -4
  3. package/dist/_chunks/Forms-BF4_zA-m.js +1007 -0
  4. package/dist/_chunks/Forms-CW1KFO9U.mjs +989 -0
  5. package/dist/_chunks/{HubspotObjectInput-DutelVp6.mjs → HubspotObjectInput-CpxeM2J0.mjs} +4 -16
  6. package/dist/_chunks/{HubspotObjectInput-Br2jTFkG.js → HubspotObjectInput-hjAYWtaU.js} +4 -16
  7. package/dist/_chunks/HubspotPropertyInput-C-52Lu24.mjs +227 -0
  8. package/dist/_chunks/HubspotPropertyInput-OSeIBaRC.js +245 -0
  9. package/dist/_chunks/Settings-2V7TH5CX.mjs +321 -0
  10. package/dist/_chunks/Settings-DcA9M5Tw.js +339 -0
  11. package/dist/_chunks/en-CJ9wYHgN.mjs +197 -0
  12. package/dist/_chunks/en-C_-N8cjv.js +197 -0
  13. package/dist/_chunks/fr-BHObPoUp.js +197 -0
  14. package/dist/_chunks/fr-DttDn-2Y.mjs +197 -0
  15. package/dist/_chunks/{Settings-CQsB0B2T.mjs → index-C1UJXE_o.mjs} +576 -317
  16. package/dist/_chunks/{index-BUlB1rK9.js → index-CMzg-x5i.js} +12 -4
  17. package/dist/_chunks/{Settings-CvbKVl-t.js → index-Dgbid2LK.js} +580 -322
  18. package/dist/_chunks/{index-CKoBcX1T.mjs → index-DkGOTp8p.mjs} +12 -4
  19. package/dist/_chunks/{objectLabels-dM4o67Fz.mjs → objectLabels-D9UEuOGr.mjs} +1 -1
  20. package/dist/_chunks/{objectLabels-B76t8qqg.js → objectLabels-Th7aYW-B.js} +1 -1
  21. package/dist/_chunks/useHubspotSchema-Bgrgg8Mt.js +75 -0
  22. package/dist/_chunks/useHubspotSchema-DQkQcA_V.mjs +58 -0
  23. package/dist/admin/index.js +2 -1
  24. package/dist/admin/index.mjs +2 -1
  25. package/dist/admin/src/builder/ConditionEditor.d.ts +17 -0
  26. package/dist/admin/src/builder/FieldPanel.d.ts +16 -0
  27. package/dist/admin/src/builder/types.d.ts +90 -0
  28. package/dist/admin/src/index.d.ts +1 -0
  29. package/dist/admin/src/pages/FormEditor.d.ts +9 -0
  30. package/dist/admin/src/pages/Forms.d.ts +4 -0
  31. package/dist/admin/src/pages/FormsList.d.ts +8 -0
  32. package/dist/admin/src/useHubspotSchema.d.ts +28 -0
  33. package/dist/server/index.js +880 -5
  34. package/dist/server/index.mjs +880 -5
  35. package/dist/server/src/__tests__/conditions.test.d.ts +1 -0
  36. package/dist/server/src/__tests__/forms.test.d.ts +1 -0
  37. package/dist/server/src/__tests__/formsSubmit.test.d.ts +1 -0
  38. package/dist/server/src/__tests__/importLegacy.test.d.ts +1 -0
  39. package/dist/server/src/conditions.d.ts +88 -0
  40. package/dist/server/src/content-types.d.ts +161 -9
  41. package/dist/server/src/forms.d.ts +81 -0
  42. package/dist/server/src/formsAdmin.d.ts +39 -0
  43. package/dist/server/src/importLegacy.d.ts +25 -0
  44. package/dist/server/src/index.d.ts +214 -16
  45. package/dist/shared/types.d.ts +103 -0
  46. package/dist/shared/types.js +2 -0
  47. package/dist/shared/types.mjs +2 -0
  48. package/package.json +23 -6
  49. package/dist/_chunks/HubspotPropertyInput-CK6Yj9rC.mjs +0 -177
  50. package/dist/_chunks/HubspotPropertyInput-DIPDPRXJ.js +0 -195
  51. package/dist/_chunks/en-D4ueeUcJ.mjs +0 -82
  52. package/dist/_chunks/en-PBG7-vMR.js +0 -82
  53. package/dist/_chunks/fr-DL4wdV8I.js +0 -82
  54. package/dist/_chunks/fr-DYnxVmIO.mjs +0 -82
@@ -0,0 +1,321 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { u as useIntl, o as objectLabel } from "./objectLabels-D9UEuOGr.mjs";
4
+ import { Flex, Divider, Typography, Button, Box, Link, Badge, Loader, Field } from "@strapi/design-system";
5
+ import { useFetchClient } from "@strapi/strapi/admin";
6
+ import { g as getTranslation, P as PLUGIN_ID } from "./index-DkGOTp8p.mjs";
7
+ import { L as Link$1 } from "./index-C1UJXE_o.mjs";
8
+ const AuditSection = ({ configured }) => {
9
+ const intl = useIntl();
10
+ const { formatMessage } = intl;
11
+ const { get } = useFetchClient();
12
+ const [busy, setBusy] = React.useState(false);
13
+ const [error, setError] = React.useState(false);
14
+ const [result, setResult] = React.useState(null);
15
+ const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
16
+ const problemText = (p) => {
17
+ switch (p.code) {
18
+ case "unknown":
19
+ return t("problem.unknown", "“{property}” does not exist on {object} in this portal", {
20
+ property: p.property,
21
+ object: objectLabel(intl, p.object)
22
+ });
23
+ case "wrong-object":
24
+ return t("problem.wrong-object", "“{property}” exists on {actual}, not on {object}", {
25
+ property: p.property,
26
+ object: objectLabel(intl, p.object),
27
+ actual: objectLabel(intl, p.actualObject)
28
+ });
29
+ case "whitespace":
30
+ return t("problem.whitespace", "“{property}” has surrounding whitespace", {
31
+ property: p.property
32
+ });
33
+ case "bad-option":
34
+ return t("problem.bad-option", "“{property}” does not accept {values}", {
35
+ property: p.property,
36
+ values: p.values.join(", ")
37
+ });
38
+ }
39
+ };
40
+ const run = async () => {
41
+ setBusy(true);
42
+ setError(false);
43
+ setResult(null);
44
+ try {
45
+ const { data } = await get(`/${PLUGIN_ID}/audit`);
46
+ setResult(data);
47
+ } catch {
48
+ setError(true);
49
+ } finally {
50
+ setBusy(false);
51
+ }
52
+ };
53
+ const entryLink = (uid, entry) => `/content-manager/collection-types/${uid}/${entry.documentId}` + (entry.locale ? `?plugins[i18n][locale]=${entry.locale}` : "");
54
+ const totalInvalid = result?.targets.reduce((n, r) => n + r.invalid.length, 0) ?? 0;
55
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
56
+ /* @__PURE__ */ jsx(Divider, {}),
57
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
58
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", children: t("audit.title", "Mapping audit") }),
59
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: t(
60
+ "audit.subtitle",
61
+ "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."
62
+ ) })
63
+ ] }),
64
+ /* @__PURE__ */ jsx(Flex, { gap: 2, children: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: run, loading: busy, disabled: !configured, children: t("audit.run", "Run the audit") }) }),
65
+ error ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger600", children: t("audit.error", "Audit failed — HubSpot unreachable?") }) : null,
66
+ result && !result.targets.length ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
67
+ "audit.no-targets",
68
+ "No content type to audit — declare them under `validate` in the plugin config."
69
+ ) }) : null,
70
+ result && result.targets.length ? /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
71
+ totalInvalid === 0 && result.targets.every((r) => !r.error) ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "success600", children: t("audit.clean", "Nothing to report — every mapping is valid.") }) : null,
72
+ result.targets.map((report) => /* @__PURE__ */ jsxs(Box, { children: [
73
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", paddingBottom: 2, children: [
74
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", children: report.uid }),
75
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t("audit.summary", "{entries} entries · {mappings} mappings", {
76
+ entries: report.entries,
77
+ mappings: report.mappings
78
+ }) })
79
+ ] }),
80
+ report.error ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger600", children: t("audit.target-error", "Could not scan: {error}", { error: report.error }) }) : null,
81
+ /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: report.invalid.map((entry) => /* @__PURE__ */ jsxs(
82
+ Box,
83
+ {
84
+ padding: 3,
85
+ background: "neutral100",
86
+ hasRadius: true,
87
+ children: [
88
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
89
+ /* @__PURE__ */ jsx(Link, { tag: Link$1, to: entryLink(report.uid, entry), children: entry.label }),
90
+ entry.locale ? /* @__PURE__ */ jsx(Badge, { children: entry.locale }) : null
91
+ ] }),
92
+ /* @__PURE__ */ jsx(Box, { paddingTop: 1, children: entry.problems.map((p, i) => /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger600", tag: "p", children: problemText(p) }, i)) })
93
+ ]
94
+ },
95
+ `${entry.documentId}-${entry.locale ?? ""}`
96
+ )) })
97
+ ] }, report.uid))
98
+ ] }) : null
99
+ ] });
100
+ };
101
+ const FailuresSection = ({ configured }) => {
102
+ const { formatMessage } = useIntl();
103
+ const { get, post } = useFetchClient();
104
+ const [total, setTotal] = React.useState(null);
105
+ const [busy, setBusy] = React.useState(false);
106
+ const [feedback, setFeedback] = React.useState(
107
+ null
108
+ );
109
+ const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
110
+ const load = React.useCallback(async () => {
111
+ try {
112
+ const { data } = await get(`/${PLUGIN_ID}/failures`);
113
+ setTotal(data.total);
114
+ } catch {
115
+ setTotal(null);
116
+ }
117
+ }, [get]);
118
+ React.useEffect(() => {
119
+ load();
120
+ }, [load]);
121
+ const retry = async () => {
122
+ setBusy(true);
123
+ setFeedback(null);
124
+ try {
125
+ const { data } = await post(
126
+ `/${PLUGIN_ID}/failures/retry`
127
+ );
128
+ setFeedback({
129
+ tone: data.failed ? "danger" : "success",
130
+ text: t("failures.retried", "{succeeded} replayed, {failed} still failing.", {
131
+ succeeded: data.succeeded,
132
+ failed: data.failed
133
+ })
134
+ });
135
+ await load();
136
+ } catch {
137
+ setFeedback({ tone: "danger", text: t("failures.error", "Replay failed.") });
138
+ } finally {
139
+ setBusy(false);
140
+ }
141
+ };
142
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
143
+ /* @__PURE__ */ jsx(Divider, {}),
144
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
145
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", children: t("failures.title", "Failed submissions") }),
146
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: t(
147
+ "failures.subtitle",
148
+ "Submissions HubSpot couldn't take after the automatic retries. They can be inspected under Content Manager → HubSpot failed submissions."
149
+ ) })
150
+ ] }),
151
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
152
+ /* @__PURE__ */ jsx(Badge, { active: Boolean(total), children: t("failures.count", "{count} queued", { count: total ?? 0 }) }),
153
+ /* @__PURE__ */ jsx(
154
+ Button,
155
+ {
156
+ variant: "secondary",
157
+ onClick: retry,
158
+ loading: busy,
159
+ disabled: !configured || !total,
160
+ children: t("failures.retry", "Replay now")
161
+ }
162
+ )
163
+ ] }),
164
+ feedback ? /* @__PURE__ */ jsx(
165
+ Typography,
166
+ {
167
+ variant: "pi",
168
+ textColor: feedback.tone === "success" ? "success600" : "danger600",
169
+ children: feedback.text
170
+ }
171
+ ) : null
172
+ ] });
173
+ };
174
+ const HubspotSettings = () => {
175
+ const { formatMessage } = useIntl();
176
+ const { get, put, del } = useFetchClient();
177
+ const [settings, setSettings] = React.useState(null);
178
+ const [apiKey, setApiKey] = React.useState("");
179
+ const [busy, setBusy] = React.useState(false);
180
+ const [feedback, setFeedback] = React.useState(
181
+ null
182
+ );
183
+ const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
184
+ const load = React.useCallback(async () => {
185
+ const { data } = await get(`/${PLUGIN_ID}/settings`);
186
+ setSettings(data);
187
+ }, [get]);
188
+ React.useEffect(() => {
189
+ load().catch(
190
+ () => setFeedback({ tone: "danger", text: t("settings.load-error", "Could not load settings.") })
191
+ );
192
+ }, [load]);
193
+ const save = async () => {
194
+ setBusy(true);
195
+ setFeedback(null);
196
+ try {
197
+ const { data } = await put(`/${PLUGIN_ID}/settings`, { apiKey });
198
+ setSettings(data);
199
+ setApiKey("");
200
+ setFeedback({ tone: "success", text: t("settings.saved", "Key saved.") });
201
+ } catch {
202
+ setFeedback({ tone: "danger", text: t("settings.save-error", "Could not save the key.") });
203
+ } finally {
204
+ setBusy(false);
205
+ }
206
+ };
207
+ const remove = async () => {
208
+ setBusy(true);
209
+ setFeedback(null);
210
+ try {
211
+ const { data } = await del(`/${PLUGIN_ID}/settings`);
212
+ setSettings(data);
213
+ setApiKey("");
214
+ setFeedback({ tone: "success", text: t("settings.removed", "Key removed.") });
215
+ } catch {
216
+ setFeedback({ tone: "danger", text: t("settings.remove-error", "Could not remove the key.") });
217
+ } finally {
218
+ setBusy(false);
219
+ }
220
+ };
221
+ const test = async () => {
222
+ setBusy(true);
223
+ setFeedback(null);
224
+ try {
225
+ const { data } = await get(
226
+ `/${PLUGIN_ID}/properties?refresh=1`
227
+ );
228
+ setFeedback(
229
+ data.configured ? {
230
+ tone: "success",
231
+ text: t("settings.test-ok", "Connection established — {count} readable properties.", {
232
+ count: data.properties.length
233
+ })
234
+ } : { tone: "danger", text: t("settings.test-no-key", "No key configured.") }
235
+ );
236
+ } catch {
237
+ setFeedback({
238
+ tone: "danger",
239
+ text: t("settings.test-error", "HubSpot unreachable — invalid or revoked key?")
240
+ });
241
+ } finally {
242
+ setBusy(false);
243
+ }
244
+ };
245
+ if (!settings) {
246
+ return /* @__PURE__ */ jsx(Box, { padding: 8, children: /* @__PURE__ */ jsx(Loader, { small: true, children: t("settings.loading", "Loading…") }) });
247
+ }
248
+ const sourceLabel = {
249
+ settings: t("settings.source.settings", "set here"),
250
+ config: t("settings.source.config", "config/plugins.ts"),
251
+ env: t("settings.source.env", "environment variable")
252
+ };
253
+ return /* @__PURE__ */ jsx(Box, { padding: 8, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
254
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
255
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "HubSpot" }),
256
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: t(
257
+ "settings.subtitle",
258
+ "The private app token used to read the portal's properties and validate form mappings."
259
+ ) })
260
+ ] }),
261
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
262
+ /* @__PURE__ */ jsx(Badge, { active: settings.configured, children: settings.configured ? t("settings.configured", "Configured {hint}", { hint: settings.hint }) : t("settings.not-configured", "Not configured") }),
263
+ settings.keySource ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t("settings.source", "source: {source}", { source: sourceLabel[settings.keySource] }) }) : null
264
+ ] }),
265
+ /* @__PURE__ */ jsx(Box, { maxWidth: "32rem", children: /* @__PURE__ */ jsxs(
266
+ Field.Root,
267
+ {
268
+ name: "apiKey",
269
+ hint: t(
270
+ "settings.key.hint",
271
+ "A key entered here overrides the ones from config/plugins.ts and HUBSPOT_API_KEY."
272
+ ),
273
+ children: [
274
+ /* @__PURE__ */ jsx(Field.Label, { children: t("settings.key.label", "Private app token") }),
275
+ /* @__PURE__ */ jsx(
276
+ Field.Input,
277
+ {
278
+ type: "password",
279
+ value: apiKey,
280
+ autoComplete: "off",
281
+ placeholder: settings.configured ? t("settings.key.placeholder.set", "•••••••• (leave empty to keep)") : t("settings.key.placeholder.empty", "pat-eu1-…"),
282
+ onChange: (e) => setApiKey(e.target.value)
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsx(Field.Hint, {})
286
+ ]
287
+ }
288
+ ) }),
289
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
290
+ /* @__PURE__ */ jsx(Button, { onClick: save, loading: busy, disabled: !apiKey.trim(), children: t("settings.save", "Save") }),
291
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: test, loading: busy, disabled: !settings.configured, children: t("settings.test", "Test connection") }),
292
+ /* @__PURE__ */ jsx(
293
+ Button,
294
+ {
295
+ variant: "danger-light",
296
+ onClick: remove,
297
+ loading: busy,
298
+ disabled: settings.keySource !== "settings",
299
+ children: t("settings.remove", "Remove")
300
+ }
301
+ )
302
+ ] }),
303
+ feedback ? /* @__PURE__ */ jsx(
304
+ Typography,
305
+ {
306
+ variant: "pi",
307
+ textColor: feedback.tone === "success" ? "success600" : "danger600",
308
+ children: feedback.text
309
+ }
310
+ ) : null,
311
+ /* @__PURE__ */ jsx(Box, { paddingTop: 4, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
312
+ "settings.scopes",
313
+ "The token needs the crm.schemas.contacts.read and crm.schemas.companies.read scopes to list properties. It is never returned to the browser: only its existence, its source and its last four characters are."
314
+ ) }) }),
315
+ /* @__PURE__ */ jsx(AuditSection, { configured: settings.configured }),
316
+ /* @__PURE__ */ jsx(FailuresSection, { configured: settings.configured })
317
+ ] }) });
318
+ };
319
+ export {
320
+ HubspotSettings as default
321
+ };
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const objectLabels = require("./objectLabels-Th7aYW-B.js");
6
+ const designSystem = require("@strapi/design-system");
7
+ const admin = require("@strapi/strapi/admin");
8
+ const index$1 = require("./index-CMzg-x5i.js");
9
+ const index = require("./index-Dgbid2LK.js");
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
13
+ if (e) {
14
+ for (const k in e) {
15
+ if (k !== "default") {
16
+ const d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: () => e[k]
20
+ });
21
+ }
22
+ }
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+ const React__namespace = /* @__PURE__ */ _interopNamespace(React);
28
+ const AuditSection = ({ configured }) => {
29
+ const intl = objectLabels.useIntl();
30
+ const { formatMessage } = intl;
31
+ const { get } = admin.useFetchClient();
32
+ const [busy, setBusy] = React__namespace.useState(false);
33
+ const [error, setError] = React__namespace.useState(false);
34
+ const [result, setResult] = React__namespace.useState(null);
35
+ const t = (id, defaultMessage, values) => formatMessage({ id: index$1.getTranslation(id), defaultMessage }, values);
36
+ const problemText = (p) => {
37
+ switch (p.code) {
38
+ case "unknown":
39
+ return t("problem.unknown", "“{property}” does not exist on {object} in this portal", {
40
+ property: p.property,
41
+ object: objectLabels.objectLabel(intl, p.object)
42
+ });
43
+ case "wrong-object":
44
+ return t("problem.wrong-object", "“{property}” exists on {actual}, not on {object}", {
45
+ property: p.property,
46
+ object: objectLabels.objectLabel(intl, p.object),
47
+ actual: objectLabels.objectLabel(intl, p.actualObject)
48
+ });
49
+ case "whitespace":
50
+ return t("problem.whitespace", "“{property}” has surrounding whitespace", {
51
+ property: p.property
52
+ });
53
+ case "bad-option":
54
+ return t("problem.bad-option", "“{property}” does not accept {values}", {
55
+ property: p.property,
56
+ values: p.values.join(", ")
57
+ });
58
+ }
59
+ };
60
+ const run = async () => {
61
+ setBusy(true);
62
+ setError(false);
63
+ setResult(null);
64
+ try {
65
+ const { data } = await get(`/${index$1.PLUGIN_ID}/audit`);
66
+ setResult(data);
67
+ } catch {
68
+ setError(true);
69
+ } finally {
70
+ setBusy(false);
71
+ }
72
+ };
73
+ const entryLink = (uid, entry) => `/content-manager/collection-types/${uid}/${entry.documentId}` + (entry.locale ? `?plugins[i18n][locale]=${entry.locale}` : "");
74
+ const totalInvalid = result?.targets.reduce((n, r) => n + r.invalid.length, 0) ?? 0;
75
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
76
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}),
77
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
78
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: t("audit.title", "Mapping audit") }),
79
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: t(
80
+ "audit.subtitle",
81
+ "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."
82
+ ) })
83
+ ] }),
84
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: run, loading: busy, disabled: !configured, children: t("audit.run", "Run the audit") }) }),
85
+ error ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger600", children: t("audit.error", "Audit failed — HubSpot unreachable?") }) : null,
86
+ result && !result.targets.length ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
87
+ "audit.no-targets",
88
+ "No content type to audit — declare them under `validate` in the plugin config."
89
+ ) }) : null,
90
+ result && result.targets.length ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
91
+ totalInvalid === 0 && result.targets.every((r) => !r.error) ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "success600", children: t("audit.clean", "Nothing to report — every mapping is valid.") }) : null,
92
+ result.targets.map((report) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
93
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", paddingBottom: 2, children: [
94
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", children: report.uid }),
95
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t("audit.summary", "{entries} entries · {mappings} mappings", {
96
+ entries: report.entries,
97
+ mappings: report.mappings
98
+ }) })
99
+ ] }),
100
+ report.error ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger600", children: t("audit.target-error", "Could not scan: {error}", { error: report.error }) }) : null,
101
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: report.invalid.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
102
+ designSystem.Box,
103
+ {
104
+ padding: 3,
105
+ background: "neutral100",
106
+ hasRadius: true,
107
+ children: [
108
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
109
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { tag: index.Link, to: entryLink(report.uid, entry), children: entry.label }),
110
+ entry.locale ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: entry.locale }) : null
111
+ ] }),
112
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 1, children: entry.problems.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger600", tag: "p", children: problemText(p) }, i)) })
113
+ ]
114
+ },
115
+ `${entry.documentId}-${entry.locale ?? ""}`
116
+ )) })
117
+ ] }, report.uid))
118
+ ] }) : null
119
+ ] });
120
+ };
121
+ const FailuresSection = ({ configured }) => {
122
+ const { formatMessage } = objectLabels.useIntl();
123
+ const { get, post } = admin.useFetchClient();
124
+ const [total, setTotal] = React__namespace.useState(null);
125
+ const [busy, setBusy] = React__namespace.useState(false);
126
+ const [feedback, setFeedback] = React__namespace.useState(
127
+ null
128
+ );
129
+ const t = (id, defaultMessage, values) => formatMessage({ id: index$1.getTranslation(id), defaultMessage }, values);
130
+ const load = React__namespace.useCallback(async () => {
131
+ try {
132
+ const { data } = await get(`/${index$1.PLUGIN_ID}/failures`);
133
+ setTotal(data.total);
134
+ } catch {
135
+ setTotal(null);
136
+ }
137
+ }, [get]);
138
+ React__namespace.useEffect(() => {
139
+ load();
140
+ }, [load]);
141
+ const retry = async () => {
142
+ setBusy(true);
143
+ setFeedback(null);
144
+ try {
145
+ const { data } = await post(
146
+ `/${index$1.PLUGIN_ID}/failures/retry`
147
+ );
148
+ setFeedback({
149
+ tone: data.failed ? "danger" : "success",
150
+ text: t("failures.retried", "{succeeded} replayed, {failed} still failing.", {
151
+ succeeded: data.succeeded,
152
+ failed: data.failed
153
+ })
154
+ });
155
+ await load();
156
+ } catch {
157
+ setFeedback({ tone: "danger", text: t("failures.error", "Replay failed.") });
158
+ } finally {
159
+ setBusy(false);
160
+ }
161
+ };
162
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
163
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}),
164
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
165
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: t("failures.title", "Failed submissions") }),
166
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: t(
167
+ "failures.subtitle",
168
+ "Submissions HubSpot couldn't take after the automatic retries. They can be inspected under Content Manager → HubSpot failed submissions."
169
+ ) })
170
+ ] }),
171
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
172
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { active: Boolean(total), children: t("failures.count", "{count} queued", { count: total ?? 0 }) }),
173
+ /* @__PURE__ */ jsxRuntime.jsx(
174
+ designSystem.Button,
175
+ {
176
+ variant: "secondary",
177
+ onClick: retry,
178
+ loading: busy,
179
+ disabled: !configured || !total,
180
+ children: t("failures.retry", "Replay now")
181
+ }
182
+ )
183
+ ] }),
184
+ feedback ? /* @__PURE__ */ jsxRuntime.jsx(
185
+ designSystem.Typography,
186
+ {
187
+ variant: "pi",
188
+ textColor: feedback.tone === "success" ? "success600" : "danger600",
189
+ children: feedback.text
190
+ }
191
+ ) : null
192
+ ] });
193
+ };
194
+ const HubspotSettings = () => {
195
+ const { formatMessage } = objectLabels.useIntl();
196
+ const { get, put, del } = admin.useFetchClient();
197
+ const [settings, setSettings] = React__namespace.useState(null);
198
+ const [apiKey, setApiKey] = React__namespace.useState("");
199
+ const [busy, setBusy] = React__namespace.useState(false);
200
+ const [feedback, setFeedback] = React__namespace.useState(
201
+ null
202
+ );
203
+ const t = (id, defaultMessage, values) => formatMessage({ id: index$1.getTranslation(id), defaultMessage }, values);
204
+ const load = React__namespace.useCallback(async () => {
205
+ const { data } = await get(`/${index$1.PLUGIN_ID}/settings`);
206
+ setSettings(data);
207
+ }, [get]);
208
+ React__namespace.useEffect(() => {
209
+ load().catch(
210
+ () => setFeedback({ tone: "danger", text: t("settings.load-error", "Could not load settings.") })
211
+ );
212
+ }, [load]);
213
+ const save = async () => {
214
+ setBusy(true);
215
+ setFeedback(null);
216
+ try {
217
+ const { data } = await put(`/${index$1.PLUGIN_ID}/settings`, { apiKey });
218
+ setSettings(data);
219
+ setApiKey("");
220
+ setFeedback({ tone: "success", text: t("settings.saved", "Key saved.") });
221
+ } catch {
222
+ setFeedback({ tone: "danger", text: t("settings.save-error", "Could not save the key.") });
223
+ } finally {
224
+ setBusy(false);
225
+ }
226
+ };
227
+ const remove = async () => {
228
+ setBusy(true);
229
+ setFeedback(null);
230
+ try {
231
+ const { data } = await del(`/${index$1.PLUGIN_ID}/settings`);
232
+ setSettings(data);
233
+ setApiKey("");
234
+ setFeedback({ tone: "success", text: t("settings.removed", "Key removed.") });
235
+ } catch {
236
+ setFeedback({ tone: "danger", text: t("settings.remove-error", "Could not remove the key.") });
237
+ } finally {
238
+ setBusy(false);
239
+ }
240
+ };
241
+ const test = async () => {
242
+ setBusy(true);
243
+ setFeedback(null);
244
+ try {
245
+ const { data } = await get(
246
+ `/${index$1.PLUGIN_ID}/properties?refresh=1`
247
+ );
248
+ setFeedback(
249
+ data.configured ? {
250
+ tone: "success",
251
+ text: t("settings.test-ok", "Connection established — {count} readable properties.", {
252
+ count: data.properties.length
253
+ })
254
+ } : { tone: "danger", text: t("settings.test-no-key", "No key configured.") }
255
+ );
256
+ } catch {
257
+ setFeedback({
258
+ tone: "danger",
259
+ text: t("settings.test-error", "HubSpot unreachable — invalid or revoked key?")
260
+ });
261
+ } finally {
262
+ setBusy(false);
263
+ }
264
+ };
265
+ if (!settings) {
266
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true, children: t("settings.loading", "Loading…") }) });
267
+ }
268
+ const sourceLabel = {
269
+ settings: t("settings.source.settings", "set here"),
270
+ config: t("settings.source.config", "config/plugins.ts"),
271
+ env: t("settings.source.env", "environment variable")
272
+ };
273
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 8, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
274
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 2, children: [
275
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", children: "HubSpot" }),
276
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: t(
277
+ "settings.subtitle",
278
+ "The private app token used to read the portal's properties and validate form mappings."
279
+ ) })
280
+ ] }),
281
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
282
+ /* @__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") }),
283
+ settings.keySource ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t("settings.source", "source: {source}", { source: sourceLabel[settings.keySource] }) }) : null
284
+ ] }),
285
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { maxWidth: "32rem", children: /* @__PURE__ */ jsxRuntime.jsxs(
286
+ designSystem.Field.Root,
287
+ {
288
+ name: "apiKey",
289
+ hint: t(
290
+ "settings.key.hint",
291
+ "A key entered here overrides the ones from config/plugins.ts and HUBSPOT_API_KEY."
292
+ ),
293
+ children: [
294
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("settings.key.label", "Private app token") }),
295
+ /* @__PURE__ */ jsxRuntime.jsx(
296
+ designSystem.Field.Input,
297
+ {
298
+ type: "password",
299
+ value: apiKey,
300
+ autoComplete: "off",
301
+ placeholder: settings.configured ? t("settings.key.placeholder.set", "•••••••• (leave empty to keep)") : t("settings.key.placeholder.empty", "pat-eu1-…"),
302
+ onChange: (e) => setApiKey(e.target.value)
303
+ }
304
+ ),
305
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
306
+ ]
307
+ }
308
+ ) }),
309
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
310
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: save, loading: busy, disabled: !apiKey.trim(), children: t("settings.save", "Save") }),
311
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: test, loading: busy, disabled: !settings.configured, children: t("settings.test", "Test connection") }),
312
+ /* @__PURE__ */ jsxRuntime.jsx(
313
+ designSystem.Button,
314
+ {
315
+ variant: "danger-light",
316
+ onClick: remove,
317
+ loading: busy,
318
+ disabled: settings.keySource !== "settings",
319
+ children: t("settings.remove", "Remove")
320
+ }
321
+ )
322
+ ] }),
323
+ feedback ? /* @__PURE__ */ jsxRuntime.jsx(
324
+ designSystem.Typography,
325
+ {
326
+ variant: "pi",
327
+ textColor: feedback.tone === "success" ? "success600" : "danger600",
328
+ children: feedback.text
329
+ }
330
+ ) : null,
331
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: t(
332
+ "settings.scopes",
333
+ "The token needs the crm.schemas.contacts.read and crm.schemas.companies.read scopes to list properties. It is never returned to the browser: only its existence, its source and its last four characters are."
334
+ ) }) }),
335
+ /* @__PURE__ */ jsxRuntime.jsx(AuditSection, { configured: settings.configured }),
336
+ /* @__PURE__ */ jsxRuntime.jsx(FailuresSection, { configured: settings.configured })
337
+ ] }) });
338
+ };
339
+ exports.default = HubspotSettings;