strapi-plugin-hubspot 0.8.0 → 0.9.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 (36) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +28 -0
  3. package/dist/_chunks/{Forms-77Mornxt.mjs → Forms-BsKys-Hc.mjs} +295 -29
  4. package/dist/_chunks/{Forms-B8kkPY0c.js → Forms-JuzNfsbL.js} +292 -26
  5. package/dist/_chunks/{HubspotFormInput-BVknnlmR.mjs → HubspotFormInput-CIvHS5rp.mjs} +1 -1
  6. package/dist/_chunks/{HubspotFormInput-kx3LlQYF.js → HubspotFormInput-ph9pe6xY.js} +1 -1
  7. package/dist/_chunks/{HubspotObjectInput-B-Y1PZ6c.mjs → HubspotObjectInput-BX1qYD2c.mjs} +3 -3
  8. package/dist/_chunks/{HubspotObjectInput-BOO1PvOj.js → HubspotObjectInput-dAd-lMdD.js} +3 -3
  9. package/dist/_chunks/{HubspotPropertyInput-awfD1opH.js → HubspotPropertyInput-BM3ZaY8f.js} +3 -3
  10. package/dist/_chunks/{HubspotPropertyInput-Drxn_Ddt.mjs → HubspotPropertyInput-C2Z6472q.mjs} +3 -3
  11. package/dist/_chunks/{Settings-BT1NhvxO.js → Settings-Cdcjy9DA.js} +2 -2
  12. package/dist/_chunks/{Settings-DrujhMxS.mjs → Settings-DnwGetcc.mjs} +2 -2
  13. package/dist/_chunks/{en-BYKKi1LK.mjs → en-CcQWnXra.mjs} +41 -1
  14. package/dist/_chunks/{en-B8adhJhO.js → en-Cqfair98.js} +41 -1
  15. package/dist/_chunks/{fr-DzgArgi3.mjs → fr-CBRZ9q8U.mjs} +41 -1
  16. package/dist/_chunks/{fr-DnKtGXaB.js → fr-D117-3Ta.js} +41 -1
  17. package/dist/_chunks/{index-P7p8ASb6.js → index-BbRtXnAL.js} +6 -6
  18. package/dist/_chunks/{index-LrCSAHoH.mjs → index-LTaVoyWJ.mjs} +6 -6
  19. package/dist/_chunks/{objectLabels-yNrnTOBo.mjs → objectLabels-DE2xGq7f.mjs} +1 -1
  20. package/dist/_chunks/{objectLabels-Dn-zriuZ.js → objectLabels-DU0Cagt_.js} +1 -1
  21. package/dist/_chunks/{useHubspotSchema-DccG2KbZ.mjs → useHubspotSchema-BSKow0X3.mjs} +1 -1
  22. package/dist/_chunks/{useHubspotSchema-BottzdRR.js → useHubspotSchema-DzBMAxv7.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/types.d.ts +25 -0
  26. package/dist/admin/src/pages/Forms.d.ts +5 -1
  27. package/dist/admin/src/pages/Submissions.d.ts +8 -0
  28. package/dist/server/index.js +329 -13
  29. package/dist/server/index.mjs +329 -13
  30. package/dist/server/src/__tests__/importHubspot.test.d.ts +1 -0
  31. package/dist/server/src/__tests__/submissions.test.d.ts +1 -0
  32. package/dist/server/src/formsAdmin.d.ts +13 -0
  33. package/dist/server/src/importHubspot.d.ts +85 -0
  34. package/dist/server/src/index.d.ts +28 -1
  35. package/dist/server/src/submissions.d.ts +28 -0
  36. package/package.json +2 -2
@@ -7,9 +7,9 @@ const index = require("./index-DkTxsEqL.js");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const icons = require("@strapi/icons");
9
9
  const admin = require("@strapi/strapi/admin");
10
- const index$1 = require("./index-P7p8ASb6.js");
11
- const objectLabels = require("./objectLabels-Dn-zriuZ.js");
12
- const useHubspotSchema = require("./useHubspotSchema-BottzdRR.js");
10
+ const index$1 = require("./index-BbRtXnAL.js");
11
+ const objectLabels = require("./objectLabels-DU0Cagt_.js");
12
+ const useHubspotSchema = require("./useHubspotSchema-DzBMAxv7.js");
13
13
  function _interopNamespace(e) {
14
14
  if (e && e.__esModule) return e;
15
15
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -930,6 +930,12 @@ const FormsList = () => {
930
930
  const [error, setError] = React__namespace.useState(null);
931
931
  const [sources, setSources] = React__namespace.useState([]);
932
932
  const [sourceId, setSourceId] = React__namespace.useState("");
933
+ const [hsForms, setHsForms] = React__namespace.useState([]);
934
+ const [hsFormId, setHsFormId] = React__namespace.useState("");
935
+ const [hsError, setHsError] = React__namespace.useState(null);
936
+ const [report, setReport] = React__namespace.useState(
937
+ null
938
+ );
933
939
  const t = (id, defaultMessage, values) => formatMessage({ id: index$1.getTranslation(id), defaultMessage }, values);
934
940
  const load = React__namespace.useCallback(async () => {
935
941
  const { data } = await get(`/${index$1.PLUGIN_ID}/builder/forms`);
@@ -940,6 +946,17 @@ const FormsList = () => {
940
946
  get(
941
947
  `/${index$1.PLUGIN_ID}/builder/import/sources`
942
948
  ).then(({ data }) => setSources(data.sources ?? [])).catch(() => setSources([]));
949
+ get(`/${index$1.PLUGIN_ID}/builder/import/hubspot`).then(({ data }) => setHsForms(data.configured ? data.forms ?? [] : [])).catch(
950
+ () => (
951
+ // Reachable settings but no portal list: almost always the missing scope.
952
+ setHsError(
953
+ t(
954
+ "forms.hubspot-list-error",
955
+ "Could not list the portal's forms — the token needs the `forms` read scope."
956
+ )
957
+ )
958
+ )
959
+ );
943
960
  }, [load]);
944
961
  const runImport = async () => {
945
962
  if (!sourceId) return;
@@ -955,6 +972,48 @@ const FormsList = () => {
955
972
  setBusy(false);
956
973
  }
957
974
  };
975
+ const runHubspotImport = async () => {
976
+ if (!hsFormId) return;
977
+ setBusy(true);
978
+ setError(null);
979
+ setReport(null);
980
+ try {
981
+ const { data } = await post(
982
+ `/${index$1.PLUGIN_ID}/builder/import/hubspot`,
983
+ { formId: hsFormId }
984
+ );
985
+ if (data.skipped.length) {
986
+ setReport(data);
987
+ setBusy(false);
988
+ await load();
989
+ } else {
990
+ navigate(data.documentId);
991
+ }
992
+ } catch {
993
+ setError(t("forms.import-error", "Could not import the form."));
994
+ setBusy(false);
995
+ }
996
+ };
997
+ const skippedLine = (item) => {
998
+ const label = item.label ?? "";
999
+ const detail = item.detail ?? "";
1000
+ switch (item.code) {
1001
+ case "field-type":
1002
+ return t("forms.skipped-field-type", '"{label}" skipped — unsupported type ({detail})', { label, detail });
1003
+ case "hidden-field":
1004
+ return t("forms.skipped-hidden", '"{label}" skipped — hidden fields are not supported yet', { label });
1005
+ case "object":
1006
+ return t("forms.skipped-object", '"{label}" skipped — unmapped object ({detail})', { label, detail });
1007
+ case "duplicate":
1008
+ return t("forms.skipped-duplicate", '"{label}" skipped — the name "{detail}" is already used', { label, detail });
1009
+ case "condition":
1010
+ return t("forms.skipped-condition", 'Condition on "{label}" dropped — no equivalent operator ({detail})', { label, detail });
1011
+ case "rich-text":
1012
+ return t("forms.skipped-rich-text", "A content block was skipped — the builder has no rich-text element");
1013
+ case "legal-consent":
1014
+ return t("forms.skipped-consent", "The GDPR consent block was skipped — rebuild it as a field if needed");
1015
+ }
1016
+ };
958
1017
  const create = async () => {
959
1018
  if (!name.trim()) return;
960
1019
  setBusy(true);
@@ -1007,6 +1066,7 @@ const FormsList = () => {
1007
1066
  ) })
1008
1067
  ] }),
1009
1068
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1069
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.List, {}), onClick: () => navigate("submissions"), children: t("forms.submissions", "Submissions") }),
1010
1070
  /* @__PURE__ */ jsxRuntime.jsx(
1011
1071
  designSystem.TextInput,
1012
1072
  {
@@ -1021,38 +1081,74 @@ const FormsList = () => {
1021
1081
  ] })
1022
1082
  ] }),
1023
1083
  error && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: error }) }),
1024
- sources.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 4, marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", justifyContent: "space-between", children: [
1084
+ (sources.length > 0 || hsForms.length > 0 || hsError) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 4, marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1085
+ hsForms.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", justifyContent: "space-between", children: [
1086
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1087
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: t("forms.import-hubspot-title", "Import from your HubSpot portal") }),
1088
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: t(
1089
+ "forms.import-hubspot-hint",
1090
+ "Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing overwrites the draft."
1091
+ ) })
1092
+ ] }),
1093
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1094
+ /* @__PURE__ */ jsxRuntime.jsx(
1095
+ designSystem.SingleSelect,
1096
+ {
1097
+ "aria-label": t("forms.import-hubspot-source", "HubSpot form to import"),
1098
+ placeholder: t("forms.import-hubspot-source", "HubSpot form to import"),
1099
+ value: hsFormId,
1100
+ onChange: (v) => setHsFormId(String(v)),
1101
+ children: hsForms.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: f.id, children: f.name }, f.id))
1102
+ }
1103
+ ),
1104
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: runHubspotImport, disabled: busy || !hsFormId, children: t("forms.import", "Import") })
1105
+ ] })
1106
+ ] }),
1107
+ hsError && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", children: hsError }),
1108
+ sources.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", justifyContent: "space-between", children: [
1109
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1110
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: t("forms.import-title", "Import an existing form") }),
1111
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: t(
1112
+ "forms.import-hint",
1113
+ "Converts a legacy content-type form (all locales) into a draft here — same slug, source untouched. Re-importing overwrites the draft."
1114
+ ) })
1115
+ ] }),
1116
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1117
+ /* @__PURE__ */ jsxRuntime.jsx(
1118
+ designSystem.SingleSelect,
1119
+ {
1120
+ "aria-label": t("forms.import-source", "Form to import"),
1121
+ placeholder: t("forms.import-source", "Form to import"),
1122
+ value: sourceId,
1123
+ onChange: (v) => setSourceId(String(v)),
1124
+ children: sources.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: s.documentId, children: [
1125
+ s.name,
1126
+ " ",
1127
+ s.slug ? `(${s.slug})` : ""
1128
+ ] }, s.documentId))
1129
+ }
1130
+ ),
1131
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: runImport, disabled: busy || !sourceId, children: t("forms.import", "Import") })
1132
+ ] })
1133
+ ] })
1134
+ ] }) }),
1135
+ report && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "warning100", hasRadius: true, padding: 4, marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "flex-start", gap: 4, children: [
1025
1136
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1026
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: t("forms.import-title", "Import an existing form") }),
1027
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", tag: "p", children: t(
1028
- "forms.import-hint",
1029
- "Converts a legacy content-type form (all locales) into a draft here — same slug, source untouched. Re-importing overwrites the draft."
1030
- ) })
1137
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", textColor: "warning700", children: t("forms.report-title", "Imported with gaps") }),
1138
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { tag: "ul", paddingTop: 2, style: { listStyle: "disc", paddingLeft: 16 }, children: report.skipped.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "warning700", children: skippedLine(item) }) }, i)) })
1031
1139
  ] }),
1032
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1033
- /* @__PURE__ */ jsxRuntime.jsx(
1034
- designSystem.SingleSelect,
1035
- {
1036
- "aria-label": t("forms.import-source", "Form to import"),
1037
- placeholder: t("forms.import-source", "Form to import"),
1038
- value: sourceId,
1039
- onChange: (v) => setSourceId(String(v)),
1040
- children: sources.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: s.documentId, children: [
1041
- s.name,
1042
- " ",
1043
- s.slug ? `(${s.slug})` : ""
1044
- ] }, s.documentId))
1045
- }
1046
- ),
1047
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: runImport, disabled: busy || !sourceId, children: t("forms.import", "Import") })
1140
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, shrink: 0, children: [
1141
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: () => setReport(null), children: t("forms.report-dismiss", "Dismiss") }),
1142
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => navigate(report.documentId), children: t("forms.report-open", "Open the form") })
1048
1143
  ] })
1049
1144
  ] }) }),
1050
- !rows ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: t("forms.loading", "Loading…") }) : rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("forms.empty", "No form yet — name one above to open the builder.") }) }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 6, rowCount: rows.length, children: [
1145
+ !rows ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: t("forms.loading", "Loading…") }) : rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("forms.empty", "No form yet — name one above to open the builder.") }) }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 7, rowCount: rows.length, children: [
1051
1146
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1052
1147
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-name", "Name") }) }),
1053
1148
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-slug", "Slug") }) }),
1054
1149
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-structure", "Structure") }) }),
1055
1150
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-status", "Status") }) }),
1151
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-submissions", "Submissions") }) }),
1056
1152
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-updated", "Updated") }) }),
1057
1153
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("forms.col-actions", "Actions") }) })
1058
1154
  ] }) }),
@@ -1069,6 +1165,15 @@ const FormsList = () => {
1069
1165
  fields: row.fields
1070
1166
  }) }) }),
1071
1167
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: row.published ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: t("forms.published", "Published") }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: t("forms.draft", "Draft") }) }),
1168
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { onClick: (e) => e.stopPropagation(), children: row.submissions > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1169
+ designSystem.Button,
1170
+ {
1171
+ variant: "ghost",
1172
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.List, {}),
1173
+ onClick: () => navigate(`submissions?form=${encodeURIComponent(row.slug)}`),
1174
+ children: row.submissions
1175
+ }
1176
+ ) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "—" }) }),
1072
1177
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: row.updatedAt ? new Date(row.updatedAt).toLocaleString() : "—" }) }),
1073
1178
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
1074
1179
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1097,8 +1202,169 @@ const FormsList = () => {
1097
1202
  ] })
1098
1203
  ] });
1099
1204
  };
1205
+ const PAGE_SIZE = 20;
1206
+ const preview = (values, max = 3) => {
1207
+ const parts = Object.entries(values ?? {}).slice(0, max).map(([k, v]) => `${k}: ${String(v)}`);
1208
+ const more = Object.keys(values ?? {}).length - max;
1209
+ return parts.join(" · ") + (more > 0 ? ` · +${more}` : "");
1210
+ };
1211
+ const Submissions = () => {
1212
+ const { formatMessage } = index.useIntl();
1213
+ const { get } = admin.useFetchClient();
1214
+ const navigate = reactRouterDom.useNavigate();
1215
+ const [searchParams, setSearchParams] = reactRouterDom.useSearchParams();
1216
+ const form = searchParams.get("form") ?? "";
1217
+ const [forms, setForms] = React__namespace.useState([]);
1218
+ const [rows, setRows] = React__namespace.useState(null);
1219
+ const [total, setTotal] = React__namespace.useState(0);
1220
+ const [page, setPage] = React__namespace.useState(1);
1221
+ const [selected, setSelected] = React__namespace.useState(null);
1222
+ const [error, setError] = React__namespace.useState(null);
1223
+ const [exporting, setExporting] = React__namespace.useState(false);
1224
+ const t = (id, defaultMessage, values) => formatMessage({ id: index$1.getTranslation(id), defaultMessage }, values);
1225
+ React__namespace.useEffect(() => {
1226
+ get(`/${index$1.PLUGIN_ID}/builder/forms`).then(({ data }) => setForms(data.forms)).catch(() => setForms([]));
1227
+ }, [get]);
1228
+ React__namespace.useEffect(() => {
1229
+ setRows(null);
1230
+ const query = new URLSearchParams({ page: String(page), pageSize: String(PAGE_SIZE) });
1231
+ if (form) query.set("form", form);
1232
+ get(
1233
+ `/${index$1.PLUGIN_ID}/builder/submissions?${query}`
1234
+ ).then(({ data }) => {
1235
+ setRows(data.submissions);
1236
+ setTotal(data.total);
1237
+ }).catch(() => setError(t("submissions.load-error", "Could not load the submissions.")));
1238
+ }, [get, form, page]);
1239
+ const changeForm = (slug) => {
1240
+ setPage(1);
1241
+ setSearchParams(slug ? { form: slug } : {});
1242
+ };
1243
+ const exportCsv = async () => {
1244
+ if (!form) return;
1245
+ setExporting(true);
1246
+ setError(null);
1247
+ try {
1248
+ const { data } = await get(
1249
+ `/${index$1.PLUGIN_ID}/builder/submissions/export?form=${encodeURIComponent(form)}`
1250
+ );
1251
+ const url = URL.createObjectURL(new Blob([data.csv], { type: "text/csv;charset=utf-8" }));
1252
+ const link = document.createElement("a");
1253
+ link.href = url;
1254
+ link.download = data.filename;
1255
+ link.click();
1256
+ URL.revokeObjectURL(url);
1257
+ } catch {
1258
+ setError(t("submissions.export-error", "Could not export the submissions."));
1259
+ } finally {
1260
+ setExporting(false);
1261
+ }
1262
+ };
1263
+ const pages = Math.max(1, Math.ceil(total / PAGE_SIZE));
1264
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, children: [
1265
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "flex-end", paddingBottom: 6, children: [
1266
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1268
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: t("submissions.back", "Back to the forms"), onClick: () => navigate(".."), children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}) }),
1269
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: t("submissions.title", "Submissions") })
1270
+ ] }),
1271
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", tag: "p", children: t(
1272
+ "submissions.subtitle",
1273
+ "Every answer received by the built forms — stored whatever the CRM's mood."
1274
+ ) })
1275
+ ] }),
1276
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1277
+ /* @__PURE__ */ jsxRuntime.jsx(
1278
+ designSystem.SingleSelect,
1279
+ {
1280
+ "aria-label": t("submissions.filter", "Filter by form"),
1281
+ placeholder: t("submissions.all-forms", "All forms"),
1282
+ value: form,
1283
+ onChange: (v) => changeForm(String(v)),
1284
+ onClear: () => changeForm(""),
1285
+ children: forms.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: f.slug, children: f.name }, f.slug))
1286
+ }
1287
+ ),
1288
+ /* @__PURE__ */ jsxRuntime.jsx(
1289
+ designSystem.Button,
1290
+ {
1291
+ variant: "secondary",
1292
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Download, {}),
1293
+ onClick: exportCsv,
1294
+ disabled: !form || exporting,
1295
+ title: form ? void 0 : t("submissions.export-pick", "Pick a form to export"),
1296
+ children: t("submissions.export", "Export CSV")
1297
+ }
1298
+ )
1299
+ ] })
1300
+ ] }),
1301
+ error && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", children: error }) }),
1302
+ !rows ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: t("submissions.loading", "Loading…") }) : rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { background: "neutral0", hasRadius: true, shadow: "tableShadow", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: form ? t("submissions.empty-form", "No submission for this form yet.") : t("submissions.empty", "No submission yet.") }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1303
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 5, rowCount: rows.length, children: [
1304
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1305
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("submissions.col-date", "Date") }) }),
1306
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("submissions.col-form", "Form") }) }),
1307
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("submissions.col-email", "Email") }) }),
1308
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("submissions.col-answers", "Answers") }) }),
1309
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: t("submissions.col-crm", "CRM") }) })
1310
+ ] }) }),
1311
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
1312
+ designSystem.Tr,
1313
+ {
1314
+ onClick: () => setSelected(row),
1315
+ style: { cursor: "pointer" },
1316
+ children: [
1317
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: row.createdAt ? new Date(row.createdAt).toLocaleString() : "—" }) }),
1318
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: row.formTitle || row.form }) }),
1319
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", children: row.email || "—" }) }),
1320
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", ellipsis: true, children: preview(row.values) }) }),
1321
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: row.hubspotSynced ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "success100", textColor: "success700", children: t("submissions.synced", "Synced") }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "warning100", textColor: "warning700", children: t("submissions.not-synced", "Not synced") }) })
1322
+ ]
1323
+ },
1324
+ row.documentId
1325
+ )) })
1326
+ ] }),
1327
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", paddingTop: 4, children: [
1328
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("submissions.count", "{total, plural, one {# submission} other {# submissions}}", {
1329
+ total
1330
+ }) }),
1331
+ pages > 1 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1332
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", disabled: page <= 1, onClick: () => setPage(page - 1), children: t("submissions.prev", "Previous") }),
1333
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: t("submissions.page", "{page} / {pages}", { page, pages }) }),
1334
+ /* @__PURE__ */ jsxRuntime.jsx(
1335
+ designSystem.Button,
1336
+ {
1337
+ variant: "tertiary",
1338
+ disabled: page >= pages,
1339
+ onClick: () => setPage(page + 1),
1340
+ children: t("submissions.next", "Next")
1341
+ }
1342
+ )
1343
+ ] })
1344
+ ] })
1345
+ ] }),
1346
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: Boolean(selected), onOpenChange: (open) => !open && setSelected(null), children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1347
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Title, { children: [
1348
+ selected?.formTitle || selected?.form,
1349
+ " ",
1350
+ selected?.createdAt ? `— ${new Date(selected.createdAt).toLocaleString()}` : ""
1351
+ ] }) }),
1352
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: selected && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
1353
+ Object.entries(selected.values ?? {}).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1354
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", tag: "p", children: key }),
1355
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", children: String(value) })
1356
+ ] }, key)),
1357
+ selected.meta?.pagePath && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
1358
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", tag: "p", children: t("submissions.meta-page", "Submitted from") }),
1359
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", children: selected.meta.pagePath })
1360
+ ] })
1361
+ ] }) })
1362
+ ] }) })
1363
+ ] });
1364
+ };
1100
1365
  const Forms = () => /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
1101
1366
  /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(FormsList, {}) }),
1367
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "submissions", element: /* @__PURE__ */ jsxRuntime.jsx(Submissions, {}) }),
1102
1368
  /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: ":documentId", element: /* @__PURE__ */ jsxRuntime.jsx(FormEditor, {}) })
1103
1369
  ] });
1104
1370
  exports.default = Forms;
@@ -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-LrCSAHoH.mjs";
6
+ import { P as PLUGIN_ID, g as getTranslation } from "./index-LTaVoyWJ.mjs";
7
7
  const HubspotFormInput = React.forwardRef(
8
8
  ({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
9
9
  const { formatMessage } = useIntl();
@@ -5,7 +5,7 @@ const React = require("react");
5
5
  const index = require("./index-DkTxsEqL.js");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const admin = require("@strapi/strapi/admin");
8
- const index$1 = require("./index-P7p8ASb6.js");
8
+ const index$1 = require("./index-BbRtXnAL.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" } });
@@ -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-LrCSAHoH.mjs";
6
- import { o as objectLabel } from "./objectLabels-yNrnTOBo.mjs";
7
- import { u as useHubspotSchema } from "./useHubspotSchema-DccG2KbZ.mjs";
5
+ import { g as getTranslation } from "./index-LTaVoyWJ.mjs";
6
+ import { o as objectLabel } from "./objectLabels-DE2xGq7f.mjs";
7
+ import { u as useHubspotSchema } from "./useHubspotSchema-BSKow0X3.mjs";
8
8
  const HubspotObjectInput = React.forwardRef(
9
9
  ({ name, value, onChange, disabled, error, required, label, hint, labelAction }, ref) => {
10
10
  const intl = 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-P7p8ASb6.js");
8
- const objectLabels = require("./objectLabels-Dn-zriuZ.js");
9
- const useHubspotSchema = require("./useHubspotSchema-BottzdRR.js");
7
+ const index$1 = require("./index-BbRtXnAL.js");
8
+ const objectLabels = require("./objectLabels-DU0Cagt_.js");
9
+ const useHubspotSchema = require("./useHubspotSchema-DzBMAxv7.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" } });
@@ -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-P7p8ASb6.js");
10
- const objectLabels = require("./objectLabels-Dn-zriuZ.js");
11
- const useHubspotSchema = require("./useHubspotSchema-BottzdRR.js");
9
+ const index$1 = require("./index-BbRtXnAL.js");
10
+ const objectLabels = require("./objectLabels-DU0Cagt_.js");
11
+ const useHubspotSchema = require("./useHubspotSchema-DzBMAxv7.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-LrCSAHoH.mjs";
8
- import { o as objectLabel } from "./objectLabels-yNrnTOBo.mjs";
9
- import { u as useHubspotSchema } from "./useHubspotSchema-DccG2KbZ.mjs";
7
+ import { g as getTranslation } from "./index-LTaVoyWJ.mjs";
8
+ import { o as objectLabel } from "./objectLabels-DE2xGq7f.mjs";
9
+ import { u as useHubspotSchema } from "./useHubspotSchema-BSKow0X3.mjs";
10
10
  const oneLine = {
11
11
  display: "block",
12
12
  whiteSpace: "nowrap",
@@ -5,9 +5,9 @@ const React = require("react");
5
5
  const index = require("./index-DkTxsEqL.js");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const admin = require("@strapi/strapi/admin");
8
- const index$1 = require("./index-P7p8ASb6.js");
8
+ const index$1 = require("./index-BbRtXnAL.js");
9
9
  const reactRouterDom = require("react-router-dom");
10
- const objectLabels = require("./objectLabels-Dn-zriuZ.js");
10
+ const objectLabels = require("./objectLabels-DU0Cagt_.js");
11
11
  function _interopNamespace(e) {
12
12
  if (e && e.__esModule) return e;
13
13
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -3,9 +3,9 @@ import * as React from "react";
3
3
  import { u as useIntl } from "./index-CEh8vkxY.mjs";
4
4
  import { Flex, Divider, Typography, Button, Box, Link, Badge, Loader, Field } from "@strapi/design-system";
5
5
  import { useFetchClient } from "@strapi/strapi/admin";
6
- import { g as getTranslation, P as PLUGIN_ID } from "./index-LrCSAHoH.mjs";
6
+ import { g as getTranslation, P as PLUGIN_ID } from "./index-LTaVoyWJ.mjs";
7
7
  import { Link as Link$1 } from "react-router-dom";
8
- import { o as objectLabel } from "./objectLabels-yNrnTOBo.mjs";
8
+ import { o as objectLabel } from "./objectLabels-DE2xGq7f.mjs";
9
9
  const AuditSection = ({ configured }) => {
10
10
  const intl = useIntl();
11
11
  const { formatMessage } = intl;
@@ -204,7 +204,47 @@ const en = {
204
204
  "panel.persist": "Answer memory",
205
205
  "panel.persist-session": "This visit only",
206
206
  "panel.persist-days30": "30 days (non-identifying fields only)",
207
- "panel.class": "CSS classes"
207
+ "panel.class": "CSS classes",
208
+ "forms.col-submissions": "Submissions",
209
+ "forms.submissions": "Submissions",
210
+ "forms.import-hubspot-title": "Import from your HubSpot portal",
211
+ "forms.import-hubspot-hint": "Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing overwrites the draft.",
212
+ "forms.import-hubspot-source": "HubSpot form to import",
213
+ "forms.hubspot-list-error": "Could not list the portal's forms — the token needs the `forms` read scope.",
214
+ "forms.report-title": "Imported with gaps",
215
+ "forms.report-dismiss": "Dismiss",
216
+ "forms.report-open": "Open the form",
217
+ "forms.skipped-field-type": '"{label}" skipped — unsupported type ({detail})',
218
+ "forms.skipped-hidden": '"{label}" skipped — hidden fields are not supported yet',
219
+ "forms.skipped-object": '"{label}" skipped — unmapped object ({detail})',
220
+ "forms.skipped-duplicate": '"{label}" skipped — the name "{detail}" is already used',
221
+ "forms.skipped-condition": 'Condition on "{label}" dropped — no equivalent operator ({detail})',
222
+ "forms.skipped-rich-text": "A content block was skipped — the builder has no rich-text element",
223
+ "forms.skipped-consent": "The GDPR consent block was skipped — rebuild it as a field if needed",
224
+ "submissions.title": "Submissions",
225
+ "submissions.subtitle": "Every answer received by the built forms — stored whatever the CRM's mood.",
226
+ "submissions.back": "Back to the forms",
227
+ "submissions.filter": "Filter by form",
228
+ "submissions.all-forms": "All forms",
229
+ "submissions.export": "Export CSV",
230
+ "submissions.export-pick": "Pick a form to export",
231
+ "submissions.export-error": "Could not export the submissions.",
232
+ "submissions.load-error": "Could not load the submissions.",
233
+ "submissions.loading": "Loading…",
234
+ "submissions.empty": "No submission yet.",
235
+ "submissions.empty-form": "No submission for this form yet.",
236
+ "submissions.col-date": "Date",
237
+ "submissions.col-form": "Form",
238
+ "submissions.col-email": "Email",
239
+ "submissions.col-answers": "Answers",
240
+ "submissions.col-crm": "CRM",
241
+ "submissions.synced": "Synced",
242
+ "submissions.not-synced": "Not synced",
243
+ "submissions.count": "{total, plural, one {# submission} other {# submissions}}",
244
+ "submissions.prev": "Previous",
245
+ "submissions.next": "Next",
246
+ "submissions.page": "{page} / {pages}",
247
+ "submissions.meta-page": "Submitted from"
208
248
  };
209
249
  export {
210
250
  en as default
@@ -206,6 +206,46 @@ const en = {
206
206
  "panel.persist": "Answer memory",
207
207
  "panel.persist-session": "This visit only",
208
208
  "panel.persist-days30": "30 days (non-identifying fields only)",
209
- "panel.class": "CSS classes"
209
+ "panel.class": "CSS classes",
210
+ "forms.col-submissions": "Submissions",
211
+ "forms.submissions": "Submissions",
212
+ "forms.import-hubspot-title": "Import from your HubSpot portal",
213
+ "forms.import-hubspot-hint": "Translates a form built in HubSpot into a draft here, CRM mapping included. What the builder can't express is skipped and reported. Re-importing overwrites the draft.",
214
+ "forms.import-hubspot-source": "HubSpot form to import",
215
+ "forms.hubspot-list-error": "Could not list the portal's forms — the token needs the `forms` read scope.",
216
+ "forms.report-title": "Imported with gaps",
217
+ "forms.report-dismiss": "Dismiss",
218
+ "forms.report-open": "Open the form",
219
+ "forms.skipped-field-type": '"{label}" skipped — unsupported type ({detail})',
220
+ "forms.skipped-hidden": '"{label}" skipped — hidden fields are not supported yet',
221
+ "forms.skipped-object": '"{label}" skipped — unmapped object ({detail})',
222
+ "forms.skipped-duplicate": '"{label}" skipped — the name "{detail}" is already used',
223
+ "forms.skipped-condition": 'Condition on "{label}" dropped — no equivalent operator ({detail})',
224
+ "forms.skipped-rich-text": "A content block was skipped — the builder has no rich-text element",
225
+ "forms.skipped-consent": "The GDPR consent block was skipped — rebuild it as a field if needed",
226
+ "submissions.title": "Submissions",
227
+ "submissions.subtitle": "Every answer received by the built forms — stored whatever the CRM's mood.",
228
+ "submissions.back": "Back to the forms",
229
+ "submissions.filter": "Filter by form",
230
+ "submissions.all-forms": "All forms",
231
+ "submissions.export": "Export CSV",
232
+ "submissions.export-pick": "Pick a form to export",
233
+ "submissions.export-error": "Could not export the submissions.",
234
+ "submissions.load-error": "Could not load the submissions.",
235
+ "submissions.loading": "Loading…",
236
+ "submissions.empty": "No submission yet.",
237
+ "submissions.empty-form": "No submission for this form yet.",
238
+ "submissions.col-date": "Date",
239
+ "submissions.col-form": "Form",
240
+ "submissions.col-email": "Email",
241
+ "submissions.col-answers": "Answers",
242
+ "submissions.col-crm": "CRM",
243
+ "submissions.synced": "Synced",
244
+ "submissions.not-synced": "Not synced",
245
+ "submissions.count": "{total, plural, one {# submission} other {# submissions}}",
246
+ "submissions.prev": "Previous",
247
+ "submissions.next": "Next",
248
+ "submissions.page": "{page} / {pages}",
249
+ "submissions.meta-page": "Submitted from"
210
250
  };
211
251
  exports.default = en;