@nyaruka/temba-components 0.106.0 → 0.107.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 +7 -0
- package/dist/temba-components.js +481 -368
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/date/TembaDate.js +4 -0
- package/out-tsc/src/date/TembaDate.js.map +1 -1
- package/out-tsc/src/progress/FlowStartProgress.js +60 -0
- package/out-tsc/src/progress/FlowStartProgress.js.map +1 -0
- package/out-tsc/src/progress/ProgressBar.js +163 -0
- package/out-tsc/src/progress/ProgressBar.js.map +1 -0
- package/out-tsc/src/store/Store.js +23 -0
- package/out-tsc/src/store/Store.js.map +1 -1
- package/out-tsc/temba-modules.js +4 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/package.json +2 -2
- package/src/date/TembaDate.ts +3 -0
- package/src/progress/FlowStartProgress.ts +67 -0
- package/src/progress/ProgressBar.ts +159 -0
- package/src/store/Store.ts +28 -0
- package/temba-modules.ts +4 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { html, PropertyValueMap, TemplateResult } from 'lit';
|
|
2
|
+
import { RapidElement } from '../RapidElement';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { fetchResults } from '../utils';
|
|
5
|
+
|
|
6
|
+
export class FlowStartProgress extends RapidElement {
|
|
7
|
+
@property({ type: String })
|
|
8
|
+
uuid: string;
|
|
9
|
+
|
|
10
|
+
@property({ type: Number })
|
|
11
|
+
started: number;
|
|
12
|
+
|
|
13
|
+
@property({ type: Number })
|
|
14
|
+
total: number;
|
|
15
|
+
|
|
16
|
+
@property({ type: Number })
|
|
17
|
+
refreshes: number = 0;
|
|
18
|
+
|
|
19
|
+
@property({ type: String })
|
|
20
|
+
eta: string;
|
|
21
|
+
|
|
22
|
+
public updated(
|
|
23
|
+
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
24
|
+
): void {
|
|
25
|
+
super.updated(changes);
|
|
26
|
+
if (changes.has('uuid')) {
|
|
27
|
+
this.refresh();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public refresh(): void {
|
|
32
|
+
fetchResults(`/api/v2/flow_starts.json?uuid=${this.uuid}`).then(
|
|
33
|
+
(data: any) => {
|
|
34
|
+
if (data.length > 0) {
|
|
35
|
+
this.refreshes++;
|
|
36
|
+
const start = data[0];
|
|
37
|
+
this.started = start.progress.started;
|
|
38
|
+
this.total = start.progress.total;
|
|
39
|
+
|
|
40
|
+
const elapsed =
|
|
41
|
+
new Date().getTime() - new Date(start.created_on).getTime();
|
|
42
|
+
const rate = this.started / (elapsed / 1000);
|
|
43
|
+
|
|
44
|
+
// calculate the estimated time of arrival
|
|
45
|
+
this.eta = new Date(
|
|
46
|
+
new Date().getTime() + ((this.total - this.started) / rate) * 1000
|
|
47
|
+
).toISOString();
|
|
48
|
+
|
|
49
|
+
if (this.started < this.total) {
|
|
50
|
+
// refresh with a backoff up to 1 minute
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
this.refresh();
|
|
53
|
+
}, Math.min(1000 * this.refreshes, 60000));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public render(): TemplateResult {
|
|
61
|
+
return html`<temba-progress
|
|
62
|
+
total=${this.total}
|
|
63
|
+
current=${this.started}
|
|
64
|
+
eta=${this.eta}
|
|
65
|
+
></temba-progress>`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { css, html, PropertyValueMap, TemplateResult } from 'lit';
|
|
2
|
+
import { RapidElement } from '../RapidElement';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
|
|
5
|
+
export class ProgressBar extends RapidElement {
|
|
6
|
+
static styles = css`
|
|
7
|
+
.meter {
|
|
8
|
+
display: flex;
|
|
9
|
+
box-sizing: content-box;
|
|
10
|
+
height: 8px;
|
|
11
|
+
position: relative;
|
|
12
|
+
background: #f1f1f1;
|
|
13
|
+
border-radius: var(--curvature);
|
|
14
|
+
padding: 4px;
|
|
15
|
+
box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.05);
|
|
16
|
+
}
|
|
17
|
+
.meter > span {
|
|
18
|
+
display: block;
|
|
19
|
+
height: 100%;
|
|
20
|
+
border-top-right-radius: var(--curvature);
|
|
21
|
+
border-bottom-right-radius: var(--curvature);
|
|
22
|
+
border-top-left-radius: var(--curvature);
|
|
23
|
+
border-bottom-left-radius: var(--curvature);
|
|
24
|
+
background-color: var(--color-primary-dark);
|
|
25
|
+
background-image: linear-gradient(
|
|
26
|
+
center bottom,
|
|
27
|
+
rgb(43, 194, 83) 37%,
|
|
28
|
+
rgb(84, 240, 83) 69%
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
position: relative;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
}
|
|
34
|
+
.meter > span:after,
|
|
35
|
+
.animate > span > span {
|
|
36
|
+
content: '';
|
|
37
|
+
position: absolute;
|
|
38
|
+
top: 0;
|
|
39
|
+
left: 0;
|
|
40
|
+
bottom: 0;
|
|
41
|
+
right: 0;
|
|
42
|
+
background-image: linear-gradient(
|
|
43
|
+
-45deg,
|
|
44
|
+
rgba(255, 255, 255, 0.2) 25%,
|
|
45
|
+
transparent 25%,
|
|
46
|
+
transparent 50%,
|
|
47
|
+
rgba(255, 255, 255, 0.2) 50%,
|
|
48
|
+
rgba(255, 255, 255, 0.2) 75%,
|
|
49
|
+
transparent 75%,
|
|
50
|
+
transparent
|
|
51
|
+
);
|
|
52
|
+
z-index: 1;
|
|
53
|
+
background-size: 50px 50px;
|
|
54
|
+
animation: move 16s linear infinite;
|
|
55
|
+
border-top-right-radius: var(--curvature);
|
|
56
|
+
border-bottom-right-radius: var(--curvature);
|
|
57
|
+
border-top-left-radius: var(--curvature);
|
|
58
|
+
border-bottom-left-radius: var(--curvature);
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.animate > span:after {
|
|
63
|
+
display: none;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@keyframes move {
|
|
67
|
+
0% {
|
|
68
|
+
background-position: 0 0;
|
|
69
|
+
}
|
|
70
|
+
100% {
|
|
71
|
+
background-position: 50px 50px;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.complete {
|
|
76
|
+
transition: width 2s;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.incomplete {
|
|
80
|
+
flex-grow: 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.etc {
|
|
84
|
+
font-size: 0.7em;
|
|
85
|
+
padding: 4px;
|
|
86
|
+
padding-top: 1px;
|
|
87
|
+
padding-left: 8px;
|
|
88
|
+
padding-right: 8px;
|
|
89
|
+
margin-top: -4px;
|
|
90
|
+
margin-bottom: -4px;
|
|
91
|
+
margin-right: -4px;
|
|
92
|
+
margin-left: 0.5em;
|
|
93
|
+
background: rgba(0, 0, 0, 0.07);
|
|
94
|
+
font-weight: bold;
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.meter.done > span:after,
|
|
99
|
+
.done .animate > span > span {
|
|
100
|
+
display: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.meter.done > span {
|
|
104
|
+
background: rgb(var(--success-rgb));
|
|
105
|
+
}
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
@property({ type: Number })
|
|
109
|
+
total = 100;
|
|
110
|
+
|
|
111
|
+
@property({ type: Number })
|
|
112
|
+
current = 0;
|
|
113
|
+
|
|
114
|
+
@property({ type: Number })
|
|
115
|
+
pct = 0;
|
|
116
|
+
|
|
117
|
+
@property({ type: Boolean })
|
|
118
|
+
done = false;
|
|
119
|
+
|
|
120
|
+
@property({ type: String })
|
|
121
|
+
eta: string;
|
|
122
|
+
|
|
123
|
+
@property({ type: String, attribute: false })
|
|
124
|
+
estimatedCompletionDate: Date;
|
|
125
|
+
|
|
126
|
+
@property({ type: Boolean })
|
|
127
|
+
showEstimatedCompletion = false;
|
|
128
|
+
|
|
129
|
+
public updated(
|
|
130
|
+
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
131
|
+
): void {
|
|
132
|
+
if (changes.has('eta') && this.eta) {
|
|
133
|
+
this.estimatedCompletionDate = new Date(this.eta);
|
|
134
|
+
this.showEstimatedCompletion = this.estimatedCompletionDate > new Date();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (changes.has('current')) {
|
|
138
|
+
this.pct = Math.floor(Math.min((this.current / this.total) * 100, 100));
|
|
139
|
+
this.done = this.pct >= 100;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public render(): TemplateResult {
|
|
144
|
+
return html`<div class="meter ${this.done ? 'done' : ''}">
|
|
145
|
+
<span class="complete" style="width: ${this.pct}%"></span>
|
|
146
|
+
<div class="incomplete"></div>
|
|
147
|
+
${this.pct >= 0 || this.estimatedCompletionDate
|
|
148
|
+
? html` <div class="etc">
|
|
149
|
+
${this.estimatedCompletionDate
|
|
150
|
+
? html`<temba-date
|
|
151
|
+
value="${this.estimatedCompletionDate.toISOString()}"
|
|
152
|
+
display="countdown"
|
|
153
|
+
></temba-date>`
|
|
154
|
+
: html`${this.pct}%`}
|
|
155
|
+
</div>`
|
|
156
|
+
: null}
|
|
157
|
+
</div>`;
|
|
158
|
+
}
|
|
159
|
+
}
|
package/src/store/Store.ts
CHANGED
|
@@ -289,6 +289,34 @@ export class Store extends RapidElement {
|
|
|
289
289
|
});
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
public shiftAndRound(duration, unit: string, singular: string) {
|
|
293
|
+
const value = Math.round(duration.shiftTo(unit).get(unit));
|
|
294
|
+
if (value == 1) {
|
|
295
|
+
return `1 ${singular}`;
|
|
296
|
+
} else {
|
|
297
|
+
return `${value} ${unit}`;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
public getCountdown(futureDate: DateTime) {
|
|
302
|
+
const duration = futureDate.diff(DateTime.now());
|
|
303
|
+
const comps = duration.rescale();
|
|
304
|
+
|
|
305
|
+
if (comps.months > 0) {
|
|
306
|
+
return '> 1 month';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (comps.days > 1) {
|
|
310
|
+
return `~ ${this.shiftAndRound(comps, 'days', 'day')}`;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (comps.hours > 0) {
|
|
314
|
+
return `~ ${this.shiftAndRound(comps, 'hours', 'hour')}`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return `~ ${this.shiftAndRound(comps, 'minutes', 'minute')}`;
|
|
318
|
+
}
|
|
319
|
+
|
|
292
320
|
public getShortDuration(scheduled: DateTime, compareDate: DateTime = null) {
|
|
293
321
|
const now = compareDate || DateTime.now();
|
|
294
322
|
return scheduled
|
package/temba-modules.ts
CHANGED
|
@@ -59,6 +59,8 @@ import { Chat } from './src/chat/Chat';
|
|
|
59
59
|
import { MediaPicker } from './src/mediapicker/MediaPicker';
|
|
60
60
|
import { ContactNotepad } from './src/contacts/ContactNotepad';
|
|
61
61
|
import { OutboxMonitor } from './src/outboxmonitor/OutboxMonitor';
|
|
62
|
+
import { ProgressBar } from './src/progress/ProgressBar';
|
|
63
|
+
import { FlowStartProgress } from './src/progress/FlowStartProgress';
|
|
62
64
|
|
|
63
65
|
export function addCustomElement(name: string, comp: any) {
|
|
64
66
|
if (!window.customElements.get(name)) {
|
|
@@ -128,3 +130,5 @@ addCustomElement('temba-chat', Chat);
|
|
|
128
130
|
addCustomElement('temba-media-picker', MediaPicker);
|
|
129
131
|
addCustomElement('temba-contact-notepad', ContactNotepad);
|
|
130
132
|
addCustomElement('temba-outbox-monitor', OutboxMonitor);
|
|
133
|
+
addCustomElement('temba-progress', ProgressBar);
|
|
134
|
+
addCustomElement('temba-flowstart-progress', FlowStartProgress);
|