@dev.smartpricing/message-composer-layer 4.0.2-templates.0 → 4.0.2-templates.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.
@@ -4,6 +4,7 @@ import type {
4
4
  TemplateWithMessageSummary,
5
5
  CreateTemplatePayload,
6
6
  UpdateTemplatePayload,
7
+ PromoteTemplatePayload,
7
8
  } from '@dev.smartpricing/message-composer-utils/types'
8
9
  import { apiClient } from './client'
9
10
 
@@ -47,6 +48,12 @@ export const templatesApi = {
47
48
  unpublish: (id: string) =>
48
49
  apiClient<TemplateFromDb>(`/templates/${id}/unpublish`, { method: 'PUT' }),
49
50
 
51
+ promote: (id: string, data: PromoteTemplatePayload) =>
52
+ apiClient<TemplateFromDb>(`/templates/${id}/promote`, {
53
+ method: 'POST',
54
+ body: data,
55
+ }),
56
+
50
57
  saveFromMessageGroup: (messageGroupId: string, name?: string) =>
51
58
  apiClient<TemplateFromDb>(`/message-groups/${messageGroupId}/save-as-template`, {
52
59
  method: 'POST',
@@ -0,0 +1,69 @@
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>
@@ -13,9 +13,6 @@ 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
-
19
16
  const { mutateAsync: createTemplate } = useCreateTemplateMutation()
20
17
  const saving = ref(false)
21
18
 
@@ -36,7 +33,6 @@ async function handleSave() {
36
33
  name: store.name,
37
34
  category: store.category,
38
35
  messages,
39
- ...(appContext.isAdmin && isSystem.value ? { is_system: true } : {}),
40
36
  })
41
37
 
42
38
  toast.add({ title: t('templates.dialogs.save_as_template.success'), color: 'success' })
@@ -73,14 +69,6 @@ async function handleSave() {
73
69
  />
74
70
  </template>
75
71
 
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>
72
+ <ComposerEmailComposer ref="composerRef" mode="create" />
85
73
  </LayoutBasePage>
86
74
  </template>
@@ -38,6 +38,13 @@ const { data: templates, isPending } = useTemplatesQuery(
38
38
  const { mutateAsync: deleteTemplate } = useDeleteTemplateMutation()
39
39
  const { mutateAsync: publishTemplate } = usePublishTemplateMutation()
40
40
  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
+ })
41
48
 
42
49
  const tabItems = computed(() => {
43
50
  const items = []
@@ -117,6 +124,16 @@ function rowActions(tmpl: TemplateWithMessageSummary) {
117
124
  ]
118
125
 
119
126
  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
+
120
137
  if (tmpl.visibility === 'internal') {
121
138
  actions.push({
122
139
  label: t('common.actions.publish'),
@@ -240,5 +257,7 @@ function createTemplate() {
240
257
  </UTable>
241
258
  </UCard>
242
259
  </div>
260
+
261
+ <TemplateListPromoteModal v-model:open="isPromoteModalOpen" :template="promoteTarget" />
243
262
  </LayoutBasePage>
244
263
  </template>
@@ -13,9 +13,6 @@ 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
-
19
16
  const { mutateAsync: createTemplate } = useCreateTemplateMutation()
20
17
  const saving = ref(false)
21
18
 
@@ -36,7 +33,6 @@ async function handleSave() {
36
33
  name: store.name,
37
34
  category: store.category,
38
35
  messages,
39
- ...(appContext.isAdmin && isSystem.value ? { is_system: true } : {}),
40
36
  })
41
37
 
42
38
  toast.add({ title: t('templates.dialogs.save_as_template.success'), color: 'success' })
@@ -73,14 +69,6 @@ async function handleSave() {
73
69
  />
74
70
  </template>
75
71
 
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>
72
+ <ComposerWhatsappComposer ref="composerRef" mode="create" />
85
73
  </LayoutBasePage>
86
74
  </template>
@@ -4,6 +4,7 @@ import type {
4
4
  TemplateWithMessageSummary,
5
5
  CreateTemplatePayload,
6
6
  UpdateTemplatePayload,
7
+ PromoteTemplatePayload,
7
8
  } from '@dev.smartpricing/message-composer-utils/types'
8
9
  import { templatesApi } from '~/api/templates'
9
10
 
@@ -106,6 +107,18 @@ export function useUnpublishTemplateMutation() {
106
107
  })
107
108
  }
108
109
 
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
+
109
122
  export function useSaveAsTemplateMutation() {
110
123
  const queryCache = useQueryCache()
111
124
 
@@ -43,6 +43,7 @@ export default {
43
43
  save: 'Speichern',
44
44
  remove: 'Entfernen',
45
45
  reset_defaults: 'Auf Standardwerte zurücksetzen',
46
+ confirm: 'Bestätigen',
46
47
  },
47
48
  internal: 'Intern',
48
49
  imageUpload: {
@@ -43,6 +43,7 @@ export default {
43
43
  save: 'Save',
44
44
  remove: 'Remove',
45
45
  reset_defaults: 'Reset to defaults',
46
+ confirm: 'Confirm',
46
47
  },
47
48
  internal: 'Internal',
48
49
  imageUpload: {
@@ -860,6 +861,17 @@ These messages must be functional, not promotional.
860
861
  message: 'Save this message as a reusable template?',
861
862
  success: 'Template saved successfully.',
862
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
+ },
863
875
  },
864
876
  copy_success: 'Message created from template.',
865
877
  },
@@ -43,6 +43,7 @@ export default {
43
43
  save: 'Guardar',
44
44
  remove: 'Eliminar',
45
45
  reset_defaults: 'Restablecer valores predeterminados',
46
+ confirm: 'Confirmar',
46
47
  },
47
48
  internal: 'Interno',
48
49
  imageUpload: {
@@ -43,6 +43,7 @@ export default {
43
43
  save: 'Enregistrer',
44
44
  remove: 'Supprimer',
45
45
  reset_defaults: 'Réinitialiser les valeurs par défaut',
46
+ confirm: 'Confirmer',
46
47
  },
47
48
  internal: 'Interne',
48
49
  imageUpload: {
@@ -43,6 +43,7 @@ export default {
43
43
  save: 'Salva',
44
44
  remove: 'Rimuovi',
45
45
  reset_defaults: 'Ripristina valori predefiniti',
46
+ confirm: 'Conferma',
46
47
  },
47
48
  internal: 'Interno',
48
49
  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.0",
3
+ "version": "4.0.2-templates.1",
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.0"
41
+ "@dev.smartpricing/message-composer-utils": "4.0.3-templates.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@nuxt/eslint": "^1.15.2",