@antelopejs/dms-mailing 0.6.1 → 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
|
@@ -267,7 +267,7 @@ async function submit(): Promise<void> {
|
|
|
267
267
|
if (!canSubmit.value) return
|
|
268
268
|
saving.value = true
|
|
269
269
|
try {
|
|
270
|
-
const
|
|
270
|
+
const { _id: id } = await api.createTemplate(creationInput())
|
|
271
271
|
close(true)
|
|
272
272
|
if (id) await navigateDms(editorRoute(id))
|
|
273
273
|
} catch (error) {
|
|
@@ -330,7 +330,9 @@ async function submit(): Promise<void> {
|
|
|
330
330
|
:ui="{ base: 'font-mono' }"
|
|
331
331
|
:trailing-icon="slugIssue ? undefined : 'i-ph-check-circle'"
|
|
332
332
|
:aria-invalid="Boolean(slugError)"
|
|
333
|
-
@update:model-value="
|
|
333
|
+
@update:model-value="
|
|
334
|
+
(value: unknown) => onSlugInput(String(value))
|
|
335
|
+
"
|
|
334
336
|
/>
|
|
335
337
|
<UBadge
|
|
336
338
|
color="neutral"
|
|
@@ -36,13 +36,6 @@ const { t } = useI18n()
|
|
|
36
36
|
const { open: openModal } = useModal()
|
|
37
37
|
|
|
38
38
|
const { data: facts, error, status, refresh } = useSetupFacts()
|
|
39
|
-
/**
|
|
40
|
-
* The header's period selector sets up once the dashboard has: a server
|
|
41
|
-
* render, which renders the dashboard once the facts are in, would otherwise
|
|
42
|
-
* set the selector up first and render the cards with a period the browser's
|
|
43
|
-
* cards do not have yet as they hydrate.
|
|
44
|
-
*/
|
|
45
|
-
const DashboardRendered = holdPageHeaderActions()
|
|
46
39
|
const isResumed = ref(false)
|
|
47
40
|
|
|
48
41
|
/** A failure shows once its retry is over; the facts read last stay meanwhile. */
|
|
@@ -207,5 +200,4 @@ function createTemplate(): void {
|
|
|
207
200
|
|
|
208
201
|
<slot v-if="showsDashboard" />
|
|
209
202
|
</div>
|
|
210
|
-
<DashboardRendered />
|
|
211
203
|
</template>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useProviderInfo } from '../composables/useMailingReads'
|
|
3
|
+
import type { MailingComponentProps } from '../types/component'
|
|
4
|
+
import type { ProviderInfo } from '../types/mailing'
|
|
5
|
+
|
|
6
|
+
interface ProviderPillProps extends MailingComponentProps {
|
|
7
|
+
showProvider?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
defineOptions({ inheritAttrs: false })
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<ProviderPillProps>(), {
|
|
13
|
+
showProvider: true,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const { t } = useI18n()
|
|
17
|
+
|
|
18
|
+
/** Shown as "no provider" when the read fails. */
|
|
19
|
+
const NO_PROVIDER: ProviderInfo = { name: '', connected: false, features: null }
|
|
20
|
+
|
|
21
|
+
const providerRead = useProviderInfo(() => props.showProvider)
|
|
22
|
+
const provider = computed<ProviderInfo | null>(() =>
|
|
23
|
+
providerRead.error.value ? NO_PROVIDER : providerRead.data.value,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
/** The leading child of the page toolbar: is a provider carrying the mail. */
|
|
27
|
+
const pill = computed(() =>
|
|
28
|
+
provider.value?.connected
|
|
29
|
+
? {
|
|
30
|
+
tone: 'success',
|
|
31
|
+
dot: 'live',
|
|
32
|
+
label: t('dms_mailing.page_header.provider_connected', {
|
|
33
|
+
name: provider.value.name,
|
|
34
|
+
}),
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
tone: 'warning',
|
|
38
|
+
dot: 'static',
|
|
39
|
+
label: t('dms_mailing.page_header.no_provider'),
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<DmsStatusPill
|
|
46
|
+
v-if="props.showProvider && provider"
|
|
47
|
+
v-bind="pill"
|
|
48
|
+
:title="t('dms_mailing.page_header.provider_hint')"
|
|
49
|
+
/>
|
|
50
|
+
</template>
|
|
@@ -412,208 +412,231 @@ onMounted(() => {
|
|
|
412
412
|
|
|
413
413
|
<template>
|
|
414
414
|
<!--
|
|
415
|
-
|
|
416
|
-
|
|
415
|
+
The drawer's body is the scroll container and sets the inset: the send
|
|
416
|
+
adds no padding of its own. It grows in the body's flex column, so the
|
|
417
|
+
footer sits at the bottom of the drawer, and sticks there once the send
|
|
418
|
+
scrolls.
|
|
417
419
|
-->
|
|
418
|
-
<div class="flex
|
|
419
|
-
<USkeleton v-if="loading && !send" class="
|
|
420
|
+
<div class="@container flex flex-1 flex-col gap-6" data-send-drawer>
|
|
421
|
+
<USkeleton v-if="loading && !send" class="h-64" />
|
|
420
422
|
|
|
421
423
|
<template v-else-if="send">
|
|
422
|
-
<
|
|
423
|
-
<
|
|
424
|
-
|
|
424
|
+
<header class="border-default flex flex-col gap-1.5 border-b pb-4">
|
|
425
|
+
<div class="flex items-center gap-1">
|
|
426
|
+
<DmsEyebrow
|
|
427
|
+
class="min-w-0 flex-1"
|
|
428
|
+
truncate
|
|
429
|
+
:label="key('eyebrow', { id: shortSendId(send._id) })"
|
|
430
|
+
/>
|
|
431
|
+
<UButton
|
|
432
|
+
icon="i-ph-link"
|
|
433
|
+
color="neutral"
|
|
434
|
+
variant="ghost"
|
|
435
|
+
size="sm"
|
|
436
|
+
:aria-label="key('copy_link')"
|
|
437
|
+
@click="copyLink"
|
|
438
|
+
/>
|
|
439
|
+
</div>
|
|
440
|
+
<h3
|
|
441
|
+
class="text-highlighted flex flex-wrap items-center gap-2 text-lg font-semibold"
|
|
425
442
|
>
|
|
426
|
-
<
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
443
|
+
<span class="min-w-0 truncate">
|
|
444
|
+
{{ send.recipientName || send.recipientEmail }}
|
|
445
|
+
</span>
|
|
446
|
+
<DmsStatusPill
|
|
447
|
+
:tone="SEND_STATUS_TONES[send.status]"
|
|
448
|
+
:label="t(`dms_mailing.sends.status.${send.status}`)"
|
|
449
|
+
/>
|
|
450
|
+
<DmsStatusPill
|
|
451
|
+
v-if="send.isTest"
|
|
452
|
+
tone="warning"
|
|
453
|
+
dot="none"
|
|
454
|
+
size="sm"
|
|
455
|
+
uppercase
|
|
456
|
+
:label="key('test')"
|
|
457
|
+
/>
|
|
458
|
+
</h3>
|
|
459
|
+
<!-- The address gives way first: the date drops to its own line
|
|
460
|
+
rather than being cut off with it. -->
|
|
461
|
+
<p
|
|
462
|
+
class="text-muted flex flex-wrap gap-x-1.5 text-[12.5px]"
|
|
463
|
+
data-send-recipient
|
|
464
|
+
>
|
|
465
|
+
<span class="min-w-0 truncate font-mono" :title="send.recipientEmail">
|
|
466
|
+
{{ send.recipientEmail }}
|
|
467
|
+
</span>
|
|
468
|
+
<span class="whitespace-nowrap">
|
|
469
|
+
· {{ dateOf(send.createdAt, DATE_FORMAT) }}
|
|
470
|
+
</span>
|
|
471
|
+
</p>
|
|
472
|
+
</header>
|
|
473
|
+
|
|
474
|
+
<section class="flex flex-col gap-3">
|
|
475
|
+
<!-- Three short values: side by side from a 28rem group (a 600px
|
|
476
|
+
drawer) rather than the DmsStatGroup's own 36rem (dms#399). -->
|
|
477
|
+
<DmsStatGroup
|
|
478
|
+
layout="joined"
|
|
479
|
+
:columns="3"
|
|
480
|
+
columns-from="md"
|
|
481
|
+
:items="stats"
|
|
482
|
+
/>
|
|
483
|
+
<DmsBanner
|
|
484
|
+
v-if="problem"
|
|
485
|
+
:tone="isResolved ? 'info' : problemTone(problem.kind)"
|
|
486
|
+
:size="isResolved ? 'sm' : undefined"
|
|
487
|
+
:icon="problem.icon"
|
|
488
|
+
:title="t(problem.titleKey, problem.params)"
|
|
489
|
+
:description="
|
|
490
|
+
isResolved ? undefined : t(problem.descriptionKey, problem.params)
|
|
491
|
+
"
|
|
492
|
+
data-problem-banner
|
|
493
|
+
/>
|
|
494
|
+
<DmsBanner
|
|
495
|
+
v-if="suppression"
|
|
496
|
+
:tone="suppression.tone"
|
|
497
|
+
size="sm"
|
|
498
|
+
:icon="suppression.icon"
|
|
499
|
+
:title="t(suppression.titleKey, suppression.params)"
|
|
500
|
+
>
|
|
501
|
+
<template #description>
|
|
502
|
+
<span
|
|
503
|
+
class="text-highlighted block truncate font-mono text-[12px]"
|
|
504
|
+
:title="suppression.params.email"
|
|
505
|
+
data-suppressed-address
|
|
506
|
+
>
|
|
507
|
+
{{ suppression.params.email }}
|
|
508
|
+
</span>
|
|
509
|
+
{{ t(suppression.descriptionKey, suppression.params) }}
|
|
510
|
+
</template>
|
|
511
|
+
<template v-if="suppression.hasListLink" #actions>
|
|
432
512
|
<UButton
|
|
433
|
-
|
|
513
|
+
size="xs"
|
|
434
514
|
color="neutral"
|
|
435
|
-
variant="
|
|
436
|
-
|
|
437
|
-
:
|
|
438
|
-
@click="
|
|
439
|
-
/>
|
|
440
|
-
</div>
|
|
441
|
-
<h3
|
|
442
|
-
class="text-highlighted flex flex-wrap items-center gap-2 text-lg font-semibold"
|
|
443
|
-
>
|
|
444
|
-
<span class="min-w-0 truncate">
|
|
445
|
-
{{ send.recipientName || send.recipientEmail }}
|
|
446
|
-
</span>
|
|
447
|
-
<DmsStatusPill
|
|
448
|
-
:tone="SEND_STATUS_TONES[send.status]"
|
|
449
|
-
:label="t(`dms_mailing.sends.status.${send.status}`)"
|
|
515
|
+
variant="outline"
|
|
516
|
+
trailing-icon="i-ph-arrow-right"
|
|
517
|
+
:label="t('dms_mailing.suppressions.notice.open_list')"
|
|
518
|
+
@click="openSuppressionList"
|
|
450
519
|
/>
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
v-if="problem"
|
|
471
|
-
:tone="isResolved ? 'info' : problemTone(problem.kind)"
|
|
472
|
-
:size="isResolved ? 'sm' : undefined"
|
|
473
|
-
:icon="problem.icon"
|
|
474
|
-
:title="t(problem.titleKey, problem.params)"
|
|
475
|
-
:description="
|
|
476
|
-
isResolved
|
|
477
|
-
? undefined
|
|
478
|
-
: t(problem.descriptionKey, problem.params)
|
|
479
|
-
"
|
|
480
|
-
data-problem-banner
|
|
520
|
+
</template>
|
|
521
|
+
</DmsBanner>
|
|
522
|
+
<DmsBanner
|
|
523
|
+
v-for="link in linkedSends"
|
|
524
|
+
:key="link.id"
|
|
525
|
+
tone="info"
|
|
526
|
+
size="sm"
|
|
527
|
+
icon="i-ph-arrow-clockwise"
|
|
528
|
+
:title="link.text"
|
|
529
|
+
>
|
|
530
|
+
<template #actions>
|
|
531
|
+
<UButton
|
|
532
|
+
v-if="showRow"
|
|
533
|
+
size="xs"
|
|
534
|
+
color="neutral"
|
|
535
|
+
variant="outline"
|
|
536
|
+
trailing-icon="i-ph-arrow-right"
|
|
537
|
+
:label="key('see_send')"
|
|
538
|
+
@click="void showSend(link.id)"
|
|
481
539
|
/>
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
540
|
+
</template>
|
|
541
|
+
</DmsBanner>
|
|
542
|
+
</section>
|
|
543
|
+
|
|
544
|
+
<!--
|
|
545
|
+
Side by side once the drawer is resized wide:
|
|
546
|
+
both are short lists, and the data sent below keeps the full width.
|
|
547
|
+
-->
|
|
548
|
+
<div class="@2xl:grid-cols-2 grid gap-6" data-send-details-grid>
|
|
549
|
+
<section class="flex min-w-0 flex-col gap-2">
|
|
550
|
+
<DmsEyebrow :label="key('timeline')" />
|
|
551
|
+
<ol class="flex flex-col">
|
|
552
|
+
<li
|
|
553
|
+
v-for="item in timeline"
|
|
554
|
+
:key="item.id"
|
|
555
|
+
:class="item.isPending && 'opacity-50'"
|
|
488
556
|
>
|
|
489
|
-
<
|
|
557
|
+
<DmsActivityItem
|
|
558
|
+
:icon="item.icon"
|
|
559
|
+
:icon-color="item.isPending ? 'neutral' : item.tone"
|
|
560
|
+
:subtitle="
|
|
561
|
+
item.isPending ? key('pending') : item.detail || undefined
|
|
562
|
+
"
|
|
563
|
+
:mono="item.isDetailMono && !item.isPending"
|
|
564
|
+
:trailing="stepTime(item)"
|
|
565
|
+
:interactive="false"
|
|
566
|
+
wrap
|
|
567
|
+
>
|
|
490
568
|
<span
|
|
491
|
-
class="
|
|
492
|
-
|
|
493
|
-
|
|
569
|
+
:class="
|
|
570
|
+
item.tone === 'error' && !item.isPending
|
|
571
|
+
? 'text-error'
|
|
572
|
+
: undefined
|
|
573
|
+
"
|
|
494
574
|
>
|
|
495
|
-
{{
|
|
575
|
+
{{ stepTitle(item) }}
|
|
496
576
|
</span>
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
v-for="link in linkedSends"
|
|
512
|
-
:key="link.id"
|
|
513
|
-
tone="info"
|
|
577
|
+
</DmsActivityItem>
|
|
578
|
+
</li>
|
|
579
|
+
</ol>
|
|
580
|
+
</section>
|
|
581
|
+
|
|
582
|
+
<section class="flex min-w-0 flex-col gap-2">
|
|
583
|
+
<DmsEyebrow :label="key('origin')" />
|
|
584
|
+
<div
|
|
585
|
+
class="border-default divide-default divide-y overflow-hidden rounded-lg border"
|
|
586
|
+
>
|
|
587
|
+
<DmsListRow
|
|
588
|
+
:to="editorLink"
|
|
589
|
+
icon="i-ph-envelope-simple"
|
|
590
|
+
icon-size="2xs"
|
|
514
591
|
size="sm"
|
|
515
|
-
|
|
516
|
-
:title="link.text"
|
|
592
|
+
:title="originTitle"
|
|
517
593
|
>
|
|
518
|
-
<template #
|
|
519
|
-
<
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
variant="outline"
|
|
524
|
-
trailing-icon="i-ph-arrow-right"
|
|
525
|
-
:label="key('see_send')"
|
|
526
|
-
@click="void showSend(link.id)"
|
|
594
|
+
<template #trailing>
|
|
595
|
+
<UIcon
|
|
596
|
+
name="i-ph-arrow-square-out"
|
|
597
|
+
class="text-dimmed size-4"
|
|
598
|
+
aria-hidden="true"
|
|
527
599
|
/>
|
|
528
600
|
</template>
|
|
529
|
-
</
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
<
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
<DmsActivityItem
|
|
541
|
-
:icon="item.icon"
|
|
542
|
-
:icon-color="item.isPending ? 'neutral' : item.tone"
|
|
543
|
-
:subtitle="
|
|
544
|
-
item.isPending ? key('pending') : item.detail || undefined
|
|
545
|
-
"
|
|
546
|
-
:mono="item.isDetailMono && !item.isPending"
|
|
547
|
-
:trailing="stepTime(item)"
|
|
548
|
-
:interactive="false"
|
|
549
|
-
>
|
|
550
|
-
<span
|
|
551
|
-
:class="
|
|
552
|
-
item.tone === 'error' && !item.isPending
|
|
553
|
-
? 'text-error'
|
|
554
|
-
: undefined
|
|
555
|
-
"
|
|
556
|
-
>
|
|
557
|
-
{{ stepTitle(item) }}
|
|
558
|
-
</span>
|
|
559
|
-
</DmsActivityItem>
|
|
560
|
-
</li>
|
|
561
|
-
</ol>
|
|
562
|
-
</section>
|
|
563
|
-
|
|
564
|
-
<section class="flex flex-col gap-2">
|
|
565
|
-
<DmsEyebrow :label="key('origin')" />
|
|
566
|
-
<div
|
|
567
|
-
class="border-default divide-default divide-y overflow-hidden rounded-lg border"
|
|
601
|
+
</DmsListRow>
|
|
602
|
+
<!-- The "source" label goes under the value: beside it, a dotted
|
|
603
|
+
name like `automation.overdue-reminders` broke at its hyphen
|
|
604
|
+
against the label in a narrow drawer. -->
|
|
605
|
+
<DmsListRow
|
|
606
|
+
v-if="send.source"
|
|
607
|
+
icon="i-ph-code"
|
|
608
|
+
icon-size="2xs"
|
|
609
|
+
size="sm"
|
|
610
|
+
:meta="[key('source')]"
|
|
611
|
+
data-send-source
|
|
568
612
|
>
|
|
569
|
-
<
|
|
570
|
-
|
|
571
|
-
icon="i-ph-envelope-simple"
|
|
572
|
-
icon-size="2xs"
|
|
573
|
-
size="sm"
|
|
574
|
-
:title="originTitle"
|
|
575
|
-
>
|
|
576
|
-
<template #trailing>
|
|
577
|
-
<UIcon
|
|
578
|
-
name="i-ph-arrow-square-out"
|
|
579
|
-
class="text-dimmed size-4"
|
|
580
|
-
aria-hidden="true"
|
|
581
|
-
/>
|
|
582
|
-
</template>
|
|
583
|
-
</DmsListRow>
|
|
584
|
-
<DmsListRow
|
|
585
|
-
v-if="send.source"
|
|
586
|
-
icon="i-ph-code"
|
|
587
|
-
icon-size="2xs"
|
|
588
|
-
size="sm"
|
|
589
|
-
:trailing="key('source')"
|
|
613
|
+
<span
|
|
614
|
+
class="min-w-0 font-mono text-[12.5px] [overflow-wrap:anywhere]"
|
|
590
615
|
>
|
|
591
|
-
|
|
592
|
-
</
|
|
593
|
-
</
|
|
594
|
-
</
|
|
595
|
-
|
|
596
|
-
<section class="flex flex-col gap-2">
|
|
597
|
-
<div class="flex items-center justify-between gap-2">
|
|
598
|
-
<DmsEyebrow :label="key('payload')" />
|
|
599
|
-
<UButton
|
|
600
|
-
v-if="payload"
|
|
601
|
-
size="xs"
|
|
602
|
-
variant="link"
|
|
603
|
-
:label="key('copy_json')"
|
|
604
|
-
@click="copyPayload"
|
|
605
|
-
/>
|
|
606
|
-
</div>
|
|
607
|
-
<pre
|
|
608
|
-
class="bg-elevated/50 border-default text-toned max-h-72 overflow-auto rounded-lg border p-3 font-mono text-[11.5px] leading-relaxed"
|
|
609
|
-
>{{ payload || '{}' }}</pre>
|
|
610
|
-
</section>
|
|
611
|
-
</div>
|
|
616
|
+
{{ send.source }}
|
|
617
|
+
</span>
|
|
618
|
+
</DmsListRow>
|
|
619
|
+
</div>
|
|
620
|
+
</section>
|
|
612
621
|
</div>
|
|
613
622
|
|
|
614
|
-
<
|
|
615
|
-
class="
|
|
616
|
-
|
|
623
|
+
<section class="flex flex-col gap-2">
|
|
624
|
+
<div class="flex items-center justify-between gap-2">
|
|
625
|
+
<DmsEyebrow :label="key('payload')" />
|
|
626
|
+
<UButton
|
|
627
|
+
v-if="payload"
|
|
628
|
+
size="xs"
|
|
629
|
+
variant="link"
|
|
630
|
+
:label="key('copy_json')"
|
|
631
|
+
@click="copyPayload"
|
|
632
|
+
/>
|
|
633
|
+
</div>
|
|
634
|
+
<pre
|
|
635
|
+
class="bg-elevated/50 border-default text-toned max-h-72 overflow-auto rounded-lg border p-3 font-mono text-[11.5px] leading-relaxed"
|
|
636
|
+
>{{ payload || '{}' }}</pre>
|
|
637
|
+
</section>
|
|
638
|
+
|
|
639
|
+
<DmsContainerFooter data-send-footer>
|
|
617
640
|
<UButton
|
|
618
641
|
v-for="(action, index) in actions"
|
|
619
642
|
:key="action.id"
|
|
@@ -628,7 +651,7 @@ onMounted(() => {
|
|
|
628
651
|
:loading="action.id === 'send_again' && sending"
|
|
629
652
|
@click="ACTION_HANDLERS[action.id]()"
|
|
630
653
|
/>
|
|
631
|
-
</
|
|
654
|
+
</DmsContainerFooter>
|
|
632
655
|
</template>
|
|
633
656
|
</div>
|
|
634
657
|
</template>
|
|
@@ -56,7 +56,7 @@ async function createFrom(starter: StarterSummary): Promise<void> {
|
|
|
56
56
|
if (creatingId.value) return
|
|
57
57
|
creatingId.value = starter.id
|
|
58
58
|
try {
|
|
59
|
-
const
|
|
59
|
+
const { _id: id } = await api.createTemplate({
|
|
60
60
|
name: starter.name,
|
|
61
61
|
slug: starter.slug,
|
|
62
62
|
category: knownCategory(starter.category),
|
|
@@ -17,6 +17,9 @@ import type {
|
|
|
17
17
|
TemplateStats,
|
|
18
18
|
TemplateStatus,
|
|
19
19
|
} from '../types/mailing'
|
|
20
|
+
// Imported rather than resolved by name: every module component is a lazy
|
|
21
|
+
// chunk, and a card would first render without its thumbnail.
|
|
22
|
+
import TemplatePreviewFrame from './TemplatePreviewFrame.vue'
|
|
20
23
|
|
|
21
24
|
interface TemplateCardProps {
|
|
22
25
|
template: TemplateRow
|
|
@@ -172,7 +175,7 @@ function chipLabel(code: string, present: boolean): string {
|
|
|
172
175
|
class="bg-elevated/40 relative flex h-[172px] justify-center overflow-hidden bg-[radial-gradient(var(--ui-border)_1px,transparent_1px)] bg-[length:14px_14px] pt-4"
|
|
173
176
|
>
|
|
174
177
|
<div class="pointer-events-none rounded-t-md shadow-sm">
|
|
175
|
-
<
|
|
178
|
+
<TemplatePreviewFrame
|
|
176
179
|
:template-id="template._id"
|
|
177
180
|
:version="previewVersion"
|
|
178
181
|
:width="PREVIEW_WIDTH"
|
|
@@ -513,7 +513,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
513
513
|
</script>
|
|
514
514
|
|
|
515
515
|
<template>
|
|
516
|
-
<div class="flex w-[30rem] max-w-full flex-col">
|
|
516
|
+
<div class="flex w-[30rem] max-w-full flex-1 flex-col">
|
|
517
517
|
<USkeleton v-if="loading && !template" class="h-64 w-full" />
|
|
518
518
|
|
|
519
519
|
<template v-else-if="template">
|
|
@@ -882,9 +882,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
882
882
|
/>
|
|
883
883
|
</div>
|
|
884
884
|
|
|
885
|
-
<
|
|
886
|
-
class="border-default bg-default sticky bottom-0 -mx-1 mt-auto flex items-center gap-2 border-t px-1 py-3"
|
|
887
|
-
>
|
|
885
|
+
<DmsContainerFooter data-template-footer>
|
|
888
886
|
<UButton
|
|
889
887
|
icon="i-ph-flask"
|
|
890
888
|
color="neutral"
|
|
@@ -905,7 +903,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
905
903
|
:label="t('dms_mailing.templates.actions.open_editor')"
|
|
906
904
|
@click="openEditor(locale)"
|
|
907
905
|
/>
|
|
908
|
-
</
|
|
906
|
+
</DmsContainerFooter>
|
|
909
907
|
</template>
|
|
910
908
|
</div>
|
|
911
909
|
</template>
|