@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
package/src/flow/NodeEditor.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { html, TemplateResult, css } from 'lit';
|
|
2
2
|
import { property, state } from 'lit/decorators.js';
|
|
3
3
|
import { RapidElement } from '../RapidElement';
|
|
4
|
-
import { Node, NodeUI, Action } from '../store/flow-definition';
|
|
4
|
+
import { Node, NodeUI, Action, FlowDefinition } from '../store/flow-definition';
|
|
5
5
|
import {
|
|
6
6
|
ValidationResult,
|
|
7
7
|
NodeConfig,
|
|
@@ -22,6 +22,8 @@ import { CustomEventType } from '../interfaces';
|
|
|
22
22
|
import { generateUUID } from '../utils';
|
|
23
23
|
import { FieldRenderer } from '../form/FieldRenderer';
|
|
24
24
|
import { renderMarkdownInline } from '../markdown';
|
|
25
|
+
import { AppState, fromStore, zustand } from '../store/AppState';
|
|
26
|
+
import { getStore } from '../store/Store';
|
|
25
27
|
|
|
26
28
|
export class NodeEditor extends RapidElement {
|
|
27
29
|
static get styles() {
|
|
@@ -319,6 +321,54 @@ export class NodeEditor extends RapidElement {
|
|
|
319
321
|
.optional-field-link a:hover {
|
|
320
322
|
text-decoration: underline;
|
|
321
323
|
}
|
|
324
|
+
|
|
325
|
+
.original-value {
|
|
326
|
+
background: #fff8dc;
|
|
327
|
+
padding: 10px;
|
|
328
|
+
border-radius: 4px;
|
|
329
|
+
font-size: 13px;
|
|
330
|
+
color: #666;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.original-value-content {
|
|
334
|
+
color: #333;
|
|
335
|
+
white-space: pre-wrap;
|
|
336
|
+
word-break: break-word;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.category-localization-table {
|
|
340
|
+
width: 100%;
|
|
341
|
+
display: flex;
|
|
342
|
+
flex-direction: column;
|
|
343
|
+
gap: 5px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.category-localization-row {
|
|
347
|
+
display: grid;
|
|
348
|
+
grid-template-columns: 40% 60%;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.category-localization-row:last-child {
|
|
352
|
+
border-bottom: none;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.original-name {
|
|
356
|
+
padding: 10px 20px;
|
|
357
|
+
background: #fff8dc;
|
|
358
|
+
display: flex;
|
|
359
|
+
align-items: center;
|
|
360
|
+
border-radius: var(--curvature);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.localized-name {
|
|
364
|
+
padding: 10px;
|
|
365
|
+
display: flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.localized-name temba-textinput {
|
|
370
|
+
width: 100%;
|
|
371
|
+
}
|
|
322
372
|
`;
|
|
323
373
|
}
|
|
324
374
|
|
|
@@ -352,6 +402,15 @@ export class NodeEditor extends RapidElement {
|
|
|
352
402
|
@state()
|
|
353
403
|
private revealedOptionalFields: Set<string> = new Set();
|
|
354
404
|
|
|
405
|
+
@fromStore(zustand, (state: AppState) => state.languageCode)
|
|
406
|
+
private languageCode!: string;
|
|
407
|
+
|
|
408
|
+
@fromStore(zustand, (state: AppState) => state.isTranslating)
|
|
409
|
+
private isTranslating!: boolean;
|
|
410
|
+
|
|
411
|
+
@fromStore(zustand, (state: AppState) => state.flowDefinition)
|
|
412
|
+
private flowDefinition!: FlowDefinition;
|
|
413
|
+
|
|
355
414
|
connectedCallback(): void {
|
|
356
415
|
super.connectedCallback();
|
|
357
416
|
this.initializeFormData();
|
|
@@ -392,7 +451,23 @@ export class NodeEditor extends RapidElement {
|
|
|
392
451
|
// Action editing mode - use action config
|
|
393
452
|
const actionConfig = ACTION_CONFIG[this.action.type];
|
|
394
453
|
|
|
395
|
-
if
|
|
454
|
+
// Check if we're in localization mode
|
|
455
|
+
if (
|
|
456
|
+
this.isTranslating &&
|
|
457
|
+
actionConfig?.localizable &&
|
|
458
|
+
actionConfig.toLocalizationFormData
|
|
459
|
+
) {
|
|
460
|
+
// Get localized values for this action
|
|
461
|
+
const localization =
|
|
462
|
+
this.flowDefinition?.localization?.[this.languageCode]?.[
|
|
463
|
+
this.action.uuid
|
|
464
|
+
] || {};
|
|
465
|
+
|
|
466
|
+
this.formData = actionConfig.toLocalizationFormData(
|
|
467
|
+
this.action,
|
|
468
|
+
localization
|
|
469
|
+
);
|
|
470
|
+
} else if (actionConfig?.toFormData) {
|
|
396
471
|
this.formData = actionConfig.toFormData(this.action);
|
|
397
472
|
} else {
|
|
398
473
|
this.formData = { ...this.action };
|
|
@@ -406,7 +481,22 @@ export class NodeEditor extends RapidElement {
|
|
|
406
481
|
} else if (this.node) {
|
|
407
482
|
// Node editing mode - use node config
|
|
408
483
|
const nodeConfig = this.getNodeConfig();
|
|
409
|
-
|
|
484
|
+
|
|
485
|
+
// Check if we're in localization mode for a node with localizable categories
|
|
486
|
+
if (
|
|
487
|
+
this.isTranslating &&
|
|
488
|
+
nodeConfig?.localizable === 'categories' &&
|
|
489
|
+
nodeConfig.toLocalizationFormData
|
|
490
|
+
) {
|
|
491
|
+
// Get localized values for this node's categories
|
|
492
|
+
const localization =
|
|
493
|
+
this.flowDefinition?.localization?.[this.languageCode] || {};
|
|
494
|
+
|
|
495
|
+
this.formData = nodeConfig.toLocalizationFormData(
|
|
496
|
+
this.node,
|
|
497
|
+
localization
|
|
498
|
+
);
|
|
499
|
+
} else if (nodeConfig?.toFormData) {
|
|
410
500
|
this.formData = nodeConfig.toFormData(this.node, this.nodeUI);
|
|
411
501
|
} else {
|
|
412
502
|
this.formData = { ...this.node };
|
|
@@ -551,19 +641,93 @@ export class NodeEditor extends RapidElement {
|
|
|
551
641
|
}
|
|
552
642
|
|
|
553
643
|
private handleSave(): void {
|
|
554
|
-
//
|
|
555
|
-
const
|
|
556
|
-
if (!validation.valid) {
|
|
557
|
-
this.errors = validation.errors;
|
|
644
|
+
// Process form data first
|
|
645
|
+
const processedFormData = this.processFormDataForSave();
|
|
558
646
|
|
|
559
|
-
|
|
560
|
-
|
|
647
|
+
// Skip validation if we're in localization mode
|
|
648
|
+
// (localization only deals with translating text, not changing structure)
|
|
649
|
+
if (!this.isTranslating) {
|
|
650
|
+
// Validate the form
|
|
651
|
+
const validation = this.validateForm();
|
|
652
|
+
if (!validation.valid) {
|
|
653
|
+
this.errors = validation.errors;
|
|
561
654
|
|
|
562
|
-
|
|
655
|
+
// Expand any groups that contain validation errors
|
|
656
|
+
this.expandGroupsWithErrors(validation.errors);
|
|
657
|
+
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
563
660
|
}
|
|
564
661
|
|
|
565
|
-
//
|
|
566
|
-
|
|
662
|
+
// Check if we're in localization mode
|
|
663
|
+
if (this.isTranslating) {
|
|
664
|
+
// Handle action localization
|
|
665
|
+
if (this.action) {
|
|
666
|
+
const actionConfig = ACTION_CONFIG[this.action.type];
|
|
667
|
+
|
|
668
|
+
if (
|
|
669
|
+
actionConfig?.localizable &&
|
|
670
|
+
actionConfig.fromLocalizationFormData
|
|
671
|
+
) {
|
|
672
|
+
// Save to localization structure
|
|
673
|
+
const localizationData = actionConfig.fromLocalizationFormData(
|
|
674
|
+
processedFormData,
|
|
675
|
+
this.action
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
// Update the flow definition's localization
|
|
679
|
+
this.updateLocalization(
|
|
680
|
+
this.languageCode,
|
|
681
|
+
this.action.uuid,
|
|
682
|
+
localizationData
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
// Close the dialog
|
|
686
|
+
this.fireCustomEvent(CustomEventType.NodeEditCancelled, {});
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Handle node localization (for router categories)
|
|
692
|
+
if (this.node) {
|
|
693
|
+
const nodeConfig = this.getNodeConfig();
|
|
694
|
+
|
|
695
|
+
if (
|
|
696
|
+
nodeConfig?.localizable === 'categories' &&
|
|
697
|
+
nodeConfig.fromLocalizationFormData
|
|
698
|
+
) {
|
|
699
|
+
// Get localization data for all categories
|
|
700
|
+
const localizationData = nodeConfig.fromLocalizationFormData(
|
|
701
|
+
processedFormData,
|
|
702
|
+
this.node
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
const languageLocalization =
|
|
706
|
+
this.flowDefinition?.localization?.[this.languageCode] || {};
|
|
707
|
+
const categories = this.node?.router?.categories || [];
|
|
708
|
+
|
|
709
|
+
categories.forEach((category) => {
|
|
710
|
+
const categoryUuid = category.uuid;
|
|
711
|
+
const nextLocalization = localizationData[categoryUuid];
|
|
712
|
+
|
|
713
|
+
if (nextLocalization) {
|
|
714
|
+
this.updateLocalization(
|
|
715
|
+
this.languageCode,
|
|
716
|
+
categoryUuid,
|
|
717
|
+
nextLocalization
|
|
718
|
+
);
|
|
719
|
+
} else if (languageLocalization[categoryUuid]) {
|
|
720
|
+
// Remove existing localization when the translation was cleared
|
|
721
|
+
this.updateLocalization(this.languageCode, categoryUuid, {});
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
// Close the dialog
|
|
726
|
+
this.fireCustomEvent(CustomEventType.NodeEditCancelled, {});
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
567
731
|
|
|
568
732
|
// Determine whether to use node or action saving based on context
|
|
569
733
|
// If we have a node with a router, always use node saving (even if action is set)
|
|
@@ -605,6 +769,17 @@ export class NodeEditor extends RapidElement {
|
|
|
605
769
|
}
|
|
606
770
|
}
|
|
607
771
|
|
|
772
|
+
private updateLocalization(
|
|
773
|
+
languageCode: string,
|
|
774
|
+
actionUuid: string,
|
|
775
|
+
localizationData: Record<string, any>
|
|
776
|
+
): void {
|
|
777
|
+
// Use the store method to properly update localization with immer
|
|
778
|
+
zustand
|
|
779
|
+
.getState()
|
|
780
|
+
.updateLocalization(languageCode, actionUuid, localizationData);
|
|
781
|
+
}
|
|
782
|
+
|
|
608
783
|
private processFormDataForSave(): FormData {
|
|
609
784
|
const processed = { ...this.formData };
|
|
610
785
|
|
|
@@ -664,8 +839,9 @@ export class NodeEditor extends RapidElement {
|
|
|
664
839
|
}
|
|
665
840
|
}
|
|
666
841
|
|
|
667
|
-
// Check required fields
|
|
842
|
+
// Check required fields (skip in localization mode since all fields are optional)
|
|
668
843
|
if (
|
|
844
|
+
!this.isTranslating &&
|
|
669
845
|
(fieldConfig as any).required &&
|
|
670
846
|
(!value || (Array.isArray(value) && value.length === 0))
|
|
671
847
|
) {
|
|
@@ -1114,6 +1290,12 @@ export class NodeEditor extends RapidElement {
|
|
|
1114
1290
|
? `max-width: ${config.maxWidth};`
|
|
1115
1291
|
: '';
|
|
1116
1292
|
|
|
1293
|
+
// Render original value if in localization mode and action has the field
|
|
1294
|
+
const originalValueDisplay =
|
|
1295
|
+
this.isTranslating && this.action && fieldName in this.action
|
|
1296
|
+
? this.renderOriginalValue(fieldName, this.action[fieldName])
|
|
1297
|
+
: html``;
|
|
1298
|
+
|
|
1117
1299
|
const fieldContent = this.renderFieldContent(
|
|
1118
1300
|
fieldName,
|
|
1119
1301
|
config,
|
|
@@ -1121,12 +1303,14 @@ export class NodeEditor extends RapidElement {
|
|
|
1121
1303
|
errors
|
|
1122
1304
|
);
|
|
1123
1305
|
|
|
1306
|
+
const content = html` ${originalValueDisplay} ${fieldContent} `;
|
|
1307
|
+
|
|
1124
1308
|
// Wrap in container with style if maxWidth is specified
|
|
1125
1309
|
if (containerStyle) {
|
|
1126
|
-
return html`<div style="${containerStyle}">${
|
|
1310
|
+
return html`<div style="${containerStyle}">${content}</div>`;
|
|
1127
1311
|
}
|
|
1128
1312
|
|
|
1129
|
-
return
|
|
1313
|
+
return content;
|
|
1130
1314
|
}
|
|
1131
1315
|
|
|
1132
1316
|
private renderOptionalField(
|
|
@@ -1166,14 +1350,104 @@ export class NodeEditor extends RapidElement {
|
|
|
1166
1350
|
]);
|
|
1167
1351
|
}
|
|
1168
1352
|
|
|
1353
|
+
private renderCategoryLocalizationTable(): TemplateResult {
|
|
1354
|
+
const categories = this.formData.categories || {};
|
|
1355
|
+
const categoryEntries = Object.entries(categories);
|
|
1356
|
+
|
|
1357
|
+
if (categoryEntries.length === 0) {
|
|
1358
|
+
return html`<div>No categories to localize</div>`;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const languageName = getStore().getLanguageName(this.languageCode);
|
|
1362
|
+
|
|
1363
|
+
return html`
|
|
1364
|
+
<div class="category-localization-table">
|
|
1365
|
+
${categoryEntries.map(
|
|
1366
|
+
([categoryUuid, categoryData]: [string, any]) => html`
|
|
1367
|
+
<div class="category-localization-row">
|
|
1368
|
+
<div class="original-name">${categoryData.originalName}</div>
|
|
1369
|
+
<div class="localized-name">
|
|
1370
|
+
<temba-textinput
|
|
1371
|
+
name="${categoryUuid}"
|
|
1372
|
+
placeholder="${languageName} Translation"
|
|
1373
|
+
value="${categoryData.localizedName || ''}"
|
|
1374
|
+
@change=${(e: Event) =>
|
|
1375
|
+
this.handleCategoryLocalizationChange(
|
|
1376
|
+
categoryUuid,
|
|
1377
|
+
(e.target as any).value
|
|
1378
|
+
)}
|
|
1379
|
+
></temba-textinput>
|
|
1380
|
+
</div>
|
|
1381
|
+
</div>
|
|
1382
|
+
`
|
|
1383
|
+
)}
|
|
1384
|
+
</div>
|
|
1385
|
+
`;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
private handleCategoryLocalizationChange(
|
|
1389
|
+
categoryUuid: string,
|
|
1390
|
+
value: string
|
|
1391
|
+
): void {
|
|
1392
|
+
// Update formData with new localized value
|
|
1393
|
+
if (!this.formData.categories) {
|
|
1394
|
+
this.formData.categories = {};
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
if (!this.formData.categories[categoryUuid]) {
|
|
1398
|
+
this.formData.categories[categoryUuid] = {};
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
this.formData.categories[categoryUuid].localizedName = value;
|
|
1402
|
+
|
|
1403
|
+
// Trigger a re-render
|
|
1404
|
+
this.requestUpdate();
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
private renderOriginalValue(
|
|
1408
|
+
fieldName: string,
|
|
1409
|
+
originalValue: any
|
|
1410
|
+
): TemplateResult {
|
|
1411
|
+
// Format the original value for display
|
|
1412
|
+
let displayValue = '';
|
|
1413
|
+
|
|
1414
|
+
if (Array.isArray(originalValue)) {
|
|
1415
|
+
if (originalValue.length === 0) {
|
|
1416
|
+
return html``; // Don't show anything for empty arrays
|
|
1417
|
+
}
|
|
1418
|
+
// For arrays, join with commas
|
|
1419
|
+
displayValue = originalValue.join(', ');
|
|
1420
|
+
} else if (typeof originalValue === 'string') {
|
|
1421
|
+
displayValue = originalValue;
|
|
1422
|
+
} else if (originalValue) {
|
|
1423
|
+
displayValue = String(originalValue);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// Don't show if empty
|
|
1427
|
+
if (!displayValue || displayValue.trim() === '') {
|
|
1428
|
+
return html``;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
return html`
|
|
1432
|
+
<div class="original-value">
|
|
1433
|
+
<div class="original-value-content">${displayValue}</div>
|
|
1434
|
+
</div>
|
|
1435
|
+
`;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1169
1438
|
private renderFieldContent(
|
|
1170
1439
|
fieldName: string,
|
|
1171
1440
|
config: FieldConfig,
|
|
1172
1441
|
value: any,
|
|
1173
1442
|
errors: string[]
|
|
1174
1443
|
): TemplateResult {
|
|
1444
|
+
// In localization mode, make all fields optional (not required)
|
|
1445
|
+
const fieldConfig = this.isTranslating
|
|
1446
|
+
? { ...config, required: false }
|
|
1447
|
+
: config;
|
|
1448
|
+
|
|
1175
1449
|
// Use FieldRenderer for consistent field rendering
|
|
1176
|
-
return FieldRenderer.renderField(fieldName,
|
|
1450
|
+
return FieldRenderer.renderField(fieldName, fieldConfig, value, {
|
|
1177
1451
|
errors,
|
|
1178
1452
|
onChange: (e: Event) => {
|
|
1179
1453
|
// Handle different change event types
|
|
@@ -1613,6 +1887,15 @@ export class NodeEditor extends RapidElement {
|
|
|
1613
1887
|
return html` <div>No configuration available</div> `;
|
|
1614
1888
|
}
|
|
1615
1889
|
|
|
1890
|
+
// Special rendering for category localization
|
|
1891
|
+
if (
|
|
1892
|
+
this.isTranslating &&
|
|
1893
|
+
config.localizable === 'categories' &&
|
|
1894
|
+
this.formData.categories
|
|
1895
|
+
) {
|
|
1896
|
+
return this.renderCategoryLocalizationTable();
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1616
1899
|
// Use the new fields configuration system
|
|
1617
1900
|
if (config.form) {
|
|
1618
1901
|
// If layout is specified, use it
|
|
@@ -1675,6 +1958,11 @@ export class NodeEditor extends RapidElement {
|
|
|
1675
1958
|
return html``;
|
|
1676
1959
|
}
|
|
1677
1960
|
|
|
1961
|
+
// Don't show gutter when localizing categories
|
|
1962
|
+
if (this.isTranslating && config.localizable === 'categories') {
|
|
1963
|
+
return html``;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1678
1966
|
// Use the same layout rendering system for gutter fields
|
|
1679
1967
|
const renderedFields = new Set<string>();
|
|
1680
1968
|
|
|
@@ -1756,18 +2044,28 @@ export class NodeEditor extends RapidElement {
|
|
|
1756
2044
|
return html``;
|
|
1757
2045
|
}
|
|
1758
2046
|
|
|
1759
|
-
const headerColor = this.getHeaderColor();
|
|
2047
|
+
const headerColor = this.isTranslating ? '#505050' : this.getHeaderColor();
|
|
2048
|
+
const headerTextColor = this.isTranslating ? '#fff' : '#fff';
|
|
1760
2049
|
const config = this.getConfig();
|
|
1761
2050
|
const dialogSize = config?.dialogSize || 'medium'; // Default to 'large' if not specified
|
|
1762
2051
|
|
|
2052
|
+
const languageName = this.isTranslating
|
|
2053
|
+
? getStore().getLanguageName(this.languageCode)
|
|
2054
|
+
: '';
|
|
2055
|
+
|
|
2056
|
+
let header = config?.name || 'Edit';
|
|
2057
|
+
if (this.isTranslating) {
|
|
2058
|
+
header = languageName ? `${languageName} - ${header}` : header;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
1763
2061
|
return html`
|
|
1764
2062
|
<temba-dialog
|
|
1765
|
-
header="${
|
|
2063
|
+
header="${header}"
|
|
1766
2064
|
.open="${this.isOpen}"
|
|
1767
2065
|
@temba-button-clicked=${this.handleDialogButtonClick}
|
|
1768
2066
|
primaryButtonName="Save"
|
|
1769
2067
|
cancelButtonName="Cancel"
|
|
1770
|
-
style="--header-bg: ${headerColor}"
|
|
2068
|
+
style="--header-bg: ${headerColor}; --header-text: ${headerTextColor};"
|
|
1771
2069
|
size="${dialogSize}"
|
|
1772
2070
|
>
|
|
1773
2071
|
<div class="node-editor-form">
|
|
@@ -241,6 +241,12 @@ export class NodeTypeSelector extends RapidElement {
|
|
|
241
241
|
@property({ type: String })
|
|
242
242
|
public mode: 'action' | 'split' | 'action-no-branching' = 'action';
|
|
243
243
|
|
|
244
|
+
@property({ type: String })
|
|
245
|
+
public flowType: string = 'message';
|
|
246
|
+
|
|
247
|
+
@property({ type: Array })
|
|
248
|
+
public features: string[] = [];
|
|
249
|
+
|
|
244
250
|
@state()
|
|
245
251
|
private clickPosition = { x: 0, y: 0 };
|
|
246
252
|
|
|
@@ -257,6 +263,35 @@ export class NodeTypeSelector extends RapidElement {
|
|
|
257
263
|
this.open = false;
|
|
258
264
|
}
|
|
259
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Check if a config is available for the current flow type and features
|
|
268
|
+
*/
|
|
269
|
+
private isConfigAvailable(config: NodeConfig | ActionConfig): boolean {
|
|
270
|
+
// Check flow type filter
|
|
271
|
+
if (config.flowTypes !== undefined) {
|
|
272
|
+
// Empty array means not available for any flow type in selector
|
|
273
|
+
if (config.flowTypes.length === 0) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
// Non-empty array means check if current flow type is included
|
|
277
|
+
if (!config.flowTypes.includes(this.flowType as any)) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// undefined/null flowTypes means available for all flow types
|
|
282
|
+
|
|
283
|
+
// Check features filter - all required features must be present
|
|
284
|
+
if (config.features && config.features.length > 0) {
|
|
285
|
+
for (const requiredFeature of config.features) {
|
|
286
|
+
if (!this.features.includes(requiredFeature)) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
260
295
|
private handleNodeTypeClick(nodeType: string) {
|
|
261
296
|
this.fireCustomEvent(CustomEventType.Selection, {
|
|
262
297
|
nodeType,
|
|
@@ -284,7 +319,12 @@ export class NodeTypeSelector extends RapidElement {
|
|
|
284
319
|
// Collect regular actions (from ACTION_CONFIG, unless hideFromActions is true)
|
|
285
320
|
Object.entries(ACTION_CONFIG)
|
|
286
321
|
.filter(([_, config]) => {
|
|
287
|
-
return
|
|
322
|
+
return (
|
|
323
|
+
config.name &&
|
|
324
|
+
!config.hideFromActions &&
|
|
325
|
+
config.group &&
|
|
326
|
+
this.isConfigAvailable(config)
|
|
327
|
+
);
|
|
288
328
|
})
|
|
289
329
|
.forEach(([type, config]) => {
|
|
290
330
|
const group = config.group;
|
|
@@ -303,7 +343,8 @@ export class NodeTypeSelector extends RapidElement {
|
|
|
303
343
|
type !== 'execute_actions' &&
|
|
304
344
|
config.name &&
|
|
305
345
|
config.showAsAction &&
|
|
306
|
-
config.group
|
|
346
|
+
config.group &&
|
|
347
|
+
this.isConfigAvailable(config)
|
|
307
348
|
);
|
|
308
349
|
})
|
|
309
350
|
.forEach(([type, config]) => {
|
|
@@ -400,7 +441,10 @@ export class NodeTypeSelector extends RapidElement {
|
|
|
400
441
|
// exclude execute_actions (it's the default action-only node)
|
|
401
442
|
// exclude nodes that have showAsAction=true (they appear in action mode)
|
|
402
443
|
return (
|
|
403
|
-
type !== 'execute_actions' &&
|
|
444
|
+
type !== 'execute_actions' &&
|
|
445
|
+
config.name &&
|
|
446
|
+
!config.showAsAction &&
|
|
447
|
+
this.isConfigAvailable(config)
|
|
404
448
|
);
|
|
405
449
|
})
|
|
406
450
|
.forEach(([type, config]) => {
|
package/src/flow/StickyNote.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { property } from 'lit/decorators.js';
|
|
|
3
3
|
import { RapidElement } from '../RapidElement';
|
|
4
4
|
import { StickyNote as StickyNoteData } from '../store/flow-definition';
|
|
5
5
|
import { getStore } from '../store/Store';
|
|
6
|
+
import { AppState, fromStore, zustand } from '../store/AppState';
|
|
6
7
|
|
|
7
8
|
export class StickyNote extends RapidElement {
|
|
8
9
|
@property({ type: String })
|
|
@@ -20,6 +21,9 @@ export class StickyNote extends RapidElement {
|
|
|
20
21
|
@property({ type: Boolean })
|
|
21
22
|
private colorPickerExpanded = false;
|
|
22
23
|
|
|
24
|
+
@fromStore(zustand, (state: AppState) => state.isTranslating)
|
|
25
|
+
private isTranslating!: boolean;
|
|
26
|
+
|
|
23
27
|
static get styles() {
|
|
24
28
|
return css`
|
|
25
29
|
:host {
|
|
@@ -119,6 +123,8 @@ export class StickyNote extends RapidElement {
|
|
|
119
123
|
border-top-left-radius: var(--curvature);
|
|
120
124
|
border-top-right-radius: var(--curvature);
|
|
121
125
|
flex-grow: 1;
|
|
126
|
+
padding: 4px 8px !important;
|
|
127
|
+
margin: 2px;
|
|
122
128
|
padding-left: 8px;
|
|
123
129
|
}
|
|
124
130
|
.sticky-title:empty::before {
|
|
@@ -139,6 +145,7 @@ export class StickyNote extends RapidElement {
|
|
|
139
145
|
min-height: 48px;
|
|
140
146
|
word-wrap: break-word;
|
|
141
147
|
white-space: pre-wrap;
|
|
148
|
+
margin: 2px;
|
|
142
149
|
}
|
|
143
150
|
.sticky-body:empty::before {
|
|
144
151
|
content: 'Click to add note';
|
|
@@ -377,7 +384,7 @@ export class StickyNote extends RapidElement {
|
|
|
377
384
|
<temba-icon name="drag" class="drag-handle"></temba-icon>
|
|
378
385
|
<div
|
|
379
386
|
class="sticky-title"
|
|
380
|
-
contenteditable="
|
|
387
|
+
contenteditable="${!this.isTranslating}"
|
|
381
388
|
@blur="${this.handleTitleBlur}"
|
|
382
389
|
@keydown="${this.handleKeyDown}"
|
|
383
390
|
@mousedown="${this.handleContentMouseDown}"
|
|
@@ -387,13 +394,15 @@ export class StickyNote extends RapidElement {
|
|
|
387
394
|
<div class="sticky-body-container">
|
|
388
395
|
<div
|
|
389
396
|
class="sticky-body"
|
|
390
|
-
contenteditable="
|
|
397
|
+
contenteditable="${!this.isTranslating}"
|
|
391
398
|
@blur="${this.handleBodyBlur}"
|
|
392
399
|
@keydown="${this.handleKeyDown}"
|
|
393
400
|
@mousedown="${this.handleContentMouseDown}"
|
|
394
401
|
.textContent="${this.data.body}"
|
|
395
402
|
></div>
|
|
396
|
-
|
|
403
|
+
${!this.isTranslating
|
|
404
|
+
? html`<div class="edit-icon" title="Edit note"></div>`
|
|
405
|
+
: ''}
|
|
397
406
|
|
|
398
407
|
<!-- Color picker -->
|
|
399
408
|
<div
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
|
-
import { ActionConfig, ACTION_GROUPS, FormData } from '../types';
|
|
2
|
+
import { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';
|
|
3
3
|
import { Node, AddToGroup } from '../../store/flow-definition';
|
|
4
4
|
import { renderNamedObjects } from '../utils';
|
|
5
5
|
|
|
6
6
|
export const add_contact_groups: ActionConfig = {
|
|
7
7
|
name: 'Add to Group',
|
|
8
8
|
group: ACTION_GROUPS.contacts,
|
|
9
|
+
flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
|
|
9
10
|
render: (_node: Node, action: AddToGroup) => {
|
|
10
11
|
return html`<div>${renderNamedObjects(action.groups, 'group')}</div>`;
|
|
11
12
|
},
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
ActionConfig,
|
|
4
4
|
ACTION_GROUPS,
|
|
5
5
|
FormData,
|
|
6
|
-
ValidationResult
|
|
6
|
+
ValidationResult,
|
|
7
|
+
FlowTypes
|
|
7
8
|
} from '../types';
|
|
8
9
|
import { Node, AddContactUrn } from '../../store/flow-definition';
|
|
9
10
|
import { SCHEMES } from '../utils';
|
|
@@ -11,6 +12,7 @@ import { SCHEMES } from '../utils';
|
|
|
11
12
|
export const add_contact_urn: ActionConfig = {
|
|
12
13
|
name: 'Add URN',
|
|
13
14
|
group: ACTION_GROUPS.contacts,
|
|
15
|
+
flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
|
|
14
16
|
render: (_node: Node, action: AddContactUrn) => {
|
|
15
17
|
const schemeObj = SCHEMES.find((s) => s.scheme === action.scheme);
|
|
16
18
|
const friendlyScheme = schemeObj?.path || action.scheme;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
|
-
import { ActionConfig, ACTION_GROUPS, FormData } from '../types';
|
|
2
|
+
import { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';
|
|
3
3
|
import { Node, AddInputLabels } from '../../store/flow-definition';
|
|
4
4
|
import { renderNamedObjects } from '../utils';
|
|
5
5
|
|
|
6
6
|
export const add_input_labels: ActionConfig = {
|
|
7
7
|
name: 'Add Input Labels',
|
|
8
8
|
group: ACTION_GROUPS.save,
|
|
9
|
+
flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
|
|
9
10
|
render: (_node: Node, action: AddInputLabels) => {
|
|
10
11
|
return html`<div>${renderNamedObjects(action.labels, 'label')}</div>`;
|
|
11
12
|
},
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
|
-
import { ActionConfig, ACTION_GROUPS } from '../types';
|
|
2
|
+
import { ActionConfig, ACTION_GROUPS, FlowTypes } from '../types';
|
|
3
3
|
import { Node, PlayAudio } from '../../store/flow-definition';
|
|
4
4
|
|
|
5
5
|
export const play_audio: ActionConfig = {
|
|
6
6
|
name: 'Play Audio',
|
|
7
7
|
group: ACTION_GROUPS.send,
|
|
8
|
+
flowTypes: [FlowTypes.VOICE],
|
|
8
9
|
render: (_node: Node, _action: PlayAudio) => {
|
|
9
10
|
// This will need to be implemented based on the actual render logic
|
|
10
11
|
return html`<div>Play Audio</div>`;
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
ActionConfig,
|
|
4
4
|
ACTION_GROUPS,
|
|
5
5
|
FormData,
|
|
6
|
-
ValidationResult
|
|
6
|
+
ValidationResult,
|
|
7
|
+
FlowTypes
|
|
7
8
|
} from '../types';
|
|
8
9
|
import { Node, RemoveFromGroup } from '../../store/flow-definition';
|
|
9
10
|
import { renderNamedObjects } from '../utils';
|
|
@@ -11,6 +12,7 @@ import { renderNamedObjects } from '../utils';
|
|
|
11
12
|
export const remove_contact_groups: ActionConfig = {
|
|
12
13
|
name: 'Remove from Group',
|
|
13
14
|
group: ACTION_GROUPS.contacts,
|
|
15
|
+
flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
|
|
14
16
|
render: (_node: Node, action: RemoveFromGroup) => {
|
|
15
17
|
if (action.all_groups) {
|
|
16
18
|
return html`<div>Remove from all groups</div>`;
|
|
@@ -5,6 +5,7 @@ import { Node, RequestOptin } from '../../store/flow-definition';
|
|
|
5
5
|
export const request_optin: ActionConfig = {
|
|
6
6
|
name: 'Request Opt-in',
|
|
7
7
|
group: ACTION_GROUPS.send,
|
|
8
|
+
flowTypes: [],
|
|
8
9
|
render: (_node: Node, _action: RequestOptin) => {
|
|
9
10
|
// This will need to be implemented based on the actual render logic
|
|
10
11
|
return html`<div>Request Opt-in</div>`;
|