@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
@@ -5,6 +5,7 @@ var react = require('react');
5
5
  var navigation = require('next/navigation');
6
6
  var Link = require('next/link');
7
7
  var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
8
+ var dateLocale = require('../shared/dateLocale');
8
9
  var StatusPill = require('../shared/StatusPill');
9
10
  var sla = require('../shared/sla');
10
11
  var s = require('../../styles/TicketInbox.module.scss');
@@ -27,16 +28,16 @@ const STATUS_VARIANT = {
27
28
  resolved: "resolved",
28
29
  closed: "closed"
29
30
  };
30
- function relativeTime(dateStr) {
31
+ function relativeTime(dateStr, tFn) {
31
32
  const diff = Date.now() - new Date(dateStr).getTime();
32
33
  const mins = Math.floor(diff / 6e4);
33
- if (mins < 1) return "maintenant";
34
+ if (mins < 1) return tFn("inbox.timeAgo.now");
34
35
  if (mins < 60) return `${mins}m`;
35
36
  const hours = Math.floor(mins / 60);
36
37
  if (hours < 24) return `${hours}h`;
37
38
  const days = Math.floor(hours / 24);
38
39
  if (days < 7) return `${days}j`;
39
- return new Date(dateStr).toLocaleDateString("fr-FR", { day: "numeric", month: "short" });
40
+ return new Date(dateStr).toLocaleDateString(dateLocale.DATE_LOCALE, { day: "numeric", month: "short" });
40
41
  }
41
42
  const TicketInboxClient = () => {
42
43
  const { t } = useTranslation.useTranslation();
@@ -121,6 +122,8 @@ const TicketInboxClient = () => {
121
122
  params.push(`where[or][0][subject][contains]=${encodeURIComponent(search)}`);
122
123
  params.push(`where[or][1][ticketNumber][contains]=${encodeURIComponent(search)}`);
123
124
  }
125
+ params.push(`where[and][0][or][0][snoozeUntil][exists]=false`);
126
+ params.push(`where[and][0][or][1][snoozeUntil][less_than_equal]=${encodeURIComponent((/* @__PURE__ */ new Date()).toISOString())}`);
124
127
  try {
125
128
  const url = `/api/tickets?${params.join("&")}`;
126
129
  const res = await fetch(url, { credentials: "include" });
@@ -142,12 +145,13 @@ const TicketInboxClient = () => {
142
145
  react.useEffect(() => {
143
146
  const fetchCounts = async () => {
144
147
  try {
148
+ const sn = `where[and][0][or][0][snoozeUntil][exists]=false&where[and][0][or][1][snoozeUntil][less_than_equal]=${encodeURIComponent((/* @__PURE__ */ new Date()).toISOString())}`;
145
149
  const [all, openRes, waiting, resolved, breach] = await Promise.all([
146
- fetch("/api/tickets?limit=0&depth=0", { credentials: "include" }),
147
- fetch("/api/tickets?limit=0&depth=0&where[status][equals]=open", { credentials: "include" }),
148
- fetch("/api/tickets?limit=0&depth=0&where[status][equals]=waiting_client", { credentials: "include" }),
149
- fetch("/api/tickets?limit=0&depth=0&where[status][equals]=resolved", { credentials: "include" }),
150
- fetch("/api/tickets?limit=0&depth=0&where[or][0][slaFirstResponseBreached][equals]=true&where[or][1][slaResolutionBreached][equals]=true", { credentials: "include" })
150
+ fetch(`/api/tickets?limit=0&depth=0&${sn}`, { credentials: "include" }),
151
+ fetch(`/api/tickets?limit=0&depth=0&where[status][equals]=open&${sn}`, { credentials: "include" }),
152
+ fetch(`/api/tickets?limit=0&depth=0&where[status][equals]=waiting_client&${sn}`, { credentials: "include" }),
153
+ fetch(`/api/tickets?limit=0&depth=0&where[status][equals]=resolved&${sn}`, { credentials: "include" }),
154
+ fetch(`/api/tickets?limit=0&depth=0&where[or][0][slaFirstResponseBreached][equals]=true&where[or][1][slaResolutionBreached][equals]=true&${sn}`, { credentials: "include" })
151
155
  ]);
152
156
  const [a, o, w, r, b] = await Promise.all([all.json(), openRes.json(), waiting.json(), resolved.json(), breach.json()]);
153
157
  setCounts({
@@ -325,11 +329,11 @@ const TicketInboxClient = () => {
325
329
  sla$1.state === "warn" ? s__default.default.slaWarn : "",
326
330
  sla$1.state === "ok" ? s__default.default.slaOk : ""
327
331
  ].filter(Boolean).join(" "),
328
- title: sla$1.due ? new Date(sla$1.due).toLocaleString("fr-FR") : "",
332
+ title: sla$1.due ? new Date(sla$1.due).toLocaleString(dateLocale.DATE_LOCALE) : "",
329
333
  children: sla.formatSlaRemaining(sla$1.remainingMs)
330
334
  }
331
335
  ),
332
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.timeAgo, children: relativeTime(tk.updatedAt) }),
336
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.timeAgo, children: relativeTime(tk.updatedAt, t) }),
333
337
  isUnread ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.unreadDot }) : /* @__PURE__ */ jsxRuntime.jsx("span", {})
334
338
  ]
335
339
  },
@@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from 'react';
4
4
  import { useSearchParams } from 'next/navigation';
5
5
  import Link from 'next/link';
6
6
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
7
+ import { DATE_LOCALE } from '../shared/dateLocale.js';
7
8
  import { StatusPill } from '../shared/StatusPill.js';
8
9
  import { computeSlaState, formatSlaRemaining } from '../shared/sla.js';
9
10
  import s from '../../styles/TicketInbox.module.scss';
@@ -21,16 +22,16 @@ const STATUS_VARIANT = {
21
22
  resolved: "resolved",
22
23
  closed: "closed"
23
24
  };
24
- function relativeTime(dateStr) {
25
+ function relativeTime(dateStr, tFn) {
25
26
  const diff = Date.now() - new Date(dateStr).getTime();
26
27
  const mins = Math.floor(diff / 6e4);
27
- if (mins < 1) return "maintenant";
28
+ if (mins < 1) return tFn("inbox.timeAgo.now");
28
29
  if (mins < 60) return `${mins}m`;
29
30
  const hours = Math.floor(mins / 60);
30
31
  if (hours < 24) return `${hours}h`;
31
32
  const days = Math.floor(hours / 24);
32
33
  if (days < 7) return `${days}j`;
33
- return new Date(dateStr).toLocaleDateString("fr-FR", { day: "numeric", month: "short" });
34
+ return new Date(dateStr).toLocaleDateString(DATE_LOCALE, { day: "numeric", month: "short" });
34
35
  }
35
36
  const TicketInboxClient = () => {
36
37
  const { t } = useTranslation();
@@ -115,6 +116,8 @@ const TicketInboxClient = () => {
115
116
  params.push(`where[or][0][subject][contains]=${encodeURIComponent(search)}`);
116
117
  params.push(`where[or][1][ticketNumber][contains]=${encodeURIComponent(search)}`);
117
118
  }
119
+ params.push(`where[and][0][or][0][snoozeUntil][exists]=false`);
120
+ params.push(`where[and][0][or][1][snoozeUntil][less_than_equal]=${encodeURIComponent((/* @__PURE__ */ new Date()).toISOString())}`);
118
121
  try {
119
122
  const url = `/api/tickets?${params.join("&")}`;
120
123
  const res = await fetch(url, { credentials: "include" });
@@ -136,12 +139,13 @@ const TicketInboxClient = () => {
136
139
  useEffect(() => {
137
140
  const fetchCounts = async () => {
138
141
  try {
142
+ const sn = `where[and][0][or][0][snoozeUntil][exists]=false&where[and][0][or][1][snoozeUntil][less_than_equal]=${encodeURIComponent((/* @__PURE__ */ new Date()).toISOString())}`;
139
143
  const [all, openRes, waiting, resolved, breach] = await Promise.all([
140
- fetch("/api/tickets?limit=0&depth=0", { credentials: "include" }),
141
- fetch("/api/tickets?limit=0&depth=0&where[status][equals]=open", { credentials: "include" }),
142
- fetch("/api/tickets?limit=0&depth=0&where[status][equals]=waiting_client", { credentials: "include" }),
143
- fetch("/api/tickets?limit=0&depth=0&where[status][equals]=resolved", { credentials: "include" }),
144
- fetch("/api/tickets?limit=0&depth=0&where[or][0][slaFirstResponseBreached][equals]=true&where[or][1][slaResolutionBreached][equals]=true", { credentials: "include" })
144
+ fetch(`/api/tickets?limit=0&depth=0&${sn}`, { credentials: "include" }),
145
+ fetch(`/api/tickets?limit=0&depth=0&where[status][equals]=open&${sn}`, { credentials: "include" }),
146
+ fetch(`/api/tickets?limit=0&depth=0&where[status][equals]=waiting_client&${sn}`, { credentials: "include" }),
147
+ fetch(`/api/tickets?limit=0&depth=0&where[status][equals]=resolved&${sn}`, { credentials: "include" }),
148
+ fetch(`/api/tickets?limit=0&depth=0&where[or][0][slaFirstResponseBreached][equals]=true&where[or][1][slaResolutionBreached][equals]=true&${sn}`, { credentials: "include" })
145
149
  ]);
146
150
  const [a, o, w, r, b] = await Promise.all([all.json(), openRes.json(), waiting.json(), resolved.json(), breach.json()]);
147
151
  setCounts({
@@ -319,11 +323,11 @@ const TicketInboxClient = () => {
319
323
  sla.state === "warn" ? s.slaWarn : "",
320
324
  sla.state === "ok" ? s.slaOk : ""
321
325
  ].filter(Boolean).join(" "),
322
- title: sla.due ? new Date(sla.due).toLocaleString("fr-FR") : "",
326
+ title: sla.due ? new Date(sla.due).toLocaleString(DATE_LOCALE) : "",
323
327
  children: formatSlaRemaining(sla.remainingMs)
324
328
  }
325
329
  ),
326
- /* @__PURE__ */ jsx("span", { className: s.timeAgo, children: relativeTime(tk.updatedAt) }),
330
+ /* @__PURE__ */ jsx("span", { className: s.timeAgo, children: relativeTime(tk.updatedAt, t) }),
327
331
  isUnread ? /* @__PURE__ */ jsx("div", { className: s.unreadDot }) : /* @__PURE__ */ jsx("span", {})
328
332
  ]
329
333
  },
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
- var react = require('react');
4
+ var React = require('react');
5
5
  var lucideReact = require('lucide-react');
6
6
  var adminTokens = require('../shared/adminTokens');
7
7
  var AdminViewHeader = require('../shared/AdminViewHeader');
@@ -11,6 +11,7 @@ var ts = require('../../styles/TicketingSettings.module.scss');
11
11
 
12
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
13
 
14
+ var React__default = /*#__PURE__*/_interopDefault(React);
14
15
  var ts__default = /*#__PURE__*/_interopDefault(ts);
15
16
 
16
17
  const DEFAULT_SETTINGS = {
@@ -102,31 +103,35 @@ async function saveUserPrefs(prefs) {
102
103
  return false;
103
104
  }
104
105
  }
105
- const FEATURE_LIST = [
106
- // Core
107
- { key: "canned", label: "R\xE9ponses rapides", description: "Templates de r\xE9ponses pr\xE9-enregistr\xE9es avec variables dynamiques", category: "core" },
108
- { key: "scheduledReplies", label: "R\xE9ponses programm\xE9es", description: "Envoyer une r\xE9ponse \xE0 une date/heure future", category: "core" },
109
- { key: "activityLog", label: "Journal d'activit\xE9", description: "Timeline des actions sur chaque ticket (changements de statut, assignation...)", category: "core" },
110
- // Communication
111
- { key: "emailTracking", label: "Suivi des emails", description: "Tracking d'envoi et d'ouverture des notifications email", category: "communication" },
112
- { key: "chat", label: "Live Chat", description: "Chat en temps r\xE9el avec conversion en ticket", category: "communication" },
113
- { key: "externalMessages", label: "Messages externes", description: "Ajouter manuellement des messages re\xE7us par email, SMS, WhatsApp...", category: "communication" },
114
- // Productivity
115
- { key: "ai", label: "Intelligence Artificielle", description: "Analyse de sentiment, synth\xE8se, suggestion de r\xE9ponse, reformulation", category: "productivity" },
116
- { key: "timeTracking", label: "Suivi du temps", description: "Timer, entr\xE9es manuelles, facturation", category: "productivity" },
117
- { key: "satisfaction", label: "Enqu\xEAtes satisfaction", description: "Score CSAT apr\xE8s r\xE9solution du ticket", category: "productivity" },
118
- // Advanced
119
- { key: "merge", label: "Fusion de tickets", description: "Combiner deux tickets en un seul", category: "advanced" },
120
- { key: "splitTicket", label: "Extraction de message", description: "Extraire un message en nouveau ticket li\xE9", category: "advanced" },
121
- { key: "snooze", label: "Snooze", description: "Masquer temporairement un ticket et rappel automatique", category: "advanced" },
122
- { key: "clientHistory", label: "Historique client", description: "Tickets pass\xE9s, projets et notes internes du client", category: "advanced" }
123
- ];
124
- const CATEGORIES = [
125
- { key: "core", label: "Fonctionnalit\xE9s de base", color: adminTokens.V.blue },
126
- { key: "communication", label: "Communication", color: adminTokens.V.green },
127
- { key: "productivity", label: "Productivit\xE9", color: adminTokens.V.amber },
128
- { key: "advanced", label: "Avanc\xE9", color: "#7c3aed" }
129
- ];
106
+ function getFeatureList(t) {
107
+ return [
108
+ // Core
109
+ { key: "canned", label: t("settingsView.features.canned.label"), description: t("settingsView.features.canned.description"), category: "core" },
110
+ { key: "scheduledReplies", label: t("settingsView.features.scheduledReplies.label"), description: t("settingsView.features.scheduledReplies.description"), category: "core" },
111
+ { key: "activityLog", label: t("settingsView.features.activityLog.label"), description: t("settingsView.features.activityLog.description"), category: "core" },
112
+ // Communication
113
+ { key: "emailTracking", label: t("settingsView.features.emailTracking.label"), description: t("settingsView.features.emailTracking.description"), category: "communication" },
114
+ { key: "chat", label: t("settingsView.features.chat.label"), description: t("settingsView.features.chat.description"), category: "communication" },
115
+ { key: "externalMessages", label: t("settingsView.features.externalMessages.label"), description: t("settingsView.features.externalMessages.description"), category: "communication" },
116
+ // Productivity
117
+ { key: "ai", label: t("settingsView.features.ai.label"), description: t("settingsView.features.ai.description"), category: "productivity" },
118
+ { key: "timeTracking", label: t("settingsView.features.timeTracking.label"), description: t("settingsView.features.timeTracking.description"), category: "productivity" },
119
+ { key: "satisfaction", label: t("settingsView.features.satisfaction.label"), description: t("settingsView.features.satisfaction.description"), category: "productivity" },
120
+ // Advanced
121
+ { key: "merge", label: t("settingsView.features.merge.label"), description: t("settingsView.features.merge.description"), category: "advanced" },
122
+ { key: "splitTicket", label: t("settingsView.features.splitTicket.label"), description: t("settingsView.features.splitTicket.description"), category: "advanced" },
123
+ { key: "snooze", label: t("settingsView.features.snooze.label"), description: t("settingsView.features.snooze.description"), category: "advanced" },
124
+ { key: "clientHistory", label: t("settingsView.features.clientHistory.label"), description: t("settingsView.features.clientHistory.description"), category: "advanced" }
125
+ ];
126
+ }
127
+ function getCategories(t) {
128
+ return [
129
+ { key: "core", label: t("settingsView.categories.core"), color: adminTokens.V.blue },
130
+ { key: "communication", label: t("settingsView.categories.communication"), color: adminTokens.V.green },
131
+ { key: "productivity", label: t("settingsView.categories.productivity"), color: adminTokens.V.amber },
132
+ { key: "advanced", label: t("settingsView.categories.advanced"), color: "#7c3aed" }
133
+ ];
134
+ }
130
135
  const Toggle = ({ checked, onChange, color = adminTokens.V.blue, size = "md" }) => {
131
136
  const w = size === "sm" ? 36 : 40;
132
137
  const h = size === "sm" ? 20 : 22;
@@ -169,7 +174,7 @@ const Toggle = ({ checked, onChange, color = adminTokens.V.blue, size = "md" })
169
174
  );
170
175
  };
171
176
  const CollapsibleSection = ({ title, icon, color, defaultOpen = true, children, badge }) => {
172
- const [open, setOpen] = react.useState(defaultOpen);
177
+ const [open, setOpen] = React.useState(defaultOpen);
173
178
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.sectionWrapper, children: [
174
179
  /* @__PURE__ */ jsxRuntime.jsxs(
175
180
  "div",
@@ -205,14 +210,16 @@ const FieldRow = ({ label, description, children }) => /* @__PURE__ */ jsxRuntim
205
210
  ] });
206
211
  const TicketingSettingsClient = () => {
207
212
  const { t } = useTranslation.useTranslation();
208
- const [features, setFeatures] = react.useState(() => config.getFeatures());
209
- const [settings, setSettings] = react.useState(DEFAULT_SETTINGS);
210
- const [signature, setSignature] = react.useState("");
211
- const [saved, setSaved] = react.useState(false);
212
- const [saving, setSaving] = react.useState(false);
213
- const [showApiKey, setShowApiKey] = react.useState(false);
214
- const [loadingSettings, setLoadingSettings] = react.useState(true);
215
- react.useEffect(() => {
213
+ const FEATURE_LIST = React__default.default.useMemo(() => getFeatureList(t), [t]);
214
+ const CATEGORIES = React__default.default.useMemo(() => getCategories(t), [t]);
215
+ const [features, setFeatures] = React.useState(() => config.getFeatures());
216
+ const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);
217
+ const [signature, setSignature] = React.useState("");
218
+ const [saved, setSaved] = React.useState(false);
219
+ const [saving, setSaving] = React.useState(false);
220
+ const [showApiKey, setShowApiKey] = React.useState(false);
221
+ const [loadingSettings, setLoadingSettings] = React.useState(true);
222
+ React.useEffect(() => {
216
223
  let cancelled = false;
217
224
  Promise.all([fetchSettingsFromAPI(), fetchUserPrefs()]).then(([s, prefs]) => {
218
225
  if (!cancelled) {
@@ -350,13 +357,13 @@ const TicketingSettingsClient = () => {
350
357
  /* @__PURE__ */ jsxRuntime.jsxs(
351
358
  CollapsibleSection,
352
359
  {
353
- title: "Configuration Email",
360
+ title: t("settingsView.emailConfig"),
354
361
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mail, { size: 16 }),
355
362
  color: "#ea580c",
356
363
  defaultOpen: false,
357
364
  children: [
358
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: "Parametres d'envoi des notifications email. L'adresse SMTP est configuree via les variables d'environnement du serveur." }),
359
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Adresse expediteur", description: "Adresse email affichee dans le champ From", children: /* @__PURE__ */ jsxRuntime.jsx(
365
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: t("settingsView.emailDescription") }),
366
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.senderAddress"), description: t("settingsView.senderAddressDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(
360
367
  "input",
361
368
  {
362
369
  type: "email",
@@ -366,7 +373,7 @@ const TicketingSettingsClient = () => {
366
373
  className: ts__default.default.input
367
374
  }
368
375
  ) }),
369
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Nom expediteur", description: "Nom affiche a cote de l'adresse email", children: /* @__PURE__ */ jsxRuntime.jsx(
376
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.senderName"), description: t("settingsView.senderNameDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(
370
377
  "input",
371
378
  {
372
379
  type: "text",
@@ -376,7 +383,7 @@ const TicketingSettingsClient = () => {
376
383
  className: ts__default.default.input
377
384
  }
378
385
  ) }),
379
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Adresse Reply-To", description: "Si differente de l'adresse expediteur", children: /* @__PURE__ */ jsxRuntime.jsx(
386
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.replyTo"), description: t("settingsView.replyToDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(
380
387
  "input",
381
388
  {
382
389
  type: "email",
@@ -387,7 +394,7 @@ const TicketingSettingsClient = () => {
387
394
  }
388
395
  ) }),
389
396
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: ts__default.default.separator }),
390
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Serveur SMTP", description: "Configure via variables d'environnement", children: /* @__PURE__ */ jsxRuntime.jsx(
397
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.smtpServer"), description: t("settingsView.smtpServerDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(
391
398
  "input",
392
399
  {
393
400
  type: "text",
@@ -396,7 +403,7 @@ const TicketingSettingsClient = () => {
396
403
  className: ts__default.default.inputReadonly
397
404
  }
398
405
  ) }),
399
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Port SMTP", children: /* @__PURE__ */ jsxRuntime.jsx(
406
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.smtpPort"), children: /* @__PURE__ */ jsxRuntime.jsx(
400
407
  "input",
401
408
  {
402
409
  type: "text",
@@ -412,14 +419,14 @@ const TicketingSettingsClient = () => {
412
419
  /* @__PURE__ */ jsxRuntime.jsxs(
413
420
  CollapsibleSection,
414
421
  {
415
- title: "Intelligence Artificielle",
422
+ title: t("settingsView.aiTitle"),
416
423
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bot, { size: 16 }),
417
424
  color: "#7c3aed",
418
425
  defaultOpen: false,
419
- badge: features.ai ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.badge, style: { backgroundColor: "#dcfce7", color: "#166534" }, children: "Active" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.badge, style: { backgroundColor: "#fee2e2", color: "#991b1b" }, children: "Inactive" }),
426
+ badge: features.ai ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.badge, style: { backgroundColor: "#dcfce7", color: "#166534" }, children: t("settingsView.aiBadgeActive") }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.badge, style: { backgroundColor: "#fee2e2", color: "#991b1b" }, children: t("settingsView.aiBadgeInactive") }),
420
427
  children: [
421
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: `Configurez le fournisseur d'IA et activez/d\xE9sactivez chaque fonctionnalit\xE9 ind\xE9pendamment. Les fonctionnalit\xE9s IA n\xE9cessitent que le flag "Intelligence Artificielle" soit actif dans la section pr\xE9c\xE9dente.` }),
422
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Fournisseur", description: "Service d'IA utilise pour l'analyse", children: /* @__PURE__ */ jsxRuntime.jsxs(
428
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: t("settingsView.aiDescription") }),
429
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.aiProvider"), description: t("settingsView.aiProviderDesc"), children: /* @__PURE__ */ jsxRuntime.jsxs(
423
430
  "select",
424
431
  {
425
432
  value: settings.ai.provider,
@@ -433,7 +440,7 @@ const TicketingSettingsClient = () => {
433
440
  ]
434
441
  }
435
442
  ) }),
436
- settings.ai.provider !== "ollama" && /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Cle API", description: "Cle secrete du fournisseur (non stockee en clair)", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.apiKeyRow, children: [
443
+ settings.ai.provider !== "ollama" && /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.aiApiKey"), description: t("settingsView.aiApiKeyDesc"), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.apiKeyRow, children: [
437
444
  /* @__PURE__ */ jsxRuntime.jsx(
438
445
  "input",
439
446
  {
@@ -450,11 +457,11 @@ const TicketingSettingsClient = () => {
450
457
  {
451
458
  onClick: () => setShowApiKey(!showApiKey),
452
459
  className: ts__default.default.apiKeyToggle,
453
- children: showApiKey ? "Masquer" : "Afficher"
460
+ children: showApiKey ? t("settingsView.hideKey") : t("settingsView.showKey")
454
461
  }
455
462
  )
456
463
  ] }) }),
457
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Modele", description: "Nom du modele a utiliser", children: /* @__PURE__ */ jsxRuntime.jsx(
464
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.aiModel"), description: t("settingsView.aiModelDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(
458
465
  "input",
459
466
  {
460
467
  type: "text",
@@ -465,12 +472,12 @@ const TicketingSettingsClient = () => {
465
472
  }
466
473
  ) }),
467
474
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: ts__default.default.separator }),
468
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.aiSubFeaturesLabel, children: "Fonctionnalit\xE9s IA individuelles" }),
475
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.aiSubFeaturesLabel, children: t("settingsView.aiSubFeatures") }),
469
476
  [
470
- { key: "enableSentiment", label: "Analyse de sentiment", desc: "Detecte le niveau de frustration ou satisfaction du client" },
471
- { key: "enableSynthesis", label: "Synthese automatique", desc: "Resume les conversations longues en quelques phrases" },
472
- { key: "enableSuggestion", label: "Suggestion de reponse", desc: "Propose un brouillon de reponse base sur le contexte" },
473
- { key: "enableRewrite", label: "Reformulation", desc: "Reformule un message pour le rendre plus professionnel" }
477
+ { key: "enableSentiment", label: t("settingsView.aiSentiment"), desc: t("settingsView.aiSentimentDesc") },
478
+ { key: "enableSynthesis", label: t("settingsView.aiSynthesis"), desc: t("settingsView.aiSynthesisDesc") },
479
+ { key: "enableSuggestion", label: t("settingsView.aiSuggestion"), desc: t("settingsView.aiSuggestionDesc") },
480
+ { key: "enableRewrite", label: t("settingsView.aiRewrite"), desc: t("settingsView.aiRewriteDesc") }
474
481
  ].map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.aiToggleRow, children: [
475
482
  /* @__PURE__ */ jsxRuntime.jsx(
476
483
  Toggle,
@@ -492,13 +499,13 @@ const TicketingSettingsClient = () => {
492
499
  /* @__PURE__ */ jsxRuntime.jsxs(
493
500
  CollapsibleSection,
494
501
  {
495
- title: "SLA (Accords de niveau de service)",
502
+ title: t("settingsView.slaTitle"),
496
503
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { size: 16 }),
497
504
  color: "#0891b2",
498
505
  defaultOpen: false,
499
506
  children: [
500
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: "Definissez les delais de reponse et de resolution attendus. Ces seuils sont utilises pour le suivi de performance et les alertes d'escalade." }),
501
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Premiere reponse", description: "D\xE9lai maximum en minutes (d\xE9faut : 120 = 2h)", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
507
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: t("settingsView.slaDescription") }),
508
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.slaFirstResponse"), description: t("settingsView.slaFirstResponseDesc"), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
502
509
  /* @__PURE__ */ jsxRuntime.jsx(
503
510
  "input",
504
511
  {
@@ -510,14 +517,15 @@ const TicketingSettingsClient = () => {
510
517
  }
511
518
  ),
512
519
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: ts__default.default.slaHint, children: [
513
- "minutes (",
520
+ t("settingsView.minutes"),
521
+ " (",
514
522
  Math.floor(settings.sla.firstResponseMinutes / 60),
515
523
  "h",
516
524
  String(settings.sla.firstResponseMinutes % 60).padStart(2, "0"),
517
525
  ")"
518
526
  ] })
519
527
  ] }) }),
520
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "R\xE9solution", description: "D\xE9lai maximum en minutes (d\xE9faut : 1440 = 24h)", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
528
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.slaResolution"), description: t("settingsView.slaResolutionDesc"), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
521
529
  /* @__PURE__ */ jsxRuntime.jsx(
522
530
  "input",
523
531
  {
@@ -529,7 +537,8 @@ const TicketingSettingsClient = () => {
529
537
  }
530
538
  ),
531
539
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: ts__default.default.slaHint, children: [
532
- "minutes (",
540
+ t("settingsView.minutes"),
541
+ " (",
533
542
  Math.floor(settings.sla.resolutionMinutes / 60),
534
543
  "h",
535
544
  String(settings.sla.resolutionMinutes % 60).padStart(2, "0"),
@@ -547,12 +556,12 @@ const TicketingSettingsClient = () => {
547
556
  }
548
557
  ),
549
558
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
550
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.inlineLabel, children: "Heures ouvrables uniquement" }),
551
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: ts__default.default.inlineDesc, children: "Le decompte SLA est suspendu en dehors des heures de bureau (Lun-Ven, 9h-18h)" })
559
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.inlineLabel, children: t("settingsView.businessHoursOnly") }),
560
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: ts__default.default.inlineDesc, children: t("settingsView.businessHoursDesc") })
552
561
  ] })
553
562
  ] }),
554
563
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: ts__default.default.separator }),
555
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Email d'escalade", description: "Adresse notifiee en cas de depassement SLA", children: /* @__PURE__ */ jsxRuntime.jsx(
564
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.escalationEmail"), description: t("settingsView.escalationEmailDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(
556
565
  "input",
557
566
  {
558
567
  type: "email",
@@ -568,7 +577,7 @@ const TicketingSettingsClient = () => {
568
577
  /* @__PURE__ */ jsxRuntime.jsxs(
569
578
  CollapsibleSection,
570
579
  {
571
- title: "Fermeture automatique",
580
+ title: t("settingsView.autoCloseTitle"),
572
581
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Timer, { size: 16 }),
573
582
  color: "#d97706",
574
583
  defaultOpen: false,
@@ -577,7 +586,7 @@ const TicketingSettingsClient = () => {
577
586
  "j"
578
587
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.badge, style: { backgroundColor: "#e5e7eb", color: "#374151" }, children: "Off" }),
579
588
  children: [
580
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: "Les tickets en attente client sans reponse seront automatiquement resolus apres le delai configure. Un email de rappel est envoye avant la fermeture." }),
589
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: t("settingsView.autoCloseDescription") }),
581
590
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.toggleRow, style: { paddingBottom: 14 }, children: [
582
591
  /* @__PURE__ */ jsxRuntime.jsx(
583
592
  Toggle,
@@ -587,10 +596,10 @@ const TicketingSettingsClient = () => {
587
596
  color: "#d97706"
588
597
  }
589
598
  ),
590
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.inlineLabel, children: "Activer la fermeture automatique" })
599
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.inlineLabel, children: t("settingsView.autoCloseEnable") })
591
600
  ] }),
592
601
  settings.autoClose.enabled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
593
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Delai avant fermeture", description: "Nombre de jours sans reponse du client", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
602
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.autoCloseDelay"), description: t("settingsView.autoCloseDelayDesc"), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
594
603
  /* @__PURE__ */ jsxRuntime.jsx(
595
604
  "input",
596
605
  {
@@ -602,9 +611,9 @@ const TicketingSettingsClient = () => {
602
611
  className: ts__default.default.numberInput
603
612
  }
604
613
  ),
605
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.slaHint, children: "jours" })
614
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.slaHint, children: t("settingsView.days") })
606
615
  ] }) }),
607
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Rappel avant fermeture", description: "Email de rappel envoye X jours avant", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
616
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.autoCloseReminder"), description: t("settingsView.autoCloseReminderDesc"), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.slaInline, children: [
608
617
  /* @__PURE__ */ jsxRuntime.jsx(
609
618
  "input",
610
619
  {
@@ -616,11 +625,7 @@ const TicketingSettingsClient = () => {
616
625
  className: ts__default.default.numberInput
617
626
  }
618
627
  ),
619
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: ts__default.default.slaHint, children: [
620
- "jours avant (rappel a J-",
621
- settings.autoClose.reminderDaysBefore,
622
- ")"
623
- ] })
628
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.slaHint, children: t("settingsView.autoCloseDaysBeforeReminder", { count: String(settings.autoClose.reminderDaysBefore) }) })
624
629
  ] }) })
625
630
  ] })
626
631
  ]
@@ -634,27 +639,27 @@ const TicketingSettingsClient = () => {
634
639
  background: "linear-gradient(135deg, #dbeafe 0%, #ede9fe 100%)",
635
640
  border: "1px solid #c7d2fe"
636
641
  }, children: [
637
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 700, fontSize: 15, color: "#1e293b" }, children: "Mes preferences" }),
638
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, color: "#64748b", marginTop: 2 }, children: "Ces reglages sont propres a votre compte et ne s'appliquent qu'a vous." })
642
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 700, fontSize: 15, color: "#1e293b" }, children: t("settingsView.myPreferences") }),
643
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, color: "#64748b", marginTop: 2 }, children: t("settingsView.myPreferencesDesc") })
639
644
  ] }),
640
645
  /* @__PURE__ */ jsxRuntime.jsxs(
641
646
  CollapsibleSection,
642
647
  {
643
- title: "Langue et localisation",
648
+ title: t("settingsView.localeTitle"),
644
649
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { size: 16 }),
645
650
  color: "#16a34a",
646
651
  defaultOpen: false,
647
652
  children: [
648
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: "Langue de l'interface du module de support et des notifications email envoyees aux clients." }),
649
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Langue", description: "Langue principale du module", children: /* @__PURE__ */ jsxRuntime.jsxs(
653
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: t("settingsView.localeDescription") }),
654
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: t("settingsView.language"), description: t("settingsView.languageDesc"), children: /* @__PURE__ */ jsxRuntime.jsxs(
650
655
  "select",
651
656
  {
652
657
  value: settings.locale.language,
653
658
  onChange: (e) => updateLocale("language", e.target.value),
654
659
  className: ts__default.default.select,
655
660
  children: [
656
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "fr", children: "Francais" }),
657
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "en", children: "English" })
661
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "fr", children: t("settingsView.french") }),
662
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "en", children: t("settingsView.english") })
658
663
  ]
659
664
  }
660
665
  ) })
@@ -664,12 +669,12 @@ const TicketingSettingsClient = () => {
664
669
  /* @__PURE__ */ jsxRuntime.jsxs(
665
670
  CollapsibleSection,
666
671
  {
667
- title: "Signature email",
672
+ title: t("settingsView.signatureTitle"),
668
673
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileSignature, { size: 16 }),
669
674
  color: "#6366f1",
670
675
  defaultOpen: false,
671
676
  children: [
672
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: "Signature ajoutee automatiquement en bas de chaque reponse email envoyee au client. Supporte le texte brut et le HTML basique." }),
677
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: t("settingsView.signatureDescription") }),
673
678
  /* @__PURE__ */ jsxRuntime.jsx(
674
679
  "textarea",
675
680
  {
@@ -678,7 +683,7 @@ const TicketingSettingsClient = () => {
678
683
  setSignature(e.target.value);
679
684
  setSaved(false);
680
685
  },
681
- placeholder: "Cordialement,\nL'equipe ConsilioWEB",
686
+ placeholder: t("settingsView.signaturePlaceholder"),
682
687
  rows: 6,
683
688
  className: ts__default.default.signatureTextarea
684
689
  }
@@ -686,28 +691,28 @@ const TicketingSettingsClient = () => {
686
691
  ]
687
692
  }
688
693
  ),
689
- /* @__PURE__ */ jsxRuntime.jsxs(CollapsibleSection, { title: "Purge des logs", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { size: 18 }), color: "#ef4444", defaultOpen: false, children: [
690
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: "Supprimez les anciens logs pour lib\xE9rer de l'espace. Cette action est irr\xE9versible." }),
694
+ /* @__PURE__ */ jsxRuntime.jsxs(CollapsibleSection, { title: t("settingsView.purgeTitle"), icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { size: 18 }), color: "#ef4444", defaultOpen: false, children: [
695
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: ts__default.default.sectionDescription, children: t("settingsView.purgeDescription") }),
691
696
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: ts__default.default.purgeGroup, children: ["email-logs", "auth-logs"].map((col) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.purgeCategory, children: [
692
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.purgeCategoryLabel, children: col === "email-logs" ? "Logs Email" : "Logs Auth" }),
697
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: ts__default.default.purgeCategoryLabel, children: col === "email-logs" ? t("settingsView.purgeEmailLogs") : t("settingsView.purgeAuthLogs") }),
693
698
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: ts__default.default.purgeButtons, children: [
694
- { label: "7 jours", days: 7 },
695
- { label: "30 jours", days: 30 },
696
- { label: "90 jours", days: 90 },
697
- { label: "Tout", days: 0 }
699
+ { label: t("settingsView.purge7days"), days: 7 },
700
+ { label: t("settingsView.purge30days"), days: 30 },
701
+ { label: t("settingsView.purge90days"), days: 90 },
702
+ { label: t("settingsView.purgeAllLabel"), days: 0 }
698
703
  ].map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
699
704
  "button",
700
705
  {
701
706
  onClick: async () => {
702
- if (!window.confirm(`Supprimer les logs ${col} de plus de ${opt.days || "tous les"} jours ?`)) return;
707
+ if (!window.confirm(t("settingsView.purgeConfirm", { collection: col, days: opt.days === 0 ? t("settingsView.purgeAllLabel") : String(opt.days) }))) return;
703
708
  try {
704
709
  const res = await fetch(`/api/support/purge-logs?collection=${col}&days=${opt.days}`, { method: "DELETE", credentials: "include" });
705
710
  if (res.ok) {
706
711
  const d = await res.json();
707
- alert(`${d.purged} log(s) supprim\xE9(s)`);
712
+ alert(`${d.purged} ${t("settingsView.purgeDone")}`);
708
713
  }
709
714
  } catch {
710
- alert("Erreur");
715
+ alert(t("import.errorTitle"));
711
716
  }
712
717
  },
713
718
  style: { ...adminTokens.btnStyle(opt.days === 0 ? "#ef4444" : "var(--theme-elevation-500)", { small: true }), fontSize: 11 },
@@ -718,14 +723,14 @@ const TicketingSettingsClient = () => {
718
723
  ] }, col)) })
719
724
  ] }),
720
725
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ts__default.default.bottomBar, children: [
721
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleReset, style: adminTokens.btnStyle("var(--theme-elevation-400)", { small: true }), children: "R\xE9initialiser tout" }),
726
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleReset, style: adminTokens.btnStyle("var(--theme-elevation-400)", { small: true }), children: t("settingsView.resetAll") }),
722
727
  /* @__PURE__ */ jsxRuntime.jsx(
723
728
  "button",
724
729
  {
725
730
  onClick: handleSave,
726
731
  disabled: saving,
727
732
  style: adminTokens.btnStyle(saved ? adminTokens.V.green : adminTokens.V.blue, { small: true }),
728
- children: saving ? "Sauvegarde..." : saved ? "\u2713 Sauvegard\xE9" : "Sauvegarder les modifications"
733
+ children: saving ? t("settingsView.saving") : saved ? t("settingsView.saved2") : t("settingsView.saveChanges")
729
734
  }
730
735
  )
731
736
  ] })