@nyaruka/temba-components 0.54.2 → 0.55.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.
@@ -34,17 +34,41 @@ export class ContactFieldEditor extends RapidElement {
34
34
 
35
35
  static get styles() {
36
36
  return css`
37
+ :host {
38
+ --transition-speed: 0ms;
39
+ }
40
+
37
41
  .wrapper {
38
42
  --temba-textinput-padding: 1.4em 0.8em 0.4em 0.8em;
39
43
  --disabled-opacity: 1;
40
44
  position: relative;
45
+ --color-widget-bg: transparent;
46
+ --color-widget-bg-focused: #fff;
47
+ --widget-box-shadow: none;
48
+ padding-bottom: 0.6em;
49
+ border-bottom: 1px solid #ececec;
50
+ }
51
+
52
+ .wrapper.disabled {
53
+ --color-widget-border: transparent;
54
+ }
55
+
56
+ .wrapper.mutable:hover {
57
+ --color-widget-border: rgb(225, 225, 225);
58
+ }
59
+
60
+ .wrapper.mutable {
61
+ --color-widget-border: transparent;
62
+ --color-widget-bg: transparent;
63
+ --input-cursor: pointer;
64
+ --color-widget-text-focused: #666;
65
+ --color-widget-text: var(--color-link-primary) !important;
41
66
  }
42
67
 
43
68
  .prefix {
44
69
  border-top-left-radius: var(--curvature-widget);
45
70
  border-bottom-left-radius: var(--curvature-widget);
46
- color: #888;
47
- cursor: pointer;
71
+ cursor: pointer !important;
48
72
  white-space: nowrap;
49
73
  overflow: hidden;
50
74
  text-overflow: ellipsis;
@@ -52,6 +76,7 @@ export class ContactFieldEditor extends RapidElement {
52
76
  padding: 0em 0.5em;
53
77
  position: absolute;
54
78
  margin-top: 0.2em;
79
+ pointer-events: none;
55
80
  }
56
81
 
57
82
  .wrapper {
@@ -60,7 +85,7 @@ export class ContactFieldEditor extends RapidElement {
60
85
 
61
86
  .prefix .name {
62
87
  padding: 0em 0.4em;
63
- color: #999;
88
+ color: rgba(100, 100, 100, 0.7);
64
89
  white-space: nowrap;
65
90
  overflow: hidden;
66
91
  text-overflow: ellipsis;
@@ -74,13 +99,12 @@ export class ContactFieldEditor extends RapidElement {
74
99
 
75
100
  .popper {
76
101
  padding: 0.5em 0.75em;
77
- background: rgba(240, 240, 240, 1);
102
+ background: rgba(0, 0, 0, 0.03);
78
103
  border-top-right-radius: var(--curvature-widget);
79
104
  border-bottom-right-radius: var(--curvature-widget);
80
105
  --icon-color: #888;
81
106
  opacity: 0;
82
107
  cursor: default;
83
- transform: scale(0.5);
84
108
  transition: all var(--transition-speed) ease-in-out;
85
109
  display: flex;
86
110
  align-items: stretch;
@@ -100,27 +124,15 @@ export class ContactFieldEditor extends RapidElement {
100
124
  --icon-color: rgba(0, 0, 0, 0.3);
101
125
  }
102
126
 
127
+ temba-textinput:focus .popper,
103
128
  temba-textinput:hover .popper {
104
129
  opacity: 1;
105
- transform: scale(1);
106
- }
107
-
108
- temba-textinput:focus .popper {
109
- opacity: 1;
110
- transform: scale(1);
111
130
  }
112
131
 
113
132
  .disabled temba-textinput .postfix {
114
133
  display: none;
115
134
  }
116
135
 
117
- .disabled {
118
- --widget-box-shadow: none;
119
- --color-widget-border: transparent;
120
- padding-bottom: 0.4em;
121
- border-bottom: 1px solid #e6e6e6;
122
- }
123
-
124
136
  .unset temba-textinput:focus .popper,
125
137
  .unset temba-textinput:hover .popper {
126
138
  opacity: 0;
@@ -208,6 +220,7 @@ export class ContactFieldEditor extends RapidElement {
208
220
  set: !!this.value,
209
221
  unset: !this.value,
210
222
  disabled: this.disabled,
223
+ mutable: !this.disabled,
211
224
  })}
212
225
  >
213
226
  ${this.type === 'datetime'
@@ -10,12 +10,6 @@ const MIN_FOR_FILTER = 10;
10
10
  export class ContactFields extends ContactStoreElement {
11
11
  static get styles() {
12
12
  return css`
13
- :host {
14
- }
15
-
16
- .fields {
17
- }
18
-
19
13
  .field {
20
14
  display: flex;
21
15
  margin: 0.3em 0.3em;
@@ -25,8 +19,9 @@ export class ContactFields extends ContactStoreElement {
25
19
  overflow: hidden;
26
20
  }
27
21
 
28
- .show-all .unset {
29
- display: block;
22
+ .show-all .unset,
23
+ .featured {
24
+ display: block !important;
30
25
  }
31
26
 
32
27
  .unset {
@@ -61,9 +56,6 @@ export class ContactFields extends ContactStoreElement {
61
56
  font-size: 0.9em;
62
57
  }
63
58
 
64
- temba-contact-field {
65
- }
66
-
67
59
  .toggle {
68
60
  display: flex;
69
61
  background: #fff;
@@ -77,9 +69,6 @@ export class ContactFields extends ContactStoreElement {
77
69
  `;
78
70
  }
79
71
 
80
- @property({ type: Boolean })
81
- featured: boolean;
82
-
83
72
  @property({ type: Boolean })
84
73
  system: boolean;
85
74
 
@@ -92,6 +81,9 @@ export class ContactFields extends ContactStoreElement {
92
81
  @property({ type: String })
93
82
  timezone: string;
94
83
 
84
+ @property({ type: String })
85
+ role: string;
86
+
95
87
  @property({ type: Boolean })
96
88
  disabled = false;
97
89
 
@@ -100,6 +92,10 @@ export class ContactFields extends ContactStoreElement {
100
92
  this.handleFieldChanged = this.handleFieldChanged.bind(this);
101
93
  }
102
94
 
95
+ private isAgent(): boolean {
96
+ return this.role === 'T';
97
+ }
98
+
103
99
  protected updated(
104
100
  changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
105
101
  ): void {
@@ -129,25 +125,29 @@ export class ContactFields extends ContactStoreElement {
129
125
 
130
126
  public render(): TemplateResult {
131
127
  if (this.data) {
132
- const fieldsToShow = Object.entries(this.data.fields)
133
- .filter((entry: [string, string]) => {
134
- return (
135
- (this.featured && this.store.getContactField(entry[0]).featured) ||
136
- (!this.featured && !this.store.getContactField(entry[0]).featured)
137
- );
138
- })
139
- .sort((a: [string, string], b: [string, string]) => {
128
+ const fieldsToShow = Object.entries(this.data.fields).sort(
129
+ (a: [string, string], b: [string, string]) => {
140
130
  const [ak] = a;
141
131
  const [bk] = b;
142
- const priority =
143
- this.store.getContactField(bk).priority -
144
- this.store.getContactField(ak).priority;
132
+ const fieldA = this.store.getContactField(ak);
133
+ const fieldB = this.store.getContactField(bk);
134
+
135
+ if (fieldA.featured && !fieldB.featured) {
136
+ return -1;
137
+ }
138
+
139
+ if (fieldB.featured && !fieldA.featured) {
140
+ return 1;
141
+ }
142
+
143
+ const priority = fieldB.priority - fieldA.priority;
145
144
  if (priority !== 0) {
146
145
  return priority;
147
146
  }
148
147
 
149
148
  return ak.localeCompare(bk);
150
- });
149
+ }
150
+ );
151
151
 
152
152
  if (fieldsToShow.length == 0) {
153
153
  return html`<slot name="empty"></slot>`;
@@ -157,21 +157,24 @@ export class ContactFields extends ContactStoreElement {
157
157
  const [k, v] = entry;
158
158
  const field = this.store.getContactField(k);
159
159
  return html`<temba-contact-field
160
- class=${v ? 'set' : 'unset'}
160
+ class=${getClasses({ set: !!v, unset: !v, featured: field.featured })}
161
161
  key=${field.key}
162
162
  name=${field.label}
163
163
  value=${v}
164
164
  type=${field.value_type}
165
165
  @change=${this.handleFieldChanged}
166
166
  timezone=${this.timezone}
167
- ?disabled=${this.disabled}
167
+ ?disabled=${(this.isAgent() && field.agent_access === 'view') ||
168
+ this.disabled
169
+ ? true
170
+ : false}
168
171
  ></temba-contact-field>`;
169
172
  });
170
173
 
171
174
  return html`
172
175
  <div class=${getClasses({ disabled: this.disabled })}>
173
- ${!this.featured &&
174
- Object.keys(this.data.fields).length >= MIN_FOR_FILTER
176
+ <div class="fields ${this.showAll ? 'show-all' : ''}">${fields}</div>
177
+ ${Object.keys(this.data.fields).length >= MIN_FOR_FILTER
175
178
  ? html`<div class="toggle">
176
179
  <div style="flex-grow: 1"></div>
177
180
  <div>
@@ -179,16 +182,10 @@ export class ContactFields extends ContactStoreElement {
179
182
  ?checked=${this.showAll}
180
183
  @change=${this.handleToggle}
181
184
  label="Show All"
182
- />
185
+ ></temba-checkbox>
183
186
  </div>
184
187
  </div>`
185
188
  : null}
186
-
187
- <div
188
- class="fields ${this.showAll || this.featured ? 'show-all' : ''}"
189
- >
190
- ${fields}
191
- </div>
192
189
  </div>
193
190
  `;
194
191
  }
package/src/interfaces.ts CHANGED
@@ -99,6 +99,7 @@ export interface ContactField {
99
99
  value_type: string;
100
100
  featured: boolean;
101
101
  priority: number;
102
+ agent_access: string;
102
103
  usages: { campaign_events: number; flows: number; groups: number };
103
104
  }
104
105
 
@@ -12,7 +12,7 @@ export class TextInput extends FormElement {
12
12
  return css`
13
13
  .input-container {
14
14
  border-radius: var(--curvature-widget);
15
- cursor: text;
15
+ cursor: var(--input-cursor);
16
16
  background: var(--color-widget-bg);
17
17
  border: 1px solid var(--color-widget-border);
18
18
  transition: all ease-in-out var(--transition-speed);
@@ -48,7 +48,6 @@ export class TextInput extends FormElement {
48
48
  }
49
49
 
50
50
  .input-container:hover {
51
- background: var(--color-widget-bg-focused);
52
51
  }
53
52
 
54
53
  textarea {
@@ -61,11 +60,12 @@ export class TextInput extends FormElement {
61
60
  flex: 1;
62
61
  margin: 0;
63
62
  background: none;
63
+ background-color: transparent;
64
64
  color: var(--color-widget-text);
65
65
  font-family: var(--font-family);
66
66
  font-size: var(--temba-textinput-font-size);
67
67
  line-height: normal;
68
- cursor: text;
68
+ cursor: var(--input-cursor);
69
69
  resize: none;
70
70
  font-weight: 300;
71
71
  width: 100%;
@@ -75,6 +75,7 @@ export class TextInput extends FormElement {
75
75
  outline: none;
76
76
  box-shadow: none;
77
77
  cursor: text;
78
+ color: var(--color-widget-text-focused, var(--color-widget-text));
78
79
  }
79
80
 
80
81
  .textinput::placeholder {
@@ -1,11 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-this-alias */
2
2
  import { html, TemplateResult } from 'lit-html';
3
3
  import { Button } from '../button/Button';
4
- import { upload_endpoint } from '../compose/Compose';
5
4
  import { Dialog } from '../dialog/Dialog';
6
5
  import { ContactField, Ticket, User } from '../interfaces';
7
6
  import ColorHash from 'color-hash';
8
7
 
8
+ export const DEFAULT_MEDIA_ENDPOINT = '/api/v2/media.json';
9
+
9
10
  const colorHash = new ColorHash();
10
11
 
11
12
  export type Asset = KeyedAsset & Ticket & ContactField;
@@ -252,7 +253,7 @@ export const postFormData = (
252
253
  if (response.status >= 200 && response.status < 400) {
253
254
  resolve(response);
254
255
  } else {
255
- if (url === upload_endpoint) {
256
+ if (url === DEFAULT_MEDIA_ENDPOINT) {
256
257
  reject(response);
257
258
  } else {
258
259
  reject('Server failure');
@@ -1,8 +1,9 @@
1
- import { assert, expect, fixture } from '@open-wc/testing';
2
- import { Attachment, Compose, upload_endpoint } from '../src/compose/Compose';
1
+ import { assert, expect } from '@open-wc/testing';
2
+ import { Attachment, Compose } from '../src/compose/Compose';
3
3
  import { assertScreenshot, getClip, getComponent } from './utils.test';
4
4
  import { Button } from '../src/button/Button';
5
5
  import { Completion } from '../src/completion/Completion';
6
+ import { DEFAULT_MEDIA_ENDPOINT } from '../src/utils';
6
7
 
7
8
  const TAG = 'temba-compose';
8
9
  const getCompose = async (attrs: any = {}, width = 500, height = 500) => {
@@ -113,7 +114,7 @@ describe('temba-compose chatbox', () => {
113
114
  it('can be created', async () => {
114
115
  const compose: Compose = await getCompose();
115
116
  assert.instanceOf(compose, Compose);
116
- expect(compose.endpoint).equals(upload_endpoint);
117
+ expect(compose.endpoint).equals(DEFAULT_MEDIA_ENDPOINT);
117
118
  });
118
119
 
119
120
  it('cannot be created with a different endpoint', async () => {
@@ -121,7 +122,7 @@ describe('temba-compose chatbox', () => {
121
122
  endpoint: '/schmsgmedia/schmupload/',
122
123
  });
123
124
  assert.instanceOf(compose, Compose);
124
- expect(compose.endpoint).equals(upload_endpoint);
125
+ expect(compose.endpoint).equals(DEFAULT_MEDIA_ENDPOINT);
125
126
  });
126
127
 
127
128
  it('chatbox no counter no send button', async () => {