@consilioweb/payload-support 0.16.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 (193) 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/constants.cjs +27 -0
  7. package/dist/components/TicketConversation/constants.js +26 -1
  8. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.cjs +20 -0
  9. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.js +19 -0
  10. package/dist/components/TicketConversation/index.cjs +10 -147
  11. package/dist/components/TicketConversation/index.js +4 -141
  12. package/dist/index.cjs +1604 -671
  13. package/dist/index.d.cts +3 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.js +1602 -671
  16. package/dist/views/BillingView/client.cjs +40 -54
  17. package/dist/views/BillingView/client.js +40 -54
  18. package/dist/views/ChatView/client.cjs +4 -3
  19. package/dist/views/ChatView/client.js +4 -3
  20. package/dist/views/CrmView/client.cjs +7 -7
  21. package/dist/views/CrmView/client.js +7 -7
  22. package/dist/views/EmailTrackingView/client.cjs +6 -5
  23. package/dist/views/EmailTrackingView/client.js +6 -5
  24. package/dist/views/ImportConversationView/client.cjs +4 -4
  25. package/dist/views/ImportConversationView/client.js +4 -4
  26. package/dist/views/LogsView/client.cjs +3 -2
  27. package/dist/views/LogsView/client.js +3 -2
  28. package/dist/views/NewTicketView/client.cjs +1 -1
  29. package/dist/views/NewTicketView/client.js +1 -1
  30. package/dist/views/PendingEmailsView/client.cjs +7 -6
  31. package/dist/views/PendingEmailsView/client.js +7 -6
  32. package/dist/views/SupportDashboardView/client.cjs +19 -22
  33. package/dist/views/SupportDashboardView/client.js +19 -22
  34. package/dist/views/TicketDetailView/NextActionItem.cjs +40 -0
  35. package/dist/views/TicketDetailView/NextActionItem.js +34 -0
  36. package/dist/views/TicketDetailView/RewriteDropdown.cjs +80 -0
  37. package/dist/views/TicketDetailView/RewriteDropdown.js +78 -0
  38. package/dist/views/TicketDetailView/client.cjs +59 -190
  39. package/dist/views/TicketDetailView/client.js +52 -183
  40. package/dist/views/TicketDetailView/constants.cjs +20 -0
  41. package/dist/views/TicketDetailView/constants.js +16 -0
  42. package/dist/views/TicketDetailView/helpers.cjs +23 -0
  43. package/dist/views/TicketDetailView/helpers.js +20 -0
  44. package/dist/views/TicketDetailView/types.cjs +2 -0
  45. package/dist/views/TicketDetailView/types.js +1 -0
  46. package/dist/views/TicketInboxView/client.cjs +14 -10
  47. package/dist/views/TicketInboxView/client.js +14 -10
  48. package/dist/views/TicketingSettingsView/client.cjs +101 -96
  49. package/dist/views/TicketingSettingsView/client.js +92 -88
  50. package/dist/views/TimeDashboardView/client.cjs +2 -2
  51. package/dist/views/TimeDashboardView/client.js +2 -2
  52. package/dist/views/shared/StatusPill.cjs +23 -12
  53. package/dist/views/shared/StatusPill.js +23 -12
  54. package/dist/views/shared/dateLocale.cjs +5 -0
  55. package/dist/views/shared/dateLocale.js +3 -0
  56. package/package.json +23 -15
  57. package/src/__tests__/generateTrackingToken.test.ts +53 -0
  58. package/src/__tests__/integration/ai-agent.test.ts +46 -0
  59. package/src/__tests__/integration/auth.test.ts +43 -0
  60. package/src/__tests__/integration/automation-rules.test.ts +68 -0
  61. package/src/__tests__/integration/buildTestPayload.ts +52 -0
  62. package/src/__tests__/integration/channels.test.ts +52 -0
  63. package/src/__tests__/integration/core-behaviors.test.ts +58 -0
  64. package/src/__tests__/integration/dashboard-volume.test.ts +31 -0
  65. package/src/__tests__/integration/digest.test.ts +48 -0
  66. package/src/__tests__/integration/invoice-pdf.test.ts +30 -0
  67. package/src/__tests__/integration/isolation.test.ts +81 -0
  68. package/src/__tests__/integration/mentions-invoice.test.ts +50 -0
  69. package/src/__tests__/integration/nps.test.ts +37 -0
  70. package/src/__tests__/integration/sanitization.test.ts +35 -0
  71. package/src/__tests__/integration/sla-pause.test.ts +39 -0
  72. package/src/__tests__/integration/smoke.test.ts +26 -0
  73. package/src/__tests__/integration/snooze.test.ts +68 -0
  74. package/src/__tests__/integration/teams.test.ts +38 -0
  75. package/src/__tests__/rateLimiter.test.ts +89 -0
  76. package/src/__tests__/sanitizeHtml.test.ts +165 -0
  77. package/src/__tests__/sla.test.ts +258 -0
  78. package/src/collections/AutomationRules.ts +66 -0
  79. package/src/collections/NotificationQueue.ts +30 -0
  80. package/src/collections/SatisfactionSurveys.ts +11 -2
  81. package/src/collections/SupportClients.ts +58 -1
  82. package/src/collections/SupportTeam.ts +27 -0
  83. package/src/collections/TicketActivityLog.ts +9 -4
  84. package/src/collections/TicketMessages.ts +129 -31
  85. package/src/collections/Tickets.ts +31 -73
  86. package/src/collections/index.ts +3 -0
  87. package/src/components/TicketConversation/RewriteDropdown.tsx +86 -0
  88. package/src/components/TicketConversation/SkeletonText.tsx +23 -0
  89. package/src/components/TicketConversation/constants.ts +29 -0
  90. package/src/components/TicketConversation/hooks/useDocumentIdFromUrl.ts +21 -0
  91. package/src/components/TicketConversation/index.tsx +4 -152
  92. package/src/endpoints/admin-chat-stream.ts +3 -4
  93. package/src/endpoints/admin-chat.ts +11 -20
  94. package/src/endpoints/admin-stats.ts +70 -32
  95. package/src/endpoints/ai-agent.ts +36 -0
  96. package/src/endpoints/apply-macro.ts +8 -14
  97. package/src/endpoints/auth-2fa.ts +12 -12
  98. package/src/endpoints/auto-close.ts +6 -10
  99. package/src/endpoints/billing.ts +17 -4
  100. package/src/endpoints/bulk-action.ts +7 -12
  101. package/src/endpoints/channels.ts +89 -0
  102. package/src/endpoints/chat-stream.ts +3 -4
  103. package/src/endpoints/chat.ts +7 -12
  104. package/src/endpoints/chatbot.ts +2 -2
  105. package/src/endpoints/client-intelligence.ts +4 -4
  106. package/src/endpoints/delete-account.ts +9 -16
  107. package/src/endpoints/email-stats.ts +2 -2
  108. package/src/endpoints/escalate.ts +2 -2
  109. package/src/endpoints/export-csv.ts +2 -2
  110. package/src/endpoints/export-data.ts +5 -8
  111. package/src/endpoints/import-conversation.ts +6 -10
  112. package/src/endpoints/index.ts +18 -1
  113. package/src/endpoints/invite-collaborator.ts +30 -15
  114. package/src/endpoints/invoice.ts +127 -0
  115. package/src/endpoints/kb-search.ts +2 -2
  116. package/src/endpoints/login.ts +12 -4
  117. package/src/endpoints/merge-clients.ts +14 -19
  118. package/src/endpoints/merge-tickets.ts +11 -18
  119. package/src/endpoints/oauth-google.ts +86 -39
  120. package/src/endpoints/pending-emails-process.ts +15 -25
  121. package/src/endpoints/process-digests.ts +93 -0
  122. package/src/endpoints/process-scheduled.ts +6 -10
  123. package/src/endpoints/process-snooze.ts +78 -0
  124. package/src/endpoints/purge-logs.ts +2 -2
  125. package/src/endpoints/resend-notification.ts +3 -4
  126. package/src/endpoints/round-robin-config.ts +3 -4
  127. package/src/endpoints/satisfaction.ts +21 -14
  128. package/src/endpoints/search.ts +5 -8
  129. package/src/endpoints/seed-kb.ts +3 -4
  130. package/src/endpoints/send-reminder.ts +6 -9
  131. package/src/endpoints/settings.ts +5 -2
  132. package/src/endpoints/signature.ts +3 -4
  133. package/src/endpoints/sla-check.ts +2 -2
  134. package/src/endpoints/split-ticket.ts +7 -12
  135. package/src/endpoints/statuses.ts +2 -2
  136. package/src/endpoints/ticket-feedback.ts +4 -6
  137. package/src/endpoints/ticket-synthesis.ts +2 -2
  138. package/src/endpoints/track-open.ts +16 -28
  139. package/src/endpoints/transfer-ticket.ts +21 -9
  140. package/src/endpoints/user-prefs.ts +3 -4
  141. package/src/hooks/automationRules.ts +84 -0
  142. package/src/hooks/checkSLA.ts +48 -16
  143. package/src/plugin.ts +6 -0
  144. package/src/portal/LiveChat.tsx +2 -63
  145. package/src/portal/auth/tickets/detail/CloseTicketButton.tsx +10 -0
  146. package/src/portal/auth/tickets/detail/MarkSolutionButton.tsx +14 -2
  147. package/src/portal/auth/tickets/detail/MessageActions.tsx +37 -18
  148. package/src/portal/auth/tickets/detail/ReopenTicketButton.tsx +10 -0
  149. package/src/portal/auth/tickets/detail/TransferAndInviteActions.tsx +3 -2
  150. package/src/portal/auth/tickets/detail/page.tsx +2 -2
  151. package/src/portal/helpers.ts +46 -0
  152. package/src/portal/login/page.tsx +3 -8
  153. package/src/portal/types.ts +15 -0
  154. package/src/types/lucide-react.d.ts +34 -0
  155. package/src/types/payload-config.d.ts +10 -0
  156. package/src/utils/adminNotification.ts +2 -2
  157. package/src/utils/aiAgent.ts +147 -0
  158. package/src/utils/auth.ts +5 -4
  159. package/src/utils/channels.ts +36 -0
  160. package/src/utils/db.ts +73 -0
  161. package/src/utils/emailTemplate.ts +8 -1
  162. package/src/utils/fireWebhooks.ts +4 -6
  163. package/src/utils/generateTicketSynthesis.ts +7 -12
  164. package/src/utils/invoicePdf.ts +88 -0
  165. package/src/utils/notificationQueue.ts +36 -0
  166. package/src/utils/readSettings.ts +22 -6
  167. package/src/utils/sanitizeHtml.ts +57 -0
  168. package/src/utils/slugs.ts +6 -0
  169. package/src/utils/teamAccess.ts +27 -0
  170. package/src/utils/ticketAccess.ts +53 -0
  171. package/src/utils/webhookDispatcher.ts +4 -6
  172. package/src/views/BillingView/client.tsx +49 -37
  173. package/src/views/ChatView/client.tsx +4 -3
  174. package/src/views/CrmView/client.tsx +7 -7
  175. package/src/views/EmailTrackingView/client.tsx +7 -6
  176. package/src/views/ImportConversationView/client.tsx +4 -4
  177. package/src/views/LogsView/client.tsx +3 -2
  178. package/src/views/NewTicketView/client.tsx +1 -1
  179. package/src/views/PendingEmailsView/client.tsx +7 -6
  180. package/src/views/SupportDashboardView/client.tsx +21 -21
  181. package/src/views/TicketDetailView/NextActionItem.tsx +30 -0
  182. package/src/views/TicketDetailView/RewriteDropdown.tsx +63 -0
  183. package/src/views/TicketDetailView/client.tsx +54 -179
  184. package/src/views/TicketDetailView/constants.ts +23 -0
  185. package/src/views/TicketDetailView/helpers.ts +17 -0
  186. package/src/views/TicketDetailView/types.ts +10 -0
  187. package/src/views/TicketInboxView/client.tsx +16 -10
  188. package/src/views/TicketingSettingsView/client.tsx +90 -87
  189. package/src/views/TimeDashboardView/client.tsx +2 -2
  190. package/src/views/shared/StatusPill.tsx +24 -12
  191. package/src/views/shared/dateLocale.ts +5 -0
  192. package/src/views/shared/locales/en.json +708 -0
  193. package/src/views/shared/locales/fr.json +708 -0
@@ -3,6 +3,7 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
5
  var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
6
+ var dateLocale = require('../shared/dateLocale');
6
7
  var s = require('../../styles/EmailTracking.module.scss');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -11,12 +12,12 @@ var React__default = /*#__PURE__*/_interopDefault(React);
11
12
  var s__default = /*#__PURE__*/_interopDefault(s);
12
13
 
13
14
  const STATUS_CFG = {
14
- success: { label: "Succ\xE8s", bg: "#dcfce7", color: "#16a34a" },
15
- error: { label: "Erreur", bg: "#fef2f2", color: "#dc2626" },
16
- ignored: { label: "Ignor\xE9", bg: "#f3f4f6", color: "#6b7280" }
15
+ success: { labelKey: "logs.statusSuccess", bg: "#dcfce7", color: "#16a34a" },
16
+ error: { labelKey: "emailTracking.errorLabel", bg: "#fef2f2", color: "#dc2626" },
17
+ ignored: { labelKey: "logs.statusIgnored", bg: "#f3f4f6", color: "#6b7280" }
17
18
  };
18
19
  function fmtDate(d) {
19
- return new Date(d).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
20
+ return new Date(d).toLocaleDateString(dateLocale.DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
20
21
  }
21
22
  const EmailTrackingClient = () => {
22
23
  const { t } = useTranslation.useTranslation();
@@ -130,7 +131,7 @@ const EmailTrackingClient = () => {
130
131
  /* @__PURE__ */ jsxRuntime.jsx("td", { style: { maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.recipientEmail, children: log.recipientEmail || "\u2014" }),
131
132
  /* @__PURE__ */ jsxRuntime.jsx("td", { style: { maxWidth: 250, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.subject, children: log.subject || "\u2014" }),
132
133
  /* @__PURE__ */ jsxRuntime.jsx("td", { children: log.ticketNumber ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.ticketLink, children: log.ticketNumber }) : "\u2014" }),
133
- /* @__PURE__ */ jsxRuntime.jsx("td", { children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.label || log.status }) }),
134
+ /* @__PURE__ */ jsxRuntime.jsx("td", { children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.labelKey ? t(STATUS_CFG[log.status].labelKey) : log.status }) }),
134
135
  /* @__PURE__ */ jsxRuntime.jsx("td", { style: { fontSize: 12 }, children: log.action || "\u2014" }),
135
136
  /* @__PURE__ */ jsxRuntime.jsxs("td", { style: { textAlign: "right" }, children: [
136
137
  log.processingTimeMs != null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${s__default.default.processingTime} ${log.processingTimeMs > 2e3 ? s__default.default.timeSlow : log.processingTimeMs > 500 ? s__default.default.timeMedium : s__default.default.timeFast}`, children: [
@@ -2,15 +2,16 @@
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import React, { useState, useCallback, useEffect } from 'react';
4
4
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
5
+ import { DATE_LOCALE } from '../shared/dateLocale.js';
5
6
  import s from '../../styles/EmailTracking.module.scss';
6
7
 
7
8
  const STATUS_CFG = {
8
- success: { label: "Succ\xE8s", bg: "#dcfce7", color: "#16a34a" },
9
- error: { label: "Erreur", bg: "#fef2f2", color: "#dc2626" },
10
- ignored: { label: "Ignor\xE9", bg: "#f3f4f6", color: "#6b7280" }
9
+ success: { labelKey: "logs.statusSuccess", bg: "#dcfce7", color: "#16a34a" },
10
+ error: { labelKey: "emailTracking.errorLabel", bg: "#fef2f2", color: "#dc2626" },
11
+ ignored: { labelKey: "logs.statusIgnored", bg: "#f3f4f6", color: "#6b7280" }
11
12
  };
12
13
  function fmtDate(d) {
13
- return new Date(d).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
14
+ return new Date(d).toLocaleDateString(DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
14
15
  }
15
16
  const EmailTrackingClient = () => {
16
17
  const { t } = useTranslation();
@@ -124,7 +125,7 @@ const EmailTrackingClient = () => {
124
125
  /* @__PURE__ */ jsx("td", { style: { maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.recipientEmail, children: log.recipientEmail || "\u2014" }),
125
126
  /* @__PURE__ */ jsx("td", { style: { maxWidth: 250, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.subject, children: log.subject || "\u2014" }),
126
127
  /* @__PURE__ */ jsx("td", { children: log.ticketNumber ? /* @__PURE__ */ jsx("span", { className: s.ticketLink, children: log.ticketNumber }) : "\u2014" }),
127
- /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("span", { className: s.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.label || log.status }) }),
128
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("span", { className: s.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.labelKey ? t(STATUS_CFG[log.status].labelKey) : log.status }) }),
128
129
  /* @__PURE__ */ jsx("td", { style: { fontSize: 12 }, children: log.action || "\u2014" }),
129
130
  /* @__PURE__ */ jsxs("td", { style: { textAlign: "right" }, children: [
130
131
  log.processingTimeMs != null ? /* @__PURE__ */ jsxs("span", { className: `${s.processingTime} ${log.processingTimeMs > 2e3 ? s.timeSlow : log.processingTimeMs > 500 ? s.timeMedium : s.timeFast}`, children: [
@@ -68,12 +68,12 @@ function ImportConversationClient() {
68
68
  });
69
69
  const data = await res.json();
70
70
  if (!res.ok) {
71
- setError(data.error || "Erreur lors de l'analyse");
71
+ setError(data.error || t("import.analyzeError"));
72
72
  return;
73
73
  }
74
74
  setPreview(data);
75
75
  } catch {
76
- setError("Erreur reseau");
76
+ setError(t("import.networkError"));
77
77
  } finally {
78
78
  setLoading(false);
79
79
  }
@@ -90,13 +90,13 @@ function ImportConversationClient() {
90
90
  });
91
91
  const data = await res.json();
92
92
  if (!res.ok) {
93
- setError(data.error || "Erreur lors de l'import");
93
+ setError(data.error || t("import.importError"));
94
94
  return;
95
95
  }
96
96
  setResult(data);
97
97
  setPreview(null);
98
98
  } catch {
99
- setError("Erreur reseau");
99
+ setError(t("import.networkError"));
100
100
  } finally {
101
101
  setLoading(false);
102
102
  }
@@ -63,12 +63,12 @@ function ImportConversationClient() {
63
63
  });
64
64
  const data = await res.json();
65
65
  if (!res.ok) {
66
- setError(data.error || "Erreur lors de l'analyse");
66
+ setError(data.error || t("import.analyzeError"));
67
67
  return;
68
68
  }
69
69
  setPreview(data);
70
70
  } catch {
71
- setError("Erreur reseau");
71
+ setError(t("import.networkError"));
72
72
  } finally {
73
73
  setLoading(false);
74
74
  }
@@ -85,13 +85,13 @@ function ImportConversationClient() {
85
85
  });
86
86
  const data = await res.json();
87
87
  if (!res.ok) {
88
- setError(data.error || "Erreur lors de l'import");
88
+ setError(data.error || t("import.importError"));
89
89
  return;
90
90
  }
91
91
  setResult(data);
92
92
  setPreview(null);
93
93
  } catch {
94
- setError("Erreur reseau");
94
+ setError(t("import.networkError"));
95
95
  } finally {
96
96
  setLoading(false);
97
97
  }
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
5
  var navigation = require('next/navigation');
6
6
  var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
7
+ var dateLocale = require('../shared/dateLocale');
7
8
  var s = require('../../styles/Logs.module.scss');
8
9
 
9
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -11,7 +12,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
12
  var s__default = /*#__PURE__*/_interopDefault(s);
12
13
 
13
14
  function fmtDate(d) {
14
- return new Date(d).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
15
+ return new Date(d).toLocaleDateString(dateLocale.DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
15
16
  }
16
17
  const LogsClient = () => {
17
18
  const { t } = useTranslation.useTranslation();
@@ -67,7 +68,7 @@ const LogsClient = () => {
67
68
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.headerActions, children: [
68
69
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(7), children: t("logs.purge7") }),
69
70
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(30), children: t("logs.purge30") }),
70
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(90), children: "Purger +90j" }),
71
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(90), children: t("logs.purge90") }),
71
72
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: `${s__default.default.purgeBtn} ${s__default.default.purgeBtnDanger}`, onClick: () => handlePurge(0), children: t("logs.purgeAll") })
72
73
  ] })
73
74
  ] }),
@@ -3,10 +3,11 @@ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useState, useCallback, useEffect } from 'react';
4
4
  import { useSearchParams } from 'next/navigation';
5
5
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
6
+ import { DATE_LOCALE } from '../shared/dateLocale.js';
6
7
  import s from '../../styles/Logs.module.scss';
7
8
 
8
9
  function fmtDate(d) {
9
- return new Date(d).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
10
+ return new Date(d).toLocaleDateString(DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
10
11
  }
11
12
  const LogsClient = () => {
12
13
  const { t } = useTranslation();
@@ -62,7 +63,7 @@ const LogsClient = () => {
62
63
  /* @__PURE__ */ jsxs("div", { className: s.headerActions, children: [
63
64
  /* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(7), children: t("logs.purge7") }),
64
65
  /* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(30), children: t("logs.purge30") }),
65
- /* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(90), children: "Purger +90j" }),
66
+ /* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(90), children: t("logs.purge90") }),
66
67
  /* @__PURE__ */ jsx("button", { className: `${s.purgeBtn} ${s.purgeBtnDanger}`, onClick: () => handlePurge(0), children: t("logs.purgeAll") })
67
68
  ] })
68
69
  ] }),
@@ -196,7 +196,7 @@ const NewTicketClient = () => {
196
196
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.fieldGroup, children: [
197
197
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: s__default.default.label, children: t("newTicket.projectLabel") }),
198
198
  /* @__PURE__ */ jsxRuntime.jsxs("select", { className: s__default.default.select, value: projectId || "", onChange: (e) => setProjectId(e.target.value ? Number(e.target.value) : null), children: [
199
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 Aucun \u2014" }),
199
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("newTicket.noProject") }),
200
200
  projects.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.id, children: p.name }, p.id))
201
201
  ] })
202
202
  ] })
@@ -190,7 +190,7 @@ const NewTicketClient = () => {
190
190
  /* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
191
191
  /* @__PURE__ */ jsx("label", { className: s.label, children: t("newTicket.projectLabel") }),
192
192
  /* @__PURE__ */ jsxs("select", { className: s.select, value: projectId || "", onChange: (e) => setProjectId(e.target.value ? Number(e.target.value) : null), children: [
193
- /* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Aucun \u2014" }),
193
+ /* @__PURE__ */ jsx("option", { value: "", children: t("newTicket.noProject") }),
194
194
  projects.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
195
195
  ] })
196
196
  ] })
@@ -11,15 +11,15 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
12
12
  var styles__default = /*#__PURE__*/_interopDefault(styles);
13
13
 
14
- function timeAgo(dateStr) {
14
+ function timeAgo(dateStr, tFn) {
15
15
  const diff = Date.now() - new Date(dateStr).getTime();
16
16
  const mins = Math.floor(diff / 6e4);
17
- if (mins < 1) return "\xE0 l'instant";
18
- if (mins < 60) return `il y a ${mins}min`;
17
+ if (mins < 1) return tFn("pendingEmails.timeAgo.justNow");
18
+ if (mins < 60) return tFn("pendingEmails.timeAgo.minutesAgo", { count: mins });
19
19
  const hours = Math.floor(mins / 60);
20
- if (hours < 24) return `il y a ${hours}h`;
20
+ if (hours < 24) return tFn("pendingEmails.timeAgo.hoursAgo", { count: hours });
21
21
  const days = Math.floor(hours / 24);
22
- return `il y a ${days}j`;
22
+ return tFn("pendingEmails.timeAgo.daysAgo", { count: days });
23
23
  }
24
24
  function TicketSearchModal({
25
25
  suggestions,
@@ -256,6 +256,7 @@ function EmailCard({
256
256
  onProcess,
257
257
  processing
258
258
  }) {
259
+ const { t } = useTranslation.useTranslation();
259
260
  const [expanded, setExpanded] = react.useState(false);
260
261
  const [showLinkModal, setShowLinkModal] = react.useState(false);
261
262
  const [showClientPicker, setShowClientPicker] = react.useState(false);
@@ -278,7 +279,7 @@ function EmailCard({
278
279
  ] }),
279
280
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.cardSubject, children: email.subject }),
280
281
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.cardMeta, children: [
281
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: timeAgo(email.createdAt) }),
282
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: timeAgo(email.createdAt, t) }),
282
283
  attachmentCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles__default.default.attachment, children: [
283
284
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { size: 12 }),
284
285
  " ",
@@ -6,15 +6,15 @@ import { SkeletonDashboard } from '../shared/Skeleton.js';
6
6
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
7
7
  import styles from '../../styles/PendingEmails.module.scss';
8
8
 
9
- function timeAgo(dateStr) {
9
+ function timeAgo(dateStr, tFn) {
10
10
  const diff = Date.now() - new Date(dateStr).getTime();
11
11
  const mins = Math.floor(diff / 6e4);
12
- if (mins < 1) return "\xE0 l'instant";
13
- if (mins < 60) return `il y a ${mins}min`;
12
+ if (mins < 1) return tFn("pendingEmails.timeAgo.justNow");
13
+ if (mins < 60) return tFn("pendingEmails.timeAgo.minutesAgo", { count: mins });
14
14
  const hours = Math.floor(mins / 60);
15
- if (hours < 24) return `il y a ${hours}h`;
15
+ if (hours < 24) return tFn("pendingEmails.timeAgo.hoursAgo", { count: hours });
16
16
  const days = Math.floor(hours / 24);
17
- return `il y a ${days}j`;
17
+ return tFn("pendingEmails.timeAgo.daysAgo", { count: days });
18
18
  }
19
19
  function TicketSearchModal({
20
20
  suggestions,
@@ -251,6 +251,7 @@ function EmailCard({
251
251
  onProcess,
252
252
  processing
253
253
  }) {
254
+ const { t } = useTranslation();
254
255
  const [expanded, setExpanded] = useState(false);
255
256
  const [showLinkModal, setShowLinkModal] = useState(false);
256
257
  const [showClientPicker, setShowClientPicker] = useState(false);
@@ -273,7 +274,7 @@ function EmailCard({
273
274
  ] }),
274
275
  /* @__PURE__ */ jsx("div", { className: styles.cardSubject, children: email.subject }),
275
276
  /* @__PURE__ */ jsxs("div", { className: styles.cardMeta, children: [
276
- /* @__PURE__ */ jsx("span", { children: timeAgo(email.createdAt) }),
277
+ /* @__PURE__ */ jsx("span", { children: timeAgo(email.createdAt, t) }),
277
278
  attachmentCount > 0 && /* @__PURE__ */ jsxs("span", { className: styles.attachment, children: [
278
279
  /* @__PURE__ */ jsx(Paperclip, { size: 12 }),
279
280
  " ",
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
5
  var Link = require('next/link');
6
6
  var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
7
+ var dateLocale = require('../shared/dateLocale');
7
8
  var styles = require('../../styles/SupportDashboard.module.scss');
8
9
 
9
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -38,6 +39,7 @@ function computeTrend(current, previous) {
38
39
  return { pct: Math.abs(pct), dir: pct > 0 ? "up" : "down" };
39
40
  }
40
41
  function StatCard({ label, value, trend, accentColor, onClick }) {
42
+ const { t } = useTranslation.useTranslation();
41
43
  const style = accentColor ? { "--stat-accent": accentColor } : void 0;
42
44
  const Tag = onClick ? "button" : "div";
43
45
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -55,7 +57,7 @@ function StatCard({ label, value, trend, accentColor, onClick }) {
55
57
  trend.pct,
56
58
  "%"
57
59
  ] }),
58
- trend && trend.dir === "neutral" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${styles__default.default.statTrend} ${styles__default.default.neutral}`, children: "\u2014 stable" })
60
+ trend && trend.dir === "neutral" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${styles__default.default.statTrend} ${styles__default.default.neutral}`, children: t("dashboard.stable") })
59
61
  ]
60
62
  }
61
63
  );
@@ -79,6 +81,7 @@ function VolumeChart({ data }) {
79
81
  ] });
80
82
  }
81
83
  function CSATRing({ score, count }) {
84
+ const { t } = useTranslation.useTranslation();
82
85
  const pct = score > 0 ? score / 5 * 100 : 0;
83
86
  const radius = 42;
84
87
  const circumference = 2 * Math.PI * radius;
@@ -108,8 +111,8 @@ function CSATRing({ score, count }) {
108
111
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatValue, children: score > 0 ? score.toFixed(1) : "--" })
109
112
  ] }),
110
113
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.csatMeta, children: [
111
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : "Pas de donn\xE9es" }),
112
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatSub, children: count > 0 ? `${count} avis recueillis` : "Aucun avis" })
114
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : t("dashboard.csat.noData") }),
115
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatSub, children: count > 0 ? t("dashboard.csat.reviews", { count }) : t("dashboard.csat.noReviews") })
113
116
  ] })
114
117
  ] });
115
118
  }
@@ -124,6 +127,7 @@ function formatSlaTime(minutes) {
124
127
  return remainH > 0 ? `${d}j ${remainH}h` : `${d}j`;
125
128
  }
126
129
  function SlaSection() {
130
+ const { t } = useTranslation.useTranslation();
127
131
  const [sla, setSla] = react.useState(null);
128
132
  const [slaLoading, setSlaLoading] = react.useState(true);
129
133
  react.useEffect(() => {
@@ -143,8 +147,8 @@ function SlaSection() {
143
147
  }, []);
144
148
  if (slaLoading) {
145
149
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaSection, children: [
146
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: "SLA" }),
147
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaLoading, children: "Chargement..." })
150
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: t("dashboard.slaTitle") }),
151
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaLoading, children: t("dashboard.sla.loading") })
148
152
  ] });
149
153
  }
150
154
  if (!sla) return null;
@@ -152,7 +156,7 @@ function SlaSection() {
152
156
  window.location.href = `/admin/support/ticket?id=${id}`;
153
157
  };
154
158
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaSection, children: [
155
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: "SLA" }),
159
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: t("dashboard.slaTitle") }),
156
160
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaGrid, children: [
157
161
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.slaCard} ${styles__default.default.slaBreach}`, children: [
158
162
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaCardHeader, children: [
@@ -162,11 +166,11 @@ function SlaSection() {
162
166
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
163
167
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
164
168
  ] }),
165
- "En breach"
169
+ t("dashboard.sla.breached")
166
170
  ] }),
167
171
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${styles__default.default.slaCount} ${styles__default.default.slaCountBreach}`, children: sla.breached.length })
168
172
  ] }),
169
- sla.breached.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: "Aucun ticket en breach" }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: styles__default.default.slaList, children: sla.breached.map((ticket) => {
173
+ sla.breached.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: t("dashboard.sla.noBreached") }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: styles__default.default.slaList, children: sla.breached.map((ticket) => {
170
174
  const now = /* @__PURE__ */ new Date();
171
175
  const created = new Date(ticket.createdAt);
172
176
  const overdueMin = Math.round((now.getTime() - created.getTime()) / 6e4);
@@ -200,11 +204,11 @@ function SlaSection() {
200
204
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
201
205
  /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "12 6 12 12 16 14" })
202
206
  ] }),
203
- "\xC0 risque"
207
+ t("dashboard.sla.atRisk")
204
208
  ] }),
205
209
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${styles__default.default.slaCount} ${styles__default.default.slaCountRisk}`, children: sla.atRisk.length })
206
210
  ] }),
207
- sla.atRisk.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: "Aucun ticket \xE0 risque" }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: styles__default.default.slaList, children: sla.atRisk.map((ticket) => {
211
+ sla.atRisk.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: t("dashboard.sla.noAtRisk") }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: styles__default.default.slaList, children: sla.atRisk.map((ticket) => {
208
212
  const now = /* @__PURE__ */ new Date();
209
213
  const created = new Date(ticket.createdAt);
210
214
  const elapsedMin = Math.round((now.getTime() - created.getTime()) / 6e4);
@@ -223,10 +227,7 @@ function SlaSection() {
223
227
  ] }),
224
228
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.slaItemSubject, children: ticket.subject })
225
229
  ] }),
226
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${styles__default.default.slaItemTime} ${styles__default.default.slaTimeRisk}`, children: [
227
- formatSlaTime(remainingMin),
228
- " restant"
229
- ] })
230
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${styles__default.default.slaItemTime} ${styles__default.default.slaTimeRisk}`, children: t("dashboard.sla.remaining", { time: formatSlaTime(remainingMin) }) })
230
231
  ]
231
232
  },
232
233
  ticket.id
@@ -275,14 +276,10 @@ const SupportDashboardClient = () => {
275
276
  return () => window.removeEventListener("focus", onFocus);
276
277
  }, [fetchData, sessionExpired]);
277
278
  const volumeData = react.useMemo(() => {
278
- if (!stats) return [];
279
- const avg = stats.createdLast7Days;
280
- const days = ["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"];
281
- const base = Math.max(Math.floor(avg / 7), 0);
282
- return days.map((label, i) => ({
283
- label,
284
- // Simple deterministic variation based on index
285
- value: Math.max(base + (i * 3 + 1) % 5 - 2, 0)
279
+ if (!stats?.volumeByDay?.length) return [];
280
+ return stats.volumeByDay.map((d) => ({
281
+ label: new Date(d.date).toLocaleDateString(dateLocale.DATE_LOCALE, { weekday: "short" }),
282
+ value: d.count
286
283
  }));
287
284
  }, [stats]);
288
285
  if (loading) {
@@ -3,6 +3,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useState, useCallback, useEffect, useMemo } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
6
+ import { DATE_LOCALE } from '../shared/dateLocale.js';
6
7
  import styles from '../../styles/SupportDashboard.module.scss';
7
8
 
8
9
  function formatResponseTime(hours) {
@@ -32,6 +33,7 @@ function computeTrend(current, previous) {
32
33
  return { pct: Math.abs(pct), dir: pct > 0 ? "up" : "down" };
33
34
  }
34
35
  function StatCard({ label, value, trend, accentColor, onClick }) {
36
+ const { t } = useTranslation();
35
37
  const style = accentColor ? { "--stat-accent": accentColor } : void 0;
36
38
  const Tag = onClick ? "button" : "div";
37
39
  return /* @__PURE__ */ jsxs(
@@ -49,7 +51,7 @@ function StatCard({ label, value, trend, accentColor, onClick }) {
49
51
  trend.pct,
50
52
  "%"
51
53
  ] }),
52
- trend && trend.dir === "neutral" && /* @__PURE__ */ jsx("div", { className: `${styles.statTrend} ${styles.neutral}`, children: "\u2014 stable" })
54
+ trend && trend.dir === "neutral" && /* @__PURE__ */ jsx("div", { className: `${styles.statTrend} ${styles.neutral}`, children: t("dashboard.stable") })
53
55
  ]
54
56
  }
55
57
  );
@@ -73,6 +75,7 @@ function VolumeChart({ data }) {
73
75
  ] });
74
76
  }
75
77
  function CSATRing({ score, count }) {
78
+ const { t } = useTranslation();
76
79
  const pct = score > 0 ? score / 5 * 100 : 0;
77
80
  const radius = 42;
78
81
  const circumference = 2 * Math.PI * radius;
@@ -102,8 +105,8 @@ function CSATRing({ score, count }) {
102
105
  /* @__PURE__ */ jsx("div", { className: styles.csatValue, children: score > 0 ? score.toFixed(1) : "--" })
103
106
  ] }),
104
107
  /* @__PURE__ */ jsxs("div", { className: styles.csatMeta, children: [
105
- /* @__PURE__ */ jsx("div", { className: styles.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : "Pas de donn\xE9es" }),
106
- /* @__PURE__ */ jsx("div", { className: styles.csatSub, children: count > 0 ? `${count} avis recueillis` : "Aucun avis" })
108
+ /* @__PURE__ */ jsx("div", { className: styles.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : t("dashboard.csat.noData") }),
109
+ /* @__PURE__ */ jsx("div", { className: styles.csatSub, children: count > 0 ? t("dashboard.csat.reviews", { count }) : t("dashboard.csat.noReviews") })
107
110
  ] })
108
111
  ] });
109
112
  }
@@ -118,6 +121,7 @@ function formatSlaTime(minutes) {
118
121
  return remainH > 0 ? `${d}j ${remainH}h` : `${d}j`;
119
122
  }
120
123
  function SlaSection() {
124
+ const { t } = useTranslation();
121
125
  const [sla, setSla] = useState(null);
122
126
  const [slaLoading, setSlaLoading] = useState(true);
123
127
  useEffect(() => {
@@ -137,8 +141,8 @@ function SlaSection() {
137
141
  }, []);
138
142
  if (slaLoading) {
139
143
  return /* @__PURE__ */ jsxs("div", { className: styles.slaSection, children: [
140
- /* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: "SLA" }),
141
- /* @__PURE__ */ jsx("div", { className: styles.slaLoading, children: "Chargement..." })
144
+ /* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: t("dashboard.slaTitle") }),
145
+ /* @__PURE__ */ jsx("div", { className: styles.slaLoading, children: t("dashboard.sla.loading") })
142
146
  ] });
143
147
  }
144
148
  if (!sla) return null;
@@ -146,7 +150,7 @@ function SlaSection() {
146
150
  window.location.href = `/admin/support/ticket?id=${id}`;
147
151
  };
148
152
  return /* @__PURE__ */ jsxs("div", { className: styles.slaSection, children: [
149
- /* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: "SLA" }),
153
+ /* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: t("dashboard.slaTitle") }),
150
154
  /* @__PURE__ */ jsxs("div", { className: styles.slaGrid, children: [
151
155
  /* @__PURE__ */ jsxs("div", { className: `${styles.slaCard} ${styles.slaBreach}`, children: [
152
156
  /* @__PURE__ */ jsxs("div", { className: styles.slaCardHeader, children: [
@@ -156,11 +160,11 @@ function SlaSection() {
156
160
  /* @__PURE__ */ jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
157
161
  /* @__PURE__ */ jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
158
162
  ] }),
159
- "En breach"
163
+ t("dashboard.sla.breached")
160
164
  ] }),
161
165
  /* @__PURE__ */ jsx("span", { className: `${styles.slaCount} ${styles.slaCountBreach}`, children: sla.breached.length })
162
166
  ] }),
163
- sla.breached.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: "Aucun ticket en breach" }) : /* @__PURE__ */ jsx("ul", { className: styles.slaList, children: sla.breached.map((ticket) => {
167
+ sla.breached.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: t("dashboard.sla.noBreached") }) : /* @__PURE__ */ jsx("ul", { className: styles.slaList, children: sla.breached.map((ticket) => {
164
168
  const now = /* @__PURE__ */ new Date();
165
169
  const created = new Date(ticket.createdAt);
166
170
  const overdueMin = Math.round((now.getTime() - created.getTime()) / 6e4);
@@ -194,11 +198,11 @@ function SlaSection() {
194
198
  /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
195
199
  /* @__PURE__ */ jsx("polyline", { points: "12 6 12 12 16 14" })
196
200
  ] }),
197
- "\xC0 risque"
201
+ t("dashboard.sla.atRisk")
198
202
  ] }),
199
203
  /* @__PURE__ */ jsx("span", { className: `${styles.slaCount} ${styles.slaCountRisk}`, children: sla.atRisk.length })
200
204
  ] }),
201
- sla.atRisk.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: "Aucun ticket \xE0 risque" }) : /* @__PURE__ */ jsx("ul", { className: styles.slaList, children: sla.atRisk.map((ticket) => {
205
+ sla.atRisk.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: t("dashboard.sla.noAtRisk") }) : /* @__PURE__ */ jsx("ul", { className: styles.slaList, children: sla.atRisk.map((ticket) => {
202
206
  const now = /* @__PURE__ */ new Date();
203
207
  const created = new Date(ticket.createdAt);
204
208
  const elapsedMin = Math.round((now.getTime() - created.getTime()) / 6e4);
@@ -217,10 +221,7 @@ function SlaSection() {
217
221
  ] }),
218
222
  /* @__PURE__ */ jsx("span", { className: styles.slaItemSubject, children: ticket.subject })
219
223
  ] }),
220
- /* @__PURE__ */ jsxs("span", { className: `${styles.slaItemTime} ${styles.slaTimeRisk}`, children: [
221
- formatSlaTime(remainingMin),
222
- " restant"
223
- ] })
224
+ /* @__PURE__ */ jsx("span", { className: `${styles.slaItemTime} ${styles.slaTimeRisk}`, children: t("dashboard.sla.remaining", { time: formatSlaTime(remainingMin) }) })
224
225
  ]
225
226
  },
226
227
  ticket.id
@@ -269,14 +270,10 @@ const SupportDashboardClient = () => {
269
270
  return () => window.removeEventListener("focus", onFocus);
270
271
  }, [fetchData, sessionExpired]);
271
272
  const volumeData = useMemo(() => {
272
- if (!stats) return [];
273
- const avg = stats.createdLast7Days;
274
- const days = ["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"];
275
- const base = Math.max(Math.floor(avg / 7), 0);
276
- return days.map((label, i) => ({
277
- label,
278
- // Simple deterministic variation based on index
279
- value: Math.max(base + (i * 3 + 1) % 5 - 2, 0)
273
+ if (!stats?.volumeByDay?.length) return [];
274
+ return stats.volumeByDay.map((d) => ({
275
+ label: new Date(d.date).toLocaleDateString(DATE_LOCALE, { weekday: "short" }),
276
+ value: d.count
280
277
  }));
281
278
  }, [stats]);
282
279
  if (loading) {
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
5
+ var s = require('../../styles/TicketDetail.module.scss');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var s__default = /*#__PURE__*/_interopDefault(s);
10
+
11
+ const labelMap = { now: "Maintenant", today: "Aujourd'hui", "this-week": "Cette semaine" };
12
+ function NextActionItem({ action, index, onToggle }) {
13
+ const [pending, setPending] = react.useState(false);
14
+ const done = !!action.done;
15
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: s__default.default.aiCardNextAction, children: [
16
+ /* @__PURE__ */ jsxRuntime.jsx(
17
+ "button",
18
+ {
19
+ type: "button",
20
+ className: `${s__default.default.aiCardNextCheck} ${done ? s__default.default.aiCardNextCheckDone : ""}`,
21
+ onClick: async () => {
22
+ setPending(true);
23
+ try {
24
+ await onToggle(!done);
25
+ } finally {
26
+ setPending(false);
27
+ }
28
+ },
29
+ "aria-pressed": done,
30
+ "aria-label": done ? "Marquer non fait" : "Marquer fait",
31
+ disabled: pending,
32
+ children: done && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" })
33
+ }
34
+ ),
35
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${s__default.default.aiCardNextLabel} ${done ? s__default.default.aiCardNextLabelDone : ""}`, children: action.label }),
36
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiCardNextWhen, children: labelMap[action.priority] || action.priority })
37
+ ] });
38
+ }
39
+
40
+ exports.NextActionItem = NextActionItem;
@@ -0,0 +1,34 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import s from '../../styles/TicketDetail.module.scss';
4
+
5
+ const labelMap = { now: "Maintenant", today: "Aujourd'hui", "this-week": "Cette semaine" };
6
+ function NextActionItem({ action, index, onToggle }) {
7
+ const [pending, setPending] = useState(false);
8
+ const done = !!action.done;
9
+ return /* @__PURE__ */ jsxs("li", { className: s.aiCardNextAction, children: [
10
+ /* @__PURE__ */ jsx(
11
+ "button",
12
+ {
13
+ type: "button",
14
+ className: `${s.aiCardNextCheck} ${done ? s.aiCardNextCheckDone : ""}`,
15
+ onClick: async () => {
16
+ setPending(true);
17
+ try {
18
+ await onToggle(!done);
19
+ } finally {
20
+ setPending(false);
21
+ }
22
+ },
23
+ "aria-pressed": done,
24
+ "aria-label": done ? "Marquer non fait" : "Marquer fait",
25
+ disabled: pending,
26
+ children: done && /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2713" })
27
+ }
28
+ ),
29
+ /* @__PURE__ */ jsx("span", { className: `${s.aiCardNextLabel} ${done ? s.aiCardNextLabelDone : ""}`, children: action.label }),
30
+ /* @__PURE__ */ jsx("span", { className: s.aiCardNextWhen, children: labelMap[action.priority] || action.priority })
31
+ ] });
32
+ }
33
+
34
+ export { NextActionItem };