@nyaruka/temba-components 0.17.0 → 0.18.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 (88) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/a525ddb7.js +1 -0
  3. package/dist/index.js +1 -1
  4. package/dist/sw.js +1 -1
  5. package/dist/sw.js.map +1 -1
  6. package/dist/templates/components-body.html +1 -1
  7. package/dist/templates/components-head.html +1 -1
  8. package/out-tsc/src/button/Button.js +1 -0
  9. package/out-tsc/src/button/Button.js.map +1 -1
  10. package/out-tsc/src/contacts/ContactChat.js +81 -33
  11. package/out-tsc/src/contacts/ContactChat.js.map +1 -1
  12. package/out-tsc/src/contacts/ContactDetails.js +22 -22
  13. package/out-tsc/src/contacts/ContactDetails.js.map +1 -1
  14. package/out-tsc/src/contacts/ContactHistory.js +131 -138
  15. package/out-tsc/src/contacts/ContactHistory.js.map +1 -1
  16. package/out-tsc/src/contacts/events.js +55 -44
  17. package/out-tsc/src/contacts/events.js.map +1 -1
  18. package/out-tsc/src/interfaces.js.map +1 -1
  19. package/out-tsc/src/list/ContactList.js +1 -1
  20. package/out-tsc/src/list/ContactList.js.map +1 -1
  21. package/out-tsc/src/list/TembaList.js +10 -3
  22. package/out-tsc/src/list/TembaList.js.map +1 -1
  23. package/out-tsc/src/list/TembaMenu.js +7 -2
  24. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  25. package/out-tsc/src/loading/Loading.js +9 -1
  26. package/out-tsc/src/loading/Loading.js.map +1 -1
  27. package/out-tsc/src/options/Options.js +14 -2
  28. package/out-tsc/src/options/Options.js.map +1 -1
  29. package/out-tsc/src/tip/Tip.js +6 -0
  30. package/out-tsc/src/tip/Tip.js.map +1 -1
  31. package/out-tsc/src/vectoricon/VectorIcon.js +17 -5
  32. package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
  33. package/out-tsc/test/temba-contact-history.test.js +2 -2
  34. package/out-tsc/test/temba-contact-history.test.js.map +1 -1
  35. package/package.json +1 -1
  36. package/screenshots/truth/contacts/history-expanded.png +0 -0
  37. package/screenshots/truth/contacts/history.png +0 -0
  38. package/screenshots/truth/list/items-selected.png +0 -0
  39. package/screenshots/truth/list/items-updated.png +0 -0
  40. package/screenshots/truth/list/items.png +0 -0
  41. package/screenshots/truth/modax/simple.png +0 -0
  42. package/screenshots/truth/options/block.png +0 -0
  43. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  44. package/screenshots/truth/select/disabled-selection.png +0 -0
  45. package/screenshots/truth/select/disabled.png +0 -0
  46. package/screenshots/truth/select/embedded.png +0 -0
  47. package/screenshots/truth/select/expression-selected.png +0 -0
  48. package/screenshots/truth/select/expressions.png +0 -0
  49. package/screenshots/truth/select/functions.png +0 -0
  50. package/screenshots/truth/select/local-options.png +0 -0
  51. package/screenshots/truth/select/remote-options.png +0 -0
  52. package/screenshots/truth/select/search-enabled.png +0 -0
  53. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  54. package/screenshots/truth/select/search-selected-focus.png +0 -0
  55. package/screenshots/truth/select/search-selected.png +0 -0
  56. package/screenshots/truth/select/search-with-selected.png +0 -0
  57. package/screenshots/truth/select/searching.png +0 -0
  58. package/screenshots/truth/select/selected-multi.png +0 -0
  59. package/screenshots/truth/select/selected-single.png +0 -0
  60. package/screenshots/truth/select/with-placeholder.png +0 -0
  61. package/screenshots/truth/select/without-placeholder.png +0 -0
  62. package/screenshots/truth/textinput/date-form.png +0 -0
  63. package/screenshots/truth/textinput/input-disabled.png +0 -0
  64. package/screenshots/truth/textinput/input-form.png +0 -0
  65. package/screenshots/truth/textinput/input-placeholder.png +0 -0
  66. package/screenshots/truth/textinput/input-updated.png +0 -0
  67. package/screenshots/truth/textinput/input.png +0 -0
  68. package/screenshots/truth/textinput/textarea.png +0 -0
  69. package/screenshots/truth/tip/bottom.png +0 -0
  70. package/screenshots/truth/tip/left.png +0 -0
  71. package/screenshots/truth/tip/right.png +0 -0
  72. package/screenshots/truth/tip/top.png +0 -0
  73. package/src/button/Button.ts +1 -0
  74. package/src/contacts/ContactChat.ts +93 -33
  75. package/src/contacts/ContactDetails.ts +23 -23
  76. package/src/contacts/ContactHistory.ts +156 -159
  77. package/src/contacts/events.ts +59 -44
  78. package/src/interfaces.ts +1 -1
  79. package/src/list/ContactList.ts +1 -1
  80. package/src/list/TembaList.ts +13 -4
  81. package/src/list/TembaMenu.ts +7 -2
  82. package/src/loading/Loading.ts +8 -1
  83. package/src/options/Options.ts +14 -2
  84. package/src/tip/Tip.ts +6 -0
  85. package/src/vectoricon/VectorIcon.ts +17 -5
  86. package/test/temba-contact-history.test.ts +2 -2
  87. package/test-assets/style.css +4 -1
  88. package/dist/d59a9ae2.js +0 -1
@@ -1,10 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/camelcase */
2
2
  import { css, property } from 'lit-element';
3
3
  import { html, TemplateResult } from 'lit-html';
4
- import { CustomEventType, Ticket } from '../interfaces';
4
+ import { Contact, CustomEventType, Ticket } from '../interfaces';
5
5
  import { RapidElement } from '../RapidElement';
6
6
  import { Asset, getAssets, getClasses, postJSON, throttle } from '../utils';
7
- import ResizeObserver from 'resize-observer-polyfill';
7
+
8
8
  import {
9
9
  AirtimeTransferredEvent,
10
10
  CampaignFiredEvent,
@@ -56,9 +56,39 @@ import {
56
56
  SCROLL_THRESHOLD,
57
57
  } from './helpers';
58
58
 
59
+ // when images load, make sure we are on the bottom of the scroll window if necessary
60
+ export const loadHandler = function (event) {
61
+ const target = event.target as HTMLElement;
62
+ const events = this.host.getEventsPane();
63
+ if (target.tagName == 'IMG') {
64
+ if (!this.host.showMessageAlert) {
65
+ if (
66
+ events.scrollTop > target.offsetTop - 1000 &&
67
+ target.offsetTop > events.scrollHeight - 500
68
+ ) {
69
+ this.host.scrollToBottom();
70
+ }
71
+ }
72
+ }
73
+ };
74
+
59
75
  export class ContactHistory extends RapidElement {
60
76
  public httpComplete: Promise<void | ContactHistoryPage>;
61
77
 
78
+ public constructor() {
79
+ super();
80
+ }
81
+
82
+ connectedCallback() {
83
+ super.connectedCallback();
84
+ this.shadowRoot.addEventListener('load', loadHandler, true);
85
+ }
86
+
87
+ disconnectedCallback() {
88
+ super.disconnectedCallback();
89
+ this.shadowRoot.removeEventListener('load', loadHandler, true);
90
+ }
91
+
62
92
  private getStickyId = (event: ContactEvent) => {
63
93
  if (event.type === Events.TICKET_OPENED) {
64
94
  const ticket = this.getTicketForEvent(event as TicketEvent);
@@ -95,6 +125,7 @@ export class ContactHistory extends RapidElement {
95
125
  flex-grow: 1;
96
126
  border-top-left-radius: var(--curvature);
97
127
  padding-top: 1em;
128
+ background: #fff;
98
129
  }
99
130
 
100
131
  temba-loading {
@@ -135,15 +166,27 @@ export class ContactHistory extends RapidElement {
135
166
  pointer: cursor;
136
167
  }
137
168
 
169
+ .scroll-title {
170
+ display: flex;
171
+ flex-direction: column;
172
+ z-index: 2;
173
+ border-top-left-radius: var(--curvature);
174
+ overflow: hidden;
175
+ box-shadow: 0px 3px 3px 0px rgba(0, 0, 0, 0.15);
176
+ background: rgb(240, 240, 240);
177
+ padding: 1em 1.2em;
178
+ font-size: 1.2em;
179
+ font-weight: 400;
180
+ }
181
+
138
182
  .sticky-bin {
139
183
  display: flex;
140
184
  flex-direction: column;
141
- position: fixed;
142
185
  z-index: 2;
143
186
  border-top-left-radius: var(--curvature);
144
187
  overflow: hidden;
145
- background: rgba(240, 240, 240, 0.95);
146
188
  box-shadow: 0px 3px 3px 0px rgba(0, 0, 0, 0.15);
189
+ background: rgb(240, 240, 240);
147
190
  }
148
191
 
149
192
  .sticky-bin temba-icon {
@@ -183,6 +226,9 @@ export class ContactHistory extends RapidElement {
183
226
  `;
184
227
  }
185
228
 
229
+ @property({ type: Object })
230
+ contact: Contact;
231
+
186
232
  @property({ type: String })
187
233
  uuid: string;
188
234
 
@@ -222,9 +268,6 @@ export class ContactHistory extends RapidElement {
222
268
  @property({ type: Array })
223
269
  tickets: Ticket[] = null;
224
270
 
225
- @property({ type: Object })
226
- currentTicket: Ticket = null;
227
-
228
271
  ticketEvents: { [uuid: string]: TicketEvent } = {};
229
272
 
230
273
  nextBefore: number;
@@ -236,35 +279,23 @@ export class ContactHistory extends RapidElement {
236
279
 
237
280
  public firstUpdated(changedProperties: Map<string, any>) {
238
281
  super.firstUpdated(changedProperties);
239
-
240
282
  this.handleClose = this.handleClose.bind(this);
241
-
242
- const stickyBin = this.getDiv('.sticky-bin');
243
- const resizer = new ResizeObserver(entries => {
244
- for (const entry of entries) {
245
- const eventContainer = entry.contentRect;
246
- stickyBin.style.width =
247
- eventContainer.width + eventContainer.left - 16 + 'px';
248
- }
249
- });
250
- resizer.observe(this);
251
283
  }
252
284
 
253
285
  public updated(changedProperties: Map<string, any>) {
254
286
  super.updated(changedProperties);
255
287
 
256
- // fire an event when our current ticket changes
257
- if (changedProperties.has('currentTicket')) {
258
- this.fireCustomEvent(CustomEventType.ContextChanged, {
259
- context: this.currentTicket,
260
- });
261
- }
262
-
263
288
  // fire an event if we get a new event
264
289
  if (changedProperties.has('mostRecentEvent')) {
265
290
  this.fireCustomEvent(CustomEventType.Refreshed);
266
291
  }
267
292
 
293
+ if (changedProperties.has('endDate')) {
294
+ if (this.refreshTimeout && this.endDate) {
295
+ window.clearTimeout(this.refreshTimeout);
296
+ }
297
+ }
298
+
268
299
  // if we don't have an endpoint infer one
269
300
  if (changedProperties.has('uuid')) {
270
301
  if (this.uuid == null) {
@@ -294,7 +325,8 @@ export class ContactHistory extends RapidElement {
294
325
  if (
295
326
  changedProperties.has('refreshing') &&
296
327
  this.refreshing &&
297
- this.endpoint
328
+ this.endpoint &&
329
+ !this.endDate
298
330
  ) {
299
331
  const after = (this.getLastEventTime() - 1) * 1000;
300
332
  let forceOpen = false;
@@ -488,30 +520,20 @@ export class ContactHistory extends RapidElement {
488
520
  stickyBin.removeChild(child);
489
521
  }
490
522
  }
491
- if (!this.currentTicket) {
492
- this.updateCurrentTicket();
493
- }
494
- }
495
- }
496
-
497
- private updateCurrentTicket() {
498
- const openTickets = (this.tickets || []).filter(
499
- ticket => ticket && ticket.status === 'open'
500
- );
501
- if (openTickets.length > 0) {
502
- this.currentTicket = openTickets[openTickets.length - 1];
503
523
  }
504
524
  }
505
525
 
506
526
  private refreshTickets() {
507
- let url = `/api/v2/tickets.json?contact=${this.uuid}`;
508
527
  if (this.ticket) {
509
- url = `${url}&ticket=${this.ticket}`;
510
- }
528
+ let url = `/api/v2/tickets.json?contact=${this.uuid}`;
529
+ if (this.ticket) {
530
+ url = `${url}&ticket=${this.ticket}`;
531
+ }
511
532
 
512
- getAssets(url).then((tickets: Ticket[]) => {
513
- this.tickets = tickets.reverse();
514
- });
533
+ getAssets(url).then((tickets: Ticket[]) => {
534
+ this.tickets = tickets.reverse();
535
+ });
536
+ }
515
537
  }
516
538
 
517
539
  public getEventsPane() {
@@ -568,6 +590,10 @@ export class ContactHistory extends RapidElement {
568
590
  }
569
591
 
570
592
  private scheduleRefresh(wait = -1) {
593
+ if (this.endDate) {
594
+ return;
595
+ }
596
+
571
597
  let refreshWait = wait;
572
598
 
573
599
  if (wait === -1) {
@@ -596,11 +622,12 @@ export class ContactHistory extends RapidElement {
596
622
  private reset() {
597
623
  // clear out our sticky bin which we manipulated manually
598
624
  const stickyBin = this.getDiv('.sticky-bin');
599
- while (stickyBin.childElementCount > 0) {
600
- stickyBin.removeChild(stickyBin.firstElementChild);
625
+ if (stickyBin) {
626
+ while (stickyBin.childElementCount > 0) {
627
+ stickyBin.removeChild(stickyBin.firstElementChild);
628
+ }
601
629
  }
602
630
 
603
- this.currentTicket = null;
604
631
  this.endpoint = null;
605
632
  this.tickets = null;
606
633
  this.ticketEvents = {};
@@ -657,43 +684,12 @@ export class ContactHistory extends RapidElement {
657
684
  sticky.classList.add('pinned');
658
685
  (sticky as any).eventElement = eventElement;
659
686
  stickyBin.appendChild(eventElement);
660
- const uuid = eventElement.getAttribute('data-sticky-id');
661
- const ticket = this.getTicket(uuid);
662
- if (ticket) {
663
- if (
664
- !this.currentTicket ||
665
- this.currentTicket.uuid !== ticket.uuid
666
- ) {
667
- this.currentTicket = ticket;
668
- }
669
- }
670
687
  }
671
688
  } else {
672
689
  const eventElement = (sticky as any).eventElement;
673
690
  if (scrollBoundary < sticky.offsetTop + sticky.offsetHeight) {
674
691
  sticky.appendChild(eventElement);
675
692
  sticky.classList.remove('pinned');
676
-
677
- const uuid = eventElement.getAttribute('data-sticky-id');
678
- let previousTicket: Ticket = null;
679
- for (const ticket of this.tickets) {
680
- if (ticket.uuid === uuid) {
681
- break;
682
- }
683
- previousTicket = ticket;
684
- }
685
-
686
- if (
687
- previousTicket &&
688
- (!this.currentTicket ||
689
- this.currentTicket.uuid !== previousTicket.uuid)
690
- ) {
691
- if (previousTicket.status === 'open') {
692
- this.currentTicket = previousTicket;
693
- } else {
694
- this.currentTicket = null;
695
- }
696
- }
697
693
  }
698
694
  }
699
695
  });
@@ -765,7 +761,7 @@ export class ContactHistory extends RapidElement {
765
761
  if (activeTicket && ticket && ticket.status === 'open') {
766
762
  closeHandler = this.handleClose;
767
763
  }
768
- return renderTicketOpened(ticketEvent, closeHandler);
764
+ return renderTicketOpened(ticketEvent, closeHandler, !this.ticket);
769
765
  }
770
766
  case Events.TICKET_NOTE_ADDED:
771
767
  return renderNoteCreated(event as TicketEvent, this.agent);
@@ -773,10 +769,14 @@ export class ContactHistory extends RapidElement {
773
769
  case Events.TICKET_ASSIGNED:
774
770
  return renderTicketAssigned(event as TicketEvent);
775
771
  case Events.TICKET_REOPENED: {
776
- return renderTicketAction(event as TicketEvent, 'reopened');
772
+ return renderTicketAction(
773
+ event as TicketEvent,
774
+ 'reopened',
775
+ !this.ticket
776
+ );
777
777
  }
778
778
  case Events.TICKET_CLOSED:
779
- return renderTicketAction(event as TicketEvent, 'closed');
779
+ return renderTicketAction(event as TicketEvent, 'closed', !this.ticket);
780
780
 
781
781
  case Events.ERROR:
782
782
  case Events.FAILURE:
@@ -816,7 +816,6 @@ export class ContactHistory extends RapidElement {
816
816
  this.fireCustomEvent(CustomEventType.ContentChanged, {
817
817
  ticket: { uuid, status: 'closed' },
818
818
  });
819
- this.updateCurrentTicket();
820
819
  })
821
820
  .catch((response: any) => {
822
821
  console.error(response);
@@ -824,24 +823,22 @@ export class ContactHistory extends RapidElement {
824
823
  }
825
824
 
826
825
  public checkForAgentAssignmentEvent(agent: number) {
827
- if (this.currentTicket) {
828
- this.httpComplete = getAssets(
829
- `/api/v2/tickets.json?ticket=${this.currentTicket.uuid}`
830
- ).then((assets: Asset[]) => {
831
- if (assets.length === 1) {
832
- const ticket = assets[0] as Ticket;
833
- if (ticket.assignee && ticket.assignee.id === agent) {
834
- this.fireCustomEvent(CustomEventType.ContentChanged, {
835
- ticket: { uuid: this.currentTicket.uuid, assigned: 'self' },
836
- });
837
- } else {
838
- this.fireCustomEvent(CustomEventType.ContentChanged, {
839
- ticket: { uuid: this.currentTicket.uuid, assigned: 'other' },
840
- });
841
- }
826
+ this.httpComplete = getAssets(
827
+ `/api/v2/tickets.json?ticket=${this.ticket}`
828
+ ).then((assets: Asset[]) => {
829
+ if (assets.length === 1) {
830
+ const ticket = assets[0] as Ticket;
831
+ if (ticket.assignee && ticket.assignee.id === agent) {
832
+ this.fireCustomEvent(CustomEventType.ContentChanged, {
833
+ ticket: { uuid: this.ticket, assigned: 'self' },
834
+ });
835
+ } else {
836
+ this.fireCustomEvent(CustomEventType.ContentChanged, {
837
+ ticket: { uuid: this.ticket, assigned: 'other' },
838
+ });
842
839
  }
843
- });
844
- }
840
+ }
841
+ });
845
842
  }
846
843
 
847
844
  public getEventHandlers() {
@@ -864,7 +861,9 @@ export class ContactHistory extends RapidElement {
864
861
 
865
862
  const renderedEvent = html`
866
863
  <div
867
- class="event ${event.type} ${isSticky ? 'has-sticky' : ''}"
864
+ class="${this.ticket
865
+ ? 'active-ticket'
866
+ : ''} event ${event.type} ${isSticky ? 'has-sticky' : ''}"
868
867
  data-sticky-id="${stickyId}"
869
868
  >
870
869
  ${this.renderEvent(event)}
@@ -901,7 +900,8 @@ export class ContactHistory extends RapidElement {
901
900
 
902
901
  const renderedEvent = renderTicketOpened(
903
902
  ticketOpenedEvent,
904
- this.handleClose
903
+ this.handleClose,
904
+ !this.ticket
905
905
  );
906
906
  return html`<div class="event ticket_opened">
907
907
  ${renderedEvent}
@@ -912,68 +912,65 @@ export class ContactHistory extends RapidElement {
912
912
  : null;
913
913
 
914
914
  return html`
915
- <div class="sticky-bin">${unfetchedTickets}</div>
915
+ ${this.ticket
916
+ ? html`<div class="sticky-bin">${unfetchedTickets}</div>`
917
+ : this.contact
918
+ ? html`<div class="scroll-title">${this.contact.name}</div>`
919
+ : null}
916
920
  ${this.fetching
917
921
  ? html`<temba-loading units="5" size="10"></temba-loading>`
918
922
  : html`<div style="height:0em"></div>`}
919
923
  <div class="events" @scroll=${this.handleScroll}>
920
- ${this.tickets
921
- ? this.eventGroups.map((eventGroup: EventGroup, index: number) => {
922
- const grouping = getEventGroupType(
923
- eventGroup.events[0],
924
- this.ticket
925
- );
926
- const groupIndex = this.eventGroups.length - index - 1;
927
-
928
- const classes = getClasses({
929
- grouping: true,
930
- [grouping]: true,
931
- expanded: eventGroup.open,
932
- closing: eventGroup.closing,
933
- });
934
-
935
- return html`<div class="${classes}">
936
- ${grouping === 'verbose'
937
- ? html`<div
938
- class="event-count"
939
- @click=${this.handleEventGroupShow}
940
- data-group-index="${groupIndex}"
941
- >
942
- ${eventGroup.events.length}
943
- ${eventGroup.events.length === 1
944
- ? html`event`
945
- : html`events`}
946
- </div>`
947
- : null}
948
- ${grouping === 'verbose'
949
- ? html`
950
- <temba-icon
951
- @click=${this.handleEventGroupHide}
952
- data-group-index="${groupIndex}"
953
- class="grouping-close-button"
954
- name="x"
955
- clickable
956
- ></temba-icon>
957
- `
958
- : null}
959
- ${eventGroup.events.map((event: ContactEvent) => {
960
- if (
961
- event.type === Events.TICKET_ASSIGNED &&
962
- (event as TicketEvent).note
963
- ) {
964
- const noteEvent = { ...event };
965
- noteEvent.type = Events.TICKET_NOTE_ADDED;
966
-
967
- return html`${this.renderEventContainer(
968
- noteEvent
969
- )}${this.renderEventContainer(event)}`;
970
- } else {
971
- return this.renderEventContainer(event);
972
- }
973
- })}
974
- </div>`;
975
- })
976
- : null}
924
+ ${this.eventGroups.map((eventGroup: EventGroup, index: number) => {
925
+ const grouping = getEventGroupType(eventGroup.events[0], this.ticket);
926
+ const groupIndex = this.eventGroups.length - index - 1;
927
+
928
+ const classes = getClasses({
929
+ grouping: true,
930
+ [grouping]: true,
931
+ expanded: eventGroup.open,
932
+ closing: eventGroup.closing,
933
+ });
934
+
935
+ return html`<div class="${classes}">
936
+ ${grouping === 'verbose'
937
+ ? html`<div
938
+ class="event-count"
939
+ @click=${this.handleEventGroupShow}
940
+ data-group-index="${groupIndex}"
941
+ >
942
+ ${eventGroup.events.length}
943
+ ${eventGroup.events.length === 1 ? html`event` : html`events`}
944
+ </div>`
945
+ : null}
946
+ ${grouping === 'verbose'
947
+ ? html`
948
+ <temba-icon
949
+ @click=${this.handleEventGroupHide}
950
+ data-group-index="${groupIndex}"
951
+ class="grouping-close-button"
952
+ name="x"
953
+ clickable
954
+ ></temba-icon>
955
+ `
956
+ : null}
957
+ ${eventGroup.events.map((event: ContactEvent) => {
958
+ if (
959
+ event.type === Events.TICKET_ASSIGNED &&
960
+ (event as TicketEvent).note
961
+ ) {
962
+ const noteEvent = { ...event };
963
+ noteEvent.type = Events.TICKET_NOTE_ADDED;
964
+
965
+ return html`${this.renderEventContainer(
966
+ noteEvent
967
+ )}${this.renderEventContainer(event)}`;
968
+ } else {
969
+ return this.renderEventContainer(event);
970
+ }
971
+ })}
972
+ </div>`;
973
+ })}
977
974
  </div>
978
975
 
979
976
  <div class="new-messages-container">
@@ -284,7 +284,7 @@ export const getEventStyles = () => {
284
284
  fill: rgba(223, 65, 159, 1);
285
285
  }
286
286
 
287
- .ticket_opened {
287
+ .active-ticket.ticket_opened {
288
288
  padding: 0em 1em;
289
289
  }
290
290
 
@@ -622,9 +622,14 @@ export const getEventGroupType = (event: ContactEvent, ticket: string) => {
622
622
  case Events.TICKET_OPENED:
623
623
  case Events.TICKET_CLOSED:
624
624
  case Events.TICKET_REOPENED:
625
- if (!ticket || (event as TicketEvent).ticket.uuid === ticket) {
625
+ if (!ticket) {
626
+ return 'verbose';
627
+ }
628
+
629
+ if ((event as TicketEvent).ticket.uuid === ticket) {
626
630
  return 'tickets';
627
631
  }
632
+
628
633
  break;
629
634
  case Events.FLOW_ENTERED:
630
635
  case Events.FLOW_EXITED:
@@ -681,7 +686,7 @@ export const renderAttachment = (attachment: string): TemplateResult => {
681
686
  inner = html`<a href="${url}"><img src="${url}" style="width:100%;height:auto;display:block"></img></a>`;
682
687
  } else if (ext === 'pdf') {
683
688
  return html`<div
684
- style="width:100%;height:300px;border-radius:var(--curvature); box-shadow:0px 0px 9px -4px rgb(140,140,140);overflow:hidden"
689
+ style="width:100%;height:300px;border-radius:var(--curvature);box-shadow:0px 0px 10px -1px rgb(160 160 160);overflow:hidden"
685
690
  ><embed src="${url}#view=Fit" type="application/pdf" frameBorder="0" scrolling="auto" height="100%" width="100%"></embed></div>`;
686
691
  } else if (mediaType === 'video') {
687
692
  return html`<video max-width="400px" height="auto" controls="controls">
@@ -695,7 +700,7 @@ export const renderAttachment = (attachment: string): TemplateResult => {
695
700
  }
696
701
 
697
702
  return html`<div
698
- style="width:40%;max-width:900px;border-radius:var(--curvature); box-shadow:0px 0px 9px -4px rgb(140,140,140);overflow:hidden"
703
+ style="width:100%;max-width:300px;border-radius:var(--curvature); box-shadow:0px 0px 10px -1px rgb(160 160 160);overflow:hidden"
699
704
  >
700
705
  ${inner}
701
706
  </div>`;
@@ -892,20 +897,6 @@ export const renderNoteCreated = (
892
897
  </div>`;
893
898
  };
894
899
 
895
- export const renderTicketClosed = (event: TicketEvent): TemplateResult => {
896
- const closed = new Date(event.ticket.opened_on);
897
- return html`
898
- <div class="assigned active">
899
- <div style="text-align:center">
900
- ${getDisplayName(event.created_by)} closed this ticket
901
- </div>
902
- <div class="subtext" style="justify-content:center">
903
- ${timeSince(closed, { hideRecentText: true, suffix: ' ago' })}
904
- </div>
905
- </div>
906
- `;
907
- };
908
-
909
900
  const getTicketIcon = (event: TicketEvent) => {
910
901
  let icon = 'inbox';
911
902
  if (event.ticket.ticketer.name.indexOf('Email') > -1) {
@@ -918,9 +909,21 @@ const getTicketIcon = (event: TicketEvent) => {
918
909
 
919
910
  export const renderTicketAction = (
920
911
  event: TicketEvent,
921
- action: string
912
+ action: string,
913
+ grouped: boolean
922
914
  ): TemplateResult => {
923
915
  const reopened = new Date(event.created_on);
916
+ const icon = getTicketIcon(event);
917
+ if (grouped) {
918
+ return html`<div class="" style="display: flex">
919
+ <temba-icon name="${icon}"></temba-icon>
920
+ <div class="description">
921
+ ${getDisplayName(event.created_by)} ${action} a
922
+ <a href="/tickets/all/open/${event.ticket.uuid}">ticket</a>
923
+ </div>
924
+ </div>`;
925
+ }
926
+
924
927
  return html`
925
928
  <div class="assigned active">
926
929
  <div style="text-align:center">
@@ -954,35 +957,47 @@ export const renderTicketAssigned = (event: TicketEvent): TemplateResult => {
954
957
 
955
958
  export const renderTicketOpened = (
956
959
  event: TicketEvent,
957
- handleClose: (uuid: string) => void
960
+ handleClose: (uuid: string) => void,
961
+ grouped: boolean
958
962
  ): TemplateResult => {
959
963
  const icon = getTicketIcon(event);
960
- return html`
961
- <temba-icon size="1.5" name="${icon}"></temba-icon>
962
964
 
963
- <div class="active" style="flex-grow:1;">
964
- Opened
965
- <div class="attn">
966
- ${event.ticket.topic ? event.ticket.topic.name : 'General'}
965
+ if (grouped) {
966
+ return html`<div class="" style="display: flex">
967
+ <temba-icon name="${icon}"></temba-icon>
968
+ <div class="description">
969
+ ${event.ticket.topic.name}
970
+ <a href="/tickets/all/open/${event.ticket.uuid}">ticket</a> was opened
967
971
  </div>
968
- <div class="subtext">${timeSince(new Date(event.created_on))}</div>
969
- </div>
970
- ${handleClose
971
- ? html`
972
- <temba-tip text="Resolve" position="left" style="width:1.5em">
973
- <temba-icon
974
- class="clickable"
975
- size="1.5"
976
- name="check"
977
- @click=${() => {
978
- handleClose(event.ticket.uuid);
979
- }}
980
- ?clickable=${open}
981
- />
982
- </temba-tip>
983
- `
984
- : null}
985
- `;
972
+ </div>`;
973
+ } else {
974
+ return html`
975
+ <temba-icon size="1.5" name="${icon}"></temba-icon>
976
+
977
+ <div class="active" style="flex-grow:1;">
978
+ Opened
979
+ <div class="attn">
980
+ ${event.ticket.topic ? event.ticket.topic.name : 'General'}
981
+ </div>
982
+ <div class="subtext">${timeSince(new Date(event.created_on))}</div>
983
+ </div>
984
+ ${handleClose
985
+ ? html`
986
+ <temba-tip text="Resolve" position="left" style="width:1.5em">
987
+ <temba-icon
988
+ class="clickable"
989
+ size="1.5"
990
+ name="check"
991
+ @click=${() => {
992
+ handleClose(event.ticket.uuid);
993
+ }}
994
+ ?clickable=${open}
995
+ />
996
+ </temba-tip>
997
+ `
998
+ : null}
999
+ `;
1000
+ }
986
1001
  };
987
1002
 
988
1003
  export const renderErrorMessage = (
package/src/interfaces.ts CHANGED
@@ -10,13 +10,13 @@ export interface User {
10
10
  export interface Ticket {
11
11
  uuid: string;
12
12
  subject: string;
13
- topic: ObjectReference;
14
13
  body?: string;
15
14
  closed_on: string;
16
15
  opened_on: string;
17
16
  status: string;
18
17
  contact: ObjectReference;
19
18
  ticketer: ObjectReference;
19
+ topic: ObjectReference;
20
20
  assignee?: User;
21
21
  }
22
22
 
@@ -23,7 +23,7 @@ export class ContactList extends TembaList {
23
23
  this.renderOption = (contact: Contact): TemplateResult => {
24
24
  return html`
25
25
  <div
26
- style="display: flex-col; align-items:center; margin-top: 0.1em; margin-bottom: 0.1em"
26
+ style="display: align-items:center; margin-top: 0.1em; margin-bottom: 0.1em"
27
27
  >
28
28
  <div
29
29
  style="display:flex; align-items: flex-start;border:0px solid red;"