@consilioweb/payload-support 0.8.0 → 0.8.2
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.
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -31,6 +31,9 @@ const CrmClient = () => {
|
|
|
31
31
|
const [detailLoading, setDetailLoading] = react.useState(false);
|
|
32
32
|
const [showMerge, setShowMerge] = react.useState(false);
|
|
33
33
|
const [mergeSearch, setMergeSearch] = react.useState("");
|
|
34
|
+
const [intelligence, setIntelligence] = react.useState(null);
|
|
35
|
+
const [intelLoading, setIntelLoading] = react.useState(false);
|
|
36
|
+
const [intelRefreshing, setIntelRefreshing] = react.useState(false);
|
|
34
37
|
const [mergeResults, setMergeResults] = react.useState([]);
|
|
35
38
|
const [merging, setMerging] = react.useState(false);
|
|
36
39
|
const [mergeSuccess, setMergeSuccess] = react.useState("");
|
|
@@ -77,11 +80,28 @@ const CrmClient = () => {
|
|
|
77
80
|
}
|
|
78
81
|
setDetailLoading(false);
|
|
79
82
|
}, []);
|
|
83
|
+
const fetchIntelligence = react.useCallback(async (clientId, force = false) => {
|
|
84
|
+
if (force) setIntelRefreshing(true);
|
|
85
|
+
else setIntelLoading(true);
|
|
86
|
+
try {
|
|
87
|
+
const method = force ? "POST" : "GET";
|
|
88
|
+
const url = force ? "/api/support/client-intelligence" : `/api/support/client-intelligence?clientId=${clientId}`;
|
|
89
|
+
const opts = { method, credentials: "include", headers: { "Content-Type": "application/json" } };
|
|
90
|
+
if (force) opts.body = JSON.stringify({ clientId });
|
|
91
|
+
const res = await fetch(url, opts);
|
|
92
|
+
if (res.ok) setIntelligence(await res.json());
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
setIntelLoading(false);
|
|
96
|
+
setIntelRefreshing(false);
|
|
97
|
+
}, []);
|
|
80
98
|
const selectClient = (id) => {
|
|
81
99
|
setSelectedId(id);
|
|
82
100
|
fetchDetail(id);
|
|
101
|
+
fetchIntelligence(id);
|
|
83
102
|
setShowMerge(false);
|
|
84
103
|
setMergeSuccess("");
|
|
104
|
+
setIntelligence(null);
|
|
85
105
|
};
|
|
86
106
|
react.useEffect(() => {
|
|
87
107
|
if (!mergeSearch || mergeSearch.length < 2) {
|
|
@@ -203,6 +223,63 @@ const CrmClient = () => {
|
|
|
203
223
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)", marginBottom: 2 }, children: stat.label }),
|
|
204
224
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 18, fontWeight: 700, color: "var(--theme-text)" }, children: stat.value })
|
|
205
225
|
] }, stat.label)) }),
|
|
226
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
227
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }, children: [
|
|
228
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h3", { style: { fontSize: 14, fontWeight: 700, margin: 0, display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
229
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 16 }, children: "\u{1F9E0}" }),
|
|
230
|
+
" R\xE9sum\xE9 IA"
|
|
231
|
+
] }),
|
|
232
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
233
|
+
"button",
|
|
234
|
+
{
|
|
235
|
+
onClick: () => selectedId && fetchIntelligence(selectedId, true),
|
|
236
|
+
disabled: intelRefreshing,
|
|
237
|
+
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)" },
|
|
238
|
+
children: intelRefreshing ? "\u23F3 G\xE9n\xE9ration..." : "\u{1F504} Actualiser"
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
] }),
|
|
242
|
+
intelLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 20, textAlign: "center", color: "var(--theme-elevation-400)", fontSize: 13 }, children: "Chargement du r\xE9sum\xE9..." }) : intelligence ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
|
|
243
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: 13, lineHeight: 1.6, color: "var(--theme-text)" }, children: intelligence.summary }),
|
|
244
|
+
intelligence.recurringTopics && intelligence.recurringTopics.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
245
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--theme-elevation-500)", marginBottom: 6, textTransform: "uppercase" }, children: "Sujets r\xE9currents" }),
|
|
246
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: intelligence.recurringTopics.map((t2, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { padding: "3px 10px", borderRadius: 12, background: "rgba(37,99,235,0.08)", color: "#2563eb", fontSize: 11, fontWeight: 600 }, children: [
|
|
247
|
+
t2.topic,
|
|
248
|
+
" (",
|
|
249
|
+
t2.count,
|
|
250
|
+
"x)"
|
|
251
|
+
] }, i)) })
|
|
252
|
+
] }),
|
|
253
|
+
intelligence.patterns && intelligence.patterns.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
254
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--theme-elevation-500)", marginBottom: 6, textTransform: "uppercase" }, children: "Patterns d\xE9tect\xE9s" }),
|
|
255
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12, color: "var(--theme-text)", lineHeight: 1.8 }, children: intelligence.patterns.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: p }, i)) })
|
|
256
|
+
] }),
|
|
257
|
+
intelligence.keyFacts && intelligence.keyFacts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
258
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--theme-elevation-500)", marginBottom: 6, textTransform: "uppercase" }, children: "Faits cl\xE9s" }),
|
|
259
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: intelligence.keyFacts.map((f, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: "3px 10px", borderRadius: 12, background: "rgba(22,163,74,0.08)", color: "#16a34a", fontSize: 11, fontWeight: 600 }, children: f }, i)) })
|
|
260
|
+
] }),
|
|
261
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 10, color: "var(--theme-elevation-400)", display: "flex", gap: 12, marginTop: 4 }, children: [
|
|
262
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
263
|
+
intelligence.ticketCount,
|
|
264
|
+
" tickets analys\xE9s"
|
|
265
|
+
] }),
|
|
266
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
267
|
+
intelligence.messageCount,
|
|
268
|
+
" messages"
|
|
269
|
+
] }),
|
|
270
|
+
intelligence.averageSatisfaction && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
271
|
+
"Satisfaction: ",
|
|
272
|
+
intelligence.averageSatisfaction,
|
|
273
|
+
"/5"
|
|
274
|
+
] }),
|
|
275
|
+
intelligence.fromCache && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Cache" }),
|
|
276
|
+
intelligence.generatedAt && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
277
|
+
"G\xE9n\xE9r\xE9 ",
|
|
278
|
+
timeAgo(intelligence.generatedAt)
|
|
279
|
+
] })
|
|
280
|
+
] })
|
|
281
|
+
] }) : /* @__PURE__ */ jsxRuntime.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.' })
|
|
282
|
+
] }),
|
|
206
283
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 16, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", marginBottom: 16 }, children: [
|
|
207
284
|
/* @__PURE__ */ jsxRuntime.jsxs("h3", { style: { fontSize: 14, fontWeight: 700, margin: "0 0 8px" }, children: [
|
|
208
285
|
t("crm.sections.tickets"),
|
|
@@ -30,6 +30,9 @@ const CrmClient = () => {
|
|
|
30
30
|
const [detailLoading, setDetailLoading] = useState(false);
|
|
31
31
|
const [showMerge, setShowMerge] = useState(false);
|
|
32
32
|
const [mergeSearch, setMergeSearch] = useState("");
|
|
33
|
+
const [intelligence, setIntelligence] = useState(null);
|
|
34
|
+
const [intelLoading, setIntelLoading] = useState(false);
|
|
35
|
+
const [intelRefreshing, setIntelRefreshing] = useState(false);
|
|
33
36
|
const [mergeResults, setMergeResults] = useState([]);
|
|
34
37
|
const [merging, setMerging] = useState(false);
|
|
35
38
|
const [mergeSuccess, setMergeSuccess] = useState("");
|
|
@@ -76,11 +79,28 @@ const CrmClient = () => {
|
|
|
76
79
|
}
|
|
77
80
|
setDetailLoading(false);
|
|
78
81
|
}, []);
|
|
82
|
+
const fetchIntelligence = useCallback(async (clientId, force = false) => {
|
|
83
|
+
if (force) setIntelRefreshing(true);
|
|
84
|
+
else setIntelLoading(true);
|
|
85
|
+
try {
|
|
86
|
+
const method = force ? "POST" : "GET";
|
|
87
|
+
const url = force ? "/api/support/client-intelligence" : `/api/support/client-intelligence?clientId=${clientId}`;
|
|
88
|
+
const opts = { method, credentials: "include", headers: { "Content-Type": "application/json" } };
|
|
89
|
+
if (force) opts.body = JSON.stringify({ clientId });
|
|
90
|
+
const res = await fetch(url, opts);
|
|
91
|
+
if (res.ok) setIntelligence(await res.json());
|
|
92
|
+
} catch {
|
|
93
|
+
}
|
|
94
|
+
setIntelLoading(false);
|
|
95
|
+
setIntelRefreshing(false);
|
|
96
|
+
}, []);
|
|
79
97
|
const selectClient = (id) => {
|
|
80
98
|
setSelectedId(id);
|
|
81
99
|
fetchDetail(id);
|
|
100
|
+
fetchIntelligence(id);
|
|
82
101
|
setShowMerge(false);
|
|
83
102
|
setMergeSuccess("");
|
|
103
|
+
setIntelligence(null);
|
|
84
104
|
};
|
|
85
105
|
useEffect(() => {
|
|
86
106
|
if (!mergeSearch || mergeSearch.length < 2) {
|
|
@@ -202,6 +222,63 @@ const CrmClient = () => {
|
|
|
202
222
|
/* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)", marginBottom: 2 }, children: stat.label }),
|
|
203
223
|
/* @__PURE__ */ jsx("div", { style: { fontSize: 18, fontWeight: 700, color: "var(--theme-text)" }, children: stat.value })
|
|
204
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: [
|
|
228
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 16 }, children: "\u{1F9E0}" }),
|
|
229
|
+
" R\xE9sum\xE9 IA"
|
|
230
|
+
] }),
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
"button",
|
|
233
|
+
{
|
|
234
|
+
onClick: () => selectedId && fetchIntelligence(selectedId, true),
|
|
235
|
+
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)" },
|
|
237
|
+
children: intelRefreshing ? "\u23F3 G\xE9n\xE9ration..." : "\u{1F504} Actualiser"
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
] }),
|
|
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,
|
|
247
|
+
" (",
|
|
248
|
+
t2.count,
|
|
249
|
+
"x)"
|
|
250
|
+
] }, i)) })
|
|
251
|
+
] }),
|
|
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)) })
|
|
255
|
+
] }),
|
|
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" }),
|
|
258
|
+
/* @__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
|
+
] }),
|
|
260
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: 10, color: "var(--theme-elevation-400)", display: "flex", gap: 12, marginTop: 4 }, children: [
|
|
261
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
262
|
+
intelligence.ticketCount,
|
|
263
|
+
" tickets analys\xE9s"
|
|
264
|
+
] }),
|
|
265
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
266
|
+
intelligence.messageCount,
|
|
267
|
+
" messages"
|
|
268
|
+
] }),
|
|
269
|
+
intelligence.averageSatisfaction && /* @__PURE__ */ jsxs("span", { children: [
|
|
270
|
+
"Satisfaction: ",
|
|
271
|
+
intelligence.averageSatisfaction,
|
|
272
|
+
"/5"
|
|
273
|
+
] }),
|
|
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
|
+
] })
|
|
279
|
+
] })
|
|
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.' })
|
|
281
|
+
] }),
|
|
205
282
|
/* @__PURE__ */ jsxs("div", { style: { padding: 16, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", marginBottom: 16 }, children: [
|
|
206
283
|
/* @__PURE__ */ jsxs("h3", { style: { fontSize: 14, fontWeight: 700, margin: "0 0 8px" }, children: [
|
|
207
284
|
t("crm.sections.tickets"),
|
package/package.json
CHANGED
|
@@ -18,6 +18,19 @@ const statusColors: Record<string, string> = { open: '#3b82f6', waiting_client:
|
|
|
18
18
|
function formatDuration(minutes: number): string { const h = Math.floor(minutes / 60); const m = minutes % 60; if (h === 0) return `${m}min`; if (m === 0) return `${h}h`; return `${h}h${m}m` }
|
|
19
19
|
function timeAgo(dateStr: string): string { const diff = Date.now() - new Date(dateStr).getTime(); const days = Math.floor(diff / 86400000); if (days === 0) return "Aujourd'hui"; if (days === 1) return 'Hier'; if (days < 30) return `Il y a ${days}j`; return `Il y a ${Math.floor(days / 30)} mois` }
|
|
20
20
|
|
|
21
|
+
interface ClientSummary {
|
|
22
|
+
summary: string
|
|
23
|
+
recurringTopics: { topic: string; count: number; lastSeen: string }[]
|
|
24
|
+
patterns: string[]
|
|
25
|
+
keyFacts: string[]
|
|
26
|
+
ticketCount: number
|
|
27
|
+
messageCount: number
|
|
28
|
+
averageSatisfaction: number | null
|
|
29
|
+
generatedAt: string
|
|
30
|
+
aiModel: string
|
|
31
|
+
fromCache?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
export const CrmClient: React.FC = () => {
|
|
22
35
|
const { t } = useTranslation()
|
|
23
36
|
const [clients, setClients] = useState<Client[]>([])
|
|
@@ -28,6 +41,10 @@ export const CrmClient: React.FC = () => {
|
|
|
28
41
|
const [detailLoading, setDetailLoading] = useState(false)
|
|
29
42
|
const [showMerge, setShowMerge] = useState(false)
|
|
30
43
|
const [mergeSearch, setMergeSearch] = useState('')
|
|
44
|
+
// Client Intelligence
|
|
45
|
+
const [intelligence, setIntelligence] = useState<ClientSummary | null>(null)
|
|
46
|
+
const [intelLoading, setIntelLoading] = useState(false)
|
|
47
|
+
const [intelRefreshing, setIntelRefreshing] = useState(false)
|
|
31
48
|
const [mergeResults, setMergeResults] = useState<Client[]>([])
|
|
32
49
|
const [merging, setMerging] = useState(false)
|
|
33
50
|
const [mergeSuccess, setMergeSuccess] = useState('')
|
|
@@ -66,7 +83,22 @@ export const CrmClient: React.FC = () => {
|
|
|
66
83
|
setDetailLoading(false)
|
|
67
84
|
}, [])
|
|
68
85
|
|
|
69
|
-
const
|
|
86
|
+
const fetchIntelligence = useCallback(async (clientId: number, force = false) => {
|
|
87
|
+
if (force) setIntelRefreshing(true); else setIntelLoading(true)
|
|
88
|
+
try {
|
|
89
|
+
const method = force ? 'POST' : 'GET'
|
|
90
|
+
const url = force
|
|
91
|
+
? '/api/support/client-intelligence'
|
|
92
|
+
: `/api/support/client-intelligence?clientId=${clientId}`
|
|
93
|
+
const opts: RequestInit = { method, credentials: 'include', headers: { 'Content-Type': 'application/json' } }
|
|
94
|
+
if (force) opts.body = JSON.stringify({ clientId })
|
|
95
|
+
const res = await fetch(url, opts)
|
|
96
|
+
if (res.ok) setIntelligence(await res.json())
|
|
97
|
+
} catch { /* silent */ }
|
|
98
|
+
setIntelLoading(false); setIntelRefreshing(false)
|
|
99
|
+
}, [])
|
|
100
|
+
|
|
101
|
+
const selectClient = (id: number) => { setSelectedId(id); fetchDetail(id); fetchIntelligence(id); setShowMerge(false); setMergeSuccess(''); setIntelligence(null) }
|
|
70
102
|
|
|
71
103
|
useEffect(() => {
|
|
72
104
|
if (!mergeSearch || mergeSearch.length < 2) { setMergeResults([]); return }
|
|
@@ -178,6 +210,81 @@ export const CrmClient: React.FC = () => {
|
|
|
178
210
|
))}
|
|
179
211
|
</div>
|
|
180
212
|
|
|
213
|
+
{/* Client Intelligence */}
|
|
214
|
+
<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%)' }}>
|
|
215
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
|
216
|
+
<h3 style={{ fontSize: 14, fontWeight: 700, margin: 0, display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
217
|
+
<span style={{ fontSize: 16 }}>🧠</span> Résumé IA
|
|
218
|
+
</h3>
|
|
219
|
+
<button
|
|
220
|
+
onClick={() => selectedId && fetchIntelligence(selectedId, true)}
|
|
221
|
+
disabled={intelRefreshing}
|
|
222
|
+
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)' }}
|
|
223
|
+
>
|
|
224
|
+
{intelRefreshing ? '⏳ Génération...' : '🔄 Actualiser'}
|
|
225
|
+
</button>
|
|
226
|
+
</div>
|
|
227
|
+
{intelLoading ? (
|
|
228
|
+
<div style={{ padding: 20, textAlign: 'center', color: 'var(--theme-elevation-400)', fontSize: 13 }}>Chargement du résumé...</div>
|
|
229
|
+
) : intelligence ? (
|
|
230
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
231
|
+
{/* Summary */}
|
|
232
|
+
<p style={{ margin: 0, fontSize: 13, lineHeight: 1.6, color: 'var(--theme-text)' }}>{intelligence.summary}</p>
|
|
233
|
+
|
|
234
|
+
{/* Recurring Topics */}
|
|
235
|
+
{intelligence.recurringTopics && intelligence.recurringTopics.length > 0 && (
|
|
236
|
+
<div>
|
|
237
|
+
<div style={{ fontSize: 11, fontWeight: 700, color: 'var(--theme-elevation-500)', marginBottom: 6, textTransform: 'uppercase' }}>Sujets récurrents</div>
|
|
238
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
239
|
+
{intelligence.recurringTopics.map((t, i) => (
|
|
240
|
+
<span key={i} style={{ padding: '3px 10px', borderRadius: 12, background: 'rgba(37,99,235,0.08)', color: '#2563eb', fontSize: 11, fontWeight: 600 }}>
|
|
241
|
+
{t.topic} ({t.count}x)
|
|
242
|
+
</span>
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
|
|
248
|
+
{/* Patterns */}
|
|
249
|
+
{intelligence.patterns && intelligence.patterns.length > 0 && (
|
|
250
|
+
<div>
|
|
251
|
+
<div style={{ fontSize: 11, fontWeight: 700, color: 'var(--theme-elevation-500)', marginBottom: 6, textTransform: 'uppercase' }}>Patterns détectés</div>
|
|
252
|
+
<ul style={{ margin: 0, paddingLeft: 18, fontSize: 12, color: 'var(--theme-text)', lineHeight: 1.8 }}>
|
|
253
|
+
{intelligence.patterns.map((p, i) => <li key={i}>{p}</li>)}
|
|
254
|
+
</ul>
|
|
255
|
+
</div>
|
|
256
|
+
)}
|
|
257
|
+
|
|
258
|
+
{/* Key Facts */}
|
|
259
|
+
{intelligence.keyFacts && intelligence.keyFacts.length > 0 && (
|
|
260
|
+
<div>
|
|
261
|
+
<div style={{ fontSize: 11, fontWeight: 700, color: 'var(--theme-elevation-500)', marginBottom: 6, textTransform: 'uppercase' }}>Faits clés</div>
|
|
262
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
|
263
|
+
{intelligence.keyFacts.map((f, i) => (
|
|
264
|
+
<span key={i} style={{ padding: '3px 10px', borderRadius: 12, background: 'rgba(22,163,74,0.08)', color: '#16a34a', fontSize: 11, fontWeight: 600 }}>
|
|
265
|
+
{f}
|
|
266
|
+
</span>
|
|
267
|
+
))}
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
|
|
272
|
+
{/* Meta */}
|
|
273
|
+
<div style={{ fontSize: 10, color: 'var(--theme-elevation-400)', display: 'flex', gap: 12, marginTop: 4 }}>
|
|
274
|
+
<span>{intelligence.ticketCount} tickets analysés</span>
|
|
275
|
+
<span>{intelligence.messageCount} messages</span>
|
|
276
|
+
{intelligence.averageSatisfaction && <span>Satisfaction: {intelligence.averageSatisfaction}/5</span>}
|
|
277
|
+
{intelligence.fromCache && <span>Cache</span>}
|
|
278
|
+
{intelligence.generatedAt && <span>Généré {timeAgo(intelligence.generatedAt)}</span>}
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
) : (
|
|
282
|
+
<div style={{ padding: 16, textAlign: 'center', color: 'var(--theme-elevation-400)', fontSize: 13 }}>
|
|
283
|
+
Cliquez sur "Actualiser" pour générer le résumé IA de ce client.
|
|
284
|
+
</div>
|
|
285
|
+
)}
|
|
286
|
+
</div>
|
|
287
|
+
|
|
181
288
|
{/* Tickets table */}
|
|
182
289
|
<div style={{ padding: 16, borderRadius: 10, border: '1px solid var(--theme-elevation-150)', marginBottom: 16 }}>
|
|
183
290
|
<h3 style={{ fontSize: 14, fontWeight: 700, margin: '0 0 8px' }}>{t('crm.sections.tickets')} ({detail.tickets.length})</h3>
|