@antelopejs/dms-mailing 0.3.3 → 0.4.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 (705) hide show
  1. package/README.md +232 -22
  2. package/dist/automation/email-event-trigger.js +2 -2
  3. package/dist/automation/email-event-trigger.js.map +1 -1
  4. package/dist/automation/send-template-action.js +2 -2
  5. package/dist/automation/send-template-action.js.map +1 -1
  6. package/dist/config.js +3 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/constants.d.ts +10 -1
  9. package/dist/constants.js +15 -3
  10. package/dist/constants.js.map +1 -1
  11. package/dist/crons/attention.d.ts +3 -0
  12. package/dist/crons/attention.js +19 -0
  13. package/dist/crons/attention.js.map +1 -0
  14. package/dist/crons/index.d.ts +2 -0
  15. package/dist/crons/index.js +2 -0
  16. package/dist/crons/index.js.map +1 -1
  17. package/dist/crons/jobs.d.ts +12 -0
  18. package/dist/crons/jobs.js +27 -0
  19. package/dist/crons/jobs.js.map +1 -0
  20. package/dist/crons/retention.d.ts +2 -4
  21. package/dist/crons/retention.js +7 -22
  22. package/dist/crons/retention.js.map +1 -1
  23. package/dist/data/displays.d.ts +5 -0
  24. package/dist/data/displays.js +23 -0
  25. package/dist/data/displays.js.map +1 -0
  26. package/dist/data/index.d.ts +1 -0
  27. package/dist/data/index.js +1 -0
  28. package/dist/data/index.js.map +1 -1
  29. package/dist/data/locale-fallback.d.ts +3 -0
  30. package/dist/data/locale-fallback.js +20 -0
  31. package/dist/data/locale-fallback.js.map +1 -0
  32. package/dist/data/read-only-routes.d.ts +7 -0
  33. package/dist/data/read-only-routes.js +13 -0
  34. package/dist/data/read-only-routes.js.map +1 -0
  35. package/dist/data/sends-table.d.ts +19 -5
  36. package/dist/data/sends-table.js +185 -17
  37. package/dist/data/sends-table.js.map +1 -1
  38. package/dist/data/suppressions-table.d.ts +24 -0
  39. package/dist/data/suppressions-table.js +120 -0
  40. package/dist/data/suppressions-table.js.map +1 -0
  41. package/dist/data/templates-table.d.ts +20 -2
  42. package/dist/data/templates-table.js +188 -31
  43. package/dist/data/templates-table.js.map +1 -1
  44. package/dist/data-types/categories-type.d.ts +11 -13
  45. package/dist/data-types/categories-type.js +3 -3
  46. package/dist/data-types/categories-type.js.map +1 -1
  47. package/dist/data-types/category-type.js +1 -1
  48. package/dist/data-types/category-type.js.map +1 -1
  49. package/dist/db/models/index.d.ts +3 -0
  50. package/dist/db/models/index.js +3 -0
  51. package/dist/db/models/index.js.map +1 -1
  52. package/dist/db/models/sends.model.d.ts +12 -1
  53. package/dist/db/models/sends.model.js +64 -0
  54. package/dist/db/models/sends.model.js.map +1 -1
  55. package/dist/db/models/settings.model.d.ts +0 -1
  56. package/dist/db/models/settings.model.js +0 -7
  57. package/dist/db/models/settings.model.js.map +1 -1
  58. package/dist/db/models/suppressions.model.d.ts +28 -0
  59. package/dist/db/models/suppressions.model.js +46 -0
  60. package/dist/db/models/suppressions.model.js.map +1 -0
  61. package/dist/db/models/template-versions.model.d.ts +24 -0
  62. package/dist/db/models/template-versions.model.js +17 -0
  63. package/dist/db/models/template-versions.model.js.map +1 -0
  64. package/dist/db/models/templates.model.d.ts +2 -0
  65. package/dist/db/models/templates.model.js +3 -0
  66. package/dist/db/models/templates.model.js.map +1 -1
  67. package/dist/db/models/webhook-status.model.d.ts +24 -0
  68. package/dist/db/models/webhook-status.model.js +21 -0
  69. package/dist/db/models/webhook-status.model.js.map +1 -0
  70. package/dist/db/tables/index.d.ts +3 -0
  71. package/dist/db/tables/index.js +3 -0
  72. package/dist/db/tables/index.js.map +1 -1
  73. package/dist/db/tables/send-events.table.d.ts +2 -2
  74. package/dist/db/tables/send-events.table.js.map +1 -1
  75. package/dist/db/tables/sends.table.d.ts +10 -2
  76. package/dist/db/tables/sends.table.js +34 -1
  77. package/dist/db/tables/sends.table.js.map +1 -1
  78. package/dist/db/tables/suppressions.table.d.ts +12 -0
  79. package/dist/db/tables/suppressions.table.js +53 -0
  80. package/dist/db/tables/suppressions.table.js.map +1 -0
  81. package/dist/db/tables/template-versions.table.d.ts +13 -0
  82. package/dist/db/tables/template-versions.table.js +59 -0
  83. package/dist/db/tables/template-versions.table.js.map +1 -0
  84. package/dist/db/tables/templates.table.d.ts +11 -0
  85. package/dist/db/tables/templates.table.js +45 -0
  86. package/dist/db/tables/templates.table.js.map +1 -1
  87. package/dist/db/tables/webhook-status.table.d.ts +14 -0
  88. package/dist/db/tables/webhook-status.table.js +58 -0
  89. package/dist/db/tables/webhook-status.table.js.map +1 -0
  90. package/dist/engine/changes.d.ts +12 -0
  91. package/dist/engine/changes.js +107 -0
  92. package/dist/engine/changes.js.map +1 -0
  93. package/dist/engine/conditions.js +0 -2
  94. package/dist/engine/conditions.js.map +1 -1
  95. package/dist/engine/coverage.d.ts +1 -0
  96. package/dist/engine/coverage.js +6 -2
  97. package/dist/engine/coverage.js.map +1 -1
  98. package/dist/engine/fallback-html.d.ts +1 -1
  99. package/dist/engine/fallback-html.js +51 -34
  100. package/dist/engine/fallback-html.js.map +1 -1
  101. package/dist/engine/index.d.ts +3 -0
  102. package/dist/engine/index.js +3 -0
  103. package/dist/engine/index.js.map +1 -1
  104. package/dist/engine/plain-text.d.ts +3 -0
  105. package/dist/engine/plain-text.js +60 -0
  106. package/dist/engine/plain-text.js.map +1 -0
  107. package/dist/engine/resolve.d.ts +2 -1
  108. package/dist/engine/resolve.js +17 -9
  109. package/dist/engine/resolve.js.map +1 -1
  110. package/dist/engine/system-variables.d.ts +10 -0
  111. package/dist/engine/system-variables.js +70 -0
  112. package/dist/engine/system-variables.js.map +1 -0
  113. package/dist/engine/tokens.d.ts +2 -1
  114. package/dist/engine/tokens.js +6 -2
  115. package/dist/engine/tokens.js.map +1 -1
  116. package/dist/hooks/tenant-export.js +13 -1
  117. package/dist/hooks/tenant-export.js.map +1 -1
  118. package/dist/implementations/dms-mailing/index.d.ts +1 -1
  119. package/dist/implementations/dms-mailing/index.js +2 -2
  120. package/dist/implementations/dms-mailing/index.js.map +1 -1
  121. package/dist/index.js +13 -11
  122. package/dist/index.js.map +1 -1
  123. package/dist/pages/editor.js +9 -2
  124. package/dist/pages/editor.js.map +1 -1
  125. package/dist/pages/index.d.ts +1 -0
  126. package/dist/pages/index.js +1 -0
  127. package/dist/pages/index.js.map +1 -1
  128. package/dist/pages/module.d.ts +2 -0
  129. package/dist/pages/module.js +26 -2
  130. package/dist/pages/module.js.map +1 -1
  131. package/dist/pages/overview.d.ts +2 -6
  132. package/dist/pages/overview.js +109 -52
  133. package/dist/pages/overview.js.map +1 -1
  134. package/dist/pages/sends.d.ts +3 -4
  135. package/dist/pages/sends.js +197 -60
  136. package/dist/pages/sends.js.map +1 -1
  137. package/dist/pages/settings/form.js +114 -53
  138. package/dist/pages/settings/form.js.map +1 -1
  139. package/dist/pages/settings/page.d.ts +0 -1
  140. package/dist/pages/settings/page.js +8 -11
  141. package/dist/pages/settings/page.js.map +1 -1
  142. package/dist/pages/suppressions.d.ts +9 -0
  143. package/dist/pages/suppressions.js +151 -0
  144. package/dist/pages/suppressions.js.map +1 -0
  145. package/dist/pages/templates.d.ts +0 -1
  146. package/dist/pages/templates.js +114 -40
  147. package/dist/pages/templates.js.map +1 -1
  148. package/dist/permissions.js +2 -2
  149. package/dist/permissions.js.map +1 -1
  150. package/dist/quick-actions.js +7 -4
  151. package/dist/quick-actions.js.map +1 -1
  152. package/dist/routes/events.d.ts +8 -5
  153. package/dist/routes/events.js +55 -24
  154. package/dist/routes/events.js.map +1 -1
  155. package/dist/routes/index.d.ts +2 -0
  156. package/dist/routes/index.js +2 -0
  157. package/dist/routes/index.js.map +1 -1
  158. package/dist/routes/metrics.d.ts +60 -23
  159. package/dist/routes/metrics.js +231 -83
  160. package/dist/routes/metrics.js.map +1 -1
  161. package/dist/routes/period.d.ts +2 -1
  162. package/dist/routes/period.js +9 -3
  163. package/dist/routes/period.js.map +1 -1
  164. package/dist/routes/sends.d.ts +49 -8
  165. package/dist/routes/sends.js +239 -24
  166. package/dist/routes/sends.js.map +1 -1
  167. package/dist/routes/settings.d.ts +44 -5
  168. package/dist/routes/settings.js +190 -15
  169. package/dist/routes/settings.js.map +1 -1
  170. package/dist/routes/suppressions.d.ts +16 -0
  171. package/dist/routes/suppressions.js +92 -0
  172. package/dist/routes/suppressions.js.map +1 -0
  173. package/dist/routes/template-versions.d.ts +28 -0
  174. package/dist/routes/template-versions.js +87 -0
  175. package/dist/routes/template-versions.js.map +1 -0
  176. package/dist/routes/templates.d.ts +60 -6
  177. package/dist/routes/templates.js +204 -35
  178. package/dist/routes/templates.js.map +1 -1
  179. package/dist/services/attention.d.ts +19 -6
  180. package/dist/services/attention.js +177 -12
  181. package/dist/services/attention.js.map +1 -1
  182. package/dist/services/catalog.d.ts +3 -0
  183. package/dist/services/catalog.js +25 -0
  184. package/dist/services/catalog.js.map +1 -0
  185. package/dist/services/categories.d.ts +8 -32
  186. package/dist/services/categories.js +14 -25
  187. package/dist/services/categories.js.map +1 -1
  188. package/dist/services/checked-send.d.ts +9 -0
  189. package/dist/services/checked-send.js +54 -0
  190. package/dist/services/checked-send.js.map +1 -0
  191. package/dist/services/events.d.ts +5 -0
  192. package/dist/services/events.js +102 -9
  193. package/dist/services/events.js.map +1 -1
  194. package/dist/services/metrics.d.ts +17 -6
  195. package/dist/services/metrics.js +57 -18
  196. package/dist/services/metrics.js.map +1 -1
  197. package/dist/services/notify.d.ts +18 -10
  198. package/dist/services/notify.js +82 -37
  199. package/dist/services/notify.js.map +1 -1
  200. package/dist/services/provider-banner.d.ts +14 -0
  201. package/dist/services/provider-banner.js +115 -0
  202. package/dist/services/provider-banner.js.map +1 -0
  203. package/dist/services/render.js.map +1 -1
  204. package/dist/services/send-stat-items.d.ts +7 -0
  205. package/dist/services/send-stat-items.js +127 -0
  206. package/dist/services/send-stat-items.js.map +1 -0
  207. package/dist/services/send-stats.d.ts +23 -0
  208. package/dist/services/send-stats.js +63 -0
  209. package/dist/services/send-stats.js.map +1 -0
  210. package/dist/services/send.d.ts +18 -3
  211. package/dist/services/send.js +81 -17
  212. package/dist/services/send.js.map +1 -1
  213. package/dist/services/settings.d.ts +5 -0
  214. package/dist/services/settings.js +28 -7
  215. package/dist/services/settings.js.map +1 -1
  216. package/dist/services/starters.d.ts +15 -0
  217. package/dist/services/starters.js +264 -0
  218. package/dist/services/starters.js.map +1 -0
  219. package/dist/services/suppressions.d.ts +20 -0
  220. package/dist/services/suppressions.js +53 -0
  221. package/dist/services/suppressions.js.map +1 -0
  222. package/dist/services/template-attention.d.ts +4 -0
  223. package/dist/services/template-attention.js +43 -0
  224. package/dist/services/template-attention.js.map +1 -0
  225. package/dist/services/template-stats.d.ts +27 -0
  226. package/dist/services/template-stats.js +59 -0
  227. package/dist/services/template-stats.js.map +1 -0
  228. package/dist/services/templates.d.ts +31 -4
  229. package/dist/services/templates.js +240 -21
  230. package/dist/services/templates.js.map +1 -1
  231. package/dist/services/webhook-status.d.ts +27 -0
  232. package/dist/services/webhook-status.js +134 -0
  233. package/dist/services/webhook-status.js.map +1 -0
  234. package/dist/types/blocks.d.ts +1 -0
  235. package/dist/types/index.d.ts +2 -0
  236. package/dist/types/index.js +2 -0
  237. package/dist/types/index.js.map +1 -1
  238. package/dist/types/sends.d.ts +10 -1
  239. package/dist/types/sends.js +35 -2
  240. package/dist/types/sends.js.map +1 -1
  241. package/dist/types/suppressions.d.ts +9 -0
  242. package/dist/types/suppressions.js +10 -0
  243. package/dist/types/suppressions.js.map +1 -0
  244. package/dist/types/webhook.d.ts +17 -0
  245. package/dist/types/webhook.js +3 -0
  246. package/dist/types/webhook.js.map +1 -0
  247. package/dist/validation/settings.schema.d.ts +144 -9
  248. package/dist/validation/settings.schema.js +113 -16
  249. package/dist/validation/settings.schema.js.map +1 -1
  250. package/dist/validation/templates.schema.d.ts +137 -10
  251. package/dist/validation/templates.schema.js +28 -2
  252. package/dist/validation/templates.schema.js.map +1 -1
  253. package/frontend-vue/app/build/components/FormFieldRow.vue +90 -0
  254. package/frontend-vue/app/build/components/FormRows.vue +26 -0
  255. package/frontend-vue/app/build/components/RequiredLegend.vue +11 -0
  256. package/frontend-vue/app/components/CategoriesInput.vue +323 -77
  257. package/frontend-vue/app/components/CategoryInput.vue +13 -14
  258. package/frontend-vue/app/components/CategoryLabel.vue +34 -0
  259. package/frontend-vue/app/components/DuplicateModal.vue +201 -0
  260. package/frontend-vue/app/components/Editor.vue +55 -20
  261. package/frontend-vue/app/components/FixAddressModal.vue +133 -0
  262. package/frontend-vue/app/components/FunnelCard.vue +146 -43
  263. package/frontend-vue/app/components/LocaleSelect.vue +98 -0
  264. package/frontend-vue/app/components/NewTemplateModal.vue +388 -102
  265. package/frontend-vue/app/components/NewTemplateSourceTile.vue +70 -0
  266. package/frontend-vue/app/components/OverviewGate.vue +220 -0
  267. package/frontend-vue/app/components/PageHeader.vue +98 -0
  268. package/frontend-vue/app/components/RealSendModal.vue +555 -0
  269. package/frontend-vue/app/components/RecipientsField.vue +41 -0
  270. package/frontend-vue/app/components/RenderingModal.vue +189 -0
  271. package/frontend-vue/app/components/RetentionInput.vue +212 -0
  272. package/frontend-vue/app/components/SendDrawer.vue +542 -209
  273. package/frontend-vue/app/components/StarterGallery.vue +153 -0
  274. package/frontend-vue/app/components/SystemVariables.vue +63 -0
  275. package/frontend-vue/app/components/TemplateCard.vue +218 -57
  276. package/frontend-vue/app/components/TemplateDrawer.vue +785 -274
  277. package/frontend-vue/app/components/TemplateGallery.vue +312 -56
  278. package/frontend-vue/app/components/TemplatePreviewFrame.vue +112 -22
  279. package/frontend-vue/app/components/TestSendModal.vue +266 -95
  280. package/frontend-vue/app/components/TokenText.vue +61 -10
  281. package/frontend-vue/app/components/VariablePath.vue +20 -0
  282. package/frontend-vue/app/components/VersionHistory.vue +196 -0
  283. package/frontend-vue/app/components/VersionPreview.vue +164 -0
  284. package/frontend-vue/app/components/WebhookStatus.vue +130 -0
  285. package/frontend-vue/app/components/blocks/Button.vue +1 -1
  286. package/frontend-vue/app/components/blocks/Code.vue +1 -1
  287. package/frontend-vue/app/components/blocks/Footer.vue +56 -7
  288. package/frontend-vue/app/components/blocks/Heading.vue +1 -1
  289. package/frontend-vue/app/components/blocks/Hero.vue +13 -7
  290. package/frontend-vue/app/components/blocks/List.vue +50 -6
  291. package/frontend-vue/app/components/blocks/Paragraph.vue +1 -1
  292. package/frontend-vue/app/components/blocks/Total.vue +3 -3
  293. package/frontend-vue/app/components/editor/EditorAutocompleteMenu.vue +50 -0
  294. package/frontend-vue/app/components/editor/EditorBlock.vue +37 -90
  295. package/frontend-vue/app/components/editor/EditorBlockFields.vue +140 -0
  296. package/frontend-vue/app/components/editor/EditorBlockInspector.vue +92 -0
  297. package/frontend-vue/app/components/editor/EditorBlockList.vue +32 -25
  298. package/frontend-vue/app/components/editor/EditorBlockPicker.vue +58 -0
  299. package/frontend-vue/app/components/editor/EditorBlockTools.vue +71 -0
  300. package/frontend-vue/app/components/editor/EditorCanvas.vue +105 -70
  301. package/frontend-vue/app/components/editor/EditorCompare.vue +211 -0
  302. package/frontend-vue/app/components/editor/EditorConditionFields.vue +56 -46
  303. package/frontend-vue/app/components/editor/EditorConditionInspector.vue +54 -0
  304. package/frontend-vue/app/components/editor/EditorEnvelope.vue +77 -0
  305. package/frontend-vue/app/components/editor/EditorIfBlock.vue +36 -86
  306. package/frontend-vue/app/components/editor/EditorInsertPoint.vue +64 -0
  307. package/frontend-vue/app/components/editor/EditorInspector.vue +80 -0
  308. package/frontend-vue/app/components/editor/EditorLeftPane.vue +103 -0
  309. package/frontend-vue/app/components/editor/EditorLifecycle.vue +67 -0
  310. package/frontend-vue/app/components/editor/EditorLoading.vue +23 -0
  311. package/frontend-vue/app/components/editor/EditorLocaleSwitch.vue +61 -0
  312. package/frontend-vue/app/components/editor/EditorMissingLocale.vue +72 -48
  313. package/frontend-vue/app/components/editor/EditorMissingPolicy.vue +42 -0
  314. package/frontend-vue/app/components/editor/EditorNotice.vue +127 -0
  315. package/frontend-vue/app/components/editor/EditorOutline.vue +65 -0
  316. package/frontend-vue/app/components/editor/EditorPalette.vue +95 -42
  317. package/frontend-vue/app/components/editor/EditorPaletteGroup.vue +66 -0
  318. package/frontend-vue/app/components/editor/EditorPublishReview.vue +102 -0
  319. package/frontend-vue/app/components/editor/EditorSaveState.vue +88 -0
  320. package/frontend-vue/app/components/editor/EditorShortcutHint.vue +17 -0
  321. package/frontend-vue/app/components/editor/EditorSimChip.vue +51 -0
  322. package/frontend-vue/app/components/editor/EditorTemplatePane.vue +131 -0
  323. package/frontend-vue/app/components/editor/EditorTestDataPane.vue +98 -0
  324. package/frontend-vue/app/components/editor/EditorTitle.vue +82 -0
  325. package/frontend-vue/app/components/editor/EditorTokenField.vue +224 -0
  326. package/frontend-vue/app/components/editor/EditorToolbar.vue +34 -134
  327. package/frontend-vue/app/components/editor/EditorVariableForm.vue +100 -0
  328. package/frontend-vue/app/components/editor/EditorVariableRow.vue +87 -0
  329. package/frontend-vue/app/components/editor/EditorVariablesPane.vue +161 -0
  330. package/frontend-vue/app/components/editor/EditorViewSwitches.vue +76 -0
  331. package/frontend-vue/app/components/editor/EditorVisibility.vue +101 -0
  332. package/frontend-vue/app/components/editor/EditorWorkspace.vue +62 -149
  333. package/frontend-vue/app/composables/useBlockContext.ts +16 -0
  334. package/frontend-vue/app/composables/useEditorActions.ts +381 -0
  335. package/frontend-vue/app/composables/useEditorAnnouncements.ts +51 -0
  336. package/frontend-vue/app/composables/useEditorContext.ts +30 -23
  337. package/frontend-vue/app/composables/useEditorSession.ts +299 -0
  338. package/frontend-vue/app/composables/useEditorShortcuts.ts +81 -0
  339. package/frontend-vue/app/composables/useLocalizedDefaults.ts +17 -0
  340. package/frontend-vue/app/composables/useMailingApi.ts +142 -27
  341. package/frontend-vue/app/composables/useOverviewSetup.ts +13 -0
  342. package/frontend-vue/app/composables/useReplayToast.ts +72 -0
  343. package/frontend-vue/app/composables/useTemplateCategories.ts +35 -0
  344. package/frontend-vue/app/composables/useTemplateDrawer.ts +196 -62
  345. package/frontend-vue/app/composables/useTemplateEditor.ts +318 -86
  346. package/frontend-vue/app/emails/EmailMailingTemplate.vue +16 -4
  347. package/frontend-vue/app/emails/components/MailingBlocks.vue +96 -38
  348. package/frontend-vue/app/emails/components/MailingTextLines.vue +20 -0
  349. package/frontend-vue/app/plugins/column-displays.ts +65 -0
  350. package/frontend-vue/app/plugins/{template-gallery-display.client.ts → template-gallery-display.ts} +1 -1
  351. package/frontend-vue/app/types/mailing.ts +193 -18
  352. package/frontend-vue/app/utils/api-error.ts +33 -0
  353. package/frontend-vue/app/utils/autocomplete.ts +120 -0
  354. package/frontend-vue/app/utils/blocks/components.ts +10 -10
  355. package/frontend-vue/app/utils/blocks/dnd.ts +7 -3
  356. package/frontend-vue/app/utils/blocks/factory.ts +199 -38
  357. package/frontend-vue/app/utils/blocks/fields.ts +39 -19
  358. package/frontend-vue/app/utils/blocks/index.ts +1 -0
  359. package/frontend-vue/app/utils/blocks/outline.ts +91 -0
  360. package/frontend-vue/app/utils/blocks/tree.ts +114 -2
  361. package/frontend-vue/app/utils/categories.ts +201 -15
  362. package/frontend-vue/app/utils/changes.ts +152 -0
  363. package/frontend-vue/app/utils/conditions.ts +25 -19
  364. package/frontend-vue/app/utils/editor-checks.ts +266 -0
  365. package/frontend-vue/app/utils/editor-route.ts +43 -4
  366. package/frontend-vue/app/utils/funnel.ts +74 -0
  367. package/frontend-vue/app/utils/gallery.ts +179 -1
  368. package/frontend-vue/app/utils/history.ts +66 -0
  369. package/frontend-vue/app/utils/lifecycle.ts +185 -0
  370. package/frontend-vue/app/utils/locales.ts +107 -2
  371. package/frontend-vue/app/utils/overview-setup.ts +83 -0
  372. package/frontend-vue/app/utils/pages.ts +5 -0
  373. package/frontend-vue/app/utils/paths.ts +51 -15
  374. package/frontend-vue/app/utils/period.ts +62 -22
  375. package/frontend-vue/app/utils/problems.ts +181 -0
  376. package/frontend-vue/app/utils/recipients.ts +49 -0
  377. package/frontend-vue/app/utils/rendering-title.ts +23 -0
  378. package/frontend-vue/app/utils/send-actions.ts +207 -0
  379. package/frontend-vue/app/utils/send-link.ts +28 -0
  380. package/frontend-vue/app/utils/send-snippet.ts +97 -0
  381. package/frontend-vue/app/utils/send-stats.ts +24 -0
  382. package/frontend-vue/app/utils/send-values.ts +156 -0
  383. package/frontend-vue/app/utils/shortcuts.ts +113 -0
  384. package/frontend-vue/app/utils/slug.ts +82 -0
  385. package/frontend-vue/app/utils/starters.ts +23 -0
  386. package/frontend-vue/app/utils/suppression.ts +112 -0
  387. package/frontend-vue/app/utils/system-variables.ts +165 -0
  388. package/frontend-vue/app/utils/template-attention.ts +104 -0
  389. package/frontend-vue/app/utils/timeline.ts +221 -19
  390. package/frontend-vue/app/utils/tokens.ts +115 -13
  391. package/frontend-vue/app/utils/variable-types.ts +23 -0
  392. package/frontend-vue/app/utils/variables.ts +208 -1
  393. package/frontend-vue/app/utils/version-history.ts +53 -0
  394. package/frontend-vue/app/utils/webhook-status.ts +170 -0
  395. package/frontend-vue/dms.frontend.ts +19 -11
  396. package/frontend-vue/i18n/locales/mailing-en-GB.json +1173 -161
  397. package/frontend-vue/i18n/locales/mailing-fr-FR.json +1220 -208
  398. package/frontend-vue/package.json +4 -1
  399. package/frontend-vue/pnpm-lock.yaml +46 -161
  400. package/frontend-vue/tests/api-error.test.ts +27 -0
  401. package/frontend-vue/tests/autocomplete.test.ts +84 -0
  402. package/frontend-vue/tests/block-components.test.ts +1 -1
  403. package/frontend-vue/tests/block-fields.test.ts +12 -2
  404. package/frontend-vue/tests/blocks-factory.test.ts +46 -0
  405. package/frontend-vue/tests/blocks-outline.test.ts +102 -0
  406. package/frontend-vue/tests/blocks-tree.test.ts +28 -1
  407. package/frontend-vue/tests/categories-input.test.ts +139 -0
  408. package/frontend-vue/tests/categories.test.ts +148 -15
  409. package/frontend-vue/tests/changes.test.ts +136 -0
  410. package/frontend-vue/tests/column-displays.test.ts +73 -0
  411. package/frontend-vue/tests/conditions.test.ts +49 -19
  412. package/frontend-vue/tests/dnd.test.ts +10 -0
  413. package/frontend-vue/tests/editor-block-list.test.ts +24 -2
  414. package/frontend-vue/tests/editor-checks.test.ts +178 -0
  415. package/frontend-vue/tests/editor-route.test.ts +37 -9
  416. package/frontend-vue/tests/editor-session.test.ts +206 -0
  417. package/frontend-vue/tests/footer-block.test.ts +88 -0
  418. package/frontend-vue/tests/funnel-card.test.ts +116 -0
  419. package/frontend-vue/tests/funnel.test.ts +81 -0
  420. package/frontend-vue/tests/gallery.test.ts +124 -2
  421. package/frontend-vue/tests/helpers/nuxt-globals.ts +232 -0
  422. package/frontend-vue/tests/history.test.ts +48 -0
  423. package/frontend-vue/tests/i18n-editor.test.ts +38 -0
  424. package/frontend-vue/tests/i18n.test.ts +55 -0
  425. package/frontend-vue/tests/lifecycle.test.ts +145 -0
  426. package/frontend-vue/tests/locales.test.ts +60 -0
  427. package/frontend-vue/tests/localized-defaults.test.ts +54 -0
  428. package/frontend-vue/tests/new-template-modal.test.ts +210 -0
  429. package/frontend-vue/tests/overview-gate.test.ts +146 -0
  430. package/frontend-vue/tests/overview-setup.test.ts +78 -0
  431. package/frontend-vue/tests/paths.test.ts +23 -0
  432. package/frontend-vue/tests/period.test.ts +59 -10
  433. package/frontend-vue/tests/problems.test.ts +153 -0
  434. package/frontend-vue/tests/recipients.test.ts +47 -0
  435. package/frontend-vue/tests/rendering-title.test.ts +24 -0
  436. package/frontend-vue/tests/replay-toast.test.ts +67 -0
  437. package/frontend-vue/tests/retention-input.test.ts +129 -0
  438. package/frontend-vue/tests/send-actions.test.ts +128 -0
  439. package/frontend-vue/tests/send-modals.test.ts +415 -0
  440. package/frontend-vue/tests/send-snippet.test.ts +51 -0
  441. package/frontend-vue/tests/send-stats.test.ts +12 -0
  442. package/frontend-vue/tests/send-values.test.ts +119 -0
  443. package/frontend-vue/tests/shortcuts.test.ts +75 -0
  444. package/frontend-vue/tests/slug.test.ts +88 -0
  445. package/frontend-vue/tests/suppression.test.ts +159 -0
  446. package/frontend-vue/tests/system-variables.test.ts +133 -0
  447. package/frontend-vue/tests/template-attention.test.ts +123 -0
  448. package/frontend-vue/tests/template-drawer.test.ts +449 -0
  449. package/frontend-vue/tests/template-editor.test.ts +218 -9
  450. package/frontend-vue/tests/template-gallery-display.test.ts +32 -0
  451. package/frontend-vue/tests/template-gallery.test.ts +370 -0
  452. package/frontend-vue/tests/template-overlays.test.ts +125 -0
  453. package/frontend-vue/tests/timeline.test.ts +175 -20
  454. package/frontend-vue/tests/tokens.test.ts +79 -1
  455. package/frontend-vue/tests/variable-path.test.ts +22 -0
  456. package/frontend-vue/tests/variable-types.test.ts +20 -0
  457. package/frontend-vue/tests/variables.test.ts +90 -2
  458. package/frontend-vue/tests/version-history.test.ts +41 -0
  459. package/frontend-vue/tests/webhook-status.test.ts +108 -0
  460. package/package.json +11 -9
  461. package/src/automation/email-event-trigger.ts +3 -3
  462. package/src/automation/send-template-action.ts +7 -5
  463. package/src/config.ts +3 -1
  464. package/src/constants.ts +18 -2
  465. package/src/crons/attention.ts +21 -0
  466. package/src/crons/index.ts +2 -0
  467. package/src/crons/jobs.ts +35 -0
  468. package/src/crons/retention.ts +7 -22
  469. package/src/data/displays.ts +18 -0
  470. package/src/data/index.ts +1 -0
  471. package/src/data/locale-fallback.ts +26 -0
  472. package/src/data/read-only-routes.ts +14 -0
  473. package/src/data/sends-table.ts +204 -19
  474. package/src/data/suppressions-table.ts +127 -0
  475. package/src/data/templates-table.ts +295 -63
  476. package/src/data-types/categories-type.ts +4 -5
  477. package/src/data-types/category-type.ts +1 -1
  478. package/src/db/models/index.ts +3 -0
  479. package/src/db/models/sends.model.ts +102 -1
  480. package/src/db/models/settings.model.ts +2 -12
  481. package/src/db/models/suppressions.model.ts +53 -0
  482. package/src/db/models/template-versions.model.ts +23 -0
  483. package/src/db/models/templates.model.ts +17 -0
  484. package/src/db/models/webhook-status.model.ts +25 -0
  485. package/src/db/tables/index.ts +3 -0
  486. package/src/db/tables/send-events.table.ts +6 -2
  487. package/src/db/tables/sends.table.ts +37 -2
  488. package/src/db/tables/suppressions.table.ts +46 -0
  489. package/src/db/tables/template-versions.table.ts +44 -0
  490. package/src/db/tables/templates.table.ts +67 -5
  491. package/src/db/tables/webhook-status.table.ts +44 -0
  492. package/src/engine/changes.ts +198 -0
  493. package/src/engine/conditions.ts +1 -7
  494. package/src/engine/coverage.ts +7 -2
  495. package/src/engine/fallback-html.ts +80 -42
  496. package/src/engine/index.ts +3 -0
  497. package/src/engine/plain-text.ts +86 -0
  498. package/src/engine/resolve.ts +49 -15
  499. package/src/engine/system-variables.ts +124 -0
  500. package/src/engine/tokens.ts +13 -1
  501. package/src/hooks/tenant-export.ts +21 -2
  502. package/src/implementations/dms-mailing/index.ts +1 -1
  503. package/src/index.ts +29 -13
  504. package/src/pages/editor.ts +13 -2
  505. package/src/pages/index.ts +6 -3
  506. package/src/pages/module.ts +31 -2
  507. package/src/pages/overview.ts +187 -128
  508. package/src/pages/sends.ts +271 -112
  509. package/src/pages/settings/form.ts +144 -54
  510. package/src/pages/settings/page.ts +9 -17
  511. package/src/pages/suppressions.ts +157 -0
  512. package/src/pages/templates.ts +139 -42
  513. package/src/permissions.ts +6 -4
  514. package/src/quick-actions.ts +12 -5
  515. package/src/routes/events.ts +110 -33
  516. package/src/routes/index.ts +2 -0
  517. package/src/routes/metrics.ts +348 -98
  518. package/src/routes/period.ts +23 -6
  519. package/src/routes/sends.ts +413 -41
  520. package/src/routes/settings.ts +291 -26
  521. package/src/routes/suppressions.ts +87 -0
  522. package/src/routes/template-versions.ts +116 -0
  523. package/src/routes/templates.ts +334 -48
  524. package/src/services/attention.ts +283 -30
  525. package/src/services/catalog.ts +28 -0
  526. package/src/services/categories.ts +28 -27
  527. package/src/services/checked-send.ts +123 -0
  528. package/src/services/events.ts +174 -20
  529. package/src/services/metrics.ts +137 -28
  530. package/src/services/notify.ts +126 -44
  531. package/src/services/provider-banner.ts +165 -0
  532. package/src/services/render.ts +1 -0
  533. package/src/services/send-stat-items.ts +160 -0
  534. package/src/services/send-stats.ts +122 -0
  535. package/src/services/send.ts +193 -31
  536. package/src/services/settings.ts +52 -8
  537. package/src/services/starters.ts +383 -0
  538. package/src/services/suppressions.ts +116 -0
  539. package/src/services/template-attention.ts +82 -0
  540. package/src/services/template-stats.ts +97 -0
  541. package/src/services/templates.ts +400 -37
  542. package/src/services/webhook-status.ts +202 -0
  543. package/src/types/blocks.ts +6 -0
  544. package/src/types/index.ts +2 -0
  545. package/src/types/sends.ts +64 -2
  546. package/src/types/suppressions.ts +27 -0
  547. package/src/types/webhook.ts +29 -0
  548. package/src/validation/settings.schema.ts +173 -17
  549. package/src/validation/templates.schema.ts +53 -1
  550. package/dist/antelope-modules.d.ts +0 -32
  551. package/dist/antelope-modules.js +0 -36
  552. package/dist/antelope-modules.js.map +0 -1
  553. package/dist/test/antelope.test.d.ts +0 -2
  554. package/dist/test/antelope.test.js +0 -180
  555. package/dist/test/antelope.test.js.map +0 -1
  556. package/dist/test/helpers/http.d.ts +0 -4
  557. package/dist/test/helpers/http.js +0 -28
  558. package/dist/test/helpers/http.js.map +0 -1
  559. package/dist/test/helpers/owner.d.ts +0 -5
  560. package/dist/test/helpers/owner.js +0 -30
  561. package/dist/test/helpers/owner.js.map +0 -1
  562. package/dist/test/integration/events.test.d.ts +0 -1
  563. package/dist/test/integration/events.test.js +0 -143
  564. package/dist/test/integration/events.test.js.map +0 -1
  565. package/dist/test/integration/retention.test.d.ts +0 -1
  566. package/dist/test/integration/retention.test.js +0 -291
  567. package/dist/test/integration/retention.test.js.map +0 -1
  568. package/dist/test/integration/sends.test.d.ts +0 -1
  569. package/dist/test/integration/sends.test.js +0 -210
  570. package/dist/test/integration/sends.test.js.map +0 -1
  571. package/dist/test/integration/settings.test.d.ts +0 -1
  572. package/dist/test/integration/settings.test.js +0 -137
  573. package/dist/test/integration/settings.test.js.map +0 -1
  574. package/dist/test/integration/setup.test.d.ts +0 -1
  575. package/dist/test/integration/setup.test.js +0 -32
  576. package/dist/test/integration/setup.test.js.map +0 -1
  577. package/dist/test/integration/templates.test.d.ts +0 -1
  578. package/dist/test/integration/templates.test.js +0 -233
  579. package/dist/test/integration/templates.test.js.map +0 -1
  580. package/dist/test/integration/tenant-export.test.d.ts +0 -1
  581. package/dist/test/integration/tenant-export.test.js +0 -85
  582. package/dist/test/integration/tenant-export.test.js.map +0 -1
  583. package/dist/test/unit/attention.test.d.ts +0 -1
  584. package/dist/test/unit/attention.test.js +0 -109
  585. package/dist/test/unit/attention.test.js.map +0 -1
  586. package/dist/test/unit/automation.test.d.ts +0 -1
  587. package/dist/test/unit/automation.test.js +0 -104
  588. package/dist/test/unit/automation.test.js.map +0 -1
  589. package/dist/test/unit/categories.test.d.ts +0 -1
  590. package/dist/test/unit/categories.test.js +0 -38
  591. package/dist/test/unit/categories.test.js.map +0 -1
  592. package/dist/test/unit/engine/conditions.test.d.ts +0 -1
  593. package/dist/test/unit/engine/conditions.test.js +0 -23
  594. package/dist/test/unit/engine/conditions.test.js.map +0 -1
  595. package/dist/test/unit/engine/coverage.test.d.ts +0 -1
  596. package/dist/test/unit/engine/coverage.test.js +0 -106
  597. package/dist/test/unit/engine/coverage.test.js.map +0 -1
  598. package/dist/test/unit/engine/fallback-html.test.d.ts +0 -1
  599. package/dist/test/unit/engine/fallback-html.test.js +0 -78
  600. package/dist/test/unit/engine/fallback-html.test.js.map +0 -1
  601. package/dist/test/unit/engine/paths.test.d.ts +0 -1
  602. package/dist/test/unit/engine/paths.test.js +0 -33
  603. package/dist/test/unit/engine/paths.test.js.map +0 -1
  604. package/dist/test/unit/engine/resolve.test.d.ts +0 -1
  605. package/dist/test/unit/engine/resolve.test.js +0 -157
  606. package/dist/test/unit/engine/resolve.test.js.map +0 -1
  607. package/dist/test/unit/engine/tokens.test.d.ts +0 -1
  608. package/dist/test/unit/engine/tokens.test.js +0 -26
  609. package/dist/test/unit/engine/tokens.test.js.map +0 -1
  610. package/dist/test/unit/events.test.d.ts +0 -1
  611. package/dist/test/unit/events.test.js +0 -13
  612. package/dist/test/unit/events.test.js.map +0 -1
  613. package/dist/test/unit/metrics.test.d.ts +0 -1
  614. package/dist/test/unit/metrics.test.js +0 -108
  615. package/dist/test/unit/metrics.test.js.map +0 -1
  616. package/dist/test/unit/notify.test.d.ts +0 -1
  617. package/dist/test/unit/notify.test.js +0 -49
  618. package/dist/test/unit/notify.test.js.map +0 -1
  619. package/dist/test/unit/period.test.d.ts +0 -1
  620. package/dist/test/unit/period.test.js +0 -61
  621. package/dist/test/unit/period.test.js.map +0 -1
  622. package/dist/test/unit/permissions.test.d.ts +0 -1
  623. package/dist/test/unit/permissions.test.js +0 -27
  624. package/dist/test/unit/permissions.test.js.map +0 -1
  625. package/dist/test/unit/provider.test.d.ts +0 -1
  626. package/dist/test/unit/provider.test.js +0 -47
  627. package/dist/test/unit/provider.test.js.map +0 -1
  628. package/dist/test/unit/realtime.test.d.ts +0 -1
  629. package/dist/test/unit/realtime.test.js +0 -13
  630. package/dist/test/unit/realtime.test.js.map +0 -1
  631. package/dist/test/unit/render.test.d.ts +0 -1
  632. package/dist/test/unit/render.test.js +0 -23
  633. package/dist/test/unit/render.test.js.map +0 -1
  634. package/dist/test/unit/retention.test.d.ts +0 -1
  635. package/dist/test/unit/retention.test.js +0 -66
  636. package/dist/test/unit/retention.test.js.map +0 -1
  637. package/dist/test/unit/send-batch.test.d.ts +0 -1
  638. package/dist/test/unit/send-batch.test.js +0 -46
  639. package/dist/test/unit/send-batch.test.js.map +0 -1
  640. package/dist/test/unit/send.test.d.ts +0 -1
  641. package/dist/test/unit/send.test.js +0 -120
  642. package/dist/test/unit/send.test.js.map +0 -1
  643. package/dist/test/unit/smoke.test.d.ts +0 -1
  644. package/dist/test/unit/smoke.test.js +0 -10
  645. package/dist/test/unit/smoke.test.js.map +0 -1
  646. package/dist/test/unit/status.test.d.ts +0 -1
  647. package/dist/test/unit/status.test.js +0 -16
  648. package/dist/test/unit/status.test.js.map +0 -1
  649. package/dist/test/unit/templates.test.d.ts +0 -1
  650. package/dist/test/unit/templates.test.js +0 -48
  651. package/dist/test/unit/templates.test.js.map +0 -1
  652. package/dist/test/unit/tenant-export.test.d.ts +0 -1
  653. package/dist/test/unit/tenant-export.test.js +0 -59
  654. package/dist/test/unit/tenant-export.test.js.map +0 -1
  655. package/dist/test/unit/webhook-secret.test.d.ts +0 -1
  656. package/dist/test/unit/webhook-secret.test.js +0 -43
  657. package/dist/test/unit/webhook-secret.test.js.map +0 -1
  658. package/frontend-vue/app/components/AttentionCard.vue +0 -78
  659. package/frontend-vue/app/components/HtmlPreviewModal.vue +0 -20
  660. package/frontend-vue/app/components/ProviderChip.vue +0 -39
  661. package/frontend-vue/app/components/editor/EditorBlockSettings.vue +0 -186
  662. package/frontend-vue/app/components/editor/EditorDataPanel.vue +0 -130
  663. package/frontend-vue/app/components/editor/EditorDraftBar.vue +0 -35
  664. package/frontend-vue/app/components/editor/EditorRail.vue +0 -58
  665. package/frontend-vue/app/components/editor/EditorSubjectSettings.vue +0 -37
  666. package/frontend-vue/app/components/editor/EditorTemplateSettings.vue +0 -87
  667. package/frontend-vue/app/components/editor/EditorTestSendModal.vue +0 -66
  668. package/frontend-vue/app/components/editor/EditorVariables.vue +0 -163
  669. package/frontend-vue/app/composables/useMailingPeriodData.ts +0 -39
  670. package/src/antelope-modules.ts +0 -48
  671. package/src/test/antelope.test.ts +0 -190
  672. package/src/test/helpers/http.ts +0 -23
  673. package/src/test/helpers/owner.ts +0 -37
  674. package/src/test/integration/events.test.ts +0 -159
  675. package/src/test/integration/retention.test.ts +0 -363
  676. package/src/test/integration/sends.test.ts +0 -233
  677. package/src/test/integration/settings.test.ts +0 -128
  678. package/src/test/integration/setup.test.ts +0 -35
  679. package/src/test/integration/templates.test.ts +0 -272
  680. package/src/test/integration/tenant-export.test.ts +0 -125
  681. package/src/test/unit/attention.test.ts +0 -129
  682. package/src/test/unit/automation.test.ts +0 -130
  683. package/src/test/unit/categories.test.ts +0 -51
  684. package/src/test/unit/engine/conditions.test.ts +0 -35
  685. package/src/test/unit/engine/coverage.test.ts +0 -118
  686. package/src/test/unit/engine/fallback-html.test.ts +0 -86
  687. package/src/test/unit/engine/paths.test.ts +0 -39
  688. package/src/test/unit/engine/resolve.test.ts +0 -176
  689. package/src/test/unit/engine/tokens.test.ts +0 -26
  690. package/src/test/unit/events.test.ts +0 -19
  691. package/src/test/unit/metrics.test.ts +0 -170
  692. package/src/test/unit/notify.test.ts +0 -71
  693. package/src/test/unit/period.test.ts +0 -91
  694. package/src/test/unit/permissions.test.ts +0 -30
  695. package/src/test/unit/provider.test.ts +0 -57
  696. package/src/test/unit/realtime.test.ts +0 -11
  697. package/src/test/unit/render.test.ts +0 -23
  698. package/src/test/unit/retention.test.ts +0 -91
  699. package/src/test/unit/send-batch.test.ts +0 -72
  700. package/src/test/unit/send.test.ts +0 -161
  701. package/src/test/unit/smoke.test.ts +0 -8
  702. package/src/test/unit/status.test.ts +0 -14
  703. package/src/test/unit/templates.test.ts +0 -52
  704. package/src/test/unit/tenant-export.test.ts +0 -88
  705. package/src/test/unit/webhook-secret.test.ts +0 -51
@@ -1,23 +1,49 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
- import EmailButton from '#dms-layout/app/emails/components/EmailButton.vue'
4
- import EmailOTP from '#dms-layout/app/emails/components/EmailOTP.vue'
5
3
  import { emailStyles } from '#dms-layout/app/emails/components/index'
4
+ import MailingTextLines from './MailingTextLines.vue'
6
5
  import type {
7
6
  ResolvedBlock,
7
+ ResolvedButton,
8
+ ResolvedFooter,
8
9
  ResolvedHeading,
9
10
  ResolvedParagraph,
10
11
  } from '../../types/mailing'
11
12
 
12
- const FOOTER_TYPE = 'footer'
13
+ interface FooterLink {
14
+ label: string
15
+ href: string
16
+ }
13
17
 
14
- const heroStyle = { width: '100%', borderRadius: '8px', marginBottom: '20px' }
15
- const totalStyle = { ...emailStyles.text, fontWeight: '600' }
16
- const rowLabelStyle = { ...emailStyles.text, margin: '0' }
17
- const rowValueStyle = {
18
- ...emailStyles.text,
19
- margin: '0',
20
- textAlign: 'right' as const,
18
+ const FOOTER_TYPE = 'footer'
19
+ const FOOTER_LINK_SEPARATOR = ' · '
20
+
21
+ /** Long words (a URL, a reference) wrap instead of widening the e-mail. */
22
+ const wrap = { overflowWrap: 'anywhere', wordBreak: 'break-word' }
23
+ const blockSpacing = { margin: '0 0 16px' }
24
+ const textStyle = { ...emailStyles.text, ...wrap }
25
+ const heroStyle = {
26
+ display: 'block',
27
+ width: '100%',
28
+ borderRadius: '8px',
29
+ margin: '0 0 20px',
30
+ }
31
+ const cellStyle = { ...textStyle, margin: '0' }
32
+ const valueCellStyle = { ...cellStyle, textAlign: 'right' as const }
33
+ const totalStyle = { ...cellStyle, fontWeight: '600' }
34
+ const totalValueStyle = { ...valueCellStyle, fontWeight: '600' }
35
+ const codeStyle = { ...emailStyles.otp.code, ...wrap }
36
+ const dividerStyle = { border: '0', ...emailStyles.divider }
37
+ /** Darker than the DMS footer grey: small print stays readable (4.8:1). */
38
+ const FOOTER_TEXT_COLOR = '#71717a'
39
+ const footerStyle = {
40
+ ...emailStyles.footer,
41
+ color: FOOTER_TEXT_COLOR,
42
+ ...wrap,
43
+ }
44
+ const footerLinkStyle = {
45
+ color: FOOTER_TEXT_COLOR,
46
+ textDecoration: 'underline',
21
47
  }
22
48
 
23
49
  interface Props {
@@ -27,28 +53,49 @@ interface Props {
27
53
 
28
54
  const props = defineProps<Props>()
29
55
 
56
+ /** A footer with neither text nor link would draw a lone divider. */
57
+ const isEmptyFooter = (block: ResolvedBlock): boolean =>
58
+ block.type === FOOTER_TYPE && !block.text.trim() && !footerLinks(block).length
59
+
30
60
  const visibleBlocks = computed(() =>
31
- props.blocks.filter((block) =>
32
- props.footerOnly ? block.type === FOOTER_TYPE : block.type !== FOOTER_TYPE,
33
- ),
61
+ props.blocks
62
+ .filter((block) =>
63
+ props.footerOnly
64
+ ? block.type === FOOTER_TYPE
65
+ : block.type !== FOOTER_TYPE,
66
+ )
67
+ .filter((block) => !isEmptyFooter(block)),
34
68
  )
35
69
 
36
70
  const headingStyle = (block: ResolvedHeading) => ({
37
71
  ...emailStyles.heading,
72
+ ...wrap,
38
73
  textAlign: block.align,
39
74
  fontSize: `${block.size}px`,
40
75
  })
41
76
 
42
77
  const paragraphStyle = (block: ResolvedParagraph) => ({
43
- ...emailStyles.text,
78
+ ...textStyle,
44
79
  textAlign: block.align,
45
80
  })
81
+
82
+ const buttonRowStyle = (block: ResolvedButton) => ({
83
+ ...emailStyles.buttonContainer,
84
+ textAlign: block.align,
85
+ })
86
+
87
+ function footerLinks(block: ResolvedFooter): FooterLink[] {
88
+ return [
89
+ { label: block.unsubscribeLabel, href: block.unsubscribeUrl },
90
+ { label: block.preferencesLabel, href: block.preferencesUrl },
91
+ ].filter((link) => link.href && link.label.trim())
92
+ }
46
93
  </script>
47
94
 
48
95
  <template>
49
96
  <template v-for="block in visibleBlocks" :key="block.id">
50
97
  <EImg
51
- v-if="block.type === 'hero'"
98
+ v-if="block.type === 'hero' && block.imageUrl"
52
99
  :src="block.imageUrl"
53
100
  :alt="block.alt"
54
101
  :style="heroStyle"
@@ -59,59 +106,70 @@ const paragraphStyle = (block: ResolvedParagraph) => ({
59
106
  as="h1"
60
107
  :style="headingStyle(block)"
61
108
  >
62
- {{ block.text }}
109
+ <MailingTextLines :text="block.text" />
63
110
  </EHeading>
64
111
 
65
112
  <EText
66
113
  v-else-if="block.type === 'paragraph'"
67
114
  :style="paragraphStyle(block)"
68
115
  >
69
- {{ block.text }}
116
+ <MailingTextLines :text="block.text" />
70
117
  </EText>
71
118
 
72
- <EmailOTP v-else-if="block.type === 'code'" :code="block.text" />
119
+ <ESection v-else-if="block.type === 'code'" :style="emailStyles.otp.box">
120
+ <EText :style="codeStyle">{{ block.text }}</EText>
121
+ </ESection>
73
122
 
74
- <ESection v-else-if="block.type === 'list'">
123
+ <ESection v-else-if="block.type === 'list'" :style="blockSpacing">
75
124
  <ERow v-for="(row, index) in block.rows" :key="index">
76
125
  <EColumn>
77
- <EText :style="rowLabelStyle">{{ row.label }}</EText>
126
+ <EText :style="cellStyle">{{ row.label }}</EText>
78
127
  </EColumn>
79
128
  <EColumn align="right">
80
- <EText :style="rowValueStyle">{{ row.value }}</EText>
129
+ <EText :style="valueCellStyle">{{ row.value }}</EText>
81
130
  </EColumn>
82
131
  </ERow>
83
132
  </ESection>
84
133
 
85
- <ESection v-else-if="block.type === 'total'">
134
+ <ESection v-else-if="block.type === 'total'" :style="blockSpacing">
86
135
  <ERow>
87
136
  <EColumn>
88
137
  <EText :style="totalStyle">{{ block.label }}</EText>
89
138
  </EColumn>
90
139
  <EColumn align="right">
91
- <EText :style="totalStyle">{{ block.value }}</EText>
140
+ <EText :style="totalValueStyle">{{ block.value }}</EText>
92
141
  </EColumn>
93
142
  </ERow>
94
143
  </ESection>
95
144
 
96
145
  <ESection
97
146
  v-else-if="block.type === 'button'"
98
- :style="{ textAlign: block.align }"
147
+ :style="buttonRowStyle(block)"
99
148
  >
100
- <EmailButton :href="block.href">{{ block.text }}</EmailButton>
149
+ <EButton :href="block.href" :style="emailStyles.button">
150
+ {{ block.text }}
151
+ </EButton>
101
152
  </ESection>
102
153
 
103
- <EHr v-else-if="block.type === 'divider'" :style="emailStyles.divider" />
104
-
105
- <EText v-else-if="block.type === 'footer'" :style="emailStyles.footer">
106
- {{ block.text }}
107
- <br />
108
- <ELink v-if="block.unsubscribeUrl" :href="block.unsubscribeUrl">
109
- {{ block.unsubscribeLabel }}
110
- </ELink>
111
- ·
112
- <ELink v-if="block.preferencesUrl" :href="block.preferencesUrl">
113
- {{ block.preferencesLabel }}
114
- </ELink>
115
- </EText>
154
+ <EHr v-else-if="block.type === 'divider'" :style="dividerStyle" />
155
+
156
+ <template v-else-if="block.type === 'footer'">
157
+ <EHr :style="dividerStyle" />
158
+ <EText :style="footerStyle">
159
+ <MailingTextLines :text="block.text" />
160
+ <template v-if="footerLinks(block).length">
161
+ <br />
162
+ <template
163
+ v-for="(link, index) in footerLinks(block)"
164
+ :key="link.href"
165
+ >
166
+ <template v-if="index > 0">{{ FOOTER_LINK_SEPARATOR }}</template>
167
+ <ELink :href="link.href" :style="footerLinkStyle">
168
+ {{ link.label }}
169
+ </ELink>
170
+ </template>
171
+ </template>
172
+ </EText>
173
+ </template>
116
174
  </template>
117
175
  </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ interface Props {
5
+ text: string
6
+ }
7
+
8
+ const LINE_BREAK = /\r?\n/
9
+
10
+ const props = defineProps<Props>()
11
+
12
+ const lines = computed(() => props.text.split(LINE_BREAK))
13
+ </script>
14
+
15
+ <template>
16
+ <template v-for="(line, index) in lines" :key="index">
17
+ <br v-if="index > 0" />
18
+ {{ line }}
19
+ </template>
20
+ </template>
@@ -0,0 +1,65 @@
1
+ import { h, type VNode } from 'vue'
2
+ import CategoryLabel from '../components/CategoryLabel.vue'
3
+ import { localeChipClass, localeChips } from '../utils/locales'
4
+
5
+ export const LOCALE_CHIPS_DISPLAY_ID = 'mailing:locale-chips'
6
+ export const CATEGORY_DISPLAY_ID = 'mailing:category'
7
+
8
+ const EMPTY_CELL = '—'
9
+ const CHIP_ROW_CLASS = 'inline-flex items-center gap-1'
10
+
11
+ interface WorkspaceLocale {
12
+ code: string
13
+ }
14
+
15
+ const emptyCell = () => h('span', { class: 'text-dimmed' }, EMPTY_CELL)
16
+
17
+ /**
18
+ * A template's locales as mono chips, the workspace locales it lacks drawn
19
+ * dashed in the warning tone, like the gallery card.
20
+ */
21
+ export function renderLocaleChips(
22
+ value: unknown,
23
+ workspaceCodes: string[],
24
+ ): VNode {
25
+ const chips = localeChips(
26
+ typeof value === 'string' ? value : '',
27
+ workspaceCodes,
28
+ )
29
+ if (!chips.length) return emptyCell()
30
+ return h(
31
+ 'span',
32
+ { class: CHIP_ROW_CLASS },
33
+ chips.map((chip) =>
34
+ h('span', { class: localeChipClass(chip.present) }, chip.code),
35
+ ),
36
+ )
37
+ }
38
+
39
+ /** A template's category as its settings label and icon. */
40
+ export function renderCategory(value: unknown): VNode {
41
+ return h(CategoryLabel, { value: typeof value === 'string' ? value : null })
42
+ }
43
+
44
+ function workspaceCodes(): string[] {
45
+ const { uniqueLocales } = useUniqueLocales()
46
+ return uniqueLocales.value.map((entry: WorkspaceLocale) => entry.code)
47
+ }
48
+
49
+ /**
50
+ * Frontend renderers of the backend column displays `mailing:locale-chips`
51
+ * and `mailing:category` (`src/data/displays.ts`).
52
+ */
53
+ export default defineDmsPlugin(() => {
54
+ const { registerDataType } = useDataTypes()
55
+ registerDataType({
56
+ id: LOCALE_CHIPS_DISPLAY_ID,
57
+ formatter: {
58
+ default: (value: unknown) => renderLocaleChips(value, workspaceCodes()),
59
+ },
60
+ })
61
+ registerDataType({
62
+ id: CATEGORY_DISPLAY_ID,
63
+ formatter: { default: renderCategory },
64
+ })
65
+ })
@@ -1,6 +1,6 @@
1
1
  import TemplateGallery from '../components/TemplateGallery.vue'
2
2
 
3
- const GALLERY_DISPLAY_ID = 'gallery'
3
+ const GALLERY_DISPLAY_ID = 'mailing:gallery'
4
4
  const GALLERY_DISPLAY_ORDER = 5
5
5
 
6
6
  export default defineDmsPlugin(() => {
@@ -253,6 +253,7 @@ export type SendStatus =
253
253
  | 'spam'
254
254
  | 'failed'
255
255
  | 'unsubscribed'
256
+ | 'suppressed'
256
257
 
257
258
  export const SEND_STATUSES: SendStatus[] = [
258
259
  'queued',
@@ -264,9 +265,11 @@ export const SEND_STATUSES: SendStatus[] = [
264
265
  'spam',
265
266
  'failed',
266
267
  'unsubscribed',
268
+ 'suppressed',
267
269
  ]
268
270
 
269
- export type SendEventType = SendStatus
271
+ /** What a provider reports: a suppressed send never reached one. */
272
+ export type SendEventType = Exclude<SendStatus, 'suppressed'>
270
273
 
271
274
  export interface TemplateCategory {
272
275
  id: string
@@ -278,26 +281,140 @@ export interface TemplateRow {
278
281
  _id: string
279
282
  slug: string
280
283
  name: string
281
- /** Absent or empty when the template is uncategorised. */
282
- category?: string
284
+ /** Absent, `null` or empty when the template is uncategorised. */
285
+ category?: string | null
283
286
  status: TemplateStatus
284
287
  /** Comma-separated locale codes of the content; blank means unknown. */
285
288
  locales?: string
286
289
  updatedAt: string
287
290
  updatedBy: string
291
+ /** Version customers receive; 0 or absent when never published. */
292
+ publishedVersion?: number
293
+ publishedAt?: string | null
294
+ /** When the template last left `live` by an unpublish. */
295
+ unpublishedAt?: string | null
296
+ /** Whether a draft holds changes customers do not receive yet. */
297
+ isDraftPending?: boolean
298
+ /** Why the template needs a look, comma-separated; blank when nothing does. */
299
+ attention: string
300
+ }
301
+
302
+ export type TemplateChangeKind =
303
+ | 'locale_added'
304
+ | 'locale_removed'
305
+ | 'subject'
306
+ | 'preheader'
307
+ | 'block_added'
308
+ | 'block_removed'
309
+ | 'block_changed'
310
+ | 'blocks_reordered'
311
+ | 'variable_added'
312
+ | 'variable_removed'
313
+ | 'variable_changed'
314
+
315
+ /**
316
+ * One difference between what customers receive and the draft: per locale
317
+ * for the content, per path for the variable declarations.
318
+ */
319
+ export interface TemplateChange {
320
+ /** Blank for a change of the variable declarations. */
321
+ locale: string
322
+ kind: TemplateChangeKind
323
+ blockId?: string
324
+ blockType?: BlockType
325
+ /** The declared path a variable change is about. */
326
+ variable?: string
327
+ }
328
+
329
+ export interface TemplateSender {
330
+ name: string
331
+ email: string
332
+ replyTo: string
288
333
  }
289
334
 
290
335
  export interface TemplateContentResponse {
291
- template: TemplateRow
336
+ template: TemplateRow & {
337
+ json_variables?: string
338
+ draftUpdatedAt?: string
339
+ draftUpdatedBy?: string
340
+ publishedBy?: string
341
+ }
342
+ /** The content the editor works on: the draft, else the published one. */
292
343
  content: TemplateContent
344
+ /** What customers receive; `null` when the template was never published. */
345
+ publishedContent: TemplateContent | null
346
+ hasDraft: boolean
347
+ /** Version customers receive; 0 when never published. */
348
+ version: number
349
+ /** The draft revision a save or a publish carries back. */
350
+ revision: number
351
+ changes: TemplateChange[]
352
+ /** The declarations the editor works on: the draft's, else the published. */
293
353
  variables: VariableDefinition[]
294
- /** Paths the content actually references, across every locale. */
354
+ /** Paths the working content references, across every locale. */
295
355
  detectedVariables: string[]
356
+ /**
357
+ * The contract callers send against: the published declarations and the
358
+ * paths the published content references; `null` when never published.
359
+ */
360
+ publishedVariables?: VariableDefinition[] | null
361
+ publishedDetectedVariables?: string[] | null
296
362
  testData: Record<string, unknown>
297
363
  fallbackLocale: string
364
+ /** Whether a send missing a variable fails rather than goes out blank. */
365
+ blockOnMissingVariables: boolean
298
366
  categories: TemplateCategory[]
367
+ sender: TemplateSender
368
+ }
369
+
370
+ export interface TemplateVersionSummary {
371
+ version: number
372
+ publishedAt: string
373
+ publishedBy: string
374
+ locales: string
375
+ changes: TemplateChange[]
376
+ /** The declarations the version shipped with. */
377
+ variables: VariableDefinition[]
378
+ }
379
+
380
+ /** Last 30 days of real sends of one template. */
381
+ export interface TemplateStats {
382
+ id: string
383
+ slug: string
384
+ sends: number
385
+ openRate: number
386
+ problems: number
387
+ lastSentAt: string | null
299
388
  }
300
389
 
390
+ export interface SourceCount {
391
+ source: string
392
+ count: number
393
+ }
394
+
395
+ /** Recipients who asked for a locale the template did not carry. */
396
+ export interface FallbackCount {
397
+ requested: string
398
+ used: string
399
+ count: number
400
+ }
401
+
402
+ export interface TemplatePerformance extends TemplateStats {
403
+ sources: SourceCount[]
404
+ fallbacks: FallbackCount[]
405
+ }
406
+
407
+ export interface StarterSummary {
408
+ id: string
409
+ name: string
410
+ slug: string
411
+ category: string
412
+ locales: string[]
413
+ }
414
+
415
+ /** Which content a preview renders when it carries none. */
416
+ export type PreviewVersion = 'draft' | 'published' | number
417
+
301
418
  export interface PreviewResponse {
302
419
  html: string
303
420
  subject: string
@@ -306,17 +423,31 @@ export interface PreviewResponse {
306
423
  hiddenBlockIds: string[]
307
424
  }
308
425
 
426
+ export type SendStage =
427
+ | 'problem'
428
+ | 'resolved'
429
+ | 'suppressed'
430
+ | 'in_progress'
431
+ | 'delivered'
432
+ | 'engaged'
433
+ | 'unsubscribed'
434
+
309
435
  export interface SendRow {
310
436
  _id: string
311
437
  templateId: string
312
438
  templateSlug: string
313
439
  locale: string
440
+ /** Locale the caller asked for; differs from `locale` on a fallback. */
441
+ requestedLocale?: string
442
+ /** Template version the send used; 0 for a test send of a draft. */
443
+ templateVersion?: number
444
+ stage?: SendStage
314
445
  recipientEmail: string
315
446
  recipientName?: string
316
447
  status: SendStatus
317
448
  provider?: string
318
449
  providerMessageId?: string
319
- latencyMs: number
450
+ latencyMs: number | null
320
451
  error?: string
321
452
  source?: string
322
453
  json_variables: string
@@ -324,38 +455,72 @@ export interface SendRow {
324
455
  clicks: number
325
456
  createdAt: string
326
457
  isTest: boolean
458
+ /** The send this one repeats. */
459
+ replayOf?: string
460
+ /** The send that repeated this one, once someone sent it again. */
461
+ replayedBy?: string
462
+ /** When someone sent it again. */
463
+ resolvedAt?: string
464
+ /** Who sent it again: a user id. */
465
+ resolvedBy?: string
327
466
  }
328
467
 
329
468
  export interface SendEventRow {
330
469
  _id: string
331
470
  sendId: string
332
- type: SendStatus
471
+ type: SendEventType
333
472
  at: string
334
473
  json_details: string
335
474
  }
336
475
 
476
+ export type SuppressionReason =
477
+ 'hard_bounce' | 'complaint' | 'unsubscribe' | 'manual'
478
+
479
+ /** An address's entry on the suppression list, as the API answers it. */
480
+ export interface SuppressionSummary {
481
+ id: string
482
+ email: string
483
+ reason: SuppressionReason
484
+ createdAt: string
485
+ /** The send whose event put the address on the list. */
486
+ sendId?: string
487
+ }
488
+
489
+ export interface SendTemplateSummary {
490
+ id: string
491
+ name: string
492
+ status: TemplateStatus
493
+ publishedVersion: number
494
+ }
495
+
337
496
  export interface SendDetailResponse {
338
497
  send: SendRow
339
498
  events: SendEventRow[]
499
+ /** `null` when the template was deleted since. */
500
+ template: SendTemplateSummary | null
501
+ /** The recipient's entry on the suppression list, if any. */
502
+ suppression?: SuppressionSummary | null
340
503
  }
341
504
 
342
- export type AttentionTone = 'warning' | 'error' | 'neutral'
343
-
344
- export interface AttentionItem {
345
- id: string
346
- tone: AttentionTone
347
- icon: string
348
- title: string
349
- description: string
350
- to?: string
351
- params?: Record<string, string | number>
505
+ export interface SendHtmlResponse {
506
+ html: string
507
+ subject: string
508
+ locale: string
509
+ version: number
510
+ /** A draft test is rendered from today's draft, which may differ from what was sent. */
511
+ approximate?: boolean
512
+ from: string
513
+ fromName: string
352
514
  }
353
515
 
354
516
  export interface FunnelStep {
355
517
  id: string
356
518
  label: string
357
519
  count: number
358
- rate: number
520
+ /** Share of the previous step; `null` when there is nothing to divide by. */
521
+ rate: number | null
522
+ /** `false` when nothing reports the events this step counts. */
523
+ isMeasured?: boolean
359
524
  }
360
525
 
361
526
  export interface ProviderInfo {
@@ -363,3 +528,13 @@ export interface ProviderInfo {
363
528
  connected: boolean
364
529
  features: Record<string, boolean> | null
365
530
  }
531
+
532
+ export type MissingVariablesPolicy = 'refuse' | 'send'
533
+
534
+ export interface RetentionPreview {
535
+ days: number
536
+ count: number
537
+ nextRunAt: string
538
+ }
539
+
540
+ export type FunnelKind = 'all' | 'marketing' | 'transactional'
@@ -0,0 +1,33 @@
1
+ /** What a failed `$authFetch` call carries, as far as the module reads it. */
2
+ export interface FetchFailure {
3
+ statusCode?: number
4
+ status?: number
5
+ data?: unknown
6
+ message?: string
7
+ }
8
+
9
+ export const HTTP_CONFLICT = 409
10
+
11
+ const asFailure = (error: unknown): FetchFailure =>
12
+ error !== null && typeof error === 'object' ? (error as FetchFailure) : {}
13
+
14
+ /** The HTTP status of a failed call; 0 for a network failure. */
15
+ export function apiErrorStatus(error: unknown): number {
16
+ const failure = asFailure(error)
17
+ return failure.statusCode ?? failure.status ?? 0
18
+ }
19
+
20
+ /**
21
+ * The message a failed call answers: the backend's i18n key (`$dms_mailing…`)
22
+ * when it sent one, else the error's own text, for `processApiMessage`.
23
+ */
24
+ export function apiErrorMessage(error: unknown): string {
25
+ const failure = asFailure(error)
26
+ const data = failure.data
27
+ if (data && typeof data === 'object') {
28
+ const message = (data as Record<string, unknown>).message
29
+ if (typeof message === 'string') return message
30
+ }
31
+ if (typeof data === 'string' && data) return data
32
+ return failure.message ?? String(error)
33
+ }