@consilioweb/payload-support 0.15.0 → 1.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 (202) hide show
  1. package/README.md +347 -435
  2. package/dist/components/TicketConversation/RewriteDropdown.cjs +98 -0
  3. package/dist/components/TicketConversation/RewriteDropdown.js +93 -0
  4. package/dist/components/TicketConversation/SkeletonText.cjs +21 -0
  5. package/dist/components/TicketConversation/SkeletonText.js +20 -0
  6. package/dist/components/TicketConversation/components/ActionPanels.cjs +72 -0
  7. package/dist/components/TicketConversation/components/ActionPanels.js +73 -2
  8. package/dist/components/TicketConversation/components/QuickActions.cjs +22 -1
  9. package/dist/components/TicketConversation/components/QuickActions.js +22 -1
  10. package/dist/components/TicketConversation/constants.cjs +27 -0
  11. package/dist/components/TicketConversation/constants.js +26 -1
  12. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.cjs +20 -0
  13. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.js +19 -0
  14. package/dist/components/TicketConversation/hooks/useTicketActions.cjs +59 -0
  15. package/dist/components/TicketConversation/hooks/useTicketActions.js +59 -0
  16. package/dist/components/TicketConversation/index.cjs +42 -149
  17. package/dist/components/TicketConversation/index.js +37 -144
  18. package/dist/index.cjs +1835 -741
  19. package/dist/index.d.cts +3 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.js +1833 -741
  22. package/dist/views/BillingView/client.cjs +40 -54
  23. package/dist/views/BillingView/client.js +40 -54
  24. package/dist/views/ChatView/client.cjs +4 -3
  25. package/dist/views/ChatView/client.js +4 -3
  26. package/dist/views/CrmView/client.cjs +7 -7
  27. package/dist/views/CrmView/client.js +7 -7
  28. package/dist/views/EmailTrackingView/client.cjs +6 -5
  29. package/dist/views/EmailTrackingView/client.js +6 -5
  30. package/dist/views/ImportConversationView/client.cjs +4 -4
  31. package/dist/views/ImportConversationView/client.js +4 -4
  32. package/dist/views/LogsView/client.cjs +3 -2
  33. package/dist/views/LogsView/client.js +3 -2
  34. package/dist/views/NewTicketView/client.cjs +1 -1
  35. package/dist/views/NewTicketView/client.js +1 -1
  36. package/dist/views/PendingEmailsView/client.cjs +7 -6
  37. package/dist/views/PendingEmailsView/client.js +7 -6
  38. package/dist/views/SupportDashboardView/client.cjs +19 -22
  39. package/dist/views/SupportDashboardView/client.js +19 -22
  40. package/dist/views/TicketDetailView/NextActionItem.cjs +40 -0
  41. package/dist/views/TicketDetailView/NextActionItem.js +34 -0
  42. package/dist/views/TicketDetailView/RewriteDropdown.cjs +80 -0
  43. package/dist/views/TicketDetailView/RewriteDropdown.js +78 -0
  44. package/dist/views/TicketDetailView/client.cjs +59 -190
  45. package/dist/views/TicketDetailView/client.js +52 -183
  46. package/dist/views/TicketDetailView/constants.cjs +20 -0
  47. package/dist/views/TicketDetailView/constants.js +16 -0
  48. package/dist/views/TicketDetailView/helpers.cjs +23 -0
  49. package/dist/views/TicketDetailView/helpers.js +20 -0
  50. package/dist/views/TicketDetailView/types.cjs +2 -0
  51. package/dist/views/TicketDetailView/types.js +1 -0
  52. package/dist/views/TicketInboxView/client.cjs +14 -10
  53. package/dist/views/TicketInboxView/client.js +14 -10
  54. package/dist/views/TicketingSettingsView/client.cjs +101 -96
  55. package/dist/views/TicketingSettingsView/client.js +92 -88
  56. package/dist/views/TimeDashboardView/client.cjs +2 -2
  57. package/dist/views/TimeDashboardView/client.js +2 -2
  58. package/dist/views/shared/StatusPill.cjs +23 -12
  59. package/dist/views/shared/StatusPill.js +23 -12
  60. package/dist/views/shared/dateLocale.cjs +5 -0
  61. package/dist/views/shared/dateLocale.js +3 -0
  62. package/package.json +23 -15
  63. package/src/__tests__/generateTrackingToken.test.ts +53 -0
  64. package/src/__tests__/integration/ai-agent.test.ts +46 -0
  65. package/src/__tests__/integration/auth.test.ts +43 -0
  66. package/src/__tests__/integration/automation-rules.test.ts +68 -0
  67. package/src/__tests__/integration/buildTestPayload.ts +52 -0
  68. package/src/__tests__/integration/channels.test.ts +52 -0
  69. package/src/__tests__/integration/core-behaviors.test.ts +58 -0
  70. package/src/__tests__/integration/dashboard-volume.test.ts +31 -0
  71. package/src/__tests__/integration/digest.test.ts +48 -0
  72. package/src/__tests__/integration/invoice-pdf.test.ts +30 -0
  73. package/src/__tests__/integration/isolation.test.ts +81 -0
  74. package/src/__tests__/integration/mentions-invoice.test.ts +50 -0
  75. package/src/__tests__/integration/nps.test.ts +37 -0
  76. package/src/__tests__/integration/sanitization.test.ts +35 -0
  77. package/src/__tests__/integration/sla-pause.test.ts +39 -0
  78. package/src/__tests__/integration/smoke.test.ts +26 -0
  79. package/src/__tests__/integration/snooze.test.ts +68 -0
  80. package/src/__tests__/integration/teams.test.ts +38 -0
  81. package/src/__tests__/rateLimiter.test.ts +89 -0
  82. package/src/__tests__/sanitizeHtml.test.ts +165 -0
  83. package/src/__tests__/sla.test.ts +258 -0
  84. package/src/collections/AutomationRules.ts +66 -0
  85. package/src/collections/NotificationQueue.ts +30 -0
  86. package/src/collections/SatisfactionSurveys.ts +11 -2
  87. package/src/collections/SupportClients.ts +58 -1
  88. package/src/collections/SupportTeam.ts +27 -0
  89. package/src/collections/TicketActivityLog.ts +9 -4
  90. package/src/collections/TicketMessages.ts +137 -31
  91. package/src/collections/Tickets.ts +31 -72
  92. package/src/collections/index.ts +3 -0
  93. package/src/components/TicketConversation/RewriteDropdown.tsx +86 -0
  94. package/src/components/TicketConversation/SkeletonText.tsx +23 -0
  95. package/src/components/TicketConversation/components/ActionPanels.tsx +82 -1
  96. package/src/components/TicketConversation/components/QuickActions.tsx +25 -0
  97. package/src/components/TicketConversation/constants.ts +29 -0
  98. package/src/components/TicketConversation/hooks/useDocumentIdFromUrl.ts +21 -0
  99. package/src/components/TicketConversation/hooks/useTicketActions.ts +51 -0
  100. package/src/components/TicketConversation/index.tsx +29 -157
  101. package/src/endpoints/admin-chat-stream.ts +3 -4
  102. package/src/endpoints/admin-chat.ts +11 -20
  103. package/src/endpoints/admin-stats.ts +70 -32
  104. package/src/endpoints/ai-agent.ts +36 -0
  105. package/src/endpoints/apply-macro.ts +8 -14
  106. package/src/endpoints/auth-2fa.ts +12 -12
  107. package/src/endpoints/auto-close.ts +38 -18
  108. package/src/endpoints/billing.ts +17 -4
  109. package/src/endpoints/bulk-action.ts +7 -12
  110. package/src/endpoints/channels.ts +89 -0
  111. package/src/endpoints/chat-stream.ts +3 -4
  112. package/src/endpoints/chat.ts +7 -12
  113. package/src/endpoints/chatbot.ts +2 -2
  114. package/src/endpoints/client-intelligence.ts +4 -4
  115. package/src/endpoints/delete-account.ts +9 -16
  116. package/src/endpoints/email-stats.ts +2 -2
  117. package/src/endpoints/escalate.ts +2 -2
  118. package/src/endpoints/export-csv.ts +2 -2
  119. package/src/endpoints/export-data.ts +5 -8
  120. package/src/endpoints/import-conversation.ts +6 -10
  121. package/src/endpoints/index.ts +24 -2
  122. package/src/endpoints/invite-collaborator.ts +30 -15
  123. package/src/endpoints/invoice.ts +127 -0
  124. package/src/endpoints/kb-search.ts +2 -2
  125. package/src/endpoints/login.ts +12 -4
  126. package/src/endpoints/merge-clients.ts +14 -19
  127. package/src/endpoints/merge-tickets.ts +11 -18
  128. package/src/endpoints/oauth-google.ts +86 -39
  129. package/src/endpoints/pending-emails-process.ts +15 -25
  130. package/src/endpoints/process-digests.ts +93 -0
  131. package/src/endpoints/process-scheduled.ts +6 -10
  132. package/src/endpoints/process-snooze.ts +78 -0
  133. package/src/endpoints/purge-logs.ts +2 -2
  134. package/src/endpoints/resend-notification.ts +3 -4
  135. package/src/endpoints/round-robin-config.ts +3 -4
  136. package/src/endpoints/satisfaction.ts +21 -14
  137. package/src/endpoints/search.ts +5 -8
  138. package/src/endpoints/seed-kb.ts +3 -4
  139. package/src/endpoints/send-reminder.ts +186 -0
  140. package/src/endpoints/settings.ts +5 -2
  141. package/src/endpoints/signature.ts +3 -4
  142. package/src/endpoints/sla-check.ts +2 -2
  143. package/src/endpoints/split-ticket.ts +7 -12
  144. package/src/endpoints/statuses.ts +2 -2
  145. package/src/endpoints/ticket-feedback.ts +4 -6
  146. package/src/endpoints/ticket-synthesis.ts +2 -2
  147. package/src/endpoints/track-open.ts +16 -28
  148. package/src/endpoints/transfer-ticket.ts +21 -9
  149. package/src/endpoints/user-prefs.ts +3 -4
  150. package/src/hooks/automationRules.ts +84 -0
  151. package/src/hooks/checkSLA.ts +48 -16
  152. package/src/plugin.ts +6 -0
  153. package/src/portal/LiveChat.tsx +2 -63
  154. package/src/portal/auth/tickets/detail/CloseTicketButton.tsx +10 -0
  155. package/src/portal/auth/tickets/detail/MarkSolutionButton.tsx +14 -2
  156. package/src/portal/auth/tickets/detail/MessageActions.tsx +37 -18
  157. package/src/portal/auth/tickets/detail/ReopenTicketButton.tsx +10 -0
  158. package/src/portal/auth/tickets/detail/TransferAndInviteActions.tsx +3 -2
  159. package/src/portal/auth/tickets/detail/page.tsx +2 -2
  160. package/src/portal/helpers.ts +46 -0
  161. package/src/portal/login/page.tsx +3 -8
  162. package/src/portal/types.ts +15 -0
  163. package/src/types/lucide-react.d.ts +34 -0
  164. package/src/types/payload-config.d.ts +10 -0
  165. package/src/utils/adminNotification.ts +2 -2
  166. package/src/utils/aiAgent.ts +147 -0
  167. package/src/utils/auth.ts +5 -4
  168. package/src/utils/channels.ts +36 -0
  169. package/src/utils/db.ts +73 -0
  170. package/src/utils/emailTemplate.ts +8 -1
  171. package/src/utils/fireWebhooks.ts +4 -6
  172. package/src/utils/generateTicketSynthesis.ts +7 -12
  173. package/src/utils/invoicePdf.ts +88 -0
  174. package/src/utils/notificationQueue.ts +36 -0
  175. package/src/utils/readSettings.ts +22 -6
  176. package/src/utils/sanitizeHtml.ts +57 -0
  177. package/src/utils/slugs.ts +6 -0
  178. package/src/utils/teamAccess.ts +27 -0
  179. package/src/utils/ticketAccess.ts +53 -0
  180. package/src/utils/webhookDispatcher.ts +4 -6
  181. package/src/views/BillingView/client.tsx +49 -37
  182. package/src/views/ChatView/client.tsx +4 -3
  183. package/src/views/CrmView/client.tsx +7 -7
  184. package/src/views/EmailTrackingView/client.tsx +7 -6
  185. package/src/views/ImportConversationView/client.tsx +4 -4
  186. package/src/views/LogsView/client.tsx +3 -2
  187. package/src/views/NewTicketView/client.tsx +1 -1
  188. package/src/views/PendingEmailsView/client.tsx +7 -6
  189. package/src/views/SupportDashboardView/client.tsx +21 -21
  190. package/src/views/TicketDetailView/NextActionItem.tsx +30 -0
  191. package/src/views/TicketDetailView/RewriteDropdown.tsx +63 -0
  192. package/src/views/TicketDetailView/client.tsx +54 -179
  193. package/src/views/TicketDetailView/constants.ts +23 -0
  194. package/src/views/TicketDetailView/helpers.ts +17 -0
  195. package/src/views/TicketDetailView/types.ts +10 -0
  196. package/src/views/TicketInboxView/client.tsx +16 -10
  197. package/src/views/TicketingSettingsView/client.tsx +90 -87
  198. package/src/views/TimeDashboardView/client.tsx +2 -2
  199. package/src/views/shared/StatusPill.tsx +24 -12
  200. package/src/views/shared/dateLocale.ts +5 -0
  201. package/src/views/shared/locales/en.json +708 -0
  202. package/src/views/shared/locales/fr.json +708 -0
@@ -19,6 +19,12 @@ function useTicketActions(id, fetchAll) {
19
19
  const [showSnooze, setShowSnooze] = react.useState(false);
20
20
  const [snoozeUntil, setSnoozeUntil] = react.useState(null);
21
21
  const [snoozeSaving, setSnoozeSaving] = react.useState(false);
22
+ const [showReminder, setShowReminder] = react.useState(false);
23
+ const [reminderHours, setReminderHours] = react.useState(24);
24
+ const [reminderSending, setReminderSending] = react.useState(false);
25
+ const [reminderError, setReminderError] = react.useState("");
26
+ const [reminderSuccess, setReminderSuccess] = react.useState(false);
27
+ const [cancelingClose, setCancelingClose] = react.useState(false);
22
28
  const [showNextTicket, setShowNextTicket] = react.useState(false);
23
29
  const [nextTicketId, setNextTicketId] = react.useState(null);
24
30
  const [nextTicketInfo, setNextTicketInfo] = react.useState("");
@@ -170,6 +176,48 @@ function useTicketActions(id, fetchAll) {
170
176
  setSnoozeSaving(false);
171
177
  }
172
178
  };
179
+ const handleSendReminder = async () => {
180
+ if (!id) return;
181
+ setReminderSending(true);
182
+ setReminderError("");
183
+ try {
184
+ const res = await fetch("/api/support/send-reminder", {
185
+ method: "POST",
186
+ headers: { "Content-Type": "application/json" },
187
+ credentials: "include",
188
+ body: JSON.stringify({ ticketId: id, hours: reminderHours })
189
+ });
190
+ const d = await res.json().catch(() => ({}));
191
+ if (res.ok) {
192
+ setShowReminder(false);
193
+ setReminderSuccess(true);
194
+ setTimeout(() => setReminderSuccess(false), 5e3);
195
+ fetchAll();
196
+ } else {
197
+ setReminderError(d.error || "Erreur lors de la relance");
198
+ }
199
+ } catch {
200
+ setReminderError("Erreur r\xE9seau");
201
+ } finally {
202
+ setReminderSending(false);
203
+ }
204
+ };
205
+ const handleCancelScheduledClose = async () => {
206
+ if (!id) return;
207
+ setCancelingClose(true);
208
+ try {
209
+ await fetch(`/api/tickets/${id}`, {
210
+ method: "PATCH",
211
+ headers: { "Content-Type": "application/json" },
212
+ credentials: "include",
213
+ body: JSON.stringify({ autoCloseScheduledAt: null, autoCloseRemindedAt: null })
214
+ });
215
+ fetchAll();
216
+ } catch {
217
+ } finally {
218
+ setCancelingClose(false);
219
+ }
220
+ };
173
221
  const handleNextTicket = async () => {
174
222
  try {
175
223
  const res = await fetch("/api/tickets?where[status][equals]=open&sort=updatedAt&limit=1&depth=0", { credentials: "include" });
@@ -220,6 +268,17 @@ function useTicketActions(id, fetchAll) {
220
268
  setSnoozeUntil,
221
269
  snoozeSaving,
222
270
  handleSnooze,
271
+ showReminder,
272
+ setShowReminder,
273
+ reminderHours,
274
+ setReminderHours,
275
+ reminderSending,
276
+ reminderError,
277
+ setReminderError,
278
+ reminderSuccess,
279
+ handleSendReminder,
280
+ cancelingClose,
281
+ handleCancelScheduledClose,
223
282
  showNextTicket,
224
283
  setShowNextTicket,
225
284
  nextTicketId,
@@ -18,6 +18,12 @@ function useTicketActions(id, fetchAll) {
18
18
  const [showSnooze, setShowSnooze] = useState(false);
19
19
  const [snoozeUntil, setSnoozeUntil] = useState(null);
20
20
  const [snoozeSaving, setSnoozeSaving] = useState(false);
21
+ const [showReminder, setShowReminder] = useState(false);
22
+ const [reminderHours, setReminderHours] = useState(24);
23
+ const [reminderSending, setReminderSending] = useState(false);
24
+ const [reminderError, setReminderError] = useState("");
25
+ const [reminderSuccess, setReminderSuccess] = useState(false);
26
+ const [cancelingClose, setCancelingClose] = useState(false);
21
27
  const [showNextTicket, setShowNextTicket] = useState(false);
22
28
  const [nextTicketId, setNextTicketId] = useState(null);
23
29
  const [nextTicketInfo, setNextTicketInfo] = useState("");
@@ -169,6 +175,48 @@ function useTicketActions(id, fetchAll) {
169
175
  setSnoozeSaving(false);
170
176
  }
171
177
  };
178
+ const handleSendReminder = async () => {
179
+ if (!id) return;
180
+ setReminderSending(true);
181
+ setReminderError("");
182
+ try {
183
+ const res = await fetch("/api/support/send-reminder", {
184
+ method: "POST",
185
+ headers: { "Content-Type": "application/json" },
186
+ credentials: "include",
187
+ body: JSON.stringify({ ticketId: id, hours: reminderHours })
188
+ });
189
+ const d = await res.json().catch(() => ({}));
190
+ if (res.ok) {
191
+ setShowReminder(false);
192
+ setReminderSuccess(true);
193
+ setTimeout(() => setReminderSuccess(false), 5e3);
194
+ fetchAll();
195
+ } else {
196
+ setReminderError(d.error || "Erreur lors de la relance");
197
+ }
198
+ } catch {
199
+ setReminderError("Erreur r\xE9seau");
200
+ } finally {
201
+ setReminderSending(false);
202
+ }
203
+ };
204
+ const handleCancelScheduledClose = async () => {
205
+ if (!id) return;
206
+ setCancelingClose(true);
207
+ try {
208
+ await fetch(`/api/tickets/${id}`, {
209
+ method: "PATCH",
210
+ headers: { "Content-Type": "application/json" },
211
+ credentials: "include",
212
+ body: JSON.stringify({ autoCloseScheduledAt: null, autoCloseRemindedAt: null })
213
+ });
214
+ fetchAll();
215
+ } catch {
216
+ } finally {
217
+ setCancelingClose(false);
218
+ }
219
+ };
172
220
  const handleNextTicket = async () => {
173
221
  try {
174
222
  const res = await fetch("/api/tickets?where[status][equals]=open&sort=updatedAt&limit=1&depth=0", { credentials: "include" });
@@ -219,6 +267,17 @@ function useTicketActions(id, fetchAll) {
219
267
  setSnoozeUntil,
220
268
  snoozeSaving,
221
269
  handleSnooze,
270
+ showReminder,
271
+ setShowReminder,
272
+ reminderHours,
273
+ setReminderHours,
274
+ reminderSending,
275
+ reminderError,
276
+ setReminderError,
277
+ reminderSuccess,
278
+ handleSendReminder,
279
+ cancelingClose,
280
+ handleCancelScheduledClose,
222
281
  showNextTicket,
223
282
  setShowNextTicket,
224
283
  nextTicketId,
@@ -19,155 +19,18 @@ var useMessageActions = require('./hooks/useMessageActions');
19
19
  var useTicketActions = require('./hooks/useTicketActions');
20
20
  var useReply = require('./hooks/useReply');
21
21
  var useAI = require('./hooks/useAI');
22
+ var useDocumentIdFromUrl = require('./hooks/useDocumentIdFromUrl');
22
23
  var config = require('./config');
24
+ var SkeletonText = require('./SkeletonText');
25
+ var RewriteDropdown = require('./RewriteDropdown');
23
26
  require('../../styles/theme.css');
24
27
 
25
28
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
26
29
 
27
30
  var React__default = /*#__PURE__*/_interopDefault(React);
28
31
 
29
- function useDocumentIdFromUrl() {
30
- const [id, setId] = React.useState(void 0);
31
- React.useEffect(() => {
32
- const match = window.location.pathname.match(
33
- /\/admin\/collections\/[^/]+\/([^/?#]+)/
34
- );
35
- if (match && match[1] !== "create") {
36
- const raw = match[1];
37
- const num = Number(raw);
38
- setId(Number.isFinite(num) && String(num) === raw ? num : raw);
39
- }
40
- }, []);
41
- return { id };
42
- }
43
- function SkeletonText({ lines = 3 }) {
44
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "8px", padding: "12px 0" }, children: Array.from({ length: lines }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
45
- "div",
46
- {
47
- style: {
48
- height: "14px",
49
- borderRadius: "4px",
50
- backgroundColor: "#e2e8f0",
51
- width: i === lines - 1 ? "60%" : "100%",
52
- animation: "pulse 1.5s ease-in-out infinite"
53
- }
54
- },
55
- i
56
- )) });
57
- }
58
- const layoutStyles = {
59
- root: { padding: "12px 0" },
60
- twoColumns: {
61
- display: "grid",
62
- gridTemplateColumns: "1fr 320px",
63
- gap: "16px",
64
- alignItems: "start"
65
- },
66
- mainColumn: { minWidth: 0 },
67
- sideColumn: {
68
- display: "flex",
69
- flexDirection: "column",
70
- gap: "12px",
71
- position: "sticky",
72
- top: "80px"
73
- }
74
- };
75
- const REWRITE_STYLES = [
76
- { id: "auto", label: "\u270F\uFE0F Auto", desc: "Garde le ton actuel" },
77
- { id: "tutoyer", label: "\u{1F44B} Tutoyer", desc: "Passe en tu" },
78
- { id: "vouvoyer", label: "\u{1F3A9} Vouvoyer", desc: "Passe en vous" },
79
- { id: "formel", label: "\u{1F4BC} Formel", desc: "Ton professionnel" },
80
- { id: "amical", label: "\u{1F60A} Amical", desc: "Ton chaleureux" },
81
- { id: "court", label: "\u26A1 Court", desc: "Version concise" }
82
- ];
83
- const RewriteDropdown = ({ disabled, loading, onSelect }) => {
84
- const [open, setOpen] = React.useState(false);
85
- const ref = React__default.default.useRef(null);
86
- React__default.default.useEffect(() => {
87
- if (!open) return;
88
- const close = (e) => {
89
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
90
- };
91
- document.addEventListener("mousedown", close);
92
- return () => document.removeEventListener("mousedown", close);
93
- }, [open]);
94
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, style: { position: "relative", display: "inline-block" }, children: [
95
- /* @__PURE__ */ jsxRuntime.jsxs(
96
- "button",
97
- {
98
- type: "button",
99
- onClick: () => setOpen(!open),
100
- disabled,
101
- style: {
102
- ...constants.s.outlineBtn("#0891b2", disabled),
103
- fontSize: "11px",
104
- padding: "3px 10px",
105
- borderRadius: "14px",
106
- display: "flex",
107
- alignItems: "center",
108
- gap: "4px"
109
- },
110
- children: [
111
- loading ? "Reformulation..." : "\u270F\uFE0F Reformuler",
112
- !loading && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 9, opacity: 0.7 }, children: "\u25BC" })
113
- ]
114
- }
115
- ),
116
- open && !disabled && !loading && /* @__PURE__ */ jsxRuntime.jsx(
117
- "div",
118
- {
119
- style: {
120
- position: "absolute",
121
- bottom: "100%",
122
- left: 0,
123
- marginBottom: 4,
124
- background: "var(--theme-elevation-0, #fff)",
125
- border: "1px solid var(--theme-elevation-200, #e5e7eb)",
126
- borderRadius: 8,
127
- boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
128
- zIndex: 100,
129
- minWidth: 180,
130
- overflow: "hidden"
131
- },
132
- children: REWRITE_STYLES.map((style) => /* @__PURE__ */ jsxRuntime.jsxs(
133
- "button",
134
- {
135
- type: "button",
136
- onClick: () => {
137
- setOpen(false);
138
- onSelect(style.id);
139
- },
140
- style: {
141
- display: "flex",
142
- flexDirection: "column",
143
- width: "100%",
144
- padding: "8px 12px",
145
- border: "none",
146
- background: "transparent",
147
- cursor: "pointer",
148
- textAlign: "left",
149
- borderBottom: "1px solid var(--theme-elevation-100, #f3f4f6)",
150
- transition: "background 120ms"
151
- },
152
- onMouseEnter: (e) => {
153
- e.target.style.background = "var(--theme-elevation-50, #f9fafb)";
154
- },
155
- onMouseLeave: (e) => {
156
- e.target.style.background = "transparent";
157
- },
158
- children: [
159
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, fontWeight: 600, color: "var(--theme-text, #111)" }, children: style.label }),
160
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, color: "var(--theme-elevation-500, #6b7280)" }, children: style.desc })
161
- ]
162
- },
163
- style.id
164
- ))
165
- }
166
- )
167
- ] });
168
- };
169
32
  const TicketConversation = () => {
170
- const { id } = useDocumentIdFromUrl();
33
+ const { id } = useDocumentIdFromUrl.useDocumentIdFromUrl();
171
34
  const [features] = React.useState(() => config.getFeatures());
172
35
  const [messages, setMessages] = React.useState([]);
173
36
  const [timeEntries, setTimeEntries] = React.useState([]);
@@ -188,6 +51,7 @@ const TicketConversation = () => {
188
51
  const [ticketSubject, setTicketSubject] = React.useState("");
189
52
  const [ticketSource, setTicketSource] = React.useState("");
190
53
  const [chatSession, setChatSession] = React.useState("");
54
+ const [autoCloseScheduledAt, setAutoCloseScheduledAt] = React.useState(null);
191
55
  const [clientTickets, setClientTickets] = React.useState([]);
192
56
  const [clientProjects, setClientProjects] = React.useState([]);
193
57
  const [clientNotes, setClientNotes] = React.useState("");
@@ -220,6 +84,7 @@ const TicketConversation = () => {
220
84
  setClient(d.client);
221
85
  }
222
86
  setSnoozeUntil(d.snoozeUntil || null);
87
+ setAutoCloseScheduledAt(d.autoCloseScheduledAt || null);
223
88
  setLastClientReadAt(d.lastClientReadAt || null);
224
89
  setCurrentStatus(d.status || "");
225
90
  setTicketNumber(d.ticketNumber || "");
@@ -364,7 +229,7 @@ const TicketConversation = () => {
364
229
  const ma = useMessageActions.useMessageActions(id, client, fetchAll);
365
230
  const { togglingAuthor, editingMsg, editBody, editHtml, setEditHtml, savingEdit, handleEditStart, handleEditSave, handleEditCancel, deletingMsg, handleDelete, resendingMsg, resendSuccess, handleResend, handleToggleAuthor, handleSplitMessage, setEditBody } = ma;
366
231
  const ta = useTicketActions.useTicketActions(id, fetchAll);
367
- 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, showNextTicket, setShowNextTicket, nextTicketId, nextTicketInfo, handleNextTicket } = ta;
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;
368
233
  const replyEditorRef = React.useRef(null);
369
234
  const rp = useReply.useReply(id, client, cannedResponses, ticketNumber, ticketSubject, fetchAll, handleNextTicket, replyEditorRef);
370
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;
@@ -393,6 +258,7 @@ const TicketConversation = () => {
393
258
  const d = await ticketRes.json();
394
259
  setCurrentStatus(d.status || "");
395
260
  setSnoozeUntil(d.snoozeUntil || null);
261
+ setAutoCloseScheduledAt(d.autoCloseScheduledAt || null);
396
262
  setLastClientReadAt(d.lastClientReadAt || null);
397
263
  }
398
264
  if (activityRes.ok) {
@@ -480,7 +346,7 @@ const TicketConversation = () => {
480
346
  ];
481
347
  }
482
348
  })();
483
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: layoutStyles.root, children: [
349
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.root, children: [
484
350
  /* @__PURE__ */ jsxRuntime.jsx(
485
351
  TicketHeader.TicketHeader,
486
352
  {
@@ -496,8 +362,8 @@ const TicketConversation = () => {
496
362
  }
497
363
  ),
498
364
  client && /* @__PURE__ */ jsxRuntime.jsx(ClientBar.ClientBar, { client }),
499
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: layoutStyles.twoColumns, children: [
500
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: layoutStyles.mainColumn, children: [
365
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.twoColumns, children: [
366
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.mainColumn, children: [
501
367
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "4px", display: "flex", alignItems: "center", gap: "10px" }, children: [
502
368
  /* @__PURE__ */ jsxRuntime.jsxs("h3", { style: { fontSize: "14px", fontWeight: 600, margin: 0, display: "flex", alignItems: "center", gap: "8px" }, children: [
503
369
  "Conversation ",
@@ -514,7 +380,7 @@ const TicketConversation = () => {
514
380
  }
515
381
  ) })
516
382
  ] }),
517
- loading ? /* @__PURE__ */ jsxRuntime.jsx(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: (() => {
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: (() => {
518
384
  const filtered = messages.filter((msg) => !searchQuery.trim() || msg.body.toLowerCase().includes(searchQuery.toLowerCase()));
519
385
  const isSearching = searchQuery.trim().length > 0;
520
386
  const VISIBLE_COUNT = 3;
@@ -899,7 +765,7 @@ const TicketConversation = () => {
899
765
  }
900
766
  ),
901
767
  features.ai && /* @__PURE__ */ jsxRuntime.jsx(
902
- RewriteDropdown,
768
+ RewriteDropdown.RewriteDropdown,
903
769
  {
904
770
  disabled: aiRewriting || !replyBody.trim(),
905
771
  loading: aiRewriting,
@@ -1080,18 +946,33 @@ const TicketConversation = () => {
1080
946
  setShowMerge(!showMerge);
1081
947
  setShowExtMsg(false);
1082
948
  setShowSnooze(false);
949
+ setShowReminder(false);
1083
950
  },
1084
951
  onToggleExtMsg: () => {
1085
952
  setShowExtMsg(!showExtMsg);
1086
953
  setShowMerge(false);
1087
954
  setShowSnooze(false);
955
+ setShowReminder(false);
1088
956
  },
1089
957
  onToggleSnooze: () => {
1090
958
  setShowSnooze(!showSnooze);
1091
959
  setShowMerge(false);
1092
960
  setShowExtMsg(false);
961
+ setShowReminder(false);
1093
962
  },
1094
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,
1095
976
  showNextTicket,
1096
977
  nextTicketId,
1097
978
  nextTicketInfo,
@@ -1141,9 +1022,21 @@ const TicketConversation = () => {
1141
1022
  handleExtFileChange
1142
1023
  }
1143
1024
  ),
1144
- features.snooze && showSnooze && /* @__PURE__ */ jsxRuntime.jsx(ActionPanels.SnoozePanel, { snoozeSaving, handleSnooze })
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
+ )
1145
1038
  ] }),
1146
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: layoutStyles.sideColumn, children: [
1039
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: constants.layoutStyles.sideColumn, children: [
1147
1040
  /* @__PURE__ */ jsxRuntime.jsx(
1148
1041
  TimeTrackingPanel.TimeTrackingPanel,
1149
1042
  {