@nyaruka/temba-components 0.131.2 → 0.131.3
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 +14 -0
- package/demo/components/floating-tabs/example.html +400 -0
- package/demo/components/flow/index.html +1 -1
- package/demo/data/flows/sample-flow.json +41 -2
- package/demo/data/flows/voicemail.json +613 -0
- package/demo/index.html +6 -0
- package/dist/locales/es.js +5 -5
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fr.js +5 -5
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/locale-codes.js +11 -2
- package/dist/locales/locale-codes.js.map +1 -1
- package/dist/locales/pt.js +5 -5
- package/dist/locales/pt.js.map +1 -1
- package/dist/temba-components.js +1109 -535
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +167 -0
- package/out-tsc/src/display/FloatingTab.js.map +1 -0
- package/out-tsc/src/display/ProgressBar.js +22 -2
- package/out-tsc/src/display/ProgressBar.js.map +1 -1
- package/out-tsc/src/events.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +165 -31
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +857 -3
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +239 -19
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/NodeTypeSelector.js +44 -3
- package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +12 -3
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js +2 -1
- package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js +2 -1
- package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
- package/out-tsc/src/flow/actions/add_input_labels.js +2 -1
- package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
- package/out-tsc/src/flow/actions/play_audio.js +2 -1
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js +2 -1
- package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
- package/out-tsc/src/flow/actions/request_optin.js +1 -0
- package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +2 -1
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/send_broadcast.js +2 -1
- package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
- package/out-tsc/src/flow/actions/send_email.js +2 -1
- package/out-tsc/src/flow/actions/send_email.js.map +1 -1
- package/out-tsc/src/flow/actions/send_msg.js +93 -3
- package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_field.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_language.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_name.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
- package/out-tsc/src/flow/actions/set_contact_status.js +2 -1
- package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
- package/out-tsc/src/flow/actions/set_run_result.js +2 -1
- package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
- package/out-tsc/src/flow/actions/start_session.js +2 -1
- package/out-tsc/src/flow/actions/start_session.js.map +1 -1
- package/out-tsc/src/flow/config.js +2 -10
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/shared.js +54 -0
- package/out-tsc/src/flow/nodes/shared.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_airtime.js +9 -3
- package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_contact_field.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_expression.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_groups.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_intent.js +3 -2
- package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm.js +9 -2
- package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +9 -2
- package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_random.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_resthook.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_run_result.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_scheme.js +8 -3
- package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_subflow.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_ticket.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
- package/out-tsc/src/flow/nodes/split_by_webhook.js +8 -2
- package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_response.js +8 -3
- package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
- package/out-tsc/src/flow/types.js +15 -0
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/layout/FloatingWindow.js +346 -0
- package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
- package/out-tsc/src/live/ContactChat.js +3 -19
- package/out-tsc/src/live/ContactChat.js.map +1 -1
- package/out-tsc/src/locales/es.js +5 -5
- package/out-tsc/src/locales/es.js.map +1 -1
- package/out-tsc/src/locales/fr.js +5 -5
- package/out-tsc/src/locales/fr.js.map +1 -1
- package/out-tsc/src/locales/locale-codes.js +11 -2
- package/out-tsc/src/locales/locale-codes.js.map +1 -1
- package/out-tsc/src/locales/pt.js +5 -5
- package/out-tsc/src/locales/pt.js.map +1 -1
- package/out-tsc/src/store/AppState.js +67 -0
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/temba-modules.js +4 -0
- package/out-tsc/temba-modules.js.map +1 -1
- package/out-tsc/test/temba-floating-tab.test.js +91 -0
- package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
- package/out-tsc/test/temba-floating-window.test.js +301 -0
- package/out-tsc/test/temba-floating-window.test.js.map +1 -0
- package/out-tsc/test/temba-flow-editor-node.test.js +117 -0
- package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
- package/out-tsc/test/temba-localization.test.js +471 -0
- package/out-tsc/test/temba-localization.test.js.map +1 -0
- package/out-tsc/test/temba-node-type-selector.test.js +150 -0
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
- package/out-tsc/test/utils.test.js +18 -0
- package/out-tsc/test/utils.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/floating-tab/default.png +0 -0
- package/screenshots/truth/floating-tab/gray.png +0 -0
- package/screenshots/truth/floating-tab/green.png +0 -0
- package/screenshots/truth/floating-tab/hidden.png +0 -0
- package/screenshots/truth/floating-tab/hover.png +0 -0
- package/screenshots/truth/floating-tab/purple.png +0 -0
- package/screenshots/truth/floating-window/chromeless.png +0 -0
- package/screenshots/truth/floating-window/custom-size.png +0 -0
- package/screenshots/truth/floating-window/default.png +0 -0
- package/screenshots/truth/floating-window/with-header.png +0 -0
- package/screenshots/truth/node-type-selector/action-mode.png +0 -0
- package/screenshots/truth/node-type-selector/split-mode.png +0 -0
- package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
- package/src/display/FloatingTab.ts +174 -0
- package/src/display/ProgressBar.ts +22 -2
- package/src/events.ts +2 -4
- package/src/flow/CanvasNode.ts +190 -32
- package/src/flow/Editor.ts +1040 -3
- package/src/flow/NodeEditor.ts +317 -19
- package/src/flow/NodeTypeSelector.ts +47 -3
- package/src/flow/StickyNote.ts +12 -3
- package/src/flow/actions/add_contact_groups.ts +2 -1
- package/src/flow/actions/add_contact_urn.ts +3 -1
- package/src/flow/actions/add_input_labels.ts +2 -1
- package/src/flow/actions/play_audio.ts +2 -1
- package/src/flow/actions/remove_contact_groups.ts +3 -1
- package/src/flow/actions/request_optin.ts +1 -0
- package/src/flow/actions/say_msg.ts +2 -1
- package/src/flow/actions/send_broadcast.ts +2 -1
- package/src/flow/actions/send_email.ts +3 -1
- package/src/flow/actions/send_msg.ts +134 -3
- package/src/flow/actions/set_contact_channel.ts +2 -1
- package/src/flow/actions/set_contact_field.ts +2 -1
- package/src/flow/actions/set_contact_language.ts +3 -1
- package/src/flow/actions/set_contact_name.ts +2 -1
- package/src/flow/actions/set_contact_status.ts +2 -1
- package/src/flow/actions/set_run_result.ts +2 -1
- package/src/flow/actions/start_session.ts +3 -1
- package/src/flow/config.ts +2 -12
- package/src/flow/nodes/shared.ts +70 -1
- package/src/flow/nodes/split_by_airtime.ts +20 -3
- package/src/flow/nodes/split_by_contact_field.ts +13 -3
- package/src/flow/nodes/split_by_expression.ts +13 -3
- package/src/flow/nodes/split_by_groups.ts +13 -3
- package/src/flow/nodes/split_by_intent.ts +3 -2
- package/src/flow/nodes/split_by_llm.ts +19 -2
- package/src/flow/nodes/split_by_llm_categorize.ts +19 -2
- package/src/flow/nodes/split_by_random.ts +12 -2
- package/src/flow/nodes/split_by_resthook.ts +13 -3
- package/src/flow/nodes/split_by_run_result.ts +13 -3
- package/src/flow/nodes/split_by_scheme.ts +13 -3
- package/src/flow/nodes/split_by_subflow.ts +12 -2
- package/src/flow/nodes/split_by_ticket.ts +12 -2
- package/src/flow/nodes/split_by_webhook.ts +12 -2
- package/src/flow/nodes/wait_for_digits.ts +3 -2
- package/src/flow/nodes/wait_for_menu.ts +3 -2
- package/src/flow/nodes/wait_for_response.ts +13 -3
- package/src/flow/types.ts +47 -0
- package/src/layout/FloatingWindow.ts +386 -0
- package/src/live/ContactChat.ts +4 -19
- package/src/locales/es.ts +18 -13
- package/src/locales/fr.ts +18 -13
- package/src/locales/locale-codes.ts +11 -2
- package/src/locales/pt.ts +18 -13
- package/src/store/AppState.ts +104 -0
- package/static/api/llms.json +18 -0
- package/temba-modules.ts +4 -0
- package/test/temba-floating-tab.test.ts +110 -0
- package/test/temba-floating-window.test.ts +477 -0
- package/test/temba-flow-editor-node.test.ts +144 -0
- package/test/temba-localization.test.ts +611 -0
- package/test/temba-node-type-selector.test.ts +203 -0
- package/test/utils.test.ts +20 -0
- package/test-assets/contacts/history.json +5 -6
- package/test-assets/select/llms.json +2 -2
- package/web-dev-server.config.mjs +47 -1
- package/web-test-runner.config.mjs +0 -1
- package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
- package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
- package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
- package/src/flow/nodes/wait_for_audio.ts +0 -7
- package/src/flow/nodes/wait_for_image.ts +0 -7
- package/src/flow/nodes/wait_for_location.ts +0 -7
- package/src/flow/nodes/wait_for_video.ts +0 -7
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { css, html, PropertyValueMap, TemplateResult } from 'lit';
|
|
2
|
+
import { property } from 'lit/decorators.js';
|
|
3
|
+
import { RapidElement } from '../RapidElement';
|
|
4
|
+
import { CustomEventType } from '../interfaces';
|
|
5
|
+
import { getClasses } from '../utils';
|
|
6
|
+
import { FloatingTab } from '../display/FloatingTab';
|
|
7
|
+
|
|
8
|
+
export class FloatingWindow extends RapidElement {
|
|
9
|
+
static get styles() {
|
|
10
|
+
return css`
|
|
11
|
+
.window.hidden {
|
|
12
|
+
transform: translateX(100%);
|
|
13
|
+
opacity: 0;
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.window {
|
|
18
|
+
transition: transform var(--transition-duration, 300ms) ease-in-out,
|
|
19
|
+
opacity var(--transition-duration, 300ms) ease-in-out;
|
|
20
|
+
position: fixed;
|
|
21
|
+
z-index: 9999;
|
|
22
|
+
top: 100px;
|
|
23
|
+
background: white;
|
|
24
|
+
border-radius: 8px;
|
|
25
|
+
box-shadow: -4px 4px 20px rgba(0, 0, 0, 0.3);
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.window.chromeless {
|
|
32
|
+
background: transparent;
|
|
33
|
+
border-radius: 0;
|
|
34
|
+
box-shadow: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.window.dragging {
|
|
38
|
+
user-select: none;
|
|
39
|
+
cursor: move;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.header {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: space-between;
|
|
46
|
+
padding: 6px 6px;
|
|
47
|
+
background: var(--header-color, var(--color-primary-light, #f3f4f6));
|
|
48
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
49
|
+
cursor: move;
|
|
50
|
+
user-select: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.title {
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
font-size: 16px;
|
|
56
|
+
color: white;
|
|
57
|
+
padding-left: 8px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.close-button {
|
|
61
|
+
background: none;
|
|
62
|
+
border: none;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
padding: 4px;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
border-radius: 4px;
|
|
69
|
+
transition: background-color calc(var(--transition-duration, 150ms) / 2)
|
|
70
|
+
ease-in-out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.close-button:hover {
|
|
74
|
+
background-color: rgba(255, 255, 255, 0.2);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.close-button temba-icon {
|
|
78
|
+
--icon-color: white;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.body {
|
|
82
|
+
flex: 1;
|
|
83
|
+
overflow-y: auto;
|
|
84
|
+
padding: 16px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.window.chromeless .body {
|
|
88
|
+
padding: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
::slotted(.drag-handle) {
|
|
92
|
+
cursor: move;
|
|
93
|
+
user-select: none;
|
|
94
|
+
border: 1px solid red;
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@property({ type: String })
|
|
100
|
+
header = '';
|
|
101
|
+
|
|
102
|
+
@property({ type: Number })
|
|
103
|
+
width = 500;
|
|
104
|
+
|
|
105
|
+
@property({ type: Number })
|
|
106
|
+
minHeight = 200;
|
|
107
|
+
|
|
108
|
+
@property({ type: Number })
|
|
109
|
+
maxHeight = 800;
|
|
110
|
+
|
|
111
|
+
@property({ type: Number })
|
|
112
|
+
top = 100;
|
|
113
|
+
|
|
114
|
+
@property({ type: Number })
|
|
115
|
+
left = -1; // -1 means calculate from right side
|
|
116
|
+
|
|
117
|
+
@property({ type: Boolean })
|
|
118
|
+
hidden = true;
|
|
119
|
+
|
|
120
|
+
@property({ type: Boolean })
|
|
121
|
+
dragging = false;
|
|
122
|
+
|
|
123
|
+
@property({ type: Boolean })
|
|
124
|
+
chromeless = false;
|
|
125
|
+
|
|
126
|
+
@property({ type: String })
|
|
127
|
+
color = '#6B7280';
|
|
128
|
+
|
|
129
|
+
private dragStartX = 0;
|
|
130
|
+
private dragStartY = 0;
|
|
131
|
+
private dragOffsetX = 0;
|
|
132
|
+
private dragOffsetY = 0;
|
|
133
|
+
private positionFromRight = false;
|
|
134
|
+
private defaultTop = 100;
|
|
135
|
+
private defaultLeft = -1;
|
|
136
|
+
|
|
137
|
+
public firstUpdated(
|
|
138
|
+
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
139
|
+
): void {
|
|
140
|
+
super.firstUpdated(changes);
|
|
141
|
+
|
|
142
|
+
// store the default position from properties
|
|
143
|
+
this.defaultTop = this.top;
|
|
144
|
+
this.defaultLeft = this.left;
|
|
145
|
+
|
|
146
|
+
// determine if we should position from right side
|
|
147
|
+
if (this.left === -1) {
|
|
148
|
+
this.positionFromRight = true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// set up drag handle listeners for chromeless windows
|
|
152
|
+
if (this.chromeless) {
|
|
153
|
+
this.setupDragHandles();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// listen for window resize to keep window in bounds
|
|
157
|
+
window.addEventListener('resize', this.handleResize);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
disconnectedCallback(): void {
|
|
161
|
+
super.disconnectedCallback();
|
|
162
|
+
window.removeEventListener('resize', this.handleResize);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private setupDragHandles() {
|
|
166
|
+
// listen for mousedown on slotted content
|
|
167
|
+
this.addEventListener('mousedown', this.handleSlotMouseDown);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private handleSlotMouseDown = (event: MouseEvent) => {
|
|
171
|
+
// check if the target or any parent has the drag-handle class
|
|
172
|
+
const target = event.target as HTMLElement;
|
|
173
|
+
const dragHandle = target.closest('.drag-handle');
|
|
174
|
+
|
|
175
|
+
if (!dragHandle) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.dragging = true;
|
|
180
|
+
this.dragStartX = event.clientX;
|
|
181
|
+
this.dragStartY = event.clientY;
|
|
182
|
+
this.dragOffsetX = this.left;
|
|
183
|
+
this.dragOffsetY = this.top;
|
|
184
|
+
|
|
185
|
+
document.addEventListener('mousemove', this.handleMouseMove);
|
|
186
|
+
document.addEventListener('mouseup', this.handleMouseUp);
|
|
187
|
+
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
public updated(
|
|
192
|
+
changes: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
|
193
|
+
): void {
|
|
194
|
+
super.updated(changes);
|
|
195
|
+
if (changes.has('hidden')) {
|
|
196
|
+
this.classList.toggle('hidden', this.hidden);
|
|
197
|
+
|
|
198
|
+
// when hiding, reset positioning behavior to original
|
|
199
|
+
if (this.hidden && !changes.get('hidden')) {
|
|
200
|
+
if (this.defaultLeft === -1) {
|
|
201
|
+
this.positionFromRight = true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// reset to default position when showing
|
|
206
|
+
if (!this.hidden && changes.get('hidden')) {
|
|
207
|
+
// reset top to default
|
|
208
|
+
this.top = this.defaultTop;
|
|
209
|
+
|
|
210
|
+
// if positioned from right, recalculate based on current viewport
|
|
211
|
+
if (this.positionFromRight) {
|
|
212
|
+
this.left = window.innerWidth - this.width - 20;
|
|
213
|
+
} else {
|
|
214
|
+
// reset left to default
|
|
215
|
+
this.left = this.defaultLeft;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// setup drag handles if chromeless changed to true
|
|
221
|
+
if (changes.has('chromeless') && this.chromeless) {
|
|
222
|
+
this.setupDragHandles();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private handleClose() {
|
|
227
|
+
this.hidden = true;
|
|
228
|
+
// show all tabs when window is closed
|
|
229
|
+
FloatingTab.showAllTabs();
|
|
230
|
+
this.fireCustomEvent(CustomEventType.DialogHidden);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private handleHeaderMouseDown(event: MouseEvent) {
|
|
234
|
+
// don't start drag if clicking on close button
|
|
235
|
+
if ((event.target as HTMLElement).closest('.close-button')) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.dragging = true;
|
|
240
|
+
this.dragStartX = event.clientX;
|
|
241
|
+
this.dragStartY = event.clientY;
|
|
242
|
+
this.dragOffsetX = this.left;
|
|
243
|
+
this.dragOffsetY = this.top;
|
|
244
|
+
|
|
245
|
+
document.addEventListener('mousemove', this.handleMouseMove);
|
|
246
|
+
document.addEventListener('mouseup', this.handleMouseUp);
|
|
247
|
+
|
|
248
|
+
event.preventDefault();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private handleMouseMove = (event: MouseEvent) => {
|
|
252
|
+
if (!this.dragging) return;
|
|
253
|
+
|
|
254
|
+
const deltaX = event.clientX - this.dragStartX;
|
|
255
|
+
const deltaY = event.clientY - this.dragStartY;
|
|
256
|
+
|
|
257
|
+
this.left = this.dragOffsetX + deltaX;
|
|
258
|
+
this.top = this.dragOffsetY + deltaY;
|
|
259
|
+
|
|
260
|
+
// keep window within viewport bounds with 20px padding
|
|
261
|
+
const padding = 20;
|
|
262
|
+
this.left = Math.max(
|
|
263
|
+
padding,
|
|
264
|
+
Math.min(this.left, window.innerWidth - this.width - padding)
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// get the actual rendered height of the window element
|
|
268
|
+
const windowElement = this.shadowRoot?.querySelector(
|
|
269
|
+
'.window'
|
|
270
|
+
) as HTMLElement;
|
|
271
|
+
const currentHeight =
|
|
272
|
+
windowElement?.offsetHeight || this.maxHeight || window.innerHeight;
|
|
273
|
+
const maxTop = Math.max(
|
|
274
|
+
padding,
|
|
275
|
+
window.innerHeight - currentHeight - padding
|
|
276
|
+
);
|
|
277
|
+
this.top = Math.max(padding, Math.min(this.top, maxTop));
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
private handleMouseUp = () => {
|
|
281
|
+
this.dragging = false;
|
|
282
|
+
document.removeEventListener('mousemove', this.handleMouseMove);
|
|
283
|
+
document.removeEventListener('mouseup', this.handleMouseUp);
|
|
284
|
+
|
|
285
|
+
// once user drags the window, stop auto-positioning from right
|
|
286
|
+
this.positionFromRight = false;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
private handleResize = () => {
|
|
290
|
+
// only constrain position if window is visible
|
|
291
|
+
if (this.hidden) return;
|
|
292
|
+
|
|
293
|
+
const padding = 20;
|
|
294
|
+
const windowElement = this.shadowRoot?.querySelector(
|
|
295
|
+
'.window'
|
|
296
|
+
) as HTMLElement;
|
|
297
|
+
const currentHeight =
|
|
298
|
+
windowElement?.offsetHeight || this.maxHeight || window.innerHeight;
|
|
299
|
+
|
|
300
|
+
// if positioned from right, always recalculate from right edge
|
|
301
|
+
if (this.positionFromRight) {
|
|
302
|
+
this.left = window.innerWidth - this.width - padding;
|
|
303
|
+
} else {
|
|
304
|
+
// only adjust left if out of bounds
|
|
305
|
+
const minLeft = padding;
|
|
306
|
+
const maxLeft = window.innerWidth - this.width - padding;
|
|
307
|
+
|
|
308
|
+
if (this.left < minLeft) {
|
|
309
|
+
this.left = minLeft;
|
|
310
|
+
} else if (this.left > maxLeft) {
|
|
311
|
+
this.left = maxLeft;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// only adjust top if out of bounds
|
|
316
|
+
const minTop = padding;
|
|
317
|
+
const maxTop = Math.max(
|
|
318
|
+
padding,
|
|
319
|
+
window.innerHeight - currentHeight - padding
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
if (this.top < minTop) {
|
|
323
|
+
this.top = minTop;
|
|
324
|
+
} else if (this.top > maxTop) {
|
|
325
|
+
this.top = maxTop;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
public show() {
|
|
330
|
+
this.hidden = false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public hide() {
|
|
334
|
+
this.hidden = true;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
public close() {
|
|
338
|
+
this.hidden = true;
|
|
339
|
+
// show all tabs when window is closed
|
|
340
|
+
FloatingTab.showAllTabs();
|
|
341
|
+
this.fireCustomEvent(CustomEventType.DialogHidden);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
public render(): TemplateResult {
|
|
345
|
+
const minHeightStyle = this.minHeight
|
|
346
|
+
? `min-height: ${this.minHeight}px;`
|
|
347
|
+
: '';
|
|
348
|
+
const maxHeightStyle = this.maxHeight
|
|
349
|
+
? `max-height: ${this.maxHeight}px;`
|
|
350
|
+
: '';
|
|
351
|
+
|
|
352
|
+
const windowStyle = `
|
|
353
|
+
width: ${this.width}px;
|
|
354
|
+
${minHeightStyle}
|
|
355
|
+
${maxHeightStyle}
|
|
356
|
+
top: ${this.top}px;
|
|
357
|
+
left: ${this.left}px;
|
|
358
|
+
--header-color: ${this.color};
|
|
359
|
+
`;
|
|
360
|
+
|
|
361
|
+
const windowClasses = getClasses({
|
|
362
|
+
window: true,
|
|
363
|
+
dragging: this.dragging,
|
|
364
|
+
hidden: this.hidden,
|
|
365
|
+
chromeless: this.chromeless
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
return html`
|
|
369
|
+
<div class="${windowClasses}" style="${windowStyle}">
|
|
370
|
+
${!this.chromeless
|
|
371
|
+
? html`
|
|
372
|
+
<div class="header" @mousedown=${this.handleHeaderMouseDown}>
|
|
373
|
+
<div class="title">${this.header}</div>
|
|
374
|
+
<button class="close-button" @click=${this.handleClose}>
|
|
375
|
+
<temba-icon name="close" size="1.5"></temba-icon>
|
|
376
|
+
</button>
|
|
377
|
+
</div>
|
|
378
|
+
`
|
|
379
|
+
: ''}
|
|
380
|
+
<div class="body">
|
|
381
|
+
<slot></slot>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
386
|
+
}
|
package/src/live/ContactChat.ts
CHANGED
|
@@ -812,15 +812,6 @@ export class ContactChat extends ContactStoreElement {
|
|
|
812
812
|
return null;
|
|
813
813
|
}
|
|
814
814
|
|
|
815
|
-
private isMessageError(status: string | { status: string }): boolean {
|
|
816
|
-
if (typeof status === 'string') {
|
|
817
|
-
return status === 'E' || status === 'F';
|
|
818
|
-
} else if (status && typeof status === 'object' && 'status' in status) {
|
|
819
|
-
return status.status === 'errored' || status.status === 'failed';
|
|
820
|
-
}
|
|
821
|
-
return false;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
815
|
private createMessages(page: ContactHistoryPage): ChatEvent[] {
|
|
825
816
|
if (page.events) {
|
|
826
817
|
let messages = [];
|
|
@@ -866,7 +857,10 @@ export class ContactChat extends ContactStoreElement {
|
|
|
866
857
|
date: new Date(msgEvent.created_on),
|
|
867
858
|
attachments: msgEvent.msg.attachments,
|
|
868
859
|
text: msgEvent.msg.text,
|
|
869
|
-
sendError:
|
|
860
|
+
sendError:
|
|
861
|
+
msgEvent._status &&
|
|
862
|
+
(msgEvent._status.status === 'errored' ||
|
|
863
|
+
msgEvent._status.status === 'failed'),
|
|
870
864
|
popup: html`<div
|
|
871
865
|
style="display: flex; flex-direction: row; align-items:center; justify-content: space-between;font-size:0.9em;line-height:1em;min-width:10em"
|
|
872
866
|
>
|
|
@@ -881,15 +875,6 @@ export class ContactChat extends ContactStoreElement {
|
|
|
881
875
|
${msgEvent.optin.name}
|
|
882
876
|
</div>`
|
|
883
877
|
: null}
|
|
884
|
-
${msgEvent._failed_reason
|
|
885
|
-
? html`
|
|
886
|
-
<div
|
|
887
|
-
style="margin-top:0.2em;margin-right: 0.5em;min-width:10em;max-width:15em;color:var(--color-error);font-size:0.9em"
|
|
888
|
-
>
|
|
889
|
-
${msgEvent._failed_reason}
|
|
890
|
-
</div>
|
|
891
|
-
`
|
|
892
|
-
: null}
|
|
893
878
|
</div>
|
|
894
879
|
${msgEvent._logs_url
|
|
895
880
|
? html`<a style="margin-left:0.5em" href="${msgEvent._logs_url}"
|
package/src/locales/es.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
|
|
2
|
+
// Do not modify this file by hand!
|
|
3
|
+
// Re-generate this file by running lit-localize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-irregular-whitespace */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
|
|
11
|
+
export const templates = {
|
|
12
|
+
'scf1453991c986b25': `Tab para completar, enter para seleccionar`,
|
|
13
|
+
's73b4d70c02f4b4e0': `No options`,
|
|
14
|
+
's8f02e3a18ffc083a': `Are not currently in a flow`,
|
|
15
|
+
's638236250662c6b3': `Have sent a message in the last`,
|
|
16
|
+
's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
|
|
17
|
+
};
|
|
18
|
+
|
package/src/locales/fr.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
|
|
2
|
+
// Do not modify this file by hand!
|
|
3
|
+
// Re-generate this file by running lit-localize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-irregular-whitespace */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
|
|
11
|
+
export const templates = {
|
|
12
|
+
's73b4d70c02f4b4e0': `No options`,
|
|
13
|
+
'scf1453991c986b25': `Tab to complete, enter to select`,
|
|
14
|
+
's8f02e3a18ffc083a': `Are not currently in a flow`,
|
|
15
|
+
's638236250662c6b3': `Have sent a message in the last`,
|
|
16
|
+
's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
|
|
17
|
+
};
|
|
18
|
+
|
|
@@ -10,9 +10,18 @@ export const sourceLocale = `en`;
|
|
|
10
10
|
* The other locale codes that this application is localized into. Sorted
|
|
11
11
|
* lexicographically.
|
|
12
12
|
*/
|
|
13
|
-
export const targetLocales = [
|
|
13
|
+
export const targetLocales = [
|
|
14
|
+
`es`,
|
|
15
|
+
`fr`,
|
|
16
|
+
`pt`,
|
|
17
|
+
] as const;
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* All valid project locale codes. Sorted lexicographically.
|
|
17
21
|
*/
|
|
18
|
-
export const allLocales = [
|
|
22
|
+
export const allLocales = [
|
|
23
|
+
`en`,
|
|
24
|
+
`es`,
|
|
25
|
+
`fr`,
|
|
26
|
+
`pt`,
|
|
27
|
+
] as const;
|
package/src/locales/pt.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
|
|
2
|
+
// Do not modify this file by hand!
|
|
3
|
+
// Re-generate this file by running lit-localize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-irregular-whitespace */
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
|
|
11
|
+
export const templates = {
|
|
12
|
+
's73b4d70c02f4b4e0': `No options`,
|
|
13
|
+
'scf1453991c986b25': `Tab to complete, enter to select`,
|
|
14
|
+
's8f02e3a18ffc083a': `Are not currently in a flow`,
|
|
15
|
+
's638236250662c6b3': `Have sent a message in the last`,
|
|
16
|
+
's4788ee206c4570c7': `Have not started this flow in the last 90 days`,
|
|
17
|
+
};
|
|
18
|
+
|
package/src/store/AppState.ts
CHANGED
|
@@ -109,6 +109,17 @@ export interface AppState {
|
|
|
109
109
|
createStickyNote(position: FlowPosition): string;
|
|
110
110
|
createNode(nodeType: string, position: FlowPosition): string;
|
|
111
111
|
addNode(node: Node, nodeUI: NodeUI): void;
|
|
112
|
+
updateLocalization(
|
|
113
|
+
languageCode: string,
|
|
114
|
+
actionUuid: string,
|
|
115
|
+
localizationData: Record<string, any>
|
|
116
|
+
): void;
|
|
117
|
+
setTranslationFilters: (filters: { categories: boolean }) => void;
|
|
118
|
+
markAutoTranslated: (
|
|
119
|
+
languageCode: string,
|
|
120
|
+
uuid: string,
|
|
121
|
+
attributes: string[]
|
|
122
|
+
) => void;
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
export const zustand = createStore<AppState>()(
|
|
@@ -407,6 +418,99 @@ export const zustand = createStore<AppState>()(
|
|
|
407
418
|
|
|
408
419
|
state.dirtyDate = new Date();
|
|
409
420
|
});
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
updateLocalization: (
|
|
424
|
+
languageCode: string,
|
|
425
|
+
actionUuid: string,
|
|
426
|
+
localizationData: Record<string, any>
|
|
427
|
+
) => {
|
|
428
|
+
set((state: AppState) => {
|
|
429
|
+
// Initialize localization structure if it doesn't exist
|
|
430
|
+
if (!state.flowDefinition.localization) {
|
|
431
|
+
state.flowDefinition.localization = {};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!state.flowDefinition.localization[languageCode]) {
|
|
435
|
+
state.flowDefinition.localization[languageCode] = {};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Update or remove the localization for this action
|
|
439
|
+
if (Object.keys(localizationData).length > 0) {
|
|
440
|
+
state.flowDefinition.localization[languageCode][actionUuid] =
|
|
441
|
+
localizationData;
|
|
442
|
+
} else {
|
|
443
|
+
// If no localized values, remove the entry
|
|
444
|
+
delete state.flowDefinition.localization[languageCode][actionUuid];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Clean up empty language sections
|
|
448
|
+
if (
|
|
449
|
+
Object.keys(state.flowDefinition.localization[languageCode])
|
|
450
|
+
.length === 0
|
|
451
|
+
) {
|
|
452
|
+
delete state.flowDefinition.localization[languageCode];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Clean up empty localization object
|
|
456
|
+
if (Object.keys(state.flowDefinition.localization).length === 0) {
|
|
457
|
+
delete state.flowDefinition.localization;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
state.dirtyDate = new Date();
|
|
461
|
+
});
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
setTranslationFilters: (filters: { categories: boolean }) => {
|
|
465
|
+
set((state: AppState) => {
|
|
466
|
+
if (!state.flowDefinition?._ui) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const currentFilters = state.flowDefinition._ui
|
|
471
|
+
.translation_filters || {
|
|
472
|
+
categories: false
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
state.flowDefinition._ui.translation_filters = {
|
|
476
|
+
...currentFilters,
|
|
477
|
+
categories: !!filters.categories
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
state.dirtyDate = new Date();
|
|
481
|
+
});
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
markAutoTranslated: (
|
|
485
|
+
languageCode: string,
|
|
486
|
+
uuid: string,
|
|
487
|
+
attributes: string[]
|
|
488
|
+
) => {
|
|
489
|
+
set((state: AppState) => {
|
|
490
|
+
if (!state.flowDefinition?._ui) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (!state.flowDefinition._ui.auto_translations) {
|
|
495
|
+
state.flowDefinition._ui.auto_translations = {};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (!state.flowDefinition._ui.auto_translations[languageCode]) {
|
|
499
|
+
state.flowDefinition._ui.auto_translations[languageCode] = {};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const existing =
|
|
503
|
+
state.flowDefinition._ui.auto_translations[languageCode][uuid] ||
|
|
504
|
+
[];
|
|
505
|
+
|
|
506
|
+
const merged = Array.from(
|
|
507
|
+
new Set([...existing, ...(attributes || [])])
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
state.flowDefinition._ui.auto_translations[languageCode][uuid] =
|
|
511
|
+
merged;
|
|
512
|
+
state.dirtyDate = new Date();
|
|
513
|
+
});
|
|
410
514
|
}
|
|
411
515
|
}))
|
|
412
516
|
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"next": null,
|
|
3
|
+
"previous": null,
|
|
4
|
+
"results": [
|
|
5
|
+
{
|
|
6
|
+
"uuid": "2399e7d6-fcdf-4e47-a835-f3bdb7f80938",
|
|
7
|
+
"name": "GPT 4.1",
|
|
8
|
+
"type": "openai",
|
|
9
|
+
"description": "General-purpose reasoning model"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"uuid": "4399e7d6-fcdf-4e47-a835-f3bdb7f80938",
|
|
13
|
+
"name": "GPT 5",
|
|
14
|
+
"type": "openai",
|
|
15
|
+
"description": "Latest experimental model"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/temba-modules.ts
CHANGED
|
@@ -74,6 +74,8 @@ import { KeyValueEditor } from './src/form/KeyValueEditor';
|
|
|
74
74
|
import { TembaArrayEditor } from './src/form/ArrayEditor';
|
|
75
75
|
import { MessageEditor } from './src/form/MessageEditor';
|
|
76
76
|
import './src/form/BaseListEditor'; // Import base class
|
|
77
|
+
import { FloatingTab } from './src/display/FloatingTab';
|
|
78
|
+
import { FloatingWindow } from './src/layout/FloatingWindow';
|
|
77
79
|
|
|
78
80
|
export function addCustomElement(name: string, comp: any) {
|
|
79
81
|
if (!window.customElements.get(name)) {
|
|
@@ -158,3 +160,5 @@ addCustomElement('temba-chart', TembaChart);
|
|
|
158
160
|
addCustomElement('temba-key-value-editor', KeyValueEditor);
|
|
159
161
|
addCustomElement('temba-array-editor', TembaArrayEditor);
|
|
160
162
|
addCustomElement('temba-message-editor', MessageEditor);
|
|
163
|
+
addCustomElement('temba-floating-tab', FloatingTab);
|
|
164
|
+
addCustomElement('temba-floating-window', FloatingWindow);
|