@dev.smartpricing/message-composer-layer 4.3.2-chat.0 → 4.3.2-fix.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.
- package/app/api/client.ts +3 -1
- package/app/api/dynamicValues.ts +2 -5
- package/app/api/messageGroups.ts +12 -23
- package/app/components/Common/MessageGroupSelect.vue +8 -109
- package/app/components/Composer/MessageLibrary.vue +7 -17
- package/app/components/Editor/RichTextEditor.vue +8 -4
- package/app/components/TemplateList/BaseTable.vue +108 -156
- package/app/components/Whatsapp/Editor/Header.vue +1 -4
- package/app/components/Whatsapp/Preview.vue +4 -2
- package/app/components/Whatsapp/Translator/Header.vue +7 -6
- package/app/composables/composerContext.ts +2 -4
- package/app/composables/useTemplateOperations.ts +3 -13
- package/app/layouts/default.vue +0 -1
- package/app/queries/dynamicValues.ts +2 -8
- package/app/queries/messageGroups.ts +16 -18
- package/app/stores/appContext.ts +6 -12
- package/app/types/tracking.ts +0 -4
- package/app/utils/testIds.const.ts +0 -6
- package/i18n/locales/de.ts +2 -18
- package/i18n/locales/en.ts +2 -18
- package/i18n/locales/es.ts +2 -18
- package/i18n/locales/fr.ts +2 -18
- package/i18n/locales/it.ts +2 -18
- package/nuxt.config.ts +3 -4
- package/package.json +4 -3
- package/app/components/Common/AppContextErrors.vue +0 -30
- package/app/composables/useMessageGroupList.ts +0 -186
- package/app/composables/usePagedMessageGroups.ts +0 -66
|
@@ -513,16 +513,11 @@ export const templateListTableTestIds = {
|
|
|
513
513
|
searchInput: 'template-list-table-search-input',
|
|
514
514
|
categorySelect: 'template-list-table-category-select',
|
|
515
515
|
statusSelect: 'template-list-table-status-select',
|
|
516
|
-
productSelect: 'template-list-table-product-select',
|
|
517
|
-
visibilitySelect: 'template-list-table-visibility-select',
|
|
518
|
-
pagination: 'template-list-table-pagination',
|
|
519
|
-
paginationSummary: 'template-list-table-pagination-summary',
|
|
520
516
|
card: 'template-list-table-card',
|
|
521
517
|
table: 'template-list-table-table',
|
|
522
518
|
rowName: 'template-list-table-row-name',
|
|
523
519
|
rowInternalBadge: 'template-list-table-row-internal-badge',
|
|
524
520
|
rowCategoryBadge: 'template-list-table-row-category-badge',
|
|
525
|
-
rowProductBadge: 'template-list-table-row-product-badge',
|
|
526
521
|
rowPreview: 'template-list-table-row-preview',
|
|
527
522
|
rowActions: 'template-list-table-row-actions',
|
|
528
523
|
rowEditAction: 'template-list-table-row-edit',
|
|
@@ -756,7 +751,6 @@ export const previewByIdTestIds = {
|
|
|
756
751
|
|
|
757
752
|
export const messageGroupSelectTestIds = {
|
|
758
753
|
root: 'message-group-select-root',
|
|
759
|
-
loadMore: 'message-group-select-load-more',
|
|
760
754
|
} as const
|
|
761
755
|
|
|
762
756
|
/** @deprecated Use `messageGroupSelectTestIds` instead */
|
package/i18n/locales/de.ts
CHANGED
|
@@ -18,7 +18,6 @@ export default {
|
|
|
18
18
|
all: 'Alle',
|
|
19
19
|
copy: 'Kopieren',
|
|
20
20
|
copy_of: 'Kopie von {name}',
|
|
21
|
-
context_error_title: 'Konfigurationsfehler des Composers',
|
|
22
21
|
desktop: 'Desktop',
|
|
23
22
|
error: 'Fehler',
|
|
24
23
|
errore: 'Fehler',
|
|
@@ -59,7 +58,6 @@ export default {
|
|
|
59
58
|
inactive_tooltip:
|
|
60
59
|
'Diese Nachrichtengruppe enthält nicht genehmigte oder fehlende Nachrichten',
|
|
61
60
|
hide_inactive: 'Inaktive ausblenden',
|
|
62
|
-
load_more: 'Mehr laden',
|
|
63
61
|
},
|
|
64
62
|
view_less: 'Weniger anzeigen',
|
|
65
63
|
view_more: 'Mehr anzeigen',
|
|
@@ -785,28 +783,20 @@ export default {
|
|
|
785
783
|
},
|
|
786
784
|
message_library: {
|
|
787
785
|
add_message_group: 'Neue Nachricht',
|
|
788
|
-
all_products: 'Alle Produkte',
|
|
789
786
|
category_placeholder: 'Kategorie auswählen',
|
|
790
787
|
headers: {
|
|
791
788
|
category: 'Kategorie',
|
|
792
789
|
name: 'Name',
|
|
793
790
|
preview: 'Vorschau',
|
|
794
|
-
product: 'Produkt',
|
|
795
791
|
status: 'Status',
|
|
792
|
+
tags: 'Tags',
|
|
796
793
|
updated_at: 'Aktualisiert am',
|
|
797
794
|
},
|
|
798
|
-
pagination_summary: '{from}–{to} von {total}',
|
|
799
|
-
product_placeholder: 'Produkt auswählen',
|
|
800
795
|
render_type_placeholder: 'Render-Typ auswählen',
|
|
801
796
|
search_placeholder: 'Suchen',
|
|
802
797
|
status_placeholder: 'Status auswählen',
|
|
798
|
+
tags_placeholder: 'Nach Tags filtern',
|
|
803
799
|
title: 'Nachrichtenbibliothek',
|
|
804
|
-
visibility: {
|
|
805
|
-
all: 'Alle',
|
|
806
|
-
internal: 'Intern',
|
|
807
|
-
public: 'Öffentlich',
|
|
808
|
-
},
|
|
809
|
-
visibility_placeholder: 'Sichtbarkeit auswählen',
|
|
810
800
|
},
|
|
811
801
|
whatsapp: {
|
|
812
802
|
create_message_group: {
|
|
@@ -839,12 +829,6 @@ export default {
|
|
|
839
829
|
},
|
|
840
830
|
},
|
|
841
831
|
},
|
|
842
|
-
products: {
|
|
843
|
-
chat: 'Smartchat',
|
|
844
|
-
connect: 'Smartconnect',
|
|
845
|
-
pms: 'Smartpms',
|
|
846
|
-
pricing: 'Smartpricing',
|
|
847
|
-
},
|
|
848
832
|
public: {
|
|
849
833
|
select_message: {
|
|
850
834
|
choose_language: 'Sprache auswählen...',
|
package/i18n/locales/en.ts
CHANGED
|
@@ -18,7 +18,6 @@ export default {
|
|
|
18
18
|
all: 'All',
|
|
19
19
|
copy: 'Copy',
|
|
20
20
|
copy_of: 'Copy of {name}',
|
|
21
|
-
context_error_title: 'Composer configuration error',
|
|
22
21
|
desktop: 'Desktop',
|
|
23
22
|
error: 'Error',
|
|
24
23
|
errore: 'Error',
|
|
@@ -57,7 +56,6 @@ export default {
|
|
|
57
56
|
messageGroupSelect: {
|
|
58
57
|
inactive_tooltip: 'This message group has unapproved or missing messages',
|
|
59
58
|
hide_inactive: 'Hide inactive',
|
|
60
|
-
load_more: 'Load more',
|
|
61
59
|
},
|
|
62
60
|
view_less: 'View less',
|
|
63
61
|
view_more: 'View more',
|
|
@@ -774,28 +772,20 @@ export default {
|
|
|
774
772
|
},
|
|
775
773
|
message_library: {
|
|
776
774
|
add_message_group: 'New message',
|
|
777
|
-
all_products: 'All products',
|
|
778
775
|
category_placeholder: 'Select category',
|
|
779
776
|
headers: {
|
|
780
777
|
category: 'Category',
|
|
781
778
|
name: 'Name',
|
|
782
779
|
preview: 'Preview',
|
|
783
|
-
product: 'Product',
|
|
784
780
|
status: 'Status',
|
|
781
|
+
tags: 'Tags',
|
|
785
782
|
updated_at: 'Updated at',
|
|
786
783
|
},
|
|
787
|
-
pagination_summary: '{from}–{to} of {total}',
|
|
788
|
-
product_placeholder: 'Select product',
|
|
789
784
|
render_type_placeholder: 'Select render type',
|
|
790
785
|
search_placeholder: 'Search',
|
|
791
786
|
status_placeholder: 'Select status',
|
|
787
|
+
tags_placeholder: 'Filter by tags',
|
|
792
788
|
title: 'Messages',
|
|
793
|
-
visibility: {
|
|
794
|
-
all: 'All',
|
|
795
|
-
internal: 'Internal',
|
|
796
|
-
public: 'Public',
|
|
797
|
-
},
|
|
798
|
-
visibility_placeholder: 'Select visibility',
|
|
799
789
|
},
|
|
800
790
|
whatsapp: {
|
|
801
791
|
create_message_group: {
|
|
@@ -828,12 +818,6 @@ export default {
|
|
|
828
818
|
},
|
|
829
819
|
},
|
|
830
820
|
},
|
|
831
|
-
products: {
|
|
832
|
-
chat: 'Smartchat',
|
|
833
|
-
connect: 'Smartconnect',
|
|
834
|
-
pms: 'Smartpms',
|
|
835
|
-
pricing: 'Smartpricing',
|
|
836
|
-
},
|
|
837
821
|
public: {
|
|
838
822
|
select_message: {
|
|
839
823
|
choose_language: 'Choose a language...',
|
package/i18n/locales/es.ts
CHANGED
|
@@ -18,7 +18,6 @@ export default {
|
|
|
18
18
|
all: 'Todos',
|
|
19
19
|
copy: 'Copiar',
|
|
20
20
|
copy_of: 'Copia de {name}',
|
|
21
|
-
context_error_title: 'Error de configuración del composer',
|
|
22
21
|
desktop: 'Escritorio',
|
|
23
22
|
error: 'Error',
|
|
24
23
|
errore: 'Error',
|
|
@@ -58,7 +57,6 @@ export default {
|
|
|
58
57
|
messageGroupSelect: {
|
|
59
58
|
inactive_tooltip: 'Este grupo de mensajes tiene mensajes no aprobados o faltantes',
|
|
60
59
|
hide_inactive: 'Ocultar inactivos',
|
|
61
|
-
load_more: 'Cargar más',
|
|
62
60
|
},
|
|
63
61
|
view_less: 'Ver menos',
|
|
64
62
|
view_more: 'Ver más',
|
|
@@ -783,28 +781,20 @@ export default {
|
|
|
783
781
|
},
|
|
784
782
|
message_library: {
|
|
785
783
|
add_message_group: 'Nuevo mensaje',
|
|
786
|
-
all_products: 'Todos los productos',
|
|
787
784
|
category_placeholder: 'Seleccionar categoría',
|
|
788
785
|
headers: {
|
|
789
786
|
category: 'Categoría',
|
|
790
787
|
name: 'Nombre',
|
|
791
788
|
preview: 'Vista previa',
|
|
792
|
-
product: 'Producto',
|
|
793
789
|
status: 'Estado',
|
|
790
|
+
tags: 'Etiquetas',
|
|
794
791
|
updated_at: 'Actualizado el',
|
|
795
792
|
},
|
|
796
|
-
pagination_summary: '{from}–{to} de {total}',
|
|
797
|
-
product_placeholder: 'Seleccionar producto',
|
|
798
793
|
render_type_placeholder: 'Seleccionar tipo de renderizado',
|
|
799
794
|
search_placeholder: 'Buscar',
|
|
800
795
|
status_placeholder: 'Seleccionar estado',
|
|
796
|
+
tags_placeholder: 'Filtrar por etiquetas',
|
|
801
797
|
title: 'Libreria de mensajes',
|
|
802
|
-
visibility: {
|
|
803
|
-
all: 'Todas',
|
|
804
|
-
internal: 'Internas',
|
|
805
|
-
public: 'Públicas',
|
|
806
|
-
},
|
|
807
|
-
visibility_placeholder: 'Seleccionar visibilidad',
|
|
808
798
|
},
|
|
809
799
|
whatsapp: {
|
|
810
800
|
create_message_group: {
|
|
@@ -837,12 +827,6 @@ export default {
|
|
|
837
827
|
},
|
|
838
828
|
},
|
|
839
829
|
},
|
|
840
|
-
products: {
|
|
841
|
-
chat: 'Smartchat',
|
|
842
|
-
connect: 'Smartconnect',
|
|
843
|
-
pms: 'Smartpms',
|
|
844
|
-
pricing: 'Smartpricing',
|
|
845
|
-
},
|
|
846
830
|
public: {
|
|
847
831
|
select_message: {
|
|
848
832
|
choose_language: 'Elige un idioma...',
|
package/i18n/locales/fr.ts
CHANGED
|
@@ -18,7 +18,6 @@ export default {
|
|
|
18
18
|
all: 'Tous',
|
|
19
19
|
copy: 'Copier',
|
|
20
20
|
copy_of: 'Copie de {name}',
|
|
21
|
-
context_error_title: 'Erreur de configuration du composer',
|
|
22
21
|
desktop: 'Bureau',
|
|
23
22
|
error: 'Erreur',
|
|
24
23
|
errore: 'Erreur',
|
|
@@ -58,7 +57,6 @@ export default {
|
|
|
58
57
|
messageGroupSelect: {
|
|
59
58
|
inactive_tooltip: 'Ce groupe de messages contient des messages non approuvés ou manquants',
|
|
60
59
|
hide_inactive: 'Masquer inactifs',
|
|
61
|
-
load_more: 'Charger plus',
|
|
62
60
|
},
|
|
63
61
|
view_less: 'Afficher moins',
|
|
64
62
|
view_more: 'Afficher plus',
|
|
@@ -787,28 +785,20 @@ export default {
|
|
|
787
785
|
},
|
|
788
786
|
message_library: {
|
|
789
787
|
add_message_group: 'Nouveau message',
|
|
790
|
-
all_products: 'Tous les produits',
|
|
791
788
|
category_placeholder: 'Sélectionner une catégorie',
|
|
792
789
|
headers: {
|
|
793
790
|
category: 'Catégorie',
|
|
794
791
|
name: 'Nom',
|
|
795
792
|
preview: 'Aperçu',
|
|
796
|
-
product: 'Produit',
|
|
797
793
|
status: 'Statut',
|
|
794
|
+
tags: 'Tags',
|
|
798
795
|
updated_at: 'Mis à jour le',
|
|
799
796
|
},
|
|
800
|
-
pagination_summary: '{from}–{to} sur {total}',
|
|
801
|
-
product_placeholder: 'Sélectionner un produit',
|
|
802
797
|
render_type_placeholder: 'Sélectionner le type de rendu',
|
|
803
798
|
search_placeholder: 'Rechercher',
|
|
804
799
|
status_placeholder: 'Sélectionner un statut',
|
|
800
|
+
tags_placeholder: 'Filtrer par tags',
|
|
805
801
|
title: 'Bibliothèque de messages',
|
|
806
|
-
visibility: {
|
|
807
|
-
all: 'Toutes',
|
|
808
|
-
internal: 'Internes',
|
|
809
|
-
public: 'Publiques',
|
|
810
|
-
},
|
|
811
|
-
visibility_placeholder: 'Sélectionner la visibilité',
|
|
812
802
|
},
|
|
813
803
|
whatsapp: {
|
|
814
804
|
create_message_group: {
|
|
@@ -841,12 +831,6 @@ export default {
|
|
|
841
831
|
},
|
|
842
832
|
},
|
|
843
833
|
},
|
|
844
|
-
products: {
|
|
845
|
-
chat: 'Smartchat',
|
|
846
|
-
connect: 'Smartconnect',
|
|
847
|
-
pms: 'Smartpms',
|
|
848
|
-
pricing: 'Smartpricing',
|
|
849
|
-
},
|
|
850
834
|
public: {
|
|
851
835
|
select_message: {
|
|
852
836
|
choose_language: 'Choisir une langue...',
|
package/i18n/locales/it.ts
CHANGED
|
@@ -18,7 +18,6 @@ export default {
|
|
|
18
18
|
all: 'Tutti',
|
|
19
19
|
copy: 'Copia',
|
|
20
20
|
copy_of: 'Copia di {name}',
|
|
21
|
-
context_error_title: 'Errore di configurazione del composer',
|
|
22
21
|
desktop: 'Desktop',
|
|
23
22
|
error: 'Errore',
|
|
24
23
|
errore: 'Errore',
|
|
@@ -57,7 +56,6 @@ export default {
|
|
|
57
56
|
messageGroupSelect: {
|
|
58
57
|
inactive_tooltip: 'Questo gruppo di messaggi contiene messaggi non approvati o mancanti',
|
|
59
58
|
hide_inactive: 'Nascondi inattivi',
|
|
60
|
-
load_more: 'Carica altri',
|
|
61
59
|
},
|
|
62
60
|
view_less: 'Mostra meno',
|
|
63
61
|
view_more: 'Mostra di più',
|
|
@@ -780,28 +778,20 @@ export default {
|
|
|
780
778
|
},
|
|
781
779
|
message_library: {
|
|
782
780
|
add_message_group: 'Nuovo messaggio',
|
|
783
|
-
all_products: 'Tutti i prodotti',
|
|
784
781
|
category_placeholder: 'Seleziona categoria',
|
|
785
782
|
headers: {
|
|
786
783
|
category: 'Categoria',
|
|
787
784
|
name: 'Nome',
|
|
788
785
|
preview: 'Anteprima',
|
|
789
|
-
product: 'Prodotto',
|
|
790
786
|
status: 'Stato',
|
|
787
|
+
tags: 'Tag',
|
|
791
788
|
updated_at: 'Aggiornato il',
|
|
792
789
|
},
|
|
793
|
-
pagination_summary: '{from}–{to} di {total}',
|
|
794
|
-
product_placeholder: 'Seleziona prodotto',
|
|
795
790
|
render_type_placeholder: 'Seleziona tipo di rendering',
|
|
796
791
|
search_placeholder: 'Cerca',
|
|
797
792
|
status_placeholder: 'Seleziona stato',
|
|
793
|
+
tags_placeholder: 'Filtra per tag',
|
|
798
794
|
title: 'Libreria messaggi',
|
|
799
|
-
visibility: {
|
|
800
|
-
all: 'Tutte',
|
|
801
|
-
internal: 'Interne',
|
|
802
|
-
public: 'Pubbliche',
|
|
803
|
-
},
|
|
804
|
-
visibility_placeholder: 'Seleziona visibilità',
|
|
805
795
|
},
|
|
806
796
|
whatsapp: {
|
|
807
797
|
create_message_group: {
|
|
@@ -834,12 +824,6 @@ export default {
|
|
|
834
824
|
},
|
|
835
825
|
},
|
|
836
826
|
},
|
|
837
|
-
products: {
|
|
838
|
-
chat: 'Smartchat',
|
|
839
|
-
connect: 'Smartconnect',
|
|
840
|
-
pms: 'Smartpms',
|
|
841
|
-
pricing: 'Smartpricing',
|
|
842
|
-
},
|
|
843
827
|
public: {
|
|
844
828
|
select_message: {
|
|
845
829
|
choose_language: 'Scegli una lingua...',
|
package/nuxt.config.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
export default defineNuxtConfig({
|
|
2
2
|
compatibilityDate: '2025-07-15',
|
|
3
3
|
ssr: false,
|
|
4
|
-
experimental: {
|
|
5
|
-
viteEnvironmentApi: true,
|
|
6
|
-
},
|
|
7
4
|
devtools: { enabled: true },
|
|
8
5
|
imports: {
|
|
9
6
|
dirs: ['stores', 'queries'],
|
|
@@ -57,9 +54,11 @@ export default defineNuxtConfig({
|
|
|
57
54
|
runtimeConfig: {
|
|
58
55
|
public: {
|
|
59
56
|
COMPOSER_API_URL: '',
|
|
57
|
+
// BACKEND_BASE_URL, PLATFORM_URL and CSRF_COOKIE_NAME are declared by
|
|
58
|
+
// platform-layer (extended above); the host sets them once for both.
|
|
60
59
|
BACKEND_BASE_URL: '',
|
|
61
60
|
PLATFORM_URL: '',
|
|
62
|
-
|
|
61
|
+
CSRF_COOKIE_NAME: '',
|
|
63
62
|
},
|
|
64
63
|
},
|
|
65
64
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev.smartpricing/message-composer-layer",
|
|
3
|
-
"version": "4.3.2-
|
|
3
|
+
"version": "4.3.2-fix.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./nuxt.config.ts",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"app.config.ts"
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@dev.smartpricing/platform-layer": "^0.0.23",
|
|
16
15
|
"@formkit/drag-and-drop": "^0.5.3",
|
|
17
16
|
"@pinia/colada": "^1.3.0",
|
|
18
17
|
"@tiptap/extension-character-count": "^3.23.4",
|
|
@@ -36,9 +35,10 @@
|
|
|
36
35
|
"vue-router": "^5.0.7",
|
|
37
36
|
"vue3-emoji-picker": "^1.1.8",
|
|
38
37
|
"zod": "^4.4.3",
|
|
39
|
-
"@dev.smartpricing/message-composer-utils": "4.3.2-
|
|
38
|
+
"@dev.smartpricing/message-composer-utils": "4.3.2-fix.1"
|
|
40
39
|
},
|
|
41
40
|
"devDependencies": {
|
|
41
|
+
"@dev.smartpricing/platform-layer": "^0.0.23",
|
|
42
42
|
"@nuxt/eslint": "^1.15.2",
|
|
43
43
|
"@nuxt/test-utils": "^4.0.3",
|
|
44
44
|
"@playwright/test": "^1.60.0",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"vue-tsc": "^3.2.9"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
+
"@dev.smartpricing/platform-layer": ">=0.0.20",
|
|
51
52
|
"nuxt-ui-layer": ">=1.3.0"
|
|
52
53
|
},
|
|
53
54
|
"scripts": {
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { useAppContextStore } from '~/stores/appContext'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Host-integration problems reported by the app-context store (missing
|
|
6
|
-
* Product, missing Meta config, ...). Shown persistently: the host has to fix
|
|
7
|
-
* its context, the user cannot.
|
|
8
|
-
*/
|
|
9
|
-
const { validationErrors } = storeToRefs(useAppContextStore())
|
|
10
|
-
const messages = computed(() => Object.values(validationErrors.value))
|
|
11
|
-
</script>
|
|
12
|
-
|
|
13
|
-
<template>
|
|
14
|
-
<UAlert
|
|
15
|
-
v-if="messages.length"
|
|
16
|
-
color="error"
|
|
17
|
-
variant="subtle"
|
|
18
|
-
icon="i-lucide-alert-triangle"
|
|
19
|
-
:title="$t('common.context_error_title')"
|
|
20
|
-
class="rounded-none"
|
|
21
|
-
>
|
|
22
|
-
<template #description>
|
|
23
|
-
<ul class="list-disc pl-4 space-y-1">
|
|
24
|
-
<li v-for="message in messages" :key="message">
|
|
25
|
-
{{ message }}
|
|
26
|
-
</li>
|
|
27
|
-
</ul>
|
|
28
|
-
</template>
|
|
29
|
-
</UAlert>
|
|
30
|
-
</template>
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import type { MaybeRefOrGetter } from 'vue'
|
|
2
|
-
import {
|
|
3
|
-
MESSAGE_GROUP_CATEGORIES,
|
|
4
|
-
MESSAGE_GROUP_LIST_SORT,
|
|
5
|
-
MESSAGE_GROUP_LIST_VISIBILITY,
|
|
6
|
-
MESSAGE_GROUP_PRODUCTS,
|
|
7
|
-
MESSAGE_STATUS,
|
|
8
|
-
SORT_ORDER,
|
|
9
|
-
type MessageGroupCategory,
|
|
10
|
-
type MessageGroupListSort,
|
|
11
|
-
type MessageGroupListVisibility,
|
|
12
|
-
type MessageGroupProduct,
|
|
13
|
-
type MessageStatus,
|
|
14
|
-
type RenderType,
|
|
15
|
-
type SortOrder,
|
|
16
|
-
} from '@dev.smartpricing/message-composer-utils/types'
|
|
17
|
-
import { usePagedMessageGroups } from '~/composables/usePagedMessageGroups'
|
|
18
|
-
import { useAppContextStore } from '~/stores/appContext'
|
|
19
|
-
|
|
20
|
-
/** Sentinel for "no filter" in the selects; never sent to the server. */
|
|
21
|
-
export const ALL_FILTER = 'all'
|
|
22
|
-
|
|
23
|
-
export interface MessageGroupListFilters {
|
|
24
|
-
search: string
|
|
25
|
-
category: MessageGroupCategory | typeof ALL_FILTER
|
|
26
|
-
status: MessageStatus | typeof ALL_FILTER
|
|
27
|
-
product: MessageGroupProduct | typeof ALL_FILTER
|
|
28
|
-
/** Only admins may ask for `internal` or `all`; non-admins always request `public`. */
|
|
29
|
-
visibility: MessageGroupListVisibility
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface MessageGroupListSorting {
|
|
33
|
-
sort: MessageGroupListSort
|
|
34
|
-
order: SortOrder
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface MessageGroupListState extends MessageGroupListFilters, MessageGroupListSorting {
|
|
38
|
-
page: number
|
|
39
|
-
perPage: number
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const STORAGE_KEY = 'message_library_list_state'
|
|
43
|
-
const PER_PAGE = 25
|
|
44
|
-
|
|
45
|
-
function isOneOf<T extends string>(values: readonly T[], value: unknown): value is T {
|
|
46
|
-
return typeof value === 'string' && (values as readonly string[]).includes(value)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type StoredState = Partial<Record<keyof MessageGroupListState | 'contextProduct', unknown>>
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Reads the state saved by this composer session. State is scoped to the
|
|
53
|
-
* context Product it was saved under: after a host Product switch the library
|
|
54
|
-
* must open prefiltered on the new Product, not on a stale choice.
|
|
55
|
-
*/
|
|
56
|
-
function readStoredState(contextProduct: MessageGroupProduct | undefined): StoredState {
|
|
57
|
-
try {
|
|
58
|
-
const raw = sessionStorage.getItem(STORAGE_KEY)
|
|
59
|
-
const parsed: unknown = raw ? JSON.parse(raw) : null
|
|
60
|
-
if (!parsed || typeof parsed !== 'object') return {}
|
|
61
|
-
const stored = parsed as StoredState
|
|
62
|
-
return stored.contextProduct === contextProduct ? stored : {}
|
|
63
|
-
} catch {
|
|
64
|
-
return {}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Restores the previous list state of this composer session, field by field:
|
|
70
|
-
* anything missing or no longer valid falls back to the default.
|
|
71
|
-
*/
|
|
72
|
-
function restoreState(
|
|
73
|
-
defaults: MessageGroupListState,
|
|
74
|
-
contextProduct: MessageGroupProduct | undefined,
|
|
75
|
-
): MessageGroupListState {
|
|
76
|
-
const stored = readStoredState(contextProduct)
|
|
77
|
-
const withAll = <T extends string>(values: readonly T[]) => [...values, ALL_FILTER] as const
|
|
78
|
-
return {
|
|
79
|
-
search: typeof stored.search === 'string' ? stored.search : defaults.search,
|
|
80
|
-
category: isOneOf(withAll(MESSAGE_GROUP_CATEGORIES), stored.category)
|
|
81
|
-
? stored.category
|
|
82
|
-
: defaults.category,
|
|
83
|
-
status: isOneOf(withAll(MESSAGE_STATUS), stored.status) ? stored.status : defaults.status,
|
|
84
|
-
product: isOneOf(withAll(MESSAGE_GROUP_PRODUCTS), stored.product)
|
|
85
|
-
? stored.product
|
|
86
|
-
: defaults.product,
|
|
87
|
-
visibility: isOneOf(MESSAGE_GROUP_LIST_VISIBILITY, stored.visibility)
|
|
88
|
-
? stored.visibility
|
|
89
|
-
: defaults.visibility,
|
|
90
|
-
sort: isOneOf(MESSAGE_GROUP_LIST_SORT, stored.sort) ? stored.sort : defaults.sort,
|
|
91
|
-
order: isOneOf(SORT_ORDER, stored.order) ? stored.order : defaults.order,
|
|
92
|
-
page:
|
|
93
|
-
Number.isInteger(stored.page) && (stored.page as number) >= 1 ? (stored.page as number) : 1,
|
|
94
|
-
perPage: defaults.perPage,
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** A select value of `all` means "no filter": the param is left out. */
|
|
99
|
-
function unlessAll<T extends string>(value: T | typeof ALL_FILTER): T | undefined {
|
|
100
|
-
return value === ALL_FILTER ? undefined : value
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function persistState(
|
|
104
|
-
state: MessageGroupListState,
|
|
105
|
-
contextProduct: MessageGroupProduct | undefined,
|
|
106
|
-
) {
|
|
107
|
-
try {
|
|
108
|
-
sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ ...state, contextProduct }))
|
|
109
|
-
} catch {
|
|
110
|
-
// Storage unavailable (private mode, quota): the list still works, it just won't be restored.
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Owns the library table's state (filters, sorting, page) as component-scoped
|
|
116
|
-
* reactive state and exposes the server-side paginated query (ADR 0012).
|
|
117
|
-
*
|
|
118
|
-
* - The Product filter starts on the context Product and can be widened to all.
|
|
119
|
-
* - Any filter or sort change lands on page one; search is debounced.
|
|
120
|
-
* - The previous page stays visible while the next one loads.
|
|
121
|
-
* - State survives navigating into a group and back within the session, never the host URL.
|
|
122
|
-
*/
|
|
123
|
-
export function useMessageGroupList(renderType: MaybeRefOrGetter<RenderType>) {
|
|
124
|
-
const appContext = useAppContextStore()
|
|
125
|
-
|
|
126
|
-
const defaults: MessageGroupListState = {
|
|
127
|
-
search: '',
|
|
128
|
-
category: ALL_FILTER,
|
|
129
|
-
status: ALL_FILTER,
|
|
130
|
-
product: appContext.product ?? ALL_FILTER,
|
|
131
|
-
// Admins keep seeing internal and public groups by default, as before the
|
|
132
|
-
// server-side list; the select lets them narrow. Non-admins never send this.
|
|
133
|
-
visibility: appContext.isAdmin ? 'all' : 'public',
|
|
134
|
-
sort: 'updated_at',
|
|
135
|
-
order: 'desc',
|
|
136
|
-
page: 1,
|
|
137
|
-
perPage: PER_PAGE,
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const state = reactive<MessageGroupListState>(restoreState(defaults, appContext.product))
|
|
141
|
-
|
|
142
|
-
const { rows, meta, loading, query } = usePagedMessageGroups({
|
|
143
|
-
search: toRef(state, 'search'),
|
|
144
|
-
page: toRef(state, 'page'),
|
|
145
|
-
perPage: PER_PAGE,
|
|
146
|
-
renderType,
|
|
147
|
-
filters: () => ({
|
|
148
|
-
category: unlessAll(state.category),
|
|
149
|
-
status: unlessAll(state.status),
|
|
150
|
-
product: unlessAll(state.product),
|
|
151
|
-
visibility: appContext.isAdmin ? state.visibility : 'public',
|
|
152
|
-
sort: state.sort,
|
|
153
|
-
order: state.order,
|
|
154
|
-
}),
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
watch(state, (value) => persistState(value, appContext.product), { deep: true })
|
|
158
|
-
|
|
159
|
-
function setFilters(patch: Partial<MessageGroupListFilters>) {
|
|
160
|
-
Object.assign(state, patch)
|
|
161
|
-
// Search resets the page only once the debounced value reaches the query.
|
|
162
|
-
const { search: _search, ...immediate } = patch
|
|
163
|
-
if (Object.keys(immediate).length > 0) state.page = 1
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function setSorting(sorting: MessageGroupListSorting) {
|
|
167
|
-
state.sort = sorting.sort
|
|
168
|
-
state.order = sorting.order
|
|
169
|
-
state.page = 1
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function setPage(page: number) {
|
|
173
|
-
state.page = page
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
state: readonly(state),
|
|
178
|
-
setFilters,
|
|
179
|
-
setSorting,
|
|
180
|
-
setPage,
|
|
181
|
-
rows,
|
|
182
|
-
meta,
|
|
183
|
-
loading,
|
|
184
|
-
refetch: query.refetch,
|
|
185
|
-
}
|
|
186
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { MaybeRefOrGetter, Ref, WatchSource } from 'vue'
|
|
2
|
-
import type { RenderType } from '@dev.smartpricing/message-composer-utils/types'
|
|
3
|
-
import type { ListMessageGroupsOptions } from '~/api/messageGroups'
|
|
4
|
-
import { useMessageGroupListQuery } from '~/queries/messageGroups'
|
|
5
|
-
|
|
6
|
-
export const SEARCH_DEBOUNCE_MS = 300
|
|
7
|
-
|
|
8
|
-
type PagedListFilters = Omit<
|
|
9
|
-
ListMessageGroupsOptions,
|
|
10
|
-
'search' | 'page' | 'per_page' | 'render_type'
|
|
11
|
-
>
|
|
12
|
-
|
|
13
|
-
export interface PagedMessageGroupsOptions {
|
|
14
|
-
/** Raw search term as typed; it is debounced and trimmed before it reaches the request. */
|
|
15
|
-
search: Ref<string>
|
|
16
|
-
/** Current page. Lands on one whenever the search settles or a `resetOn` source changes. */
|
|
17
|
-
page: Ref<number>
|
|
18
|
-
perPage: number
|
|
19
|
-
renderType: MaybeRefOrGetter<RenderType | undefined>
|
|
20
|
-
/** Remaining filters and sorting, merged into the request as they are. */
|
|
21
|
-
filters?: MaybeRefOrGetter<PagedListFilters>
|
|
22
|
-
/** Further sources whose change lands on page one, besides the search and render type. */
|
|
23
|
-
resetOn?: WatchSource[]
|
|
24
|
-
/** When false no request is made (the query stays pending). */
|
|
25
|
-
enabled?: MaybeRefOrGetter<boolean>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* One server-side paginated Message Group list (ADR 0012) as a reactive source:
|
|
30
|
-
* debounced search, page reset, request options and the query itself. The
|
|
31
|
-
* library table and the in-composer picker both build on it and own only what
|
|
32
|
-
* differs (filters, page size, how rows are shown).
|
|
33
|
-
*/
|
|
34
|
-
export function usePagedMessageGroups(options: PagedMessageGroupsOptions) {
|
|
35
|
-
const debouncedSearch = refDebounced(options.search, SEARCH_DEBOUNCE_MS)
|
|
36
|
-
|
|
37
|
-
const listOptions = computed<ListMessageGroupsOptions>(() => ({
|
|
38
|
-
...toValue(options.filters),
|
|
39
|
-
render_type: toValue(options.renderType),
|
|
40
|
-
search: debouncedSearch.value.trim() || undefined,
|
|
41
|
-
page: options.page.value,
|
|
42
|
-
per_page: options.perPage,
|
|
43
|
-
}))
|
|
44
|
-
|
|
45
|
-
const query = useMessageGroupListQuery(listOptions, { enabled: options.enabled })
|
|
46
|
-
|
|
47
|
-
// The reset happens in the same tick as the change (sync flush) so the query
|
|
48
|
-
// sees one key change, not two, and the user never opens an empty page.
|
|
49
|
-
watch(
|
|
50
|
-
[debouncedSearch, () => toValue(options.renderType), ...(options.resetOn ?? [])],
|
|
51
|
-
() => {
|
|
52
|
-
options.page.value = 1
|
|
53
|
-
},
|
|
54
|
-
{ flush: 'sync' },
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
debouncedSearch,
|
|
59
|
-
listOptions,
|
|
60
|
-
query,
|
|
61
|
-
rows: computed(() => query.data.value?.data ?? []),
|
|
62
|
-
meta: computed(() => query.data.value?.meta),
|
|
63
|
-
/** True on the first load and while a new page or filter set is fetching over previous data. */
|
|
64
|
-
loading: computed(() => query.isPending.value || query.isPlaceholderData.value),
|
|
65
|
-
}
|
|
66
|
-
}
|