@droppii-org/chat-sdk 0.1.6 → 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 (99) 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/index.d.ts.map +1 -1
  30. package/dist/components/message/item/index.js +11 -1
  31. package/dist/components/session/DeskAssignedSession.d.ts.map +1 -1
  32. package/dist/components/session/DeskAssignedSession.js +14 -109
  33. package/dist/components/session/DeskTeamInbox.d.ts +3 -0
  34. package/dist/components/session/DeskTeamInbox.d.ts.map +1 -0
  35. package/dist/components/session/DeskTeamInbox.js +56 -0
  36. package/dist/components/session/SessionFilterMenu.d.ts +13 -0
  37. package/dist/components/session/SessionFilterMenu.d.ts.map +1 -0
  38. package/dist/components/session/SessionFilterMenu.js +27 -0
  39. package/dist/components/session/sessionMenuItems.d.ts +26 -0
  40. package/dist/components/session/sessionMenuItems.d.ts.map +1 -0
  41. package/dist/components/session/sessionMenuItems.js +108 -0
  42. package/dist/hooks/conversation/useConversationPreview.d.ts +12 -0
  43. package/dist/hooks/conversation/useConversationPreview.d.ts.map +1 -0
  44. package/dist/hooks/conversation/useConversationPreview.js +22 -0
  45. package/dist/hooks/message/useConversationMessages.d.ts +27 -0
  46. package/dist/hooks/message/useConversationMessages.d.ts.map +1 -0
  47. package/dist/hooks/message/useConversationMessages.js +29 -0
  48. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  49. package/dist/hooks/message/usePullSessionMessages.d.ts +9 -0
  50. package/dist/hooks/message/usePullSessionMessages.d.ts.map +1 -0
  51. package/dist/hooks/message/usePullSessionMessages.js +27 -0
  52. package/dist/hooks/message/useSendMessage.d.ts +8 -6
  53. package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
  54. package/dist/hooks/message/useSendMessage.js +8 -8
  55. package/dist/hooks/session/useConversationSessionState.d.ts +21 -0
  56. package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -0
  57. package/dist/hooks/session/useConversationSessionState.js +41 -0
  58. package/dist/hooks/session/useGetSession.d.ts.map +1 -1
  59. package/dist/hooks/session/useGetSession.js +138 -52
  60. package/dist/hooks/session/useGetTeamSessionSummary.d.ts +3 -0
  61. package/dist/hooks/session/useGetTeamSessionSummary.d.ts.map +1 -0
  62. package/dist/hooks/session/useGetTeamSessionSummary.js +12 -0
  63. package/dist/hooks/session/useIsJoinedGroup.d.ts +5 -0
  64. package/dist/hooks/session/useIsJoinedGroup.d.ts.map +1 -0
  65. package/dist/hooks/session/useIsJoinedGroup.js +24 -0
  66. package/dist/hooks/session/useJoinGroupFlow.d.ts +12 -0
  67. package/dist/hooks/session/useJoinGroupFlow.d.ts.map +1 -0
  68. package/dist/hooks/session/useJoinGroupFlow.js +59 -0
  69. package/dist/hooks/session/useJoinSession.d.ts +3 -0
  70. package/dist/hooks/session/useJoinSession.d.ts.map +1 -0
  71. package/dist/hooks/session/useJoinSession.js +38 -0
  72. package/dist/hooks/user/useCurrentUserAccountType.d.ts +3 -0
  73. package/dist/hooks/user/useCurrentUserAccountType.d.ts.map +1 -0
  74. package/dist/hooks/user/useCurrentUserAccountType.js +30 -0
  75. package/dist/locales/vi/common.json +12 -2
  76. package/dist/services/query.d.ts +5 -0
  77. package/dist/services/query.d.ts.map +1 -1
  78. package/dist/services/query.js +5 -0
  79. package/dist/services/routes.d.ts +5 -0
  80. package/dist/services/routes.d.ts.map +1 -1
  81. package/dist/services/routes.js +5 -0
  82. package/dist/store/conversation.d.ts.map +1 -1
  83. package/dist/store/conversation.js +41 -12
  84. package/dist/store/session.js +1 -1
  85. package/dist/styles/global.css +1 -1
  86. package/dist/tsconfig.tsbuildinfo +1 -1
  87. package/dist/types/chat.d.ts +18 -1
  88. package/dist/types/chat.d.ts.map +1 -1
  89. package/dist/types/chat.js +9 -0
  90. package/dist/types/dto.d.ts +87 -0
  91. package/dist/types/dto.d.ts.map +1 -1
  92. package/dist/utils/events.d.ts +1 -0
  93. package/dist/utils/events.d.ts.map +1 -1
  94. package/dist/utils/messageTransform.d.ts +5 -0
  95. package/dist/utils/messageTransform.d.ts.map +1 -0
  96. package/dist/utils/messageTransform.js +106 -0
  97. package/dist/utils/queryHelpers.d.ts.map +1 -1
  98. package/dist/utils/queryHelpers.js +2 -0
  99. 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":"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;
@@ -0,0 +1,26 @@
1
+ import { MenuProps } from "antd";
2
+ import { TFunction } from "i18next";
3
+ import { IFilterSummary } from "../../store/type";
4
+ import { ISessionSummaryResponse } from "../../types/dto";
5
+ type MenuItem = Required<MenuProps>["items"][number];
6
+ interface SessionFilterConfig {
7
+ key: string;
8
+ labelKey: string;
9
+ iconName: string;
10
+ iconClassName?: string;
11
+ filter: IFilterSummary;
12
+ countSource: "status" | "tag";
13
+ teamOnly?: boolean;
14
+ highlighted?: boolean;
15
+ }
16
+ export declare const SESSION_FILTER_CONFIGS: SessionFilterConfig[];
17
+ export declare const getFilterConfig: (key: string) => SessionFilterConfig | undefined;
18
+ interface BuildSessionMenuItemsOptions {
19
+ summary: ISessionSummaryResponse;
20
+ t: TFunction;
21
+ onFilterSelect: (filter: IFilterSummary) => void;
22
+ includeUnassigned?: boolean;
23
+ }
24
+ export declare const buildSessionMenuItems: ({ summary, t, onFilterSelect, includeUnassigned, }: BuildSessionMenuItemsOptions) => MenuItem[];
25
+ export {};
26
+ //# sourceMappingURL=sessionMenuItems.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionMenuItems.d.ts","sourceRoot":"","sources":["../../../src/components/session/sessionMenuItems.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,KAAK,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,UAAU,mBAAmB;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,EA0DvD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,oCACQ,CAAC;AAEpD,UAAU,4BAA4B;IACpC,OAAO,EAAE,uBAAuB,CAAC;IACjC,CAAC,EAAE,SAAS,CAAC;IACb,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA0BD,eAAO,MAAM,qBAAqB,GAAI,oDAKnC,4BAA4B,KAAG,QAAQ,EA4CzC,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Icon } from "../icon";
3
+ import { SessionStatus, SessionTag } from "../../types/chat";
4
+ export const SESSION_FILTER_CONFIGS = [
5
+ {
6
+ key: SessionStatus.UNASSIGNED,
7
+ labelKey: "unassigned",
8
+ iconName: "user-del-o",
9
+ iconClassName: "!text-amber-500",
10
+ filter: { status: SessionStatus.UNASSIGNED, tag: undefined },
11
+ countSource: "status",
12
+ teamOnly: true,
13
+ },
14
+ {
15
+ key: SessionTag.SLOW_PROCESSING,
16
+ labelKey: "slow_processing",
17
+ iconName: "warning-square-o",
18
+ iconClassName: "!text-red-500",
19
+ filter: { status: undefined, tag: SessionTag.SLOW_PROCESSING },
20
+ countSource: "tag",
21
+ highlighted: true,
22
+ },
23
+ {
24
+ key: SessionStatus.WAITING_PROCESS,
25
+ labelKey: "waiting_process",
26
+ iconName: "time-circle-o",
27
+ iconClassName: "!text-orange-400",
28
+ filter: { status: SessionStatus.WAITING_PROCESS, tag: undefined },
29
+ countSource: "status",
30
+ highlighted: true,
31
+ },
32
+ {
33
+ key: SessionTag.AWAITING_REPLY,
34
+ labelKey: "awaiting_reply",
35
+ iconName: "arrow-reply-o",
36
+ iconClassName: "!text-purple-500",
37
+ filter: { status: undefined, tag: SessionTag.AWAITING_REPLY },
38
+ countSource: "tag",
39
+ highlighted: true,
40
+ },
41
+ {
42
+ key: SessionStatus.IN_PROCESS,
43
+ labelKey: "in_process",
44
+ iconName: "play-circle-o",
45
+ filter: { status: SessionStatus.IN_PROCESS, tag: undefined },
46
+ countSource: "status",
47
+ },
48
+ {
49
+ key: SessionTag.TEMPORARILY_PAUSED,
50
+ labelKey: "temporarily_paused",
51
+ iconName: "pause-o",
52
+ filter: { status: undefined, tag: SessionTag.TEMPORARILY_PAUSED },
53
+ countSource: "tag",
54
+ },
55
+ {
56
+ key: SessionStatus.COMPLETED,
57
+ labelKey: "closed_sessions",
58
+ iconName: "check-square-o",
59
+ filter: { status: SessionStatus.COMPLETED, tag: undefined },
60
+ countSource: "status",
61
+ },
62
+ ];
63
+ export const getFilterConfig = (key) => SESSION_FILTER_CONFIGS.find((c) => c.key === key);
64
+ const countBadge = (count, highlighted) => {
65
+ if (!count)
66
+ return null;
67
+ if (highlighted) {
68
+ return (_jsx("span", { className: "text-xs font-medium text-white bg-red-500 rounded-full px-1.5 py-0.5 leading-none -mr-1.5", children: count }));
69
+ }
70
+ return _jsx("span", { className: "text-xs text-gray-500", children: count });
71
+ };
72
+ const getCount = (config, summary) => {
73
+ var _a, _b, _c, _d;
74
+ if (config.countSource === "status") {
75
+ return (((_b = (_a = summary.sessionStatuses) === null || _a === void 0 ? void 0 : _a.find((s) => s.type === config.key)) === null || _b === void 0 ? void 0 : _b.count) || "");
76
+ }
77
+ return ((_d = (_c = summary.tagCounts) === null || _c === void 0 ? void 0 : _c.find((s) => s.type === config.key)) === null || _d === void 0 ? void 0 : _d.count) || "";
78
+ };
79
+ export const buildSessionMenuItems = ({ summary, t, onFilterSelect, includeUnassigned = false, }) => {
80
+ const activeConfigs = SESSION_FILTER_CONFIGS.filter((c) => c.key !== SessionStatus.COMPLETED &&
81
+ (!c.teamOnly || includeUnassigned));
82
+ const activeChildren = activeConfigs.map((config) => ({
83
+ label: t(config.labelKey),
84
+ key: config.key,
85
+ icon: (_jsx(Icon, { icon: config.iconName, size: 18, className: config.iconClassName })),
86
+ onClick: () => onFilterSelect(config.filter),
87
+ itemIcon: countBadge(getCount(config, summary), config.highlighted),
88
+ }));
89
+ const closedConfig = SESSION_FILTER_CONFIGS.find((c) => c.key === SessionStatus.COMPLETED);
90
+ const closedItem = closedConfig
91
+ ? {
92
+ label: t(closedConfig.labelKey),
93
+ key: closedConfig.key,
94
+ icon: _jsx(Icon, { icon: closedConfig.iconName, size: 20 }),
95
+ onClick: () => onFilterSelect(closedConfig.filter),
96
+ itemIcon: countBadge(summary.completedSessionCount),
97
+ }
98
+ : null;
99
+ return [
100
+ {
101
+ label: t("active_sessions"),
102
+ key: "ACTIVE_SESSIONS",
103
+ icon: _jsx(Icon, { icon: "chat-dot-o", size: 20 }),
104
+ children: activeChildren,
105
+ },
106
+ closedItem,
107
+ ].filter(Boolean);
108
+ };