@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.
Files changed (50) hide show
  1. package/dist/components/RichTextEditor/index.cjs +233 -0
  2. package/dist/components/RichTextEditor/index.js +232 -0
  3. package/dist/components/TicketConversation/components/CodeBlock.cjs +24 -7
  4. package/dist/components/TicketConversation/components/CodeBlock.js +23 -9
  5. package/dist/index.cjs +19 -1
  6. package/dist/index.js +19 -1
  7. package/dist/views/BillingView/client.cjs +260 -103
  8. package/dist/views/BillingView/client.js +259 -103
  9. package/dist/views/ChatView/client.cjs +184 -137
  10. package/dist/views/ChatView/client.js +180 -137
  11. package/dist/views/CrmView/client.cjs +270 -122
  12. package/dist/views/CrmView/client.js +266 -122
  13. package/dist/views/EmailTrackingView/client.cjs +80 -69
  14. package/dist/views/EmailTrackingView/client.js +80 -70
  15. package/dist/views/ImportConversationView/client.cjs +127 -94
  16. package/dist/views/ImportConversationView/client.js +123 -94
  17. package/dist/views/LogsView/client.cjs +56 -58
  18. package/dist/views/LogsView/client.js +52 -58
  19. package/dist/views/NewTicketView/client.cjs +39 -55
  20. package/dist/views/NewTicketView/client.js +38 -55
  21. package/dist/views/PendingEmailsView/client.cjs +399 -102
  22. package/dist/views/PendingEmailsView/client.js +396 -103
  23. package/dist/views/SupportDashboardView/client.cjs +276 -137
  24. package/dist/views/SupportDashboardView/client.js +275 -137
  25. package/dist/views/TicketDetailView/client.cjs +487 -204
  26. package/dist/views/TicketDetailView/client.js +486 -204
  27. package/dist/views/TicketInboxView/client.cjs +62 -65
  28. package/dist/views/TicketInboxView/client.js +62 -66
  29. package/dist/views/TicketingSettingsView/client.cjs +10 -8
  30. package/dist/views/TicketingSettingsView/client.js +10 -8
  31. package/dist/views/TimeDashboardView/client.cjs +70 -59
  32. package/dist/views/TimeDashboardView/client.js +69 -59
  33. package/package.json +6 -2
  34. package/src/components/RichTextEditor/index.tsx +261 -0
  35. package/src/components/TicketConversation/components/CodeBlock.tsx +53 -14
  36. package/src/plugin.ts +2 -0
  37. package/src/utils/emailTemplate.ts +37 -0
  38. package/src/views/BillingView/client.tsx +362 -69
  39. package/src/views/ChatView/client.tsx +225 -140
  40. package/src/views/CrmView/client.tsx +447 -189
  41. package/src/views/EmailTrackingView/client.tsx +111 -71
  42. package/src/views/ImportConversationView/client.tsx +255 -70
  43. package/src/views/LogsView/client.tsx +85 -50
  44. package/src/views/NewTicketView/client.tsx +37 -53
  45. package/src/views/PendingEmailsView/client.tsx +512 -92
  46. package/src/views/SupportDashboardView/client.tsx +294 -134
  47. package/src/views/TicketDetailView/client.tsx +486 -213
  48. package/src/views/TicketInboxView/client.tsx +52 -61
  49. package/src/views/TicketingSettingsView/client.tsx +10 -9
  50. package/src/views/TimeDashboardView/client.tsx +184 -69
@@ -3,6 +3,11 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
5
  var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
6
+ var styles = require('../../styles/ChatView.module.scss');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
6
11
 
7
12
  const ChatViewClient = () => {
8
13
  const { t } = useTranslation.useTranslation();
@@ -16,10 +21,13 @@ const ChatViewClient = () => {
16
21
  const [cannedResponses, setCannedResponses] = react.useState([]);
17
22
  const messagesEndRef = react.useRef(null);
18
23
  const lastFetchRef = react.useRef(null);
24
+ const sessionsPollInterval = react.useRef(5e3);
25
+ const sessionsPollTimeout = react.useRef(void 0);
26
+ const messagesPollInterval = react.useRef(3e3);
27
+ const messagesPollTimeout = react.useRef(void 0);
19
28
  const [sessionExpired, setSessionExpired] = react.useState(false);
20
- const sessionsESRef = react.useRef(null);
21
- const messagesESRef = react.useRef(null);
22
29
  const fetchSessions = react.useCallback(async () => {
30
+ let hadChanges = false;
23
31
  try {
24
32
  const res = await fetch("/api/support/admin-chat");
25
33
  if (res.status === 401 || res.status === 403) {
@@ -28,41 +36,36 @@ const ChatViewClient = () => {
28
36
  }
29
37
  if (res.ok) {
30
38
  const data = await res.json();
31
- setSessions({ active: data.active || [], closed: data.closed || [] });
39
+ setSessions((prev) => {
40
+ const newActive = data.active || [];
41
+ const newClosed = data.closed || [];
42
+ if (JSON.stringify(prev.active) !== JSON.stringify(newActive) || JSON.stringify(prev.closed) !== JSON.stringify(newClosed)) {
43
+ hadChanges = true;
44
+ return { active: newActive, closed: newClosed };
45
+ }
46
+ return prev;
47
+ });
32
48
  }
33
49
  } catch {
34
50
  }
35
51
  setLoading(false);
52
+ if (hadChanges) {
53
+ sessionsPollInterval.current = 5e3;
54
+ } else {
55
+ sessionsPollInterval.current = Math.min(sessionsPollInterval.current + 2e3, 15e3);
56
+ }
36
57
  }, []);
37
58
  react.useEffect(() => {
38
- if (sessionExpired) return;
39
59
  fetchSessions();
40
- if (typeof EventSource !== "undefined") {
41
- const es = new EventSource("/api/support/admin-chat-stream");
42
- sessionsESRef.current = es;
43
- es.onmessage = (event) => {
44
- try {
45
- const parsed = JSON.parse(event.data);
46
- if (parsed.type === "sessions" && parsed.data) {
47
- setSessions({ active: parsed.data.active || [], closed: parsed.data.closed || [] });
48
- setLoading(false);
49
- }
50
- } catch {
51
- }
52
- };
53
- es.onerror = () => {
54
- es.close();
55
- sessionsESRef.current = null;
56
- const iv2 = setInterval(fetchSessions, 5e3);
57
- return () => clearInterval(iv2);
58
- };
59
- return () => {
60
- es.close();
61
- sessionsESRef.current = null;
62
- };
63
- }
64
- const iv = setInterval(fetchSessions, 5e3);
65
- return () => clearInterval(iv);
60
+ if (sessionExpired) return;
61
+ const schedulePoll = () => {
62
+ sessionsPollTimeout.current = setTimeout(async () => {
63
+ await fetchSessions();
64
+ schedulePoll();
65
+ }, sessionsPollInterval.current);
66
+ };
67
+ schedulePoll();
68
+ return () => clearTimeout(sessionsPollTimeout.current);
66
69
  }, [fetchSessions, sessionExpired]);
67
70
  react.useEffect(() => {
68
71
  fetch("/api/canned-responses?sort=sortOrder&limit=50&depth=0", { credentials: "include" }).then((res) => res.ok ? res.json() : null).then((data) => {
@@ -73,6 +76,7 @@ const ChatViewClient = () => {
73
76
  react.useEffect(() => {
74
77
  if (!selectedSession) return;
75
78
  const fetchMessages = async () => {
79
+ let hadNewMessages = false;
76
80
  try {
77
81
  const after = lastFetchRef.current || "";
78
82
  const url = `/api/support/admin-chat?session=${selectedSession}${after ? `&after=${after}` : ""}`;
@@ -81,12 +85,14 @@ const ChatViewClient = () => {
81
85
  const data = await res.json();
82
86
  if (!lastFetchRef.current) {
83
87
  setMessages(data.messages || []);
88
+ hadNewMessages = (data.messages?.length || 0) > 0;
84
89
  } else if (data.messages?.length > 0) {
85
90
  setMessages((prev) => {
86
91
  const ids = new Set(prev.map((m) => m.id));
87
92
  const newMsgs = data.messages.filter((m) => !ids.has(m.id));
88
93
  return newMsgs.length > 0 ? [...prev, ...newMsgs] : prev;
89
94
  });
95
+ hadNewMessages = true;
90
96
  }
91
97
  if (data.messages?.length > 0) {
92
98
  lastFetchRef.current = data.messages[data.messages.length - 1].createdAt;
@@ -94,39 +100,23 @@ const ChatViewClient = () => {
94
100
  }
95
101
  } catch {
96
102
  }
103
+ if (hadNewMessages) {
104
+ messagesPollInterval.current = 3e3;
105
+ } else {
106
+ messagesPollInterval.current = Math.min(messagesPollInterval.current + 1e3, 1e4);
107
+ }
97
108
  };
98
109
  lastFetchRef.current = null;
110
+ messagesPollInterval.current = 3e3;
99
111
  fetchMessages();
100
- if (typeof EventSource !== "undefined") {
101
- const es = new EventSource(`/api/support/admin-chat-stream?session=${selectedSession}`);
102
- messagesESRef.current = es;
103
- es.onmessage = (event) => {
104
- try {
105
- const parsed = JSON.parse(event.data);
106
- if (parsed.type === "messages" && parsed.data?.length > 0) {
107
- setMessages((prev) => {
108
- const ids = new Set(prev.map((m) => m.id));
109
- const newMsgs = parsed.data.filter((m) => !ids.has(m.id));
110
- return newMsgs.length > 0 ? [...prev, ...newMsgs] : prev;
111
- });
112
- }
113
- } catch {
114
- }
115
- };
116
- es.onerror = () => {
117
- es.close();
118
- messagesESRef.current = null;
119
- const iv2 = setInterval(fetchMessages, 3e3);
120
- fetchMessages._fallbackIv = iv2;
121
- };
122
- return () => {
123
- es.close();
124
- messagesESRef.current = null;
125
- if (fetchMessages._fallbackIv) clearInterval(fetchMessages._fallbackIv);
126
- };
127
- }
128
- const iv = setInterval(fetchMessages, 3e3);
129
- return () => clearInterval(iv);
112
+ const schedulePoll = () => {
113
+ messagesPollTimeout.current = setTimeout(async () => {
114
+ await fetchMessages();
115
+ schedulePoll();
116
+ }, messagesPollInterval.current);
117
+ };
118
+ schedulePoll();
119
+ return () => clearTimeout(messagesPollTimeout.current);
130
120
  }, [selectedSession]);
131
121
  react.useEffect(() => {
132
122
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
@@ -136,7 +126,11 @@ const ChatViewClient = () => {
136
126
  if (!input.trim() || !selectedSession || sending) return;
137
127
  setSending(true);
138
128
  try {
139
- const res = await fetch("/api/support/admin-chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "send", session: selectedSession, message: input.trim() }) });
129
+ const res = await fetch("/api/support/admin-chat", {
130
+ method: "POST",
131
+ headers: { "Content-Type": "application/json" },
132
+ body: JSON.stringify({ action: "send", session: selectedSession, message: input.trim() })
133
+ });
140
134
  if (res.ok) {
141
135
  const data = await res.json();
142
136
  setMessages((prev) => [...prev, data.message]);
@@ -150,7 +144,11 @@ const ChatViewClient = () => {
150
144
  const closeSession = async () => {
151
145
  if (!selectedSession) return;
152
146
  try {
153
- await fetch("/api/support/admin-chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "close", session: selectedSession }) });
147
+ await fetch("/api/support/admin-chat", {
148
+ method: "POST",
149
+ headers: { "Content-Type": "application/json" },
150
+ body: JSON.stringify({ action: "close", session: selectedSession })
151
+ });
154
152
  setSelectedSession(null);
155
153
  fetchSessions();
156
154
  } catch {
@@ -162,87 +160,136 @@ const ChatViewClient = () => {
162
160
  if (parts.length > 0) return parts.join(" ");
163
161
  return client.email || `Client #${client.id}`;
164
162
  };
165
- const displayedSessions = showClosed ? sessions.closed : sessions.active;
166
- const S = {
167
- page: { padding: "20px 30px", maxWidth: 1200, margin: "0 auto" },
168
- container: { display: "grid", gridTemplateColumns: "320px 1fr", gap: 16, minHeight: "calc(100vh - 300px)" },
169
- sidebar: { borderRight: "1px solid var(--theme-elevation-200)" },
170
- sessionItem: { display: "block", width: "100%", padding: "10px 14px", border: "none", background: "none", cursor: "pointer", textAlign: "left", borderBottom: "1px solid var(--theme-elevation-100)", fontSize: 13 },
171
- sessionActive: { background: "var(--theme-elevation-50)" },
172
- chatPanel: { display: "flex", flexDirection: "column" },
173
- messagesArea: { flex: 1, overflowY: "auto", padding: "12px 0" },
174
- bubble: { maxWidth: "70%", padding: "8px 12px", borderRadius: 10, marginBottom: 8, fontSize: 14 },
175
- bubbleAgent: { background: "#dbeafe", color: "#1e3a5f", marginLeft: "auto" },
176
- bubbleClient: { background: "var(--theme-elevation-100)", color: "var(--theme-text)" },
177
- bubbleSystem: { margin: "4px auto", padding: "4px 12px", fontSize: 11, color: "#6b7280", textAlign: "center" },
178
- composer: { borderTop: "1px solid var(--theme-elevation-200)", padding: "8px 0" },
179
- composerInput: { flex: 1, padding: "8px 12px", borderRadius: 8, border: "1px solid var(--theme-elevation-200)", fontSize: 13, background: "var(--theme-elevation-0)", color: "var(--theme-text)" },
180
- sendBtn: { padding: "8px 16px", borderRadius: 8, background: "#2563eb", color: "#fff", border: "none", fontWeight: 600, cursor: "pointer", fontSize: 13 },
181
- tabsRow: { display: "flex", gap: 4, padding: "8px 14px", borderBottom: "1px solid var(--theme-elevation-200)" },
182
- tab: { padding: "4px 10px", borderRadius: 6, border: "none", background: "none", cursor: "pointer", fontSize: 12, color: "var(--theme-elevation-500)" },
183
- tabActive: { background: "var(--theme-elevation-100)", fontWeight: 700, color: "var(--theme-text)" }
163
+ const getClientCompany = (client) => {
164
+ if (typeof client === "number") return "";
165
+ return client.company || "";
184
166
  };
185
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.page, children: [
186
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 16 }, children: [
187
- /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: 22, fontWeight: 700, margin: 0, color: "var(--theme-text)" }, children: t("chat.title") }),
188
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--theme-elevation-500)", fontSize: 13, margin: "4px 0 0" }, children: sessions.active.length !== 1 ? t("chat.sessionCountPlural", { count: String(sessions.active.length) }) : t("chat.sessionCount", { count: String(sessions.active.length) }) })
189
- ] }),
190
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.container, children: [
191
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.sidebar, children: [
192
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.tabsRow, children: [
193
- /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: () => setShowClosed(false), style: { ...S.tab, ...!showClosed ? S.tabActive : {} }, children: [
194
- t("chat.tabs.active"),
195
- " (",
196
- sessions.active.length,
197
- ")"
198
- ] }),
199
- /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: () => setShowClosed(true), style: { ...S.tab, ...showClosed ? S.tabActive : {} }, children: [
200
- t("chat.tabs.closed"),
201
- " (",
202
- sessions.closed.length,
203
- ")"
204
- ] })
167
+ const displayedSessions = showClosed ? sessions.closed : sessions.active;
168
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.page, children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.header, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
170
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: styles__default.default.title, children: t("chat.title") }),
171
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: styles__default.default.subtitle, children: sessions.active.length !== 1 ? t("chat.sessionCountPlural", { count: String(sessions.active.length) }) : t("chat.sessionCount", { count: String(sessions.active.length) }) })
172
+ ] }) }),
173
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.container, children: [
174
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.sidebar, children: [
175
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.tabs, children: [
176
+ /* @__PURE__ */ jsxRuntime.jsxs(
177
+ "button",
178
+ {
179
+ onClick: () => setShowClosed(false),
180
+ className: `${styles__default.default.tab} ${!showClosed ? styles__default.default.tabActive : ""}`,
181
+ children: [
182
+ t("chat.tabs.active"),
183
+ " (",
184
+ sessions.active.length,
185
+ ")"
186
+ ]
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsxRuntime.jsxs(
190
+ "button",
191
+ {
192
+ onClick: () => setShowClosed(true),
193
+ className: `${styles__default.default.tab} ${showClosed ? styles__default.default.tabActive : ""}`,
194
+ children: [
195
+ t("chat.tabs.closed"),
196
+ " (",
197
+ sessions.closed.length,
198
+ ")"
199
+ ]
200
+ }
201
+ )
205
202
  ] }),
206
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 20, textAlign: "center", color: "#94a3b8" }, children: t("common.loading") }) : displayedSessions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 20, textAlign: "center", color: "#94a3b8" }, children: showClosed ? t("chat.noSessionClosed") : t("chat.noSessionActive") }) : displayedSessions.map((s2) => /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: () => setSelectedSession(s2.session), style: { ...S.sessionItem, ...selectedSession === s2.session ? S.sessionActive : {} }, children: [
207
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
208
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600 }, children: getClientName(s2.client) }),
209
- s2.unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: "1px 6px", borderRadius: 10, background: "#dc2626", color: "#fff", fontSize: 10, fontWeight: 700 }, children: s2.unreadCount })
210
- ] }),
211
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "var(--theme-elevation-500)", marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: s2.lastMessage }),
212
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 11, color: "var(--theme-elevation-400)", marginTop: 2 }, children: [
213
- new Date(s2.lastMessageAt).toLocaleString("fr-FR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" }),
214
- " -- ",
215
- s2.messageCount,
216
- " ",
217
- t("chat.msg")
218
- ] })
219
- ] }, s2.session)) })
203
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.sessionList, children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.loadingState, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.emptyState, children: t("common.loading") }) }) : displayedSessions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.emptyState, children: showClosed ? t("chat.noSessionClosed") : t("chat.noSessionActive") }) : displayedSessions.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
204
+ "button",
205
+ {
206
+ onClick: () => setSelectedSession(s.session),
207
+ className: `${styles__default.default.sessionItem} ${selectedSession === s.session ? styles__default.default.sessionItemActive : ""}`,
208
+ children: [
209
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.sessionHeader, children: [
210
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.sessionName, children: getClientName(s.client) }),
211
+ s.unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.unreadBadge, children: s.unreadCount })
212
+ ] }),
213
+ getClientCompany(s.client) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.sessionCompany, children: getClientCompany(s.client) }),
214
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.sessionPreview, children: s.lastMessage.startsWith("Note:") ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.sessionRating, children: s.lastMessage.match(/[★☆]+/)?.[0] || "\u2B50" }) : s.lastMessage }),
215
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.sessionMeta, children: [
216
+ new Date(s.lastMessageAt).toLocaleString("fr-FR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" }),
217
+ " \xB7 ",
218
+ s.messageCount,
219
+ " ",
220
+ t("chat.msg")
221
+ ] })
222
+ ]
223
+ },
224
+ s.session
225
+ )) })
220
226
  ] }),
221
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.chatPanel, children: !selectedSession ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", color: "#94a3b8", fontSize: 14 }, children: t("chat.selectSession") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
222
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 14px", borderBottom: "1px solid var(--theme-elevation-200)" }, children: [
223
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontFamily: "monospace", fontSize: 12, color: "var(--theme-elevation-500)" }, children: selectedSession }),
224
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: closeSession, style: { padding: "4px 12px", borderRadius: 6, border: "1px solid #dc2626", background: "none", color: "#dc2626", fontSize: 12, cursor: "pointer" }, children: t("chat.closeChat") })
227
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.chatPanel, children: !selectedSession ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.chatEmpty, children: t("chat.selectSession") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
228
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.chatHeader, children: [
229
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.chatSessionId, children: selectedSession }),
230
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: closeSession, className: styles__default.default.closeBtn, children: t("chat.closeChat") })
225
231
  ] }),
226
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.messagesArea, children: [
227
- messages.map((msg) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: msg.senderType === "agent" ? "row-reverse" : "row", padding: "2px 14px" }, children: msg.senderType === "system" ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: S.bubbleSystem, children: msg.message }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...S.bubble, ...msg.senderType === "agent" ? S.bubbleAgent : S.bubbleClient }, children: [
228
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 600, marginBottom: 2 }, children: msg.senderType === "agent" ? t("chat.you") : t("chat.clientLabel") }),
229
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: msg.message }),
230
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 10, color: msg.senderType === "agent" ? "#1e40af" : "var(--theme-elevation-400)", marginTop: 2 }, children: new Date(msg.createdAt).toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" }) })
231
- ] }) }, msg.id)),
232
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.messagesArea, children: [
233
+ messages.map((msg) => /* @__PURE__ */ jsxRuntime.jsx(
234
+ "div",
235
+ {
236
+ className: `${styles__default.default.messageRow} ${msg.senderType === "agent" ? styles__default.default.messageRowAgent : msg.senderType === "system" ? styles__default.default.messageRowSystem : styles__default.default.messageRowClient}`,
237
+ children: msg.senderType === "system" ? msg.message.startsWith("Note:") || msg.message.startsWith("Commentaire:") ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.bubbleRating, children: [
238
+ msg.message.includes("\u2605") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.ratingStars, children: msg.message.match(/[★☆]+/)?.[0] || "" }),
239
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.ratingComment, children: msg.message.includes("\u2014") ? msg.message.split("\u2014").slice(1).join("\u2014").trim() : msg.message.replace(/Note:\s*[★☆]+\s*\(\d\/5\)\s*/, "").replace("Commentaire: ", "") }),
240
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.ratingMeta, children: [
241
+ t("chat.clientReview"),
242
+ " \xB7 ",
243
+ new Date(msg.createdAt).toLocaleString("fr-FR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" })
244
+ ] })
245
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.bubbleSystem, children: msg.message }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.bubble} ${msg.senderType === "agent" ? styles__default.default.bubbleAgent : styles__default.default.bubbleClient}`, children: [
246
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.bubbleSender, children: msg.senderType === "agent" ? msg.agent ? `${msg.agent.firstName || t("chat.agent")}` : t("chat.you") : t("chat.clientLabel") }),
247
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.bubbleBody, children: msg.message }),
248
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.bubbleTime, children: new Date(msg.createdAt).toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" }) })
249
+ ] })
250
+ },
251
+ msg.id
252
+ )),
232
253
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
233
254
  ] }),
234
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: sendMessage, style: S.composer, children: [
235
- cannedResponses.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("select", { onChange: (e) => {
236
- const cr = cannedResponses.find((c) => String(c.id) === e.target.value);
237
- if (cr) setInput(cr.body);
238
- e.target.value = "";
239
- }, style: { padding: "4px 8px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 11, marginBottom: 6, color: "var(--theme-text)", background: "var(--theme-elevation-0)" }, children: [
240
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("chat.quickReply") }),
241
- cannedResponses.map((cr) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(cr.id), children: cr.title }, cr.id))
242
- ] }),
243
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8 }, children: [
244
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: t("chat.inputPlaceholder"), maxLength: 2e3, style: S.composerInput, autoFocus: true }),
245
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: !input.trim() || sending, style: S.sendBtn, children: t("chat.sendButton") })
255
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: sendMessage, className: styles__default.default.composer, children: [
256
+ cannedResponses.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
257
+ "select",
258
+ {
259
+ onChange: (e) => {
260
+ const cr = cannedResponses.find((c) => String(c.id) === e.target.value);
261
+ if (cr) setInput(cr.body);
262
+ e.target.value = "";
263
+ },
264
+ className: styles__default.default.cannedSelect,
265
+ children: [
266
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("chat.quickReply") }),
267
+ cannedResponses.map((cr) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(cr.id), children: cr.title }, cr.id))
268
+ ]
269
+ }
270
+ ),
271
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.composerRow, children: [
272
+ /* @__PURE__ */ jsxRuntime.jsx(
273
+ "input",
274
+ {
275
+ type: "text",
276
+ value: input,
277
+ onChange: (e) => setInput(e.target.value),
278
+ placeholder: t("chat.inputPlaceholder"),
279
+ maxLength: 2e3,
280
+ className: styles__default.default.composerInput,
281
+ autoFocus: true
282
+ }
283
+ ),
284
+ /* @__PURE__ */ jsxRuntime.jsx(
285
+ "button",
286
+ {
287
+ type: "submit",
288
+ disabled: !input.trim() || sending,
289
+ className: styles__default.default.sendBtn,
290
+ children: t("chat.sendButton")
291
+ }
292
+ )
246
293
  ] })
247
294
  ] })
248
295
  ] }) })