@consilioweb/payload-support 0.16.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +347 -435
- package/dist/components/TicketConversation/RewriteDropdown.cjs +98 -0
- package/dist/components/TicketConversation/RewriteDropdown.js +93 -0
- package/dist/components/TicketConversation/SkeletonText.cjs +21 -0
- package/dist/components/TicketConversation/SkeletonText.js +20 -0
- package/dist/components/TicketConversation/constants.cjs +27 -0
- package/dist/components/TicketConversation/constants.js +26 -1
- package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.cjs +20 -0
- package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.js +19 -0
- package/dist/components/TicketConversation/index.cjs +10 -147
- package/dist/components/TicketConversation/index.js +4 -141
- package/dist/index.cjs +1604 -671
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1602 -671
- package/dist/views/BillingView/client.cjs +40 -54
- package/dist/views/BillingView/client.js +40 -54
- package/dist/views/ChatView/client.cjs +4 -3
- package/dist/views/ChatView/client.js +4 -3
- package/dist/views/CrmView/client.cjs +7 -7
- package/dist/views/CrmView/client.js +7 -7
- package/dist/views/EmailTrackingView/client.cjs +6 -5
- package/dist/views/EmailTrackingView/client.js +6 -5
- package/dist/views/ImportConversationView/client.cjs +4 -4
- package/dist/views/ImportConversationView/client.js +4 -4
- package/dist/views/LogsView/client.cjs +3 -2
- package/dist/views/LogsView/client.js +3 -2
- package/dist/views/NewTicketView/client.cjs +1 -1
- package/dist/views/NewTicketView/client.js +1 -1
- package/dist/views/PendingEmailsView/client.cjs +7 -6
- package/dist/views/PendingEmailsView/client.js +7 -6
- package/dist/views/SupportDashboardView/client.cjs +19 -22
- package/dist/views/SupportDashboardView/client.js +19 -22
- package/dist/views/TicketDetailView/NextActionItem.cjs +40 -0
- package/dist/views/TicketDetailView/NextActionItem.js +34 -0
- package/dist/views/TicketDetailView/RewriteDropdown.cjs +80 -0
- package/dist/views/TicketDetailView/RewriteDropdown.js +78 -0
- package/dist/views/TicketDetailView/client.cjs +59 -190
- package/dist/views/TicketDetailView/client.js +52 -183
- package/dist/views/TicketDetailView/constants.cjs +20 -0
- package/dist/views/TicketDetailView/constants.js +16 -0
- package/dist/views/TicketDetailView/helpers.cjs +23 -0
- package/dist/views/TicketDetailView/helpers.js +20 -0
- package/dist/views/TicketDetailView/types.cjs +2 -0
- package/dist/views/TicketDetailView/types.js +1 -0
- package/dist/views/TicketInboxView/client.cjs +14 -10
- package/dist/views/TicketInboxView/client.js +14 -10
- package/dist/views/TicketingSettingsView/client.cjs +101 -96
- package/dist/views/TicketingSettingsView/client.js +92 -88
- package/dist/views/TimeDashboardView/client.cjs +2 -2
- package/dist/views/TimeDashboardView/client.js +2 -2
- package/dist/views/shared/StatusPill.cjs +23 -12
- package/dist/views/shared/StatusPill.js +23 -12
- package/dist/views/shared/dateLocale.cjs +5 -0
- package/dist/views/shared/dateLocale.js +3 -0
- package/package.json +23 -15
- package/src/__tests__/generateTrackingToken.test.ts +53 -0
- package/src/__tests__/integration/ai-agent.test.ts +46 -0
- package/src/__tests__/integration/auth.test.ts +43 -0
- package/src/__tests__/integration/automation-rules.test.ts +68 -0
- package/src/__tests__/integration/buildTestPayload.ts +52 -0
- package/src/__tests__/integration/channels.test.ts +52 -0
- package/src/__tests__/integration/core-behaviors.test.ts +58 -0
- package/src/__tests__/integration/dashboard-volume.test.ts +31 -0
- package/src/__tests__/integration/digest.test.ts +48 -0
- package/src/__tests__/integration/invoice-pdf.test.ts +30 -0
- package/src/__tests__/integration/isolation.test.ts +81 -0
- package/src/__tests__/integration/mentions-invoice.test.ts +50 -0
- package/src/__tests__/integration/nps.test.ts +37 -0
- package/src/__tests__/integration/sanitization.test.ts +35 -0
- package/src/__tests__/integration/sla-pause.test.ts +39 -0
- package/src/__tests__/integration/smoke.test.ts +26 -0
- package/src/__tests__/integration/snooze.test.ts +68 -0
- package/src/__tests__/integration/teams.test.ts +38 -0
- package/src/__tests__/rateLimiter.test.ts +89 -0
- package/src/__tests__/sanitizeHtml.test.ts +165 -0
- package/src/__tests__/sla.test.ts +258 -0
- package/src/collections/AutomationRules.ts +66 -0
- package/src/collections/NotificationQueue.ts +30 -0
- package/src/collections/SatisfactionSurveys.ts +11 -2
- package/src/collections/SupportClients.ts +58 -1
- package/src/collections/SupportTeam.ts +27 -0
- package/src/collections/TicketActivityLog.ts +9 -4
- package/src/collections/TicketMessages.ts +129 -31
- package/src/collections/Tickets.ts +31 -73
- package/src/collections/index.ts +3 -0
- package/src/components/TicketConversation/RewriteDropdown.tsx +86 -0
- package/src/components/TicketConversation/SkeletonText.tsx +23 -0
- package/src/components/TicketConversation/constants.ts +29 -0
- package/src/components/TicketConversation/hooks/useDocumentIdFromUrl.ts +21 -0
- package/src/components/TicketConversation/index.tsx +4 -152
- package/src/endpoints/admin-chat-stream.ts +3 -4
- package/src/endpoints/admin-chat.ts +11 -20
- package/src/endpoints/admin-stats.ts +70 -32
- package/src/endpoints/ai-agent.ts +36 -0
- package/src/endpoints/apply-macro.ts +8 -14
- package/src/endpoints/auth-2fa.ts +12 -12
- package/src/endpoints/auto-close.ts +6 -10
- package/src/endpoints/billing.ts +17 -4
- package/src/endpoints/bulk-action.ts +7 -12
- package/src/endpoints/channels.ts +89 -0
- package/src/endpoints/chat-stream.ts +3 -4
- package/src/endpoints/chat.ts +7 -12
- package/src/endpoints/chatbot.ts +2 -2
- package/src/endpoints/client-intelligence.ts +4 -4
- package/src/endpoints/delete-account.ts +9 -16
- package/src/endpoints/email-stats.ts +2 -2
- package/src/endpoints/escalate.ts +2 -2
- package/src/endpoints/export-csv.ts +2 -2
- package/src/endpoints/export-data.ts +5 -8
- package/src/endpoints/import-conversation.ts +6 -10
- package/src/endpoints/index.ts +18 -1
- package/src/endpoints/invite-collaborator.ts +30 -15
- package/src/endpoints/invoice.ts +127 -0
- package/src/endpoints/kb-search.ts +2 -2
- package/src/endpoints/login.ts +12 -4
- package/src/endpoints/merge-clients.ts +14 -19
- package/src/endpoints/merge-tickets.ts +11 -18
- package/src/endpoints/oauth-google.ts +86 -39
- package/src/endpoints/pending-emails-process.ts +15 -25
- package/src/endpoints/process-digests.ts +93 -0
- package/src/endpoints/process-scheduled.ts +6 -10
- package/src/endpoints/process-snooze.ts +78 -0
- package/src/endpoints/purge-logs.ts +2 -2
- package/src/endpoints/resend-notification.ts +3 -4
- package/src/endpoints/round-robin-config.ts +3 -4
- package/src/endpoints/satisfaction.ts +21 -14
- package/src/endpoints/search.ts +5 -8
- package/src/endpoints/seed-kb.ts +3 -4
- package/src/endpoints/send-reminder.ts +6 -9
- package/src/endpoints/settings.ts +5 -2
- package/src/endpoints/signature.ts +3 -4
- package/src/endpoints/sla-check.ts +2 -2
- package/src/endpoints/split-ticket.ts +7 -12
- package/src/endpoints/statuses.ts +2 -2
- package/src/endpoints/ticket-feedback.ts +4 -6
- package/src/endpoints/ticket-synthesis.ts +2 -2
- package/src/endpoints/track-open.ts +16 -28
- package/src/endpoints/transfer-ticket.ts +21 -9
- package/src/endpoints/user-prefs.ts +3 -4
- package/src/hooks/automationRules.ts +84 -0
- package/src/hooks/checkSLA.ts +48 -16
- package/src/plugin.ts +6 -0
- package/src/portal/LiveChat.tsx +2 -63
- package/src/portal/auth/tickets/detail/CloseTicketButton.tsx +10 -0
- package/src/portal/auth/tickets/detail/MarkSolutionButton.tsx +14 -2
- package/src/portal/auth/tickets/detail/MessageActions.tsx +37 -18
- package/src/portal/auth/tickets/detail/ReopenTicketButton.tsx +10 -0
- package/src/portal/auth/tickets/detail/TransferAndInviteActions.tsx +3 -2
- package/src/portal/auth/tickets/detail/page.tsx +2 -2
- package/src/portal/helpers.ts +46 -0
- package/src/portal/login/page.tsx +3 -8
- package/src/portal/types.ts +15 -0
- package/src/types/lucide-react.d.ts +34 -0
- package/src/types/payload-config.d.ts +10 -0
- package/src/utils/adminNotification.ts +2 -2
- package/src/utils/aiAgent.ts +147 -0
- package/src/utils/auth.ts +5 -4
- package/src/utils/channels.ts +36 -0
- package/src/utils/db.ts +73 -0
- package/src/utils/emailTemplate.ts +8 -1
- package/src/utils/fireWebhooks.ts +4 -6
- package/src/utils/generateTicketSynthesis.ts +7 -12
- package/src/utils/invoicePdf.ts +88 -0
- package/src/utils/notificationQueue.ts +36 -0
- package/src/utils/readSettings.ts +22 -6
- package/src/utils/sanitizeHtml.ts +57 -0
- package/src/utils/slugs.ts +6 -0
- package/src/utils/teamAccess.ts +27 -0
- package/src/utils/ticketAccess.ts +53 -0
- package/src/utils/webhookDispatcher.ts +4 -6
- package/src/views/BillingView/client.tsx +49 -37
- package/src/views/ChatView/client.tsx +4 -3
- package/src/views/CrmView/client.tsx +7 -7
- package/src/views/EmailTrackingView/client.tsx +7 -6
- package/src/views/ImportConversationView/client.tsx +4 -4
- package/src/views/LogsView/client.tsx +3 -2
- package/src/views/NewTicketView/client.tsx +1 -1
- package/src/views/PendingEmailsView/client.tsx +7 -6
- package/src/views/SupportDashboardView/client.tsx +21 -21
- package/src/views/TicketDetailView/NextActionItem.tsx +30 -0
- package/src/views/TicketDetailView/RewriteDropdown.tsx +63 -0
- package/src/views/TicketDetailView/client.tsx +54 -179
- package/src/views/TicketDetailView/constants.ts +23 -0
- package/src/views/TicketDetailView/helpers.ts +17 -0
- package/src/views/TicketDetailView/types.ts +10 -0
- package/src/views/TicketInboxView/client.tsx +16 -10
- package/src/views/TicketingSettingsView/client.tsx +90 -87
- package/src/views/TimeDashboardView/client.tsx +2 -2
- package/src/views/shared/StatusPill.tsx +24 -12
- package/src/views/shared/dateLocale.ts +5 -0
- package/src/views/shared/locales/en.json +708 -0
- package/src/views/shared/locales/fr.json +708 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
|
|
6
|
+
var dateLocale = require('../shared/dateLocale');
|
|
6
7
|
var s = require('../../styles/EmailTracking.module.scss');
|
|
7
8
|
|
|
8
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -11,12 +12,12 @@ var React__default = /*#__PURE__*/_interopDefault(React);
|
|
|
11
12
|
var s__default = /*#__PURE__*/_interopDefault(s);
|
|
12
13
|
|
|
13
14
|
const STATUS_CFG = {
|
|
14
|
-
success: {
|
|
15
|
-
error: {
|
|
16
|
-
ignored: {
|
|
15
|
+
success: { labelKey: "logs.statusSuccess", bg: "#dcfce7", color: "#16a34a" },
|
|
16
|
+
error: { labelKey: "emailTracking.errorLabel", bg: "#fef2f2", color: "#dc2626" },
|
|
17
|
+
ignored: { labelKey: "logs.statusIgnored", bg: "#f3f4f6", color: "#6b7280" }
|
|
17
18
|
};
|
|
18
19
|
function fmtDate(d) {
|
|
19
|
-
return new Date(d).toLocaleDateString(
|
|
20
|
+
return new Date(d).toLocaleDateString(dateLocale.DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
20
21
|
}
|
|
21
22
|
const EmailTrackingClient = () => {
|
|
22
23
|
const { t } = useTranslation.useTranslation();
|
|
@@ -130,7 +131,7 @@ const EmailTrackingClient = () => {
|
|
|
130
131
|
/* @__PURE__ */ jsxRuntime.jsx("td", { style: { maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.recipientEmail, children: log.recipientEmail || "\u2014" }),
|
|
131
132
|
/* @__PURE__ */ jsxRuntime.jsx("td", { style: { maxWidth: 250, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.subject, children: log.subject || "\u2014" }),
|
|
132
133
|
/* @__PURE__ */ jsxRuntime.jsx("td", { children: log.ticketNumber ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.ticketLink, children: log.ticketNumber }) : "\u2014" }),
|
|
133
|
-
/* @__PURE__ */ jsxRuntime.jsx("td", { children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.labelKey ? t(STATUS_CFG[log.status].labelKey) : log.status }) }),
|
|
134
135
|
/* @__PURE__ */ jsxRuntime.jsx("td", { style: { fontSize: 12 }, children: log.action || "\u2014" }),
|
|
135
136
|
/* @__PURE__ */ jsxRuntime.jsxs("td", { style: { textAlign: "right" }, children: [
|
|
136
137
|
log.processingTimeMs != null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${s__default.default.processingTime} ${log.processingTimeMs > 2e3 ? s__default.default.timeSlow : log.processingTimeMs > 500 ? s__default.default.timeMedium : s__default.default.timeFast}`, children: [
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import React, { useState, useCallback, useEffect } from 'react';
|
|
4
4
|
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
|
|
5
|
+
import { DATE_LOCALE } from '../shared/dateLocale.js';
|
|
5
6
|
import s from '../../styles/EmailTracking.module.scss';
|
|
6
7
|
|
|
7
8
|
const STATUS_CFG = {
|
|
8
|
-
success: {
|
|
9
|
-
error: {
|
|
10
|
-
ignored: {
|
|
9
|
+
success: { labelKey: "logs.statusSuccess", bg: "#dcfce7", color: "#16a34a" },
|
|
10
|
+
error: { labelKey: "emailTracking.errorLabel", bg: "#fef2f2", color: "#dc2626" },
|
|
11
|
+
ignored: { labelKey: "logs.statusIgnored", bg: "#f3f4f6", color: "#6b7280" }
|
|
11
12
|
};
|
|
12
13
|
function fmtDate(d) {
|
|
13
|
-
return new Date(d).toLocaleDateString(
|
|
14
|
+
return new Date(d).toLocaleDateString(DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
14
15
|
}
|
|
15
16
|
const EmailTrackingClient = () => {
|
|
16
17
|
const { t } = useTranslation();
|
|
@@ -124,7 +125,7 @@ const EmailTrackingClient = () => {
|
|
|
124
125
|
/* @__PURE__ */ jsx("td", { style: { maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.recipientEmail, children: log.recipientEmail || "\u2014" }),
|
|
125
126
|
/* @__PURE__ */ jsx("td", { style: { maxWidth: 250, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.subject, children: log.subject || "\u2014" }),
|
|
126
127
|
/* @__PURE__ */ jsx("td", { children: log.ticketNumber ? /* @__PURE__ */ jsx("span", { className: s.ticketLink, children: log.ticketNumber }) : "\u2014" }),
|
|
127
|
-
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("span", { className: s.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.
|
|
128
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("span", { className: s.statusBadge, style: { background: STATUS_CFG[log.status]?.bg, color: STATUS_CFG[log.status]?.color }, children: STATUS_CFG[log.status]?.labelKey ? t(STATUS_CFG[log.status].labelKey) : log.status }) }),
|
|
128
129
|
/* @__PURE__ */ jsx("td", { style: { fontSize: 12 }, children: log.action || "\u2014" }),
|
|
129
130
|
/* @__PURE__ */ jsxs("td", { style: { textAlign: "right" }, children: [
|
|
130
131
|
log.processingTimeMs != null ? /* @__PURE__ */ jsxs("span", { className: `${s.processingTime} ${log.processingTimeMs > 2e3 ? s.timeSlow : log.processingTimeMs > 500 ? s.timeMedium : s.timeFast}`, children: [
|
|
@@ -68,12 +68,12 @@ function ImportConversationClient() {
|
|
|
68
68
|
});
|
|
69
69
|
const data = await res.json();
|
|
70
70
|
if (!res.ok) {
|
|
71
|
-
setError(data.error || "
|
|
71
|
+
setError(data.error || t("import.analyzeError"));
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
setPreview(data);
|
|
75
75
|
} catch {
|
|
76
|
-
setError("
|
|
76
|
+
setError(t("import.networkError"));
|
|
77
77
|
} finally {
|
|
78
78
|
setLoading(false);
|
|
79
79
|
}
|
|
@@ -90,13 +90,13 @@ function ImportConversationClient() {
|
|
|
90
90
|
});
|
|
91
91
|
const data = await res.json();
|
|
92
92
|
if (!res.ok) {
|
|
93
|
-
setError(data.error || "
|
|
93
|
+
setError(data.error || t("import.importError"));
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
setResult(data);
|
|
97
97
|
setPreview(null);
|
|
98
98
|
} catch {
|
|
99
|
-
setError("
|
|
99
|
+
setError(t("import.networkError"));
|
|
100
100
|
} finally {
|
|
101
101
|
setLoading(false);
|
|
102
102
|
}
|
|
@@ -63,12 +63,12 @@ function ImportConversationClient() {
|
|
|
63
63
|
});
|
|
64
64
|
const data = await res.json();
|
|
65
65
|
if (!res.ok) {
|
|
66
|
-
setError(data.error || "
|
|
66
|
+
setError(data.error || t("import.analyzeError"));
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
setPreview(data);
|
|
70
70
|
} catch {
|
|
71
|
-
setError("
|
|
71
|
+
setError(t("import.networkError"));
|
|
72
72
|
} finally {
|
|
73
73
|
setLoading(false);
|
|
74
74
|
}
|
|
@@ -85,13 +85,13 @@ function ImportConversationClient() {
|
|
|
85
85
|
});
|
|
86
86
|
const data = await res.json();
|
|
87
87
|
if (!res.ok) {
|
|
88
|
-
setError(data.error || "
|
|
88
|
+
setError(data.error || t("import.importError"));
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
setResult(data);
|
|
92
92
|
setPreview(null);
|
|
93
93
|
} catch {
|
|
94
|
-
setError("
|
|
94
|
+
setError(t("import.networkError"));
|
|
95
95
|
} finally {
|
|
96
96
|
setLoading(false);
|
|
97
97
|
}
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var navigation = require('next/navigation');
|
|
6
6
|
var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
|
|
7
|
+
var dateLocale = require('../shared/dateLocale');
|
|
7
8
|
var s = require('../../styles/Logs.module.scss');
|
|
8
9
|
|
|
9
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -11,7 +12,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
11
12
|
var s__default = /*#__PURE__*/_interopDefault(s);
|
|
12
13
|
|
|
13
14
|
function fmtDate(d) {
|
|
14
|
-
return new Date(d).toLocaleDateString(
|
|
15
|
+
return new Date(d).toLocaleDateString(dateLocale.DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
15
16
|
}
|
|
16
17
|
const LogsClient = () => {
|
|
17
18
|
const { t } = useTranslation.useTranslation();
|
|
@@ -67,7 +68,7 @@ const LogsClient = () => {
|
|
|
67
68
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.headerActions, children: [
|
|
68
69
|
/* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(7), children: t("logs.purge7") }),
|
|
69
70
|
/* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(30), children: t("logs.purge30") }),
|
|
70
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(90), children: "
|
|
71
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.purgeBtn, onClick: () => handlePurge(90), children: t("logs.purge90") }),
|
|
71
72
|
/* @__PURE__ */ jsxRuntime.jsx("button", { className: `${s__default.default.purgeBtn} ${s__default.default.purgeBtnDanger}`, onClick: () => handlePurge(0), children: t("logs.purgeAll") })
|
|
72
73
|
] })
|
|
73
74
|
] }),
|
|
@@ -3,10 +3,11 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import { useState, useCallback, useEffect } from 'react';
|
|
4
4
|
import { useSearchParams } from 'next/navigation';
|
|
5
5
|
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
|
|
6
|
+
import { DATE_LOCALE } from '../shared/dateLocale.js';
|
|
6
7
|
import s from '../../styles/Logs.module.scss';
|
|
7
8
|
|
|
8
9
|
function fmtDate(d) {
|
|
9
|
-
return new Date(d).toLocaleDateString(
|
|
10
|
+
return new Date(d).toLocaleDateString(DATE_LOCALE, { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
|
|
10
11
|
}
|
|
11
12
|
const LogsClient = () => {
|
|
12
13
|
const { t } = useTranslation();
|
|
@@ -62,7 +63,7 @@ const LogsClient = () => {
|
|
|
62
63
|
/* @__PURE__ */ jsxs("div", { className: s.headerActions, children: [
|
|
63
64
|
/* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(7), children: t("logs.purge7") }),
|
|
64
65
|
/* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(30), children: t("logs.purge30") }),
|
|
65
|
-
/* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(90), children: "
|
|
66
|
+
/* @__PURE__ */ jsx("button", { className: s.purgeBtn, onClick: () => handlePurge(90), children: t("logs.purge90") }),
|
|
66
67
|
/* @__PURE__ */ jsx("button", { className: `${s.purgeBtn} ${s.purgeBtnDanger}`, onClick: () => handlePurge(0), children: t("logs.purgeAll") })
|
|
67
68
|
] })
|
|
68
69
|
] }),
|
|
@@ -196,7 +196,7 @@ const NewTicketClient = () => {
|
|
|
196
196
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.fieldGroup, children: [
|
|
197
197
|
/* @__PURE__ */ jsxRuntime.jsx("label", { className: s__default.default.label, children: t("newTicket.projectLabel") }),
|
|
198
198
|
/* @__PURE__ */ jsxRuntime.jsxs("select", { className: s__default.default.select, value: projectId || "", onChange: (e) => setProjectId(e.target.value ? Number(e.target.value) : null), children: [
|
|
199
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "
|
|
199
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("newTicket.noProject") }),
|
|
200
200
|
projects.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.id, children: p.name }, p.id))
|
|
201
201
|
] })
|
|
202
202
|
] })
|
|
@@ -190,7 +190,7 @@ const NewTicketClient = () => {
|
|
|
190
190
|
/* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
|
|
191
191
|
/* @__PURE__ */ jsx("label", { className: s.label, children: t("newTicket.projectLabel") }),
|
|
192
192
|
/* @__PURE__ */ jsxs("select", { className: s.select, value: projectId || "", onChange: (e) => setProjectId(e.target.value ? Number(e.target.value) : null), children: [
|
|
193
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "
|
|
193
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("newTicket.noProject") }),
|
|
194
194
|
projects.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
|
|
195
195
|
] })
|
|
196
196
|
] })
|
|
@@ -11,15 +11,15 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
11
11
|
|
|
12
12
|
var styles__default = /*#__PURE__*/_interopDefault(styles);
|
|
13
13
|
|
|
14
|
-
function timeAgo(dateStr) {
|
|
14
|
+
function timeAgo(dateStr, tFn) {
|
|
15
15
|
const diff = Date.now() - new Date(dateStr).getTime();
|
|
16
16
|
const mins = Math.floor(diff / 6e4);
|
|
17
|
-
if (mins < 1) return "
|
|
18
|
-
if (mins < 60) return
|
|
17
|
+
if (mins < 1) return tFn("pendingEmails.timeAgo.justNow");
|
|
18
|
+
if (mins < 60) return tFn("pendingEmails.timeAgo.minutesAgo", { count: mins });
|
|
19
19
|
const hours = Math.floor(mins / 60);
|
|
20
|
-
if (hours < 24) return
|
|
20
|
+
if (hours < 24) return tFn("pendingEmails.timeAgo.hoursAgo", { count: hours });
|
|
21
21
|
const days = Math.floor(hours / 24);
|
|
22
|
-
return
|
|
22
|
+
return tFn("pendingEmails.timeAgo.daysAgo", { count: days });
|
|
23
23
|
}
|
|
24
24
|
function TicketSearchModal({
|
|
25
25
|
suggestions,
|
|
@@ -256,6 +256,7 @@ function EmailCard({
|
|
|
256
256
|
onProcess,
|
|
257
257
|
processing
|
|
258
258
|
}) {
|
|
259
|
+
const { t } = useTranslation.useTranslation();
|
|
259
260
|
const [expanded, setExpanded] = react.useState(false);
|
|
260
261
|
const [showLinkModal, setShowLinkModal] = react.useState(false);
|
|
261
262
|
const [showClientPicker, setShowClientPicker] = react.useState(false);
|
|
@@ -278,7 +279,7 @@ function EmailCard({
|
|
|
278
279
|
] }),
|
|
279
280
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.cardSubject, children: email.subject }),
|
|
280
281
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.cardMeta, children: [
|
|
281
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: timeAgo(email.createdAt) }),
|
|
282
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: timeAgo(email.createdAt, t) }),
|
|
282
283
|
attachmentCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles__default.default.attachment, children: [
|
|
283
284
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { size: 12 }),
|
|
284
285
|
" ",
|
|
@@ -6,15 +6,15 @@ import { SkeletonDashboard } from '../shared/Skeleton.js';
|
|
|
6
6
|
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
|
|
7
7
|
import styles from '../../styles/PendingEmails.module.scss';
|
|
8
8
|
|
|
9
|
-
function timeAgo(dateStr) {
|
|
9
|
+
function timeAgo(dateStr, tFn) {
|
|
10
10
|
const diff = Date.now() - new Date(dateStr).getTime();
|
|
11
11
|
const mins = Math.floor(diff / 6e4);
|
|
12
|
-
if (mins < 1) return "
|
|
13
|
-
if (mins < 60) return
|
|
12
|
+
if (mins < 1) return tFn("pendingEmails.timeAgo.justNow");
|
|
13
|
+
if (mins < 60) return tFn("pendingEmails.timeAgo.minutesAgo", { count: mins });
|
|
14
14
|
const hours = Math.floor(mins / 60);
|
|
15
|
-
if (hours < 24) return
|
|
15
|
+
if (hours < 24) return tFn("pendingEmails.timeAgo.hoursAgo", { count: hours });
|
|
16
16
|
const days = Math.floor(hours / 24);
|
|
17
|
-
return
|
|
17
|
+
return tFn("pendingEmails.timeAgo.daysAgo", { count: days });
|
|
18
18
|
}
|
|
19
19
|
function TicketSearchModal({
|
|
20
20
|
suggestions,
|
|
@@ -251,6 +251,7 @@ function EmailCard({
|
|
|
251
251
|
onProcess,
|
|
252
252
|
processing
|
|
253
253
|
}) {
|
|
254
|
+
const { t } = useTranslation();
|
|
254
255
|
const [expanded, setExpanded] = useState(false);
|
|
255
256
|
const [showLinkModal, setShowLinkModal] = useState(false);
|
|
256
257
|
const [showClientPicker, setShowClientPicker] = useState(false);
|
|
@@ -273,7 +274,7 @@ function EmailCard({
|
|
|
273
274
|
] }),
|
|
274
275
|
/* @__PURE__ */ jsx("div", { className: styles.cardSubject, children: email.subject }),
|
|
275
276
|
/* @__PURE__ */ jsxs("div", { className: styles.cardMeta, children: [
|
|
276
|
-
/* @__PURE__ */ jsx("span", { children: timeAgo(email.createdAt) }),
|
|
277
|
+
/* @__PURE__ */ jsx("span", { children: timeAgo(email.createdAt, t) }),
|
|
277
278
|
attachmentCount > 0 && /* @__PURE__ */ jsxs("span", { className: styles.attachment, children: [
|
|
278
279
|
/* @__PURE__ */ jsx(Paperclip, { size: 12 }),
|
|
279
280
|
" ",
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var Link = require('next/link');
|
|
6
6
|
var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
|
|
7
|
+
var dateLocale = require('../shared/dateLocale');
|
|
7
8
|
var styles = require('../../styles/SupportDashboard.module.scss');
|
|
8
9
|
|
|
9
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -38,6 +39,7 @@ function computeTrend(current, previous) {
|
|
|
38
39
|
return { pct: Math.abs(pct), dir: pct > 0 ? "up" : "down" };
|
|
39
40
|
}
|
|
40
41
|
function StatCard({ label, value, trend, accentColor, onClick }) {
|
|
42
|
+
const { t } = useTranslation.useTranslation();
|
|
41
43
|
const style = accentColor ? { "--stat-accent": accentColor } : void 0;
|
|
42
44
|
const Tag = onClick ? "button" : "div";
|
|
43
45
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -55,7 +57,7 @@ function StatCard({ label, value, trend, accentColor, onClick }) {
|
|
|
55
57
|
trend.pct,
|
|
56
58
|
"%"
|
|
57
59
|
] }),
|
|
58
|
-
trend && trend.dir === "neutral" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${styles__default.default.statTrend} ${styles__default.default.neutral}`, children: "
|
|
60
|
+
trend && trend.dir === "neutral" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${styles__default.default.statTrend} ${styles__default.default.neutral}`, children: t("dashboard.stable") })
|
|
59
61
|
]
|
|
60
62
|
}
|
|
61
63
|
);
|
|
@@ -79,6 +81,7 @@ function VolumeChart({ data }) {
|
|
|
79
81
|
] });
|
|
80
82
|
}
|
|
81
83
|
function CSATRing({ score, count }) {
|
|
84
|
+
const { t } = useTranslation.useTranslation();
|
|
82
85
|
const pct = score > 0 ? score / 5 * 100 : 0;
|
|
83
86
|
const radius = 42;
|
|
84
87
|
const circumference = 2 * Math.PI * radius;
|
|
@@ -108,8 +111,8 @@ function CSATRing({ score, count }) {
|
|
|
108
111
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatValue, children: score > 0 ? score.toFixed(1) : "--" })
|
|
109
112
|
] }),
|
|
110
113
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.csatMeta, children: [
|
|
111
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : "
|
|
112
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatSub, children: count > 0 ?
|
|
114
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : t("dashboard.csat.noData") }),
|
|
115
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.csatSub, children: count > 0 ? t("dashboard.csat.reviews", { count }) : t("dashboard.csat.noReviews") })
|
|
113
116
|
] })
|
|
114
117
|
] });
|
|
115
118
|
}
|
|
@@ -124,6 +127,7 @@ function formatSlaTime(minutes) {
|
|
|
124
127
|
return remainH > 0 ? `${d}j ${remainH}h` : `${d}j`;
|
|
125
128
|
}
|
|
126
129
|
function SlaSection() {
|
|
130
|
+
const { t } = useTranslation.useTranslation();
|
|
127
131
|
const [sla, setSla] = react.useState(null);
|
|
128
132
|
const [slaLoading, setSlaLoading] = react.useState(true);
|
|
129
133
|
react.useEffect(() => {
|
|
@@ -143,8 +147,8 @@ function SlaSection() {
|
|
|
143
147
|
}, []);
|
|
144
148
|
if (slaLoading) {
|
|
145
149
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaSection, children: [
|
|
146
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: "
|
|
147
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaLoading, children: "
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: t("dashboard.slaTitle") }),
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaLoading, children: t("dashboard.sla.loading") })
|
|
148
152
|
] });
|
|
149
153
|
}
|
|
150
154
|
if (!sla) return null;
|
|
@@ -152,7 +156,7 @@ function SlaSection() {
|
|
|
152
156
|
window.location.href = `/admin/support/ticket?id=${id}`;
|
|
153
157
|
};
|
|
154
158
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaSection, children: [
|
|
155
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: "
|
|
159
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: styles__default.default.slaSectionTitle, children: t("dashboard.slaTitle") }),
|
|
156
160
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaGrid, children: [
|
|
157
161
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.slaCard} ${styles__default.default.slaBreach}`, children: [
|
|
158
162
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.slaCardHeader, children: [
|
|
@@ -162,11 +166,11 @@ function SlaSection() {
|
|
|
162
166
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
|
|
163
167
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
164
168
|
] }),
|
|
165
|
-
"
|
|
169
|
+
t("dashboard.sla.breached")
|
|
166
170
|
] }),
|
|
167
171
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${styles__default.default.slaCount} ${styles__default.default.slaCountBreach}`, children: sla.breached.length })
|
|
168
172
|
] }),
|
|
169
|
-
sla.breached.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: "
|
|
173
|
+
sla.breached.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: t("dashboard.sla.noBreached") }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: styles__default.default.slaList, children: sla.breached.map((ticket) => {
|
|
170
174
|
const now = /* @__PURE__ */ new Date();
|
|
171
175
|
const created = new Date(ticket.createdAt);
|
|
172
176
|
const overdueMin = Math.round((now.getTime() - created.getTime()) / 6e4);
|
|
@@ -200,11 +204,11 @@ function SlaSection() {
|
|
|
200
204
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
201
205
|
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "12 6 12 12 16 14" })
|
|
202
206
|
] }),
|
|
203
|
-
"
|
|
207
|
+
t("dashboard.sla.atRisk")
|
|
204
208
|
] }),
|
|
205
209
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${styles__default.default.slaCount} ${styles__default.default.slaCountRisk}`, children: sla.atRisk.length })
|
|
206
210
|
] }),
|
|
207
|
-
sla.atRisk.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: "
|
|
211
|
+
sla.atRisk.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.slaEmpty, children: t("dashboard.sla.noAtRisk") }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: styles__default.default.slaList, children: sla.atRisk.map((ticket) => {
|
|
208
212
|
const now = /* @__PURE__ */ new Date();
|
|
209
213
|
const created = new Date(ticket.createdAt);
|
|
210
214
|
const elapsedMin = Math.round((now.getTime() - created.getTime()) / 6e4);
|
|
@@ -223,10 +227,7 @@ function SlaSection() {
|
|
|
223
227
|
] }),
|
|
224
228
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.slaItemSubject, children: ticket.subject })
|
|
225
229
|
] }),
|
|
226
|
-
/* @__PURE__ */ jsxRuntime.
|
|
227
|
-
formatSlaTime(remainingMin),
|
|
228
|
-
" restant"
|
|
229
|
-
] })
|
|
230
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${styles__default.default.slaItemTime} ${styles__default.default.slaTimeRisk}`, children: t("dashboard.sla.remaining", { time: formatSlaTime(remainingMin) }) })
|
|
230
231
|
]
|
|
231
232
|
},
|
|
232
233
|
ticket.id
|
|
@@ -275,14 +276,10 @@ const SupportDashboardClient = () => {
|
|
|
275
276
|
return () => window.removeEventListener("focus", onFocus);
|
|
276
277
|
}, [fetchData, sessionExpired]);
|
|
277
278
|
const volumeData = react.useMemo(() => {
|
|
278
|
-
if (!stats) return [];
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
return days.map((label, i) => ({
|
|
283
|
-
label,
|
|
284
|
-
// Simple deterministic variation based on index
|
|
285
|
-
value: Math.max(base + (i * 3 + 1) % 5 - 2, 0)
|
|
279
|
+
if (!stats?.volumeByDay?.length) return [];
|
|
280
|
+
return stats.volumeByDay.map((d) => ({
|
|
281
|
+
label: new Date(d.date).toLocaleDateString(dateLocale.DATE_LOCALE, { weekday: "short" }),
|
|
282
|
+
value: d.count
|
|
286
283
|
}));
|
|
287
284
|
}, [stats]);
|
|
288
285
|
if (loading) {
|
|
@@ -3,6 +3,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
|
|
6
|
+
import { DATE_LOCALE } from '../shared/dateLocale.js';
|
|
6
7
|
import styles from '../../styles/SupportDashboard.module.scss';
|
|
7
8
|
|
|
8
9
|
function formatResponseTime(hours) {
|
|
@@ -32,6 +33,7 @@ function computeTrend(current, previous) {
|
|
|
32
33
|
return { pct: Math.abs(pct), dir: pct > 0 ? "up" : "down" };
|
|
33
34
|
}
|
|
34
35
|
function StatCard({ label, value, trend, accentColor, onClick }) {
|
|
36
|
+
const { t } = useTranslation();
|
|
35
37
|
const style = accentColor ? { "--stat-accent": accentColor } : void 0;
|
|
36
38
|
const Tag = onClick ? "button" : "div";
|
|
37
39
|
return /* @__PURE__ */ jsxs(
|
|
@@ -49,7 +51,7 @@ function StatCard({ label, value, trend, accentColor, onClick }) {
|
|
|
49
51
|
trend.pct,
|
|
50
52
|
"%"
|
|
51
53
|
] }),
|
|
52
|
-
trend && trend.dir === "neutral" && /* @__PURE__ */ jsx("div", { className: `${styles.statTrend} ${styles.neutral}`, children: "
|
|
54
|
+
trend && trend.dir === "neutral" && /* @__PURE__ */ jsx("div", { className: `${styles.statTrend} ${styles.neutral}`, children: t("dashboard.stable") })
|
|
53
55
|
]
|
|
54
56
|
}
|
|
55
57
|
);
|
|
@@ -73,6 +75,7 @@ function VolumeChart({ data }) {
|
|
|
73
75
|
] });
|
|
74
76
|
}
|
|
75
77
|
function CSATRing({ score, count }) {
|
|
78
|
+
const { t } = useTranslation();
|
|
76
79
|
const pct = score > 0 ? score / 5 * 100 : 0;
|
|
77
80
|
const radius = 42;
|
|
78
81
|
const circumference = 2 * Math.PI * radius;
|
|
@@ -102,8 +105,8 @@ function CSATRing({ score, count }) {
|
|
|
102
105
|
/* @__PURE__ */ jsx("div", { className: styles.csatValue, children: score > 0 ? score.toFixed(1) : "--" })
|
|
103
106
|
] }),
|
|
104
107
|
/* @__PURE__ */ jsxs("div", { className: styles.csatMeta, children: [
|
|
105
|
-
/* @__PURE__ */ jsx("div", { className: styles.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : "
|
|
106
|
-
/* @__PURE__ */ jsx("div", { className: styles.csatSub, children: count > 0 ?
|
|
108
|
+
/* @__PURE__ */ jsx("div", { className: styles.csatLabel, children: score > 0 ? `${score.toFixed(1)} / 5` : t("dashboard.csat.noData") }),
|
|
109
|
+
/* @__PURE__ */ jsx("div", { className: styles.csatSub, children: count > 0 ? t("dashboard.csat.reviews", { count }) : t("dashboard.csat.noReviews") })
|
|
107
110
|
] })
|
|
108
111
|
] });
|
|
109
112
|
}
|
|
@@ -118,6 +121,7 @@ function formatSlaTime(minutes) {
|
|
|
118
121
|
return remainH > 0 ? `${d}j ${remainH}h` : `${d}j`;
|
|
119
122
|
}
|
|
120
123
|
function SlaSection() {
|
|
124
|
+
const { t } = useTranslation();
|
|
121
125
|
const [sla, setSla] = useState(null);
|
|
122
126
|
const [slaLoading, setSlaLoading] = useState(true);
|
|
123
127
|
useEffect(() => {
|
|
@@ -137,8 +141,8 @@ function SlaSection() {
|
|
|
137
141
|
}, []);
|
|
138
142
|
if (slaLoading) {
|
|
139
143
|
return /* @__PURE__ */ jsxs("div", { className: styles.slaSection, children: [
|
|
140
|
-
/* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: "
|
|
141
|
-
/* @__PURE__ */ jsx("div", { className: styles.slaLoading, children: "
|
|
144
|
+
/* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: t("dashboard.slaTitle") }),
|
|
145
|
+
/* @__PURE__ */ jsx("div", { className: styles.slaLoading, children: t("dashboard.sla.loading") })
|
|
142
146
|
] });
|
|
143
147
|
}
|
|
144
148
|
if (!sla) return null;
|
|
@@ -146,7 +150,7 @@ function SlaSection() {
|
|
|
146
150
|
window.location.href = `/admin/support/ticket?id=${id}`;
|
|
147
151
|
};
|
|
148
152
|
return /* @__PURE__ */ jsxs("div", { className: styles.slaSection, children: [
|
|
149
|
-
/* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: "
|
|
153
|
+
/* @__PURE__ */ jsx("h2", { className: styles.slaSectionTitle, children: t("dashboard.slaTitle") }),
|
|
150
154
|
/* @__PURE__ */ jsxs("div", { className: styles.slaGrid, children: [
|
|
151
155
|
/* @__PURE__ */ jsxs("div", { className: `${styles.slaCard} ${styles.slaBreach}`, children: [
|
|
152
156
|
/* @__PURE__ */ jsxs("div", { className: styles.slaCardHeader, children: [
|
|
@@ -156,11 +160,11 @@ function SlaSection() {
|
|
|
156
160
|
/* @__PURE__ */ jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
|
|
157
161
|
/* @__PURE__ */ jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
158
162
|
] }),
|
|
159
|
-
"
|
|
163
|
+
t("dashboard.sla.breached")
|
|
160
164
|
] }),
|
|
161
165
|
/* @__PURE__ */ jsx("span", { className: `${styles.slaCount} ${styles.slaCountBreach}`, children: sla.breached.length })
|
|
162
166
|
] }),
|
|
163
|
-
sla.breached.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: "
|
|
167
|
+
sla.breached.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: t("dashboard.sla.noBreached") }) : /* @__PURE__ */ jsx("ul", { className: styles.slaList, children: sla.breached.map((ticket) => {
|
|
164
168
|
const now = /* @__PURE__ */ new Date();
|
|
165
169
|
const created = new Date(ticket.createdAt);
|
|
166
170
|
const overdueMin = Math.round((now.getTime() - created.getTime()) / 6e4);
|
|
@@ -194,11 +198,11 @@ function SlaSection() {
|
|
|
194
198
|
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
195
199
|
/* @__PURE__ */ jsx("polyline", { points: "12 6 12 12 16 14" })
|
|
196
200
|
] }),
|
|
197
|
-
"
|
|
201
|
+
t("dashboard.sla.atRisk")
|
|
198
202
|
] }),
|
|
199
203
|
/* @__PURE__ */ jsx("span", { className: `${styles.slaCount} ${styles.slaCountRisk}`, children: sla.atRisk.length })
|
|
200
204
|
] }),
|
|
201
|
-
sla.atRisk.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: "
|
|
205
|
+
sla.atRisk.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.slaEmpty, children: t("dashboard.sla.noAtRisk") }) : /* @__PURE__ */ jsx("ul", { className: styles.slaList, children: sla.atRisk.map((ticket) => {
|
|
202
206
|
const now = /* @__PURE__ */ new Date();
|
|
203
207
|
const created = new Date(ticket.createdAt);
|
|
204
208
|
const elapsedMin = Math.round((now.getTime() - created.getTime()) / 6e4);
|
|
@@ -217,10 +221,7 @@ function SlaSection() {
|
|
|
217
221
|
] }),
|
|
218
222
|
/* @__PURE__ */ jsx("span", { className: styles.slaItemSubject, children: ticket.subject })
|
|
219
223
|
] }),
|
|
220
|
-
/* @__PURE__ */
|
|
221
|
-
formatSlaTime(remainingMin),
|
|
222
|
-
" restant"
|
|
223
|
-
] })
|
|
224
|
+
/* @__PURE__ */ jsx("span", { className: `${styles.slaItemTime} ${styles.slaTimeRisk}`, children: t("dashboard.sla.remaining", { time: formatSlaTime(remainingMin) }) })
|
|
224
225
|
]
|
|
225
226
|
},
|
|
226
227
|
ticket.id
|
|
@@ -269,14 +270,10 @@ const SupportDashboardClient = () => {
|
|
|
269
270
|
return () => window.removeEventListener("focus", onFocus);
|
|
270
271
|
}, [fetchData, sessionExpired]);
|
|
271
272
|
const volumeData = useMemo(() => {
|
|
272
|
-
if (!stats) return [];
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return days.map((label, i) => ({
|
|
277
|
-
label,
|
|
278
|
-
// Simple deterministic variation based on index
|
|
279
|
-
value: Math.max(base + (i * 3 + 1) % 5 - 2, 0)
|
|
273
|
+
if (!stats?.volumeByDay?.length) return [];
|
|
274
|
+
return stats.volumeByDay.map((d) => ({
|
|
275
|
+
label: new Date(d.date).toLocaleDateString(DATE_LOCALE, { weekday: "short" }),
|
|
276
|
+
value: d.count
|
|
280
277
|
}));
|
|
281
278
|
}, [stats]);
|
|
282
279
|
if (loading) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var s = require('../../styles/TicketDetail.module.scss');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var s__default = /*#__PURE__*/_interopDefault(s);
|
|
10
|
+
|
|
11
|
+
const labelMap = { now: "Maintenant", today: "Aujourd'hui", "this-week": "Cette semaine" };
|
|
12
|
+
function NextActionItem({ action, index, onToggle }) {
|
|
13
|
+
const [pending, setPending] = react.useState(false);
|
|
14
|
+
const done = !!action.done;
|
|
15
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: s__default.default.aiCardNextAction, children: [
|
|
16
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17
|
+
"button",
|
|
18
|
+
{
|
|
19
|
+
type: "button",
|
|
20
|
+
className: `${s__default.default.aiCardNextCheck} ${done ? s__default.default.aiCardNextCheckDone : ""}`,
|
|
21
|
+
onClick: async () => {
|
|
22
|
+
setPending(true);
|
|
23
|
+
try {
|
|
24
|
+
await onToggle(!done);
|
|
25
|
+
} finally {
|
|
26
|
+
setPending(false);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"aria-pressed": done,
|
|
30
|
+
"aria-label": done ? "Marquer non fait" : "Marquer fait",
|
|
31
|
+
disabled: pending,
|
|
32
|
+
children: done && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" })
|
|
33
|
+
}
|
|
34
|
+
),
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${s__default.default.aiCardNextLabel} ${done ? s__default.default.aiCardNextLabelDone : ""}`, children: action.label }),
|
|
36
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiCardNextWhen, children: labelMap[action.priority] || action.priority })
|
|
37
|
+
] });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.NextActionItem = NextActionItem;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import s from '../../styles/TicketDetail.module.scss';
|
|
4
|
+
|
|
5
|
+
const labelMap = { now: "Maintenant", today: "Aujourd'hui", "this-week": "Cette semaine" };
|
|
6
|
+
function NextActionItem({ action, index, onToggle }) {
|
|
7
|
+
const [pending, setPending] = useState(false);
|
|
8
|
+
const done = !!action.done;
|
|
9
|
+
return /* @__PURE__ */ jsxs("li", { className: s.aiCardNextAction, children: [
|
|
10
|
+
/* @__PURE__ */ jsx(
|
|
11
|
+
"button",
|
|
12
|
+
{
|
|
13
|
+
type: "button",
|
|
14
|
+
className: `${s.aiCardNextCheck} ${done ? s.aiCardNextCheckDone : ""}`,
|
|
15
|
+
onClick: async () => {
|
|
16
|
+
setPending(true);
|
|
17
|
+
try {
|
|
18
|
+
await onToggle(!done);
|
|
19
|
+
} finally {
|
|
20
|
+
setPending(false);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"aria-pressed": done,
|
|
24
|
+
"aria-label": done ? "Marquer non fait" : "Marquer fait",
|
|
25
|
+
disabled: pending,
|
|
26
|
+
children: done && /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u2713" })
|
|
27
|
+
}
|
|
28
|
+
),
|
|
29
|
+
/* @__PURE__ */ jsx("span", { className: `${s.aiCardNextLabel} ${done ? s.aiCardNextLabelDone : ""}`, children: action.label }),
|
|
30
|
+
/* @__PURE__ */ jsx("span", { className: s.aiCardNextWhen, children: labelMap[action.priority] || action.priority })
|
|
31
|
+
] });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { NextActionItem };
|