@consilioweb/payload-support 4.0.0 → 6.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.
Files changed (159) hide show
  1. package/README.md +272 -35
  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 +421 -73
  15. package/dist/index.d.cts +127 -38
  16. package/dist/index.d.ts +127 -38
  17. package/dist/index.js +415 -72
  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.d.ts +34 -0
  34. package/dist/utils/readSettings.d.ts +90 -0
  35. package/dist/views/BillingView/client.js +15 -8
  36. package/dist/views/BillingView/index.js +4 -4
  37. package/dist/views/ChatView/client.js +2 -0
  38. package/dist/views/ChatView/index.js +4 -4
  39. package/dist/views/CrmView/client.js +2 -0
  40. package/dist/views/CrmView/index.js +4 -4
  41. package/dist/views/EmailTrackingView/client.js +23 -11
  42. package/dist/views/EmailTrackingView/index.js +4 -4
  43. package/dist/views/ImportConversationView/client.js +1 -0
  44. package/dist/views/ImportConversationView/index.js +4 -4
  45. package/dist/views/LogsView/client.js +1 -1
  46. package/dist/views/LogsView/index.js +4 -2
  47. package/dist/views/NewTicketView/client.js +20 -13
  48. package/dist/views/NewTicketView/index.js +4 -2
  49. package/dist/views/PendingEmailsView/client.js +9 -3
  50. package/dist/views/PendingEmailsView/index.js +4 -4
  51. package/dist/views/SupportDashboardView/client.js +17 -19
  52. package/dist/views/SupportDashboardView/index.js +4 -4
  53. package/dist/views/TicketDetailView/client.js +23 -11
  54. package/dist/views/TicketDetailView/index.js +4 -4
  55. package/dist/views/TicketInboxView/client.js +52 -26
  56. package/dist/views/TicketInboxView/index.js +4 -2
  57. package/dist/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
  58. package/dist/views/TicketingSettingsView/client.js +44 -26
  59. package/dist/views/TicketingSettingsView/index.js +4 -4
  60. package/dist/views/TimeDashboardView/client.js +10 -7
  61. package/dist/views/TimeDashboardView/index.js +4 -4
  62. package/dist/views/shared/ErrorBoundary.d.ts +36 -1
  63. package/dist/views/shared/ErrorBoundary.js +59 -28
  64. package/dist/views/shared/adminTokens.d.ts +15 -5
  65. package/dist/views/shared/adminTokens.js +23 -7
  66. package/dist/views/shared/icons.d.ts +49 -0
  67. package/dist/views/shared/icons.js +92 -0
  68. package/dist/views/shared/locales/en.json +15 -1
  69. package/dist/views/shared/locales/fr.json +15 -1
  70. package/dist/views/shared/viewAccess.d.ts +29 -0
  71. package/dist/views/shared/viewAccess.js +24 -0
  72. package/package.json +31 -21
  73. package/scripts/uninstall-data.mjs +178 -0
  74. package/scripts/uninstall.mjs +184 -0
  75. package/src/collections/Tickets.ts +7 -0
  76. package/src/collections/TimeEntries.ts +11 -3
  77. package/src/components/RichTextEditor/index.tsx +1 -1
  78. package/src/components/TicketConversation/RewriteDropdown.tsx +1 -1
  79. package/src/components/TicketConversation/components/AISummaryPanel.tsx +3 -0
  80. package/src/components/TicketConversation/components/ActionPanels.tsx +13 -8
  81. package/src/components/TicketConversation/components/ClientHistory.tsx +1 -0
  82. package/src/components/TicketConversation/components/CodeBlock.tsx +1 -0
  83. package/src/components/TicketConversation/components/CodeBlockInserter.tsx +1 -1
  84. package/src/components/TicketConversation/components/QuickActions.tsx +10 -8
  85. package/src/components/TicketConversation/components/TicketHeader.tsx +2 -0
  86. package/src/components/TicketConversation/components/TimeTrackingPanel.tsx +17 -9
  87. package/src/components/TicketConversation/index.tsx +41 -7
  88. package/src/endpoints/auth-2fa.ts +53 -8
  89. package/src/endpoints/capabilities.ts +2 -4
  90. package/src/endpoints/chatbot.ts +2 -2
  91. package/src/endpoints/delete-account.ts +191 -47
  92. package/src/endpoints/export-data.ts +104 -4
  93. package/src/endpoints/import-conversation.ts +2 -2
  94. package/src/endpoints/login.ts +13 -3
  95. package/src/endpoints/purge-logs.ts +6 -15
  96. package/src/endpoints/push.ts +14 -1
  97. package/src/endpoints/statuses.ts +17 -0
  98. package/src/index.ts +2 -0
  99. package/src/plugin.ts +51 -1
  100. package/src/portal/LiveChat.tsx +1 -1
  101. package/src/portal/auth/profile/page.tsx +3 -2
  102. package/src/portal/icons.tsx +91 -0
  103. package/src/portal/login/page.tsx +14 -4
  104. package/src/styles/BillingView.module.scss +11 -11
  105. package/src/styles/ChatView.module.scss +7 -7
  106. package/src/styles/CommandPalette.module.scss +1 -1
  107. package/src/styles/CrmView.module.scss +9 -9
  108. package/src/styles/EmailTracking.module.scss +6 -6
  109. package/src/styles/ImportConversation.module.scss +5 -5
  110. package/src/styles/Logs.module.scss +5 -5
  111. package/src/styles/NewTicket.module.scss +2 -2
  112. package/src/styles/PendingEmails.module.scss +13 -13
  113. package/src/styles/SupportDashboard.module.scss +24 -8
  114. package/src/styles/TicketDetail.module.scss +20 -20
  115. package/src/styles/TicketInbox.module.scss +6 -6
  116. package/src/styles/TicketingSettings.module.scss +10 -10
  117. package/src/styles/TimeDashboard.module.scss +6 -6
  118. package/src/styles/_tokens.scss +3 -1
  119. package/src/types.ts +20 -0
  120. package/src/utils/push.ts +22 -0
  121. package/src/utils/rateLimiter.ts +98 -0
  122. package/src/utils/retention.ts +111 -0
  123. package/src/utils/slugs.ts +15 -0
  124. package/src/utils/twoFactorChallenge.ts +6 -1
  125. package/src/utils/urlSafety.ts +36 -1
  126. package/src/views/BillingView/client.tsx +17 -8
  127. package/src/views/BillingView/index.tsx +4 -4
  128. package/src/views/ChatView/client.tsx +2 -0
  129. package/src/views/ChatView/index.tsx +4 -4
  130. package/src/views/CrmView/client.tsx +2 -0
  131. package/src/views/CrmView/index.tsx +4 -4
  132. package/src/views/EmailTrackingView/client.tsx +18 -7
  133. package/src/views/EmailTrackingView/index.tsx +4 -4
  134. package/src/views/ImportConversationView/client.tsx +1 -0
  135. package/src/views/ImportConversationView/index.tsx +4 -4
  136. package/src/views/LogsView/client.tsx +1 -1
  137. package/src/views/LogsView/index.tsx +4 -2
  138. package/src/views/NewTicketView/client.tsx +22 -13
  139. package/src/views/NewTicketView/index.tsx +4 -2
  140. package/src/views/PendingEmailsView/client.tsx +9 -3
  141. package/src/views/PendingEmailsView/index.tsx +4 -4
  142. package/src/views/SupportDashboardView/client.tsx +11 -5
  143. package/src/views/SupportDashboardView/index.tsx +4 -4
  144. package/src/views/TicketDetailView/client.tsx +25 -11
  145. package/src/views/TicketDetailView/index.tsx +4 -4
  146. package/src/views/TicketInboxView/client.tsx +47 -7
  147. package/src/views/TicketInboxView/index.tsx +4 -2
  148. package/src/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
  149. package/src/views/TicketingSettingsView/client.tsx +201 -144
  150. package/src/views/TicketingSettingsView/index.tsx +4 -4
  151. package/src/views/TimeDashboardView/client.tsx +10 -7
  152. package/src/views/TimeDashboardView/index.tsx +4 -4
  153. package/src/views/shared/ErrorBoundary.tsx +95 -31
  154. package/src/views/shared/adminTokens.ts +28 -11
  155. package/src/views/shared/icons.tsx +146 -0
  156. package/src/views/shared/locales/en.json +15 -1
  157. package/src/views/shared/locales/fr.json +15 -1
  158. package/src/views/shared/viewAccess.ts +73 -0
  159. package/src/types/lucide-react.d.ts +0 -42
@@ -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) => {
@@ -1,14 +1,14 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { DefaultTemplate } from '@payloadcms/next/templates';
3
3
  import { redirect } from 'next/navigation';
4
+ import { supportViewRedirectTarget } from '../shared/viewAccess.js';
4
5
  import { AdminErrorBoundary } from '../shared/ErrorBoundary.js';
5
6
  import { TicketDetailClient } from './client.js';
6
7
 
7
8
  const TicketDetailView = ({ initPageResult }) => {
8
9
  const { req, visibleEntities } = initPageResult;
9
- if (!req.user) {
10
- redirect("/admin/login");
11
- }
10
+ const redirectTo = supportViewRedirectTarget(initPageResult);
11
+ if (redirectTo) redirect(redirectTo);
12
12
  return /* @__PURE__ */ jsx(
13
13
  DefaultTemplate,
14
14
  {
@@ -18,7 +18,7 @@ const TicketDetailView = ({ initPageResult }) => {
18
18
  payload: req.payload,
19
19
  permissions: initPageResult.permissions,
20
20
  searchParams: {},
21
- user: req.user,
21
+ user: req.user ?? void 0,
22
22
  visibleEntities,
23
23
  children: /* @__PURE__ */ jsx(AdminErrorBoundary, { viewName: "TicketDetailView", children: /* @__PURE__ */ jsx(TicketDetailClient, {}) })
24
24
  }
@@ -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" }),
@@ -1,12 +1,14 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { DefaultTemplate } from '@payloadcms/next/templates';
3
3
  import { redirect } from 'next/navigation';
4
+ import { supportViewRedirectTarget } from '../shared/viewAccess.js';
4
5
  import { AdminErrorBoundary } from '../shared/ErrorBoundary.js';
5
6
  import { TicketInboxClient } from './client.js';
6
7
 
7
8
  const TicketInboxView = ({ initPageResult }) => {
8
9
  const { req, visibleEntities } = initPageResult;
9
- if (!req.user) redirect("/admin/login");
10
+ const redirectTo = supportViewRedirectTarget(initPageResult);
11
+ if (redirectTo) redirect(redirectTo);
10
12
  return /* @__PURE__ */ jsx(
11
13
  DefaultTemplate,
12
14
  {
@@ -16,7 +18,7 @@ const TicketInboxView = ({ initPageResult }) => {
16
18
  payload: req.payload,
17
19
  permissions: initPageResult.permissions,
18
20
  searchParams: {},
19
- user: req.user,
21
+ user: req.user ?? void 0,
20
22
  visibleEntities,
21
23
  children: /* @__PURE__ */ jsx(AdminErrorBoundary, { viewName: "TicketInboxView", children: /* @__PURE__ */ jsx(TicketInboxClient, {}) })
22
24
  }
@@ -70,7 +70,7 @@
70
70
 
71
71
  .sectionDescription {
72
72
  font-size: 0.8125rem;
73
- color: var(--theme-elevation-500);
73
+ color: var(--theme-elevation-650);
74
74
  margin: 0 0 1rem;
75
75
  line-height: 1.5;
76
76
  }
@@ -143,7 +143,7 @@
143
143
 
144
144
  .featureDesc {
145
145
  font-size: 0.75rem;
146
- color: var(--theme-elevation-500);
146
+ color: var(--theme-elevation-650);
147
147
  margin-top: 0.125rem;
148
148
  }
149
149
 
@@ -166,7 +166,7 @@
166
166
 
167
167
  .fieldDescription {
168
168
  font-size: 0.75rem;
169
- color: var(--theme-elevation-500);
169
+ color: var(--theme-elevation-650);
170
170
  font-weight: 400;
171
171
  margin-top: 0.125rem;
172
172
  }
@@ -194,7 +194,7 @@
194
194
  .inputReadonly {
195
195
  composes: input;
196
196
  background: var(--theme-elevation-50);
197
- color: var(--theme-elevation-500);
197
+ color: var(--theme-elevation-650);
198
198
  cursor: not-allowed;
199
199
  }
200
200
 
@@ -230,7 +230,7 @@
230
230
 
231
231
  .inlineDesc {
232
232
  font-size: 0.75rem;
233
- color: var(--theme-elevation-500);
233
+ color: var(--theme-elevation-650);
234
234
  margin-top: 0.125rem;
235
235
  }
236
236
 
@@ -244,7 +244,7 @@
244
244
 
245
245
  .slaHint {
246
246
  font-size: 0.75rem;
247
- color: var(--theme-elevation-500);
247
+ color: var(--theme-elevation-650);
248
248
  }
249
249
 
250
250
  // ── API Key ───────────────────────────────────
@@ -290,7 +290,7 @@
290
290
  .aiToggleDesc {
291
291
  display: block;
292
292
  font-size: 0.6875rem;
293
- color: var(--theme-elevation-500);
293
+ color: var(--theme-elevation-650);
294
294
  }
295
295
 
296
296
  // ── Signature ─────────────────────────────────
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
- import React, { useState, useEffect } from 'react';
4
- import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from 'lucide-react';
3
+ import React, { useState, useEffect, useId } from 'react';
4
+ import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from '../shared/icons.js';
5
5
  import { btnStyle, V } from '../shared/adminTokens.js';
6
6
  import { AdminViewHeader } from '../shared/AdminViewHeader.js';
7
7
  import { DEFAULT_FEATURES } from '../../components/TicketConversation/config.js';
@@ -196,13 +196,16 @@ const CollapsibleSection = ({ title, icon, color, defaultOpen = true, children,
196
196
  open && /* @__PURE__ */ jsx("div", { className: ts.sectionBody, children })
197
197
  ] });
198
198
  };
199
- const FieldRow = ({ label, description, children }) => /* @__PURE__ */ jsxs("div", { className: ts.fieldRow, children: [
200
- /* @__PURE__ */ jsxs("div", { className: ts.fieldLabel, children: [
201
- label,
202
- description && /* @__PURE__ */ jsx("div", { className: ts.fieldDescription, children: description })
203
- ] }),
204
- /* @__PURE__ */ jsx("div", { className: ts.fieldContent, children })
205
- ] });
199
+ const FieldRow = ({ label, description, children }) => {
200
+ const id = useId();
201
+ return /* @__PURE__ */ jsxs("div", { className: ts.fieldRow, children: [
202
+ /* @__PURE__ */ jsxs("div", { className: ts.fieldLabel, children: [
203
+ /* @__PURE__ */ jsx("label", { htmlFor: id, children: label }),
204
+ description && /* @__PURE__ */ jsx("div", { className: ts.fieldDescription, children: description })
205
+ ] }),
206
+ /* @__PURE__ */ jsx("div", { className: ts.fieldContent, children: typeof children === "function" ? children(id) : children })
207
+ ] });
208
+ };
206
209
  const TicketingSettingsClient = () => {
207
210
  const { t } = useTranslation();
208
211
  const FEATURE_LIST = React.useMemo(() => getFeatureList(t), [t]);
@@ -283,7 +286,7 @@ const TicketingSettingsClient = () => {
283
286
  title: t("settingsView.configTitle"),
284
287
  subtitle: t("settings.subtitle", { enabled: String(enabledCount), total: String(totalCount) }),
285
288
  actions: /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
286
- /* @__PURE__ */ jsx("button", { onClick: handleReset, style: btnStyle("var(--theme-elevation-400)", { small: true }), children: t("settingsView.reset") }),
289
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: handleReset, style: btnStyle(V.neutralBg, { small: true, fg: V.neutralFg }), children: t("settingsView.reset") }),
287
290
  /* @__PURE__ */ jsx(
288
291
  "button",
289
292
  {
@@ -358,9 +361,10 @@ const TicketingSettingsClient = () => {
358
361
  defaultOpen: false,
359
362
  children: [
360
363
  /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.emailDescription") }),
361
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderAddress"), description: t("settingsView.senderAddressDesc"), children: /* @__PURE__ */ jsx(
364
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderAddress"), description: t("settingsView.senderAddressDesc"), children: (id) => /* @__PURE__ */ jsx(
362
365
  "input",
363
366
  {
367
+ id,
364
368
  type: "email",
365
369
  value: settings.email.fromAddress,
366
370
  onChange: (e) => updateEmail("fromAddress", e.target.value),
@@ -368,9 +372,10 @@ const TicketingSettingsClient = () => {
368
372
  className: ts.input
369
373
  }
370
374
  ) }),
371
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderName"), description: t("settingsView.senderNameDesc"), children: /* @__PURE__ */ jsx(
375
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderName"), description: t("settingsView.senderNameDesc"), children: (id) => /* @__PURE__ */ jsx(
372
376
  "input",
373
377
  {
378
+ id,
374
379
  type: "text",
375
380
  value: settings.email.fromName,
376
381
  onChange: (e) => updateEmail("fromName", e.target.value),
@@ -378,9 +383,10 @@ const TicketingSettingsClient = () => {
378
383
  className: ts.input
379
384
  }
380
385
  ) }),
381
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.replyTo"), description: t("settingsView.replyToDesc"), children: /* @__PURE__ */ jsx(
386
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.replyTo"), description: t("settingsView.replyToDesc"), children: (id) => /* @__PURE__ */ jsx(
382
387
  "input",
383
388
  {
389
+ id,
384
390
  type: "email",
385
391
  value: settings.email.replyToAddress,
386
392
  onChange: (e) => updateEmail("replyToAddress", e.target.value),
@@ -389,18 +395,20 @@ const TicketingSettingsClient = () => {
389
395
  }
390
396
  ) }),
391
397
  /* @__PURE__ */ jsx("div", { className: ts.separator }),
392
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpServer"), description: t("settingsView.smtpServerDesc"), children: /* @__PURE__ */ jsx(
398
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpServer"), description: t("settingsView.smtpServerDesc"), children: (id) => /* @__PURE__ */ jsx(
393
399
  "input",
394
400
  {
401
+ id,
395
402
  type: "text",
396
403
  value: smtpHost,
397
404
  readOnly: true,
398
405
  className: ts.inputReadonly
399
406
  }
400
407
  ) }),
401
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpPort"), children: /* @__PURE__ */ jsx(
408
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpPort"), children: (id) => /* @__PURE__ */ jsx(
402
409
  "input",
403
410
  {
411
+ id,
404
412
  type: "text",
405
413
  value: smtpPort,
406
414
  readOnly: true,
@@ -421,9 +429,10 @@ const TicketingSettingsClient = () => {
421
429
  badge: features.ai ? /* @__PURE__ */ jsx("span", { className: ts.badge, style: { backgroundColor: "#dcfce7", color: "#166534" }, children: t("settingsView.aiBadgeActive") }) : /* @__PURE__ */ jsx("span", { className: ts.badge, style: { backgroundColor: "#fee2e2", color: "#991b1b" }, children: t("settingsView.aiBadgeInactive") }),
422
430
  children: [
423
431
  /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.aiDescription") }),
424
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiProvider"), description: t("settingsView.aiProviderDesc"), children: /* @__PURE__ */ jsxs(
432
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiProvider"), description: t("settingsView.aiProviderDesc"), children: (id) => /* @__PURE__ */ jsxs(
425
433
  "select",
426
434
  {
435
+ id,
427
436
  value: settings.ai.provider,
428
437
  onChange: (e) => updateAI("provider", e.target.value),
429
438
  className: ts.select,
@@ -435,10 +444,11 @@ const TicketingSettingsClient = () => {
435
444
  ]
436
445
  }
437
446
  ) }),
438
- settings.ai.provider !== "ollama" && /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiApiKey"), description: t("settingsView.aiApiKeyDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.apiKeyRow, children: [
447
+ settings.ai.provider !== "ollama" && /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiApiKey"), description: t("settingsView.aiApiKeyDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.apiKeyRow, children: [
439
448
  /* @__PURE__ */ jsx(
440
449
  "input",
441
450
  {
451
+ id,
442
452
  type: showApiKey ? "text" : "password",
443
453
  value: settings.ai.apiKey,
444
454
  onChange: (e) => updateAI("apiKey", e.target.value),
@@ -456,9 +466,10 @@ const TicketingSettingsClient = () => {
456
466
  }
457
467
  )
458
468
  ] }) }),
459
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiModel"), description: t("settingsView.aiModelDesc"), children: /* @__PURE__ */ jsx(
469
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiModel"), description: t("settingsView.aiModelDesc"), children: (id) => /* @__PURE__ */ jsx(
460
470
  "input",
461
471
  {
472
+ id,
462
473
  type: "text",
463
474
  value: settings.ai.model,
464
475
  onChange: (e) => updateAI("model", e.target.value),
@@ -500,10 +511,11 @@ const TicketingSettingsClient = () => {
500
511
  defaultOpen: false,
501
512
  children: [
502
513
  /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.slaDescription") }),
503
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaFirstResponse"), description: t("settingsView.slaFirstResponseDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
514
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaFirstResponse"), description: t("settingsView.slaFirstResponseDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
504
515
  /* @__PURE__ */ jsx(
505
516
  "input",
506
517
  {
518
+ id,
507
519
  type: "number",
508
520
  min: 1,
509
521
  value: settings.sla.firstResponseMinutes,
@@ -520,10 +532,11 @@ const TicketingSettingsClient = () => {
520
532
  ")"
521
533
  ] })
522
534
  ] }) }),
523
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaResolution"), description: t("settingsView.slaResolutionDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
535
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaResolution"), description: t("settingsView.slaResolutionDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
524
536
  /* @__PURE__ */ jsx(
525
537
  "input",
526
538
  {
539
+ id,
527
540
  type: "number",
528
541
  min: 1,
529
542
  value: settings.sla.resolutionMinutes,
@@ -556,9 +569,10 @@ const TicketingSettingsClient = () => {
556
569
  ] })
557
570
  ] }),
558
571
  /* @__PURE__ */ jsx("div", { className: ts.separator }),
559
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.escalationEmail"), description: t("settingsView.escalationEmailDesc"), children: /* @__PURE__ */ jsx(
572
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.escalationEmail"), description: t("settingsView.escalationEmailDesc"), children: (id) => /* @__PURE__ */ jsx(
560
573
  "input",
561
574
  {
575
+ id,
562
576
  type: "email",
563
577
  value: settings.sla.escalationEmail,
564
578
  onChange: (e) => updateSLA("escalationEmail", e.target.value),
@@ -594,10 +608,11 @@ const TicketingSettingsClient = () => {
594
608
  /* @__PURE__ */ jsx("span", { className: ts.inlineLabel, children: t("settingsView.autoCloseEnable") })
595
609
  ] }),
596
610
  settings.autoClose.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
597
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseDelay"), description: t("settingsView.autoCloseDelayDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
611
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseDelay"), description: t("settingsView.autoCloseDelayDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
598
612
  /* @__PURE__ */ jsx(
599
613
  "input",
600
614
  {
615
+ id,
601
616
  type: "number",
602
617
  min: 1,
603
618
  max: 90,
@@ -608,10 +623,11 @@ const TicketingSettingsClient = () => {
608
623
  ),
609
624
  /* @__PURE__ */ jsx("span", { className: ts.slaHint, children: t("settingsView.days") })
610
625
  ] }) }),
611
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseReminder"), description: t("settingsView.autoCloseReminderDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
626
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseReminder"), description: t("settingsView.autoCloseReminderDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
612
627
  /* @__PURE__ */ jsx(
613
628
  "input",
614
629
  {
630
+ id,
615
631
  type: "number",
616
632
  min: 1,
617
633
  max: settings.autoClose.daysBeforeClose - 1,
@@ -646,9 +662,10 @@ const TicketingSettingsClient = () => {
646
662
  defaultOpen: false,
647
663
  children: [
648
664
  /* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.localeDescription") }),
649
- /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.language"), description: t("settingsView.languageDesc"), children: /* @__PURE__ */ jsxs(
665
+ /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.language"), description: t("settingsView.languageDesc"), children: (id) => /* @__PURE__ */ jsxs(
650
666
  "select",
651
667
  {
668
+ id,
652
669
  value: settings.locale.language,
653
670
  onChange: (e) => updateLocale("language", e.target.value),
654
671
  className: ts.select,
@@ -673,6 +690,7 @@ const TicketingSettingsClient = () => {
673
690
  /* @__PURE__ */ jsx(
674
691
  "textarea",
675
692
  {
693
+ "aria-label": t("settingsView.signatureTitle"),
676
694
  value: signature,
677
695
  onChange: (e) => {
678
696
  setSignature(e.target.value);
@@ -710,7 +728,7 @@ const TicketingSettingsClient = () => {
710
728
  alert(t("import.errorTitle"));
711
729
  }
712
730
  },
713
- style: { ...btnStyle(opt.days === 0 ? "#ef4444" : "var(--theme-elevation-500)", { small: true }), fontSize: 11 },
731
+ style: { ...btnStyle(opt.days === 0 ? V.red : V.neutralBg, { small: true, fg: opt.days === 0 ? void 0 : V.neutralFg }), fontSize: 11 },
714
732
  children: opt.label
715
733
  },
716
734
  opt.days
@@ -718,7 +736,7 @@ const TicketingSettingsClient = () => {
718
736
  ] }, col)) })
719
737
  ] }),
720
738
  /* @__PURE__ */ jsxs("div", { className: ts.bottomBar, children: [
721
- /* @__PURE__ */ jsx("button", { onClick: handleReset, style: btnStyle("var(--theme-elevation-400)", { small: true }), children: t("settingsView.resetAll") }),
739
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: handleReset, style: btnStyle(V.neutralBg, { small: true, fg: V.neutralFg }), children: t("settingsView.resetAll") }),
722
740
  /* @__PURE__ */ jsx(
723
741
  "button",
724
742
  {