@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.
Files changed (120) hide show
  1. package/README.md +249 -22
  2. package/dist/components/RichTextEditor/index.js +1 -1
  3. package/dist/components/TicketConversation/RewriteDropdown.js +1 -1
  4. package/dist/components/TicketConversation/components/AISummaryPanel.js +3 -0
  5. package/dist/components/TicketConversation/components/ActionPanels.js +13 -8
  6. package/dist/components/TicketConversation/components/ClientHistory.js +1 -0
  7. package/dist/components/TicketConversation/components/CodeBlock.js +1 -0
  8. package/dist/components/TicketConversation/components/CodeBlockInserter.js +1 -1
  9. package/dist/components/TicketConversation/components/QuickActions.js +10 -8
  10. package/dist/components/TicketConversation/components/TicketHeader.js +2 -0
  11. package/dist/components/TicketConversation/components/TimeTrackingPanel.js +15 -9
  12. package/dist/components/TicketConversation/index.d.ts +10 -0
  13. package/dist/components/TicketConversation/index.js +25 -7
  14. package/dist/index.cjs +306 -60
  15. package/dist/index.d.cts +107 -38
  16. package/dist/index.d.ts +107 -38
  17. package/dist/index.js +300 -59
  18. package/dist/styles/BillingView.module.scss +11 -11
  19. package/dist/styles/ChatView.module.scss +7 -7
  20. package/dist/styles/CommandPalette.module.scss +1 -1
  21. package/dist/styles/CrmView.module.scss +9 -9
  22. package/dist/styles/EmailTracking.module.scss +6 -6
  23. package/dist/styles/ImportConversation.module.scss +5 -5
  24. package/dist/styles/Logs.module.scss +5 -5
  25. package/dist/styles/NewTicket.module.scss +2 -2
  26. package/dist/styles/PendingEmails.module.scss +13 -13
  27. package/dist/styles/SupportDashboard.module.scss +24 -8
  28. package/dist/styles/TicketDetail.module.scss +20 -20
  29. package/dist/styles/TicketInbox.module.scss +6 -6
  30. package/dist/styles/TicketingSettings.module.scss +10 -10
  31. package/dist/styles/TimeDashboard.module.scss +6 -6
  32. package/dist/styles/_tokens.scss +3 -1
  33. package/dist/utils/db.js +20 -0
  34. package/dist/utils/readSettings.js +150 -0
  35. package/dist/views/BillingView/client.js +15 -8
  36. package/dist/views/ChatView/client.js +2 -0
  37. package/dist/views/CrmView/client.js +2 -0
  38. package/dist/views/EmailTrackingView/client.js +23 -11
  39. package/dist/views/ImportConversationView/client.js +1 -0
  40. package/dist/views/LogsView/client.js +1 -1
  41. package/dist/views/NewTicketView/client.js +20 -13
  42. package/dist/views/PendingEmailsView/client.js +9 -3
  43. package/dist/views/SupportDashboardView/client.js +17 -19
  44. package/dist/views/TicketDetailView/client.js +23 -11
  45. package/dist/views/TicketInboxView/client.js +52 -26
  46. package/dist/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
  47. package/dist/views/TicketingSettingsView/client.js +44 -26
  48. package/dist/views/TimeDashboardView/client.js +10 -7
  49. package/dist/views/shared/ErrorBoundary.d.ts +36 -1
  50. package/dist/views/shared/ErrorBoundary.js +59 -28
  51. package/dist/views/shared/adminTokens.d.ts +15 -5
  52. package/dist/views/shared/adminTokens.js +23 -7
  53. package/dist/views/shared/icons.d.ts +49 -0
  54. package/dist/views/shared/icons.js +92 -0
  55. package/dist/views/shared/locales/en.json +15 -1
  56. package/dist/views/shared/locales/fr.json +15 -1
  57. package/package.json +9 -4
  58. package/scripts/copy-subpath-types.mjs +103 -0
  59. package/scripts/uninstall-data.mjs +178 -0
  60. package/scripts/uninstall.mjs +184 -0
  61. package/scripts/verify-dist-imports.mjs +84 -0
  62. package/src/collections/Tickets.ts +7 -0
  63. package/src/collections/TimeEntries.ts +11 -3
  64. package/src/components/RichTextEditor/index.tsx +1 -1
  65. package/src/components/TicketConversation/RewriteDropdown.tsx +1 -1
  66. package/src/components/TicketConversation/components/AISummaryPanel.tsx +3 -0
  67. package/src/components/TicketConversation/components/ActionPanels.tsx +13 -8
  68. package/src/components/TicketConversation/components/ClientHistory.tsx +1 -0
  69. package/src/components/TicketConversation/components/CodeBlock.tsx +1 -0
  70. package/src/components/TicketConversation/components/CodeBlockInserter.tsx +1 -1
  71. package/src/components/TicketConversation/components/QuickActions.tsx +10 -8
  72. package/src/components/TicketConversation/components/TicketHeader.tsx +2 -0
  73. package/src/components/TicketConversation/components/TimeTrackingPanel.tsx +17 -9
  74. package/src/components/TicketConversation/index.tsx +41 -7
  75. package/src/endpoints/delete-account.ts +191 -47
  76. package/src/endpoints/export-data.ts +104 -4
  77. package/src/endpoints/purge-logs.ts +6 -15
  78. package/src/index.ts +2 -0
  79. package/src/plugin.ts +51 -1
  80. package/src/portal/LiveChat.tsx +1 -1
  81. package/src/portal/auth/profile/page.tsx +3 -2
  82. package/src/portal/icons.tsx +91 -0
  83. package/src/styles/BillingView.module.scss +11 -11
  84. package/src/styles/ChatView.module.scss +7 -7
  85. package/src/styles/CommandPalette.module.scss +1 -1
  86. package/src/styles/CrmView.module.scss +9 -9
  87. package/src/styles/EmailTracking.module.scss +6 -6
  88. package/src/styles/ImportConversation.module.scss +5 -5
  89. package/src/styles/Logs.module.scss +5 -5
  90. package/src/styles/NewTicket.module.scss +2 -2
  91. package/src/styles/PendingEmails.module.scss +13 -13
  92. package/src/styles/SupportDashboard.module.scss +24 -8
  93. package/src/styles/TicketDetail.module.scss +20 -20
  94. package/src/styles/TicketInbox.module.scss +6 -6
  95. package/src/styles/TicketingSettings.module.scss +10 -10
  96. package/src/styles/TimeDashboard.module.scss +6 -6
  97. package/src/styles/_tokens.scss +3 -1
  98. package/src/types.ts +20 -0
  99. package/src/utils/retention.ts +111 -0
  100. package/src/utils/slugs.ts +15 -0
  101. package/src/views/BillingView/client.tsx +17 -8
  102. package/src/views/ChatView/client.tsx +2 -0
  103. package/src/views/CrmView/client.tsx +2 -0
  104. package/src/views/EmailTrackingView/client.tsx +18 -7
  105. package/src/views/ImportConversationView/client.tsx +1 -0
  106. package/src/views/LogsView/client.tsx +1 -1
  107. package/src/views/NewTicketView/client.tsx +22 -13
  108. package/src/views/PendingEmailsView/client.tsx +9 -3
  109. package/src/views/SupportDashboardView/client.tsx +11 -5
  110. package/src/views/TicketDetailView/client.tsx +25 -11
  111. package/src/views/TicketInboxView/client.tsx +47 -7
  112. package/src/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
  113. package/src/views/TicketingSettingsView/client.tsx +201 -144
  114. package/src/views/TimeDashboardView/client.tsx +10 -7
  115. package/src/views/shared/ErrorBoundary.tsx +95 -31
  116. package/src/views/shared/adminTokens.ts +28 -11
  117. package/src/views/shared/icons.tsx +146 -0
  118. package/src/views/shared/locales/en.json +15 -1
  119. package/src/views/shared/locales/fr.json +15 -1
  120. 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", { onClick: () => log.status === "error" && log.errorMessage ? setExpandedRow(expandedRow === log.id ? null : log.id) : null, style: { cursor: log.status === "error" && log.errorMessage ? "pointer" : "default" }, children: [
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 && /* @__PURE__ */ jsx("button", { className: s.expandBtn, onClick: (e) => {
136
- e.stopPropagation();
137
- setExpandedRow(expandedRow === log.id ? null : log.id);
138
- }, children: expandedRow === log.id ? "\u25B4" : "\u25BE" })
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
- "Erreur",
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: "var(--theme-elevation-500)", alignSelf: "center" }, children: [
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
  };
@@ -132,6 +132,7 @@ function ImportConversationClient() {
132
132
  ref: fileRef,
133
133
  type: "file",
134
134
  accept: ".md,.txt",
135
+ "aria-label": t("import.acceptedFormats"),
135
136
  onChange: onFileChange,
136
137
  style: { display: "none" }
137
138
  }
@@ -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-500)" }, children: log.action || "\u2014" }),
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-500)" }, children: selectedClient.email }),
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 'lucide-react';
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) => /* @__PURE__ */ jsxs(
376
- "tr",
377
- {
378
- onClick: () => {
379
- window.location.href = `/admin/support/ticket?id=${tk.id}`;
380
- },
381
- 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: 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
- ]
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: "manual-time-input" }),
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("manual-time-input");
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-500)" }, children: [
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-500)" }, children: [
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("span", { style: { fontSize: 12 }, children: t("detail.billing.type") }),
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("span", { style: { fontSize: 12 }, children: t("detail.billing.flatAmount") }),
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("span", { style: { fontSize: 12 }, children: t("detail.billing.billedAmount") }),
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("span", { style: { fontSize: 12 }, children: t("detail.billing.payment") }),
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("div", { className: s.tabs, role: "tablist", children: tabs.map((tk) => {
227
- const isAlert = tk.alert && tk.count > 0;
228
- return /* @__PURE__ */ jsxs(
229
- "button",
230
- {
231
- role: "tab",
232
- "aria-selected": tab === tk.key,
233
- className: `${s.tab} ${tab === tk.key ? s.tabActive : ""} ${isAlert ? s.tabAlert : ""}`,
234
- onClick: () => {
235
- setTab(tk.key);
236
- setSelectedIdx(-1);
237
- },
238
- children: [
239
- tk.label,
240
- /* @__PURE__ */ jsx("span", { className: `${s.tabCount} ${isAlert ? s.tabCountAlert : ""}`, children: tk.count })
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.key
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" }),