@nyaruka/temba-components 0.156.2 → 0.156.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 +10 -0
- package/dist/temba-components.js +191 -131
- package/dist/temba-components.js.map +1 -1
- package/package.json +1 -1
- package/src/display/Chat.ts +19 -17
- package/src/events/eventRenderers.ts +6 -1
- package/src/flow/CanvasNode.ts +49 -2
- package/src/flow/Editor.ts +25 -33
- package/src/flow/FlowSearch.ts +42 -10
- package/src/flow/MessageTable.ts +332 -122
- package/src/flow/NodeEditor.ts +29 -4
- package/src/flow/actions/say_msg.ts +1 -0
- package/src/flow/actions/send_broadcast.ts +48 -0
- package/src/flow/actions/send_email.ts +42 -1
- package/src/flow/actions/send_msg.ts +1 -0
- package/src/flow/actions/set_run_result.ts +1 -0
- package/src/flow/nodes/shared-rules.ts +1 -0
- package/src/flow/nodes/shared.ts +1 -0
- package/src/flow/nodes/wait_for_audio.ts +1 -0
- package/src/simulator/Simulator.ts +17 -12
- package/web-test-runner.config.mjs +2 -0
package/package.json
CHANGED
package/src/display/Chat.ts
CHANGED
|
@@ -583,34 +583,36 @@ export class Chat extends RapidElement {
|
|
|
583
583
|
text-align: center;
|
|
584
584
|
font-size: 11px;
|
|
585
585
|
color: #8e8e93;
|
|
586
|
+
max-width: 100%;
|
|
587
|
+
overflow: hidden;
|
|
586
588
|
}
|
|
587
589
|
|
|
588
590
|
.event .webhook-event {
|
|
589
591
|
display: inline-flex;
|
|
590
|
-
align-items:
|
|
592
|
+
align-items: flex-start;
|
|
591
593
|
gap: 6px;
|
|
594
|
+
max-width: 100%;
|
|
595
|
+
min-width: 0;
|
|
592
596
|
}
|
|
593
597
|
|
|
594
|
-
.event .webhook-event-
|
|
595
|
-
|
|
596
|
-
display:
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
border-radius: 4px;
|
|
603
|
-
transition: color var(--animation-time, 200ms) ease;
|
|
598
|
+
.event .webhook-event-text {
|
|
599
|
+
overflow: hidden;
|
|
600
|
+
display: -webkit-box;
|
|
601
|
+
-webkit-line-clamp: 2;
|
|
602
|
+
-webkit-box-orient: vertical;
|
|
603
|
+
word-break: break-all;
|
|
604
|
+
min-width: 0;
|
|
605
|
+
padding: 4px 0;
|
|
604
606
|
}
|
|
605
607
|
|
|
606
|
-
.event .webhook-event-
|
|
607
|
-
|
|
608
|
-
|
|
608
|
+
.event .webhook-event-url {
|
|
609
|
+
color: inherit;
|
|
610
|
+
text-decoration: underline;
|
|
611
|
+
cursor: pointer;
|
|
609
612
|
}
|
|
610
613
|
|
|
611
|
-
.event .webhook-event-
|
|
612
|
-
|
|
613
|
-
outline-offset: 2px;
|
|
614
|
+
.event .webhook-event-url:hover {
|
|
615
|
+
text-decoration: none;
|
|
614
616
|
}
|
|
615
617
|
|
|
616
618
|
.event p {
|
|
@@ -389,7 +389,12 @@ export const renderResthookCalled = (event: any): TemplateResult | null => {
|
|
|
389
389
|
};
|
|
390
390
|
|
|
391
391
|
export const renderWebhookCalled = (event: any): TemplateResult | null => {
|
|
392
|
-
|
|
392
|
+
const maxLen = 50;
|
|
393
|
+
const displayUrl =
|
|
394
|
+
event.url && event.url.length > maxLen
|
|
395
|
+
? event.url.slice(0, maxLen) + '...'
|
|
396
|
+
: event.url;
|
|
397
|
+
return html`<div>Called <strong>${displayUrl}</strong></div>`;
|
|
393
398
|
};
|
|
394
399
|
|
|
395
400
|
export const renderServiceCalled = (event: any): TemplateResult | null => {
|
package/src/flow/CanvasNode.ts
CHANGED
|
@@ -361,6 +361,9 @@ export class CanvasNode extends RapidElement {
|
|
|
361
361
|
|
|
362
362
|
.router .body {
|
|
363
363
|
padding: 0.75em;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.router .body > div {
|
|
364
367
|
max-width: 180px;
|
|
365
368
|
}
|
|
366
369
|
|
|
@@ -368,7 +371,32 @@ export class CanvasNode extends RapidElement {
|
|
|
368
371
|
font-weight: 500;
|
|
369
372
|
display: inline-block;
|
|
370
373
|
}
|
|
371
|
-
|
|
374
|
+
|
|
375
|
+
.router {
|
|
376
|
+
display: flex;
|
|
377
|
+
flex-direction: column;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.rules-count {
|
|
381
|
+
position: absolute;
|
|
382
|
+
right: 4px;
|
|
383
|
+
top: 50%;
|
|
384
|
+
transform: translateY(-50%);
|
|
385
|
+
background: #fff8dc;
|
|
386
|
+
border-radius: 10px;
|
|
387
|
+
min-width: 18px;
|
|
388
|
+
height: 18px;
|
|
389
|
+
padding: 0 5px;
|
|
390
|
+
font-size: 11px;
|
|
391
|
+
font-weight: 600;
|
|
392
|
+
color: #333;
|
|
393
|
+
display: flex;
|
|
394
|
+
align-items: center;
|
|
395
|
+
justify-content: center;
|
|
396
|
+
line-height: 1;
|
|
397
|
+
box-sizing: border-box;
|
|
398
|
+
}
|
|
399
|
+
|
|
372
400
|
.exit-wrapper {
|
|
373
401
|
display: flex;
|
|
374
402
|
justify-content: center;
|
|
@@ -1611,9 +1639,10 @@ export class CanvasNode extends RapidElement {
|
|
|
1611
1639
|
? ACTION_GROUP_METADATA[config.group]?.color ||
|
|
1612
1640
|
SPLIT_GROUP_METADATA[config.group]?.color
|
|
1613
1641
|
: '#aaaaaa';
|
|
1642
|
+
const untranslatedRules = this.getUntranslatedRulesCount();
|
|
1614
1643
|
return html`<div
|
|
1615
1644
|
class="cn-title ${isRemoving ? 'removing' : ''}"
|
|
1616
|
-
style="background:${color}"
|
|
1645
|
+
style="background:${color}; position: relative;"
|
|
1617
1646
|
>
|
|
1618
1647
|
<div class="title-spacer"></div>
|
|
1619
1648
|
<div class="name">
|
|
@@ -1630,6 +1659,9 @@ export class CanvasNode extends RapidElement {
|
|
|
1630
1659
|
>
|
|
1631
1660
|
✕
|
|
1632
1661
|
</div>
|
|
1662
|
+
${untranslatedRules > 0
|
|
1663
|
+
? html`<div class="rules-count">${untranslatedRules}</div>`
|
|
1664
|
+
: null}
|
|
1633
1665
|
</div>`;
|
|
1634
1666
|
}
|
|
1635
1667
|
|
|
@@ -1752,6 +1784,21 @@ export class CanvasNode extends RapidElement {
|
|
|
1752
1784
|
return result;
|
|
1753
1785
|
}
|
|
1754
1786
|
|
|
1787
|
+
private getUntranslatedRulesCount(): number {
|
|
1788
|
+
if (!this.isTranslating || !this.ui?.config?.localizeRules) return 0;
|
|
1789
|
+
const cases = this.node?.router?.cases;
|
|
1790
|
+
if (!cases?.length) return 0;
|
|
1791
|
+
|
|
1792
|
+
const langLocalization =
|
|
1793
|
+
this.flowDefinition?.localization?.[this.languageCode] || {};
|
|
1794
|
+
|
|
1795
|
+
return cases.filter((c) => {
|
|
1796
|
+
if (!c.arguments?.length || !c.arguments.some((a) => a)) return false;
|
|
1797
|
+
const localized = langLocalization[c.uuid]?.arguments;
|
|
1798
|
+
return !Array.isArray(localized) || !localized.some((a: string) => a);
|
|
1799
|
+
}).length;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1755
1802
|
private renderRouter(router: Router, ui: NodeUI) {
|
|
1756
1803
|
const nodeConfig = NODE_CONFIG[ui.type];
|
|
1757
1804
|
if (nodeConfig) {
|
package/src/flow/Editor.ts
CHANGED
|
@@ -705,6 +705,12 @@ export class Editor extends RapidElement {
|
|
|
705
705
|
border: 1px solid #d7dce2;
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
+
.language-pill.complete {
|
|
709
|
+
background: #d4f5e0;
|
|
710
|
+
color: #1a7f37;
|
|
711
|
+
--icon-color: #1a7f37;
|
|
712
|
+
}
|
|
713
|
+
|
|
708
714
|
.language-pill-caret {
|
|
709
715
|
margin-left: 1px;
|
|
710
716
|
--icon-color: currentColor;
|
|
@@ -720,6 +726,10 @@ export class Editor extends RapidElement {
|
|
|
720
726
|
white-space: nowrap;
|
|
721
727
|
}
|
|
722
728
|
|
|
729
|
+
.language-pill.complete .language-percent {
|
|
730
|
+
color: #1a7f37;
|
|
731
|
+
}
|
|
732
|
+
|
|
723
733
|
.toolbar-zoom-level {
|
|
724
734
|
font-size: 12px;
|
|
725
735
|
min-width: 40px;
|
|
@@ -6307,12 +6317,20 @@ export class Editor extends RapidElement {
|
|
|
6307
6317
|
`;
|
|
6308
6318
|
}
|
|
6309
6319
|
|
|
6320
|
+
const isComplete = option.percent === 100;
|
|
6321
|
+
const optionBg = isComplete ? '#d4f5e0' : '';
|
|
6322
|
+
const optionHoverBg = isComplete ? '#c0edce' : '';
|
|
6323
|
+
const optionRadius = isComplete ? 'border-radius:4px;' : '';
|
|
6324
|
+
const percentColor = isComplete ? 'color:#1a7f37;' : 'color:#5f6b7a;';
|
|
6325
|
+
|
|
6310
6326
|
return html`
|
|
6311
6327
|
<div
|
|
6312
|
-
style="display:flex; align-items:center; justify-content:space-between; gap:8px; padding:6px 10px;"
|
|
6328
|
+
style="display:flex; align-items:center; justify-content:space-between; gap:8px; padding:6px 10px; ${optionBg ? `background:${optionBg};` : ''} ${optionRadius}"
|
|
6329
|
+
@mouseenter=${isComplete ? (e: MouseEvent) => { (e.currentTarget as HTMLElement).style.background = optionHoverBg; } : null}
|
|
6330
|
+
@mouseleave=${isComplete ? (e: MouseEvent) => { (e.currentTarget as HTMLElement).style.background = optionBg; } : null}
|
|
6313
6331
|
>
|
|
6314
|
-
<span>${option.name}</span>
|
|
6315
|
-
<span style="font-size:11px; font-weight:600;
|
|
6332
|
+
<span style="${isComplete ? 'color:#1a7f37;' : ''}">${option.name}</span>
|
|
6333
|
+
<span style="font-size:11px; font-weight:600; ${percentColor}"
|
|
6316
6334
|
>${option.percent ?? 0}%</span
|
|
6317
6335
|
>
|
|
6318
6336
|
</div>
|
|
@@ -6407,7 +6425,7 @@ export class Editor extends RapidElement {
|
|
|
6407
6425
|
'Change language',
|
|
6408
6426
|
html`
|
|
6409
6427
|
<button
|
|
6410
|
-
class="language-pill ${isBaseSelected ? 'primary' : ''}"
|
|
6428
|
+
class="language-pill ${isBaseSelected ? 'primary' : percent === 100 ? 'complete' : ''}"
|
|
6411
6429
|
id="language-btn"
|
|
6412
6430
|
@click=${this.handleLanguageIconClick}
|
|
6413
6431
|
aria-label="Change language"
|
|
@@ -6542,35 +6560,9 @@ export class Editor extends RapidElement {
|
|
|
6542
6560
|
`;
|
|
6543
6561
|
}
|
|
6544
6562
|
|
|
6545
|
-
private renderToolbarTranslationTools(
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
? 'Stop auto translate'
|
|
6549
|
-
: 'Auto translate';
|
|
6550
|
-
return html`
|
|
6551
|
-
<div class="toolbar-translation">
|
|
6552
|
-
${this.renderToolbarTip(
|
|
6553
|
-
autoTranslateLabel,
|
|
6554
|
-
html`
|
|
6555
|
-
<button
|
|
6556
|
-
class="toolbar-btn language-tool ${this.autoTranslating
|
|
6557
|
-
? 'active'
|
|
6558
|
-
: ''}"
|
|
6559
|
-
@click=${this.handleAutoTranslateClick}
|
|
6560
|
-
?disabled=${disableTranslationControls ||
|
|
6561
|
-
(!this.autoTranslating && !hasTranslations)}
|
|
6562
|
-
aria-label=${autoTranslateLabel}
|
|
6563
|
-
>
|
|
6564
|
-
<temba-icon
|
|
6565
|
-
name=${this.autoTranslating ? 'progress_spinner' : Icon.ai}
|
|
6566
|
-
size="0.9"
|
|
6567
|
-
?spin=${this.autoTranslating}
|
|
6568
|
-
></temba-icon>
|
|
6569
|
-
</button>
|
|
6570
|
-
`
|
|
6571
|
-
)}
|
|
6572
|
-
</div>
|
|
6573
|
-
`;
|
|
6563
|
+
private renderToolbarTranslationTools(_hasTranslations: boolean): TemplateResult {
|
|
6564
|
+
// auto translate button hidden pending backend changes
|
|
6565
|
+
return html``;
|
|
6574
6566
|
}
|
|
6575
6567
|
|
|
6576
6568
|
/**
|
package/src/flow/FlowSearch.ts
CHANGED
|
@@ -221,10 +221,23 @@ function getActionSearchTexts(action: Action): string[] {
|
|
|
221
221
|
return texts;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
function
|
|
224
|
+
function getTableSearchTexts(action: Action): string[] {
|
|
225
225
|
const texts: string[] = [];
|
|
226
|
-
|
|
227
|
-
if (
|
|
226
|
+
const config = ACTION_CONFIG[action.type];
|
|
227
|
+
if (!config?.localizable) return texts;
|
|
228
|
+
const a = action as Record<string, any>;
|
|
229
|
+
for (const key of config.localizable) {
|
|
230
|
+
const val = a[key];
|
|
231
|
+
if (typeof val === 'string' && val.trim()) {
|
|
232
|
+
texts.push(val);
|
|
233
|
+
} else if (Array.isArray(val)) {
|
|
234
|
+
for (const item of val) {
|
|
235
|
+
if (typeof item === 'string' && item.trim()) {
|
|
236
|
+
texts.push(item);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
228
241
|
return texts;
|
|
229
242
|
}
|
|
230
243
|
|
|
@@ -729,20 +742,37 @@ export class FlowSearch extends LitElement {
|
|
|
729
742
|
const nodeUI = this.definition._ui?.nodes[node.uuid];
|
|
730
743
|
const nodeType = nodeUI?.type || 'execute_actions';
|
|
731
744
|
|
|
732
|
-
// Message table rows: one row per
|
|
745
|
+
// Message table rows: one row per action with localizable fields
|
|
733
746
|
if (node.actions) {
|
|
734
747
|
for (const action of node.actions) {
|
|
735
|
-
|
|
748
|
+
const actionConfig = ACTION_CONFIG[action.type];
|
|
749
|
+
if (
|
|
750
|
+
action.type !== 'send_msg' &&
|
|
751
|
+
(!actionConfig?.localizable || actionConfig.localizable.length === 0)
|
|
752
|
+
) {
|
|
736
753
|
continue;
|
|
737
754
|
}
|
|
738
755
|
|
|
739
|
-
|
|
740
|
-
const
|
|
756
|
+
// Search both original and localized texts, but only add one result per action
|
|
757
|
+
const originalTexts = getTableSearchTexts(action);
|
|
758
|
+
const localizedAction = localizeAction(
|
|
741
759
|
action,
|
|
742
760
|
langLocalization?.[action.uuid]
|
|
743
|
-
)
|
|
744
|
-
const
|
|
745
|
-
|
|
761
|
+
);
|
|
762
|
+
const localizedTexts = getTableSearchTexts(localizedAction);
|
|
763
|
+
|
|
764
|
+
// Deduplicate: combine both, originals first
|
|
765
|
+
const allTexts: string[] = [];
|
|
766
|
+
const seen = new Set<string>();
|
|
767
|
+
for (const text of [...originalTexts, ...localizedTexts]) {
|
|
768
|
+
if (!seen.has(text)) {
|
|
769
|
+
seen.add(text);
|
|
770
|
+
allTexts.push(text);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
let found = false;
|
|
775
|
+
for (const text of allTexts) {
|
|
746
776
|
const idx = text.toLowerCase().indexOf(query);
|
|
747
777
|
if (idx !== -1) {
|
|
748
778
|
results.push({
|
|
@@ -754,9 +784,11 @@ export class FlowSearch extends LitElement {
|
|
|
754
784
|
matchStart: idx,
|
|
755
785
|
matchLength: query.length
|
|
756
786
|
});
|
|
787
|
+
found = true;
|
|
757
788
|
break;
|
|
758
789
|
}
|
|
759
790
|
}
|
|
791
|
+
if (found) continue;
|
|
760
792
|
}
|
|
761
793
|
}
|
|
762
794
|
|