@consilioweb/payload-support 0.8.2 → 0.9.4

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 (50) hide show
  1. package/dist/components/RichTextEditor/index.cjs +233 -0
  2. package/dist/components/RichTextEditor/index.js +232 -0
  3. package/dist/components/TicketConversation/components/CodeBlock.cjs +24 -7
  4. package/dist/components/TicketConversation/components/CodeBlock.js +23 -9
  5. package/dist/index.cjs +19 -1
  6. package/dist/index.js +19 -1
  7. package/dist/views/BillingView/client.cjs +260 -103
  8. package/dist/views/BillingView/client.js +259 -103
  9. package/dist/views/ChatView/client.cjs +184 -137
  10. package/dist/views/ChatView/client.js +180 -137
  11. package/dist/views/CrmView/client.cjs +270 -122
  12. package/dist/views/CrmView/client.js +266 -122
  13. package/dist/views/EmailTrackingView/client.cjs +80 -69
  14. package/dist/views/EmailTrackingView/client.js +80 -70
  15. package/dist/views/ImportConversationView/client.cjs +127 -94
  16. package/dist/views/ImportConversationView/client.js +123 -94
  17. package/dist/views/LogsView/client.cjs +56 -58
  18. package/dist/views/LogsView/client.js +52 -58
  19. package/dist/views/NewTicketView/client.cjs +39 -55
  20. package/dist/views/NewTicketView/client.js +38 -55
  21. package/dist/views/PendingEmailsView/client.cjs +399 -102
  22. package/dist/views/PendingEmailsView/client.js +396 -103
  23. package/dist/views/SupportDashboardView/client.cjs +276 -137
  24. package/dist/views/SupportDashboardView/client.js +275 -137
  25. package/dist/views/TicketDetailView/client.cjs +487 -204
  26. package/dist/views/TicketDetailView/client.js +486 -204
  27. package/dist/views/TicketInboxView/client.cjs +62 -65
  28. package/dist/views/TicketInboxView/client.js +62 -66
  29. package/dist/views/TicketingSettingsView/client.cjs +10 -8
  30. package/dist/views/TicketingSettingsView/client.js +10 -8
  31. package/dist/views/TimeDashboardView/client.cjs +70 -59
  32. package/dist/views/TimeDashboardView/client.js +69 -59
  33. package/package.json +6 -2
  34. package/src/components/RichTextEditor/index.tsx +261 -0
  35. package/src/components/TicketConversation/components/CodeBlock.tsx +53 -14
  36. package/src/plugin.ts +2 -0
  37. package/src/utils/emailTemplate.ts +37 -0
  38. package/src/views/BillingView/client.tsx +362 -69
  39. package/src/views/ChatView/client.tsx +225 -140
  40. package/src/views/CrmView/client.tsx +447 -189
  41. package/src/views/EmailTrackingView/client.tsx +111 -71
  42. package/src/views/ImportConversationView/client.tsx +255 -70
  43. package/src/views/LogsView/client.tsx +85 -50
  44. package/src/views/NewTicketView/client.tsx +37 -53
  45. package/src/views/PendingEmailsView/client.tsx +512 -92
  46. package/src/views/SupportDashboardView/client.tsx +294 -134
  47. package/src/views/TicketDetailView/client.tsx +486 -213
  48. package/src/views/TicketInboxView/client.tsx +52 -61
  49. package/src/views/TicketingSettingsView/client.tsx +10 -9
  50. package/src/views/TimeDashboardView/client.tsx +184 -69
@@ -2,9 +2,28 @@
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useState, useCallback, useEffect } from 'react';
4
4
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
5
+ import styles from '../../styles/CrmView.module.scss';
5
6
 
6
- const statusLabelKeys = { open: "ticket.status.open", waiting_client: "ticket.status.waiting_client", resolved: "ticket.status.resolved" };
7
- const statusColors = { open: "#3b82f6", waiting_client: "#f59e0b", resolved: "#22c55e" };
7
+ const statusBadgeClass = {
8
+ open: "badgeAccent",
9
+ waiting_client: "badgeOrange",
10
+ resolved: "badgeGreen"
11
+ };
12
+ const statusLabelKeys = {
13
+ open: "ticket.status.open",
14
+ waiting_client: "ticket.status.waiting_client",
15
+ resolved: "ticket.status.resolved"
16
+ };
17
+ const projectStatusLabelKeys = {
18
+ active: "crm.projectStatus.active",
19
+ paused: "crm.projectStatus.paused",
20
+ completed: "crm.projectStatus.completed"
21
+ };
22
+ const projectStatusBadgeClass = {
23
+ active: "badgeGreen",
24
+ paused: "badgeOrange",
25
+ completed: "badgeMuted"
26
+ };
8
27
  function formatDuration(minutes) {
9
28
  const h = Math.floor(minutes / 60);
10
29
  const m = minutes % 60;
@@ -18,7 +37,24 @@ function timeAgo(dateStr) {
18
37
  if (days === 0) return "Aujourd'hui";
19
38
  if (days === 1) return "Hier";
20
39
  if (days < 30) return `Il y a ${days}j`;
21
- return `Il y a ${Math.floor(days / 30)} mois`;
40
+ const months = Math.floor(days / 30);
41
+ return `Il y a ${months} mois`;
42
+ }
43
+ function getStatColorClass(key, detail) {
44
+ switch (key) {
45
+ case "total":
46
+ return styles.statAccent;
47
+ case "open":
48
+ return detail.openTickets > 0 ? styles.statOrange : styles.statGreen;
49
+ case "resolved":
50
+ return styles.statGreen;
51
+ case "time":
52
+ return styles.statAmber;
53
+ case "activity":
54
+ return styles.statAccent;
55
+ default:
56
+ return styles.statAccent;
57
+ }
22
58
  }
23
59
  const CrmClient = () => {
24
60
  const { t } = useTranslation();
@@ -30,11 +66,11 @@ const CrmClient = () => {
30
66
  const [detailLoading, setDetailLoading] = useState(false);
31
67
  const [showMerge, setShowMerge] = useState(false);
32
68
  const [mergeSearch, setMergeSearch] = useState("");
69
+ const [mergeResults, setMergeResults] = useState([]);
70
+ const [merging, setMerging] = useState(false);
33
71
  const [intelligence, setIntelligence] = useState(null);
34
72
  const [intelLoading, setIntelLoading] = useState(false);
35
73
  const [intelRefreshing, setIntelRefreshing] = useState(false);
36
- const [mergeResults, setMergeResults] = useState([]);
37
- const [merging, setMerging] = useState(false);
38
74
  const [mergeSuccess, setMergeSuccess] = useState("");
39
75
  const fetchClients = useCallback(async () => {
40
76
  try {
@@ -71,9 +107,25 @@ const CrmClient = () => {
71
107
  const timeData = await timeRes.json();
72
108
  totalTimeMinutes = timeData.docs.filter((e) => ticketIds.includes(e.ticket)).reduce((sum, e) => sum + (e.duration || 0), 0);
73
109
  }
74
- const openTickets = tickets.filter((t2) => ["open", "waiting_client"].includes(t2.status)).length;
75
- const resolvedTickets = tickets.filter((t2) => t2.status === "resolved").length;
76
- setDetail({ client, tickets, projects, stats: { totalTickets: tickets.length, openTickets, resolvedTickets, totalTimeMinutes, lastActivity: tickets.length > 0 ? tickets[0].createdAt : null } });
110
+ const openTickets = tickets.filter(
111
+ (t2) => ["open", "waiting_client"].includes(t2.status)
112
+ ).length;
113
+ const resolvedTickets = tickets.filter(
114
+ (t2) => t2.status === "resolved"
115
+ ).length;
116
+ const lastActivity = tickets.length > 0 ? tickets[0].createdAt : null;
117
+ setDetail({
118
+ client,
119
+ tickets,
120
+ projects,
121
+ stats: {
122
+ totalTickets: tickets.length,
123
+ openTickets,
124
+ resolvedTickets,
125
+ totalTimeMinutes,
126
+ lastActivity
127
+ }
128
+ });
77
129
  } catch {
78
130
  setDetail(null);
79
131
  }
@@ -98,9 +150,9 @@ const CrmClient = () => {
98
150
  setSelectedId(id);
99
151
  fetchDetail(id);
100
152
  fetchIntelligence(id);
153
+ setIntelligence(null);
101
154
  setShowMerge(false);
102
155
  setMergeSuccess("");
103
- setIntelligence(null);
104
156
  };
105
157
  useEffect(() => {
106
158
  if (!mergeSearch || mergeSearch.length < 2) {
@@ -109,7 +161,7 @@ const CrmClient = () => {
109
161
  }
110
162
  const timer = setTimeout(async () => {
111
163
  try {
112
- const res = await fetch(`/api/support-clients?where[or][0][company][like]=${encodeURIComponent(mergeSearch)}&where[or][1][email][like]=${encodeURIComponent(mergeSearch)}&limit=10&depth=0`);
164
+ const res = await fetch(`/api/support-clients?where[or][0][company][like]=${encodeURIComponent(mergeSearch)}&where[or][1][email][like]=${encodeURIComponent(mergeSearch)}&where[or][2][firstName][like]=${encodeURIComponent(mergeSearch)}&limit=10&depth=0`);
113
165
  if (res.ok) {
114
166
  const json = await res.json();
115
167
  setMergeResults((json.docs || []).filter((c) => c.id !== selectedId));
@@ -121,10 +173,20 @@ const CrmClient = () => {
121
173
  }, [mergeSearch, selectedId]);
122
174
  const handleMerge = async (targetId) => {
123
175
  if (!selectedId || !detail) return;
124
- if (!confirm(`Fusionner ce client dans un autre ? Cette action est irreversible.`)) return;
176
+ const targetClient = mergeResults.find((c) => c.id === targetId) || clients.find((c) => c.id === targetId);
177
+ if (!confirm(`Fusionner "${detail.client.company} (${detail.client.email})" dans "${targetClient?.company || targetId}" ?
178
+
179
+ Tous les tickets, messages et projets seront transf\xE9r\xE9s.
180
+ Le client "${detail.client.company}" sera supprim\xE9.
181
+
182
+ Cette action est irr\xE9versible.`)) return;
125
183
  setMerging(true);
126
184
  try {
127
- const res = await fetch("/api/support/merge-clients", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sourceId: selectedId, targetId }) });
185
+ const res = await fetch("/api/support/merge-clients", {
186
+ method: "POST",
187
+ headers: { "Content-Type": "application/json" },
188
+ body: JSON.stringify({ sourceId: selectedId, targetId })
189
+ });
128
190
  if (res.ok) {
129
191
  const data = await res.json();
130
192
  setMergeSuccess(data.message);
@@ -137,94 +199,166 @@ const CrmClient = () => {
137
199
  alert(`Erreur : ${err.error}`);
138
200
  }
139
201
  } catch {
140
- alert("Erreur reseau");
202
+ alert("Erreur r\xE9seau");
141
203
  }
142
204
  setMerging(false);
143
205
  };
144
- const S = {
145
- page: { padding: "20px 30px", maxWidth: 1200, margin: "0 auto" },
146
- grid: { display: "grid", gridTemplateColumns: "300px 1fr", gap: 20 },
147
- sidebar: { borderRight: "1px solid var(--theme-elevation-200)", paddingRight: 16 },
148
- searchInput: { width: "100%", padding: "8px 12px", borderRadius: 8, border: "1px solid var(--theme-elevation-200)", fontSize: 13, marginBottom: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" },
149
- clientItem: { padding: "10px 12px", borderRadius: 8, cursor: "pointer", marginBottom: 4, border: "1px solid transparent" },
150
- clientItemActive: { background: "var(--theme-elevation-50)", borderColor: "var(--theme-elevation-200)" },
151
- statsGrid: { display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 8, marginBottom: 16 },
152
- statCard: { padding: "10px 12px", borderRadius: 8, border: "1px solid var(--theme-elevation-150)", textAlign: "center" },
153
- table: { width: "100%", borderCollapse: "collapse", fontSize: 13 },
154
- th: { textAlign: "left", padding: "6px 8px", borderBottom: "1px solid var(--theme-elevation-200)", fontSize: 11, color: "var(--theme-elevation-500)" },
155
- td: { padding: "6px 8px", borderBottom: "1px solid var(--theme-elevation-100)" },
156
- badge: { padding: "2px 6px", borderRadius: 4, fontSize: 10, fontWeight: 600 }
157
- };
158
- return /* @__PURE__ */ jsxs("div", { style: S.page, children: [
159
- /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
160
- /* @__PURE__ */ jsx("h1", { style: { fontSize: 22, fontWeight: 700, margin: 0, color: "var(--theme-text)" }, children: t("crm.title") }),
161
- /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: "var(--theme-elevation-500)", marginTop: 4 }, children: t("crm.subtitle") })
162
- ] }),
163
- /* @__PURE__ */ jsxs("div", { style: S.grid, children: [
164
- /* @__PURE__ */ jsxs("div", { style: S.sidebar, children: [
165
- /* @__PURE__ */ jsx("input", { type: "text", placeholder: t("crm.searchPlaceholder"), value: search, onChange: (e) => setSearch(e.target.value), style: S.searchInput }),
166
- /* @__PURE__ */ jsx("div", { children: loading ? /* @__PURE__ */ jsx("div", { style: { padding: 20, textAlign: "center", color: "#94a3b8" }, children: t("common.loading") }) : clients.length === 0 ? /* @__PURE__ */ jsx("div", { style: { padding: 20, textAlign: "center", color: "#94a3b8" }, children: t("crm.noClientFound") }) : clients.map((c) => /* @__PURE__ */ jsxs("div", { onClick: () => selectClient(c.id), style: { ...S.clientItem, ...selectedId === c.id ? S.clientItemActive : {} }, children: [
167
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, fontSize: 13 }, children: c.company }),
168
- /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: "var(--theme-elevation-500)" }, children: [
169
- c.firstName,
170
- " ",
171
- c.lastName
172
- ] }),
173
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-400)" }, children: c.email })
174
- ] }, c.id)) })
206
+ return /* @__PURE__ */ jsxs("div", { className: styles.page, children: [
207
+ /* @__PURE__ */ jsx("div", { className: styles.header, children: /* @__PURE__ */ jsxs("div", { children: [
208
+ /* @__PURE__ */ jsx("h1", { className: styles.title, children: t("crm.title") }),
209
+ /* @__PURE__ */ jsx("div", { className: styles.subtitle, children: t("crm.subtitle") })
210
+ ] }) }),
211
+ /* @__PURE__ */ jsxs("div", { className: styles.grid, children: [
212
+ /* @__PURE__ */ jsxs("div", { className: styles.sidebar, children: [
213
+ /* @__PURE__ */ jsx("div", { className: styles.sidebarSearch, children: /* @__PURE__ */ jsx(
214
+ "input",
215
+ {
216
+ type: "text",
217
+ placeholder: t("crm.searchPlaceholder"),
218
+ value: search,
219
+ onChange: (e) => setSearch(e.target.value),
220
+ className: styles.searchInput
221
+ }
222
+ ) }),
223
+ /* @__PURE__ */ jsx("div", { className: styles.clientList, children: loading ? /* @__PURE__ */ jsxs("div", { className: styles.emptyState, children: [
224
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton }),
225
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton }),
226
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton }),
227
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton })
228
+ ] }) : clients.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.emptyState, children: t("crm.noClientFound") }) : clients.map((c) => /* @__PURE__ */ jsxs(
229
+ "div",
230
+ {
231
+ onClick: () => selectClient(c.id),
232
+ className: `${styles.clientItem} ${selectedId === c.id ? styles.clientItemActive : ""}`,
233
+ children: [
234
+ /* @__PURE__ */ jsx("div", { className: styles.clientCompany, children: c.company }),
235
+ /* @__PURE__ */ jsxs("div", { className: styles.clientName, children: [
236
+ c.firstName,
237
+ " ",
238
+ c.lastName
239
+ ] }),
240
+ /* @__PURE__ */ jsx("div", { className: styles.clientEmail, children: c.email })
241
+ ]
242
+ },
243
+ c.id
244
+ )) })
175
245
  ] }),
176
- /* @__PURE__ */ jsx("div", { children: !selectedId ? /* @__PURE__ */ jsx("div", { style: { padding: 60, textAlign: "center", color: "#94a3b8" }, children: t("crm.selectClient") }) : detailLoading ? /* @__PURE__ */ jsx("div", { style: { padding: 60, textAlign: "center", color: "#94a3b8" }, children: t("common.loading") }) : detail ? /* @__PURE__ */ jsxs("div", { children: [
177
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 16 }, children: [
246
+ /* @__PURE__ */ jsx("div", { children: !selectedId ? /* @__PURE__ */ jsx("div", { className: styles.placeholder, children: t("crm.selectClient") }) : detailLoading ? /* @__PURE__ */ jsxs("div", { className: styles.loadingCard, children: [
247
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton }),
248
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton }),
249
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton }),
250
+ /* @__PURE__ */ jsx("div", { className: styles.skeleton })
251
+ ] }) : detail ? /* @__PURE__ */ jsxs("div", { className: styles.detailStack, children: [
252
+ /* @__PURE__ */ jsxs("div", { className: styles.clientHeader, children: [
178
253
  /* @__PURE__ */ jsxs("div", { children: [
179
- /* @__PURE__ */ jsx("h2", { style: { fontSize: 20, fontWeight: 700, margin: 0 }, children: detail.client.company }),
180
- /* @__PURE__ */ jsxs("div", { style: { fontSize: 13, color: "var(--theme-elevation-500)" }, children: [
254
+ /* @__PURE__ */ jsx("h2", { className: styles.clientHeaderName, children: detail.client.company }),
255
+ /* @__PURE__ */ jsxs("div", { className: styles.clientHeaderContact, children: [
181
256
  detail.client.firstName,
182
257
  " ",
183
258
  detail.client.lastName
184
259
  ] }),
185
- /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: "var(--theme-elevation-400)" }, children: [
186
- detail.client.email,
187
- " ",
188
- detail.client.phone && `-- ${detail.client.phone}`
260
+ /* @__PURE__ */ jsxs("div", { className: styles.clientContactRow, children: [
261
+ /* @__PURE__ */ jsx("span", { children: detail.client.email }),
262
+ detail.client.phone && /* @__PURE__ */ jsx("span", { children: detail.client.phone })
189
263
  ] }),
190
- detail.client.notes && /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: "var(--theme-elevation-500)", marginTop: 4, fontStyle: "italic" }, children: detail.client.notes })
264
+ detail.client.notes && /* @__PURE__ */ jsx("div", { className: styles.clientNotes, children: detail.client.notes })
191
265
  ] }),
192
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
193
- /* @__PURE__ */ jsx("a", { href: `/admin/collections/support-clients/${detail.client.id}`, style: { padding: "6px 14px", borderRadius: 6, background: "#2563eb", color: "#fff", fontSize: 12, fontWeight: 600, textDecoration: "none" }, children: t("crm.editButton") }),
194
- /* @__PURE__ */ jsx("button", { onClick: () => {
195
- setShowMerge(!showMerge);
196
- setMergeSearch("");
197
- setMergeResults([]);
198
- }, style: { padding: "6px 14px", borderRadius: 6, border: "1px solid #d97706", background: "none", color: "#d97706", fontSize: 12, fontWeight: 600, cursor: "pointer" }, children: t("crm.mergeButton") })
266
+ /* @__PURE__ */ jsxs("div", { className: styles.headerActions, children: [
267
+ /* @__PURE__ */ jsx(
268
+ "a",
269
+ {
270
+ href: `/admin/collections/support-clients/${detail.client.id}`,
271
+ className: styles.btnPrimary,
272
+ children: t("crm.editButton")
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsx(
276
+ "button",
277
+ {
278
+ onClick: () => {
279
+ setShowMerge(!showMerge);
280
+ setMergeSearch("");
281
+ setMergeResults([]);
282
+ },
283
+ className: styles.btnWarning,
284
+ children: t("crm.mergeButton")
285
+ }
286
+ )
199
287
  ] })
200
288
  ] }),
201
- mergeSuccess && /* @__PURE__ */ jsx("div", { style: { padding: "8px 14px", borderRadius: 6, background: "#dcfce7", color: "#166534", fontSize: 13, marginBottom: 12 }, children: mergeSuccess }),
202
- showMerge && /* @__PURE__ */ jsxs("div", { style: { padding: 16, borderRadius: 8, border: "1px solid #fde68a", background: "#fefce8", marginBottom: 16 }, children: [
203
- /* @__PURE__ */ jsx("h4", { style: { margin: "0 0 8px", fontSize: 14 }, children: t("crm.mergeTitle") }),
204
- /* @__PURE__ */ jsx("input", { type: "text", value: mergeSearch, onChange: (e) => setMergeSearch(e.target.value), placeholder: t("crm.mergeSearchPlaceholder"), style: S.searchInput }),
205
- mergeResults.map((c) => /* @__PURE__ */ jsxs("button", { onClick: () => handleMerge(c.id), disabled: merging, style: { display: "block", width: "100%", padding: "8px 12px", border: "1px solid var(--theme-elevation-200)", borderRadius: 6, background: "var(--theme-elevation-0)", cursor: "pointer", textAlign: "left", marginBottom: 4, fontSize: 13 }, children: [
206
- /* @__PURE__ */ jsx("strong", { children: c.company }),
207
- " -- ",
208
- c.firstName,
209
- " ",
210
- c.lastName,
211
- " ",
212
- /* @__PURE__ */ jsx("span", { style: { color: "var(--theme-elevation-400)", fontSize: 11 }, children: c.email })
213
- ] }, c.id))
289
+ mergeSuccess && /* @__PURE__ */ jsx("div", { className: styles.successBanner, children: mergeSuccess }),
290
+ showMerge && /* @__PURE__ */ jsxs("div", { className: styles.mergePanel, children: [
291
+ /* @__PURE__ */ jsx("h4", { className: styles.mergePanelTitle, children: t("crm.mergeTitle") }),
292
+ /* @__PURE__ */ jsxs("p", { className: styles.mergePanelDesc, children: [
293
+ "Tous les tickets, messages, projets et enquetes de ",
294
+ /* @__PURE__ */ jsx("strong", { children: detail.client.company }),
295
+ " seront transferes vers le client cible. Le client actuel sera supprime."
296
+ ] }),
297
+ /* @__PURE__ */ jsx(
298
+ "input",
299
+ {
300
+ type: "text",
301
+ value: mergeSearch,
302
+ onChange: (e) => setMergeSearch(e.target.value),
303
+ placeholder: t("crm.mergeSearchPlaceholder"),
304
+ className: styles.mergeInput
305
+ }
306
+ ),
307
+ mergeResults.length > 0 && /* @__PURE__ */ jsx("div", { className: styles.mergeResultList, children: mergeResults.map((c) => /* @__PURE__ */ jsxs(
308
+ "button",
309
+ {
310
+ onClick: () => handleMerge(c.id),
311
+ disabled: merging,
312
+ className: styles.mergeResultItem,
313
+ children: [
314
+ /* @__PURE__ */ jsxs("div", { children: [
315
+ /* @__PURE__ */ jsx("strong", { children: c.company }),
316
+ /* @__PURE__ */ jsxs("span", { className: styles.mergeResultName, children: [
317
+ c.firstName,
318
+ " ",
319
+ c.lastName
320
+ ] })
321
+ ] }),
322
+ /* @__PURE__ */ jsx("span", { className: styles.mergeResultEmail, children: c.email })
323
+ ]
324
+ },
325
+ c.id
326
+ )) }),
327
+ mergeSearch.length >= 2 && mergeResults.length === 0 && /* @__PURE__ */ jsx("p", { className: styles.mergeEmpty, children: t("crm.mergeNoClient") })
328
+ ] }),
329
+ /* @__PURE__ */ jsx("div", { className: styles.statsGrid, children: [
330
+ { key: "total", label: t("crm.stats.totalTickets"), value: String(detail.stats.totalTickets) },
331
+ { key: "open", label: t("crm.stats.openTickets"), value: String(detail.stats.openTickets) },
332
+ { key: "resolved", label: t("crm.stats.resolvedTickets"), value: String(detail.stats.resolvedTickets) },
333
+ { key: "time", label: t("crm.stats.timeSpent"), value: formatDuration(detail.stats.totalTimeMinutes) },
334
+ { key: "activity", label: t("crm.stats.lastActivity"), value: detail.stats.lastActivity ? timeAgo(detail.stats.lastActivity) : "-" }
335
+ ].map((stat) => /* @__PURE__ */ jsxs("div", { className: styles.statCard, children: [
336
+ /* @__PURE__ */ jsx("div", { className: styles.statLabel, children: stat.label }),
337
+ /* @__PURE__ */ jsx("div", { className: `${styles.statValue} ${getStatColorClass(stat.key, detail.stats)}`, children: stat.value })
338
+ ] }, stat.key)) }),
339
+ detail.projects.length > 0 && /* @__PURE__ */ jsxs("div", { className: styles.sectionCard, children: [
340
+ /* @__PURE__ */ jsxs("h3", { className: styles.sectionTitle, children: [
341
+ t("crm.sections.projects"),
342
+ " (",
343
+ detail.projects.length,
344
+ ")"
345
+ ] }),
346
+ /* @__PURE__ */ jsx("div", { className: styles.projectList, children: detail.projects.map((p) => /* @__PURE__ */ jsxs(
347
+ "a",
348
+ {
349
+ href: `/admin/collections/projects/${p.id}`,
350
+ className: styles.projectChip,
351
+ children: [
352
+ p.name,
353
+ /* @__PURE__ */ jsx("span", { className: `${styles.badge} ${styles[projectStatusBadgeClass[p.status] || "badgeAccent"]}`, children: t(projectStatusLabelKeys[p.status] || "crm.projectStatus.active") })
354
+ ]
355
+ },
356
+ p.id
357
+ )) })
214
358
  ] }),
215
- /* @__PURE__ */ jsx("div", { style: S.statsGrid, children: [
216
- { label: t("crm.stats.totalTickets"), value: String(detail.stats.totalTickets) },
217
- { label: t("crm.stats.openTickets"), value: String(detail.stats.openTickets) },
218
- { label: t("crm.stats.resolvedTickets"), value: String(detail.stats.resolvedTickets) },
219
- { label: t("crm.stats.timeSpent"), value: formatDuration(detail.stats.totalTimeMinutes) },
220
- { label: t("crm.stats.lastActivity"), value: detail.stats.lastActivity ? timeAgo(detail.stats.lastActivity) : "-" }
221
- ].map((stat) => /* @__PURE__ */ jsxs("div", { style: S.statCard, children: [
222
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)", marginBottom: 2 }, children: stat.label }),
223
- /* @__PURE__ */ jsx("div", { style: { fontSize: 18, fontWeight: 700, color: "var(--theme-text)" }, children: stat.value })
224
- ] }, stat.label)) }),
225
- /* @__PURE__ */ jsxs("div", { style: { padding: 16, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", marginBottom: 16, background: "linear-gradient(135deg, rgba(37,99,235,0.03) 0%, rgba(139,92,246,0.03) 100%)" }, children: [
226
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }, children: [
227
- /* @__PURE__ */ jsxs("h3", { style: { fontSize: 14, fontWeight: 700, margin: 0, display: "flex", alignItems: "center", gap: 6 }, children: [
359
+ /* @__PURE__ */ jsxs("div", { className: styles.sectionCard, style: { background: "linear-gradient(135deg, rgba(37,99,235,0.03) 0%, rgba(139,92,246,0.03) 100%)" }, children: [
360
+ /* @__PURE__ */ jsxs("div", { className: styles.sectionHeader, children: [
361
+ /* @__PURE__ */ jsxs("h3", { className: styles.sectionTitle, style: { display: "flex", alignItems: "center", gap: 6 }, children: [
228
362
  /* @__PURE__ */ jsx("span", { style: { fontSize: 16 }, children: "\u{1F9E0}" }),
229
363
  " R\xE9sum\xE9 IA"
230
364
  ] }),
@@ -233,34 +367,35 @@ const CrmClient = () => {
233
367
  {
234
368
  onClick: () => selectedId && fetchIntelligence(selectedId, true),
235
369
  disabled: intelRefreshing,
236
- style: { padding: "4px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", background: "var(--theme-elevation-0)", fontSize: 11, fontWeight: 600, cursor: "pointer", color: "var(--theme-text)" },
370
+ className: styles.viewAllLink,
371
+ style: { cursor: "pointer", background: "none", border: "none", fontWeight: 600 },
237
372
  children: intelRefreshing ? "\u23F3 G\xE9n\xE9ration..." : "\u{1F504} Actualiser"
238
373
  }
239
374
  )
240
375
  ] }),
241
- intelLoading ? /* @__PURE__ */ jsx("div", { style: { padding: 20, textAlign: "center", color: "var(--theme-elevation-400)", fontSize: 13 }, children: "Chargement du r\xE9sum\xE9..." }) : intelligence ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
242
- /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: 13, lineHeight: 1.6, color: "var(--theme-text)" }, children: intelligence.summary }),
243
- intelligence.recurringTopics && intelligence.recurringTopics.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
244
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--theme-elevation-500)", marginBottom: 6, textTransform: "uppercase" }, children: "Sujets r\xE9currents" }),
245
- /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: intelligence.recurringTopics.map((t2, i) => /* @__PURE__ */ jsxs("span", { style: { padding: "3px 10px", borderRadius: 12, background: "rgba(37,99,235,0.08)", color: "#2563eb", fontSize: 11, fontWeight: 600 }, children: [
246
- t2.topic,
376
+ intelLoading ? /* @__PURE__ */ jsx("div", { style: { padding: 20, textAlign: "center", color: "#94a3b8", fontSize: 13 }, children: "Chargement du r\xE9sum\xE9..." }) : intelligence ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
377
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: 13, lineHeight: 1.6 }, children: intelligence.summary }),
378
+ intelligence.recurringTopics?.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
379
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "#6b7280", marginBottom: 6, textTransform: "uppercase" }, children: "Sujets r\xE9currents" }),
380
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: intelligence.recurringTopics.map((tp, i) => /* @__PURE__ */ jsxs("span", { style: { padding: "3px 10px", borderRadius: 12, background: "rgba(37,99,235,0.08)", color: "#2563eb", fontSize: 11, fontWeight: 600 }, children: [
381
+ tp.topic,
247
382
  " (",
248
- t2.count,
383
+ tp.count,
249
384
  "x)"
250
385
  ] }, i)) })
251
386
  ] }),
252
- intelligence.patterns && intelligence.patterns.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
253
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--theme-elevation-500)", marginBottom: 6, textTransform: "uppercase" }, children: "Patterns d\xE9tect\xE9s" }),
254
- /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12, color: "var(--theme-text)", lineHeight: 1.8 }, children: intelligence.patterns.map((p, i) => /* @__PURE__ */ jsx("li", { children: p }, i)) })
387
+ intelligence.patterns?.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
388
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "#6b7280", marginBottom: 6, textTransform: "uppercase" }, children: "Patterns" }),
389
+ /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12, lineHeight: 1.8 }, children: intelligence.patterns.map((p, i) => /* @__PURE__ */ jsx("li", { children: p }, i)) })
255
390
  ] }),
256
- intelligence.keyFacts && intelligence.keyFacts.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
257
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--theme-elevation-500)", marginBottom: 6, textTransform: "uppercase" }, children: "Faits cl\xE9s" }),
391
+ intelligence.keyFacts?.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
392
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "#6b7280", marginBottom: 6, textTransform: "uppercase" }, children: "Faits cl\xE9s" }),
258
393
  /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: intelligence.keyFacts.map((f, i) => /* @__PURE__ */ jsx("span", { style: { padding: "3px 10px", borderRadius: 12, background: "rgba(22,163,74,0.08)", color: "#16a34a", fontSize: 11, fontWeight: 600 }, children: f }, i)) })
259
394
  ] }),
260
- /* @__PURE__ */ jsxs("div", { style: { fontSize: 10, color: "var(--theme-elevation-400)", display: "flex", gap: 12, marginTop: 4 }, children: [
395
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 10, color: "#9ca3af", display: "flex", gap: 12 }, children: [
261
396
  /* @__PURE__ */ jsxs("span", { children: [
262
397
  intelligence.ticketCount,
263
- " tickets analys\xE9s"
398
+ " tickets"
264
399
  ] }),
265
400
  /* @__PURE__ */ jsxs("span", { children: [
266
401
  intelligence.messageCount,
@@ -271,33 +406,42 @@ const CrmClient = () => {
271
406
  intelligence.averageSatisfaction,
272
407
  "/5"
273
408
  ] }),
274
- intelligence.fromCache && /* @__PURE__ */ jsx("span", { children: "Cache" }),
275
- intelligence.generatedAt && /* @__PURE__ */ jsxs("span", { children: [
276
- "G\xE9n\xE9r\xE9 ",
277
- timeAgo(intelligence.generatedAt)
278
- ] })
409
+ intelligence.fromCache && /* @__PURE__ */ jsx("span", { children: "\u2022 Cache" })
279
410
  ] })
280
- ] }) : /* @__PURE__ */ jsx("div", { style: { padding: 16, textAlign: "center", color: "var(--theme-elevation-400)", fontSize: 13 }, children: 'Cliquez sur "Actualiser" pour g\xE9n\xE9rer le r\xE9sum\xE9 IA de ce client.' })
411
+ ] }) : /* @__PURE__ */ jsx("div", { style: { padding: 16, textAlign: "center", color: "#94a3b8", fontSize: 13 }, children: "Cliquez sur \xAB Actualiser \xBB pour g\xE9n\xE9rer le r\xE9sum\xE9 IA." })
281
412
  ] }),
282
- /* @__PURE__ */ jsxs("div", { style: { padding: 16, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", marginBottom: 16 }, children: [
283
- /* @__PURE__ */ jsxs("h3", { style: { fontSize: 14, fontWeight: 700, margin: "0 0 8px" }, children: [
284
- t("crm.sections.tickets"),
285
- " (",
286
- detail.tickets.length,
287
- ")"
413
+ /* @__PURE__ */ jsxs("div", { className: styles.sectionCard, children: [
414
+ /* @__PURE__ */ jsxs("div", { className: styles.sectionHeader, children: [
415
+ /* @__PURE__ */ jsxs("h3", { className: styles.sectionTitle, children: [
416
+ t("crm.sections.tickets"),
417
+ " (",
418
+ detail.tickets.length,
419
+ ")"
420
+ ] }),
421
+ /* @__PURE__ */ jsxs(
422
+ "a",
423
+ {
424
+ href: `/admin/collections/tickets?where[client][equals]=${detail.client.id}`,
425
+ className: styles.sectionLink,
426
+ children: [
427
+ t("crm.sections.viewAll"),
428
+ " \u2192"
429
+ ]
430
+ }
431
+ )
288
432
  ] }),
289
- detail.tickets.length === 0 ? /* @__PURE__ */ jsx("div", { style: { color: "#94a3b8", fontSize: 13 }, children: t("crm.noTickets") }) : /* @__PURE__ */ jsxs("table", { style: S.table, children: [
433
+ detail.tickets.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.ticketEmpty, children: t("crm.noTickets") }) : /* @__PURE__ */ jsxs("table", { className: styles.table, children: [
290
434
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
291
- /* @__PURE__ */ jsx("th", { style: S.th, children: t("crm.tableHeaders.number") }),
292
- /* @__PURE__ */ jsx("th", { style: S.th, children: t("crm.tableHeaders.subject") }),
293
- /* @__PURE__ */ jsx("th", { style: S.th, children: t("crm.tableHeaders.status") }),
294
- /* @__PURE__ */ jsx("th", { style: S.th, children: t("crm.tableHeaders.date") })
435
+ /* @__PURE__ */ jsx("th", { children: t("crm.tableHeaders.number") }),
436
+ /* @__PURE__ */ jsx("th", { children: t("crm.tableHeaders.subject") }),
437
+ /* @__PURE__ */ jsx("th", { children: t("crm.tableHeaders.status") }),
438
+ /* @__PURE__ */ jsx("th", { children: t("crm.tableHeaders.date") })
295
439
  ] }) }),
296
440
  /* @__PURE__ */ jsx("tbody", { children: detail.tickets.map((tk) => /* @__PURE__ */ jsxs("tr", { children: [
297
- /* @__PURE__ */ jsx("td", { style: S.td, children: /* @__PURE__ */ jsx("a", { href: `/admin/support/ticket?id=${tk.id}`, style: { color: "#2563eb", fontWeight: 600, textDecoration: "none" }, children: tk.ticketNumber }) }),
298
- /* @__PURE__ */ jsx("td", { style: S.td, children: tk.subject }),
299
- /* @__PURE__ */ jsx("td", { style: S.td, children: /* @__PURE__ */ jsx("span", { style: { ...S.badge, background: `${statusColors[tk.status] || "#94a3b8"}20`, color: statusColors[tk.status] || "#94a3b8" }, children: statusLabelKeys[tk.status] ? t(statusLabelKeys[tk.status]) : tk.status }) }),
300
- /* @__PURE__ */ jsx("td", { style: { ...S.td, fontSize: 12, color: "var(--theme-elevation-400)" }, children: timeAgo(tk.createdAt) })
441
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("a", { href: `/admin/collections/tickets/${tk.id}`, className: styles.ticketLink, children: tk.ticketNumber }) }),
442
+ /* @__PURE__ */ jsx("td", { className: styles.ticketSubject, children: tk.subject }),
443
+ /* @__PURE__ */ jsx("td", { className: styles.ticketStatusCell, children: /* @__PURE__ */ jsx("span", { className: `${styles.badge} ${styles[statusBadgeClass[tk.status] || "badgeAccent"]}`, children: t(statusLabelKeys[tk.status] || "ticket.status.open") }) }),
444
+ /* @__PURE__ */ jsx("td", { className: styles.ticketDate, children: timeAgo(tk.createdAt) })
301
445
  ] }, tk.id)) })
302
446
  ] })
303
447
  ] })