@antelopejs/dms-mailing 0.2.2

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 (550) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +162 -0
  3. package/dist/antelope-modules.d.ts +32 -0
  4. package/dist/antelope-modules.js +36 -0
  5. package/dist/antelope-modules.js.map +1 -0
  6. package/dist/automation/email-event-trigger.d.ts +16 -0
  7. package/dist/automation/email-event-trigger.js +70 -0
  8. package/dist/automation/email-event-trigger.js.map +1 -0
  9. package/dist/automation/index.d.ts +4 -0
  10. package/dist/automation/index.js +39 -0
  11. package/dist/automation/index.js.map +1 -0
  12. package/dist/automation/send-template-action.d.ts +13 -0
  13. package/dist/automation/send-template-action.js +71 -0
  14. package/dist/automation/send-template-action.js.map +1 -0
  15. package/dist/config.d.ts +5 -0
  16. package/dist/config.js +13 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/constants.d.ts +28 -0
  19. package/dist/constants.js +32 -0
  20. package/dist/constants.js.map +1 -0
  21. package/dist/crons/index.d.ts +1 -0
  22. package/dist/crons/index.js +18 -0
  23. package/dist/crons/index.js.map +1 -0
  24. package/dist/crons/retention.d.ts +18 -0
  25. package/dist/crons/retention.js +107 -0
  26. package/dist/crons/retention.js.map +1 -0
  27. package/dist/data/body.d.ts +1 -0
  28. package/dist/data/body.js +18 -0
  29. package/dist/data/body.js.map +1 -0
  30. package/dist/data/index.d.ts +3 -0
  31. package/dist/data/index.js +20 -0
  32. package/dist/data/index.js.map +1 -0
  33. package/dist/data/sends-table.d.ts +28 -0
  34. package/dist/data/sends-table.js +161 -0
  35. package/dist/data/sends-table.js.map +1 -0
  36. package/dist/data/templates-table.d.ts +31 -0
  37. package/dist/data/templates-table.js +173 -0
  38. package/dist/data/templates-table.js.map +1 -0
  39. package/dist/data-types/categories-type.d.ts +35 -0
  40. package/dist/data-types/categories-type.js +37 -0
  41. package/dist/data-types/categories-type.js.map +1 -0
  42. package/dist/data-types/category-type.d.ts +8 -0
  43. package/dist/data-types/category-type.js +43 -0
  44. package/dist/data-types/category-type.js.map +1 -0
  45. package/dist/data-types/index.d.ts +2 -0
  46. package/dist/data-types/index.js +19 -0
  47. package/dist/data-types/index.js.map +1 -0
  48. package/dist/db/index.d.ts +2 -0
  49. package/dist/db/index.js +19 -0
  50. package/dist/db/index.js.map +1 -0
  51. package/dist/db/models/index.d.ts +4 -0
  52. package/dist/db/models/index.js +21 -0
  53. package/dist/db/models/index.js.map +1 -0
  54. package/dist/db/models/send-events.model.d.ts +27 -0
  55. package/dist/db/models/send-events.model.js +35 -0
  56. package/dist/db/models/send-events.model.js.map +1 -0
  57. package/dist/db/models/sends.model.d.ts +32 -0
  58. package/dist/db/models/sends.model.js +115 -0
  59. package/dist/db/models/sends.model.js.map +1 -0
  60. package/dist/db/models/settings.model.d.ts +23 -0
  61. package/dist/db/models/settings.model.js +13 -0
  62. package/dist/db/models/settings.model.js.map +1 -0
  63. package/dist/db/models/templates.model.d.ts +23 -0
  64. package/dist/db/models/templates.model.js +13 -0
  65. package/dist/db/models/templates.model.js.map +1 -0
  66. package/dist/db/tables/index.d.ts +4 -0
  67. package/dist/db/tables/index.js +21 -0
  68. package/dist/db/tables/index.js.map +1 -0
  69. package/dist/db/tables/send-events.table.d.ts +10 -0
  70. package/dist/db/tables/send-events.table.js +44 -0
  71. package/dist/db/tables/send-events.table.js.map +1 -0
  72. package/dist/db/tables/sends.table.d.ts +26 -0
  73. package/dist/db/tables/sends.table.js +113 -0
  74. package/dist/db/tables/sends.table.js.map +1 -0
  75. package/dist/db/tables/settings.table.d.ts +15 -0
  76. package/dist/db/tables/settings.table.js +68 -0
  77. package/dist/db/tables/settings.table.js.map +1 -0
  78. package/dist/db/tables/templates.table.d.ts +18 -0
  79. package/dist/db/tables/templates.table.js +81 -0
  80. package/dist/db/tables/templates.table.js.map +1 -0
  81. package/dist/engine/conditions.d.ts +2 -0
  82. package/dist/engine/conditions.js +20 -0
  83. package/dist/engine/conditions.js.map +1 -0
  84. package/dist/engine/coverage.d.ts +4 -0
  85. package/dist/engine/coverage.js +45 -0
  86. package/dist/engine/coverage.js.map +1 -0
  87. package/dist/engine/fallback-html.d.ts +4 -0
  88. package/dist/engine/fallback-html.js +84 -0
  89. package/dist/engine/fallback-html.js.map +1 -0
  90. package/dist/engine/index.d.ts +7 -0
  91. package/dist/engine/index.js +24 -0
  92. package/dist/engine/index.js.map +1 -0
  93. package/dist/engine/paths.d.ts +3 -0
  94. package/dist/engine/paths.js +32 -0
  95. package/dist/engine/paths.js.map +1 -0
  96. package/dist/engine/resolve.d.ts +4 -0
  97. package/dist/engine/resolve.js +139 -0
  98. package/dist/engine/resolve.js.map +1 -0
  99. package/dist/engine/tokens.d.ts +7 -0
  100. package/dist/engine/tokens.js +29 -0
  101. package/dist/engine/tokens.js.map +1 -0
  102. package/dist/engine/walk.d.ts +3 -0
  103. package/dist/engine/walk.js +14 -0
  104. package/dist/engine/walk.js.map +1 -0
  105. package/dist/hooks/index.d.ts +1 -0
  106. package/dist/hooks/index.js +18 -0
  107. package/dist/hooks/index.js.map +1 -0
  108. package/dist/hooks/tenant-export.d.ts +14 -0
  109. package/dist/hooks/tenant-export.js +84 -0
  110. package/dist/hooks/tenant-export.js.map +1 -0
  111. package/dist/implementations/dms-mailing/index.d.ts +2 -0
  112. package/dist/implementations/dms-mailing/index.js +8 -0
  113. package/dist/implementations/dms-mailing/index.js.map +1 -0
  114. package/dist/index.d.ts +13 -0
  115. package/dist/index.js +95 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/pages/editor.d.ts +9 -0
  118. package/dist/pages/editor.js +30 -0
  119. package/dist/pages/editor.js.map +1 -0
  120. package/dist/pages/index.d.ts +7 -0
  121. package/dist/pages/index.js +24 -0
  122. package/dist/pages/index.js.map +1 -0
  123. package/dist/pages/module.d.ts +1 -0
  124. package/dist/pages/module.js +12 -0
  125. package/dist/pages/module.js.map +1 -0
  126. package/dist/pages/overview.d.ts +14 -0
  127. package/dist/pages/overview.js +92 -0
  128. package/dist/pages/overview.js.map +1 -0
  129. package/dist/pages/sends.d.ts +13 -0
  130. package/dist/pages/sends.js +125 -0
  131. package/dist/pages/sends.js.map +1 -0
  132. package/dist/pages/settings/form.d.ts +1 -0
  133. package/dist/pages/settings/form.js +68 -0
  134. package/dist/pages/settings/form.js.map +1 -0
  135. package/dist/pages/settings/index.d.ts +2 -0
  136. package/dist/pages/settings/index.js +19 -0
  137. package/dist/pages/settings/index.js.map +1 -0
  138. package/dist/pages/settings/page.d.ts +10 -0
  139. package/dist/pages/settings/page.js +35 -0
  140. package/dist/pages/settings/page.js.map +1 -0
  141. package/dist/pages/templates.d.ts +10 -0
  142. package/dist/pages/templates.js +140 -0
  143. package/dist/pages/templates.js.map +1 -0
  144. package/dist/permissions.d.ts +1 -0
  145. package/dist/permissions.js +37 -0
  146. package/dist/permissions.js.map +1 -0
  147. package/dist/quick-actions.d.ts +2 -0
  148. package/dist/quick-actions.js +18 -0
  149. package/dist/quick-actions.js.map +1 -0
  150. package/dist/realtime.d.ts +4 -0
  151. package/dist/realtime.js +44 -0
  152. package/dist/realtime.js.map +1 -0
  153. package/dist/routes/events.d.ts +9 -0
  154. package/dist/routes/events.js +63 -0
  155. package/dist/routes/events.js.map +1 -0
  156. package/dist/routes/index.d.ts +7 -0
  157. package/dist/routes/index.js +24 -0
  158. package/dist/routes/index.js.map +1 -0
  159. package/dist/routes/metrics.d.ts +54 -0
  160. package/dist/routes/metrics.js +235 -0
  161. package/dist/routes/metrics.js.map +1 -0
  162. package/dist/routes/period.d.ts +5 -0
  163. package/dist/routes/period.js +36 -0
  164. package/dist/routes/period.js.map +1 -0
  165. package/dist/routes/provider.d.ts +13 -0
  166. package/dist/routes/provider.js +41 -0
  167. package/dist/routes/provider.js.map +1 -0
  168. package/dist/routes/sends.d.ts +31 -0
  169. package/dist/routes/sends.js +178 -0
  170. package/dist/routes/sends.js.map +1 -0
  171. package/dist/routes/settings.d.ts +14 -0
  172. package/dist/routes/settings.js +72 -0
  173. package/dist/routes/settings.js.map +1 -0
  174. package/dist/routes/templates.d.ts +61 -0
  175. package/dist/routes/templates.js +230 -0
  176. package/dist/routes/templates.js.map +1 -0
  177. package/dist/services/attention.d.ts +23 -0
  178. package/dist/services/attention.js +104 -0
  179. package/dist/services/attention.js.map +1 -0
  180. package/dist/services/categories.d.ts +34 -0
  181. package/dist/services/categories.js +34 -0
  182. package/dist/services/categories.js.map +1 -0
  183. package/dist/services/events.d.ts +10 -0
  184. package/dist/services/events.js +89 -0
  185. package/dist/services/events.js.map +1 -0
  186. package/dist/services/metrics.d.ts +34 -0
  187. package/dist/services/metrics.js +103 -0
  188. package/dist/services/metrics.js.map +1 -0
  189. package/dist/services/notify.d.ts +20 -0
  190. package/dist/services/notify.js +63 -0
  191. package/dist/services/notify.js.map +1 -0
  192. package/dist/services/permissions.d.ts +2 -0
  193. package/dist/services/permissions.js +15 -0
  194. package/dist/services/permissions.js.map +1 -0
  195. package/dist/services/provider.d.ts +6 -0
  196. package/dist/services/provider.js +25 -0
  197. package/dist/services/provider.js.map +1 -0
  198. package/dist/services/render.d.ts +6 -0
  199. package/dist/services/render.js +21 -0
  200. package/dist/services/render.js.map +1 -0
  201. package/dist/services/send.d.ts +49 -0
  202. package/dist/services/send.js +259 -0
  203. package/dist/services/send.js.map +1 -0
  204. package/dist/services/settings.d.ts +4 -0
  205. package/dist/services/settings.js +61 -0
  206. package/dist/services/settings.js.map +1 -0
  207. package/dist/services/status.d.ts +3 -0
  208. package/dist/services/status.js +16 -0
  209. package/dist/services/status.js.map +1 -0
  210. package/dist/services/templates.d.ts +17 -0
  211. package/dist/services/templates.js +92 -0
  212. package/dist/services/templates.js.map +1 -0
  213. package/dist/test/antelope.test.d.ts +2 -0
  214. package/dist/test/antelope.test.js +179 -0
  215. package/dist/test/antelope.test.js.map +1 -0
  216. package/dist/test/helpers/http.d.ts +4 -0
  217. package/dist/test/helpers/http.js +28 -0
  218. package/dist/test/helpers/http.js.map +1 -0
  219. package/dist/test/helpers/owner.d.ts +5 -0
  220. package/dist/test/helpers/owner.js +30 -0
  221. package/dist/test/helpers/owner.js.map +1 -0
  222. package/dist/test/integration/events.test.d.ts +1 -0
  223. package/dist/test/integration/events.test.js +132 -0
  224. package/dist/test/integration/events.test.js.map +1 -0
  225. package/dist/test/integration/retention.test.d.ts +1 -0
  226. package/dist/test/integration/retention.test.js +291 -0
  227. package/dist/test/integration/retention.test.js.map +1 -0
  228. package/dist/test/integration/sends.test.d.ts +1 -0
  229. package/dist/test/integration/sends.test.js +157 -0
  230. package/dist/test/integration/sends.test.js.map +1 -0
  231. package/dist/test/integration/settings.test.d.ts +1 -0
  232. package/dist/test/integration/settings.test.js +45 -0
  233. package/dist/test/integration/settings.test.js.map +1 -0
  234. package/dist/test/integration/setup.test.d.ts +1 -0
  235. package/dist/test/integration/setup.test.js +32 -0
  236. package/dist/test/integration/setup.test.js.map +1 -0
  237. package/dist/test/integration/templates.test.d.ts +1 -0
  238. package/dist/test/integration/templates.test.js +167 -0
  239. package/dist/test/integration/templates.test.js.map +1 -0
  240. package/dist/test/integration/tenant-export.test.d.ts +1 -0
  241. package/dist/test/integration/tenant-export.test.js +85 -0
  242. package/dist/test/integration/tenant-export.test.js.map +1 -0
  243. package/dist/test/unit/attention.test.d.ts +1 -0
  244. package/dist/test/unit/attention.test.js +109 -0
  245. package/dist/test/unit/attention.test.js.map +1 -0
  246. package/dist/test/unit/automation.test.d.ts +1 -0
  247. package/dist/test/unit/automation.test.js +104 -0
  248. package/dist/test/unit/automation.test.js.map +1 -0
  249. package/dist/test/unit/categories.test.d.ts +1 -0
  250. package/dist/test/unit/categories.test.js +38 -0
  251. package/dist/test/unit/categories.test.js.map +1 -0
  252. package/dist/test/unit/engine/conditions.test.d.ts +1 -0
  253. package/dist/test/unit/engine/conditions.test.js +23 -0
  254. package/dist/test/unit/engine/conditions.test.js.map +1 -0
  255. package/dist/test/unit/engine/coverage.test.d.ts +1 -0
  256. package/dist/test/unit/engine/coverage.test.js +106 -0
  257. package/dist/test/unit/engine/coverage.test.js.map +1 -0
  258. package/dist/test/unit/engine/fallback-html.test.d.ts +1 -0
  259. package/dist/test/unit/engine/fallback-html.test.js +78 -0
  260. package/dist/test/unit/engine/fallback-html.test.js.map +1 -0
  261. package/dist/test/unit/engine/paths.test.d.ts +1 -0
  262. package/dist/test/unit/engine/paths.test.js +33 -0
  263. package/dist/test/unit/engine/paths.test.js.map +1 -0
  264. package/dist/test/unit/engine/resolve.test.d.ts +1 -0
  265. package/dist/test/unit/engine/resolve.test.js +157 -0
  266. package/dist/test/unit/engine/resolve.test.js.map +1 -0
  267. package/dist/test/unit/engine/tokens.test.d.ts +1 -0
  268. package/dist/test/unit/engine/tokens.test.js +26 -0
  269. package/dist/test/unit/engine/tokens.test.js.map +1 -0
  270. package/dist/test/unit/events.test.d.ts +1 -0
  271. package/dist/test/unit/events.test.js +13 -0
  272. package/dist/test/unit/events.test.js.map +1 -0
  273. package/dist/test/unit/metrics.test.d.ts +1 -0
  274. package/dist/test/unit/metrics.test.js +108 -0
  275. package/dist/test/unit/metrics.test.js.map +1 -0
  276. package/dist/test/unit/notify.test.d.ts +1 -0
  277. package/dist/test/unit/notify.test.js +49 -0
  278. package/dist/test/unit/notify.test.js.map +1 -0
  279. package/dist/test/unit/period.test.d.ts +1 -0
  280. package/dist/test/unit/period.test.js +44 -0
  281. package/dist/test/unit/period.test.js.map +1 -0
  282. package/dist/test/unit/provider.test.d.ts +1 -0
  283. package/dist/test/unit/provider.test.js +47 -0
  284. package/dist/test/unit/provider.test.js.map +1 -0
  285. package/dist/test/unit/realtime.test.d.ts +1 -0
  286. package/dist/test/unit/realtime.test.js +13 -0
  287. package/dist/test/unit/realtime.test.js.map +1 -0
  288. package/dist/test/unit/render.test.d.ts +1 -0
  289. package/dist/test/unit/render.test.js +23 -0
  290. package/dist/test/unit/render.test.js.map +1 -0
  291. package/dist/test/unit/retention.test.d.ts +1 -0
  292. package/dist/test/unit/retention.test.js +66 -0
  293. package/dist/test/unit/retention.test.js.map +1 -0
  294. package/dist/test/unit/send-batch.test.d.ts +1 -0
  295. package/dist/test/unit/send-batch.test.js +46 -0
  296. package/dist/test/unit/send-batch.test.js.map +1 -0
  297. package/dist/test/unit/send.test.d.ts +1 -0
  298. package/dist/test/unit/send.test.js +120 -0
  299. package/dist/test/unit/send.test.js.map +1 -0
  300. package/dist/test/unit/smoke.test.d.ts +1 -0
  301. package/dist/test/unit/smoke.test.js +10 -0
  302. package/dist/test/unit/smoke.test.js.map +1 -0
  303. package/dist/test/unit/status.test.d.ts +1 -0
  304. package/dist/test/unit/status.test.js +16 -0
  305. package/dist/test/unit/status.test.js.map +1 -0
  306. package/dist/test/unit/templates.test.d.ts +1 -0
  307. package/dist/test/unit/templates.test.js +48 -0
  308. package/dist/test/unit/templates.test.js.map +1 -0
  309. package/dist/test/unit/tenant-export.test.d.ts +1 -0
  310. package/dist/test/unit/tenant-export.test.js +59 -0
  311. package/dist/test/unit/tenant-export.test.js.map +1 -0
  312. package/dist/test/unit/webhook-secret.test.d.ts +1 -0
  313. package/dist/test/unit/webhook-secret.test.js +42 -0
  314. package/dist/test/unit/webhook-secret.test.js.map +1 -0
  315. package/dist/types/blocks.d.ts +2 -0
  316. package/dist/types/blocks.js +3 -0
  317. package/dist/types/blocks.js.map +1 -0
  318. package/dist/types/index.d.ts +4 -0
  319. package/dist/types/index.js +21 -0
  320. package/dist/types/index.js.map +1 -0
  321. package/dist/types/resolved.d.ts +68 -0
  322. package/dist/types/resolved.js +3 -0
  323. package/dist/types/resolved.js.map +1 -0
  324. package/dist/types/sends.d.ts +14 -0
  325. package/dist/types/sends.js +19 -0
  326. package/dist/types/sends.js.map +1 -0
  327. package/dist/types/settings.d.ts +16 -0
  328. package/dist/types/settings.js +11 -0
  329. package/dist/types/settings.js.map +1 -0
  330. package/dist/validation/settings.schema.d.ts +63 -0
  331. package/dist/validation/settings.schema.js +29 -0
  332. package/dist/validation/settings.schema.js.map +1 -0
  333. package/dist/validation/templates.schema.d.ts +248 -0
  334. package/dist/validation/templates.schema.js +126 -0
  335. package/dist/validation/templates.schema.js.map +1 -0
  336. package/frontend-vue/.editorconfig +12 -0
  337. package/frontend-vue/.prettierrc +11 -0
  338. package/frontend-vue/app/components/AttentionCard.vue +78 -0
  339. package/frontend-vue/app/components/CategoriesInput.vue +126 -0
  340. package/frontend-vue/app/components/CategoryInput.vue +67 -0
  341. package/frontend-vue/app/components/Editor.vue +63 -0
  342. package/frontend-vue/app/components/FunnelCard.vue +66 -0
  343. package/frontend-vue/app/components/HtmlPreviewModal.vue +20 -0
  344. package/frontend-vue/app/components/NewTemplateModal.vue +158 -0
  345. package/frontend-vue/app/components/ProviderChip.vue +39 -0
  346. package/frontend-vue/app/components/SendDrawer.vue +298 -0
  347. package/frontend-vue/app/components/TemplateCard.vue +127 -0
  348. package/frontend-vue/app/components/TemplateDrawer.vue +394 -0
  349. package/frontend-vue/app/components/TemplateGallery.vue +112 -0
  350. package/frontend-vue/app/components/TemplatePreviewFrame.vue +103 -0
  351. package/frontend-vue/app/components/TestSendModal.vue +162 -0
  352. package/frontend-vue/app/components/TokenText.vue +26 -0
  353. package/frontend-vue/app/components/blocks/Button.vue +19 -0
  354. package/frontend-vue/app/components/blocks/Code.vue +17 -0
  355. package/frontend-vue/app/components/blocks/Divider.vue +3 -0
  356. package/frontend-vue/app/components/blocks/Footer.vue +22 -0
  357. package/frontend-vue/app/components/blocks/Heading.vue +18 -0
  358. package/frontend-vue/app/components/blocks/Hero.vue +32 -0
  359. package/frontend-vue/app/components/blocks/List.vue +33 -0
  360. package/frontend-vue/app/components/blocks/Paragraph.vue +18 -0
  361. package/frontend-vue/app/components/blocks/Total.vue +18 -0
  362. package/frontend-vue/app/components/editor/EditorBlock.vue +121 -0
  363. package/frontend-vue/app/components/editor/EditorBlockList.vue +44 -0
  364. package/frontend-vue/app/components/editor/EditorBlockSettings.vue +186 -0
  365. package/frontend-vue/app/components/editor/EditorCanvas.vue +91 -0
  366. package/frontend-vue/app/components/editor/EditorConditionFields.vue +88 -0
  367. package/frontend-vue/app/components/editor/EditorDataPanel.vue +128 -0
  368. package/frontend-vue/app/components/editor/EditorDraftBar.vue +35 -0
  369. package/frontend-vue/app/components/editor/EditorIfBlock.vue +120 -0
  370. package/frontend-vue/app/components/editor/EditorMissingLocale.vue +62 -0
  371. package/frontend-vue/app/components/editor/EditorPalette.vue +62 -0
  372. package/frontend-vue/app/components/editor/EditorRail.vue +58 -0
  373. package/frontend-vue/app/components/editor/EditorSubjectSettings.vue +37 -0
  374. package/frontend-vue/app/components/editor/EditorTemplateSettings.vue +87 -0
  375. package/frontend-vue/app/components/editor/EditorTestSendModal.vue +66 -0
  376. package/frontend-vue/app/components/editor/EditorToolbar.vue +155 -0
  377. package/frontend-vue/app/components/editor/EditorVariables.vue +163 -0
  378. package/frontend-vue/app/components/editor/EditorWorkspace.vue +189 -0
  379. package/frontend-vue/app/composables/useEditorContext.ts +44 -0
  380. package/frontend-vue/app/composables/useMailingApi.ts +197 -0
  381. package/frontend-vue/app/composables/useMailingPeriodData.ts +39 -0
  382. package/frontend-vue/app/composables/useTemplateDrawer.ts +90 -0
  383. package/frontend-vue/app/composables/useTemplateEditor.ts +228 -0
  384. package/frontend-vue/app/emails/EmailMailingTemplate.vue +24 -0
  385. package/frontend-vue/app/emails/components/MailingBlocks.vue +117 -0
  386. package/frontend-vue/app/plugins/template-gallery-display.client.ts +14 -0
  387. package/frontend-vue/app/types/component.ts +29 -0
  388. package/frontend-vue/app/types/mailing.ts +365 -0
  389. package/frontend-vue/app/utils/blocks/components.ts +14 -0
  390. package/frontend-vue/app/utils/blocks/dnd.ts +15 -0
  391. package/frontend-vue/app/utils/blocks/factory.ts +100 -0
  392. package/frontend-vue/app/utils/blocks/fields.ts +58 -0
  393. package/frontend-vue/app/utils/blocks/index.ts +5 -0
  394. package/frontend-vue/app/utils/blocks/tree.ts +74 -0
  395. package/frontend-vue/app/utils/categories.ts +54 -0
  396. package/frontend-vue/app/utils/clone.ts +3 -0
  397. package/frontend-vue/app/utils/conditions.ts +50 -0
  398. package/frontend-vue/app/utils/editor-route.ts +18 -0
  399. package/frontend-vue/app/utils/gallery.ts +41 -0
  400. package/frontend-vue/app/utils/locales.ts +41 -0
  401. package/frontend-vue/app/utils/paths.ts +84 -0
  402. package/frontend-vue/app/utils/period.ts +23 -0
  403. package/frontend-vue/app/utils/testData.ts +10 -0
  404. package/frontend-vue/app/utils/timeline.ts +68 -0
  405. package/frontend-vue/app/utils/tokens.ts +24 -0
  406. package/frontend-vue/app/utils/variables.ts +48 -0
  407. package/frontend-vue/dms.email.ts +9 -0
  408. package/frontend-vue/dms.frontend.ts +29 -0
  409. package/frontend-vue/eslint.config.mjs +13 -0
  410. package/frontend-vue/i18n/locales/mailing-en-GB.json +397 -0
  411. package/frontend-vue/i18n/locales/mailing-fr-FR.json +397 -0
  412. package/frontend-vue/package.json +32 -0
  413. package/frontend-vue/pnpm-lock.yaml +6446 -0
  414. package/frontend-vue/pnpm-workspace.yaml +2 -0
  415. package/frontend-vue/tests/block-components.test.ts +14 -0
  416. package/frontend-vue/tests/block-fields.test.ts +23 -0
  417. package/frontend-vue/tests/blocks-factory.test.ts +28 -0
  418. package/frontend-vue/tests/blocks-tree.test.ts +49 -0
  419. package/frontend-vue/tests/categories.test.ts +64 -0
  420. package/frontend-vue/tests/conditions.test.ts +36 -0
  421. package/frontend-vue/tests/dnd.test.ts +11 -0
  422. package/frontend-vue/tests/editor-block-list.test.ts +54 -0
  423. package/frontend-vue/tests/editor-route.test.ts +23 -0
  424. package/frontend-vue/tests/email-render-cases.json +77 -0
  425. package/frontend-vue/tests/gallery.test.ts +39 -0
  426. package/frontend-vue/tests/i18n-editor.test.ts +21 -0
  427. package/frontend-vue/tests/i18n.test.ts +83 -0
  428. package/frontend-vue/tests/locales.test.ts +38 -0
  429. package/frontend-vue/tests/mailing-api.test.ts +10 -0
  430. package/frontend-vue/tests/paths.test.ts +128 -0
  431. package/frontend-vue/tests/period.test.ts +13 -0
  432. package/frontend-vue/tests/smoke.test.ts +8 -0
  433. package/frontend-vue/tests/template-editor.test.ts +65 -0
  434. package/frontend-vue/tests/test-data.test.ts +15 -0
  435. package/frontend-vue/tests/timeline.test.ts +29 -0
  436. package/frontend-vue/tests/tokens.test.ts +12 -0
  437. package/frontend-vue/tests/variables.test.ts +46 -0
  438. package/frontend-vue/vitest.config.ts +10 -0
  439. package/package.json +94 -0
  440. package/src/antelope-modules.ts +48 -0
  441. package/src/automation/email-event-trigger.ts +112 -0
  442. package/src/automation/index.ts +33 -0
  443. package/src/automation/send-template-action.ts +100 -0
  444. package/src/config.ts +15 -0
  445. package/src/constants.ts +42 -0
  446. package/src/crons/index.ts +1 -0
  447. package/src/crons/retention.ts +126 -0
  448. package/src/data/body.ts +14 -0
  449. package/src/data/index.ts +3 -0
  450. package/src/data/sends-table.ts +154 -0
  451. package/src/data/templates-table.ts +202 -0
  452. package/src/data-types/categories-type.ts +29 -0
  453. package/src/data-types/category-type.ts +44 -0
  454. package/src/data-types/index.ts +2 -0
  455. package/src/db/index.ts +2 -0
  456. package/src/db/models/index.ts +4 -0
  457. package/src/db/models/send-events.model.ts +46 -0
  458. package/src/db/models/sends.model.ts +147 -0
  459. package/src/db/models/settings.model.ts +12 -0
  460. package/src/db/models/templates.model.ts +12 -0
  461. package/src/db/tables/index.ts +4 -0
  462. package/src/db/tables/send-events.table.ts +31 -0
  463. package/src/db/tables/sends.table.ts +85 -0
  464. package/src/db/tables/settings.table.ts +49 -0
  465. package/src/db/tables/templates.table.ts +70 -0
  466. package/src/engine/conditions.ts +30 -0
  467. package/src/engine/coverage.ts +67 -0
  468. package/src/engine/fallback-html.ts +116 -0
  469. package/src/engine/index.ts +7 -0
  470. package/src/engine/paths.ts +33 -0
  471. package/src/engine/resolve.ts +178 -0
  472. package/src/engine/tokens.ts +35 -0
  473. package/src/engine/walk.ts +18 -0
  474. package/src/hooks/index.ts +1 -0
  475. package/src/hooks/tenant-export.ts +127 -0
  476. package/src/implementations/dms-mailing/index.ts +2 -0
  477. package/src/index.ts +62 -0
  478. package/src/pages/editor.ts +23 -0
  479. package/src/pages/index.ts +11 -0
  480. package/src/pages/module.ts +9 -0
  481. package/src/pages/overview.ts +175 -0
  482. package/src/pages/sends.ts +174 -0
  483. package/src/pages/settings/form.ts +67 -0
  484. package/src/pages/settings/index.ts +2 -0
  485. package/src/pages/settings/page.ts +34 -0
  486. package/src/pages/templates.ts +141 -0
  487. package/src/permissions.ts +44 -0
  488. package/src/quick-actions.ts +20 -0
  489. package/src/realtime.ts +53 -0
  490. package/src/routes/events.ts +57 -0
  491. package/src/routes/index.ts +7 -0
  492. package/src/routes/metrics.ts +243 -0
  493. package/src/routes/period.ts +46 -0
  494. package/src/routes/provider.ts +31 -0
  495. package/src/routes/sends.ts +215 -0
  496. package/src/routes/settings.ts +66 -0
  497. package/src/routes/templates.ts +256 -0
  498. package/src/services/attention.ts +151 -0
  499. package/src/services/categories.ts +43 -0
  500. package/src/services/events.ts +121 -0
  501. package/src/services/metrics.ts +175 -0
  502. package/src/services/notify.ts +90 -0
  503. package/src/services/permissions.ts +42 -0
  504. package/src/services/provider.ts +46 -0
  505. package/src/services/render.ts +33 -0
  506. package/src/services/send.ts +409 -0
  507. package/src/services/settings.ts +74 -0
  508. package/src/services/status.ts +16 -0
  509. package/src/services/templates.ts +157 -0
  510. package/src/test/antelope.test.ts +188 -0
  511. package/src/test/helpers/http.ts +23 -0
  512. package/src/test/helpers/owner.ts +37 -0
  513. package/src/test/integration/events.test.ts +136 -0
  514. package/src/test/integration/retention.test.ts +363 -0
  515. package/src/test/integration/sends.test.ts +200 -0
  516. package/src/test/integration/settings.test.ts +50 -0
  517. package/src/test/integration/setup.test.ts +35 -0
  518. package/src/test/integration/templates.test.ts +229 -0
  519. package/src/test/integration/tenant-export.test.ts +125 -0
  520. package/src/test/unit/attention.test.ts +129 -0
  521. package/src/test/unit/automation.test.ts +130 -0
  522. package/src/test/unit/categories.test.ts +51 -0
  523. package/src/test/unit/engine/conditions.test.ts +35 -0
  524. package/src/test/unit/engine/coverage.test.ts +118 -0
  525. package/src/test/unit/engine/fallback-html.test.ts +86 -0
  526. package/src/test/unit/engine/paths.test.ts +39 -0
  527. package/src/test/unit/engine/resolve.test.ts +176 -0
  528. package/src/test/unit/engine/tokens.test.ts +26 -0
  529. package/src/test/unit/events.test.ts +19 -0
  530. package/src/test/unit/metrics.test.ts +170 -0
  531. package/src/test/unit/notify.test.ts +71 -0
  532. package/src/test/unit/period.test.ts +64 -0
  533. package/src/test/unit/provider.test.ts +57 -0
  534. package/src/test/unit/realtime.test.ts +11 -0
  535. package/src/test/unit/render.test.ts +23 -0
  536. package/src/test/unit/retention.test.ts +91 -0
  537. package/src/test/unit/send-batch.test.ts +72 -0
  538. package/src/test/unit/send.test.ts +161 -0
  539. package/src/test/unit/smoke.test.ts +8 -0
  540. package/src/test/unit/status.test.ts +14 -0
  541. package/src/test/unit/templates.test.ts +52 -0
  542. package/src/test/unit/tenant-export.test.ts +88 -0
  543. package/src/test/unit/webhook-secret.test.ts +50 -0
  544. package/src/types/blocks.ts +31 -0
  545. package/src/types/index.ts +4 -0
  546. package/src/types/resolved.ts +89 -0
  547. package/src/types/sends.ts +44 -0
  548. package/src/types/settings.ts +24 -0
  549. package/src/validation/settings.schema.ts +33 -0
  550. package/src/validation/templates.schema.ts +145 -0
@@ -0,0 +1,189 @@
1
+ <script setup lang="ts">
2
+ import { computed, reactive, ref } from 'vue'
3
+ import type {
4
+ PreviewResponse,
5
+ TemplateContentResponse,
6
+ TemplateRow,
7
+ TemplateStatus,
8
+ VariableDefinition,
9
+ } from '../../types/mailing'
10
+ import { provideEditorContext } from '../../composables/useEditorContext'
11
+ import { useMailingApi } from '../../composables/useMailingApi'
12
+ import { createEditorState } from '../../composables/useTemplateEditor'
13
+ import { deepClone } from '../../utils/clone'
14
+
15
+ interface Props {
16
+ loaded: TemplateContentResponse
17
+ templateId: string
18
+ requestedLocale: string | null
19
+ }
20
+
21
+ const props = defineProps<Props>()
22
+
23
+ const { t } = useI18n()
24
+ const toast = useToast()
25
+ const { processApiMessage } = useTranslation()
26
+ const { confirm } = useConfirm()
27
+ const { uniqueLocales } = useUniqueLocales()
28
+ const { registerGuard } = usePageLeaveGuard()
29
+ const api = useMailingApi()
30
+
31
+ const localeCodes = computed(() =>
32
+ uniqueLocales.value.map((locale) => locale.code),
33
+ )
34
+
35
+ const editor = createEditorState(
36
+ props.loaded.content,
37
+ props.requestedLocale ?? props.loaded.fallbackLocale,
38
+ localeCodes.value,
39
+ )
40
+
41
+ const template = ref<TemplateRow>(props.loaded.template)
42
+ const name = ref(props.loaded.template.name)
43
+ const category = ref(props.loaded.template.category ?? '')
44
+ const variables = ref<VariableDefinition[]>(props.loaded.variables ?? [])
45
+ const detectedVariables = ref<string[]>(props.loaded.detectedVariables ?? [])
46
+ const testData = ref(JSON.stringify(props.loaded.testData ?? {}, null, 2))
47
+ const preview = ref<PreviewResponse | null>(null)
48
+ const simulation = reactive<Record<string, boolean>>({})
49
+ const savedContent = ref(deepClone(props.loaded.content))
50
+ const testSendOpen = ref(false)
51
+
52
+ registerGuard(
53
+ () =>
54
+ editor.dirty === 0 ||
55
+ confirm({
56
+ title: t('dms_mailing.editor.leave_guard'),
57
+ description: t('dms_mailing.editor.unsaved', { count: editor.dirty }),
58
+ confirmColor: 'error',
59
+ }),
60
+ )
61
+
62
+ // The slug travels unchanged: `edit` replaces the writable fields, so omitting
63
+ // it would null it.
64
+ async function saveDetails(): Promise<void> {
65
+ const trimmed = name.value.trim() || template.value.name
66
+ const next = {
67
+ name: trimmed,
68
+ slug: template.value.slug,
69
+ category: category.value || undefined,
70
+ }
71
+ if (
72
+ next.name === template.value.name &&
73
+ next.category === (template.value.category || undefined)
74
+ ) {
75
+ return
76
+ }
77
+ await api.saveTemplateDetails(props.templateId, next)
78
+ template.value = { ...template.value, ...next }
79
+ name.value = trimmed
80
+ }
81
+
82
+ /**
83
+ * Surfaces a rejected call. Save and the status actions used to discard their
84
+ * promise at the click handler, so a duplicate slug, a 403 or a dropped
85
+ * connection left the author with no toast at all — and an editor that still
86
+ * looked saved.
87
+ */
88
+ function reportFailure(error: unknown): void {
89
+ toast.add({
90
+ color: 'error',
91
+ title: processApiMessage(
92
+ (error as { data?: { message?: string } }).data?.message ?? error,
93
+ ),
94
+ })
95
+ }
96
+
97
+ async function save(): Promise<void> {
98
+ try {
99
+ await saveDetails()
100
+ const { detectedVariables: detected } = await api.saveContent(
101
+ props.templateId,
102
+ deepClone(editor.content),
103
+ )
104
+ detectedVariables.value = detected ?? detectedVariables.value
105
+ savedContent.value = deepClone(editor.content)
106
+ editor.markSaved()
107
+ toast.add({ title: t('dms_mailing.editor.saved'), color: 'success' })
108
+ } catch (error) {
109
+ reportFailure(error)
110
+ }
111
+ }
112
+
113
+ type StatusAction = 'publish' | 'unpublish' | 'archive'
114
+
115
+ async function runStatusAction(action: StatusAction): Promise<void> {
116
+ const calls = {
117
+ publish: api.publish,
118
+ unpublish: api.unpublish,
119
+ archive: api.archive,
120
+ }
121
+ try {
122
+ const { status } = await calls[action](props.templateId)
123
+ template.value = { ...template.value, status: status as TemplateStatus }
124
+ toast.add({
125
+ title: t(`dms_mailing.templates.actions.${action}`),
126
+ color: 'success',
127
+ })
128
+ } catch (error) {
129
+ reportFailure(error)
130
+ }
131
+ }
132
+
133
+ function publish(): Promise<void> {
134
+ return runStatusAction('publish')
135
+ }
136
+
137
+ async function discard(): Promise<void> {
138
+ const confirmed = await confirm({
139
+ title: t('dms_mailing.editor.discard_title'),
140
+ description: t('dms_mailing.editor.discard_description'),
141
+ confirmColor: 'error',
142
+ })
143
+ if (!confirmed) return
144
+ editor.reset(savedContent.value)
145
+ }
146
+
147
+ provideEditorContext({
148
+ editor,
149
+ api,
150
+ templateId: props.templateId,
151
+ template,
152
+ variables,
153
+ detectedVariables,
154
+ name,
155
+ category,
156
+ runStatusAction,
157
+ testData,
158
+ preview,
159
+ simulation,
160
+ save,
161
+ publish,
162
+ discard,
163
+ openTestSend: () => (testSendOpen.value = true),
164
+ })
165
+ </script>
166
+
167
+ <template>
168
+ <div class="flex h-full min-h-0 flex-col">
169
+ <DmsMailingEditorToolbar />
170
+ <DmsMailingEditorDraftBar v-if="editor.dirty > 0" />
171
+
172
+ <div class="flex min-h-0 flex-1">
173
+ <DmsMailingEditorMissingLocale
174
+ v-if="!editor.current"
175
+ :fallback="loaded.fallbackLocale"
176
+ />
177
+ <DmsMailingEditorCanvas v-else />
178
+
179
+ <aside
180
+ v-if="editor.railOpen"
181
+ class="border-default bg-default flex w-[340px] shrink-0 flex-col overflow-y-auto border-l"
182
+ >
183
+ <DmsMailingEditorRail />
184
+ </aside>
185
+ </div>
186
+
187
+ <DmsMailingEditorTestSendModal v-model:open="testSendOpen" />
188
+ </div>
189
+ </template>
@@ -0,0 +1,44 @@
1
+ import { inject, provide } from 'vue'
2
+ import type { InjectionKey, Ref } from 'vue'
3
+ import type {
4
+ PreviewResponse,
5
+ TemplateRow,
6
+ VariableDefinition,
7
+ } from '../types/mailing'
8
+ import type { MailingApi } from './useMailingApi'
9
+ import type { EditorState } from './useTemplateEditor'
10
+
11
+ export interface EditorContext {
12
+ editor: EditorState
13
+ api: MailingApi
14
+ templateId: string
15
+ template: Ref<TemplateRow>
16
+ variables: Ref<VariableDefinition[]>
17
+ detectedVariables: Ref<string[]>
18
+ name: Ref<string>
19
+ category: Ref<string>
20
+ runStatusAction: (
21
+ action: 'publish' | 'unpublish' | 'archive',
22
+ ) => Promise<void>
23
+ testData: Ref<string>
24
+ preview: Ref<PreviewResponse | null>
25
+ simulation: Record<string, boolean>
26
+ save: () => Promise<void>
27
+ publish: () => Promise<void>
28
+ discard: () => Promise<void>
29
+ openTestSend: () => void
30
+ }
31
+
32
+ const EDITOR_KEY: InjectionKey<EditorContext> = Symbol('dms-mailing-editor')
33
+ const MISSING_CONTEXT =
34
+ 'useEditorContext() must be called inside DmsMailingEditor'
35
+
36
+ export function provideEditorContext(context: EditorContext): void {
37
+ provide(EDITOR_KEY, context)
38
+ }
39
+
40
+ export function useEditorContext(): EditorContext {
41
+ const context = inject(EDITOR_KEY, null)
42
+ if (!context) throw new Error(MISSING_CONTEXT)
43
+ return context
44
+ }
@@ -0,0 +1,197 @@
1
+ import type {
2
+ AttentionItem,
3
+ FunnelStep,
4
+ PreviewResponse,
5
+ ProviderInfo,
6
+ SendDetailResponse,
7
+ TemplateContent,
8
+ TemplateCategory,
9
+ TemplateContentResponse,
10
+ TemplateRow,
11
+ VariableDefinition,
12
+ } from '../types/mailing'
13
+
14
+ const BASE = '/api/mailing'
15
+
16
+ /** Far above any real tenant's template count; the data-api caps at 100. */
17
+ const TEMPLATE_LIST_LIMIT = '100'
18
+
19
+ /**
20
+ * Joins the module's API segments. Exported on its own so the path shape stays
21
+ * testable without a Nuxt runtime.
22
+ */
23
+ export function mailingPath(...segments: string[]): string {
24
+ return [BASE, ...segments].join('/')
25
+ }
26
+
27
+ export interface PreviewInput {
28
+ /** Absent lets the server pick the tenant fallback locale. */
29
+ locale?: string
30
+ content?: TemplateContent
31
+ data?: Record<string, unknown>
32
+ }
33
+
34
+ export interface TestSendInput extends PreviewInput {
35
+ to: string[]
36
+ }
37
+
38
+ /** A real send never carries a content override: it uses the saved template. */
39
+ export interface RealSendInput {
40
+ to: string[]
41
+ locale?: string
42
+ data?: Record<string, unknown>
43
+ }
44
+
45
+ export interface SendResult {
46
+ sendId: string
47
+ status: string
48
+ error?: string
49
+ }
50
+
51
+ export interface AttentionResponse {
52
+ items: AttentionItem[]
53
+ }
54
+
55
+ export interface FunnelResponse {
56
+ steps: FunnelStep[]
57
+ }
58
+
59
+ export interface SavedResponse {
60
+ saved: boolean
61
+ detectedVariables: string[]
62
+ }
63
+
64
+ /**
65
+ * The template's own writable fields. All of them, always: the data-api `edit`
66
+ * route replaces the writable set rather than patching it, so a body missing
67
+ * one nulls it in the database.
68
+ */
69
+ export interface TemplateDetails {
70
+ name: string
71
+ slug: string
72
+ category?: string
73
+ }
74
+
75
+ export interface CategoriesResponse {
76
+ categories: TemplateCategory[]
77
+ }
78
+
79
+ export interface TemplateListResponse {
80
+ results: TemplateRow[]
81
+ total: number
82
+ }
83
+
84
+ export interface CreateTemplateInput {
85
+ name: string
86
+ slug: string
87
+ category?: string
88
+ /** Absent starts blank; otherwise the template to copy. */
89
+ sourceTemplateId?: string
90
+ }
91
+
92
+ export interface StatusResponse {
93
+ status: string
94
+ }
95
+
96
+ export interface DuplicateResponse {
97
+ id: string
98
+ }
99
+
100
+ export interface VariablesResponse {
101
+ variables: VariableDefinition[]
102
+ }
103
+
104
+ export interface TestDataResponse {
105
+ data: Record<string, unknown>
106
+ }
107
+
108
+ export interface TestSendResponse {
109
+ results: SendResult[]
110
+ }
111
+
112
+ export interface HtmlResponse {
113
+ html: string
114
+ }
115
+
116
+ export interface MailingSettings extends Record<string, unknown> {
117
+ blockOnMissingVariables?: boolean
118
+ }
119
+
120
+ /**
121
+ * The single place that knows the mailing endpoints; every component reads
122
+ * through it so a route change is a one-file edit.
123
+ */
124
+ export function useMailingApi() {
125
+ const { $authFetch } = useAuthFetch()
126
+ const post = <T>(path: string, body: object) =>
127
+ $authFetch<T>(path, { method: 'POST', body })
128
+
129
+ return {
130
+ provider: () => $authFetch<ProviderInfo>(mailingPath('provider')),
131
+ attention: (query: Record<string, string>) =>
132
+ $authFetch<AttentionResponse>(mailingPath('metrics', 'attention'), {
133
+ query,
134
+ }),
135
+ funnel: (query: Record<string, string>) =>
136
+ $authFetch<FunnelResponse>(mailingPath('metrics', 'funnel'), { query }),
137
+ templateContent: (id: string) =>
138
+ $authFetch<TemplateContentResponse>(
139
+ mailingPath('templates', id, 'content'),
140
+ ),
141
+ saveTemplateDetails: (id: string, details: TemplateDetails) =>
142
+ $authFetch<unknown>(mailingPath('tables', 'templates', 'edit'), {
143
+ method: 'PUT',
144
+ query: { id },
145
+ body: details,
146
+ }),
147
+ listCategories: () =>
148
+ $authFetch<CategoriesResponse>(mailingPath('templates', 'categories')),
149
+ listTemplates: () =>
150
+ $authFetch<TemplateListResponse>(
151
+ mailingPath('tables', 'templates', 'list'),
152
+ { query: { limit: TEMPLATE_LIST_LIMIT, sortKey: 'name' } },
153
+ ),
154
+ createTemplate: (input: CreateTemplateInput) =>
155
+ post<string[]>(mailingPath('tables', 'templates', 'new'), input),
156
+ saveContent: (id: string, content: TemplateContent) =>
157
+ post<SavedResponse>(mailingPath('templates', id, 'content'), {
158
+ content,
159
+ }),
160
+ publish: (id: string) =>
161
+ post<StatusResponse>(mailingPath('templates', id, 'publish'), {}),
162
+ unpublish: (id: string) =>
163
+ post<StatusResponse>(mailingPath('templates', id, 'unpublish'), {}),
164
+ archive: (id: string) =>
165
+ post<StatusResponse>(mailingPath('templates', id, 'archive'), {}),
166
+ duplicate: (id: string, slug: string, name: string) =>
167
+ post<DuplicateResponse>(mailingPath('templates', id, 'duplicate'), {
168
+ slug,
169
+ name,
170
+ }),
171
+ saveVariables: (id: string, variables: VariableDefinition[]) =>
172
+ post<VariablesResponse>(mailingPath('templates', id, 'variables'), {
173
+ variables,
174
+ }),
175
+ saveTestData: (id: string, data: Record<string, unknown>) =>
176
+ post<TestDataResponse>(mailingPath('templates', id, 'test-data'), {
177
+ data,
178
+ }),
179
+ preview: (id: string, input: PreviewInput) =>
180
+ post<PreviewResponse>(mailingPath('templates', id, 'preview'), input),
181
+ realSend: (id: string, input: RealSendInput) =>
182
+ post<TestSendResponse>(mailingPath('templates', id, 'send'), input),
183
+ testSend: (id: string, input: TestSendInput) =>
184
+ post<TestSendResponse>(mailingPath('templates', id, 'test-send'), input),
185
+ send: (id: string) =>
186
+ $authFetch<SendDetailResponse>(mailingPath('sends', id)),
187
+ sendHtml: (id: string) =>
188
+ $authFetch<HtmlResponse>(mailingPath('sends', id, 'html')),
189
+ replay: (id: string) =>
190
+ post<SendResult>(mailingPath('sends', id, 'replay'), {}),
191
+ settings: () => $authFetch<MailingSettings>(mailingPath('settings')),
192
+ saveSettings: (settings: MailingSettings) =>
193
+ post<MailingSettings>(mailingPath('settings'), settings),
194
+ }
195
+ }
196
+
197
+ export type MailingApi = ReturnType<typeof useMailingApi>
@@ -0,0 +1,39 @@
1
+ import type { Ref } from 'vue'
2
+ import { periodQuery } from '../utils/period'
3
+
4
+ export interface PeriodDataState<T> {
5
+ data: Ref<T | null>
6
+ loading: Ref<boolean>
7
+ refresh: () => Promise<void>
8
+ }
9
+
10
+ /**
11
+ * Reads a DMS period-selector scope and re-runs `load` whenever the window
12
+ * changes. The scope registers when the selector mounts, so the first run is
13
+ * driven by the watcher rather than by `onMounted`.
14
+ */
15
+ export function useMailingPeriodData<T>(
16
+ scope: string,
17
+ load: (query: Record<string, string>) => Promise<T>,
18
+ ): PeriodDataState<T> {
19
+ const period = usePeriodScope(scope)
20
+ const data = ref(null) as Ref<T | null>
21
+ const loading = ref(false)
22
+
23
+ async function refresh(): Promise<void> {
24
+ const state = period.value
25
+ if (!state) return
26
+ loading.value = true
27
+ try {
28
+ data.value = await load(periodQuery(state))
29
+ } catch {
30
+ data.value = null
31
+ } finally {
32
+ loading.value = false
33
+ }
34
+ }
35
+
36
+ watch(period, refresh, { immediate: true })
37
+
38
+ return { data, loading, refresh }
39
+ }
@@ -0,0 +1,90 @@
1
+ import type { Component } from 'vue'
2
+ import type { TemplateContent, TemplateRow } from '../types/mailing'
3
+
4
+ /**
5
+ * How a DMS row action reaches these containers (verified against
6
+ * `@antelopejs/interface-dms/base/types/action-target` and
7
+ * `useTableViewRowActions.ts`):
8
+ *
9
+ * - the declared target is
10
+ * `{ type: 'drawer' | 'modal', component: CustomComponent('DmsMailingX'), title?, description?, size? }`;
11
+ * there is no `event` target type — the set is
12
+ * `drawer | modal | page | external | api | exportJob`.
13
+ * - the DMS opens the container itself and mounts the component with
14
+ * `{ ...target.component.options, pageId, componentId, containerId, rowData, onSuccessCallback }`,
15
+ * where `rowData` is the full row and `onSuccessCallback` closes the
16
+ * container and refreshes the table view.
17
+ *
18
+ * The same components are opened from our own surfaces (the gallery) through
19
+ * `useDrawer()` / `useModal()` with an explicit id instead of `rowData`.
20
+ */
21
+ const TEMPLATE_DRAWER_COMPONENT = 'DmsMailingTemplateDrawer'
22
+ const TEST_SEND_MODAL_COMPONENT = 'DmsMailingTestSendModal'
23
+ const DRAWER_DIRECTION = 'right'
24
+ const TEST_SEND_MODAL_SIZE = 'md'
25
+
26
+ export interface TemplateDrawerHandle {
27
+ open: (templateId: string) => void
28
+ }
29
+
30
+ export function useTemplateDrawer(): TemplateDrawerHandle {
31
+ const { open } = useDrawer()
32
+ const { t } = useI18n()
33
+ const component = resolveComponent(TEMPLATE_DRAWER_COMPONENT) as Component
34
+
35
+ return {
36
+ open: (templateId: string) =>
37
+ void open({
38
+ title: t('dms_mailing.templates.actions.details'),
39
+ component,
40
+ componentOptions: { templateId },
41
+ direction: DRAWER_DIRECTION,
42
+ }),
43
+ }
44
+ }
45
+
46
+ export interface TestSendModalHandle {
47
+ open: (
48
+ templateId: string,
49
+ locale?: string,
50
+ content?: TemplateContent,
51
+ data?: Record<string, unknown>,
52
+ ) => void
53
+ }
54
+
55
+ export interface RealSendModalHandle {
56
+ open: (templateId: string, rowData: TemplateRow, locale?: string) => void
57
+ }
58
+
59
+ /** The real-send modal: same component, `real` mode, its own title. */
60
+ export function useRealSendModal(): RealSendModalHandle {
61
+ const { open } = useModal()
62
+ const { t } = useI18n()
63
+ const component = resolveComponent(TEST_SEND_MODAL_COMPONENT) as Component
64
+
65
+ return {
66
+ open: (templateId, rowData, locale) =>
67
+ void open({
68
+ title: t('dms_mailing.templates.send_modal.title'),
69
+ component,
70
+ componentOptions: { templateId, rowData, locale, mode: 'real' },
71
+ size: TEST_SEND_MODAL_SIZE,
72
+ }),
73
+ }
74
+ }
75
+
76
+ export function useTestSendModal(): TestSendModalHandle {
77
+ const { open } = useModal()
78
+ const { t } = useI18n()
79
+ const component = resolveComponent(TEST_SEND_MODAL_COMPONENT) as Component
80
+
81
+ return {
82
+ open: (templateId, locale, content, data) =>
83
+ void open({
84
+ title: t('dms_mailing.editor.test_send_modal.title'),
85
+ component,
86
+ componentOptions: { templateId, locale, content, data },
87
+ size: TEST_SEND_MODAL_SIZE,
88
+ }),
89
+ }
90
+ }