@consilioweb/payload-support 5.0.0 → 6.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 +249 -22
- package/dist/components/RichTextEditor/index.js +1 -1
- package/dist/components/TicketConversation/RewriteDropdown.js +1 -1
- package/dist/components/TicketConversation/components/AISummaryPanel.js +3 -0
- package/dist/components/TicketConversation/components/ActionPanels.js +13 -8
- package/dist/components/TicketConversation/components/ClientHistory.js +1 -0
- package/dist/components/TicketConversation/components/CodeBlock.js +1 -0
- package/dist/components/TicketConversation/components/CodeBlockInserter.js +1 -1
- package/dist/components/TicketConversation/components/QuickActions.js +10 -8
- package/dist/components/TicketConversation/components/TicketHeader.js +2 -0
- package/dist/components/TicketConversation/components/TimeTrackingPanel.js +15 -9
- package/dist/components/TicketConversation/index.d.ts +10 -0
- package/dist/components/TicketConversation/index.js +25 -7
- package/dist/index.cjs +306 -60
- package/dist/index.d.cts +107 -38
- package/dist/index.d.ts +107 -38
- package/dist/index.js +300 -59
- package/dist/styles/BillingView.module.scss +11 -11
- package/dist/styles/ChatView.module.scss +7 -7
- package/dist/styles/CommandPalette.module.scss +1 -1
- package/dist/styles/CrmView.module.scss +9 -9
- package/dist/styles/EmailTracking.module.scss +6 -6
- package/dist/styles/ImportConversation.module.scss +5 -5
- package/dist/styles/Logs.module.scss +5 -5
- package/dist/styles/NewTicket.module.scss +2 -2
- package/dist/styles/PendingEmails.module.scss +13 -13
- package/dist/styles/SupportDashboard.module.scss +24 -8
- package/dist/styles/TicketDetail.module.scss +20 -20
- package/dist/styles/TicketInbox.module.scss +6 -6
- package/dist/styles/TicketingSettings.module.scss +10 -10
- package/dist/styles/TimeDashboard.module.scss +6 -6
- package/dist/styles/_tokens.scss +3 -1
- package/dist/utils/db.js +20 -0
- package/dist/utils/readSettings.js +150 -0
- package/dist/views/BillingView/client.js +15 -8
- package/dist/views/ChatView/client.js +2 -0
- package/dist/views/CrmView/client.js +2 -0
- package/dist/views/EmailTrackingView/client.js +23 -11
- package/dist/views/ImportConversationView/client.js +1 -0
- package/dist/views/LogsView/client.js +1 -1
- package/dist/views/NewTicketView/client.js +20 -13
- package/dist/views/PendingEmailsView/client.js +9 -3
- package/dist/views/SupportDashboardView/client.js +17 -19
- package/dist/views/TicketDetailView/client.js +23 -11
- package/dist/views/TicketInboxView/client.js +52 -26
- package/dist/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
- package/dist/views/TicketingSettingsView/client.js +44 -26
- package/dist/views/TimeDashboardView/client.js +10 -7
- package/dist/views/shared/ErrorBoundary.d.ts +36 -1
- package/dist/views/shared/ErrorBoundary.js +59 -28
- package/dist/views/shared/adminTokens.d.ts +15 -5
- package/dist/views/shared/adminTokens.js +23 -7
- package/dist/views/shared/icons.d.ts +49 -0
- package/dist/views/shared/icons.js +92 -0
- package/dist/views/shared/locales/en.json +15 -1
- package/dist/views/shared/locales/fr.json +15 -1
- package/package.json +9 -4
- package/scripts/copy-subpath-types.mjs +103 -0
- package/scripts/uninstall-data.mjs +178 -0
- package/scripts/uninstall.mjs +184 -0
- package/scripts/verify-dist-imports.mjs +84 -0
- package/src/collections/Tickets.ts +7 -0
- package/src/collections/TimeEntries.ts +11 -3
- package/src/components/RichTextEditor/index.tsx +1 -1
- package/src/components/TicketConversation/RewriteDropdown.tsx +1 -1
- package/src/components/TicketConversation/components/AISummaryPanel.tsx +3 -0
- package/src/components/TicketConversation/components/ActionPanels.tsx +13 -8
- package/src/components/TicketConversation/components/ClientHistory.tsx +1 -0
- package/src/components/TicketConversation/components/CodeBlock.tsx +1 -0
- package/src/components/TicketConversation/components/CodeBlockInserter.tsx +1 -1
- package/src/components/TicketConversation/components/QuickActions.tsx +10 -8
- package/src/components/TicketConversation/components/TicketHeader.tsx +2 -0
- package/src/components/TicketConversation/components/TimeTrackingPanel.tsx +17 -9
- package/src/components/TicketConversation/index.tsx +41 -7
- package/src/endpoints/delete-account.ts +191 -47
- package/src/endpoints/export-data.ts +104 -4
- package/src/endpoints/purge-logs.ts +6 -15
- package/src/index.ts +2 -0
- package/src/plugin.ts +51 -1
- package/src/portal/LiveChat.tsx +1 -1
- package/src/portal/auth/profile/page.tsx +3 -2
- package/src/portal/icons.tsx +91 -0
- package/src/styles/BillingView.module.scss +11 -11
- package/src/styles/ChatView.module.scss +7 -7
- package/src/styles/CommandPalette.module.scss +1 -1
- package/src/styles/CrmView.module.scss +9 -9
- package/src/styles/EmailTracking.module.scss +6 -6
- package/src/styles/ImportConversation.module.scss +5 -5
- package/src/styles/Logs.module.scss +5 -5
- package/src/styles/NewTicket.module.scss +2 -2
- package/src/styles/PendingEmails.module.scss +13 -13
- package/src/styles/SupportDashboard.module.scss +24 -8
- package/src/styles/TicketDetail.module.scss +20 -20
- package/src/styles/TicketInbox.module.scss +6 -6
- package/src/styles/TicketingSettings.module.scss +10 -10
- package/src/styles/TimeDashboard.module.scss +6 -6
- package/src/styles/_tokens.scss +3 -1
- package/src/types.ts +20 -0
- package/src/utils/retention.ts +111 -0
- package/src/utils/slugs.ts +15 -0
- package/src/views/BillingView/client.tsx +17 -8
- package/src/views/ChatView/client.tsx +2 -0
- package/src/views/CrmView/client.tsx +2 -0
- package/src/views/EmailTrackingView/client.tsx +18 -7
- package/src/views/ImportConversationView/client.tsx +1 -0
- package/src/views/LogsView/client.tsx +1 -1
- package/src/views/NewTicketView/client.tsx +22 -13
- package/src/views/PendingEmailsView/client.tsx +9 -3
- package/src/views/SupportDashboardView/client.tsx +11 -5
- package/src/views/TicketDetailView/client.tsx +25 -11
- package/src/views/TicketInboxView/client.tsx +47 -7
- package/src/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
- package/src/views/TicketingSettingsView/client.tsx +201 -144
- package/src/views/TimeDashboardView/client.tsx +10 -7
- package/src/views/shared/ErrorBoundary.tsx +95 -31
- package/src/views/shared/adminTokens.ts +28 -11
- package/src/views/shared/icons.tsx +146 -0
- package/src/views/shared/locales/en.json +15 -1
- package/src/views/shared/locales/fr.json +15 -1
- package/src/types/lucide-react.d.ts +0 -42
|
@@ -2,6 +2,7 @@
|
|
|
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 { V } from '../shared/adminTokens.js';
|
|
5
6
|
import { DATE_LOCALE } from '../shared/dateLocale.js';
|
|
6
7
|
import s from '../../styles/EmailTracking.module.scss';
|
|
7
8
|
|
|
@@ -107,7 +108,7 @@ const EmailTrackingClient = () => {
|
|
|
107
108
|
v,
|
|
108
109
|
"j"
|
|
109
110
|
] }, v)) }),
|
|
110
|
-
/* @__PURE__ */ jsx("input", { type: "text", className: s.searchInput, placeholder: t("emailTracking.searchPlaceholder"), value: search, onChange: (e) => setSearch(e.target.value) })
|
|
111
|
+
/* @__PURE__ */ jsx("input", { type: "text", className: s.searchInput, "aria-label": t("emailTracking.searchLabel"), placeholder: t("emailTracking.searchPlaceholder"), value: search, onChange: (e) => setSearch(e.target.value) })
|
|
111
112
|
] }),
|
|
112
113
|
logs.length === 0 ? /* @__PURE__ */ jsx("div", { className: s.empty, children: t("emailTracking.noLogs") }) : /* @__PURE__ */ jsxs("table", { className: s.table, children: [
|
|
113
114
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
@@ -120,7 +121,7 @@ const EmailTrackingClient = () => {
|
|
|
120
121
|
/* @__PURE__ */ jsx("th", { style: { textAlign: "right" }, children: t("emailTracking.tableHeaders.time") })
|
|
121
122
|
] }) }),
|
|
122
123
|
/* @__PURE__ */ jsx("tbody", { children: logs.map((log) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
123
|
-
/* @__PURE__ */ jsxs("tr", {
|
|
124
|
+
/* @__PURE__ */ jsxs("tr", { children: [
|
|
124
125
|
/* @__PURE__ */ jsx("td", { style: { fontSize: 12, whiteSpace: "nowrap" }, children: fmtDate(log.createdAt) }),
|
|
125
126
|
/* @__PURE__ */ jsx("td", { style: { maxWidth: 200, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.recipientEmail, children: log.recipientEmail || "\u2014" }),
|
|
126
127
|
/* @__PURE__ */ jsx("td", { style: { maxWidth: 250, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: log.subject, children: log.subject || "\u2014" }),
|
|
@@ -132,14 +133,25 @@ const EmailTrackingClient = () => {
|
|
|
132
133
|
log.processingTimeMs,
|
|
133
134
|
"ms"
|
|
134
135
|
] }) : "\u2014",
|
|
135
|
-
log.status === "error" && log.errorMessage && /*
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
log.status === "error" && log.errorMessage && /* The row itself carries no handler: a `<tr onClick>` is
|
|
137
|
+
* unreachable by keyboard and role="button" on a row would
|
|
138
|
+
* destroy the table semantics. This button is the control. */
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
"button",
|
|
141
|
+
{
|
|
142
|
+
type: "button",
|
|
143
|
+
className: s.expandBtn,
|
|
144
|
+
"aria-expanded": expandedRow === log.id,
|
|
145
|
+
"aria-controls": `email-error-${log.id}`,
|
|
146
|
+
"aria-label": t("emailTracking.toggleError"),
|
|
147
|
+
onClick: () => setExpandedRow(expandedRow === log.id ? null : log.id),
|
|
148
|
+
children: expandedRow === log.id ? "\u25B4" : "\u25BE"
|
|
149
|
+
}
|
|
150
|
+
)
|
|
139
151
|
] })
|
|
140
152
|
] }),
|
|
141
|
-
expandedRow === log.id && log.errorMessage && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 7, children: /* @__PURE__ */ jsxs("div", { className: s.errorDetail, children: [
|
|
142
|
-
"
|
|
153
|
+
expandedRow === log.id && log.errorMessage && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 7, children: /* @__PURE__ */ jsxs("div", { className: s.errorDetail, id: `email-error-${log.id}`, children: [
|
|
154
|
+
t("emailTracking.errorLabel"),
|
|
143
155
|
log.httpStatus ? ` (HTTP ${log.httpStatus})` : "",
|
|
144
156
|
": ",
|
|
145
157
|
log.errorMessage
|
|
@@ -147,8 +159,8 @@ const EmailTrackingClient = () => {
|
|
|
147
159
|
] }, log.id)) })
|
|
148
160
|
] }),
|
|
149
161
|
totalDocs > 0 && /* @__PURE__ */ jsxs("div", { className: s.pagination, children: [
|
|
150
|
-
/* @__PURE__ */ jsx("button", { className: s.pageBtn, onClick: () => setPage((p) => Math.max(1, p - 1)), disabled: page <= 1, children: t("common.previous") }),
|
|
151
|
-
/* @__PURE__ */ jsxs("span", { style: { fontSize: 12, color:
|
|
162
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: s.pageBtn, onClick: () => setPage((p) => Math.max(1, p - 1)), disabled: page <= 1, children: t("common.previous") }),
|
|
163
|
+
/* @__PURE__ */ jsxs("span", { style: { fontSize: 12, color: V.textSecondary, alignSelf: "center" }, children: [
|
|
152
164
|
t("common.page"),
|
|
153
165
|
" ",
|
|
154
166
|
page,
|
|
@@ -157,7 +169,7 @@ const EmailTrackingClient = () => {
|
|
|
157
169
|
" ",
|
|
158
170
|
t("common.results")
|
|
159
171
|
] }),
|
|
160
|
-
/* @__PURE__ */ jsx("button", { className: s.pageBtn, onClick: () => setPage((p) => p + 1), disabled: !hasMore, children: t("common.next") })
|
|
172
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: s.pageBtn, onClick: () => setPage((p) => p + 1), disabled: !hasMore, children: t("common.next") })
|
|
161
173
|
] })
|
|
162
174
|
] });
|
|
163
175
|
};
|
|
@@ -99,7 +99,7 @@ const LogsClient = () => {
|
|
|
99
99
|
}, children: log.status === "success" ? t("logs.statusSuccess") : log.status === "error" ? t("logs.statusError") : t("logs.statusIgnored") }) }),
|
|
100
100
|
/* @__PURE__ */ jsx("td", { className: s.truncate, title: log.recipientEmail, children: log.recipientEmail || "\u2014" }),
|
|
101
101
|
/* @__PURE__ */ jsx("td", { className: s.truncate, title: log.subject, children: log.subject || "\u2014" }),
|
|
102
|
-
/* @__PURE__ */ jsx("td", { style: { fontSize: 12, color: "var(--theme-elevation-
|
|
102
|
+
/* @__PURE__ */ jsx("td", { style: { fontSize: 12, color: "var(--theme-elevation-650)" }, children: log.action || "\u2014" }),
|
|
103
103
|
/* @__PURE__ */ jsx("td", { style: { textAlign: "right" }, children: log.processingTimeMs != null ? /* @__PURE__ */ jsxs("span", { className: s.mono, style: { color: log.processingTimeMs > 2e3 ? "#dc2626" : log.processingTimeMs > 500 ? "#d97706" : "#16a34a" }, children: [
|
|
104
104
|
log.processingTimeMs,
|
|
105
105
|
"ms"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { useState, useEffect } from 'react';
|
|
3
|
+
import { useId, useState, useEffect } from 'react';
|
|
4
4
|
import Link from 'next/link';
|
|
5
5
|
import { useRouter } from 'next/navigation';
|
|
6
6
|
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
|
|
@@ -22,6 +22,12 @@ const PRIORITY_KEYS = [
|
|
|
22
22
|
];
|
|
23
23
|
const NewTicketClient = () => {
|
|
24
24
|
const { t } = useTranslation();
|
|
25
|
+
const clientSearchId = useId();
|
|
26
|
+
const subjectId = useId();
|
|
27
|
+
const categoryId = useId();
|
|
28
|
+
const priorityId = useId();
|
|
29
|
+
const projectId_ = useId();
|
|
30
|
+
const descriptionId = useId();
|
|
25
31
|
const router = useRouter();
|
|
26
32
|
const [subject, setSubject] = useState("");
|
|
27
33
|
const [description, setDescription] = useState("");
|
|
@@ -121,7 +127,7 @@ const NewTicketClient = () => {
|
|
|
121
127
|
error && /* @__PURE__ */ jsx("div", { className: s.error, children: error }),
|
|
122
128
|
/* @__PURE__ */ jsxs("form", { className: s.form, onSubmit: handleSubmit, children: [
|
|
123
129
|
/* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
|
|
124
|
-
/* @__PURE__ */ jsxs("label", { className: s.label, children: [
|
|
130
|
+
/* @__PURE__ */ jsxs("label", { className: s.label, htmlFor: clientSearchId, children: [
|
|
125
131
|
t("newTicket.clientLabel"),
|
|
126
132
|
" ",
|
|
127
133
|
/* @__PURE__ */ jsx("span", { className: s.required, children: "*" })
|
|
@@ -134,7 +140,7 @@ const NewTicketClient = () => {
|
|
|
134
140
|
" \u2014 ",
|
|
135
141
|
selectedClient.company
|
|
136
142
|
] }),
|
|
137
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "var(--theme-elevation-
|
|
143
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "var(--theme-elevation-650)" }, children: selectedClient.email }),
|
|
138
144
|
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => {
|
|
139
145
|
setSelectedClient(null);
|
|
140
146
|
setClientId(null);
|
|
@@ -144,6 +150,7 @@ const NewTicketClient = () => {
|
|
|
144
150
|
/* @__PURE__ */ jsx(
|
|
145
151
|
"input",
|
|
146
152
|
{
|
|
153
|
+
id: clientSearchId,
|
|
147
154
|
type: "text",
|
|
148
155
|
className: s.input,
|
|
149
156
|
placeholder: t("newTicket.clientSearchPlaceholder"),
|
|
@@ -171,33 +178,33 @@ const NewTicketClient = () => {
|
|
|
171
178
|
] })
|
|
172
179
|
] }),
|
|
173
180
|
/* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
|
|
174
|
-
/* @__PURE__ */ jsxs("label", { className: s.label, children: [
|
|
181
|
+
/* @__PURE__ */ jsxs("label", { className: s.label, htmlFor: subjectId, children: [
|
|
175
182
|
t("newTicket.subjectLabel"),
|
|
176
183
|
" ",
|
|
177
184
|
/* @__PURE__ */ jsx("span", { className: s.required, children: "*" })
|
|
178
185
|
] }),
|
|
179
|
-
/* @__PURE__ */ jsx("input", { type: "text", className: s.input, placeholder: t("newTicket.subjectPlaceholder"), value: subject, onChange: (e) => setSubject(e.target.value) })
|
|
186
|
+
/* @__PURE__ */ jsx("input", { id: subjectId, type: "text", className: s.input, placeholder: t("newTicket.subjectPlaceholder"), value: subject, onChange: (e) => setSubject(e.target.value) })
|
|
180
187
|
] }),
|
|
181
188
|
/* @__PURE__ */ jsxs("div", { className: s.row3, children: [
|
|
182
189
|
/* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
|
|
183
|
-
/* @__PURE__ */ jsx("label", { className: s.label, children: t("newTicket.categoryLabel") }),
|
|
184
|
-
/* @__PURE__ */ jsx("select", { className: s.select, value: category, onChange: (e) => setCategory(e.target.value), children: CATEGORY_KEYS.map((c) => /* @__PURE__ */ jsx("option", { value: c.value, children: t(c.key) }, c.value)) })
|
|
190
|
+
/* @__PURE__ */ jsx("label", { className: s.label, htmlFor: categoryId, children: t("newTicket.categoryLabel") }),
|
|
191
|
+
/* @__PURE__ */ jsx("select", { id: categoryId, className: s.select, value: category, onChange: (e) => setCategory(e.target.value), children: CATEGORY_KEYS.map((c) => /* @__PURE__ */ jsx("option", { value: c.value, children: t(c.key) }, c.value)) })
|
|
185
192
|
] }),
|
|
186
193
|
/* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
|
|
187
|
-
/* @__PURE__ */ jsx("label", { className: s.label, children: t("newTicket.priorityLabel") }),
|
|
188
|
-
/* @__PURE__ */ jsx("select", { className: s.select, value: priority, onChange: (e) => setPriority(e.target.value), children: PRIORITY_KEYS.map((p) => /* @__PURE__ */ jsx("option", { value: p.value, children: t(p.key) }, p.value)) })
|
|
194
|
+
/* @__PURE__ */ jsx("label", { className: s.label, htmlFor: priorityId, children: t("newTicket.priorityLabel") }),
|
|
195
|
+
/* @__PURE__ */ jsx("select", { id: priorityId, className: s.select, value: priority, onChange: (e) => setPriority(e.target.value), children: PRIORITY_KEYS.map((p) => /* @__PURE__ */ jsx("option", { value: p.value, children: t(p.key) }, p.value)) })
|
|
189
196
|
] }),
|
|
190
197
|
/* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
|
|
191
|
-
/* @__PURE__ */ jsx("label", { className: s.label, children: t("newTicket.projectLabel") }),
|
|
192
|
-
/* @__PURE__ */ jsxs("select", { className: s.select, value: projectId || "", onChange: (e) => setProjectId(e.target.value ? Number(e.target.value) : null), children: [
|
|
198
|
+
/* @__PURE__ */ jsx("label", { className: s.label, htmlFor: projectId_, children: t("newTicket.projectLabel") }),
|
|
199
|
+
/* @__PURE__ */ jsxs("select", { id: projectId_, className: s.select, value: projectId || "", onChange: (e) => setProjectId(e.target.value ? Number(e.target.value) : null), children: [
|
|
193
200
|
/* @__PURE__ */ jsx("option", { value: "", children: t("newTicket.noProject") }),
|
|
194
201
|
projects.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
|
|
195
202
|
] })
|
|
196
203
|
] })
|
|
197
204
|
] }),
|
|
198
205
|
/* @__PURE__ */ jsxs("div", { className: s.fieldGroup, children: [
|
|
199
|
-
/* @__PURE__ */ jsx("label", { className: s.label, children: t("newTicket.descriptionLabel") }),
|
|
200
|
-
/* @__PURE__ */ jsx("textarea", { className: s.textarea, placeholder: t("newTicket.descriptionPlaceholder"), value: description, onChange: (e) => setDescription(e.target.value) })
|
|
206
|
+
/* @__PURE__ */ jsx("label", { className: s.label, htmlFor: descriptionId, children: t("newTicket.descriptionLabel") }),
|
|
207
|
+
/* @__PURE__ */ jsx("textarea", { id: descriptionId, className: s.textarea, placeholder: t("newTicket.descriptionPlaceholder"), value: description, onChange: (e) => setDescription(e.target.value) })
|
|
201
208
|
] }),
|
|
202
209
|
/* @__PURE__ */ jsx("button", { type: "submit", className: s.submitBtn, disabled: submitting, children: submitting ? t("newTicket.submitting") : t("newTicket.submitButton") })
|
|
203
210
|
] })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { useState, useCallback, useEffect } from 'react';
|
|
4
|
-
import { Inbox, Paperclip, ChevronUp, ChevronDown, Plus, Link2, X, Search } from '
|
|
4
|
+
import { Inbox, Paperclip, ChevronUp, ChevronDown, Plus, Link2, X, Search } from '../shared/icons.js';
|
|
5
5
|
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';
|
|
@@ -52,7 +52,7 @@ function TicketSearchModal({
|
|
|
52
52
|
return /* @__PURE__ */ jsx("div", { className: styles.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs("div", { className: styles.modal, onClick: (e) => e.stopPropagation(), children: [
|
|
53
53
|
/* @__PURE__ */ jsxs("div", { className: styles.modalHeader, children: [
|
|
54
54
|
/* @__PURE__ */ jsx("h3", { className: styles.modalTitle, children: "Rattacher a un ticket" }),
|
|
55
|
-
/* @__PURE__ */ jsx("button", { onClick: onClose, className: styles.modalClose, children: /* @__PURE__ */ jsx(X, { size: 20 }) })
|
|
55
|
+
/* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Fermer", onClick: onClose, className: styles.modalClose, children: /* @__PURE__ */ jsx(X, { size: 20 }) })
|
|
56
56
|
] }),
|
|
57
57
|
suggestions.length > 0 && /* @__PURE__ */ jsxs("div", { className: styles.sectionBlock, children: [
|
|
58
58
|
/* @__PURE__ */ jsx("div", { className: styles.sectionLabel, children: "Suggestions" }),
|
|
@@ -71,6 +71,7 @@ function TicketSearchModal({
|
|
|
71
71
|
"input",
|
|
72
72
|
{
|
|
73
73
|
type: "text",
|
|
74
|
+
"aria-label": "Rechercher un ticket",
|
|
74
75
|
placeholder: "Rechercher un ticket (TK-0042, sujet...)...",
|
|
75
76
|
value: search,
|
|
76
77
|
onChange: (e) => setSearch(e.target.value),
|
|
@@ -163,7 +164,7 @@ function ClientPickerModal({
|
|
|
163
164
|
return /* @__PURE__ */ jsx("div", { className: styles.overlay, onClick: onClose, children: /* @__PURE__ */ jsxs("div", { className: styles.modal, onClick: (e) => e.stopPropagation(), children: [
|
|
164
165
|
/* @__PURE__ */ jsxs("div", { className: styles.modalHeader, children: [
|
|
165
166
|
/* @__PURE__ */ jsx("h3", { className: styles.modalTitle, children: "Choisir un client" }),
|
|
166
|
-
/* @__PURE__ */ jsx("button", { onClick: onClose, className: styles.modalClose, children: /* @__PURE__ */ jsx(X, { size: 20 }) })
|
|
167
|
+
/* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Fermer", onClick: onClose, className: styles.modalClose, children: /* @__PURE__ */ jsx(X, { size: 20 }) })
|
|
167
168
|
] }),
|
|
168
169
|
detectedClient && /* @__PURE__ */ jsxs("div", { className: styles.sectionBlock, children: [
|
|
169
170
|
/* @__PURE__ */ jsx("div", { className: styles.sectionLabel, children: "Client detecte" }),
|
|
@@ -181,6 +182,7 @@ function ClientPickerModal({
|
|
|
181
182
|
"input",
|
|
182
183
|
{
|
|
183
184
|
type: "text",
|
|
185
|
+
"aria-label": "Rechercher un client",
|
|
184
186
|
placeholder: "Rechercher un client (nom, email, entreprise)...",
|
|
185
187
|
value: search,
|
|
186
188
|
onChange: (e) => setSearch(e.target.value),
|
|
@@ -205,6 +207,7 @@ function ClientPickerModal({
|
|
|
205
207
|
"input",
|
|
206
208
|
{
|
|
207
209
|
type: "text",
|
|
210
|
+
"aria-label": "Prenom",
|
|
208
211
|
placeholder: "Prenom *",
|
|
209
212
|
value: newClient.firstName,
|
|
210
213
|
onChange: (e) => setNewClient((p) => ({ ...p, firstName: e.target.value })),
|
|
@@ -215,6 +218,7 @@ function ClientPickerModal({
|
|
|
215
218
|
"input",
|
|
216
219
|
{
|
|
217
220
|
type: "text",
|
|
221
|
+
"aria-label": "Nom",
|
|
218
222
|
placeholder: "Nom",
|
|
219
223
|
value: newClient.lastName,
|
|
220
224
|
onChange: (e) => setNewClient((p) => ({ ...p, lastName: e.target.value })),
|
|
@@ -226,6 +230,7 @@ function ClientPickerModal({
|
|
|
226
230
|
"input",
|
|
227
231
|
{
|
|
228
232
|
type: "email",
|
|
233
|
+
"aria-label": "Email",
|
|
229
234
|
placeholder: "Email *",
|
|
230
235
|
value: newClient.email,
|
|
231
236
|
onChange: (e) => setNewClient((p) => ({ ...p, email: e.target.value })),
|
|
@@ -236,6 +241,7 @@ function ClientPickerModal({
|
|
|
236
241
|
"input",
|
|
237
242
|
{
|
|
238
243
|
type: "text",
|
|
244
|
+
"aria-label": "Entreprise",
|
|
239
245
|
placeholder: "Entreprise *",
|
|
240
246
|
value: newClient.company,
|
|
241
247
|
onChange: (e) => setNewClient((p) => ({ ...p, company: e.target.value })),
|
|
@@ -372,25 +372,23 @@ const SupportDashboardClient = () => {
|
|
|
372
372
|
/* @__PURE__ */ jsx("th", { children: t("dashboard.tableHeaders.modified") }),
|
|
373
373
|
/* @__PURE__ */ jsx("th", {})
|
|
374
374
|
] }) }),
|
|
375
|
-
/* @__PURE__ */ jsx("tbody", { children: tickets.map((tk) =>
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
},
|
|
393
|
-
tk.id
|
|
375
|
+
/* @__PURE__ */ jsx("tbody", { children: tickets.map((tk) => (
|
|
376
|
+
/*
|
|
377
|
+
* The row is not the control: a `<tr onClick>` is invisible to
|
|
378
|
+
* the keyboard, and turning it into role="button" would cost
|
|
379
|
+
* the table semantics. The link lives in the subject cell.
|
|
380
|
+
*/
|
|
381
|
+
/* @__PURE__ */ jsxs("tr", { children: [
|
|
382
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("span", { className: `${styles.statusDot} ${getStatusDotClass(tk.status)}` }) }),
|
|
383
|
+
/* @__PURE__ */ jsxs("td", { className: styles.ticketNum, children: [
|
|
384
|
+
"#",
|
|
385
|
+
tk.ticketNumber
|
|
386
|
+
] }),
|
|
387
|
+
/* @__PURE__ */ jsx("td", { className: styles.ticketSubject, children: /* @__PURE__ */ jsx("a", { className: styles.ticketLink, href: `/admin/support/ticket?id=${tk.id}`, children: tk.subject }) }),
|
|
388
|
+
/* @__PURE__ */ jsx("td", { className: styles.ticketClient, children: getClientName(tk) }),
|
|
389
|
+
/* @__PURE__ */ jsx("td", { className: styles.ticketTime, children: timeAgo(tk.updatedAt) }),
|
|
390
|
+
/* @__PURE__ */ jsx("td", { className: styles.ticketArrow, children: "\u2192" })
|
|
391
|
+
] }, tk.id)
|
|
394
392
|
)) })
|
|
395
393
|
] })
|
|
396
394
|
] }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import React, { useRef, useState, useEffect, useCallback } from 'react';
|
|
3
|
+
import React, { useRef, useId, useState, useEffect, useCallback } from 'react';
|
|
4
4
|
import { useSearchParams } from 'next/navigation';
|
|
5
5
|
import Link from 'next/link';
|
|
6
6
|
import { RichTextEditor } from '../../components/RichTextEditor/index.js';
|
|
@@ -25,6 +25,11 @@ const TicketDetailClient = () => {
|
|
|
25
25
|
const threadEndRef = useRef(null);
|
|
26
26
|
const dropdownRef = useRef(null);
|
|
27
27
|
const fileInputRef = useRef(null);
|
|
28
|
+
const manualTimeId = useId();
|
|
29
|
+
const billingTypeId = useId();
|
|
30
|
+
const flatAmountId = useId();
|
|
31
|
+
const billedAmountId = useId();
|
|
32
|
+
const paymentStatusId = useId();
|
|
28
33
|
const [ticket, setTicket] = useState(null);
|
|
29
34
|
const [messages, setMessages] = useState([]);
|
|
30
35
|
const [client, setClient] = useState(null);
|
|
@@ -755,7 +760,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
755
760
|
Array.isArray(msg.attachments) && msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: s.attachments, children: msg.attachments.map((att, i) => {
|
|
756
761
|
const file = typeof att.file === "object" ? att.file : null;
|
|
757
762
|
if (!file) return null;
|
|
758
|
-
return (file.mimeType || "").startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: file.url || "#", target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx("img", { src: file.url || "", alt: "", className: s.attachmentImg }) }, i) : /* @__PURE__ */ jsxs("a", { href: file.url || "#", target: "_blank", rel: "noopener noreferrer", className: s.attachmentFile, children: [
|
|
763
|
+
return (file.mimeType || "").startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: file.url || "#", target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx("img", { src: file.url || "", alt: file.filename || t("detail.file"), className: s.attachmentImg }) }, i) : /* @__PURE__ */ jsxs("a", { href: file.url || "#", target: "_blank", rel: "noopener noreferrer", className: s.attachmentFile, children: [
|
|
759
764
|
"PJ ",
|
|
760
765
|
file.filename || "Fichier"
|
|
761
766
|
] }, i);
|
|
@@ -846,7 +851,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
846
851
|
"\u{1F4CE} ",
|
|
847
852
|
t("detail.file")
|
|
848
853
|
] }),
|
|
849
|
-
/* @__PURE__ */ jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: s.hiddenFileInput, onChange: (e) => handleFileSelect(e.target.files) }),
|
|
854
|
+
/* @__PURE__ */ jsx("input", { ref: fileInputRef, type: "file", multiple: true, "aria-label": t("detail.file"), className: s.hiddenFileInput, onChange: (e) => handleFileSelect(e.target.files) }),
|
|
850
855
|
macros.length > 0 && /* @__PURE__ */ jsxs(
|
|
851
856
|
"select",
|
|
852
857
|
{
|
|
@@ -920,6 +925,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
920
925
|
/* @__PURE__ */ jsxs(
|
|
921
926
|
"select",
|
|
922
927
|
{
|
|
928
|
+
"aria-label": t("detail.sendAs"),
|
|
923
929
|
value: sendAsClient ? "client" : "admin",
|
|
924
930
|
onChange: (e) => {
|
|
925
931
|
const asClient = e.target.value === "client";
|
|
@@ -1132,9 +1138,9 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1132
1138
|
] }),
|
|
1133
1139
|
/* @__PURE__ */ jsx("div", { className: s.timerBar, "aria-hidden": true, children: /* @__PURE__ */ jsx("div", { className: s.timerBarFill, style: { width: `${Math.min(100, (totalMin + Math.floor(timerSeconds / 60)) / 120 * 100)}%` } }) }),
|
|
1134
1140
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6, marginTop: 8, alignItems: "center" }, children: [
|
|
1135
|
-
/* @__PURE__ */ jsx("input", { type: "number", min: "1", placeholder: "min", style: { width: 60, padding: "4px 8px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" }, id:
|
|
1141
|
+
/* @__PURE__ */ jsx("input", { type: "number", min: "1", placeholder: "min", "aria-label": t("detail.manualMinutes"), style: { width: 60, padding: "4px 8px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" }, id: manualTimeId }),
|
|
1136
1142
|
/* @__PURE__ */ jsx("button", { className: `${s.timerBtn} ${s.timerBtnGhost}`, onClick: async () => {
|
|
1137
|
-
const input = document.getElementById(
|
|
1143
|
+
const input = document.getElementById(manualTimeId);
|
|
1138
1144
|
const mins = Number(input?.value);
|
|
1139
1145
|
if (!mins || mins < 1 || !ticketId) return;
|
|
1140
1146
|
try {
|
|
@@ -1145,7 +1151,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1145
1151
|
}
|
|
1146
1152
|
}, style: { fontSize: 11 }, children: t("detail.addTime") })
|
|
1147
1153
|
] }),
|
|
1148
|
-
/* @__PURE__ */ jsxs("div", { style: { marginTop: 8, fontSize: 11, color: "var(--theme-elevation-
|
|
1154
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: 8, fontSize: 11, color: "var(--theme-elevation-650)" }, children: [
|
|
1149
1155
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "2px 0", alignItems: "center" }, children: [
|
|
1150
1156
|
/* @__PURE__ */ jsx("span", { children: t("detail.billing.billable") }),
|
|
1151
1157
|
/* @__PURE__ */ jsx(
|
|
@@ -1172,7 +1178,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1172
1178
|
] })
|
|
1173
1179
|
] })
|
|
1174
1180
|
] }),
|
|
1175
|
-
timeEntries.length > 0 && /* @__PURE__ */ jsx("div", { style: { marginTop: 8, fontSize: 11 }, children: timeEntries.slice(0, 6).map((e) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "3px 0", color: "var(--theme-elevation-
|
|
1181
|
+
timeEntries.length > 0 && /* @__PURE__ */ jsx("div", { style: { marginTop: 8, fontSize: 11 }, children: timeEntries.slice(0, 6).map((e) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "3px 0", color: "var(--theme-elevation-650)" }, children: [
|
|
1176
1182
|
/* @__PURE__ */ jsx("span", { children: new Date(e.date).toLocaleDateString(DATE_LOCALE, { day: "numeric", month: "short" }) }),
|
|
1177
1183
|
/* @__PURE__ */ jsxs("span", { title: e.description, style: { fontWeight: 600, cursor: e.description ? "help" : "default" }, children: [
|
|
1178
1184
|
e.duration,
|
|
@@ -1191,6 +1197,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1191
1197
|
"input",
|
|
1192
1198
|
{
|
|
1193
1199
|
className: s.tagInput,
|
|
1200
|
+
"aria-label": t("detail.addTag"),
|
|
1194
1201
|
value: newTagValue,
|
|
1195
1202
|
onChange: (e) => setNewTagValue(e.target.value),
|
|
1196
1203
|
onKeyDown: (e) => {
|
|
@@ -1211,10 +1218,11 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1211
1218
|
/* @__PURE__ */ jsx("div", { className: s.sideSectionTitle, children: t("detail.billing.title") }),
|
|
1212
1219
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
1213
1220
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1214
|
-
/* @__PURE__ */ jsx("
|
|
1221
|
+
/* @__PURE__ */ jsx("label", { style: { fontSize: 12 }, htmlFor: billingTypeId, children: t("detail.billing.type") }),
|
|
1215
1222
|
/* @__PURE__ */ jsxs(
|
|
1216
1223
|
"select",
|
|
1217
1224
|
{
|
|
1225
|
+
id: billingTypeId,
|
|
1218
1226
|
value: ticket?.billingType || "hourly",
|
|
1219
1227
|
onChange: async (e) => {
|
|
1220
1228
|
try {
|
|
@@ -1237,11 +1245,12 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1237
1245
|
)
|
|
1238
1246
|
] }),
|
|
1239
1247
|
ticket?.billingType === "flat" && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1240
|
-
/* @__PURE__ */ jsx("
|
|
1248
|
+
/* @__PURE__ */ jsx("label", { style: { fontSize: 12 }, htmlFor: flatAmountId, children: t("detail.billing.flatAmount") }),
|
|
1241
1249
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
1242
1250
|
/* @__PURE__ */ jsx(
|
|
1243
1251
|
"input",
|
|
1244
1252
|
{
|
|
1253
|
+
id: flatAmountId,
|
|
1245
1254
|
type: "number",
|
|
1246
1255
|
defaultValue: ticket?.flatRateAmount ?? "",
|
|
1247
1256
|
placeholder: "0",
|
|
@@ -1265,11 +1274,12 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1265
1274
|
] })
|
|
1266
1275
|
] }),
|
|
1267
1276
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1268
|
-
/* @__PURE__ */ jsx("
|
|
1277
|
+
/* @__PURE__ */ jsx("label", { style: { fontSize: 12 }, htmlFor: billedAmountId, children: t("detail.billing.billedAmount") }),
|
|
1269
1278
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
1270
1279
|
/* @__PURE__ */ jsx(
|
|
1271
1280
|
"input",
|
|
1272
1281
|
{
|
|
1282
|
+
id: billedAmountId,
|
|
1273
1283
|
type: "number",
|
|
1274
1284
|
defaultValue: ticket?.billedAmount ?? "",
|
|
1275
1285
|
placeholder: "0",
|
|
@@ -1293,10 +1303,11 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1293
1303
|
] })
|
|
1294
1304
|
] }),
|
|
1295
1305
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
1296
|
-
/* @__PURE__ */ jsx("
|
|
1306
|
+
/* @__PURE__ */ jsx("label", { style: { fontSize: 12 }, htmlFor: paymentStatusId, children: t("detail.billing.payment") }),
|
|
1297
1307
|
/* @__PURE__ */ jsxs(
|
|
1298
1308
|
"select",
|
|
1299
1309
|
{
|
|
1310
|
+
id: paymentStatusId,
|
|
1300
1311
|
value: ticket?.paymentStatus || "unpaid",
|
|
1301
1312
|
onChange: async (e) => {
|
|
1302
1313
|
try {
|
|
@@ -1382,6 +1393,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
|
|
|
1382
1393
|
"input",
|
|
1383
1394
|
{
|
|
1384
1395
|
className: s.splitInput,
|
|
1396
|
+
"aria-label": t("detail.split.subjectLabel"),
|
|
1385
1397
|
value: splitSubject,
|
|
1386
1398
|
onChange: (e) => setSplitSubject(e.target.value),
|
|
1387
1399
|
onKeyDown: (e) => {
|
|
@@ -9,6 +9,7 @@ import { StatusPill } from '../shared/StatusPill.js';
|
|
|
9
9
|
import { computeSlaState, formatSlaRemaining } from '../shared/sla.js';
|
|
10
10
|
import s from '../../styles/TicketInbox.module.scss';
|
|
11
11
|
|
|
12
|
+
const INBOX_TABPANEL_ID = "inbox-tabpanel";
|
|
12
13
|
const PRIORITY_COLORS = {
|
|
13
14
|
urgent: "var(--theme-error-500)",
|
|
14
15
|
high: "var(--theme-warning-500)",
|
|
@@ -213,6 +214,7 @@ const TicketInboxClient = () => {
|
|
|
213
214
|
{
|
|
214
215
|
type: "text",
|
|
215
216
|
className: s.searchInput,
|
|
217
|
+
"aria-label": t("inbox.searchLabel"),
|
|
216
218
|
placeholder: t("inbox.searchPlaceholder"),
|
|
217
219
|
value: search,
|
|
218
220
|
onChange: (e) => setSearch(e.target.value)
|
|
@@ -223,31 +225,55 @@ const TicketInboxClient = () => {
|
|
|
223
225
|
/* @__PURE__ */ jsx(Link, { href: "/admin/support/new-ticket", className: s.newTicketBtn, children: t("inbox.newTicketBtn") })
|
|
224
226
|
] })
|
|
225
227
|
] }),
|
|
226
|
-
/* @__PURE__ */ jsx(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
228
|
+
/* @__PURE__ */ jsx(
|
|
229
|
+
"div",
|
|
230
|
+
{
|
|
231
|
+
className: s.tabs,
|
|
232
|
+
role: "tablist",
|
|
233
|
+
"aria-label": t("inbox.tabsLabel"),
|
|
234
|
+
onKeyDown: (e) => {
|
|
235
|
+
const keys = ["ArrowLeft", "ArrowRight", "Home", "End"];
|
|
236
|
+
if (!keys.includes(e.key)) return;
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
const i = tabs.findIndex((tk) => tk.key === tab);
|
|
239
|
+
const next = e.key === "Home" ? 0 : e.key === "End" ? tabs.length - 1 : e.key === "ArrowLeft" ? (i - 1 + tabs.length) % tabs.length : (i + 1) % tabs.length;
|
|
240
|
+
const target = tabs[next];
|
|
241
|
+
if (!target) return;
|
|
242
|
+
setTab(target.key);
|
|
243
|
+
setSelectedIdx(-1);
|
|
244
|
+
document.getElementById(`inbox-tab-${target.key}`)?.focus();
|
|
242
245
|
},
|
|
243
|
-
tk
|
|
244
|
-
|
|
245
|
-
|
|
246
|
+
children: tabs.map((tk) => {
|
|
247
|
+
const isAlert = tk.alert && tk.count > 0;
|
|
248
|
+
return /* @__PURE__ */ jsxs(
|
|
249
|
+
"button",
|
|
250
|
+
{
|
|
251
|
+
type: "button",
|
|
252
|
+
role: "tab",
|
|
253
|
+
id: `inbox-tab-${tk.key}`,
|
|
254
|
+
"aria-selected": tab === tk.key,
|
|
255
|
+
"aria-controls": INBOX_TABPANEL_ID,
|
|
256
|
+
tabIndex: tab === tk.key ? 0 : -1,
|
|
257
|
+
className: `${s.tab} ${tab === tk.key ? s.tabActive : ""} ${isAlert ? s.tabAlert : ""}`,
|
|
258
|
+
onClick: () => {
|
|
259
|
+
setTab(tk.key);
|
|
260
|
+
setSelectedIdx(-1);
|
|
261
|
+
},
|
|
262
|
+
children: [
|
|
263
|
+
tk.label,
|
|
264
|
+
/* @__PURE__ */ jsx("span", { className: `${s.tabCount} ${isAlert ? s.tabCountAlert : ""}`, children: tk.count })
|
|
265
|
+
]
|
|
266
|
+
},
|
|
267
|
+
tk.key
|
|
268
|
+
);
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
),
|
|
246
272
|
/* @__PURE__ */ jsx("div", { className: s.sortRow, children: checkedIds.size > 0 ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center", flex: 1 }, children: [
|
|
247
273
|
/* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 600, color: "var(--theme-text)" }, children: checkedIds.size > 1 ? t("inbox.selectedPlural", { count: String(checkedIds.size) }) : t("inbox.selected", { count: String(checkedIds.size) }) }),
|
|
248
|
-
/* @__PURE__ */ jsx("button", { className: s.sortSelect, onClick: () => handleBulkAction("close"), disabled: bulkProcessing, children: t("inbox.closeAction") }),
|
|
249
|
-
/* @__PURE__ */ jsx("button", { className: s.sortSelect, onClick: () => handleBulkAction("reopen"), disabled: bulkProcessing, children: t("inbox.reopenAction") }),
|
|
250
|
-
/* @__PURE__ */ jsxs("select", { className: s.sortSelect, value: bulkAction, onChange: (e) => {
|
|
274
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: s.sortSelect, onClick: () => handleBulkAction("close"), disabled: bulkProcessing, children: t("inbox.closeAction") }),
|
|
275
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: s.sortSelect, onClick: () => handleBulkAction("reopen"), disabled: bulkProcessing, children: t("inbox.reopenAction") }),
|
|
276
|
+
/* @__PURE__ */ jsxs("select", { className: s.sortSelect, "aria-label": t("inbox.moreActions"), value: bulkAction, onChange: (e) => {
|
|
251
277
|
if (e.target.value) handleBulkAction(e.target.value);
|
|
252
278
|
setBulkAction("");
|
|
253
279
|
}, children: [
|
|
@@ -255,14 +281,14 @@ const TicketInboxClient = () => {
|
|
|
255
281
|
/* @__PURE__ */ jsx("option", { value: "set_priority", children: t("inbox.changePriority") }),
|
|
256
282
|
/* @__PURE__ */ jsx("option", { value: "delete", children: t("inbox.deleteAction") })
|
|
257
283
|
] }),
|
|
258
|
-
/* @__PURE__ */ jsx("button", { className: s.sortSelect, onClick: () => setCheckedIds(/* @__PURE__ */ new Set()), style: { marginLeft: "auto" }, children: t("inbox.deselect") })
|
|
259
|
-
] }) : /* @__PURE__ */ jsxs("select", { className: s.sortSelect, value: sort, onChange: (e) => setSort(e.target.value), children: [
|
|
284
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: s.sortSelect, onClick: () => setCheckedIds(/* @__PURE__ */ new Set()), style: { marginLeft: "auto" }, children: t("inbox.deselect") })
|
|
285
|
+
] }) : /* @__PURE__ */ jsxs("select", { className: s.sortSelect, "aria-label": t("inbox.sortLabel"), value: sort, onChange: (e) => setSort(e.target.value), children: [
|
|
260
286
|
/* @__PURE__ */ jsx("option", { value: "-updatedAt", children: t("inbox.sort.newest") }),
|
|
261
287
|
/* @__PURE__ */ jsx("option", { value: "updatedAt", children: t("inbox.sort.oldest") }),
|
|
262
288
|
/* @__PURE__ */ jsx("option", { value: "-createdAt", children: t("inbox.sort.created") }),
|
|
263
289
|
/* @__PURE__ */ jsx("option", { value: "priority", children: t("inbox.sort.priority") })
|
|
264
290
|
] }) }),
|
|
265
|
-
loading ? /* @__PURE__ */ jsx("div", { className: s.loading, children: t("common.loading") }) : tickets.length === 0 ? /* @__PURE__ */ jsxs("div", { className: s.empty, children: [
|
|
291
|
+
/* @__PURE__ */ jsx("div", { role: "tabpanel", id: INBOX_TABPANEL_ID, "aria-labelledby": `inbox-tab-${tab}`, children: loading ? /* @__PURE__ */ jsx("div", { className: s.loading, children: t("common.loading") }) : tickets.length === 0 ? /* @__PURE__ */ jsxs("div", { className: s.empty, children: [
|
|
266
292
|
/* @__PURE__ */ jsx("div", { className: s.emptyIcon, children: "--" }),
|
|
267
293
|
/* @__PURE__ */ jsx("div", { className: s.emptyText, children: t("inbox.empty") })
|
|
268
294
|
] }) : /* @__PURE__ */ jsx("div", { className: s.list, children: tickets.map((tk, idx) => {
|
|
@@ -333,7 +359,7 @@ const TicketInboxClient = () => {
|
|
|
333
359
|
},
|
|
334
360
|
tk.id
|
|
335
361
|
);
|
|
336
|
-
}) }),
|
|
362
|
+
}) }) }),
|
|
337
363
|
/* @__PURE__ */ jsxs("div", { className: s.keyboardHints, children: [
|
|
338
364
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
339
365
|
/* @__PURE__ */ jsx("kbd", { children: "\u2191" }),
|