@dev.smartpricing/message-composer-layer 4.5.1-next.1 → 4.5.1-next.3

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/README.md CHANGED
@@ -85,14 +85,14 @@ watch(
85
85
  )
86
86
  ```
87
87
 
88
- **WABA.** The Host does not supply the WABA: the WABAs follow the Organization and the backend resolves them (ADR [0015](../../docs/adr/0015-message-group-waba-fixed-at-first-submission.md)). WhatsApp is configured when the Host's `channels` include `whatsapp` and the Organization has at least one WABA. A Message Group is submitted to one WABA, chosen at its first submission and fixed from then on; with several WABAs the user picks one when requesting approval. `metaWabaId` in the context is optional and is only the preselection in that picker.
88
+ **WABA.** The Host does not have to supply the WABA: the WABAs follow the Organization and the backend resolves them (ADR [0015](../../docs/adr/0015-message-group-waba-fixed-at-first-submission.md)). WhatsApp is configured when the Host's `channels` include `whatsapp` and the Organization has at least one WABA. A Message Group is submitted to one WABA, chosen at its first submission and fixed from then on; with several WABAs it is the `metaWabaId` the Host passed, when that is one of them, and otherwise the user picks one when requesting approval. `metaWabaId` in the context is optional.
89
89
 
90
90
  ### Upgrading to 4.5.0
91
91
 
92
92
  Nothing is required: a Host that keeps passing `organization_id` and `metaWabaId` behaves as before.
93
93
 
94
94
  - **Removed** from `ComposerContext` and from `AppContextConfig` in utils (no code read them): `authenticationToken`, `strategy`, `metaAccessToken`, `dinamicValues`. Remove them from the `defineComposerContext()` call. The utils types `AppContextState` and `ContextSource` are gone too.
95
- - **Changed**: `metaWabaId` no longer decides where a WhatsApp submission goes and is no longer required for the WhatsApp channel; it preselects the WABA when the user has to pick one.
95
+ - **Changed**: `metaWabaId` no longer decides where a WhatsApp submission goes and is no longer required for the WhatsApp channel; with several WABAs on the Organization it names the one a new Message Group is submitted to, and the user is asked only without it.
96
96
  - **Deprecated**, removed in 5.0.0: the `sm-organization-id` and `x-meta-waba-id` request headers. The layer no longer sends them; the backend still accepts them from Hosts on an older layer.
97
97
  - **New**: `organization_id` is optional, the Organization selector, `useEffectiveOrganization()`, and the `organizationId` and `wabaId` props of the Message Send Flow.
98
98
 
@@ -8,13 +8,11 @@ import type { OrganizationWaba } from '@dev.smartpricing/message-composer-utils/
8
8
  */
9
9
  const props = defineProps<{
10
10
  wabas: OrganizationWaba[]
11
- /** The Host's WABA, already checked to be one of `wabas`. */
12
- preselected?: string
13
11
  }>()
14
12
 
15
13
  const emit = defineEmits<{ close: [wabaId?: string] }>()
16
14
 
17
- const selected = ref<string | undefined>(props.preselected)
15
+ const selected = ref<string>()
18
16
 
19
17
  const items = computed(() =>
20
18
  props.wabas.map((waba) => ({
@@ -5,6 +5,7 @@ import {
5
5
  MESSAGE_GROUP_PRODUCTS,
6
6
  MESSAGE_STATUS,
7
7
  type MessageGroupListSort,
8
+ type MessageGroupProduct,
8
9
  type MessageGroupWithMessageSummary,
9
10
  type PaginationMeta,
10
11
  type RenderType,
@@ -203,8 +204,17 @@ const categoryItems = computed(() =>
203
204
  toSelectItems(MESSAGE_GROUP_CATEGORIES, 'categories', t('common.all')),
204
205
  )
205
206
  const statusItems = computed(() => toSelectItems(MESSAGE_STATUS, 'status', t('common.all')))
207
+ // Only the Products that have Message Groups today. The Host's own Product
208
+ // is always offered, since the filter starts on it.
209
+ const PRODUCTS_WITH_MESSAGES: readonly MessageGroupProduct[] = ['connect', 'chat']
206
210
  const productItems = computed(() =>
207
- toSelectItems(MESSAGE_GROUP_PRODUCTS, 'products', t('pages.message_library.all_products')),
211
+ toSelectItems(
212
+ MESSAGE_GROUP_PRODUCTS.filter(
213
+ (product) => PRODUCTS_WITH_MESSAGES.includes(product) || product === appContext.product,
214
+ ),
215
+ 'products',
216
+ t('pages.message_library.all_products'),
217
+ ),
208
218
  )
209
219
 
210
220
  const visibilityItems = computed(() =>
@@ -322,6 +332,17 @@ const paginationSummary = computed(() => {
322
332
  v-model:sorting="sorting"
323
333
  :data-testid="templateListTableTestIds.table"
324
334
  >
335
+ <!-- Shown instead of the "no data" row while a list with no rows is loading. -->
336
+ <template #loading>
337
+ <div
338
+ class="flex items-center justify-center gap-2 text-muted"
339
+ :data-testid="templateListTableTestIds.loading"
340
+ >
341
+ <UIcon name="ph:circle-notch-bold" class="size-4 animate-spin" />
342
+ <span>{{ t('pages.message_library.loading') }}</span>
343
+ </div>
344
+ </template>
345
+
325
346
  <template #name-cell="{ row }">
326
347
  <div
327
348
  class="flex items-center gap-2"
@@ -17,9 +17,9 @@ export interface ComposerContext {
17
17
  mainLanguage?: Ref<string>
18
18
  default_tags?: Ref<string[]>
19
19
  /**
20
- * The WABA the Host works on. Only a preselection: when the user has to
21
- * pick the WABA of a Message Group, this one is proposed if it is one of
22
- * the Organization's (ADR 0015).
20
+ * The WABA the Host works on. A new Message Group is submitted to it and
21
+ * the user is not asked, as long as it is one of the Organization's
22
+ * (ADR 0015). Without it the user picks when there are several.
23
23
  */
24
24
  metaWabaId?: Ref<string>
25
25
  isAdmin?: Ref<boolean>
@@ -43,9 +43,10 @@ export function useMessageGroupWaba(storedWabaId: MaybeRefOrGetter<string | null
43
43
  )
44
44
 
45
45
  /**
46
- * The WABA to name in a submission. Asks the user only at the first
47
- * submission of a Message Group whose Organization has several WABAs;
48
- * otherwise names none and the backend resolves it. A lookup that has not
46
+ * The WABA to name in a submission. At the first submission of a Message
47
+ * Group whose Organization has several WABAs, it is the Host's when the
48
+ * Host passed one of them, otherwise the user is asked. In every other
49
+ * case none is named and the backend resolves it. A lookup that has not
49
50
  * answered yet is waited for; one that failed leaves it to the backend.
50
51
  * Returns null when the user gives up.
51
52
  */
@@ -55,10 +56,12 @@ export function useMessageGroupWaba(storedWabaId: MaybeRefOrGetter<string | null
55
56
  const candidates = wabas.value
56
57
  if (!candidates || candidates.length < 2) return {}
57
58
 
58
- const preselected = candidates.some((candidate) => candidate.id === metaWabaId.value)
59
- ? metaWabaId.value
60
- : undefined
61
- const chosen = await modal.open({ wabas: candidates, preselected }).result
59
+ const hostWabaId = metaWabaId.value
60
+ if (hostWabaId && candidates.some((candidate) => candidate.id === hostWabaId)) {
61
+ return { wabaId: hostWabaId }
62
+ }
63
+
64
+ const chosen = await modal.open({ wabas: candidates }).result
62
65
 
63
66
  return chosen ? { wabaId: chosen } : null
64
67
  }
@@ -56,7 +56,9 @@ export function useMessageGroupListQuery(
56
56
  // Never across Organizations: a switch must not show the previous one's rows.
57
57
  placeholderData: (previousData, previousEntry) =>
58
58
  previousEntry?.key.at(-1) === listOrganizationId() ? previousData : undefined,
59
- ...enabledOption(queryOptions.enabled),
59
+ // Until an Organization is resolved the list waits (pending, so callers
60
+ // show it as loading) instead of failing into an empty table.
61
+ enabled: () => toValue(queryOptions.enabled ?? true) && !!listOrganizationId(),
60
62
  })
61
63
  }
62
64
 
@@ -23,7 +23,7 @@ export const useAppContextStore = defineStore('appContext', () => {
23
23
  const mainLanguage = computed(() => ctx.mainLanguage?.value ?? 'en')
24
24
  const default_tags = computed<string[]>(() => ctx.default_tags?.value ?? [])
25
25
  const isAdmin = computed(() => ctx.isAdmin?.value ?? false)
26
- // The Host's preselection among the Organization's WABAs (ADR 0015).
26
+ // The WABA the Host works on: the target of a new Message Group (ADR 0015).
27
27
  const metaWabaId = computed(() => ctx.metaWabaId?.value || undefined)
28
28
 
29
29
  // Sync i18n locale when mainLanguage changes
@@ -519,6 +519,7 @@ export const templateListTableTestIds = {
519
519
  paginationSummary: 'template-list-table-pagination-summary',
520
520
  card: 'template-list-table-card',
521
521
  table: 'template-list-table-table',
522
+ loading: 'template-list-table-loading',
522
523
  rowName: 'template-list-table-row-name',
523
524
  rowInternalBadge: 'template-list-table-row-internal-badge',
524
525
  rowCategoryBadge: 'template-list-table-row-category-badge',
@@ -825,6 +825,7 @@ export default {
825
825
  status: 'Status',
826
826
  updated_at: 'Aktualisiert am',
827
827
  },
828
+ loading: 'Nachrichten werden geladen...',
828
829
  pagination_summary: '{from}–{to} von {total}',
829
830
  product_placeholder: 'Produkt auswählen',
830
831
  render_type_placeholder: 'Render-Typ auswählen',
@@ -814,6 +814,7 @@ export default {
814
814
  status: 'Status',
815
815
  updated_at: 'Updated at',
816
816
  },
817
+ loading: 'Loading messages...',
817
818
  pagination_summary: '{from}–{to} of {total}',
818
819
  product_placeholder: 'Select product',
819
820
  render_type_placeholder: 'Select render type',
@@ -823,6 +823,7 @@ export default {
823
823
  status: 'Estado',
824
824
  updated_at: 'Actualizado el',
825
825
  },
826
+ loading: 'Cargando mensajes...',
826
827
  pagination_summary: '{from}–{to} de {total}',
827
828
  product_placeholder: 'Seleccionar producto',
828
829
  render_type_placeholder: 'Seleccionar tipo de renderizado',
@@ -827,6 +827,7 @@ export default {
827
827
  status: 'Statut',
828
828
  updated_at: 'Mis à jour le',
829
829
  },
830
+ loading: 'Chargement des messages...',
830
831
  pagination_summary: '{from}–{to} sur {total}',
831
832
  product_placeholder: 'Sélectionner un produit',
832
833
  render_type_placeholder: 'Sélectionner le type de rendu',
@@ -820,6 +820,7 @@ export default {
820
820
  status: 'Stato',
821
821
  updated_at: 'Aggiornato il',
822
822
  },
823
+ loading: 'Caricamento dei messaggi...',
823
824
  pagination_summary: '{from}–{to} di {total}',
824
825
  product_placeholder: 'Seleziona prodotto',
825
826
  render_type_placeholder: 'Seleziona tipo di rendering',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.smartpricing/message-composer-layer",
3
- "version": "4.5.1-next.1",
3
+ "version": "4.5.1-next.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -35,7 +35,7 @@
35
35
  "vue-router": "^5.0.7",
36
36
  "vue3-emoji-picker": "^1.1.8",
37
37
  "zod": "^4.4.3",
38
- "@dev.smartpricing/message-composer-utils": "4.5.1-next.1"
38
+ "@dev.smartpricing/message-composer-utils": "4.5.1-next.3"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@dev.smartpricing/platform-layer": "^0.0.23",