@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
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var constants = require('./constants');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefault(React);
10
+
11
+ const RewriteDropdown = ({ disabled, loading, onSelect }) => {
12
+ const [open, setOpen] = React.useState(false);
13
+ const ref = React__default.default.useRef(null);
14
+ React__default.default.useEffect(() => {
15
+ if (!open) return;
16
+ const close = (e) => {
17
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
18
+ };
19
+ document.addEventListener("mousedown", close);
20
+ return () => document.removeEventListener("mousedown", close);
21
+ }, [open]);
22
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, style: { position: "relative", display: "inline-block" }, children: [
23
+ /* @__PURE__ */ jsxRuntime.jsxs(
24
+ "button",
25
+ {
26
+ type: "button",
27
+ onClick: () => setOpen(!open),
28
+ disabled,
29
+ style: {
30
+ ...constants.s.outlineBtn("#0891b2", disabled),
31
+ fontSize: "11px",
32
+ padding: "3px 10px",
33
+ borderRadius: "14px",
34
+ display: "flex",
35
+ alignItems: "center",
36
+ gap: "4px"
37
+ },
38
+ children: [
39
+ loading ? "Reformulation..." : "\u270F\uFE0F Reformuler",
40
+ !loading && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 9, opacity: 0.7 }, children: "\u25BC" })
41
+ ]
42
+ }
43
+ ),
44
+ open && !disabled && !loading && /* @__PURE__ */ jsxRuntime.jsx(
45
+ "div",
46
+ {
47
+ style: {
48
+ position: "absolute",
49
+ bottom: "100%",
50
+ left: 0,
51
+ marginBottom: 4,
52
+ background: "var(--theme-elevation-0, #fff)",
53
+ border: "1px solid var(--theme-elevation-200, #e5e7eb)",
54
+ borderRadius: 8,
55
+ boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
56
+ zIndex: 100,
57
+ minWidth: 180,
58
+ overflow: "hidden"
59
+ },
60
+ children: constants.REWRITE_STYLES.map((style) => /* @__PURE__ */ jsxRuntime.jsxs(
61
+ "button",
62
+ {
63
+ type: "button",
64
+ onClick: () => {
65
+ setOpen(false);
66
+ onSelect(style.id);
67
+ },
68
+ style: {
69
+ display: "flex",
70
+ flexDirection: "column",
71
+ width: "100%",
72
+ padding: "8px 12px",
73
+ border: "none",
74
+ background: "transparent",
75
+ cursor: "pointer",
76
+ textAlign: "left",
77
+ borderBottom: "1px solid var(--theme-elevation-100, #f3f4f6)",
78
+ transition: "background 120ms"
79
+ },
80
+ onMouseEnter: (e) => {
81
+ e.target.style.background = "var(--theme-elevation-50, #f9fafb)";
82
+ },
83
+ onMouseLeave: (e) => {
84
+ e.target.style.background = "transparent";
85
+ },
86
+ children: [
87
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, fontWeight: 600, color: "var(--theme-text, #111)" }, children: style.label }),
88
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, color: "var(--theme-elevation-500, #6b7280)" }, children: style.desc })
89
+ ]
90
+ },
91
+ style.id
92
+ ))
93
+ }
94
+ )
95
+ ] });
96
+ };
97
+
98
+ exports.RewriteDropdown = RewriteDropdown;
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import React, { useState } from 'react';
4
+ import { s, REWRITE_STYLES } from './constants.js';
5
+
6
+ const RewriteDropdown = ({ disabled, loading, onSelect }) => {
7
+ const [open, setOpen] = useState(false);
8
+ const ref = React.useRef(null);
9
+ React.useEffect(() => {
10
+ if (!open) return;
11
+ const close = (e) => {
12
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
13
+ };
14
+ document.addEventListener("mousedown", close);
15
+ return () => document.removeEventListener("mousedown", close);
16
+ }, [open]);
17
+ return /* @__PURE__ */ jsxs("div", { ref, style: { position: "relative", display: "inline-block" }, children: [
18
+ /* @__PURE__ */ jsxs(
19
+ "button",
20
+ {
21
+ type: "button",
22
+ onClick: () => setOpen(!open),
23
+ disabled,
24
+ style: {
25
+ ...s.outlineBtn("#0891b2", disabled),
26
+ fontSize: "11px",
27
+ padding: "3px 10px",
28
+ borderRadius: "14px",
29
+ display: "flex",
30
+ alignItems: "center",
31
+ gap: "4px"
32
+ },
33
+ children: [
34
+ loading ? "Reformulation..." : "\u270F\uFE0F Reformuler",
35
+ !loading && /* @__PURE__ */ jsx("span", { style: { fontSize: 9, opacity: 0.7 }, children: "\u25BC" })
36
+ ]
37
+ }
38
+ ),
39
+ open && !disabled && !loading && /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ style: {
43
+ position: "absolute",
44
+ bottom: "100%",
45
+ left: 0,
46
+ marginBottom: 4,
47
+ background: "var(--theme-elevation-0, #fff)",
48
+ border: "1px solid var(--theme-elevation-200, #e5e7eb)",
49
+ borderRadius: 8,
50
+ boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
51
+ zIndex: 100,
52
+ minWidth: 180,
53
+ overflow: "hidden"
54
+ },
55
+ children: REWRITE_STYLES.map((style) => /* @__PURE__ */ jsxs(
56
+ "button",
57
+ {
58
+ type: "button",
59
+ onClick: () => {
60
+ setOpen(false);
61
+ onSelect(style.id);
62
+ },
63
+ style: {
64
+ display: "flex",
65
+ flexDirection: "column",
66
+ width: "100%",
67
+ padding: "8px 12px",
68
+ border: "none",
69
+ background: "transparent",
70
+ cursor: "pointer",
71
+ textAlign: "left",
72
+ borderBottom: "1px solid var(--theme-elevation-100, #f3f4f6)",
73
+ transition: "background 120ms"
74
+ },
75
+ onMouseEnter: (e) => {
76
+ e.target.style.background = "var(--theme-elevation-50, #f9fafb)";
77
+ },
78
+ onMouseLeave: (e) => {
79
+ e.target.style.background = "transparent";
80
+ },
81
+ children: [
82
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, fontWeight: 600, color: "var(--theme-text, #111)" }, children: style.label }),
83
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 10, color: "var(--theme-elevation-500, #6b7280)" }, children: style.desc })
84
+ ]
85
+ },
86
+ style.id
87
+ ))
88
+ }
89
+ )
90
+ ] });
91
+ };
92
+
93
+ export { RewriteDropdown };
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+
5
+ function SkeletonText({ lines = 3 }) {
6
+ 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(
7
+ "div",
8
+ {
9
+ style: {
10
+ height: "14px",
11
+ borderRadius: "4px",
12
+ backgroundColor: "#e2e8f0",
13
+ width: i === lines - 1 ? "60%" : "100%",
14
+ animation: "pulse 1.5s ease-in-out infinite"
15
+ }
16
+ },
17
+ i
18
+ )) });
19
+ }
20
+
21
+ exports.SkeletonText = SkeletonText;
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ function SkeletonText({ lines = 3 }) {
5
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "8px", padding: "12px 0" }, children: Array.from({ length: lines }).map((_, i) => /* @__PURE__ */ jsx(
6
+ "div",
7
+ {
8
+ style: {
9
+ height: "14px",
10
+ borderRadius: "4px",
11
+ backgroundColor: "#e2e8f0",
12
+ width: i === lines - 1 ? "60%" : "100%",
13
+ animation: "pulse 1.5s ease-in-out infinite"
14
+ }
15
+ },
16
+ i
17
+ )) });
18
+ }
19
+
20
+ export { SkeletonText };
@@ -95,6 +95,77 @@ function ExtMessagePanel({
95
95
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleSendExtMsg, disabled: sendingExtMsg || !extMsgBody.trim(), style: constants.s.btn("#818cf8", sendingExtMsg || !extMsgBody.trim()), children: sendingExtMsg ? "Ajout..." : "Ajouter (sans notification)" })
96
96
  ] });
97
97
  }
98
+ const REMINDER_DELAYS = [
99
+ { hours: 24, label: "24 h" },
100
+ { hours: 48, label: "48 h" },
101
+ { hours: 72, label: "72 h" }
102
+ ];
103
+ function ReminderPanel({
104
+ clientFirstName,
105
+ clientEmail,
106
+ reminderHours,
107
+ setReminderHours,
108
+ reminderSending,
109
+ reminderError,
110
+ handleSendReminder
111
+ }) {
112
+ const deadline = new Date(Date.now() + reminderHours * 60 * 60 * 1e3);
113
+ const deadlineLabel = deadline.toLocaleString("fr-FR", {
114
+ weekday: "long",
115
+ day: "numeric",
116
+ month: "long",
117
+ hour: "2-digit",
118
+ minute: "2-digit"
119
+ });
120
+ const name = clientFirstName?.trim() || "le client";
121
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#fff7ed", border: "1px solid #fed7aa", marginBottom: "14px" }, children: [
122
+ /* @__PURE__ */ jsxRuntime.jsxs("h4", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "8px", color: "#9a3412" }, children: [
123
+ "Relancer ",
124
+ name,
125
+ " \u2014 fermeture automatique sans r\xE9ponse"
126
+ ] }),
127
+ clientEmail ? /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "12px", color: "#7c2d12", margin: "0 0 12px 0", lineHeight: 1.5 }, children: [
128
+ "Un email de relance g\xE9n\xE9rique sera envoy\xE9 \xE0 ",
129
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: clientEmail }),
130
+ ". Sans retour de sa part avant l'\xE9ch\xE9ance, le ticket sera automatiquement r\xE9solu (via le cron auto-close)."
131
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "12px", color: "#b91c1c", margin: "0 0 12px 0", fontWeight: 600 }, children: "Ce client n'a pas d'adresse email : impossible d'envoyer une relance." }),
132
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "6px", alignItems: "center", flexWrap: "wrap", marginBottom: "12px" }, children: [
133
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "12px", fontWeight: 600, color: "#9a3412" }, children: "Fermer dans :" }),
134
+ REMINDER_DELAYS.map((d) => {
135
+ const active = reminderHours === d.hours;
136
+ return /* @__PURE__ */ jsxRuntime.jsx(
137
+ "button",
138
+ {
139
+ type: "button",
140
+ onClick: () => setReminderHours(d.hours),
141
+ style: {
142
+ ...active ? constants.s.btn(constants.C.orange) : constants.s.outlineBtn("#ea580c"),
143
+ fontSize: "12px",
144
+ padding: "6px 14px"
145
+ },
146
+ children: d.label
147
+ },
148
+ d.hours
149
+ );
150
+ })
151
+ ] }),
152
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "12px", color: "#7c2d12", marginBottom: "12px" }, children: [
153
+ "Fermeture automatique pr\xE9vue le ",
154
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: deadlineLabel }),
155
+ " sans r\xE9ponse."
156
+ ] }),
157
+ reminderError && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "12px", color: "#b91c1c", fontWeight: 600, marginBottom: "10px" }, children: reminderError }),
158
+ /* @__PURE__ */ jsxRuntime.jsx(
159
+ "button",
160
+ {
161
+ onClick: handleSendReminder,
162
+ disabled: reminderSending || !clientEmail,
163
+ style: { ...constants.s.btn(constants.C.orange, reminderSending || !clientEmail), color: "#fff", fontSize: "13px", padding: "8px 18px" },
164
+ children: reminderSending ? "Envoi..." : "Confirmer la relance"
165
+ }
166
+ )
167
+ ] });
168
+ }
98
169
  function SnoozePanel({ snoozeSaving, handleSnooze }) {
99
170
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#faf5ff", border: "1px solid #e9d5ff", marginBottom: "14px" }, children: [
100
171
  /* @__PURE__ */ jsxRuntime.jsx("h4", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "10px", color: "#5b21b6" }, children: "Snooze \u2014 masquer temporairement" }),
@@ -118,4 +189,5 @@ function SnoozePanel({ snoozeSaving, handleSnooze }) {
118
189
 
119
190
  exports.ExtMessagePanel = ExtMessagePanel;
120
191
  exports.MergePanel = MergePanel;
192
+ exports.ReminderPanel = ReminderPanel;
121
193
  exports.SnoozePanel = SnoozePanel;
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useRef } from 'react';
4
- import { s } from '../constants.js';
4
+ import { s, C } from '../constants.js';
5
5
 
6
6
  function MergePanel({
7
7
  mergeTarget,
@@ -94,6 +94,77 @@ function ExtMessagePanel({
94
94
  /* @__PURE__ */ jsx("button", { onClick: handleSendExtMsg, disabled: sendingExtMsg || !extMsgBody.trim(), style: s.btn("#818cf8", sendingExtMsg || !extMsgBody.trim()), children: sendingExtMsg ? "Ajout..." : "Ajouter (sans notification)" })
95
95
  ] });
96
96
  }
97
+ const REMINDER_DELAYS = [
98
+ { hours: 24, label: "24 h" },
99
+ { hours: 48, label: "48 h" },
100
+ { hours: 72, label: "72 h" }
101
+ ];
102
+ function ReminderPanel({
103
+ clientFirstName,
104
+ clientEmail,
105
+ reminderHours,
106
+ setReminderHours,
107
+ reminderSending,
108
+ reminderError,
109
+ handleSendReminder
110
+ }) {
111
+ const deadline = new Date(Date.now() + reminderHours * 60 * 60 * 1e3);
112
+ const deadlineLabel = deadline.toLocaleString("fr-FR", {
113
+ weekday: "long",
114
+ day: "numeric",
115
+ month: "long",
116
+ hour: "2-digit",
117
+ minute: "2-digit"
118
+ });
119
+ const name = clientFirstName?.trim() || "le client";
120
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#fff7ed", border: "1px solid #fed7aa", marginBottom: "14px" }, children: [
121
+ /* @__PURE__ */ jsxs("h4", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "8px", color: "#9a3412" }, children: [
122
+ "Relancer ",
123
+ name,
124
+ " \u2014 fermeture automatique sans r\xE9ponse"
125
+ ] }),
126
+ clientEmail ? /* @__PURE__ */ jsxs("p", { style: { fontSize: "12px", color: "#7c2d12", margin: "0 0 12px 0", lineHeight: 1.5 }, children: [
127
+ "Un email de relance g\xE9n\xE9rique sera envoy\xE9 \xE0 ",
128
+ /* @__PURE__ */ jsx("strong", { children: clientEmail }),
129
+ ". Sans retour de sa part avant l'\xE9ch\xE9ance, le ticket sera automatiquement r\xE9solu (via le cron auto-close)."
130
+ ] }) : /* @__PURE__ */ jsx("p", { style: { fontSize: "12px", color: "#b91c1c", margin: "0 0 12px 0", fontWeight: 600 }, children: "Ce client n'a pas d'adresse email : impossible d'envoyer une relance." }),
131
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px", alignItems: "center", flexWrap: "wrap", marginBottom: "12px" }, children: [
132
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", fontWeight: 600, color: "#9a3412" }, children: "Fermer dans :" }),
133
+ REMINDER_DELAYS.map((d) => {
134
+ const active = reminderHours === d.hours;
135
+ return /* @__PURE__ */ jsx(
136
+ "button",
137
+ {
138
+ type: "button",
139
+ onClick: () => setReminderHours(d.hours),
140
+ style: {
141
+ ...active ? s.btn(C.orange) : s.outlineBtn("#ea580c"),
142
+ fontSize: "12px",
143
+ padding: "6px 14px"
144
+ },
145
+ children: d.label
146
+ },
147
+ d.hours
148
+ );
149
+ })
150
+ ] }),
151
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: "12px", color: "#7c2d12", marginBottom: "12px" }, children: [
152
+ "Fermeture automatique pr\xE9vue le ",
153
+ /* @__PURE__ */ jsx("strong", { children: deadlineLabel }),
154
+ " sans r\xE9ponse."
155
+ ] }),
156
+ reminderError && /* @__PURE__ */ jsx("div", { style: { fontSize: "12px", color: "#b91c1c", fontWeight: 600, marginBottom: "10px" }, children: reminderError }),
157
+ /* @__PURE__ */ jsx(
158
+ "button",
159
+ {
160
+ onClick: handleSendReminder,
161
+ disabled: reminderSending || !clientEmail,
162
+ style: { ...s.btn(C.orange, reminderSending || !clientEmail), color: "#fff", fontSize: "13px", padding: "8px 18px" },
163
+ children: reminderSending ? "Envoi..." : "Confirmer la relance"
164
+ }
165
+ )
166
+ ] });
167
+ }
97
168
  function SnoozePanel({ snoozeSaving, handleSnooze }) {
98
169
  return /* @__PURE__ */ jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#faf5ff", border: "1px solid #e9d5ff", marginBottom: "14px" }, children: [
99
170
  /* @__PURE__ */ jsx("h4", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "10px", color: "#5b21b6" }, children: "Snooze \u2014 masquer temporairement" }),
@@ -115,4 +186,4 @@ function SnoozePanel({ snoozeSaving, handleSnooze }) {
115
186
  ] });
116
187
  }
117
188
 
118
- export { ExtMessagePanel, MergePanel, SnoozePanel };
189
+ export { ExtMessagePanel, MergePanel, ReminderPanel, SnoozePanel };
@@ -17,11 +17,18 @@ function QuickActions({
17
17
  onToggleExtMsg,
18
18
  onToggleSnooze,
19
19
  onNextTicket,
20
+ showReminderButton,
21
+ onToggleReminder,
22
+ autoCloseScheduledAt,
23
+ onCancelScheduledClose,
24
+ cancelingClose,
25
+ reminderSuccess,
20
26
  showNextTicket,
21
27
  nextTicketId,
22
28
  nextTicketInfo,
23
29
  onCloseNextTicket
24
30
  }) {
31
+ const scheduledClose = autoCloseScheduledAt && new Date(autoCloseScheduledAt) > /* @__PURE__ */ new Date() ? new Date(autoCloseScheduledAt).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }) : null;
25
32
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
26
33
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap", marginBottom: "16px", alignItems: "center" }, children: [
27
34
  statusTransitions.map((a) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -39,7 +46,21 @@ function QuickActions({
39
46
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onToggleMerge, style: constants.s.ghostBtn("#be185d"), children: "Fusionner" }),
40
47
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onToggleExtMsg, style: constants.s.ghostBtn("#4f46e5"), children: "+ Message re\xE7u" }),
41
48
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onToggleSnooze, style: constants.s.ghostBtn("#7c3aed"), children: "Snooze" }),
42
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onNextTicket, style: constants.s.ghostBtn("#16a34a"), children: "Ticket suivant" })
49
+ showReminderButton && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: onToggleReminder, style: constants.s.ghostBtn("#ea580c"), children: [
50
+ "\u23F0",
51
+ " Relancer"
52
+ ] }),
53
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onNextTicket, style: constants.s.ghostBtn("#16a34a"), children: "Ticket suivant" }),
54
+ scheduledClose && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "8px", padding: "5px 10px", borderRadius: "6px", backgroundColor: "#fff7ed", border: "1px solid #fed7aa", fontSize: "12px", color: "#9a3412", fontWeight: 600 }, children: [
55
+ "\u23F0",
56
+ " Fermeture auto le ",
57
+ scheduledClose,
58
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onCancelScheduledClose, disabled: cancelingClose, style: { border: "none", background: "none", color: "#b91c1c", cursor: cancelingClose ? "not-allowed" : "pointer", fontWeight: 700, fontSize: "12px", textDecoration: "underline", padding: 0, opacity: cancelingClose ? 0.5 : 1 }, children: cancelingClose ? "..." : "Annuler" })
59
+ ] }),
60
+ reminderSuccess && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...constants.s.badge("#f0fdf4", "#16a34a"), fontSize: "12px", padding: "5px 10px" }, children: [
61
+ "Relance envoy\xE9e ",
62
+ "\u2713"
63
+ ] })
43
64
  ] }),
44
65
  showNextTicket && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "10px 14px", borderRadius: "8px", backgroundColor: "#f0fdf4", border: `1px solid ${constants.C.border}`, marginBottom: "14px", display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
45
66
  nextTicketId ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -16,11 +16,18 @@ function QuickActions({
16
16
  onToggleExtMsg,
17
17
  onToggleSnooze,
18
18
  onNextTicket,
19
+ showReminderButton,
20
+ onToggleReminder,
21
+ autoCloseScheduledAt,
22
+ onCancelScheduledClose,
23
+ cancelingClose,
24
+ reminderSuccess,
19
25
  showNextTicket,
20
26
  nextTicketId,
21
27
  nextTicketInfo,
22
28
  onCloseNextTicket
23
29
  }) {
30
+ const scheduledClose = autoCloseScheduledAt && new Date(autoCloseScheduledAt) > /* @__PURE__ */ new Date() ? new Date(autoCloseScheduledAt).toLocaleString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }) : null;
24
31
  return /* @__PURE__ */ jsxs(Fragment, { children: [
25
32
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap", marginBottom: "16px", alignItems: "center" }, children: [
26
33
  statusTransitions.map((a) => /* @__PURE__ */ jsx(
@@ -38,7 +45,21 @@ function QuickActions({
38
45
  /* @__PURE__ */ jsx("button", { onClick: onToggleMerge, style: s.ghostBtn("#be185d"), children: "Fusionner" }),
39
46
  /* @__PURE__ */ jsx("button", { onClick: onToggleExtMsg, style: s.ghostBtn("#4f46e5"), children: "+ Message re\xE7u" }),
40
47
  /* @__PURE__ */ jsx("button", { onClick: onToggleSnooze, style: s.ghostBtn("#7c3aed"), children: "Snooze" }),
41
- /* @__PURE__ */ jsx("button", { onClick: onNextTicket, style: s.ghostBtn("#16a34a"), children: "Ticket suivant" })
48
+ showReminderButton && /* @__PURE__ */ jsxs("button", { onClick: onToggleReminder, style: s.ghostBtn("#ea580c"), children: [
49
+ "\u23F0",
50
+ " Relancer"
51
+ ] }),
52
+ /* @__PURE__ */ jsx("button", { onClick: onNextTicket, style: s.ghostBtn("#16a34a"), children: "Ticket suivant" }),
53
+ scheduledClose && /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "8px", padding: "5px 10px", borderRadius: "6px", backgroundColor: "#fff7ed", border: "1px solid #fed7aa", fontSize: "12px", color: "#9a3412", fontWeight: 600 }, children: [
54
+ "\u23F0",
55
+ " Fermeture auto le ",
56
+ scheduledClose,
57
+ /* @__PURE__ */ jsx("button", { onClick: onCancelScheduledClose, disabled: cancelingClose, style: { border: "none", background: "none", color: "#b91c1c", cursor: cancelingClose ? "not-allowed" : "pointer", fontWeight: 700, fontSize: "12px", textDecoration: "underline", padding: 0, opacity: cancelingClose ? 0.5 : 1 }, children: cancelingClose ? "..." : "Annuler" })
58
+ ] }),
59
+ reminderSuccess && /* @__PURE__ */ jsxs("span", { style: { ...s.badge("#f0fdf4", "#16a34a"), fontSize: "12px", padding: "5px 10px" }, children: [
60
+ "Relance envoy\xE9e ",
61
+ "\u2713"
62
+ ] })
42
63
  ] }),
43
64
  showNextTicket && /* @__PURE__ */ jsxs("div", { style: { padding: "10px 14px", borderRadius: "8px", backgroundColor: "#f0fdf4", border: `1px solid ${C.border}`, marginBottom: "14px", display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
44
65
  nextTicketId ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1,5 +1,30 @@
1
1
  'use strict';
2
2
 
3
+ const REWRITE_STYLES = [
4
+ { id: "auto", label: "\u270F\uFE0F Auto", desc: "Garde le ton actuel" },
5
+ { id: "tutoyer", label: "\u{1F44B} Tutoyer", desc: "Passe en tu" },
6
+ { id: "vouvoyer", label: "\u{1F3A9} Vouvoyer", desc: "Passe en vous" },
7
+ { id: "formel", label: "\u{1F4BC} Formel", desc: "Ton professionnel" },
8
+ { id: "amical", label: "\u{1F60A} Amical", desc: "Ton chaleureux" },
9
+ { id: "court", label: "\u26A1 Court", desc: "Version concise" }
10
+ ];
11
+ const layoutStyles = {
12
+ root: { padding: "12px 0" },
13
+ twoColumns: {
14
+ display: "grid",
15
+ gridTemplateColumns: "1fr 320px",
16
+ gap: "16px",
17
+ alignItems: "start"
18
+ },
19
+ mainColumn: { minWidth: 0 },
20
+ sideColumn: {
21
+ display: "flex",
22
+ flexDirection: "column",
23
+ gap: "12px",
24
+ position: "sticky",
25
+ top: "80px"
26
+ }
27
+ };
3
28
  const statusLabels = {
4
29
  open: { label: "Ouvert", bg: "#dbeafe", color: "#1e40af" },
5
30
  waiting_client: { label: "Attente", bg: "#fef3c7", color: "#92400e" },
@@ -95,6 +120,8 @@ const s = {
95
120
  };
96
121
 
97
122
  exports.C = C;
123
+ exports.REWRITE_STYLES = REWRITE_STYLES;
124
+ exports.layoutStyles = layoutStyles;
98
125
  exports.projectStatusLabels = projectStatusLabels;
99
126
  exports.s = s;
100
127
  exports.statusLabels = statusLabels;
@@ -1,4 +1,29 @@
1
1
  "use client";
2
+ const REWRITE_STYLES = [
3
+ { id: "auto", label: "\u270F\uFE0F Auto", desc: "Garde le ton actuel" },
4
+ { id: "tutoyer", label: "\u{1F44B} Tutoyer", desc: "Passe en tu" },
5
+ { id: "vouvoyer", label: "\u{1F3A9} Vouvoyer", desc: "Passe en vous" },
6
+ { id: "formel", label: "\u{1F4BC} Formel", desc: "Ton professionnel" },
7
+ { id: "amical", label: "\u{1F60A} Amical", desc: "Ton chaleureux" },
8
+ { id: "court", label: "\u26A1 Court", desc: "Version concise" }
9
+ ];
10
+ const layoutStyles = {
11
+ root: { padding: "12px 0" },
12
+ twoColumns: {
13
+ display: "grid",
14
+ gridTemplateColumns: "1fr 320px",
15
+ gap: "16px",
16
+ alignItems: "start"
17
+ },
18
+ mainColumn: { minWidth: 0 },
19
+ sideColumn: {
20
+ display: "flex",
21
+ flexDirection: "column",
22
+ gap: "12px",
23
+ position: "sticky",
24
+ top: "80px"
25
+ }
26
+ };
2
27
  const statusLabels = {
3
28
  open: { label: "Ouvert", bg: "#dbeafe", color: "#1e40af" },
4
29
  waiting_client: { label: "Attente", bg: "#fef3c7", color: "#92400e" },
@@ -93,4 +118,4 @@ const s = {
93
118
  })
94
119
  };
95
120
 
96
- export { C, projectStatusLabels, s, statusLabels };
121
+ export { C, REWRITE_STYLES, layoutStyles, projectStatusLabels, s, statusLabels };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ function useDocumentIdFromUrl() {
6
+ const [id, setId] = react.useState(void 0);
7
+ react.useEffect(() => {
8
+ const match = window.location.pathname.match(
9
+ /\/admin\/collections\/[^/]+\/([^/?#]+)/
10
+ );
11
+ if (match && match[1] !== "create") {
12
+ const raw = match[1];
13
+ const num = Number(raw);
14
+ setId(Number.isFinite(num) && String(num) === raw ? num : raw);
15
+ }
16
+ }, []);
17
+ return { id };
18
+ }
19
+
20
+ exports.useDocumentIdFromUrl = useDocumentIdFromUrl;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { useState, useEffect } from 'react';
3
+
4
+ function useDocumentIdFromUrl() {
5
+ const [id, setId] = useState(void 0);
6
+ useEffect(() => {
7
+ const match = window.location.pathname.match(
8
+ /\/admin\/collections\/[^/]+\/([^/?#]+)/
9
+ );
10
+ if (match && match[1] !== "create") {
11
+ const raw = match[1];
12
+ const num = Number(raw);
13
+ setId(Number.isFinite(num) && String(num) === raw ? num : raw);
14
+ }
15
+ }, []);
16
+ return { id };
17
+ }
18
+
19
+ export { useDocumentIdFromUrl };