@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.
@@ -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
+ }
@@ -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);