@cossistant/react 0.0.3 → 0.0.5
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/README.md +74 -0
- package/_virtual/rolldown_runtime.js +19 -0
- package/conversation.d.ts +26 -3
- package/conversation.d.ts.map +1 -1
- package/hooks/index.d.ts +5 -3
- package/hooks/index.js +7 -5
- package/hooks/private/store/use-conversations-store.d.ts +8 -0
- package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
- package/hooks/private/store/use-conversations-store.js +8 -0
- package/hooks/private/store/use-conversations-store.js.map +1 -1
- package/hooks/private/store/use-store-selector.d.ts +4 -0
- package/hooks/private/store/use-store-selector.d.ts.map +1 -1
- package/hooks/private/store/use-store-selector.js +5 -2
- package/hooks/private/store/use-store-selector.js.map +1 -1
- package/hooks/private/store/use-website-store.d.ts +4 -0
- package/hooks/private/store/use-website-store.d.ts.map +1 -1
- package/hooks/private/store/use-website-store.js +6 -3
- package/hooks/private/store/use-website-store.js.map +1 -1
- package/hooks/private/typing.d.ts +35 -0
- package/hooks/private/typing.d.ts.map +1 -0
- package/hooks/private/typing.js +49 -0
- package/hooks/private/typing.js.map +1 -0
- package/hooks/private/use-client-query.d.ts +5 -0
- package/hooks/private/use-client-query.d.ts.map +1 -1
- package/hooks/private/use-client-query.js +5 -0
- package/hooks/private/use-client-query.js.map +1 -1
- package/hooks/private/use-grouped-messages.d.ts +10 -4
- package/hooks/private/use-grouped-messages.d.ts.map +1 -1
- package/hooks/private/use-grouped-messages.js +24 -4
- package/hooks/private/use-grouped-messages.js.map +1 -1
- package/hooks/private/use-multimodal-input.d.ts.map +1 -1
- package/hooks/private/use-rest-client.d.ts.map +1 -1
- package/hooks/private/use-visitor-typing-reporter.d.ts +6 -0
- package/hooks/private/use-visitor-typing-reporter.d.ts.map +1 -1
- package/hooks/private/use-visitor-typing-reporter.js +6 -0
- package/hooks/private/use-visitor-typing-reporter.js.map +1 -1
- package/hooks/use-composer-refocus.d.ts.map +1 -1
- package/hooks/use-conversation-auto-seen.d.ts +9 -0
- package/hooks/use-conversation-auto-seen.d.ts.map +1 -1
- package/hooks/use-conversation-auto-seen.js +44 -3
- package/hooks/use-conversation-auto-seen.js.map +1 -1
- package/hooks/use-conversation-history-page.d.ts.map +1 -1
- package/hooks/use-conversation-history-page.js +16 -18
- package/hooks/use-conversation-history-page.js.map +1 -1
- package/hooks/use-conversation-lifecycle.d.ts.map +1 -1
- package/hooks/use-conversation-lifecycle.js +2 -4
- package/hooks/use-conversation-lifecycle.js.map +1 -1
- package/hooks/use-conversation-page.d.ts +6 -0
- package/hooks/use-conversation-page.d.ts.map +1 -1
- package/hooks/use-conversation-page.js +41 -3
- package/hooks/use-conversation-page.js.map +1 -1
- package/hooks/use-conversation-preview.d.ts +61 -0
- package/hooks/use-conversation-preview.d.ts.map +1 -0
- package/hooks/use-conversation-preview.js +173 -0
- package/hooks/use-conversation-preview.js.map +1 -0
- package/hooks/use-conversation-seen.d.ts +4 -0
- package/hooks/use-conversation-seen.d.ts.map +1 -1
- package/hooks/use-conversation-seen.js +4 -0
- package/hooks/use-conversation-seen.js.map +1 -1
- package/hooks/use-conversation-timeline-items.d.ts +4 -0
- package/hooks/use-conversation-timeline-items.d.ts.map +1 -1
- package/hooks/use-conversation-timeline-items.js +4 -0
- package/hooks/use-conversation-timeline-items.js.map +1 -1
- package/hooks/use-conversation-timeline.d.ts +32 -0
- package/hooks/use-conversation-timeline.d.ts.map +1 -0
- package/hooks/use-conversation-timeline.js +41 -0
- package/hooks/use-conversation-timeline.js.map +1 -0
- package/hooks/use-conversation-typing.d.ts +4 -0
- package/hooks/use-conversation-typing.d.ts.map +1 -1
- package/hooks/use-conversation-typing.js +4 -0
- package/hooks/use-conversation-typing.js.map +1 -1
- package/hooks/use-conversation.d.ts +11 -0
- package/hooks/use-conversation.d.ts.map +1 -1
- package/hooks/use-conversation.js +11 -0
- package/hooks/use-conversation.js.map +1 -1
- package/hooks/use-conversations.d.ts +12 -0
- package/hooks/use-conversations.d.ts.map +1 -1
- package/hooks/use-conversations.js +12 -0
- package/hooks/use-conversations.js.map +1 -1
- package/hooks/use-create-conversation.d.ts +5 -0
- package/hooks/use-create-conversation.d.ts.map +1 -1
- package/hooks/use-create-conversation.js +12 -9
- package/hooks/use-create-conversation.js.map +1 -1
- package/hooks/use-home-page.d.ts.map +1 -1
- package/hooks/use-home-page.js +6 -4
- package/hooks/use-home-page.js.map +1 -1
- package/hooks/use-message-composer.d.ts.map +1 -1
- package/hooks/use-realtime-support.d.ts.map +1 -1
- package/hooks/use-send-message.d.ts +9 -0
- package/hooks/use-send-message.d.ts.map +1 -1
- package/hooks/use-send-message.js +15 -13
- package/hooks/use-send-message.js.map +1 -1
- package/hooks/use-visitor.d.ts.map +1 -1
- package/hooks/use-visitor.js +28 -30
- package/hooks/use-visitor.js.map +1 -1
- package/hooks/use-window-visibility-focus.d.ts +4 -0
- package/hooks/use-window-visibility-focus.d.ts.map +1 -1
- package/hooks/use-window-visibility-focus.js +5 -2
- package/hooks/use-window-visibility-focus.js.map +1 -1
- package/identify-visitor.d.ts +12 -3
- package/identify-visitor.d.ts.map +1 -1
- package/identify-visitor.js +58 -9
- package/identify-visitor.js.map +1 -1
- package/index.d.ts +10 -7
- package/index.js +10 -9
- package/package.json +14 -17
- package/primitives/avatar/avatar.d.ts.map +1 -1
- package/primitives/avatar/fallback.d.ts.map +1 -1
- package/primitives/avatar/fallback.js +1 -3
- package/primitives/avatar/fallback.js.map +1 -1
- package/primitives/avatar/image.d.ts.map +1 -1
- package/primitives/avatar/index.d.ts +1 -0
- package/primitives/bubble.d.ts +2 -0
- package/primitives/bubble.d.ts.map +1 -1
- package/primitives/bubble.js +8 -2
- package/primitives/bubble.js.map +1 -1
- package/primitives/button.d.ts.map +1 -1
- package/primitives/conversation-timeline.d.ts.map +1 -1
- package/primitives/conversation-timeline.js +58 -5
- package/primitives/conversation-timeline.js.map +1 -1
- package/primitives/index.d.ts +1 -0
- package/primitives/index.parts.d.ts +1 -0
- package/primitives/multimodal-input.d.ts.map +1 -1
- package/primitives/timeline-item-group.d.ts +7 -7
- package/primitives/timeline-item-group.d.ts.map +1 -1
- package/primitives/timeline-item-group.js.map +1 -1
- package/primitives/timeline-item.d.ts +1 -1
- package/primitives/timeline-item.d.ts.map +1 -1
- package/primitives/timeline-item.js +7 -1
- package/primitives/timeline-item.js.map +1 -1
- package/primitives/window.d.ts +1 -1
- package/primitives/window.d.ts.map +1 -1
- package/primitives/window.js +4 -4
- package/primitives/window.js.map +1 -1
- package/provider.d.ts +23 -43
- package/provider.d.ts.map +1 -1
- package/provider.js +152 -49
- package/provider.js.map +1 -1
- package/realtime/event-filter.d.ts +4 -0
- package/realtime/event-filter.d.ts.map +1 -1
- package/realtime/event-filter.js +4 -0
- package/realtime/event-filter.js.map +1 -1
- package/realtime/index.js +1 -1
- package/realtime/provider.d.ts +7 -2
- package/realtime/provider.d.ts.map +1 -1
- package/realtime/provider.js +23 -1
- package/realtime/provider.js.map +1 -1
- package/realtime/seen-store.d.ts +13 -0
- package/realtime/seen-store.d.ts.map +1 -1
- package/realtime/seen-store.js +14 -2
- package/realtime/seen-store.js.map +1 -1
- package/realtime/support-provider.d.ts +1 -2
- package/realtime/support-provider.d.ts.map +1 -1
- package/realtime/support-provider.js +19 -20
- package/realtime/support-provider.js.map +1 -1
- package/realtime/typing-store.d.ts +18 -0
- package/realtime/typing-store.d.ts.map +1 -1
- package/realtime/typing-store.js +19 -2
- package/realtime/typing-store.js.map +1 -1
- package/realtime/use-realtime.d.ts +8 -4
- package/realtime/use-realtime.d.ts.map +1 -1
- package/realtime/use-realtime.js +4 -0
- package/realtime/use-realtime.js.map +1 -1
- package/realtime-events.d.ts +17 -3
- package/realtime-events.d.ts.map +1 -1
- package/schemas.d.ts +7 -1
- package/schemas.d.ts.map +1 -1
- package/support/components/avatar-stack.d.ts +8 -4
- package/support/components/avatar-stack.d.ts.map +1 -1
- package/support/components/avatar-stack.js +4 -0
- package/support/components/avatar-stack.js.map +1 -1
- package/support/components/avatar.d.ts +11 -6
- package/support/components/avatar.d.ts.map +1 -1
- package/support/components/avatar.js +4 -0
- package/support/components/avatar.js.map +1 -1
- package/support/components/bubble.d.ts.map +1 -1
- package/support/components/bubble.js +29 -6
- package/support/components/bubble.js.map +1 -1
- package/support/components/button.d.ts +8 -5
- package/support/components/button.d.ts.map +1 -1
- package/support/components/button.js +5 -1
- package/support/components/button.js.map +1 -1
- package/support/components/container.d.ts +0 -1
- package/support/components/container.d.ts.map +1 -1
- package/support/components/container.js +2 -8
- package/support/components/container.js.map +1 -1
- package/support/components/conversation-button-link.d.ts +8 -21
- package/support/components/conversation-button-link.d.ts.map +1 -1
- package/support/components/conversation-button-link.js +62 -178
- package/support/components/conversation-button-link.js.map +1 -1
- package/support/components/conversation-event.d.ts.map +1 -1
- package/support/components/conversation-event.js +4 -0
- package/support/components/conversation-event.js.map +1 -1
- package/support/components/conversation-timeline.d.ts +10 -1
- package/support/components/conversation-timeline.d.ts.map +1 -1
- package/support/components/conversation-timeline.js +63 -57
- package/support/components/conversation-timeline.js.map +1 -1
- package/support/components/cossistant-branding.d.ts +5 -2
- package/support/components/cossistant-branding.d.ts.map +1 -1
- package/support/components/cossistant-branding.js +3 -0
- package/support/components/cossistant-branding.js.map +1 -1
- package/support/components/header.d.ts.map +1 -1
- package/support/components/header.js +2 -2
- package/support/components/header.js.map +1 -1
- package/support/components/icons.d.ts.map +1 -1
- package/support/components/multimodal-input.d.ts.map +1 -1
- package/support/components/multimodal-input.js +5 -24
- package/support/components/multimodal-input.js.map +1 -1
- package/support/components/navigation-tab.d.ts +7 -2
- package/support/components/navigation-tab.d.ts.map +1 -1
- package/support/components/navigation-tab.js +4 -0
- package/support/components/navigation-tab.js.map +1 -1
- package/support/components/support-content.d.ts +1 -1
- package/support/components/support-content.d.ts.map +1 -1
- package/support/components/support-content.js +7 -10
- package/support/components/support-content.js.map +1 -1
- package/support/components/text-effect.d.ts +5 -2
- package/support/components/text-effect.d.ts.map +1 -1
- package/support/components/text-effect.js +4 -0
- package/support/components/text-effect.js.map +1 -1
- package/support/components/timeline-identification-tool.d.ts +7 -0
- package/support/components/timeline-identification-tool.d.ts.map +1 -0
- package/support/components/timeline-identification-tool.js +139 -0
- package/support/components/timeline-identification-tool.js.map +1 -0
- package/support/components/timeline-message-group.d.ts +2 -1
- package/support/components/timeline-message-group.d.ts.map +1 -1
- package/support/components/timeline-message-group.js +4 -19
- package/support/components/timeline-message-group.js.map +1 -1
- package/support/components/timeline-message-item.d.ts +6 -2
- package/support/components/timeline-message-item.d.ts.map +1 -1
- package/support/components/timeline-message-item.js +8 -4
- package/support/components/timeline-message-item.js.map +1 -1
- package/support/components/typing-indicator.d.ts +5 -2
- package/support/components/typing-indicator.d.ts.map +1 -1
- package/support/components/typing-indicator.js +4 -4
- package/support/components/typing-indicator.js.map +1 -1
- package/support/components/watermark.d.ts.map +1 -1
- package/support/context/websocket.d.ts +8 -0
- package/support/context/websocket.d.ts.map +1 -1
- package/support/context/websocket.js +12 -6
- package/support/context/websocket.js.map +1 -1
- package/support/index.d.ts +8 -8
- package/support/index.d.ts.map +1 -1
- package/support/index.js +18 -18
- package/support/index.js.map +1 -1
- package/support/pages/conversation-history.js +46 -54
- package/support/pages/conversation-history.js.map +1 -1
- package/support/pages/conversation.d.ts +3 -6
- package/support/pages/conversation.d.ts.map +1 -1
- package/support/pages/conversation.js +19 -9
- package/support/pages/conversation.js.map +1 -1
- package/support/pages/home.d.ts +2 -2
- package/support/pages/home.d.ts.map +1 -1
- package/support/pages/home.js +64 -77
- package/support/pages/home.js.map +1 -1
- package/support/store/support-store.d.ts +18 -2
- package/support/store/support-store.d.ts.map +1 -1
- package/support/store/support-store.js +20 -5
- package/support/store/support-store.js.map +1 -1
- package/support/{support-CMoDLQoC.css → support-Ck4jy29i.css} +1 -2
- package/support/support-Ck4jy29i.css.map +1 -0
- package/support/text/index.d.ts +15 -2
- package/support/text/index.d.ts.map +1 -1
- package/support/text/index.js +15 -2
- package/support/text/index.js.map +1 -1
- package/support/text/locales/en.js +22 -4
- package/support/text/locales/en.js.map +1 -1
- package/support/text/locales/es.js +18 -0
- package/support/text/locales/es.js.map +1 -1
- package/support/text/locales/fr.js +18 -0
- package/support/text/locales/fr.js.map +1 -1
- package/support/text/locales/keys.d.ts +69 -9
- package/support/text/locales/keys.d.ts.map +1 -1
- package/support/text/locales/keys.js +18 -0
- package/support/text/locales/keys.js.map +1 -1
- package/support/text/runtime.d.ts +21 -0
- package/support/text/runtime.d.ts.map +1 -1
- package/support/text/runtime.js +21 -0
- package/support/text/runtime.js.map +1 -1
- package/support/utils/index.d.ts +4 -0
- package/support/utils/index.d.ts.map +1 -1
- package/support/utils/index.js +4 -1
- package/support/utils/index.js.map +1 -1
- package/support/utils/time.d.ts +3 -0
- package/support/utils/time.d.ts.map +1 -1
- package/support/utils/time.js +3 -0
- package/support/utils/time.js.map +1 -1
- package/support-config.d.ts +2 -1
- package/support-config.d.ts.map +1 -1
- package/support-config.js.map +1 -1
- package/support.css +2 -2
- package/timeline-item.d.ts +10 -0
- package/timeline-item.d.ts.map +1 -1
- package/utils/conversation.d.ts +7 -0
- package/utils/conversation.d.ts.map +1 -0
- package/utils/conversation.js +18 -0
- package/utils/conversation.js.map +1 -0
- package/utils/id.d.ts +3 -0
- package/utils/id.d.ts.map +1 -1
- package/utils/id.js +3 -0
- package/utils/id.js.map +1 -1
- package/utils/index.d.ts +2 -1
- package/utils/index.js +2 -1
- package/utils/metadata-hash.d.ts +12 -0
- package/utils/metadata-hash.d.ts.map +1 -0
- package/utils/metadata-hash.js +26 -0
- package/utils/metadata-hash.js.map +1 -0
- package/utils/text.d.ts +3 -0
- package/utils/text.d.ts.map +1 -1
- package/utils/text.js +3 -0
- package/utils/text.js.map +1 -1
- package/utils/use-render-element.d.ts +3 -0
- package/utils/use-render-element.d.ts.map +1 -1
- package/utils/use-render-element.js +3 -0
- package/utils/use-render-element.js.map +1 -1
- package/support/context/config.d.ts +0 -32
- package/support/context/config.d.ts.map +0 -1
- package/support/context/config.js +0 -27
- package/support/context/config.js.map +0 -1
- package/support/support-CMoDLQoC.css.map +0 -1
|
@@ -1,192 +1,76 @@
|
|
|
1
1
|
import { cn } from "../utils/index.js";
|
|
2
|
-
import { useRenderElement } from "../../utils/use-render-element.js";
|
|
3
2
|
import { Avatar } from "./avatar.js";
|
|
4
3
|
import { BouncingDots } from "./typing-indicator.js";
|
|
5
4
|
import { coButtonVariants } from "./button.js";
|
|
6
5
|
import icons_default from "./icons.js";
|
|
7
6
|
import { useSupportText } from "../text/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import { formatTimeAgo } from "../utils/time.js";
|
|
10
|
-
import { useSupport } from "../../provider.js";
|
|
11
|
-
import { useConversationTyping } from "../../hooks/use-conversation-typing.js";
|
|
12
|
-
import { useMemo } from "react";
|
|
7
|
+
import { useConversationPreview } from "../../hooks/use-conversation-preview.js";
|
|
13
8
|
import { ConversationStatus } from "@cossistant/types";
|
|
14
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
10
|
|
|
16
11
|
//#region src/support/components/conversation-button-link.tsx
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} else senderName = text("common.fallbacks.supportTeam");
|
|
35
|
-
return {
|
|
36
|
-
content: item.text || "",
|
|
37
|
-
time: formatTimeAgo(item.createdAt),
|
|
38
|
-
isFromVisitor,
|
|
39
|
-
senderName,
|
|
40
|
-
senderImage
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function ConversationButtonLink({ conversation, onClick,...props }) {
|
|
44
|
-
const { availableHumanAgents, availableAIAgents, website, visitor } = useSupport();
|
|
45
|
-
const { items } = useConversationTimelineItems(conversation.id);
|
|
12
|
+
const STATUS_BADGE_CLASSNAMES = {
|
|
13
|
+
[ConversationStatus.OPEN]: "bg-co-success/20 text-co-success-foreground",
|
|
14
|
+
[ConversationStatus.RESOLVED]: "bg-co-neutral/20 text-co-neutral-foreground",
|
|
15
|
+
[ConversationStatus.SPAM]: "bg-co-warning/20 text-co-warning-foreground"
|
|
16
|
+
};
|
|
17
|
+
const DEFAULT_STATUS_BADGE_CLASSNAME = "bg-co-neutral/20 text-co-neutral-foreground";
|
|
18
|
+
const STATUS_TEXT_KEYS = {
|
|
19
|
+
[ConversationStatus.OPEN]: "component.conversationButtonLink.status.open",
|
|
20
|
+
[ConversationStatus.RESOLVED]: "component.conversationButtonLink.status.resolved",
|
|
21
|
+
[ConversationStatus.SPAM]: "component.conversationButtonLink.status.spam"
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Renders a navigable preview card for a conversation including assigned agent
|
|
25
|
+
* details, last message snippets and typing indicators.
|
|
26
|
+
*/
|
|
27
|
+
function ConversationButtonLink({ conversation, onClick, className }) {
|
|
28
|
+
const preview = useConversationPreview({ conversation });
|
|
46
29
|
const text = useSupportText();
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
type: "human",
|
|
93
|
-
name: supportFallbackName,
|
|
94
|
-
image: null
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
if (lastAgentItem?.aiAgentId) {
|
|
98
|
-
const ai = availableAIAgents.find((agent) => agent.id === lastAgentItem.aiAgentId);
|
|
99
|
-
if (ai) return {
|
|
100
|
-
type: "ai",
|
|
101
|
-
name: ai.name,
|
|
102
|
-
image: ai.image ?? null
|
|
103
|
-
};
|
|
104
|
-
return {
|
|
105
|
-
type: "ai",
|
|
106
|
-
name: aiFallbackName,
|
|
107
|
-
image: null
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
const fallbackHuman = availableHumanAgents[0];
|
|
111
|
-
if (fallbackHuman) return {
|
|
112
|
-
type: "human",
|
|
113
|
-
name: fallbackHuman.name,
|
|
114
|
-
image: fallbackHuman.image ?? null
|
|
115
|
-
};
|
|
116
|
-
const fallbackAi = availableAIAgents[0];
|
|
117
|
-
if (fallbackAi) return {
|
|
118
|
-
type: "ai",
|
|
119
|
-
name: fallbackAi.name,
|
|
120
|
-
image: fallbackAi.image ?? null
|
|
121
|
-
};
|
|
122
|
-
return {
|
|
123
|
-
type: "fallback",
|
|
124
|
-
name: supportFallbackName,
|
|
125
|
-
image: null
|
|
126
|
-
};
|
|
127
|
-
}, [
|
|
128
|
-
items,
|
|
129
|
-
conversation.lastTimelineItem,
|
|
130
|
-
availableHumanAgents,
|
|
131
|
-
availableAIAgents,
|
|
132
|
-
text
|
|
133
|
-
]);
|
|
134
|
-
const conversationTitle = useMemo(() => {
|
|
135
|
-
if (conversation.title) return conversation.title;
|
|
136
|
-
if (lastMessage?.content) return lastMessage.content;
|
|
137
|
-
return text("component.conversationButtonLink.fallbackTitle");
|
|
138
|
-
}, [
|
|
139
|
-
conversation.title,
|
|
140
|
-
lastMessage?.content,
|
|
141
|
-
text
|
|
142
|
-
]);
|
|
143
|
-
const state = {
|
|
144
|
-
conversation,
|
|
145
|
-
lastMessage,
|
|
146
|
-
assignedAgent
|
|
147
|
-
};
|
|
148
|
-
return useRenderElement("button", props, {
|
|
149
|
-
state,
|
|
150
|
-
props: {
|
|
151
|
-
onClick,
|
|
152
|
-
type: "button",
|
|
153
|
-
className: cn(coButtonVariants({
|
|
154
|
-
variant: "secondary",
|
|
155
|
-
size: "large"
|
|
156
|
-
}), "relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0", typeof props.className === "function" ? props.className(state) : props.className),
|
|
157
|
-
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
158
|
-
/* @__PURE__ */ jsx(Avatar, {
|
|
159
|
-
className: "size-8 flex-shrink-0",
|
|
160
|
-
image: assignedAgent.image,
|
|
161
|
-
name: assignedAgent.name
|
|
162
|
-
}),
|
|
163
|
-
/* @__PURE__ */ jsx("div", {
|
|
164
|
-
className: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
165
|
-
children: typingInfo ? /* @__PURE__ */ jsx(BouncingDots, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
166
|
-
className: "flex max-w-[90%] items-center justify-between gap-2",
|
|
167
|
-
children: /* @__PURE__ */ jsx("h3", {
|
|
168
|
-
className: "truncate font-medium text-co-primary text-sm",
|
|
169
|
-
children: conversationTitle
|
|
170
|
-
})
|
|
171
|
-
}), lastMessage ? /* @__PURE__ */ jsx("p", {
|
|
172
|
-
className: "text-co-primary/60 text-xs",
|
|
173
|
-
children: lastMessage.isFromVisitor ? /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.visitor", { time: lastMessage.time }) }) : /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.agent", {
|
|
174
|
-
name: lastMessage.senderName || text("common.fallbacks.supportTeam"),
|
|
175
|
-
time: lastMessage.time
|
|
176
|
-
}) })
|
|
177
|
-
}) : null] })
|
|
178
|
-
}),
|
|
179
|
-
/* @__PURE__ */ jsx("div", {
|
|
180
|
-
className: cn("mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase", conversation.status === ConversationStatus.OPEN ? "bg-co-success/20 text-co-success-foreground" : conversation.status === ConversationStatus.RESOLVED ? "bg-co-neutral/20 text-co-neutral-foreground" : "bg-co-warning/20 text-co-warning-foreground"),
|
|
181
|
-
children: conversation.status
|
|
182
|
-
}),
|
|
183
|
-
/* @__PURE__ */ jsx(icons_default, {
|
|
184
|
-
className: "-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary",
|
|
185
|
-
name: "arrow-right",
|
|
186
|
-
variant: "default"
|
|
187
|
-
})
|
|
188
|
-
] })
|
|
189
|
-
}
|
|
30
|
+
const { lastMessage, assignedAgent, typing } = preview;
|
|
31
|
+
const statusBadgeClassName = conversation.deletedAt ? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED] : STATUS_BADGE_CLASSNAMES[conversation.status] ?? DEFAULT_STATUS_BADGE_CLASSNAME;
|
|
32
|
+
const statusTextKey = conversation.deletedAt ? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED] : STATUS_TEXT_KEYS[conversation.status];
|
|
33
|
+
const statusText = conversation.deletedAt ? text("component.conversationButtonLink.status.closed") : statusTextKey ? text(statusTextKey) : text("common.fallbacks.unknown");
|
|
34
|
+
const lastMessageContent = lastMessage ? lastMessage.isFromVisitor ? /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.visitor", { time: lastMessage.time }) }) : /* @__PURE__ */ jsx("span", { children: text("component.conversationButtonLink.lastMessage.agent", {
|
|
35
|
+
name: lastMessage.senderName || text("common.fallbacks.supportTeam"),
|
|
36
|
+
time: lastMessage.time
|
|
37
|
+
}) }) : void 0;
|
|
38
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
39
|
+
className: cn(coButtonVariants({
|
|
40
|
+
variant: "secondary",
|
|
41
|
+
size: "large"
|
|
42
|
+
}), "group/btn relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0", className),
|
|
43
|
+
onClick,
|
|
44
|
+
type: "button",
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ jsx(Avatar, {
|
|
47
|
+
className: "flex size-8 flex-shrink-0 items-center justify-center overflow-clip rounded-full bg-co-background-200 dark:bg-co-background-500",
|
|
48
|
+
image: assignedAgent?.image,
|
|
49
|
+
name: assignedAgent?.name ?? text("common.fallbacks.supportTeam")
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ jsx("div", {
|
|
52
|
+
className: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
53
|
+
children: typing.isTyping ? /* @__PURE__ */ jsx(BouncingDots, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
54
|
+
className: "flex max-w-[90%] items-center justify-between gap-2",
|
|
55
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
56
|
+
className: "truncate font-medium text-co-primary text-sm",
|
|
57
|
+
children: preview.title
|
|
58
|
+
})
|
|
59
|
+
}), lastMessageContent ? /* @__PURE__ */ jsx("p", {
|
|
60
|
+
className: "text-co-primary/60 text-xs",
|
|
61
|
+
children: lastMessageContent
|
|
62
|
+
}) : null] })
|
|
63
|
+
}),
|
|
64
|
+
/* @__PURE__ */ jsx("div", {
|
|
65
|
+
className: cn("mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase", statusBadgeClassName),
|
|
66
|
+
children: statusText
|
|
67
|
+
}),
|
|
68
|
+
/* @__PURE__ */ jsx(icons_default, {
|
|
69
|
+
className: "-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary",
|
|
70
|
+
name: "arrow-right",
|
|
71
|
+
variant: "default"
|
|
72
|
+
})
|
|
73
|
+
]
|
|
190
74
|
});
|
|
191
75
|
}
|
|
192
76
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-button-link.js","names":["senderImage: string | null","state: ConversationButtonLinkState","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport {\n\tuseConversationTimelineItems,\n\tuseConversationTyping,\n\tuseSupport,\n} from \"../..\";\nimport { useRenderElement } from \"../../utils/use-render-element\";\nimport { useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { formatTimeAgo } from \"../utils/time\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string | ((state: ConversationButtonLinkState) => string);\n\trender?: (\n\t\tprops: React.HTMLProps<HTMLButtonElement>,\n\t\tstate: ConversationButtonLinkState\n\t) => React.ReactElement;\n};\n\nexport type ConversationButtonLinkAgent = {\n\tname: string;\n\timage: string | null;\n\ttype: \"human\" | \"ai\" | \"fallback\";\n};\n\nexport type ConversationButtonLinkState = {\n\tconversation: Conversation;\n\tlastMessage: {\n\t\tcontent: string;\n\t\ttime: string;\n\t\tisFromVisitor: boolean;\n\t\tsenderName?: string;\n\t\tsenderImage?: string | null;\n\t} | null;\n\tassignedAgent: ConversationButtonLinkAgent;\n};\n\nfunction getLastTimelineItemInfo(\n\titem: NonNullable<Conversation[\"lastTimelineItem\"]>,\n\tavailableHumanAgents: ReturnType<typeof useSupport>[\"availableHumanAgents\"],\n\twebsite: ReturnType<typeof useSupport>[\"website\"],\n\ttext: ReturnType<typeof useSupportText>\n) {\n\tconst isFromVisitor = item.visitorId !== null;\n\n\tlet senderName = text(\"common.fallbacks.unknown\");\n\tlet senderImage: string | null = null;\n\n\tif (isFromVisitor) {\n\t\tsenderName = text(\"common.fallbacks.you\");\n\t} else if (item.userId) {\n\t\tconst agent = availableHumanAgents.find((a) => a.id === item.userId);\n\t\tif (agent) {\n\t\t\tsenderName = agent.name;\n\t\t\tsenderImage = agent.image;\n\t\t} else {\n\t\t\tsenderName = text(\"common.fallbacks.supportTeam\");\n\t\t}\n\t} else if (item.aiAgentId && website?.availableAIAgents) {\n\t\tconst aiAgent = website.availableAIAgents.find(\n\t\t\t(a) => a.id === item.aiAgentId\n\t\t);\n\t\tif (aiAgent) {\n\t\t\tsenderName = aiAgent.name;\n\t\t\tsenderImage = aiAgent.image;\n\t\t} else {\n\t\t\tsenderName = text(\"common.fallbacks.aiAssistant\");\n\t\t}\n\t} else {\n\t\tsenderName = text(\"common.fallbacks.supportTeam\");\n\t}\n\n\treturn {\n\t\tcontent: item.text || \"\",\n\t\ttime: formatTimeAgo(item.createdAt),\n\t\tisFromVisitor,\n\t\tsenderName,\n\t\tsenderImage,\n\t};\n}\n\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\t...props\n}: ConversationButtonLinkProps) {\n\tconst { availableHumanAgents, availableAIAgents, website, visitor } =\n\t\tuseSupport();\n\tconst { items } = useConversationTimelineItems(conversation.id);\n\tconst text = useSupportText();\n\tconst typingEntries = useConversationTyping(conversation.id, {\n\t\texcludeVisitorId: visitor?.id ?? null,\n\t});\n\n\t// Check if anyone is typing in this conversation\n\tconst typingInfo = useMemo(() => {\n\t\tif (typingEntries.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst entry = typingEntries[0];\n\t\tlet name = text(\"common.fallbacks.someone\");\n\t\tlet image: string | null = null;\n\n\t\tif (entry?.actorType === \"user\") {\n\t\t\tconst human = availableHumanAgents.find(\n\t\t\t\t(agent) => agent.id === entry.actorId\n\t\t\t);\n\t\t\tname = human?.name || text(\"common.fallbacks.supportTeam\");\n\t\t\timage = human?.image || null;\n\t\t} else if (entry?.actorType === \"ai_agent\") {\n\t\t\tconst ai = availableAIAgents.find((agent) => agent.id === entry.actorId);\n\t\t\tname = ai?.name || text(\"common.fallbacks.aiAssistant\");\n\t\t\timage = ai?.image || null;\n\t\t}\n\n\t\treturn { name };\n\t}, [typingEntries, availableHumanAgents, availableAIAgents, text]);\n\n\t// Process the last timeline item (memoized to avoid expensive recomputation)\n\tconst lastMessage = useMemo(() => {\n\t\tconst cachedLastTimelineItem =\n\t\t\t// biome-ignore lint/style/useAtIndex: ok here\n\t\t\titems.length > 0 ? items[items.length - 1] : null;\n\n\t\tconst timelineItemToDisplay =\n\t\t\tcachedLastTimelineItem || conversation.lastTimelineItem;\n\n\t\treturn timelineItemToDisplay\n\t\t\t? getLastTimelineItemInfo(\n\t\t\t\t\ttimelineItemToDisplay,\n\t\t\t\t\tavailableHumanAgents,\n\t\t\t\t\twebsite,\n\t\t\t\t\ttext\n\t\t\t\t)\n\t\t\t: null;\n\t}, [\n\t\titems,\n\t\tconversation.lastTimelineItem,\n\t\tavailableHumanAgents,\n\t\twebsite,\n\t\ttext,\n\t]);\n\n\tconst assignedAgent = useMemo<ConversationButtonLinkAgent>(() => {\n\t\tconst supportFallbackName = text(\"common.fallbacks.supportTeam\");\n\t\tconst aiFallbackName = text(\"common.fallbacks.aiAssistant\");\n\n\t\tconst knownItems = items.slice();\n\t\tif (\n\t\t\tconversation.lastTimelineItem &&\n\t\t\t!knownItems.some((item) => item.id === conversation.lastTimelineItem?.id)\n\t\t) {\n\t\t\tknownItems.push(conversation.lastTimelineItem);\n\t\t}\n\n\t\tconst lastAgentItem = [...knownItems]\n\t\t\t.reverse()\n\t\t\t.find((item) => item.userId !== null || item.aiAgentId !== null);\n\n\t\tif (lastAgentItem?.userId) {\n\t\t\tconst human = availableHumanAgents.find(\n\t\t\t\t(agent) => agent.id === lastAgentItem.userId\n\t\t\t);\n\n\t\t\tif (human) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"human\",\n\t\t\t\t\tname: human.name,\n\t\t\t\t\timage: human.image ?? null,\n\t\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: \"human\",\n\t\t\t\tname: supportFallbackName,\n\t\t\t\timage: null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\tif (lastAgentItem?.aiAgentId) {\n\t\t\tconst ai = availableAIAgents.find(\n\t\t\t\t(agent) => agent.id === lastAgentItem.aiAgentId\n\t\t\t);\n\n\t\t\tif (ai) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"ai\",\n\t\t\t\t\tname: ai.name,\n\t\t\t\t\timage: ai.image ?? null,\n\t\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: \"ai\",\n\t\t\t\tname: aiFallbackName,\n\t\t\t\timage: null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\tconst fallbackHuman = availableHumanAgents[0];\n\t\tif (fallbackHuman) {\n\t\t\treturn {\n\t\t\t\ttype: \"human\",\n\t\t\t\tname: fallbackHuman.name,\n\t\t\t\timage: fallbackHuman.image ?? null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\tconst fallbackAi = availableAIAgents[0];\n\t\tif (fallbackAi) {\n\t\t\treturn {\n\t\t\t\ttype: \"ai\",\n\t\t\t\tname: fallbackAi.name,\n\t\t\t\timage: fallbackAi.image ?? null,\n\t\t\t} satisfies ConversationButtonLinkAgent;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: \"fallback\",\n\t\t\tname: supportFallbackName,\n\t\t\timage: null,\n\t\t} satisfies ConversationButtonLinkAgent;\n\t}, [\n\t\titems,\n\t\tconversation.lastTimelineItem,\n\t\tavailableHumanAgents,\n\t\tavailableAIAgents,\n\t\ttext,\n\t]);\n\n\tconst conversationTitle = useMemo(() => {\n\t\tif (conversation.title) {\n\t\t\treturn conversation.title;\n\t\t}\n\n\t\tif (lastMessage?.content) {\n\t\t\treturn lastMessage.content;\n\t\t}\n\n\t\treturn text(\"component.conversationButtonLink.fallbackTitle\");\n\t}, [conversation.title, lastMessage?.content, text]);\n\n\tconst state: ConversationButtonLinkState = {\n\t\tconversation,\n\t\tlastMessage,\n\t\tassignedAgent,\n\t};\n\n\treturn useRenderElement(\"button\", props, {\n\t\tstate,\n\t\tprops: {\n\t\t\tonClick,\n\t\t\ttype: \"button\",\n\t\t\tclassName: cn(\n\t\t\t\tcoButtonVariants({ variant: \"secondary\", size: \"large\" }),\n\t\t\t\t\"relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0\",\n\t\t\t\ttypeof props.className === \"function\"\n\t\t\t\t\t? props.className(state)\n\t\t\t\t\t: props.className\n\t\t\t),\n\t\t\tchildren: (\n\t\t\t\t<>\n\t\t\t\t\t<Avatar\n\t\t\t\t\t\tclassName=\"size-8 flex-shrink-0\"\n\t\t\t\t\t\timage={assignedAgent.image}\n\t\t\t\t\t\tname={assignedAgent.name}\n\t\t\t\t\t/>\n\n\t\t\t\t\t<div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t\t\t{typingInfo ? (\n\t\t\t\t\t\t\t<BouncingDots />\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t\t\t\t\t{conversationTitle}\n\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{lastMessage ? (\n\t\t\t\t\t\t\t\t\t<p className=\"text-co-primary/60 text-xs\">\n\t\t\t\t\t\t\t\t\t\t{lastMessage.isFromVisitor ? (\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t{text(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"component.conversationButtonLink.lastMessage.visitor\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t{text(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"component.conversationButtonLink.lastMessage.agent\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tname:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlastMessage.senderName ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext(\"common.fallbacks.supportTeam\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n\t\t\t\t\t\t\tconversation.status === ConversationStatus.OPEN\n\t\t\t\t\t\t\t\t? \"bg-co-success/20 text-co-success-foreground\"\n\t\t\t\t\t\t\t\t: conversation.status === ConversationStatus.RESOLVED\n\t\t\t\t\t\t\t\t\t? \"bg-co-neutral/20 text-co-neutral-foreground\"\n\t\t\t\t\t\t\t\t\t: \"bg-co-warning/20 text-co-warning-foreground\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{conversation.status}\n\t\t\t\t\t</div>\n\t\t\t\t\t<Icon\n\t\t\t\t\t\tclassName=\"-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary\"\n\t\t\t\t\t\tname=\"arrow-right\"\n\t\t\t\t\t\tvariant=\"default\"\n\t\t\t\t\t/>\n\t\t\t\t</>\n\t\t\t),\n\t\t},\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA8CA,SAAS,wBACR,MACA,sBACA,SACA,MACC;CACD,MAAM,gBAAgB,KAAK,cAAc;CAEzC,IAAI,aAAa,KAAK,2BAA2B;CACjD,IAAIA,cAA6B;AAEjC,KAAI,cACH,cAAa,KAAK,uBAAuB;UAC/B,KAAK,QAAQ;EACvB,MAAM,QAAQ,qBAAqB,MAAM,MAAM,EAAE,OAAO,KAAK,OAAO;AACpE,MAAI,OAAO;AACV,gBAAa,MAAM;AACnB,iBAAc,MAAM;QAEpB,cAAa,KAAK,+BAA+B;YAExC,KAAK,aAAa,SAAS,mBAAmB;EACxD,MAAM,UAAU,QAAQ,kBAAkB,MACxC,MAAM,EAAE,OAAO,KAAK,UACrB;AACD,MAAI,SAAS;AACZ,gBAAa,QAAQ;AACrB,iBAAc,QAAQ;QAEtB,cAAa,KAAK,+BAA+B;OAGlD,cAAa,KAAK,+BAA+B;AAGlD,QAAO;EACN,SAAS,KAAK,QAAQ;EACtB,MAAM,cAAc,KAAK,UAAU;EACnC;EACA;EACA;EACA;;AAGF,SAAgB,uBAAuB,EACtC,cACA,QACA,GAAG,SAC4B;CAC/B,MAAM,EAAE,sBAAsB,mBAAmB,SAAS,YACzD,YAAY;CACb,MAAM,EAAE,UAAU,6BAA6B,aAAa,GAAG;CAC/D,MAAM,OAAO,gBAAgB;CAC7B,MAAM,gBAAgB,sBAAsB,aAAa,IAAI,EAC5D,kBAAkB,SAAS,MAAM,MACjC,CAAC;CAGF,MAAM,aAAa,cAAc;AAChC,MAAI,cAAc,WAAW,EAC5B,QAAO;EAGR,MAAM,QAAQ,cAAc;EAC5B,IAAI,OAAO,KAAK,2BAA2B;AAG3C,MAAI,OAAO,cAAc,QAAQ;GAChC,MAAM,QAAQ,qBAAqB,MACjC,UAAU,MAAM,OAAO,MAAM,QAC9B;AACD,UAAO,OAAO,QAAQ,KAAK,+BAA+B;AAC1D,GAAQ,OAAO;aACL,OAAO,cAAc,YAAY;GAC3C,MAAM,KAAK,kBAAkB,MAAM,UAAU,MAAM,OAAO,MAAM,QAAQ;AACxE,UAAO,IAAI,QAAQ,KAAK,+BAA+B;AACvD,GAAQ,IAAI;;AAGb,SAAO,EAAE,MAAM;IACb;EAAC;EAAe;EAAsB;EAAmB;EAAK,CAAC;CAGlE,MAAM,cAAc,cAAc;EAKjC,MAAM,yBAFL,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,KAAK,SAGnB,aAAa;AAExC,SAAO,wBACJ,wBACA,uBACA,sBACA,SACA,KACA,GACA;IACD;EACF;EACA,aAAa;EACb;EACA;EACA;EACA,CAAC;CAEF,MAAM,gBAAgB,cAA2C;EAChE,MAAM,sBAAsB,KAAK,+BAA+B;EAChE,MAAM,iBAAiB,KAAK,+BAA+B;EAE3D,MAAM,aAAa,MAAM,OAAO;AAChC,MACC,aAAa,oBACb,CAAC,WAAW,MAAM,SAAS,KAAK,OAAO,aAAa,kBAAkB,GAAG,CAEzE,YAAW,KAAK,aAAa,iBAAiB;EAG/C,MAAM,gBAAgB,CAAC,GAAG,WAAW,CACnC,SAAS,CACT,MAAM,SAAS,KAAK,WAAW,QAAQ,KAAK,cAAc,KAAK;AAEjE,MAAI,eAAe,QAAQ;GAC1B,MAAM,QAAQ,qBAAqB,MACjC,UAAU,MAAM,OAAO,cAAc,OACtC;AAED,OAAI,MACH,QAAO;IACN,MAAM;IACN,MAAM,MAAM;IACZ,OAAO,MAAM,SAAS;IACtB;AAGF,UAAO;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP;;AAGF,MAAI,eAAe,WAAW;GAC7B,MAAM,KAAK,kBAAkB,MAC3B,UAAU,MAAM,OAAO,cAAc,UACtC;AAED,OAAI,GACH,QAAO;IACN,MAAM;IACN,MAAM,GAAG;IACT,OAAO,GAAG,SAAS;IACnB;AAGF,UAAO;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP;;EAGF,MAAM,gBAAgB,qBAAqB;AAC3C,MAAI,cACH,QAAO;GACN,MAAM;GACN,MAAM,cAAc;GACpB,OAAO,cAAc,SAAS;GAC9B;EAGF,MAAM,aAAa,kBAAkB;AACrC,MAAI,WACH,QAAO;GACN,MAAM;GACN,MAAM,WAAW;GACjB,OAAO,WAAW,SAAS;GAC3B;AAGF,SAAO;GACN,MAAM;GACN,MAAM;GACN,OAAO;GACP;IACC;EACF;EACA,aAAa;EACb;EACA;EACA;EACA,CAAC;CAEF,MAAM,oBAAoB,cAAc;AACvC,MAAI,aAAa,MAChB,QAAO,aAAa;AAGrB,MAAI,aAAa,QAChB,QAAO,YAAY;AAGpB,SAAO,KAAK,iDAAiD;IAC3D;EAAC,aAAa;EAAO,aAAa;EAAS;EAAK,CAAC;CAEpD,MAAMC,QAAqC;EAC1C;EACA;EACA;EACA;AAED,QAAO,iBAAiB,UAAU,OAAO;EACxC;EACA,OAAO;GACN;GACA,MAAM;GACN,WAAW,GACV,iBAAiB;IAAE,SAAS;IAAa,MAAM;IAAS,CAAC,EACzD,2JACA,OAAO,MAAM,cAAc,aACxB,MAAM,UAAU,MAAM,GACtB,MAAM,UACT;GACD,UACC;IACC,oBAAC;KACA,WAAU;KACV,OAAO,cAAc;KACrB,MAAM,cAAc;MACnB;IAEF,oBAAC;KAAI,WAAU;eACb,aACA,oBAAC,iBAAe,GAEhB,4CACC,oBAAC;MAAI,WAAU;gBACd,oBAAC;OAAG,WAAU;iBACZ;QACG;OACA,EAEL,cACA,oBAAC;MAAE,WAAU;gBACX,YAAY,gBACZ,oBAAC,oBACC,KACA,wDACA,EACC,MAAM,YAAY,MAClB,CACD,GACK,GAEP,oBAAC,oBACC,KACA,sDACA;OACC,MACC,YAAY,cACZ,KAAK,+BAA+B;OACrC,MAAM,YAAY;OAClB,CACD,GACK;OAEL,GACD,QACF;MAEC;IACN,oBAAC;KACA,WAAW,GACV,sFACA,aAAa,WAAW,mBAAmB,OACxC,gDACA,aAAa,WAAW,mBAAmB,WAC1C,gDACA,8CACJ;eAEA,aAAa;MACT;IACN,oBAACC;KACA,WAAU;KACV,MAAK;KACL,SAAQ;MACP;OACA;GAEJ;EACD,CAAC"}
|
|
1
|
+
{"version":3,"file":"conversation-button-link.js","names":["STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string>","STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey>","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type React from \"react\";\nimport { useConversationPreview } from \"../../hooks/use-conversation-preview\";\nimport { type SupportTextKey, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string;\n};\n\nconst STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string> = {\n\t[ConversationStatus.OPEN]: \"bg-co-success/20 text-co-success-foreground\",\n\t[ConversationStatus.RESOLVED]: \"bg-co-neutral/20 text-co-neutral-foreground\",\n\t[ConversationStatus.SPAM]: \"bg-co-warning/20 text-co-warning-foreground\",\n};\n\nconst DEFAULT_STATUS_BADGE_CLASSNAME =\n\t\"bg-co-neutral/20 text-co-neutral-foreground\";\n\nconst STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey> = {\n\t[ConversationStatus.OPEN]: \"component.conversationButtonLink.status.open\",\n\t[ConversationStatus.RESOLVED]:\n\t\t\"component.conversationButtonLink.status.resolved\",\n\t[ConversationStatus.SPAM]: \"component.conversationButtonLink.status.spam\",\n};\n\n/**\n * Renders a navigable preview card for a conversation including assigned agent\n * details, last message snippets and typing indicators.\n */\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\tclassName,\n}: ConversationButtonLinkProps): React.ReactElement | null {\n\tconst preview = useConversationPreview({ conversation });\n\tconst text = useSupportText();\n\tconst { lastMessage, assignedAgent, typing } = preview;\n\n\tconst statusBadgeClassName = conversation.deletedAt\n\t\t? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED]\n\t\t: (STATUS_BADGE_CLASSNAMES[conversation.status] ??\n\t\t\tDEFAULT_STATUS_BADGE_CLASSNAME);\n\n\tconst statusTextKey = conversation.deletedAt\n\t\t? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED]\n\t\t: STATUS_TEXT_KEYS[conversation.status];\n\n\tconst statusText = conversation.deletedAt\n\t\t? text(\"component.conversationButtonLink.status.closed\")\n\t\t: statusTextKey\n\t\t\t? text(statusTextKey)\n\t\t\t: text(\"common.fallbacks.unknown\");\n\n\tconst lastMessageContent = lastMessage ? (\n\t\tlastMessage.isFromVisitor ? (\n\t\t\t<span>\n\t\t\t\t{text(\"component.conversationButtonLink.lastMessage.visitor\", {\n\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t})}\n\t\t\t</span>\n\t\t) : (\n\t\t\t<span>\n\t\t\t\t{text(\"component.conversationButtonLink.lastMessage.agent\", {\n\t\t\t\t\tname: lastMessage.senderName || text(\"common.fallbacks.supportTeam\"),\n\t\t\t\t\ttime: lastMessage.time,\n\t\t\t\t})}\n\t\t\t</span>\n\t\t)\n\t) : undefined;\n\n\treturn (\n\t\t<button\n\t\t\tclassName={cn(\n\t\t\t\tcoButtonVariants({\n\t\t\t\t\tvariant: \"secondary\",\n\t\t\t\t\tsize: \"large\",\n\t\t\t\t}),\n\t\t\t\t\"group/btn relative gap-3 border-0 border-co-border/50 border-b text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t\ttype=\"button\"\n\t\t>\n\t\t\t<Avatar\n\t\t\t\tclassName=\"flex size-8 flex-shrink-0 items-center justify-center overflow-clip rounded-full bg-co-background-200 dark:bg-co-background-500\"\n\t\t\t\timage={assignedAgent?.image}\n\t\t\t\tname={assignedAgent?.name ?? text(\"common.fallbacks.supportTeam\")}\n\t\t\t/>\n\n\t\t\t<div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t{typing.isTyping ? (\n\t\t\t\t\t<BouncingDots />\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t\t\t{preview.title}\n\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{lastMessageContent ? (\n\t\t\t\t\t\t\t<p className=\"text-co-primary/60 text-xs\">{lastMessageContent}</p>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n\t\t\t\t\tstatusBadgeClassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{statusText}\n\t\t\t</div>\n\n\t\t\t<Icon\n\t\t\t\tclassName=\"-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary\"\n\t\t\t\tname=\"arrow-right\"\n\t\t\t\tvariant=\"default\"\n\t\t\t/>\n\t\t</button>\n\t);\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAMA,0BAA8D;EAClE,mBAAmB,OAAO;EAC1B,mBAAmB,WAAW;EAC9B,mBAAmB,OAAO;CAC3B;AAED,MAAM,iCACL;AAED,MAAMC,mBAA+D;EACnE,mBAAmB,OAAO;EAC1B,mBAAmB,WACnB;EACA,mBAAmB,OAAO;CAC3B;;;;;AAMD,SAAgB,uBAAuB,EACtC,cACA,SACA,aAC0D;CAC1D,MAAM,UAAU,uBAAuB,EAAE,cAAc,CAAC;CACxD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,aAAa,eAAe,WAAW;CAE/C,MAAM,uBAAuB,aAAa,YACvC,wBAAwB,mBAAmB,YAC1C,wBAAwB,aAAa,WACvC;CAEF,MAAM,gBAAgB,aAAa,YAChC,iBAAiB,mBAAmB,YACpC,iBAAiB,aAAa;CAEjC,MAAM,aAAa,aAAa,YAC7B,KAAK,iDAAiD,GACtD,gBACC,KAAK,cAAc,GACnB,KAAK,2BAA2B;CAEpC,MAAM,qBAAqB,cAC1B,YAAY,gBACX,oBAAC,oBACC,KAAK,wDAAwD,EAC7D,MAAM,YAAY,MAClB,CAAC,GACI,GAEP,oBAAC,oBACC,KAAK,sDAAsD;EAC3D,MAAM,YAAY,cAAc,KAAK,+BAA+B;EACpE,MAAM,YAAY;EAClB,CAAC,GACI,GAEL;AAEJ,QACC,qBAAC;EACA,WAAW,GACV,iBAAiB;GAChB,SAAS;GACT,MAAM;GACN,CAAC,EACF,qKACA,UACA;EACQ;EACT,MAAK;;GAEL,oBAAC;IACA,WAAU;IACV,OAAO,eAAe;IACtB,MAAM,eAAe,QAAQ,KAAK,+BAA+B;KAChE;GAEF,oBAAC;IAAI,WAAU;cACb,OAAO,WACP,oBAAC,iBAAe,GAEhB,4CACC,oBAAC;KAAI,WAAU;eACd,oBAAC;MAAG,WAAU;gBACZ,QAAQ;OACL;MACA,EACL,qBACA,oBAAC;KAAE,WAAU;eAA8B;MAAuB,GAC/D,QACF;KAEC;GAEN,oBAAC;IACA,WAAW,GACV,sFACA,qBACA;cAEA;KACI;GAEN,oBAACC;IACA,WAAU;IACV,MAAK;IACL,SAAQ;KACP;;GACM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-event.d.ts","names":[],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":[],"mappings":";;;;KAWY,sBAAA;SACJ;EADI,iBAAA,EAEQ,gBAFc,EAAA;
|
|
1
|
+
{"version":3,"file":"conversation-event.d.ts","names":[],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":[],"mappings":";;;;KAWY,sBAAA;SACJ;EADI,iBAAA,EAEQ,gBAFc,EAAA;EAC1B,oBAAA,EAEe,mBAFf,EAAA;EACY,SAAA,CAAA,EAAA,MAAA;CACG;AAAmB,cAI7B,iBAJ6B,EAIV,KAAA,CAAM,EAJI,CAID,sBAJC,CAAA"}
|
|
@@ -24,6 +24,9 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
|
|
|
24
24
|
case "tag_removed": return text("component.conversationEvent.tagRemoved", { actorName });
|
|
25
25
|
case "resolved": return text("component.conversationEvent.resolved", { actorName });
|
|
26
26
|
case "reopened": return text("component.conversationEvent.reopened", { actorName });
|
|
27
|
+
case "visitor_blocked": return text("component.conversationEvent.visitorBlocked", { actorName });
|
|
28
|
+
case "visitor_unblocked": return text("component.conversationEvent.visitorUnblocked", { actorName });
|
|
29
|
+
case "visitor_identified": return text("component.conversationEvent.visitorIdentified", { actorName });
|
|
27
30
|
default: return text("component.conversationEvent.default", { actorName });
|
|
28
31
|
}
|
|
29
32
|
};
|
|
@@ -70,6 +73,7 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
|
|
|
70
73
|
})
|
|
71
74
|
});
|
|
72
75
|
};
|
|
76
|
+
ConversationEvent.displayName = "ConversationEvent";
|
|
73
77
|
|
|
74
78
|
//#endregion
|
|
75
79
|
export { ConversationEvent };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center py-4\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<div className=\"flex size-5 items-center justify-center rounded-full bg-primary/10\">\n\t\t\t\t\t\t\t<CossistantLogo className=\"h-3 w-3 text-primary\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-full\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n"],"mappings":";;;;;;;AAkBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MAAI,WAAU;gBACd,oBAAC,kBAAe,WAAU,yBAAyB;OAC9C,GAEN,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM"}
|
|
1
|
+
{"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\nimport { CossistantLogo } from \"./cossistant-branding\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_blocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorBlocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_unblocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorUnblocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_identified\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorIdentified\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center py-4\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<div className=\"flex size-5 items-center justify-center rounded-full bg-primary/10\">\n\t\t\t\t\t\t\t<CossistantLogo className=\"h-3 w-3 text-primary\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-full\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n\nConversationEvent.displayName = \"ConversationEvent\";\n"],"mappings":";;;;;;;AAkBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,kBACJ,QAAO,KAAK,8CAA8C,EACzD,WACA,CAAC;GACH,KAAK,oBACJ,QAAO,KAAK,gDAAgD,EAC3D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MAAI,WAAU;gBACd,oBAAC,kBAAe,WAAU,yBAAyB;OAC9C,GAEN,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM;;AAIf,kBAAkB,cAAc"}
|
|
@@ -3,6 +3,14 @@ import React from "react";
|
|
|
3
3
|
import { AvailableAIAgent, AvailableHumanAgent } from "@cossistant/types";
|
|
4
4
|
|
|
5
5
|
//#region src/support/components/conversation-timeline.d.ts
|
|
6
|
+
type ConversationTimelineToolProps = {
|
|
7
|
+
item: TimelineItem;
|
|
8
|
+
conversationId: string;
|
|
9
|
+
};
|
|
10
|
+
type ConversationTimelineToolDefinition = {
|
|
11
|
+
component: React.ComponentType<ConversationTimelineToolProps>;
|
|
12
|
+
};
|
|
13
|
+
type ConversationTimelineTools = Record<string, ConversationTimelineToolDefinition>;
|
|
6
14
|
type ConversationTimelineProps = {
|
|
7
15
|
conversationId: string;
|
|
8
16
|
items: TimelineItem[];
|
|
@@ -10,8 +18,9 @@ type ConversationTimelineProps = {
|
|
|
10
18
|
availableAIAgents: AvailableAIAgent[];
|
|
11
19
|
availableHumanAgents: AvailableHumanAgent[];
|
|
12
20
|
currentVisitorId?: string;
|
|
21
|
+
tools?: ConversationTimelineTools;
|
|
13
22
|
};
|
|
14
23
|
declare const ConversationTimelineList: React.FC<ConversationTimelineProps>;
|
|
15
24
|
//#endregion
|
|
16
|
-
export { ConversationTimelineList, ConversationTimelineProps };
|
|
25
|
+
export { ConversationTimelineList, ConversationTimelineProps, ConversationTimelineToolDefinition, ConversationTimelineToolProps, ConversationTimelineTools };
|
|
17
26
|
//# sourceMappingURL=conversation-timeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"conversation-timeline.d.ts","names":[],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":[],"mappings":";;;;;KAiCY,6BAAA;QACL;EADK,cAAA,EAAA,MAAA;AAKZ,CAAA;AAIY,KAJA,kCAAA,GAMX;EAGW,SAAA,EARA,KAAA,CAAM,aAQmB,CARL,6BAQK,CAAA;CAE7B;AAEY,KATR,yBAAA,GAA4B,MASpB,CAAA,MAAA,EAPnB,kCAOmB,CAAA;AACG,KALX,yBAAA,GAKW;EAEd,cAAA,EAAA,MAAA;EAAyB,KAAA,EAL1B,YAK0B,EAAA;EAGrB,SAAA,CAAA,EAAA,MAAA;qBANO;wBACG;;UAEd;;cAGI,0BAA0B,KAAA,CAAM,GAAG"}
|
|
@@ -1,51 +1,48 @@
|
|
|
1
1
|
import { cn } from "../utils/index.js";
|
|
2
2
|
import { TypingIndicator } from "./typing-indicator.js";
|
|
3
3
|
import { ConversationTimeline, ConversationTimelineContainer } from "../../primitives/conversation-timeline.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useConversationTimeline } from "../../hooks/use-conversation-timeline.js";
|
|
5
5
|
import { ConversationEvent } from "./conversation-event.js";
|
|
6
6
|
import { TimelineMessageGroup } from "./timeline-message-group.js";
|
|
7
|
-
import {
|
|
8
|
-
import { useConversationTyping } from "../../hooks/use-conversation-typing.js";
|
|
9
|
-
import { useMemo } from "react";
|
|
10
|
-
import { SenderType } from "@cossistant/types";
|
|
7
|
+
import { useCallback, useMemo } from "react";
|
|
11
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
-
import { AnimatePresence } from "motion/react";
|
|
13
9
|
|
|
14
10
|
//#region src/support/components/conversation-timeline.tsx
|
|
15
11
|
function extractEventPart(item) {
|
|
16
12
|
if (item.type !== "event") return null;
|
|
17
13
|
return item.parts.find((part) => part.type === "event") || null;
|
|
18
14
|
}
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
15
|
+
const EMPTY_SEEN_BY_IDS = Object.freeze([]);
|
|
16
|
+
const EMPTY_SEEN_BY_NAMES = Object.freeze([]);
|
|
17
|
+
const ConversationTimelineList = ({ conversationId, items: timelineItems, className, availableAIAgents = [], availableHumanAgents = [], currentVisitorId, tools }) => {
|
|
18
|
+
const timeline = useConversationTimeline({
|
|
19
|
+
conversationId,
|
|
23
20
|
items: timelineItems,
|
|
24
|
-
|
|
25
|
-
currentViewerId: currentVisitorId,
|
|
26
|
-
viewerType: SenderType.VISITOR
|
|
21
|
+
currentVisitorId
|
|
27
22
|
});
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
const typingIndicatorParticipants = useMemo(() => timeline.typingParticipants.map((participant) => ({
|
|
24
|
+
id: participant.id,
|
|
25
|
+
type: participant.type
|
|
26
|
+
})), [timeline.typingParticipants]);
|
|
27
|
+
const seenNameLookup = useMemo(() => {
|
|
28
|
+
const map = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const agent of availableHumanAgents) if (agent.name) map.set(agent.id, agent.name);
|
|
30
|
+
for (const agent of availableAIAgents) if (agent.name) map.set(agent.id, agent.name);
|
|
31
|
+
return map;
|
|
32
|
+
}, [availableHumanAgents, availableAIAgents]);
|
|
33
|
+
const getSeenByNames = useCallback((ids = EMPTY_SEEN_BY_IDS) => {
|
|
34
|
+
if (ids.length === 0 || seenNameLookup.size === 0) return EMPTY_SEEN_BY_NAMES;
|
|
35
|
+
const uniqueNames = /* @__PURE__ */ new Set();
|
|
36
|
+
const names = [];
|
|
37
|
+
for (const id of ids) {
|
|
38
|
+
const name = seenNameLookup.get(id);
|
|
39
|
+
if (!name || uniqueNames.has(name)) continue;
|
|
40
|
+
uniqueNames.add(name);
|
|
41
|
+
names.push(name);
|
|
35
42
|
}
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (entry.actorType === "user") return {
|
|
40
|
-
id: entry.actorId,
|
|
41
|
-
type: "team_member"
|
|
42
|
-
};
|
|
43
|
-
if (entry.actorType === "ai_agent") return {
|
|
44
|
-
id: entry.actorId,
|
|
45
|
-
type: "ai"
|
|
46
|
-
};
|
|
47
|
-
return null;
|
|
48
|
-
}).filter((participant) => participant !== null), [typingEntries]);
|
|
43
|
+
if (names.length === 0) return EMPTY_SEEN_BY_NAMES;
|
|
44
|
+
return Object.freeze(names);
|
|
45
|
+
}, [seenNameLookup]);
|
|
49
46
|
return /* @__PURE__ */ jsx(ConversationTimeline, {
|
|
50
47
|
autoScroll: true,
|
|
51
48
|
className: cn("overflow-y-auto scroll-smooth px-3 py-6", "scrollbar-thin scrollbar-thumb-co-background-300 scrollbar-track-transparent", "h-full w-full", className),
|
|
@@ -53,37 +50,46 @@ const ConversationTimelineList = ({ conversationId, items: timelineItems, classN
|
|
|
53
50
|
items: timelineItems,
|
|
54
51
|
children: /* @__PURE__ */ jsxs(ConversationTimelineContainer, {
|
|
55
52
|
className: "flex min-h-full w-full flex-col gap-3",
|
|
56
|
-
children: [
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const eventPart = extractEventPart(item.item);
|
|
62
|
-
if (!eventPart) return null;
|
|
63
|
-
return /* @__PURE__ */ jsx(ConversationEvent, {
|
|
64
|
-
availableAIAgents,
|
|
65
|
-
availableHumanAgents,
|
|
66
|
-
createdAt: item.item.createdAt,
|
|
67
|
-
event: eventPart
|
|
68
|
-
}, item.item.id || `timeline-event-${index}`);
|
|
69
|
-
}
|
|
70
|
-
const seenByIds = index === lastVisitorMessageGroupIndex && item.lastMessageId ? getMessageSeenBy(item.lastMessageId) : [];
|
|
71
|
-
const groupKey = item.items?.[0]?.id || `group-${index}`;
|
|
72
|
-
return /* @__PURE__ */ jsx(TimelineMessageGroup, {
|
|
53
|
+
children: [timeline.groupedMessages.items.map((item, index) => {
|
|
54
|
+
if (item.type === "timeline_event") {
|
|
55
|
+
const eventPart = extractEventPart(item.item);
|
|
56
|
+
if (!eventPart) return null;
|
|
57
|
+
return /* @__PURE__ */ jsx(ConversationEvent, {
|
|
73
58
|
availableAIAgents,
|
|
74
59
|
availableHumanAgents,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
60
|
+
createdAt: item.item.createdAt,
|
|
61
|
+
event: eventPart
|
|
62
|
+
}, item.item.id ?? `timeline-event-${item.item.createdAt}`);
|
|
63
|
+
}
|
|
64
|
+
if (item.type === "timeline_tool") {
|
|
65
|
+
const toolName = item.tool ?? item.item.tool ?? item.item.type;
|
|
66
|
+
const toolDefinition = toolName ? tools?.[toolName] : void 0;
|
|
67
|
+
if (!toolDefinition) return null;
|
|
68
|
+
const ToolComponent = toolDefinition.component;
|
|
69
|
+
const toolKey = item.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;
|
|
70
|
+
return /* @__PURE__ */ jsx(ToolComponent, {
|
|
71
|
+
conversationId,
|
|
72
|
+
item: item.item
|
|
73
|
+
}, toolKey);
|
|
74
|
+
}
|
|
75
|
+
const seenByIds = index === timeline.lastVisitorMessageGroupIndex && item.lastMessageId ? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId) : EMPTY_SEEN_BY_IDS;
|
|
76
|
+
const seenByNames = seenByIds.length > 0 ? getSeenByNames(seenByIds) : EMPTY_SEEN_BY_NAMES;
|
|
77
|
+
const groupKey = item.lastMessageId ?? item.items?.[0]?.id ?? `group-${item.items?.[0]?.createdAt ?? index}`;
|
|
78
|
+
return /* @__PURE__ */ jsx(TimelineMessageGroup, {
|
|
79
|
+
availableAIAgents,
|
|
80
|
+
availableHumanAgents,
|
|
81
|
+
currentVisitorId,
|
|
82
|
+
items: item.items || [],
|
|
83
|
+
seenByIds,
|
|
84
|
+
seenByNames
|
|
85
|
+
}, groupKey);
|
|
80
86
|
}), /* @__PURE__ */ jsx("div", {
|
|
81
87
|
className: "h-6 w-full",
|
|
82
|
-
children:
|
|
88
|
+
children: typingIndicatorParticipants.length > 0 ? /* @__PURE__ */ jsx(TypingIndicator, {
|
|
83
89
|
availableAIAgents,
|
|
84
90
|
availableHumanAgents,
|
|
85
91
|
className: "mt-2",
|
|
86
|
-
participants:
|
|
92
|
+
participants: typingIndicatorParticipants
|
|
87
93
|
}) : null
|
|
88
94
|
})]
|
|
89
95
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-timeline.js","names":["ConversationTimelineList: React.FC<ConversationTimelineProps>","
|
|
1
|
+
{"version":3,"file":"conversation-timeline.js","names":["EMPTY_SEEN_BY_IDS: readonly string[]","EMPTY_SEEN_BY_NAMES: readonly string[]","ConversationTimelineList: React.FC<ConversationTimelineProps>","names: string[]","PrimitiveConversationTimeline"],"sources":["../../../src/support/components/conversation-timeline.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport type {\n\tTimelineItem,\n\tTimelinePartEvent,\n} from \"@cossistant/types/api/timeline-item\";\nimport type React from \"react\";\nimport { useCallback, useMemo } from \"react\";\nimport { useConversationTimeline } from \"../../hooks/use-conversation-timeline\";\nimport {\n\tConversationTimelineContainer,\n\tConversationTimeline as PrimitiveConversationTimeline,\n} from \"../../primitives/conversation-timeline\";\nimport { cn } from \"../utils\";\nimport { ConversationEvent } from \"./conversation-event\";\nimport { TimelineMessageGroup } from \"./timeline-message-group\";\nimport { TypingIndicator, type TypingParticipant } from \"./typing-indicator\";\n\n// Helper to extract event part from timeline item\nfunction extractEventPart(item: TimelineItem): TimelinePartEvent | null {\n\tif (item.type !== \"event\") {\n\t\treturn null;\n\t}\n\n\tconst eventPart = item.parts.find(\n\t\t(part): part is TimelinePartEvent => part.type === \"event\"\n\t);\n\n\treturn eventPart || null;\n}\n\nconst EMPTY_SEEN_BY_IDS: readonly string[] = Object.freeze([]);\nconst EMPTY_SEEN_BY_NAMES: readonly string[] = Object.freeze([]);\n\nexport type ConversationTimelineToolProps = {\n\titem: TimelineItem;\n\tconversationId: string;\n};\n\nexport type ConversationTimelineToolDefinition = {\n\tcomponent: React.ComponentType<ConversationTimelineToolProps>;\n};\n\nexport type ConversationTimelineTools = Record<\n\tstring,\n\tConversationTimelineToolDefinition\n>;\n\nexport type ConversationTimelineProps = {\n\tconversationId: string;\n\titems: TimelineItem[];\n\tclassName?: string;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcurrentVisitorId?: string;\n\ttools?: ConversationTimelineTools;\n};\n\nexport const ConversationTimelineList: React.FC<ConversationTimelineProps> = ({\n\tconversationId,\n\titems: timelineItems,\n\tclassName,\n\tavailableAIAgents = [],\n\tavailableHumanAgents = [],\n\tcurrentVisitorId,\n\ttools,\n}) => {\n\tconst timeline = useConversationTimeline({\n\t\tconversationId,\n\t\titems: timelineItems,\n\t\tcurrentVisitorId,\n\t});\n\n\tconst typingIndicatorParticipants = useMemo(\n\t\t() =>\n\t\t\ttimeline.typingParticipants.map<TypingParticipant>((participant) => ({\n\t\t\t\tid: participant.id,\n\t\t\t\ttype: participant.type,\n\t\t\t})),\n\t\t[timeline.typingParticipants]\n\t);\n\n\tconst seenNameLookup = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\n\t\tfor (const agent of availableHumanAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\tfor (const agent of availableAIAgents) {\n\t\t\tif (agent.name) {\n\t\t\t\tmap.set(agent.id, agent.name);\n\t\t\t}\n\t\t}\n\n\t\treturn map;\n\t}, [availableHumanAgents, availableAIAgents]);\n\n\tconst getSeenByNames = useCallback(\n\t\t(ids: readonly string[] = EMPTY_SEEN_BY_IDS): readonly string[] => {\n\t\t\tif (ids.length === 0 || seenNameLookup.size === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\tconst uniqueNames = new Set<string>();\n\t\t\tconst names: string[] = [];\n\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst name = seenNameLookup.get(id);\n\t\t\t\tif (!name || uniqueNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tuniqueNames.add(name);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\n\t\t\tif (names.length === 0) {\n\t\t\t\treturn EMPTY_SEEN_BY_NAMES;\n\t\t\t}\n\n\t\t\treturn Object.freeze(names);\n\t\t},\n\t\t[seenNameLookup]\n\t);\n\n\treturn (\n\t\t<PrimitiveConversationTimeline\n\t\t\tautoScroll={true}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-y-auto scroll-smooth px-3 py-6\",\n\t\t\t\t\"scrollbar-thin scrollbar-thumb-co-background-300 scrollbar-track-transparent\",\n\t\t\t\t\"h-full w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tid=\"conversation-timeline\"\n\t\t\titems={timelineItems}\n\t\t>\n\t\t\t<ConversationTimelineContainer className=\"flex min-h-full w-full flex-col gap-3\">\n\t\t\t\t{timeline.groupedMessages.items.map((item, index) => {\n\t\t\t\t\tif (item.type === \"timeline_event\") {\n\t\t\t\t\t\t// Extract event data from parts\n\t\t\t\t\t\tconst eventPart = extractEventPart(item.item);\n\n\t\t\t\t\t\t// Only render if we have valid event data\n\t\t\t\t\t\tif (!eventPart) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ConversationEvent\n\t\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\t\tcreatedAt={item.item.createdAt}\n\t\t\t\t\t\t\t\tevent={eventPart}\n\t\t\t\t\t\t\t\tkey={item.item.id ?? `timeline-event-${item.item.createdAt}`}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (item.type === \"timeline_tool\") {\n\t\t\t\t\t\tconst toolName = item.tool ?? item.item.tool ?? item.item.type;\n\t\t\t\t\t\tconst toolDefinition = toolName ? tools?.[toolName] : undefined;\n\n\t\t\t\t\t\tif (!toolDefinition) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ToolComponent = toolDefinition.component;\n\n\t\t\t\t\t\tconst toolKey =\n\t\t\t\t\t\t\titem.item.id ?? `${toolName}-${item.item.createdAt}-${index}`;\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<ToolComponent\n\t\t\t\t\t\t\t\tconversationId={conversationId}\n\t\t\t\t\t\t\t\titem={item.item}\n\t\t\t\t\t\t\t\tkey={toolKey}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only show seen indicator on the LAST message group sent by the visitor\n\t\t\t\t\tconst isLastVisitorGroup =\n\t\t\t\t\t\tindex === timeline.lastVisitorMessageGroupIndex;\n\t\t\t\t\tconst seenByIds =\n\t\t\t\t\t\tisLastVisitorGroup && item.lastMessageId\n\t\t\t\t\t\t\t? timeline.groupedMessages.getMessageSeenBy(item.lastMessageId)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_IDS;\n\t\t\t\t\tconst seenByNames =\n\t\t\t\t\t\tseenByIds.length > 0\n\t\t\t\t\t\t\t? getSeenByNames(seenByIds)\n\t\t\t\t\t\t\t: EMPTY_SEEN_BY_NAMES;\n\n\t\t\t\t\t// Use first timeline item ID as stable key\n\t\t\t\t\tconst groupKey =\n\t\t\t\t\t\titem.lastMessageId ??\n\t\t\t\t\t\titem.items?.[0]?.id ??\n\t\t\t\t\t\t`group-${item.items?.[0]?.createdAt ?? index}`;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<TimelineMessageGroup\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tcurrentVisitorId={currentVisitorId}\n\t\t\t\t\t\t\titems={item.items || []}\n\t\t\t\t\t\t\tkey={groupKey}\n\t\t\t\t\t\t\tseenByIds={seenByIds}\n\t\t\t\t\t\t\tseenByNames={seenByNames}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t<div className=\"h-6 w-full\">\n\t\t\t\t\t{typingIndicatorParticipants.length > 0 ? (\n\t\t\t\t\t\t<TypingIndicator\n\t\t\t\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\tparticipants={typingIndicatorParticipants}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</ConversationTimelineContainer>\n\t\t</PrimitiveConversationTimeline>\n\t);\n};\n"],"mappings":"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"}
|