@consilioweb/payload-support 2.0.0 → 3.0.0

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 (222) hide show
  1. package/README.md +656 -355
  2. package/dist/components/RichTextEditor/index.d.ts +19 -0
  3. package/dist/components/TicketConversation/RewriteDropdown.d.ts +6 -0
  4. package/dist/components/TicketConversation/SkeletonText.d.ts +4 -0
  5. package/dist/components/TicketConversation/components/AISummaryPanel.d.ts +14 -0
  6. package/dist/components/TicketConversation/components/ActionPanels.d.ts +47 -0
  7. package/dist/components/TicketConversation/components/ActivityLog.d.ts +5 -0
  8. package/dist/components/TicketConversation/components/ClientBar.d.ts +5 -0
  9. package/dist/components/TicketConversation/components/ClientHistory.d.ts +24 -0
  10. package/dist/components/TicketConversation/components/CodeBlock.d.ts +26 -0
  11. package/dist/components/TicketConversation/components/CodeBlockInserter.d.ts +12 -0
  12. package/dist/components/TicketConversation/components/QuickActions.d.ts +32 -0
  13. package/dist/components/TicketConversation/components/TicketHeader.d.ts +19 -0
  14. package/dist/components/TicketConversation/components/TimeTrackingPanel.d.ts +24 -0
  15. package/dist/components/TicketConversation/config.d.ts +10 -0
  16. package/dist/components/TicketConversation/config.js +1 -40
  17. package/dist/components/TicketConversation/constants.d.ts +56 -0
  18. package/dist/components/TicketConversation/context.d.ts +53 -0
  19. package/dist/components/TicketConversation/hooks/useAI.d.ts +21 -0
  20. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.d.ts +3 -0
  21. package/dist/components/TicketConversation/hooks/useFeatures.d.ts +23 -0
  22. package/dist/components/TicketConversation/hooks/useFeatures.js +61 -0
  23. package/dist/components/TicketConversation/hooks/useMessageActions.d.ts +20 -0
  24. package/dist/components/TicketConversation/hooks/useReply.d.ts +27 -0
  25. package/dist/components/TicketConversation/hooks/useTicketActions.d.ts +58 -0
  26. package/dist/components/TicketConversation/hooks/useTimeTracking.d.ts +18 -0
  27. package/dist/components/TicketConversation/hooks/useTranslation.d.ts +33 -0
  28. package/dist/components/TicketConversation/hooks/useTranslation.js +22 -3
  29. package/dist/components/TicketConversation/index.d.ts +4 -0
  30. package/dist/components/TicketConversation/index.js +2 -2
  31. package/dist/components/TicketConversation/types.d.ts +56 -0
  32. package/dist/components/TicketConversation/utils.d.ts +2 -0
  33. package/dist/index.cjs +392 -320
  34. package/dist/index.d.cts +100 -3
  35. package/dist/index.d.ts +100 -3
  36. package/dist/index.js +388 -321
  37. package/dist/utils/features.d.ts +123 -0
  38. package/dist/utils/features.js +147 -0
  39. package/dist/views/BillingView/client.d.ts +2 -0
  40. package/dist/views/BillingView/index.d.ts +4 -0
  41. package/dist/views/ChatView/client.d.ts +2 -0
  42. package/dist/views/ChatView/client.js +9 -9
  43. package/dist/views/ChatView/index.d.ts +4 -0
  44. package/dist/views/CrmView/client.d.ts +2 -0
  45. package/dist/views/CrmView/client.js +6 -9
  46. package/dist/views/CrmView/index.d.ts +4 -0
  47. package/dist/views/EmailTrackingView/client.d.ts +2 -0
  48. package/dist/views/EmailTrackingView/index.d.ts +4 -0
  49. package/dist/views/ImportConversationView/client.d.ts +2 -0
  50. package/dist/views/ImportConversationView/index.d.ts +4 -0
  51. package/dist/views/LogsView/client.d.ts +2 -0
  52. package/dist/views/LogsView/index.d.ts +4 -0
  53. package/dist/views/NewTicketView/client.d.ts +2 -0
  54. package/dist/views/NewTicketView/index.d.ts +4 -0
  55. package/dist/views/PendingEmailsView/client.d.ts +2 -0
  56. package/dist/views/PendingEmailsView/index.d.ts +4 -0
  57. package/dist/views/SupportDashboardView/client.d.ts +2 -0
  58. package/dist/views/SupportDashboardView/client.js +1 -1
  59. package/dist/views/SupportDashboardView/index.d.ts +4 -0
  60. package/dist/views/TicketDetailView/NextActionItem.d.ts +6 -0
  61. package/dist/views/TicketDetailView/RewriteDropdown.d.ts +7 -0
  62. package/dist/views/TicketDetailView/client.d.ts +2 -0
  63. package/dist/views/TicketDetailView/client.js +2 -2
  64. package/dist/views/TicketDetailView/constants.d.ts +13 -0
  65. package/dist/views/TicketDetailView/helpers.d.ts +2 -0
  66. package/dist/views/TicketDetailView/index.d.ts +4 -0
  67. package/dist/views/TicketDetailView/types.d.ts +40 -0
  68. package/dist/views/TicketInboxView/client.d.ts +2 -0
  69. package/dist/views/TicketInboxView/client.js +5 -5
  70. package/dist/views/TicketInboxView/index.d.ts +4 -0
  71. package/dist/views/TicketingSettingsView/client.d.ts +2 -0
  72. package/dist/views/TicketingSettingsView/client.js +6 -5
  73. package/dist/views/TicketingSettingsView/index.d.ts +4 -0
  74. package/dist/views/TimeDashboardView/client.d.ts +2 -0
  75. package/dist/views/TimeDashboardView/index.d.ts +4 -0
  76. package/dist/views/shared/AdminViewHeader.d.ts +13 -0
  77. package/dist/views/shared/ErrorBoundary.d.ts +17 -0
  78. package/dist/views/shared/Skeleton.d.ts +21 -0
  79. package/dist/views/shared/StatusPill.d.ts +8 -0
  80. package/dist/views/shared/adminTokens.d.ts +19 -0
  81. package/dist/views/shared/config.d.ts +8 -0
  82. package/dist/views/shared/config.js +1 -40
  83. package/dist/views/shared/dateLocale.d.ts +5 -0
  84. package/dist/views/shared/index.d.ts +12 -0
  85. package/dist/views/shared/index.js +2 -1
  86. package/dist/views/shared/locales/en.json +708 -0
  87. package/dist/views/shared/locales/fr.json +708 -0
  88. package/dist/views/shared/sla.d.ts +14 -0
  89. package/dist/views.d.ts +13 -13
  90. package/package.json +26 -41
  91. package/src/collections/SupportClients.ts +29 -0
  92. package/src/collections/TicketCollaborators.ts +6 -1
  93. package/src/collections/TicketMessages.ts +46 -24
  94. package/src/collections/Tickets.ts +117 -79
  95. package/src/collections/TimeEntries.ts +57 -32
  96. package/src/components/TicketConversation/config.ts +16 -80
  97. package/src/components/TicketConversation/hooks/useFeatures.ts +109 -0
  98. package/src/components/TicketConversation/hooks/useTranslation.ts +38 -4
  99. package/src/components/TicketConversation/index.tsx +2 -2
  100. package/src/endpoints/ai.ts +2 -1
  101. package/src/endpoints/client-intelligence.ts +2 -1
  102. package/src/endpoints/process-scheduled.ts +13 -8
  103. package/src/endpoints/round-robin-config.ts +30 -20
  104. package/src/endpoints/settings.ts +28 -77
  105. package/src/index.ts +6 -2
  106. package/src/portal/LiveChat.tsx +33 -22
  107. package/src/portal/auth/tickets/detail/TicketPolling.tsx +5 -1
  108. package/src/types/lucide-react.d.ts +10 -2
  109. package/src/utils/aiAgent.ts +2 -1
  110. package/src/utils/aiProvider.ts +21 -0
  111. package/src/utils/features.ts +297 -0
  112. package/src/utils/fireWebhooks.ts +6 -0
  113. package/src/utils/generateTicketSynthesis.ts +2 -1
  114. package/src/utils/index.ts +4 -2
  115. package/src/utils/readSettings.ts +82 -9
  116. package/src/utils/webhookDispatcher.ts +19 -7
  117. package/src/views/ChatView/client.tsx +13 -9
  118. package/src/views/CrmView/client.tsx +10 -11
  119. package/src/views/SupportDashboardView/client.tsx +1 -1
  120. package/src/views/TicketDetailView/client.tsx +2 -2
  121. package/src/views/TicketInboxView/client.tsx +10 -5
  122. package/src/views/TicketingSettingsView/client.tsx +9 -7
  123. package/src/views/shared/config.ts +14 -80
  124. package/src/views/shared/index.ts +3 -1
  125. package/src/views/shared/locales/en.json +2 -2
  126. package/src/views/shared/locales/fr.json +2 -2
  127. package/dist/components/RichTextEditor/index.cjs +0 -279
  128. package/dist/components/TicketConversation/RewriteDropdown.cjs +0 -98
  129. package/dist/components/TicketConversation/SkeletonText.cjs +0 -21
  130. package/dist/components/TicketConversation/components/AISummaryPanel.cjs +0 -85
  131. package/dist/components/TicketConversation/components/ActionPanels.cjs +0 -193
  132. package/dist/components/TicketConversation/components/ActivityLog.cjs +0 -24
  133. package/dist/components/TicketConversation/components/ClientBar.cjs +0 -43
  134. package/dist/components/TicketConversation/components/ClientHistory.cjs +0 -138
  135. package/dist/components/TicketConversation/components/CodeBlock.cjs +0 -171
  136. package/dist/components/TicketConversation/components/CodeBlockInserter.cjs +0 -156
  137. package/dist/components/TicketConversation/components/QuickActions.cjs +0 -87
  138. package/dist/components/TicketConversation/components/TicketHeader.cjs +0 -93
  139. package/dist/components/TicketConversation/components/TimeTrackingPanel.cjs +0 -134
  140. package/dist/components/TicketConversation/config.cjs +0 -44
  141. package/dist/components/TicketConversation/constants.cjs +0 -127
  142. package/dist/components/TicketConversation/context.cjs +0 -13
  143. package/dist/components/TicketConversation/hooks/useAI.cjs +0 -196
  144. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.cjs +0 -20
  145. package/dist/components/TicketConversation/hooks/useMessageActions.cjs +0 -136
  146. package/dist/components/TicketConversation/hooks/useReply.cjs +0 -188
  147. package/dist/components/TicketConversation/hooks/useTicketActions.cjs +0 -290
  148. package/dist/components/TicketConversation/hooks/useTimeTracking.cjs +0 -123
  149. package/dist/components/TicketConversation/hooks/useTranslation.cjs +0 -78
  150. package/dist/components/TicketConversation/index.cjs +0 -1114
  151. package/dist/components/TicketConversation/types.cjs +0 -2
  152. package/dist/components/TicketConversation/utils.cjs +0 -27
  153. package/dist/views/BillingView/client.cjs +0 -469
  154. package/dist/views/BillingView/index.cjs +0 -34
  155. package/dist/views/ChatView/client.cjs +0 -301
  156. package/dist/views/ChatView/index.cjs +0 -34
  157. package/dist/views/CrmView/client.cjs +0 -458
  158. package/dist/views/CrmView/index.cjs +0 -34
  159. package/dist/views/EmailTrackingView/client.cjs +0 -171
  160. package/dist/views/EmailTrackingView/index.cjs +0 -34
  161. package/dist/views/ImportConversationView/client.cjs +0 -238
  162. package/dist/views/ImportConversationView/index.cjs +0 -34
  163. package/dist/views/LogsView/client.cjs +0 -148
  164. package/dist/views/LogsView/index.cjs +0 -32
  165. package/dist/views/NewTicketView/client.cjs +0 -213
  166. package/dist/views/NewTicketView/index.cjs +0 -32
  167. package/dist/views/PendingEmailsView/client.cjs +0 -471
  168. package/dist/views/PendingEmailsView/index.cjs +0 -34
  169. package/dist/views/SupportDashboardView/client.cjs +0 -437
  170. package/dist/views/SupportDashboardView/index.cjs +0 -34
  171. package/dist/views/TicketDetailView/NextActionItem.cjs +0 -40
  172. package/dist/views/TicketDetailView/RewriteDropdown.cjs +0 -80
  173. package/dist/views/TicketDetailView/client.cjs +0 -1412
  174. package/dist/views/TicketDetailView/constants.cjs +0 -20
  175. package/dist/views/TicketDetailView/helpers.cjs +0 -23
  176. package/dist/views/TicketDetailView/index.cjs +0 -34
  177. package/dist/views/TicketDetailView/types.cjs +0 -2
  178. package/dist/views/TicketInboxView/client.cjs +0 -359
  179. package/dist/views/TicketInboxView/index.cjs +0 -32
  180. package/dist/views/TicketingSettingsView/client.cjs +0 -740
  181. package/dist/views/TicketingSettingsView/index.cjs +0 -34
  182. package/dist/views/TimeDashboardView/client.cjs +0 -180
  183. package/dist/views/TimeDashboardView/index.cjs +0 -34
  184. package/dist/views/shared/AdminViewHeader.cjs +0 -68
  185. package/dist/views/shared/ErrorBoundary.cjs +0 -55
  186. package/dist/views/shared/Skeleton.cjs +0 -77
  187. package/dist/views/shared/StatusPill.cjs +0 -47
  188. package/dist/views/shared/adminTokens.cjs +0 -34
  189. package/dist/views/shared/config.cjs +0 -44
  190. package/dist/views/shared/dateLocale.cjs +0 -5
  191. package/dist/views/shared/index.cjs +0 -72
  192. package/dist/views/shared/sla.cjs +0 -37
  193. package/dist/views.cjs +0 -86
  194. package/dist/views.d.cts +0 -13
  195. package/src/__tests__/aiSummaryRendering.test.ts +0 -21
  196. package/src/__tests__/authResponses.test.ts +0 -69
  197. package/src/__tests__/capabilities.test.ts +0 -71
  198. package/src/__tests__/generateTrackingToken.test.ts +0 -67
  199. package/src/__tests__/integration/ai-agent.test.ts +0 -46
  200. package/src/__tests__/integration/auth.test.ts +0 -43
  201. package/src/__tests__/integration/automation-rules.test.ts +0 -68
  202. package/src/__tests__/integration/buildTestPayload.ts +0 -52
  203. package/src/__tests__/integration/channels.test.ts +0 -52
  204. package/src/__tests__/integration/core-behaviors.test.ts +0 -81
  205. package/src/__tests__/integration/dashboard-volume.test.ts +0 -31
  206. package/src/__tests__/integration/digest.test.ts +0 -48
  207. package/src/__tests__/integration/invoice-pdf.test.ts +0 -30
  208. package/src/__tests__/integration/isolation.test.ts +0 -81
  209. package/src/__tests__/integration/mentions-invoice.test.ts +0 -50
  210. package/src/__tests__/integration/nps.test.ts +0 -37
  211. package/src/__tests__/integration/per-team-sla.test.ts +0 -44
  212. package/src/__tests__/integration/push.test.ts +0 -75
  213. package/src/__tests__/integration/sanitization.test.ts +0 -35
  214. package/src/__tests__/integration/sla-pause.test.ts +0 -39
  215. package/src/__tests__/integration/smoke.test.ts +0 -26
  216. package/src/__tests__/integration/snooze.test.ts +0 -68
  217. package/src/__tests__/integration/teams.test.ts +0 -38
  218. package/src/__tests__/rateLimiter.test.ts +0 -83
  219. package/src/__tests__/sanitizeHtml.test.ts +0 -165
  220. package/src/__tests__/sla.test.ts +0 -258
  221. package/src/__tests__/trackOpenEndpoint.test.ts +0 -54
  222. package/src/__tests__/webhookSecurity.test.ts +0 -58
@@ -1,1114 +0,0 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var React = require('react');
5
- var constants = require('./constants');
6
- var utils = require('./utils');
7
- var CodeBlock = require('./components/CodeBlock');
8
- var CodeBlockInserter = require('./components/CodeBlockInserter');
9
- var TicketHeader = require('./components/TicketHeader');
10
- var ClientBar = require('./components/ClientBar');
11
- var QuickActions = require('./components/QuickActions');
12
- var AISummaryPanel = require('./components/AISummaryPanel');
13
- var ActionPanels = require('./components/ActionPanels');
14
- var ActivityLog = require('./components/ActivityLog');
15
- var ClientHistory = require('./components/ClientHistory');
16
- var TimeTrackingPanel = require('./components/TimeTrackingPanel');
17
- var useTimeTracking = require('./hooks/useTimeTracking');
18
- var useMessageActions = require('./hooks/useMessageActions');
19
- var useTicketActions = require('./hooks/useTicketActions');
20
- var useReply = require('./hooks/useReply');
21
- var useAI = require('./hooks/useAI');
22
- var useDocumentIdFromUrl = require('./hooks/useDocumentIdFromUrl');
23
- var config = require('./config');
24
- var SkeletonText = require('./SkeletonText');
25
- var RewriteDropdown = require('./RewriteDropdown');
26
- require('../../styles/theme.css');
27
-
28
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
29
-
30
- var React__default = /*#__PURE__*/_interopDefault(React);
31
-
32
- const TicketConversation = () => {
33
- const { id } = useDocumentIdFromUrl.useDocumentIdFromUrl();
34
- const [features] = React.useState(() => config.getFeatures());
35
- const [messages, setMessages] = React.useState([]);
36
- const [timeEntries, setTimeEntries] = React.useState([]);
37
- const [client, setClient] = React.useState(null);
38
- const [cannedResponses, setCannedResponses] = React.useState([]);
39
- const [activityLog, setActivityLog] = React.useState([]);
40
- const [satisfaction, setSatisfaction] = React.useState(null);
41
- const [loading, setLoading] = React.useState(true);
42
- const [messagesCollapsed, setMessagesCollapsed] = React.useState(true);
43
- const [searchQuery, setSearchQuery] = React.useState("");
44
- const [copiedLink, setCopiedLink] = React.useState(null);
45
- const prevMessageCountRef = React.useRef(0);
46
- const [clientTyping, setClientTyping] = React.useState(false);
47
- const [clientTypingName, setClientTypingName] = React.useState("");
48
- const typingLastSent = React.useRef(0);
49
- const [currentStatus, setCurrentStatus] = React.useState("");
50
- const [ticketNumber, setTicketNumber] = React.useState("");
51
- const [ticketSubject, setTicketSubject] = React.useState("");
52
- const [ticketSource, setTicketSource] = React.useState("");
53
- const [chatSession, setChatSession] = React.useState("");
54
- const [autoCloseScheduledAt, setAutoCloseScheduledAt] = React.useState(null);
55
- const [clientTickets, setClientTickets] = React.useState([]);
56
- const [clientProjects, setClientProjects] = React.useState([]);
57
- const [clientNotes, setClientNotes] = React.useState("");
58
- const [savingNotes, setSavingNotes] = React.useState(false);
59
- const [notesSaved, setNotesSaved] = React.useState(false);
60
- const [lastClientReadAt, setLastClientReadAt] = React.useState(null);
61
- const fetchAll = React.useCallback(async () => {
62
- if (!id) return;
63
- try {
64
- const [msgRes, timeRes, ticketRes, cannedRes, activityRes, csatRes] = await Promise.all([
65
- fetch(`/api/ticket-messages?where[ticket][equals]=${id}&sort=createdAt&limit=200&depth=1`, { credentials: "include" }),
66
- fetch(`/api/time-entries?where[ticket][equals]=${id}&sort=-date&limit=50&depth=0`, { credentials: "include" }),
67
- fetch(`/api/tickets/${id}?depth=1`, { credentials: "include" }),
68
- fetch(`/api/canned-responses?sort=sortOrder&limit=50&depth=0`, { credentials: "include" }),
69
- fetch(`/api/ticket-activity-log?where[ticket][equals]=${id}&sort=-createdAt&limit=50&depth=0`, { credentials: "include" }),
70
- fetch(`/api/satisfaction-surveys?where[ticket][equals]=${id}&limit=1&depth=0`, { credentials: "include" })
71
- ]);
72
- if (msgRes.ok) {
73
- const d = await msgRes.json();
74
- setMessages(d.docs || []);
75
- }
76
- if (timeRes.ok) {
77
- const d = await timeRes.json();
78
- setTimeEntries(d.docs || []);
79
- }
80
- let resolvedChatSession = "";
81
- if (ticketRes.ok) {
82
- const d = await ticketRes.json();
83
- if (d.client && typeof d.client === "object") {
84
- setClient(d.client);
85
- }
86
- setSnoozeUntil(d.snoozeUntil || null);
87
- setAutoCloseScheduledAt(d.autoCloseScheduledAt || null);
88
- setLastClientReadAt(d.lastClientReadAt || null);
89
- setCurrentStatus(d.status || "");
90
- setTicketNumber(d.ticketNumber || "");
91
- setTicketSubject(d.subject || "");
92
- setTicketSource(d.source || "");
93
- setChatSession(d.chatSession || "");
94
- resolvedChatSession = d.chatSession || "";
95
- const clientId = typeof d.client === "object" ? d.client?.id : d.client;
96
- if (clientId) {
97
- const [clientTicketsRes, projectsRes, clientDetailRes] = await Promise.all([
98
- fetch(`/api/tickets?where[client][equals]=${clientId}&where[id][not_equals]=${id}&sort=-createdAt&limit=5&depth=0`, { credentials: "include" }),
99
- fetch(`/api/projects?where[client][contains]=${clientId}&depth=0`, { credentials: "include" }),
100
- fetch(`/api/support-clients/${clientId}?depth=0`, { credentials: "include" })
101
- ]);
102
- if (clientTicketsRes.ok) {
103
- const ctData = await clientTicketsRes.json();
104
- setClientTickets((ctData.docs || []).map((t) => ({
105
- id: t.id,
106
- ticketNumber: t.ticketNumber,
107
- subject: t.subject,
108
- status: t.status,
109
- createdAt: t.createdAt
110
- })));
111
- }
112
- if (projectsRes.ok) {
113
- const pData = await projectsRes.json();
114
- setClientProjects((pData.docs || []).map((p) => ({
115
- id: p.id,
116
- name: p.name,
117
- status: p.status
118
- })));
119
- }
120
- if (clientDetailRes.ok) {
121
- const cdData = await clientDetailRes.json();
122
- setClientNotes(cdData.notes || "");
123
- }
124
- }
125
- }
126
- if (cannedRes.ok) {
127
- const d = await cannedRes.json();
128
- setCannedResponses(d.docs || []);
129
- }
130
- if (activityRes.ok) {
131
- const d = await activityRes.json();
132
- setActivityLog(d.docs || []);
133
- }
134
- if (csatRes.ok) {
135
- const d = await csatRes.json();
136
- setSatisfaction(d.docs?.[0] || null);
137
- }
138
- if (resolvedChatSession) {
139
- try {
140
- const chatRes = await fetch(`/api/support/admin-chat?session=${encodeURIComponent(resolvedChatSession)}`, { credentials: "include" });
141
- if (chatRes.ok) {
142
- const chatData = await chatRes.json();
143
- const chatMsgs = (chatData.messages || []).filter((cm) => cm.senderType !== "system").map((cm) => ({
144
- id: `chat-${cm.id}`,
145
- body: cm.message,
146
- authorType: cm.senderType === "agent" ? "admin" : "client",
147
- isInternal: false,
148
- createdAt: cm.createdAt,
149
- fromChat: true
150
- }));
151
- setMessages((prev) => {
152
- const ticketMsgs = prev;
153
- const merged = [...ticketMsgs];
154
- for (const chatMsg of chatMsgs) {
155
- const isDuplicate = ticketMsgs.some((tm) => {
156
- if (tm.body !== chatMsg.body) return false;
157
- const timeDiff = Math.abs(new Date(tm.createdAt).getTime() - new Date(chatMsg.createdAt).getTime());
158
- return timeDiff < 5e3;
159
- });
160
- if (!isDuplicate) {
161
- merged.push(chatMsg);
162
- }
163
- }
164
- return merged.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
165
- });
166
- }
167
- } catch (err) {
168
- console.warn("[TicketConversation] Chat fetch error:", err);
169
- }
170
- }
171
- } catch (err) {
172
- console.warn("[TicketConversation] Fetch error:", err);
173
- } finally {
174
- setLoading(false);
175
- }
176
- }, [id]);
177
- React.useEffect(() => {
178
- fetchAll();
179
- }, [fetchAll]);
180
- React.useEffect(() => {
181
- if (!id) return;
182
- fetch(`/api/tickets/${id}`, {
183
- method: "PATCH",
184
- headers: { "Content-Type": "application/json" },
185
- credentials: "include",
186
- body: JSON.stringify({ lastAdminReadAt: (/* @__PURE__ */ new Date()).toISOString() })
187
- }).catch(() => {
188
- });
189
- }, [id, messages.length]);
190
- const typingFailCount = React.useRef(0);
191
- React.useEffect(() => {
192
- if (!id) return;
193
- typingFailCount.current = 0;
194
- const poll = async () => {
195
- if (typingFailCount.current >= 3) return;
196
- try {
197
- const res = await fetch(`/api/support/typing?ticketId=${id}`, { credentials: "include" });
198
- if (res.ok) {
199
- typingFailCount.current = 0;
200
- const data = await res.json();
201
- setClientTyping(data.typing);
202
- setClientTypingName(data.name || "");
203
- } else {
204
- typingFailCount.current++;
205
- }
206
- } catch {
207
- typingFailCount.current++;
208
- }
209
- };
210
- poll();
211
- const interval = setInterval(poll, 3e3);
212
- return () => clearInterval(interval);
213
- }, [id]);
214
- const sendAdminTyping = React.useCallback(() => {
215
- if (!id) return;
216
- const now = Date.now();
217
- if (now - typingLastSent.current < 3e3) return;
218
- typingLastSent.current = now;
219
- fetch("/api/support/typing", {
220
- method: "POST",
221
- headers: { "Content-Type": "application/json" },
222
- credentials: "include",
223
- body: JSON.stringify({ ticketId: id })
224
- }).catch(() => {
225
- });
226
- }, [id]);
227
- const tt = useTimeTracking.useTimeTracking(id, fetchAll);
228
- const { duration, setDuration, timeDescription, setTimeDescription, addingTime, timeSuccess, timerRunning, timerSeconds, setTimerSeconds, timerDescription, setTimerDescription, handleAddTime, handleTimerStart, handleTimerStop, handleTimerSave, handleTimerDiscard } = tt;
229
- const ma = useMessageActions.useMessageActions(id, client, fetchAll);
230
- const { togglingAuthor, editingMsg, editBody, editHtml, setEditHtml, savingEdit, handleEditStart, handleEditSave, handleEditCancel, deletingMsg, handleDelete, resendingMsg, resendSuccess, handleResend, handleToggleAuthor, handleSplitMessage, setEditBody } = ma;
231
- const ta = useTicketActions.useTicketActions(id, fetchAll);
232
- const { statusUpdating, handleStatusChange, showMerge, setShowMerge, mergeTarget, setMergeTarget, mergeTargetInfo, setMergeTargetInfo, mergeError, setMergeError, merging, handleMergeLookup, handleMerge, showExtMsg, setShowExtMsg, extMsgBody, setExtMsgBody, extMsgAuthor, setExtMsgAuthor, extMsgDate, setExtMsgDate, extMsgFiles, setExtMsgFiles, sendingExtMsg, handleExtFileChange, handleSendExtMsg, showSnooze, setShowSnooze, snoozeUntil, setSnoozeUntil, snoozeSaving, handleSnooze, showReminder, setShowReminder, reminderHours, setReminderHours, reminderSending, reminderError, setReminderError, reminderSuccess, handleSendReminder, cancelingClose, handleCancelScheduledClose, showNextTicket, setShowNextTicket, nextTicketId, nextTicketInfo, handleNextTicket } = ta;
233
- const replyEditorRef = React.useRef(null);
234
- const rp = useReply.useReply(id, client, cannedResponses, ticketNumber, ticketSubject, fetchAll, handleNextTicket, replyEditorRef);
235
- const { fileInputRef, replyBody, setReplyBody, replyHtml, setReplyHtml, replyFiles, setReplyFiles, isInternal, setIsInternal, notifyClient, setNotifyClient, sendAsClient, setSendAsClient, sending, showSchedule, setShowSchedule, scheduleDate, setScheduleDate, handleEditorFileUpload, handleCannedSelect, handleReplyFileChange, handleSendReply, handleScheduleReply } = rp;
236
- const ai = useAI.useAI(messages, client, ticketSubject, replyBody, setReplyBody, setReplyHtml, replyEditorRef);
237
- const { clientSentiment, aiReplying, handleAiSuggestReply, aiRewriting, handleAiRewrite, showAiSummary, setShowAiSummary, aiSummary, aiGenerating, handleAiGenerate, aiSaving, aiSaved, handleAiSave: aiSaveRaw } = ai;
238
- const handleAiSave = () => aiSaveRaw(id, fetchAll);
239
- const [pollExpired, setPollExpired] = React.useState(false);
240
- React.useEffect(() => {
241
- if (!id || loading || pollExpired) return;
242
- const poll = async () => {
243
- try {
244
- const [msgRes, ticketRes, activityRes] = await Promise.all([
245
- fetch(`/api/ticket-messages?where[ticket][equals]=${id}&sort=createdAt&limit=200&depth=1`, { credentials: "include" }),
246
- fetch(`/api/tickets/${id}?depth=0`, { credentials: "include" }),
247
- fetch(`/api/ticket-activity-log?where[ticket][equals]=${id}&sort=-createdAt&limit=50&depth=0`, { credentials: "include" })
248
- ]);
249
- if (msgRes.status === 401 || msgRes.status === 403) {
250
- setPollExpired(true);
251
- return;
252
- }
253
- if (msgRes.ok) {
254
- const d = await msgRes.json();
255
- setMessages(d.docs || []);
256
- }
257
- if (ticketRes.ok) {
258
- const d = await ticketRes.json();
259
- setCurrentStatus(d.status || "");
260
- setSnoozeUntil(d.snoozeUntil || null);
261
- setAutoCloseScheduledAt(d.autoCloseScheduledAt || null);
262
- setLastClientReadAt(d.lastClientReadAt || null);
263
- }
264
- if (activityRes.ok) {
265
- const d = await activityRes.json();
266
- setActivityLog(d.docs || []);
267
- }
268
- } catch {
269
- }
270
- };
271
- const interval = setInterval(poll, 15e3);
272
- return () => clearInterval(interval);
273
- }, [id, loading, pollExpired]);
274
- React.useEffect(() => {
275
- const handleKeyDown = (e) => {
276
- if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && !e.shiftKey) {
277
- e.preventDefault();
278
- const sendBtn = document.querySelector('[data-action="send-reply"]');
279
- if (sendBtn && !sendBtn.disabled) sendBtn.click();
280
- }
281
- if ((e.ctrlKey || e.metaKey) && e.shiftKey && (e.key === "N" || e.key === "n")) {
282
- e.preventDefault();
283
- setIsInternal((prev) => !prev);
284
- }
285
- };
286
- window.addEventListener("keydown", handleKeyDown);
287
- return () => window.removeEventListener("keydown", handleKeyDown);
288
- }, []);
289
- const playNotificationSound = React.useCallback(() => {
290
- try {
291
- const ctx = new (window.AudioContext || window.webkitAudioContext)();
292
- const osc = ctx.createOscillator();
293
- const gain = ctx.createGain();
294
- osc.connect(gain);
295
- gain.connect(ctx.destination);
296
- osc.frequency.value = 800;
297
- osc.type = "sine";
298
- gain.gain.value = 0.1;
299
- osc.start();
300
- gain.gain.exponentialRampToValueAtTime(1e-3, ctx.currentTime + 0.3);
301
- osc.stop(ctx.currentTime + 0.3);
302
- } catch {
303
- }
304
- }, []);
305
- React.useEffect(() => {
306
- const currentCount = messages.length;
307
- if (prevMessageCountRef.current > 0 && currentCount > prevMessageCountRef.current) {
308
- const lastMsg = messages[messages.length - 1];
309
- if (lastMsg && lastMsg.authorType !== "admin") {
310
- playNotificationSound();
311
- }
312
- }
313
- prevMessageCountRef.current = currentCount;
314
- }, [messages, playNotificationSound]);
315
- const handleCopyLink = (type) => {
316
- const url = type === "admin" ? `${window.location.origin}/admin/collections/tickets/${id}` : `${window.location.origin}/support/tickets/${id}`;
317
- navigator.clipboard.writeText(url);
318
- setCopiedLink(type);
319
- setTimeout(() => setCopiedLink(null), 2e3);
320
- };
321
- if (!id) {
322
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "16px", color: "#666", fontStyle: "italic" }, children: "Enregistrez le ticket pour voir le tableau de bord." });
323
- }
324
- const totalMinutes = timeEntries.reduce((sum, e) => sum + (e.duration || 0), 0);
325
- const statusTransitions = (() => {
326
- switch (currentStatus) {
327
- case "open":
328
- return [
329
- { status: "waiting_client", label: "Attente client", color: constants.C.statusWaiting },
330
- { status: "resolved", label: "R\xE9solu", color: constants.C.statusResolved }
331
- ];
332
- case "waiting_client":
333
- return [
334
- { status: "open", label: "Ouvrir", color: constants.C.statusOpen },
335
- { status: "resolved", label: "R\xE9solu", color: constants.C.statusResolved }
336
- ];
337
- case "resolved":
338
- return [
339
- { status: "open", label: "Rouvrir", color: constants.C.statusOpen }
340
- ];
341
- default:
342
- return [
343
- { status: "open", label: "Ouvrir", color: constants.C.statusOpen },
344
- { status: "waiting_client", label: "Attente client", color: constants.C.statusWaiting },
345
- { status: "resolved", label: "R\xE9solu", color: constants.C.statusResolved }
346
- ];
347
- }
348
- })();
349
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.root, children: [
350
- /* @__PURE__ */ jsxRuntime.jsx(
351
- TicketHeader.TicketHeader,
352
- {
353
- ticketNumber,
354
- currentStatus,
355
- clientSentiment,
356
- ticketSource,
357
- chatSession,
358
- snoozeUntil,
359
- satisfaction,
360
- copiedLink,
361
- onCopyLink: handleCopyLink
362
- }
363
- ),
364
- client && /* @__PURE__ */ jsxRuntime.jsx(ClientBar.ClientBar, { client }),
365
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.twoColumns, children: [
366
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.mainColumn, children: [
367
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "4px", display: "flex", alignItems: "center", gap: "10px" }, children: [
368
- /* @__PURE__ */ jsxRuntime.jsxs("h3", { style: { fontSize: "14px", fontWeight: 600, margin: 0, display: "flex", alignItems: "center", gap: "8px" }, children: [
369
- "Conversation ",
370
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: constants.s.badge("#f1f5f9", "#475569"), children: messages.length })
371
- ] }),
372
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
373
- "input",
374
- {
375
- type: "text",
376
- value: searchQuery,
377
- onChange: (e) => setSearchQuery(e.target.value),
378
- placeholder: "Rechercher...",
379
- style: { ...constants.s.input, width: "100%", fontSize: "12px", padding: "6px 10px" }
380
- }
381
- ) })
382
- ] }),
383
- loading ? /* @__PURE__ */ jsxRuntime.jsx(SkeletonText.SkeletonText, { lines: 4 }) : messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#999", fontStyle: "italic", padding: "12px 0" }, children: "Aucun message." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "10px", marginBottom: "16px", paddingRight: "4px" }, children: (() => {
384
- const filtered = messages.filter((msg) => !searchQuery.trim() || msg.body.toLowerCase().includes(searchQuery.toLowerCase()));
385
- const isSearching = searchQuery.trim().length > 0;
386
- const VISIBLE_COUNT = 3;
387
- const showCollapse = !isSearching && messagesCollapsed && filtered.length > VISIBLE_COUNT;
388
- const visibleMessages = showCollapse ? filtered.slice(-VISIBLE_COUNT) : filtered;
389
- const hiddenCount = filtered.length - VISIBLE_COUNT;
390
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
391
- showCollapse && hiddenCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
392
- "button",
393
- {
394
- onClick: () => setMessagesCollapsed(false),
395
- style: {
396
- background: "none",
397
- border: `1px dashed ${constants.C.border}`,
398
- borderRadius: "6px",
399
- padding: "8px",
400
- cursor: "pointer",
401
- color: constants.C.textMuted,
402
- fontSize: "12px",
403
- fontWeight: 600,
404
- textAlign: "center"
405
- },
406
- children: [
407
- "Voir les ",
408
- hiddenCount,
409
- " message",
410
- hiddenCount > 1 ? "s" : "",
411
- " pr\xE9c\xE9dent",
412
- hiddenCount > 1 ? "s" : ""
413
- ]
414
- }
415
- ),
416
- !messagesCollapsed && filtered.length > 1 && !isSearching && /* @__PURE__ */ jsxRuntime.jsx(
417
- "button",
418
- {
419
- onClick: () => setMessagesCollapsed(true),
420
- style: {
421
- background: "none",
422
- border: `1px dashed ${constants.C.border}`,
423
- borderRadius: "6px",
424
- padding: "8px",
425
- cursor: "pointer",
426
- color: constants.C.textMuted,
427
- fontSize: "12px",
428
- fontWeight: 600,
429
- textAlign: "center"
430
- },
431
- children: "Masquer les anciens messages"
432
- }
433
- ),
434
- visibleMessages.map((msg, msgIdx) => {
435
- const borderColor = msg.isInternal ? constants.C.internalBorder : msg.fromChat ? "#bae6fd" : msg.authorType === "admin" ? "#bfdbfe" : msg.authorType === "email" ? "#fed7aa" : constants.C.clientBorder;
436
- const bgColor = msg.isInternal ? constants.C.internalBg : msg.fromChat ? "#f0f9ff" : msg.authorType === "admin" ? constants.C.adminBg : msg.authorType === "email" ? constants.C.emailBg : constants.C.clientBg;
437
- const prevVisMsg = msgIdx > 0 ? visibleMessages[msgIdx - 1] : null;
438
- const showDateSep = msg.createdAt && (!prevVisMsg?.createdAt || new Date(msg.createdAt).toDateString() !== new Date(prevVisMsg.createdAt).toDateString());
439
- return /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
440
- showDateSep && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "12px", padding: "4px 0" }, children: [
441
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, borderTop: `1px solid ${constants.C.border}` } }),
442
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "11px", fontWeight: 600, color: constants.C.textMuted, whiteSpace: "nowrap" }, children: utils.getDateLabel(msg.createdAt) }),
443
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, borderTop: `1px solid ${constants.C.border}` } })
444
- ] }),
445
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "flex-start" }, children: [
446
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
447
- flexShrink: 0,
448
- width: "28px",
449
- height: "28px",
450
- borderRadius: "50%",
451
- display: "flex",
452
- alignItems: "center",
453
- justifyContent: "center",
454
- fontSize: "10px",
455
- fontWeight: 700,
456
- color: "#fff",
457
- marginTop: "2px",
458
- backgroundColor: msg.authorType === "admin" ? constants.C.blue : msg.authorType === "email" ? constants.C.orange : "#94a3b8"
459
- }, children: msg.authorType === "admin" ? "CW" : client ? `${(client.firstName?.[0] || "").toUpperCase()}${(client.lastName?.[0] || "").toUpperCase()}` || "?" : "?" }),
460
- /* @__PURE__ */ jsxRuntime.jsxs(
461
- "div",
462
- {
463
- style: {
464
- flex: 1,
465
- padding: "10px 14px",
466
- borderRadius: "8px",
467
- border: msg.isInternal ? `1px dashed ${constants.C.internalBorder}` : `1px solid ${borderColor}`,
468
- backgroundColor: bgColor
469
- },
470
- children: [
471
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "4px", fontSize: "12px", alignItems: "center" }, children: [
472
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 600, display: "inline-flex", alignItems: "center", gap: "6px" }, children: [
473
- msg.authorType === "email" ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: constants.s.badge(constants.C.emailBg, constants.C.orange), children: "Email" }) : /* @__PURE__ */ jsxRuntime.jsxs(
474
- "select",
475
- {
476
- value: msg.authorType,
477
- onChange: (e) => handleToggleAuthor(msg.id, e.target.value === "admin" ? "client" : "admin"),
478
- disabled: togglingAuthor === msg.id,
479
- style: {
480
- fontSize: "12px",
481
- fontWeight: 600,
482
- padding: "2px 6px",
483
- borderRadius: "4px",
484
- border: `1px solid ${constants.C.border}`,
485
- cursor: "pointer",
486
- backgroundColor: msg.authorType === "admin" ? "#eff6ff" : "#f9fafb",
487
- color: "#374151",
488
- opacity: togglingAuthor === msg.id ? 0.5 : 1
489
- },
490
- children: [
491
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "admin", children: "Support" }),
492
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "client", children: "Client" })
493
- ]
494
- }
495
- ),
496
- msg.fromChat && /* @__PURE__ */ jsxRuntime.jsx("span", { style: constants.s.badge("#e0f2fe", "#0284c7"), children: "Chat" }),
497
- msg.isInternal && /* @__PURE__ */ jsxRuntime.jsx("span", { style: constants.s.badge("#fef3c7", "#92400e"), children: "Interne" }),
498
- msg.scheduledAt && (() => {
499
- const sched = msg;
500
- const scheduledDate = new Date(sched.scheduledAt).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
501
- const createdDate = new Date(msg.createdAt).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
502
- return sched.scheduledSent ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: constants.s.badge("#f0fdf4", "#16a34a"), children: [
503
- "\u2713",
504
- " Programm\xE9 le ",
505
- createdDate,
506
- " \u2014 r\xE9dig\xE9 le ",
507
- scheduledDate !== createdDate ? scheduledDate : createdDate
508
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { style: constants.s.badge("#eef8f7", "#17807c"), children: [
509
- "\u23F0",
510
- " R\xE9dig\xE9 le ",
511
- createdDate,
512
- " \u2014 envoi programm\xE9 le ",
513
- scheduledDate
514
- ] });
515
- })()
516
- ] }),
517
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: constants.C.textMuted, fontWeight: 500, fontSize: "11px", display: "inline-flex", alignItems: "center", gap: "6px" }, children: [
518
- utils.formatMessageDate(msg.createdAt),
519
- msg.editedAt && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "10px", color: "#6b7280", fontStyle: "italic" }, children: "(modifi\xE9)" }),
520
- !msg.isInternal && !msg.deletedAt && /* @__PURE__ */ jsxRuntime.jsxs(
521
- "button",
522
- {
523
- onClick: () => handleSplitMessage(msg.id, ticketSubject),
524
- style: { background: "none", border: "none", cursor: "pointer", fontSize: "10px", color: "#6b7280", padding: 0 },
525
- title: "Extraire en nouveau ticket",
526
- children: [
527
- "\u2197",
528
- " Extraire"
529
- ]
530
- }
531
- ),
532
- msg.authorType === "admin" && !msg.isInternal && (() => {
533
- const msgExt = msg;
534
- const isRead = lastClientReadAt && msg.createdAt && new Date(msg.createdAt) < new Date(lastClientReadAt);
535
- const sentAt = msgExt.emailSentAt;
536
- const openedAt = msgExt.emailOpenedAt;
537
- const sentTo = msgExt.emailSentTo;
538
- if (openedAt) {
539
- const openDate = new Date(openedAt).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit", timeZone: "Europe/Paris" });
540
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { title: `Envoy\xE9 \xE0 ${sentTo || "?"} \u2014 Ouvert le ${openDate}`, style: { fontSize: "10px", color: "#16a34a", fontWeight: 600, cursor: "help" }, children: [
541
- "\u2709 Ouvert ",
542
- openDate
543
- ] });
544
- }
545
- if (sentAt) {
546
- const sentDate = new Date(sentAt).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit", timeZone: "Europe/Paris" });
547
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { title: `Envoy\xE9 \xE0 ${sentTo || "?"} le ${sentDate}`, style: { fontSize: "10px", color: "#2563eb", fontWeight: 600, cursor: "help" }, children: [
548
- "\u2709 Envoy\xE9 \xE0 ",
549
- sentTo,
550
- " \u2014 ",
551
- sentDate
552
- ] });
553
- }
554
- return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "10px", color: isRead ? "#16a34a" : "#94a3b8", fontWeight: 600 }, children: isRead ? "\u2713\u2713 Lu" : "\u2713 Envoy\xE9" });
555
- })()
556
- ] })
557
- ] }),
558
- editingMsg === msg.id ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "6px" }, children: [
559
- /* @__PURE__ */ jsxRuntime.jsx(
560
- "textarea",
561
- {
562
- value: editBody,
563
- onChange: (e) => {
564
- setEditBody(e.target.value);
565
- setEditHtml(e.target.value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n/g, "<br />"));
566
- },
567
- rows: 4,
568
- style: { ...constants.s.input, width: "100%", resize: "vertical", fontSize: "13px" }
569
- }
570
- ),
571
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", marginTop: "8px" }, children: [
572
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleEditSave(msg.id), disabled: savingEdit || !editBody.trim() && !editHtml, style: { ...constants.s.btn(constants.C.blue, savingEdit), fontSize: "11px", padding: "5px 12px" }, children: savingEdit ? "..." : "Enregistrer" }),
573
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleEditCancel, style: { ...constants.s.ghostBtn("#6b7280"), fontSize: "11px", padding: "5px 12px" }, children: "Annuler" })
574
- ] })
575
- ] }) : msg.deletedAt ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "13px", color: "#94a3b8", fontStyle: "italic" }, children: "Ce message a \xE9t\xE9 supprim\xE9." }) : msg.bodyHtml ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
576
- /* @__PURE__ */ jsxRuntime.jsx(
577
- "div",
578
- {
579
- className: "rte-display",
580
- style: { fontSize: "13px", color: "#374151", lineHeight: 1.5 },
581
- dangerouslySetInnerHTML: { __html: msg.bodyHtml }
582
- }
583
- ),
584
- /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlockRendererHtml, { html: msg.bodyHtml })
585
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
586
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { whiteSpace: "pre-wrap", fontSize: "13px", color: "#374151", lineHeight: 1.5 }, children: searchQuery.trim() ? msg.body.split(new RegExp(`(${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`, "gi")).map(
587
- (part, i) => part.toLowerCase() === searchQuery.toLowerCase() ? /* @__PURE__ */ jsxRuntime.jsx("mark", { style: { backgroundColor: "#fde68a", borderRadius: "2px", padding: "0 2px", fontWeight: 600 }, children: part }, i) : part
588
- ) : msg.body }),
589
- /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlockRenderer, { text: msg.body })
590
- ] }),
591
- Array.isArray(msg.attachments) && msg.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "8px", display: "flex", gap: "8px", flexWrap: "wrap" }, children: msg.attachments.map((att, i) => {
592
- const file = typeof att.file === "object" ? att.file : null;
593
- if (!file) return null;
594
- const mime = (file.mimeType || file.filename || "").toLowerCase();
595
- const isImage = mime.includes("image/") || /\.(png|jpg|jpeg|webp|gif|svg)$/i.test(file.filename || "");
596
- const isGif = mime.includes("image/gif") || /\.gif$/i.test(file.filename || "");
597
- const isVideo = mime.includes("video/") || /\.(mp4|webm|mov|avi)$/i.test(file.filename || "");
598
- if (isImage) {
599
- return /* @__PURE__ */ jsxRuntime.jsx("a", { href: file.url || "#", target: "_blank", rel: "noopener noreferrer", style: { display: "block" }, children: /* @__PURE__ */ jsxRuntime.jsx(
600
- "img",
601
- {
602
- src: file.url || "",
603
- alt: file.filename || "Image",
604
- style: {
605
- maxWidth: isGif ? 300 : 240,
606
- maxHeight: 200,
607
- borderRadius: "6px",
608
- border: `1px solid ${constants.C.border}`,
609
- objectFit: "cover",
610
- cursor: "pointer"
611
- }
612
- }
613
- ) }, i);
614
- }
615
- if (isVideo) {
616
- return /* @__PURE__ */ jsxRuntime.jsx(
617
- "video",
618
- {
619
- src: file.url || "",
620
- controls: true,
621
- preload: "metadata",
622
- style: {
623
- maxWidth: 360,
624
- maxHeight: 240,
625
- borderRadius: "6px",
626
- border: `1px solid ${constants.C.border}`,
627
- backgroundColor: "#000"
628
- }
629
- },
630
- i
631
- );
632
- }
633
- return /* @__PURE__ */ jsxRuntime.jsxs(
634
- "a",
635
- {
636
- href: file.url || "#",
637
- target: "_blank",
638
- rel: "noopener noreferrer",
639
- style: {
640
- display: "inline-flex",
641
- alignItems: "center",
642
- gap: "4px",
643
- padding: "4px 10px",
644
- borderRadius: "4px",
645
- border: `1px solid ${constants.C.border}`,
646
- fontSize: "11px",
647
- fontWeight: 600,
648
- color: "#374151",
649
- textDecoration: "none",
650
- backgroundColor: constants.C.white
651
- },
652
- children: [
653
- "\u{1F4CE}",
654
- " ",
655
- file.filename || "Fichier"
656
- ]
657
- },
658
- i
659
- );
660
- }) }),
661
- editingMsg !== msg.id && !msg.fromChat && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "6px", display: "flex", gap: "12px", alignItems: "center" }, children: [
662
- /* @__PURE__ */ jsxRuntime.jsx(
663
- "button",
664
- {
665
- type: "button",
666
- onClick: () => handleEditStart(msg),
667
- style: { border: "none", background: "none", cursor: "pointer", fontSize: "11px", color: constants.C.textSecondary, padding: 0, fontWeight: 600, textDecoration: "underline" },
668
- children: "Modifier"
669
- }
670
- ),
671
- /* @__PURE__ */ jsxRuntime.jsx(
672
- "button",
673
- {
674
- type: "button",
675
- onClick: () => handleDelete(msg.id),
676
- disabled: deletingMsg === msg.id,
677
- style: { border: "none", background: "none", cursor: "pointer", fontSize: "11px", color: "#ef4444", padding: 0, fontWeight: 600, textDecoration: "underline", opacity: deletingMsg === msg.id ? 0.3 : 1 },
678
- children: "Supprimer"
679
- }
680
- ),
681
- msg.authorType === "admin" && !msg.isInternal && /* @__PURE__ */ jsxRuntime.jsx(
682
- "button",
683
- {
684
- type: "button",
685
- onClick: () => handleResend(msg.id),
686
- disabled: resendingMsg === msg.id,
687
- style: { border: "none", background: "none", cursor: "pointer", fontSize: "11px", color: "#2563eb", padding: 0, fontWeight: 600, textDecoration: "underline", opacity: resendingMsg === msg.id ? 0.3 : 1 },
688
- children: resendingMsg === msg.id ? "Envoi..." : resendSuccess === msg.id ? "Envoy\xE9 !" : "Renvoyer email"
689
- }
690
- )
691
- ] })
692
- ]
693
- }
694
- )
695
- ] })
696
- ] }, msg.id);
697
- })
698
- ] });
699
- })() }),
700
- clientTyping && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
701
- display: "flex",
702
- alignItems: "center",
703
- gap: 8,
704
- padding: "8px 12px",
705
- fontSize: 12,
706
- color: "#17807c",
707
- fontWeight: 500
708
- }, children: [
709
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", gap: 2 }, children: [
710
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "0ms" } }),
711
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "150ms" } }),
712
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "300ms" } })
713
- ] }),
714
- clientTypingName || "Client",
715
- " est en train d'\xE9crire..."
716
- ] }),
717
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "16px" }, children: [
718
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "6px", alignItems: "center", overflowX: "auto", paddingBottom: "8px", marginBottom: "6px", flexWrap: "wrap" }, children: [
719
- [
720
- "Bien re\xE7u, je regarde \xE7a !",
721
- "C'est corrig\xE9 !",
722
- "Pouvez-vous pr\xE9ciser ?",
723
- "Je reviens vers vous rapidement",
724
- "Pouvez-vous m'envoyer une capture d'\xE9cran ?"
725
- ].map((text) => /* @__PURE__ */ jsxRuntime.jsx(
726
- "button",
727
- {
728
- type: "button",
729
- onClick: () => {
730
- setReplyBody(text);
731
- const html = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
732
- setReplyHtml(html);
733
- if (replyEditorRef.current?.setContent) {
734
- replyEditorRef.current.setContent(html);
735
- }
736
- },
737
- onMouseEnter: (e) => {
738
- e.currentTarget.style.backgroundColor = "#f1f5f9";
739
- },
740
- onMouseLeave: (e) => {
741
- e.currentTarget.style.backgroundColor = "white";
742
- },
743
- style: {
744
- padding: "3px 10px",
745
- borderRadius: "14px",
746
- border: `1px solid ${constants.C.border}`,
747
- backgroundColor: "white",
748
- fontSize: "11px",
749
- fontWeight: 500,
750
- color: "#475569",
751
- cursor: "pointer",
752
- whiteSpace: "nowrap"
753
- },
754
- children: text
755
- },
756
- text
757
- )),
758
- features.ai && /* @__PURE__ */ jsxRuntime.jsx(
759
- "button",
760
- {
761
- onClick: handleAiSuggestReply,
762
- disabled: aiReplying || messages.length === 0,
763
- style: { ...constants.s.outlineBtn("#17807c", aiReplying || messages.length === 0), fontSize: "11px", padding: "3px 10px", borderRadius: "14px" },
764
- children: aiReplying ? "G\xE9n\xE9ration..." : "Suggestion IA"
765
- }
766
- ),
767
- features.ai && /* @__PURE__ */ jsxRuntime.jsx(
768
- RewriteDropdown.RewriteDropdown,
769
- {
770
- disabled: aiRewriting || !replyBody.trim(),
771
- loading: aiRewriting,
772
- onSelect: (style) => handleAiRewrite(style)
773
- }
774
- ),
775
- /* @__PURE__ */ jsxRuntime.jsx(
776
- CodeBlockInserter.CodeBlockInserter,
777
- {
778
- style: { ...constants.s.outlineBtn("#059669", false), fontSize: "11px", padding: "3px 10px", borderRadius: "14px" },
779
- onInsert: (block) => {
780
- const nb = replyBody ? replyBody + block : block;
781
- setReplyBody(nb);
782
- setReplyHtml(nb.replace(/\n/g, "<br/>"));
783
- replyEditorRef.current?.setContent(nb.replace(/\n/g, "<br/>"));
784
- }
785
- }
786
- ),
787
- features.canned && cannedResponses.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
788
- /* @__PURE__ */ jsxRuntime.jsxs("select", { onChange: handleCannedSelect, style: { ...constants.s.input, fontSize: "11px", padding: "3px 8px", fontWeight: 600 }, children: [
789
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "R\xE9ponse rapide..." }),
790
- cannedResponses.map((cr) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(cr.id), children: cr.title }, cr.id))
791
- ] }),
792
- /* @__PURE__ */ jsxRuntime.jsx(
793
- "span",
794
- {
795
- title: "Variables disponibles : {{client.firstName}}, {{client.lastName}}, {{client.company}}, {{client.email}}, {{ticket.number}}, {{ticket.subject}}, {{agent.name}}",
796
- style: { cursor: "help", fontSize: "13px", color: constants.C.textMuted },
797
- children: "\u24D8"
798
- }
799
- )
800
- ] })
801
- ] }),
802
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { border: `1px solid ${constants.C.border}`, borderRadius: "8px", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(
803
- "textarea",
804
- {
805
- value: replyBody,
806
- onChange: (e) => {
807
- const text = e.target.value;
808
- setReplyBody(text);
809
- setReplyHtml(text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n/g, "<br />"));
810
- sendAdminTyping();
811
- },
812
- placeholder: "\xC9crire une r\xE9ponse au client...",
813
- style: {
814
- width: "100%",
815
- minHeight: "120px",
816
- padding: "12px",
817
- border: "none",
818
- outline: "none",
819
- fontSize: "14px",
820
- lineHeight: 1.5,
821
- resize: "vertical",
822
- fontFamily: "inherit",
823
- color: "#374151",
824
- backgroundColor: "transparent",
825
- boxSizing: "border-box"
826
- }
827
- }
828
- ) }),
829
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "8px", display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" }, children: [
830
- /* @__PURE__ */ jsxRuntime.jsx(
831
- "input",
832
- {
833
- ref: fileInputRef,
834
- type: "file",
835
- multiple: true,
836
- onChange: handleReplyFileChange,
837
- style: { display: "none" },
838
- accept: "image/*,.pdf,.doc,.docx,.txt,.zip"
839
- }
840
- ),
841
- /* @__PURE__ */ jsxRuntime.jsx(
842
- "button",
843
- {
844
- type: "button",
845
- onClick: () => fileInputRef.current?.click(),
846
- style: { ...constants.s.ghostBtn("#6b7280"), fontSize: "12px", padding: "5px 10px" },
847
- children: "+ Pi\xE8ce jointe"
848
- }
849
- ),
850
- replyFiles.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: replyFiles.map((file, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...constants.s.badge("#f1f5f9", "#374151"), display: "inline-flex", alignItems: "center", gap: "4px" }, children: [
851
- "\u{1F4CE}",
852
- " ",
853
- file.name,
854
- /* @__PURE__ */ jsxRuntime.jsx(
855
- "button",
856
- {
857
- type: "button",
858
- onClick: () => setReplyFiles((prev) => prev.filter((_, idx) => idx !== i)),
859
- style: { border: "none", background: "none", color: "#ef4444", fontWeight: 700, cursor: "pointer", fontSize: "14px", lineHeight: 1 },
860
- children: "\xD7"
861
- }
862
- )
863
- ] }, i)) })
864
- ] }),
865
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "10px", flexWrap: "wrap", marginTop: "8px" }, children: [
866
- /* @__PURE__ */ jsxRuntime.jsxs(
867
- "select",
868
- {
869
- value: sendAsClient ? "client" : "admin",
870
- onChange: (e) => setSendAsClient(e.target.value === "client"),
871
- style: { ...constants.s.input, fontSize: "12px", padding: "6px 8px", fontWeight: 600 },
872
- children: [
873
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "admin", children: "En tant que : Support" }),
874
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "client", children: "En tant que : Client" })
875
- ]
876
- }
877
- ),
878
- /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: "5px", fontSize: "12px", cursor: "pointer", fontWeight: 600 }, children: [
879
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: isInternal, onChange: (e) => {
880
- setIsInternal(e.target.checked);
881
- if (e.target.checked) setNotifyClient(false);
882
- }, style: { width: "14px", height: "14px", accentColor: constants.C.amber } }),
883
- "Note interne"
884
- ] }),
885
- !isInternal && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: "5px", fontSize: "12px", cursor: "pointer", fontWeight: 600 }, children: [
886
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: notifyClient, onChange: (e) => setNotifyClient(e.target.checked), style: { width: "14px", height: "14px", accentColor: "#16a34a" } }),
887
- "Envoyer au client"
888
- ] }),
889
- /* @__PURE__ */ jsxRuntime.jsx("button", { "data-action": "send-reply", onClick: handleSendReply, disabled: sending || !replyBody.trim() && !replyHtml, style: { ...constants.s.btn(isInternal ? constants.C.amber : notifyClient ? "#16a34a" : constants.C.blue, sending || !replyBody.trim() && !replyHtml), fontSize: "13px", padding: "8px 20px", marginLeft: "auto" }, children: sending ? "Envoi..." : isInternal ? "Ajouter note" : notifyClient ? "Envoyer + Notifier" : "Sauvegarder" }),
890
- !isInternal && /* @__PURE__ */ jsxRuntime.jsx(
891
- "button",
892
- {
893
- onClick: () => setShowSchedule(!showSchedule),
894
- disabled: !replyBody.trim() && !replyHtml,
895
- style: { ...constants.s.outlineBtn("#17807c", !replyBody.trim() && !replyHtml), fontSize: "12px", padding: "8px 12px" },
896
- title: "Programmer l'envoi \xE0 une date/heure pr\xE9cise",
897
- children: "\u23F0"
898
- }
899
- )
900
- ] }),
901
- showSchedule && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center", marginTop: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "#eef8f7", border: "1px solid #b8dcda" }, children: [
902
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "12px", fontWeight: 600, color: "#17807c" }, children: "Programmer pour :" }),
903
- /* @__PURE__ */ jsxRuntime.jsx(
904
- "input",
905
- {
906
- type: "datetime-local",
907
- value: scheduleDate,
908
- onChange: (e) => setScheduleDate(e.target.value),
909
- min: (/* @__PURE__ */ new Date()).toISOString().slice(0, 16),
910
- style: { ...constants.s.input, fontSize: "12px", width: "auto" }
911
- }
912
- ),
913
- /* @__PURE__ */ jsxRuntime.jsx(
914
- "button",
915
- {
916
- onClick: handleScheduleReply,
917
- disabled: sending || !scheduleDate || !replyBody.trim() && !replyHtml,
918
- style: { ...constants.s.btn("#17807c", sending || !scheduleDate || !replyBody.trim() && !replyHtml), fontSize: "12px", padding: "6px 14px" },
919
- children: sending ? "..." : "\u23F0 Programmer"
920
- }
921
- ),
922
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setShowSchedule(false), style: { background: "none", border: "none", cursor: "pointer", color: "#94a3b8", fontSize: "14px" }, children: "\u2715" })
923
- ] }),
924
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "11px", color: constants.C.textMuted, marginTop: "4px", textAlign: "right" }, children: [
925
- "\u2318",
926
- "Enter pour envoyer \xB7 ",
927
- "\u2318",
928
- "\u21E7",
929
- "N note interne"
930
- ] })
931
- ] }),
932
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { borderTop: `1px solid ${constants.C.border}`, marginBottom: "16px" } }),
933
- /* @__PURE__ */ jsxRuntime.jsx(
934
- QuickActions.QuickActions,
935
- {
936
- statusTransitions,
937
- statusUpdating,
938
- onStatusChange: handleStatusChange,
939
- snoozeUntil,
940
- snoozeSaving,
941
- onCancelSnooze: () => handleSnooze(null),
942
- showMerge,
943
- showExtMsg,
944
- showSnooze,
945
- onToggleMerge: () => {
946
- setShowMerge(!showMerge);
947
- setShowExtMsg(false);
948
- setShowSnooze(false);
949
- setShowReminder(false);
950
- },
951
- onToggleExtMsg: () => {
952
- setShowExtMsg(!showExtMsg);
953
- setShowMerge(false);
954
- setShowSnooze(false);
955
- setShowReminder(false);
956
- },
957
- onToggleSnooze: () => {
958
- setShowSnooze(!showSnooze);
959
- setShowMerge(false);
960
- setShowExtMsg(false);
961
- setShowReminder(false);
962
- },
963
- onNextTicket: handleNextTicket,
964
- showReminderButton: features.autoClose,
965
- onToggleReminder: () => {
966
- setShowReminder(!showReminder);
967
- setReminderError("");
968
- setShowMerge(false);
969
- setShowExtMsg(false);
970
- setShowSnooze(false);
971
- },
972
- autoCloseScheduledAt,
973
- onCancelScheduledClose: handleCancelScheduledClose,
974
- cancelingClose,
975
- reminderSuccess,
976
- showNextTicket,
977
- nextTicketId,
978
- nextTicketInfo,
979
- onCloseNextTicket: () => setShowNextTicket(false)
980
- }
981
- ),
982
- features.ai && /* @__PURE__ */ jsxRuntime.jsx(
983
- AISummaryPanel.AISummaryPanel,
984
- {
985
- showAiSummary,
986
- setShowAiSummary,
987
- aiSummary,
988
- aiGenerating,
989
- aiSaving,
990
- aiSaved,
991
- handleAiGenerate,
992
- handleAiSave
993
- }
994
- ),
995
- features.merge && showMerge && /* @__PURE__ */ jsxRuntime.jsx(
996
- ActionPanels.MergePanel,
997
- {
998
- mergeTarget,
999
- setMergeTarget,
1000
- mergeTargetInfo,
1001
- setMergeTargetInfo,
1002
- mergeError,
1003
- setMergeError,
1004
- merging,
1005
- handleMergeLookup,
1006
- handleMerge
1007
- }
1008
- ),
1009
- features.externalMessages && showExtMsg && /* @__PURE__ */ jsxRuntime.jsx(
1010
- ActionPanels.ExtMessagePanel,
1011
- {
1012
- extMsgBody,
1013
- setExtMsgBody,
1014
- extMsgAuthor,
1015
- setExtMsgAuthor,
1016
- extMsgDate,
1017
- setExtMsgDate,
1018
- extMsgFiles,
1019
- setExtMsgFiles,
1020
- sendingExtMsg,
1021
- handleSendExtMsg,
1022
- handleExtFileChange
1023
- }
1024
- ),
1025
- features.snooze && showSnooze && /* @__PURE__ */ jsxRuntime.jsx(ActionPanels.SnoozePanel, { snoozeSaving, handleSnooze }),
1026
- features.autoClose && showReminder && /* @__PURE__ */ jsxRuntime.jsx(
1027
- ActionPanels.ReminderPanel,
1028
- {
1029
- clientFirstName: client?.firstName || "",
1030
- clientEmail: client?.email || "",
1031
- reminderHours,
1032
- setReminderHours,
1033
- reminderSending,
1034
- reminderError,
1035
- handleSendReminder
1036
- }
1037
- )
1038
- ] }),
1039
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.sideColumn, children: [
1040
- /* @__PURE__ */ jsxRuntime.jsx(
1041
- TimeTrackingPanel.TimeTrackingPanel,
1042
- {
1043
- timeEntries,
1044
- totalMinutes,
1045
- timerRunning,
1046
- timerSeconds,
1047
- setTimerSeconds,
1048
- timerDescription,
1049
- setTimerDescription,
1050
- handleTimerStart,
1051
- handleTimerStop,
1052
- handleTimerSave,
1053
- handleTimerDiscard,
1054
- duration,
1055
- setDuration,
1056
- timeDescription,
1057
- setTimeDescription,
1058
- handleAddTime,
1059
- addingTime,
1060
- timeSuccess
1061
- }
1062
- ),
1063
- features.clientHistory && client && /* @__PURE__ */ jsxRuntime.jsx(
1064
- ClientHistory.ClientHistory,
1065
- {
1066
- client,
1067
- clientTickets,
1068
- clientProjects,
1069
- clientNotes,
1070
- onNotesChange: (v) => {
1071
- setClientNotes(v);
1072
- setNotesSaved(false);
1073
- },
1074
- onNotesSave: async () => {
1075
- if (!client) return;
1076
- setSavingNotes(true);
1077
- try {
1078
- const res = await fetch(`/api/support-clients/${client.id}`, {
1079
- method: "PATCH",
1080
- headers: { "Content-Type": "application/json" },
1081
- credentials: "include",
1082
- body: JSON.stringify({ notes: clientNotes })
1083
- });
1084
- if (res.ok) {
1085
- setNotesSaved(true);
1086
- setTimeout(() => setNotesSaved(false), 3e3);
1087
- }
1088
- } catch {
1089
- } finally {
1090
- setSavingNotes(false);
1091
- }
1092
- },
1093
- savingNotes,
1094
- notesSaved
1095
- }
1096
- ),
1097
- features.activityLog && /* @__PURE__ */ jsxRuntime.jsx(ActivityLog.ActivityLog, { activityLog }),
1098
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: constants.s.section, children: /* @__PURE__ */ jsxRuntime.jsx(
1099
- "a",
1100
- {
1101
- href: "/api/support/export-csv",
1102
- target: "_blank",
1103
- rel: "noopener noreferrer",
1104
- style: { ...constants.s.ghostBtn("#6b7280"), fontSize: "12px", textDecoration: "none", display: "inline-block" },
1105
- children: "Exporter tous les tickets (CSV)"
1106
- }
1107
- ) })
1108
- ] })
1109
- ] })
1110
- ] });
1111
- };
1112
- var TicketConversation_default = TicketConversation;
1113
-
1114
- module.exports = TicketConversation_default;