@droppii-org/chat-sdk 0.4.1 → 0.4.2

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 (74) hide show
  1. package/dist/assets/images/searchEmpty.png +0 -0
  2. package/dist/components/conversation-detail/ConversationDetailInput.d.ts.map +1 -1
  3. package/dist/components/conversation-detail/ConversationDetailInput.js +142 -4
  4. package/dist/components/conversation-detail/mention/BizMentionMenu.d.ts +15 -0
  5. package/dist/components/conversation-detail/mention/BizMentionMenu.d.ts.map +1 -0
  6. package/dist/components/conversation-detail/mention/BizMentionMenu.js +11 -0
  7. package/dist/components/conversation-detail/mention/buildMentionPayload.d.ts +4 -0
  8. package/dist/components/conversation-detail/mention/buildMentionPayload.d.ts.map +1 -0
  9. package/dist/components/conversation-detail/mention/buildMentionPayload.js +23 -0
  10. package/dist/components/conversation-detail/mention/getCaretCoordinates.d.ts +11 -0
  11. package/dist/components/conversation-detail/mention/getCaretCoordinates.d.ts.map +1 -0
  12. package/dist/components/conversation-detail/mention/getCaretCoordinates.js +36 -0
  13. package/dist/components/conversation-detail/mention/useMentionTokens.d.ts +21 -0
  14. package/dist/components/conversation-detail/mention/useMentionTokens.d.ts.map +1 -0
  15. package/dist/components/conversation-detail/mention/useMentionTokens.js +58 -0
  16. package/dist/components/conversation-detail/mention/useMentionTrigger.d.ts +7 -0
  17. package/dist/components/conversation-detail/mention/useMentionTrigger.d.ts.map +1 -0
  18. package/dist/components/conversation-detail/mention/useMentionTrigger.js +19 -0
  19. package/dist/components/conversation-inbox/ConversationInboxHeader.d.ts +4 -1
  20. package/dist/components/conversation-inbox/ConversationInboxHeader.d.ts.map +1 -1
  21. package/dist/components/conversation-inbox/ConversationInboxHeader.js +3 -2
  22. package/dist/components/conversation-inbox/contact-search/ContactSearchFilters.d.ts +8 -0
  23. package/dist/components/conversation-inbox/contact-search/ContactSearchFilters.d.ts.map +1 -0
  24. package/dist/components/conversation-inbox/contact-search/ContactSearchFilters.js +19 -0
  25. package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.d.ts +7 -0
  26. package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.d.ts.map +1 -0
  27. package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.js +116 -0
  28. package/dist/components/conversation-inbox/contact-search/ContactSearchResultItem.d.ts +9 -0
  29. package/dist/components/conversation-inbox/contact-search/ContactSearchResultItem.d.ts.map +1 -0
  30. package/dist/components/conversation-inbox/contact-search/ContactSearchResultItem.js +34 -0
  31. package/dist/components/conversation-inbox/contact-search/ContactSearchSectionHeader.d.ts +8 -0
  32. package/dist/components/conversation-inbox/contact-search/ContactSearchSectionHeader.d.ts.map +1 -0
  33. package/dist/components/conversation-inbox/contact-search/ContactSearchSectionHeader.js +7 -0
  34. package/dist/components/conversation-inbox/contact-search/ContactSearchStateMessage.d.ts +8 -0
  35. package/dist/components/conversation-inbox/contact-search/ContactSearchStateMessage.d.ts.map +1 -0
  36. package/dist/components/conversation-inbox/contact-search/ContactSearchStateMessage.js +9 -0
  37. package/dist/components/conversation-inbox/contact-search/ContactSearchTabs.d.ts +10 -0
  38. package/dist/components/conversation-inbox/contact-search/ContactSearchTabs.d.ts.map +1 -0
  39. package/dist/components/conversation-inbox/contact-search/ContactSearchTabs.js +39 -0
  40. package/dist/components/conversation-inbox/contact-search/ContactSearchViewMoreBar.d.ts +6 -0
  41. package/dist/components/conversation-inbox/contact-search/ContactSearchViewMoreBar.d.ts.map +1 -0
  42. package/dist/components/conversation-inbox/contact-search/ContactSearchViewMoreBar.js +9 -0
  43. package/dist/components/conversation-inbox/contact-search/constants.d.ts +18 -0
  44. package/dist/components/conversation-inbox/contact-search/constants.d.ts.map +1 -0
  45. package/dist/components/conversation-inbox/contact-search/constants.js +23 -0
  46. package/dist/components/conversation-inbox/contact-search/index.d.ts +10 -0
  47. package/dist/components/conversation-inbox/contact-search/index.d.ts.map +1 -0
  48. package/dist/components/conversation-inbox/contact-search/index.js +9 -0
  49. package/dist/components/conversation-inbox/contact-search/utils.d.ts +6 -0
  50. package/dist/components/conversation-inbox/contact-search/utils.d.ts.map +1 -0
  51. package/dist/components/conversation-inbox/contact-search/utils.js +44 -0
  52. package/dist/components/message/footer/mention/useMentionOptions.d.ts +1 -0
  53. package/dist/components/message/footer/mention/useMentionOptions.d.ts.map +1 -1
  54. package/dist/components/message/footer/mention/useMentionOptions.js +1 -1
  55. package/dist/constants/images.d.ts +1 -0
  56. package/dist/constants/images.d.ts.map +1 -1
  57. package/dist/constants/images.js +2 -0
  58. package/dist/hooks/search/useSearchContacts.d.ts +18 -0
  59. package/dist/hooks/search/useSearchContacts.d.ts.map +1 -0
  60. package/dist/hooks/search/useSearchContacts.js +39 -0
  61. package/dist/locales/vi/conversation-inbox.json +22 -1
  62. package/dist/screens/conversation-inbox/index.d.ts.map +1 -1
  63. package/dist/screens/conversation-inbox/index.js +14 -3
  64. package/dist/services/query.d.ts +1 -0
  65. package/dist/services/query.d.ts.map +1 -1
  66. package/dist/services/query.js +1 -0
  67. package/dist/services/routes.d.ts +1 -0
  68. package/dist/services/routes.d.ts.map +1 -1
  69. package/dist/services/routes.js +1 -0
  70. package/dist/styles/global.css +1 -1
  71. package/dist/types/dto.d.ts +26 -0
  72. package/dist/types/dto.d.ts.map +1 -1
  73. package/dist/types/dto.js +10 -1
  74. package/package.json +2 -1
@@ -0,0 +1,116 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { Drawer, Input, Spin } from "antd";
5
+ import { useDebounce } from "ahooks";
6
+ import InfiniteScroll from "react-infinite-scroll-component";
7
+ import { useTranslation } from "react-i18next";
8
+ import { Icon } from "../../../components/icon";
9
+ import { useChatContext } from "../../../context/ChatContext";
10
+ import { useSearchContacts } from "../../../hooks/search/useSearchContacts";
11
+ import ContactSearchFilters from "./ContactSearchFilters";
12
+ import ContactSearchResultItem from "./ContactSearchResultItem";
13
+ import ContactSearchSectionHeader from "./ContactSearchSectionHeader";
14
+ import ContactSearchStateMessage from "./ContactSearchStateMessage";
15
+ import ContactSearchTabs from "./ContactSearchTabs";
16
+ import ContactSearchViewMoreBar from "./ContactSearchViewMoreBar";
17
+ import { CONTACT_SEARCH_DEBOUNCE_MS, CONTACT_SEARCH_ICON_SIZE, CONTACT_SEARCH_PREVIEW_LIMIT, ContactSearchTab, } from "./constants";
18
+ import { buildContactSearchParams } from "./utils";
19
+ const SCROLL_TARGET_ID = "contact-search-scroll";
20
+ const ContactSearchPanel = ({ open, onClose }) => {
21
+ const { t } = useTranslation("conversation-inbox");
22
+ const { onNavigate } = useChatContext();
23
+ const [keyword, setKeyword] = useState("");
24
+ const [activeType, setActiveType] = useState();
25
+ const [activeTab, setActiveTab] = useState(ContactSearchTab.All);
26
+ const debouncedKeyword = useDebounce(keyword, {
27
+ wait: CONTACT_SEARCH_DEBOUNCE_MS,
28
+ });
29
+ const trimmedKeyword = debouncedKeyword.trim();
30
+ // The keyword and filter belong to one search session — reset both whenever
31
+ // the panel is reopened so a stale query never flashes.
32
+ useEffect(() => {
33
+ if (!open) {
34
+ setKeyword("");
35
+ setActiveType(undefined);
36
+ setActiveTab(ContactSearchTab.All);
37
+ }
38
+ }, [open]);
39
+ const { contacts, totalElements, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, } = useSearchContacts({
40
+ searchTerm: trimmedKeyword,
41
+ type: activeType,
42
+ enabled: open,
43
+ });
44
+ const handleSelectContact = (contact) => {
45
+ const params = buildContactSearchParams(contact);
46
+ onClose();
47
+ onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(`${window.location.pathname}?${params.toString()}`);
48
+ };
49
+ // Filters belong to the `Liên hệ` tab only, so leaving it drops the active
50
+ // type — `Tất cả` always previews every contact type.
51
+ const handleChangeTab = (tab) => {
52
+ setActiveTab(tab);
53
+ if (tab !== ContactSearchTab.Contacts)
54
+ setActiveType(undefined);
55
+ };
56
+ const renderContactItems = (items) => items.map((contact) => (_jsx(ContactSearchResultItem, { contact: contact, searchTerm: trimmedKeyword, onSelect: handleSelectContact }, contact.id)));
57
+ // Every contact tab shares the same not-ready states before it can render
58
+ // results; returns null once there is a list to show.
59
+ const renderPlaceholderState = () => {
60
+ if (!trimmedKeyword) {
61
+ return (_jsx(ContactSearchStateMessage, { testId: "contact-search-initial", children: t("contact_search.initial") }));
62
+ }
63
+ if (isLoading) {
64
+ return (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) }));
65
+ }
66
+ if (isError) {
67
+ return (_jsx(ContactSearchStateMessage, { testId: "contact-search-failed", children: t("contact_search.failed") }));
68
+ }
69
+ if (contacts.length === 0) {
70
+ return (_jsxs(ContactSearchStateMessage, { testId: "contact-search-no-result", children: [t("contact_search.no_result_title"), _jsx("br", {}), t("contact_search.no_result_hint")] }));
71
+ }
72
+ return null;
73
+ };
74
+ // `Tất cả` only previews the contact section and hands off to `Liên hệ`;
75
+ // the message section belongs to the message-search ticket.
76
+ const renderAllTab = () => {
77
+ const placeholder = renderPlaceholderState();
78
+ if (placeholder)
79
+ return placeholder;
80
+ const hasMore = contacts.length > CONTACT_SEARCH_PREVIEW_LIMIT || !!hasNextPage;
81
+ const openContactsTab = () => handleChangeTab(ContactSearchTab.Contacts);
82
+ return (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsx(ContactSearchSectionHeader, { title: t("contact_search.tabs.contacts"), count: totalElements || undefined, onSelect: openContactsTab }), renderContactItems(contacts.slice(0, CONTACT_SEARCH_PREVIEW_LIMIT)), hasMore && _jsx(ContactSearchViewMoreBar, { onSelect: openContactsTab })] }));
83
+ };
84
+ const renderContactList = () => (_jsx("div", { id: SCROLL_TARGET_ID, className: "flex-1 overflow-y-auto", children: _jsx(InfiniteScroll, { dataLength: contacts.length, next: fetchNextPage, hasMore: !!hasNextPage, scrollableTarget: SCROLL_TARGET_ID, loader: isFetchingNextPage ? (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx(Spin, { size: "small" }) })) : null, children: renderContactItems(contacts) }) }));
85
+ // Filters stay mounted even with no results so the seller can switch type.
86
+ const renderContactsTab = () => {
87
+ var _a;
88
+ return (_jsxs(_Fragment, { children: [_jsx(ContactSearchFilters, { activeType: activeType, onChange: setActiveType }), (_a = renderPlaceholderState()) !== null && _a !== void 0 ? _a : renderContactList()] }));
89
+ };
90
+ const tabItems = [
91
+ {
92
+ key: ContactSearchTab.All,
93
+ label: t("contact_search.tabs.all"),
94
+ children: renderAllTab(),
95
+ },
96
+ {
97
+ key: ContactSearchTab.Contacts,
98
+ label: t("contact_search.tabs.contacts"),
99
+ children: renderContactsTab(),
100
+ },
101
+ {
102
+ key: ContactSearchTab.Messages,
103
+ label: t("contact_search.tabs.messages"),
104
+ children: (_jsx(ContactSearchStateMessage, { testId: "contact-search-messages", children: t("contact_search.messages_not_available") })),
105
+ },
106
+ ];
107
+ return (_jsx(Drawer, { open: open, onClose: onClose, placement: "right", closeIcon: false, width: "100%", "data-cp-name": "ContactSearchPanel", styles: { body: { padding: 0 } }, getContainer: false, destroyOnHidden: true, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [_jsx("button", { type: "button", "data-testid": "contact-search-back", "aria-label": t("contact_search.back"), className: "flex shrink-0 cursor-pointer items-center justify-center p-1 text-primary-400", onClick: onClose, children: _jsx(Icon, { icon: "arrow-left-o", size: 24 }) }), _jsx(Input, { allowClear: true, autoFocus: true, value: keyword, "data-testid": "contact-search-input", placeholder: t("search_placeholder"), prefix: _jsx("span", { className: "flex h-10 w-10 items-center justify-center", children: _jsx(Icon, { icon: "search-o", size: CONTACT_SEARCH_ICON_SIZE }) }),
108
+ // Padding lives on the prefix/input instead of the wrapper so the
109
+ // icon keeps its 40x40 box from the design.
110
+ className: "!h-10 !rounded-lg !border-none !bg-gray-25 !p-0 !shadow-none", classNames: {
111
+ prefix: "!m-0 text-gray-600",
112
+ input: "!bg-transparent !px-0 !text-[14px] !leading-[1.6] !tracking-[0.14px] !text-gray-900",
113
+ suffix: "!mr-4",
114
+ }, onChange: (event) => setKeyword(event.target.value) })] }), _jsx(ContactSearchTabs, { activeTab: activeTab, items: tabItems, onChange: handleChangeTab })] }) }));
115
+ };
116
+ export default ContactSearchPanel;
@@ -0,0 +1,9 @@
1
+ import { ContactSearchItem } from "../../../types/dto";
2
+ interface ContactSearchResultItemProps {
3
+ contact: ContactSearchItem;
4
+ searchTerm: string;
5
+ onSelect: (contact: ContactSearchItem) => void;
6
+ }
7
+ declare const ContactSearchResultItem: ({ contact, searchTerm, onSelect, }: ContactSearchResultItemProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default ContactSearchResultItem;
9
+ //# sourceMappingURL=ContactSearchResultItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSearchResultItem.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchResultItem.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,iBAAiB,EAAqB,MAAM,aAAa,CAAC;AAGnE,UAAU,4BAA4B;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAChD;AAED,QAAA,MAAM,uBAAuB,GAAI,oCAI9B,4BAA4B,4CAiE9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTranslation } from "react-i18next";
4
+ import { Icon } from "../../../components/icon";
5
+ import ConversationPeerAvatar from "../../../components/conversation-inbox/ConversationPeerAvatar";
6
+ import { highlightSearch, sanitizeHtml } from "../../../utils/common";
7
+ import { resolvePeerBadgeLabel } from "../../../utils/conversation";
8
+ import { ContactSearchType } from "../../../types/dto";
9
+ import { formatLastOnlineTime, resolveContactPeerType } from "./utils";
10
+ const ContactSearchResultItem = ({ contact, searchTerm, onSelect, }) => {
11
+ const { t } = useTranslation("conversation-inbox");
12
+ const isGroup = contact.type === ContactSearchType.Group;
13
+ const peerType = resolveContactPeerType(contact.type);
14
+ // Supporting line differs per contact type (spec §Contact Result Content).
15
+ // PDP intentionally has none — its last online time was dropped from spec.
16
+ const buildSupportingText = () => {
17
+ var _a;
18
+ if (isGroup) {
19
+ return t("contact_search.member_count", {
20
+ count: (_a = contact.memberCount) !== null && _a !== void 0 ? _a : 0,
21
+ });
22
+ }
23
+ if (contact.type === ContactSearchType.PdpBot)
24
+ return "";
25
+ return formatLastOnlineTime(contact.lastOnlineTime);
26
+ };
27
+ const supportingText = buildSupportingText();
28
+ return (_jsxs("button", { type: "button", "data-cp-name": "ContactSearchResultItem", "data-testid": `contact-search-result-${contact.id}`, className: "flex w-full items-center gap-3 border-b border-gray-25 px-3 py-2.5 text-left hover:bg-gray-10", onClick: () => onSelect(contact), children: [_jsx(ConversationPeerAvatar, { peerType: peerType, displayName: contact.targetName, avatarUrl: contact.targetAvatar, badgeLabel: resolvePeerBadgeLabel(peerType, t) }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-1", children: [isGroup && (_jsx(Icon, { icon: "user-three-b", size: 16, className: "shrink-0 text-gray-900", "aria-hidden": true })), _jsx("span", { className: "min-w-0 truncate text-[16px] font-medium leading-[1.6] text-gray-900",
29
+ // highlightSearch returns a <mark> around the matched part only.
30
+ dangerouslySetInnerHTML: {
31
+ __html: sanitizeHtml(highlightSearch(contact.targetName || "", searchTerm)),
32
+ } })] }), supportingText && (_jsx("span", { className: "truncate text-[14px] leading-[1.6] text-gray-500", children: supportingText }))] })] }));
33
+ };
34
+ export default ContactSearchResultItem;
@@ -0,0 +1,8 @@
1
+ interface ContactSearchSectionHeaderProps {
2
+ title: string;
3
+ count?: number;
4
+ onSelect: () => void;
5
+ }
6
+ declare const ContactSearchSectionHeader: ({ title, count, onSelect, }: ContactSearchSectionHeaderProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default ContactSearchSectionHeader;
8
+ //# sourceMappingURL=ContactSearchSectionHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSearchSectionHeader.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchSectionHeader.tsx"],"names":[],"mappings":"AAIA,UAAU,+BAA+B;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,0BAA0B,GAAI,6BAIjC,+BAA+B,4CA2BjC,CAAC;AAEF,eAAe,0BAA0B,CAAC"}
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Icon } from "../../../components/icon";
4
+ const ContactSearchSectionHeader = ({ title, count, onSelect, }) => {
5
+ return (_jsxs("button", { type: "button", "data-cp-name": "ContactSearchSectionHeader", "data-testid": "contact-search-section-header", className: "flex w-full items-center gap-4 bg-white px-3 py-2 text-left", onClick: onSelect, children: [_jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-[16px] font-bold leading-[1.6] tracking-[0.16px] text-gray-900", children: title }), count !== undefined && (_jsx("span", { className: "text-[14px] font-medium leading-[1.6] text-gray-400", children: ` (${count})` }))] }), _jsx(Icon, { icon: "angle-right-o", size: 20, className: "shrink-0 text-gray-400", "aria-hidden": true })] }));
6
+ };
7
+ export default ContactSearchSectionHeader;
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ interface ContactSearchStateMessageProps {
3
+ children: ReactNode;
4
+ testId?: string;
5
+ }
6
+ declare const ContactSearchStateMessage: ({ children, testId, }: ContactSearchStateMessageProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default ContactSearchStateMessage;
8
+ //# sourceMappingURL=ContactSearchStateMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSearchStateMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchStateMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,UAAU,8BAA8B;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,QAAA,MAAM,yBAAyB,GAAI,uBAGhC,8BAA8B,4CAqBhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { images } from "../../../constants/images";
4
+ // Shared body for the initial / no-result / failed states: the same 100px
5
+ // illustration above centered gray copy.
6
+ const ContactSearchStateMessage = ({ children, testId, }) => {
7
+ return (_jsxs("div", { "data-cp-name": "ContactSearchStateMessage", "data-testid": testId, className: "flex flex-1 flex-col items-center gap-3 bg-white px-8 py-[42px]", children: [_jsx("div", { className: "flex h-[100px] w-[100px] shrink-0 items-center justify-center", children: _jsx("img", { src: images.searchEmpty, alt: "", "aria-hidden": "true", className: "h-[87px] w-[101px] max-w-none" }) }), _jsx("p", { className: "w-full text-center text-[16px] leading-[1.6] tracking-[0.16px] text-gray-600", children: children })] }));
8
+ };
9
+ export default ContactSearchStateMessage;
@@ -0,0 +1,10 @@
1
+ import { type TabsProps } from "antd";
2
+ import { ContactSearchTab } from "./constants";
3
+ interface ContactSearchTabsProps {
4
+ activeTab: ContactSearchTab;
5
+ items: TabsProps["items"];
6
+ onChange: (tab: ContactSearchTab) => void;
7
+ }
8
+ declare const ContactSearchTabs: ({ activeTab, items, onChange, }: ContactSearchTabsProps) => import("react/jsx-runtime").JSX.Element;
9
+ export default ContactSearchTabs;
10
+ //# sourceMappingURL=ContactSearchTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSearchTabs.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchTabs.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiC/C,UAAU,sBAAsB;IAC9B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAC1B,QAAQ,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC3C;AAED,QAAA,MAAM,iBAAiB,GAAI,iCAIxB,sBAAsB,4CAgBxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Tabs } from "antd";
4
+ // antd's line tabs are restyled to the design: no nav margin, adjacent tabs,
5
+ // 14px bold labels, and a 3px rounded ink bar in the brand colour.
6
+ const TAB_CLASS_NAMES = [
7
+ "flex flex-1 flex-col overflow-hidden",
8
+ "[&_.ant-tabs-nav]:mb-0 [&_.ant-tabs-nav]:h-[42px] [&_.ant-tabs-nav]:bg-white",
9
+ "[&_.ant-tabs-nav]:before:border-b [&_.ant-tabs-nav]:before:border-gray-25",
10
+ "[&_.ant-tabs-nav-wrap]:h-full [&_.ant-tabs-nav-list]:h-full",
11
+ // Design padding is `2px 12px 0 12px` — no bottom padding, the tab stretches
12
+ // to the bar height and the indicator sits flush at its bottom edge.
13
+ "[&_.ant-tabs-tab]:h-full [&_.ant-tabs-tab]:px-3 [&_.ant-tabs-tab]:pb-0 [&_.ant-tabs-tab]:pt-0.5",
14
+ "[&_.ant-tabs-tab+.ant-tabs-tab]:ml-0",
15
+ "[&_.ant-tabs-tab-btn]:text-[14px] [&_.ant-tabs-tab-btn]:font-bold",
16
+ "[&_.ant-tabs-tab-btn]:leading-[1.6] [&_.ant-tabs-tab-btn]:tracking-[0.14px]",
17
+ "[&_.ant-tabs-tab-btn]:!text-gray-400",
18
+ "[&_.ant-tabs-tab-active_.ant-tabs-tab-btn]:!text-primary-400",
19
+ "[&_.ant-tabs-ink-bar]:h-[3px] [&_.ant-tabs-ink-bar]:rounded",
20
+ "[&_.ant-tabs-ink-bar]:bg-primary-400",
21
+ "[&_.ant-tabs-content-holder]:flex [&_.ant-tabs-content-holder]:flex-1",
22
+ "[&_.ant-tabs-content-holder]:overflow-hidden",
23
+ "[&_.ant-tabs-content]:h-full",
24
+ // Only the active pane gets sizing — styling every `.ant-tabs-tabpane`
25
+ // overrides the height:0 antd uses to hide the inactive ones, which made
26
+ // two panes render side by side.
27
+ "[&_.ant-tabs-tabpane-active]:flex [&_.ant-tabs-tabpane-active]:h-full",
28
+ "[&_.ant-tabs-tabpane-active]:flex-col",
29
+ ].join(" ");
30
+ // The design draws the indicator under the label only, while antd spans the
31
+ // whole tab including its horizontal padding.
32
+ const TAB_HORIZONTAL_PADDING = 24;
33
+ const ContactSearchTabs = ({ activeTab, items, onChange, }) => {
34
+ return (_jsx(Tabs, { "data-cp-name": "ContactSearchTabs", activeKey: activeTab, items: items, className: TAB_CLASS_NAMES, tabBarGutter: 0, indicator: {
35
+ size: (origin) => origin - TAB_HORIZONTAL_PADDING,
36
+ align: "center",
37
+ }, destroyOnHidden: true, onChange: (key) => onChange(key) }));
38
+ };
39
+ export default ContactSearchTabs;
@@ -0,0 +1,6 @@
1
+ interface ContactSearchViewMoreBarProps {
2
+ onSelect: () => void;
3
+ }
4
+ declare const ContactSearchViewMoreBar: ({ onSelect, }: ContactSearchViewMoreBarProps) => import("react/jsx-runtime").JSX.Element;
5
+ export default ContactSearchViewMoreBar;
6
+ //# sourceMappingURL=ContactSearchViewMoreBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactSearchViewMoreBar.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/ContactSearchViewMoreBar.tsx"],"names":[],"mappings":"AAKA,UAAU,6BAA6B;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,wBAAwB,GAAI,eAE/B,6BAA6B,4CA4B/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useTranslation } from "react-i18next";
4
+ import { Icon } from "../../../components/icon";
5
+ const ContactSearchViewMoreBar = ({ onSelect, }) => {
6
+ const { t } = useTranslation("conversation-inbox");
7
+ return (_jsx("div", { "data-cp-name": "ContactSearchViewMoreBar", className: "flex w-full flex-col items-center border-t border-gray-25 bg-white", children: _jsx("div", { className: "flex w-full items-center justify-center p-1", children: _jsxs("button", { type: "button", "data-testid": "contact-search-view-all", className: "flex h-8 items-center justify-center gap-2 px-3", onClick: onSelect, children: [_jsx("span", { className: "whitespace-nowrap text-center text-[13px] font-medium leading-[1.6] text-primary-400", children: t("contact_search.view_all") }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "shrink-0 text-primary-400", "aria-hidden": true })] }) }) }));
8
+ };
9
+ export default ContactSearchViewMoreBar;
@@ -0,0 +1,18 @@
1
+ import { ContactSearchType } from "../../../types/dto";
2
+ export declare const CONTACT_SEARCH_DEBOUNCE_MS = 300;
3
+ export declare const CONTACT_SEARCH_ICON_SIZE = 18;
4
+ export declare const CONTACT_SEARCH_PREVIEW_LIMIT = 5;
5
+ export declare enum ContactSearchTab {
6
+ All = "ALL",
7
+ Contacts = "CONTACTS",
8
+ Messages = "MESSAGES"
9
+ }
10
+ export declare const CONTACT_SEARCH_TABS: {
11
+ tab: ContactSearchTab;
12
+ labelKey: string;
13
+ }[];
14
+ export declare const CONTACT_SEARCH_FILTERS: {
15
+ type: ContactSearchType;
16
+ labelKey: string;
17
+ }[];
18
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAI3C,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,oBAAY,gBAAgB;IAC1B,GAAG,QAAQ;IACX,QAAQ,aAAa;IACrB,QAAQ,aAAa;CACtB;AAED,eAAO,MAAM,mBAAmB,EAAE;IAChC,GAAG,EAAE,gBAAgB,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,EAIA,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE;IACnC,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,EAKA,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { ContactSearchType } from "../../../types/dto";
2
+ export const CONTACT_SEARCH_DEBOUNCE_MS = 300;
3
+ export const CONTACT_SEARCH_ICON_SIZE = 18;
4
+ // `Tất cả` shows a capped preview of the contact section before handing the
5
+ // seller over to the full `Liên hệ` tab.
6
+ export const CONTACT_SEARCH_PREVIEW_LIMIT = 5;
7
+ export var ContactSearchTab;
8
+ (function (ContactSearchTab) {
9
+ ContactSearchTab["All"] = "ALL";
10
+ ContactSearchTab["Contacts"] = "CONTACTS";
11
+ ContactSearchTab["Messages"] = "MESSAGES";
12
+ })(ContactSearchTab || (ContactSearchTab = {}));
13
+ export const CONTACT_SEARCH_TABS = [
14
+ { tab: ContactSearchTab.All, labelKey: "contact_search.tabs.all" },
15
+ { tab: ContactSearchTab.Contacts, labelKey: "contact_search.tabs.contacts" },
16
+ { tab: ContactSearchTab.Messages, labelKey: "contact_search.tabs.messages" },
17
+ ];
18
+ export const CONTACT_SEARCH_FILTERS = [
19
+ { type: ContactSearchType.Customer, labelKey: "contact_search.filters.customer" },
20
+ { type: ContactSearchType.PdpBot, labelKey: "contact_search.filters.pdp" },
21
+ { type: ContactSearchType.Group, labelKey: "contact_search.filters.group" },
22
+ { type: ContactSearchType.User, labelKey: "contact_search.filters.seller" },
23
+ ];
@@ -0,0 +1,10 @@
1
+ export { default as ContactSearchPanel } from "./ContactSearchPanel";
2
+ export { default as ContactSearchFilters } from "./ContactSearchFilters";
3
+ export { default as ContactSearchResultItem } from "./ContactSearchResultItem";
4
+ export { default as ContactSearchStateMessage } from "./ContactSearchStateMessage";
5
+ export { default as ContactSearchTabs } from "./ContactSearchTabs";
6
+ export { default as ContactSearchSectionHeader } from "./ContactSearchSectionHeader";
7
+ export { default as ContactSearchViewMoreBar } from "./ContactSearchViewMoreBar";
8
+ export * from "./constants";
9
+ export * from "./utils";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACjF,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { default as ContactSearchPanel } from "./ContactSearchPanel";
2
+ export { default as ContactSearchFilters } from "./ContactSearchFilters";
3
+ export { default as ContactSearchResultItem } from "./ContactSearchResultItem";
4
+ export { default as ContactSearchStateMessage } from "./ContactSearchStateMessage";
5
+ export { default as ContactSearchTabs } from "./ContactSearchTabs";
6
+ export { default as ContactSearchSectionHeader } from "./ContactSearchSectionHeader";
7
+ export { default as ContactSearchViewMoreBar } from "./ContactSearchViewMoreBar";
8
+ export * from "./constants";
9
+ export * from "./utils";
@@ -0,0 +1,6 @@
1
+ import { ContactSearchItem, ContactSearchType } from "../../../types/dto";
2
+ import { PeerType } from "../../../types/conversation";
3
+ export declare const resolveContactPeerType: (type: ContactSearchType) => PeerType;
4
+ export declare const formatLastOnlineTime: (lastOnlineTime: string | null) => string;
5
+ export declare const buildContactSearchParams: (contact: ContactSearchItem) => URLSearchParams;
6
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-inbox/contact-search/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAahD,eAAO,MAAM,sBAAsB,GAAI,MAAM,iBAAiB,KAAG,QAChC,CAAC;AAIlC,eAAO,MAAM,oBAAoB,GAAI,gBAAgB,MAAM,GAAG,IAAI,KAAG,MACd,CAAC;AAIxD,eAAO,MAAM,wBAAwB,GACnC,SAAS,iBAAiB,KACzB,eA0BF,CAAC"}
@@ -0,0 +1,44 @@
1
+ import dayjs from "dayjs";
2
+ import relativeTime from "dayjs/plugin/relativeTime";
3
+ import { ContactSearchType } from "../../../types/dto";
4
+ import { PeerType } from "../../../types/conversation";
5
+ dayjs.extend(relativeTime);
6
+ // Search results reuse the inbox avatar, which drives its Mall / Bot badge
7
+ // off PeerType rather than the search-specific contact type.
8
+ const CONTACT_TYPE_TO_PEER_TYPE = {
9
+ [ContactSearchType.Customer]: PeerType.CUSTOMER,
10
+ [ContactSearchType.PdpBot]: PeerType.BOT,
11
+ [ContactSearchType.Group]: PeerType.GROUP,
12
+ [ContactSearchType.User]: PeerType.USER,
13
+ };
14
+ export const resolveContactPeerType = (type) => CONTACT_TYPE_TO_PEER_TYPE[type];
15
+ // The Vietnamese dayjs locale is already registered in the SDK layout, so
16
+ // fromNow() renders "1 giờ trước" without extra setup here.
17
+ export const formatLastOnlineTime = (lastOnlineTime) => lastOnlineTime ? dayjs(lastOnlineTime).fromNow() : "";
18
+ // Each contact type resolves to a different route param; the conversation
19
+ // detail screen turns it into an existing thread or the new-chat flow.
20
+ export const buildContactSearchParams = (contact) => {
21
+ var _a;
22
+ const params = new URLSearchParams(window.location.search);
23
+ params.delete("threadId");
24
+ params.delete("userId");
25
+ params.delete("groupId");
26
+ params.delete("botId");
27
+ if (contact.type === ContactSearchType.Group) {
28
+ if (contact.targetGroupId)
29
+ params.set("groupId", contact.targetGroupId);
30
+ return params;
31
+ }
32
+ // PDP bots carry their id in targetBotId; targetUserId is null for them.
33
+ if (contact.type === ContactSearchType.PdpBot) {
34
+ if (contact.targetBotId)
35
+ params.set("botId", contact.targetBotId);
36
+ return params;
37
+ }
38
+ // Individual contacts: sellers carry targetUserId, customers carry
39
+ // targetCustomerId — the other field is null in each case.
40
+ const peerId = (_a = contact.targetUserId) !== null && _a !== void 0 ? _a : contact.targetCustomerId;
41
+ if (peerId)
42
+ params.set("userId", peerId);
43
+ return params;
44
+ };
@@ -4,5 +4,6 @@ export interface MentionOption {
4
4
  faceURL?: string;
5
5
  isAtAll: boolean;
6
6
  }
7
+ export declare const ALL_MEMBERS_USER_ID = "__all__";
7
8
  export declare const useMentionOptions: (groupID: string | undefined, query: string) => MentionOption[];
8
9
  //# sourceMappingURL=useMentionOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMentionOptions.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/useMentionOptions.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAaD,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,EAC3B,OAAO,MAAM,KACZ,aAAa,EAuCf,CAAC"}
1
+ {"version":3,"file":"useMentionOptions.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/useMentionOptions.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAW7C,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,EAC3B,OAAO,MAAM,KACZ,aAAa,EAuCf,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { useMemo } from "react";
2
2
  import { useTranslation } from "react-i18next";
3
3
  import { useGroupMembers } from "../../../../hooks/group/useGroupMembers";
4
- const ALL_MEMBERS_USER_ID = "__all__";
4
+ export const ALL_MEMBERS_USER_ID = "__all__";
5
5
  // Prefix matches rank above mid-string matches, then alphabetical — matches
6
6
  // the "closest match first" ranking spec'd for this feature (not just a
7
7
  // plain filter). No keyword: falls back to plain A-Z (AC-Open default).
@@ -3,5 +3,6 @@ export declare const images: {
3
3
  imageFailed: string;
4
4
  emptyResources: string;
5
5
  muteNotificationWarning: string;
6
+ searchEmpty: string;
6
7
  };
7
8
  //# sourceMappingURL=images.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/constants/images.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,MAAM;;;;;CAKlB,CAAC"}
1
+ {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/constants/images.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,MAAM;;;;;;CAMlB,CAAC"}
@@ -3,9 +3,11 @@ const imageFailed = new URL("../assets/images/imageFailed.png", import.meta.url)
3
3
  .href;
4
4
  const emptyResources = new URL("../assets/images/emptyResources.png", import.meta.url).href;
5
5
  const muteNotificationWarning = new URL("../assets/images/muteNotificationWarning.png", import.meta.url).href;
6
+ const searchEmpty = new URL("../assets/images/searchEmpty.png", import.meta.url).href;
6
7
  export const images = {
7
8
  conversationBg,
8
9
  imageFailed,
9
10
  emptyResources,
10
11
  muteNotificationWarning,
12
+ searchEmpty,
11
13
  };
@@ -0,0 +1,18 @@
1
+ import type { BaseResponse, ContactSearchItem, ContactSearchType } from "../../types/dto";
2
+ interface UseSearchContactsParams {
3
+ searchTerm: string;
4
+ type?: ContactSearchType;
5
+ enabled?: boolean;
6
+ pageSize?: number;
7
+ }
8
+ export declare const useSearchContacts: ({ searchTerm, type, enabled, pageSize, }: UseSearchContactsParams) => {
9
+ contacts: ContactSearchItem[];
10
+ totalElements: number;
11
+ fetchNextPage: (options?: import("@tanstack/react-query").FetchNextPageOptions) => Promise<import("@tanstack/react-query").InfiniteQueryObserverResult<import("@tanstack/react-query").InfiniteData<BaseResponse<ContactSearchItem[]>, unknown>, Error>>;
12
+ hasNextPage: boolean;
13
+ isFetchingNextPage: boolean;
14
+ isLoading: boolean;
15
+ isError: boolean;
16
+ };
17
+ export {};
18
+ //# sourceMappingURL=useSearchContacts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSearchContacts.d.ts","sourceRoot":"","sources":["../../../src/hooks/search/useSearchContacts.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EAElB,MAAM,aAAa,CAAC;AAErB,UAAU,uBAAuB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,iBAAiB,GAAI,0CAK/B,uBAAuB;;;;;;;;CA+CzB,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { useMemo } from "react";
3
+ import { useInfiniteQuery } from "@tanstack/react-query";
4
+ import { apiInstance } from "../../services/api";
5
+ import { ENDPOINTS } from "../../services/routes";
6
+ import { QUERY_KEYS } from "../../services/query";
7
+ import { PAGE_SIZE } from "../../constants";
8
+ import useAuthStore from "../../store/auth";
9
+ export const useSearchContacts = ({ searchTerm, type, enabled = true, pageSize = PAGE_SIZE.medium, }) => {
10
+ var _a, _b, _c, _d;
11
+ const trimmedSearchTerm = searchTerm.trim();
12
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError } = useInfiniteQuery({
13
+ initialPageParam: 1,
14
+ queryKey: [QUERY_KEYS.SEARCH_CONTACTS, trimmedSearchTerm, type, pageSize],
15
+ queryFn: async ({ pageParam = 1 }) => {
16
+ const payload = Object.assign({ applicationType: useAuthStore.getState().applicationType, searchTerm: trimmedSearchTerm, page: pageParam, pageSize }, (type ? { type } : {}));
17
+ const res = await apiInstance.post(ENDPOINTS.chatService.searchContacts, payload);
18
+ return res.data;
19
+ },
20
+ getNextPageParam: (lastPage) => {
21
+ var _a, _b;
22
+ const dataLength = ((_a = lastPage === null || lastPage === void 0 ? void 0 : lastPage.data) === null || _a === void 0 ? void 0 : _a.length) || 0;
23
+ const currentPage = ((_b = lastPage === null || lastPage === void 0 ? void 0 : lastPage.pageable) === null || _b === void 0 ? void 0 : _b.pageNumber) || 1;
24
+ return dataLength < pageSize ? undefined : currentPage + 1;
25
+ },
26
+ enabled: enabled && trimmedSearchTerm !== "",
27
+ });
28
+ const contacts = useMemo(() => { var _a, _b; return (_b = (_a = data === null || data === void 0 ? void 0 : data.pages) === null || _a === void 0 ? void 0 : _a.flatMap((page) => { var _a; return (_a = page === null || page === void 0 ? void 0 : page.data) !== null && _a !== void 0 ? _a : []; })) !== null && _b !== void 0 ? _b : []; }, [data]);
29
+ const totalElements = (_d = (_c = (_b = (_a = data === null || data === void 0 ? void 0 : data.pages) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.pageable) === null || _c === void 0 ? void 0 : _c.totalElements) !== null && _d !== void 0 ? _d : 0;
30
+ return {
31
+ contacts,
32
+ totalElements,
33
+ fetchNextPage,
34
+ hasNextPage,
35
+ isFetchingNextPage,
36
+ isLoading,
37
+ isError,
38
+ };
39
+ };
@@ -118,5 +118,26 @@
118
118
  "loading": "Đang tải xem trước liên kết...",
119
119
  "remove": "Xóa xem trước liên kết"
120
120
  },
121
- "link_open_error": "Không thể mở liên kết. Vui lòng thử lại."
121
+ "link_open_error": "Không thể mở liên kết. Vui lòng thử lại.",
122
+ "contact_search": {
123
+ "back": "Quay lại",
124
+ "tabs": {
125
+ "all": "Tất cả",
126
+ "contacts": "Liên hệ",
127
+ "messages": "Tin nhắn"
128
+ },
129
+ "view_all": "Xem tất cả",
130
+ "initial": "Bạn có thể tìm kiếm theo liên hệ hoặc nội dung tin nhắn",
131
+ "no_result_title": "Không tìm thấy kết quả nào",
132
+ "no_result_hint": "Hãy thử lại với cách viết hoặc kết hợp cụm từ khác phù hợp hơn.",
133
+ "failed": "Không tải được kết quả. Vui lòng thử lại",
134
+ "messages_not_available": "Tìm kiếm tin nhắn sẽ được bổ sung sau",
135
+ "member_count": "{{count}} thành viên",
136
+ "filters": {
137
+ "customer": "Khách hàng",
138
+ "pdp": "PDP",
139
+ "group": "Nhóm",
140
+ "seller": "Người bán"
141
+ }
142
+ }
122
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-inbox/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,MAAM,WAAW,2BAA2B;IAC1C,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAClC;AAED,QAAA,MAAM,sBAAsB,GAAI,6CAI7B,2BAA2B,4CA2D7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-inbox/index.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,MAAM,WAAW,2BAA2B;IAC1C,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAClC;AAED,QAAA,MAAM,sBAAsB,GAAI,6CAI7B,2BAA2B,4CAyE7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}