@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.
- package/CHANGELOG.md +9 -0
- package/dist/a525ddb7.js +1 -0
- package/dist/index.js +1 -1
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/dist/templates/components-body.html +1 -1
- package/dist/templates/components-head.html +1 -1
- package/out-tsc/src/button/Button.js +1 -0
- package/out-tsc/src/button/Button.js.map +1 -1
- package/out-tsc/src/contacts/ContactChat.js +81 -33
- package/out-tsc/src/contacts/ContactChat.js.map +1 -1
- package/out-tsc/src/contacts/ContactDetails.js +22 -22
- package/out-tsc/src/contacts/ContactDetails.js.map +1 -1
- package/out-tsc/src/contacts/ContactHistory.js +131 -138
- package/out-tsc/src/contacts/ContactHistory.js.map +1 -1
- package/out-tsc/src/contacts/events.js +55 -44
- package/out-tsc/src/contacts/events.js.map +1 -1
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/list/ContactList.js +1 -1
- package/out-tsc/src/list/ContactList.js.map +1 -1
- package/out-tsc/src/list/TembaList.js +10 -3
- package/out-tsc/src/list/TembaList.js.map +1 -1
- package/out-tsc/src/list/TembaMenu.js +7 -2
- package/out-tsc/src/list/TembaMenu.js.map +1 -1
- package/out-tsc/src/loading/Loading.js +9 -1
- package/out-tsc/src/loading/Loading.js.map +1 -1
- package/out-tsc/src/options/Options.js +14 -2
- package/out-tsc/src/options/Options.js.map +1 -1
- package/out-tsc/src/tip/Tip.js +6 -0
- package/out-tsc/src/tip/Tip.js.map +1 -1
- package/out-tsc/src/vectoricon/VectorIcon.js +17 -5
- package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
- package/out-tsc/test/temba-contact-history.test.js +2 -2
- package/out-tsc/test/temba-contact-history.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/contacts/history-expanded.png +0 -0
- package/screenshots/truth/contacts/history.png +0 -0
- package/screenshots/truth/list/items-selected.png +0 -0
- package/screenshots/truth/list/items-updated.png +0 -0
- package/screenshots/truth/list/items.png +0 -0
- package/screenshots/truth/modax/simple.png +0 -0
- package/screenshots/truth/options/block.png +0 -0
- package/screenshots/truth/select/disabled-multi-selection.png +0 -0
- package/screenshots/truth/select/disabled-selection.png +0 -0
- package/screenshots/truth/select/disabled.png +0 -0
- package/screenshots/truth/select/embedded.png +0 -0
- package/screenshots/truth/select/expression-selected.png +0 -0
- package/screenshots/truth/select/expressions.png +0 -0
- package/screenshots/truth/select/functions.png +0 -0
- package/screenshots/truth/select/local-options.png +0 -0
- package/screenshots/truth/select/remote-options.png +0 -0
- package/screenshots/truth/select/search-enabled.png +0 -0
- package/screenshots/truth/select/search-multi-no-matches.png +0 -0
- package/screenshots/truth/select/search-selected-focus.png +0 -0
- package/screenshots/truth/select/search-selected.png +0 -0
- package/screenshots/truth/select/search-with-selected.png +0 -0
- package/screenshots/truth/select/searching.png +0 -0
- package/screenshots/truth/select/selected-multi.png +0 -0
- package/screenshots/truth/select/selected-single.png +0 -0
- package/screenshots/truth/select/with-placeholder.png +0 -0
- package/screenshots/truth/select/without-placeholder.png +0 -0
- package/screenshots/truth/textinput/date-form.png +0 -0
- package/screenshots/truth/textinput/input-disabled.png +0 -0
- package/screenshots/truth/textinput/input-form.png +0 -0
- package/screenshots/truth/textinput/input-placeholder.png +0 -0
- package/screenshots/truth/textinput/input-updated.png +0 -0
- package/screenshots/truth/textinput/input.png +0 -0
- package/screenshots/truth/textinput/textarea.png +0 -0
- package/screenshots/truth/tip/bottom.png +0 -0
- package/screenshots/truth/tip/left.png +0 -0
- package/screenshots/truth/tip/right.png +0 -0
- package/screenshots/truth/tip/top.png +0 -0
- package/src/button/Button.ts +1 -0
- package/src/contacts/ContactChat.ts +93 -33
- package/src/contacts/ContactDetails.ts +23 -23
- package/src/contacts/ContactHistory.ts +156 -159
- package/src/contacts/events.ts +59 -44
- package/src/interfaces.ts +1 -1
- package/src/list/ContactList.ts +1 -1
- package/src/list/TembaList.ts +13 -4
- package/src/list/TembaMenu.ts +7 -2
- package/src/loading/Loading.ts +8 -1
- package/src/options/Options.ts +14 -2
- package/src/tip/Tip.ts +6 -0
- package/src/vectoricon/VectorIcon.ts +17 -5
- package/test/temba-contact-history.test.ts +2 -2
- package/test-assets/style.css +4 -1
- 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
|
-
|
|
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 =
|
|
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
|
-
|
|
513
|
-
|
|
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
|
-
|
|
600
|
-
|
|
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(
|
|
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
|
-
|
|
828
|
-
this.
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
this.
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
this.
|
|
839
|
-
|
|
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="
|
|
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
|
-
|
|
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.
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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">
|
package/src/contacts/events.ts
CHANGED
|
@@ -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
|
|
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);
|
|
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:
|
|
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
|
-
|
|
964
|
-
|
|
965
|
-
<
|
|
966
|
-
|
|
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
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
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
|
|
package/src/list/ContactList.ts
CHANGED
|
@@ -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:
|
|
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;"
|