@dev.smartpricing/message-composer-layer 4.0.2-templates.1 → 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 (32) 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 +2 -71
  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 -43
  17. package/i18n/locales/en.ts +3 -53
  18. package/i18n/locales/es.ts +3 -42
  19. package/i18n/locales/fr.ts +3 -42
  20. package/i18n/locales/it.ts +3 -42
  21. package/package.json +2 -2
  22. package/app/api/templates.ts +0 -62
  23. package/app/components/Common/NewMessageModal.vue +0 -274
  24. package/app/components/TemplateList/PromoteModal.vue +0 -69
  25. package/app/pages/templates/email/[id].vue +0 -82
  26. package/app/pages/templates/email/create.vue +0 -74
  27. package/app/pages/templates/index.vue +0 -263
  28. package/app/pages/templates/whatsapp/[id].vue +0 -82
  29. package/app/pages/templates/whatsapp/create.vue +0 -74
  30. package/app/queries/templates.ts +0 -132
  31. /package/app/pages/{compose → template}/email/[id].vue +0 -0
  32. /package/app/pages/{compose → template}/whatsapp/[id].vue +0 -0
@@ -43,7 +43,6 @@ export default {
43
43
  save: 'Save',
44
44
  remove: 'Remove',
45
45
  reset_defaults: 'Reset to defaults',
46
- confirm: 'Confirm',
47
46
  },
48
47
  internal: 'Internal',
49
48
  imageUpload: {
@@ -237,7 +236,10 @@ These messages must be functional, not promotional.
237
236
  email: {
238
237
  send_test: 'Send test',
239
238
  send_test_success: 'Test email sent successfully.',
239
+ test_email_to: 'To',
240
240
  test_email_to_placeholder: 'Recipient email(s), separated by ;',
241
+ test_email_from: 'From',
242
+ test_email_from_placeholder: 'Sender email address',
241
243
  create_message_group: {
242
244
  title: 'Create message',
243
245
  save: 'Save',
@@ -823,58 +825,6 @@ These messages must be functional, not promotional.
823
825
  no_preview: 'No preview available',
824
826
  subject: 'Subject',
825
827
  },
826
- templates: {
827
- title: 'Templates',
828
- my_templates: 'My templates',
829
- system_templates: 'System templates',
830
- add_template: 'New template',
831
- create_template: 'Create template',
832
- edit_template: 'Edit template',
833
- use_template: 'Use this template',
834
- start_from_scratch: 'Start from scratch',
835
- pick_template: 'Choose a template',
836
- save_as_template: 'Save as template',
837
- system_template: 'System template',
838
- template_name: 'Template name',
839
- template_name_placeholder: 'Enter template name',
840
- no_templates: 'No templates available',
841
- new_message_email: 'New email message',
842
- new_message_whatsapp: 'New WhatsApp message',
843
- dialogs: {
844
- delete: {
845
- title: 'Delete template',
846
- message: "This action can't be undone. Are you sure you want to delete {name}?",
847
- success: '"{name}" has been deleted.',
848
- },
849
- publish: {
850
- title: 'Publish template',
851
- message: 'Publish "{name}" to make it available to all users?',
852
- success: '"{name}" has been published.',
853
- },
854
- unpublish: {
855
- title: 'Unpublish template',
856
- message: 'Unpublish "{name}"? It will no longer be visible to other users.',
857
- success: '"{name}" has been unpublished.',
858
- },
859
- save_as_template: {
860
- title: 'Save as template',
861
- message: 'Save this message as a reusable template?',
862
- success: 'Template saved successfully.',
863
- },
864
- promote: {
865
- title: 'Promote to System Template',
866
- clone_label: 'Clone as System',
867
- clone_description:
868
- 'Create an independent copy as a system template. The original template stays unchanged.',
869
- convert_label: 'Convert to System',
870
- convert_description:
871
- 'This template will become a system template. The original owner will lose ownership. This action cannot be undone.',
872
- success_clone: '"{name}" cloned as system template.',
873
- success_convert: '"{name}" promoted to system template.',
874
- },
875
- },
876
- copy_success: 'Message created from template.',
877
- },
878
828
  public: {
879
829
  select_message: {
880
830
  choose_message: 'Choose a message...',
@@ -43,7 +43,6 @@ export default {
43
43
  save: 'Guardar',
44
44
  remove: 'Eliminar',
45
45
  reset_defaults: 'Restablecer valores predeterminados',
46
- confirm: 'Confirmar',
47
46
  },
48
47
  internal: 'Interno',
49
48
  imageUpload: {
@@ -237,7 +236,10 @@ Estos mensajes deben ser funcionales, no promocionales.
237
236
  email: {
238
237
  send_test: 'Enviar prueba',
239
238
  send_test_success: 'Correo de prueba enviado correctamente.',
239
+ test_email_to: 'Para',
240
240
  test_email_to_placeholder: 'Correo(s) del destinatario, separados por ;',
241
+ test_email_from: 'De',
242
+ test_email_from_placeholder: 'Dirección de email del remitente',
241
243
  create_message_group: {
242
244
  title: 'Crear mensaje',
243
245
  save: 'Guardar',
@@ -831,47 +833,6 @@ Estos mensajes deben ser funcionales, no promocionales.
831
833
  no_preview: 'No hay vista previa disponible',
832
834
  subject: 'Asunto',
833
835
  },
834
- templates: {
835
- title: 'Plantillas',
836
- my_templates: 'Mis plantillas',
837
- system_templates: 'Plantillas del sistema',
838
- add_template: 'Nueva plantilla',
839
- create_template: 'Crear plantilla',
840
- edit_template: 'Editar plantilla',
841
- use_template: 'Usar esta plantilla',
842
- start_from_scratch: 'Empezar desde cero',
843
- pick_template: 'Elegir una plantilla',
844
- save_as_template: 'Guardar como plantilla',
845
- system_template: 'Plantilla de sistema',
846
- template_name: 'Nombre de plantilla',
847
- template_name_placeholder: 'Ingrese nombre de plantilla',
848
- no_templates: 'No hay plantillas disponibles',
849
- new_message_email: 'Nuevo mensaje de email',
850
- new_message_whatsapp: 'Nuevo mensaje de WhatsApp',
851
- dialogs: {
852
- delete: {
853
- title: 'Eliminar plantilla',
854
- message: 'Esta acción no se puede deshacer. ¿Está seguro de que desea eliminar {name}?',
855
- success: '"{name}" ha sido eliminado.',
856
- },
857
- publish: {
858
- title: 'Publicar plantilla',
859
- message: '¿Publicar "{name}" para hacerla disponible a todos los usuarios?',
860
- success: '"{name}" ha sido publicado.',
861
- },
862
- unpublish: {
863
- title: 'Ocultar plantilla',
864
- message: '¿Ocultar "{name}"? Ya no será visible para otros usuarios.',
865
- success: '"{name}" ha sido ocultado.',
866
- },
867
- save_as_template: {
868
- title: 'Guardar como plantilla',
869
- message: '¿Guardar este mensaje como plantilla reutilizable?',
870
- success: 'Plantilla guardada exitosamente.',
871
- },
872
- },
873
- copy_success: 'Mensaje creado a partir de la plantilla.',
874
- },
875
836
  public: {
876
837
  select_message: {
877
838
  choose_message: 'Elige un mensaje...',
@@ -43,7 +43,6 @@ export default {
43
43
  save: 'Enregistrer',
44
44
  remove: 'Supprimer',
45
45
  reset_defaults: 'Réinitialiser les valeurs par défaut',
46
- confirm: 'Confirmer',
47
46
  },
48
47
  internal: 'Interne',
49
48
  imageUpload: {
@@ -237,7 +236,10 @@ Ces messages doivent être fonctionnels, et non promotionnels.
237
236
  email: {
238
237
  send_test: 'Envoyer un test',
239
238
  send_test_success: 'E-mail de test envoyé avec succès.',
239
+ test_email_to: 'À',
240
240
  test_email_to_placeholder: 'E-mail(s) du/des destinataire(s), séparé(s) par ;',
241
+ test_email_from: 'De',
242
+ test_email_from_placeholder: "Adresse e-mail de l'expéditeur",
241
243
  create_message_group: {
242
244
  title: 'Créer un message',
243
245
  save: 'Enregistrer',
@@ -835,47 +837,6 @@ Ces messages doivent être fonctionnels, et non promotionnels.
835
837
  no_preview: 'Aucun aperçu disponible',
836
838
  subject: 'Objet',
837
839
  },
838
- templates: {
839
- title: 'Modèles',
840
- my_templates: 'Mes modèles',
841
- system_templates: 'Modèles système',
842
- add_template: 'Nouveau modèle',
843
- create_template: 'Créer un modèle',
844
- edit_template: 'Modifier le modèle',
845
- use_template: 'Utiliser ce modèle',
846
- start_from_scratch: 'Partir de zéro',
847
- pick_template: 'Choisir un modèle',
848
- save_as_template: 'Enregistrer comme modèle',
849
- system_template: 'Modèle système',
850
- template_name: 'Nom du modèle',
851
- template_name_placeholder: 'Entrez le nom du modèle',
852
- no_templates: 'Aucun modèle disponible',
853
- new_message_email: 'Nouveau message email',
854
- new_message_whatsapp: 'Nouveau message WhatsApp',
855
- dialogs: {
856
- delete: {
857
- title: 'Supprimer le modèle',
858
- message: 'Cette action est irréversible. Êtes-vous sûr de vouloir supprimer {name} ?',
859
- success: '"{name}" a été supprimé.',
860
- },
861
- publish: {
862
- title: 'Publier le modèle',
863
- message: 'Publier "{name}" pour le rendre accessible à tous les utilisateurs ?',
864
- success: '"{name}" a été publié.',
865
- },
866
- unpublish: {
867
- title: 'Masquer le modèle',
868
- message: 'Masquer "{name}" ? Il ne sera plus visible pour les autres utilisateurs.',
869
- success: '"{name}" a été masqué.',
870
- },
871
- save_as_template: {
872
- title: 'Enregistrer comme modèle',
873
- message: 'Enregistrer ce message comme modèle réutilisable ?',
874
- success: 'Modèle enregistré avec succès.',
875
- },
876
- },
877
- copy_success: 'Message créé à partir du modèle.',
878
- },
879
840
  public: {
880
841
  select_message: {
881
842
  choose_message: 'Choisir un message...',
@@ -43,7 +43,6 @@ export default {
43
43
  save: 'Salva',
44
44
  remove: 'Rimuovi',
45
45
  reset_defaults: 'Ripristina valori predefiniti',
46
- confirm: 'Conferma',
47
46
  },
48
47
  internal: 'Interno',
49
48
  imageUpload: {
@@ -237,7 +236,10 @@ Questi messaggi devono essere funzionali, non promozionali.
237
236
  email: {
238
237
  send_test: 'Invia test',
239
238
  send_test_success: 'Email di test inviata con successo.',
239
+ test_email_to: 'A',
240
240
  test_email_to_placeholder: 'Email destinatario/i, separate da ;',
241
+ test_email_from: 'Da',
242
+ test_email_from_placeholder: 'Indirizzo email mittente',
241
243
  create_message_group: {
242
244
  title: 'Crea messaggio',
243
245
  save: 'Salva',
@@ -829,47 +831,6 @@ Questi messaggi devono essere funzionali, non promozionali.
829
831
  no_preview: 'Nessuna anteprima disponibile',
830
832
  subject: 'Oggetto',
831
833
  },
832
- templates: {
833
- title: 'Modelli',
834
- my_templates: 'I miei modelli',
835
- system_templates: 'Modelli di sistema',
836
- add_template: 'Nuovo modello',
837
- create_template: 'Crea modello',
838
- edit_template: 'Modifica modello',
839
- use_template: 'Usa questo modello',
840
- start_from_scratch: 'Inizia da zero',
841
- pick_template: 'Scegli un modello',
842
- save_as_template: 'Salva come modello',
843
- system_template: 'Modello di sistema',
844
- template_name: 'Nome modello',
845
- template_name_placeholder: 'Inserisci nome modello',
846
- no_templates: 'Nessun modello disponibile',
847
- new_message_email: 'Nuovo messaggio email',
848
- new_message_whatsapp: 'Nuovo messaggio WhatsApp',
849
- dialogs: {
850
- delete: {
851
- title: 'Elimina modello',
852
- message: 'Questa azione non può essere annullata. Sei sicuro di voler eliminare {name}?',
853
- success: '"{name}" è stato eliminato.',
854
- },
855
- publish: {
856
- title: 'Pubblica modello',
857
- message: 'Pubblicare "{name}" per renderlo disponibile a tutti gli utenti?',
858
- success: '"{name}" è stato pubblicato.',
859
- },
860
- unpublish: {
861
- title: 'Nascondi modello',
862
- message: 'Nascondere "{name}"? Non sarà più visibile agli altri utenti.',
863
- success: '"{name}" è stato nascosto.',
864
- },
865
- save_as_template: {
866
- title: 'Salva come modello',
867
- message: 'Salvare questo messaggio come modello riutilizzabile?',
868
- success: 'Modello salvato con successo.',
869
- },
870
- },
871
- copy_success: 'Messaggio creato dal modello.',
872
- },
873
834
  public: {
874
835
  select_message: {
875
836
  choose_message: 'Scegli un messaggio...',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.smartpricing/message-composer-layer",
3
- "version": "4.0.2-templates.1",
3
+ "version": "4.0.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -38,7 +38,7 @@
38
38
  "vue-router": "^5.0.7",
39
39
  "vue3-emoji-picker": "^1.1.8",
40
40
  "zod": "^4.4.3",
41
- "@dev.smartpricing/message-composer-utils": "4.0.3-templates.1"
41
+ "@dev.smartpricing/message-composer-utils": "4.0.3"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@nuxt/eslint": "^1.15.2",
@@ -1,62 +0,0 @@
1
- import type { TemplateFromDb, RenderType } from '@dev.smartpricing/message-composer-utils/types'
2
- import type {
3
- TemplateWithMessages,
4
- TemplateWithMessageSummary,
5
- CreateTemplatePayload,
6
- UpdateTemplatePayload,
7
- PromoteTemplatePayload,
8
- } from '@dev.smartpricing/message-composer-utils/types'
9
- import { apiClient } from './client'
10
-
11
- export const templatesApi = {
12
- getAll: (options?: {
13
- renderType?: RenderType
14
- scope?: 'system' | 'mine' | 'all'
15
- tags?: string[]
16
- tagsMode?: 'and' | 'or'
17
- }) => {
18
- const params = new URLSearchParams()
19
- if (options?.renderType) params.set('render_type', options.renderType)
20
- if (options?.scope) params.set('scope', options.scope)
21
- if (options?.tags && options.tags.length > 0) {
22
- options.tags.forEach((tag) => params.append('tags', tag))
23
- }
24
- if (options?.tagsMode) params.set('tags_mode', options.tagsMode)
25
-
26
- const query = params.size ? `?${params.toString()}` : ''
27
- return apiClient<TemplateWithMessageSummary[]>(`/templates${query}`)
28
- },
29
-
30
- getById: (id: string) => apiClient<TemplateWithMessages>(`/templates/${id}`),
31
-
32
- create: (data: CreateTemplatePayload) =>
33
- apiClient<TemplateFromDb & { tags: string[] }>('/templates', {
34
- method: 'POST',
35
- body: data,
36
- }),
37
-
38
- update: (id: string, data: UpdateTemplatePayload) =>
39
- apiClient<TemplateFromDb>(`/templates/${id}`, {
40
- method: 'PUT',
41
- body: data,
42
- }),
43
-
44
- delete: (id: string) => apiClient(`/templates/${id}`, { method: 'DELETE' }),
45
-
46
- publish: (id: string) => apiClient<TemplateFromDb>(`/templates/${id}/publish`, { method: 'PUT' }),
47
-
48
- unpublish: (id: string) =>
49
- apiClient<TemplateFromDb>(`/templates/${id}/unpublish`, { method: 'PUT' }),
50
-
51
- promote: (id: string, data: PromoteTemplatePayload) =>
52
- apiClient<TemplateFromDb>(`/templates/${id}/promote`, {
53
- method: 'POST',
54
- body: data,
55
- }),
56
-
57
- saveFromMessageGroup: (messageGroupId: string, name?: string) =>
58
- apiClient<TemplateFromDb>(`/message-groups/${messageGroupId}/save-as-template`, {
59
- method: 'POST',
60
- body: { name },
61
- }),
62
- }
@@ -1,274 +0,0 @@
1
- <script setup lang="ts">
2
- import { SYSTEM_USER_ID } from '@dev.smartpricing/message-composer-utils/types'
3
- import type {
4
- RenderType,
5
- TemplateWithMessageSummary,
6
- PostEntity,
7
- WhatsappMessage,
8
- EmailMessage,
9
- BrandFromDb,
10
- } from '@dev.smartpricing/message-composer-utils/types'
11
- import type { TemplateWithMessages } from '@dev.smartpricing/message-composer-utils/types'
12
- import { getEmailEmpty, getWhatsappEmpty } from '@dev.smartpricing/message-composer-utils/utils'
13
- import { templatesApi } from '~/api/templates'
14
-
15
- const isOpen = defineModel<boolean>('open', { required: true })
16
-
17
- const { t, locale } = useI18n()
18
- const { mainLanguage } = storeToRefs(useAppContextStore())
19
- const { allowedRenderTypes, defaultRenderType } = useChannels()
20
-
21
- const renderType = ref<RenderType>(defaultRenderType.value)
22
- const selectedId = ref<string>() // undefined = "start from scratch"
23
- const selectedTemplateData = ref<TemplateWithMessages>()
24
- const loadingPreview = ref(false)
25
- const brand = ref<BrandFromDb>()
26
-
27
- const isFromScratch = computed(() => selectedId.value == null)
28
- const isEmail = computed(() => renderType.value === 'email_v1')
29
-
30
- const channelItems = computed(() =>
31
- allowedRenderTypes.value.map((rt) => ({ label: t(`message.render_type.${rt}`), value: rt })),
32
- )
33
-
34
- function renderTypeToChannel(rt: RenderType): 'email' | 'whatsapp' {
35
- return rt === 'email_v1' ? 'email' : 'whatsapp'
36
- }
37
-
38
- // Brands query (for email brand selector)
39
- const { data: brands } = useBrandsQuery()
40
-
41
- // Templates query
42
- const { data: templates, isPending: templatesLoading } = useTemplatesQuery(
43
- computed(() => ({ renderType: renderType.value })),
44
- )
45
-
46
- const systemTemplates = computed(() =>
47
- (templates.value || []).filter((t) => t.user_id === SYSTEM_USER_ID),
48
- )
49
-
50
- const myTemplates = computed(() =>
51
- (templates.value || []).filter((t) => t.user_id !== SYSTEM_USER_ID),
52
- )
53
-
54
- // Default empty message for "Start from scratch" preview
55
- const scratchMessage = computed(() => {
56
- if (isEmail.value) {
57
- return getEmailEmpty(mainLanguage.value)
58
- }
59
- return getWhatsappEmpty(mainLanguage.value)
60
- })
61
-
62
- // Preview message: scratch or selected template
63
- const previewMessage = computed<PostEntity<EmailMessage> | PostEntity<WhatsappMessage> | undefined>(
64
- () => {
65
- if (isFromScratch.value) {
66
- return scratchMessage.value
67
- }
68
-
69
- if (!selectedTemplateData.value?.messages?.length) return undefined
70
- const msgs = selectedTemplateData.value.messages
71
- const msg =
72
- msgs.find((m) => m.language_id === locale.value) ||
73
- msgs.find((m) => m.language_id === 'en') ||
74
- msgs[0]
75
- if (!msg) return undefined
76
- return {
77
- render_type: msg.render_type,
78
- body: msg.body,
79
- metadata: msg.metadata || {},
80
- language_id: msg.language_id,
81
- message_group_id: '',
82
- status: 'draft' as const,
83
- owner: '',
84
- user_id: '',
85
- } as PostEntity<EmailMessage> | PostEntity<WhatsappMessage>
86
- },
87
- )
88
-
89
- // Footer button label
90
- const actionLabel = computed(() =>
91
- isFromScratch.value ? t('templates.start_from_scratch') : t('templates.use_template'),
92
- )
93
-
94
- // Reset on open — pre-select "Start from scratch"
95
- watch(isOpen, (open) => {
96
- if (open) {
97
- selectedId.value = undefined
98
- selectedTemplateData.value = undefined
99
- brand.value = undefined
100
- renderType.value = defaultRenderType.value
101
- }
102
- })
103
-
104
- // Reset selection when render type changes
105
- watch(renderType, () => {
106
- selectedId.value = undefined
107
- selectedTemplateData.value = undefined
108
- brand.value = undefined
109
- })
110
-
111
- // Fetch full template when selected
112
- watch(selectedId, async (id) => {
113
- if (!id) {
114
- selectedTemplateData.value = undefined
115
- return
116
- }
117
- loadingPreview.value = true
118
- try {
119
- selectedTemplateData.value = await templatesApi.getById(id)
120
- } finally {
121
- loadingPreview.value = false
122
- }
123
- })
124
-
125
- function selectScratch() {
126
- selectedId.value = undefined
127
- selectedTemplateData.value = undefined
128
- }
129
-
130
- function selectTemplate(tmpl: TemplateWithMessageSummary) {
131
- selectedId.value = selectedId.value === tmpl.id ? undefined : tmpl.id
132
- }
133
-
134
- function handleCreate() {
135
- isOpen.value = false
136
- const channel = renderTypeToChannel(renderType.value)
137
- const query: Record<string, string> = {}
138
- if (selectedId.value) query.from_template = selectedId.value
139
- if (brand.value) query.brand_id = brand.value.id
140
- navigateTo({ name: `${channel}-message-create`, query })
141
- }
142
- </script>
143
-
144
- <template>
145
- <USlideover
146
- v-model:open="isOpen"
147
- :title="$t('pages.message_library.add_message_group')"
148
- side="right"
149
- :ui="{ content: 'max-w-5xl', body: 'sm:p-0 p-0', footer: 'justify-end' }"
150
- >
151
- <template #body>
152
- <div class="grid grid-cols-[1fr_1fr] divide-x divide-muted min-h-full">
153
- <!-- Left: Controls + Template list -->
154
- <div class="overflow-y-auto p-6 space-y-4">
155
- <!-- Channel selector -->
156
- <USelect v-if="channelItems.length > 1" v-model="renderType" :items="channelItems" />
157
-
158
- <!-- Brand selector (email only) -->
159
- <UFormField
160
- v-if="isEmail && brands?.length"
161
- :label="$t('editor.email.generalStyle.brandSection')"
162
- >
163
- <USelectMenu v-model="brand" labelKey="name" :items="brands" />
164
- </UFormField>
165
-
166
- <!-- Start from scratch -->
167
- <button
168
- class="flex w-full items-center gap-3 rounded-lg border-2 border-dashed p-3 text-left text-sm transition"
169
- :class="
170
- isFromScratch
171
- ? 'border-(--ui-primary) bg-(--ui-primary)/5'
172
- : 'border-(--ui-border-muted) hover:border-(--ui-border) hover:bg-(--ui-bg-elevated)'
173
- "
174
- @click="selectScratch"
175
- >
176
- <UIcon name="ph:plus-circle" class="text-xl text-(--ui-text-muted)" />
177
- <span class="font-medium">{{ $t('templates.start_from_scratch') }}</span>
178
- </button>
179
-
180
- <!-- Loading -->
181
- <div v-if="templatesLoading" class="flex justify-center py-4">
182
- <UIcon name="ph:spinner" class="animate-spin text-xl text-(--ui-text-muted)" />
183
- </div>
184
-
185
- <template v-else>
186
- <!-- My Templates -->
187
- <div v-if="myTemplates.length > 0">
188
- <h4 class="mb-2 text-xs font-semibold text-(--ui-text-muted) uppercase">
189
- {{ $t('templates.my_templates') }}
190
- </h4>
191
- <div class="space-y-1">
192
- <button
193
- v-for="tmpl in myTemplates"
194
- :key="tmpl.id"
195
- class="flex w-full items-center justify-between rounded-md p-2.5 text-left text-sm transition"
196
- :class="
197
- selectedId === tmpl.id
198
- ? 'bg-(--ui-primary)/10 ring-1 ring-(--ui-primary)'
199
- : 'hover:bg-(--ui-bg-elevated)'
200
- "
201
- @click="selectTemplate(tmpl)"
202
- >
203
- <div class="flex items-center gap-2">
204
- <span>{{ tmpl.name }}</span>
205
- <UBadge :label="tmpl.category" size="sm" color="neutral" variant="soft" />
206
- </div>
207
- <UBadge
208
- :label="`${tmpl.messages.length} lang`"
209
- size="sm"
210
- color="info"
211
- variant="soft"
212
- />
213
- </button>
214
- </div>
215
- </div>
216
-
217
- <!-- System Templates -->
218
- <div v-if="systemTemplates.length > 0">
219
- <h4 class="mb-2 text-xs font-semibold text-(--ui-text-muted) uppercase">
220
- {{ $t('templates.system_templates') }}
221
- </h4>
222
- <div class="space-y-1">
223
- <button
224
- v-for="tmpl in systemTemplates"
225
- :key="tmpl.id"
226
- class="flex w-full items-center justify-between rounded-md p-2.5 text-left text-sm transition"
227
- :class="
228
- selectedId === tmpl.id
229
- ? 'bg-(--ui-primary)/10 ring-1 ring-(--ui-primary)'
230
- : 'hover:bg-(--ui-bg-elevated)'
231
- "
232
- @click="selectTemplate(tmpl)"
233
- >
234
- <div class="flex items-center gap-2">
235
- <span>{{ tmpl.name }}</span>
236
- <UBadge :label="tmpl.category" size="sm" color="neutral" variant="soft" />
237
- </div>
238
- <UBadge
239
- :label="`${tmpl.messages.length} lang`"
240
- size="sm"
241
- color="info"
242
- variant="soft"
243
- />
244
- </button>
245
- </div>
246
- </div>
247
- </template>
248
- </div>
249
-
250
- <!-- Right: Preview -->
251
- <div class="bg-neutral-50 overflow-y-auto">
252
- <div v-if="loadingPreview" class="flex items-center justify-center h-full">
253
- <UIcon name="ph:spinner" class="animate-spin text-xl text-(--ui-text-muted)" />
254
- </div>
255
-
256
- <div v-else-if="previewMessage && !isEmail" class="p-6">
257
- <WhatsappPreview :message="previewMessage as PostEntity<WhatsappMessage>" />
258
- </div>
259
-
260
- <div v-else-if="previewMessage && isEmail" class="p-6">
261
- <EmailInlinePreview
262
- :message="previewMessage as PostEntity<EmailMessage>"
263
- :brand="brand"
264
- />
265
- </div>
266
- </div>
267
- </div>
268
- </template>
269
-
270
- <template #footer>
271
- <UButton :label="actionLabel" color="primary" @click="handleCreate" />
272
- </template>
273
- </USlideover>
274
- </template>