@nyaruka/temba-components 0.120.7 → 0.121.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.
@@ -18,7 +18,6 @@ import {
18
18
  KeyedAssets,
19
19
  CustomEventType,
20
20
  Workspace,
21
- User,
22
21
  Shortcut
23
22
  } from '../interfaces';
24
23
  import { RapidElement } from '../RapidElement';
@@ -29,6 +28,8 @@ import { configureLocalization } from '@lit/localize';
29
28
  import { sourceLocale, targetLocales } from '../locales/locale-codes';
30
29
  import { StoreMonitorElement } from './StoreMonitorElement';
31
30
  import { getFullName } from '../user/TembaUser';
31
+ import app, { AppState } from './AppState';
32
+ import { StoreApi } from 'zustand/vanilla';
32
33
 
33
34
  const { setLocale } = configureLocalization({
34
35
  sourceLocale,
@@ -36,6 +37,10 @@ const { setLocale } = configureLocalization({
36
37
  loadLocale: (locale) => import(`./locales/${locale}.js`)
37
38
  });
38
39
 
40
+ export const getStore = () => {
41
+ return document.querySelector('temba-store') as Store;
42
+ };
43
+
39
44
  export class Store extends RapidElement {
40
45
  public static get styles() {
41
46
  return css`
@@ -102,10 +107,8 @@ export class Store extends RapidElement {
102
107
  private groups: { [uuid: string]: ContactGroup } = {};
103
108
  private shortcuts: Shortcut[] = [];
104
109
  private languages: any = {};
105
- private users: User[] = [];
106
110
  private workspace: Workspace;
107
111
  private featuredFields: ContactField[] = [];
108
- private flowContents: FlowContents;
109
112
 
110
113
  // http promise to monitor for completeness
111
114
  public initialHttpComplete: Promise<void | WebResponse[]>;
@@ -146,6 +149,7 @@ export class Store extends RapidElement {
146
149
  }
147
150
 
148
151
  public reset() {
152
+ const appState = this.getState();
149
153
  this.ready = false;
150
154
  this.clearCache();
151
155
  this.settings = JSON.parse(getCookie('settings') || '{}');
@@ -188,6 +192,7 @@ export class Store extends RapidElement {
188
192
  }
189
193
 
190
194
  if (this.languagesEndpoint) {
195
+ appState.fetchAllLanguages(this.languagesEndpoint);
191
196
  fetches.push(
192
197
  getAssets(this.languagesEndpoint).then((results: any[]) => {
193
198
  // convert array of objects to lookup
@@ -214,6 +219,7 @@ export class Store extends RapidElement {
214
219
  }
215
220
 
216
221
  if (this.workspaceEndpoint) {
222
+ appState.fetchWorkspace(this.workspaceEndpoint);
217
223
  fetches.push(
218
224
  getUrl(this.workspaceEndpoint).then((response: WebResponse) => {
219
225
  this.workspace = response.json;
@@ -617,61 +623,20 @@ export class Store extends RapidElement {
617
623
  }
618
624
  }
619
625
 
620
- // TODO: for now we let the flow editor set this externally to avoid
621
- // double fetches and updates
622
- public setFlowContents(contents: FlowContents) {
623
- this.flowContents = contents;
624
- }
625
-
626
- public setFlowInfo(info: FlowInfo) {
627
- this.flowContents.info = info;
626
+ public getCompletions(type: string) {
627
+ const info = this.getState().flow.info;
628
+ if (type === 'results') {
629
+ return info.results.map((result) => result.key);
630
+ } else if (type === 'locals') {
631
+ return info.locals;
632
+ }
628
633
  }
629
634
 
630
- public async loadFlow(
631
- flowUUID: string,
632
- revision = 'latest'
633
- ): Promise<FlowContents> {
634
- const response = await getUrl(
635
- `/flow/revisions/${flowUUID}/${revision}/?version=14.3`
636
- );
637
- this.flowContents = response.json;
638
- return this.flowContents;
635
+ public getApp(): StoreApi<AppState> {
636
+ return app;
639
637
  }
640
638
 
641
- public getFlowResults(): InfoResult[] {
642
- return this.flowContents.info.results;
639
+ public getState(): AppState {
640
+ return app.getState();
643
641
  }
644
642
  }
645
-
646
- export interface InfoResult {
647
- key: string;
648
- name: string;
649
- categories: string[];
650
- node_uuids: string[];
651
- }
652
-
653
- export interface ObjectRef {
654
- uuid: string;
655
- name: string;
656
- }
657
-
658
- export interface TypedObjectRef extends ObjectRef {
659
- type: string;
660
- }
661
-
662
- export interface Language {
663
- code: string;
664
- name: string;
665
- }
666
-
667
- export interface FlowInfo {
668
- results: InfoResult[];
669
- dependencies: TypedObjectRef[];
670
- counts: { nodes: number; languages: number };
671
- locals: string[];
672
- }
673
-
674
- export interface FlowContents {
675
- definition: any;
676
- info: FlowInfo;
677
- }
@@ -0,0 +1,139 @@
1
+ export type LocalizationMap = Record<string, Record<string, any>>;
2
+
3
+ export type FlowTypes =
4
+ | 'messaging'
5
+ | 'messaging_background'
6
+ | 'messaging_offline'
7
+ | 'voice'
8
+ | '-';
9
+
10
+ export type ActionType =
11
+ | 'execute_actions'
12
+ | 'add_contact_urn'
13
+ | 'add_contact_groups'
14
+ | 'add_input_labels'
15
+ | 'remove_contact_groups'
16
+ | 'set_contact_channel'
17
+ | 'set_contact_field'
18
+ | 'set_contact_name'
19
+ | 'set_contact_language'
20
+ | 'set_contact_status'
21
+ | 'set_run_result'
22
+ | 'call_classifier'
23
+ | 'call_resthook'
24
+ | 'call_webhook'
25
+ | 'call_llm'
26
+ | 'open_ticket'
27
+ | 'send_msg'
28
+ | 'send_email'
29
+ | 'send_broadcast'
30
+ | 'enter_flow'
31
+ | 'start_session'
32
+ | 'transfer_airtime'
33
+ | 'split_by_airtime'
34
+ | 'split_by_expression'
35
+ | 'split_by_contact_field'
36
+ | 'split_by_run_result'
37
+ | 'split_by_run_result_delimited'
38
+ | 'split_by_groups'
39
+ | 'split_by_intent'
40
+ | 'split_by_random'
41
+ | 'split_by_resthook'
42
+ | 'split_by_ticket'
43
+ | 'split_by_scheme'
44
+ | 'split_by_subflow'
45
+ | 'split_by_webhook'
46
+ | 'split_by_llm'
47
+ | 'wait_for_response'
48
+ | 'wait_for_menu'
49
+ | 'wait_for_dial'
50
+ | 'wait_for_digits'
51
+ | 'wait_for_audio'
52
+ | 'wait_for_video'
53
+ | 'wait_for_location'
54
+ | 'wait_for_image'
55
+ | 'request_optin'
56
+ | 'missing'
57
+ | 'say_msg'
58
+ | 'play_audio';
59
+
60
+ export interface Action {
61
+ type: ActionType;
62
+ uuid: string;
63
+ }
64
+
65
+ export interface Exit {
66
+ uuid: string;
67
+ destination_uuid?: string;
68
+ }
69
+
70
+ export type Hint = {
71
+ type: 'digits' | 'audio' | 'image' | 'video' | 'location';
72
+ count?: number;
73
+ };
74
+
75
+ export interface Timeout {
76
+ category_uuid: string;
77
+ seconds: number;
78
+ }
79
+
80
+ export interface Wait {
81
+ type: 'msg' | 'dial';
82
+ timeout?: Timeout;
83
+ hint?: Hint;
84
+ phone?: string;
85
+ dial_limit_seconds?: number;
86
+ call_limit_seconds?: number;
87
+ }
88
+
89
+ export interface Category {
90
+ uuid: string;
91
+ name: string;
92
+ exit_uuid: string;
93
+ }
94
+
95
+ export interface Router {
96
+ type: 'switch' | 'random';
97
+ result_name?: string;
98
+ categories: Category[];
99
+ wait?: Wait;
100
+ }
101
+
102
+ export interface FlowNode {
103
+ uuid: string;
104
+ actions: Action[];
105
+ exits: Exit[];
106
+ router?: Router;
107
+ }
108
+
109
+ export interface FlowPosition {
110
+ left: number;
111
+ top: number;
112
+ right?: number;
113
+ bottom?: number;
114
+ }
115
+
116
+ export interface UINode {
117
+ position: FlowPosition;
118
+ type?: ActionType;
119
+ config?: Record<string, any>;
120
+ }
121
+
122
+ export interface UIMetaData {
123
+ nodes: Record<string, UINode>;
124
+ languages: Record<string, string>[];
125
+ translation_filters?: { categories: boolean };
126
+ auto_translations?: Record<string, Record<string, string[]>>;
127
+ }
128
+
129
+ export interface FlowDefinition {
130
+ localization: LocalizationMap;
131
+ language: string;
132
+ name: string;
133
+ nodes: FlowNode[];
134
+ uuid: string;
135
+ type: FlowTypes;
136
+ revision: number;
137
+ spec_version: string;
138
+ _ui: UIMetaData;
139
+ }
@@ -82,7 +82,7 @@ const createJSONResponse = (mocked) => {
82
82
  return Promise.resolve(mockResponse);
83
83
  };
84
84
 
85
- const getResponse = (endpoint: string, options) => {
85
+ const getResponse = (endpoint: string, options = { method: 'GET' }) => {
86
86
  // check if our path has been mocked in code
87
87
  const mocks = options.method === 'GET' ? gets : posts;
88
88
  const codeMock = mocks.find((mock) => mock.endpoint.test(endpoint));
@@ -99,7 +99,6 @@ const getResponse = (endpoint: string, options) => {
99
99
  return createJSONResponse(codeMock);
100
100
  }
101
101
  }
102
-
103
102
  // otherwise fetch over http
104
103
  return normalFetch(endpoint, options);
105
104
  };
@@ -262,6 +261,7 @@ export const loadStore = async () => {
262
261
  languages='/test-assets/store/languages.json'
263
262
  fields='/test-assets/store/fields.json'
264
263
  users='/test-assets/store/users.json'
264
+ workspace='/test-assets/store/workspace.json'
265
265
  />`
266
266
  );
267
267
  await store.initialHttpComplete;
@@ -0,0 +1,14 @@
1
+ {
2
+ "uuid": "3e8e7154-0233-4d53-b564-c258ee8390f9",
3
+ "name": "WHO Chile",
4
+ "country": "NG",
5
+ "languages": ["eng", "spa", "afr"],
6
+ "timezone": "PST8PDT",
7
+ "date_style": "day_first",
8
+ "anon": false,
9
+ "credits": {
10
+ "used": -1,
11
+ "remaining": -1
12
+ },
13
+ "primary_language": "eng"
14
+ }
package/tsconfig.json CHANGED
@@ -19,5 +19,5 @@
19
19
  "declaration": false,
20
20
  "noEmit": false,
21
21
  },
22
- "include": ["**/*.ts"]
22
+ "include": ["**/*.ts", "setup-test.js"]
23
23
  }
@@ -0,0 +1,14 @@
1
+ import replace from '@rollup/plugin-replace';
2
+ import { fromRollup } from '@web/dev-server-rollup';
3
+
4
+ const replacePlugin = fromRollup(replace);
5
+
6
+ export default {
7
+ nodeResolve: true,
8
+ plugins: [
9
+ replacePlugin({
10
+ preventAssignment: true,
11
+ 'process.env.NODE_ENV': JSON.stringify('development'),
12
+ }),
13
+ ],
14
+ };
@@ -11,6 +11,10 @@ import sizeOf from 'image-size';
11
11
 
12
12
  import rimraf from 'rimraf';
13
13
 
14
+ import replace from '@rollup/plugin-replace';
15
+ import { fromRollup } from '@web/dev-server-rollup';
16
+ const replacePlugin = fromRollup(replace);
17
+
14
18
  const SCREENSHOTS = 'screenshots';
15
19
  const DIFF = 'diff';
16
20
  const TEST = 'test';
@@ -297,7 +301,7 @@ export default {
297
301
  rootDir: './',
298
302
  files: '**/test/**/*.test.ts',
299
303
  nodeResolve: true,
300
-
304
+ setupFiles: ['./test-setup.js'],
301
305
  testFramework: {
302
306
  config: {
303
307
  timeout: '10000',
@@ -305,6 +309,10 @@ export default {
305
309
  },
306
310
 
307
311
  plugins: [
312
+ replacePlugin({
313
+ preventAssignment: true,
314
+ 'process.env.NODE_ENV': JSON.stringify('test'),
315
+ }),
308
316
  {
309
317
  name: 'add-style',
310
318
  transform(context) {
@@ -372,3 +380,4 @@ export default {
372
380
  }),
373
381
  ],
374
382
  };
383
+