@dev.smartpricing/message-composer-layer 4.0.2-tepmlates.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/app/api/index.ts +1 -1
  2. package/app/api/organization.ts +5 -0
  3. package/app/components/Common/MessagePreviewSlideover.vue +0 -1
  4. package/app/components/Composer/EmailComposer.vue +1 -31
  5. package/app/components/Composer/MessageLibrary.vue +1 -70
  6. package/app/components/Composer/WhatsappComposer.vue +0 -24
  7. package/app/components/CreateTemplateButton.vue +45 -8
  8. package/app/components/Email/InlinePreview.vue +3 -26
  9. package/app/components/Email/SendTestEmailModal.vue +34 -2
  10. package/app/components/TemplateList/BaseTable.vue +0 -7
  11. package/app/composables/composerContext.ts +5 -0
  12. package/app/pages/{compose → template}/email/create.vue +1 -10
  13. package/app/pages/{compose → template}/whatsapp/create.vue +1 -4
  14. package/app/queries/index.ts +0 -1
  15. package/app/utils/testIds.const.ts +0 -1
  16. package/i18n/locales/de.ts +3 -42
  17. package/i18n/locales/en.ts +3 -41
  18. package/i18n/locales/es.ts +3 -41
  19. package/i18n/locales/fr.ts +3 -41
  20. package/i18n/locales/it.ts +3 -41
  21. package/package.json +2 -2
  22. package/app/api/templates.ts +0 -55
  23. package/app/components/Common/NewMessageModal.vue +0 -276
  24. package/app/pages/templates/email/[id].vue +0 -82
  25. package/app/pages/templates/email/create.vue +0 -86
  26. package/app/pages/templates/index.vue +0 -245
  27. package/app/pages/templates/whatsapp/[id].vue +0 -82
  28. package/app/pages/templates/whatsapp/create.vue +0 -86
  29. package/app/queries/templates.ts +0 -119
  30. /package/app/pages/{compose → template}/email/[id].vue +0 -0
  31. /package/app/pages/{compose → template}/whatsapp/[id].vue +0 -0
package/app/api/index.ts CHANGED
@@ -9,4 +9,4 @@ export * from './compilation'
9
9
  export * from './tracking'
10
10
  export * from './dynamicValues'
11
11
  export * from './brands'
12
- export * from './templates'
12
+ export * from './organization'
@@ -0,0 +1,5 @@
1
+ import { apiClient } from './client'
2
+
3
+ export const organizationApi = {
4
+ getData: () => apiClient<Record<string, string>>('/organization/data'),
5
+ }
@@ -81,7 +81,6 @@ const isEmail = computed(() => renderType.value === 'email_v1')
81
81
  v-else-if="isEmail"
82
82
  :message="message as EmailMessage"
83
83
  :brand="emailEditorStore.brand"
84
- :dynamic-data="dynamicData"
85
84
  />
86
85
  </template>
87
86
  </USlideover>
@@ -8,13 +8,10 @@ import {
8
8
  } from '@dev.smartpricing/message-composer-utils/utils'
9
9
  import { useAppContextStore } from '@/stores/appContext'
10
10
  import { useMessageGroupQuery, useLanguagesQuery } from '~/queries'
11
- import { templatesApi } from '~/api/templates'
12
11
 
13
12
  const props = defineProps<{
14
13
  mode: 'create' | 'edit'
15
14
  messageGroupId?: string
16
- fromTemplateId?: string
17
- brandId?: string
18
15
  }>()
19
16
 
20
17
  const emit = defineEmits<{
@@ -31,34 +28,7 @@ const { goBackOrFallback } = useGoBack()
31
28
  // ── Init ──────────────────────────────────────────────────────────
32
29
  if (props.mode === 'create') {
33
30
  store.initCreate(mainLanguage.value)
34
- emailEditorStore.initBrand(props.brandId)
35
-
36
- // Pre-fill from template if provided
37
- if (props.fromTemplateId) {
38
- templatesApi.getById(props.fromTemplateId).then((template) => {
39
- if (!template?.messages?.length) return
40
- store.name = template.name
41
- store.category = template.category
42
- for (const msg of template.messages) {
43
- if (msg.render_type !== 'email_v1') continue
44
- store.messages[msg.language_id] = {
45
- render_type: msg.render_type,
46
- body: msg.body,
47
- metadata: {},
48
- language_id: msg.language_id,
49
- message_group_id: '',
50
- status: 'draft',
51
- owner: '',
52
- user_id: '',
53
- } as PostEntity<EmailMessage>
54
- }
55
- // Reload editor with first message
56
- const currentMessage = store.currentMessage
57
- if (currentMessage) {
58
- emailEditorStore.loadFromData(currentMessage as PostEntity<EmailMessage>)
59
- }
60
- })
61
- }
31
+ emailEditorStore.initBrand()
62
32
  } else {
63
33
  // Edit mode: data loaded via watcher below
64
34
  }
@@ -4,14 +4,13 @@ import {
4
4
  useDeleteMessageGroupMutation,
5
5
  usePublishMessageGroupMutation,
6
6
  useUnpublishMessageGroupMutation,
7
- useSaveAsTemplateMutation,
8
7
  } from '~/queries'
9
8
  import type {
10
9
  MessageGroupWithMessageSummary,
11
10
  RenderType,
12
11
  } from '@dev.smartpricing/message-composer-utils/types'
13
12
  import type { MessageActionProperties, MessagesKind } from '@/types/tracking'
14
- import { computed, ref } from 'vue'
13
+ import { computed } from 'vue'
15
14
  import BaseTable from '@/components/TemplateList/BaseTable.vue'
16
15
 
17
16
  const renderType = defineModel<RenderType>('renderType', { required: true })
@@ -43,34 +42,6 @@ const { confirm } = useConfirm()
43
42
  const { mutateAsync: deleteGroup } = useDeleteMessageGroupMutation()
44
43
  const { mutateAsync: publishGroup } = usePublishMessageGroupMutation()
45
44
  const { mutateAsync: unpublishGroup } = useUnpublishMessageGroupMutation()
46
- const { mutateAsync: saveAsTemplate, isPending: isSavingAsTemplate } = useSaveAsTemplateMutation()
47
-
48
- // Save as template modal state
49
- const saveAsTemplateModalOpen = ref(false)
50
- const saveAsTemplateName = ref('')
51
- const saveAsTemplateTarget = ref<MessageGroupWithMessageSummary | null>(null)
52
-
53
- function openSaveAsTemplateModal(row: MessageGroupWithMessageSummary) {
54
- saveAsTemplateTarget.value = row
55
- saveAsTemplateName.value = row.name
56
- saveAsTemplateModalOpen.value = true
57
- }
58
-
59
- async function confirmSaveAsTemplate() {
60
- if (!saveAsTemplateTarget.value) return
61
-
62
- const row = saveAsTemplateTarget.value
63
- const name = saveAsTemplateName.value.trim() || row.name
64
-
65
- await saveAsTemplate({ messageGroupId: row.id, name })
66
-
67
- useToast().add({
68
- title: t('templates.dialogs.save_as_template.success'),
69
- color: 'success',
70
- })
71
-
72
- saveAsTemplateModalOpen.value = false
73
- }
74
45
 
75
46
  function getMessageTrackingProps(row: MessageGroupWithMessageSummary): MessageActionProperties {
76
47
  return {
@@ -180,45 +151,5 @@ defineExpose({ execute })
180
151
  @duplicate="handleDuplicate"
181
152
  @publish="askPublishGroup"
182
153
  @unpublish="askUnpublishGroup"
183
- @save-as-template="openSaveAsTemplateModal"
184
154
  />
185
-
186
- <UModal
187
- v-model:open="saveAsTemplateModalOpen"
188
- :title="t('templates.dialogs.save_as_template.title')"
189
- close-icon="ph:x"
190
- >
191
- <template #body>
192
- <div class="flex flex-col gap-4">
193
- <p class="text-sm text-muted">
194
- {{ t('templates.dialogs.save_as_template.message') }}
195
- </p>
196
- <UFormField :label="t('templates.template_name')">
197
- <UInput
198
- v-model="saveAsTemplateName"
199
- :placeholder="t('templates.template_name_placeholder')"
200
- class="w-full"
201
- autofocus
202
- @keydown.enter="confirmSaveAsTemplate"
203
- />
204
- </UFormField>
205
- </div>
206
- </template>
207
-
208
- <template #footer>
209
- <div class="flex gap-2 justify-end w-full">
210
- <UButton variant="outline" color="neutral" @click="saveAsTemplateModalOpen = false">
211
- {{ t('common.actions.cancel') }}
212
- </UButton>
213
- <UButton
214
- color="primary"
215
- :loading="isSavingAsTemplate"
216
- :disabled="!saveAsTemplateName.trim()"
217
- @click="confirmSaveAsTemplate"
218
- >
219
- {{ t('templates.save_as_template') }}
220
- </UButton>
221
- </div>
222
- </template>
223
- </UModal>
224
155
  </template>
@@ -9,13 +9,11 @@ import type {
9
9
  import { useAppContextStore } from '@/stores/appContext'
10
10
  import { useMetaSubmissionStore } from '@/stores/metaSubmission'
11
11
  import { useMessageGroupQuery, useLanguagesQuery } from '~/queries'
12
- import { templatesApi } from '~/api/templates'
13
12
  import type { WaHeaderType } from '~/types/tracking'
14
13
 
15
14
  const props = defineProps<{
16
15
  mode: 'create' | 'edit'
17
16
  messageGroupId?: string
18
- fromTemplateId?: string
19
17
  }>()
20
18
 
21
19
  const emit = defineEmits<{
@@ -32,28 +30,6 @@ const { submitToMeta } = useMetaSubmissionStore()
32
30
  // ── Init ──────────────────────────────────────────────────────────
33
31
  if (props.mode === 'create') {
34
32
  store.initCreate(mainLanguage.value)
35
-
36
- // Pre-fill from template if provided
37
- if (props.fromTemplateId) {
38
- templatesApi.getById(props.fromTemplateId).then((template) => {
39
- if (!template?.messages?.length) return
40
- store.name = template.name
41
- store.category = template.category
42
- for (const msg of template.messages) {
43
- if (msg.render_type !== 'whatsapp_v1') continue
44
- store.messages[msg.language_id] = {
45
- render_type: msg.render_type,
46
- body: msg.body,
47
- metadata: {},
48
- language_id: msg.language_id,
49
- message_group_id: '',
50
- status: 'draft',
51
- owner: '',
52
- user_id: '',
53
- } as PostEntity<WhatsappMessage>
54
- }
55
- })
56
- }
57
33
  }
58
34
 
59
35
  // ── Edit mode: fetch message group ────────────────────────────────
@@ -1,22 +1,59 @@
1
1
  <script setup lang="ts">
2
- import NewMessageModal from '@/components/Common/NewMessageModal.vue'
2
+ import { computed } from 'vue'
3
+ import { useRouter } from 'vue-router'
3
4
 
5
+ const router = useRouter()
6
+ const { hasWhatsapp, hasEmail } = useChannels()
7
+ const { t } = useI18n()
4
8
  const { trackEvent } = useTracking()
5
- const modalOpen = ref(false)
6
9
 
7
- function openModal() {
8
- trackEvent('message_create', { message_kind: 'open_modal' })
9
- modalOpen.value = true
10
+ function createTemplate(type: 'whatsapp' | 'email') {
11
+ trackEvent('message_create', { message_kind: type })
12
+ router.push({ name: `${type}-message-create` })
10
13
  }
14
+
15
+ const templateOptions = computed(() => {
16
+ const options = []
17
+ if (hasWhatsapp.value) {
18
+ options.push({
19
+ label: t('message.render_type.whatsapp_v1'),
20
+ onSelect: () => createTemplate('whatsapp'),
21
+ })
22
+ }
23
+ if (hasEmail.value) {
24
+ options.push({
25
+ label: t('message.render_type.email_v1'),
26
+ onSelect: () => createTemplate('email'),
27
+ })
28
+ }
29
+ return options
30
+ })
31
+
32
+ const singleChannelType = computed(() => {
33
+ if (hasWhatsapp.value && !hasEmail.value) return 'whatsapp'
34
+ if (hasEmail.value && !hasWhatsapp.value) return 'email'
35
+ return null
36
+ })
11
37
  </script>
12
38
 
13
39
  <template>
14
40
  <UButton
41
+ v-if="singleChannelType"
15
42
  :label="$t('pages.message_library.add_message_group')"
16
43
  color="primary"
17
44
  :data-testid="homeTestIds.createTemplateButton"
18
- @click="openModal"
45
+ @click="createTemplate(singleChannelType)"
19
46
  />
20
-
21
- <NewMessageModal v-model:open="modalOpen" />
47
+ <UDropdownMenu
48
+ v-else
49
+ :items="templateOptions"
50
+ :popper="{ placement: 'bottom-end' }"
51
+ :data-testid="homeTestIds.createTemplateDropdown"
52
+ >
53
+ <UButton
54
+ :label="$t('pages.message_library.add_message_group')"
55
+ color="primary"
56
+ :data-testid="homeTestIds.createTemplateButton"
57
+ />
58
+ </UDropdownMenu>
22
59
  </template>
@@ -4,7 +4,6 @@ import type {
4
4
  PostEntity,
5
5
  BrandFromDb,
6
6
  } from '@dev.smartpricing/message-composer-utils/types'
7
- import { extractEmailParameters } from '@dev.smartpricing/message-composer-utils/utils'
8
7
  import { ref, computed, watch } from 'vue'
9
8
  import { useDebounceFn } from '@vueuse/core'
10
9
  import HtmlPreviewIframe from '@/components/Common/HtmlPreviewIframe.vue'
@@ -16,7 +15,6 @@ const props = withDefaults(
16
15
  defineProps<{
17
16
  message: PostEntity<EmailMessage> | EmailMessage
18
17
  brand?: BrandFromDb | null
19
- dynamicData?: Record<string, string>
20
18
  view?: 'desktop' | 'mobile'
21
19
  showSubject?: boolean
22
20
  }>(),
@@ -27,16 +25,11 @@ const iframeWidth = computed(() => {
27
25
  return props.view === 'mobile' ? '400px' : '600px'
28
26
  })
29
27
 
30
- // Extract parameters client-side
31
- const parameters = computed(() => extractEmailParameters(props.message))
32
-
33
- const parameterValues = ref<Record<string, string>>({})
34
28
  const { mutate: preview, data: previewed, isLoading: isPreviewing } = usePreviewEmailMutation()
35
29
 
36
30
  const previewEmail = useDebounceFn(() => {
37
31
  preview({
38
32
  message: props.message as any,
39
- parameters: parameterValues.value,
40
33
  brand: props.brand
41
34
  ? {
42
35
  default_email_settings: props.brand.default_email_settings,
@@ -44,32 +37,16 @@ const previewEmail = useDebounceFn(() => {
44
37
  default_socials: normalizeBrandSocials(props.brand.default_socials),
45
38
  logo_url: props.brand.logo_url,
46
39
  }
47
- : undefined,
40
+ : null,
48
41
  })
49
42
  }, 0)
50
43
 
51
44
  watch(
52
- parameters,
53
- (params) => {
54
- const defaults = props.dynamicData || {}
55
- parameterValues.value = params.reduce(
56
- (acc, param) => {
57
- acc[param] = defaults[param] || `Sample ${param}`
58
- return acc
59
- },
60
- {} as Record<string, string>,
61
- )
62
-
63
- previewEmail()
64
- },
65
- { immediate: true },
66
- )
67
-
68
- watch(
69
- () => props.brand,
45
+ () => props.message,
70
46
  () => {
71
47
  previewEmail()
72
48
  },
49
+ { immediate: true, deep: true },
73
50
  )
74
51
 
75
52
  const iframeContent = ref<string>('')
@@ -5,6 +5,7 @@ import { useQuery } from '@pinia/colada'
5
5
  import { useSendTestEmailMutation } from '~/queries'
6
6
  import { messageGroupsApi, messagesApi } from '~/api'
7
7
  import { useEmailEditorStore } from '~/stores/emailEditor'
8
+ import { useComposerContext } from '~/composables/composerContext'
8
9
  import { z } from 'zod'
9
10
 
10
11
  defineOptions({
@@ -83,12 +84,28 @@ const isResolvingMessage = computed(
83
84
  const { t } = useI18n()
84
85
  const toast = useToast()
85
86
  const emailEditorStore = useEmailEditorStore()
87
+ const composerContext = useComposerContext()
86
88
  const { mutate: sendTest, isLoading: isSending, status } = useSendTestEmailMutation()
87
89
 
90
+ const defaultSenderEmail = computed(
91
+ () => composerContext.organizationData?.value?.senderEmail ?? '',
92
+ )
93
+
88
94
  const state = ref<Schema>({
89
95
  to: '',
96
+ from: '',
90
97
  })
91
98
 
99
+ watch(
100
+ defaultSenderEmail,
101
+ (email) => {
102
+ if (!state.value.from) {
103
+ state.value.from = email
104
+ }
105
+ },
106
+ { immediate: true },
107
+ )
108
+
92
109
  const schema = z.object({
93
110
  to: z
94
111
  .string()
@@ -103,6 +120,9 @@ const schema = z.object({
103
120
  },
104
121
  { message: t('editor.validation.error.emailInvalid') },
105
122
  ),
123
+ from: z.string().refine((value) => !value || isEmail(value), {
124
+ message: t('editor.validation.error.emailInvalid'),
125
+ }),
106
126
  })
107
127
 
108
128
  type Schema = z.output<typeof schema>
@@ -117,6 +137,7 @@ function onSubmit() {
117
137
  message: resolvedMessage.value,
118
138
  to: state.value.to,
119
139
  brand_id: resolvedBrandId.value,
140
+ from: state.value.from || undefined,
120
141
  })
121
142
  }
122
143
 
@@ -161,14 +182,25 @@ watch(status, (newStatus) => {
161
182
  </slot>
162
183
 
163
184
  <template #body>
164
- <UForm ref="formRef" :schema="schema" :state="state" @submit="onSubmit">
165
- <UFormField name="to" :data-testid="sendTestEmailModalTestIds.toFormField">
185
+ <UForm ref="formRef" :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
186
+ <UFormField
187
+ name="to"
188
+ :label="$t('pages.email.test_email_to')"
189
+ :data-testid="sendTestEmailModalTestIds.toFormField"
190
+ >
166
191
  <UInput
167
192
  v-model="state.to"
168
193
  :placeholder="$t('pages.email.test_email_to_placeholder')"
169
194
  :data-testid="sendTestEmailModalTestIds.toInput"
170
195
  />
171
196
  </UFormField>
197
+ <UFormField name="from" :label="$t('pages.email.test_email_from')">
198
+ <UInput
199
+ v-model="state.from"
200
+ type="email"
201
+ :placeholder="$t('pages.email.test_email_from_placeholder')"
202
+ />
203
+ </UFormField>
172
204
  </UForm>
173
205
  </template>
174
206
 
@@ -18,7 +18,6 @@ const emit = defineEmits<{
18
18
  duplicate: [MessageGroupWithMessageSummary]
19
19
  publish: [MessageGroupWithMessageSummary]
20
20
  unpublish: [MessageGroupWithMessageSummary]
21
- saveAsTemplate: [MessageGroupWithMessageSummary]
22
21
  }>()
23
22
 
24
23
  const appContext = useAppContextStore()
@@ -77,12 +76,6 @@ function rowActions(row: MessageGroupWithMessageSummary) {
77
76
  onSelect: () => emit('duplicate', row),
78
77
  'data-testid': `${templateListTableTestIds.rowDuplicateAction}-${row.id}`,
79
78
  },
80
- {
81
- label: t('templates.save_as_template'),
82
- icon: 'ph:bookmark-simple',
83
- onSelect: () => emit('saveAsTemplate', row),
84
- 'data-testid': `${templateListTableTestIds.rowSaveAsTemplateAction}-${row.id}`,
85
- },
86
79
  {
87
80
  label: t('common.actions.delete'),
88
81
  icon: 'ph:trash',
@@ -1,6 +1,10 @@
1
1
  import type { InjectionKey, Ref } from 'vue'
2
2
  import type { AppContextConfig } from '@dev.smartpricing/message-composer-utils/types'
3
3
 
4
+ export interface OrganizationData {
5
+ senderEmail: string
6
+ }
7
+
4
8
  export interface ComposerContext {
5
9
  authenticationToken: Ref<string>
6
10
  owner_id?: Ref<string>
@@ -14,6 +18,7 @@ export interface ComposerContext {
14
18
  metaWabaId?: Ref<string>
15
19
  metaAccessToken?: Ref<string>
16
20
  isAdmin?: Ref<boolean>
21
+ organizationData?: Ref<OrganizationData>
17
22
  }
18
23
 
19
24
  export const COMPOSER_CONTEXT_KEY: InjectionKey<ComposerContext> = Symbol.for('composer-context')
@@ -5,12 +5,8 @@ definePageMeta({
5
5
  channel: 'email',
6
6
  })
7
7
 
8
- const route = useRoute()
9
8
  const composerRef = useTemplateRef('composerRef')
10
9
  const store = useEmailComposerStore()
11
-
12
- const fromTemplateId = computed(() => route.query.from_template as string | undefined)
13
- const brandId = computed(() => route.query.brand_id as string | undefined)
14
10
  </script>
15
11
 
16
12
  <template>
@@ -56,12 +52,7 @@ const brandId = computed(() => route.query.brand_id as string | undefined)
56
52
  />
57
53
  </template>
58
54
 
59
- <ComposerEmailComposer
60
- ref="composerRef"
61
- mode="create"
62
- :from-template-id="fromTemplateId"
63
- :brand-id="brandId"
64
- >
55
+ <ComposerEmailComposer ref="composerRef" mode="create">
65
56
  <template #actions></template>
66
57
  </ComposerEmailComposer>
67
58
  </LayoutBasePage>
@@ -5,11 +5,8 @@ definePageMeta({
5
5
  channel: 'whatsapp',
6
6
  })
7
7
 
8
- const route = useRoute()
9
8
  const composerRef = useTemplateRef('composerRef')
10
9
  const store = useWhatsappComposerStore()
11
-
12
- const fromTemplateId = computed(() => route.query.from_template as string | undefined)
13
10
  </script>
14
11
 
15
12
  <template>
@@ -49,7 +46,7 @@ const fromTemplateId = computed(() => route.query.from_template as string | unde
49
46
  />
50
47
  </template>
51
48
 
52
- <ComposerWhatsappComposer ref="composerRef" mode="create" :from-template-id="fromTemplateId">
49
+ <ComposerWhatsappComposer ref="composerRef" mode="create">
53
50
  <template #actions></template>
54
51
  </ComposerWhatsappComposer>
55
52
  </LayoutBasePage>
@@ -7,4 +7,3 @@ export * from './meta'
7
7
  export * from './compilation'
8
8
  export * from './dynamicValues'
9
9
  export * from './brands'
10
- export * from './templates'
@@ -520,7 +520,6 @@ export const templateListTableTestIds = {
520
520
  rowDuplicateAction: 'template-list-table-row-duplicate',
521
521
  rowPublishAction: 'template-list-table-row-publish',
522
522
  rowUnpublishAction: 'template-list-table-row-unpublish',
523
- rowSaveAsTemplateAction: 'template-list-table-row-save-as-template',
524
523
  } as const
525
524
 
526
525
  export const templateListStatusCellTestIds = {
@@ -239,7 +239,10 @@ Diese Nachrichten müssen funktional sein, nicht werblich.
239
239
  email: {
240
240
  send_test: 'Test senden',
241
241
  send_test_success: 'Test-E-Mail erfolgreich gesendet.',
242
+ test_email_to: 'An',
242
243
  test_email_to_placeholder: 'Empfänger-E-Mail(s), getrennt durch ;',
244
+ test_email_from: 'Von',
245
+ test_email_from_placeholder: 'Absender-E-Mail-Adresse',
243
246
  create_message_group: {
244
247
  title: 'Nachricht erstellen',
245
248
  save: 'Speichern',
@@ -831,48 +834,6 @@ Diese Nachrichten müssen funktional sein, nicht werblich.
831
834
  no_preview: 'Keine Vorschau verfügbar',
832
835
  subject: 'Betreff',
833
836
  },
834
- templates: {
835
- title: 'Vorlagen',
836
- my_templates: 'Meine Vorlagen',
837
- system_templates: 'Systemvorlagen',
838
- add_template: 'Neue Vorlage',
839
- create_template: 'Vorlage erstellen',
840
- edit_template: 'Vorlage bearbeiten',
841
- use_template: 'Diese Vorlage verwenden',
842
- start_from_scratch: 'Neu beginnen',
843
- pick_template: 'Vorlage auswählen',
844
- save_as_template: 'Als Vorlage speichern',
845
- system_template: 'Systemvorlage',
846
- template_name: 'Vorlagenname',
847
- template_name_placeholder: 'Vorlagenname eingeben',
848
- no_templates: 'Keine Vorlagen verfügbar',
849
- new_message_email: 'Neue E-Mail-Nachricht',
850
- new_message_whatsapp: 'Neue WhatsApp-Nachricht',
851
- dialogs: {
852
- delete: {
853
- title: 'Vorlage löschen',
854
- message:
855
- 'Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie {name} wirklich löschen?',
856
- success: '"{name}" wurde gelöscht.',
857
- },
858
- publish: {
859
- title: 'Vorlage veröffentlichen',
860
- message: '"{name}" veröffentlichen, um sie allen Benutzern zugänglich zu machen?',
861
- success: '"{name}" wurde veröffentlicht.',
862
- },
863
- unpublish: {
864
- title: 'Vorlage verbergen',
865
- message: '"{name}" verbergen? Sie wird für andere Benutzer nicht mehr sichtbar sein.',
866
- success: '"{name}" wurde verborgen.',
867
- },
868
- save_as_template: {
869
- title: 'Als Vorlage speichern',
870
- message: 'Diese Nachricht als wiederverwendbare Vorlage speichern?',
871
- success: 'Vorlage erfolgreich gespeichert.',
872
- },
873
- },
874
- copy_success: 'Nachricht aus Vorlage erstellt.',
875
- },
876
837
  public: {
877
838
  select_message: {
878
839
  choose_message: 'Nachricht auswählen...',
@@ -236,7 +236,10 @@ These messages must be functional, not promotional.
236
236
  email: {
237
237
  send_test: 'Send test',
238
238
  send_test_success: 'Test email sent successfully.',
239
+ test_email_to: 'To',
239
240
  test_email_to_placeholder: 'Recipient email(s), separated by ;',
241
+ test_email_from: 'From',
242
+ test_email_from_placeholder: 'Sender email address',
240
243
  create_message_group: {
241
244
  title: 'Create message',
242
245
  save: 'Save',
@@ -822,47 +825,6 @@ These messages must be functional, not promotional.
822
825
  no_preview: 'No preview available',
823
826
  subject: 'Subject',
824
827
  },
825
- templates: {
826
- title: 'Templates',
827
- my_templates: 'My templates',
828
- system_templates: 'System templates',
829
- add_template: 'New template',
830
- create_template: 'Create template',
831
- edit_template: 'Edit template',
832
- use_template: 'Use this template',
833
- start_from_scratch: 'Start from scratch',
834
- pick_template: 'Choose a template',
835
- save_as_template: 'Save as template',
836
- system_template: 'System template',
837
- template_name: 'Template name',
838
- template_name_placeholder: 'Enter template name',
839
- no_templates: 'No templates available',
840
- new_message_email: 'New email message',
841
- new_message_whatsapp: 'New WhatsApp message',
842
- dialogs: {
843
- delete: {
844
- title: 'Delete template',
845
- message: "This action can't be undone. Are you sure you want to delete {name}?",
846
- success: '"{name}" has been deleted.',
847
- },
848
- publish: {
849
- title: 'Publish template',
850
- message: 'Publish "{name}" to make it available to all users?',
851
- success: '"{name}" has been published.',
852
- },
853
- unpublish: {
854
- title: 'Unpublish template',
855
- message: 'Unpublish "{name}"? It will no longer be visible to other users.',
856
- success: '"{name}" has been unpublished.',
857
- },
858
- save_as_template: {
859
- title: 'Save as template',
860
- message: 'Save this message as a reusable template?',
861
- success: 'Template saved successfully.',
862
- },
863
- },
864
- copy_success: 'Message created from template.',
865
- },
866
828
  public: {
867
829
  select_message: {
868
830
  choose_message: 'Choose a message...',