@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.
@@ -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 */
@@ -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...',
@@ -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...',
@@ -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...',
@@ -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...',
@@ -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
- ENV_CSRF_COOKIE_NAME: 'smt_csrf',
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-chat.0",
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-chat.0"
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
- }