strapi-plugin-hubspot 0.15.0 → 0.16.1

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 (37) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +28 -13
  3. package/dist/_chunks/{Forms-ClyI4MJ1.js → Forms-D0QXkYAN.js} +216 -112
  4. package/dist/_chunks/{Forms-B7eIm0Fi.mjs → Forms-DEoZGrEt.mjs} +218 -114
  5. package/dist/_chunks/{HubspotFormInput-Do7F83V6.js → HubspotFormInput-4ZMYXWM_.js} +1 -1
  6. package/dist/_chunks/{HubspotFormInput-cZAiaAaq.mjs → HubspotFormInput-CPep55GL.mjs} +1 -1
  7. package/dist/_chunks/{HubspotObjectInput-DWqk2C26.js → HubspotObjectInput-BTqYXJIa.js} +3 -3
  8. package/dist/_chunks/{HubspotObjectInput-ZZk_7hWq.mjs → HubspotObjectInput-CQszIx1W.mjs} +3 -3
  9. package/dist/_chunks/{HubspotPropertyInput-v9B3wb1a.js → HubspotPropertyInput-2MgIKlR_.js} +3 -3
  10. package/dist/_chunks/{HubspotPropertyInput-5bkDGmGo.mjs → HubspotPropertyInput-ClNph0f1.mjs} +3 -3
  11. package/dist/_chunks/{Settings-QcFbm3s5.mjs → Settings-CZ-qQSKT.mjs} +78 -5
  12. package/dist/_chunks/{Settings-X3EHnTaO.js → Settings-DhiHQC6U.js} +77 -4
  13. package/dist/_chunks/{en-ZHvaZLi7.js → en-CSgFmKmM.js} +16 -3
  14. package/dist/_chunks/{en-DMJeF7EN.mjs → en-Dh3bMS6N.mjs} +16 -3
  15. package/dist/_chunks/{fr-Ds_2EAzZ.js → fr-D_EhZS0P.js} +16 -3
  16. package/dist/_chunks/{fr-CVAntLmc.mjs → fr-aWOIOlS9.mjs} +16 -3
  17. package/dist/_chunks/{index-BpcilDy1.js → index-ciliGv0E.js} +6 -6
  18. package/dist/_chunks/{index-BSJAhj8r.mjs → index-gKM4jVf8.mjs} +6 -6
  19. package/dist/_chunks/{objectLabels-CYXXFpNB.js → objectLabels-BjYfdhRB.js} +1 -1
  20. package/dist/_chunks/{objectLabels-CrYRzsqe.mjs → objectLabels-DQpSLUaJ.mjs} +1 -1
  21. package/dist/_chunks/{useHubspotSchema-CBF4GkmK.mjs → useHubspotSchema-CE7tJgOM.mjs} +1 -1
  22. package/dist/_chunks/{useHubspotSchema-Ba6xZUCL.js → useHubspotSchema-KKVppjC2.js} +1 -1
  23. package/dist/admin/index.js +1 -1
  24. package/dist/admin/index.mjs +1 -1
  25. package/dist/admin/src/builder/Sortable.d.ts +25 -12
  26. package/dist/admin/src/builder/__tests__/relocateField.test.d.ts +1 -0
  27. package/dist/admin/src/builder/relocateField.d.ts +14 -0
  28. package/dist/server/index.js +216 -7
  29. package/dist/server/index.mjs +216 -7
  30. package/dist/server/src/__tests__/formSync.test.d.ts +1 -0
  31. package/dist/server/src/formSync.d.ts +37 -0
  32. package/dist/server/src/forms.d.ts +6 -8
  33. package/dist/server/src/hsforms.d.ts +8 -0
  34. package/dist/server/src/importHubspot.d.ts +4 -0
  35. package/dist/server/src/index.d.ts +6 -0
  36. package/dist/server/src/settings.d.ts +32 -3
  37. package/package.json +1 -1
@@ -5,12 +5,12 @@ import { u as useIntl } from "./index-CEh8vkxY.mjs";
5
5
  import { Typography, Flex, Box, SingleSelect, SingleSelectOption, MultiSelect, MultiSelectOption, TextInput, IconButton, Button, Combobox, ComboboxOption, Field, Textarea, Checkbox, Divider, Loader, Badge, Table, Thead, Tr, Th, Tbody, Td, Modal } from "@strapi/design-system";
6
6
  import { Trash, Plus, ArrowClockwise, ArrowLeft, Drag, ArrowUp, ArrowDown, List, Duplicate, Download } from "@strapi/icons";
7
7
  import { useFetchClient } from "@strapi/strapi/admin";
8
- import { g as getTranslation, P as PLUGIN_ID } from "./index-BSJAhj8r.mjs";
9
- import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter } from "@dnd-kit/core";
10
- import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from "@dnd-kit/sortable";
8
+ import { g as getTranslation, P as PLUGIN_ID } from "./index-gKM4jVf8.mjs";
9
+ import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, useDroppable, closestCorners } from "@dnd-kit/core";
10
+ import { arrayMove, sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
11
11
  import { CSS } from "@dnd-kit/utilities";
12
- import { o as objectLabel } from "./objectLabels-CrYRzsqe.mjs";
13
- import { u as useHubspotSchema } from "./useHubspotSchema-CBF4GkmK.mjs";
12
+ import { o as objectLabel } from "./objectLabels-DQpSLUaJ.mjs";
13
+ import { u as useHubspotSchema } from "./useHubspotSchema-CE7tJgOM.mjs";
14
14
  const OPERATORS = [
15
15
  "eq",
16
16
  "neq",
@@ -153,32 +153,107 @@ const ConditionEditor = ({ condition, candidates, onChange }) => {
153
153
  ] })
154
154
  ] });
155
155
  };
156
- function SortableList({
157
- ids,
158
- onReorder,
159
- children
156
+ function relocateField(steps, fieldId, overId) {
157
+ const next = steps.map((step) => ({ ...step, fields: [...step.fields] }));
158
+ let fromStep = -1;
159
+ let fromIndex = -1;
160
+ for (let i = 0; i < next.length; i += 1) {
161
+ const index = next[i].fields.findIndex((field2) => field2.id === fieldId);
162
+ if (index !== -1) {
163
+ fromStep = i;
164
+ fromIndex = index;
165
+ break;
166
+ }
167
+ }
168
+ if (fromStep === -1) return steps;
169
+ let toStep = -1;
170
+ let toIndex = -1;
171
+ const containerId = overId.startsWith("container:") ? overId.slice("container:".length) : "";
172
+ if (containerId) {
173
+ toStep = next.findIndex((step) => step.id === containerId);
174
+ toIndex = toStep === -1 ? -1 : next[toStep].fields.length;
175
+ } else {
176
+ for (let i = 0; i < next.length; i += 1) {
177
+ const index = next[i].fields.findIndex((field2) => field2.id === overId);
178
+ if (index !== -1) {
179
+ toStep = i;
180
+ toIndex = index;
181
+ break;
182
+ }
183
+ }
184
+ if (toStep === -1) {
185
+ toStep = next.findIndex((step) => step.id === overId);
186
+ toIndex = toStep === -1 ? -1 : next[toStep].fields.length;
187
+ }
188
+ }
189
+ if (toStep === -1 || toIndex === -1) return steps;
190
+ if (fromStep === toStep) {
191
+ if (fromIndex === toIndex) return steps;
192
+ next[fromStep].fields = arrayMove(next[fromStep].fields, fromIndex, toIndex);
193
+ return next;
194
+ }
195
+ const [field] = next[fromStep].fields.splice(fromIndex, 1);
196
+ if (!field) return steps;
197
+ next[toStep].fields.splice(toIndex, 0, field);
198
+ return next;
199
+ }
200
+ const collisionDetection = (args) => {
201
+ const type = args.active.data.current?.type;
202
+ const droppableContainers = args.droppableContainers.filter((container) => {
203
+ const other = container.data.current?.type;
204
+ if (type === "step") return other === "step";
205
+ if (type === "field") return other === "field" || other === "container";
206
+ return true;
207
+ });
208
+ return closestCorners({ ...args, droppableContainers });
209
+ };
210
+ function SortableBoard({
211
+ children,
212
+ onDragEnd
160
213
  }) {
161
214
  const sensors = useSensors(
162
215
  // An 8px activation distance keeps clicks (selection!) from starting drags.
163
216
  useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
164
217
  useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
165
218
  );
166
- const handleDragEnd = (event) => {
167
- const { active, over } = event;
168
- if (!over || active.id === over.id) return;
169
- const from = ids.indexOf(String(active.id));
170
- const to = ids.indexOf(String(over.id));
171
- if (from === -1 || to === -1) return;
172
- onReorder(arrayMove(ids, from, to));
173
- };
174
- return /* @__PURE__ */ jsx(DndContext, { sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children }) });
219
+ return /* @__PURE__ */ jsx(DndContext, { sensors, collisionDetection, onDragEnd, children });
220
+ }
221
+ function SortableGroup({
222
+ ids,
223
+ children
224
+ }) {
225
+ return /* @__PURE__ */ jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children });
226
+ }
227
+ function SortableContainer({
228
+ stepId,
229
+ children
230
+ }) {
231
+ const { setNodeRef, isOver } = useDroppable({
232
+ id: `container:${stepId}`,
233
+ data: { type: "container", stepId }
234
+ });
235
+ return /* @__PURE__ */ jsx(
236
+ "div",
237
+ {
238
+ ref: setNodeRef,
239
+ style: {
240
+ minHeight: 8,
241
+ borderRadius: 4,
242
+ outline: isOver ? "2px dashed currentColor" : void 0,
243
+ outlineOffset: 4
244
+ },
245
+ children
246
+ }
247
+ );
175
248
  }
176
249
  function SortableItem({
177
250
  id,
251
+ data,
178
252
  children
179
253
  }) {
180
254
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
181
- id
255
+ id,
256
+ data
182
257
  });
183
258
  return /* @__PURE__ */ jsx(
184
259
  "div",
@@ -774,11 +849,32 @@ const FormEditor = () => {
774
849
  setBusy(true);
775
850
  setFeedback(null);
776
851
  try {
777
- await post(`/${PLUGIN_ID}/builder/forms/${documentId}/publish${query}`);
852
+ const { data } = await post(`/${PLUGIN_ID}/builder/forms/${documentId}/publish${query}`);
778
853
  setPublished(true);
779
854
  setErrors([]);
780
855
  setProblems([]);
781
- setFeedback({ tone: "success", text: t("editor.published", "Form published.") });
856
+ const added = Array.isArray(data?.addedFields) ? data.addedFields.length : 0;
857
+ if (data?.syncWarning) {
858
+ setFeedback({
859
+ tone: "success",
860
+ text: t(
861
+ "editor.published-sync-warn",
862
+ "Form published. HubSpot field sync failed: {error}",
863
+ { error: data.syncWarning }
864
+ )
865
+ });
866
+ } else if (added) {
867
+ setFeedback({
868
+ tone: "success",
869
+ text: t(
870
+ "editor.published-synced",
871
+ "Form published. {count, plural, one {# field added} other {# fields added}} to the HubSpot form.",
872
+ { count: added }
873
+ )
874
+ });
875
+ } else {
876
+ setFeedback({ tone: "success", text: t("editor.published", "Form published.") });
877
+ }
782
878
  } catch (err) {
783
879
  const data = err.response?.data;
784
880
  setErrors(data?.errors ?? []);
@@ -848,14 +944,28 @@ const FormEditor = () => {
848
944
  /* @__PURE__ */ jsxs(Flex, { gap: 6, alignItems: "flex-start", children: [
849
945
  /* @__PURE__ */ jsx(Box, { flex: "1", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
850
946
  /* @__PURE__ */ jsx(
851
- SortableList,
947
+ SortableBoard,
852
948
  {
853
- ids: definition.steps.map((s) => s.id),
854
- onReorder: (ids) => patchDefinition((steps) => reorderByIds(steps, ids)),
855
- children: definition.steps.map((step, stepIndex) => /* @__PURE__ */ jsx(
949
+ onDragEnd: ({ active, over }) => {
950
+ if (!over || active.id === over.id) return;
951
+ const kind = active.data.current?.type;
952
+ if (kind === "step") {
953
+ const ids = definition.steps.map((s) => s.id);
954
+ const from = ids.indexOf(String(active.id));
955
+ const to = ids.indexOf(String(over.id));
956
+ if (from === -1 || to === -1) return;
957
+ patchDefinition((steps) => reorderByIds(steps, arrayMove(ids, from, to)));
958
+ return;
959
+ }
960
+ if (kind === "field") {
961
+ patchDefinition((steps) => relocateField(steps, String(active.id), String(over.id)));
962
+ }
963
+ },
964
+ children: /* @__PURE__ */ jsx(SortableGroup, { ids: definition.steps.map((s) => s.id), children: definition.steps.map((step, stepIndex) => /* @__PURE__ */ jsx(
856
965
  SortableItem,
857
966
  {
858
967
  id: step.id,
968
+ data: { type: "step" },
859
969
  children: (stepHandle) => /* @__PURE__ */ jsxs(
860
970
  Box,
861
971
  {
@@ -931,95 +1041,89 @@ const FormEditor = () => {
931
1041
  ] })
932
1042
  ] }),
933
1043
  /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
934
- /* @__PURE__ */ jsx(
935
- SortableList,
936
- {
937
- ids: step.fields.map((f) => f.id),
938
- onReorder: (ids) => patchStep(step.id, { fields: reorderByIds(step.fields, ids) }),
939
- children: step.fields.map((field, fieldIndex) => {
940
- const flags = fieldFlags(field.id);
941
- const isSelected = selection.kind === "field" && selection.fieldId === field.id;
942
- return /* @__PURE__ */ jsx(
943
- SortableItem,
1044
+ /* @__PURE__ */ jsx(SortableContainer, { stepId: step.id, children: /* @__PURE__ */ jsx(SortableGroup, { ids: step.fields.map((f) => f.id), children: step.fields.map((field, fieldIndex) => {
1045
+ const flags = fieldFlags(field.id);
1046
+ const isSelected = selection.kind === "field" && selection.fieldId === field.id;
1047
+ return /* @__PURE__ */ jsx(
1048
+ SortableItem,
1049
+ {
1050
+ id: field.id,
1051
+ data: { type: "field", stepId: step.id },
1052
+ children: (fieldHandle) => /* @__PURE__ */ jsx(
1053
+ Box,
944
1054
  {
945
- id: field.id,
946
- children: (fieldHandle) => /* @__PURE__ */ jsx(
947
- Box,
948
- {
949
- background: isSelected ? "primary100" : "neutral100",
950
- hasRadius: true,
951
- padding: 3,
952
- onClick: (e) => {
953
- e.stopPropagation();
954
- setSelection({ kind: "field", fieldId: field.id });
955
- },
956
- style: { cursor: "pointer" },
957
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
958
- /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
959
- /* @__PURE__ */ jsx(
960
- "span",
961
- {
962
- ...fieldHandle.attributes,
963
- ...fieldHandle.listeners,
964
- onClick: (e) => e.stopPropagation(),
965
- style: { cursor: "grab", display: "inline-flex" },
966
- "aria-label": t("editor.drag-field", "Drag to reorder the field"),
967
- children: /* @__PURE__ */ jsx(Drag, {})
968
- }
969
- ),
970
- /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: field.label || field.name || "—" }),
971
- /* @__PURE__ */ jsx(Badge, { children: field.type }),
972
- field.required && /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: t("editor.required", "required") }),
973
- field.hubspot?.property && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
974
- field.hubspot.object ?? "contact",
975
- ".",
976
- field.hubspot.property
977
- ] }),
978
- field.visibleIf?.rules?.length ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "alternative100", textColor: "alternative700", children: t("editor.conditional", "Conditional") }) : null,
979
- (flags.error || flags.problem) && /* @__PURE__ */ jsx(Badge, { backgroundColor: "danger100", textColor: "danger700", children: flags.error?.code ?? flags.problem?.code })
980
- ] }),
981
- /* @__PURE__ */ jsxs(Flex, { gap: 1, onClick: (e) => e.stopPropagation(), children: [
982
- /* @__PURE__ */ jsx(
983
- IconButton,
984
- {
985
- label: t("editor.field-up", "Move up"),
986
- onClick: () => moveField(stepIndex, fieldIndex, -1),
987
- disabled: stepIndex === 0 && fieldIndex === 0,
988
- children: /* @__PURE__ */ jsx(ArrowUp, {})
989
- }
990
- ),
991
- /* @__PURE__ */ jsx(
992
- IconButton,
993
- {
994
- label: t("editor.field-down", "Move down"),
995
- onClick: () => moveField(stepIndex, fieldIndex, 1),
996
- disabled: stepIndex === definition.steps.length - 1 && fieldIndex === step.fields.length - 1,
997
- children: /* @__PURE__ */ jsx(ArrowDown, {})
998
- }
999
- ),
1000
- /* @__PURE__ */ jsx(
1001
- IconButton,
1002
- {
1003
- label: t("editor.field-delete", "Delete the field"),
1004
- onClick: () => {
1005
- setSelection({ kind: "form" });
1006
- patchStep(step.id, {
1007
- fields: step.fields.filter((f) => f.id !== field.id)
1008
- });
1009
- },
1010
- children: /* @__PURE__ */ jsx(Trash, {})
1011
- }
1012
- )
1013
- ] })
1014
- ] })
1015
- }
1016
- )
1017
- },
1018
- field.id
1019
- );
1020
- })
1021
- }
1022
- ),
1055
+ background: isSelected ? "primary100" : "neutral100",
1056
+ hasRadius: true,
1057
+ padding: 3,
1058
+ onClick: (e) => {
1059
+ e.stopPropagation();
1060
+ setSelection({ kind: "field", fieldId: field.id });
1061
+ },
1062
+ style: { cursor: "pointer" },
1063
+ children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
1064
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
1065
+ /* @__PURE__ */ jsx(
1066
+ "span",
1067
+ {
1068
+ ...fieldHandle.attributes,
1069
+ ...fieldHandle.listeners,
1070
+ onClick: (e) => e.stopPropagation(),
1071
+ style: { cursor: "grab", display: "inline-flex" },
1072
+ "aria-label": t("editor.drag-field", "Drag to move the field, including to another step"),
1073
+ children: /* @__PURE__ */ jsx(Drag, {})
1074
+ }
1075
+ ),
1076
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: field.label || field.name || "—" }),
1077
+ /* @__PURE__ */ jsx(Badge, { children: field.type }),
1078
+ field.required && /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral150", textColor: "neutral600", children: t("editor.required", "required") }),
1079
+ field.hubspot?.property && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary700", children: [
1080
+ field.hubspot.object ?? "contact",
1081
+ ".",
1082
+ field.hubspot.property
1083
+ ] }),
1084
+ field.visibleIf?.rules?.length ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "alternative100", textColor: "alternative700", children: t("editor.conditional", "Conditional") }) : null,
1085
+ (flags.error || flags.problem) && /* @__PURE__ */ jsx(Badge, { backgroundColor: "danger100", textColor: "danger700", children: flags.error?.code ?? flags.problem?.code })
1086
+ ] }),
1087
+ /* @__PURE__ */ jsxs(Flex, { gap: 1, onClick: (e) => e.stopPropagation(), children: [
1088
+ /* @__PURE__ */ jsx(
1089
+ IconButton,
1090
+ {
1091
+ label: t("editor.field-up", "Move up"),
1092
+ onClick: () => moveField(stepIndex, fieldIndex, -1),
1093
+ disabled: stepIndex === 0 && fieldIndex === 0,
1094
+ children: /* @__PURE__ */ jsx(ArrowUp, {})
1095
+ }
1096
+ ),
1097
+ /* @__PURE__ */ jsx(
1098
+ IconButton,
1099
+ {
1100
+ label: t("editor.field-down", "Move down"),
1101
+ onClick: () => moveField(stepIndex, fieldIndex, 1),
1102
+ disabled: stepIndex === definition.steps.length - 1 && fieldIndex === step.fields.length - 1,
1103
+ children: /* @__PURE__ */ jsx(ArrowDown, {})
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ jsx(
1107
+ IconButton,
1108
+ {
1109
+ label: t("editor.field-delete", "Delete the field"),
1110
+ onClick: () => {
1111
+ setSelection({ kind: "form" });
1112
+ patchStep(step.id, {
1113
+ fields: step.fields.filter((f) => f.id !== field.id)
1114
+ });
1115
+ },
1116
+ children: /* @__PURE__ */ jsx(Trash, {})
1117
+ }
1118
+ )
1119
+ ] })
1120
+ ] })
1121
+ }
1122
+ )
1123
+ },
1124
+ field.id
1125
+ );
1126
+ }) }) }),
1023
1127
  /* @__PURE__ */ jsx(
1024
1128
  Button,
1025
1129
  {
@@ -1040,7 +1144,7 @@ const FormEditor = () => {
1040
1144
  )
1041
1145
  },
1042
1146
  step.id
1043
- ))
1147
+ )) })
1044
1148
  }
1045
1149
  ),
1046
1150
  /* @__PURE__ */ jsx(
@@ -5,7 +5,7 @@ const React = require("react");
5
5
  const index = require("./index-DkTxsEqL.js");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const admin = require("@strapi/strapi/admin");
8
- const index$1 = require("./index-BpcilDy1.js");
8
+ const index$1 = require("./index-ciliGv0E.js");
9
9
  function _interopNamespace(e) {
10
10
  if (e && e.__esModule) return e;
11
11
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import { u as useIntl } from "./index-CEh8vkxY.mjs";
4
4
  import { Field, Flex, Loader, Combobox, ComboboxOption } from "@strapi/design-system";
5
5
  import { useFetchClient } from "@strapi/strapi/admin";
6
- import { P as PLUGIN_ID, g as getTranslation } from "./index-BSJAhj8r.mjs";
6
+ import { P as PLUGIN_ID, g as getTranslation } from "./index-gKM4jVf8.mjs";
7
7
  const HubspotFormInput = React.forwardRef(
8
8
  ({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
9
9
  const { formatMessage } = useIntl();
@@ -4,9 +4,9 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
5
  const index = require("./index-DkTxsEqL.js");
6
6
  const designSystem = require("@strapi/design-system");
7
- const index$1 = require("./index-BpcilDy1.js");
8
- const objectLabels = require("./objectLabels-CYXXFpNB.js");
9
- const useHubspotSchema = require("./useHubspotSchema-Ba6xZUCL.js");
7
+ const index$1 = require("./index-ciliGv0E.js");
8
+ const objectLabels = require("./objectLabels-BjYfdhRB.js");
9
+ const useHubspotSchema = require("./useHubspotSchema-KKVppjC2.js");
10
10
  function _interopNamespace(e) {
11
11
  if (e && e.__esModule) return e;
12
12
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -2,9 +2,9 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { u as useIntl } from "./index-CEh8vkxY.mjs";
4
4
  import { Field, Flex, Loader, SingleSelect, SingleSelectOption } from "@strapi/design-system";
5
- import { g as getTranslation } from "./index-BSJAhj8r.mjs";
6
- import { o as objectLabel } from "./objectLabels-CrYRzsqe.mjs";
7
- import { u as useHubspotSchema } from "./useHubspotSchema-CBF4GkmK.mjs";
5
+ import { g as getTranslation } from "./index-gKM4jVf8.mjs";
6
+ import { o as objectLabel } from "./objectLabels-DQpSLUaJ.mjs";
7
+ import { u as useHubspotSchema } from "./useHubspotSchema-CE7tJgOM.mjs";
8
8
  const HubspotObjectInput = React.forwardRef(
9
9
  ({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
10
10
  const intl = useIntl();
@@ -6,9 +6,9 @@ const index = require("./index-DkTxsEqL.js");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const icons = require("@strapi/icons");
8
8
  const admin = require("@strapi/strapi/admin");
9
- const index$1 = require("./index-BpcilDy1.js");
10
- const objectLabels = require("./objectLabels-CYXXFpNB.js");
11
- const useHubspotSchema = require("./useHubspotSchema-Ba6xZUCL.js");
9
+ const index$1 = require("./index-ciliGv0E.js");
10
+ const objectLabels = require("./objectLabels-BjYfdhRB.js");
11
+ const useHubspotSchema = require("./useHubspotSchema-KKVppjC2.js");
12
12
  function _interopNamespace(e) {
13
13
  if (e && e.__esModule) return e;
14
14
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -4,9 +4,9 @@ import { u as useIntl } from "./index-CEh8vkxY.mjs";
4
4
  import { Field, Flex, Loader, Combobox, ComboboxOption, IconButton, Button, Typography, Link } from "@strapi/design-system";
5
5
  import { ArrowClockwise } from "@strapi/icons";
6
6
  import { useField } from "@strapi/strapi/admin";
7
- import { g as getTranslation } from "./index-BSJAhj8r.mjs";
8
- import { o as objectLabel } from "./objectLabels-CrYRzsqe.mjs";
9
- import { u as useHubspotSchema } from "./useHubspotSchema-CBF4GkmK.mjs";
7
+ import { g as getTranslation } from "./index-gKM4jVf8.mjs";
8
+ import { o as objectLabel } from "./objectLabels-DQpSLUaJ.mjs";
9
+ import { u as useHubspotSchema } from "./useHubspotSchema-CE7tJgOM.mjs";
10
10
  const oneLine = {
11
11
  display: "block",
12
12
  whiteSpace: "nowrap",
@@ -1,11 +1,11 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { u as useIntl } from "./index-CEh8vkxY.mjs";
4
- import { Flex, Divider, Typography, Button, Box, Link, Badge, Loader, Field, SingleSelect, SingleSelectOption } from "@strapi/design-system";
4
+ import { Flex, Divider, Typography, Button, Box, Link, Badge, Loader, Field, SingleSelect, SingleSelectOption, Checkbox } from "@strapi/design-system";
5
5
  import { useFetchClient } from "@strapi/strapi/admin";
6
- import { g as getTranslation, P as PLUGIN_ID } from "./index-BSJAhj8r.mjs";
6
+ import { g as getTranslation, P as PLUGIN_ID } from "./index-gKM4jVf8.mjs";
7
7
  import { Link as Link$1 } from "react-router-dom";
8
- import { o as objectLabel } from "./objectLabels-CrYRzsqe.mjs";
8
+ import { o as objectLabel } from "./objectLabels-DQpSLUaJ.mjs";
9
9
  const AuditSection = ({ configured }) => {
10
10
  const intl = useIntl();
11
11
  const { formatMessage } = intl;
@@ -181,6 +181,9 @@ const HubspotSettings = () => {
181
181
  const [portalId, setPortalId] = React.useState("");
182
182
  const [region, setRegion] = React.useState("eu1");
183
183
  const [defaultFormId, setDefaultFormId] = React.useState("");
184
+ const [submissionMode, setSubmissionMode] = React.useState("auto");
185
+ const [writeExtraProperties, setWriteExtraProperties] = React.useState(true);
186
+ const [syncFieldsOnPublish, setSyncFieldsOnPublish] = React.useState(false);
184
187
  const [forms, setForms] = React.useState([]);
185
188
  const [busy, setBusy] = React.useState(false);
186
189
  const [feedback, setFeedback] = React.useState(
@@ -192,6 +195,9 @@ const HubspotSettings = () => {
192
195
  setPortalId(data.portalId ?? "");
193
196
  setRegion(data.region || "eu1");
194
197
  setDefaultFormId(data.defaultFormId ?? "");
198
+ setSubmissionMode(data.submissionMode === "crm" || data.submissionMode === "forms" ? data.submissionMode : "auto");
199
+ setWriteExtraProperties(data.writeExtraProperties !== false);
200
+ setSyncFieldsOnPublish(data.syncFieldsOnPublish === true);
195
201
  setForms(Array.isArray(data.forms) ? data.forms : []);
196
202
  };
197
203
  const load = React.useCallback(async () => {
@@ -211,7 +217,10 @@ const HubspotSettings = () => {
211
217
  ...apiKey.trim() ? { apiKey: apiKey.trim() } : {},
212
218
  portalId,
213
219
  region,
214
- defaultFormId
220
+ defaultFormId,
221
+ submissionMode,
222
+ writeExtraProperties,
223
+ syncFieldsOnPublish
215
224
  });
216
225
  setApiKey("");
217
226
  await load();
@@ -381,7 +390,71 @@ const HubspotSettings = () => {
381
390
  sourceHint(settings.formSource)
382
391
  ]
383
392
  }
384
- )
393
+ ),
394
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", children: t("settings.policy.title", "How submissions reach HubSpot") }),
395
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
396
+ "settings.policy.hint",
397
+ "Pick the policy that matches this portal. Defaults stay safe for every install: conversions when a form is linked, leftover answers still written on the contact, HubSpot forms never mutated unless you ask."
398
+ ) }),
399
+ /* @__PURE__ */ jsxs(
400
+ Field.Root,
401
+ {
402
+ name: "submissionMode",
403
+ hint: t(
404
+ "settings.mode.hint",
405
+ "Automatic uses the marketing form when one is linked, otherwise a CRM upsert. CRM-only never counts as a conversion. Forms-only prefers the Forms API and falls back to the CRM if HubSpot refuses the submit."
406
+ ),
407
+ children: [
408
+ /* @__PURE__ */ jsx(Field.Label, { children: t("settings.mode.label", "Submission path") }),
409
+ /* @__PURE__ */ jsxs(
410
+ SingleSelect,
411
+ {
412
+ value: submissionMode,
413
+ onChange: (value) => {
414
+ const next = String(value);
415
+ if (next === "crm" || next === "forms" || next === "auto") setSubmissionMode(next);
416
+ },
417
+ children: [
418
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "auto", children: t("settings.mode.auto", "Automatic — form conversion if linked, else CRM") }),
419
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "forms", children: t("settings.mode.forms", "Marketing form — native HubSpot conversions") }),
420
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "crm", children: t("settings.mode.crm", "CRM only — upsert the contact, no form conversion") })
421
+ ]
422
+ }
423
+ ),
424
+ /* @__PURE__ */ jsx(Field.Hint, {})
425
+ ]
426
+ }
427
+ ),
428
+ /* @__PURE__ */ jsx(
429
+ Checkbox,
430
+ {
431
+ checked: writeExtraProperties,
432
+ onCheckedChange: (checked) => setWriteExtraProperties(checked === true),
433
+ children: t(
434
+ "settings.extra.label",
435
+ "After a conversion, also write mapped contact fields that the HubSpot form doesn't have"
436
+ )
437
+ }
438
+ ),
439
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
440
+ "settings.extra.hint",
441
+ "The Forms API drops unknown fields (they 400 the submit). With this on, leftovers still land on the contact. Turn it off if HubSpot must only receive what the marketing form declares."
442
+ ) }),
443
+ /* @__PURE__ */ jsx(
444
+ Checkbox,
445
+ {
446
+ checked: syncFieldsOnPublish,
447
+ onCheckedChange: (checked) => setSyncFieldsOnPublish(checked === true),
448
+ children: t(
449
+ "settings.sync.label",
450
+ "On publish, add mapped contact fields that are missing on the HubSpot form"
451
+ )
452
+ }
453
+ ),
454
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: t(
455
+ "settings.sync.hint",
456
+ "Strapi-first installs: a field added here appears on the HubSpot form next publish. HubSpot-first installs should leave this off — importing a form already carries its fields. Properties themselves are never created; map to ones that exist in the portal."
457
+ ) })
385
458
  ] }) }),
386
459
  /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
387
460
  /* @__PURE__ */ jsx(Button, { onClick: save, loading: busy, children: t("settings.save", "Save") }),