@nyaruka/temba-components 0.122.0 → 0.124.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 (262) hide show
  1. package/.github/copilot-instructions.md +181 -0
  2. package/.github/workflows/build.yml +3 -3
  3. package/.github/workflows/cla.yml +6 -6
  4. package/.github/workflows/copilot-setup-steps.yml +86 -0
  5. package/CHANGELOG.md +44 -0
  6. package/demo/drag-drop-demo.html +141 -0
  7. package/demo/index.html +57 -0
  8. package/demo/test-drag-drop.html +94 -0
  9. package/dist/locales/es.js +1 -0
  10. package/dist/locales/es.js.map +1 -1
  11. package/dist/locales/fr.js +1 -0
  12. package/dist/locales/fr.js.map +1 -1
  13. package/dist/locales/pt.js +1 -0
  14. package/dist/locales/pt.js.map +1 -1
  15. package/dist/temba-components.js +366 -247
  16. package/dist/temba-components.js.map +1 -1
  17. package/out-tsc/src/chart/TembaChart.js +81 -14
  18. package/out-tsc/src/chart/TembaChart.js.map +1 -1
  19. package/out-tsc/src/fields/FieldManager.js +27 -34
  20. package/out-tsc/src/fields/FieldManager.js.map +1 -1
  21. package/out-tsc/src/list/RunList.js +13 -8
  22. package/out-tsc/src/list/RunList.js.map +1 -1
  23. package/out-tsc/src/list/SortableList.js +257 -60
  24. package/out-tsc/src/list/SortableList.js.map +1 -1
  25. package/out-tsc/src/locales/es.js +1 -0
  26. package/out-tsc/src/locales/es.js.map +1 -1
  27. package/out-tsc/src/locales/fr.js +1 -0
  28. package/out-tsc/src/locales/fr.js.map +1 -1
  29. package/out-tsc/src/locales/pt.js +1 -0
  30. package/out-tsc/src/locales/pt.js.map +1 -1
  31. package/out-tsc/src/omnibox/Omnibox.js +1 -1
  32. package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
  33. package/out-tsc/src/options/Options.js +36 -13
  34. package/out-tsc/src/options/Options.js.map +1 -1
  35. package/out-tsc/src/select/Select.js +226 -43
  36. package/out-tsc/src/select/Select.js.map +1 -1
  37. package/out-tsc/src/store/AppState.js +3 -3
  38. package/out-tsc/src/store/AppState.js.map +1 -1
  39. package/out-tsc/src/utils/index.js +6 -1
  40. package/out-tsc/src/utils/index.js.map +1 -1
  41. package/out-tsc/src/vectoricon/VectorIcon.js +2 -1
  42. package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
  43. package/out-tsc/src/webchat/WebChat.js +5 -2
  44. package/out-tsc/src/webchat/WebChat.js.map +1 -1
  45. package/out-tsc/temba-modules.js +0 -2
  46. package/out-tsc/temba-modules.js.map +1 -1
  47. package/out-tsc/test/temba-appstate-language.test.js +176 -0
  48. package/out-tsc/test/temba-appstate-language.test.js.map +1 -0
  49. package/out-tsc/test/temba-chart.test.js +125 -0
  50. package/out-tsc/test/temba-chart.test.js.map +1 -1
  51. package/out-tsc/test/temba-dropdown.test.js +317 -0
  52. package/out-tsc/test/temba-dropdown.test.js.map +1 -0
  53. package/out-tsc/test/temba-flow-editor-node.test.js +273 -0
  54. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -0
  55. package/out-tsc/test/temba-flow-editor.test.js +244 -0
  56. package/out-tsc/test/temba-flow-editor.test.js.map +1 -0
  57. package/out-tsc/test/temba-flow-plumber.test.js +145 -0
  58. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -0
  59. package/out-tsc/test/temba-flow-render.test.js +171 -0
  60. package/out-tsc/test/temba-flow-render.test.js.map +1 -0
  61. package/out-tsc/test/temba-omnibox.test.js +2 -3
  62. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  63. package/out-tsc/test/temba-run-list.test.js +588 -0
  64. package/out-tsc/test/temba-run-list.test.js.map +1 -0
  65. package/out-tsc/test/temba-select.test.js +149 -52
  66. package/out-tsc/test/temba-select.test.js.map +1 -1
  67. package/out-tsc/test/temba-sortable-list.test.js +91 -15
  68. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  69. package/out-tsc/test/temba-toast.test.js +299 -0
  70. package/out-tsc/test/temba-toast.test.js.map +1 -0
  71. package/out-tsc/test/temba-utils-index.test.js +1178 -0
  72. package/out-tsc/test/temba-utils-index.test.js.map +1 -0
  73. package/out-tsc/test/temba-webchat-lightbox-fix.test.js +42 -0
  74. package/out-tsc/test/temba-webchat-lightbox-fix.test.js.map +1 -0
  75. package/out-tsc/test/temba-webchat.test.js +816 -0
  76. package/out-tsc/test/temba-webchat.test.js.map +1 -0
  77. package/out-tsc/test/utils.test.js +33 -1
  78. package/out-tsc/test/utils.test.js.map +1 -1
  79. package/package.json +6 -8
  80. package/screenshots/truth/alert/error.png +0 -0
  81. package/screenshots/truth/alert/info.png +0 -0
  82. package/screenshots/truth/alert/warning.png +0 -0
  83. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  84. package/screenshots/truth/checkbox/checked.png +0 -0
  85. package/screenshots/truth/checkbox/default.png +0 -0
  86. package/screenshots/truth/colorpicker/default.png +0 -0
  87. package/screenshots/truth/colorpicker/focused.png +0 -0
  88. package/screenshots/truth/colorpicker/initialized.png +0 -0
  89. package/screenshots/truth/colorpicker/selected.png +0 -0
  90. package/screenshots/truth/compose/attachments-tab.png +0 -0
  91. package/screenshots/truth/compose/attachments-with-files-focused.png +0 -0
  92. package/screenshots/truth/compose/attachments-with-files.png +0 -0
  93. package/screenshots/truth/compose/intial-text.png +0 -0
  94. package/screenshots/truth/compose/no-counter.png +0 -0
  95. package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
  96. package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
  97. package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
  98. package/screenshots/truth/contacts/badges.png +0 -0
  99. package/screenshots/truth/contacts/chat-failure.png +0 -0
  100. package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
  101. package/screenshots/truth/contacts/chat-for-archived-contact.png +0 -0
  102. package/screenshots/truth/contacts/chat-for-blocked-contact.png +0 -0
  103. package/screenshots/truth/contacts/chat-for-stopped-contact.png +0 -0
  104. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  105. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  106. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  107. package/screenshots/truth/content-menu/button-no-items.png +0 -0
  108. package/screenshots/truth/content-menu/items-and-buttons.png +0 -0
  109. package/screenshots/truth/counter/summary.png +0 -0
  110. package/screenshots/truth/counter/text.png +0 -0
  111. package/screenshots/truth/counter/unicode-variables.png +0 -0
  112. package/screenshots/truth/counter/unicode.png +0 -0
  113. package/screenshots/truth/counter/variable.png +0 -0
  114. package/screenshots/truth/date/date-inline.png +0 -0
  115. package/screenshots/truth/date/date.png +0 -0
  116. package/screenshots/truth/date/datetime.png +0 -0
  117. package/screenshots/truth/date/duration.png +0 -0
  118. package/screenshots/truth/date/timedate.png +0 -0
  119. package/screenshots/truth/datepicker/date-truncated-time.png +0 -0
  120. package/screenshots/truth/datepicker/date.png +0 -0
  121. package/screenshots/truth/datepicker/initial-timezone.png +0 -0
  122. package/screenshots/truth/datepicker/updated-keyboard-date.png +0 -0
  123. package/screenshots/truth/dialog/focused.png +0 -0
  124. package/screenshots/truth/dropdown/after-blur.png +0 -0
  125. package/screenshots/truth/dropdown/bottom-edge-collision.png +0 -0
  126. package/screenshots/truth/dropdown/custom-arrow-size.png +0 -0
  127. package/screenshots/truth/dropdown/default.png +0 -0
  128. package/screenshots/truth/dropdown/narrow-toggle.png +0 -0
  129. package/screenshots/truth/dropdown/no-mask.png +0 -0
  130. package/screenshots/truth/dropdown/opened.png +0 -0
  131. package/screenshots/truth/dropdown/positioned.png +0 -0
  132. package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
  133. package/screenshots/truth/dropdown/with-mask.png +0 -0
  134. package/screenshots/truth/flow/editor-basic.png +0 -0
  135. package/screenshots/truth/label/custom.png +0 -0
  136. package/screenshots/truth/label/danger.png +0 -0
  137. package/screenshots/truth/label/dark.png +0 -0
  138. package/screenshots/truth/label/default-icon.png +0 -0
  139. package/screenshots/truth/label/no-icon.png +0 -0
  140. package/screenshots/truth/label/primary.png +0 -0
  141. package/screenshots/truth/label/secondary.png +0 -0
  142. package/screenshots/truth/label/shadow.png +0 -0
  143. package/screenshots/truth/label/tertiary.png +0 -0
  144. package/screenshots/truth/lightbox/img-zoomed.png +0 -0
  145. package/screenshots/truth/list/fields-dragging.png +0 -0
  146. package/screenshots/truth/list/fields-filtered.png +0 -0
  147. package/screenshots/truth/list/fields-hovered.png +0 -0
  148. package/screenshots/truth/list/fields.png +0 -0
  149. package/screenshots/truth/list/items-selected.png +0 -0
  150. package/screenshots/truth/list/items-updated.png +0 -0
  151. package/screenshots/truth/list/items.png +0 -0
  152. package/screenshots/truth/list/sortable-dragging.png +0 -0
  153. package/screenshots/truth/list/sortable-dropped.png +0 -0
  154. package/screenshots/truth/list/sortable.png +0 -0
  155. package/screenshots/truth/menu/menu-focused-with items.png +0 -0
  156. package/screenshots/truth/menu/menu-refresh-1.png +0 -0
  157. package/screenshots/truth/menu/menu-refresh-2.png +0 -0
  158. package/screenshots/truth/menu/menu-root.png +0 -0
  159. package/screenshots/truth/menu/menu-submenu.png +0 -0
  160. package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
  161. package/screenshots/truth/menu/menu-tasks.png +0 -0
  162. package/screenshots/truth/modax/form.png +0 -0
  163. package/screenshots/truth/modax/simple.png +0 -0
  164. package/screenshots/truth/omnibox/selected.png +0 -0
  165. package/screenshots/truth/options/block.png +0 -0
  166. package/screenshots/truth/run-list/basic.png +0 -0
  167. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  168. package/screenshots/truth/select/disabled-selection.png +0 -0
  169. package/screenshots/truth/select/disabled.png +0 -0
  170. package/screenshots/truth/select/embedded.png +0 -0
  171. package/screenshots/truth/select/empty-options.png +0 -0
  172. package/screenshots/truth/select/expression-selected.png +0 -0
  173. package/screenshots/truth/select/expressions.png +0 -0
  174. package/screenshots/truth/select/functions.png +0 -0
  175. package/screenshots/truth/select/local-options.png +0 -0
  176. package/screenshots/truth/select/multi-reorder-final.png +0 -0
  177. package/screenshots/truth/select/multi-reorder-initial.png +0 -0
  178. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  179. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  180. package/screenshots/truth/select/remote-options.png +0 -0
  181. package/screenshots/truth/select/search-enabled.png +0 -0
  182. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  183. package/screenshots/truth/select/search-selected-focus.png +0 -0
  184. package/screenshots/truth/select/search-selected.png +0 -0
  185. package/screenshots/truth/select/search-with-selected.png +0 -0
  186. package/screenshots/truth/select/searching.png +0 -0
  187. package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
  188. package/screenshots/truth/select/selected-multi.png +0 -0
  189. package/screenshots/truth/select/selected-single.png +0 -0
  190. package/screenshots/truth/select/selection-clearable.png +0 -0
  191. package/screenshots/truth/select/static-initial-value.png +0 -0
  192. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  193. package/screenshots/truth/select/truncated-selection.png +0 -0
  194. package/screenshots/truth/select/with-placeholder.png +0 -0
  195. package/screenshots/truth/select/without-placeholder.png +0 -0
  196. package/screenshots/truth/slider/custom-min-custom-max-valid-value.png +0 -0
  197. package/screenshots/truth/slider/custom-min-default-max-no-value.png +0 -0
  198. package/screenshots/truth/slider/default-min-custom-max-no-value.png +0 -0
  199. package/screenshots/truth/slider/default-min-default-max-invalid-value.png +0 -0
  200. package/screenshots/truth/slider/default-min-default-max-valid-value.png +0 -0
  201. package/screenshots/truth/slider/update-slider-on-value-change.png +0 -0
  202. package/screenshots/truth/templates/default.png +0 -0
  203. package/screenshots/truth/templates/unapproved.png +0 -0
  204. package/screenshots/truth/textinput/input-disabled.png +0 -0
  205. package/screenshots/truth/textinput/input-focused.png +0 -0
  206. package/screenshots/truth/textinput/input-form.png +0 -0
  207. package/screenshots/truth/textinput/input-inserted.png +0 -0
  208. package/screenshots/truth/textinput/input-placeholder.png +0 -0
  209. package/screenshots/truth/textinput/input-updated.png +0 -0
  210. package/screenshots/truth/textinput/input.png +0 -0
  211. package/screenshots/truth/textinput/textarea-focused.png +0 -0
  212. package/screenshots/truth/textinput/textarea.png +0 -0
  213. package/screenshots/truth/tip/bottom.png +0 -0
  214. package/screenshots/truth/tip/left.png +0 -0
  215. package/screenshots/truth/tip/right.png +0 -0
  216. package/screenshots/truth/tip/top.png +0 -0
  217. package/screenshots/truth/webchat/closed-widget.png +0 -0
  218. package/screenshots/truth/webchat/connected-state.png +0 -0
  219. package/screenshots/truth/webchat/connecting-state.png +0 -0
  220. package/screenshots/truth/webchat/disconnected-state.png +0 -0
  221. package/screenshots/truth/webchat/opened-widget.png +0 -0
  222. package/src/chart/TembaChart.ts +86 -15
  223. package/src/fields/FieldManager.ts +30 -38
  224. package/src/list/RunList.ts +11 -8
  225. package/src/list/SortableList.ts +291 -67
  226. package/src/locales/es.ts +1 -0
  227. package/src/locales/fr.ts +1 -0
  228. package/src/locales/pt.ts +1 -0
  229. package/src/omnibox/Omnibox.ts +1 -1
  230. package/src/options/Options.ts +38 -13
  231. package/src/select/Select.ts +245 -47
  232. package/src/store/AppState.ts +3 -3
  233. package/src/utils/index.ts +17 -5
  234. package/src/vectoricon/VectorIcon.ts +2 -1
  235. package/src/webchat/WebChat.ts +5 -2
  236. package/temba-modules.ts +0 -2
  237. package/test/temba-appstate-language.test.ts +218 -0
  238. package/test/temba-chart.test.ts +161 -1
  239. package/test/temba-dropdown.test.ts +444 -0
  240. package/test/temba-flow-editor-node.test.ts +344 -0
  241. package/test/temba-flow-editor.test.ts +301 -0
  242. package/test/temba-flow-plumber.test.ts +189 -0
  243. package/test/temba-flow-render.test.ts +220 -0
  244. package/test/temba-omnibox.test.ts +2 -3
  245. package/test/temba-run-list.test.ts +774 -0
  246. package/test/temba-select.test.ts +206 -78
  247. package/test/temba-sortable-list.test.ts +108 -15
  248. package/test/temba-toast.test.ts +386 -0
  249. package/test/temba-utils-index.test.ts +1547 -0
  250. package/test/temba-webchat-lightbox-fix.test.ts +57 -0
  251. package/test/temba-webchat.test.ts +1095 -0
  252. package/test/utils.test.ts +56 -2
  253. package/test-assets/list/flow-results.json +17 -0
  254. package/test-assets/list/runs.json +126 -0
  255. package/test-assets/style.css +23 -0
  256. package/web-test-runner.config.mjs +33 -7
  257. package/xliff/es.xlf +3 -0
  258. package/xliff/fr.xlf +3 -0
  259. package/xliff/pt.xlf +3 -0
  260. package/out-tsc/src/outboxmonitor/OutboxMonitor.js +0 -136
  261. package/out-tsc/src/outboxmonitor/OutboxMonitor.js.map +0 -1
  262. package/src/outboxmonitor/OutboxMonitor.ts +0 -148
@@ -11,6 +11,8 @@ import { expect, fixture, html, assert, waitUntil } from '@open-wc/testing';
11
11
  import MouseHelper from './MouseHelper';
12
12
  import { Store } from '../src/store/Store';
13
13
  import { replace, stub } from 'sinon';
14
+ import { Select, SelectOption } from '../src/select/Select';
15
+ import { Options } from '../src/options/Options';
14
16
 
15
17
  export interface CodeMock {
16
18
  endpoint: RegExp;
@@ -188,7 +190,9 @@ export const assertScreenshot = async (
188
190
  await waitUntil(waitFor.predicate);
189
191
  }
190
192
 
191
- const threshold = 0.1;
193
+ // detect if we're running in copilot's environment and use adaptive threshold
194
+ const isCopilotEnvironment = (window as any).isCopilotEnvironment;
195
+ const threshold = isCopilotEnvironment ? 1.0 : 0.1;
192
196
  const exclude: Clip[] = [];
193
197
 
194
198
  try {
@@ -238,7 +242,7 @@ export const getClip = (ele: HTMLElement) => {
238
242
  return newClip;
239
243
  };
240
244
 
241
- export const mouseClickElement = async (ele: HTMLElement) => {
245
+ export const mouseClickElement = async (ele: HTMLElement | Element) => {
242
246
  const bounds = ele.getBoundingClientRect();
243
247
  await mouseClick(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
244
248
  };
@@ -277,3 +281,53 @@ export const mockNow = (isodate: string) => {
277
281
  return now;
278
282
  });
279
283
  };
284
+
285
+ export const getOptions = (select: Select<SelectOption>): Options => {
286
+ return select.shadowRoot.querySelector('temba-options[visible]');
287
+ };
288
+
289
+ export const clickOption = async (
290
+ clock: any,
291
+ select: Select<SelectOption>,
292
+ index: number
293
+ ) => {
294
+ const options = getOptions(select);
295
+ const option = options.shadowRoot.querySelector(
296
+ `[data-option-index="${index}"]`
297
+ ) as HTMLDivElement;
298
+
299
+ await mouseClickElement(option);
300
+ await options.updateComplete;
301
+ await select.updateComplete;
302
+ await clock.runAll();
303
+
304
+ checkTimers(clock);
305
+ };
306
+ export const openSelect = async (clock: any, select: Select<SelectOption>) => {
307
+ const container = select.shadowRoot.querySelector('.select-container');
308
+ container.dispatchEvent(new MouseEvent('click', { bubbles: true }));
309
+
310
+ clock.runAll();
311
+
312
+ // add more explicit waiting and clock ticks
313
+ await select.updateComplete;
314
+ clock.runAll();
315
+
316
+ // ensure options are visible before proceeding
317
+ await waitFor(100);
318
+ clock.runAll();
319
+ };
320
+
321
+ export const openAndClick = async (
322
+ clock: any,
323
+ select: Select<SelectOption>,
324
+ idx: number
325
+ ) => {
326
+ await openSelect(clock, select);
327
+
328
+ // Add this line to ensure proper timing when running as part of a test suite
329
+ await select.updateComplete;
330
+ clock.tick(50); // Give extra time for options to render
331
+
332
+ await clickOption(clock, select, idx);
333
+ };
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "key": "name",
4
+ "name": "Name",
5
+ "categories": ["Text"]
6
+ },
7
+ {
8
+ "key": "age",
9
+ "name": "Age",
10
+ "categories": ["Number"]
11
+ },
12
+ {
13
+ "key": "gender",
14
+ "name": "Gender",
15
+ "categories": ["Male", "Female", "Other"]
16
+ }
17
+ ]
@@ -0,0 +1,126 @@
1
+ {
2
+ "next": null,
3
+ "previous": null,
4
+ "results": [
5
+ {
6
+ "id": 1,
7
+ "uuid": "run-uuid-1",
8
+ "contact": {
9
+ "uuid": "contact-uuid-1",
10
+ "name": "John Doe",
11
+ "urn": "tel:+1234567890",
12
+ "anon_display": "1234567890"
13
+ },
14
+ "flow": {
15
+ "uuid": "flow-uuid-1",
16
+ "name": "Registration Flow"
17
+ },
18
+ "exit_type": "completed",
19
+ "exited_on": "2023-12-01T10:30:00.000Z",
20
+ "created_on": "2023-12-01T10:00:00.000Z",
21
+ "modified_on": "2023-12-01T10:30:00.000Z",
22
+ "responded": true,
23
+ "values": {
24
+ "name": {
25
+ "name": "Name",
26
+ "key": "name",
27
+ "value": "John Doe",
28
+ "category": "Text"
29
+ },
30
+ "age": {
31
+ "name": "Age",
32
+ "key": "age",
33
+ "value": "25",
34
+ "category": "Number"
35
+ }
36
+ }
37
+ },
38
+ {
39
+ "id": 2,
40
+ "uuid": "run-uuid-2",
41
+ "contact": {
42
+ "uuid": "contact-uuid-2",
43
+ "name": "Jane Smith",
44
+ "urn": "tel:+1987654321",
45
+ "anon_display": "1987654321"
46
+ },
47
+ "flow": {
48
+ "uuid": "flow-uuid-1",
49
+ "name": "Registration Flow"
50
+ },
51
+ "exit_type": "interrupted",
52
+ "exited_on": "2023-12-01T11:15:00.000Z",
53
+ "created_on": "2023-12-01T11:00:00.000Z",
54
+ "modified_on": "2023-12-01T11:15:00.000Z",
55
+ "responded": false,
56
+ "values": {}
57
+ },
58
+ {
59
+ "id": 3,
60
+ "uuid": "run-uuid-3",
61
+ "contact": {
62
+ "uuid": "contact-uuid-3",
63
+ "name": null,
64
+ "urn": "tel:+1122334455",
65
+ "anon_display": "1122334455"
66
+ },
67
+ "flow": {
68
+ "uuid": "flow-uuid-1",
69
+ "name": "Registration Flow"
70
+ },
71
+ "exit_type": "expired",
72
+ "exited_on": "2023-12-01T12:00:00.000Z",
73
+ "created_on": "2023-12-01T11:30:00.000Z",
74
+ "modified_on": "2023-12-01T12:00:00.000Z",
75
+ "responded": true,
76
+ "values": {
77
+ "name": {
78
+ "name": "Name",
79
+ "key": "name",
80
+ "value": "Anonymous User",
81
+ "category": "Text"
82
+ }
83
+ }
84
+ },
85
+ {
86
+ "id": 4,
87
+ "uuid": "run-uuid-4",
88
+ "contact": {
89
+ "uuid": "contact-uuid-4",
90
+ "name": "Active User",
91
+ "urn": "tel:+1555666777",
92
+ "anon_display": "1555666777"
93
+ },
94
+ "flow": {
95
+ "uuid": "flow-uuid-1",
96
+ "name": "Registration Flow"
97
+ },
98
+ "exit_type": null,
99
+ "exited_on": null,
100
+ "created_on": "2023-12-01T12:30:00.000Z",
101
+ "modified_on": "2023-12-01T12:45:00.000Z",
102
+ "responded": true,
103
+ "values": {}
104
+ },
105
+ {
106
+ "id": 5,
107
+ "uuid": "run-uuid-5",
108
+ "contact": {
109
+ "uuid": "contact-uuid-5",
110
+ "name": "Pending User",
111
+ "urn": "tel:+1888999000",
112
+ "anon_display": "1888999000"
113
+ },
114
+ "flow": {
115
+ "uuid": "flow-uuid-1",
116
+ "name": "Registration Flow"
117
+ },
118
+ "exit_type": null,
119
+ "exited_on": null,
120
+ "created_on": "2023-12-01T13:00:00.000Z",
121
+ "modified_on": "2023-12-01T13:00:00.000Z",
122
+ "responded": false,
123
+ "values": {}
124
+ }
125
+ ]
126
+ }
@@ -25,6 +25,29 @@ temba-dialog {
25
25
  caret-color: transparent;
26
26
  }
27
27
 
28
+ /* Disable CSS animations for deterministic screenshots */
29
+ *,
30
+ *::before,
31
+ *::after {
32
+ animation-duration: 0s !important;
33
+ animation-delay: 0s !important;
34
+ transition-duration: 0s !important;
35
+ transition-delay: 0s !important;
36
+ animation-iteration-count: 1 !important;
37
+ }
38
+
39
+ /* Override CSS custom properties for animation control in tests */
40
+ :root {
41
+ --test-animation-duration: 0s !important;
42
+ --test-animation-play-state: paused !important;
43
+ }
44
+
45
+ /* Force disable spin animations by overriding keyframes - this pierces shadow DOM */
46
+ @keyframes spin {
47
+ from { transform: rotate(0deg); }
48
+ to { transform: rotate(0deg); }
49
+ }
50
+
28
51
  html {
29
52
  --transition-speed: 0ms !important;
30
53
  --input-caret: transparent !important;
@@ -186,7 +186,7 @@ const wireScreenshots = async (page, context, wait, replaceScreenshots) => {
186
186
  await page.screenshot({ path: testFile, clip });
187
187
 
188
188
  try {
189
- const result = await checkScreenshot(filename);
189
+ const result = await checkScreenshot(filename, excluded, threshold);
190
190
  // const end = Date.now();
191
191
  // console.log(`Screenshot took ${end - start}ms`);
192
192
  resolve(result);
@@ -351,6 +351,28 @@ export default {
351
351
  '--force-device-scale-factor=1',
352
352
  '--no-sandbox',
353
353
  '--disable-gpu',
354
+ '--disable-font-subpixel-positioning',
355
+ '--disable-lcd-text',
356
+ '--force-prefers-reduced-motion',
357
+ '--disable-background-timer-throttling',
358
+ '--disable-backgrounding-occluded-windows',
359
+ '--disable-renderer-backgrounding',
360
+ // additional flags for consistent rendering across environments
361
+ '--disable-gpu-sandbox',
362
+ '--disable-software-rasterizer',
363
+ '--disable-background-networking',
364
+ '--disable-default-apps',
365
+ '--disable-extensions',
366
+ '--disable-sync',
367
+ '--disable-translate',
368
+ '--hide-crash-restore-bubble',
369
+ '--metrics-recording-only',
370
+ '--no-first-run',
371
+ '--safebrowsing-disable-auto-update',
372
+ '--use-mock-keychain',
373
+ '--disable-ipc-flooding-protection',
374
+ '--disable-component-update',
375
+ '--disable-domain-reliability',
354
376
  ],
355
377
  headless: true,
356
378
  },
@@ -363,13 +385,17 @@ export default {
363
385
  await page.setUserAgent(
364
386
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'
365
387
  );
388
+
389
+ // detect if we're running in copilot's environment
390
+ const isCopilotEnvironment = process.env.COPILOT_API_URL || process.env.COPILOT_AGENT_CALLBACK_URL;
391
+
392
+ // inject script into every document that loads
393
+ await page.evaluateOnNewDocument((watched, copilotEnv) => {
394
+ window.watched = watched;
395
+ window.isCopilotEnvironment = copilotEnv;
396
+ }, config.watch, !!isCopilotEnvironment);
397
+
366
398
  await page.once('load', async () => {
367
- await page.addScriptTag({
368
- content: `
369
- window.watched = ${config.watch};
370
- `,
371
- });
372
-
373
399
  await wireScreenshots(page, context, wait, replaceScreenshots);
374
400
  });
375
401
 
package/xliff/es.xlf CHANGED
@@ -15,6 +15,9 @@
15
15
  <trans-unit id="s4788ee206c4570c7">
16
16
  <source>Have not started this flow in the last 90 days</source>
17
17
  </trans-unit>
18
+ <trans-unit id="s73b4d70c02f4b4e0">
19
+ <source>No options</source>
20
+ </trans-unit>
18
21
  </body>
19
22
  </file>
20
23
  </xliff>
package/xliff/fr.xlf CHANGED
@@ -14,6 +14,9 @@
14
14
  <trans-unit id="s4788ee206c4570c7">
15
15
  <source>Have not started this flow in the last 90 days</source>
16
16
  </trans-unit>
17
+ <trans-unit id="s73b4d70c02f4b4e0">
18
+ <source>No options</source>
19
+ </trans-unit>
17
20
  </body>
18
21
  </file>
19
22
  </xliff>
package/xliff/pt.xlf CHANGED
@@ -14,6 +14,9 @@
14
14
  <trans-unit id="s4788ee206c4570c7">
15
15
  <source>Have not started this flow in the last 90 days</source>
16
16
  </trans-unit>
17
+ <trans-unit id="s73b4d70c02f4b4e0">
18
+ <source>No options</source>
19
+ </trans-unit>
17
20
  </body>
18
21
  </file>
19
22
  </xliff>
@@ -1,136 +0,0 @@
1
- import { __decorate } from "tslib";
2
- import { css, html } from 'lit';
3
- import { property } from 'lit/decorators.js';
4
- import { ResizeElement } from '../ResizeElement';
5
- import { fetchResults } from '../utils';
6
- const MIN_BACKLOG = 500000;
7
- export class OutboxMonitor extends ResizeElement {
8
- constructor() {
9
- super(...arguments);
10
- this.backlogSize = 0;
11
- this.endpoint = '/msg/menu/';
12
- this.folders = {};
13
- this.fetches = 0;
14
- this.msgsPerSecond = 0;
15
- }
16
- static get styles() {
17
- return css `
18
- .monitor {
19
- margin: 1rem;
20
- margin-bottom: -0.5rem;
21
- }
22
-
23
- .header {
24
- font-weight: bold;
25
- }
26
-
27
- .estimate {
28
- font-size: 0.9em;
29
- }
30
- `;
31
- }
32
- fetchFolders() {
33
- fetchResults(this.endpoint).then((items) => {
34
- items
35
- .filter((item) => item.id === 'outbox' || item.id === 'sent' || item.id === 'failed')
36
- .forEach((item) => {
37
- if (this.folders[item.id]) {
38
- this.folders[item.id].current = item.count;
39
- }
40
- else {
41
- this.folders[item.id] = {
42
- start: item.count,
43
- current: item.count
44
- };
45
- }
46
- });
47
- if (this.firstFetch) {
48
- this.lastFetch = new Date();
49
- }
50
- else {
51
- this.firstFetch = new Date();
52
- }
53
- this.fetches++;
54
- this.scheduleRefresh(Math.min(this.fetches * 5000, 60000));
55
- const outbox = this.folders['outbox'];
56
- this.backlogSize = outbox.current;
57
- if (outbox.current > 1) {
58
- this.estimateCompletion();
59
- }
60
- });
61
- }
62
- estimateCompletion() {
63
- if (this.lastFetch) {
64
- const time = (this.lastFetch.getTime() - this.firstFetch.getTime()) / 1000;
65
- const sent = this.folders['sent'];
66
- const failed = this.folders['failed'];
67
- const totalCompleted = sent.current + failed.current;
68
- const startCount = sent.start + failed.start;
69
- const sentInWindow = totalCompleted - startCount;
70
- this.msgsPerSecond = sentInWindow / time;
71
- const remaining = this.folders['outbox'].current;
72
- const secondsRemaining = remaining / this.msgsPerSecond;
73
- this.estimatedCompletionDate = new Date(new Date().getTime() + secondsRemaining * 1000);
74
- }
75
- }
76
- scheduleRefresh(time) {
77
- setTimeout(() => {
78
- this.fetchFolders();
79
- }, time);
80
- }
81
- firstUpdated(changes) {
82
- if (changes.has('endpoint') && this.endpoint) {
83
- this.fetchFolders();
84
- }
85
- }
86
- hasBacklog() {
87
- return this.backlogSize > MIN_BACKLOG;
88
- }
89
- render() {
90
- const roundedRate = Math.round(this.msgsPerSecond);
91
- if (this.hasBacklog() && this.estimatedCompletionDate && !this.isMobile()) {
92
- return html `<div class="monitor">
93
- <temba-alert
94
- ><div class="header">Outbox Notice</div>
95
- <div class="estimate">
96
- If your outbox becomes too full, you won't be able to send new flows
97
- or broadcasts. Your channels are currently sending at
98
- ${roundedRate.toLocaleString()}
99
- message${roundedRate == 1 ? '' : 's'} per second. At that rate, your
100
- outbox will clear
101
- <temba-date
102
- value="${this.estimatedCompletionDate.toISOString()}"
103
- display="duration"
104
- ></temba-date
105
- >.
106
- </div></temba-alert
107
- >
108
- </div>`;
109
- }
110
- else {
111
- return null;
112
- }
113
- }
114
- }
115
- __decorate([
116
- property({ type: Number })
117
- ], OutboxMonitor.prototype, "backlogSize", void 0);
118
- __decorate([
119
- property({ type: String })
120
- ], OutboxMonitor.prototype, "endpoint", void 0);
121
- __decorate([
122
- property({ type: Object })
123
- ], OutboxMonitor.prototype, "firstFetch", void 0);
124
- __decorate([
125
- property({ type: Object })
126
- ], OutboxMonitor.prototype, "lastFetch", void 0);
127
- __decorate([
128
- property({ type: Number })
129
- ], OutboxMonitor.prototype, "fetches", void 0);
130
- __decorate([
131
- property({ type: Number })
132
- ], OutboxMonitor.prototype, "msgsPerSecond", void 0);
133
- __decorate([
134
- property({ type: Object })
135
- ], OutboxMonitor.prototype, "estimatedCompletionDate", void 0);
136
- //# sourceMappingURL=OutboxMonitor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"OutboxMonitor.js","sourceRoot":"","sources":["../../../src/outboxmonitor/OutboxMonitor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAoB,MAAM,KAAK,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,WAAW,GAAG,MAAM,CAAC;AAC3B,MAAM,OAAO,aAAc,SAAQ,aAAa;IAAhD;;QAEE,gBAAW,GAAG,CAAC,CAAC;QAGhB,aAAQ,GAAG,YAAY,CAAC;QAExB,YAAO,GAAyD,EAAE,CAAC;QASnE,YAAO,GAAG,CAAC,CAAC;QAGZ,kBAAa,GAAG,CAAC,CAAC;IA0HpB,CAAC;IArHQ,MAAM,KAAK,MAAM;QACtB,OAAO,GAAG,CAAA;;;;;;;;;;;;;KAaT,CAAC;IACJ,CAAC;IAEO,YAAY;QAClB,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;YACzC,KAAK;iBACF,MAAM,CACL,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI,IAAI,CAAC,EAAE,KAAK,QAAQ,CACrE;iBACA,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBAChB,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC;gBAC7C,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG;wBACtB,KAAK,EAAE,IAAI,CAAC,KAAK;wBACjB,OAAO,EAAE,IAAI,CAAC,KAAK;qBACpB,CAAC;gBACJ,CAAC;YACH,CAAC,CAAC,CAAC;YAEL,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACpB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;YAC/B,CAAC;YACD,IAAI,CAAC,OAAO,EAAE,CAAC;YAEf,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YAE3D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAEtC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC;YAClC,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,IAAI,GACR,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC;YAChE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAEtC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YACrD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAE7C,MAAM,YAAY,GAAG,cAAc,GAAG,UAAU,CAAC;YACjD,IAAI,CAAC,aAAa,GAAG,YAAY,GAAG,IAAI,CAAC;YAEzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;YACjD,MAAM,gBAAgB,GAAG,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC;YAExD,IAAI,CAAC,uBAAuB,GAAG,IAAI,IAAI,CACrC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,gBAAgB,GAAG,IAAI,CAC/C,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,IAAY;QAClC,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC;IAES,YAAY,CACpB,OAA0D;QAE1D,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7C,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACxC,CAAC;IAEM,MAAM;QACX,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACnD,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,uBAAuB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC1E,OAAO,IAAI,CAAA;;;;;;cAMH,WAAW,CAAC,cAAc,EAAE;qBACrB,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG;;;uBAGzB,IAAI,CAAC,uBAAuB,CAAC,WAAW,EAAE;;;;;;aAMpD,CAAC;QACV,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF;AA3IC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;kDACX;AAGhB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;+CACH;AAKxB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;iDACV;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gDACX;AAGhB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CACf;AAGZ;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oDACT;AAGlB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8DACG","sourcesContent":["import { css, html, PropertyValueMap } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { ResizeElement } from '../ResizeElement';\nimport { fetchResults } from '../utils';\n\nconst MIN_BACKLOG = 500000;\nexport class OutboxMonitor extends ResizeElement {\n @property({ type: Number })\n backlogSize = 0;\n\n @property({ type: String })\n endpoint = '/msg/menu/';\n\n folders: { [id: string]: { start: number; current: number } } = {};\n\n @property({ type: Object })\n firstFetch: Date;\n\n @property({ type: Object })\n lastFetch: Date;\n\n @property({ type: Number })\n fetches = 0;\n\n @property({ type: Number })\n msgsPerSecond = 0;\n\n @property({ type: Object })\n estimatedCompletionDate: Date;\n\n public static get styles() {\n return css`\n .monitor {\n margin: 1rem;\n margin-bottom: -0.5rem;\n }\n\n .header {\n font-weight: bold;\n }\n\n .estimate {\n font-size: 0.9em;\n }\n `;\n }\n\n private fetchFolders() {\n fetchResults(this.endpoint).then((items) => {\n items\n .filter(\n (item) =>\n item.id === 'outbox' || item.id === 'sent' || item.id === 'failed'\n )\n .forEach((item) => {\n if (this.folders[item.id]) {\n this.folders[item.id].current = item.count;\n } else {\n this.folders[item.id] = {\n start: item.count,\n current: item.count\n };\n }\n });\n\n if (this.firstFetch) {\n this.lastFetch = new Date();\n } else {\n this.firstFetch = new Date();\n }\n this.fetches++;\n\n this.scheduleRefresh(Math.min(this.fetches * 5000, 60000));\n\n const outbox = this.folders['outbox'];\n\n this.backlogSize = outbox.current;\n if (outbox.current > 1) {\n this.estimateCompletion();\n }\n });\n }\n\n private estimateCompletion() {\n if (this.lastFetch) {\n const time =\n (this.lastFetch.getTime() - this.firstFetch.getTime()) / 1000;\n const sent = this.folders['sent'];\n const failed = this.folders['failed'];\n\n const totalCompleted = sent.current + failed.current;\n const startCount = sent.start + failed.start;\n\n const sentInWindow = totalCompleted - startCount;\n this.msgsPerSecond = sentInWindow / time;\n\n const remaining = this.folders['outbox'].current;\n const secondsRemaining = remaining / this.msgsPerSecond;\n\n this.estimatedCompletionDate = new Date(\n new Date().getTime() + secondsRemaining * 1000\n );\n }\n }\n\n private scheduleRefresh(time: number) {\n setTimeout(() => {\n this.fetchFolders();\n }, time);\n }\n\n protected firstUpdated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n if (changes.has('endpoint') && this.endpoint) {\n this.fetchFolders();\n }\n }\n\n public hasBacklog() {\n return this.backlogSize > MIN_BACKLOG;\n }\n\n public render() {\n const roundedRate = Math.round(this.msgsPerSecond);\n if (this.hasBacklog() && this.estimatedCompletionDate && !this.isMobile()) {\n return html`<div class=\"monitor\">\n <temba-alert\n ><div class=\"header\">Outbox Notice</div>\n <div class=\"estimate\">\n If your outbox becomes too full, you won't be able to send new flows\n or broadcasts. Your channels are currently sending at\n ${roundedRate.toLocaleString()}\n message${roundedRate == 1 ? '' : 's'} per second. At that rate, your\n outbox will clear\n <temba-date\n value=\"${this.estimatedCompletionDate.toISOString()}\"\n display=\"duration\"\n ></temba-date\n >.\n </div></temba-alert\n >\n </div>`;\n } else {\n return null;\n }\n }\n}\n"]}
@@ -1,148 +0,0 @@
1
- import { css, html, PropertyValueMap } from 'lit';
2
- import { property } from 'lit/decorators.js';
3
- import { ResizeElement } from '../ResizeElement';
4
- import { fetchResults } from '../utils';
5
-
6
- const MIN_BACKLOG = 500000;
7
- export class OutboxMonitor extends ResizeElement {
8
- @property({ type: Number })
9
- backlogSize = 0;
10
-
11
- @property({ type: String })
12
- endpoint = '/msg/menu/';
13
-
14
- folders: { [id: string]: { start: number; current: number } } = {};
15
-
16
- @property({ type: Object })
17
- firstFetch: Date;
18
-
19
- @property({ type: Object })
20
- lastFetch: Date;
21
-
22
- @property({ type: Number })
23
- fetches = 0;
24
-
25
- @property({ type: Number })
26
- msgsPerSecond = 0;
27
-
28
- @property({ type: Object })
29
- estimatedCompletionDate: Date;
30
-
31
- public static get styles() {
32
- return css`
33
- .monitor {
34
- margin: 1rem;
35
- margin-bottom: -0.5rem;
36
- }
37
-
38
- .header {
39
- font-weight: bold;
40
- }
41
-
42
- .estimate {
43
- font-size: 0.9em;
44
- }
45
- `;
46
- }
47
-
48
- private fetchFolders() {
49
- fetchResults(this.endpoint).then((items) => {
50
- items
51
- .filter(
52
- (item) =>
53
- item.id === 'outbox' || item.id === 'sent' || item.id === 'failed'
54
- )
55
- .forEach((item) => {
56
- if (this.folders[item.id]) {
57
- this.folders[item.id].current = item.count;
58
- } else {
59
- this.folders[item.id] = {
60
- start: item.count,
61
- current: item.count
62
- };
63
- }
64
- });
65
-
66
- if (this.firstFetch) {
67
- this.lastFetch = new Date();
68
- } else {
69
- this.firstFetch = new Date();
70
- }
71
- this.fetches++;
72
-
73
- this.scheduleRefresh(Math.min(this.fetches * 5000, 60000));
74
-
75
- const outbox = this.folders['outbox'];
76
-
77
- this.backlogSize = outbox.current;
78
- if (outbox.current > 1) {
79
- this.estimateCompletion();
80
- }
81
- });
82
- }
83
-
84
- private estimateCompletion() {
85
- if (this.lastFetch) {
86
- const time =
87
- (this.lastFetch.getTime() - this.firstFetch.getTime()) / 1000;
88
- const sent = this.folders['sent'];
89
- const failed = this.folders['failed'];
90
-
91
- const totalCompleted = sent.current + failed.current;
92
- const startCount = sent.start + failed.start;
93
-
94
- const sentInWindow = totalCompleted - startCount;
95
- this.msgsPerSecond = sentInWindow / time;
96
-
97
- const remaining = this.folders['outbox'].current;
98
- const secondsRemaining = remaining / this.msgsPerSecond;
99
-
100
- this.estimatedCompletionDate = new Date(
101
- new Date().getTime() + secondsRemaining * 1000
102
- );
103
- }
104
- }
105
-
106
- private scheduleRefresh(time: number) {
107
- setTimeout(() => {
108
- this.fetchFolders();
109
- }, time);
110
- }
111
-
112
- protected firstUpdated(
113
- changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
114
- ): void {
115
- if (changes.has('endpoint') && this.endpoint) {
116
- this.fetchFolders();
117
- }
118
- }
119
-
120
- public hasBacklog() {
121
- return this.backlogSize > MIN_BACKLOG;
122
- }
123
-
124
- public render() {
125
- const roundedRate = Math.round(this.msgsPerSecond);
126
- if (this.hasBacklog() && this.estimatedCompletionDate && !this.isMobile()) {
127
- return html`<div class="monitor">
128
- <temba-alert
129
- ><div class="header">Outbox Notice</div>
130
- <div class="estimate">
131
- If your outbox becomes too full, you won't be able to send new flows
132
- or broadcasts. Your channels are currently sending at
133
- ${roundedRate.toLocaleString()}
134
- message${roundedRate == 1 ? '' : 's'} per second. At that rate, your
135
- outbox will clear
136
- <temba-date
137
- value="${this.estimatedCompletionDate.toISOString()}"
138
- display="duration"
139
- ></temba-date
140
- >.
141
- </div></temba-alert
142
- >
143
- </div>`;
144
- } else {
145
- return null;
146
- }
147
- }
148
- }