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,103 @@
1
+ /**
2
+ * Public types of strapi-plugin-hubspot, for host apps and frontends:
3
+ *
4
+ * import type { PublicForm, Condition } from "strapi-plugin-hubspot/types";
5
+ *
6
+ * They describe the payloads of the public content-api routes:
7
+ * GET /api/hubspot/forms/:slug → PublicForm
8
+ * POST /api/hubspot/forms/:slug/submit → SubmitResponse (SubmitRequest in)
9
+ */
10
+
11
+ export type Operator = "eq" | "neq" | "contains" | "empty" | "notEmpty" | "gt" | "lt";
12
+
13
+ export interface Rule {
14
+ /** Stable id (`fld_…`) of the field the rule reads — never its name. */
15
+ field: string;
16
+ operator: Operator;
17
+ value?: string;
18
+ }
19
+
20
+ export interface Condition {
21
+ logic: "and" | "or";
22
+ rules: Rule[];
23
+ }
24
+
25
+ export type FieldType =
26
+ | "text"
27
+ | "email"
28
+ | "tel"
29
+ | "number"
30
+ | "website"
31
+ | "textarea"
32
+ | "select"
33
+ | "checkbox"
34
+ | "radio";
35
+
36
+ export interface FieldOption {
37
+ value: string;
38
+ label?: string;
39
+ }
40
+
41
+ /** A field as the public endpoint serves it — the CRM mapping is stripped. */
42
+ export interface PublicFormField {
43
+ id: string;
44
+ name: string;
45
+ label: string;
46
+ type: FieldType;
47
+ placeholder?: string;
48
+ helpText?: string;
49
+ icon?: string;
50
+ class?: string;
51
+ width?: "full" | "half";
52
+ required?: boolean;
53
+ persist?: "session" | "days30";
54
+ options?: FieldOption[];
55
+ visibleIf?: Condition | null;
56
+ }
57
+
58
+ export interface PublicFormStep {
59
+ id: string;
60
+ title?: string;
61
+ description?: string;
62
+ class?: string;
63
+ visibleIf?: Condition | null;
64
+ fields: PublicFormField[];
65
+ }
66
+
67
+ /** GET /api/hubspot/forms/:slug */
68
+ export interface PublicForm {
69
+ slug: string;
70
+ title?: string | null;
71
+ subtitle?: string | null;
72
+ nextLabel?: string | null;
73
+ submitLabel?: string | null;
74
+ successMessage?: string | null;
75
+ locale?: string | null;
76
+ steps: PublicFormStep[];
77
+ }
78
+
79
+ /** POST /api/hubspot/forms/:slug/submit — request body. */
80
+ export interface SubmitRequest {
81
+ /** Answers keyed by field `name`. Values hidden by a condition are ignored. */
82
+ values: Record<string, string | number | boolean>;
83
+ meta?: {
84
+ pagePath?: string;
85
+ pageUrl?: string;
86
+ originPath?: string;
87
+ originLabel?: string;
88
+ source?: string;
89
+ };
90
+ }
91
+
92
+ /** POST /api/hubspot/forms/:slug/submit — 200 response. */
93
+ export interface SubmitResponse {
94
+ ok: true;
95
+ /** False when the CRM was unreachable — the submission is stored either way. */
96
+ hubspotSynced: boolean;
97
+ }
98
+
99
+ /** POST /api/hubspot/forms/:slug/submit — 422 response. */
100
+ export interface SubmitValidationError {
101
+ ok: false;
102
+ missingRequired: { id: string; name: string; label: string }[];
103
+ }
@@ -0,0 +1,2 @@
1
+ // types-only entry
2
+ module.exports = {};
@@ -0,0 +1,2 @@
1
+ // types-only entry
2
+ export default {};
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "strapi-plugin-hubspot",
3
- "version": "0.4.2",
4
- "description": "HubSpot integration for Strapi — pick CRM properties from a searchable list filtered by object, and catch bad mappings before they reach the API.",
3
+ "version": "0.6.0",
4
+ "description": "The form builder for HubSpot in Strapi — multi-step forms with conditional fields mapped to real CRM properties, a public submit pipeline with retries, and save-time mapping validation.",
5
5
  "keywords": [
6
6
  "strapi",
7
7
  "plugin",
8
8
  "hubspot",
9
9
  "crm",
10
+ "form-builder",
11
+ "forms",
12
+ "conditional-fields",
13
+ "leads",
10
14
  "custom-field"
11
15
  ],
12
16
  "license": "MIT",
@@ -35,20 +39,30 @@
35
39
  "import": "./dist/server/index.mjs",
36
40
  "require": "./dist/server/index.js",
37
41
  "default": "./dist/server/index.js"
42
+ },
43
+ "./types": {
44
+ "types": "./dist/shared/types.d.ts",
45
+ "source": "./shared/types.ts",
46
+ "import": "./dist/shared/types.mjs",
47
+ "require": "./dist/shared/types.js",
48
+ "default": "./dist/shared/types.js"
38
49
  }
39
50
  },
40
51
  "files": [
41
- "dist"
52
+ "dist",
53
+ "CHANGELOG.md"
42
54
  ],
43
55
  "scripts": {
44
- "build": "strapi-plugin build",
56
+ "build": "strapi-plugin build && node scripts/build-types.mjs",
45
57
  "watch": "strapi-plugin watch",
46
58
  "verify": "strapi-plugin verify",
47
59
  "test": "vitest run",
48
60
  "test:watch": "vitest",
49
- "prepublishOnly": "npm run build"
61
+ "prepublishOnly": "npm run build",
62
+ "lint": "eslint admin/src server/src"
50
63
  },
51
64
  "devDependencies": {
65
+ "@eslint/js": "^10.0.1",
52
66
  "@strapi/design-system": "^2.0.0-rc.29",
53
67
  "@strapi/icons": "^2.0.0-rc.29",
54
68
  "@strapi/sdk-plugin": "^5.3.2",
@@ -57,11 +71,14 @@
57
71
  "@strapi/utils": "^5.0.0",
58
72
  "@types/react": "^18.3.3",
59
73
  "@types/react-dom": "^18.3.0",
74
+ "eslint": "^10.9.1",
75
+ "eslint-plugin-react-hooks": "^7.1.1",
60
76
  "react": "^18.3.1",
61
77
  "react-dom": "^18.3.1",
62
78
  "react-router-dom": "^6.26.0",
63
79
  "styled-components": "^6.1.11",
64
80
  "typescript": "^5.5.4",
81
+ "typescript-eslint": "^8.68.0",
65
82
  "vitest": "^3.2.4"
66
83
  },
67
84
  "peerDependencies": {
@@ -82,4 +99,4 @@
82
99
  "engines": {
83
100
  "node": ">=18.0.0"
84
101
  }
85
- }
102
+ }
@@ -1,177 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import * as React from "react";
3
- import { u as useIntl, o as objectLabel } from "./objectLabels-dM4o67Fz.mjs";
4
- import { Field, Flex, Loader, Combobox, ComboboxOption, Button, Typography, Link } from "@strapi/design-system";
5
- import { useFetchClient, useField } from "@strapi/strapi/admin";
6
- import { P as PLUGIN_ID, g as getTranslation } from "./index-CKoBcX1T.mjs";
7
- const oneLine = {
8
- display: "block",
9
- whiteSpace: "nowrap",
10
- overflow: "hidden",
11
- textOverflow: "ellipsis"
12
- };
13
- const HubspotPropertyInput = React.forwardRef(
14
- ({ name, value, onChange, attribute, disabled, error, required, label, hint, labelAction }, ref) => {
15
- const intl = useIntl();
16
- const { formatMessage } = intl;
17
- const { get } = useFetchClient();
18
- const [schema, setSchema] = React.useState(null);
19
- const [loadError, setLoadError] = React.useState("");
20
- const objectFieldName = attribute?.options?.objectField || "hsObject";
21
- const siblingPath = React.useMemo(
22
- () => name.replace(/[^.]+$/, objectFieldName),
23
- [name, objectFieldName]
24
- );
25
- const siblingField = useField(siblingPath);
26
- const selectedObject = siblingField?.value;
27
- const optionsFieldName = attribute?.options?.optionsField || "options";
28
- const optionsPath = React.useMemo(
29
- () => name.replace(/[^.]+$/, optionsFieldName),
30
- [name, optionsFieldName]
31
- );
32
- const optionsField = useField(optionsPath);
33
- const [imported, setImported] = React.useState(false);
34
- React.useEffect(() => {
35
- let cancelled = false;
36
- get(`/${PLUGIN_ID}/properties`).then(({ data }) => {
37
- if (!cancelled) setSchema(data);
38
- }).catch((err) => {
39
- if (cancelled) return;
40
- setSchema({ configured: false, properties: [], objects: [], unavailable: [] });
41
- setLoadError(
42
- err?.response?.data?.error?.message ?? formatMessage({
43
- id: getTranslation("picker.load-error"),
44
- defaultMessage: "HubSpot properties unavailable"
45
- })
46
- );
47
- });
48
- return () => {
49
- cancelled = true;
50
- };
51
- }, [get]);
52
- const emit = (next) => onChange({ target: { name, value: next ?? "", type: "string" } });
53
- const options = React.useMemo(() => {
54
- const all = schema?.properties ?? [];
55
- const scoped = selectedObject ? all.filter((p) => p.object === selectedObject) : all;
56
- const sorted = selectedObject ? [...scoped].sort(
57
- (a, b) => (a.group ?? "").localeCompare(b.group ?? "") || a.label.localeCompare(b.label)
58
- ) : scoped;
59
- const shown = sorted.map((p) => ({
60
- value: p.name,
61
- label: selectedObject ? `${p.group ? `${p.group} · ` : ""}${p.label} (${p.name})` : `${objectLabel(intl, p.object)} · ${p.label} (${p.name})`
62
- }));
63
- if (value && !scoped.some((p) => p.name === value)) {
64
- const elsewhere = all.find((p) => p.name === value);
65
- shown.unshift({
66
- value,
67
- label: elsewhere ? formatMessage(
68
- { id: getTranslation("picker.belongs-to"), defaultMessage: "{property} — belongs to {object}" },
69
- { property: value, object: objectLabel(intl, elsewhere.object) }
70
- ) : formatMessage(
71
- { id: getTranslation("picker.unknown"), defaultMessage: "{property} — unknown to the portal" },
72
- { property: value }
73
- )
74
- });
75
- }
76
- return shown;
77
- }, [schema, selectedObject, value, intl, formatMessage]);
78
- const describe = () => {
79
- if (loadError) return loadError;
80
- if (schema && !schema.configured) {
81
- return formatMessage({
82
- id: getTranslation("picker.not-configured"),
83
- defaultMessage: "No HubSpot API key — free text. Set it in Settings → HubSpot."
84
- });
85
- }
86
- if (selectedObject && schema?.unavailable.some((u) => u.object === selectedObject)) {
87
- return formatMessage(
88
- {
89
- id: getTranslation("picker.object-unavailable"),
90
- defaultMessage: "Properties of “{object}” unreadable — missing scope on the token."
91
- },
92
- { object: objectLabel(intl, selectedObject) }
93
- );
94
- }
95
- return hint;
96
- };
97
- const crmLink = schema?.portalId && value && schema.properties.some((p) => p.name === value) ? `https://${schema.uiDomain || "app.hubspot.com"}/property-settings/${schema.portalId}/properties?search=${encodeURIComponent(value)}` : null;
98
- const selectedProperty = value && schema ? schema.properties.find(
99
- (p) => p.name === value && (!selectedObject || p.object === selectedObject)
100
- ) : void 0;
101
- const importable = selectedProperty?.type === "enumeration" && (selectedProperty.options?.length ?? 0) > 0;
102
- React.useEffect(() => setImported(false), [value]);
103
- const importOptions = () => {
104
- if (!selectedProperty || !optionsField) return;
105
- const rows = selectedProperty.options.map((o, i) => ({
106
- __temp_key__: `a${i}`,
107
- value: o.value,
108
- label: o.label ?? o.value
109
- }));
110
- optionsField.onChange({
111
- target: { name: optionsPath, value: rows, type: "json" }
112
- });
113
- setImported(true);
114
- };
115
- return /* @__PURE__ */ jsxs(Field.Root, { name, error, hint: describe(), required, children: [
116
- /* @__PURE__ */ jsx(Field.Label, { action: labelAction, children: label }),
117
- !schema ? /* @__PURE__ */ jsx(Flex, { paddingTop: 2, paddingBottom: 2, children: /* @__PURE__ */ jsx(Loader, { small: true, children: formatMessage({
118
- id: getTranslation("picker.loading"),
119
- defaultMessage: "Loading HubSpot properties…"
120
- }) }) }) : schema.configured ? /* @__PURE__ */ jsx(
121
- Combobox,
122
- {
123
- ref,
124
- value: value || "",
125
- onChange: emit,
126
- onClear: () => emit(""),
127
- disabled,
128
- placeholder: formatMessage({
129
- id: getTranslation("picker.placeholder"),
130
- defaultMessage: "Search a property…"
131
- }),
132
- autocomplete: { type: "list", filter: "contains" },
133
- noOptionsMessage: () => formatMessage({
134
- id: getTranslation("picker.no-results"),
135
- defaultMessage: "No matching property — properties are managed in HubSpot."
136
- }),
137
- children: options.map((o) => /* @__PURE__ */ jsx(ComboboxOption, { value: o.value, textValue: o.label, children: /* @__PURE__ */ jsx("span", { title: o.label, style: oneLine, children: o.label }) }, o.value))
138
- }
139
- ) : /* @__PURE__ */ jsx(
140
- Field.Input,
141
- {
142
- ref,
143
- value: value || "",
144
- onChange: (e) => emit(e.target.value),
145
- disabled,
146
- placeholder: formatMessage({
147
- id: getTranslation("picker.free-placeholder"),
148
- defaultMessage: "e.g. hs_role, numberofemployees"
149
- })
150
- }
151
- ),
152
- crmLink || importable ? /* @__PURE__ */ jsxs(Flex, { paddingTop: 1, gap: 3, alignItems: "center", wrap: "wrap", children: [
153
- importable ? /* @__PURE__ */ jsx(Button, { variant: "tertiary", size: "S", onClick: importOptions, disabled, children: formatMessage(
154
- {
155
- id: getTranslation("picker.import"),
156
- defaultMessage: "Import the {count} options from HubSpot"
157
- },
158
- { count: selectedProperty?.options.length ?? 0 }
159
- ) }) : null,
160
- imported ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "success600", children: formatMessage({
161
- id: getTranslation("picker.imported"),
162
- defaultMessage: "Options imported — save to keep them."
163
- }) }) : null,
164
- crmLink ? /* @__PURE__ */ jsx(Link, { href: crmLink, isExternal: true, children: formatMessage({
165
- id: getTranslation("picker.view"),
166
- defaultMessage: "View in HubSpot"
167
- }) }) : null
168
- ] }) : null,
169
- /* @__PURE__ */ jsx(Field.Hint, {}),
170
- /* @__PURE__ */ jsx(Field.Error, {})
171
- ] });
172
- }
173
- );
174
- HubspotPropertyInput.displayName = "HubspotPropertyInput";
175
- export {
176
- HubspotPropertyInput as default
177
- };
@@ -1,195 +0,0 @@
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-B76t8qqg.js");
6
- const designSystem = require("@strapi/design-system");
7
- const admin = require("@strapi/strapi/admin");
8
- const index = require("./index-BUlB1rK9.js");
9
- function _interopNamespace(e) {
10
- if (e && e.__esModule) return e;
11
- const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
12
- if (e) {
13
- for (const k in e) {
14
- if (k !== "default") {
15
- const d = Object.getOwnPropertyDescriptor(e, k);
16
- Object.defineProperty(n, k, d.get ? d : {
17
- enumerable: true,
18
- get: () => e[k]
19
- });
20
- }
21
- }
22
- }
23
- n.default = e;
24
- return Object.freeze(n);
25
- }
26
- const React__namespace = /* @__PURE__ */ _interopNamespace(React);
27
- const oneLine = {
28
- display: "block",
29
- whiteSpace: "nowrap",
30
- overflow: "hidden",
31
- textOverflow: "ellipsis"
32
- };
33
- const HubspotPropertyInput = React__namespace.forwardRef(
34
- ({ name, value, onChange, attribute, disabled, error, required, label, hint, labelAction }, ref) => {
35
- const intl = objectLabels.useIntl();
36
- const { formatMessage } = intl;
37
- const { get } = admin.useFetchClient();
38
- const [schema, setSchema] = React__namespace.useState(null);
39
- const [loadError, setLoadError] = React__namespace.useState("");
40
- const objectFieldName = attribute?.options?.objectField || "hsObject";
41
- const siblingPath = React__namespace.useMemo(
42
- () => name.replace(/[^.]+$/, objectFieldName),
43
- [name, objectFieldName]
44
- );
45
- const siblingField = admin.useField(siblingPath);
46
- const selectedObject = siblingField?.value;
47
- const optionsFieldName = attribute?.options?.optionsField || "options";
48
- const optionsPath = React__namespace.useMemo(
49
- () => name.replace(/[^.]+$/, optionsFieldName),
50
- [name, optionsFieldName]
51
- );
52
- const optionsField = admin.useField(optionsPath);
53
- const [imported, setImported] = React__namespace.useState(false);
54
- React__namespace.useEffect(() => {
55
- let cancelled = false;
56
- get(`/${index.PLUGIN_ID}/properties`).then(({ data }) => {
57
- if (!cancelled) setSchema(data);
58
- }).catch((err) => {
59
- if (cancelled) return;
60
- setSchema({ configured: false, properties: [], objects: [], unavailable: [] });
61
- setLoadError(
62
- err?.response?.data?.error?.message ?? formatMessage({
63
- id: index.getTranslation("picker.load-error"),
64
- defaultMessage: "HubSpot properties unavailable"
65
- })
66
- );
67
- });
68
- return () => {
69
- cancelled = true;
70
- };
71
- }, [get]);
72
- const emit = (next) => onChange({ target: { name, value: next ?? "", type: "string" } });
73
- const options = React__namespace.useMemo(() => {
74
- const all = schema?.properties ?? [];
75
- const scoped = selectedObject ? all.filter((p) => p.object === selectedObject) : all;
76
- const sorted = selectedObject ? [...scoped].sort(
77
- (a, b) => (a.group ?? "").localeCompare(b.group ?? "") || a.label.localeCompare(b.label)
78
- ) : scoped;
79
- const shown = sorted.map((p) => ({
80
- value: p.name,
81
- label: selectedObject ? `${p.group ? `${p.group} · ` : ""}${p.label} (${p.name})` : `${objectLabels.objectLabel(intl, p.object)} · ${p.label} (${p.name})`
82
- }));
83
- if (value && !scoped.some((p) => p.name === value)) {
84
- const elsewhere = all.find((p) => p.name === value);
85
- shown.unshift({
86
- value,
87
- label: elsewhere ? formatMessage(
88
- { id: index.getTranslation("picker.belongs-to"), defaultMessage: "{property} — belongs to {object}" },
89
- { property: value, object: objectLabels.objectLabel(intl, elsewhere.object) }
90
- ) : formatMessage(
91
- { id: index.getTranslation("picker.unknown"), defaultMessage: "{property} — unknown to the portal" },
92
- { property: value }
93
- )
94
- });
95
- }
96
- return shown;
97
- }, [schema, selectedObject, value, intl, formatMessage]);
98
- const describe = () => {
99
- if (loadError) return loadError;
100
- if (schema && !schema.configured) {
101
- return formatMessage({
102
- id: index.getTranslation("picker.not-configured"),
103
- defaultMessage: "No HubSpot API key — free text. Set it in Settings → HubSpot."
104
- });
105
- }
106
- if (selectedObject && schema?.unavailable.some((u) => u.object === selectedObject)) {
107
- return formatMessage(
108
- {
109
- id: index.getTranslation("picker.object-unavailable"),
110
- defaultMessage: "Properties of “{object}” unreadable — missing scope on the token."
111
- },
112
- { object: objectLabels.objectLabel(intl, selectedObject) }
113
- );
114
- }
115
- return hint;
116
- };
117
- const crmLink = schema?.portalId && value && schema.properties.some((p) => p.name === value) ? `https://${schema.uiDomain || "app.hubspot.com"}/property-settings/${schema.portalId}/properties?search=${encodeURIComponent(value)}` : null;
118
- const selectedProperty = value && schema ? schema.properties.find(
119
- (p) => p.name === value && (!selectedObject || p.object === selectedObject)
120
- ) : void 0;
121
- const importable = selectedProperty?.type === "enumeration" && (selectedProperty.options?.length ?? 0) > 0;
122
- React__namespace.useEffect(() => setImported(false), [value]);
123
- const importOptions = () => {
124
- if (!selectedProperty || !optionsField) return;
125
- const rows = selectedProperty.options.map((o, i) => ({
126
- __temp_key__: `a${i}`,
127
- value: o.value,
128
- label: o.label ?? o.value
129
- }));
130
- optionsField.onChange({
131
- target: { name: optionsPath, value: rows, type: "json" }
132
- });
133
- setImported(true);
134
- };
135
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { name, error, hint: describe(), required, children: [
136
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { action: labelAction, children: label }),
137
- !schema ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { paddingTop: 2, paddingBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true, children: formatMessage({
138
- id: index.getTranslation("picker.loading"),
139
- defaultMessage: "Loading HubSpot properties…"
140
- }) }) }) : schema.configured ? /* @__PURE__ */ jsxRuntime.jsx(
141
- designSystem.Combobox,
142
- {
143
- ref,
144
- value: value || "",
145
- onChange: emit,
146
- onClear: () => emit(""),
147
- disabled,
148
- placeholder: formatMessage({
149
- id: index.getTranslation("picker.placeholder"),
150
- defaultMessage: "Search a property…"
151
- }),
152
- autocomplete: { type: "list", filter: "contains" },
153
- noOptionsMessage: () => formatMessage({
154
- id: index.getTranslation("picker.no-results"),
155
- defaultMessage: "No matching property — properties are managed in HubSpot."
156
- }),
157
- children: options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: o.value, textValue: o.label, children: /* @__PURE__ */ jsxRuntime.jsx("span", { title: o.label, style: oneLine, children: o.label }) }, o.value))
158
- }
159
- ) : /* @__PURE__ */ jsxRuntime.jsx(
160
- designSystem.Field.Input,
161
- {
162
- ref,
163
- value: value || "",
164
- onChange: (e) => emit(e.target.value),
165
- disabled,
166
- placeholder: formatMessage({
167
- id: index.getTranslation("picker.free-placeholder"),
168
- defaultMessage: "e.g. hs_role, numberofemployees"
169
- })
170
- }
171
- ),
172
- crmLink || importable ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 1, gap: 3, alignItems: "center", wrap: "wrap", children: [
173
- importable ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", size: "S", onClick: importOptions, disabled, children: formatMessage(
174
- {
175
- id: index.getTranslation("picker.import"),
176
- defaultMessage: "Import the {count} options from HubSpot"
177
- },
178
- { count: selectedProperty?.options.length ?? 0 }
179
- ) }) : null,
180
- imported ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "success600", children: formatMessage({
181
- id: index.getTranslation("picker.imported"),
182
- defaultMessage: "Options imported — save to keep them."
183
- }) }) : null,
184
- crmLink ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: crmLink, isExternal: true, children: formatMessage({
185
- id: index.getTranslation("picker.view"),
186
- defaultMessage: "View in HubSpot"
187
- }) }) : null
188
- ] }) : null,
189
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
190
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
191
- ] });
192
- }
193
- );
194
- HubspotPropertyInput.displayName = "HubspotPropertyInput";
195
- exports.default = HubspotPropertyInput;
@@ -1,82 +0,0 @@
1
- const en = {
2
- "property.label": "HubSpot property",
3
- "property.description": "Picked from the portal's real properties",
4
- "options.section": "HubSpot",
5
- "options.objectField.label": "Object field",
6
- "options.objectField.description": "Name of the sibling field holding the HubSpot object (e.g. hsObject). Leave empty to list every object's properties.",
7
- "options.objectField.placeholder": "hsObject",
8
- "options.optionsField.label": "Options field",
9
- "options.optionsField.description": `Name of the sibling repeatable holding this field's choices (value/label entries), filled by the import button on enumeration properties. Defaults to "options".`,
10
- "options.optionsField.placeholder": "options",
11
- "object.label": "HubSpot object",
12
- "object.description": "Picked from the portal's configured objects",
13
- "object.loading": "Loading HubSpot objects…",
14
- "object.placeholder": "Choose an object…",
15
- "object.no-scope": "{object} — missing scope on the token",
16
- "settings.section": "HubSpot",
17
- "settings.link": "Configuration",
18
- "picker.loading": "Loading HubSpot properties…",
19
- "picker.placeholder": "Search a property…",
20
- "picker.free-placeholder": "e.g. hs_role, numberofemployees",
21
- "picker.load-error": "HubSpot properties unavailable",
22
- "picker.not-configured": "No HubSpot API key — free text. Set it in Settings → HubSpot.",
23
- "picker.object-unavailable": "Properties of “{object}” unreadable — missing scope on the token.",
24
- "picker.belongs-to": "{property} — belongs to {object}",
25
- "picker.unknown": "{property} — unknown to the portal",
26
- "picker.view": "View in HubSpot",
27
- "picker.no-results": "No matching property — properties are managed in HubSpot.",
28
- "picker.import": "Import the {count} options from HubSpot",
29
- "picker.imported": "Options imported — save to keep them.",
30
- "settings.subtitle": "The private app token used to read the portal's properties and validate form mappings.",
31
- "settings.configured": "Configured {hint}",
32
- "settings.not-configured": "Not configured",
33
- "settings.source": "source: {source}",
34
- "settings.source.settings": "set here",
35
- "settings.source.config": "config/plugins.ts",
36
- "settings.source.env": "environment variable",
37
- "settings.key.label": "Private app token",
38
- "settings.key.hint": "A key entered here overrides the ones from config/plugins.ts and HUBSPOT_API_KEY.",
39
- "settings.key.placeholder.set": "•••••••• (leave empty to keep)",
40
- "settings.key.placeholder.empty": "pat-eu1-…",
41
- "settings.save": "Save",
42
- "settings.test": "Test connection",
43
- "settings.remove": "Remove",
44
- "settings.loading": "Loading…",
45
- "settings.load-error": "Could not load settings.",
46
- "settings.saved": "Key saved.",
47
- "settings.save-error": "Could not save the key.",
48
- "settings.removed": "Key removed.",
49
- "settings.remove-error": "Could not remove the key.",
50
- "settings.test-ok": "Connection established — {count} readable properties.",
51
- "settings.test-no-key": "No key configured.",
52
- "settings.test-error": "HubSpot unreachable — invalid or revoked key?",
53
- "settings.scopes": "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.",
54
- "audit.title": "Mapping audit",
55
- "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.",
56
- "audit.run": "Run the audit",
57
- "audit.error": "Audit failed — HubSpot unreachable?",
58
- "audit.no-targets": "No content type to audit — declare them under `validate` in the plugin config.",
59
- "audit.clean": "Nothing to report — every mapping is valid.",
60
- "audit.summary": "{entries} entries · {mappings} mappings",
61
- "audit.target-error": "Could not scan: {error}",
62
- "failures.title": "Failed submissions",
63
- "failures.subtitle": "Submissions HubSpot couldn't take after the automatic retries. They can be inspected under Content Manager → HubSpot failed submissions.",
64
- "failures.count": "{count} queued",
65
- "failures.retry": "Replay now",
66
- "failures.retried": "{succeeded} replayed, {failed} still failing.",
67
- "failures.error": "Replay failed.",
68
- "problem.unknown": "“{property}” does not exist on {object} in this portal",
69
- "problem.wrong-object": "“{property}” exists on {actual}, not on {object}",
70
- "problem.whitespace": "“{property}” has surrounding whitespace",
71
- "problem.bad-option": "“{property}” does not accept {values}",
72
- "objects.contact": "Contact",
73
- "objects.company": "Company",
74
- "objects.deal": "Deal",
75
- "objects.ticket": "Ticket",
76
- "objects.product": "Product",
77
- "objects.line_item": "Line item",
78
- "objects.quote": "Quote"
79
- };
80
- export {
81
- en as default
82
- };