@consilioweb/payload-support 0.6.1 → 0.6.3

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 (134) hide show
  1. package/dist/components/TicketConversation/components/AISummaryPanel.cjs +77 -0
  2. package/dist/components/TicketConversation/components/AISummaryPanel.js +76 -0
  3. package/dist/components/TicketConversation/components/ActionPanels.cjs +121 -0
  4. package/dist/components/TicketConversation/components/ActionPanels.js +118 -0
  5. package/dist/components/TicketConversation/components/ActivityLog.cjs +24 -0
  6. package/dist/components/TicketConversation/components/ActivityLog.js +23 -0
  7. package/dist/components/TicketConversation/components/ClientBar.cjs +43 -0
  8. package/dist/components/TicketConversation/components/ClientBar.js +42 -0
  9. package/dist/components/TicketConversation/components/ClientHistory.cjs +138 -0
  10. package/dist/components/TicketConversation/components/ClientHistory.js +137 -0
  11. package/dist/components/TicketConversation/components/CodeBlock.cjs +154 -0
  12. package/dist/components/TicketConversation/components/CodeBlock.js +152 -0
  13. package/dist/components/TicketConversation/components/CodeBlockInserter.cjs +156 -0
  14. package/dist/components/TicketConversation/components/CodeBlockInserter.js +155 -0
  15. package/dist/components/TicketConversation/components/QuickActions.cjs +66 -0
  16. package/dist/components/TicketConversation/components/QuickActions.js +65 -0
  17. package/dist/components/TicketConversation/components/TicketHeader.cjs +93 -0
  18. package/dist/components/TicketConversation/components/TicketHeader.js +92 -0
  19. package/dist/components/TicketConversation/components/TimeTrackingPanel.cjs +134 -0
  20. package/dist/components/TicketConversation/components/TimeTrackingPanel.js +133 -0
  21. package/dist/components/TicketConversation/config.cjs +44 -0
  22. package/dist/components/TicketConversation/config.js +41 -0
  23. package/dist/components/TicketConversation/constants.cjs +100 -0
  24. package/dist/components/TicketConversation/constants.js +96 -0
  25. package/dist/components/TicketConversation/context.cjs +13 -0
  26. package/dist/components/TicketConversation/context.js +11 -0
  27. package/dist/components/TicketConversation/hooks/useAI.cjs +177 -0
  28. package/dist/components/TicketConversation/hooks/useAI.js +176 -0
  29. package/dist/components/TicketConversation/hooks/useMessageActions.cjs +136 -0
  30. package/dist/components/TicketConversation/hooks/useMessageActions.js +135 -0
  31. package/dist/components/TicketConversation/hooks/useReply.cjs +188 -0
  32. package/dist/components/TicketConversation/hooks/useReply.js +187 -0
  33. package/dist/components/TicketConversation/hooks/useTicketActions.cjs +231 -0
  34. package/dist/components/TicketConversation/hooks/useTicketActions.js +230 -0
  35. package/dist/components/TicketConversation/hooks/useTimeTracking.cjs +123 -0
  36. package/dist/components/TicketConversation/hooks/useTimeTracking.js +122 -0
  37. package/dist/components/TicketConversation/hooks/useTranslation.cjs +78 -0
  38. package/dist/components/TicketConversation/hooks/useTranslation.js +70 -0
  39. package/dist/components/TicketConversation/index.cjs +1128 -0
  40. package/dist/components/TicketConversation/index.js +1123 -0
  41. package/dist/components/TicketConversation/locales/en.json +878 -0
  42. package/dist/components/TicketConversation/locales/fr.json +878 -0
  43. package/dist/components/TicketConversation/types.cjs +2 -0
  44. package/dist/components/TicketConversation/types.js +2 -0
  45. package/dist/components/TicketConversation/utils.cjs +27 -0
  46. package/dist/components/TicketConversation/utils.js +25 -0
  47. package/dist/styles/BillingView.module.scss +311 -0
  48. package/dist/styles/ChatView.module.scss +438 -0
  49. package/dist/styles/CommandPalette.module.scss +160 -0
  50. package/dist/styles/CrmView.module.scss +554 -0
  51. package/dist/styles/EmailTracking.module.scss +238 -0
  52. package/dist/styles/ImportConversation.module.scss +267 -0
  53. package/dist/styles/Layout.module.scss +55 -0
  54. package/dist/styles/Logs.module.scss +164 -0
  55. package/dist/styles/NewTicket.module.scss +143 -0
  56. package/dist/styles/PendingEmails.module.scss +629 -0
  57. package/dist/styles/SupportDashboard.module.scss +649 -0
  58. package/dist/styles/TicketDetail.module.scss +1050 -0
  59. package/dist/styles/TicketInbox.module.scss +296 -0
  60. package/dist/styles/TicketingSettings.module.scss +358 -0
  61. package/dist/styles/TimeDashboard.module.scss +287 -0
  62. package/dist/styles/_tokens.scss +78 -0
  63. package/dist/{components/TicketConversation.css → styles/theme.css} +326 -13
  64. package/dist/views/BillingView/client.cjs +209 -0
  65. package/dist/views/BillingView/client.js +204 -0
  66. package/dist/views/BillingView/index.cjs +34 -0
  67. package/dist/views/BillingView/index.js +29 -0
  68. package/dist/views/ChatView/client.cjs +253 -0
  69. package/dist/views/ChatView/client.js +252 -0
  70. package/dist/views/ChatView/index.cjs +34 -0
  71. package/dist/views/ChatView/index.js +29 -0
  72. package/dist/views/CrmView/client.cjs +233 -0
  73. package/dist/views/CrmView/client.js +232 -0
  74. package/dist/views/CrmView/index.cjs +34 -0
  75. package/dist/views/CrmView/index.js +29 -0
  76. package/dist/views/EmailTrackingView/client.cjs +159 -0
  77. package/dist/views/EmailTrackingView/client.js +154 -0
  78. package/dist/views/EmailTrackingView/index.cjs +34 -0
  79. package/dist/views/EmailTrackingView/index.js +29 -0
  80. package/dist/views/ImportConversationView/client.cjs +205 -0
  81. package/dist/views/ImportConversationView/client.js +204 -0
  82. package/dist/views/ImportConversationView/index.cjs +34 -0
  83. package/dist/views/ImportConversationView/index.js +29 -0
  84. package/dist/views/LogsView/client.cjs +149 -0
  85. package/dist/views/LogsView/client.js +148 -0
  86. package/dist/views/LogsView/index.cjs +32 -0
  87. package/dist/views/LogsView/index.js +27 -0
  88. package/dist/views/NewTicketView/client.cjs +229 -0
  89. package/dist/views/NewTicketView/client.js +224 -0
  90. package/dist/views/NewTicketView/index.cjs +32 -0
  91. package/dist/views/NewTicketView/index.js +27 -0
  92. package/dist/views/PendingEmailsView/client.cjs +173 -0
  93. package/dist/views/PendingEmailsView/client.js +172 -0
  94. package/dist/views/PendingEmailsView/index.cjs +34 -0
  95. package/dist/views/PendingEmailsView/index.js +29 -0
  96. package/dist/views/SupportDashboardView/client.cjs +301 -0
  97. package/dist/views/SupportDashboardView/client.js +296 -0
  98. package/dist/views/SupportDashboardView/index.cjs +34 -0
  99. package/dist/views/SupportDashboardView/index.js +29 -0
  100. package/dist/views/TicketDetailView/client.cjs +850 -0
  101. package/dist/views/TicketDetailView/client.js +844 -0
  102. package/dist/views/TicketDetailView/index.cjs +34 -0
  103. package/dist/views/TicketDetailView/index.js +29 -0
  104. package/dist/views/TicketInboxView/client.cjs +299 -0
  105. package/dist/views/TicketInboxView/client.js +294 -0
  106. package/dist/views/TicketInboxView/index.cjs +32 -0
  107. package/dist/views/TicketInboxView/index.js +27 -0
  108. package/dist/{views.css → views/TicketingSettingsView/TicketingSettings.module.scss} +138 -66
  109. package/dist/views/TicketingSettingsView/client.cjs +733 -0
  110. package/dist/views/TicketingSettingsView/client.js +728 -0
  111. package/dist/views/TicketingSettingsView/index.cjs +34 -0
  112. package/dist/views/TicketingSettingsView/index.js +29 -0
  113. package/dist/views/TimeDashboardView/client.cjs +169 -0
  114. package/dist/views/TimeDashboardView/client.js +164 -0
  115. package/dist/views/TimeDashboardView/index.cjs +34 -0
  116. package/dist/views/TimeDashboardView/index.js +29 -0
  117. package/dist/views/shared/AdminViewHeader.cjs +68 -0
  118. package/dist/views/shared/AdminViewHeader.js +67 -0
  119. package/dist/views/shared/ErrorBoundary.cjs +55 -0
  120. package/dist/views/shared/ErrorBoundary.js +50 -0
  121. package/dist/views/shared/Skeleton.cjs +77 -0
  122. package/dist/views/shared/Skeleton.js +72 -0
  123. package/dist/views/shared/adminTokens.cjs +34 -0
  124. package/dist/views/shared/adminTokens.js +31 -0
  125. package/dist/views/shared/config.cjs +44 -0
  126. package/dist/views/shared/config.js +40 -0
  127. package/dist/views/shared/index.cjs +58 -0
  128. package/dist/views/shared/index.js +5 -0
  129. package/dist/views.cjs +77 -6175
  130. package/dist/views.d.cts +13 -30
  131. package/dist/views.d.ts +13 -30
  132. package/dist/views.js +13 -6165
  133. package/package.json +2 -2
  134. package/dist/components/TicketConversation.js +0 -3004
@@ -0,0 +1,728 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { useState, useEffect } from 'react';
4
+ import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from 'lucide-react';
5
+ import { V, btnStyle } from '../shared/adminTokens';
6
+ import { AdminViewHeader } from '../shared/AdminViewHeader';
7
+ import { getFeatures, saveFeatures, DEFAULT_FEATURES } from '../../components/TicketConversation/config';
8
+ import ts from './TicketingSettings.module.scss';
9
+
10
+ const DEFAULT_SETTINGS = {
11
+ email: {
12
+ fromAddress: "",
13
+ fromName: "Support ConsilioWEB",
14
+ replyToAddress: ""
15
+ },
16
+ ai: {
17
+ provider: "ollama",
18
+ apiKey: "",
19
+ model: "qwen2.5:32b",
20
+ enableSentiment: true,
21
+ enableSynthesis: true,
22
+ enableSuggestion: true,
23
+ enableRewrite: true
24
+ },
25
+ sla: {
26
+ firstResponseMinutes: 120,
27
+ resolutionMinutes: 1440,
28
+ businessHoursOnly: true,
29
+ escalationEmail: ""
30
+ },
31
+ autoClose: {
32
+ enabled: true,
33
+ daysBeforeClose: 7,
34
+ reminderDaysBefore: 2
35
+ },
36
+ locale: {
37
+ language: "fr"
38
+ }
39
+ };
40
+ async function fetchSettingsFromAPI() {
41
+ try {
42
+ const res = await fetch("/api/support/settings", { credentials: "include" });
43
+ if (res.ok) {
44
+ const data = await res.json();
45
+ return {
46
+ email: { ...DEFAULT_SETTINGS.email, ...data.email },
47
+ ai: { ...DEFAULT_SETTINGS.ai, apiKey: "", ...data.ai },
48
+ sla: { ...DEFAULT_SETTINGS.sla, ...data.sla },
49
+ autoClose: { ...DEFAULT_SETTINGS.autoClose, ...data.autoClose },
50
+ locale: DEFAULT_SETTINGS.locale
51
+ // locale is now per-user
52
+ };
53
+ }
54
+ } catch {
55
+ }
56
+ return DEFAULT_SETTINGS;
57
+ }
58
+ async function saveSettingsToAPI(settings) {
59
+ try {
60
+ const toSave = {
61
+ email: settings.email,
62
+ ai: { ...settings.ai, apiKey: void 0 },
63
+ sla: settings.sla,
64
+ autoClose: settings.autoClose
65
+ // locale excluded — saved per-user via user-prefs
66
+ };
67
+ const res = await fetch("/api/support/settings", {
68
+ method: "POST",
69
+ headers: { "Content-Type": "application/json" },
70
+ credentials: "include",
71
+ body: JSON.stringify(toSave)
72
+ });
73
+ return res.ok;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+ async function fetchUserPrefs() {
79
+ try {
80
+ const res = await fetch("/api/support/user-prefs", { credentials: "include" });
81
+ if (res.ok) return await res.json();
82
+ } catch {
83
+ }
84
+ return { locale: "fr", signature: "" };
85
+ }
86
+ async function saveUserPrefs(prefs) {
87
+ try {
88
+ const res = await fetch("/api/support/user-prefs", {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/json" },
91
+ credentials: "include",
92
+ body: JSON.stringify(prefs)
93
+ });
94
+ return res.ok;
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+ const FEATURE_LIST = [
100
+ // Core
101
+ { key: "canned", label: "R\xE9ponses rapides", description: "Templates de r\xE9ponses pr\xE9-enregistr\xE9es avec variables dynamiques", category: "core" },
102
+ { key: "scheduledReplies", label: "R\xE9ponses programm\xE9es", description: "Envoyer une r\xE9ponse \xE0 une date/heure future", category: "core" },
103
+ { key: "activityLog", label: "Journal d'activit\xE9", description: "Timeline des actions sur chaque ticket (changements de statut, assignation...)", category: "core" },
104
+ // Communication
105
+ { key: "emailTracking", label: "Suivi des emails", description: "Tracking d'envoi et d'ouverture des notifications email", category: "communication" },
106
+ { key: "chat", label: "Live Chat", description: "Chat en temps r\xE9el avec conversion en ticket", category: "communication" },
107
+ { key: "externalMessages", label: "Messages externes", description: "Ajouter manuellement des messages re\xE7us par email, SMS, WhatsApp...", category: "communication" },
108
+ // Productivity
109
+ { key: "ai", label: "Intelligence Artificielle", description: "Analyse de sentiment, synth\xE8se, suggestion de r\xE9ponse, reformulation", category: "productivity" },
110
+ { key: "timeTracking", label: "Suivi du temps", description: "Timer, entr\xE9es manuelles, facturation", category: "productivity" },
111
+ { key: "satisfaction", label: "Enqu\xEAtes satisfaction", description: "Score CSAT apr\xE8s r\xE9solution du ticket", category: "productivity" },
112
+ // Advanced
113
+ { key: "merge", label: "Fusion de tickets", description: "Combiner deux tickets en un seul", category: "advanced" },
114
+ { key: "splitTicket", label: "Extraction de message", description: "Extraire un message en nouveau ticket li\xE9", category: "advanced" },
115
+ { key: "snooze", label: "Snooze", description: "Masquer temporairement un ticket et rappel automatique", category: "advanced" },
116
+ { key: "clientHistory", label: "Historique client", description: "Tickets pass\xE9s, projets et notes internes du client", category: "advanced" }
117
+ ];
118
+ const CATEGORIES = [
119
+ { key: "core", label: "Fonctionnalit\xE9s de base", color: V.blue },
120
+ { key: "communication", label: "Communication", color: V.green },
121
+ { key: "productivity", label: "Productivit\xE9", color: V.amber },
122
+ { key: "advanced", label: "Avanc\xE9", color: "#7c3aed" }
123
+ ];
124
+ const Toggle = ({ checked, onChange, color = V.blue, size = "md" }) => {
125
+ const w = size === "sm" ? 36 : 40;
126
+ const h = size === "sm" ? 20 : 22;
127
+ const knob = size === "sm" ? 16 : 18;
128
+ return /* @__PURE__ */ jsx(
129
+ "div",
130
+ {
131
+ role: "switch",
132
+ "aria-checked": checked,
133
+ tabIndex: 0,
134
+ onClick: onChange,
135
+ onKeyDown: (e) => {
136
+ if (e.key === "Enter" || e.key === " ") {
137
+ e.preventDefault();
138
+ onChange();
139
+ }
140
+ },
141
+ style: {
142
+ width: w,
143
+ height: h,
144
+ borderRadius: h / 2,
145
+ flexShrink: 0,
146
+ backgroundColor: checked ? color : "var(--theme-elevation-300)",
147
+ position: "relative",
148
+ transition: "background 150ms",
149
+ cursor: "pointer"
150
+ },
151
+ children: /* @__PURE__ */ jsx("div", { style: {
152
+ width: knob,
153
+ height: knob,
154
+ borderRadius: "50%",
155
+ backgroundColor: "#fff",
156
+ position: "absolute",
157
+ top: (h - knob) / 2,
158
+ left: checked ? w - knob - (h - knob) / 2 : (h - knob) / 2,
159
+ transition: "left 150ms",
160
+ boxShadow: "0 1px 3px rgba(0,0,0,0.15)"
161
+ } })
162
+ }
163
+ );
164
+ };
165
+ const CollapsibleSection = ({ title, icon, color, defaultOpen = true, children, badge }) => {
166
+ const [open, setOpen] = useState(defaultOpen);
167
+ return /* @__PURE__ */ jsxs("div", { className: ts.sectionWrapper, children: [
168
+ /* @__PURE__ */ jsxs(
169
+ "div",
170
+ {
171
+ className: ts.sectionHeader,
172
+ onClick: () => setOpen(!open),
173
+ role: "button",
174
+ tabIndex: 0,
175
+ onKeyDown: (e) => {
176
+ if (e.key === "Enter" || e.key === " ") {
177
+ e.preventDefault();
178
+ setOpen(!open);
179
+ }
180
+ },
181
+ "aria-expanded": open,
182
+ children: [
183
+ /* @__PURE__ */ jsx("div", { className: ts.sectionIcon, style: { backgroundColor: color }, children: icon }),
184
+ /* @__PURE__ */ jsx("span", { className: ts.sectionTitle, children: title }),
185
+ badge,
186
+ /* @__PURE__ */ jsx("span", { className: `${ts.sectionChevron} ${open ? ts.open : ""}`, children: "\u25BC" })
187
+ ]
188
+ }
189
+ ),
190
+ open && /* @__PURE__ */ jsx("div", { className: ts.sectionBody, children })
191
+ ] });
192
+ };
193
+ const FieldRow = ({ label, description, children }) => /* @__PURE__ */ jsxs("div", { className: ts.fieldRow, children: [
194
+ /* @__PURE__ */ jsxs("div", { className: ts.fieldLabel, children: [
195
+ label,
196
+ description && /* @__PURE__ */ jsx("div", { className: ts.fieldDescription, children: description })
197
+ ] }),
198
+ /* @__PURE__ */ jsx("div", { className: ts.fieldContent, children })
199
+ ] });
200
+ const TicketingSettingsClient = () => {
201
+ const [features, setFeatures] = useState(() => getFeatures());
202
+ const [settings, setSettings] = useState(DEFAULT_SETTINGS);
203
+ const [signature, setSignature] = useState("");
204
+ const [saved, setSaved] = useState(false);
205
+ const [saving, setSaving] = useState(false);
206
+ const [showApiKey, setShowApiKey] = useState(false);
207
+ const [loadingSettings, setLoadingSettings] = useState(true);
208
+ useEffect(() => {
209
+ let cancelled = false;
210
+ Promise.all([fetchSettingsFromAPI(), fetchUserPrefs()]).then(([s, prefs]) => {
211
+ if (!cancelled) {
212
+ setSettings({ ...s, locale: { language: prefs.locale || "fr" } });
213
+ setSignature(prefs.signature || "");
214
+ setLoadingSettings(false);
215
+ }
216
+ });
217
+ return () => {
218
+ cancelled = true;
219
+ };
220
+ }, []);
221
+ const handleToggle = (key) => {
222
+ const updated = { ...features, [key]: !features[key] };
223
+ setFeatures(updated);
224
+ setSaved(false);
225
+ };
226
+ const updateEmail = (field, value) => {
227
+ setSettings((prev) => ({ ...prev, email: { ...prev.email, [field]: value } }));
228
+ setSaved(false);
229
+ };
230
+ const updateAI = (field, value) => {
231
+ setSettings((prev) => ({ ...prev, ai: { ...prev.ai, [field]: value } }));
232
+ setSaved(false);
233
+ };
234
+ const updateSLA = (field, value) => {
235
+ setSettings((prev) => ({ ...prev, sla: { ...prev.sla, [field]: value } }));
236
+ setSaved(false);
237
+ };
238
+ const updateAutoClose = (field, value) => {
239
+ setSettings((prev) => ({ ...prev, autoClose: { ...prev.autoClose, [field]: value } }));
240
+ setSaved(false);
241
+ };
242
+ const updateLocale = (field, value) => {
243
+ setSettings((prev) => ({ ...prev, locale: { ...prev.locale, [field]: value } }));
244
+ setSaved(false);
245
+ };
246
+ const handleSave = async () => {
247
+ setSaving(true);
248
+ saveFeatures(features);
249
+ const [settingsOk, prefsOk] = await Promise.all([
250
+ saveSettingsToAPI(settings),
251
+ saveUserPrefs({ locale: settings.locale.language, signature })
252
+ ]);
253
+ setSaving(false);
254
+ if (settingsOk && prefsOk) {
255
+ setSaved(true);
256
+ setTimeout(() => setSaved(false), 3e3);
257
+ }
258
+ };
259
+ const handleReset = () => {
260
+ setFeatures({ ...DEFAULT_FEATURES });
261
+ setSettings({ ...DEFAULT_SETTINGS });
262
+ setSignature("");
263
+ setSaved(false);
264
+ };
265
+ const enabledCount = Object.entries(features).filter(([k, v]) => typeof v === "boolean" && v).length;
266
+ const totalCount = Object.entries(features).filter(([k, v]) => typeof v === "boolean").length;
267
+ const smtpHost = process.env.NEXT_PUBLIC_SMTP_HOST || "(non configure)";
268
+ const smtpPort = process.env.NEXT_PUBLIC_SMTP_PORT || "\u2014";
269
+ return /* @__PURE__ */ jsxs("div", { className: ts.page, children: [
270
+ /* @__PURE__ */ jsx(
271
+ AdminViewHeader,
272
+ {
273
+ icon: /* @__PURE__ */ jsx(Settings, { size: 24 }),
274
+ title: "Configuration du module Support",
275
+ subtitle: `${enabledCount}/${totalCount} fonctionnalites activees`,
276
+ actions: /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
277
+ /* @__PURE__ */ jsx("button", { onClick: handleReset, style: btnStyle("var(--theme-elevation-400)", { small: true }), children: "Reinitialiser" }),
278
+ /* @__PURE__ */ jsx(
279
+ "button",
280
+ {
281
+ onClick: handleSave,
282
+ disabled: saving,
283
+ style: btnStyle(saved ? V.green : V.blue, { small: true }),
284
+ children: saving ? "..." : saved ? "\u2713 Sauvegarde" : "Sauvegarder"
285
+ }
286
+ )
287
+ ] })
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsx("p", { className: ts.intro, children: "Configurez le module de support : fonctionnalit\xE9s, email, IA, SLA et fermeture automatique. Les changements prennent effet imm\xE9diatement apr\xE8s sauvegarde (rechargez la page du ticket)." }),
291
+ /* @__PURE__ */ jsxs(
292
+ CollapsibleSection,
293
+ {
294
+ title: "Fonctionnalit\xE9s",
295
+ icon: /* @__PURE__ */ jsx(Settings, { size: 16 }),
296
+ color: V.blue,
297
+ badge: /* @__PURE__ */ jsxs("span", { className: ts.badge, style: { backgroundColor: "#dbeafe", color: "#1e40af" }, children: [
298
+ enabledCount,
299
+ "/",
300
+ totalCount
301
+ ] }),
302
+ children: [
303
+ /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: "Activez ou desactivez les fonctionnalites du module. Les fonctionnalites desactivees sont completement masquees de l'interface." }),
304
+ CATEGORIES.map((cat) => {
305
+ const categoryFeatures = FEATURE_LIST.filter((f) => f.category === cat.key);
306
+ return /* @__PURE__ */ jsxs("div", { className: ts.categoryGroup, children: [
307
+ /* @__PURE__ */ jsxs("h3", { className: ts.categoryHeading, style: { color: cat.color }, children: [
308
+ /* @__PURE__ */ jsx("span", { className: ts.categoryDot, style: { backgroundColor: cat.color } }),
309
+ cat.label
310
+ ] }),
311
+ /* @__PURE__ */ jsx("div", { className: ts.featureList, children: categoryFeatures.map((feat) => {
312
+ const enabled = features[feat.key];
313
+ return /* @__PURE__ */ jsxs(
314
+ "div",
315
+ {
316
+ onClick: () => handleToggle(feat.key),
317
+ className: `${ts.featureCard} ${enabled ? ts.enabled : ""}`,
318
+ role: "switch",
319
+ "aria-checked": !!enabled,
320
+ tabIndex: 0,
321
+ onKeyDown: (e) => {
322
+ if (e.key === "Enter" || e.key === " ") {
323
+ e.preventDefault();
324
+ handleToggle(feat.key);
325
+ }
326
+ },
327
+ children: [
328
+ /* @__PURE__ */ jsx(Toggle, { checked: !!enabled, onChange: () => handleToggle(feat.key), color: cat.color }),
329
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
330
+ /* @__PURE__ */ jsx("div", { className: ts.featureLabel, children: feat.label }),
331
+ /* @__PURE__ */ jsx("div", { className: ts.featureDesc, children: feat.description })
332
+ ] })
333
+ ]
334
+ },
335
+ feat.key
336
+ );
337
+ }) })
338
+ ] }, cat.key);
339
+ })
340
+ ]
341
+ }
342
+ ),
343
+ /* @__PURE__ */ jsxs(
344
+ CollapsibleSection,
345
+ {
346
+ title: "Configuration Email",
347
+ icon: /* @__PURE__ */ jsx(Mail, { size: 16 }),
348
+ color: "#ea580c",
349
+ defaultOpen: false,
350
+ children: [
351
+ /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: "Parametres d'envoi des notifications email. L'adresse SMTP est configuree via les variables d'environnement du serveur." }),
352
+ /* @__PURE__ */ jsx(FieldRow, { label: "Adresse expediteur", description: "Adresse email affichee dans le champ From", children: /* @__PURE__ */ jsx(
353
+ "input",
354
+ {
355
+ type: "email",
356
+ value: settings.email.fromAddress,
357
+ onChange: (e) => updateEmail("fromAddress", e.target.value),
358
+ placeholder: "support@example.com",
359
+ className: ts.input
360
+ }
361
+ ) }),
362
+ /* @__PURE__ */ jsx(FieldRow, { label: "Nom expediteur", description: "Nom affiche a cote de l'adresse email", children: /* @__PURE__ */ jsx(
363
+ "input",
364
+ {
365
+ type: "text",
366
+ value: settings.email.fromName,
367
+ onChange: (e) => updateEmail("fromName", e.target.value),
368
+ placeholder: "Support ConsilioWEB",
369
+ className: ts.input
370
+ }
371
+ ) }),
372
+ /* @__PURE__ */ jsx(FieldRow, { label: "Adresse Reply-To", description: "Si differente de l'adresse expediteur", children: /* @__PURE__ */ jsx(
373
+ "input",
374
+ {
375
+ type: "email",
376
+ value: settings.email.replyToAddress,
377
+ onChange: (e) => updateEmail("replyToAddress", e.target.value),
378
+ placeholder: "(identique a l'expediteur)",
379
+ className: ts.input
380
+ }
381
+ ) }),
382
+ /* @__PURE__ */ jsx("div", { className: ts.separator }),
383
+ /* @__PURE__ */ jsx(FieldRow, { label: "Serveur SMTP", description: "Configure via variables d'environnement", children: /* @__PURE__ */ jsx(
384
+ "input",
385
+ {
386
+ type: "text",
387
+ value: smtpHost,
388
+ readOnly: true,
389
+ className: ts.inputReadonly
390
+ }
391
+ ) }),
392
+ /* @__PURE__ */ jsx(FieldRow, { label: "Port SMTP", children: /* @__PURE__ */ jsx(
393
+ "input",
394
+ {
395
+ type: "text",
396
+ value: smtpPort,
397
+ readOnly: true,
398
+ className: ts.inputReadonly,
399
+ style: { maxWidth: 100 }
400
+ }
401
+ ) })
402
+ ]
403
+ }
404
+ ),
405
+ /* @__PURE__ */ jsxs(
406
+ CollapsibleSection,
407
+ {
408
+ title: "Intelligence Artificielle",
409
+ icon: /* @__PURE__ */ jsx(Bot, { size: 16 }),
410
+ color: "#7c3aed",
411
+ defaultOpen: false,
412
+ badge: features.ai ? /* @__PURE__ */ jsx("span", { className: ts.badge, style: { backgroundColor: "#dcfce7", color: "#166534" }, children: "Active" }) : /* @__PURE__ */ jsx("span", { className: ts.badge, style: { backgroundColor: "#fee2e2", color: "#991b1b" }, children: "Inactive" }),
413
+ children: [
414
+ /* @__PURE__ */ jsx("p", { className: ts.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.` }),
415
+ /* @__PURE__ */ jsx(FieldRow, { label: "Fournisseur", description: "Service d'IA utilise pour l'analyse", children: /* @__PURE__ */ jsxs(
416
+ "select",
417
+ {
418
+ value: settings.ai.provider,
419
+ onChange: (e) => updateAI("provider", e.target.value),
420
+ className: ts.select,
421
+ children: [
422
+ /* @__PURE__ */ jsx("option", { value: "ollama", children: "Ollama (local / tunnel)" }),
423
+ /* @__PURE__ */ jsx("option", { value: "anthropic", children: "Anthropic (Claude)" }),
424
+ /* @__PURE__ */ jsx("option", { value: "openai", children: "OpenAI (GPT)" }),
425
+ /* @__PURE__ */ jsx("option", { value: "gemini", children: "Google (Gemini)" })
426
+ ]
427
+ }
428
+ ) }),
429
+ settings.ai.provider !== "ollama" && /* @__PURE__ */ jsx(FieldRow, { label: "Cle API", description: "Cle secrete du fournisseur (non stockee en clair)", children: /* @__PURE__ */ jsxs("div", { className: ts.apiKeyRow, children: [
430
+ /* @__PURE__ */ jsx(
431
+ "input",
432
+ {
433
+ type: showApiKey ? "text" : "password",
434
+ value: settings.ai.apiKey,
435
+ onChange: (e) => updateAI("apiKey", e.target.value),
436
+ placeholder: "sk-...",
437
+ className: ts.input,
438
+ style: { flex: 1 }
439
+ }
440
+ ),
441
+ /* @__PURE__ */ jsx(
442
+ "button",
443
+ {
444
+ onClick: () => setShowApiKey(!showApiKey),
445
+ className: ts.apiKeyToggle,
446
+ children: showApiKey ? "Masquer" : "Afficher"
447
+ }
448
+ )
449
+ ] }) }),
450
+ /* @__PURE__ */ jsx(FieldRow, { label: "Modele", description: "Nom du modele a utiliser", children: /* @__PURE__ */ jsx(
451
+ "input",
452
+ {
453
+ type: "text",
454
+ value: settings.ai.model,
455
+ onChange: (e) => updateAI("model", e.target.value),
456
+ placeholder: "qwen2.5:32b",
457
+ className: ts.input
458
+ }
459
+ ) }),
460
+ /* @__PURE__ */ jsx("div", { className: ts.separator }),
461
+ /* @__PURE__ */ jsx("p", { className: ts.aiSubFeaturesLabel, children: "Fonctionnalit\xE9s IA individuelles" }),
462
+ [
463
+ { key: "enableSentiment", label: "Analyse de sentiment", desc: "Detecte le niveau de frustration ou satisfaction du client" },
464
+ { key: "enableSynthesis", label: "Synthese automatique", desc: "Resume les conversations longues en quelques phrases" },
465
+ { key: "enableSuggestion", label: "Suggestion de reponse", desc: "Propose un brouillon de reponse base sur le contexte" },
466
+ { key: "enableRewrite", label: "Reformulation", desc: "Reformule un message pour le rendre plus professionnel" }
467
+ ].map((item) => /* @__PURE__ */ jsxs("div", { className: ts.aiToggleRow, children: [
468
+ /* @__PURE__ */ jsx(
469
+ Toggle,
470
+ {
471
+ checked: settings.ai[item.key],
472
+ onChange: () => updateAI(item.key, !settings.ai[item.key]),
473
+ color: "#7c3aed",
474
+ size: "sm"
475
+ }
476
+ ),
477
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
478
+ /* @__PURE__ */ jsx("span", { className: ts.aiToggleLabel, children: item.label }),
479
+ /* @__PURE__ */ jsx("span", { className: ts.aiToggleDesc, children: item.desc })
480
+ ] })
481
+ ] }, item.key))
482
+ ]
483
+ }
484
+ ),
485
+ /* @__PURE__ */ jsxs(
486
+ CollapsibleSection,
487
+ {
488
+ title: "SLA (Accords de niveau de service)",
489
+ icon: /* @__PURE__ */ jsx(Clock, { size: 16 }),
490
+ color: "#0891b2",
491
+ defaultOpen: false,
492
+ children: [
493
+ /* @__PURE__ */ jsx("p", { className: ts.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." }),
494
+ /* @__PURE__ */ jsx(FieldRow, { label: "Premiere reponse", description: "D\xE9lai maximum en minutes (d\xE9faut : 120 = 2h)", children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
495
+ /* @__PURE__ */ jsx(
496
+ "input",
497
+ {
498
+ type: "number",
499
+ min: 1,
500
+ value: settings.sla.firstResponseMinutes,
501
+ onChange: (e) => updateSLA("firstResponseMinutes", parseInt(e.target.value) || 0),
502
+ className: ts.numberInput
503
+ }
504
+ ),
505
+ /* @__PURE__ */ jsxs("span", { className: ts.slaHint, children: [
506
+ "minutes (",
507
+ Math.floor(settings.sla.firstResponseMinutes / 60),
508
+ "h",
509
+ String(settings.sla.firstResponseMinutes % 60).padStart(2, "0"),
510
+ ")"
511
+ ] })
512
+ ] }) }),
513
+ /* @__PURE__ */ jsx(FieldRow, { label: "R\xE9solution", description: "D\xE9lai maximum en minutes (d\xE9faut : 1440 = 24h)", children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
514
+ /* @__PURE__ */ jsx(
515
+ "input",
516
+ {
517
+ type: "number",
518
+ min: 1,
519
+ value: settings.sla.resolutionMinutes,
520
+ onChange: (e) => updateSLA("resolutionMinutes", parseInt(e.target.value) || 0),
521
+ className: ts.numberInput
522
+ }
523
+ ),
524
+ /* @__PURE__ */ jsxs("span", { className: ts.slaHint, children: [
525
+ "minutes (",
526
+ Math.floor(settings.sla.resolutionMinutes / 60),
527
+ "h",
528
+ String(settings.sla.resolutionMinutes % 60).padStart(2, "0"),
529
+ ")"
530
+ ] })
531
+ ] }) }),
532
+ /* @__PURE__ */ jsxs("div", { className: ts.toggleRow, children: [
533
+ /* @__PURE__ */ jsx(
534
+ Toggle,
535
+ {
536
+ checked: settings.sla.businessHoursOnly,
537
+ onChange: () => updateSLA("businessHoursOnly", !settings.sla.businessHoursOnly),
538
+ color: "#0891b2",
539
+ size: "sm"
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsxs("div", { children: [
543
+ /* @__PURE__ */ jsx("span", { className: ts.inlineLabel, children: "Heures ouvrables uniquement" }),
544
+ /* @__PURE__ */ jsx("div", { className: ts.inlineDesc, children: "Le decompte SLA est suspendu en dehors des heures de bureau (Lun-Ven, 9h-18h)" })
545
+ ] })
546
+ ] }),
547
+ /* @__PURE__ */ jsx("div", { className: ts.separator }),
548
+ /* @__PURE__ */ jsx(FieldRow, { label: "Email d'escalade", description: "Adresse notifiee en cas de depassement SLA", children: /* @__PURE__ */ jsx(
549
+ "input",
550
+ {
551
+ type: "email",
552
+ value: settings.sla.escalationEmail,
553
+ onChange: (e) => updateSLA("escalationEmail", e.target.value),
554
+ placeholder: "admin@example.com",
555
+ className: ts.input
556
+ }
557
+ ) })
558
+ ]
559
+ }
560
+ ),
561
+ /* @__PURE__ */ jsxs(
562
+ CollapsibleSection,
563
+ {
564
+ title: "Fermeture automatique",
565
+ icon: /* @__PURE__ */ jsx(Timer, { size: 16 }),
566
+ color: "#d97706",
567
+ defaultOpen: false,
568
+ badge: settings.autoClose.enabled ? /* @__PURE__ */ jsxs("span", { className: ts.badge, style: { backgroundColor: "#dcfce7", color: "#166534" }, children: [
569
+ settings.autoClose.daysBeforeClose,
570
+ "j"
571
+ ] }) : /* @__PURE__ */ jsx("span", { className: ts.badge, style: { backgroundColor: "#e5e7eb", color: "#374151" }, children: "Off" }),
572
+ children: [
573
+ /* @__PURE__ */ jsx("p", { className: ts.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." }),
574
+ /* @__PURE__ */ jsxs("div", { className: ts.toggleRow, style: { paddingBottom: 14 }, children: [
575
+ /* @__PURE__ */ jsx(
576
+ Toggle,
577
+ {
578
+ checked: settings.autoClose.enabled,
579
+ onChange: () => updateAutoClose("enabled", !settings.autoClose.enabled),
580
+ color: "#d97706"
581
+ }
582
+ ),
583
+ /* @__PURE__ */ jsx("span", { className: ts.inlineLabel, children: "Activer la fermeture automatique" })
584
+ ] }),
585
+ settings.autoClose.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
586
+ /* @__PURE__ */ jsx(FieldRow, { label: "Delai avant fermeture", description: "Nombre de jours sans reponse du client", children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
587
+ /* @__PURE__ */ jsx(
588
+ "input",
589
+ {
590
+ type: "number",
591
+ min: 1,
592
+ max: 90,
593
+ value: settings.autoClose.daysBeforeClose,
594
+ onChange: (e) => updateAutoClose("daysBeforeClose", parseInt(e.target.value) || 7),
595
+ className: ts.numberInput
596
+ }
597
+ ),
598
+ /* @__PURE__ */ jsx("span", { className: ts.slaHint, children: "jours" })
599
+ ] }) }),
600
+ /* @__PURE__ */ jsx(FieldRow, { label: "Rappel avant fermeture", description: "Email de rappel envoye X jours avant", children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
601
+ /* @__PURE__ */ jsx(
602
+ "input",
603
+ {
604
+ type: "number",
605
+ min: 1,
606
+ max: settings.autoClose.daysBeforeClose - 1,
607
+ value: settings.autoClose.reminderDaysBefore,
608
+ onChange: (e) => updateAutoClose("reminderDaysBefore", parseInt(e.target.value) || 2),
609
+ className: ts.numberInput
610
+ }
611
+ ),
612
+ /* @__PURE__ */ jsxs("span", { className: ts.slaHint, children: [
613
+ "jours avant (rappel a J-",
614
+ settings.autoClose.reminderDaysBefore,
615
+ ")"
616
+ ] })
617
+ ] }) })
618
+ ] })
619
+ ]
620
+ }
621
+ ),
622
+ /* @__PURE__ */ jsxs("div", { style: {
623
+ marginTop: 32,
624
+ marginBottom: 16,
625
+ padding: "12px 16px",
626
+ borderRadius: 8,
627
+ background: "linear-gradient(135deg, #dbeafe 0%, #ede9fe 100%)",
628
+ border: "1px solid #c7d2fe"
629
+ }, children: [
630
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 700, fontSize: 15, color: "#1e293b" }, children: "Mes preferences" }),
631
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: "#64748b", marginTop: 2 }, children: "Ces reglages sont propres a votre compte et ne s'appliquent qu'a vous." })
632
+ ] }),
633
+ /* @__PURE__ */ jsxs(
634
+ CollapsibleSection,
635
+ {
636
+ title: "Langue et localisation",
637
+ icon: /* @__PURE__ */ jsx(Globe, { size: 16 }),
638
+ color: "#16a34a",
639
+ defaultOpen: false,
640
+ children: [
641
+ /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: "Langue de l'interface du module de support et des notifications email envoyees aux clients." }),
642
+ /* @__PURE__ */ jsx(FieldRow, { label: "Langue", description: "Langue principale du module", children: /* @__PURE__ */ jsxs(
643
+ "select",
644
+ {
645
+ value: settings.locale.language,
646
+ onChange: (e) => updateLocale("language", e.target.value),
647
+ className: ts.select,
648
+ children: [
649
+ /* @__PURE__ */ jsx("option", { value: "fr", children: "Francais" }),
650
+ /* @__PURE__ */ jsx("option", { value: "en", children: "English" })
651
+ ]
652
+ }
653
+ ) })
654
+ ]
655
+ }
656
+ ),
657
+ /* @__PURE__ */ jsxs(
658
+ CollapsibleSection,
659
+ {
660
+ title: "Signature email",
661
+ icon: /* @__PURE__ */ jsx(FileSignature, { size: 16 }),
662
+ color: "#6366f1",
663
+ defaultOpen: false,
664
+ children: [
665
+ /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: "Signature ajoutee automatiquement en bas de chaque reponse email envoyee au client. Supporte le texte brut et le HTML basique." }),
666
+ /* @__PURE__ */ jsx(
667
+ "textarea",
668
+ {
669
+ value: signature,
670
+ onChange: (e) => {
671
+ setSignature(e.target.value);
672
+ setSaved(false);
673
+ },
674
+ placeholder: "Cordialement,\nL'equipe ConsilioWEB",
675
+ rows: 6,
676
+ className: ts.signatureTextarea
677
+ }
678
+ )
679
+ ]
680
+ }
681
+ ),
682
+ /* @__PURE__ */ jsxs(CollapsibleSection, { title: "Purge des logs", icon: /* @__PURE__ */ jsx(Settings, { size: 18 }), color: "#ef4444", defaultOpen: false, children: [
683
+ /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: "Supprimez les anciens logs pour lib\xE9rer de l'espace. Cette action est irr\xE9versible." }),
684
+ /* @__PURE__ */ jsx("div", { className: ts.purgeGroup, children: ["email-logs", "auth-logs"].map((col) => /* @__PURE__ */ jsxs("div", { className: ts.purgeCategory, children: [
685
+ /* @__PURE__ */ jsx("span", { className: ts.purgeCategoryLabel, children: col === "email-logs" ? "Logs Email" : "Logs Auth" }),
686
+ /* @__PURE__ */ jsx("div", { className: ts.purgeButtons, children: [
687
+ { label: "7 jours", days: 7 },
688
+ { label: "30 jours", days: 30 },
689
+ { label: "90 jours", days: 90 },
690
+ { label: "Tout", days: 0 }
691
+ ].map((opt) => /* @__PURE__ */ jsx(
692
+ "button",
693
+ {
694
+ onClick: async () => {
695
+ if (!window.confirm(`Supprimer les logs ${col} de plus de ${opt.days || "tous les"} jours ?`)) return;
696
+ try {
697
+ const res = await fetch(`/api/support/purge-logs?collection=${col}&days=${opt.days}`, { method: "DELETE", credentials: "include" });
698
+ if (res.ok) {
699
+ const d = await res.json();
700
+ alert(`${d.purged} log(s) supprim\xE9(s)`);
701
+ }
702
+ } catch {
703
+ alert("Erreur");
704
+ }
705
+ },
706
+ style: { ...btnStyle(opt.days === 0 ? "#ef4444" : "var(--theme-elevation-500)", { small: true }), fontSize: 11 },
707
+ children: opt.label
708
+ },
709
+ opt.days
710
+ )) })
711
+ ] }, col)) })
712
+ ] }),
713
+ /* @__PURE__ */ jsxs("div", { className: ts.bottomBar, children: [
714
+ /* @__PURE__ */ jsx("button", { onClick: handleReset, style: btnStyle("var(--theme-elevation-400)", { small: true }), children: "R\xE9initialiser tout" }),
715
+ /* @__PURE__ */ jsx(
716
+ "button",
717
+ {
718
+ onClick: handleSave,
719
+ disabled: saving,
720
+ style: btnStyle(saved ? V.green : V.blue, { small: true }),
721
+ children: saving ? "Sauvegarde..." : saved ? "\u2713 Sauvegard\xE9" : "Sauvegarder les modifications"
722
+ }
723
+ )
724
+ ] })
725
+ ] });
726
+ };
727
+
728
+ export { TicketingSettingsClient };