@antelopejs/dms-mailing 0.9.2 → 0.10.1
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/README.md +11 -11
- package/dist/data/read-only-routes.d.ts +14 -1
- package/dist/data/sends-table.d.ts +14 -1
- package/dist/data/suppressions-table.d.ts +14 -1
- package/dist/data/templates-table.d.ts +15 -2
- package/dist/db/models/send-events.model.d.ts +3 -3
- package/dist/db/models/sends.model.d.ts +3 -3
- package/dist/db/models/settings.model.d.ts +3 -3
- package/dist/db/models/suppressions.model.d.ts +3 -3
- package/dist/db/models/template-versions.model.d.ts +3 -3
- package/dist/db/models/template-versions.model.js +2 -3
- package/dist/db/models/template-versions.model.js.map +1 -1
- package/dist/db/models/templates.model.d.ts +4 -3
- package/dist/db/models/templates.model.js +14 -6
- package/dist/db/models/templates.model.js.map +1 -1
- package/dist/db/models/webhook-status.model.d.ts +3 -3
- package/dist/db/tables/templates.table.d.ts +4 -1
- package/dist/hooks/tenant-export.js +5 -1
- package/dist/hooks/tenant-export.js.map +1 -1
- package/dist/pages/editor.js +1 -1
- package/dist/pages/editor.js.map +1 -1
- package/dist/routes/sends.js +2 -1
- package/dist/routes/sends.js.map +1 -1
- package/dist/routes/templates.d.ts +2 -2
- package/dist/routes/templates.js +2 -2
- package/dist/routes/templates.js.map +1 -1
- package/dist/services/settings.js +2 -1
- package/dist/services/settings.js.map +1 -1
- package/dist/services/templates.js +10 -8
- package/dist/services/templates.js.map +1 -1
- package/dist/validation/declarations.schema.d.ts +1 -1
- package/dist/validation/stored.schema.d.ts +12 -0
- package/dist/validation/stored.schema.js +198 -0
- package/dist/validation/stored.schema.js.map +1 -0
- package/frontend-vue/app/build/components/FormFieldRow.vue +1 -1
- package/frontend-vue/app/build/components/TemplateDrawerPerformance.vue +3 -3
- package/frontend-vue/app/build/components/TemplateDrawerVariables.vue +4 -4
- package/frontend-vue/app/components/CategoriesInput.vue +2 -2
- package/frontend-vue/app/components/CategoryInput.vue +1 -1
- package/frontend-vue/app/components/FunnelCard.vue +4 -4
- package/frontend-vue/app/components/LocaleSelect.vue +1 -1
- package/frontend-vue/app/components/NewTemplateModal.vue +1 -1
- package/frontend-vue/app/components/OverviewGate.vue +6 -6
- package/frontend-vue/app/components/RealSendModal.vue +6 -6
- package/frontend-vue/app/components/RenderingModal.vue +2 -2
- package/frontend-vue/app/components/RetentionInput.vue +1 -1
- package/frontend-vue/app/components/SendDrawer.vue +9 -9
- package/frontend-vue/app/components/StarterGallery.vue +2 -2
- package/frontend-vue/app/components/SystemVariables.vue +1 -1
- package/frontend-vue/app/components/TemplateCard.vue +2 -2
- package/frontend-vue/app/components/TemplateDrawer.vue +4 -4
- package/frontend-vue/app/components/TemplateGallery.vue +2 -2
- package/frontend-vue/app/components/TestSendModal.vue +4 -4
- package/frontend-vue/app/components/VersionPreview.vue +3 -3
- package/frontend-vue/app/components/editor/EditorBlockFields.vue +1 -1
- package/frontend-vue/app/components/editor/EditorCompare.vue +1 -1
- package/frontend-vue/app/components/editor/EditorEnvelope.vue +2 -2
- package/frontend-vue/app/components/editor/EditorLifecycle.vue +1 -1
- package/frontend-vue/app/components/editor/EditorPaletteGroup.vue +1 -1
- package/frontend-vue/app/components/editor/EditorPublishReview.vue +4 -4
- package/frontend-vue/app/components/editor/EditorTemplatePane.vue +2 -2
- package/frontend-vue/app/components/editor/EditorTestDataPane.vue +1 -1
- package/frontend-vue/app/components/editor/EditorVariablesPane.vue +1 -1
- package/frontend-vue/app/components/editor/EditorViewSwitches.vue +2 -2
- package/frontend-vue/app/components/editor/EditorVisibility.vue +1 -1
- package/frontend-vue/app/composables/useEditorContext.ts +1 -1
- package/frontend-vue/build/backend-boundary.ts +7 -2
- package/frontend-vue/dms.frontend.ts +1 -4
- package/frontend-vue/eslint.config.mjs +9 -1
- package/frontend-vue/package.json +2 -2
- package/frontend-vue/pnpm-lock.yaml +24 -7
- package/frontend-vue/tests/backend-boundary.test.ts +4 -4
- package/frontend-vue/tests/categories-input.test.ts +1 -1
- package/frontend-vue/tests/column-displays.test.ts +13 -12
- package/frontend-vue/tests/dms-component-names.test.ts +12 -0
- package/frontend-vue/tests/footer-block.test.ts +1 -1
- package/frontend-vue/tests/funnel-card.test.ts +3 -3
- package/frontend-vue/tests/helpers/component-tags.ts +58 -0
- package/frontend-vue/tests/helpers/nuxt-globals.ts +1 -1
- package/frontend-vue/tests/overview-gate.test.ts +1 -1
- package/frontend-vue/tests/retention-input.test.ts +2 -2
- package/frontend-vue/tests/send-drawer.test.ts +2 -2
- package/frontend-vue/tests/send-modals.test.ts +1 -1
- package/frontend-vue/tests/template-drawer.test.ts +6 -6
- package/frontend-vue/tests/template-gallery-display.test.ts +6 -5
- package/frontend-vue/tests/template-gallery.test.ts +3 -3
- package/frontend-vue/tests/template-overlays.test.ts +4 -12
- package/package.json +16 -16
- package/src/db/models/template-versions.model.ts +8 -6
- package/src/db/models/templates.model.ts +25 -9
- package/src/hooks/tenant-export.ts +5 -2
- package/src/pages/editor.ts +1 -1
- package/src/routes/sends.ts +2 -3
- package/src/routes/templates.ts +3 -2
- package/src/services/settings.ts +9 -3
- package/src/services/templates.ts +35 -16
- package/src/validation/stored.schema.ts +257 -0
- /package/frontend-vue/app/components/{Editor.vue → TemplateEditor.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Button.vue → BlockButton.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Code.vue → BlockCode.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Divider.vue → BlockDivider.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Footer.vue → BlockFooter.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Heading.vue → BlockHeading.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Hero.vue → BlockHero.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{List.vue → BlockList.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Paragraph.vue → BlockParagraph.vue} +0 -0
- /package/frontend-vue/app/components/blocks/{Total.vue → BlockTotal.vue} +0 -0
|
@@ -83,7 +83,7 @@ function createTemplate(): void {
|
|
|
83
83
|
</script>
|
|
84
84
|
|
|
85
85
|
<template>
|
|
86
|
-
<
|
|
86
|
+
<DmsCardPanel
|
|
87
87
|
v-if="state === 'loading' && !facts"
|
|
88
88
|
:padded="false"
|
|
89
89
|
aria-busy="true"
|
|
@@ -92,16 +92,16 @@ function createTemplate(): void {
|
|
|
92
92
|
<USkeleton class="h-6 w-56" />
|
|
93
93
|
<USkeleton v-for="row in SKELETON_ROWS" :key="row" class="h-10 w-full" />
|
|
94
94
|
</div>
|
|
95
|
-
</
|
|
95
|
+
</DmsCardPanel>
|
|
96
96
|
|
|
97
|
-
<
|
|
97
|
+
<DmsCardPanel v-else-if="state === 'error' && !facts" :padded="false">
|
|
98
98
|
<DmsEmptyState
|
|
99
99
|
variant="error"
|
|
100
100
|
:title="key('error_title')"
|
|
101
101
|
:description="key('error_description')"
|
|
102
102
|
:actions="errorActions"
|
|
103
103
|
/>
|
|
104
|
-
</
|
|
104
|
+
</DmsCardPanel>
|
|
105
105
|
|
|
106
106
|
<div v-else class="flex flex-col gap-4">
|
|
107
107
|
<div v-if="canResume" class="flex justify-end">
|
|
@@ -115,7 +115,7 @@ function createTemplate(): void {
|
|
|
115
115
|
/>
|
|
116
116
|
</div>
|
|
117
117
|
|
|
118
|
-
<
|
|
118
|
+
<DmsCardPanel v-if="showsSetup" :padded="false">
|
|
119
119
|
<div
|
|
120
120
|
class="grid gap-8 p-6 sm:p-8 lg:grid-cols-[minmax(0,1fr)_minmax(0,1.1fr)]"
|
|
121
121
|
>
|
|
@@ -196,7 +196,7 @@ function createTemplate(): void {
|
|
|
196
196
|
</li>
|
|
197
197
|
</ol>
|
|
198
198
|
</div>
|
|
199
|
-
</
|
|
199
|
+
</DmsCardPanel>
|
|
200
200
|
|
|
201
201
|
<slot v-if="showsDashboard" />
|
|
202
202
|
</div>
|
|
@@ -345,7 +345,7 @@ async function submit(): Promise<void> {
|
|
|
345
345
|
novalidate
|
|
346
346
|
@submit.prevent="submit"
|
|
347
347
|
>
|
|
348
|
-
<
|
|
348
|
+
<DmsNoticeBanner
|
|
349
349
|
tone="error"
|
|
350
350
|
icon="i-ph-paper-plane-right"
|
|
351
351
|
:title="
|
|
@@ -362,7 +362,7 @@ async function submit(): Promise<void> {
|
|
|
362
362
|
|
|
363
363
|
<FormFieldRow :label="t('dms_mailing.test_send.locale')">
|
|
364
364
|
<template #default="{ id }">
|
|
365
|
-
<
|
|
365
|
+
<DmsInputSelect
|
|
366
366
|
:id="id"
|
|
367
367
|
v-model="locale"
|
|
368
368
|
:items="localeItems"
|
|
@@ -420,7 +420,7 @@ async function submit(): Promise<void> {
|
|
|
420
420
|
</p>
|
|
421
421
|
</section>
|
|
422
422
|
|
|
423
|
-
<
|
|
423
|
+
<DmsNoticeBanner
|
|
424
424
|
v-if="detail && !hasSender"
|
|
425
425
|
tone="warning"
|
|
426
426
|
icon="i-ph-at"
|
|
@@ -437,7 +437,7 @@ async function submit(): Promise<void> {
|
|
|
437
437
|
@click="openSettings"
|
|
438
438
|
/>
|
|
439
439
|
</template>
|
|
440
|
-
</
|
|
440
|
+
</DmsNoticeBanner>
|
|
441
441
|
|
|
442
442
|
<div class="border-default rounded-lg border px-3">
|
|
443
443
|
<DmsKeyValueList
|
|
@@ -478,7 +478,7 @@ async function submit(): Promise<void> {
|
|
|
478
478
|
</DmsKeyValueList>
|
|
479
479
|
</div>
|
|
480
480
|
|
|
481
|
-
<
|
|
481
|
+
<DmsInputCheckbox
|
|
482
482
|
v-model="isAcknowledged"
|
|
483
483
|
:disabled="!recipients.length"
|
|
484
484
|
:label="
|
|
@@ -517,7 +517,7 @@ async function submit(): Promise<void> {
|
|
|
517
517
|
</form>
|
|
518
518
|
|
|
519
519
|
<div v-else class="flex flex-col gap-4">
|
|
520
|
-
<
|
|
520
|
+
<DmsNoticeBanner
|
|
521
521
|
tone="warning"
|
|
522
522
|
icon="i-ph-paper-plane-right"
|
|
523
523
|
:title="
|
|
@@ -116,7 +116,7 @@ onMounted(load)
|
|
|
116
116
|
{{ key('description') }}
|
|
117
117
|
<span class="font-mono text-[12px]">{{ sendIdValue }}</span>
|
|
118
118
|
</p>
|
|
119
|
-
<
|
|
119
|
+
<DmsSegmentedControl
|
|
120
120
|
v-model="view"
|
|
121
121
|
size="xs"
|
|
122
122
|
:items="viewItems"
|
|
@@ -138,7 +138,7 @@ onMounted(load)
|
|
|
138
138
|
/>
|
|
139
139
|
|
|
140
140
|
<template v-else>
|
|
141
|
-
<
|
|
141
|
+
<DmsNoticeBanner
|
|
142
142
|
v-if="rendering.approximate"
|
|
143
143
|
tone="warning"
|
|
144
144
|
size="sm"
|
|
@@ -424,7 +424,7 @@ onMounted(() => {
|
|
|
424
424
|
<template v-else-if="send">
|
|
425
425
|
<header class="border-default flex flex-col gap-1.5 border-b pb-4">
|
|
426
426
|
<div class="flex items-center gap-1">
|
|
427
|
-
<
|
|
427
|
+
<DmsEyebrowLabel
|
|
428
428
|
class="min-w-0 flex-1"
|
|
429
429
|
truncate
|
|
430
430
|
:label="key('eyebrow', { id: shortSendId(send._id) })"
|
|
@@ -481,7 +481,7 @@ onMounted(() => {
|
|
|
481
481
|
columns-from="md"
|
|
482
482
|
:items="stats"
|
|
483
483
|
/>
|
|
484
|
-
<
|
|
484
|
+
<DmsNoticeBanner
|
|
485
485
|
v-if="problem"
|
|
486
486
|
:tone="isResolved ? 'info' : problemTone(problem.kind)"
|
|
487
487
|
:size="isResolved ? 'sm' : undefined"
|
|
@@ -492,7 +492,7 @@ onMounted(() => {
|
|
|
492
492
|
"
|
|
493
493
|
data-problem-banner
|
|
494
494
|
/>
|
|
495
|
-
<
|
|
495
|
+
<DmsNoticeBanner
|
|
496
496
|
v-if="suppression"
|
|
497
497
|
:tone="suppression.tone"
|
|
498
498
|
size="sm"
|
|
@@ -519,8 +519,8 @@ onMounted(() => {
|
|
|
519
519
|
@click="openSuppressionList"
|
|
520
520
|
/>
|
|
521
521
|
</template>
|
|
522
|
-
</
|
|
523
|
-
<
|
|
522
|
+
</DmsNoticeBanner>
|
|
523
|
+
<DmsNoticeBanner
|
|
524
524
|
v-for="link in linkedSends"
|
|
525
525
|
:key="link.id"
|
|
526
526
|
tone="info"
|
|
@@ -539,7 +539,7 @@ onMounted(() => {
|
|
|
539
539
|
@click="void showSend(link.id)"
|
|
540
540
|
/>
|
|
541
541
|
</template>
|
|
542
|
-
</
|
|
542
|
+
</DmsNoticeBanner>
|
|
543
543
|
</section>
|
|
544
544
|
|
|
545
545
|
<!--
|
|
@@ -548,7 +548,7 @@ onMounted(() => {
|
|
|
548
548
|
-->
|
|
549
549
|
<div class="@2xl:grid-cols-2 grid gap-6" data-send-details-grid>
|
|
550
550
|
<section class="flex min-w-0 flex-col gap-2">
|
|
551
|
-
<
|
|
551
|
+
<DmsEyebrowLabel :label="key('timeline')" />
|
|
552
552
|
<ol class="flex flex-col">
|
|
553
553
|
<li
|
|
554
554
|
v-for="item in timeline"
|
|
@@ -581,7 +581,7 @@ onMounted(() => {
|
|
|
581
581
|
</section>
|
|
582
582
|
|
|
583
583
|
<section class="flex min-w-0 flex-col gap-2">
|
|
584
|
-
<
|
|
584
|
+
<DmsEyebrowLabel :label="key('origin')" />
|
|
585
585
|
<div
|
|
586
586
|
class="border-default divide-default divide-y overflow-hidden rounded-lg border"
|
|
587
587
|
>
|
|
@@ -623,7 +623,7 @@ onMounted(() => {
|
|
|
623
623
|
|
|
624
624
|
<section class="flex flex-col gap-2">
|
|
625
625
|
<div class="flex items-center justify-between gap-2">
|
|
626
|
-
<
|
|
626
|
+
<DmsEyebrowLabel :label="key('payload')" />
|
|
627
627
|
<UButton
|
|
628
628
|
v-if="payload"
|
|
629
629
|
size="xs"
|
|
@@ -90,7 +90,7 @@ async function createFrom(starter: StarterSummary): Promise<void> {
|
|
|
90
90
|
class="rounded-(--dms-radius-card) h-[150px] w-full"
|
|
91
91
|
/>
|
|
92
92
|
</template>
|
|
93
|
-
<
|
|
93
|
+
<DmsCardPanel
|
|
94
94
|
v-for="starter in starters"
|
|
95
95
|
v-else
|
|
96
96
|
:key="starter.id"
|
|
@@ -131,7 +131,7 @@ async function createFrom(starter: StarterSummary): Promise<void> {
|
|
|
131
131
|
{{ starter.slug }}
|
|
132
132
|
</span>
|
|
133
133
|
</div>
|
|
134
|
-
</
|
|
134
|
+
</DmsCardPanel>
|
|
135
135
|
</div>
|
|
136
136
|
<UButton
|
|
137
137
|
icon="i-ph-plus"
|
|
@@ -22,7 +22,7 @@ const { t } = useI18n()
|
|
|
22
22
|
v-if="variables.length || checks.length"
|
|
23
23
|
class="flex min-w-0 flex-col gap-1"
|
|
24
24
|
>
|
|
25
|
-
<
|
|
25
|
+
<DmsEyebrowLabel :label="t(`${KEY}.title`)" />
|
|
26
26
|
<p class="text-muted text-[12px] [overflow-wrap:anywhere]">
|
|
27
27
|
{{ t(`${KEY}.description`) }}
|
|
28
28
|
</p>
|
|
@@ -159,7 +159,7 @@ function chipLabel(code: string, present: boolean): string {
|
|
|
159
159
|
</script>
|
|
160
160
|
|
|
161
161
|
<template>
|
|
162
|
-
<
|
|
162
|
+
<DmsCardPanel
|
|
163
163
|
as="article"
|
|
164
164
|
:padded="false"
|
|
165
165
|
interactive
|
|
@@ -302,5 +302,5 @@ function chipLabel(code: string, present: boolean): string {
|
|
|
302
302
|
<span v-else class="truncate">{{ footerText }}</span>
|
|
303
303
|
</div>
|
|
304
304
|
</div>
|
|
305
|
-
</
|
|
305
|
+
</DmsCardPanel>
|
|
306
306
|
</template>
|
|
@@ -451,7 +451,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
451
451
|
<template v-else-if="template">
|
|
452
452
|
<div class="flex flex-col gap-1.5 pb-3">
|
|
453
453
|
<div class="flex items-center gap-1">
|
|
454
|
-
<
|
|
454
|
+
<DmsEyebrowLabel :label="t('dms_mailing.templates.drawer.eyebrow')" />
|
|
455
455
|
<div class="ml-auto flex items-center gap-0.5">
|
|
456
456
|
<template v-if="navigation">
|
|
457
457
|
<UButton
|
|
@@ -569,7 +569,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
569
569
|
|
|
570
570
|
<div v-show="tab === 'preview'" class="flex flex-col gap-3 py-4">
|
|
571
571
|
<div class="flex items-center justify-between gap-2">
|
|
572
|
-
<
|
|
572
|
+
<DmsSegmentedControl
|
|
573
573
|
v-if="localeItems.length > 1"
|
|
574
574
|
v-model="locale"
|
|
575
575
|
:items="localeItems"
|
|
@@ -599,7 +599,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
599
599
|
/>
|
|
600
600
|
</div>
|
|
601
601
|
</div>
|
|
602
|
-
<
|
|
602
|
+
<DmsNoticeBanner
|
|
603
603
|
v-if="isLocaleMissing"
|
|
604
604
|
tone="warning"
|
|
605
605
|
size="sm"
|
|
@@ -629,7 +629,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
629
629
|
@click="openEditor(locale)"
|
|
630
630
|
/>
|
|
631
631
|
</template>
|
|
632
|
-
</
|
|
632
|
+
</DmsNoticeBanner>
|
|
633
633
|
</div>
|
|
634
634
|
|
|
635
635
|
<div v-show="tab === 'variables'" class="flex flex-col gap-4 py-4">
|
|
@@ -250,7 +250,7 @@ useEventListener(
|
|
|
250
250
|
​
|
|
251
251
|
</div>
|
|
252
252
|
<div class="grid grid-cols-[repeat(auto-fill,minmax(272px,1fr))] gap-4">
|
|
253
|
-
<
|
|
253
|
+
<DmsCardPanel
|
|
254
254
|
v-for="index in SKELETON_CARDS"
|
|
255
255
|
:key="index"
|
|
256
256
|
:padded="false"
|
|
@@ -273,7 +273,7 @@ useEventListener(
|
|
|
273
273
|
​
|
|
274
274
|
</div>
|
|
275
275
|
</div>
|
|
276
|
-
</
|
|
276
|
+
</DmsCardPanel>
|
|
277
277
|
</div>
|
|
278
278
|
</section>
|
|
279
279
|
|
|
@@ -242,7 +242,7 @@ async function submit(): Promise<void> {
|
|
|
242
242
|
:label="t('dms_mailing.real_send.version')"
|
|
243
243
|
>
|
|
244
244
|
<template #default="{ id }">
|
|
245
|
-
<
|
|
245
|
+
<DmsInputSelect
|
|
246
246
|
:id="id"
|
|
247
247
|
v-model="versionChoice"
|
|
248
248
|
:items="versionItems"
|
|
@@ -257,7 +257,7 @@ async function submit(): Promise<void> {
|
|
|
257
257
|
|
|
258
258
|
<FormFieldRow :label="t('dms_mailing.test_send.locale')">
|
|
259
259
|
<template #default="{ id }">
|
|
260
|
-
<
|
|
260
|
+
<DmsInputSelect
|
|
261
261
|
:id="id"
|
|
262
262
|
v-model="locale"
|
|
263
263
|
:items="localeItems"
|
|
@@ -271,7 +271,7 @@ async function submit(): Promise<void> {
|
|
|
271
271
|
|
|
272
272
|
<FormFieldRow :label="t('dms_mailing.test_send.data')">
|
|
273
273
|
<template #default="{ id }">
|
|
274
|
-
<
|
|
274
|
+
<DmsInputSelect
|
|
275
275
|
:id="id"
|
|
276
276
|
v-model="dataChoice"
|
|
277
277
|
:items="dataItems"
|
|
@@ -284,7 +284,7 @@ async function submit(): Promise<void> {
|
|
|
284
284
|
</FormFieldRow>
|
|
285
285
|
</FormRows>
|
|
286
286
|
|
|
287
|
-
<
|
|
287
|
+
<DmsNoticeBanner
|
|
288
288
|
tone="info"
|
|
289
289
|
icon="i-ph-info"
|
|
290
290
|
:title="t('dms_mailing.test_send.info_title')"
|
|
@@ -62,7 +62,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
62
62
|
<template>
|
|
63
63
|
<div class="flex flex-col gap-4">
|
|
64
64
|
<div class="flex items-center justify-between gap-2">
|
|
65
|
-
<
|
|
65
|
+
<DmsSegmentedControl
|
|
66
66
|
v-if="localeItems.length > 1"
|
|
67
67
|
v-model="locale"
|
|
68
68
|
:items="localeItems"
|
|
@@ -95,7 +95,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
95
95
|
|
|
96
96
|
<div class="grid gap-4 sm:grid-cols-2">
|
|
97
97
|
<div class="flex flex-col gap-1.5">
|
|
98
|
-
<
|
|
98
|
+
<DmsEyebrowLabel :label="t(`${KEY}.brought`)" />
|
|
99
99
|
<ul v-if="lines.length" class="flex flex-col gap-1 text-[12.5px]">
|
|
100
100
|
<li
|
|
101
101
|
v-for="line in lines"
|
|
@@ -114,7 +114,7 @@ function onResolved(preview: PreviewResponse): void {
|
|
|
114
114
|
</p>
|
|
115
115
|
</div>
|
|
116
116
|
<div class="flex flex-col gap-1.5">
|
|
117
|
-
<
|
|
117
|
+
<DmsEyebrowLabel :label="t(`${KEY}.declared`)" />
|
|
118
118
|
<p v-if="!declared.length" class="text-muted text-[12.5px]">
|
|
119
119
|
{{ t(`${KEY}.no_variables`) }}
|
|
120
120
|
</p>
|
|
@@ -91,7 +91,7 @@ function patchChecked(field: BlockField, value: unknown): void {
|
|
|
91
91
|
:is-multiline="field.kind === 'tokens'"
|
|
92
92
|
@update:model-value="patch(field.key, $event)"
|
|
93
93
|
/>
|
|
94
|
-
<
|
|
94
|
+
<DmsSegmentedControl
|
|
95
95
|
v-else-if="field.kind === 'align'"
|
|
96
96
|
:model-value="text(field.key) || ALIGNMENTS[0]"
|
|
97
97
|
:items="alignItems"
|
|
@@ -178,7 +178,7 @@ watch(locale, load, { immediate: true })
|
|
|
178
178
|
>
|
|
179
179
|
<div class="flex min-w-0 flex-col gap-0.5">
|
|
180
180
|
<div class="flex items-center gap-2">
|
|
181
|
-
<
|
|
181
|
+
<DmsEyebrowLabel :label="side.label" />
|
|
182
182
|
<span
|
|
183
183
|
v-if="side.preview"
|
|
184
184
|
class="text-highlighted min-w-0 truncate text-xs font-medium"
|
|
@@ -29,7 +29,7 @@ function openEnvelope(): void {
|
|
|
29
29
|
class="hover:bg-elevated/50 col-span-2 grid min-h-[38px] w-full grid-cols-subgrid items-center gap-x-2.5 px-3 text-left"
|
|
30
30
|
@click.stop="openEnvelope()"
|
|
31
31
|
>
|
|
32
|
-
<
|
|
32
|
+
<DmsEyebrowLabel
|
|
33
33
|
:label="t(`${KEY}.subject`)"
|
|
34
34
|
class="min-w-[78px] whitespace-nowrap"
|
|
35
35
|
/>
|
|
@@ -57,7 +57,7 @@ function openEnvelope(): void {
|
|
|
57
57
|
class="border-default hover:bg-elevated/50 col-span-2 grid min-h-[38px] w-full grid-cols-subgrid items-center gap-x-2.5 border-t px-3 text-left"
|
|
58
58
|
@click.stop="openEnvelope()"
|
|
59
59
|
>
|
|
60
|
-
<
|
|
60
|
+
<DmsEyebrowLabel
|
|
61
61
|
:label="t(`${KEY}.preheader`)"
|
|
62
62
|
class="min-w-[78px] whitespace-nowrap"
|
|
63
63
|
/>
|
|
@@ -45,7 +45,7 @@ const buttons = computed(() => lifecycleActions(status.value))
|
|
|
45
45
|
|
|
46
46
|
<template>
|
|
47
47
|
<div class="flex flex-col gap-3">
|
|
48
|
-
<
|
|
48
|
+
<DmsEyebrowLabel :label="t(`${KEY}.title`)" />
|
|
49
49
|
<DmsStatGroup :items="stats" layout="joined" :columns="3" />
|
|
50
50
|
<div class="flex flex-wrap items-center gap-1.5">
|
|
51
51
|
<UButton
|
|
@@ -37,7 +37,7 @@ useSortableList(root, () => props.entries, {
|
|
|
37
37
|
|
|
38
38
|
<template>
|
|
39
39
|
<section>
|
|
40
|
-
<
|
|
40
|
+
<DmsEyebrowLabel :label="title" class="mb-2 block" />
|
|
41
41
|
<div ref="root" class="grid grid-cols-2 gap-1.5">
|
|
42
42
|
<template v-for="element in entries" :key="element.type">
|
|
43
43
|
<button
|
|
@@ -43,7 +43,7 @@ const { t } = useI18n()
|
|
|
43
43
|
{{ t(`${KEY}.no_changes`, { version: currentVersion }) }}
|
|
44
44
|
</p>
|
|
45
45
|
|
|
46
|
-
<
|
|
46
|
+
<DmsNoticeBanner
|
|
47
47
|
v-if="blockers.length"
|
|
48
48
|
tone="error"
|
|
49
49
|
icon="i-ph-prohibit"
|
|
@@ -54,9 +54,9 @@ const { t } = useI18n()
|
|
|
54
54
|
<li v-for="blocker in blockers" :key="blocker">{{ blocker }}</li>
|
|
55
55
|
</ul>
|
|
56
56
|
</template>
|
|
57
|
-
</
|
|
57
|
+
</DmsNoticeBanner>
|
|
58
58
|
|
|
59
|
-
<
|
|
59
|
+
<DmsNoticeBanner
|
|
60
60
|
v-if="warnings.length"
|
|
61
61
|
tone="warning"
|
|
62
62
|
icon="i-ph-warning"
|
|
@@ -67,7 +67,7 @@ const { t } = useI18n()
|
|
|
67
67
|
<li v-for="warning in warnings" :key="warning">{{ warning }}</li>
|
|
68
68
|
</ul>
|
|
69
69
|
</template>
|
|
70
|
-
</
|
|
70
|
+
</DmsNoticeBanner>
|
|
71
71
|
|
|
72
72
|
<div class="border-default -mx-1 flex items-center gap-2 border-t pt-4">
|
|
73
73
|
<UButton
|
|
@@ -95,7 +95,7 @@ const preheader = computed({
|
|
|
95
95
|
|
|
96
96
|
<template v-if="editor.current">
|
|
97
97
|
<USeparator />
|
|
98
|
-
<
|
|
98
|
+
<DmsEyebrowLabel
|
|
99
99
|
:label="t(`${KEY}.envelope`, { locale: editor.locale.toUpperCase() })"
|
|
100
100
|
/>
|
|
101
101
|
<UFormField :label="t('dms_mailing.editor.subject')" size="sm">
|
|
@@ -112,7 +112,7 @@ const preheader = computed({
|
|
|
112
112
|
|
|
113
113
|
<USeparator />
|
|
114
114
|
<div class="flex flex-col gap-2">
|
|
115
|
-
<
|
|
115
|
+
<DmsEyebrowLabel :label="t('dms_mailing.history.title')" />
|
|
116
116
|
<MailingVersionHistory
|
|
117
117
|
v-if="session.state.version > 0"
|
|
118
118
|
:template-id="templateId"
|
|
@@ -114,7 +114,7 @@ function insert(path: string): void {
|
|
|
114
114
|
|
|
115
115
|
<template>
|
|
116
116
|
<div class="flex flex-col gap-2">
|
|
117
|
-
<
|
|
117
|
+
<DmsEyebrowLabel :label="t(`${KEY}.insert_hint`)" />
|
|
118
118
|
<p class="text-muted text-xs">{{ t(`${KEY}.draft_hint`) }}</p>
|
|
119
119
|
|
|
120
120
|
<p v-if="merged.length === 0" class="text-muted text-xs">
|
|
@@ -58,7 +58,7 @@ const mode = computed({
|
|
|
58
58
|
"
|
|
59
59
|
>
|
|
60
60
|
<span class="inline-flex">
|
|
61
|
-
<
|
|
61
|
+
<DmsSegmentedControl
|
|
62
62
|
v-model="device"
|
|
63
63
|
size="xs"
|
|
64
64
|
:items="deviceItems"
|
|
@@ -66,7 +66,7 @@ const mode = computed({
|
|
|
66
66
|
/>
|
|
67
67
|
</span>
|
|
68
68
|
</UTooltip>
|
|
69
|
-
<
|
|
69
|
+
<DmsSegmentedControl
|
|
70
70
|
v-model="mode"
|
|
71
71
|
size="xs"
|
|
72
72
|
:items="modeItems"
|
|
@@ -21,7 +21,7 @@ export interface EditorContext {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const EDITOR_KEY: InjectionKey<EditorContext> = Symbol('dms-mailing-editor')
|
|
24
|
-
const MISSING_CONTEXT = 'useEditorContext() must be called inside
|
|
24
|
+
const MISSING_CONTEXT = 'useEditorContext() must be called inside MailingTemplateEditor'
|
|
25
25
|
|
|
26
26
|
export function provideEditorContext(context: EditorContext): void {
|
|
27
27
|
provide(EDITOR_KEY, context)
|
|
@@ -74,6 +74,11 @@ function backendSourceFile(id: string): string | undefined {
|
|
|
74
74
|
: undefined
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/** `path` with `/` separators, the form Vite gives an importer, so a boundary error names both paths alike on every OS. */
|
|
78
|
+
function forwardSlashes(path: string): string {
|
|
79
|
+
return path.split(sep).join('/')
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
/**
|
|
78
83
|
* Fails the build and the dev server when a module of the frontend resolves to
|
|
79
84
|
* a file under the `src` of the backend package: the browser reaches the
|
|
@@ -113,9 +118,9 @@ function boundaryCheck(): VitePlugin {
|
|
|
113
118
|
const from =
|
|
114
119
|
importer === undefined
|
|
115
120
|
? 'an entry point'
|
|
116
|
-
: importer.replace(QUERY_OR_HASH, '')
|
|
121
|
+
: forwardSlashes(importer.replace(QUERY_OR_HASH, ''))
|
|
117
122
|
return this.error(
|
|
118
|
-
`Backend boundary: ${from} imports ${file}, which is under the src of ${BACKEND_PACKAGE}. The frontend reaches the backend only through its HTTP interface.`,
|
|
123
|
+
`Backend boundary: ${from} imports ${forwardSlashes(file)}, which is under the src of ${BACKEND_PACKAGE}. The frontend reaches the backend only through its HTTP interface.`,
|
|
119
124
|
)
|
|
120
125
|
},
|
|
121
126
|
}
|
|
@@ -8,17 +8,14 @@ interface VueModule {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const COMPONENT_PREFIX = 'Mailing'
|
|
11
|
-
const BLOCKS_DIRECTORY = '/components/blocks/'
|
|
12
|
-
const BLOCK_NAME_PREFIX = 'Block'
|
|
13
11
|
|
|
14
12
|
const components = import.meta.glob<VueModule>('./app/components/**/*.vue')
|
|
15
13
|
|
|
16
14
|
function componentName(path: string): string {
|
|
17
|
-
|
|
15
|
+
return path
|
|
18
16
|
.split('/')
|
|
19
17
|
.at(-1)!
|
|
20
18
|
.replace(/\.vue$/, '')
|
|
21
|
-
return path.includes(BLOCKS_DIRECTORY) ? `${BLOCK_NAME_PREFIX}${base}` : base
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
const frontendModule: DmsFrontendModule = {
|
|
@@ -9,7 +9,6 @@ export default [
|
|
|
9
9
|
{
|
|
10
10
|
files: ['**/*.ts', '**/*.vue'],
|
|
11
11
|
languageOptions: { parserOptions: { parser: ts.parser } },
|
|
12
|
-
rules: { 'vue/multi-word-component-names': 'off' },
|
|
13
12
|
},
|
|
14
13
|
{ files: ['**/*.ts'], languageOptions: { parser: ts.parser } },
|
|
15
14
|
{
|
|
@@ -21,6 +20,15 @@ export default [
|
|
|
21
20
|
'antelopejs/no-typeof-function-only-test': 'error',
|
|
22
21
|
'antelopejs/no-self-import-in-tests': 'error',
|
|
23
22
|
'anti-slop/no-module-mocking': 'error',
|
|
23
|
+
'no-restricted-syntax': [
|
|
24
|
+
'error',
|
|
25
|
+
{
|
|
26
|
+
selector:
|
|
27
|
+
'CallExpression:matches([callee.name=/^(it|test)$/], [callee.object.name=/^(it|test)$/], [callee.callee.object.name=/^(it|test)$/]) ImportExpression',
|
|
28
|
+
message:
|
|
29
|
+
'Import the module under test statically at the top of the file: a dynamic import inside a test body counts the module load in the test timeout.',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
24
32
|
},
|
|
25
33
|
},
|
|
26
34
|
]
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"packageManager": "pnpm@10.2.0",
|
|
8
8
|
"engines": {
|
|
9
|
-
"@antelopejs/dms-frontend": ">=0.
|
|
9
|
+
"@antelopejs/dms-frontend": ">=0.11.0 <0.12.0"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"format": "prettier --write .",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"vue": "^3.5.18"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@antelopejs/tooling-configs": "
|
|
24
|
+
"@antelopejs/tooling-configs": ">=0.7.1 <0.8.0",
|
|
25
25
|
"@nuxt/ui": "4.10.0",
|
|
26
26
|
"@types/sortablejs": "^1.15.9",
|
|
27
27
|
"@vitejs/plugin-vue": "^6.0.0",
|