@consilioweb/payload-support 0.8.2 → 0.9.4
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/components/RichTextEditor/index.cjs +233 -0
- package/dist/components/RichTextEditor/index.js +232 -0
- package/dist/components/TicketConversation/components/CodeBlock.cjs +24 -7
- package/dist/components/TicketConversation/components/CodeBlock.js +23 -9
- package/dist/index.cjs +19 -1
- package/dist/index.js +19 -1
- package/dist/views/BillingView/client.cjs +260 -103
- package/dist/views/BillingView/client.js +259 -103
- package/dist/views/ChatView/client.cjs +184 -137
- package/dist/views/ChatView/client.js +180 -137
- package/dist/views/CrmView/client.cjs +270 -122
- package/dist/views/CrmView/client.js +266 -122
- package/dist/views/EmailTrackingView/client.cjs +80 -69
- package/dist/views/EmailTrackingView/client.js +80 -70
- package/dist/views/ImportConversationView/client.cjs +127 -94
- package/dist/views/ImportConversationView/client.js +123 -94
- package/dist/views/LogsView/client.cjs +56 -58
- package/dist/views/LogsView/client.js +52 -58
- package/dist/views/NewTicketView/client.cjs +39 -55
- package/dist/views/NewTicketView/client.js +38 -55
- package/dist/views/PendingEmailsView/client.cjs +399 -102
- package/dist/views/PendingEmailsView/client.js +396 -103
- package/dist/views/SupportDashboardView/client.cjs +276 -137
- package/dist/views/SupportDashboardView/client.js +275 -137
- package/dist/views/TicketDetailView/client.cjs +487 -204
- package/dist/views/TicketDetailView/client.js +486 -204
- package/dist/views/TicketInboxView/client.cjs +62 -65
- package/dist/views/TicketInboxView/client.js +62 -66
- package/dist/views/TicketingSettingsView/client.cjs +10 -8
- package/dist/views/TicketingSettingsView/client.js +10 -8
- package/dist/views/TimeDashboardView/client.cjs +70 -59
- package/dist/views/TimeDashboardView/client.js +69 -59
- package/package.json +6 -2
- package/src/components/RichTextEditor/index.tsx +261 -0
- package/src/components/TicketConversation/components/CodeBlock.tsx +53 -14
- package/src/plugin.ts +2 -0
- package/src/utils/emailTemplate.ts +37 -0
- package/src/views/BillingView/client.tsx +362 -69
- package/src/views/ChatView/client.tsx +225 -140
- package/src/views/CrmView/client.tsx +447 -189
- package/src/views/EmailTrackingView/client.tsx +111 -71
- package/src/views/ImportConversationView/client.tsx +255 -70
- package/src/views/LogsView/client.tsx +85 -50
- package/src/views/NewTicketView/client.tsx +37 -53
- package/src/views/PendingEmailsView/client.tsx +512 -92
- package/src/views/SupportDashboardView/client.tsx +294 -134
- package/src/views/TicketDetailView/client.tsx +486 -213
- package/src/views/TicketInboxView/client.tsx +52 -61
- package/src/views/TicketingSettingsView/client.tsx +10 -9
- package/src/views/TimeDashboardView/client.tsx +184 -69
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
6
|
+
const RichTextEditor = react.forwardRef(function RichTextEditor2({
|
|
7
|
+
initialValue = "",
|
|
8
|
+
onChange,
|
|
9
|
+
placeholder = "\xC9crivez votre message...",
|
|
10
|
+
minHeight = 150,
|
|
11
|
+
onFileUpload,
|
|
12
|
+
borderColor = "#000",
|
|
13
|
+
focusBorderColor = "#00E5FF"
|
|
14
|
+
}, ref) {
|
|
15
|
+
const editorRef = react.useRef(null);
|
|
16
|
+
const fileInputRef = react.useRef(null);
|
|
17
|
+
const [focused, setFocused] = react.useState(false);
|
|
18
|
+
const [isEmpty, setIsEmpty] = react.useState(!initialValue);
|
|
19
|
+
react.useEffect(() => {
|
|
20
|
+
if (editorRef.current && initialValue) {
|
|
21
|
+
editorRef.current.innerHTML = initialValue;
|
|
22
|
+
const text = editorRef.current.innerText?.trim() || "";
|
|
23
|
+
setIsEmpty(!text && !editorRef.current.querySelector("img"));
|
|
24
|
+
}
|
|
25
|
+
}, []);
|
|
26
|
+
const emitChange = react.useCallback(() => {
|
|
27
|
+
if (!editorRef.current) return;
|
|
28
|
+
const html = editorRef.current.innerHTML;
|
|
29
|
+
const text = editorRef.current.innerText?.trim() || "";
|
|
30
|
+
const empty = !text && !editorRef.current.querySelector("img");
|
|
31
|
+
setIsEmpty(empty);
|
|
32
|
+
onChange(empty ? "" : html, text);
|
|
33
|
+
}, [onChange]);
|
|
34
|
+
const exec = react.useCallback((command, value) => {
|
|
35
|
+
document.execCommand(command, false, value);
|
|
36
|
+
editorRef.current?.focus();
|
|
37
|
+
setTimeout(emitChange, 0);
|
|
38
|
+
}, [emitChange]);
|
|
39
|
+
react.useImperativeHandle(ref, () => ({
|
|
40
|
+
clear: () => {
|
|
41
|
+
if (editorRef.current) {
|
|
42
|
+
editorRef.current.innerHTML = "";
|
|
43
|
+
setIsEmpty(true);
|
|
44
|
+
onChange("", "");
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
setContent: (html) => {
|
|
48
|
+
if (editorRef.current) {
|
|
49
|
+
editorRef.current.innerHTML = html;
|
|
50
|
+
const text = editorRef.current.innerText?.trim() || "";
|
|
51
|
+
setIsEmpty(!text && !editorRef.current.querySelector("img"));
|
|
52
|
+
onChange(html, text);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
getHtml: () => editorRef.current?.innerHTML || "",
|
|
56
|
+
getPlainText: () => editorRef.current?.innerText?.trim() || "",
|
|
57
|
+
focus: () => editorRef.current?.focus()
|
|
58
|
+
}));
|
|
59
|
+
const handleInsertLink = react.useCallback(() => {
|
|
60
|
+
const sel = window.getSelection();
|
|
61
|
+
if (!sel || sel.isCollapsed) {
|
|
62
|
+
const url = prompt("URL du lien :");
|
|
63
|
+
if (!url) return;
|
|
64
|
+
const text = prompt("Texte du lien :", url) || url;
|
|
65
|
+
const safeUrl = url.replace(/"/g, """);
|
|
66
|
+
const safeText = text.replace(/</g, "<").replace(/>/g, ">");
|
|
67
|
+
document.execCommand("insertHTML", false, `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer">${safeText}</a>`);
|
|
68
|
+
editorRef.current?.focus();
|
|
69
|
+
setTimeout(emitChange, 0);
|
|
70
|
+
} else {
|
|
71
|
+
const url = prompt("URL du lien :");
|
|
72
|
+
if (url) exec("createLink", url);
|
|
73
|
+
}
|
|
74
|
+
}, [exec, emitChange]);
|
|
75
|
+
const handleImageClick = react.useCallback(() => {
|
|
76
|
+
if (onFileUpload) {
|
|
77
|
+
fileInputRef.current?.click();
|
|
78
|
+
} else {
|
|
79
|
+
const url = prompt("URL de l'image :");
|
|
80
|
+
if (url) {
|
|
81
|
+
const safeUrl = url.replace(/"/g, """);
|
|
82
|
+
document.execCommand("insertHTML", false, `<img src="${safeUrl}" alt="Image" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
|
|
83
|
+
editorRef.current?.focus();
|
|
84
|
+
setTimeout(emitChange, 0);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}, [onFileUpload, emitChange]);
|
|
88
|
+
const handleFileChange = react.useCallback(async (e) => {
|
|
89
|
+
if (!e.target.files || !onFileUpload) return;
|
|
90
|
+
for (const file of Array.from(e.target.files)) {
|
|
91
|
+
if (!file.type.startsWith("image/")) continue;
|
|
92
|
+
const url = await onFileUpload(file);
|
|
93
|
+
if (url) {
|
|
94
|
+
const safeName = file.name.replace(/"/g, """);
|
|
95
|
+
document.execCommand("insertHTML", false, `<img src="${url}" alt="${safeName}" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
|
|
96
|
+
editorRef.current?.focus();
|
|
97
|
+
setTimeout(emitChange, 0);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
101
|
+
}, [onFileUpload, emitChange]);
|
|
102
|
+
const handlePaste = react.useCallback(async (e) => {
|
|
103
|
+
if (!onFileUpload) return;
|
|
104
|
+
const items = e.clipboardData?.items;
|
|
105
|
+
if (!items) return;
|
|
106
|
+
for (const item of Array.from(items)) {
|
|
107
|
+
if (item.type.startsWith("image/")) {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
const file = item.getAsFile();
|
|
110
|
+
if (file) {
|
|
111
|
+
const url = await onFileUpload(file);
|
|
112
|
+
if (url) {
|
|
113
|
+
document.execCommand("insertHTML", false, `<img src="${url}" alt="Image coll\xE9e" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
|
|
114
|
+
editorRef.current?.focus();
|
|
115
|
+
setTimeout(emitChange, 0);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}, [onFileUpload, emitChange]);
|
|
122
|
+
const btn = {
|
|
123
|
+
border: "none",
|
|
124
|
+
background: "transparent",
|
|
125
|
+
cursor: "pointer",
|
|
126
|
+
padding: "8px 10px",
|
|
127
|
+
fontSize: "13px",
|
|
128
|
+
fontWeight: 700,
|
|
129
|
+
color: "#555",
|
|
130
|
+
borderRadius: "5px",
|
|
131
|
+
lineHeight: 1,
|
|
132
|
+
minHeight: "44px",
|
|
133
|
+
minWidth: "44px",
|
|
134
|
+
display: "inline-flex",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
justifyContent: "center"
|
|
137
|
+
};
|
|
138
|
+
const sep = {
|
|
139
|
+
width: "1px",
|
|
140
|
+
height: "18px",
|
|
141
|
+
background: "#d1d5db",
|
|
142
|
+
margin: "0 4px",
|
|
143
|
+
alignSelf: "center"
|
|
144
|
+
};
|
|
145
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: `3px solid ${focused ? focusBorderColor : borderColor}`, borderRadius: "12px", overflow: "hidden", transition: "border-color 0.15s", background: "#fff" }, children: [
|
|
146
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
147
|
+
.rte-toolbar button:hover { background: #e5e7eb !important; }
|
|
148
|
+
.rte-editor blockquote { border-left: 4px solid #00E5FF; margin: 8px 0; padding: 8px 16px; background: #f0f9ff; border-radius: 0 6px 6px 0; }
|
|
149
|
+
.rte-editor img { max-width: 100%; height: auto; border-radius: 8px; margin: 8px 0; }
|
|
150
|
+
.rte-editor a { color: #00838f; text-decoration: underline; }
|
|
151
|
+
.rte-editor ul, .rte-editor ol { margin: 8px 0; padding-left: 24px; }
|
|
152
|
+
.rte-editor li { margin: 2px 0; }
|
|
153
|
+
.rte-editor p { margin: 0 0 6px 0; }
|
|
154
|
+
` }),
|
|
155
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rte-toolbar", style: { display: "flex", alignItems: "center", gap: "2px", padding: "6px 10px", borderBottom: "2px solid #e5e7eb", backgroundColor: "#f9fafb", flexWrap: "wrap" }, children: [
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
exec("bold");
|
|
159
|
+
}, title: "Gras (Ctrl+B)", children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" }) }),
|
|
160
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
161
|
+
e.preventDefault();
|
|
162
|
+
exec("italic");
|
|
163
|
+
}, title: "Italique (Ctrl+I)", children: /* @__PURE__ */ jsxRuntime.jsx("em", { style: { fontStyle: "italic" }, children: "I" }) }),
|
|
164
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
165
|
+
e.preventDefault();
|
|
166
|
+
exec("underline");
|
|
167
|
+
}, title: "Soulign\xE9 (Ctrl+U)", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { textDecoration: "underline" }, children: "S" }) }),
|
|
168
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: sep }),
|
|
169
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "16px" }, onMouseDown: (e) => {
|
|
170
|
+
e.preventDefault();
|
|
171
|
+
exec("formatBlock", "blockquote");
|
|
172
|
+
}, title: "Citation", children: "\u201C\u201D" }),
|
|
173
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
exec("insertUnorderedList");
|
|
176
|
+
}, title: "Liste \xE0 puces", children: "\u2022 Liste" }),
|
|
177
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
exec("insertOrderedList");
|
|
180
|
+
}, title: "Liste num\xE9rot\xE9e", children: "1. Liste" }),
|
|
181
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: sep }),
|
|
182
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
183
|
+
e.preventDefault();
|
|
184
|
+
handleInsertLink();
|
|
185
|
+
}, title: "Ins\xE9rer un lien", children: "\u{1F517}" }),
|
|
186
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
handleImageClick();
|
|
189
|
+
}, title: "Ins\xE9rer une image", children: "\u{1F5BC}\uFE0F" })
|
|
190
|
+
] }),
|
|
191
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
192
|
+
isEmpty && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, padding: "14px 16px", color: "#9ca3af", fontSize: "14px", pointerEvents: "none", userSelect: "none" }, children: placeholder }),
|
|
193
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
194
|
+
"div",
|
|
195
|
+
{
|
|
196
|
+
ref: editorRef,
|
|
197
|
+
className: "rte-editor",
|
|
198
|
+
contentEditable: true,
|
|
199
|
+
suppressContentEditableWarning: true,
|
|
200
|
+
onInput: emitChange,
|
|
201
|
+
onFocus: () => setFocused(true),
|
|
202
|
+
onBlur: () => {
|
|
203
|
+
setFocused(false);
|
|
204
|
+
emitChange();
|
|
205
|
+
},
|
|
206
|
+
onPaste: handlePaste,
|
|
207
|
+
style: {
|
|
208
|
+
minHeight: `${minHeight}px`,
|
|
209
|
+
padding: "14px 16px",
|
|
210
|
+
fontSize: "14px",
|
|
211
|
+
lineHeight: 1.6,
|
|
212
|
+
color: "#1f2937",
|
|
213
|
+
outline: "none",
|
|
214
|
+
overflowY: "auto"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
] }),
|
|
219
|
+
onFileUpload && /* @__PURE__ */ jsxRuntime.jsx(
|
|
220
|
+
"input",
|
|
221
|
+
{
|
|
222
|
+
ref: fileInputRef,
|
|
223
|
+
type: "file",
|
|
224
|
+
accept: "image/*",
|
|
225
|
+
multiple: true,
|
|
226
|
+
onChange: handleFileChange,
|
|
227
|
+
style: { display: "none" }
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
] });
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
exports.RichTextEditor = RichTextEditor;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle } from 'react';
|
|
4
|
+
|
|
5
|
+
const RichTextEditor = forwardRef(function RichTextEditor2({
|
|
6
|
+
initialValue = "",
|
|
7
|
+
onChange,
|
|
8
|
+
placeholder = "\xC9crivez votre message...",
|
|
9
|
+
minHeight = 150,
|
|
10
|
+
onFileUpload,
|
|
11
|
+
borderColor = "#000",
|
|
12
|
+
focusBorderColor = "#00E5FF"
|
|
13
|
+
}, ref) {
|
|
14
|
+
const editorRef = useRef(null);
|
|
15
|
+
const fileInputRef = useRef(null);
|
|
16
|
+
const [focused, setFocused] = useState(false);
|
|
17
|
+
const [isEmpty, setIsEmpty] = useState(!initialValue);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (editorRef.current && initialValue) {
|
|
20
|
+
editorRef.current.innerHTML = initialValue;
|
|
21
|
+
const text = editorRef.current.innerText?.trim() || "";
|
|
22
|
+
setIsEmpty(!text && !editorRef.current.querySelector("img"));
|
|
23
|
+
}
|
|
24
|
+
}, []);
|
|
25
|
+
const emitChange = useCallback(() => {
|
|
26
|
+
if (!editorRef.current) return;
|
|
27
|
+
const html = editorRef.current.innerHTML;
|
|
28
|
+
const text = editorRef.current.innerText?.trim() || "";
|
|
29
|
+
const empty = !text && !editorRef.current.querySelector("img");
|
|
30
|
+
setIsEmpty(empty);
|
|
31
|
+
onChange(empty ? "" : html, text);
|
|
32
|
+
}, [onChange]);
|
|
33
|
+
const exec = useCallback((command, value) => {
|
|
34
|
+
document.execCommand(command, false, value);
|
|
35
|
+
editorRef.current?.focus();
|
|
36
|
+
setTimeout(emitChange, 0);
|
|
37
|
+
}, [emitChange]);
|
|
38
|
+
useImperativeHandle(ref, () => ({
|
|
39
|
+
clear: () => {
|
|
40
|
+
if (editorRef.current) {
|
|
41
|
+
editorRef.current.innerHTML = "";
|
|
42
|
+
setIsEmpty(true);
|
|
43
|
+
onChange("", "");
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
setContent: (html) => {
|
|
47
|
+
if (editorRef.current) {
|
|
48
|
+
editorRef.current.innerHTML = html;
|
|
49
|
+
const text = editorRef.current.innerText?.trim() || "";
|
|
50
|
+
setIsEmpty(!text && !editorRef.current.querySelector("img"));
|
|
51
|
+
onChange(html, text);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
getHtml: () => editorRef.current?.innerHTML || "",
|
|
55
|
+
getPlainText: () => editorRef.current?.innerText?.trim() || "",
|
|
56
|
+
focus: () => editorRef.current?.focus()
|
|
57
|
+
}));
|
|
58
|
+
const handleInsertLink = useCallback(() => {
|
|
59
|
+
const sel = window.getSelection();
|
|
60
|
+
if (!sel || sel.isCollapsed) {
|
|
61
|
+
const url = prompt("URL du lien :");
|
|
62
|
+
if (!url) return;
|
|
63
|
+
const text = prompt("Texte du lien :", url) || url;
|
|
64
|
+
const safeUrl = url.replace(/"/g, """);
|
|
65
|
+
const safeText = text.replace(/</g, "<").replace(/>/g, ">");
|
|
66
|
+
document.execCommand("insertHTML", false, `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer">${safeText}</a>`);
|
|
67
|
+
editorRef.current?.focus();
|
|
68
|
+
setTimeout(emitChange, 0);
|
|
69
|
+
} else {
|
|
70
|
+
const url = prompt("URL du lien :");
|
|
71
|
+
if (url) exec("createLink", url);
|
|
72
|
+
}
|
|
73
|
+
}, [exec, emitChange]);
|
|
74
|
+
const handleImageClick = useCallback(() => {
|
|
75
|
+
if (onFileUpload) {
|
|
76
|
+
fileInputRef.current?.click();
|
|
77
|
+
} else {
|
|
78
|
+
const url = prompt("URL de l'image :");
|
|
79
|
+
if (url) {
|
|
80
|
+
const safeUrl = url.replace(/"/g, """);
|
|
81
|
+
document.execCommand("insertHTML", false, `<img src="${safeUrl}" alt="Image" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
|
|
82
|
+
editorRef.current?.focus();
|
|
83
|
+
setTimeout(emitChange, 0);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}, [onFileUpload, emitChange]);
|
|
87
|
+
const handleFileChange = useCallback(async (e) => {
|
|
88
|
+
if (!e.target.files || !onFileUpload) return;
|
|
89
|
+
for (const file of Array.from(e.target.files)) {
|
|
90
|
+
if (!file.type.startsWith("image/")) continue;
|
|
91
|
+
const url = await onFileUpload(file);
|
|
92
|
+
if (url) {
|
|
93
|
+
const safeName = file.name.replace(/"/g, """);
|
|
94
|
+
document.execCommand("insertHTML", false, `<img src="${url}" alt="${safeName}" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
|
|
95
|
+
editorRef.current?.focus();
|
|
96
|
+
setTimeout(emitChange, 0);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
100
|
+
}, [onFileUpload, emitChange]);
|
|
101
|
+
const handlePaste = useCallback(async (e) => {
|
|
102
|
+
if (!onFileUpload) return;
|
|
103
|
+
const items = e.clipboardData?.items;
|
|
104
|
+
if (!items) return;
|
|
105
|
+
for (const item of Array.from(items)) {
|
|
106
|
+
if (item.type.startsWith("image/")) {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
const file = item.getAsFile();
|
|
109
|
+
if (file) {
|
|
110
|
+
const url = await onFileUpload(file);
|
|
111
|
+
if (url) {
|
|
112
|
+
document.execCommand("insertHTML", false, `<img src="${url}" alt="Image coll\xE9e" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
|
|
113
|
+
editorRef.current?.focus();
|
|
114
|
+
setTimeout(emitChange, 0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}, [onFileUpload, emitChange]);
|
|
121
|
+
const btn = {
|
|
122
|
+
border: "none",
|
|
123
|
+
background: "transparent",
|
|
124
|
+
cursor: "pointer",
|
|
125
|
+
padding: "8px 10px",
|
|
126
|
+
fontSize: "13px",
|
|
127
|
+
fontWeight: 700,
|
|
128
|
+
color: "#555",
|
|
129
|
+
borderRadius: "5px",
|
|
130
|
+
lineHeight: 1,
|
|
131
|
+
minHeight: "44px",
|
|
132
|
+
minWidth: "44px",
|
|
133
|
+
display: "inline-flex",
|
|
134
|
+
alignItems: "center",
|
|
135
|
+
justifyContent: "center"
|
|
136
|
+
};
|
|
137
|
+
const sep = {
|
|
138
|
+
width: "1px",
|
|
139
|
+
height: "18px",
|
|
140
|
+
background: "#d1d5db",
|
|
141
|
+
margin: "0 4px",
|
|
142
|
+
alignSelf: "center"
|
|
143
|
+
};
|
|
144
|
+
return /* @__PURE__ */ jsxs("div", { style: { border: `3px solid ${focused ? focusBorderColor : borderColor}`, borderRadius: "12px", overflow: "hidden", transition: "border-color 0.15s", background: "#fff" }, children: [
|
|
145
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
146
|
+
.rte-toolbar button:hover { background: #e5e7eb !important; }
|
|
147
|
+
.rte-editor blockquote { border-left: 4px solid #00E5FF; margin: 8px 0; padding: 8px 16px; background: #f0f9ff; border-radius: 0 6px 6px 0; }
|
|
148
|
+
.rte-editor img { max-width: 100%; height: auto; border-radius: 8px; margin: 8px 0; }
|
|
149
|
+
.rte-editor a { color: #00838f; text-decoration: underline; }
|
|
150
|
+
.rte-editor ul, .rte-editor ol { margin: 8px 0; padding-left: 24px; }
|
|
151
|
+
.rte-editor li { margin: 2px 0; }
|
|
152
|
+
.rte-editor p { margin: 0 0 6px 0; }
|
|
153
|
+
` }),
|
|
154
|
+
/* @__PURE__ */ jsxs("div", { className: "rte-toolbar", style: { display: "flex", alignItems: "center", gap: "2px", padding: "6px 10px", borderBottom: "2px solid #e5e7eb", backgroundColor: "#f9fafb", flexWrap: "wrap" }, children: [
|
|
155
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
exec("bold");
|
|
158
|
+
}, title: "Gras (Ctrl+B)", children: /* @__PURE__ */ jsx("strong", { children: "B" }) }),
|
|
159
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
160
|
+
e.preventDefault();
|
|
161
|
+
exec("italic");
|
|
162
|
+
}, title: "Italique (Ctrl+I)", children: /* @__PURE__ */ jsx("em", { style: { fontStyle: "italic" }, children: "I" }) }),
|
|
163
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
exec("underline");
|
|
166
|
+
}, title: "Soulign\xE9 (Ctrl+U)", children: /* @__PURE__ */ jsx("span", { style: { textDecoration: "underline" }, children: "S" }) }),
|
|
167
|
+
/* @__PURE__ */ jsx("span", { style: sep }),
|
|
168
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, fontSize: "16px" }, onMouseDown: (e) => {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
exec("formatBlock", "blockquote");
|
|
171
|
+
}, title: "Citation", children: "\u201C\u201D" }),
|
|
172
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
exec("insertUnorderedList");
|
|
175
|
+
}, title: "Liste \xE0 puces", children: "\u2022 Liste" }),
|
|
176
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
exec("insertOrderedList");
|
|
179
|
+
}, title: "Liste num\xE9rot\xE9e", children: "1. Liste" }),
|
|
180
|
+
/* @__PURE__ */ jsx("span", { style: sep }),
|
|
181
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
182
|
+
e.preventDefault();
|
|
183
|
+
handleInsertLink();
|
|
184
|
+
}, title: "Ins\xE9rer un lien", children: "\u{1F517}" }),
|
|
185
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
|
|
186
|
+
e.preventDefault();
|
|
187
|
+
handleImageClick();
|
|
188
|
+
}, title: "Ins\xE9rer une image", children: "\u{1F5BC}\uFE0F" })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
191
|
+
isEmpty && /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, padding: "14px 16px", color: "#9ca3af", fontSize: "14px", pointerEvents: "none", userSelect: "none" }, children: placeholder }),
|
|
192
|
+
/* @__PURE__ */ jsx(
|
|
193
|
+
"div",
|
|
194
|
+
{
|
|
195
|
+
ref: editorRef,
|
|
196
|
+
className: "rte-editor",
|
|
197
|
+
contentEditable: true,
|
|
198
|
+
suppressContentEditableWarning: true,
|
|
199
|
+
onInput: emitChange,
|
|
200
|
+
onFocus: () => setFocused(true),
|
|
201
|
+
onBlur: () => {
|
|
202
|
+
setFocused(false);
|
|
203
|
+
emitChange();
|
|
204
|
+
},
|
|
205
|
+
onPaste: handlePaste,
|
|
206
|
+
style: {
|
|
207
|
+
minHeight: `${minHeight}px`,
|
|
208
|
+
padding: "14px 16px",
|
|
209
|
+
fontSize: "14px",
|
|
210
|
+
lineHeight: 1.6,
|
|
211
|
+
color: "#1f2937",
|
|
212
|
+
outline: "none",
|
|
213
|
+
overflowY: "auto"
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
] }),
|
|
218
|
+
onFileUpload && /* @__PURE__ */ jsx(
|
|
219
|
+
"input",
|
|
220
|
+
{
|
|
221
|
+
ref: fileInputRef,
|
|
222
|
+
type: "file",
|
|
223
|
+
accept: "image/*",
|
|
224
|
+
multiple: true,
|
|
225
|
+
onChange: handleFileChange,
|
|
226
|
+
style: { display: "none" }
|
|
227
|
+
}
|
|
228
|
+
)
|
|
229
|
+
] });
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export { RichTextEditor };
|
|
@@ -129,11 +129,12 @@ function SingleCodeBlock({ lang, code }) {
|
|
|
129
129
|
] })
|
|
130
130
|
] });
|
|
131
131
|
}
|
|
132
|
-
function
|
|
132
|
+
function hasCodeBlocks(text) {
|
|
133
|
+
return /```[\s\S]*?```/.test(text);
|
|
134
|
+
}
|
|
135
|
+
function MessageWithCodeBlocks({ text, style }) {
|
|
133
136
|
const parts = text.split(/(```[\s\S]*?```)/g);
|
|
134
|
-
|
|
135
|
-
if (!hasCodeBlock) return null;
|
|
136
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: parts.map((part, i) => {
|
|
137
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style, children: parts.map((part, i) => {
|
|
137
138
|
if (part.startsWith("```")) {
|
|
138
139
|
const match = part.match(/^```(\w*)\n?([\s\S]*?)```$/);
|
|
139
140
|
if (match) {
|
|
@@ -142,13 +143,29 @@ function CodeBlockRenderer({ text }) {
|
|
|
142
143
|
return /* @__PURE__ */ jsxRuntime.jsx(SingleCodeBlock, { lang, code }, i);
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
|
-
return null;
|
|
146
|
+
if (!part) return null;
|
|
147
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { whiteSpace: "pre-wrap" }, children: part }, i);
|
|
146
148
|
}) });
|
|
147
149
|
}
|
|
150
|
+
function htmlToText(html) {
|
|
151
|
+
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|tr|pre)>/gi, "\n").replace(/<\/?(ul|ol|table|tbody|thead)[^>]*>/gi, "").replace(/<[^>]+>/g, "").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/\n{3,}/g, "\n\n").trim();
|
|
152
|
+
}
|
|
153
|
+
function MessageWithCodeBlocksHtml({ html, style }) {
|
|
154
|
+
const text = htmlToText(html);
|
|
155
|
+
if (!hasCodeBlocks(text)) return null;
|
|
156
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MessageWithCodeBlocks, { text, style });
|
|
157
|
+
}
|
|
158
|
+
function CodeBlockRenderer({ text }) {
|
|
159
|
+
if (!hasCodeBlocks(text)) return null;
|
|
160
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MessageWithCodeBlocks, { text });
|
|
161
|
+
}
|
|
148
162
|
function CodeBlockRendererHtml({ html }) {
|
|
149
|
-
const
|
|
150
|
-
return /* @__PURE__ */ jsxRuntime.jsx(CodeBlockRenderer, { text
|
|
163
|
+
const text = htmlToText(html);
|
|
164
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CodeBlockRenderer, { text });
|
|
151
165
|
}
|
|
152
166
|
|
|
153
167
|
exports.CodeBlockRenderer = CodeBlockRenderer;
|
|
154
168
|
exports.CodeBlockRendererHtml = CodeBlockRendererHtml;
|
|
169
|
+
exports.MessageWithCodeBlocks = MessageWithCodeBlocks;
|
|
170
|
+
exports.MessageWithCodeBlocksHtml = MessageWithCodeBlocksHtml;
|
|
171
|
+
exports.hasCodeBlocks = hasCodeBlocks;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx,
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
|
|
5
5
|
const LANG_CONFIG = {
|
|
@@ -128,11 +128,12 @@ function SingleCodeBlock({ lang, code }) {
|
|
|
128
128
|
] })
|
|
129
129
|
] });
|
|
130
130
|
}
|
|
131
|
-
function
|
|
131
|
+
function hasCodeBlocks(text) {
|
|
132
|
+
return /```[\s\S]*?```/.test(text);
|
|
133
|
+
}
|
|
134
|
+
function MessageWithCodeBlocks({ text, style }) {
|
|
132
135
|
const parts = text.split(/(```[\s\S]*?```)/g);
|
|
133
|
-
|
|
134
|
-
if (!hasCodeBlock) return null;
|
|
135
|
-
return /* @__PURE__ */ jsx(Fragment, { children: parts.map((part, i) => {
|
|
136
|
+
return /* @__PURE__ */ jsx("div", { style, children: parts.map((part, i) => {
|
|
136
137
|
if (part.startsWith("```")) {
|
|
137
138
|
const match = part.match(/^```(\w*)\n?([\s\S]*?)```$/);
|
|
138
139
|
if (match) {
|
|
@@ -141,12 +142,25 @@ function CodeBlockRenderer({ text }) {
|
|
|
141
142
|
return /* @__PURE__ */ jsx(SingleCodeBlock, { lang, code }, i);
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
|
-
return null;
|
|
145
|
+
if (!part) return null;
|
|
146
|
+
return /* @__PURE__ */ jsx("span", { style: { whiteSpace: "pre-wrap" }, children: part }, i);
|
|
145
147
|
}) });
|
|
146
148
|
}
|
|
149
|
+
function htmlToText(html) {
|
|
150
|
+
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|tr|pre)>/gi, "\n").replace(/<\/?(ul|ol|table|tbody|thead)[^>]*>/gi, "").replace(/<[^>]+>/g, "").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/\n{3,}/g, "\n\n").trim();
|
|
151
|
+
}
|
|
152
|
+
function MessageWithCodeBlocksHtml({ html, style }) {
|
|
153
|
+
const text = htmlToText(html);
|
|
154
|
+
if (!hasCodeBlocks(text)) return null;
|
|
155
|
+
return /* @__PURE__ */ jsx(MessageWithCodeBlocks, { text, style });
|
|
156
|
+
}
|
|
157
|
+
function CodeBlockRenderer({ text }) {
|
|
158
|
+
if (!hasCodeBlocks(text)) return null;
|
|
159
|
+
return /* @__PURE__ */ jsx(MessageWithCodeBlocks, { text });
|
|
160
|
+
}
|
|
147
161
|
function CodeBlockRendererHtml({ html }) {
|
|
148
|
-
const
|
|
149
|
-
return /* @__PURE__ */ jsx(CodeBlockRenderer, { text
|
|
162
|
+
const text = htmlToText(html);
|
|
163
|
+
return /* @__PURE__ */ jsx(CodeBlockRenderer, { text });
|
|
150
164
|
}
|
|
151
165
|
|
|
152
|
-
export { CodeBlockRenderer, CodeBlockRendererHtml };
|
|
166
|
+
export { CodeBlockRenderer, CodeBlockRendererHtml, MessageWithCodeBlocks, MessageWithCodeBlocksHtml, hasCodeBlocks };
|
package/dist/index.cjs
CHANGED
|
@@ -1454,7 +1454,23 @@ function emailRichContent(html, config) {
|
|
|
1454
1454
|
return `<${tag}${cleanAttrs} style="${style}">`;
|
|
1455
1455
|
});
|
|
1456
1456
|
}
|
|
1457
|
+
function convertFencedCodeBlocks(input) {
|
|
1458
|
+
if (!input.includes("```")) return input;
|
|
1459
|
+
const normalized = input.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>\s*<(p|div)[^>]*>/gi, "\n").replace(/<(p|div)[^>]*>/gi, "").replace(/<\/(p|div)>/gi, "\n");
|
|
1460
|
+
return normalized.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, lang, code) => {
|
|
1461
|
+
const cleanCode = escapeHtml((code || "").replace(/\n$/, ""));
|
|
1462
|
+
const label = lang ? lang.trim() : "code";
|
|
1463
|
+
return `<div style="margin: 16px 0; border: 1px solid #1f2937; border-radius: 8px; overflow: hidden; background: #0f172a;">
|
|
1464
|
+
<div style="padding: 6px 14px; background: #1e293b; border-bottom: 1px solid #334155; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">${escapeHtml(label)}</div>
|
|
1465
|
+
<pre style="margin: 0; padding: 14px 16px; overflow-x: auto; background: #0f172a; color: #e2e8f0; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 13px; line-height: 1.6; white-space: pre;"><code style="background: transparent; padding: 0; color: inherit; font-family: inherit; font-size: inherit;">${cleanCode}</code></pre>
|
|
1466
|
+
</div>`;
|
|
1467
|
+
}).split(/(<div style="margin: 16px 0[\s\S]*?<\/div>)/g).map((chunk) => {
|
|
1468
|
+
if (chunk.startsWith('<div style="margin: 16px 0')) return chunk;
|
|
1469
|
+
return chunk.split("\n").map((line) => line.trim() ? `<p>${line}</p>` : "").join("");
|
|
1470
|
+
}).join("");
|
|
1471
|
+
}
|
|
1457
1472
|
let result = html.replace(/src="\/([^"]+)"/g, `src="${baseUrl}/$1"`);
|
|
1473
|
+
result = convertFencedCodeBlocks(result);
|
|
1458
1474
|
result = styleTag(result, "blockquote", `border-left: 4px solid ${c.brandColor}; margin: 16px 0; padding: 12px 20px; background: #f0f9fa; border-radius: 0 8px 8px 0;`);
|
|
1459
1475
|
result = styleTag(result, "img", "max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; display: block;");
|
|
1460
1476
|
result = styleTag(result, "a", `color: ${c.brandColor}; text-decoration: underline; font-weight: 600;`);
|
|
@@ -8074,7 +8090,9 @@ function supportPlugin(config) {
|
|
|
8074
8090
|
"support-new-ticket": viewConfig(`${viewsBase}#NewTicketView`, `${bp}/new-ticket`),
|
|
8075
8091
|
"support-settings": viewConfig(`${viewsBase}#TicketingSettingsView`, `${bp}/settings`),
|
|
8076
8092
|
"support-logs": viewConfig(`${viewsBase}#LogsView`, `${bp}/logs`),
|
|
8077
|
-
"support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`)
|
|
8093
|
+
"support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`),
|
|
8094
|
+
"support-billing": viewConfig(`${viewsBase}#BillingView`, `${bp}/billing`),
|
|
8095
|
+
"support-import": viewConfig(`${viewsBase}#ImportConversationView`, `/import-conversation`)
|
|
8078
8096
|
};
|
|
8079
8097
|
if (features.chat) {
|
|
8080
8098
|
supportViews["support-chat"] = viewConfig(`${viewsBase}#ChatView`, `${bp}/chat`);
|
package/dist/index.js
CHANGED
|
@@ -1448,7 +1448,23 @@ function emailRichContent(html, config) {
|
|
|
1448
1448
|
return `<${tag}${cleanAttrs} style="${style}">`;
|
|
1449
1449
|
});
|
|
1450
1450
|
}
|
|
1451
|
+
function convertFencedCodeBlocks(input) {
|
|
1452
|
+
if (!input.includes("```")) return input;
|
|
1453
|
+
const normalized = input.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>\s*<(p|div)[^>]*>/gi, "\n").replace(/<(p|div)[^>]*>/gi, "").replace(/<\/(p|div)>/gi, "\n");
|
|
1454
|
+
return normalized.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, lang, code) => {
|
|
1455
|
+
const cleanCode = escapeHtml((code || "").replace(/\n$/, ""));
|
|
1456
|
+
const label = lang ? lang.trim() : "code";
|
|
1457
|
+
return `<div style="margin: 16px 0; border: 1px solid #1f2937; border-radius: 8px; overflow: hidden; background: #0f172a;">
|
|
1458
|
+
<div style="padding: 6px 14px; background: #1e293b; border-bottom: 1px solid #334155; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">${escapeHtml(label)}</div>
|
|
1459
|
+
<pre style="margin: 0; padding: 14px 16px; overflow-x: auto; background: #0f172a; color: #e2e8f0; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 13px; line-height: 1.6; white-space: pre;"><code style="background: transparent; padding: 0; color: inherit; font-family: inherit; font-size: inherit;">${cleanCode}</code></pre>
|
|
1460
|
+
</div>`;
|
|
1461
|
+
}).split(/(<div style="margin: 16px 0[\s\S]*?<\/div>)/g).map((chunk) => {
|
|
1462
|
+
if (chunk.startsWith('<div style="margin: 16px 0')) return chunk;
|
|
1463
|
+
return chunk.split("\n").map((line) => line.trim() ? `<p>${line}</p>` : "").join("");
|
|
1464
|
+
}).join("");
|
|
1465
|
+
}
|
|
1451
1466
|
let result = html.replace(/src="\/([^"]+)"/g, `src="${baseUrl}/$1"`);
|
|
1467
|
+
result = convertFencedCodeBlocks(result);
|
|
1452
1468
|
result = styleTag(result, "blockquote", `border-left: 4px solid ${c.brandColor}; margin: 16px 0; padding: 12px 20px; background: #f0f9fa; border-radius: 0 8px 8px 0;`);
|
|
1453
1469
|
result = styleTag(result, "img", "max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; display: block;");
|
|
1454
1470
|
result = styleTag(result, "a", `color: ${c.brandColor}; text-decoration: underline; font-weight: 600;`);
|
|
@@ -8068,7 +8084,9 @@ function supportPlugin(config) {
|
|
|
8068
8084
|
"support-new-ticket": viewConfig(`${viewsBase}#NewTicketView`, `${bp}/new-ticket`),
|
|
8069
8085
|
"support-settings": viewConfig(`${viewsBase}#TicketingSettingsView`, `${bp}/settings`),
|
|
8070
8086
|
"support-logs": viewConfig(`${viewsBase}#LogsView`, `${bp}/logs`),
|
|
8071
|
-
"support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`)
|
|
8087
|
+
"support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`),
|
|
8088
|
+
"support-billing": viewConfig(`${viewsBase}#BillingView`, `${bp}/billing`),
|
|
8089
|
+
"support-import": viewConfig(`${viewsBase}#ImportConversationView`, `/import-conversation`)
|
|
8072
8090
|
};
|
|
8073
8091
|
if (features.chat) {
|
|
8074
8092
|
supportViews["support-chat"] = viewConfig(`${viewsBase}#ChatView`, `${bp}/chat`);
|