@droppii-org/chat-sdk 0.1.5 → 0.1.7

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.
Files changed (101) hide show
  1. package/dist/components/cannedResponse/CannedResponseBody.d.ts.map +1 -1
  2. package/dist/components/cannedResponse/CannedResponseBody.js +54 -3
  3. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  4. package/dist/components/conversation/ConversationBySessionItem.js +7 -2
  5. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  6. package/dist/components/conversation/DeskConversationList.js +22 -17
  7. package/dist/components/message/MessageHeader.d.ts +2 -2
  8. package/dist/components/message/MessageHeader.d.ts.map +1 -1
  9. package/dist/components/message/MessageHeader.js +5 -2
  10. package/dist/components/message/MessageList.d.ts.map +1 -1
  11. package/dist/components/message/MessageList.js +31 -22
  12. package/dist/components/message/SelectSession.d.ts.map +1 -1
  13. package/dist/components/message/SelectSession.js +7 -9
  14. package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
  15. package/dist/components/message/footer/ActionBar.js +3 -2
  16. package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -1
  17. package/dist/components/message/footer/CannedResponsePlugin.js +37 -1
  18. package/dist/components/message/footer/ComposerEditor.d.ts +7 -0
  19. package/dist/components/message/footer/ComposerEditor.d.ts.map +1 -0
  20. package/dist/components/message/footer/ComposerEditor.js +13 -0
  21. package/dist/components/message/footer/ComposerTabs.d.ts +9 -0
  22. package/dist/components/message/footer/ComposerTabs.d.ts.map +1 -0
  23. package/dist/components/message/footer/ComposerTabs.js +37 -0
  24. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  25. package/dist/components/message/footer/EnterHandler.js +10 -1
  26. package/dist/components/message/footer/index.d.ts +2 -2
  27. package/dist/components/message/footer/index.d.ts.map +1 -1
  28. package/dist/components/message/footer/index.js +45 -9
  29. package/dist/components/message/item/QuoteMessage.d.ts.map +1 -1
  30. package/dist/components/message/item/QuoteMessage.js +44 -35
  31. package/dist/components/message/item/index.d.ts.map +1 -1
  32. package/dist/components/message/item/index.js +11 -1
  33. package/dist/components/session/DeskAssignedSession.d.ts.map +1 -1
  34. package/dist/components/session/DeskAssignedSession.js +14 -109
  35. package/dist/components/session/DeskTeamInbox.d.ts +3 -0
  36. package/dist/components/session/DeskTeamInbox.d.ts.map +1 -0
  37. package/dist/components/session/DeskTeamInbox.js +56 -0
  38. package/dist/components/session/SessionFilterMenu.d.ts +13 -0
  39. package/dist/components/session/SessionFilterMenu.d.ts.map +1 -0
  40. package/dist/components/session/SessionFilterMenu.js +27 -0
  41. package/dist/components/session/sessionMenuItems.d.ts +26 -0
  42. package/dist/components/session/sessionMenuItems.d.ts.map +1 -0
  43. package/dist/components/session/sessionMenuItems.js +108 -0
  44. package/dist/hooks/conversation/useConversationPreview.d.ts +12 -0
  45. package/dist/hooks/conversation/useConversationPreview.d.ts.map +1 -0
  46. package/dist/hooks/conversation/useConversationPreview.js +22 -0
  47. package/dist/hooks/message/useConversationMessages.d.ts +27 -0
  48. package/dist/hooks/message/useConversationMessages.d.ts.map +1 -0
  49. package/dist/hooks/message/useConversationMessages.js +29 -0
  50. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  51. package/dist/hooks/message/usePullSessionMessages.d.ts +9 -0
  52. package/dist/hooks/message/usePullSessionMessages.d.ts.map +1 -0
  53. package/dist/hooks/message/usePullSessionMessages.js +27 -0
  54. package/dist/hooks/message/useSendMessage.d.ts +8 -6
  55. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  56. package/dist/hooks/message/useSendMessage.js +8 -8
  57. package/dist/hooks/session/useConversationSessionState.d.ts +21 -0
  58. package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -0
  59. package/dist/hooks/session/useConversationSessionState.js +41 -0
  60. package/dist/hooks/session/useGetSession.d.ts.map +1 -1
  61. package/dist/hooks/session/useGetSession.js +138 -52
  62. package/dist/hooks/session/useGetTeamSessionSummary.d.ts +3 -0
  63. package/dist/hooks/session/useGetTeamSessionSummary.d.ts.map +1 -0
  64. package/dist/hooks/session/useGetTeamSessionSummary.js +12 -0
  65. package/dist/hooks/session/useIsJoinedGroup.d.ts +5 -0
  66. package/dist/hooks/session/useIsJoinedGroup.d.ts.map +1 -0
  67. package/dist/hooks/session/useIsJoinedGroup.js +24 -0
  68. package/dist/hooks/session/useJoinGroupFlow.d.ts +12 -0
  69. package/dist/hooks/session/useJoinGroupFlow.d.ts.map +1 -0
  70. package/dist/hooks/session/useJoinGroupFlow.js +59 -0
  71. package/dist/hooks/session/useJoinSession.d.ts +3 -0
  72. package/dist/hooks/session/useJoinSession.d.ts.map +1 -0
  73. package/dist/hooks/session/useJoinSession.js +38 -0
  74. package/dist/hooks/user/useCurrentUserAccountType.d.ts +3 -0
  75. package/dist/hooks/user/useCurrentUserAccountType.d.ts.map +1 -0
  76. package/dist/hooks/user/useCurrentUserAccountType.js +30 -0
  77. package/dist/locales/vi/common.json +12 -2
  78. package/dist/services/query.d.ts +5 -0
  79. package/dist/services/query.d.ts.map +1 -1
  80. package/dist/services/query.js +5 -0
  81. package/dist/services/routes.d.ts +5 -0
  82. package/dist/services/routes.d.ts.map +1 -1
  83. package/dist/services/routes.js +5 -0
  84. package/dist/store/conversation.d.ts.map +1 -1
  85. package/dist/store/conversation.js +41 -12
  86. package/dist/store/session.js +1 -1
  87. package/dist/styles/global.css +1 -1
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/dist/types/chat.d.ts +18 -1
  90. package/dist/types/chat.d.ts.map +1 -1
  91. package/dist/types/chat.js +9 -0
  92. package/dist/types/dto.d.ts +87 -0
  93. package/dist/types/dto.d.ts.map +1 -1
  94. package/dist/utils/events.d.ts +1 -0
  95. package/dist/utils/events.d.ts.map +1 -1
  96. package/dist/utils/messageTransform.d.ts +5 -0
  97. package/dist/utils/messageTransform.d.ts.map +1 -0
  98. package/dist/utils/messageTransform.js +106 -0
  99. package/dist/utils/queryHelpers.d.ts.map +1 -1
  100. package/dist/utils/queryHelpers.js +2 -0
  101. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AAuBA,MAAM,CAAC,OAAO,UAAU,YAAY,SAmGnC"}
1
+ {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AAuBA,MAAM,CAAC,OAAO,UAAU,YAAY,SAiHnC"}
@@ -7,9 +7,10 @@ import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND
7
7
  import { $isQuoteNode } from "@lexical/rich-text";
8
8
  import { useMessageFooterContext } from ".";
9
9
  import useConversationStore from "../../../store/conversation";
10
+ import { emit } from "../../../utils/events";
10
11
  export default function EnterHandler() {
11
12
  const [editor] = useLexicalComposerContext();
12
- const { onSendMessage, listUploadFiles } = useMessageFooterContext();
13
+ const { onSendMessage, isInternal, listUploadFiles, isOpenCanned, activeCannedIndex, } = useMessageFooterContext();
13
14
  const shiftEnterCount = useRef(0);
14
15
  const quotedMessage = useConversationStore((state) => state.quotedMessage);
15
16
  useEffect(() => {
@@ -18,6 +19,10 @@ export default function EnterHandler() {
18
19
  if (!event.shiftKey) {
19
20
  event.preventDefault();
20
21
  shiftEnterCount.current = 0;
22
+ if (isOpenCanned) {
23
+ emit("CANNED_RESPONSE_SELECT", activeCannedIndex);
24
+ return true;
25
+ }
21
26
  let plainText = "";
22
27
  let richText = "";
23
28
  // lấy plain text & html
@@ -34,6 +39,7 @@ export default function EnterHandler() {
34
39
  plainText,
35
40
  richText,
36
41
  type: hasFiles ? "file" : "text",
42
+ isInternal,
37
43
  });
38
44
  }
39
45
  editor.update(() => {
@@ -85,6 +91,9 @@ export default function EnterHandler() {
85
91
  listUploadFiles,
86
92
  quotedMessage,
87
93
  quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID,
94
+ isInternal,
95
+ isOpenCanned,
96
+ activeCannedIndex,
88
97
  ]);
89
98
  return null;
90
99
  }
@@ -1,7 +1,7 @@
1
1
  import { MessageFooterContextType } from "../../../types/chat";
2
- import { ISessionByStatus } from "../../../store/type";
2
+ import { ISessionResponse } from "../../../types/dto";
3
3
  interface MessageFooterProps {
4
- currentSession?: ISessionByStatus;
4
+ currentSession?: ISessionResponse;
5
5
  openCreateCannedModal?: () => void;
6
6
  }
7
7
  export declare const MessageFooterContext: import("react").Context<MessageFooterContextType>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAK/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQvD,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AASD,eAAO,MAAM,oBAAoB,mDAM/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,4CAG5B,kBAAkB,4CAiHpB,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAEL,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAWtD,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AASD,eAAO,MAAM,oBAAoB,mDAY/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,4CAG5B,kBAAkB,4CAkJpB,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -1,16 +1,14 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { LexicalComposer } from "@lexical/react/LexicalComposer";
4
- import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
5
- import { ContentEditable } from "@lexical/react/LexicalContentEditable";
6
- import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
7
4
  import { ToolbarPlugin } from "./ToolbarPlugin";
8
5
  import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
9
6
  import { ListPlugin } from "@lexical/react/LexicalListPlugin";
10
7
  import { editorTheme, editorNodes, editorOnError } from "./editorConfig";
11
- import { createContext, useCallback, useContext, useState } from "react";
8
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
12
9
  import EnterHandler from "./EnterHandler";
13
10
  import ActionBar from "./ActionBar";
11
+ import { AccountType, } from "../../../types/chat";
14
12
  import { useSendMessage } from "../../../hooks/message/useSendMessage";
15
13
  import { Popover } from "antd";
16
14
  import FilePreview from "./FilePreview";
@@ -21,6 +19,9 @@ import CannedResponse from "../../cannedResponse";
21
19
  import { CannedResponseTriggerPlugin } from "./CannedResponsePlugin";
22
20
  import { useBoolean } from "ahooks";
23
21
  import useAuthStore from "../../../store/auth";
22
+ import { useCurrentUserAccountType } from "../../../hooks/user/useCurrentUserAccountType";
23
+ import ComposerTabs from "./ComposerTabs";
24
+ import ComposerEditor from "./ComposerEditor";
24
25
  const initialConfig = {
25
26
  namespace: "ChatInput",
26
27
  theme: editorTheme,
@@ -29,22 +30,43 @@ const initialConfig = {
29
30
  };
30
31
  export const MessageFooterContext = createContext({
31
32
  onSendMessage: () => { },
33
+ isInternal: false,
34
+ setIsInternal: () => { },
32
35
  listUploadFiles: [],
33
36
  setListUploadFiles: () => { },
34
37
  isOpenCanned: false,
35
38
  setIsOpenCanned: () => { },
39
+ activeCannedIndex: 0,
40
+ setActiveCannedIndex: () => { },
41
+ maxActiveCannedIndex: 0,
42
+ setMaxActiveCannedIndex: () => { },
36
43
  });
37
44
  export const useMessageFooterContext = () => useContext(MessageFooterContext);
38
45
  const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
39
46
  const { t } = useTranslation();
40
47
  const { sendTextMessage, sendMergeMessage } = useSendMessage();
48
+ const accountType = useCurrentUserAccountType();
41
49
  const [listUploadFiles, setListUploadFiles] = useState([]);
42
- const [isOpenCanned, { setTrue: openCanned, setFalse: closeCanned, toggle: toggleCanned },] = useBoolean(false);
50
+ const [isOpenCanned, { setTrue: openCanned, setFalse: closeCanned },] = useBoolean(false);
43
51
  const isCrm = useAuthStore((state) => state.isCrm);
44
52
  const [cannedQuery, setCannedQuery] = useState("");
45
- const onSendMessage = useCallback(async ({ plainText, richText, type, }) => {
53
+ const [activeCannedIndex, setActiveCannedIndex] = useState(0);
54
+ const [maxActiveCannedIndex, setMaxActiveCannedIndex] = useState(0);
55
+ const [isInternal, setIsInternal] = useState(false);
56
+ const canUseInternal = accountType === AccountType.Staff;
57
+ useEffect(() => {
58
+ if (!canUseInternal && isInternal) {
59
+ setIsInternal(false);
60
+ }
61
+ }, [canUseInternal, isInternal]);
62
+ const placeholderText = useMemo(() => {
63
+ return isInternal
64
+ ? t("enter_internal_message")
65
+ : t("enter_message");
66
+ }, [isInternal, t]);
67
+ const onSendMessage = useCallback(async ({ plainText, richText, type, isInternal, }) => {
46
68
  if (type === "text") {
47
- sendTextMessage({ plainText, richText, currentSession });
69
+ sendTextMessage({ plainText, richText, currentSession, isInternal });
48
70
  }
49
71
  else {
50
72
  sendMergeMessage({
@@ -52,6 +74,7 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
52
74
  richText,
53
75
  files: listUploadFiles,
54
76
  currentSession,
77
+ isInternal,
55
78
  });
56
79
  }
57
80
  setListUploadFiles([]);
@@ -64,14 +87,27 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
64
87
  setCannedQuery(query);
65
88
  openCanned();
66
89
  }, [openCanned]);
90
+ const setIsOpenCanned = useCallback((open) => {
91
+ if (open) {
92
+ openCanned();
93
+ return;
94
+ }
95
+ closeCanned();
96
+ }, [closeCanned, openCanned]);
67
97
  return (_jsx(MessageFooterContext.Provider, { value: {
68
98
  onSendMessage,
99
+ isInternal,
100
+ setIsInternal,
69
101
  listUploadFiles,
70
102
  setListUploadFiles,
71
103
  isOpenCanned,
72
- setIsOpenCanned: toggleCanned,
104
+ setIsOpenCanned,
105
+ activeCannedIndex,
106
+ setActiveCannedIndex,
107
+ maxActiveCannedIndex,
108
+ setMaxActiveCannedIndex,
73
109
  }, children: _jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Popover, { open: isOpenCanned, content: _jsx(CannedResponse, { onClose: closeCanned, openCreateCannedModal: onOpenCreateCannedModal, cannedQuery: cannedQuery }), placement: "topLeft", trigger: "click", arrow: false, classNames: {
74
110
  body: "mx-1 !p-0",
75
- }, destroyOnHidden: true, children: _jsxs("div", { className: "border-t pb-2 flex flex-col gap-1 bg-white", children: [_jsx(QuotedMessageFooter, {}), listUploadFiles.length > 0 && _jsx(FilePreview, {}), _jsx(ToolbarPlugin, {}), _jsx("div", { className: "relative px-4", children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "border border-indigo-500 rounded-md bg-blue-100 min-h-[64px] max-h-[140px] overflow-y-auto px-3 py-2 text-sm" }), ErrorBoundary: LexicalErrorBoundary, "aria-placeholder": t("enter_message"), placeholder: _jsx("div", { className: "absolute top-2 left-7 pointer-events-none", children: _jsx("p", { className: "text-gray-500 text-sm", children: t("enter_message") }) }) }) }), _jsx(ActionBar, {})] }) }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {}), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }) }));
111
+ }, destroyOnHidden: true, children: _jsxs("div", { className: "border-t pb-2 flex flex-col gap-1 bg-white", children: [_jsx(QuotedMessageFooter, {}), listUploadFiles.length > 0 && _jsx(FilePreview, {}), _jsx(ToolbarPlugin, {}), _jsxs("div", { children: [_jsx(ComposerTabs, { canUseInternal: canUseInternal, hintText: t("canned_response_hint"), isInternal: isInternal, setIsInternal: setIsInternal }), _jsx(ComposerEditor, { isInternal: isInternal, placeholderText: placeholderText })] }), _jsx(ActionBar, {})] }) }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {}), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }) }));
76
112
  };
77
113
  export default MessageFooterProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"QuoteMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/QuoteMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAUnE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,gBAAgB,GAAI,2CAIvB,qBAAqB,4CAuGvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"QuoteMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/QuoteMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,yBAAyB,CAAC;AAUnE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAKD,QAAA,MAAM,gBAAgB,GAAI,2CAIvB,qBAAqB,4CA0GvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -5,62 +5,71 @@ import { useTranslation } from "react-i18next";
5
5
  import clsx from "clsx";
6
6
  import MediaPreviewIcon from "../MediaPreviewIcon";
7
7
  import useAuthStore from "../../../store/auth";
8
- import { useCallback } from "react";
8
+ import { useCallback, useMemo } from "react";
9
9
  import useConversationStore from "../../../store/conversation";
10
+ const capitalize = (text) => text ? text.charAt(0).toUpperCase() + text.slice(1) : "";
10
11
  const QuoteMessageItem = ({ message, isMine, onPressQuoteMessage, }) => {
11
12
  var _a, _b, _c, _d, _e, _f;
12
13
  const userID = useAuthStore((state) => state.userID);
13
14
  const isCrm = useAuthStore((state) => state.isCrm);
14
15
  const conversationData = useConversationStore((state) => state.conversationData);
15
16
  const { t } = useTranslation();
17
+ const isChatSupport = useMemo(() => {
18
+ var _a, _b, _c;
19
+ try {
20
+ return (_c = (_b = (_a = JSON.parse((conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex) || "{}")) === null || _a === void 0 ? void 0 : _a.sessionInfo) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.botId;
21
+ }
22
+ catch (_d) {
23
+ return false;
24
+ }
25
+ }, [conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex]);
26
+ const getDisplayName = useCallback(({ name, id }) => {
27
+ if (!name)
28
+ return "";
29
+ if (!isCrm && isChatSupport) {
30
+ return conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName;
31
+ }
32
+ if (id === userID)
33
+ return t("you");
34
+ return name;
35
+ }, [isCrm, isChatSupport, conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName, userID, t]);
16
36
  const parseReplyText = useCallback(() => {
17
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
37
+ var _a;
18
38
  const quoteMsg = (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage;
39
+ if (!quoteMsg)
40
+ return "";
19
41
  const senderID = message === null || message === void 0 ? void 0 : message.sendID;
20
- const senderName = message === null || message === void 0 ? void 0 : message.senderNickname;
21
42
  const targetID = quoteMsg === null || quoteMsg === void 0 ? void 0 : quoteMsg.sendID;
43
+ const senderName = message === null || message === void 0 ? void 0 : message.senderNickname;
22
44
  const targetName = quoteMsg === null || quoteMsg === void 0 ? void 0 : quoteMsg.senderNickname;
23
- const isSelfReply = senderID && targetID && senderID === targetID;
45
+ const isSelfReply = senderID === targetID;
24
46
  const isFromMe = senderID === userID;
25
47
  const isReplyToMe = targetID === userID;
26
- const convoName = conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName;
27
- const isChatSupport = (_e = (_d = (_c = (_b = JSON === null || JSON === void 0 ? void 0 : JSON.parse) === null || _b === void 0 ? void 0 : _b.call(JSON, (conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex) || "{}")) === null || _c === void 0 ? void 0 : _c.sessionInfo) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.botId;
28
- // format name theo role
29
- const formatName = (name, isTarget = false) => {
30
- if (!name)
31
- return "";
32
- // Customer view → luôn dùng tên conversation
33
- if (!isCrm && isChatSupport) {
34
- return convoName;
35
- }
36
- // CX view
37
- if (isTarget && targetID === userID) {
38
- return t("you");
39
- }
40
- if (!isTarget && isFromMe) {
41
- return t("you");
42
- }
43
- return name;
44
- };
45
- // 1. self reply
48
+ const senderDisplay = getDisplayName({ name: senderName, id: senderID });
49
+ const targetDisplay = getDisplayName({ name: targetName, id: targetID });
46
50
  if (isSelfReply) {
47
51
  return isFromMe
48
- ? ((_g = (_f = t("replied_yourself")) === null || _f === void 0 ? void 0 : _f.charAt(0)) === null || _g === void 0 ? void 0 : _g.toUpperCase()) +
49
- ((_h = t("replied_yourself")) === null || _h === void 0 ? void 0 : _h.slice(1)) // Đã trả lời chính mình
50
- : `${formatName(senderName)} ${t("replied_yourself")}`;
52
+ ? capitalize(t("replied_yourself"))
53
+ : `${senderDisplay} ${t("replied_yourself")}`;
51
54
  }
52
- // 2. mình reply người khác
53
55
  if (isFromMe) {
54
- return `${((_k = (_j = t("replied")) === null || _j === void 0 ? void 0 : _j.charAt(0)) === null || _k === void 0 ? void 0 : _k.toUpperCase()) + ((_l = t("replied")) === null || _l === void 0 ? void 0 : _l.slice(1))} ${formatName(targetName || "", true)}`;
56
+ return `${capitalize(t("replied"))} ${targetDisplay}`;
55
57
  }
56
- // 3. người khác reply mình
57
58
  if (isReplyToMe) {
58
- return `${formatName(senderName || "")} ${t("replied_you")}`;
59
+ return `${senderDisplay} ${t("replied_you")}`;
59
60
  }
60
- // 4. người khác ↔ người khác
61
- return `${formatName(senderName || "")} ${t("replied")} ${formatName(targetName || "", true)}`;
62
- }, [message, userID, isCrm, conversationData, t]);
63
- return (_jsxs("div", { className: clsx("mb-[-4px] flex flex-col gap-1 flex-1 w-full", isMine ? "items-end" : "items-start"), children: [_jsxs("div", { className: "flex flex-row flex-1 items-center gap-2", children: [_jsx(Icon, { icon: "arrow-reply-b", size: 16, className: "text-blue-500" }), _jsx("span", { className: "text-xs text-gray-500", children: parseReplyText() })] }), _jsxs("div", { className: "flex flex-row flex-1 max-w-full items-center gap-2 border border-gray-200 rounded-2xl min-w-0 px-3 py-2 bg-gray-100 cursor-pointer", onClick: () => {
61
+ return `${senderDisplay} ${t("replied")} ${targetDisplay}`;
62
+ }, [
63
+ message,
64
+ userID,
65
+ isCrm,
66
+ isChatSupport,
67
+ conversationData,
68
+ t,
69
+ getDisplayName,
70
+ ]);
71
+ const replyText = useMemo(() => parseReplyText(), [parseReplyText]);
72
+ return (_jsxs("div", { className: clsx("mb-[-4px] flex flex-col gap-1 flex-1 w-full", isMine ? "items-end" : "items-start"), children: [_jsxs("div", { className: "flex flex-row flex-1 items-center gap-2", children: [_jsx(Icon, { icon: "arrow-reply-b", size: 16, className: "text-blue-500" }), _jsx("span", { className: "text-xs text-gray-500", children: replyText })] }), _jsxs("div", { className: "flex flex-row flex-1 max-w-full items-center gap-2 border border-gray-200 rounded-2xl min-w-0 px-3 py-2 bg-gray-100 cursor-pointer", onClick: () => {
64
73
  var _a, _b;
65
74
  return onPressQuoteMessage === null || onPressQuoteMessage === void 0 ? void 0 : onPressQuoteMessage(((_b = (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage) === null || _b === void 0 ? void 0 : _b.clientMsgID) || "");
66
75
  }, children: [_jsx(MediaPreviewIcon, { message: (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage }), _jsx("span", { className: "text-sm text-gray-500 truncate", children: generateContentBasedOnMessageType((_c = (_b = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _b === void 0 ? void 0 : _b.quoteMessage) === null || _c === void 0 ? void 0 : _c.contentType, ((_f = (_e = (_d = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _d === void 0 ? void 0 : _d.quoteMessage) === null || _e === void 0 ? void 0 : _e.textElem) === null || _f === void 0 ? void 0 : _f.content) || "") })] })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,yBAAyB,CAAC;AA2BjC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,WAAW,GAAI,2HAQlB,gBAAgB,mDA0PlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,yBAAyB,CAAC;AA2BjC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,QAAA,MAAM,WAAW,GAAI,2HAQlB,gBAAgB,mDAsQlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -102,6 +102,7 @@ const MessageItem = ({ message, allMessages, contextMenuOpen, onContextMenuOpenC
102
102
  console.log("Failed to parse conversationData.ex");
103
103
  }
104
104
  }, [conversationData === null || conversationData === void 0 ? void 0 : conversationData.ex, isCrm, isMine]);
105
+ const isInternalMessage = Boolean(message === null || message === void 0 ? void 0 : message.isInternal);
105
106
  const hasContextMenu = contextMenuItems.length > 0;
106
107
  const isLatestMine = ((_a = allMessages.find((m) => m.sendID === userID)) === null || _a === void 0 ? void 0 : _a.clientMsgID) ===
107
108
  message.clientMsgID;
@@ -114,6 +115,9 @@ const MessageItem = ({ message, allMessages, contextMenuOpen, onContextMenuOpenC
114
115
  const showTimeBreak = prevTimeBreak;
115
116
  const showSenderAvatar = !nextSameUser;
116
117
  const showSenderName = !prevSameUser && !isMine;
118
+ if (!isCrm && isInternalMessage) {
119
+ return null;
120
+ }
117
121
  if (!isCrm &&
118
122
  (MessageType.CustomMessage === (message === null || message === void 0 ? void 0 : message.contentType) ||
119
123
  !visibleTypeMessage.includes(message === null || message === void 0 ? void 0 : message.contentType))) {
@@ -121,7 +125,13 @@ const MessageItem = ({ message, allMessages, contextMenuOpen, onContextMenuOpenC
121
125
  }
122
126
  return (_jsxs("div", { className: "flex flex-col gap-2 py-1 px-3 sm:px-4 min-w-0", id: `${MSG_ITEM_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, children: [showTimeBreak && (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "text-xs text-gray-600 text-center bg-neutral-100 px-2 py-1 rounded-full", children: formatTimestamp(message.sendTime, {
123
127
  dateMonthFormat: "DD MMMM",
124
- }) }) })), _jsx("div", { className: clsx("flex min-w-0", isMine ? "justify-end" : "justify-start"), children: _jsxs("div", { className: clsx("flex flex-1 items-end gap-2 min-w-0", isMine ? "justify-end" : "justify-start"), children: [showSenderInfo && (_jsx("div", { className: "flex items-center justify-center w-[32px] h-[32px]", children: showSenderAvatar && (_jsx(Avatar, { src: message === null || message === void 0 ? void 0 : message.senderFaceUrl, children: ((_c = (_b = message === null || message === void 0 ? void 0 : message.senderNickname) === null || _b === void 0 ? void 0 : _b.charAt) === null || _c === void 0 ? void 0 : _c.call(_b, 0)) || "A" })) })), _jsxs("div", { className: clsx("flex flex-col max-w-[75%] min-w-0", isMine ? "items-end" : "items-start"), children: [showSenderName && showSenderInfo && (_jsx("span", { className: "text-xs font-bold mb-1 px-3 mt-2", children: message === null || message === void 0 ? void 0 : message.senderNickname })), _jsxs("div", { className: clsx("flex flex-col flex-1 w-full", isMine ? "items-end" : "items-start"), children: [(message === null || message === void 0 ? void 0 : message.quoteElem) && (_jsx(QuoteMessageItem, { message: message, isMine: isMine, onPressQuoteMessage: onPressQuoteMessage })), _jsxs("div", { className: "flex items-end gap-1.5", children: [isMine && (_jsx(MessageStatusIndicator, { message: message, isLatest: isLatestMine })), _jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: hasContextMenu ? ["contextMenu"] : [], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, children: _jsxs("div", { className: clsx("px-3 py-2 rounded-2xl max-w-full min-w-0 break-normal flex flex-col flex-1 text-gray-900 gap-1 w-fit", isMine ? "bg-blue-100" : "bg-white"), style: {
128
+ }) }) })), _jsx("div", { className: clsx("flex min-w-0", isMine ? "justify-end" : "justify-start"), children: _jsxs("div", { className: clsx("flex flex-1 items-end gap-2 min-w-0", isMine ? "justify-end" : "justify-start"), children: [showSenderInfo && (_jsx("div", { className: "flex items-center justify-center w-[32px] h-[32px]", children: showSenderAvatar && (_jsx(Avatar, { src: message === null || message === void 0 ? void 0 : message.senderFaceUrl, children: ((_c = (_b = message === null || message === void 0 ? void 0 : message.senderNickname) === null || _b === void 0 ? void 0 : _b.charAt) === null || _c === void 0 ? void 0 : _c.call(_b, 0)) || "A" })) })), _jsxs("div", { className: clsx("flex flex-col max-w-[75%] min-w-0", isMine ? "items-end" : "items-start"), children: [showSenderName && showSenderInfo && (_jsx("span", { className: "text-xs font-bold mb-1 px-3 mt-2", children: message === null || message === void 0 ? void 0 : message.senderNickname })), _jsxs("div", { className: clsx("flex flex-col flex-1 w-full", isMine ? "items-end" : "items-start"), children: [(message === null || message === void 0 ? void 0 : message.quoteElem) && (_jsx(QuoteMessageItem, { message: message, isMine: isMine, onPressQuoteMessage: onPressQuoteMessage })), _jsxs("div", { className: "flex items-end gap-1.5", children: [isMine && (_jsx(MessageStatusIndicator, { message: message, isLatest: isLatestMine })), _jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: hasContextMenu ? ["contextMenu"] : [], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, children: _jsxs("div", { className: clsx("px-3 py-2 rounded-2xl max-w-full min-w-0 break-normal flex flex-col flex-1 text-gray-900 gap-1 w-fit", isMine
129
+ ? isInternalMessage
130
+ ? "bg-orange-100"
131
+ : "bg-blue-100"
132
+ : isInternalMessage
133
+ ? "bg-orange-100"
134
+ : "bg-white"), style: {
125
135
  wordBreak: "break-word",
126
136
  overflowWrap: "anywhere",
127
137
  }, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, children: [(message === null || message === void 0 ? void 0 : message.contentType) === MessageType.MergeMessage ? (_jsxs("div", { children: [(_e = (_d = message === null || message === void 0 ? void 0 : message.mergeElem) === null || _d === void 0 ? void 0 : _d.multiMessage) === null || _e === void 0 ? void 0 : _e.map((item) => {
@@ -1 +1 @@
1
- {"version":3,"file":"DeskAssignedSession.d.ts","sourceRoot":"","sources":["../../../src/components/session/DeskAssignedSession.tsx"],"names":[],"mappings":"AAgBA,QAAA,MAAM,mBAAmB,+CA+NxB,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"DeskAssignedSession.d.ts","sourceRoot":"","sources":["../../../src/components/session/DeskAssignedSession.tsx"],"names":[],"mappings":"AAeA,QAAA,MAAM,mBAAmB,+CA2ExB,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -1,128 +1,33 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useBoolean } from "ahooks";
4
- import { Button, Layout, Menu } from "antd";
5
- import { useEffect, useMemo } from "react";
4
+ import { Button, Layout } from "antd";
5
+ import { useCallback, useEffect } from "react";
6
6
  import { useTranslation } from "react-i18next";
7
7
  import { Icon } from "../icon";
8
8
  import { useGetSessionSummary } from "../../hooks/session/useGetSessionSummary";
9
9
  import useSessionStore from "../../store/session";
10
- import clsx from "clsx";
11
- import { SessionStatus, SessionTag } from "../../types/chat";
12
10
  import emitter from "../../utils/events";
11
+ import DeskTeamInbox from "./DeskTeamInbox";
12
+ import SessionFilterMenu from "./SessionFilterMenu";
13
13
  const { Sider } = Layout;
14
14
  const DeskAssignedSession = () => {
15
15
  const { t } = useTranslation();
16
16
  const [collapsed, { toggle }] = useBoolean(false);
17
17
  const setFilterSummary = useSessionStore((state) => state.setFilterSummary);
18
+ const filterSummary = useSessionStore((state) => state.filterSummary);
18
19
  const { data: sessionSummary, refetch } = useGetSessionSummary();
19
- const menuItems = useMemo(() => {
20
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
21
- return [
22
- {
23
- label: t("active_sessions"),
24
- key: "ACTIVE_SESSIONS",
25
- icon: _jsx(Icon, { icon: "chat-dot-o", size: 20 }),
26
- children: [
27
- {
28
- label: t("unassigned"),
29
- key: SessionStatus.UNASSIGNED,
30
- icon: (_jsx(Icon, { icon: "user-del-o", size: 18, className: "!text-amber-500" })),
31
- onClick: () => {
32
- setFilterSummary({
33
- status: SessionStatus.UNASSIGNED,
34
- tag: undefined,
35
- });
36
- },
37
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500 ", children: ((_b = (_a = sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.sessionStatuses) === null || _a === void 0 ? void 0 : _a.find((s) => s.type === SessionStatus.UNASSIGNED)) === null || _b === void 0 ? void 0 : _b.count) || "" })),
38
- },
39
- {
40
- label: t("slow_processing"),
41
- key: SessionTag.SLOW_PROCESSING,
42
- icon: (_jsx(Icon, { icon: "warning-square-o", size: 18, className: "!text-red-500" })),
43
- onClick: () => {
44
- setFilterSummary({
45
- status: undefined,
46
- tag: SessionTag.SLOW_PROCESSING,
47
- });
48
- },
49
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500", children: ((_d = (_c = sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.tagCounts) === null || _c === void 0 ? void 0 : _c.find((s) => s.type === SessionTag.SLOW_PROCESSING)) === null || _d === void 0 ? void 0 : _d.count) || "" })),
50
- },
51
- {
52
- label: t("waiting_process"),
53
- key: SessionStatus.WAITING_PROCESS,
54
- icon: (_jsx(Icon, { icon: "time-circle-o", size: 18, className: "!text-orange-400" })),
55
- onClick: () => {
56
- setFilterSummary({
57
- status: SessionStatus.WAITING_PROCESS,
58
- tag: undefined,
59
- });
60
- },
61
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500", children: ((_f = (_e = sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.sessionStatuses) === null || _e === void 0 ? void 0 : _e.find((s) => s.type === SessionStatus.WAITING_PROCESS)) === null || _f === void 0 ? void 0 : _f.count) || "" })),
62
- },
63
- {
64
- label: t("awaiting_reply"),
65
- key: SessionTag.AWAITING_REPLY,
66
- icon: (_jsx(Icon, { icon: "arrow-reply-o", size: 18, className: "!text-purple-500" })),
67
- onClick: () => {
68
- setFilterSummary({
69
- status: undefined,
70
- tag: SessionTag.AWAITING_REPLY,
71
- });
72
- },
73
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500", children: ((_h = (_g = sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.tagCounts) === null || _g === void 0 ? void 0 : _g.find((s) => s.type === SessionTag.AWAITING_REPLY)) === null || _h === void 0 ? void 0 : _h.count) || "" })),
74
- },
75
- {
76
- label: t("in_process"),
77
- key: SessionStatus.IN_PROCESS,
78
- icon: _jsx(Icon, { icon: "play-circle-o", size: 18 }),
79
- onClick: () => {
80
- setFilterSummary({
81
- status: SessionStatus.IN_PROCESS,
82
- tag: undefined,
83
- });
84
- },
85
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500", children: ((_k = (_j = sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.sessionStatuses) === null || _j === void 0 ? void 0 : _j.find((s) => s.type === SessionStatus.IN_PROCESS)) === null || _k === void 0 ? void 0 : _k.count) || "" })),
86
- },
87
- {
88
- label: t("temporarily_paused"),
89
- key: SessionTag.TEMPORARILY_PAUSED,
90
- icon: _jsx(Icon, { icon: "pause-o", size: 18 }),
91
- onClick: () => {
92
- setFilterSummary({
93
- status: undefined,
94
- tag: SessionTag.TEMPORARILY_PAUSED,
95
- });
96
- },
97
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500", children: ((_m = (_l = sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.tagCounts) === null || _l === void 0 ? void 0 : _l.find((s) => s.type === SessionTag.TEMPORARILY_PAUSED)) === null || _m === void 0 ? void 0 : _m.count) || "" })),
98
- },
99
- ],
100
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500", children: ((_p = (_o = sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.sessionStatuses) === null || _o === void 0 ? void 0 : _o.find((s) => s.type === SessionStatus.IN_PROCESS)) === null || _p === void 0 ? void 0 : _p.count) || "" })),
101
- },
102
- {
103
- label: t("closed_sessions"),
104
- key: "CLOSED_SESSIONS",
105
- icon: _jsx(Icon, { icon: "check-square-o", size: 20 }),
106
- onClick: () => {
107
- setFilterSummary({
108
- status: SessionStatus.COMPLETED,
109
- tag: undefined,
110
- });
111
- },
112
- itemIcon: !collapsed && (_jsx("span", { className: "text-xs text-gray-500", children: (sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.completedSessionCount) || "" })),
113
- },
114
- ];
115
- }, [sessionSummary, t, collapsed]);
20
+ const onFilterSelect = useCallback((filter) => setFilterSummary(filter), [setFilterSummary]);
116
21
  useEffect(() => {
117
- emitter.on("UPDATE_SESSION", () => {
118
- refetch();
119
- });
22
+ const handler = () => refetch();
23
+ emitter.on("UPDATE_SESSION", handler);
120
24
  return () => {
121
- emitter.off("UPDATE_SESSION", () => {
122
- refetch();
123
- });
25
+ emitter.off("UPDATE_SESSION", handler);
124
26
  };
125
- }, []);
126
- return (_jsxs(Sider, { collapsible: true, collapsed: collapsed, onCollapse: toggle, width: 220, className: "bg-white h-full border-r border-gray-200", trigger: null, children: [_jsxs("div", { className: clsx("flex items-center p-4 border-b", collapsed ? "justify-center" : "justify-between"), children: [!collapsed && (_jsx("span", { className: "text-md font-semibold flex-1 truncate", children: "Droppii Livechat" })), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: collapsed ? "angle-right-o" : "angle-left-o", size: 22 }) })] }), _jsx(Menu, { defaultSelectedKeys: [SessionStatus.IN_PROCESS], defaultOpenKeys: ["ACTIVE_SESSIONS"], mode: "inline", items: menuItems, inlineIndent: 12, expandIcon: _jsx("span", { className: "text-xs text-gray-500", children: sessionSummary === null || sessionSummary === void 0 ? void 0 : sessionSummary.activeSessionCount }) })] }));
27
+ }, [refetch]);
28
+ if (collapsed) {
29
+ return (_jsx("div", { className: "flex flex-col items-center pt-4 border-r border-gray-200 bg-white", children: _jsx(Button, { type: "text", shape: "circle", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "angle-right-o", size: 22 }) }) }));
30
+ }
31
+ return (_jsx(Sider, { collapsible: true, collapsed: false, width: 220, className: "bg-white h-full border-r border-gray-200", trigger: null, children: _jsxs("div", { className: "flex flex-col h-full", children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b shrink-0", children: [_jsx("span", { className: "text-md font-semibold flex-1 truncate", children: "Droppii Livechat" }), _jsx(Button, { type: "text", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "angle-left-o", size: 22 }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto min-h-0", children: [_jsx("div", { className: "px-4 pt-4 pb-2 text-xs font-semibold text-gray-400 tracking-wider", children: t("my_messages") }), sessionSummary && (_jsx(SessionFilterMenu, { summary: sessionSummary, isActive: !filterSummary.teamId, filterSummary: filterSummary, onFilterSelect: onFilterSelect, activeCount: sessionSummary.activeSessionCount })), _jsx(DeskTeamInbox, {})] })] }) }));
127
32
  };
128
33
  export default DeskAssignedSession;
@@ -0,0 +1,3 @@
1
+ declare const DeskTeamInbox: () => import("react/jsx-runtime").JSX.Element | null;
2
+ export default DeskTeamInbox;
3
+ //# sourceMappingURL=DeskTeamInbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeskTeamInbox.d.ts","sourceRoot":"","sources":["../../../src/components/session/DeskTeamInbox.tsx"],"names":[],"mappings":"AAuDA,QAAA,MAAM,aAAa,sDAyElB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { useTranslation } from "react-i18next";
5
+ import { Icon } from "../icon";
6
+ import { useGetTeamSessionSummary } from "../../hooks/session/useGetTeamSessionSummary";
7
+ import useSessionStore from "../../store/session";
8
+ import emitter from "../../utils/events";
9
+ import SessionFilterMenu from "./SessionFilterMenu";
10
+ const TeamGroup = memo(({ team, isExpanded, isSelected, selectedFilter, onToggle, onFilterSelect, }) => (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-2 cursor-pointer hover:bg-gray-50", onClick: onToggle, children: [_jsx("span", { className: "text-sm font-semibold text-blue-500 truncate flex-1", children: team.teamName }), _jsx(Icon, { icon: isExpanded ? "angle-up-o" : "angle-down-o", size: 16, className: "text-gray-400 shrink-0" })] }), isExpanded && (_jsx(SessionFilterMenu, { summary: team.summary, isActive: isSelected, filterSummary: selectedFilter, onFilterSelect: onFilterSelect, includeUnassigned: true, activeCount: team.summary.activeSessionCount }))] })));
11
+ const DeskTeamInbox = () => {
12
+ const { t } = useTranslation();
13
+ const { data: teamSummaries, refetch } = useGetTeamSessionSummary();
14
+ const filterSummary = useSessionStore((s) => s.filterSummary);
15
+ const setFilterSummary = useSessionStore((s) => s.setFilterSummary);
16
+ const [expandedTeamIds, setExpandedTeamIds] = useState([]);
17
+ const initializedRef = useRef(false);
18
+ useEffect(() => {
19
+ if ((teamSummaries === null || teamSummaries === void 0 ? void 0 : teamSummaries.length) && !initializedRef.current) {
20
+ setExpandedTeamIds([teamSummaries[0].teamId]);
21
+ initializedRef.current = true;
22
+ }
23
+ }, [teamSummaries]);
24
+ useEffect(() => {
25
+ const handler = () => refetch();
26
+ emitter.on("UPDATE_SESSION", handler);
27
+ return () => {
28
+ emitter.off("UPDATE_SESSION", handler);
29
+ };
30
+ }, [refetch]);
31
+ const toggleTeam = useCallback((teamId) => {
32
+ setExpandedTeamIds((prev) => prev.includes(teamId)
33
+ ? prev.filter((id) => id !== teamId)
34
+ : [...prev, teamId]);
35
+ }, []);
36
+ const toggleHandlers = useMemo(() => {
37
+ if (!teamSummaries)
38
+ return {};
39
+ return Object.fromEntries(teamSummaries.map((team) => [
40
+ team.teamId,
41
+ () => toggleTeam(team.teamId),
42
+ ]));
43
+ }, [teamSummaries, toggleTeam]);
44
+ const filterHandlers = useMemo(() => {
45
+ if (!teamSummaries)
46
+ return {};
47
+ return Object.fromEntries(teamSummaries.map((team) => [
48
+ team.teamId,
49
+ (filter) => setFilterSummary(Object.assign(Object.assign({}, filter), { teamId: team.teamId })),
50
+ ]));
51
+ }, [teamSummaries, setFilterSummary]);
52
+ if (!(teamSummaries === null || teamSummaries === void 0 ? void 0 : teamSummaries.length))
53
+ return null;
54
+ return (_jsxs("div", { children: [_jsx("div", { className: "px-4 pt-4 pb-2 text-xs font-semibold text-gray-400 tracking-wider", children: t("team_messages") }), teamSummaries.map((team) => (_jsx(TeamGroup, { team: team, isExpanded: expandedTeamIds.includes(team.teamId), isSelected: filterSummary.teamId === team.teamId, selectedFilter: filterSummary, onToggle: toggleHandlers[team.teamId], onFilterSelect: filterHandlers[team.teamId] }, team.teamId)))] }));
55
+ };
56
+ export default DeskTeamInbox;
@@ -0,0 +1,13 @@
1
+ import { IFilterSummary } from "../../store/type";
2
+ import { ISessionSummaryResponse } from "../../types/dto";
3
+ interface SessionFilterMenuProps {
4
+ summary: ISessionSummaryResponse;
5
+ isActive: boolean;
6
+ filterSummary: IFilterSummary;
7
+ onFilterSelect: (filter: IFilterSummary) => void;
8
+ includeUnassigned?: boolean;
9
+ activeCount?: number;
10
+ }
11
+ declare const SessionFilterMenu: ({ summary, isActive, filterSummary, onFilterSelect, includeUnassigned, activeCount, }: SessionFilterMenuProps) => import("react/jsx-runtime").JSX.Element;
12
+ export default SessionFilterMenu;
13
+ //# sourceMappingURL=SessionFilterMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionFilterMenu.d.ts","sourceRoot":"","sources":["../../../src/components/session/SessionFilterMenu.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,UAAU,sBAAsB;IAC9B,OAAO,EAAE,uBAAuB,CAAC;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,cAAc,CAAC;IAC9B,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAaD,QAAA,MAAM,iBAAiB,GAAI,uFAOxB,sBAAsB,4CAiCxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
4
+ import { useTranslation } from "react-i18next";
5
+ import { Menu } from "antd";
6
+ import { SessionStatus } from "../../types/chat";
7
+ import { buildSessionMenuItems } from "./sessionMenuItems";
8
+ const getSelectedKeys = (isActive, filterSummary) => {
9
+ if (!isActive)
10
+ return [];
11
+ if (filterSummary.status === SessionStatus.COMPLETED)
12
+ return [SessionStatus.COMPLETED];
13
+ const key = filterSummary.status || filterSummary.tag;
14
+ return key ? [key] : [];
15
+ };
16
+ const SessionFilterMenu = ({ summary, isActive, filterSummary, onFilterSelect, includeUnassigned = false, activeCount, }) => {
17
+ const { t } = useTranslation();
18
+ const selectedKeys = useMemo(() => getSelectedKeys(isActive, filterSummary), [isActive, filterSummary]);
19
+ const menuItems = useMemo(() => buildSessionMenuItems({
20
+ summary,
21
+ t,
22
+ onFilterSelect,
23
+ includeUnassigned,
24
+ }), [summary, t, onFilterSelect, includeUnassigned]);
25
+ return (_jsx(Menu, { selectedKeys: selectedKeys, defaultOpenKeys: ["ACTIVE_SESSIONS"], mode: "inline", items: menuItems, inlineIndent: 12, expandIcon: activeCount ? (_jsx("span", { className: "text-xs", children: activeCount })) : undefined }));
26
+ };
27
+ export default SessionFilterMenu;