@droppii-org/chat-sdk 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/svg/cannedResponse.d.ts +7 -0
- package/dist/assets/svg/cannedResponse.d.ts.map +1 -0
- package/dist/assets/svg/cannedResponse.js +3 -0
- package/dist/assets/svg/cannedResponse.tsx +48 -0
- package/dist/assets/svg/index.d.ts +1 -0
- package/dist/assets/svg/index.d.ts.map +1 -1
- package/dist/assets/svg/index.js +1 -0
- package/dist/assets/svg/index.ts +1 -0
- package/dist/components/cannedResponse/CannedResponseBody.d.ts +8 -0
- package/dist/components/cannedResponse/CannedResponseBody.d.ts.map +1 -0
- package/dist/components/cannedResponse/CannedResponseBody.js +58 -0
- package/dist/components/cannedResponse/CannedResponseFooter.d.ts +6 -0
- package/dist/components/cannedResponse/CannedResponseFooter.d.ts.map +1 -0
- package/dist/components/cannedResponse/CannedResponseFooter.js +8 -0
- package/dist/components/cannedResponse/CannedResponseHeader.d.ts +8 -0
- package/dist/components/cannedResponse/CannedResponseHeader.d.ts.map +1 -0
- package/dist/components/cannedResponse/CannedResponseHeader.js +11 -0
- package/dist/components/cannedResponse/index.d.ts +8 -0
- package/dist/components/cannedResponse/index.d.ts.map +1 -0
- package/dist/components/cannedResponse/index.js +34 -0
- package/dist/components/cannedResponse/team/TeamItem.d.ts +11 -0
- package/dist/components/cannedResponse/team/TeamItem.d.ts.map +1 -0
- package/dist/components/cannedResponse/team/TeamItem.js +31 -0
- package/dist/components/message/MessageList.d.ts +1 -0
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +2 -2
- package/dist/components/message/footer/CannedResponsePlugin.d.ts +7 -0
- package/dist/components/message/footer/CannedResponsePlugin.d.ts.map +1 -0
- package/dist/components/message/footer/CannedResponsePlugin.js +31 -0
- package/dist/components/message/footer/MediaActions.d.ts.map +1 -1
- package/dist/components/message/footer/MediaActions.js +22 -2
- package/dist/components/message/footer/index.d.ts +2 -1
- package/dist/components/message/footer/index.d.ts.map +1 -1
- package/dist/components/message/footer/index.js +28 -2
- package/dist/components/message/item/QuoteMessage.d.ts.map +1 -1
- package/dist/components/message/item/QuoteMessage.js +56 -10
- package/dist/components/message/item/index.js +1 -1
- package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts +3 -0
- package/dist/hooks/cannedResponse/useFetchCannedCategories.d.ts.map +1 -0
- package/dist/hooks/cannedResponse/useFetchCannedCategories.js +13 -0
- package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts +219 -0
- package/dist/hooks/cannedResponse/useFetchCannedResponse.d.ts.map +1 -0
- package/dist/hooks/cannedResponse/useFetchCannedResponse.js +55 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +17 -1
- package/dist/hooks/team/useFetchMyTeam.d.ts +3 -0
- package/dist/hooks/team/useFetchMyTeam.d.ts.map +1 -0
- package/dist/hooks/team/useFetchMyTeam.js +12 -0
- package/dist/locales/vi/common.json +9 -1
- package/dist/screens/deskMessage/index.d.ts +4 -1
- package/dist/screens/deskMessage/index.d.ts.map +1 -1
- package/dist/screens/deskMessage/index.js +2 -2
- package/dist/services/query.d.ts +3 -0
- package/dist/services/query.d.ts.map +1 -1
- package/dist/services/query.js +3 -0
- package/dist/services/routes.d.ts +3 -0
- package/dist/services/routes.d.ts.map +1 -1
- package/dist/services/routes.js +3 -0
- package/dist/styles/global.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/chat.d.ts +6 -0
- package/dist/types/chat.d.ts.map +1 -1
- package/dist/types/chat.js +5 -0
- package/dist/types/dto.d.ts +27 -1
- package/dist/types/dto.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cannedResponse.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/cannedResponse.tsx"],"names":[],"mappings":"AAAA,UAAU,SAAS;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,kBAAkB,GAAI,OAAO,SAAS,4CAyC3C,CAAC;AACF,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const CannedResponseIcon = (props) => (_jsxs("svg", { width: props.size || 18, height: props.size || 18, viewBox: `${0} ${0} ${props.size || 18} ${props.size || 18}`, fill: "none", xmlns: "http://www.w3.org/2000/svg", className: props === null || props === void 0 ? void 0 : props.className, children: [_jsxs("g", { clipPath: "url(#clip0_4070_70030)", children: [_jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M6.66732 0.833374C7.12755 0.833374 7.50065 1.20647 7.50065 1.66671L5.00065 18.3334C5.00065 18.7936 4.62755 19.1667 4.16732 19.1667C3.70708 19.1667 3.33398 18.7936 3.33398 18.3334L5.83398 1.66671C5.83398 1.20647 6.20708 0.833374 6.66732 0.833374Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M14.9993 0.833374C15.4596 0.833374 15.8327 1.20647 15.8327 1.66671L13.3327 18.3334C13.3327 18.7936 12.9596 19.1667 12.4993 19.1667C12.0391 19.1667 11.666 18.7936 11.666 18.3334L14.166 1.66671C14.166 1.20647 14.5391 0.833374 14.9993 0.833374Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M19.1673 5.83333C19.1673 6.29357 18.7942 6.66667 18.334 6.66667H1.66732C1.20708 6.66667 0.833984 6.29357 0.833984 5.83333C0.833984 5.37309 1.20708 5 1.66732 5H18.334C18.7942 5 19.1673 5.3731 19.1673 5.83333Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M19.1673 14.1667C19.1673 14.627 18.7942 15 18.334 15H1.66732C1.20708 15 0.833984 14.627 0.833984 14.1667C0.833984 13.7065 1.20708 13.3334 1.66732 13.3334H18.334C18.7942 13.3334 19.1673 13.7065 19.1673 14.1667Z", fill: "currentColor" })] }), _jsx("defs", { children: _jsx("clipPath", { id: "clip0_4070_70030", children: _jsx("rect", { width: props.size || 18, height: props.size || 18, fill: "white" }) }) })] }));
|
|
3
|
+
export default CannedResponseIcon;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
interface ISvgProps {
|
|
2
|
+
size?: number;
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const CannedResponseIcon = (props: ISvgProps) => (
|
|
7
|
+
<svg
|
|
8
|
+
width={props.size || 18}
|
|
9
|
+
height={props.size || 18}
|
|
10
|
+
viewBox={`${0} ${0} ${props.size || 18} ${props.size || 18}`}
|
|
11
|
+
fill="none"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
className={props?.className}
|
|
14
|
+
>
|
|
15
|
+
<g clipPath="url(#clip0_4070_70030)">
|
|
16
|
+
<path
|
|
17
|
+
fillRule="evenodd"
|
|
18
|
+
clipRule="evenodd"
|
|
19
|
+
d="M6.66732 0.833374C7.12755 0.833374 7.50065 1.20647 7.50065 1.66671L5.00065 18.3334C5.00065 18.7936 4.62755 19.1667 4.16732 19.1667C3.70708 19.1667 3.33398 18.7936 3.33398 18.3334L5.83398 1.66671C5.83398 1.20647 6.20708 0.833374 6.66732 0.833374Z"
|
|
20
|
+
fill="currentColor"
|
|
21
|
+
/>
|
|
22
|
+
<path
|
|
23
|
+
fillRule="evenodd"
|
|
24
|
+
clipRule="evenodd"
|
|
25
|
+
d="M14.9993 0.833374C15.4596 0.833374 15.8327 1.20647 15.8327 1.66671L13.3327 18.3334C13.3327 18.7936 12.9596 19.1667 12.4993 19.1667C12.0391 19.1667 11.666 18.7936 11.666 18.3334L14.166 1.66671C14.166 1.20647 14.5391 0.833374 14.9993 0.833374Z"
|
|
26
|
+
fill="currentColor"
|
|
27
|
+
/>
|
|
28
|
+
<path
|
|
29
|
+
fillRule="evenodd"
|
|
30
|
+
clipRule="evenodd"
|
|
31
|
+
d="M19.1673 5.83333C19.1673 6.29357 18.7942 6.66667 18.334 6.66667H1.66732C1.20708 6.66667 0.833984 6.29357 0.833984 5.83333C0.833984 5.37309 1.20708 5 1.66732 5H18.334C18.7942 5 19.1673 5.3731 19.1673 5.83333Z"
|
|
32
|
+
fill="currentColor"
|
|
33
|
+
/>
|
|
34
|
+
<path
|
|
35
|
+
fillRule="evenodd"
|
|
36
|
+
clipRule="evenodd"
|
|
37
|
+
d="M19.1673 14.1667C19.1673 14.627 18.7942 15 18.334 15H1.66732C1.20708 15 0.833984 14.627 0.833984 14.1667C0.833984 13.7065 1.20708 13.3334 1.66732 13.3334H18.334C18.7942 13.3334 19.1673 13.7065 19.1673 14.1667Z"
|
|
38
|
+
fill="currentColor"
|
|
39
|
+
/>
|
|
40
|
+
</g>
|
|
41
|
+
<defs>
|
|
42
|
+
<clipPath id="clip0_4070_70030">
|
|
43
|
+
<rect width={props.size || 18} height={props.size || 18} fill="white" />
|
|
44
|
+
</clipPath>
|
|
45
|
+
</defs>
|
|
46
|
+
</svg>
|
|
47
|
+
);
|
|
48
|
+
export default CannedResponseIcon;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/assets/svg/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC"}
|
package/dist/assets/svg/index.js
CHANGED
package/dist/assets/svg/index.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface ICannedResponseBodyProps {
|
|
2
|
+
search?: string;
|
|
3
|
+
onSelectCannedResponse?: (content: string) => void;
|
|
4
|
+
cannedQuery?: string;
|
|
5
|
+
}
|
|
6
|
+
declare const CannedResponseBody: (props: ICannedResponseBodyProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default CannedResponseBody;
|
|
8
|
+
//# sourceMappingURL=CannedResponseBody.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CannedResponseBody.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseBody.tsx"],"names":[],"mappings":"AAaA,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,kBAAkB,GAAI,OAAO,wBAAwB,4CA6J1D,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Empty, Segmented, Spin } from "antd";
|
|
3
|
+
import { useFetchMyTeams } from "../../hooks/team/useFetchMyTeam";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
6
|
+
import { CannedResponseVisibleScope } from "../../types/chat";
|
|
7
|
+
import TeamItem from "./team/TeamItem";
|
|
8
|
+
import { useFetchCannedResponse } from "../../hooks/cannedResponse/useFetchCannedResponse";
|
|
9
|
+
import InfiniteScroll from "react-infinite-scroll-component";
|
|
10
|
+
import { Icon } from "../icon";
|
|
11
|
+
import { sanitizeHtml } from "../../utils/common";
|
|
12
|
+
import { useDebounce } from "ahooks";
|
|
13
|
+
const CannedResponseBody = (props) => {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const { search, onSelectCannedResponse, cannedQuery } = props;
|
|
16
|
+
const { t } = useTranslation();
|
|
17
|
+
const { data: myTeams, isLoading: isLoadingTeams } = useFetchMyTeams();
|
|
18
|
+
const [scope, setScope] = useState(CannedResponseVisibleScope.TEAM);
|
|
19
|
+
const [selectedTeamId, setSelectedTeamId] = useState(undefined);
|
|
20
|
+
const [selectedCategoryId, setSelectedCategoryId] = useState(undefined);
|
|
21
|
+
const cannedQueryDebounced = useDebounce(cannedQuery, { wait: 300 });
|
|
22
|
+
const { dataFlatten: cannedResponses, fetchNextPage, hasNextPage, } = useFetchCannedResponse({
|
|
23
|
+
visibilityScope: scope,
|
|
24
|
+
teamId: selectedTeamId,
|
|
25
|
+
categoryId: selectedCategoryId,
|
|
26
|
+
shortcut: search || cannedQueryDebounced || "",
|
|
27
|
+
});
|
|
28
|
+
const options = useMemo(() => {
|
|
29
|
+
return [
|
|
30
|
+
{ label: t("team"), value: CannedResponseVisibleScope.TEAM },
|
|
31
|
+
{ label: t("personal"), value: CannedResponseVisibleScope.PRIVATE },
|
|
32
|
+
];
|
|
33
|
+
}, [t]);
|
|
34
|
+
const onSelectCategory = useCallback((teamId, categoryId) => {
|
|
35
|
+
setSelectedTeamId(teamId);
|
|
36
|
+
setSelectedCategoryId(categoryId);
|
|
37
|
+
}, []);
|
|
38
|
+
const renderTeamItem = useCallback((team, index) => {
|
|
39
|
+
return (_jsx(TeamItem, { team: team, onSelectCategory: onSelectCategory, selectedTeamId: selectedTeamId, selectedCategoryId: selectedCategoryId, defaultOpen: index === 0 }, team === null || team === void 0 ? void 0 : team.teamId));
|
|
40
|
+
}, [onSelectCategory, selectedTeamId, selectedCategoryId]);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!isLoadingTeams && myTeams && myTeams.length > 0) {
|
|
43
|
+
setSelectedTeamId(myTeams[0].teamId);
|
|
44
|
+
}
|
|
45
|
+
}, [isLoadingTeams, myTeams]);
|
|
46
|
+
return (_jsxs("div", { className: "flex flex-1 flex-row h-[400px]", children: [_jsxs("div", { className: "w-[170px] p-2 border-r border-gray-200", children: [_jsx(Segmented, { options: options, block: true, size: "small", value: scope, onChange: setScope }), _jsx("div", { className: "overflow-y-auto h-full", children: scope === CannedResponseVisibleScope.TEAM && (_jsx("div", { className: "mt-1 flex flex-col", children: (_a = myTeams === null || myTeams === void 0 ? void 0 : myTeams.map) === null || _a === void 0 ? void 0 : _a.call(myTeams, (team, index) => renderTeamItem(team, index)) })) })] }), _jsxs("div", { id: "scrollableCannedDiv", className: "flex flex-col flex-1 h-full overflow-y-auto", children: [!!cannedQuery && !search && (_jsx("div", { className: "px-3 py-2 border-b border-gray-200", children: _jsxs("span", { className: "text-sm text-gray-500", children: [t("canned_response_quick_search_placeholder"), _jsx("span", { className: "text-blue-600", children: ` #${cannedQuery}` })] }) })), _jsx(InfiniteScroll, { dataLength: (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableCannedDiv", children: (cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_canned_response") }) })) : ((_b = cannedResponses === null || cannedResponses === void 0 ? void 0 : cannedResponses.map) === null || _b === void 0 ? void 0 : _b.call(cannedResponses, (canned) => {
|
|
47
|
+
var _a, _b;
|
|
48
|
+
const htmlContent = (canned === null || canned === void 0 ? void 0 : canned.content) || "";
|
|
49
|
+
// 🔒 Sanitize HTML to prevent XSS attacks
|
|
50
|
+
const sanitizedContent = sanitizeHtml(htmlContent);
|
|
51
|
+
return (_jsxs("div", { className: "flex flex-col flex-1 px-3 py-2 cursor-pointer hover:bg-blue-100 w-full border-b border-gray-200", onClick: () => onSelectCannedResponse === null || onSelectCannedResponse === void 0 ? void 0 : onSelectCannedResponse(sanitizedContent), children: [_jsxs("span", { className: "text-xs truncate text-gray-500", children: [`${((_a = canned === null || canned === void 0 ? void 0 : canned.category) === null || _a === void 0 ? void 0 : _a.name)
|
|
52
|
+
? `${(_b = canned === null || canned === void 0 ? void 0 : canned.category) === null || _b === void 0 ? void 0 : _b.name} / `
|
|
53
|
+
: ""}`, _jsx("span", { className: "text-black", children: `${(canned === null || canned === void 0 ? void 0 : canned.name) || ""} ` }), _jsx("span", { children: "/" }), _jsx("span", { className: "text-blue-500", children: ` #${(canned === null || canned === void 0 ? void 0 : canned.shortcut) || ""}` })] }), _jsx("div", { dangerouslySetInnerHTML: {
|
|
54
|
+
__html: sanitizedContent,
|
|
55
|
+
}, className: "text-sm mt-1" })] }, canned === null || canned === void 0 ? void 0 : canned.id));
|
|
56
|
+
})) })] })] }));
|
|
57
|
+
};
|
|
58
|
+
export default CannedResponseBody;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
interface ICannedResponseFooterProps {
|
|
2
|
+
openCreateCannedModal?: () => void;
|
|
3
|
+
}
|
|
4
|
+
declare const CannedResponseFooter: ({ openCreateCannedModal, }: ICannedResponseFooterProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export default CannedResponseFooter;
|
|
6
|
+
//# sourceMappingURL=CannedResponseFooter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CannedResponseFooter.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseFooter.tsx"],"names":[],"mappings":"AAGA,UAAU,0BAA0B;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAED,QAAA,MAAM,oBAAoB,GAAI,4BAE3B,0BAA0B,4CAgB5B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
import { Icon } from "../icon";
|
|
4
|
+
const CannedResponseFooter = ({ openCreateCannedModal, }) => {
|
|
5
|
+
const { t } = useTranslation();
|
|
6
|
+
return (_jsx("div", { className: "flex items-center justify-end border-t border-gray-200 p-2", children: _jsxs("div", { className: "flex cursor-pointer items-center gap-1", onClick: openCreateCannedModal, children: [_jsx(Icon, { icon: "plus-circle-o", size: 16, className: "text-blue-500" }), _jsx("span", { className: "text-sm text-blue-500", children: t("add_canned_response") })] }) }));
|
|
7
|
+
};
|
|
8
|
+
export default CannedResponseFooter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface ICannedResponseHeaderProps {
|
|
2
|
+
onClose?: () => void;
|
|
3
|
+
search?: string;
|
|
4
|
+
setSearch?: (search: string) => void;
|
|
5
|
+
}
|
|
6
|
+
declare const CannedResponseHeader: (props: ICannedResponseHeaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default CannedResponseHeader;
|
|
8
|
+
//# sourceMappingURL=CannedResponseHeader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CannedResponseHeader.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/CannedResponseHeader.tsx"],"names":[],"mappings":"AAKA,UAAU,0BAA0B;IAClC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,QAAA,MAAM,oBAAoB,GAAI,OAAO,0BAA0B,4CA+B9D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
import CannedResponseIcon from "../../assets/svg/cannedResponse";
|
|
4
|
+
import { Button, Input } from "antd";
|
|
5
|
+
import { Icon } from "../icon";
|
|
6
|
+
const CannedResponseHeader = (props) => {
|
|
7
|
+
const { onClose, search, setSearch } = props;
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
return (_jsxs("div", { className: "px-3 py-2 flex flex-row items-center gap-2 border-b border-gray-200", children: [_jsxs("div", { className: "flex flex-row items-center gap-2", children: [_jsx(CannedResponseIcon, { size: 20, className: "text-blue-500" }), _jsx("span", { className: "text-base font-medium", children: t("canned_responses") })] }), _jsxs("div", { className: "flex flex-1 flex-row items-center justify-end gap-2", children: [_jsx(Input, { size: "large", placeholder: t("search"), prefix: _jsx(Icon, { icon: "search-o", size: 20, className: "text-gray-500" }), style: { width: 260 }, className: "text-sm", value: search, onChange: (e) => setSearch === null || setSearch === void 0 ? void 0 : setSearch(e.target.value) }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: onClose, children: _jsx(Icon, { icon: "close-o", size: 16 }) })] })] }));
|
|
10
|
+
};
|
|
11
|
+
export default CannedResponseHeader;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface ICannedResponseProps {
|
|
2
|
+
onClose?: () => void;
|
|
3
|
+
openCreateCannedModal?: () => void;
|
|
4
|
+
cannedQuery?: string;
|
|
5
|
+
}
|
|
6
|
+
declare const CannedResponse: ({ onClose, openCreateCannedModal, cannedQuery, }: ICannedResponseProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default CannedResponse;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/cannedResponse/index.tsx"],"names":[],"mappings":"AASA,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,QAAA,MAAM,cAAc,GAAI,kDAIrB,oBAAoB,4CAgDtB,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useDebounce } from "ahooks";
|
|
3
|
+
import CannedResponseBody from "./CannedResponseBody";
|
|
4
|
+
import CannedResponseHeader from "./CannedResponseHeader";
|
|
5
|
+
import { useCallback, useState } from "react";
|
|
6
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
7
|
+
import { $getRoot } from "lexical";
|
|
8
|
+
import { $generateNodesFromDOM } from "@lexical/html";
|
|
9
|
+
import CannedResponseFooter from "./CannedResponseFooter";
|
|
10
|
+
const CannedResponse = ({ onClose, openCreateCannedModal, cannedQuery = "", }) => {
|
|
11
|
+
const [editor] = useLexicalComposerContext();
|
|
12
|
+
const [search, setSearch] = useState("");
|
|
13
|
+
const debouncedSearch = useDebounce(search, {
|
|
14
|
+
wait: 300,
|
|
15
|
+
});
|
|
16
|
+
const onSelectCannedResponse = useCallback((content) => {
|
|
17
|
+
editor.update(() => {
|
|
18
|
+
const root = $getRoot();
|
|
19
|
+
// 1. Clear content cũ
|
|
20
|
+
root.clear();
|
|
21
|
+
// 2. Parse HTML -> DOM
|
|
22
|
+
const parser = new DOMParser();
|
|
23
|
+
const dom = parser.parseFromString(content, "text/html");
|
|
24
|
+
// 3. Convert DOM -> Lexical nodes
|
|
25
|
+
const nodes = $generateNodesFromDOM(editor, dom);
|
|
26
|
+
// 4. Append vào editor
|
|
27
|
+
root.append(...nodes);
|
|
28
|
+
// 5. Move cursor về cuối (optional nhưng nên có)
|
|
29
|
+
root.selectEnd();
|
|
30
|
+
});
|
|
31
|
+
}, [editor]);
|
|
32
|
+
return (_jsxs("div", { className: "w-[600px]", children: [_jsx(CannedResponseHeader, { onClose: onClose, search: search, setSearch: setSearch }), _jsx(CannedResponseBody, { search: debouncedSearch, onSelectCannedResponse: onSelectCannedResponse, cannedQuery: cannedQuery }), _jsx(CannedResponseFooter, { openCreateCannedModal: openCreateCannedModal })] }));
|
|
33
|
+
};
|
|
34
|
+
export default CannedResponse;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IMyTeamResponse } from "../../../types/dto";
|
|
2
|
+
interface TeamItemProps {
|
|
3
|
+
team: IMyTeamResponse;
|
|
4
|
+
onSelectCategory?: (teamId: string, categoryId?: string) => void;
|
|
5
|
+
selectedTeamId?: string;
|
|
6
|
+
selectedCategoryId?: string;
|
|
7
|
+
defaultOpen?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare const TeamItem: ({ team, onSelectCategory, selectedTeamId, selectedCategoryId, defaultOpen, }: TeamItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export default TeamItem;
|
|
11
|
+
//# sourceMappingURL=TeamItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeamItem.d.ts","sourceRoot":"","sources":["../../../../src/components/cannedResponse/team/TeamItem.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAQrD,UAAU,aAAa;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,QAAA,MAAM,QAAQ,GAAI,8EAMf,aAAa,4CAiFf,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useFetchCannedCategories } from "../../../hooks/cannedResponse/useFetchCannedCategories";
|
|
3
|
+
import { useBoolean } from "ahooks";
|
|
4
|
+
import { Icon } from "../../icon";
|
|
5
|
+
import clsx from "clsx";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { useCallback } from "react";
|
|
8
|
+
const TeamItem = ({ team, onSelectCategory, selectedTeamId, selectedCategoryId, defaultOpen = false, }) => {
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
const [isOpen, { toggle }] = useBoolean(defaultOpen);
|
|
11
|
+
const { data: categories } = useFetchCannedCategories(team.teamId, isOpen);
|
|
12
|
+
const onToggle = useCallback(() => {
|
|
13
|
+
if (!isOpen && !selectedTeamId && !!(team === null || team === void 0 ? void 0 : team.teamId)) {
|
|
14
|
+
onSelectCategory === null || onSelectCategory === void 0 ? void 0 : onSelectCategory(team === null || team === void 0 ? void 0 : team.teamId, undefined);
|
|
15
|
+
}
|
|
16
|
+
toggle();
|
|
17
|
+
}, [toggle, isOpen, onSelectCategory, team === null || team === void 0 ? void 0 : team.teamId, selectedTeamId]);
|
|
18
|
+
return (_jsxs("div", { className: "border-b border-gray-200", children: [_jsxs("div", { className: "flex items-center justify-between py-2 cursor-pointer", onClick: onToggle, children: [_jsx("span", { className: "text-sm font-medium truncate flex-1", children: (team === null || team === void 0 ? void 0 : team.name) || "" }), _jsx(Icon, { icon: isOpen ? "angle-up-b" : "angle-down-b", size: 16, className: clsx(!isOpen && "text-gray-500") })] }), isOpen && (_jsxs("div", { className: "mb-2", children: [_jsx("div", { className: clsx("px-3 py-1 cursor-pointer hover:bg-gray-100 rounded-md", selectedTeamId &&
|
|
19
|
+
selectedTeamId === (team === null || team === void 0 ? void 0 : team.teamId) &&
|
|
20
|
+
!selectedCategoryId &&
|
|
21
|
+
"bg-gray-100"), onClick: () => onSelectCategory === null || onSelectCategory === void 0 ? void 0 : onSelectCategory(team === null || team === void 0 ? void 0 : team.teamId, undefined), children: _jsx("span", { className: clsx("text-sm truncate", selectedTeamId === (team === null || team === void 0 ? void 0 : team.teamId) && !selectedCategoryId
|
|
22
|
+
? "font-medium text-blue-500"
|
|
23
|
+
: "text-gray-500"), children: t("all") }) }, `${team.teamId}-all`), categories === null || categories === void 0 ? void 0 : categories.map((category) => {
|
|
24
|
+
const isSelected = selectedTeamId &&
|
|
25
|
+
selectedCategoryId &&
|
|
26
|
+
selectedTeamId === (category === null || category === void 0 ? void 0 : category.teamId) &&
|
|
27
|
+
selectedCategoryId === (category === null || category === void 0 ? void 0 : category.id);
|
|
28
|
+
return (_jsx("div", { className: clsx("px-3 py-1 cursor-pointer hover:bg-gray-100 rounded-md", isSelected && "bg-gray-100"), onClick: () => onSelectCategory === null || onSelectCategory === void 0 ? void 0 : onSelectCategory(team === null || team === void 0 ? void 0 : team.teamId, category === null || category === void 0 ? void 0 : category.id), children: _jsx("span", { className: clsx("text-sm truncate", isSelected ? "font-medium text-blue-500" : "text-gray-500"), children: category === null || category === void 0 ? void 0 : category.name }) }, `${team.teamId}-${category.id}`));
|
|
29
|
+
})] }))] }));
|
|
30
|
+
};
|
|
31
|
+
export default TeamItem;
|
|
@@ -3,6 +3,7 @@ interface MessageListProps {
|
|
|
3
3
|
searchClientMsgID?: string;
|
|
4
4
|
className?: string;
|
|
5
5
|
onClose?: () => void;
|
|
6
|
+
openCreateCannedModal?: () => void;
|
|
6
7
|
}
|
|
7
8
|
declare const MessageList: (props: MessageListProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
9
|
export default MessageList;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAyBA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAyBA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAGD,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAiT3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -26,7 +26,7 @@ const MessageList = (props) => {
|
|
|
26
26
|
var _a;
|
|
27
27
|
const { t } = useTranslation();
|
|
28
28
|
const { user } = useChatContext();
|
|
29
|
-
const { onClose, conversationId, searchClientMsgID } = props;
|
|
29
|
+
const { onClose, conversationId, searchClientMsgID, openCreateCannedModal } = props;
|
|
30
30
|
const scrollRef = useRef(null);
|
|
31
31
|
const { getMoreOldMessages, moreOldLoading, loadState, getMoreNewMessages, moreNewLoading, latestLoadState, } = useMessage(conversationId, searchClientMsgID);
|
|
32
32
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
@@ -192,6 +192,6 @@ const MessageList = (props) => {
|
|
|
192
192
|
handleMarkConversationMessageAsRead();
|
|
193
193
|
loadMoreNewMessage();
|
|
194
194
|
}
|
|
195
|
-
}, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage }, message.clientMsgID))) }) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), _jsx(MessageFooter, { currentSession: currentSession }), _jsx(Modal, { centered: true, open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, getContainer: false, forceRender: true, children: _jsx("p", { children: t("revoke_message_confirm_message") }) })] }));
|
|
195
|
+
}, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage }, message.clientMsgID))) }) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), _jsx(MessageFooter, { currentSession: currentSession, openCreateCannedModal: openCreateCannedModal }), _jsx(Modal, { centered: true, open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, getContainer: false, forceRender: true, children: _jsx("p", { children: t("revoke_message_confirm_message") }) })] }));
|
|
196
196
|
};
|
|
197
197
|
export default MessageList;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CannedResponsePlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/CannedResponsePlugin.tsx"],"names":[],"mappings":"AAIA,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,QAiCrE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
3
|
+
import { $getSelection, $isRangeSelection } from "lexical";
|
|
4
|
+
export function CannedResponseTriggerPlugin({ onOpen, onClose }) {
|
|
5
|
+
const [editor] = useLexicalComposerContext();
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
8
|
+
editorState.read(() => {
|
|
9
|
+
const selection = $getSelection();
|
|
10
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
11
|
+
onClose();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const anchor = selection.anchor;
|
|
15
|
+
const node = anchor.getNode();
|
|
16
|
+
const text = node.getTextContent();
|
|
17
|
+
const offset = anchor.offset;
|
|
18
|
+
const before = text.slice(0, offset);
|
|
19
|
+
// match #abc (KHÔNG có space phía sau)
|
|
20
|
+
const match = before.match(/(^|\s)#(\w*)$/);
|
|
21
|
+
if (match) {
|
|
22
|
+
onOpen(match[2] || "");
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
onClose();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}, [editor, onOpen, onClose]);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaActions.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/MediaActions.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAkB,UAAU,EAA0B,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"MediaActions.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/MediaActions.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAkB,UAAU,EAA0B,MAAM,MAAM,CAAC;AAoD1E,UAAU,iBAAiB;IACzB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,aAAa,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAsB,GACvB,EAAE,iBAAiB,2CAwKnB"}
|
|
@@ -8,8 +8,11 @@ import clsx from "clsx";
|
|
|
8
8
|
import { useTranslation } from "react-i18next";
|
|
9
9
|
import EmojiPicker from "./EmojiPicker";
|
|
10
10
|
import { Icon } from "../../icon";
|
|
11
|
-
import { validateFile, validateVideoLimit } from "../../../utils/fileValidation";
|
|
11
|
+
import { validateFile, validateVideoLimit, } from "../../../utils/fileValidation";
|
|
12
12
|
import { DOCUMENT_TYPES } from "./editorConfig";
|
|
13
|
+
import CannedResponseIcon from "../../../assets/svg/cannedResponse";
|
|
14
|
+
import { useMessageFooterContext } from ".";
|
|
15
|
+
import useAuthStore from "../../../store/auth";
|
|
13
16
|
const EmojiIcon = (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M7.59997 12.4C7.59997 12.4 8.49997 13.2 9.99997 13.2C11.5 13.2 12.4 12.4 12.4 12.4M14 8.39997C14 8.8418 13.6418 9.19997 13.2 9.19997C12.7581 9.19997 12.4 8.8418 12.4 8.39997C12.4 7.95814 12.7581 7.59997 13.2 7.59997C13.6418 7.59997 14 7.95814 14 8.39997ZM18 9.99997C18 14.4182 14.4182 18 9.99997 18C5.58169 18 1.99997 14.4182 1.99997 9.99997C1.99997 5.58169 5.58169 1.99997 9.99997 1.99997C14.4182 1.99997 18 5.58169 18 9.99997ZM7.59997 8.39997C7.59997 8.8418 7.2418 9.19997 6.79997 9.19997C6.35814 9.19997 5.99997 8.8418 5.99997 8.39997C5.99997 7.95814 6.35814 7.59997 6.79997 7.59997C7.2418 7.59997 7.59997 7.95814 7.59997 8.39997Z", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
14
17
|
const AttachIcon = (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M15.6 10.0001V11.2001C15.6 14.5137 12.9137 17.2001 9.59998 17.2001C6.28626 17.2001 3.59998 14.5137 3.59998 11.2001V6.79999C3.59998 4.59085 5.39084 2.79999 7.59998 2.79999C9.8091 2.79999 11.6 4.59085 11.6 6.79999V11.2C11.6 12.3045 10.7045 13.2 9.59998 13.2C8.49542 13.2 7.59998 12.3045 7.59998 11.2V7.99999", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
15
18
|
export default function MediaActions({ listUploadFiles, onFilesChange, buttonClassName, showMediaUpload = true, }) {
|
|
@@ -17,6 +20,8 @@ export default function MediaActions({ listUploadFiles, onFilesChange, buttonCla
|
|
|
17
20
|
const { t } = useTranslation();
|
|
18
21
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
|
19
22
|
const containerRef = useRef(null);
|
|
23
|
+
const { isOpenCanned, setIsOpenCanned } = useMessageFooterContext();
|
|
24
|
+
const isCrm = useAuthStore((state) => state.isCrm);
|
|
20
25
|
const handleEmojiSelect = useCallback((emoji) => {
|
|
21
26
|
editor.update(() => {
|
|
22
27
|
const selection = $getSelection();
|
|
@@ -63,6 +68,21 @@ export default function MediaActions({ listUploadFiles, onFilesChange, buttonCla
|
|
|
63
68
|
}
|
|
64
69
|
onFilesChange(newList);
|
|
65
70
|
};
|
|
71
|
+
const onPressCanned = useCallback(async () => {
|
|
72
|
+
if (!isOpenCanned) {
|
|
73
|
+
editor.focus();
|
|
74
|
+
await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for focus to complete
|
|
75
|
+
editor.update(() => {
|
|
76
|
+
const selection = $getSelection();
|
|
77
|
+
if ($isRangeSelection(selection)) {
|
|
78
|
+
selection.insertText(" #");
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
setIsOpenCanned(false);
|
|
84
|
+
}
|
|
85
|
+
}, [isOpenCanned, editor]);
|
|
66
86
|
useEffect(() => {
|
|
67
87
|
const handleClickOutside = (event) => {
|
|
68
88
|
if (containerRef.current &&
|
|
@@ -74,5 +94,5 @@ export default function MediaActions({ listUploadFiles, onFilesChange, buttonCla
|
|
|
74
94
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
75
95
|
}, []);
|
|
76
96
|
const btnClass = buttonClassName !== null && buttonClassName !== void 0 ? buttonClassName : "w-8 h-8 p-0 text-gray-500 hover:text-gray-700 hover:bg-gray-100";
|
|
77
|
-
return (_jsxs("div", { className: "flex items-center gap-1 relative", ref: containerRef, children: [_jsx(Button, { type: "text", shape: "default", className: clsx(btnClass, showEmojiPicker && "bg-blue-100 text-blue-600"), onClick: () => setShowEmojiPicker(!showEmojiPicker), children: EmojiIcon }), showMediaUpload && (_jsxs(_Fragment, { children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg, video/*", beforeUpload: beforeUploadImagesAndVideo, multiple: true, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: btnClass, children: _jsx(Icon, { icon: "image-02-o", size: 22 }) }) }), _jsx(Upload, { accept: ".doc,.docx,.pdf", beforeUpload: beforeUploadFile, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: btnClass, children: AttachIcon }) })] })), showEmojiPicker && (_jsx(EmojiPicker, { onEmojiSelect: handleEmojiSelect, onClose: () => setShowEmojiPicker(false) }))] }));
|
|
97
|
+
return (_jsxs("div", { className: "flex items-center gap-1 relative", ref: containerRef, children: [isCrm && (_jsx(Button, { type: "text", shape: "default", className: clsx(btnClass, isOpenCanned && "bg-blue-100 text-blue-600"), onClick: onPressCanned, children: _jsx(CannedResponseIcon, { className: clsx("hover:text-gray-700", isOpenCanned && "text-blue-600") }) })), _jsx(Button, { type: "text", shape: "default", className: clsx(btnClass, showEmojiPicker && "bg-blue-100 text-blue-600"), onClick: () => setShowEmojiPicker(!showEmojiPicker), children: EmojiIcon }), showMediaUpload && (_jsxs(_Fragment, { children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg, video/*", beforeUpload: beforeUploadImagesAndVideo, multiple: true, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: btnClass, children: _jsx(Icon, { icon: "image-02-o", size: 22 }) }) }), _jsx(Upload, { accept: ".doc,.docx,.pdf", beforeUpload: beforeUploadFile, onChange: handleChange, showUploadList: false, fileList: listUploadFiles, children: _jsx(Button, { type: "text", shape: "default", className: btnClass, children: AttachIcon }) })] })), showEmojiPicker && (_jsx(EmojiPicker, { onEmojiSelect: handleEmojiSelect, onClose: () => setShowEmojiPicker(false) }))] }));
|
|
78
98
|
}
|
|
@@ -2,9 +2,10 @@ import { MessageFooterContextType } from "../../../types/chat";
|
|
|
2
2
|
import { ISessionByStatus } from "../../../store/type";
|
|
3
3
|
interface MessageFooterProps {
|
|
4
4
|
currentSession?: ISessionByStatus;
|
|
5
|
+
openCreateCannedModal?: () => void;
|
|
5
6
|
}
|
|
6
7
|
export declare const MessageFooterContext: import("react").Context<MessageFooterContextType>;
|
|
7
8
|
export declare const useMessageFooterContext: () => MessageFooterContextType;
|
|
8
|
-
declare const MessageFooterProvider: ({ currentSession }: MessageFooterProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
declare const MessageFooterProvider: ({ currentSession, openCreateCannedModal, }: MessageFooterProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
10
|
export default MessageFooterProvider;
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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;
|
|
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"}
|
|
@@ -12,10 +12,15 @@ import { createContext, useCallback, useContext, useState } from "react";
|
|
|
12
12
|
import EnterHandler from "./EnterHandler";
|
|
13
13
|
import ActionBar from "./ActionBar";
|
|
14
14
|
import { useSendMessage } from "../../../hooks/message/useSendMessage";
|
|
15
|
+
import { Popover } from "antd";
|
|
15
16
|
import FilePreview from "./FilePreview";
|
|
16
17
|
import { useTranslation } from "react-i18next";
|
|
17
18
|
import PasteAndDropPlugin from "./PasteAndDropPlugin";
|
|
18
19
|
import { QuotedMessageFooter } from "./QuotedMessage";
|
|
20
|
+
import CannedResponse from "../../cannedResponse";
|
|
21
|
+
import { CannedResponseTriggerPlugin } from "./CannedResponsePlugin";
|
|
22
|
+
import { useBoolean } from "ahooks";
|
|
23
|
+
import useAuthStore from "../../../store/auth";
|
|
19
24
|
const initialConfig = {
|
|
20
25
|
namespace: "ChatInput",
|
|
21
26
|
theme: editorTheme,
|
|
@@ -26,12 +31,17 @@ export const MessageFooterContext = createContext({
|
|
|
26
31
|
onSendMessage: () => { },
|
|
27
32
|
listUploadFiles: [],
|
|
28
33
|
setListUploadFiles: () => { },
|
|
34
|
+
isOpenCanned: false,
|
|
35
|
+
setIsOpenCanned: () => { },
|
|
29
36
|
});
|
|
30
37
|
export const useMessageFooterContext = () => useContext(MessageFooterContext);
|
|
31
|
-
const MessageFooterProvider = ({ currentSession }) => {
|
|
38
|
+
const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
|
|
32
39
|
const { t } = useTranslation();
|
|
33
40
|
const { sendTextMessage, sendMergeMessage } = useSendMessage();
|
|
34
41
|
const [listUploadFiles, setListUploadFiles] = useState([]);
|
|
42
|
+
const [isOpenCanned, { setTrue: openCanned, setFalse: closeCanned, toggle: toggleCanned },] = useBoolean(false);
|
|
43
|
+
const isCrm = useAuthStore((state) => state.isCrm);
|
|
44
|
+
const [cannedQuery, setCannedQuery] = useState("");
|
|
35
45
|
const onSendMessage = useCallback(async ({ plainText, richText, type, }) => {
|
|
36
46
|
if (type === "text") {
|
|
37
47
|
sendTextMessage({ plainText, richText, currentSession });
|
|
@@ -46,6 +56,22 @@ const MessageFooterProvider = ({ currentSession }) => {
|
|
|
46
56
|
}
|
|
47
57
|
setListUploadFiles([]);
|
|
48
58
|
}, [sendMergeMessage, sendTextMessage, listUploadFiles, currentSession]);
|
|
49
|
-
|
|
59
|
+
const onOpenCreateCannedModal = useCallback(() => {
|
|
60
|
+
closeCanned();
|
|
61
|
+
openCreateCannedModal === null || openCreateCannedModal === void 0 ? void 0 : openCreateCannedModal();
|
|
62
|
+
}, [closeCanned, openCreateCannedModal]);
|
|
63
|
+
const onOpenCanned = useCallback((query) => {
|
|
64
|
+
setCannedQuery(query);
|
|
65
|
+
openCanned();
|
|
66
|
+
}, [openCanned]);
|
|
67
|
+
return (_jsx(MessageFooterContext.Provider, { value: {
|
|
68
|
+
onSendMessage,
|
|
69
|
+
listUploadFiles,
|
|
70
|
+
setListUploadFiles,
|
|
71
|
+
isOpenCanned,
|
|
72
|
+
setIsOpenCanned: toggleCanned,
|
|
73
|
+
}, 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
|
+
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 }))] }) }));
|
|
50
76
|
};
|
|
51
77
|
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;
|
|
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"}
|