@dev.smartpricing/message-composer-layer 4.0.2-templates.1 → 4.0.2-tepmlates.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.
@@ -4,7 +4,6 @@ import type {
4
4
  TemplateWithMessageSummary,
5
5
  CreateTemplatePayload,
6
6
  UpdateTemplatePayload,
7
- PromoteTemplatePayload,
8
7
  } from '@dev.smartpricing/message-composer-utils/types'
9
8
  import { apiClient } from './client'
10
9
 
@@ -48,12 +47,6 @@ export const templatesApi = {
48
47
  unpublish: (id: string) =>
49
48
  apiClient<TemplateFromDb>(`/templates/${id}/unpublish`, { method: 'PUT' }),
50
49
 
51
- promote: (id: string, data: PromoteTemplatePayload) =>
52
- apiClient<TemplateFromDb>(`/templates/${id}/promote`, {
53
- method: 'POST',
54
- body: data,
55
- }),
56
-
57
50
  saveFromMessageGroup: (messageGroupId: string, name?: string) =>
58
51
  apiClient<TemplateFromDb>(`/message-groups/${messageGroupId}/save-as-template`, {
59
52
  method: 'POST',
@@ -14,6 +14,7 @@ import { templatesApi } from '~/api/templates'
14
14
 
15
15
  const isOpen = defineModel<boolean>('open', { required: true })
16
16
 
17
+ const router = useRouter()
17
18
  const { t, locale } = useI18n()
18
19
  const { mainLanguage } = storeToRefs(useAppContextStore())
19
20
  const { allowedRenderTypes, defaultRenderType } = useChannels()
@@ -134,10 +135,11 @@ function selectTemplate(tmpl: TemplateWithMessageSummary) {
134
135
  function handleCreate() {
135
136
  isOpen.value = false
136
137
  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 })
138
+ const params = new URLSearchParams()
139
+ if (selectedId.value) params.set('from_template', selectedId.value)
140
+ if (brand.value) params.set('brand_id', brand.value.id)
141
+ const query = params.size ? `?${params.toString()}` : ''
142
+ router.push(`/compose/${channel}/create${query}`)
141
143
  }
142
144
  </script>
143
145
 
@@ -164,7 +164,7 @@ function navigate(row: MessageGroupWithMessageSummary) {
164
164
  trackEvent('message_edit', getMessageTrackingProps(row))
165
165
  emit('edit', row)
166
166
  const type = row.messages[0]?.render_type === 'email_v1' ? 'email' : 'whatsapp'
167
- navigateTo({ name: `${type}-message-edit`, params: { id: row.id } })
167
+ router.push({ name: `${type}-message-edit`, params: { id: row.id } })
168
168
  }
169
169
 
170
170
  defineExpose({ execute })
@@ -13,6 +13,9 @@ const toast = useToast()
13
13
  const { t } = useI18n()
14
14
  const { goBackOrFallback } = useGoBack()
15
15
 
16
+ const appContext = useAppContextStore()
17
+ const isSystem = ref(false)
18
+
16
19
  const { mutateAsync: createTemplate } = useCreateTemplateMutation()
17
20
  const saving = ref(false)
18
21
 
@@ -33,6 +36,7 @@ async function handleSave() {
33
36
  name: store.name,
34
37
  category: store.category,
35
38
  messages,
39
+ ...(appContext.isAdmin && isSystem.value ? { is_system: true } : {}),
36
40
  })
37
41
 
38
42
  toast.add({ title: t('templates.dialogs.save_as_template.success'), color: 'success' })
@@ -69,6 +73,14 @@ async function handleSave() {
69
73
  />
70
74
  </template>
71
75
 
72
- <ComposerEmailComposer ref="composerRef" mode="create" />
76
+ <ComposerEmailComposer ref="composerRef" mode="create">
77
+ <template #actions>
78
+ <UCheckbox
79
+ v-if="appContext.isAdmin"
80
+ v-model="isSystem"
81
+ :label="$t('templates.system_template')"
82
+ />
83
+ </template>
84
+ </ComposerEmailComposer>
73
85
  </LayoutBasePage>
74
86
  </template>
@@ -8,6 +8,7 @@ definePageMeta({
8
8
  name: 'template-library',
9
9
  })
10
10
 
11
+ const router = useRouter()
11
12
  const { t, locale } = useI18n()
12
13
  const toast = useToast()
13
14
  const { hasWhatsapp, hasEmail, defaultRenderType, isRenderTypeAllowed } = useChannels()
@@ -38,13 +39,6 @@ const { data: templates, isPending } = useTemplatesQuery(
38
39
  const { mutateAsync: deleteTemplate } = useDeleteTemplateMutation()
39
40
  const { mutateAsync: publishTemplate } = usePublishTemplateMutation()
40
41
  const { mutateAsync: unpublishTemplate } = useUnpublishTemplateMutation()
41
- const promoteTarget = ref<TemplateWithMessageSummary | null>(null)
42
- const isPromoteModalOpen = computed({
43
- get: () => promoteTarget.value !== null,
44
- set: (v) => {
45
- if (!v) promoteTarget.value = null
46
- },
47
- })
48
42
 
49
43
  const tabItems = computed(() => {
50
44
  const items = []
@@ -85,7 +79,7 @@ function channelFromRenderType(rt: RenderType): 'email' | 'whatsapp' {
85
79
 
86
80
  function editTemplate(tmpl: TemplateWithMessageSummary) {
87
81
  const channel = channelFromRenderType(tmpl.messages[0]?.render_type || renderType.value)
88
- navigateTo({ name: `${channel}-template-edit`, params: { id: tmpl.id } })
82
+ router.push(`/templates/${channel}/${tmpl.id}`)
89
83
  }
90
84
 
91
85
  async function handleDelete(tmpl: TemplateWithMessageSummary) {
@@ -111,7 +105,7 @@ async function handleUnpublish(tmpl: TemplateWithMessageSummary) {
111
105
 
112
106
  function handleUse(tmpl: TemplateWithMessageSummary) {
113
107
  const channel = channelFromRenderType(tmpl.messages[0]?.render_type || renderType.value)
114
- navigateTo({ name: `${channel}-message-create`, query: { from_template: tmpl.id } })
108
+ router.push(`/compose/${channel}/create?from_template=${tmpl.id}`)
115
109
  }
116
110
 
117
111
  function rowActions(tmpl: TemplateWithMessageSummary) {
@@ -124,16 +118,6 @@ function rowActions(tmpl: TemplateWithMessageSummary) {
124
118
  ]
125
119
 
126
120
  if (appContext.isAdmin) {
127
- if (tmpl.user_id !== SYSTEM_USER_ID) {
128
- actions.push({
129
- label: t('templates.dialogs.promote.title'),
130
- icon: 'ph:arrow-fat-up',
131
- onSelect: () => {
132
- promoteTarget.value = tmpl
133
- },
134
- })
135
- }
136
-
137
121
  if (tmpl.visibility === 'internal') {
138
122
  actions.push({
139
123
  label: t('common.actions.publish'),
@@ -159,7 +143,7 @@ function rowActions(tmpl: TemplateWithMessageSummary) {
159
143
 
160
144
  function createTemplate() {
161
145
  const channel = channelFromRenderType(renderType.value)
162
- navigateTo({ name: `${channel}-template-create` })
146
+ router.push(`/templates/${channel}/create`)
163
147
  }
164
148
  </script>
165
149
 
@@ -257,7 +241,5 @@ function createTemplate() {
257
241
  </UTable>
258
242
  </UCard>
259
243
  </div>
260
-
261
- <TemplateListPromoteModal v-model:open="isPromoteModalOpen" :template="promoteTarget" />
262
244
  </LayoutBasePage>
263
245
  </template>
@@ -13,6 +13,9 @@ const toast = useToast()
13
13
  const { t } = useI18n()
14
14
  const { goBackOrFallback } = useGoBack()
15
15
 
16
+ const appContext = useAppContextStore()
17
+ const isSystem = ref(false)
18
+
16
19
  const { mutateAsync: createTemplate } = useCreateTemplateMutation()
17
20
  const saving = ref(false)
18
21
 
@@ -33,6 +36,7 @@ async function handleSave() {
33
36
  name: store.name,
34
37
  category: store.category,
35
38
  messages,
39
+ ...(appContext.isAdmin && isSystem.value ? { is_system: true } : {}),
36
40
  })
37
41
 
38
42
  toast.add({ title: t('templates.dialogs.save_as_template.success'), color: 'success' })
@@ -69,6 +73,14 @@ async function handleSave() {
69
73
  />
70
74
  </template>
71
75
 
72
- <ComposerWhatsappComposer ref="composerRef" mode="create" />
76
+ <ComposerWhatsappComposer ref="composerRef" mode="create">
77
+ <template #actions>
78
+ <UCheckbox
79
+ v-if="appContext.isAdmin"
80
+ v-model="isSystem"
81
+ :label="$t('templates.system_template')"
82
+ />
83
+ </template>
84
+ </ComposerWhatsappComposer>
73
85
  </LayoutBasePage>
74
86
  </template>
@@ -4,7 +4,6 @@ import type {
4
4
  TemplateWithMessageSummary,
5
5
  CreateTemplatePayload,
6
6
  UpdateTemplatePayload,
7
- PromoteTemplatePayload,
8
7
  } from '@dev.smartpricing/message-composer-utils/types'
9
8
  import { templatesApi } from '~/api/templates'
10
9
 
@@ -107,18 +106,6 @@ export function useUnpublishTemplateMutation() {
107
106
  })
108
107
  }
109
108
 
110
- export function usePromoteTemplateMutation() {
111
- const queryCache = useQueryCache()
112
-
113
- return useMutation({
114
- mutation: ({ id, data }: { id: string; data: PromoteTemplatePayload }) =>
115
- templatesApi.promote(id, data),
116
- onSuccess: () => {
117
- queryCache.invalidateQueries({ key: ['templates'] })
118
- },
119
- })
120
- }
121
-
122
109
  export function useSaveAsTemplateMutation() {
123
110
  const queryCache = useQueryCache()
124
111
 
@@ -43,7 +43,6 @@ export default {
43
43
  save: 'Speichern',
44
44
  remove: 'Entfernen',
45
45
  reset_defaults: 'Auf Standardwerte zurücksetzen',
46
- confirm: 'Bestätigen',
47
46
  },
48
47
  internal: 'Intern',
49
48
  imageUpload: {
@@ -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: {
@@ -861,17 +860,6 @@ These messages must be functional, not promotional.
861
860
  message: 'Save this message as a reusable template?',
862
861
  success: 'Template saved successfully.',
863
862
  },
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
863
  },
876
864
  copy_success: 'Message created from template.',
877
865
  },
@@ -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: {
@@ -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: {
@@ -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: {
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-tepmlates.0",
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-tepmlates.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@nuxt/eslint": "^1.15.2",
@@ -1,69 +0,0 @@
1
- <script setup lang="ts">
2
- import type { TemplateWithMessageSummary } from '@dev.smartpricing/message-composer-utils/types'
3
-
4
- const props = defineProps<{
5
- template: TemplateWithMessageSummary | null
6
- }>()
7
-
8
- const emit = defineEmits<{
9
- confirm: [mode: 'clone' | 'convert']
10
- }>()
11
-
12
- const open = defineModel<boolean>('open', { required: true })
13
-
14
- const { t } = useI18n()
15
- const { mutateAsync: promoteTemplate, isPending } = usePromoteTemplateMutation()
16
- const toast = useToast()
17
-
18
- const mode = ref<'clone' | 'convert'>('clone')
19
-
20
- const items = computed(() => [
21
- {
22
- label: t('templates.dialogs.promote.clone_label'),
23
- description: t('templates.dialogs.promote.clone_description'),
24
- value: 'clone' as const,
25
- },
26
- {
27
- label: t('templates.dialogs.promote.convert_label'),
28
- description: t('templates.dialogs.promote.convert_description'),
29
- value: 'convert' as const,
30
- },
31
- ])
32
-
33
- async function handleConfirm() {
34
- if (!props.template) return
35
- await promoteTemplate({ id: props.template.id, data: { mode: mode.value } })
36
- const key = mode.value === 'clone' ? 'success_clone' : 'success_convert'
37
- toast.add({
38
- title: t(`templates.dialogs.promote.${key}`, { name: props.template.name }),
39
- color: 'success',
40
- })
41
- open.value = false
42
- }
43
-
44
- watch(open, (v) => {
45
- if (v) mode.value = 'clone'
46
- })
47
- </script>
48
-
49
- <template>
50
- <UModal
51
- v-model:open="open"
52
- :title="$t('templates.dialogs.promote.title')"
53
- :ui="{ footer: 'justify-end' }"
54
- >
55
- <template #body>
56
- <URadioGroup v-model="mode" :items="items" variant="card" color="secondary" />
57
- </template>
58
-
59
- <template #footer>
60
- <UButton variant="outline" :label="$t('common.actions.cancel')" @click="open = false" />
61
- <UButton
62
- color="primary"
63
- :label="$t('common.actions.confirm')"
64
- :loading="isPending"
65
- @click="handleConfirm"
66
- />
67
- </template>
68
- </UModal>
69
- </template>