@adatechnology/conversations-ui 0.1.0-rc.21 → 0.1.0-rc.23
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/dist/index.d.ts +237 -3
- package/dist/index.js +854 -0
- package/dist/preview/index.d.ts +23 -1
- package/dist/preview/index.js +48 -14
- package/dist/styles.css +559 -0
- package/package.json +1 -1
- package/src/index.ts +15 -0
- package/src/preview/ConversationSimulatorPanel.test.tsx +55 -0
- package/src/preview/ConversationSimulatorPanel.tsx +89 -0
- package/src/preview/createPreviewWebhookClient.test.ts +11 -4
- package/src/preview/index.ts +3 -0
- package/src/styles.css +542 -0
- package/src/workspace/BulkTemplateModal.tsx +130 -0
- package/src/workspace/ConversationPane.tsx +287 -0
- package/src/workspace/ConversationsInboxList.tsx +185 -0
- package/src/workspace/ConversationsWorkspace.tsx +306 -0
- package/src/workspace/index.ts +12 -0
- package/src/workspace/labels.test.ts +17 -0
- package/src/workspace/labels.ts +80 -0
- package/src/workspace/useConversationsInbox.ts +329 -0
package/dist/index.js
CHANGED
|
@@ -2586,6 +2586,854 @@ function useInboxActions() {
|
|
|
2586
2586
|
[api]
|
|
2587
2587
|
);
|
|
2588
2588
|
}
|
|
2589
|
+
|
|
2590
|
+
// src/workspace/ConversationsWorkspace.tsx
|
|
2591
|
+
import { useEffect as useEffect10, useMemo as useMemo5, useState as useState14 } from "react";
|
|
2592
|
+
|
|
2593
|
+
// src/workspace/BulkTemplateModal.tsx
|
|
2594
|
+
import { useEffect as useEffect7, useMemo as useMemo3, useState as useState11 } from "react";
|
|
2595
|
+
import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2596
|
+
function BulkTemplateModal({
|
|
2597
|
+
labels,
|
|
2598
|
+
expiredCount,
|
|
2599
|
+
sending,
|
|
2600
|
+
onClose,
|
|
2601
|
+
onSend
|
|
2602
|
+
}) {
|
|
2603
|
+
const context = useConversations();
|
|
2604
|
+
if (!context) {
|
|
2605
|
+
throw new Error("BulkTemplateModal requires an ancestor <ConversationsProvider>");
|
|
2606
|
+
}
|
|
2607
|
+
const { api } = context;
|
|
2608
|
+
const [templates, setTemplates] = useState11([]);
|
|
2609
|
+
const [search, setSearch] = useState11("");
|
|
2610
|
+
const [selected, setSelected] = useState11("");
|
|
2611
|
+
useEffect7(() => {
|
|
2612
|
+
let active = true;
|
|
2613
|
+
void api.listTemplates?.().then((loaded) => {
|
|
2614
|
+
if (active) setTemplates(loaded);
|
|
2615
|
+
}).catch(() => {
|
|
2616
|
+
});
|
|
2617
|
+
return () => {
|
|
2618
|
+
active = false;
|
|
2619
|
+
};
|
|
2620
|
+
}, [api]);
|
|
2621
|
+
const filtered = useMemo3(() => {
|
|
2622
|
+
const term = search.trim().toLowerCase();
|
|
2623
|
+
if (!term) return templates;
|
|
2624
|
+
return templates.filter((template) => template.name.toLowerCase().includes(term));
|
|
2625
|
+
}, [templates, search]);
|
|
2626
|
+
return /* @__PURE__ */ jsx21("div", { className: "cv-workspace-modal", role: "dialog", "aria-modal": "true", "aria-label": labels.templateModalTitle, children: /* @__PURE__ */ jsxs19("div", { className: "cv-workspace-modal__box", children: [
|
|
2627
|
+
/* @__PURE__ */ jsxs19("header", { className: "cv-workspace-modal__header", children: [
|
|
2628
|
+
/* @__PURE__ */ jsxs19("div", { children: [
|
|
2629
|
+
/* @__PURE__ */ jsx21("h3", { children: labels.templateModalTitle }),
|
|
2630
|
+
/* @__PURE__ */ jsx21("p", { children: labels.templateModalAvailable(templates.length) })
|
|
2631
|
+
] }),
|
|
2632
|
+
/* @__PURE__ */ jsx21("button", { type: "button", onClick: onClose, "aria-label": labels.templateModalCancel, children: "\u2715" })
|
|
2633
|
+
] }),
|
|
2634
|
+
/* @__PURE__ */ jsx21("div", { className: "cv-workspace-modal__search", children: /* @__PURE__ */ jsx21(
|
|
2635
|
+
"input",
|
|
2636
|
+
{
|
|
2637
|
+
type: "search",
|
|
2638
|
+
value: search,
|
|
2639
|
+
onChange: (event) => setSearch(event.target.value),
|
|
2640
|
+
placeholder: labels.templateModalSearch,
|
|
2641
|
+
autoFocus: true
|
|
2642
|
+
}
|
|
2643
|
+
) }),
|
|
2644
|
+
/* @__PURE__ */ jsxs19("div", { className: "cv-workspace-modal__list", children: [
|
|
2645
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx21("p", { className: "cv-workspace-empty", children: labels.templateModalEmpty }) : null,
|
|
2646
|
+
filtered.map((template) => /* @__PURE__ */ jsxs19(
|
|
2647
|
+
"button",
|
|
2648
|
+
{
|
|
2649
|
+
type: "button",
|
|
2650
|
+
onClick: () => setSelected(template.name === selected ? "" : template.name),
|
|
2651
|
+
"aria-pressed": template.name === selected,
|
|
2652
|
+
className: `cv-workspace-modal__item${template.name === selected ? " cv-workspace-modal__item--on" : ""}`,
|
|
2653
|
+
children: [
|
|
2654
|
+
/* @__PURE__ */ jsx21("span", { className: "cv-workspace-modal__item-name", children: template.name }),
|
|
2655
|
+
/* @__PURE__ */ jsxs19("span", { className: "cv-workspace-modal__item-meta", children: [
|
|
2656
|
+
template.language,
|
|
2657
|
+
template.category ? ` \xB7 ${template.category.toLowerCase()}` : ""
|
|
2658
|
+
] })
|
|
2659
|
+
]
|
|
2660
|
+
},
|
|
2661
|
+
template.name
|
|
2662
|
+
))
|
|
2663
|
+
] }),
|
|
2664
|
+
expiredCount === 0 ? /* @__PURE__ */ jsx21("p", { className: "cv-workspace-modal__warning", children: labels.templateModalNoneExpired }) : null,
|
|
2665
|
+
/* @__PURE__ */ jsxs19("footer", { className: "cv-workspace-modal__footer", children: [
|
|
2666
|
+
/* @__PURE__ */ jsx21("button", { type: "button", onClick: onClose, children: labels.templateModalCancel }),
|
|
2667
|
+
/* @__PURE__ */ jsx21(
|
|
2668
|
+
"button",
|
|
2669
|
+
{
|
|
2670
|
+
type: "button",
|
|
2671
|
+
disabled: sending || expiredCount === 0,
|
|
2672
|
+
onClick: () => onSend(selected || void 0),
|
|
2673
|
+
children: sending ? labels.templateModalSending : labels.templateModalSend(expiredCount)
|
|
2674
|
+
}
|
|
2675
|
+
)
|
|
2676
|
+
] })
|
|
2677
|
+
] }) });
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// src/workspace/ConversationPane.tsx
|
|
2681
|
+
import { useEffect as useEffect8, useState as useState12 } from "react";
|
|
2682
|
+
import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2683
|
+
function ConversationPane({
|
|
2684
|
+
conversation,
|
|
2685
|
+
now,
|
|
2686
|
+
busy,
|
|
2687
|
+
labels,
|
|
2688
|
+
onTakeover,
|
|
2689
|
+
onReturnToBot,
|
|
2690
|
+
onFinish,
|
|
2691
|
+
onBack,
|
|
2692
|
+
extraUtilities,
|
|
2693
|
+
contextEntriesOf,
|
|
2694
|
+
quickReplies,
|
|
2695
|
+
quickReplyVariablesFor,
|
|
2696
|
+
flowLabel,
|
|
2697
|
+
onDownload,
|
|
2698
|
+
requireTakeoverToReply,
|
|
2699
|
+
messageSelection,
|
|
2700
|
+
initialComposerText,
|
|
2701
|
+
renderAboveTranscript,
|
|
2702
|
+
onAttach
|
|
2703
|
+
}) {
|
|
2704
|
+
const context = useConversations();
|
|
2705
|
+
if (!context) {
|
|
2706
|
+
throw new Error("ConversationPane requires an ancestor <ConversationsProvider>");
|
|
2707
|
+
}
|
|
2708
|
+
const { api } = context;
|
|
2709
|
+
const { messages, refetch } = useConversationMessages(conversation.id);
|
|
2710
|
+
const { context: conversationContext } = useConversationContext(conversation.id);
|
|
2711
|
+
const [documentsOpen, setDocumentsOpen] = useState12(false);
|
|
2712
|
+
const [sendFailure, setSendFailure] = useState12(void 0);
|
|
2713
|
+
const [selectedMessageIds, setSelectedMessageIds] = useState12(/* @__PURE__ */ new Set());
|
|
2714
|
+
const [draft, setDraft] = useState12(initialComposerText ?? "");
|
|
2715
|
+
useEffect8(() => {
|
|
2716
|
+
setSelectedMessageIds(/* @__PURE__ */ new Set());
|
|
2717
|
+
setDraft(initialComposerText ?? "");
|
|
2718
|
+
}, [conversation.id, initialComposerText]);
|
|
2719
|
+
function toggleMessageSelected(messageId) {
|
|
2720
|
+
setSelectedMessageIds((current) => {
|
|
2721
|
+
const next = new Set(current);
|
|
2722
|
+
if (next.has(messageId)) next.delete(messageId);
|
|
2723
|
+
else next.add(messageId);
|
|
2724
|
+
return next;
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2727
|
+
function copySelectedMessages() {
|
|
2728
|
+
const transcript = messages.filter((message) => selectedMessageIds.has(message.id)).map(
|
|
2729
|
+
(message) => `[${new Date(message.timestamp).toLocaleString()}] ${message.sender}: ${message.content ?? `(${message.type})`}`
|
|
2730
|
+
).join("\n");
|
|
2731
|
+
void navigator.clipboard.writeText(transcript);
|
|
2732
|
+
setSelectedMessageIds(/* @__PURE__ */ new Set());
|
|
2733
|
+
}
|
|
2734
|
+
const scroll = useScrollToLatestMessage({ conversationId: conversation.id, messageCount: messages.length });
|
|
2735
|
+
useConversationRealtime(conversation.id, () => {
|
|
2736
|
+
void refetch();
|
|
2737
|
+
});
|
|
2738
|
+
const blocked = isWindowBlocking(
|
|
2739
|
+
windowOf({ lastInboundAt: conversation.lastInboundAt, now, channel: conversation.channel })
|
|
2740
|
+
);
|
|
2741
|
+
async function runSend(action, fallback) {
|
|
2742
|
+
setSendFailure(void 0);
|
|
2743
|
+
try {
|
|
2744
|
+
await action();
|
|
2745
|
+
await refetch();
|
|
2746
|
+
} catch (error) {
|
|
2747
|
+
setSendFailure(error instanceof Error ? error.message : fallback);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
async function handleSend(text) {
|
|
2751
|
+
await runSend(() => api.sendMessage(conversation.id, text), labels.sendFailure);
|
|
2752
|
+
}
|
|
2753
|
+
async function handleAttach(file) {
|
|
2754
|
+
if (!onAttach) return;
|
|
2755
|
+
await runSend(() => onAttach(file), labels.attachFailure);
|
|
2756
|
+
}
|
|
2757
|
+
function handleDownload() {
|
|
2758
|
+
downloadTextFile(
|
|
2759
|
+
buildTranscriptFilename(conversation.whatsappNumber, /* @__PURE__ */ new Date()),
|
|
2760
|
+
buildTranscriptText({
|
|
2761
|
+
messages,
|
|
2762
|
+
whatsappNumber: conversation.whatsappNumber,
|
|
2763
|
+
clientName: conversation.clientName
|
|
2764
|
+
})
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
const contextEntries = contextEntriesOf?.(conversationContext);
|
|
2768
|
+
const botOwnsConversation = Boolean(requireTakeoverToReply) && conversation.mode !== "human";
|
|
2769
|
+
return /* @__PURE__ */ jsxs20("div", { className: "cv-workspace-pane", children: [
|
|
2770
|
+
/* @__PURE__ */ jsx22(
|
|
2771
|
+
ConversationHeader,
|
|
2772
|
+
{
|
|
2773
|
+
conversation,
|
|
2774
|
+
busy,
|
|
2775
|
+
...onTakeover ? { onTakeover } : {},
|
|
2776
|
+
...onReturnToBot ? { onReturnToBot } : {},
|
|
2777
|
+
...onFinish ? { onFinish } : {},
|
|
2778
|
+
onDownload: onDownload ?? handleDownload,
|
|
2779
|
+
onBack,
|
|
2780
|
+
onOpenDocuments: () => setDocumentsOpen(!documentsOpen),
|
|
2781
|
+
documentsOpen,
|
|
2782
|
+
...extraUtilities ? { extraUtilities } : {}
|
|
2783
|
+
}
|
|
2784
|
+
),
|
|
2785
|
+
contextEntries && contextEntries.length > 0 || flowLabel ? /* @__PURE__ */ jsx22(ConversationContextPanel, { entries: contextEntries ?? [], ...flowLabel ? { flowLabel } : {} }) : null,
|
|
2786
|
+
/* @__PURE__ */ jsx22(ConversationDocumentsPanel, { conversationId: conversation.id, open: documentsOpen }),
|
|
2787
|
+
renderAboveTranscript?.(conversation),
|
|
2788
|
+
/* @__PURE__ */ jsx22(
|
|
2789
|
+
ConversationWallpaper,
|
|
2790
|
+
{
|
|
2791
|
+
ref: scroll.containerRef,
|
|
2792
|
+
onScroll: scroll.handleScroll,
|
|
2793
|
+
className: "cv-workspace-transcript",
|
|
2794
|
+
children: messages.map((message, index) => {
|
|
2795
|
+
const previous = index > 0 ? messages[index - 1] : void 0;
|
|
2796
|
+
const startsNewDay = !previous || new Date(message.timestamp).toDateString() !== new Date(previous.timestamp).toDateString();
|
|
2797
|
+
return /* @__PURE__ */ jsxs20("div", { children: [
|
|
2798
|
+
startsNewDay ? /* @__PURE__ */ jsx22(DateDivider, { iso: message.timestamp }) : null,
|
|
2799
|
+
/* @__PURE__ */ jsx22(
|
|
2800
|
+
MessageBubble,
|
|
2801
|
+
{
|
|
2802
|
+
message,
|
|
2803
|
+
isMine: message.direction === "outbound",
|
|
2804
|
+
isFirstInGroup: !previous || previous.sender !== message.sender,
|
|
2805
|
+
...messageSelection ? {
|
|
2806
|
+
isSelecting: selectedMessageIds.size > 0,
|
|
2807
|
+
isSelected: selectedMessageIds.has(message.id),
|
|
2808
|
+
onToggleSelect: () => toggleMessageSelected(message.id)
|
|
2809
|
+
} : {}
|
|
2810
|
+
}
|
|
2811
|
+
)
|
|
2812
|
+
] }, message.id);
|
|
2813
|
+
})
|
|
2814
|
+
}
|
|
2815
|
+
),
|
|
2816
|
+
messageSelection && selectedMessageIds.size > 0 ? /* @__PURE__ */ jsxs20("div", { className: "cv-workspace-selection", children: [
|
|
2817
|
+
/* @__PURE__ */ jsx22("span", { children: labels.messagesSelected(selectedMessageIds.size) }),
|
|
2818
|
+
/* @__PURE__ */ jsxs20("div", { className: "cv-workspace-selection__actions", children: [
|
|
2819
|
+
/* @__PURE__ */ jsx22("button", { type: "button", onClick: () => setSelectedMessageIds(/* @__PURE__ */ new Set()), children: labels.bulkClear }),
|
|
2820
|
+
/* @__PURE__ */ jsx22("button", { type: "button", onClick: copySelectedMessages, children: labels.copySelected })
|
|
2821
|
+
] })
|
|
2822
|
+
] }) : null,
|
|
2823
|
+
sendFailure ? /* @__PURE__ */ jsx22("p", { role: "alert", className: "cv-workspace-alert", children: sendFailure }) : null,
|
|
2824
|
+
blocked ? /* @__PURE__ */ jsx22(
|
|
2825
|
+
WindowExpiredNotice,
|
|
2826
|
+
{
|
|
2827
|
+
disabled: busy,
|
|
2828
|
+
onSendTemplate: () => void api.sendTemplate(conversation.id, {}).then(() => refetch())
|
|
2829
|
+
}
|
|
2830
|
+
) : botOwnsConversation ? (
|
|
2831
|
+
// Responder com a conversa no bot atropelaria o fluxo automático no meio de uma pergunta.
|
|
2832
|
+
/* @__PURE__ */ jsx22("p", { className: "cv-workspace-notice", children: labels.takeoverToReply })
|
|
2833
|
+
) : /* @__PURE__ */ jsx22(
|
|
2834
|
+
MessageComposer,
|
|
2835
|
+
{
|
|
2836
|
+
value: draft,
|
|
2837
|
+
onChange: setDraft,
|
|
2838
|
+
onSend: (text) => void handleSend(text),
|
|
2839
|
+
...onAttach ? { onAttach: (file) => void handleAttach(file) } : {},
|
|
2840
|
+
placeholder: labels.composerPlaceholder,
|
|
2841
|
+
...quickReplies ? { quickReplies } : {},
|
|
2842
|
+
...quickReplyVariablesFor ? { quickReplyVariables: quickReplyVariablesFor(conversation, conversationContext) } : {}
|
|
2843
|
+
}
|
|
2844
|
+
)
|
|
2845
|
+
] });
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
// src/workspace/ConversationsInboxList.tsx
|
|
2849
|
+
import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2850
|
+
function ConversationsInboxList({
|
|
2851
|
+
inbox,
|
|
2852
|
+
labels,
|
|
2853
|
+
className,
|
|
2854
|
+
renderFilters,
|
|
2855
|
+
renderBulkActions,
|
|
2856
|
+
renderRow,
|
|
2857
|
+
onSendTemplateToSelected
|
|
2858
|
+
}) {
|
|
2859
|
+
const hasSelection = inbox.selectedIds.size > 0;
|
|
2860
|
+
return /* @__PURE__ */ jsxs21("aside", { className, children: [
|
|
2861
|
+
hasSelection ? /* @__PURE__ */ jsxs21("div", { className: "cv-workspace-bulk", children: [
|
|
2862
|
+
/* @__PURE__ */ jsx23("span", { className: "cv-workspace-bulk__count", children: labels.bulkSelected(inbox.selectedIds.size) }),
|
|
2863
|
+
/* @__PURE__ */ jsxs21("div", { className: "cv-workspace-bulk__actions", children: [
|
|
2864
|
+
/* @__PURE__ */ jsx23("button", { type: "button", onClick: inbox.clearBulkSelection, children: labels.bulkClear }),
|
|
2865
|
+
onSendTemplateToSelected ? /* @__PURE__ */ jsx23("button", { type: "button", onClick: onSendTemplateToSelected, disabled: inbox.busy, children: labels.bulkTemplate }) : null,
|
|
2866
|
+
inbox.canFinalize ? /* @__PURE__ */ jsx23(
|
|
2867
|
+
"button",
|
|
2868
|
+
{
|
|
2869
|
+
type: "button",
|
|
2870
|
+
disabled: inbox.busy,
|
|
2871
|
+
onClick: () => {
|
|
2872
|
+
if (window.confirm(labels.bulkFinalizeConfirm(inbox.selectedIds.size))) {
|
|
2873
|
+
void inbox.finalizeSelected();
|
|
2874
|
+
}
|
|
2875
|
+
},
|
|
2876
|
+
children: labels.bulkFinalize
|
|
2877
|
+
}
|
|
2878
|
+
) : null,
|
|
2879
|
+
renderBulkActions?.(inbox)
|
|
2880
|
+
] })
|
|
2881
|
+
] }) : null,
|
|
2882
|
+
/* @__PURE__ */ jsxs21("div", { className: "cv-workspace-filters", children: [
|
|
2883
|
+
/* @__PURE__ */ jsx23(
|
|
2884
|
+
"input",
|
|
2885
|
+
{
|
|
2886
|
+
type: "search",
|
|
2887
|
+
value: inbox.search,
|
|
2888
|
+
onChange: (event) => inbox.setSearch(event.target.value),
|
|
2889
|
+
placeholder: labels.search,
|
|
2890
|
+
className: "cv-workspace-search"
|
|
2891
|
+
}
|
|
2892
|
+
),
|
|
2893
|
+
/* @__PURE__ */ jsxs21("div", { className: "cv-workspace-chips", children: [
|
|
2894
|
+
/* @__PURE__ */ jsx23("span", { className: "cv-workspace-chips__legend", children: labels.windowLegend }),
|
|
2895
|
+
WINDOW_FILTERS.map((filter) => /* @__PURE__ */ jsxs21(
|
|
2896
|
+
"button",
|
|
2897
|
+
{
|
|
2898
|
+
type: "button",
|
|
2899
|
+
onClick: () => inbox.setWindowFilter(filter.value),
|
|
2900
|
+
"aria-pressed": inbox.windowFilter === filter.value,
|
|
2901
|
+
className: `cv-workspace-chip${inbox.windowFilter === filter.value ? " cv-workspace-chip--on" : ""}`,
|
|
2902
|
+
children: [
|
|
2903
|
+
filter.dotClass ? /* @__PURE__ */ jsx23("span", { className: `cv-workspace-dot ${filter.dotClass}` }) : null,
|
|
2904
|
+
filter.label
|
|
2905
|
+
]
|
|
2906
|
+
},
|
|
2907
|
+
filter.value
|
|
2908
|
+
))
|
|
2909
|
+
] }),
|
|
2910
|
+
inbox.channelFilters.length > 0 ? /* @__PURE__ */ jsxs21("div", { className: "cv-workspace-chips", children: [
|
|
2911
|
+
/* @__PURE__ */ jsx23("span", { className: "cv-workspace-chips__legend", children: labels.channelLegend }),
|
|
2912
|
+
inbox.channelFilters.map((filter) => /* @__PURE__ */ jsxs21(
|
|
2913
|
+
"button",
|
|
2914
|
+
{
|
|
2915
|
+
type: "button",
|
|
2916
|
+
onClick: () => inbox.setChannelFilter(filter.value),
|
|
2917
|
+
"aria-pressed": inbox.channelFilter === filter.value,
|
|
2918
|
+
className: `cv-workspace-chip${inbox.channelFilter === filter.value ? " cv-workspace-chip--on" : ""}`,
|
|
2919
|
+
children: [
|
|
2920
|
+
filter.value === CHANNEL_FILTER_ALL ? null : /* @__PURE__ */ jsx23(ChannelIcon, { channel: filter.value }),
|
|
2921
|
+
filter.label
|
|
2922
|
+
]
|
|
2923
|
+
},
|
|
2924
|
+
filter.value
|
|
2925
|
+
))
|
|
2926
|
+
] }) : null,
|
|
2927
|
+
renderFilters?.(inbox),
|
|
2928
|
+
/* @__PURE__ */ jsxs21("label", { className: "cv-workspace-selectall", children: [
|
|
2929
|
+
/* @__PURE__ */ jsx23("input", { type: "checkbox", checked: inbox.allOnPageSelected, onChange: inbox.toggleSelectAllOnPage }),
|
|
2930
|
+
labels.selectAll
|
|
2931
|
+
] })
|
|
2932
|
+
] }),
|
|
2933
|
+
/* @__PURE__ */ jsxs21("div", { className: "cv-workspace-rows", children: [
|
|
2934
|
+
inbox.pageConversations.map(
|
|
2935
|
+
(conversation) => renderRow ? /* @__PURE__ */ jsx23("div", { children: renderRow(conversation) }, conversation.id) : /* @__PURE__ */ jsx23(
|
|
2936
|
+
ConversationRow,
|
|
2937
|
+
{
|
|
2938
|
+
conversation,
|
|
2939
|
+
active: conversation.id === inbox.selectedId,
|
|
2940
|
+
selected: inbox.selectedIds.has(conversation.id),
|
|
2941
|
+
now: inbox.now,
|
|
2942
|
+
busy: inbox.busy,
|
|
2943
|
+
onOpen: () => inbox.selectConversation(conversation.id),
|
|
2944
|
+
onToggleSelected: () => inbox.toggleSelected(conversation.id),
|
|
2945
|
+
onTakeover: () => void inbox.takeover(conversation.id)
|
|
2946
|
+
},
|
|
2947
|
+
conversation.id
|
|
2948
|
+
)
|
|
2949
|
+
),
|
|
2950
|
+
!inbox.loading && inbox.filteredCount === 0 ? /* @__PURE__ */ jsx23("p", { className: "cv-workspace-empty", children: labels.emptyList }) : null
|
|
2951
|
+
] }),
|
|
2952
|
+
/* @__PURE__ */ jsxs21("div", { className: "cv-workspace-pager", children: [
|
|
2953
|
+
/* @__PURE__ */ jsx23("span", { children: labels.rangeOf(inbox.firstOnPage, inbox.lastOnPage, inbox.filteredCount) }),
|
|
2954
|
+
/* @__PURE__ */ jsxs21("div", { className: "cv-workspace-pager__buttons", children: [
|
|
2955
|
+
/* @__PURE__ */ jsx23("button", { type: "button", onClick: () => inbox.goToPage(1), disabled: inbox.page === 1, "aria-label": "Primeira p\xE1gina", children: "\xAB" }),
|
|
2956
|
+
/* @__PURE__ */ jsx23(
|
|
2957
|
+
"button",
|
|
2958
|
+
{
|
|
2959
|
+
type: "button",
|
|
2960
|
+
onClick: () => inbox.goToPage(inbox.page - 1),
|
|
2961
|
+
disabled: inbox.page === 1,
|
|
2962
|
+
"aria-label": "P\xE1gina anterior",
|
|
2963
|
+
children: "\u2039"
|
|
2964
|
+
}
|
|
2965
|
+
),
|
|
2966
|
+
/* @__PURE__ */ jsx23("span", { children: labels.pageOf(inbox.page, inbox.pageCount) }),
|
|
2967
|
+
/* @__PURE__ */ jsx23(
|
|
2968
|
+
"button",
|
|
2969
|
+
{
|
|
2970
|
+
type: "button",
|
|
2971
|
+
onClick: () => inbox.goToPage(inbox.page + 1),
|
|
2972
|
+
disabled: inbox.page === inbox.pageCount,
|
|
2973
|
+
"aria-label": "Pr\xF3xima p\xE1gina",
|
|
2974
|
+
children: "\u203A"
|
|
2975
|
+
}
|
|
2976
|
+
),
|
|
2977
|
+
/* @__PURE__ */ jsx23(
|
|
2978
|
+
"button",
|
|
2979
|
+
{
|
|
2980
|
+
type: "button",
|
|
2981
|
+
onClick: () => inbox.goToPage(inbox.pageCount),
|
|
2982
|
+
disabled: inbox.page === inbox.pageCount,
|
|
2983
|
+
"aria-label": "\xDAltima p\xE1gina",
|
|
2984
|
+
children: "\xBB"
|
|
2985
|
+
}
|
|
2986
|
+
)
|
|
2987
|
+
] })
|
|
2988
|
+
] })
|
|
2989
|
+
] });
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
// src/workspace/labels.ts
|
|
2993
|
+
var DEFAULT_CONVERSATIONS_WORKSPACE_LABELS = {
|
|
2994
|
+
title: "Conversas",
|
|
2995
|
+
conversations: "conversas",
|
|
2996
|
+
waiting: "aguardando",
|
|
2997
|
+
unread: "n\xE3o lidas",
|
|
2998
|
+
waitingOnly: "Aguardando atendimento",
|
|
2999
|
+
markSelectedAsRead: "Marcar selecionadas como lidas",
|
|
3000
|
+
search: "Buscar conversa...",
|
|
3001
|
+
windowLegend: "Janela:",
|
|
3002
|
+
channelLegend: "Canal:",
|
|
3003
|
+
selectAll: "Selecionar todas",
|
|
3004
|
+
emptyList: "Nenhuma conversa encontrada.",
|
|
3005
|
+
emptyDetail: "Selecione uma conversa.",
|
|
3006
|
+
bulkSelected: (count) => `${count} selecionada${count === 1 ? "" : "s"}`,
|
|
3007
|
+
bulkClear: "Limpar sele\xE7\xE3o",
|
|
3008
|
+
bulkFinalize: "Finalizar",
|
|
3009
|
+
bulkFinalizeConfirm: (count) => `Finalizar ${count} conversa${count === 1 ? "" : "s"}?`,
|
|
3010
|
+
bulkTemplate: "Enviar template",
|
|
3011
|
+
markAllAsRead: "Marcar todas como lidas",
|
|
3012
|
+
templateModalTitle: "Escolher template",
|
|
3013
|
+
templateModalSearch: "Buscar template...",
|
|
3014
|
+
templateModalEmpty: "Nenhum template encontrado.",
|
|
3015
|
+
templateModalCancel: "Cancelar",
|
|
3016
|
+
templateModalSending: "Enviando\u2026",
|
|
3017
|
+
templateModalNoneExpired: "Nenhuma das conversas selecionadas est\xE1 fora da janela de 24h.",
|
|
3018
|
+
templateModalAvailable: (count) => `${count} template${count === 1 ? "" : "s"} dispon\xEDve${count === 1 ? "l" : "is"}`,
|
|
3019
|
+
templateModalSend: (count) => `Enviar para ${count}`,
|
|
3020
|
+
messagesSelected: (count) => `${count} mensagem${count === 1 ? "" : "s"} selecionada${count === 1 ? "" : "s"}`,
|
|
3021
|
+
copySelected: "Copiar",
|
|
3022
|
+
composerPlaceholder: "Responder como atendente\u2026",
|
|
3023
|
+
attachFailure: "Falha ao enviar o arquivo.",
|
|
3024
|
+
sendFailure: "Falha ao enviar a mensagem.",
|
|
3025
|
+
takeoverToReply: "Assuma o atendimento para responder diretamente ao cliente.",
|
|
3026
|
+
signIn: "Entrar no painel",
|
|
3027
|
+
pageOf: (page, pageCount) => `${page} / ${pageCount}`,
|
|
3028
|
+
rangeOf: (first, last, total) => total === 0 ? `0 de 0` : `${first}\u2013${last} de ${total}`
|
|
3029
|
+
};
|
|
3030
|
+
|
|
3031
|
+
// src/workspace/useConversationsInbox.ts
|
|
3032
|
+
import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo4, useRef as useRef7, useState as useState13 } from "react";
|
|
3033
|
+
var CONVERSATIONS_PER_PAGE = 50;
|
|
3034
|
+
function defaultDescribeFailure(error) {
|
|
3035
|
+
const status = error.status;
|
|
3036
|
+
if (status === 401 || status === 403) {
|
|
3037
|
+
return "Sess\xE3o expirada nesta aba \u2014 entre no painel de novo para ver as conversas.";
|
|
3038
|
+
}
|
|
3039
|
+
return error instanceof Error && error.message ? `N\xE3o foi poss\xEDvel carregar as conversas: ${error.message}` : "N\xE3o foi poss\xEDvel carregar as conversas.";
|
|
3040
|
+
}
|
|
3041
|
+
function useConversationsInbox(params = {}) {
|
|
3042
|
+
const context = useConversations();
|
|
3043
|
+
if (!context) {
|
|
3044
|
+
throw new Error("useConversationsInbox requires an ancestor <ConversationsProvider>");
|
|
3045
|
+
}
|
|
3046
|
+
const { api } = context;
|
|
3047
|
+
const perPage = params.perPage ?? CONVERSATIONS_PER_PAGE;
|
|
3048
|
+
const markReadOnOpen = params.markReadOnOpen ?? true;
|
|
3049
|
+
const describeFailure = params.describeFailure ?? defaultDescribeFailure;
|
|
3050
|
+
const [waitingOnly, setWaitingOnly] = useState13(false);
|
|
3051
|
+
const [windowFilter, setWindowFilter] = useState13(CONVERSATION_WINDOW.ALL);
|
|
3052
|
+
const [channelFilter, setChannelFilter] = useState13(CHANNEL_FILTER_ALL);
|
|
3053
|
+
const [search, setSearch] = useState13("");
|
|
3054
|
+
const [page, setPage] = useState13(1);
|
|
3055
|
+
const [selectedId, setSelectedId] = useState13(void 0);
|
|
3056
|
+
const [selectedIds, setSelectedIds] = useState13(/* @__PURE__ */ new Set());
|
|
3057
|
+
const [busy, setBusy] = useState13(false);
|
|
3058
|
+
const { conversations, total, loading, error, refetch } = useConversationList({
|
|
3059
|
+
...waitingOnly ? { waitingHuman: true } : {},
|
|
3060
|
+
...search ? { search } : {},
|
|
3061
|
+
...params.filters ? { filters: params.filters } : {},
|
|
3062
|
+
...params.serverPaginated ? { page, limit: perPage } : {}
|
|
3063
|
+
});
|
|
3064
|
+
useGlobalRealtime(
|
|
3065
|
+
useCallback7(() => {
|
|
3066
|
+
void refetch();
|
|
3067
|
+
}, [refetch])
|
|
3068
|
+
);
|
|
3069
|
+
const now = useMemo4(() => Date.now(), [conversations]);
|
|
3070
|
+
const filtered = useMemo4(
|
|
3071
|
+
() => conversations.filter(
|
|
3072
|
+
(conversation) => channelFilter === CHANNEL_FILTER_ALL || (conversation.channel ?? DEFAULT_CONVERSATION_CHANNEL) === channelFilter
|
|
3073
|
+
).filter(
|
|
3074
|
+
(conversation) => windowFilter === CONVERSATION_WINDOW.ALL || windowOf({ lastInboundAt: conversation.lastInboundAt, now, channel: conversation.channel }) === windowFilter
|
|
3075
|
+
),
|
|
3076
|
+
[conversations, windowFilter, channelFilter, now]
|
|
3077
|
+
);
|
|
3078
|
+
const totalForPaging = params.serverPaginated ? total : filtered.length;
|
|
3079
|
+
const pageCount = Math.max(1, Math.ceil(totalForPaging / perPage));
|
|
3080
|
+
const currentPage = Math.min(page, pageCount);
|
|
3081
|
+
const pageConversations = params.serverPaginated ? filtered : filtered.slice((currentPage - 1) * perPage, currentPage * perPage);
|
|
3082
|
+
useEffect9(() => {
|
|
3083
|
+
setPage(1);
|
|
3084
|
+
}, [search, waitingOnly, windowFilter, channelFilter]);
|
|
3085
|
+
useEffect9(() => {
|
|
3086
|
+
if (selectedId && !conversations.some((conversation) => conversation.id === selectedId)) {
|
|
3087
|
+
setSelectedId(void 0);
|
|
3088
|
+
}
|
|
3089
|
+
}, [conversations, selectedId]);
|
|
3090
|
+
const lastMarkReadAttempt = useRef7(void 0);
|
|
3091
|
+
useEffect9(() => {
|
|
3092
|
+
if (!markReadOnOpen || !selectedId) return;
|
|
3093
|
+
const opened = conversations.find((conversation) => conversation.id === selectedId);
|
|
3094
|
+
if (!opened || opened.unread === 0) return;
|
|
3095
|
+
const attempt = `${selectedId}:${opened.unread}`;
|
|
3096
|
+
if (lastMarkReadAttempt.current === attempt) return;
|
|
3097
|
+
lastMarkReadAttempt.current = attempt;
|
|
3098
|
+
void api.markRead(selectedId).then(() => refetch()).catch(() => {
|
|
3099
|
+
});
|
|
3100
|
+
}, [markReadOnOpen, selectedId, conversations, refetch, api]);
|
|
3101
|
+
const toggleSelected = useCallback7((conversationId) => {
|
|
3102
|
+
setSelectedIds((current) => {
|
|
3103
|
+
const next = new Set(current);
|
|
3104
|
+
if (next.has(conversationId)) next.delete(conversationId);
|
|
3105
|
+
else next.add(conversationId);
|
|
3106
|
+
return next;
|
|
3107
|
+
});
|
|
3108
|
+
}, []);
|
|
3109
|
+
const pageIds = pageConversations.map((conversation) => conversation.id);
|
|
3110
|
+
const allOnPageSelected = pageIds.length > 0 && pageIds.every((id) => selectedIds.has(id));
|
|
3111
|
+
const toggleSelectAllOnPage = useCallback7(() => {
|
|
3112
|
+
setSelectedIds((current) => {
|
|
3113
|
+
const next = new Set(current);
|
|
3114
|
+
if (allOnPageSelected) pageIds.forEach((id) => next.delete(id));
|
|
3115
|
+
else pageIds.forEach((id) => next.add(id));
|
|
3116
|
+
return next;
|
|
3117
|
+
});
|
|
3118
|
+
}, [allOnPageSelected, pageIds]);
|
|
3119
|
+
const runOnSelection = useCallback7(
|
|
3120
|
+
async (action) => {
|
|
3121
|
+
setBusy(true);
|
|
3122
|
+
try {
|
|
3123
|
+
await Promise.all([...selectedIds].map(action));
|
|
3124
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
3125
|
+
await refetch();
|
|
3126
|
+
} finally {
|
|
3127
|
+
setBusy(false);
|
|
3128
|
+
}
|
|
3129
|
+
},
|
|
3130
|
+
[selectedIds, refetch]
|
|
3131
|
+
);
|
|
3132
|
+
const runConversationAction = useCallback7(
|
|
3133
|
+
async (action, conversationId) => {
|
|
3134
|
+
if (!action) return;
|
|
3135
|
+
setBusy(true);
|
|
3136
|
+
try {
|
|
3137
|
+
await action(conversationId);
|
|
3138
|
+
await refetch();
|
|
3139
|
+
} finally {
|
|
3140
|
+
setBusy(false);
|
|
3141
|
+
}
|
|
3142
|
+
},
|
|
3143
|
+
[refetch]
|
|
3144
|
+
);
|
|
3145
|
+
return {
|
|
3146
|
+
conversations,
|
|
3147
|
+
pageConversations,
|
|
3148
|
+
selectedConversation: conversations.find((conversation) => conversation.id === selectedId),
|
|
3149
|
+
loading,
|
|
3150
|
+
now,
|
|
3151
|
+
totalCount: conversations.length,
|
|
3152
|
+
unreadCount: conversations.reduce((total2, conversation) => total2 + conversation.unread, 0),
|
|
3153
|
+
waitingCount: conversations.filter((conversation) => conversation.waitingHuman).length,
|
|
3154
|
+
filteredCount: totalForPaging,
|
|
3155
|
+
expiredSelectedCount: conversations.filter(
|
|
3156
|
+
(conversation) => selectedIds.has(conversation.id) && isWindowBlocking(windowOf({ lastInboundAt: conversation.lastInboundAt, now, channel: conversation.channel }))
|
|
3157
|
+
).length,
|
|
3158
|
+
page: currentPage,
|
|
3159
|
+
pageCount,
|
|
3160
|
+
firstOnPage: (currentPage - 1) * perPage + 1,
|
|
3161
|
+
lastOnPage: Math.min(currentPage * perPage, totalForPaging),
|
|
3162
|
+
selectedId,
|
|
3163
|
+
selectedIds,
|
|
3164
|
+
allOnPageSelected,
|
|
3165
|
+
waitingOnly,
|
|
3166
|
+
windowFilter,
|
|
3167
|
+
channelFilter,
|
|
3168
|
+
channelFilters: channelFiltersFor(conversations),
|
|
3169
|
+
search,
|
|
3170
|
+
busy,
|
|
3171
|
+
loadFailure: error ? describeFailure(error) : void 0,
|
|
3172
|
+
canTakeover: Boolean(api.takeover),
|
|
3173
|
+
canFinalize: Boolean(api.finalize),
|
|
3174
|
+
canListTemplates: Boolean(api.listTemplates),
|
|
3175
|
+
canMarkAllRead: Boolean(api.markAllRead),
|
|
3176
|
+
refetch,
|
|
3177
|
+
selectConversation: setSelectedId,
|
|
3178
|
+
clearSelection: () => setSelectedId(void 0),
|
|
3179
|
+
toggleSelected,
|
|
3180
|
+
toggleSelectAllOnPage,
|
|
3181
|
+
clearBulkSelection: () => setSelectedIds(/* @__PURE__ */ new Set()),
|
|
3182
|
+
setWaitingOnly,
|
|
3183
|
+
setWindowFilter,
|
|
3184
|
+
setChannelFilter,
|
|
3185
|
+
setSearch,
|
|
3186
|
+
goToPage: setPage,
|
|
3187
|
+
markSelectedAsRead: () => runOnSelection((conversationId) => api.markRead(conversationId)),
|
|
3188
|
+
markAllAsRead: async () => {
|
|
3189
|
+
if (!api.markAllRead) return;
|
|
3190
|
+
setBusy(true);
|
|
3191
|
+
try {
|
|
3192
|
+
await api.markAllRead();
|
|
3193
|
+
await refetch();
|
|
3194
|
+
} finally {
|
|
3195
|
+
setBusy(false);
|
|
3196
|
+
}
|
|
3197
|
+
},
|
|
3198
|
+
takeover: (conversationId) => runConversationAction(api.takeover, conversationId),
|
|
3199
|
+
releaseToBot: (conversationId) => runConversationAction(api.release, conversationId),
|
|
3200
|
+
finalize: (conversationId) => runConversationAction(api.finalize, conversationId),
|
|
3201
|
+
finalizeSelected: () => runOnSelection(async (conversationId) => {
|
|
3202
|
+
await api.finalize?.(conversationId);
|
|
3203
|
+
}),
|
|
3204
|
+
sendTemplateToSelected: (templateName) => runOnSelection(async (conversationId) => {
|
|
3205
|
+
await api.sendTemplate(conversationId, templateName ? { templateName } : {});
|
|
3206
|
+
})
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
// src/workspace/ConversationsWorkspace.tsx
|
|
3211
|
+
import { Fragment as Fragment4, jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
3212
|
+
function ConversationsWorkspace({
|
|
3213
|
+
labels: labelsOverride,
|
|
3214
|
+
filters,
|
|
3215
|
+
perPage,
|
|
3216
|
+
markReadOnOpen,
|
|
3217
|
+
serverPaginated,
|
|
3218
|
+
initialConversationId,
|
|
3219
|
+
initialWhatsappNumber,
|
|
3220
|
+
simulator,
|
|
3221
|
+
quickReplies,
|
|
3222
|
+
quickReplyVariablesFor,
|
|
3223
|
+
flowLabelOf,
|
|
3224
|
+
onDownload,
|
|
3225
|
+
requireTakeoverToReply,
|
|
3226
|
+
messageSelection,
|
|
3227
|
+
initialComposerText,
|
|
3228
|
+
contextEntriesOf,
|
|
3229
|
+
onAttach,
|
|
3230
|
+
extraUtilitiesFor,
|
|
3231
|
+
renderFilters,
|
|
3232
|
+
renderBulkActions,
|
|
3233
|
+
renderRow,
|
|
3234
|
+
renderAboveTranscript,
|
|
3235
|
+
renderHeaderActions,
|
|
3236
|
+
onSendTemplateToSelected,
|
|
3237
|
+
signInHref,
|
|
3238
|
+
className
|
|
3239
|
+
}) {
|
|
3240
|
+
const labels = { ...DEFAULT_CONVERSATIONS_WORKSPACE_LABELS, ...labelsOverride };
|
|
3241
|
+
const inbox = useConversationsInbox({
|
|
3242
|
+
...filters ? { filters } : {},
|
|
3243
|
+
...perPage ? { perPage } : {},
|
|
3244
|
+
...markReadOnOpen === void 0 ? {} : { markReadOnOpen },
|
|
3245
|
+
...serverPaginated ? { serverPaginated } : {}
|
|
3246
|
+
});
|
|
3247
|
+
const [simulatorOpen, setSimulatorOpen] = useState14(false);
|
|
3248
|
+
const [templateModalOpen, setTemplateModalOpen] = useState14(false);
|
|
3249
|
+
const [openedFromLink, setOpenedFromLink] = useState14(void 0);
|
|
3250
|
+
useEffect10(() => {
|
|
3251
|
+
const link = initialConversationId ?? initialWhatsappNumber;
|
|
3252
|
+
if (!link || openedFromLink === link) return;
|
|
3253
|
+
const target = inbox.conversations.find(
|
|
3254
|
+
(conversation) => conversation.id === initialConversationId || conversation.whatsappNumber === initialWhatsappNumber
|
|
3255
|
+
);
|
|
3256
|
+
if (!target) return;
|
|
3257
|
+
inbox.selectConversation(target.id);
|
|
3258
|
+
setOpenedFromLink(link);
|
|
3259
|
+
}, [initialConversationId, initialWhatsappNumber, openedFromLink, inbox]);
|
|
3260
|
+
const selected = inbox.selectedConversation;
|
|
3261
|
+
const simulatorEnabled = Boolean(simulator && (simulator.enabled ?? true));
|
|
3262
|
+
const showSimulator = simulatorEnabled && simulatorOpen && Boolean(selected);
|
|
3263
|
+
const paneUtilities = useMemo5(() => {
|
|
3264
|
+
if (!selected) return void 0;
|
|
3265
|
+
const fromProduct = extraUtilitiesFor?.(selected) ?? [];
|
|
3266
|
+
if (!simulatorEnabled) return fromProduct;
|
|
3267
|
+
return [
|
|
3268
|
+
...fromProduct,
|
|
3269
|
+
{
|
|
3270
|
+
key: "simulator",
|
|
3271
|
+
icon: simulator?.icon ?? "\u{1F9EA}",
|
|
3272
|
+
label: simulator?.label ?? "Simular cliente",
|
|
3273
|
+
active: simulatorOpen,
|
|
3274
|
+
run: () => setSimulatorOpen((open) => !open)
|
|
3275
|
+
}
|
|
3276
|
+
];
|
|
3277
|
+
}, [selected, extraUtilitiesFor, simulatorEnabled, simulator, simulatorOpen]);
|
|
3278
|
+
return /* @__PURE__ */ jsxs22("div", { className: `cv-workspace${className ? ` ${className}` : ""}`, children: [
|
|
3279
|
+
/* @__PURE__ */ jsxs22("header", { className: "cv-workspace-header", children: [
|
|
3280
|
+
/* @__PURE__ */ jsxs22("div", { className: "cv-workspace-header__titles", children: [
|
|
3281
|
+
/* @__PURE__ */ jsx24("h1", { children: labels.title }),
|
|
3282
|
+
/* @__PURE__ */ jsxs22("p", { children: [
|
|
3283
|
+
/* @__PURE__ */ jsxs22("span", { title: labels.conversations, children: [
|
|
3284
|
+
"\u{1F4AC} ",
|
|
3285
|
+
inbox.totalCount,
|
|
3286
|
+
/* @__PURE__ */ jsxs22("span", { className: "cv-only-wide", children: [
|
|
3287
|
+
" ",
|
|
3288
|
+
labels.conversations
|
|
3289
|
+
] })
|
|
3290
|
+
] }),
|
|
3291
|
+
/* @__PURE__ */ jsxs22("span", { title: labels.waiting, className: "cv-workspace-header__waiting", children: [
|
|
3292
|
+
"\u23F3 ",
|
|
3293
|
+
inbox.waitingCount,
|
|
3294
|
+
/* @__PURE__ */ jsxs22("span", { className: "cv-only-wide", children: [
|
|
3295
|
+
" ",
|
|
3296
|
+
labels.waiting
|
|
3297
|
+
] })
|
|
3298
|
+
] }),
|
|
3299
|
+
/* @__PURE__ */ jsxs22("span", { title: labels.unread, children: [
|
|
3300
|
+
"\u2709\uFE0F ",
|
|
3301
|
+
inbox.unreadCount,
|
|
3302
|
+
/* @__PURE__ */ jsxs22("span", { className: "cv-only-wide", children: [
|
|
3303
|
+
" ",
|
|
3304
|
+
labels.unread
|
|
3305
|
+
] })
|
|
3306
|
+
] })
|
|
3307
|
+
] })
|
|
3308
|
+
] }),
|
|
3309
|
+
/* @__PURE__ */ jsxs22("div", { className: "cv-workspace-header__actions", children: [
|
|
3310
|
+
/* @__PURE__ */ jsxs22(
|
|
3311
|
+
"button",
|
|
3312
|
+
{
|
|
3313
|
+
type: "button",
|
|
3314
|
+
onClick: () => inbox.setWaitingOnly(!inbox.waitingOnly),
|
|
3315
|
+
"aria-pressed": inbox.waitingOnly,
|
|
3316
|
+
title: labels.waitingOnly,
|
|
3317
|
+
className: inbox.waitingOnly ? "cv-workspace-toggle cv-workspace-toggle--on" : "cv-workspace-toggle",
|
|
3318
|
+
children: [
|
|
3319
|
+
"\u23F3",
|
|
3320
|
+
/* @__PURE__ */ jsxs22("span", { className: "cv-only-wide", children: [
|
|
3321
|
+
" ",
|
|
3322
|
+
labels.waitingOnly
|
|
3323
|
+
] })
|
|
3324
|
+
]
|
|
3325
|
+
}
|
|
3326
|
+
),
|
|
3327
|
+
/* @__PURE__ */ jsxs22(
|
|
3328
|
+
"button",
|
|
3329
|
+
{
|
|
3330
|
+
type: "button",
|
|
3331
|
+
onClick: () => void inbox.markSelectedAsRead(),
|
|
3332
|
+
disabled: inbox.selectedIds.size === 0 || inbox.busy,
|
|
3333
|
+
title: labels.markSelectedAsRead,
|
|
3334
|
+
"aria-label": labels.markSelectedAsRead,
|
|
3335
|
+
className: "cv-workspace-toggle",
|
|
3336
|
+
children: [
|
|
3337
|
+
"\u2713",
|
|
3338
|
+
/* @__PURE__ */ jsxs22("span", { className: "cv-only-wide", children: [
|
|
3339
|
+
" ",
|
|
3340
|
+
labels.markSelectedAsRead
|
|
3341
|
+
] }),
|
|
3342
|
+
inbox.selectedIds.size > 0 ? ` (${inbox.selectedIds.size})` : ""
|
|
3343
|
+
]
|
|
3344
|
+
}
|
|
3345
|
+
),
|
|
3346
|
+
inbox.canMarkAllRead && inbox.unreadCount > 0 ? /* @__PURE__ */ jsxs22(
|
|
3347
|
+
"button",
|
|
3348
|
+
{
|
|
3349
|
+
type: "button",
|
|
3350
|
+
onClick: () => void inbox.markAllAsRead(),
|
|
3351
|
+
disabled: inbox.busy,
|
|
3352
|
+
title: labels.markAllAsRead,
|
|
3353
|
+
className: "cv-workspace-toggle",
|
|
3354
|
+
children: [
|
|
3355
|
+
"\u2713\u2713",
|
|
3356
|
+
/* @__PURE__ */ jsxs22("span", { className: "cv-only-wide", children: [
|
|
3357
|
+
" ",
|
|
3358
|
+
labels.markAllAsRead
|
|
3359
|
+
] })
|
|
3360
|
+
]
|
|
3361
|
+
}
|
|
3362
|
+
) : null,
|
|
3363
|
+
renderHeaderActions?.(inbox)
|
|
3364
|
+
] })
|
|
3365
|
+
] }),
|
|
3366
|
+
inbox.loadFailure ? /* @__PURE__ */ jsxs22("p", { role: "alert", className: "cv-workspace-failure", children: [
|
|
3367
|
+
inbox.loadFailure,
|
|
3368
|
+
signInHref ? /* @__PURE__ */ jsxs22(Fragment4, { children: [
|
|
3369
|
+
" ",
|
|
3370
|
+
/* @__PURE__ */ jsx24("a", { href: signInHref, className: "cv-workspace-failure__link", children: labels.signIn })
|
|
3371
|
+
] }) : null
|
|
3372
|
+
] }) : null,
|
|
3373
|
+
/* @__PURE__ */ jsxs22(
|
|
3374
|
+
"div",
|
|
3375
|
+
{
|
|
3376
|
+
className: `cv-workspace-grid${showSimulator ? " cv-workspace-grid--with-simulator" : ""}`,
|
|
3377
|
+
"data-detail": selected ? "open" : "closed",
|
|
3378
|
+
children: [
|
|
3379
|
+
/* @__PURE__ */ jsx24(
|
|
3380
|
+
ConversationsInboxList,
|
|
3381
|
+
{
|
|
3382
|
+
inbox,
|
|
3383
|
+
labels,
|
|
3384
|
+
className: "cv-workspace-list",
|
|
3385
|
+
...renderFilters ? { renderFilters } : {},
|
|
3386
|
+
...renderBulkActions ? { renderBulkActions } : {},
|
|
3387
|
+
...renderRow ? { renderRow } : {},
|
|
3388
|
+
...onSendTemplateToSelected ? { onSendTemplateToSelected: () => onSendTemplateToSelected(inbox) } : inbox.canListTemplates ? { onSendTemplateToSelected: () => setTemplateModalOpen(true) } : {}
|
|
3389
|
+
}
|
|
3390
|
+
),
|
|
3391
|
+
/* @__PURE__ */ jsx24("section", { className: "cv-workspace-detail", children: selected ? /* @__PURE__ */ jsx24(
|
|
3392
|
+
ConversationPane,
|
|
3393
|
+
{
|
|
3394
|
+
conversation: selected,
|
|
3395
|
+
now: inbox.now,
|
|
3396
|
+
busy: inbox.busy,
|
|
3397
|
+
labels,
|
|
3398
|
+
...inbox.canTakeover ? { onTakeover: () => void inbox.takeover(selected.id) } : {},
|
|
3399
|
+
...inbox.canTakeover ? { onReturnToBot: () => void inbox.releaseToBot(selected.id) } : {},
|
|
3400
|
+
...inbox.canFinalize ? { onFinish: () => void inbox.finalize(selected.id) } : {},
|
|
3401
|
+
...flowLabelOf ? { flowLabel: flowLabelOf(selected) } : {},
|
|
3402
|
+
...onDownload ? { onDownload: () => onDownload(selected) } : {},
|
|
3403
|
+
...requireTakeoverToReply ? { requireTakeoverToReply } : {},
|
|
3404
|
+
...messageSelection ? { messageSelection } : {},
|
|
3405
|
+
...initialComposerText ? { initialComposerText } : {},
|
|
3406
|
+
onBack: inbox.clearSelection,
|
|
3407
|
+
...paneUtilities ? { extraUtilities: paneUtilities } : {},
|
|
3408
|
+
...contextEntriesOf ? { contextEntriesOf } : {},
|
|
3409
|
+
...quickReplies ? { quickReplies } : {},
|
|
3410
|
+
...quickReplyVariablesFor ? { quickReplyVariablesFor } : {},
|
|
3411
|
+
...renderAboveTranscript ? { renderAboveTranscript } : {},
|
|
3412
|
+
...onAttach ? { onAttach: (file) => onAttach(selected, file) } : {}
|
|
3413
|
+
}
|
|
3414
|
+
) : /* @__PURE__ */ jsx24("p", { className: "cv-workspace-empty", children: labels.emptyDetail }) }),
|
|
3415
|
+
showSimulator && selected ? (
|
|
3416
|
+
// `min-height:0` junto do `min-width:0`: sem isso a linha do grid cresce com o conteúdo do
|
|
3417
|
+
// painel, o scroll interno nunca ativa e quem rola passa a ser a página inteira.
|
|
3418
|
+
/* @__PURE__ */ jsx24("div", { className: "cv-workspace-simulator", children: simulator?.render({ conversationId: selected.id, close: () => setSimulatorOpen(false) }) })
|
|
3419
|
+
) : null
|
|
3420
|
+
]
|
|
3421
|
+
}
|
|
3422
|
+
),
|
|
3423
|
+
templateModalOpen ? /* @__PURE__ */ jsx24(
|
|
3424
|
+
BulkTemplateModal,
|
|
3425
|
+
{
|
|
3426
|
+
labels,
|
|
3427
|
+
expiredCount: inbox.expiredSelectedCount,
|
|
3428
|
+
sending: inbox.busy,
|
|
3429
|
+
onClose: () => setTemplateModalOpen(false),
|
|
3430
|
+
onSend: (templateName) => {
|
|
3431
|
+
void inbox.sendTemplateToSelected(templateName).then(() => setTemplateModalOpen(false));
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
) : null
|
|
3435
|
+
] });
|
|
3436
|
+
}
|
|
2589
3437
|
export {
|
|
2590
3438
|
AudioPlayer,
|
|
2591
3439
|
AudioRecorderButton,
|
|
@@ -2594,6 +3442,7 @@ export {
|
|
|
2594
3442
|
CHANNEL_BRAND_COLOR,
|
|
2595
3443
|
CHANNEL_CAPABILITIES,
|
|
2596
3444
|
CHANNEL_FILTER_ALL,
|
|
3445
|
+
CONVERSATIONS_PER_PAGE,
|
|
2597
3446
|
CONVERSATION_CHANNEL,
|
|
2598
3447
|
CONVERSATION_WINDOW,
|
|
2599
3448
|
ChannelIcon,
|
|
@@ -2602,12 +3451,16 @@ export {
|
|
|
2602
3451
|
ConversationHeader,
|
|
2603
3452
|
ConversationListItem,
|
|
2604
3453
|
ConversationLocalesProvider,
|
|
3454
|
+
ConversationPane,
|
|
2605
3455
|
ConversationRow,
|
|
2606
3456
|
ConversationWallpaper,
|
|
3457
|
+
ConversationsInboxList,
|
|
2607
3458
|
ConversationsProvider,
|
|
3459
|
+
ConversationsWorkspace,
|
|
2608
3460
|
DEFAULT_ACCEPTED_FILE_TYPES,
|
|
2609
3461
|
DEFAULT_AUDIO_RECORDER_BUTTON_LABELS,
|
|
2610
3462
|
DEFAULT_AVATAR_LABELS,
|
|
3463
|
+
DEFAULT_CONVERSATIONS_WORKSPACE_LABELS,
|
|
2611
3464
|
DEFAULT_CONVERSATION_CHANNEL,
|
|
2612
3465
|
DEFAULT_CONVERSATION_CONTEXT_LABELS,
|
|
2613
3466
|
DEFAULT_CONVERSATION_DOCUMENTS_LABELS,
|
|
@@ -2685,6 +3538,7 @@ export {
|
|
|
2685
3538
|
useConversationMessages,
|
|
2686
3539
|
useConversationRealtime,
|
|
2687
3540
|
useConversations,
|
|
3541
|
+
useConversationsInbox,
|
|
2688
3542
|
useDarkMode,
|
|
2689
3543
|
useGlobalRealtime,
|
|
2690
3544
|
useInboxActions,
|