@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.
- package/LICENSE +190 -0
- package/README.md +162 -0
- package/dist/antelope-modules.d.ts +32 -0
- package/dist/antelope-modules.js +36 -0
- package/dist/antelope-modules.js.map +1 -0
- package/dist/automation/email-event-trigger.d.ts +16 -0
- package/dist/automation/email-event-trigger.js +70 -0
- package/dist/automation/email-event-trigger.js.map +1 -0
- package/dist/automation/index.d.ts +4 -0
- package/dist/automation/index.js +39 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/automation/send-template-action.d.ts +13 -0
- package/dist/automation/send-template-action.js +71 -0
- package/dist/automation/send-template-action.js.map +1 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +32 -0
- package/dist/constants.js.map +1 -0
- package/dist/crons/index.d.ts +1 -0
- package/dist/crons/index.js +18 -0
- package/dist/crons/index.js.map +1 -0
- package/dist/crons/retention.d.ts +18 -0
- package/dist/crons/retention.js +107 -0
- package/dist/crons/retention.js.map +1 -0
- package/dist/data/body.d.ts +1 -0
- package/dist/data/body.js +18 -0
- package/dist/data/body.js.map +1 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +20 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/sends-table.d.ts +28 -0
- package/dist/data/sends-table.js +161 -0
- package/dist/data/sends-table.js.map +1 -0
- package/dist/data/templates-table.d.ts +31 -0
- package/dist/data/templates-table.js +173 -0
- package/dist/data/templates-table.js.map +1 -0
- package/dist/data-types/categories-type.d.ts +35 -0
- package/dist/data-types/categories-type.js +37 -0
- package/dist/data-types/categories-type.js.map +1 -0
- package/dist/data-types/category-type.d.ts +8 -0
- package/dist/data-types/category-type.js +43 -0
- package/dist/data-types/category-type.js.map +1 -0
- package/dist/data-types/index.d.ts +2 -0
- package/dist/data-types/index.js +19 -0
- package/dist/data-types/index.js.map +1 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.js +19 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/models/index.d.ts +4 -0
- package/dist/db/models/index.js +21 -0
- package/dist/db/models/index.js.map +1 -0
- package/dist/db/models/send-events.model.d.ts +27 -0
- package/dist/db/models/send-events.model.js +35 -0
- package/dist/db/models/send-events.model.js.map +1 -0
- package/dist/db/models/sends.model.d.ts +32 -0
- package/dist/db/models/sends.model.js +115 -0
- package/dist/db/models/sends.model.js.map +1 -0
- package/dist/db/models/settings.model.d.ts +23 -0
- package/dist/db/models/settings.model.js +13 -0
- package/dist/db/models/settings.model.js.map +1 -0
- package/dist/db/models/templates.model.d.ts +23 -0
- package/dist/db/models/templates.model.js +13 -0
- package/dist/db/models/templates.model.js.map +1 -0
- package/dist/db/tables/index.d.ts +4 -0
- package/dist/db/tables/index.js +21 -0
- package/dist/db/tables/index.js.map +1 -0
- package/dist/db/tables/send-events.table.d.ts +10 -0
- package/dist/db/tables/send-events.table.js +44 -0
- package/dist/db/tables/send-events.table.js.map +1 -0
- package/dist/db/tables/sends.table.d.ts +26 -0
- package/dist/db/tables/sends.table.js +113 -0
- package/dist/db/tables/sends.table.js.map +1 -0
- package/dist/db/tables/settings.table.d.ts +15 -0
- package/dist/db/tables/settings.table.js +68 -0
- package/dist/db/tables/settings.table.js.map +1 -0
- package/dist/db/tables/templates.table.d.ts +18 -0
- package/dist/db/tables/templates.table.js +81 -0
- package/dist/db/tables/templates.table.js.map +1 -0
- package/dist/engine/conditions.d.ts +2 -0
- package/dist/engine/conditions.js +20 -0
- package/dist/engine/conditions.js.map +1 -0
- package/dist/engine/coverage.d.ts +4 -0
- package/dist/engine/coverage.js +45 -0
- package/dist/engine/coverage.js.map +1 -0
- package/dist/engine/fallback-html.d.ts +4 -0
- package/dist/engine/fallback-html.js +84 -0
- package/dist/engine/fallback-html.js.map +1 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.js +24 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/paths.d.ts +3 -0
- package/dist/engine/paths.js +32 -0
- package/dist/engine/paths.js.map +1 -0
- package/dist/engine/resolve.d.ts +4 -0
- package/dist/engine/resolve.js +139 -0
- package/dist/engine/resolve.js.map +1 -0
- package/dist/engine/tokens.d.ts +7 -0
- package/dist/engine/tokens.js +29 -0
- package/dist/engine/tokens.js.map +1 -0
- package/dist/engine/walk.d.ts +3 -0
- package/dist/engine/walk.js +14 -0
- package/dist/engine/walk.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +18 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/tenant-export.d.ts +14 -0
- package/dist/hooks/tenant-export.js +84 -0
- package/dist/hooks/tenant-export.js.map +1 -0
- package/dist/implementations/dms-mailing/index.d.ts +2 -0
- package/dist/implementations/dms-mailing/index.js +8 -0
- package/dist/implementations/dms-mailing/index.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/pages/editor.d.ts +9 -0
- package/dist/pages/editor.js +30 -0
- package/dist/pages/editor.js.map +1 -0
- package/dist/pages/index.d.ts +7 -0
- package/dist/pages/index.js +24 -0
- package/dist/pages/index.js.map +1 -0
- package/dist/pages/module.d.ts +1 -0
- package/dist/pages/module.js +12 -0
- package/dist/pages/module.js.map +1 -0
- package/dist/pages/overview.d.ts +14 -0
- package/dist/pages/overview.js +92 -0
- package/dist/pages/overview.js.map +1 -0
- package/dist/pages/sends.d.ts +13 -0
- package/dist/pages/sends.js +125 -0
- package/dist/pages/sends.js.map +1 -0
- package/dist/pages/settings/form.d.ts +1 -0
- package/dist/pages/settings/form.js +68 -0
- package/dist/pages/settings/form.js.map +1 -0
- package/dist/pages/settings/index.d.ts +2 -0
- package/dist/pages/settings/index.js +19 -0
- package/dist/pages/settings/index.js.map +1 -0
- package/dist/pages/settings/page.d.ts +10 -0
- package/dist/pages/settings/page.js +35 -0
- package/dist/pages/settings/page.js.map +1 -0
- package/dist/pages/templates.d.ts +10 -0
- package/dist/pages/templates.js +140 -0
- package/dist/pages/templates.js.map +1 -0
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.js +37 -0
- package/dist/permissions.js.map +1 -0
- package/dist/quick-actions.d.ts +2 -0
- package/dist/quick-actions.js +18 -0
- package/dist/quick-actions.js.map +1 -0
- package/dist/realtime.d.ts +4 -0
- package/dist/realtime.js +44 -0
- package/dist/realtime.js.map +1 -0
- package/dist/routes/events.d.ts +9 -0
- package/dist/routes/events.js +63 -0
- package/dist/routes/events.js.map +1 -0
- package/dist/routes/index.d.ts +7 -0
- package/dist/routes/index.js +24 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/metrics.d.ts +54 -0
- package/dist/routes/metrics.js +235 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/period.d.ts +5 -0
- package/dist/routes/period.js +36 -0
- package/dist/routes/period.js.map +1 -0
- package/dist/routes/provider.d.ts +13 -0
- package/dist/routes/provider.js +41 -0
- package/dist/routes/provider.js.map +1 -0
- package/dist/routes/sends.d.ts +31 -0
- package/dist/routes/sends.js +178 -0
- package/dist/routes/sends.js.map +1 -0
- package/dist/routes/settings.d.ts +14 -0
- package/dist/routes/settings.js +72 -0
- package/dist/routes/settings.js.map +1 -0
- package/dist/routes/templates.d.ts +61 -0
- package/dist/routes/templates.js +230 -0
- package/dist/routes/templates.js.map +1 -0
- package/dist/services/attention.d.ts +23 -0
- package/dist/services/attention.js +104 -0
- package/dist/services/attention.js.map +1 -0
- package/dist/services/categories.d.ts +34 -0
- package/dist/services/categories.js +34 -0
- package/dist/services/categories.js.map +1 -0
- package/dist/services/events.d.ts +10 -0
- package/dist/services/events.js +89 -0
- package/dist/services/events.js.map +1 -0
- package/dist/services/metrics.d.ts +34 -0
- package/dist/services/metrics.js +103 -0
- package/dist/services/metrics.js.map +1 -0
- package/dist/services/notify.d.ts +20 -0
- package/dist/services/notify.js +63 -0
- package/dist/services/notify.js.map +1 -0
- package/dist/services/permissions.d.ts +2 -0
- package/dist/services/permissions.js +15 -0
- package/dist/services/permissions.js.map +1 -0
- package/dist/services/provider.d.ts +6 -0
- package/dist/services/provider.js +25 -0
- package/dist/services/provider.js.map +1 -0
- package/dist/services/render.d.ts +6 -0
- package/dist/services/render.js +21 -0
- package/dist/services/render.js.map +1 -0
- package/dist/services/send.d.ts +49 -0
- package/dist/services/send.js +259 -0
- package/dist/services/send.js.map +1 -0
- package/dist/services/settings.d.ts +4 -0
- package/dist/services/settings.js +61 -0
- package/dist/services/settings.js.map +1 -0
- package/dist/services/status.d.ts +3 -0
- package/dist/services/status.js +16 -0
- package/dist/services/status.js.map +1 -0
- package/dist/services/templates.d.ts +17 -0
- package/dist/services/templates.js +92 -0
- package/dist/services/templates.js.map +1 -0
- package/dist/test/antelope.test.d.ts +2 -0
- package/dist/test/antelope.test.js +179 -0
- package/dist/test/antelope.test.js.map +1 -0
- package/dist/test/helpers/http.d.ts +4 -0
- package/dist/test/helpers/http.js +28 -0
- package/dist/test/helpers/http.js.map +1 -0
- package/dist/test/helpers/owner.d.ts +5 -0
- package/dist/test/helpers/owner.js +30 -0
- package/dist/test/helpers/owner.js.map +1 -0
- package/dist/test/integration/events.test.d.ts +1 -0
- package/dist/test/integration/events.test.js +132 -0
- package/dist/test/integration/events.test.js.map +1 -0
- package/dist/test/integration/retention.test.d.ts +1 -0
- package/dist/test/integration/retention.test.js +291 -0
- package/dist/test/integration/retention.test.js.map +1 -0
- package/dist/test/integration/sends.test.d.ts +1 -0
- package/dist/test/integration/sends.test.js +157 -0
- package/dist/test/integration/sends.test.js.map +1 -0
- package/dist/test/integration/settings.test.d.ts +1 -0
- package/dist/test/integration/settings.test.js +45 -0
- package/dist/test/integration/settings.test.js.map +1 -0
- package/dist/test/integration/setup.test.d.ts +1 -0
- package/dist/test/integration/setup.test.js +32 -0
- package/dist/test/integration/setup.test.js.map +1 -0
- package/dist/test/integration/templates.test.d.ts +1 -0
- package/dist/test/integration/templates.test.js +167 -0
- package/dist/test/integration/templates.test.js.map +1 -0
- package/dist/test/integration/tenant-export.test.d.ts +1 -0
- package/dist/test/integration/tenant-export.test.js +85 -0
- package/dist/test/integration/tenant-export.test.js.map +1 -0
- package/dist/test/unit/attention.test.d.ts +1 -0
- package/dist/test/unit/attention.test.js +109 -0
- package/dist/test/unit/attention.test.js.map +1 -0
- package/dist/test/unit/automation.test.d.ts +1 -0
- package/dist/test/unit/automation.test.js +104 -0
- package/dist/test/unit/automation.test.js.map +1 -0
- package/dist/test/unit/categories.test.d.ts +1 -0
- package/dist/test/unit/categories.test.js +38 -0
- package/dist/test/unit/categories.test.js.map +1 -0
- package/dist/test/unit/engine/conditions.test.d.ts +1 -0
- package/dist/test/unit/engine/conditions.test.js +23 -0
- package/dist/test/unit/engine/conditions.test.js.map +1 -0
- package/dist/test/unit/engine/coverage.test.d.ts +1 -0
- package/dist/test/unit/engine/coverage.test.js +106 -0
- package/dist/test/unit/engine/coverage.test.js.map +1 -0
- package/dist/test/unit/engine/fallback-html.test.d.ts +1 -0
- package/dist/test/unit/engine/fallback-html.test.js +78 -0
- package/dist/test/unit/engine/fallback-html.test.js.map +1 -0
- package/dist/test/unit/engine/paths.test.d.ts +1 -0
- package/dist/test/unit/engine/paths.test.js +33 -0
- package/dist/test/unit/engine/paths.test.js.map +1 -0
- package/dist/test/unit/engine/resolve.test.d.ts +1 -0
- package/dist/test/unit/engine/resolve.test.js +157 -0
- package/dist/test/unit/engine/resolve.test.js.map +1 -0
- package/dist/test/unit/engine/tokens.test.d.ts +1 -0
- package/dist/test/unit/engine/tokens.test.js +26 -0
- package/dist/test/unit/engine/tokens.test.js.map +1 -0
- package/dist/test/unit/events.test.d.ts +1 -0
- package/dist/test/unit/events.test.js +13 -0
- package/dist/test/unit/events.test.js.map +1 -0
- package/dist/test/unit/metrics.test.d.ts +1 -0
- package/dist/test/unit/metrics.test.js +108 -0
- package/dist/test/unit/metrics.test.js.map +1 -0
- package/dist/test/unit/notify.test.d.ts +1 -0
- package/dist/test/unit/notify.test.js +49 -0
- package/dist/test/unit/notify.test.js.map +1 -0
- package/dist/test/unit/period.test.d.ts +1 -0
- package/dist/test/unit/period.test.js +44 -0
- package/dist/test/unit/period.test.js.map +1 -0
- package/dist/test/unit/provider.test.d.ts +1 -0
- package/dist/test/unit/provider.test.js +47 -0
- package/dist/test/unit/provider.test.js.map +1 -0
- package/dist/test/unit/realtime.test.d.ts +1 -0
- package/dist/test/unit/realtime.test.js +13 -0
- package/dist/test/unit/realtime.test.js.map +1 -0
- package/dist/test/unit/render.test.d.ts +1 -0
- package/dist/test/unit/render.test.js +23 -0
- package/dist/test/unit/render.test.js.map +1 -0
- package/dist/test/unit/retention.test.d.ts +1 -0
- package/dist/test/unit/retention.test.js +66 -0
- package/dist/test/unit/retention.test.js.map +1 -0
- package/dist/test/unit/send-batch.test.d.ts +1 -0
- package/dist/test/unit/send-batch.test.js +46 -0
- package/dist/test/unit/send-batch.test.js.map +1 -0
- package/dist/test/unit/send.test.d.ts +1 -0
- package/dist/test/unit/send.test.js +120 -0
- package/dist/test/unit/send.test.js.map +1 -0
- package/dist/test/unit/smoke.test.d.ts +1 -0
- package/dist/test/unit/smoke.test.js +10 -0
- package/dist/test/unit/smoke.test.js.map +1 -0
- package/dist/test/unit/status.test.d.ts +1 -0
- package/dist/test/unit/status.test.js +16 -0
- package/dist/test/unit/status.test.js.map +1 -0
- package/dist/test/unit/templates.test.d.ts +1 -0
- package/dist/test/unit/templates.test.js +48 -0
- package/dist/test/unit/templates.test.js.map +1 -0
- package/dist/test/unit/tenant-export.test.d.ts +1 -0
- package/dist/test/unit/tenant-export.test.js +59 -0
- package/dist/test/unit/tenant-export.test.js.map +1 -0
- package/dist/test/unit/webhook-secret.test.d.ts +1 -0
- package/dist/test/unit/webhook-secret.test.js +42 -0
- package/dist/test/unit/webhook-secret.test.js.map +1 -0
- package/dist/types/blocks.d.ts +2 -0
- package/dist/types/blocks.js +3 -0
- package/dist/types/blocks.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +21 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/resolved.d.ts +68 -0
- package/dist/types/resolved.js +3 -0
- package/dist/types/resolved.js.map +1 -0
- package/dist/types/sends.d.ts +14 -0
- package/dist/types/sends.js +19 -0
- package/dist/types/sends.js.map +1 -0
- package/dist/types/settings.d.ts +16 -0
- package/dist/types/settings.js +11 -0
- package/dist/types/settings.js.map +1 -0
- package/dist/validation/settings.schema.d.ts +63 -0
- package/dist/validation/settings.schema.js +29 -0
- package/dist/validation/settings.schema.js.map +1 -0
- package/dist/validation/templates.schema.d.ts +248 -0
- package/dist/validation/templates.schema.js +126 -0
- package/dist/validation/templates.schema.js.map +1 -0
- package/frontend-vue/.editorconfig +12 -0
- package/frontend-vue/.prettierrc +11 -0
- package/frontend-vue/app/components/AttentionCard.vue +78 -0
- package/frontend-vue/app/components/CategoriesInput.vue +126 -0
- package/frontend-vue/app/components/CategoryInput.vue +67 -0
- package/frontend-vue/app/components/Editor.vue +63 -0
- package/frontend-vue/app/components/FunnelCard.vue +66 -0
- package/frontend-vue/app/components/HtmlPreviewModal.vue +20 -0
- package/frontend-vue/app/components/NewTemplateModal.vue +158 -0
- package/frontend-vue/app/components/ProviderChip.vue +39 -0
- package/frontend-vue/app/components/SendDrawer.vue +298 -0
- package/frontend-vue/app/components/TemplateCard.vue +127 -0
- package/frontend-vue/app/components/TemplateDrawer.vue +394 -0
- package/frontend-vue/app/components/TemplateGallery.vue +112 -0
- package/frontend-vue/app/components/TemplatePreviewFrame.vue +103 -0
- package/frontend-vue/app/components/TestSendModal.vue +162 -0
- package/frontend-vue/app/components/TokenText.vue +26 -0
- package/frontend-vue/app/components/blocks/Button.vue +19 -0
- package/frontend-vue/app/components/blocks/Code.vue +17 -0
- package/frontend-vue/app/components/blocks/Divider.vue +3 -0
- package/frontend-vue/app/components/blocks/Footer.vue +22 -0
- package/frontend-vue/app/components/blocks/Heading.vue +18 -0
- package/frontend-vue/app/components/blocks/Hero.vue +32 -0
- package/frontend-vue/app/components/blocks/List.vue +33 -0
- package/frontend-vue/app/components/blocks/Paragraph.vue +18 -0
- package/frontend-vue/app/components/blocks/Total.vue +18 -0
- package/frontend-vue/app/components/editor/EditorBlock.vue +121 -0
- package/frontend-vue/app/components/editor/EditorBlockList.vue +44 -0
- package/frontend-vue/app/components/editor/EditorBlockSettings.vue +186 -0
- package/frontend-vue/app/components/editor/EditorCanvas.vue +91 -0
- package/frontend-vue/app/components/editor/EditorConditionFields.vue +88 -0
- package/frontend-vue/app/components/editor/EditorDataPanel.vue +128 -0
- package/frontend-vue/app/components/editor/EditorDraftBar.vue +35 -0
- package/frontend-vue/app/components/editor/EditorIfBlock.vue +120 -0
- package/frontend-vue/app/components/editor/EditorMissingLocale.vue +62 -0
- package/frontend-vue/app/components/editor/EditorPalette.vue +62 -0
- package/frontend-vue/app/components/editor/EditorRail.vue +58 -0
- package/frontend-vue/app/components/editor/EditorSubjectSettings.vue +37 -0
- package/frontend-vue/app/components/editor/EditorTemplateSettings.vue +87 -0
- package/frontend-vue/app/components/editor/EditorTestSendModal.vue +66 -0
- package/frontend-vue/app/components/editor/EditorToolbar.vue +155 -0
- package/frontend-vue/app/components/editor/EditorVariables.vue +163 -0
- package/frontend-vue/app/components/editor/EditorWorkspace.vue +189 -0
- package/frontend-vue/app/composables/useEditorContext.ts +44 -0
- package/frontend-vue/app/composables/useMailingApi.ts +197 -0
- package/frontend-vue/app/composables/useMailingPeriodData.ts +39 -0
- package/frontend-vue/app/composables/useTemplateDrawer.ts +90 -0
- package/frontend-vue/app/composables/useTemplateEditor.ts +228 -0
- package/frontend-vue/app/emails/EmailMailingTemplate.vue +24 -0
- package/frontend-vue/app/emails/components/MailingBlocks.vue +117 -0
- package/frontend-vue/app/plugins/template-gallery-display.client.ts +14 -0
- package/frontend-vue/app/types/component.ts +29 -0
- package/frontend-vue/app/types/mailing.ts +365 -0
- package/frontend-vue/app/utils/blocks/components.ts +14 -0
- package/frontend-vue/app/utils/blocks/dnd.ts +15 -0
- package/frontend-vue/app/utils/blocks/factory.ts +100 -0
- package/frontend-vue/app/utils/blocks/fields.ts +58 -0
- package/frontend-vue/app/utils/blocks/index.ts +5 -0
- package/frontend-vue/app/utils/blocks/tree.ts +74 -0
- package/frontend-vue/app/utils/categories.ts +54 -0
- package/frontend-vue/app/utils/clone.ts +3 -0
- package/frontend-vue/app/utils/conditions.ts +50 -0
- package/frontend-vue/app/utils/editor-route.ts +18 -0
- package/frontend-vue/app/utils/gallery.ts +41 -0
- package/frontend-vue/app/utils/locales.ts +41 -0
- package/frontend-vue/app/utils/paths.ts +84 -0
- package/frontend-vue/app/utils/period.ts +23 -0
- package/frontend-vue/app/utils/testData.ts +10 -0
- package/frontend-vue/app/utils/timeline.ts +68 -0
- package/frontend-vue/app/utils/tokens.ts +24 -0
- package/frontend-vue/app/utils/variables.ts +48 -0
- package/frontend-vue/dms.email.ts +9 -0
- package/frontend-vue/dms.frontend.ts +29 -0
- package/frontend-vue/eslint.config.mjs +13 -0
- package/frontend-vue/i18n/locales/mailing-en-GB.json +397 -0
- package/frontend-vue/i18n/locales/mailing-fr-FR.json +397 -0
- package/frontend-vue/package.json +32 -0
- package/frontend-vue/pnpm-lock.yaml +6446 -0
- package/frontend-vue/pnpm-workspace.yaml +2 -0
- package/frontend-vue/tests/block-components.test.ts +14 -0
- package/frontend-vue/tests/block-fields.test.ts +23 -0
- package/frontend-vue/tests/blocks-factory.test.ts +28 -0
- package/frontend-vue/tests/blocks-tree.test.ts +49 -0
- package/frontend-vue/tests/categories.test.ts +64 -0
- package/frontend-vue/tests/conditions.test.ts +36 -0
- package/frontend-vue/tests/dnd.test.ts +11 -0
- package/frontend-vue/tests/editor-block-list.test.ts +54 -0
- package/frontend-vue/tests/editor-route.test.ts +23 -0
- package/frontend-vue/tests/email-render-cases.json +77 -0
- package/frontend-vue/tests/gallery.test.ts +39 -0
- package/frontend-vue/tests/i18n-editor.test.ts +21 -0
- package/frontend-vue/tests/i18n.test.ts +83 -0
- package/frontend-vue/tests/locales.test.ts +38 -0
- package/frontend-vue/tests/mailing-api.test.ts +10 -0
- package/frontend-vue/tests/paths.test.ts +128 -0
- package/frontend-vue/tests/period.test.ts +13 -0
- package/frontend-vue/tests/smoke.test.ts +8 -0
- package/frontend-vue/tests/template-editor.test.ts +65 -0
- package/frontend-vue/tests/test-data.test.ts +15 -0
- package/frontend-vue/tests/timeline.test.ts +29 -0
- package/frontend-vue/tests/tokens.test.ts +12 -0
- package/frontend-vue/tests/variables.test.ts +46 -0
- package/frontend-vue/vitest.config.ts +10 -0
- package/package.json +94 -0
- package/src/antelope-modules.ts +48 -0
- package/src/automation/email-event-trigger.ts +112 -0
- package/src/automation/index.ts +33 -0
- package/src/automation/send-template-action.ts +100 -0
- package/src/config.ts +15 -0
- package/src/constants.ts +42 -0
- package/src/crons/index.ts +1 -0
- package/src/crons/retention.ts +126 -0
- package/src/data/body.ts +14 -0
- package/src/data/index.ts +3 -0
- package/src/data/sends-table.ts +154 -0
- package/src/data/templates-table.ts +202 -0
- package/src/data-types/categories-type.ts +29 -0
- package/src/data-types/category-type.ts +44 -0
- package/src/data-types/index.ts +2 -0
- package/src/db/index.ts +2 -0
- package/src/db/models/index.ts +4 -0
- package/src/db/models/send-events.model.ts +46 -0
- package/src/db/models/sends.model.ts +147 -0
- package/src/db/models/settings.model.ts +12 -0
- package/src/db/models/templates.model.ts +12 -0
- package/src/db/tables/index.ts +4 -0
- package/src/db/tables/send-events.table.ts +31 -0
- package/src/db/tables/sends.table.ts +85 -0
- package/src/db/tables/settings.table.ts +49 -0
- package/src/db/tables/templates.table.ts +70 -0
- package/src/engine/conditions.ts +30 -0
- package/src/engine/coverage.ts +67 -0
- package/src/engine/fallback-html.ts +116 -0
- package/src/engine/index.ts +7 -0
- package/src/engine/paths.ts +33 -0
- package/src/engine/resolve.ts +178 -0
- package/src/engine/tokens.ts +35 -0
- package/src/engine/walk.ts +18 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/tenant-export.ts +127 -0
- package/src/implementations/dms-mailing/index.ts +2 -0
- package/src/index.ts +62 -0
- package/src/pages/editor.ts +23 -0
- package/src/pages/index.ts +11 -0
- package/src/pages/module.ts +9 -0
- package/src/pages/overview.ts +175 -0
- package/src/pages/sends.ts +174 -0
- package/src/pages/settings/form.ts +67 -0
- package/src/pages/settings/index.ts +2 -0
- package/src/pages/settings/page.ts +34 -0
- package/src/pages/templates.ts +141 -0
- package/src/permissions.ts +44 -0
- package/src/quick-actions.ts +20 -0
- package/src/realtime.ts +53 -0
- package/src/routes/events.ts +57 -0
- package/src/routes/index.ts +7 -0
- package/src/routes/metrics.ts +243 -0
- package/src/routes/period.ts +46 -0
- package/src/routes/provider.ts +31 -0
- package/src/routes/sends.ts +215 -0
- package/src/routes/settings.ts +66 -0
- package/src/routes/templates.ts +256 -0
- package/src/services/attention.ts +151 -0
- package/src/services/categories.ts +43 -0
- package/src/services/events.ts +121 -0
- package/src/services/metrics.ts +175 -0
- package/src/services/notify.ts +90 -0
- package/src/services/permissions.ts +42 -0
- package/src/services/provider.ts +46 -0
- package/src/services/render.ts +33 -0
- package/src/services/send.ts +409 -0
- package/src/services/settings.ts +74 -0
- package/src/services/status.ts +16 -0
- package/src/services/templates.ts +157 -0
- package/src/test/antelope.test.ts +188 -0
- package/src/test/helpers/http.ts +23 -0
- package/src/test/helpers/owner.ts +37 -0
- package/src/test/integration/events.test.ts +136 -0
- package/src/test/integration/retention.test.ts +363 -0
- package/src/test/integration/sends.test.ts +200 -0
- package/src/test/integration/settings.test.ts +50 -0
- package/src/test/integration/setup.test.ts +35 -0
- package/src/test/integration/templates.test.ts +229 -0
- package/src/test/integration/tenant-export.test.ts +125 -0
- package/src/test/unit/attention.test.ts +129 -0
- package/src/test/unit/automation.test.ts +130 -0
- package/src/test/unit/categories.test.ts +51 -0
- package/src/test/unit/engine/conditions.test.ts +35 -0
- package/src/test/unit/engine/coverage.test.ts +118 -0
- package/src/test/unit/engine/fallback-html.test.ts +86 -0
- package/src/test/unit/engine/paths.test.ts +39 -0
- package/src/test/unit/engine/resolve.test.ts +176 -0
- package/src/test/unit/engine/tokens.test.ts +26 -0
- package/src/test/unit/events.test.ts +19 -0
- package/src/test/unit/metrics.test.ts +170 -0
- package/src/test/unit/notify.test.ts +71 -0
- package/src/test/unit/period.test.ts +64 -0
- package/src/test/unit/provider.test.ts +57 -0
- package/src/test/unit/realtime.test.ts +11 -0
- package/src/test/unit/render.test.ts +23 -0
- package/src/test/unit/retention.test.ts +91 -0
- package/src/test/unit/send-batch.test.ts +72 -0
- package/src/test/unit/send.test.ts +161 -0
- package/src/test/unit/smoke.test.ts +8 -0
- package/src/test/unit/status.test.ts +14 -0
- package/src/test/unit/templates.test.ts +52 -0
- package/src/test/unit/tenant-export.test.ts +88 -0
- package/src/test/unit/webhook-secret.test.ts +50 -0
- package/src/types/blocks.ts +31 -0
- package/src/types/index.ts +4 -0
- package/src/types/resolved.ts +89 -0
- package/src/types/sends.ts +44 -0
- package/src/types/settings.ts +24 -0
- package/src/validation/settings.schema.ts +33 -0
- 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
|
+
}
|