@antelopejs/dms-mailing 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/data/templates-table.d.ts +4 -1
- package/dist/data/templates-table.js +55 -13
- package/dist/data/templates-table.js.map +1 -1
- package/dist/data-types/categories-type.d.ts +5 -5
- package/dist/db/models/templates.model.d.ts +10 -1
- package/dist/db/models/templates.model.js +27 -1
- package/dist/db/models/templates.model.js.map +1 -1
- package/dist/db/tables/templates.table.d.ts +7 -1
- package/dist/db/tables/templates.table.js +3 -1
- package/dist/db/tables/templates.table.js.map +1 -1
- package/dist/engine/formats.d.ts +13 -0
- package/dist/engine/formats.js +85 -0
- package/dist/engine/formats.js.map +1 -0
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +1 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/resolve.d.ts +2 -1
- package/dist/engine/resolve.js +14 -7
- package/dist/engine/resolve.js.map +1 -1
- package/dist/engine/tokens.d.ts +2 -1
- package/dist/engine/tokens.js +3 -5
- package/dist/engine/tokens.js.map +1 -1
- package/dist/frontend-module.d.ts +2 -0
- package/dist/frontend-module.js +24 -0
- package/dist/frontend-module.js.map +1 -0
- package/dist/hooks/tenant-export.js +4 -1
- package/dist/hooks/tenant-export.js.map +1 -1
- package/dist/implementations/dms-mailing/index.d.ts +1 -0
- package/dist/implementations/dms-mailing/index.js +3 -1
- package/dist/implementations/dms-mailing/index.js.map +1 -1
- package/dist/index.js +2 -19
- package/dist/index.js.map +1 -1
- package/dist/pages/overview.d.ts +1 -1
- package/dist/pages/overview.js +11 -13
- package/dist/pages/overview.js.map +1 -1
- package/dist/pages/sends.d.ts +1 -1
- package/dist/pages/sends.js +16 -8
- package/dist/pages/sends.js.map +1 -1
- package/dist/pages/templates.js +3 -8
- package/dist/pages/templates.js.map +1 -1
- package/dist/pages/toolbar.d.ts +12 -0
- package/dist/pages/toolbar.js +41 -0
- package/dist/pages/toolbar.js.map +1 -0
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js +8 -8
- package/dist/realtime.js.map +1 -1
- package/dist/routes/metrics.d.ts +9 -2
- package/dist/routes/metrics.js +90 -35
- package/dist/routes/metrics.js.map +1 -1
- package/dist/routes/period.d.ts +1 -0
- package/dist/routes/period.js +19 -0
- package/dist/routes/period.js.map +1 -1
- package/dist/routes/sends.js +6 -3
- package/dist/routes/sends.js.map +1 -1
- package/dist/routes/settings.js +6 -7
- package/dist/routes/settings.js.map +1 -1
- package/dist/routes/templates.d.ts +8 -4
- package/dist/routes/templates.js +40 -15
- package/dist/routes/templates.js.map +1 -1
- package/dist/services/categories.d.ts +2 -1
- package/dist/services/categories.js +9 -0
- package/dist/services/categories.js.map +1 -1
- package/dist/services/declarations.js +2 -2
- package/dist/services/declarations.js.map +1 -1
- package/dist/services/events.js +1 -1
- package/dist/services/events.js.map +1 -1
- package/dist/services/localized-text.d.ts +7 -0
- package/dist/services/localized-text.js +28 -0
- package/dist/services/localized-text.js.map +1 -0
- package/dist/services/send-stat-items.js +34 -12
- package/dist/services/send-stat-items.js.map +1 -1
- package/dist/services/send-stats.d.ts +7 -0
- package/dist/services/send-stats.js +17 -5
- package/dist/services/send-stats.js.map +1 -1
- package/dist/services/send.d.ts +2 -0
- package/dist/services/send.js +11 -12
- package/dist/services/send.js.map +1 -1
- package/dist/services/sender-declaration.d.ts +2 -0
- package/dist/services/sender-declaration.js +18 -0
- package/dist/services/sender-declaration.js.map +1 -0
- package/dist/services/starters.d.ts +2 -2
- package/dist/services/starters.js +1 -10
- package/dist/services/starters.js.map +1 -1
- package/dist/services/templates.d.ts +11 -5
- package/dist/services/templates.js +24 -6
- package/dist/services/templates.js.map +1 -1
- package/dist/services/webhook-status.d.ts +2 -1
- package/dist/services/webhook-status.js +6 -5
- package/dist/services/webhook-status.js.map +1 -1
- package/dist/types/settings.d.ts +6 -0
- package/dist/types/settings.js +25 -5
- package/dist/types/settings.js.map +1 -1
- package/dist/validation/declarations.schema.d.ts +25 -7
- package/dist/validation/declarations.schema.js +16 -3
- package/dist/validation/declarations.schema.js.map +1 -1
- package/dist/validation/settings.schema.d.ts +25 -25
- package/dist/validation/settings.schema.js +30 -6
- package/dist/validation/settings.schema.js.map +1 -1
- package/dist/validation/templates.schema.d.ts +13 -0
- package/dist/validation/templates.schema.js +6 -1
- package/dist/validation/templates.schema.js.map +1 -1
- package/frontend-vue/app/components/CategoriesInput.vue +15 -13
- package/frontend-vue/app/components/FunnelCard.vue +46 -3
- package/frontend-vue/app/components/NewTemplateModal.vue +4 -2
- package/frontend-vue/app/components/OverviewGate.vue +0 -8
- package/frontend-vue/app/components/ProviderPill.vue +50 -0
- package/frontend-vue/app/components/SendDrawer.vue +206 -183
- package/frontend-vue/app/components/StarterGallery.vue +1 -1
- package/frontend-vue/app/components/TemplateCard.vue +4 -1
- package/frontend-vue/app/components/TemplateDrawer.vue +3 -5
- package/frontend-vue/app/components/TemplateGallery.vue +57 -17
- package/frontend-vue/app/components/TemplatePreviewFrame.vue +41 -11
- package/frontend-vue/app/components/editor/EditorTemplatePane.vue +1 -3
- package/frontend-vue/app/components/editor/EditorTitle.vue +1 -7
- package/frontend-vue/app/composables/useLocalizedDefaults.ts +5 -7
- package/frontend-vue/app/composables/useMailingApi.ts +3 -3
- package/frontend-vue/app/composables/useMailingReads.ts +2 -2
- package/frontend-vue/app/composables/useTemplateCategories.ts +8 -7
- package/frontend-vue/app/types/mailing.ts +18 -0
- package/frontend-vue/app/utils/categories.ts +58 -49
- package/frontend-vue/app/utils/funnel.ts +25 -0
- package/frontend-vue/app/utils/period.ts +0 -7
- package/frontend-vue/app/utils/starters.ts +3 -1
- package/frontend-vue/app/utils/tokens.ts +6 -1
- package/frontend-vue/i18n/locales/mailing-en-GB.json +19 -28
- package/frontend-vue/i18n/locales/mailing-fr-FR.json +19 -28
- package/frontend-vue/package.json +1 -1
- package/frontend-vue/tests/categories-input.test.ts +4 -10
- package/frontend-vue/tests/categories.test.ts +55 -17
- package/frontend-vue/tests/conditions.test.ts +1 -4
- package/frontend-vue/tests/funnel-card.test.ts +27 -0
- package/frontend-vue/tests/funnel.test.ts +25 -0
- package/frontend-vue/tests/localized-defaults.test.ts +0 -29
- package/frontend-vue/tests/new-template-modal.test.ts +1 -1
- package/frontend-vue/tests/overview-gate.test.ts +1 -24
- package/frontend-vue/tests/page-reads.test.ts +9 -25
- package/frontend-vue/tests/period.test.ts +0 -12
- package/frontend-vue/tests/replay-toast.test.ts +2 -1
- package/frontend-vue/tests/send-drawer.test.ts +123 -0
- package/frontend-vue/tests/template-drawer.test.ts +1 -1
- package/frontend-vue/tests/template-gallery.test.ts +90 -3
- package/frontend-vue/tests/template-preview-frame.test.ts +92 -0
- package/package.json +6 -5
- package/skills/dms-mailing/SKILL.md +17 -4
- package/src/constants.ts +2 -0
- package/src/data/sends-table.ts +1 -1
- package/src/data/templates-table.ts +123 -19
- package/src/db/models/templates.model.ts +61 -3
- package/src/db/tables/templates.table.ts +10 -1
- package/src/engine/formats.ts +124 -0
- package/src/engine/index.ts +1 -0
- package/src/engine/resolve.ts +18 -7
- package/src/engine/tokens.ts +7 -5
- package/src/frontend-module.ts +36 -0
- package/src/hooks/tenant-export.ts +8 -1
- package/src/implementations/dms-mailing/index.ts +2 -0
- package/src/index.ts +2 -23
- package/src/pages/overview.ts +20 -16
- package/src/pages/sends.ts +27 -11
- package/src/pages/templates.ts +4 -8
- package/src/pages/toolbar.ts +77 -0
- package/src/realtime.ts +17 -8
- package/src/routes/metrics.ts +158 -45
- package/src/routes/period.ts +32 -0
- package/src/routes/sends.ts +13 -4
- package/src/routes/settings.ts +11 -8
- package/src/routes/templates.ts +72 -26
- package/src/services/categories.ts +14 -1
- package/src/services/declarations.ts +4 -3
- package/src/services/events.ts +1 -1
- package/src/services/localized-text.ts +53 -0
- package/src/services/send-stat-items.ts +60 -13
- package/src/services/send-stats.ts +37 -5
- package/src/services/send.ts +19 -20
- package/src/services/sender-declaration.ts +27 -0
- package/src/services/starters.ts +3 -15
- package/src/services/templates.ts +50 -19
- package/src/services/webhook-status.ts +3 -3
- package/src/types/settings.ts +35 -5
- package/src/validation/declarations.schema.ts +29 -3
- package/src/validation/settings.schema.ts +56 -14
- package/src/validation/templates.schema.ts +10 -0
- package/frontend-vue/app/components/PageHeader.vue +0 -94
- package/frontend-vue/app/composables/useOverviewSetup.ts +0 -21
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
declareTemplateCategory,
|
|
5
5
|
} from "../../services/declarations";
|
|
6
6
|
import { importSends } from "../../services/send-import";
|
|
7
|
+
import { declareSender } from "../../services/sender-declaration";
|
|
7
8
|
|
|
8
9
|
export { recordEmailEvent as RecordEmailEvent } from "../../services/events";
|
|
9
10
|
export { sendUnlessSuppressed as SendTemplate } from "../../services/checked-send";
|
|
@@ -16,4 +17,5 @@ export const DeclareTemplate = afterDatabaseReady(declareTemplate);
|
|
|
16
17
|
export const DeclareTemplateCategory = afterDatabaseReady(
|
|
17
18
|
declareTemplateCategory,
|
|
18
19
|
);
|
|
20
|
+
export const DeclareSender = afterDatabaseReady(declareSender);
|
|
19
21
|
export const ImportSends = afterDatabaseReady(importSends);
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import "./permissions";
|
|
2
2
|
import "./db";
|
|
3
|
-
import path from "node:path";
|
|
4
3
|
import { ImplementInterface } from "@antelopejs/interface-core";
|
|
5
4
|
import { AddFrontendModule } from "@antelopejs/interface-dms/page";
|
|
6
5
|
import {
|
|
@@ -8,7 +7,6 @@ import {
|
|
|
8
7
|
unregisterAutomationNodes,
|
|
9
8
|
} from "./automation";
|
|
10
9
|
import { type DmsMailingConfig, setConfig } from "./config";
|
|
11
|
-
import { API_BASE_PATH, FRONTEND_MODULE_NAME } from "./constants";
|
|
12
10
|
import {
|
|
13
11
|
ATTENTION_JOB,
|
|
14
12
|
RETENTION_JOB,
|
|
@@ -22,7 +20,7 @@ import {
|
|
|
22
20
|
unregisterTenantExport,
|
|
23
21
|
} from "./hooks";
|
|
24
22
|
import { registerRealtimeTopics } from "./realtime";
|
|
25
|
-
import {
|
|
23
|
+
import { frontendModule } from "./frontend-module";
|
|
26
24
|
|
|
27
25
|
export * from "./data";
|
|
28
26
|
export * from "./data-types";
|
|
@@ -33,18 +31,6 @@ export type { DmsMailingConfig } from "./config";
|
|
|
33
31
|
|
|
34
32
|
const CRON_JOBS = [RETENTION_JOB, ATTENTION_JOB];
|
|
35
33
|
|
|
36
|
-
/**
|
|
37
|
-
* The provider webhook (`/events/:provider/:tenantId`), served through the
|
|
38
|
-
* dashboard's public URL: the front proxy lets it through without an
|
|
39
|
-
* `Origin`, never sends it a session and relays the secret header, which the
|
|
40
|
-
* route checks itself.
|
|
41
|
-
*/
|
|
42
|
-
const PROVIDER_WEBHOOK_ENDPOINT = {
|
|
43
|
-
path: `${API_BASE_PATH}/events/*/*`,
|
|
44
|
-
methods: ["POST"],
|
|
45
|
-
headers: [WEBHOOK_SECRET_HEADER],
|
|
46
|
-
};
|
|
47
|
-
|
|
48
34
|
let scheduledJobs: ScheduledJob[] = [];
|
|
49
35
|
|
|
50
36
|
export async function construct(config: DmsMailingConfig = {}): Promise<void> {
|
|
@@ -57,14 +43,7 @@ export async function construct(config: DmsMailingConfig = {}): Promise<void> {
|
|
|
57
43
|
registerAutomationNodes();
|
|
58
44
|
registerTenantExport();
|
|
59
45
|
registerRealtimeTopics();
|
|
60
|
-
await AddFrontendModule(
|
|
61
|
-
name: FRONTEND_MODULE_NAME,
|
|
62
|
-
sourcePath: path.join(__dirname, "../frontend-vue"),
|
|
63
|
-
renderer: { name: "vue", version: "3" },
|
|
64
|
-
options: { dmsMailing: {} },
|
|
65
|
-
priority: 0,
|
|
66
|
-
publicEndpoints: [PROVIDER_WEBHOOK_ENDPOINT],
|
|
67
|
-
});
|
|
46
|
+
await AddFrontendModule(frontendModule());
|
|
68
47
|
}
|
|
69
48
|
|
|
70
49
|
export function destroy(): void {
|
package/src/pages/overview.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "../constants";
|
|
24
24
|
import { BUSINESS_AUDIENCE, SEND_AUDIENCE_QUERY_KEY } from "../types";
|
|
25
25
|
import { mailingNavCategory } from "./module";
|
|
26
|
+
import { MAILING_LIVE, mailingToolbar } from "./toolbar";
|
|
26
27
|
|
|
27
28
|
const OVERVIEW_ORDER = 0;
|
|
28
29
|
const GRID_GAP = "1rem";
|
|
@@ -30,6 +31,7 @@ const CHART_HEIGHT = "260px";
|
|
|
30
31
|
const WIDE_SPAN = 2;
|
|
31
32
|
const ATTENTION_SKELETON_ROWS = 3;
|
|
32
33
|
|
|
34
|
+
/** A KPI card of the Overview, refreshed on each send while the page is live. */
|
|
33
35
|
const kpi = (
|
|
34
36
|
id: string,
|
|
35
37
|
icon: string,
|
|
@@ -40,6 +42,7 @@ const kpi = (
|
|
|
40
42
|
variant: "stat",
|
|
41
43
|
fetchUrl: `${API_BASE_PATH}/metrics/kpi/${id}?${SEND_AUDIENCE_QUERY_KEY}=${BUSINESS_AUDIENCE}`,
|
|
42
44
|
periodScope: OVERVIEW_PERIOD_SCOPE,
|
|
45
|
+
realtimeTopic: MAILING_SENDS_TOPIC,
|
|
43
46
|
valueFormat: "compact",
|
|
44
47
|
showSparkline: true,
|
|
45
48
|
...extra,
|
|
@@ -104,9 +107,11 @@ const activity = Grid({ gap: GRID_GAP }).child(
|
|
|
104
107
|
* `name` carried by each series goes to ApexCharts verbatim.
|
|
105
108
|
*/
|
|
106
109
|
primaryLabel: "$dms_mailing.metrics.sends",
|
|
107
|
-
|
|
110
|
+
/** Neutral: the toolbar compares with the previous period or year. */
|
|
111
|
+
comparisonLabel: "$dms_mailing.metrics.compared",
|
|
108
112
|
fetchUrl: `${API_BASE_PATH}/metrics/volume`,
|
|
109
113
|
periodScope: OVERVIEW_PERIOD_SCOPE,
|
|
114
|
+
realtimeTopic: MAILING_SENDS_TOPIC,
|
|
110
115
|
chart: ChartArea({
|
|
111
116
|
height: CHART_HEIGHT,
|
|
112
117
|
xaxisType: "datetime",
|
|
@@ -157,11 +162,16 @@ const breakdown = Grid({ gap: GRID_GAP }).child(
|
|
|
157
162
|
title: "$dms_mailing.metrics.ranking",
|
|
158
163
|
fetchUrl: `${API_BASE_PATH}/metrics/top-templates?metric=open-rate`,
|
|
159
164
|
periodScope: OVERVIEW_PERIOD_SCOPE,
|
|
165
|
+
realtimeTopic: MAILING_SENDS_TOPIC,
|
|
160
166
|
valueFormat: "percent",
|
|
161
167
|
showRank: true,
|
|
162
168
|
highlightTopN: 2,
|
|
163
169
|
showBar: true,
|
|
164
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Each row's change against the window the toolbar compares with;
|
|
172
|
+
* "No comparison" leaves the rows without one.
|
|
173
|
+
*/
|
|
174
|
+
showDelta: true,
|
|
165
175
|
skeletonCount: 5,
|
|
166
176
|
emptyLabel: "$dms_mailing.metrics.ranking_empty",
|
|
167
177
|
}),
|
|
@@ -173,9 +183,10 @@ const breakdown = Grid({ gap: GRID_GAP }).child(
|
|
|
173
183
|
description: "$dms_mailing.metrics.domain_sends",
|
|
174
184
|
fetchUrl: `${API_BASE_PATH}/metrics/domains`,
|
|
175
185
|
periodScope: OVERVIEW_PERIOD_SCOPE,
|
|
186
|
+
realtimeTopic: MAILING_SENDS_TOPIC,
|
|
176
187
|
valueFormat: "percent",
|
|
177
188
|
showRank: false,
|
|
178
|
-
showDelta:
|
|
189
|
+
showDelta: true,
|
|
179
190
|
skeletonCount: 5,
|
|
180
191
|
emptyLabel: "$dms_mailing.metrics.ranking_empty",
|
|
181
192
|
}),
|
|
@@ -192,6 +203,7 @@ export class OverviewPageController extends PageController(
|
|
|
192
203
|
module: MODULE_ID,
|
|
193
204
|
category: mailingNavCategory,
|
|
194
205
|
order: OVERVIEW_ORDER,
|
|
206
|
+
live: MAILING_LIVE,
|
|
195
207
|
},
|
|
196
208
|
DefaultLayout({
|
|
197
209
|
fullWidth: true,
|
|
@@ -206,19 +218,11 @@ export class OverviewPageController extends PageController(
|
|
|
206
218
|
],
|
|
207
219
|
}),
|
|
208
220
|
) {
|
|
209
|
-
static header =
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
defaultComparison: "previous-period",
|
|
215
|
-
showProvider: true,
|
|
216
|
-
})
|
|
217
|
-
.meta({
|
|
218
|
-
name: "$dms_mailing.permissions.page_header.name",
|
|
219
|
-
description: "$dms_mailing.permissions.page_header.description",
|
|
220
|
-
icon: "i-ph-plugs-connected",
|
|
221
|
-
});
|
|
221
|
+
static header = mailingToolbar({
|
|
222
|
+
scope: OVERVIEW_PERIOD_SCOPE,
|
|
223
|
+
presets: ["last-7-days", "last-30-days", "last-90-days"],
|
|
224
|
+
defaultPreset: "last-30-days",
|
|
225
|
+
});
|
|
222
226
|
|
|
223
227
|
static dashboard = CustomComponent("MailingOverviewGate")
|
|
224
228
|
.meta({
|
package/src/pages/sends.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { SendModel } from "../db";
|
|
|
23
23
|
import { SendsTableAPI } from "../data/sends-table";
|
|
24
24
|
import { RESOLVED_STAGE, type SendStage } from "../types";
|
|
25
25
|
import { mailingNavCategory } from "./module";
|
|
26
|
+
import { MAILING_LIVE, NEW_SENDS_LABEL, mailingToolbar } from "./toolbar";
|
|
26
27
|
|
|
27
28
|
const SENDS_ORDER = 3;
|
|
28
29
|
const DAY_MS = 86_400_000;
|
|
@@ -187,18 +188,15 @@ export class SendsPageController extends PageController(
|
|
|
187
188
|
module: MODULE_ID,
|
|
188
189
|
category: mailingNavCategory,
|
|
189
190
|
order: SENDS_ORDER,
|
|
191
|
+
live: MAILING_LIVE,
|
|
190
192
|
},
|
|
191
193
|
DefaultLayout({ fullWidth: true }),
|
|
192
194
|
) {
|
|
193
|
-
static header =
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
defaultComparison: "previous-period",
|
|
199
|
-
showProvider: true,
|
|
200
|
-
})
|
|
201
|
-
.meta(permissionMeta("page_header", "i-ph-plugs-connected"));
|
|
195
|
+
static header = mailingToolbar({
|
|
196
|
+
scope: SENDS_PERIOD_SCOPE,
|
|
197
|
+
presets: ["last-24h", "last-7-days", "last-30-days"],
|
|
198
|
+
defaultPreset: "last-24h",
|
|
199
|
+
});
|
|
202
200
|
|
|
203
201
|
static health = Banner({
|
|
204
202
|
fetchUrl: `${API_BASE_PATH}/sends/banner`,
|
|
@@ -226,6 +224,16 @@ export class SendsPageController extends PageController(
|
|
|
226
224
|
searchPlaceholder: "$dms_mailing.sends.search",
|
|
227
225
|
pageSize: 50,
|
|
228
226
|
rowClick: "single",
|
|
227
|
+
/**
|
|
228
|
+
* New sends wait in a "N new sends" strip while the user reads a send,
|
|
229
|
+
* scrolls down the log or searches it, so the rows do not move under them.
|
|
230
|
+
*/
|
|
231
|
+
liveHold: {
|
|
232
|
+
whileRowOpen: true,
|
|
233
|
+
whileScrolled: true,
|
|
234
|
+
whileSearching: true,
|
|
235
|
+
missedLabel: NEW_SENDS_LABEL,
|
|
236
|
+
},
|
|
229
237
|
views: {
|
|
230
238
|
layout: "tabs",
|
|
231
239
|
items: [
|
|
@@ -294,16 +302,24 @@ export class SendsPageController extends PageController(
|
|
|
294
302
|
/**
|
|
295
303
|
* A send is an append-only record: it cannot be edited, so the
|
|
296
304
|
* built-in `edit`/`details` row click has nothing to open. This
|
|
297
|
-
* action takes the row click and opens the send in
|
|
305
|
+
* action takes the row click and opens the send in a drawer on the
|
|
306
|
+
* right, at the DMS's default width (the user's resize is kept).
|
|
298
307
|
*/
|
|
299
308
|
isDefault: true,
|
|
300
309
|
target: {
|
|
301
|
-
type: "
|
|
310
|
+
type: "drawer",
|
|
311
|
+
direction: "right",
|
|
302
312
|
component: CustomComponent("MailingSendDrawer").meta(
|
|
303
313
|
permissionMeta("send_drawer", "i-ph-info"),
|
|
304
314
|
),
|
|
305
315
|
title: "$dms_mailing.sends.actions.details",
|
|
316
|
+
// Names the open send: its recipient and the template it used.
|
|
317
|
+
rowTitle: "$dms_mailing.sends.drawer.heading",
|
|
306
318
|
positionLabel: "$dms_mailing.sends.drawer.position",
|
|
319
|
+
// A side panel keeps the open send in the URL and words a missing
|
|
320
|
+
// one by default; a drawer does both only when asked.
|
|
321
|
+
deepLink: true,
|
|
322
|
+
notFound: {},
|
|
307
323
|
},
|
|
308
324
|
},
|
|
309
325
|
{
|
package/src/pages/templates.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
assertEditable,
|
|
21
21
|
TemplatesTableAPI,
|
|
22
22
|
} from "../data/templates-table";
|
|
23
|
+
import { localeOfRequest } from "../services/localized-text";
|
|
23
24
|
import { mailingNavCategory } from "./module";
|
|
24
25
|
|
|
25
26
|
const TEMPLATES_ORDER = 1;
|
|
@@ -95,13 +96,6 @@ export class TemplatesPageController extends PageController(
|
|
|
95
96
|
filter: { accessorKey: "needsAttention", value: "true", mode: "is" },
|
|
96
97
|
},
|
|
97
98
|
],
|
|
98
|
-
quickFilters: [
|
|
99
|
-
{
|
|
100
|
-
field: "category",
|
|
101
|
-
label: "$dms_mailing.templates.filters.category",
|
|
102
|
-
icon: "i-ph-tag",
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
99
|
footer: {
|
|
106
100
|
countLabel: "$dms_mailing.templates.footer.count",
|
|
107
101
|
hint: "$dms_mailing.templates.footer.hint",
|
|
@@ -115,7 +109,7 @@ export class TemplatesPageController extends PageController(
|
|
|
115
109
|
},
|
|
116
110
|
async edit(ctx, { id, body, current }) {
|
|
117
111
|
await assertEditable(getRequestTenantId(ctx), body, current);
|
|
118
|
-
return afterEdit(this.model, id, body.name);
|
|
112
|
+
return afterEdit(this.model, id, body.name, localeOfRequest(ctx));
|
|
119
113
|
},
|
|
120
114
|
},
|
|
121
115
|
rowActions: {
|
|
@@ -152,6 +146,8 @@ export class TemplatesPageController extends PageController(
|
|
|
152
146
|
deepLink: true,
|
|
153
147
|
target: {
|
|
154
148
|
type: "drawer",
|
|
149
|
+
// On the right, as the gallery opens it (useTemplateDrawer).
|
|
150
|
+
direction: "right",
|
|
155
151
|
component: templateDrawer,
|
|
156
152
|
title: "$dms_mailing.templates.actions.details",
|
|
157
153
|
},
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type PageToolbarProps,
|
|
3
|
+
type PeriodComparison,
|
|
4
|
+
type PeriodPreset,
|
|
5
|
+
pageToolbar,
|
|
6
|
+
} from "@antelopejs/interface-dms/base";
|
|
7
|
+
import type { ComponentBuilder } from "@antelopejs/interface-dms/component";
|
|
8
|
+
import { CustomComponent } from "@antelopejs/interface-dms/base/custom";
|
|
9
|
+
import type { PageLiveOptions } from "@antelopejs/interface-dms/page";
|
|
10
|
+
import { MODULE_ID } from "../constants";
|
|
11
|
+
|
|
12
|
+
/** Shortest interval, in seconds, between two refetches of a live block. */
|
|
13
|
+
const LIVE_MAX_RATE_SECONDS = 5;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Direct / Pause of the pages fed by the sends topic. One `rememberKey` for
|
|
17
|
+
* the module: pausing the Sends log pauses the Overview too.
|
|
18
|
+
*/
|
|
19
|
+
export const MAILING_LIVE: PageLiveOptions = {
|
|
20
|
+
maxRate: LIVE_MAX_RATE_SECONDS,
|
|
21
|
+
rememberKey: MODULE_ID,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** What the paused toggle and a held table call the sends they missed. */
|
|
25
|
+
export const NEW_SENDS_LABEL = "$dms_mailing.live.new_sends";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The comparisons every mailing toolbar offers, in its "vs …" menu: the
|
|
29
|
+
* previous period by default, the same window a year back, or none, which
|
|
30
|
+
* leaves the cards without a delta.
|
|
31
|
+
*/
|
|
32
|
+
export const MAILING_COMPARISONS: PeriodComparison[] = [
|
|
33
|
+
"previous-period",
|
|
34
|
+
"previous-year",
|
|
35
|
+
"none",
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/** The period a page's toolbar offers. */
|
|
39
|
+
export interface MailingToolbarPeriod {
|
|
40
|
+
scope: string;
|
|
41
|
+
presets: PeriodPreset[];
|
|
42
|
+
defaultPreset: PeriodPreset;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The DMS page toolbar of a mailing page: the provider pill on the left, then
|
|
47
|
+
* "updated", Direct / Pause and the period with its comparison menu ("vs …").
|
|
48
|
+
*
|
|
49
|
+
* The page keeps it under its `header` static, the id the former header
|
|
50
|
+
* block had, so a role granting it keeps granting the bar.
|
|
51
|
+
*/
|
|
52
|
+
export function mailingToolbar(
|
|
53
|
+
period: MailingToolbarPeriod,
|
|
54
|
+
): ComponentBuilder<PageToolbarProps> {
|
|
55
|
+
return pageToolbar({
|
|
56
|
+
leading: {
|
|
57
|
+
provider: CustomComponent("MailingProviderPill").meta({
|
|
58
|
+
name: "$dms_mailing.permissions.provider_pill.name",
|
|
59
|
+
description: "$dms_mailing.permissions.provider_pill.description",
|
|
60
|
+
icon: "i-ph-plugs-connected",
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
live: { missedLabel: NEW_SENDS_LABEL },
|
|
64
|
+
period: {
|
|
65
|
+
id: period.scope,
|
|
66
|
+
presets: period.presets,
|
|
67
|
+
defaultPreset: period.defaultPreset,
|
|
68
|
+
// More than one comparison: pageToolbar() draws them as a "vs …" menu
|
|
69
|
+
// on the first one, the previous period.
|
|
70
|
+
comparisons: MAILING_COMPARISONS,
|
|
71
|
+
},
|
|
72
|
+
}).meta({
|
|
73
|
+
name: "$dms_mailing.permissions.page_header.name",
|
|
74
|
+
description: "$dms_mailing.permissions.page_header.description",
|
|
75
|
+
icon: "i-ph-sliders-horizontal",
|
|
76
|
+
});
|
|
77
|
+
}
|
package/src/realtime.ts
CHANGED
|
@@ -31,11 +31,20 @@ export function registerRealtimeTopics(): void {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* A send of `tenantId` was created or changed: the sends table and the
|
|
36
|
+
* Overview / Sends figures of that tenant read again (AntelopeJS/dms#353
|
|
37
|
+
* scopes the message to it; another workspace hears nothing).
|
|
38
|
+
*/
|
|
39
|
+
function publishSendChange(
|
|
40
|
+
type: string,
|
|
41
|
+
tenantId: string,
|
|
42
|
+
sendId: string,
|
|
43
|
+
): void {
|
|
44
|
+
const options = { payload: { ids: [sendId] }, tenantId };
|
|
36
45
|
void Promise.all([
|
|
37
|
-
PublishMessage(SENDS_ROW_TOPIC, type,
|
|
38
|
-
PublishMessage(MAILING_SENDS_TOPIC, type,
|
|
46
|
+
PublishMessage(SENDS_ROW_TOPIC, type, options),
|
|
47
|
+
PublishMessage(MAILING_SENDS_TOPIC, type, options),
|
|
39
48
|
]).catch((error: unknown) => {
|
|
40
49
|
Logging.Error(
|
|
41
50
|
`[dms-mailing] Failed to publish "${type}" for send "${sendId}":`,
|
|
@@ -44,10 +53,10 @@ function publishSendChange(type: string, sendId: string): void {
|
|
|
44
53
|
});
|
|
45
54
|
}
|
|
46
55
|
|
|
47
|
-
export function publishSendCreated(sendId: string): void {
|
|
48
|
-
publishSendChange(SEND_CREATED_TYPE, sendId);
|
|
56
|
+
export function publishSendCreated(tenantId: string, sendId: string): void {
|
|
57
|
+
publishSendChange(SEND_CREATED_TYPE, tenantId, sendId);
|
|
49
58
|
}
|
|
50
59
|
|
|
51
|
-
export function publishSendUpdated(sendId: string): void {
|
|
52
|
-
publishSendChange(SEND_UPDATED_TYPE, sendId);
|
|
60
|
+
export function publishSendUpdated(tenantId: string, sendId: string): void {
|
|
61
|
+
publishSendChange(SEND_UPDATED_TYPE, tenantId, sendId);
|
|
53
62
|
}
|