@nyaruka/temba-components 0.166.0 → 0.168.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 (59) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/locales/es.js +3 -1
  3. package/dist/locales/es.js.map +1 -1
  4. package/dist/locales/fr.js +3 -1
  5. package/dist/locales/fr.js.map +1 -1
  6. package/dist/locales/pt.js +3 -1
  7. package/dist/locales/pt.js.map +1 -1
  8. package/dist/temba-components.js +2791 -1666
  9. package/dist/temba-components.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Icons.ts +2 -0
  12. package/src/display/Button.ts +8 -6
  13. package/src/display/Chat.ts +541 -191
  14. package/src/display/Dropdown.ts +40 -17
  15. package/src/display/Label.ts +25 -4
  16. package/src/display/TembaDate.ts +11 -2
  17. package/src/display/Tip.ts +157 -18
  18. package/src/events/eventRenderers.ts +678 -268
  19. package/src/events.ts +16 -1
  20. package/src/flow/NodeEditor.ts +82 -9
  21. package/src/flow/actions/add_contact_urn.ts +1 -0
  22. package/src/flow/actions/play_audio.ts +1 -0
  23. package/src/flow/actions/send_broadcast.ts +17 -1
  24. package/src/flow/actions/send_msg.ts +12 -1
  25. package/src/flow/actions/set_run_result.ts +1 -0
  26. package/src/flow/actions/start_session.ts +10 -1
  27. package/src/flow/nodes/shared-rules.ts +2 -0
  28. package/src/flow/nodes/split_by_airtime.ts +6 -0
  29. package/src/flow/nodes/split_by_expression.ts +1 -0
  30. package/src/flow/nodes/split_by_llm.ts +2 -0
  31. package/src/flow/nodes/split_by_llm_categorize.ts +3 -1
  32. package/src/flow/nodes/split_by_random.ts +2 -1
  33. package/src/flow/nodes/split_by_resthook.ts +4 -1
  34. package/src/flow/nodes/split_by_webhook.ts +9 -1
  35. package/src/flow/types.ts +3 -0
  36. package/src/flow/utils.ts +49 -0
  37. package/src/form/DatePicker.ts +2 -1
  38. package/src/layout/Card.ts +9 -0
  39. package/src/layout/CardLayout.ts +426 -21
  40. package/src/layout/CardStack.ts +4 -1
  41. package/src/list/ContactList.ts +58 -22
  42. package/src/list/ContentList.ts +632 -29
  43. package/src/list/MsgList.ts +4 -2
  44. package/src/list/SortableList.ts +5 -1
  45. package/src/list/TembaMenu.ts +67 -6
  46. package/src/live/ContactChat.ts +655 -336
  47. package/src/live/ContactDetails.ts +961 -88
  48. package/src/live/ContactFieldEditor.ts +46 -11
  49. package/src/live/ContactFields.ts +57 -16
  50. package/src/live/ContactStoreElement.ts +15 -8
  51. package/src/locales/es.ts +3 -2
  52. package/src/locales/fr.ts +3 -2
  53. package/src/locales/pt.ts +3 -2
  54. package/src/simulator/Simulator.ts +28 -0
  55. package/src/styles/pillVariants.ts +13 -1
  56. package/web-test-runner.config.mjs +2 -0
  57. package/xliff/es.xlf +6 -3
  58. package/xliff/fr.xlf +6 -3
  59. package/xliff/pt.xlf +6 -3
@@ -49,6 +49,12 @@ export class ContactList extends ContentList<Contact> {
49
49
  @property({ type: String, attribute: 'fields-endpoint' })
50
50
  fieldsEndpoint = '/api/v2/fields.json';
51
51
 
52
+ /** Anonymous workspaces mask URN values, so instead of the URN
53
+ * column the list shows each contact's ref (served as its own
54
+ * anon-only key by the endpoint). */
55
+ @property({ type: Boolean })
56
+ anon = false;
57
+
52
58
  @state()
53
59
  private featuredFields: any[] = [];
54
60
 
@@ -91,6 +97,16 @@ export class ContactList extends ContentList<Contact> {
91
97
  super.disconnectedCallback();
92
98
  }
93
99
 
100
+ protected willUpdate(changes: PropertyValues): void {
101
+ super.willUpdate(changes);
102
+ // Rebuilding here (rather than in updated()) means a mounted
103
+ // anon attribute is reflected in the first paint instead of
104
+ // flashing the URN header for a frame.
105
+ if (changes.has('anon')) {
106
+ this.columns = this.buildColumns();
107
+ }
108
+ }
109
+
94
110
  protected updated(changes: PropertyValues): void {
95
111
  super.updated(changes);
96
112
  if (changes.has('fieldsEndpoint') && this.fieldsEndpoint) {
@@ -134,27 +150,28 @@ export class ContactList extends ContentList<Contact> {
134
150
  * created on.
135
151
  *
136
152
  * Name leads, with URN, the workspace's custom fields, and the
137
- * last-seen / created-on dates trailing it. Every column sizes to
138
- * its content between min/max bounds none are hard-fixed — so the
139
- * table stays compact and overflows into a horizontal scroll only
140
- * when the field set is genuinely wide. Only Name is pinned to the
141
- * left edge, so identity stays anchored while everything else
142
- * scrolls.
153
+ * last-seen / created-on dates trailing it. Columns through Last Seen
154
+ * size to their content between min/max bounds and can be resized;
155
+ * Created On grows to absorb any remaining browser width. Only Name is
156
+ * pinned to the left edge, so identity stays anchored while everything
157
+ * else scrolls.
143
158
  *
144
159
  * There is deliberately no group-membership column — contacts
145
160
  * routinely belong to dozens of groups, so a groups cell is
146
161
  * noise rather than signal in a list view. */
147
162
  private buildColumns(): ContentListColumn[] {
148
- // Custom-field columns are all left-aligned for simplicity, and
149
- // set no minWidth their natural floor is the column header
150
- // label, which the table's auto layout already honours; maxWidth
151
- // just caps a runaway value.
163
+ // Custom-field columns are all left-aligned for simplicity. Their
164
+ // resize floor is deliberately half the generic 80px floor so users
165
+ // can pack more workspace-specific fields into view; maxWidth caps a
166
+ // runaway value.
152
167
  const fieldColumns: ContentListColumn[] = (this.featuredFields || []).map(
153
168
  (f: any) => ({
154
169
  key: FIELD_PREFIX + f.key,
155
170
  label: f.name || f.label || f.key,
156
171
  sortable: true,
157
- maxWidth: '200px'
172
+ resizeMinWidth: '40px',
173
+ maxWidth: '200px',
174
+ resizable: true
158
175
  })
159
176
  );
160
177
  // Name + URN are the pinned identity columns and are not
@@ -166,14 +183,24 @@ export class ContactList extends ContentList<Contact> {
166
183
  label: 'Name',
167
184
  minWidth: '150px',
168
185
  maxWidth: '260px',
169
- pinned: true
170
- },
171
- {
172
- key: 'urn',
173
- label: 'URN',
174
- minWidth: '120px',
175
- maxWidth: '190px'
186
+ pinned: true,
187
+ resizable: true
176
188
  },
189
+ this.anon
190
+ ? {
191
+ key: 'ref',
192
+ label: 'Ref',
193
+ minWidth: '120px',
194
+ maxWidth: '190px',
195
+ resizable: true
196
+ }
197
+ : {
198
+ key: 'urn',
199
+ label: 'URN',
200
+ minWidth: '120px',
201
+ maxWidth: '190px',
202
+ resizable: true
203
+ },
177
204
  ...fieldColumns,
178
205
  {
179
206
  key: 'last_seen_on',
@@ -181,15 +208,16 @@ export class ContactList extends ContentList<Contact> {
181
208
  sortable: true,
182
209
  minWidth: '96px',
183
210
  maxWidth: '150px',
184
- align: 'right'
211
+ align: 'right',
212
+ resizable: true
185
213
  },
186
214
  {
187
215
  key: 'created_on',
188
216
  label: 'Created on',
189
217
  sortable: true,
190
218
  minWidth: '96px',
191
- maxWidth: '150px',
192
- align: 'right'
219
+ align: 'right',
220
+ grow: true
193
221
  }
194
222
  ];
195
223
  }
@@ -232,6 +260,10 @@ export class ContactList extends ContentList<Contact> {
232
260
  return html`<span class="contact-urn"
233
261
  >${this.primaryUrn(item) || EMPTY}</span
234
262
  >`;
263
+ case 'ref':
264
+ return html`<span class="contact-urn"
265
+ >${(item as any).ref || EMPTY}</span
266
+ >`;
235
267
  case 'last_seen_on':
236
268
  return item.last_seen_on
237
269
  ? html`<temba-date
@@ -281,7 +313,11 @@ export class ContactList extends ContentList<Contact> {
281
313
 
282
314
  private primaryUrn(item: Contact): string {
283
315
  const i = item as any;
284
- if (i.urn) return i.urn;
316
+ if (i.urn) {
317
+ // served as {scheme, display} by the list endpoint, but tolerate
318
+ // a plain string (e.g. the public API's urn field)
319
+ return typeof i.urn === 'string' ? i.urn : i.urn.display || '';
320
+ }
285
321
  if (Array.isArray(i.urns) && i.urns.length > 0) {
286
322
  const u = i.urns[0];
287
323
  return typeof u === 'string'