@consilioweb/payload-support 5.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 (116) 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/views/BillingView/client.js +15 -8
  34. package/dist/views/ChatView/client.js +2 -0
  35. package/dist/views/CrmView/client.js +2 -0
  36. package/dist/views/EmailTrackingView/client.js +23 -11
  37. package/dist/views/ImportConversationView/client.js +1 -0
  38. package/dist/views/LogsView/client.js +1 -1
  39. package/dist/views/NewTicketView/client.js +20 -13
  40. package/dist/views/PendingEmailsView/client.js +9 -3
  41. package/dist/views/SupportDashboardView/client.js +17 -19
  42. package/dist/views/TicketDetailView/client.js +23 -11
  43. package/dist/views/TicketInboxView/client.js +52 -26
  44. package/dist/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
  45. package/dist/views/TicketingSettingsView/client.js +44 -26
  46. package/dist/views/TimeDashboardView/client.js +10 -7
  47. package/dist/views/shared/ErrorBoundary.d.ts +36 -1
  48. package/dist/views/shared/ErrorBoundary.js +59 -28
  49. package/dist/views/shared/adminTokens.d.ts +15 -5
  50. package/dist/views/shared/adminTokens.js +23 -7
  51. package/dist/views/shared/icons.d.ts +49 -0
  52. package/dist/views/shared/icons.js +92 -0
  53. package/dist/views/shared/locales/en.json +15 -1
  54. package/dist/views/shared/locales/fr.json +15 -1
  55. package/package.json +6 -2
  56. package/scripts/uninstall-data.mjs +178 -0
  57. package/scripts/uninstall.mjs +184 -0
  58. package/src/collections/Tickets.ts +7 -0
  59. package/src/collections/TimeEntries.ts +11 -3
  60. package/src/components/RichTextEditor/index.tsx +1 -1
  61. package/src/components/TicketConversation/RewriteDropdown.tsx +1 -1
  62. package/src/components/TicketConversation/components/AISummaryPanel.tsx +3 -0
  63. package/src/components/TicketConversation/components/ActionPanels.tsx +13 -8
  64. package/src/components/TicketConversation/components/ClientHistory.tsx +1 -0
  65. package/src/components/TicketConversation/components/CodeBlock.tsx +1 -0
  66. package/src/components/TicketConversation/components/CodeBlockInserter.tsx +1 -1
  67. package/src/components/TicketConversation/components/QuickActions.tsx +10 -8
  68. package/src/components/TicketConversation/components/TicketHeader.tsx +2 -0
  69. package/src/components/TicketConversation/components/TimeTrackingPanel.tsx +17 -9
  70. package/src/components/TicketConversation/index.tsx +41 -7
  71. package/src/endpoints/delete-account.ts +191 -47
  72. package/src/endpoints/export-data.ts +104 -4
  73. package/src/endpoints/purge-logs.ts +6 -15
  74. package/src/index.ts +2 -0
  75. package/src/plugin.ts +51 -1
  76. package/src/portal/LiveChat.tsx +1 -1
  77. package/src/portal/auth/profile/page.tsx +3 -2
  78. package/src/portal/icons.tsx +91 -0
  79. package/src/styles/BillingView.module.scss +11 -11
  80. package/src/styles/ChatView.module.scss +7 -7
  81. package/src/styles/CommandPalette.module.scss +1 -1
  82. package/src/styles/CrmView.module.scss +9 -9
  83. package/src/styles/EmailTracking.module.scss +6 -6
  84. package/src/styles/ImportConversation.module.scss +5 -5
  85. package/src/styles/Logs.module.scss +5 -5
  86. package/src/styles/NewTicket.module.scss +2 -2
  87. package/src/styles/PendingEmails.module.scss +13 -13
  88. package/src/styles/SupportDashboard.module.scss +24 -8
  89. package/src/styles/TicketDetail.module.scss +20 -20
  90. package/src/styles/TicketInbox.module.scss +6 -6
  91. package/src/styles/TicketingSettings.module.scss +10 -10
  92. package/src/styles/TimeDashboard.module.scss +6 -6
  93. package/src/styles/_tokens.scss +3 -1
  94. package/src/types.ts +20 -0
  95. package/src/utils/retention.ts +111 -0
  96. package/src/utils/slugs.ts +15 -0
  97. package/src/views/BillingView/client.tsx +17 -8
  98. package/src/views/ChatView/client.tsx +2 -0
  99. package/src/views/CrmView/client.tsx +2 -0
  100. package/src/views/EmailTrackingView/client.tsx +18 -7
  101. package/src/views/ImportConversationView/client.tsx +1 -0
  102. package/src/views/LogsView/client.tsx +1 -1
  103. package/src/views/NewTicketView/client.tsx +22 -13
  104. package/src/views/PendingEmailsView/client.tsx +9 -3
  105. package/src/views/SupportDashboardView/client.tsx +11 -5
  106. package/src/views/TicketDetailView/client.tsx +25 -11
  107. package/src/views/TicketInboxView/client.tsx +47 -7
  108. package/src/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
  109. package/src/views/TicketingSettingsView/client.tsx +201 -144
  110. package/src/views/TimeDashboardView/client.tsx +10 -7
  111. package/src/views/shared/ErrorBoundary.tsx +95 -31
  112. package/src/views/shared/adminTokens.ts +28 -11
  113. package/src/views/shared/icons.tsx +146 -0
  114. package/src/views/shared/locales/en.json +15 -1
  115. package/src/views/shared/locales/fr.json +15 -1
  116. package/src/types/lucide-react.d.ts +0 -42
@@ -52,7 +52,7 @@
52
52
  font-family: ui-monospace, 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
53
53
  font-size: 12px;
54
54
  font-weight: 400;
55
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
55
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
56
56
  display: inline-flex;
57
57
  align-items: center;
58
58
  gap: 8px;
@@ -64,7 +64,7 @@
64
64
 
65
65
  > .ticketNumberCat,
66
66
  > .ticketNumberSource {
67
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
67
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
68
68
  display: inline-flex;
69
69
  align-items: center;
70
70
  gap: 4px;
@@ -194,7 +194,7 @@
194
194
  text-transform: uppercase;
195
195
  letter-spacing: 0.08em;
196
196
  line-height: 1.3;
197
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
197
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
198
198
  margin: 0 0 12px;
199
199
  display: flex;
200
200
  justify-content: space-between;
@@ -353,7 +353,7 @@
353
353
  font-size: 11px;
354
354
  font-weight: 500;
355
355
  line-height: 1.3;
356
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
356
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
357
357
  white-space: nowrap;
358
358
  text-transform: uppercase;
359
359
  letter-spacing: 0.08em;
@@ -450,7 +450,7 @@
450
450
  font-size: 11px;
451
451
  font-weight: 500;
452
452
  line-height: 1.3;
453
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
453
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
454
454
  }
455
455
 
456
456
  // Pills "Interne" / "Solution" — pill style, 22px height
@@ -499,7 +499,7 @@
499
499
  margin-top: 4px;
500
500
  font-size: 11px;
501
501
  font-weight: 500;
502
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
502
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
503
503
  opacity: 0;
504
504
  transition: opacity 80ms ease;
505
505
  }
@@ -514,7 +514,7 @@
514
514
  font-size: 11px;
515
515
  font-weight: 500;
516
516
  line-height: 1.3;
517
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
517
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
518
518
  display: inline-flex;
519
519
  align-items: center;
520
520
  height: auto;
@@ -749,7 +749,7 @@
749
749
  display: inline-flex;
750
750
  align-items: center;
751
751
  gap: 4px;
752
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
752
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
753
753
  font-size: 11px;
754
754
  font-weight: 500;
755
755
  margin-left: auto;
@@ -910,7 +910,7 @@
910
910
  background: none;
911
911
  border: none;
912
912
  cursor: pointer;
913
- color: var(--theme-elevation-500);
913
+ color: var(--theme-elevation-650);
914
914
  font-size: 13px;
915
915
  font-weight: 600;
916
916
  padding: 0;
@@ -941,7 +941,7 @@
941
941
 
942
942
  .activityContent { flex: 1; min-width: 0; }
943
943
  .activityText { font-weight: 500; color: var(--theme-text); font-size: 12px; }
944
- .activityTime { font-size: 11px; color: var(--theme-elevation-500); margin-top: 1px; }
944
+ .activityTime { font-size: 11px; color: var(--theme-elevation-650); margin-top: 1px; }
945
945
 
946
946
  // ─── DROPDOWN ───
947
947
  .dropdown { position: relative; display: inline-block; }
@@ -1055,7 +1055,7 @@
1055
1055
 
1056
1056
  .splitPreview {
1057
1057
  font-size: 12px;
1058
- color: var(--theme-elevation-500);
1058
+ color: var(--theme-elevation-650);
1059
1059
  margin: 0 0 16px;
1060
1060
  line-height: 1.5;
1061
1061
  max-height: 80px;
@@ -1092,7 +1092,7 @@
1092
1092
  background: none;
1093
1093
  font-size: 13px;
1094
1094
  font-weight: 600;
1095
- color: var(--theme-elevation-500);
1095
+ color: var(--theme-elevation-650);
1096
1096
  cursor: pointer;
1097
1097
  &:hover { background: var(--theme-elevation-100); }
1098
1098
  }
@@ -1145,7 +1145,7 @@
1145
1145
  cursor: pointer;
1146
1146
  font-size: 11px;
1147
1147
  line-height: 1;
1148
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
1148
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
1149
1149
  padding: 0;
1150
1150
  display: inline-flex;
1151
1151
  align-items: center;
@@ -1166,7 +1166,7 @@
1166
1166
  font-size: 11px;
1167
1167
  font-weight: 500;
1168
1168
  line-height: 1;
1169
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
1169
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
1170
1170
  cursor: pointer;
1171
1171
  transition: border-color 80ms ease, color 80ms ease;
1172
1172
  &:hover {
@@ -1607,7 +1607,7 @@
1607
1607
  align-items: center;
1608
1608
  gap: 6px;
1609
1609
  font-size: 10px;
1610
- color: var(--theme-elevation-500);
1610
+ color: var(--theme-elevation-650);
1611
1611
  }
1612
1612
 
1613
1613
  .previousTicketsStatus {
@@ -1755,7 +1755,7 @@
1755
1755
  text-transform: uppercase;
1756
1756
  letter-spacing: 0.08em;
1757
1757
  line-height: 1.3;
1758
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
1758
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
1759
1759
  margin: 12px 0 8px;
1760
1760
  }
1761
1761
  .aiCardNextActions {
@@ -1805,13 +1805,13 @@
1805
1805
  }
1806
1806
  .aiCardNextLabelDone {
1807
1807
  text-decoration: line-through;
1808
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
1808
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
1809
1809
  }
1810
1810
  .aiCardNextWhen {
1811
1811
  font-size: 11px;
1812
1812
  font-weight: 500;
1813
1813
  line-height: 1.3;
1814
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
1814
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
1815
1815
  flex-shrink: 0;
1816
1816
  }
1817
1817
 
@@ -1856,12 +1856,12 @@
1856
1856
  margin: 0 2px;
1857
1857
  }
1858
1858
  .ticketNumberCat {
1859
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
1859
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
1860
1860
  font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
1861
1861
  font-weight: 400;
1862
1862
  }
1863
1863
  .ticketNumberSource {
1864
- color: var(--theme-text-tertiary, var(--theme-elevation-500));
1864
+ color: var(--theme-text-tertiary, var(--theme-elevation-650));
1865
1865
  font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
1866
1866
  display: inline-flex;
1867
1867
  align-items: center;
@@ -26,7 +26,7 @@
26
26
 
27
27
  .subtitle {
28
28
  font-size: 13px;
29
- color: var(--theme-elevation-500);
29
+ color: var(--theme-elevation-650);
30
30
  margin: 2px 0 0;
31
31
  }
32
32
 
@@ -125,7 +125,7 @@
125
125
  padding: 0 12px;
126
126
  font-size: 13px;
127
127
  font-weight: 500;
128
- color: var(--theme-elevation-500);
128
+ color: var(--theme-elevation-650);
129
129
  background: none;
130
130
  border: none;
131
131
  border-radius: 6px;
@@ -155,7 +155,7 @@
155
155
  padding: 0 6px;
156
156
  font-size: 11px;
157
157
  font-weight: 600;
158
- color: var(--theme-elevation-500);
158
+ color: var(--theme-elevation-650);
159
159
  background: var(--theme-elevation-200);
160
160
  border-radius: 999px;
161
161
  }
@@ -179,7 +179,7 @@
179
179
  .sortSelect {
180
180
  font-size: 12px;
181
181
  font-weight: 600;
182
- color: var(--theme-elevation-500);
182
+ color: var(--theme-elevation-650);
183
183
  background: none;
184
184
  border: none;
185
185
  cursor: pointer;
@@ -268,7 +268,7 @@
268
268
 
269
269
  .client {
270
270
  font-size: 12px;
271
- color: var(--theme-elevation-500);
271
+ color: var(--theme-elevation-650);
272
272
  overflow: hidden;
273
273
  text-overflow: ellipsis;
274
274
  white-space: nowrap;
@@ -333,7 +333,7 @@
333
333
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
334
334
  font-size: 11px;
335
335
  font-weight: 500;
336
- color: var(--theme-elevation-500);
336
+ color: var(--theme-elevation-650);
337
337
  display: flex;
338
338
  gap: 12px;
339
339
  z-index: 10;
@@ -15,7 +15,7 @@ $green: #16a34a;
15
15
 
16
16
  .intro {
17
17
  font-size: 13px;
18
- color: var(--theme-elevation-500);
18
+ color: var(--theme-elevation-650);
19
19
  margin: 0 0 24px;
20
20
  line-height: 1.6;
21
21
  }
@@ -59,7 +59,7 @@ $green: #16a34a;
59
59
 
60
60
  .sectionChevron {
61
61
  font-size: 16px;
62
- color: var(--theme-elevation-500);
62
+ color: var(--theme-elevation-650);
63
63
  transition: transform 150ms;
64
64
 
65
65
  &.open {
@@ -73,7 +73,7 @@ $green: #16a34a;
73
73
 
74
74
  .sectionDescription {
75
75
  font-size: 12px;
76
- color: var(--theme-elevation-500);
76
+ color: var(--theme-elevation-650);
77
77
  margin: 0 0 16px;
78
78
  line-height: 1.5;
79
79
  }
@@ -97,7 +97,7 @@ $green: #16a34a;
97
97
 
98
98
  .fieldDescription {
99
99
  font-size: 11px;
100
- color: var(--theme-elevation-500);
100
+ color: var(--theme-elevation-650);
101
101
  font-weight: 400;
102
102
  margin-top: 2px;
103
103
  }
@@ -131,7 +131,7 @@ $green: #16a34a;
131
131
  border-radius: 6px;
132
132
  border: 1px solid var(--theme-elevation-300);
133
133
  font-size: 13px;
134
- color: var(--theme-elevation-500);
134
+ color: var(--theme-elevation-650);
135
135
  background-color: var(--theme-elevation-100);
136
136
  width: 100%;
137
137
  max-width: 400px;
@@ -179,7 +179,7 @@ $green: #16a34a;
179
179
 
180
180
  .inlineDesc {
181
181
  font-size: 12px;
182
- color: var(--theme-elevation-500);
182
+ color: var(--theme-elevation-650);
183
183
  }
184
184
 
185
185
  // ─── BADGE ───
@@ -251,7 +251,7 @@ $green: #16a34a;
251
251
 
252
252
  .featureDesc {
253
253
  font-size: 12px;
254
- color: var(--theme-elevation-500);
254
+ color: var(--theme-elevation-650);
255
255
  line-height: 1.4;
256
256
  }
257
257
 
@@ -271,7 +271,7 @@ $green: #16a34a;
271
271
 
272
272
  .aiToggleDesc {
273
273
  font-size: 12px;
274
- color: var(--theme-elevation-500);
274
+ color: var(--theme-elevation-650);
275
275
  margin-left: 8px;
276
276
  }
277
277
 
@@ -290,7 +290,7 @@ $green: #16a34a;
290
290
  background-color: transparent;
291
291
  cursor: pointer;
292
292
  font-size: 12px;
293
- color: var(--theme-elevation-500);
293
+ color: var(--theme-elevation-650);
294
294
  white-space: nowrap;
295
295
  }
296
296
 
@@ -303,7 +303,7 @@ $green: #16a34a;
303
303
 
304
304
  .slaHint {
305
305
  font-size: 12px;
306
- color: var(--theme-elevation-500);
306
+ color: var(--theme-elevation-650);
307
307
  }
308
308
 
309
309
  // ─── AI SUB FEATURES LABEL ───
@@ -28,7 +28,7 @@
28
28
  }
29
29
 
30
30
  .subtitle {
31
- color: var(--theme-elevation-500);
31
+ color: var(--theme-elevation-650);
32
32
  margin: 4px 0 0;
33
33
  font-size: 14px;
34
34
  }
@@ -80,7 +80,7 @@
80
80
  .label {
81
81
  font-size: 11px;
82
82
  font-weight: 700;
83
- color: var(--theme-elevation-500);
83
+ color: var(--theme-elevation-650);
84
84
  margin-bottom: 3px;
85
85
  }
86
86
 
@@ -146,7 +146,7 @@
146
146
  font-size: 10px;
147
147
  font-weight: 700;
148
148
  text-transform: uppercase;
149
- color: var(--theme-elevation-500);
149
+ color: var(--theme-elevation-650);
150
150
  }
151
151
 
152
152
  .kpiValue {
@@ -201,7 +201,7 @@
201
201
  border: 1px solid var(--theme-elevation-300);
202
202
  border-radius: 8px;
203
203
  background: var(--theme-elevation-100);
204
- color: var(--theme-elevation-500);
204
+ color: var(--theme-elevation-650);
205
205
  font-size: 14px;
206
206
  }
207
207
 
@@ -257,13 +257,13 @@
257
257
 
258
258
  .tdSubject {
259
259
  padding: 6px 8px;
260
- color: var(--theme-elevation-500);
260
+ color: var(--theme-elevation-650);
261
261
  font-size: 12px;
262
262
  }
263
263
 
264
264
  .tdDescription {
265
265
  padding: 6px 8px;
266
- color: var(--theme-elevation-500);
266
+ color: var(--theme-elevation-650);
267
267
  font-size: 12px;
268
268
  }
269
269
 
@@ -1,6 +1,8 @@
1
1
  // Design tokens — based on Payload CMS CSS variables
2
2
  $text: var(--theme-text);
3
- $text-secondary: var(--theme-elevation-500);
3
+ // elevation-650, not 500: Payload's dark theme skips the 500 step, leaving it
4
+ // at rgb(128,128,128) — 3.62:1 light / 4.03:1 dark, below WCAG AA in both.
5
+ $text-secondary: var(--theme-elevation-650);
4
6
  $text-muted: var(--theme-elevation-400);
5
7
  $bg: var(--theme-elevation-50);
6
8
  $bg-card: var(--theme-elevation-100);
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
- import React, { useState, useCallback } from 'react';
3
+ import React, { useId, useState, useCallback } from 'react';
4
4
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
5
5
  import { DATE_LOCALE } from '../shared/dateLocale.js';
6
6
  import styles from '../../styles/BillingView.module.scss';
@@ -42,6 +42,10 @@ function getQuarterRange(offset) {
42
42
  }
43
43
  const BillingClient = () => {
44
44
  const { t } = useTranslation();
45
+ const fromId = useId();
46
+ const toId = useId();
47
+ const projectFilterId = useId();
48
+ const rateId = useId();
45
49
  const [from, setFrom] = useState(() => getMonthRange(0).from);
46
50
  const [to, setTo] = useState(() => getMonthRange(0).to);
47
51
  const [projectId, setProjectId] = useState("");
@@ -218,26 +222,27 @@ const BillingClient = () => {
218
222
  ] }),
219
223
  /* @__PURE__ */ jsxs("div", { className: styles.filterRow, children: [
220
224
  /* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
221
- /* @__PURE__ */ jsx("label", { className: styles.label, children: t("billing.filters.from") }),
222
- /* @__PURE__ */ jsx("input", { type: "date", value: from, onChange: (e) => setFrom(e.target.value), className: styles.input })
225
+ /* @__PURE__ */ jsx("label", { className: styles.label, htmlFor: fromId, children: t("billing.filters.from") }),
226
+ /* @__PURE__ */ jsx("input", { id: fromId, type: "date", value: from, onChange: (e) => setFrom(e.target.value), className: styles.input })
223
227
  ] }),
224
228
  /* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
225
- /* @__PURE__ */ jsx("label", { className: styles.label, children: t("billing.filters.to") }),
226
- /* @__PURE__ */ jsx("input", { type: "date", value: to, onChange: (e) => setTo(e.target.value), className: styles.input })
229
+ /* @__PURE__ */ jsx("label", { className: styles.label, htmlFor: toId, children: t("billing.filters.to") }),
230
+ /* @__PURE__ */ jsx("input", { id: toId, type: "date", value: to, onChange: (e) => setTo(e.target.value), className: styles.input })
227
231
  ] }),
228
232
  /* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
229
- /* @__PURE__ */ jsx("label", { className: styles.label, children: t("billing.filters.project") }),
230
- /* @__PURE__ */ jsxs("select", { value: projectId, onChange: (e) => setProjectId(e.target.value), className: styles.select, children: [
233
+ /* @__PURE__ */ jsx("label", { className: styles.label, htmlFor: projectFilterId, children: t("billing.filters.project") }),
234
+ /* @__PURE__ */ jsxs("select", { id: projectFilterId, value: projectId, onChange: (e) => setProjectId(e.target.value), className: styles.select, children: [
231
235
  /* @__PURE__ */ jsx("option", { value: "", children: t("ticket.allProjects") }),
232
236
  projects.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
233
237
  ] })
234
238
  ] }),
235
239
  /* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
236
- /* @__PURE__ */ jsx("label", { className: styles.label, children: t("billing.filters.hourlyRate") }),
240
+ /* @__PURE__ */ jsx("label", { className: styles.label, htmlFor: rateId, children: t("billing.filters.hourlyRate") }),
237
241
  /* @__PURE__ */ jsxs("div", { className: styles.rateRow, children: [
238
242
  /* @__PURE__ */ jsx(
239
243
  "input",
240
244
  {
245
+ id: rateId,
241
246
  type: "number",
242
247
  value: rate,
243
248
  onChange: (e) => setRate(Number(e.target.value)),
@@ -293,6 +298,7 @@ const BillingClient = () => {
293
298
  "input",
294
299
  {
295
300
  type: "checkbox",
301
+ "aria-label": t("billing.tableCheckboxTitle"),
296
302
  checked: group.tickets.every((t2) => billedTickets.has(t2.id)),
297
303
  onChange: () => {
298
304
  const ids = group.tickets.map((t2) => t2.id);
@@ -326,6 +332,7 @@ const BillingClient = () => {
326
332
  "input",
327
333
  {
328
334
  type: "checkbox",
335
+ "aria-label": isBilled ? t("billing.markUnbilledTitle") : t("billing.markBilledTitle"),
329
336
  checked: isBilled,
330
337
  onChange: () => toggleBilled(ticket.id),
331
338
  className: styles.checkbox,
@@ -252,6 +252,7 @@ const ChatViewClient = () => {
252
252
  cannedResponses.length > 0 && /* @__PURE__ */ jsxs(
253
253
  "select",
254
254
  {
255
+ "aria-label": t("chat.quickReply"),
255
256
  onChange: (e) => {
256
257
  const cr = cannedResponses.find((c) => String(c.id) === e.target.value);
257
258
  if (cr) setInput(cr.body);
@@ -269,6 +270,7 @@ const ChatViewClient = () => {
269
270
  "input",
270
271
  {
271
272
  type: "text",
273
+ "aria-label": t("chat.inputPlaceholder"),
272
274
  value: input,
273
275
  onChange: (e) => setInput(e.target.value),
274
276
  placeholder: t("chat.inputPlaceholder"),
@@ -211,6 +211,7 @@ Cette action est irr\xE9versible.`)) return;
211
211
  "input",
212
212
  {
213
213
  type: "text",
214
+ "aria-label": t("crm.searchLabel"),
214
215
  placeholder: t("crm.searchPlaceholder"),
215
216
  value: search,
216
217
  onChange: (e) => setSearch(e.target.value),
@@ -295,6 +296,7 @@ Cette action est irr\xE9versible.`)) return;
295
296
  "input",
296
297
  {
297
298
  type: "text",
299
+ "aria-label": t("crm.mergeTitle"),
298
300
  value: mergeSearch,
299
301
  onChange: (e) => setMergeSearch(e.target.value),
300
302
  placeholder: t("crm.mergeSearchPlaceholder"),
@@ -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
  ] })