@dev.smartpricing/message-composer-layer 1.0.27 → 4.0.2-templates.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.
Files changed (35) hide show
  1. package/app/api/index.ts +1 -0
  2. package/app/api/templates.ts +55 -0
  3. package/app/components/Common/NewMessageModal.vue +274 -0
  4. package/app/components/Composer/EmailComposer.vue +31 -1
  5. package/app/components/Composer/EmailContent.vue +2 -2
  6. package/app/components/Composer/MessageLibrary.vue +71 -2
  7. package/app/components/Composer/WhatsappComposer.vue +24 -0
  8. package/app/components/Composer/WhatsappContent.vue +2 -2
  9. package/app/components/CreateTemplateButton.vue +8 -45
  10. package/app/components/Email/InlinePreview.vue +8 -1
  11. package/app/components/Email/Preview.vue +1 -2
  12. package/app/components/TemplateList/BaseTable.vue +11 -4
  13. package/app/composables/composerContext.ts +7 -0
  14. package/app/composables/useTracking.ts +2 -3
  15. package/app/pages/{template → compose}/email/create.vue +10 -1
  16. package/app/pages/{template → compose}/whatsapp/create.vue +4 -1
  17. package/app/pages/templates/email/[id].vue +82 -0
  18. package/app/pages/templates/email/create.vue +86 -0
  19. package/app/pages/templates/index.vue +244 -0
  20. package/app/pages/templates/whatsapp/[id].vue +82 -0
  21. package/app/pages/templates/whatsapp/create.vue +86 -0
  22. package/app/queries/index.ts +1 -0
  23. package/app/queries/templates.ts +119 -0
  24. package/app/stores/appContext.ts +2 -0
  25. package/app/utils/testIds.const.ts +1 -0
  26. package/i18n/locales/de.ts +42 -0
  27. package/i18n/locales/en.ts +41 -0
  28. package/i18n/locales/es.ts +41 -0
  29. package/i18n/locales/fr.ts +41 -0
  30. package/i18n/locales/it.ts +41 -0
  31. package/package.json +2 -3
  32. package/app/plugins/admin-shortcut.client.ts +0 -7
  33. package/app/stores/adminStore.ts +0 -50
  34. /package/app/pages/{template → compose}/email/[id].vue +0 -0
  35. /package/app/pages/{template → compose}/whatsapp/[id].vue +0 -0
@@ -0,0 +1,86 @@
1
+ <script setup lang="ts">
2
+ import type { WhatsappMessage, PostEntity } from '@dev.smartpricing/message-composer-utils/types'
3
+
4
+ definePageMeta({
5
+ name: 'whatsapp-template-create',
6
+ middleware: ['channel-guard'],
7
+ channel: 'whatsapp',
8
+ })
9
+
10
+ const composerRef = useTemplateRef('composerRef')
11
+ const store = useWhatsappComposerStore()
12
+ const toast = useToast()
13
+ const { t } = useI18n()
14
+ const { goBackOrFallback } = useGoBack()
15
+
16
+ const appContext = useAppContextStore()
17
+ const isSystem = ref(false)
18
+
19
+ const { mutateAsync: createTemplate } = useCreateTemplateMutation()
20
+ const saving = ref(false)
21
+
22
+ async function handleSave() {
23
+ saving.value = true
24
+ try {
25
+ const messages = Object.values(store.messages).map((msg) => {
26
+ const m = msg as PostEntity<WhatsappMessage>
27
+ return {
28
+ language_id: m.language_id,
29
+ render_type: m.render_type,
30
+ body: m.body,
31
+ metadata: m.metadata || {},
32
+ }
33
+ })
34
+
35
+ await createTemplate({
36
+ name: store.name,
37
+ category: store.category,
38
+ messages,
39
+ ...(appContext.isAdmin && isSystem.value ? { is_system: true } : {}),
40
+ })
41
+
42
+ toast.add({ title: t('templates.dialogs.save_as_template.success'), color: 'success' })
43
+ goBackOrFallback()
44
+ } finally {
45
+ saving.value = false
46
+ }
47
+ }
48
+ </script>
49
+
50
+ <template>
51
+ <LayoutBasePage
52
+ :title="$t('templates.create_template')"
53
+ :tabs="[
54
+ { label: $t('pages.whatsapp.tabs.content'), value: 'content' },
55
+ { label: $t('pages.whatsapp.tabs.translations'), value: 'translations' },
56
+ ]"
57
+ :active-tab="store.activeTab"
58
+ @tab-change="store.activeTab = $event as 'content' | 'translations'"
59
+ >
60
+ <template #actions>
61
+ <UButton
62
+ @click="composerRef?.handleCancel()"
63
+ :label="$t('common.actions.cancel')"
64
+ color="primary"
65
+ variant="ghost"
66
+ :loading="saving || composerRef?.isTranslating"
67
+ />
68
+ <UButton
69
+ @click="handleSave()"
70
+ :label="$t('common.actions.save')"
71
+ color="primary"
72
+ :loading="saving || composerRef?.isTranslating"
73
+ />
74
+ </template>
75
+
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>
85
+ </LayoutBasePage>
86
+ </template>
@@ -7,3 +7,4 @@ export * from './meta'
7
7
  export * from './compilation'
8
8
  export * from './dynamicValues'
9
9
  export * from './brands'
10
+ export * from './templates'
@@ -0,0 +1,119 @@
1
+ import { useQuery, useMutation, useQueryCache } from '@pinia/colada'
2
+ import type { RenderType } from '@dev.smartpricing/message-composer-utils/types'
3
+ import type {
4
+ TemplateWithMessageSummary,
5
+ CreateTemplatePayload,
6
+ UpdateTemplatePayload,
7
+ } from '@dev.smartpricing/message-composer-utils/types'
8
+ import { templatesApi } from '~/api/templates'
9
+
10
+ // QUERIES
11
+
12
+ export function useTemplatesQuery(
13
+ options?: MaybeRefOrGetter<{
14
+ renderType?: RenderType
15
+ scope?: 'system' | 'mine' | 'all'
16
+ tags?: string[]
17
+ tagsMode?: 'and' | 'or'
18
+ }>,
19
+ ) {
20
+ return useQuery({
21
+ key: () => ['templates', JSON.stringify(toValue(options))],
22
+ query: () => templatesApi.getAll(toValue(options)),
23
+ staleTime: 0,
24
+ })
25
+ }
26
+
27
+ export function useTemplateQuery(id: MaybeRefOrGetter<string>) {
28
+ return useQuery({
29
+ key: () => ['templates', toValue(id)],
30
+ query: () => templatesApi.getById(toValue(id)),
31
+ staleTime: 2 * 60 * 1000,
32
+ })
33
+ }
34
+
35
+ // MUTATIONS
36
+
37
+ export function useCreateTemplateMutation() {
38
+ const queryCache = useQueryCache()
39
+
40
+ return useMutation({
41
+ mutation: (data: CreateTemplatePayload) => templatesApi.create(data),
42
+ onSuccess: () => {
43
+ queryCache.invalidateQueries({ key: ['templates'] })
44
+ },
45
+ })
46
+ }
47
+
48
+ export function useUpdateTemplateMutation() {
49
+ const queryCache = useQueryCache()
50
+
51
+ return useMutation({
52
+ mutation: ({ id, data }: { id: string; data: UpdateTemplatePayload }) =>
53
+ templatesApi.update(id, data),
54
+ onSuccess: (_, { id }) => {
55
+ queryCache.invalidateQueries({ key: ['templates', id] })
56
+ queryCache.invalidateQueries({ key: ['templates'] })
57
+ },
58
+ })
59
+ }
60
+
61
+ export function useDeleteTemplateMutation() {
62
+ const queryCache = useQueryCache()
63
+
64
+ return useMutation({
65
+ mutation: templatesApi.delete,
66
+ onMutate: async (id) => {
67
+ queryCache.cancelQueries({ key: ['templates'] })
68
+ const previous = queryCache.getQueryData(['templates'])
69
+
70
+ queryCache.setQueryData(['templates'], (old: TemplateWithMessageSummary[] | undefined) =>
71
+ old?.filter((t) => t.id !== id),
72
+ )
73
+
74
+ return { previous }
75
+ },
76
+ onError: (_, __, context) => {
77
+ if (context?.previous) {
78
+ queryCache.setQueryData(['templates'], context.previous)
79
+ }
80
+ },
81
+ onSuccess: () => {
82
+ queryCache.invalidateQueries({ key: ['templates'] })
83
+ },
84
+ })
85
+ }
86
+
87
+ export function usePublishTemplateMutation() {
88
+ const queryCache = useQueryCache()
89
+
90
+ return useMutation({
91
+ mutation: templatesApi.publish,
92
+ onSuccess: () => {
93
+ queryCache.invalidateQueries({ key: ['templates'] })
94
+ },
95
+ })
96
+ }
97
+
98
+ export function useUnpublishTemplateMutation() {
99
+ const queryCache = useQueryCache()
100
+
101
+ return useMutation({
102
+ mutation: templatesApi.unpublish,
103
+ onSuccess: () => {
104
+ queryCache.invalidateQueries({ key: ['templates'] })
105
+ },
106
+ })
107
+ }
108
+
109
+ export function useSaveAsTemplateMutation() {
110
+ const queryCache = useQueryCache()
111
+
112
+ return useMutation({
113
+ mutation: ({ messageGroupId, name }: { messageGroupId: string; name?: string }) =>
114
+ templatesApi.saveFromMessageGroup(messageGroupId, name),
115
+ onSuccess: () => {
116
+ queryCache.invalidateQueries({ key: ['templates'] })
117
+ },
118
+ })
119
+ }
@@ -27,6 +27,7 @@ export const useAppContextStore = defineStore('appContext', () => {
27
27
  () => ctx.dinamicValues?.value ?? [],
28
28
  )
29
29
  const default_tags = computed<string[]>(() => ctx.default_tags?.value ?? [])
30
+ const isAdmin = computed(() => ctx.isAdmin?.value ?? false)
30
31
  const metaWabaId = computed(() => ctx.metaWabaId?.value ?? '')
31
32
  const metaAccessToken = computed(() => ctx.metaAccessToken?.value ?? '')
32
33
 
@@ -88,6 +89,7 @@ export const useAppContextStore = defineStore('appContext', () => {
88
89
  metaAccessToken,
89
90
  owner_id,
90
91
  default_tags,
92
+ isAdmin,
91
93
 
92
94
  isInitialized: computed(() => isInitialized.value),
93
95
 
@@ -520,6 +520,7 @@ 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',
523
524
  } as const
524
525
 
525
526
  export const templateListStatusCellTestIds = {
@@ -831,6 +831,48 @@ Diese Nachrichten müssen funktional sein, nicht werblich.
831
831
  no_preview: 'Keine Vorschau verfügbar',
832
832
  subject: 'Betreff',
833
833
  },
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
+ },
834
876
  public: {
835
877
  select_message: {
836
878
  choose_message: 'Nachricht auswählen...',
@@ -822,6 +822,47 @@ These messages must be functional, not promotional.
822
822
  no_preview: 'No preview available',
823
823
  subject: 'Subject',
824
824
  },
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
+ },
825
866
  public: {
826
867
  select_message: {
827
868
  choose_message: 'Choose a message...',
@@ -830,6 +830,47 @@ Estos mensajes deben ser funcionales, no promocionales.
830
830
  no_preview: 'No hay vista previa disponible',
831
831
  subject: 'Asunto',
832
832
  },
833
+ templates: {
834
+ title: 'Plantillas',
835
+ my_templates: 'Mis plantillas',
836
+ system_templates: 'Plantillas del sistema',
837
+ add_template: 'Nueva plantilla',
838
+ create_template: 'Crear plantilla',
839
+ edit_template: 'Editar plantilla',
840
+ use_template: 'Usar esta plantilla',
841
+ start_from_scratch: 'Empezar desde cero',
842
+ pick_template: 'Elegir una plantilla',
843
+ save_as_template: 'Guardar como plantilla',
844
+ system_template: 'Plantilla de sistema',
845
+ template_name: 'Nombre de plantilla',
846
+ template_name_placeholder: 'Ingrese nombre de plantilla',
847
+ no_templates: 'No hay plantillas disponibles',
848
+ new_message_email: 'Nuevo mensaje de email',
849
+ new_message_whatsapp: 'Nuevo mensaje de WhatsApp',
850
+ dialogs: {
851
+ delete: {
852
+ title: 'Eliminar plantilla',
853
+ message: 'Esta acción no se puede deshacer. ¿Está seguro de que desea eliminar {name}?',
854
+ success: '"{name}" ha sido eliminado.',
855
+ },
856
+ publish: {
857
+ title: 'Publicar plantilla',
858
+ message: '¿Publicar "{name}" para hacerla disponible a todos los usuarios?',
859
+ success: '"{name}" ha sido publicado.',
860
+ },
861
+ unpublish: {
862
+ title: 'Ocultar plantilla',
863
+ message: '¿Ocultar "{name}"? Ya no será visible para otros usuarios.',
864
+ success: '"{name}" ha sido ocultado.',
865
+ },
866
+ save_as_template: {
867
+ title: 'Guardar como plantilla',
868
+ message: '¿Guardar este mensaje como plantilla reutilizable?',
869
+ success: 'Plantilla guardada exitosamente.',
870
+ },
871
+ },
872
+ copy_success: 'Mensaje creado a partir de la plantilla.',
873
+ },
833
874
  public: {
834
875
  select_message: {
835
876
  choose_message: 'Elige un mensaje...',
@@ -834,6 +834,47 @@ Ces messages doivent être fonctionnels, et non promotionnels.
834
834
  no_preview: 'Aucun aperçu disponible',
835
835
  subject: 'Objet',
836
836
  },
837
+ templates: {
838
+ title: 'Modèles',
839
+ my_templates: 'Mes modèles',
840
+ system_templates: 'Modèles système',
841
+ add_template: 'Nouveau modèle',
842
+ create_template: 'Créer un modèle',
843
+ edit_template: 'Modifier le modèle',
844
+ use_template: 'Utiliser ce modèle',
845
+ start_from_scratch: 'Partir de zéro',
846
+ pick_template: 'Choisir un modèle',
847
+ save_as_template: 'Enregistrer comme modèle',
848
+ system_template: 'Modèle système',
849
+ template_name: 'Nom du modèle',
850
+ template_name_placeholder: 'Entrez le nom du modèle',
851
+ no_templates: 'Aucun modèle disponible',
852
+ new_message_email: 'Nouveau message email',
853
+ new_message_whatsapp: 'Nouveau message WhatsApp',
854
+ dialogs: {
855
+ delete: {
856
+ title: 'Supprimer le modèle',
857
+ message: 'Cette action est irréversible. Êtes-vous sûr de vouloir supprimer {name} ?',
858
+ success: '"{name}" a été supprimé.',
859
+ },
860
+ publish: {
861
+ title: 'Publier le modèle',
862
+ message: 'Publier "{name}" pour le rendre accessible à tous les utilisateurs ?',
863
+ success: '"{name}" a été publié.',
864
+ },
865
+ unpublish: {
866
+ title: 'Masquer le modèle',
867
+ message: 'Masquer "{name}" ? Il ne sera plus visible pour les autres utilisateurs.',
868
+ success: '"{name}" a été masqué.',
869
+ },
870
+ save_as_template: {
871
+ title: 'Enregistrer comme modèle',
872
+ message: 'Enregistrer ce message comme modèle réutilisable ?',
873
+ success: 'Modèle enregistré avec succès.',
874
+ },
875
+ },
876
+ copy_success: 'Message créé à partir du modèle.',
877
+ },
837
878
  public: {
838
879
  select_message: {
839
880
  choose_message: 'Choisir un message...',
@@ -828,6 +828,47 @@ Questi messaggi devono essere funzionali, non promozionali.
828
828
  no_preview: 'Nessuna anteprima disponibile',
829
829
  subject: 'Oggetto',
830
830
  },
831
+ templates: {
832
+ title: 'Modelli',
833
+ my_templates: 'I miei modelli',
834
+ system_templates: 'Modelli di sistema',
835
+ add_template: 'Nuovo modello',
836
+ create_template: 'Crea modello',
837
+ edit_template: 'Modifica modello',
838
+ use_template: 'Usa questo modello',
839
+ start_from_scratch: 'Inizia da zero',
840
+ pick_template: 'Scegli un modello',
841
+ save_as_template: 'Salva come modello',
842
+ system_template: 'Modello di sistema',
843
+ template_name: 'Nome modello',
844
+ template_name_placeholder: 'Inserisci nome modello',
845
+ no_templates: 'Nessun modello disponibile',
846
+ new_message_email: 'Nuovo messaggio email',
847
+ new_message_whatsapp: 'Nuovo messaggio WhatsApp',
848
+ dialogs: {
849
+ delete: {
850
+ title: 'Elimina modello',
851
+ message: 'Questa azione non può essere annullata. Sei sicuro di voler eliminare {name}?',
852
+ success: '"{name}" è stato eliminato.',
853
+ },
854
+ publish: {
855
+ title: 'Pubblica modello',
856
+ message: 'Pubblicare "{name}" per renderlo disponibile a tutti gli utenti?',
857
+ success: '"{name}" è stato pubblicato.',
858
+ },
859
+ unpublish: {
860
+ title: 'Nascondi modello',
861
+ message: 'Nascondere "{name}"? Non sarà più visibile agli altri utenti.',
862
+ success: '"{name}" è stato nascosto.',
863
+ },
864
+ save_as_template: {
865
+ title: 'Salva come modello',
866
+ message: 'Salvare questo messaggio come modello riutilizzabile?',
867
+ success: 'Modello salvato con successo.',
868
+ },
869
+ },
870
+ copy_success: 'Messaggio creato dal modello.',
871
+ },
831
872
  public: {
832
873
  select_message: {
833
874
  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": "1.0.27",
3
+ "version": "4.0.2-templates.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -30,7 +30,6 @@
30
30
  "@vueuse/core": "^14.3.0",
31
31
  "@vueuse/nuxt": "^14.3.0",
32
32
  "colorthief": "^3.3.1",
33
- "jwt-decode": "^4.0.0",
34
33
  "monaco-editor": "^0.55.1",
35
34
  "nuxt": "^4.4.5",
36
35
  "ofetch": "^1.5.1",
@@ -39,7 +38,7 @@
39
38
  "vue-router": "^5.0.7",
40
39
  "vue3-emoji-picker": "^1.1.8",
41
40
  "zod": "^4.4.3",
42
- "@dev.smartpricing/message-composer-utils": "3.1.27"
41
+ "@dev.smartpricing/message-composer-utils": "4.0.3-templates.0"
43
42
  },
44
43
  "devDependencies": {
45
44
  "@nuxt/eslint": "^1.15.2",
@@ -1,7 +0,0 @@
1
- export default defineNuxtPlugin(() => {
2
- onNuxtReady(() => {
3
- const adminStore = useAdminStore()
4
- const { shift_space } = useMagicKeys()
5
- whenever(shift_space!, () => adminStore.toggleAdminMode())
6
- })
7
- })
@@ -1,50 +0,0 @@
1
- import { jwtDecode } from 'jwt-decode'
2
-
3
- interface TokenData {
4
- id: number
5
- role: string
6
- accounting: boolean
7
- email: string
8
- }
9
-
10
- export const useAdminStore = defineStore('admin', () => {
11
- const appContext = useAppContextStore()
12
- const adminModeActive = ref(false)
13
-
14
- const isAccounting = computed(() => {
15
- const token = appContext.authenticationToken
16
- if (!token) return false
17
- try {
18
- const payload = jwtDecode<TokenData>(token)
19
- return payload.accounting === true
20
- } catch {
21
- return false
22
- }
23
- })
24
-
25
- function activateAdminMode() {
26
- if (!isAccounting.value) return
27
- adminModeActive.value = true
28
- }
29
-
30
- function deactivateAdminMode() {
31
- adminModeActive.value = false
32
- }
33
-
34
- function toggleAdminMode() {
35
- if (!isAccounting.value) {
36
- adminModeActive.value = false
37
- return
38
- }
39
- adminModeActive.value = !adminModeActive.value
40
- }
41
-
42
- const isAdminModeActive = computed(() => adminModeActive.value)
43
-
44
- return {
45
- activateAdminMode,
46
- deactivateAdminMode,
47
- isAdminModeActive,
48
- toggleAdminMode,
49
- }
50
- })
File without changes