@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
Binary file
Binary file
Binary file
Binary file
@@ -6,35 +6,66 @@ import { Select, SelectOption } from '../select/Select';
6
6
  import { getClasses } from '../utils';
7
7
  import { getStore } from '../store/Store';
8
8
 
9
+ // eslint-disable-next-line import/no-named-as-default
9
10
  import Chart, { ChartType } from 'chart.js/auto';
10
11
  import 'chartjs-adapter-luxon';
11
12
 
12
13
  const colors = [
14
+ 'rgba(54, 162, 235, 0.2)',
13
15
  'rgba(255, 159, 64, 0.2)',
14
16
  'rgba(75, 192, 192, 0.2)',
15
- 'rgba(54, 162, 235, 0.2)',
16
17
  'rgba(153, 102, 255, 0.2)',
17
18
  'rgba(255, 205, 86, 0.2)',
18
- 'rgba(255, 99, 132, 0.2)',
19
- 'rgba(201, 203, 207, 0.2)'
19
+ 'rgba(255, 99, 132, 0.2)'
20
20
  ];
21
21
 
22
22
  const colorsBorder = [
23
+ 'rgb(54, 162, 235)',
23
24
  'rgb(255, 159, 64)',
24
25
  'rgb(75, 192, 192)',
25
- 'rgb(54, 162, 235)',
26
26
  'rgb(153, 102, 255)',
27
27
  'rgb(255, 205, 86)',
28
- 'rgb(255, 99, 132)',
29
- 'rgb(201, 203, 207)'
28
+ 'rgb(255, 99, 132)'
30
29
  ];
31
30
 
32
- const allBorderColor = 'rgb(54, 162, 235)';
33
- const allBackgroundColor = 'rgba(54, 162, 235, 0.2)';
34
-
35
31
  const otherBackgroundColor = 'rgba(201, 203, 207, 0.2)';
36
32
  const otherBorderColor = 'rgb(201, 203, 207)';
37
33
 
34
+ /**
35
+ * Formats a duration in seconds to a human-readable string showing the two largest units.
36
+ * Examples: 68787 -> "19h 6m", 958000 -> "11d 2h", 3661 -> "1h 1m"
37
+ */
38
+ export function formatDurationFromSeconds(seconds: number): string {
39
+ if (seconds === 0) {
40
+ return '0s';
41
+ }
42
+
43
+ const totalDays = Math.floor(seconds / 86400);
44
+ const remainingAfterDays = seconds % 86400;
45
+ const remainingHours = Math.floor(remainingAfterDays / 3600);
46
+ const remainingAfterHours = remainingAfterDays % 3600;
47
+ const remainingMinutes = Math.floor(remainingAfterHours / 60);
48
+ const remainingSeconds = remainingAfterHours % 60;
49
+
50
+ const units = [];
51
+
52
+ if (totalDays > 0) {
53
+ units.push(`${totalDays}d`);
54
+ }
55
+ if (remainingHours > 0) {
56
+ units.push(`${remainingHours}h`);
57
+ }
58
+ if (remainingMinutes > 0 && units.length < 2) {
59
+ units.push(`${remainingMinutes}m`);
60
+ }
61
+ if (remainingSeconds > 0 && units.length < 2) {
62
+ units.push(`${remainingSeconds}s`);
63
+ }
64
+
65
+ // Return the first two most significant units
66
+ return units.slice(0, 2).join(' ');
67
+ }
68
+
38
69
  export interface RapidChartData {
39
70
  labels: string[];
40
71
  datasets: { label: string; data: number[] }[];
@@ -68,9 +99,21 @@ export class TembaChart extends RapidElement {
68
99
  @property({ type: String })
69
100
  dataname = 'Counts';
70
101
 
102
+ @property({ type: Boolean })
103
+ single: boolean = false;
104
+
105
+ @property({ type: Boolean })
106
+ legend: boolean = false;
107
+
71
108
  @property({ type: Boolean })
72
109
  config: boolean = false;
73
110
 
111
+ @property({ type: Boolean })
112
+ formatDuration: boolean = false;
113
+
114
+ @property({ type: Number })
115
+ colorIndex: number = 0;
116
+
74
117
  @state()
75
118
  showConfig: boolean = false;
76
119
 
@@ -180,8 +223,12 @@ export class TembaChart extends RapidElement {
180
223
  } else {
181
224
  datasets.push({
182
225
  ...dataset,
183
- backgroundColor: colors[datasets.length % colors.length],
184
- borderColor: colorsBorder[datasets.length % colorsBorder.length],
226
+ backgroundColor:
227
+ colors[(datasets.length + this.colorIndex) % colors.length],
228
+ borderColor:
229
+ colorsBorder[
230
+ (datasets.length + this.colorIndex) % colorsBorder.length
231
+ ],
185
232
  borderWidth: 1
186
233
  });
187
234
  }
@@ -189,10 +236,10 @@ export class TembaChart extends RapidElement {
189
236
 
190
237
  if (datasets.length === 0) {
191
238
  datasets.push({
192
- label: `All ${this.dataname}`,
239
+ label: this.single ? this.dataname : `All ${this.dataname}`,
193
240
  data: sums,
194
- backgroundColor: allBackgroundColor,
195
- borderColor: allBorderColor,
241
+ backgroundColor: colors[this.colorIndex % colors.length],
242
+ borderColor: colorsBorder[this.colorIndex % colorsBorder.length],
196
243
  borderWidth: 1
197
244
  });
198
245
  } else {
@@ -222,6 +269,23 @@ export class TembaChart extends RapidElement {
222
269
  datasets: this.datasets
223
270
  },
224
271
  options: {
272
+ plugins: {
273
+ legend: {
274
+ display: this.legend
275
+ },
276
+ ...(this.formatDuration && {
277
+ tooltip: {
278
+ callbacks: {
279
+ label: (context: any) => {
280
+ const label = context.dataset.label || '';
281
+ const value = context.parsed.y;
282
+ const formattedValue = formatDurationFromSeconds(value);
283
+ return `${label}: ${formattedValue}`;
284
+ }
285
+ }
286
+ }
287
+ })
288
+ },
225
289
  responsive: true,
226
290
  maintainAspectRatio: false,
227
291
  animation: {
@@ -240,7 +304,14 @@ export class TembaChart extends RapidElement {
240
304
  scales: {
241
305
  y: {
242
306
  min: 0,
243
- stacked: true
307
+ stacked: true,
308
+ ...(this.formatDuration && {
309
+ ticks: {
310
+ callback: (value: any) => {
311
+ return formatDurationFromSeconds(value);
312
+ }
313
+ }
314
+ })
244
315
  },
245
316
  x: {
246
317
  type: 'time',
@@ -121,7 +121,7 @@ export class FieldManager extends EndpointMonitorElement {
121
121
  border-radius: var(--curvature);
122
122
  }
123
123
 
124
- .featured temba-sortable-list .field:hover {
124
+ .featured:not(.dragging) temba-sortable-list .field:hover {
125
125
  cursor: move;
126
126
  border-color: #e6e6e6;
127
127
  background: #fcfcfc;
@@ -138,9 +138,6 @@ export class FieldManager extends EndpointMonitorElement {
138
138
  @property({ type: Object, attribute: false })
139
139
  otherFieldKeys: string[] = [];
140
140
 
141
- @property({ type: String })
142
- draggingId: string;
143
-
144
141
  @property({ type: String })
145
142
  query = '';
146
143
 
@@ -189,36 +186,31 @@ export class FieldManager extends EndpointMonitorElement {
189
186
  }
190
187
  }
191
188
 
192
- private handleSaveOrder(event) {
193
- const list = event.currentTarget as SortableList;
194
- postJSON(
195
- this.priorityEndpoint,
196
- list
197
- .getIds()
198
- .reverse()
199
- .reduce((map, key, idx) => {
200
- map[key] = idx;
201
- return map;
202
- }, {})
203
- ).then(() => {
204
- this.store.refreshFields();
205
- });
206
- }
207
-
208
189
  private handleOrderChanged(event) {
209
- const swapsies = event.detail;
210
- const temp = this.featuredFields[swapsies.fromIdx];
211
- this.featuredFields[swapsies.fromIdx] = this.featuredFields[swapsies.toIdx];
212
- this.featuredFields[swapsies.toIdx] = temp;
213
- this.requestUpdate('featuredFields');
214
- }
190
+ // Apply the reordering immediately - the SortableList now provides accurate indexes
191
+ const [fromIdx, toIdx] = event.detail.swap;
215
192
 
216
- private handleDragStart(event) {
217
- this.draggingId = event.detail.id;
218
- }
193
+ const temp = this.featuredFields[fromIdx];
194
+ this.featuredFields.splice(fromIdx, 1);
195
+ this.featuredFields.splice(toIdx, 0, temp);
196
+ this.requestUpdate();
219
197
 
220
- private handleDragStop() {
221
- this.draggingId = null;
198
+ // Save the new order to the server
199
+ const list = event.currentTarget as SortableList;
200
+ setTimeout(() => {
201
+ postJSON(
202
+ this.priorityEndpoint,
203
+ list
204
+ .getIds()
205
+ .reverse()
206
+ .reduce((map, key, idx) => {
207
+ map[key] = idx;
208
+ return map;
209
+ }, {})
210
+ ).then(() => {
211
+ this.store.refreshFields();
212
+ });
213
+ }, 0);
222
214
  }
223
215
 
224
216
  private handleFieldAction(event: MouseEvent) {
@@ -248,10 +240,13 @@ export class FieldManager extends EndpointMonitorElement {
248
240
  display: flex;
249
241
  flex-direction: row;
250
242
  align-items: center;
251
- padding: 0.25em 1em;
252
- ${field.key === this.draggingId
253
- ? 'background: var(--color-selection)'
254
- : ''}"
243
+ padding: 0.25em 1em;"
244
+ @click=${(e: MouseEvent) => {
245
+ const ele = e.currentTarget as HTMLDivElement;
246
+ const key = ele.dataset.key;
247
+ const action = ele.dataset.action;
248
+ this.fireCustomEvent(CustomEventType.Selection, { key, action });
249
+ }}
255
250
  >
256
251
  <div
257
252
  style="display: flex; min-width: 200px; width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 2em"
@@ -327,10 +322,7 @@ export class FieldManager extends EndpointMonitorElement {
327
322
  `
328
323
  : html`
329
324
  <temba-sortable-list
330
- @change=${this.handleSaveOrder}
331
325
  @temba-order-changed=${this.handleOrderChanged}
332
- @temba-drag-start=${this.handleDragStart}
333
- @temba-drag-stop=${this.handleDragStop}
334
326
  >
335
327
  ${this.featuredFields.map((field) =>
336
328
  this.renderField(field)
@@ -155,8 +155,8 @@ export class RunList extends TembaList {
155
155
  style="width:16em;white-space:nowrap;overflow: hidden; text-overflow: ellipsis;"
156
156
  >
157
157
  <temba-contact-name
158
- name=${run.contact.name || run.contact.anon_display}
159
- urn=${run.contact.urn}
158
+ name=${run.contact?.name || run.contact?.anon_display || ''}
159
+ urn=${run.contact?.urn || ''}
160
160
  icon-size="15"
161
161
  />
162
162
  </div>
@@ -255,7 +255,7 @@ export class RunList extends TembaList {
255
255
  return null;
256
256
  }
257
257
 
258
- const resultKeys = Object.keys(this.selectedRun.values);
258
+ const resultKeys = Object.keys(this.selectedRun.values || {});
259
259
 
260
260
  return html` <div
261
261
  style="margin-top: 1.5em; margin-bottom:0.5em;flex-grow:1;border-radius:var(--curvature); border: 1px solid var(--color-widget-border);"
@@ -267,11 +267,12 @@ export class RunList extends TembaList {
267
267
  <div>
268
268
  <temba-contact-name
269
269
  style="cursor:pointer"
270
- name=${this.selectedRun.contact.name ||
271
- this.selectedRun.contact.anon_display}
272
- urn=${this.selectedRun.contact.urn}
270
+ name=${this.selectedRun.contact?.name ||
271
+ this.selectedRun.contact?.anon_display ||
272
+ ''}
273
+ urn=${this.selectedRun.contact?.urn || ''}
273
274
  onclick="goto(event, this)"
274
- href="/contact/read/${this.selectedRun.contact.uuid}/"
275
+ href="/contact/read/${this.selectedRun.contact?.uuid || ''}/"
275
276
  ></temba-contact-name>
276
277
  <div
277
278
  style="display:flex;margin-left:-0.2em;margin-top:0.25em;font-size: 0.65em"
@@ -303,7 +304,9 @@ export class RunList extends TembaList {
303
304
  <div style="flex-grow:1"></div>
304
305
  <div style="display:flex;flex-direction: column">
305
306
  <div style="font-size:0.75em">
306
- ${new Date(this.selectedRun.created_on).toLocaleString()}
307
+ ${this.selectedRun.created_on
308
+ ? new Date(this.selectedRun.created_on).toLocaleString()
309
+ : ''}
307
310
  </div>
308
311
  <div
309
312
  style="font-size:0.6em;align-self:flex-end;color:#888;line-height:0.75em"