@consilioweb/payload-support 1.1.1 → 2.0.1
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/README.md +74 -3
- package/dist/components/TicketConversation/components/AISummaryPanel.cjs +22 -14
- package/dist/components/TicketConversation/components/AISummaryPanel.js +23 -16
- package/dist/components/TicketConversation/components/ActionPanels.cjs +5 -5
- package/dist/components/TicketConversation/components/ActionPanels.js +5 -5
- package/dist/components/TicketConversation/components/ClientBar.cjs +1 -1
- package/dist/components/TicketConversation/components/ClientBar.js +1 -1
- package/dist/components/TicketConversation/components/QuickActions.cjs +2 -2
- package/dist/components/TicketConversation/components/QuickActions.js +2 -2
- package/dist/components/TicketConversation/components/TicketHeader.cjs +1 -1
- package/dist/components/TicketConversation/components/TicketHeader.js +1 -1
- package/dist/components/TicketConversation/hooks/useTranslation.cjs +25 -2
- package/dist/components/TicketConversation/hooks/useTranslation.js +22 -3
- package/dist/components/TicketConversation/index.cjs +10 -10
- package/dist/components/TicketConversation/index.js +10 -10
- package/dist/index.cjs +709 -173
- package/dist/index.d.cts +165 -3
- package/dist/index.d.ts +165 -3
- package/dist/index.js +702 -175
- package/dist/styles/ChatView.module.scss +1 -1
- package/dist/styles/TicketDetail.module.scss +3 -3
- package/dist/styles/_tokens.scss +1 -1
- package/dist/styles/theme.css +6 -6
- package/dist/views/SupportDashboardView/client.cjs +1 -1
- package/dist/views/SupportDashboardView/client.js +1 -1
- package/dist/views/TicketingSettingsView/client.cjs +4 -4
- package/dist/views/TicketingSettingsView/client.js +4 -4
- package/dist/views/shared/locales/en.json +708 -0
- package/dist/views/shared/locales/fr.json +708 -0
- package/package.json +19 -19
- package/src/__tests__/aiSummaryRendering.test.ts +21 -0
- package/src/__tests__/authResponses.test.ts +69 -0
- package/src/__tests__/capabilities.test.ts +71 -0
- package/src/__tests__/generateTrackingToken.test.ts +18 -4
- package/src/__tests__/integration/core-behaviors.test.ts +23 -0
- package/src/__tests__/rateLimiter.test.ts +60 -66
- package/src/__tests__/trackOpenEndpoint.test.ts +54 -0
- package/src/__tests__/translations.test.ts +67 -0
- package/src/__tests__/webhookSecurity.test.ts +58 -0
- package/src/collections/PendingEmails.ts +2 -1
- package/src/collections/SupportClients.ts +43 -1
- package/src/collections/SupportCounters.ts +14 -0
- package/src/collections/SupportRateLimits.ts +20 -0
- package/src/collections/TicketMessages.ts +76 -0
- package/src/collections/Tickets.ts +179 -39
- package/src/collections/index.ts +2 -0
- package/src/components/TicketConversation/components/AISummaryPanel.tsx +26 -13
- package/src/components/TicketConversation/components/ActionPanels.tsx +5 -5
- package/src/components/TicketConversation/components/ClientBar.tsx +1 -1
- package/src/components/TicketConversation/components/QuickActions.tsx +2 -2
- package/src/components/TicketConversation/components/TicketHeader.tsx +1 -1
- package/src/components/TicketConversation/hooks/useTranslation.ts +38 -4
- package/src/components/TicketConversation/index.tsx +10 -10
- package/src/endpoints/admin-chat.ts +4 -4
- package/src/endpoints/ai-agent.ts +6 -1
- package/src/endpoints/ai.ts +10 -5
- package/src/endpoints/auth-2fa.ts +6 -7
- package/src/endpoints/auto-close.ts +2 -1
- package/src/endpoints/capabilities.ts +144 -0
- package/src/endpoints/channels.ts +2 -1
- package/src/endpoints/chat.ts +6 -6
- package/src/endpoints/chatbot.ts +4 -4
- package/src/endpoints/client-intelligence.ts +13 -4
- package/src/endpoints/import-conversation.ts +6 -5
- package/src/endpoints/index.ts +35 -14
- package/src/endpoints/invite-collaborator.ts +4 -4
- package/src/endpoints/login.ts +4 -5
- package/src/endpoints/oauth-google.ts +1 -1
- package/src/endpoints/process-digests.ts +2 -1
- package/src/endpoints/process-scheduled.ts +2 -1
- package/src/endpoints/process-snooze.ts +2 -1
- package/src/endpoints/resend-notification.ts +4 -4
- package/src/endpoints/send-reminder.ts +4 -4
- package/src/endpoints/ticket-synthesis.ts +17 -2
- package/src/endpoints/track-open.ts +44 -21
- package/src/endpoints/transfer-ticket.ts +4 -4
- package/src/index.ts +7 -0
- package/src/plugin.ts +16 -1
- package/src/portal/LiveChat.tsx +1 -1
- package/src/portal/auth/ChatWidget.tsx +4 -4
- package/src/portal/auth/ChatbotWidget.tsx +4 -4
- package/src/portal/auth/dashboard/DashboardClient.tsx +2 -2
- package/src/portal/auth/faq/FAQSearch.tsx +1 -1
- package/src/portal/auth/faq/page.tsx +2 -2
- package/src/portal/auth/profile/page.tsx +17 -17
- package/src/portal/auth/tickets/detail/CloseTicketButton.tsx +1 -1
- package/src/portal/auth/tickets/detail/MarkSolutionButton.tsx +1 -1
- package/src/portal/auth/tickets/detail/ReopenTicketButton.tsx +1 -1
- package/src/portal/auth/tickets/detail/SatisfactionForm.tsx +3 -3
- package/src/portal/auth/tickets/detail/TicketReplyForm.tsx +3 -3
- package/src/portal/auth/tickets/detail/page.tsx +5 -5
- package/src/portal/auth/tickets/new/page.tsx +8 -8
- package/src/portal/forgot-password/page.tsx +3 -3
- package/src/portal/layout.tsx +5 -2
- package/src/portal/login/page.tsx +7 -7
- package/src/portal/page.tsx +9 -9
- package/src/portal/register/page.tsx +4 -4
- package/src/portal/reset-password/page.tsx +3 -3
- package/src/styles/ChatView.module.scss +1 -1
- package/src/styles/TicketDetail.module.scss +3 -3
- package/src/styles/_tokens.scss +1 -1
- package/src/styles/theme.css +6 -6
- package/src/types.ts +71 -0
- package/src/utils/rateLimiter.ts +131 -38
- package/src/utils/slugs.ts +4 -0
- package/src/utils/webhookSecurity.ts +81 -0
- package/src/views/SupportDashboardView/client.tsx +1 -1
- package/src/views/TicketingSettingsView/client.tsx +4 -4
- package/src/views/shared/locales/en.json +2 -2
- package/src/views/shared/locales/fr.json +2 -2
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
[](https://payloadcms.com)
|
|
12
|
-
[](src/__tests__)
|
|
13
13
|
[](https://www.typescriptlang.org)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
@@ -154,6 +154,8 @@ supportPlugin({ features: { chat: false, pendingEmails: false }, locale: 'en' })
|
|
|
154
154
|
```ts
|
|
155
155
|
supportPlugin({
|
|
156
156
|
features: { ai: true, sla: true, roundRobin: true, webhooks: true, snooze: true },
|
|
157
|
+
rateLimitStore: 'payload', // shared, persistent limits for multi-instance deployments
|
|
158
|
+
ticketNumber: { prefix: 'TK-', padding: 6 },
|
|
157
159
|
ai: { provider: 'ollama', model: 'qwen2.5', baseUrl: process.env.OLLAMA_API_URL },
|
|
158
160
|
email: { fromName: 'Support ACME', fromAddress: 'support@acme.com', replyTo: 'support@acme.com' },
|
|
159
161
|
allowedEmailDomains: ['acme.com'], // restrict OAuth auto-registration
|
|
@@ -163,6 +165,50 @@ supportPlugin({
|
|
|
163
165
|
})
|
|
164
166
|
```
|
|
165
167
|
|
|
168
|
+
### Deployment adapters
|
|
169
|
+
|
|
170
|
+
Version 2 keeps provider-specific code in the host application while the plugin owns the
|
|
171
|
+
generic support workflow. Optional capabilities include SMS, inbound email, digests, AI
|
|
172
|
+
titles and summaries, detailed billing, volunteering, thread cleanup and project suggestions.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
supportPlugin({
|
|
176
|
+
rateLimitStore: 'payload',
|
|
177
|
+
capabilities: {
|
|
178
|
+
sms: {
|
|
179
|
+
adapter: {
|
|
180
|
+
isConfigured: () => Boolean(process.env.SMS_PROVIDER_ACCOUNT),
|
|
181
|
+
send: async ({ message, to }) => mySmsProvider.send({ message, to }),
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
inboundEmail: {
|
|
185
|
+
secret: process.env.SUPPORT_WEBHOOK_SECRET,
|
|
186
|
+
secretHeader: 'x-webhook-secret',
|
|
187
|
+
handle: handleInboundSupportEmail,
|
|
188
|
+
},
|
|
189
|
+
detailedBilling: true,
|
|
190
|
+
volunteering: true,
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Upgrading from 1.x
|
|
196
|
+
|
|
197
|
+
Version 2.0 is a security and ownership release with intentional breaking changes:
|
|
198
|
+
|
|
199
|
+
1. Generate an additive Payload migration for the new `support-counters` collection and,
|
|
200
|
+
when `rateLimitStore: 'payload'` is enabled, `support-rate-limits`. Run it before restart.
|
|
201
|
+
2. Regenerate Payload types and the admin import map.
|
|
202
|
+
3. Remove duplicated host routes and enable the plugin endpoints (`skipEndpoints: false`).
|
|
203
|
+
4. Read portal authentication exclusively from the `HttpOnly` cookie. Login, OAuth and 2FA
|
|
204
|
+
responses no longer expose the JWT in JSON.
|
|
205
|
+
5. Send cron and webhook secrets only through their configured headers. Query-string secrets
|
|
206
|
+
are rejected.
|
|
207
|
+
6. Custom rate-limit stores must implement the asynchronous `RateLimitStore` interface.
|
|
208
|
+
|
|
209
|
+
The process-local memory store remains the default for development and single-instance use.
|
|
210
|
+
Use `rateLimitStore: 'payload'` or a shared custom store in production with multiple instances.
|
|
211
|
+
|
|
166
212
|
### Run the AI agent on a ticket
|
|
167
213
|
|
|
168
214
|
```ts
|
|
@@ -212,6 +258,9 @@ import type { SupportPluginConfig, SupportFeatures } from '@consilioweb/payload-
|
|
|
212
258
|
| `features` | `SupportFeatures` | all `true` | Toggle each feature on/off. |
|
|
213
259
|
| `ai` | `AIProviderConfig` | `anthropic` | AI provider: `anthropic` \| `openai` \| `ollama` \| `custom`. |
|
|
214
260
|
| `email` | `EmailConfig` | — | `fromName`, `fromAddress`, `replyTo`. |
|
|
261
|
+
| `rateLimitStore` | `RateLimitStore \| 'payload'` | memory | Persistent/shared storage for endpoint limits. |
|
|
262
|
+
| `ticketNumber` | `{ prefix?, padding? }` | `TK-`, no padding | Atomic sequential ticket-number formatting. |
|
|
263
|
+
| `capabilities` | `SupportCapabilities` | — | Optional host adapters for SMS, inbound email, AI and deployment-specific workflows. |
|
|
215
264
|
| `locale` | `'fr' \| 'en'` | `'fr'` | Admin/portal language. |
|
|
216
265
|
| `basePath` | `string` | `'/support'` | Admin views prefix. |
|
|
217
266
|
| `userCollectionSlug` | `string` | `'users'` | Agents collection. |
|
|
@@ -333,7 +382,10 @@ Security is a first-class concern — several guardrails are validated by integr
|
|
|
333
382
|
- **Cross-client isolation** — a client can never read another's tickets/messages (filtered by owned tickets).
|
|
334
383
|
- **2FA** enforced server-side (`beforeLogin`); **OAuth** verifies the Google email.
|
|
335
384
|
- **Sanitization** of message HTML server-side (stored-XSS protection).
|
|
336
|
-
- **HMAC-signed** webhooks
|
|
385
|
+
- **HMAC-signed** webhooks and tracking pixels with constant-time verification and idempotent writes.
|
|
386
|
+
- **Cookie-only JWTs** for portal authentication (`HttpOnly`, `Secure`, `SameSite=Lax`).
|
|
387
|
+
- **Persistent rate limiting** for authentication, 2FA, chats, invitations, transfers and AI endpoints.
|
|
388
|
+
- **Bounded inbound email** payloads and attachments; cron/webhook secrets are accepted in headers only.
|
|
337
389
|
|
|
338
390
|
### Reporting Security Issues
|
|
339
391
|
|
|
@@ -362,7 +414,7 @@ Contributions are very welcome!
|
|
|
362
414
|
Run the checks before submitting:
|
|
363
415
|
|
|
364
416
|
```bash
|
|
365
|
-
|
|
417
|
+
pnpm typecheck && pnpm test && pnpm build
|
|
366
418
|
```
|
|
367
419
|
|
|
368
420
|
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" width="100%" alt="" />
|
|
@@ -371,6 +423,25 @@ npm run typecheck && npm test && npm run build
|
|
|
371
423
|
|
|
372
424
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
373
425
|
|
|
426
|
+
### [2.0.1] — 2026-07-16
|
|
427
|
+
|
|
428
|
+
- 🌍 Loads the complete shared FR/EN catalogs in every support admin view, fixing raw keys such as ticket statuses, relative dates, time tracking, tags and billing labels.
|
|
429
|
+
- 🧭 Removes translation-key collisions in the support dashboard and settings views.
|
|
430
|
+
- ✅ Adds regression coverage for all 518 literal translation keys used by the support UI and keeps both locales aligned (132 tests passing).
|
|
431
|
+
|
|
432
|
+
### [2.0.0] — 2026-07-16
|
|
433
|
+
|
|
434
|
+
- 🔐 Security hardening for authentication, tracking pixels, webhook/cron secrets, HTML/AI output and inbound-email limits.
|
|
435
|
+
- 🚦 Asynchronous persistent rate limiting with a Payload-backed store and memory fallback.
|
|
436
|
+
- 🔢 Atomic persistent ticket counters with configurable prefix and padding.
|
|
437
|
+
- 🧩 Typed deployment adapters for SMS, inbound email, AI workflows, billing, volunteering, cleanup and project suggestions.
|
|
438
|
+
- 🏗️ The plugin is now the single owner of generic support collections, hooks, views and endpoints.
|
|
439
|
+
- ✅ 124 tests covering the security regressions and new capabilities.
|
|
440
|
+
|
|
441
|
+
### [1.1.1] — 2026-06-26
|
|
442
|
+
|
|
443
|
+
- 🐛 **Fix client ticket reopen**: allow clients to set `waiting_support` (the status the portal's "Reopen" button sends). It was silently rejected, leaving the ticket `resolved`.
|
|
444
|
+
|
|
374
445
|
### [1.1.0] — 2026-06-25
|
|
375
446
|
|
|
376
447
|
- 🎯 **Per-team SLA policies & dashboards** (team policy overrides the default, `?teamId=` scoping).
|
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var constants = require('../constants');
|
|
5
5
|
|
|
6
|
+
function renderAiSummary(summary) {
|
|
7
|
+
const lines = summary.split("\n");
|
|
8
|
+
const result = [];
|
|
9
|
+
lines.forEach((line, lineIndex) => {
|
|
10
|
+
if (lineIndex > 0) result.push(/* @__PURE__ */ jsxRuntime.jsx("br", {}, `break-${lineIndex}`));
|
|
11
|
+
line.split(/(\*\*.*?\*\*)/g).filter(Boolean).forEach((part, partIndex) => {
|
|
12
|
+
if (part.startsWith("**") && part.endsWith("**")) {
|
|
13
|
+
result.push(/* @__PURE__ */ jsxRuntime.jsx("strong", { children: part.slice(2, -2) }, `${lineIndex}-${partIndex}`));
|
|
14
|
+
} else {
|
|
15
|
+
result.push(part);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
6
21
|
function AISummaryPanel({
|
|
7
22
|
showAiSummary,
|
|
8
23
|
setShowAiSummary,
|
|
@@ -22,7 +37,7 @@ function AISummaryPanel({
|
|
|
22
37
|
if (!showAiSummary && !aiSummary) handleAiGenerate();
|
|
23
38
|
},
|
|
24
39
|
style: {
|
|
25
|
-
...constants.s.ghostBtn("#
|
|
40
|
+
...constants.s.ghostBtn("#17807c", false),
|
|
26
41
|
fontSize: "12px",
|
|
27
42
|
display: "inline-flex",
|
|
28
43
|
alignItems: "center",
|
|
@@ -35,18 +50,18 @@ function AISummaryPanel({
|
|
|
35
50
|
marginTop: "10px",
|
|
36
51
|
padding: "14px 18px",
|
|
37
52
|
borderRadius: "8px",
|
|
38
|
-
backgroundColor: "#
|
|
39
|
-
border: "1px solid #
|
|
53
|
+
backgroundColor: "#eef8f7",
|
|
54
|
+
border: "1px solid #b8dcda"
|
|
40
55
|
}, children: [
|
|
41
56
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "10px" }, children: [
|
|
42
|
-
/* @__PURE__ */ jsxRuntime.jsx("h4", { style: { fontSize: "13px", fontWeight: 600, color: "#
|
|
57
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { style: { fontSize: "13px", fontWeight: 600, color: "#17807c", margin: 0 }, children: "Synth\xE8se IA" }),
|
|
43
58
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
|
|
44
59
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45
60
|
"button",
|
|
46
61
|
{
|
|
47
62
|
onClick: handleAiGenerate,
|
|
48
63
|
disabled: aiGenerating,
|
|
49
|
-
style: { ...constants.s.outlineBtn("#
|
|
64
|
+
style: { ...constants.s.outlineBtn("#17807c", aiGenerating), fontSize: "11px", padding: "4px 10px" },
|
|
50
65
|
children: aiGenerating ? "G\xE9n\xE9ration..." : "R\xE9g\xE9n\xE9rer"
|
|
51
66
|
}
|
|
52
67
|
),
|
|
@@ -61,17 +76,10 @@ function AISummaryPanel({
|
|
|
61
76
|
)
|
|
62
77
|
] })
|
|
63
78
|
] }),
|
|
64
|
-
aiGenerating ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: "10px", color: "#
|
|
65
|
-
"div",
|
|
66
|
-
{
|
|
67
|
-
style: { fontSize: "13px", lineHeight: "1.7", color: "#1e1b4b", whiteSpace: "pre-wrap" },
|
|
68
|
-
dangerouslySetInnerHTML: {
|
|
69
|
-
__html: aiSummary.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\n/g, "<br/>")
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#999", fontStyle: "italic", fontSize: "13px", margin: 0 }, children: `Cliquez sur "R\xE9g\xE9n\xE9rer" pour lancer l'analyse` })
|
|
79
|
+
aiGenerating ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: "10px", color: "#17807c", fontSize: "13px" }, children: "Analyse de la conversation en cours..." }) : aiSummary ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "13px", lineHeight: "1.7", color: "#1e1b4b", whiteSpace: "pre-wrap" }, children: renderAiSummary(aiSummary) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#999", fontStyle: "italic", fontSize: "13px", margin: 0 }, children: `Cliquez sur "R\xE9g\xE9n\xE9rer" pour lancer l'analyse` })
|
|
73
80
|
] })
|
|
74
81
|
] });
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
exports.AISummaryPanel = AISummaryPanel;
|
|
85
|
+
exports.renderAiSummary = renderAiSummary;
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import { s } from '../constants.js';
|
|
4
4
|
|
|
5
|
+
function renderAiSummary(summary) {
|
|
6
|
+
const lines = summary.split("\n");
|
|
7
|
+
const result = [];
|
|
8
|
+
lines.forEach((line, lineIndex) => {
|
|
9
|
+
if (lineIndex > 0) result.push(/* @__PURE__ */ jsx("br", {}, `break-${lineIndex}`));
|
|
10
|
+
line.split(/(\*\*.*?\*\*)/g).filter(Boolean).forEach((part, partIndex) => {
|
|
11
|
+
if (part.startsWith("**") && part.endsWith("**")) {
|
|
12
|
+
result.push(/* @__PURE__ */ jsx("strong", { children: part.slice(2, -2) }, `${lineIndex}-${partIndex}`));
|
|
13
|
+
} else {
|
|
14
|
+
result.push(part);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
5
20
|
function AISummaryPanel({
|
|
6
21
|
showAiSummary,
|
|
7
22
|
setShowAiSummary,
|
|
@@ -21,7 +36,7 @@ function AISummaryPanel({
|
|
|
21
36
|
if (!showAiSummary && !aiSummary) handleAiGenerate();
|
|
22
37
|
},
|
|
23
38
|
style: {
|
|
24
|
-
...s.ghostBtn("#
|
|
39
|
+
...s.ghostBtn("#17807c", false),
|
|
25
40
|
fontSize: "12px",
|
|
26
41
|
display: "inline-flex",
|
|
27
42
|
alignItems: "center",
|
|
@@ -34,18 +49,18 @@ function AISummaryPanel({
|
|
|
34
49
|
marginTop: "10px",
|
|
35
50
|
padding: "14px 18px",
|
|
36
51
|
borderRadius: "8px",
|
|
37
|
-
backgroundColor: "#
|
|
38
|
-
border: "1px solid #
|
|
52
|
+
backgroundColor: "#eef8f7",
|
|
53
|
+
border: "1px solid #b8dcda"
|
|
39
54
|
}, children: [
|
|
40
55
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "10px" }, children: [
|
|
41
|
-
/* @__PURE__ */ jsx("h4", { style: { fontSize: "13px", fontWeight: 600, color: "#
|
|
56
|
+
/* @__PURE__ */ jsx("h4", { style: { fontSize: "13px", fontWeight: 600, color: "#17807c", margin: 0 }, children: "Synth\xE8se IA" }),
|
|
42
57
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
|
|
43
58
|
/* @__PURE__ */ jsx(
|
|
44
59
|
"button",
|
|
45
60
|
{
|
|
46
61
|
onClick: handleAiGenerate,
|
|
47
62
|
disabled: aiGenerating,
|
|
48
|
-
style: { ...s.outlineBtn("#
|
|
63
|
+
style: { ...s.outlineBtn("#17807c", aiGenerating), fontSize: "11px", padding: "4px 10px" },
|
|
49
64
|
children: aiGenerating ? "G\xE9n\xE9ration..." : "R\xE9g\xE9n\xE9rer"
|
|
50
65
|
}
|
|
51
66
|
),
|
|
@@ -60,17 +75,9 @@ function AISummaryPanel({
|
|
|
60
75
|
)
|
|
61
76
|
] })
|
|
62
77
|
] }),
|
|
63
|
-
aiGenerating ? /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: "10px", color: "#
|
|
64
|
-
"div",
|
|
65
|
-
{
|
|
66
|
-
style: { fontSize: "13px", lineHeight: "1.7", color: "#1e1b4b", whiteSpace: "pre-wrap" },
|
|
67
|
-
dangerouslySetInnerHTML: {
|
|
68
|
-
__html: aiSummary.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\n/g, "<br/>")
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
) : /* @__PURE__ */ jsx("p", { style: { color: "#999", fontStyle: "italic", fontSize: "13px", margin: 0 }, children: `Cliquez sur "R\xE9g\xE9n\xE9rer" pour lancer l'analyse` })
|
|
78
|
+
aiGenerating ? /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: "10px", color: "#17807c", fontSize: "13px" }, children: "Analyse de la conversation en cours..." }) : aiSummary ? /* @__PURE__ */ jsx("div", { style: { fontSize: "13px", lineHeight: "1.7", color: "#1e1b4b", whiteSpace: "pre-wrap" }, children: renderAiSummary(aiSummary) }) : /* @__PURE__ */ jsx("p", { style: { color: "#999", fontStyle: "italic", fontSize: "13px", margin: 0 }, children: `Cliquez sur "R\xE9g\xE9n\xE9rer" pour lancer l'analyse` })
|
|
72
79
|
] })
|
|
73
80
|
] });
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
export { AISummaryPanel };
|
|
83
|
+
export { AISummaryPanel, renderAiSummary };
|
|
@@ -92,7 +92,7 @@ function ExtMessagePanel({
|
|
|
92
92
|
file.name,
|
|
93
93
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setExtMsgFiles((prev) => prev.filter((_, idx) => idx !== i)), style: { border: "none", background: "none", color: "#ef4444", fontWeight: 700, cursor: "pointer", fontSize: "14px", lineHeight: 1 }, children: "\xD7" })
|
|
94
94
|
] }, i)) }),
|
|
95
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleSendExtMsg, disabled: sendingExtMsg || !extMsgBody.trim(), style: constants.s.btn("#
|
|
95
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleSendExtMsg, disabled: sendingExtMsg || !extMsgBody.trim(), style: constants.s.btn("#1d2b4d", sendingExtMsg || !extMsgBody.trim()), children: sendingExtMsg ? "Ajout..." : "Ajouter (sans notification)" })
|
|
96
96
|
] });
|
|
97
97
|
}
|
|
98
98
|
const REMINDER_DELAYS = [
|
|
@@ -167,12 +167,12 @@ function ReminderPanel({
|
|
|
167
167
|
] });
|
|
168
168
|
}
|
|
169
169
|
function SnoozePanel({ snoozeSaving, handleSnooze }) {
|
|
170
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#
|
|
170
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#eef8f7", border: "1px solid #b8dcda", marginBottom: "14px" }, children: [
|
|
171
171
|
/* @__PURE__ */ jsxRuntime.jsx("h4", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "10px", color: "#5b21b6" }, children: "Snooze \u2014 masquer temporairement" }),
|
|
172
172
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap", alignItems: "center" }, children: [
|
|
173
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleSnooze(1), disabled: snoozeSaving, style: { ...constants.s.outlineBtn("#
|
|
174
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleSnooze(3), disabled: snoozeSaving, style: { ...constants.s.outlineBtn("#
|
|
175
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleSnooze(7), disabled: snoozeSaving, style: { ...constants.s.outlineBtn("#
|
|
173
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleSnooze(1), disabled: snoozeSaving, style: { ...constants.s.outlineBtn("#17807c", snoozeSaving), fontSize: "12px" }, children: "1 jour" }),
|
|
174
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleSnooze(3), disabled: snoozeSaving, style: { ...constants.s.outlineBtn("#17807c", snoozeSaving), fontSize: "12px" }, children: "3 jours" }),
|
|
175
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleSnooze(7), disabled: snoozeSaving, style: { ...constants.s.outlineBtn("#17807c", snoozeSaving), fontSize: "12px" }, children: "1 semaine" }),
|
|
176
176
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
177
177
|
"input",
|
|
178
178
|
{
|
|
@@ -91,7 +91,7 @@ function ExtMessagePanel({
|
|
|
91
91
|
file.name,
|
|
92
92
|
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setExtMsgFiles((prev) => prev.filter((_, idx) => idx !== i)), style: { border: "none", background: "none", color: "#ef4444", fontWeight: 700, cursor: "pointer", fontSize: "14px", lineHeight: 1 }, children: "\xD7" })
|
|
93
93
|
] }, i)) }),
|
|
94
|
-
/* @__PURE__ */ jsx("button", { onClick: handleSendExtMsg, disabled: sendingExtMsg || !extMsgBody.trim(), style: s.btn("#
|
|
94
|
+
/* @__PURE__ */ jsx("button", { onClick: handleSendExtMsg, disabled: sendingExtMsg || !extMsgBody.trim(), style: s.btn("#1d2b4d", sendingExtMsg || !extMsgBody.trim()), children: sendingExtMsg ? "Ajout..." : "Ajouter (sans notification)" })
|
|
95
95
|
] });
|
|
96
96
|
}
|
|
97
97
|
const REMINDER_DELAYS = [
|
|
@@ -166,12 +166,12 @@ function ReminderPanel({
|
|
|
166
166
|
] });
|
|
167
167
|
}
|
|
168
168
|
function SnoozePanel({ snoozeSaving, handleSnooze }) {
|
|
169
|
-
return /* @__PURE__ */ jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#
|
|
169
|
+
return /* @__PURE__ */ jsxs("div", { style: { padding: "14px 18px", borderRadius: "8px", backgroundColor: "#eef8f7", border: "1px solid #b8dcda", marginBottom: "14px" }, children: [
|
|
170
170
|
/* @__PURE__ */ jsx("h4", { style: { fontSize: "13px", fontWeight: 600, marginBottom: "10px", color: "#5b21b6" }, children: "Snooze \u2014 masquer temporairement" }),
|
|
171
171
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap", alignItems: "center" }, children: [
|
|
172
|
-
/* @__PURE__ */ jsx("button", { onClick: () => handleSnooze(1), disabled: snoozeSaving, style: { ...s.outlineBtn("#
|
|
173
|
-
/* @__PURE__ */ jsx("button", { onClick: () => handleSnooze(3), disabled: snoozeSaving, style: { ...s.outlineBtn("#
|
|
174
|
-
/* @__PURE__ */ jsx("button", { onClick: () => handleSnooze(7), disabled: snoozeSaving, style: { ...s.outlineBtn("#
|
|
172
|
+
/* @__PURE__ */ jsx("button", { onClick: () => handleSnooze(1), disabled: snoozeSaving, style: { ...s.outlineBtn("#17807c", snoozeSaving), fontSize: "12px" }, children: "1 jour" }),
|
|
173
|
+
/* @__PURE__ */ jsx("button", { onClick: () => handleSnooze(3), disabled: snoozeSaving, style: { ...s.outlineBtn("#17807c", snoozeSaving), fontSize: "12px" }, children: "3 jours" }),
|
|
174
|
+
/* @__PURE__ */ jsx("button", { onClick: () => handleSnooze(7), disabled: snoozeSaving, style: { ...s.outlineBtn("#17807c", snoozeSaving), fontSize: "12px" }, children: "1 semaine" }),
|
|
175
175
|
/* @__PURE__ */ jsx(
|
|
176
176
|
"input",
|
|
177
177
|
{
|
|
@@ -31,7 +31,7 @@ function ClientBar({ client }) {
|
|
|
31
31
|
{
|
|
32
32
|
type: "button",
|
|
33
33
|
onClick: () => window.open(`/api/admin/impersonate?clientId=${client.id}`, "_blank"),
|
|
34
|
-
style: { ...constants.s.ghostBtn("#
|
|
34
|
+
style: { ...constants.s.ghostBtn("#17807c"), fontSize: "11px", padding: "4px 10px" },
|
|
35
35
|
title: "Se connecter au portail support en tant que ce client",
|
|
36
36
|
children: "Voir en tant que client"
|
|
37
37
|
}
|
|
@@ -30,7 +30,7 @@ function ClientBar({ client }) {
|
|
|
30
30
|
{
|
|
31
31
|
type: "button",
|
|
32
32
|
onClick: () => window.open(`/api/admin/impersonate?clientId=${client.id}`, "_blank"),
|
|
33
|
-
style: { ...s.ghostBtn("#
|
|
33
|
+
style: { ...s.ghostBtn("#17807c"), fontSize: "11px", padding: "4px 10px" },
|
|
34
34
|
title: "Se connecter au portail support en tant que ce client",
|
|
35
35
|
children: "Voir en tant que client"
|
|
36
36
|
}
|
|
@@ -41,11 +41,11 @@ function QuickActions({
|
|
|
41
41
|
},
|
|
42
42
|
a.status
|
|
43
43
|
)),
|
|
44
|
-
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onCancelSnooze, disabled: snoozeSaving, style: { ...constants.s.ghostBtn("#
|
|
44
|
+
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onCancelSnooze, disabled: snoozeSaving, style: { ...constants.s.ghostBtn("#17807c", snoozeSaving), fontSize: "12px", padding: "5px 10px" }, children: "Annuler snooze" }),
|
|
45
45
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { borderLeft: `1px solid ${constants.C.border}`, height: "20px", margin: "0 4px" } }),
|
|
46
46
|
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onToggleMerge, style: constants.s.ghostBtn("#be185d"), children: "Fusionner" }),
|
|
47
47
|
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onToggleExtMsg, style: constants.s.ghostBtn("#4f46e5"), children: "+ Message re\xE7u" }),
|
|
48
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onToggleSnooze, style: constants.s.ghostBtn("#
|
|
48
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onToggleSnooze, style: constants.s.ghostBtn("#17807c"), children: "Snooze" }),
|
|
49
49
|
showReminderButton && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: onToggleReminder, style: constants.s.ghostBtn("#ea580c"), children: [
|
|
50
50
|
"\u23F0",
|
|
51
51
|
" Relancer"
|
|
@@ -40,11 +40,11 @@ function QuickActions({
|
|
|
40
40
|
},
|
|
41
41
|
a.status
|
|
42
42
|
)),
|
|
43
|
-
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsx("button", { onClick: onCancelSnooze, disabled: snoozeSaving, style: { ...s.ghostBtn("#
|
|
43
|
+
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsx("button", { onClick: onCancelSnooze, disabled: snoozeSaving, style: { ...s.ghostBtn("#17807c", snoozeSaving), fontSize: "12px", padding: "5px 10px" }, children: "Annuler snooze" }),
|
|
44
44
|
/* @__PURE__ */ jsx("span", { style: { borderLeft: `1px solid ${C.border}`, height: "20px", margin: "0 4px" } }),
|
|
45
45
|
/* @__PURE__ */ jsx("button", { onClick: onToggleMerge, style: s.ghostBtn("#be185d"), children: "Fusionner" }),
|
|
46
46
|
/* @__PURE__ */ jsx("button", { onClick: onToggleExtMsg, style: s.ghostBtn("#4f46e5"), children: "+ Message re\xE7u" }),
|
|
47
|
-
/* @__PURE__ */ jsx("button", { onClick: onToggleSnooze, style: s.ghostBtn("#
|
|
47
|
+
/* @__PURE__ */ jsx("button", { onClick: onToggleSnooze, style: s.ghostBtn("#17807c"), children: "Snooze" }),
|
|
48
48
|
showReminderButton && /* @__PURE__ */ jsxs("button", { onClick: onToggleReminder, style: s.ghostBtn("#ea580c"), children: [
|
|
49
49
|
"\u23F0",
|
|
50
50
|
" Relancer"
|
|
@@ -53,7 +53,7 @@ function TicketHeader({
|
|
|
53
53
|
chatSession.slice(0, 16),
|
|
54
54
|
"..."
|
|
55
55
|
] }),
|
|
56
|
-
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: constants.s.badge("#
|
|
56
|
+
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: constants.s.badge("#eef8f7", "#17807c"), children: [
|
|
57
57
|
"Snooz\xE9 ",
|
|
58
58
|
new Date(snoozeUntil).toLocaleDateString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })
|
|
59
59
|
] }),
|
|
@@ -52,7 +52,7 @@ function TicketHeader({
|
|
|
52
52
|
chatSession.slice(0, 16),
|
|
53
53
|
"..."
|
|
54
54
|
] }),
|
|
55
|
-
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsxs("span", { style: s.badge("#
|
|
55
|
+
snoozeUntil && new Date(snoozeUntil) > /* @__PURE__ */ new Date() && /* @__PURE__ */ jsxs("span", { style: s.badge("#eef8f7", "#17807c"), children: [
|
|
56
56
|
"Snooz\xE9 ",
|
|
57
57
|
new Date(snoozeUntil).toLocaleDateString("fr-FR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })
|
|
58
58
|
] }),
|
|
@@ -3,17 +3,38 @@
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
var frTranslations = require('../locales/fr.json');
|
|
5
5
|
var enTranslations = require('../locales/en.json');
|
|
6
|
+
var frViewTranslations = require('../../../views/shared/locales/fr.json');
|
|
7
|
+
var enViewTranslations = require('../../../views/shared/locales/en.json');
|
|
6
8
|
|
|
7
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
10
|
|
|
9
11
|
var frTranslations__default = /*#__PURE__*/_interopDefault(frTranslations);
|
|
10
12
|
var enTranslations__default = /*#__PURE__*/_interopDefault(enTranslations);
|
|
13
|
+
var frViewTranslations__default = /*#__PURE__*/_interopDefault(frViewTranslations);
|
|
14
|
+
var enViewTranslations__default = /*#__PURE__*/_interopDefault(enViewTranslations);
|
|
11
15
|
|
|
12
16
|
const LOCALE_STORAGE_KEY = "support_locale";
|
|
13
17
|
const DEFAULT_LOCALE = "fr";
|
|
18
|
+
function mergeTranslations(base, supplement) {
|
|
19
|
+
const merged = { ...base };
|
|
20
|
+
for (const [key, value] of Object.entries(supplement)) {
|
|
21
|
+
const current = merged[key];
|
|
22
|
+
if (current != null && value != null && typeof current === "object" && typeof value === "object" && !Array.isArray(current) && !Array.isArray(value)) {
|
|
23
|
+
merged[key] = mergeTranslations(
|
|
24
|
+
current,
|
|
25
|
+
value
|
|
26
|
+
);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (current === void 0 || typeof current !== "object" && typeof value === "object") {
|
|
30
|
+
merged[key] = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return merged;
|
|
34
|
+
}
|
|
14
35
|
const translations = {
|
|
15
|
-
fr: frTranslations__default.default,
|
|
16
|
-
en: enTranslations__default.default
|
|
36
|
+
fr: mergeTranslations(frTranslations__default.default, frViewTranslations__default.default),
|
|
37
|
+
en: mergeTranslations(enTranslations__default.default, enViewTranslations__default.default)
|
|
17
38
|
};
|
|
18
39
|
function getNestedValue(obj, path) {
|
|
19
40
|
const keys = path.split(".");
|
|
@@ -75,4 +96,6 @@ function useTranslation() {
|
|
|
75
96
|
|
|
76
97
|
exports.DEFAULT_LOCALE = DEFAULT_LOCALE;
|
|
77
98
|
exports.LOCALE_STORAGE_KEY = LOCALE_STORAGE_KEY;
|
|
99
|
+
exports.getNestedValue = getNestedValue;
|
|
100
|
+
exports.translations = translations;
|
|
78
101
|
exports.useTranslation = useTranslation;
|
|
@@ -2,12 +2,31 @@
|
|
|
2
2
|
import { useState, useEffect, useCallback } from 'react';
|
|
3
3
|
import frTranslations from '../locales/fr.json';
|
|
4
4
|
import enTranslations from '../locales/en.json';
|
|
5
|
+
import frViewTranslations from '../../../views/shared/locales/fr.json';
|
|
6
|
+
import enViewTranslations from '../../../views/shared/locales/en.json';
|
|
5
7
|
|
|
6
8
|
const LOCALE_STORAGE_KEY = "support_locale";
|
|
7
9
|
const DEFAULT_LOCALE = "fr";
|
|
10
|
+
function mergeTranslations(base, supplement) {
|
|
11
|
+
const merged = { ...base };
|
|
12
|
+
for (const [key, value] of Object.entries(supplement)) {
|
|
13
|
+
const current = merged[key];
|
|
14
|
+
if (current != null && value != null && typeof current === "object" && typeof value === "object" && !Array.isArray(current) && !Array.isArray(value)) {
|
|
15
|
+
merged[key] = mergeTranslations(
|
|
16
|
+
current,
|
|
17
|
+
value
|
|
18
|
+
);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (current === void 0 || typeof current !== "object" && typeof value === "object") {
|
|
22
|
+
merged[key] = value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return merged;
|
|
26
|
+
}
|
|
8
27
|
const translations = {
|
|
9
|
-
fr: frTranslations,
|
|
10
|
-
en: enTranslations
|
|
28
|
+
fr: mergeTranslations(frTranslations, frViewTranslations),
|
|
29
|
+
en: mergeTranslations(enTranslations, enViewTranslations)
|
|
11
30
|
};
|
|
12
31
|
function getNestedValue(obj, path) {
|
|
13
32
|
const keys = path.split(".");
|
|
@@ -67,4 +86,4 @@ function useTranslation() {
|
|
|
67
86
|
return { t, locale, setLocale };
|
|
68
87
|
}
|
|
69
88
|
|
|
70
|
-
export { DEFAULT_LOCALE, LOCALE_STORAGE_KEY, useTranslation };
|
|
89
|
+
export { DEFAULT_LOCALE, LOCALE_STORAGE_KEY, getNestedValue, translations, useTranslation };
|
|
@@ -505,7 +505,7 @@ const TicketConversation = () => {
|
|
|
505
505
|
createdDate,
|
|
506
506
|
" \u2014 r\xE9dig\xE9 le ",
|
|
507
507
|
scheduledDate !== createdDate ? scheduledDate : createdDate
|
|
508
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { style: constants.s.badge("#
|
|
508
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { style: constants.s.badge("#eef8f7", "#17807c"), children: [
|
|
509
509
|
"\u23F0",
|
|
510
510
|
" R\xE9dig\xE9 le ",
|
|
511
511
|
createdDate,
|
|
@@ -703,13 +703,13 @@ const TicketConversation = () => {
|
|
|
703
703
|
gap: 8,
|
|
704
704
|
padding: "8px 12px",
|
|
705
705
|
fontSize: 12,
|
|
706
|
-
color: "#
|
|
706
|
+
color: "#17807c",
|
|
707
707
|
fontWeight: 500
|
|
708
708
|
}, children: [
|
|
709
709
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", gap: 2 }, children: [
|
|
710
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#
|
|
711
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#
|
|
712
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#
|
|
710
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "0ms" } }),
|
|
711
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "150ms" } }),
|
|
712
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "300ms" } })
|
|
713
713
|
] }),
|
|
714
714
|
clientTypingName || "Client",
|
|
715
715
|
" est en train d'\xE9crire..."
|
|
@@ -760,7 +760,7 @@ const TicketConversation = () => {
|
|
|
760
760
|
{
|
|
761
761
|
onClick: handleAiSuggestReply,
|
|
762
762
|
disabled: aiReplying || messages.length === 0,
|
|
763
|
-
style: { ...constants.s.outlineBtn("#
|
|
763
|
+
style: { ...constants.s.outlineBtn("#17807c", aiReplying || messages.length === 0), fontSize: "11px", padding: "3px 10px", borderRadius: "14px" },
|
|
764
764
|
children: aiReplying ? "G\xE9n\xE9ration..." : "Suggestion IA"
|
|
765
765
|
}
|
|
766
766
|
),
|
|
@@ -892,14 +892,14 @@ const TicketConversation = () => {
|
|
|
892
892
|
{
|
|
893
893
|
onClick: () => setShowSchedule(!showSchedule),
|
|
894
894
|
disabled: !replyBody.trim() && !replyHtml,
|
|
895
|
-
style: { ...constants.s.outlineBtn("#
|
|
895
|
+
style: { ...constants.s.outlineBtn("#17807c", !replyBody.trim() && !replyHtml), fontSize: "12px", padding: "8px 12px" },
|
|
896
896
|
title: "Programmer l'envoi \xE0 une date/heure pr\xE9cise",
|
|
897
897
|
children: "\u23F0"
|
|
898
898
|
}
|
|
899
899
|
)
|
|
900
900
|
] }),
|
|
901
|
-
showSchedule && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center", marginTop: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "#
|
|
902
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "12px", fontWeight: 600, color: "#
|
|
901
|
+
showSchedule && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center", marginTop: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "#eef8f7", border: "1px solid #b8dcda" }, children: [
|
|
902
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "12px", fontWeight: 600, color: "#17807c" }, children: "Programmer pour :" }),
|
|
903
903
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
904
904
|
"input",
|
|
905
905
|
{
|
|
@@ -915,7 +915,7 @@ const TicketConversation = () => {
|
|
|
915
915
|
{
|
|
916
916
|
onClick: handleScheduleReply,
|
|
917
917
|
disabled: sending || !scheduleDate || !replyBody.trim() && !replyHtml,
|
|
918
|
-
style: { ...constants.s.btn("#
|
|
918
|
+
style: { ...constants.s.btn("#17807c", sending || !scheduleDate || !replyBody.trim() && !replyHtml), fontSize: "12px", padding: "6px 14px" },
|
|
919
919
|
children: sending ? "..." : "\u23F0 Programmer"
|
|
920
920
|
}
|
|
921
921
|
),
|
|
@@ -500,7 +500,7 @@ const TicketConversation = () => {
|
|
|
500
500
|
createdDate,
|
|
501
501
|
" \u2014 r\xE9dig\xE9 le ",
|
|
502
502
|
scheduledDate !== createdDate ? scheduledDate : createdDate
|
|
503
|
-
] }) : /* @__PURE__ */ jsxs("span", { style: s.badge("#
|
|
503
|
+
] }) : /* @__PURE__ */ jsxs("span", { style: s.badge("#eef8f7", "#17807c"), children: [
|
|
504
504
|
"\u23F0",
|
|
505
505
|
" R\xE9dig\xE9 le ",
|
|
506
506
|
createdDate,
|
|
@@ -698,13 +698,13 @@ const TicketConversation = () => {
|
|
|
698
698
|
gap: 8,
|
|
699
699
|
padding: "8px 12px",
|
|
700
700
|
fontSize: 12,
|
|
701
|
-
color: "#
|
|
701
|
+
color: "#17807c",
|
|
702
702
|
fontWeight: 500
|
|
703
703
|
}, children: [
|
|
704
704
|
/* @__PURE__ */ jsxs("span", { style: { display: "flex", gap: 2 }, children: [
|
|
705
|
-
/* @__PURE__ */ jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#
|
|
706
|
-
/* @__PURE__ */ jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#
|
|
707
|
-
/* @__PURE__ */ jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#
|
|
705
|
+
/* @__PURE__ */ jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "0ms" } }),
|
|
706
|
+
/* @__PURE__ */ jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "150ms" } }),
|
|
707
|
+
/* @__PURE__ */ jsx("span", { style: { width: 5, height: 5, borderRadius: "50%", backgroundColor: "#17807c", animation: "bounce 1s infinite", animationDelay: "300ms" } })
|
|
708
708
|
] }),
|
|
709
709
|
clientTypingName || "Client",
|
|
710
710
|
" est en train d'\xE9crire..."
|
|
@@ -755,7 +755,7 @@ const TicketConversation = () => {
|
|
|
755
755
|
{
|
|
756
756
|
onClick: handleAiSuggestReply,
|
|
757
757
|
disabled: aiReplying || messages.length === 0,
|
|
758
|
-
style: { ...s.outlineBtn("#
|
|
758
|
+
style: { ...s.outlineBtn("#17807c", aiReplying || messages.length === 0), fontSize: "11px", padding: "3px 10px", borderRadius: "14px" },
|
|
759
759
|
children: aiReplying ? "G\xE9n\xE9ration..." : "Suggestion IA"
|
|
760
760
|
}
|
|
761
761
|
),
|
|
@@ -887,14 +887,14 @@ const TicketConversation = () => {
|
|
|
887
887
|
{
|
|
888
888
|
onClick: () => setShowSchedule(!showSchedule),
|
|
889
889
|
disabled: !replyBody.trim() && !replyHtml,
|
|
890
|
-
style: { ...s.outlineBtn("#
|
|
890
|
+
style: { ...s.outlineBtn("#17807c", !replyBody.trim() && !replyHtml), fontSize: "12px", padding: "8px 12px" },
|
|
891
891
|
title: "Programmer l'envoi \xE0 une date/heure pr\xE9cise",
|
|
892
892
|
children: "\u23F0"
|
|
893
893
|
}
|
|
894
894
|
)
|
|
895
895
|
] }),
|
|
896
|
-
showSchedule && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center", marginTop: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "#
|
|
897
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: "12px", fontWeight: 600, color: "#
|
|
896
|
+
showSchedule && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center", marginTop: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "#eef8f7", border: "1px solid #b8dcda" }, children: [
|
|
897
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "12px", fontWeight: 600, color: "#17807c" }, children: "Programmer pour :" }),
|
|
898
898
|
/* @__PURE__ */ jsx(
|
|
899
899
|
"input",
|
|
900
900
|
{
|
|
@@ -910,7 +910,7 @@ const TicketConversation = () => {
|
|
|
910
910
|
{
|
|
911
911
|
onClick: handleScheduleReply,
|
|
912
912
|
disabled: sending || !scheduleDate || !replyBody.trim() && !replyHtml,
|
|
913
|
-
style: { ...s.btn("#
|
|
913
|
+
style: { ...s.btn("#17807c", sending || !scheduleDate || !replyBody.trim() && !replyHtml), fontSize: "12px", padding: "6px 14px" },
|
|
914
914
|
children: sending ? "..." : "\u23F0 Programmer"
|
|
915
915
|
}
|
|
916
916
|
),
|