@consilioweb/payload-support 5.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +249 -22
- package/dist/components/RichTextEditor/index.js +1 -1
- package/dist/components/TicketConversation/RewriteDropdown.js +1 -1
- package/dist/components/TicketConversation/components/AISummaryPanel.js +3 -0
- package/dist/components/TicketConversation/components/ActionPanels.js +13 -8
- package/dist/components/TicketConversation/components/ClientHistory.js +1 -0
- package/dist/components/TicketConversation/components/CodeBlock.js +1 -0
- package/dist/components/TicketConversation/components/CodeBlockInserter.js +1 -1
- package/dist/components/TicketConversation/components/QuickActions.js +10 -8
- package/dist/components/TicketConversation/components/TicketHeader.js +2 -0
- package/dist/components/TicketConversation/components/TimeTrackingPanel.js +15 -9
- package/dist/components/TicketConversation/index.d.ts +10 -0
- package/dist/components/TicketConversation/index.js +25 -7
- package/dist/index.cjs +306 -60
- package/dist/index.d.cts +107 -38
- package/dist/index.d.ts +107 -38
- package/dist/index.js +300 -59
- package/dist/styles/BillingView.module.scss +11 -11
- package/dist/styles/ChatView.module.scss +7 -7
- package/dist/styles/CommandPalette.module.scss +1 -1
- package/dist/styles/CrmView.module.scss +9 -9
- package/dist/styles/EmailTracking.module.scss +6 -6
- package/dist/styles/ImportConversation.module.scss +5 -5
- package/dist/styles/Logs.module.scss +5 -5
- package/dist/styles/NewTicket.module.scss +2 -2
- package/dist/styles/PendingEmails.module.scss +13 -13
- package/dist/styles/SupportDashboard.module.scss +24 -8
- package/dist/styles/TicketDetail.module.scss +20 -20
- package/dist/styles/TicketInbox.module.scss +6 -6
- package/dist/styles/TicketingSettings.module.scss +10 -10
- package/dist/styles/TimeDashboard.module.scss +6 -6
- package/dist/styles/_tokens.scss +3 -1
- package/dist/utils/db.js +20 -0
- package/dist/utils/readSettings.js +150 -0
- package/dist/views/BillingView/client.js +15 -8
- package/dist/views/ChatView/client.js +2 -0
- package/dist/views/CrmView/client.js +2 -0
- package/dist/views/EmailTrackingView/client.js +23 -11
- package/dist/views/ImportConversationView/client.js +1 -0
- package/dist/views/LogsView/client.js +1 -1
- package/dist/views/NewTicketView/client.js +20 -13
- package/dist/views/PendingEmailsView/client.js +9 -3
- package/dist/views/SupportDashboardView/client.js +17 -19
- package/dist/views/TicketDetailView/client.js +23 -11
- package/dist/views/TicketInboxView/client.js +52 -26
- package/dist/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
- package/dist/views/TicketingSettingsView/client.js +44 -26
- package/dist/views/TimeDashboardView/client.js +10 -7
- package/dist/views/shared/ErrorBoundary.d.ts +36 -1
- package/dist/views/shared/ErrorBoundary.js +59 -28
- package/dist/views/shared/adminTokens.d.ts +15 -5
- package/dist/views/shared/adminTokens.js +23 -7
- package/dist/views/shared/icons.d.ts +49 -0
- package/dist/views/shared/icons.js +92 -0
- package/dist/views/shared/locales/en.json +15 -1
- package/dist/views/shared/locales/fr.json +15 -1
- package/package.json +9 -4
- package/scripts/copy-subpath-types.mjs +103 -0
- package/scripts/uninstall-data.mjs +178 -0
- package/scripts/uninstall.mjs +184 -0
- package/scripts/verify-dist-imports.mjs +84 -0
- package/src/collections/Tickets.ts +7 -0
- package/src/collections/TimeEntries.ts +11 -3
- package/src/components/RichTextEditor/index.tsx +1 -1
- package/src/components/TicketConversation/RewriteDropdown.tsx +1 -1
- package/src/components/TicketConversation/components/AISummaryPanel.tsx +3 -0
- package/src/components/TicketConversation/components/ActionPanels.tsx +13 -8
- package/src/components/TicketConversation/components/ClientHistory.tsx +1 -0
- package/src/components/TicketConversation/components/CodeBlock.tsx +1 -0
- package/src/components/TicketConversation/components/CodeBlockInserter.tsx +1 -1
- package/src/components/TicketConversation/components/QuickActions.tsx +10 -8
- package/src/components/TicketConversation/components/TicketHeader.tsx +2 -0
- package/src/components/TicketConversation/components/TimeTrackingPanel.tsx +17 -9
- package/src/components/TicketConversation/index.tsx +41 -7
- package/src/endpoints/delete-account.ts +191 -47
- package/src/endpoints/export-data.ts +104 -4
- package/src/endpoints/purge-logs.ts +6 -15
- package/src/index.ts +2 -0
- package/src/plugin.ts +51 -1
- package/src/portal/LiveChat.tsx +1 -1
- package/src/portal/auth/profile/page.tsx +3 -2
- package/src/portal/icons.tsx +91 -0
- package/src/styles/BillingView.module.scss +11 -11
- package/src/styles/ChatView.module.scss +7 -7
- package/src/styles/CommandPalette.module.scss +1 -1
- package/src/styles/CrmView.module.scss +9 -9
- package/src/styles/EmailTracking.module.scss +6 -6
- package/src/styles/ImportConversation.module.scss +5 -5
- package/src/styles/Logs.module.scss +5 -5
- package/src/styles/NewTicket.module.scss +2 -2
- package/src/styles/PendingEmails.module.scss +13 -13
- package/src/styles/SupportDashboard.module.scss +24 -8
- package/src/styles/TicketDetail.module.scss +20 -20
- package/src/styles/TicketInbox.module.scss +6 -6
- package/src/styles/TicketingSettings.module.scss +10 -10
- package/src/styles/TimeDashboard.module.scss +6 -6
- package/src/styles/_tokens.scss +3 -1
- package/src/types.ts +20 -0
- package/src/utils/retention.ts +111 -0
- package/src/utils/slugs.ts +15 -0
- package/src/views/BillingView/client.tsx +17 -8
- package/src/views/ChatView/client.tsx +2 -0
- package/src/views/CrmView/client.tsx +2 -0
- package/src/views/EmailTrackingView/client.tsx +18 -7
- package/src/views/ImportConversationView/client.tsx +1 -0
- package/src/views/LogsView/client.tsx +1 -1
- package/src/views/NewTicketView/client.tsx +22 -13
- package/src/views/PendingEmailsView/client.tsx +9 -3
- package/src/views/SupportDashboardView/client.tsx +11 -5
- package/src/views/TicketDetailView/client.tsx +25 -11
- package/src/views/TicketInboxView/client.tsx +47 -7
- package/src/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
- package/src/views/TicketingSettingsView/client.tsx +201 -144
- package/src/views/TimeDashboardView/client.tsx +10 -7
- package/src/views/shared/ErrorBoundary.tsx +95 -31
- package/src/views/shared/adminTokens.ts +28 -11
- package/src/views/shared/icons.tsx +146 -0
- package/src/views/shared/locales/en.json +15 -1
- package/src/views/shared/locales/fr.json +15 -1
- package/src/types/lucide-react.d.ts +0 -42
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
266
|
+
color: var(--theme-elevation-650);
|
|
267
267
|
font-size: 12px;
|
|
268
268
|
}
|
|
269
269
|
|
package/dist/styles/_tokens.scss
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Design tokens — based on Payload CMS CSS variables
|
|
2
2
|
$text: var(--theme-text);
|
|
3
|
-
|
|
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);
|
package/dist/utils/db.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function dbFind(payload, slug, options = {}) {
|
|
2
|
+
return payload.find({ collection: slug, ...options });
|
|
3
|
+
}
|
|
4
|
+
function dbFindByID(payload, slug, options) {
|
|
5
|
+
return payload.findByID({ collection: slug, ...options });
|
|
6
|
+
}
|
|
7
|
+
function dbCreate(payload, slug, options) {
|
|
8
|
+
return payload.create({ collection: slug, ...options });
|
|
9
|
+
}
|
|
10
|
+
function dbUpdate(payload, slug, options) {
|
|
11
|
+
return payload.update({ collection: slug, ...options });
|
|
12
|
+
}
|
|
13
|
+
function dbCount(payload, slug, options = {}) {
|
|
14
|
+
return payload.count({ collection: slug, ...options });
|
|
15
|
+
}
|
|
16
|
+
function dbDelete(payload, slug, options) {
|
|
17
|
+
return payload.delete({ collection: slug, ...options });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { dbCount, dbCreate, dbDelete, dbFind, dbFindByID, dbUpdate };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { dbFind } from './db.js';
|
|
2
|
+
import { DEFAULT_TICKETING_FEATURES, projectAutoClose, normalizeFeatures } from './features.js';
|
|
3
|
+
|
|
4
|
+
const SUPPORT_SETTINGS_PREF_KEY = "support-settings";
|
|
5
|
+
const PREF_KEY = SUPPORT_SETTINGS_PREF_KEY;
|
|
6
|
+
const USER_PREFS_KEY_PREFIX = "support-user-prefs";
|
|
7
|
+
const LEGACY_ROUND_ROBIN_KEY = "support-round-robin";
|
|
8
|
+
const SUPPORT_STAFF_SLUG_CONFIG_KEY = "supportStaffCollection";
|
|
9
|
+
function resolveStaffPrefSlug(payload, staffSlug) {
|
|
10
|
+
if (staffSlug) return staffSlug;
|
|
11
|
+
const config = payload.config;
|
|
12
|
+
const registered = config?.custom?.[SUPPORT_STAFF_SLUG_CONFIG_KEY];
|
|
13
|
+
if (typeof registered === "string" && registered) return registered;
|
|
14
|
+
return config?.admin?.user || "users";
|
|
15
|
+
}
|
|
16
|
+
const DEFAULT_SETTINGS = {
|
|
17
|
+
email: { fromAddress: "", fromName: "Support", replyToAddress: "" },
|
|
18
|
+
ai: { provider: "anthropic", model: "claude-haiku-4-5-20251001", enableSentiment: true, enableSynthesis: true, enableSuggestion: true, enableRewrite: true },
|
|
19
|
+
sla: { firstResponseMinutes: 120, resolutionMinutes: 1440, businessHoursOnly: true, escalationEmail: "" },
|
|
20
|
+
autoClose: { enabled: true, daysBeforeClose: 7, reminderDaysBefore: 2 },
|
|
21
|
+
features: { ...DEFAULT_TICKETING_FEATURES }
|
|
22
|
+
};
|
|
23
|
+
const DEFAULT_USER_PREFS = {
|
|
24
|
+
locale: "fr",
|
|
25
|
+
signature: ""
|
|
26
|
+
};
|
|
27
|
+
const settingsCache = /* @__PURE__ */ new Map();
|
|
28
|
+
const SETTINGS_TTL_MS = 6e4;
|
|
29
|
+
const SETTINGS_CACHE_MAX = 8;
|
|
30
|
+
const warnedForeignSettingsRow = /* @__PURE__ */ new Set();
|
|
31
|
+
function invalidateSupportSettingsCache() {
|
|
32
|
+
settingsCache.clear();
|
|
33
|
+
warnedForeignSettingsRow.clear();
|
|
34
|
+
}
|
|
35
|
+
function mergeSupportSettings(stored, base = DEFAULT_SETTINGS) {
|
|
36
|
+
const autoClose = { ...base.autoClose, ...stored?.autoClose };
|
|
37
|
+
return {
|
|
38
|
+
email: { ...base.email, ...stored?.email },
|
|
39
|
+
ai: { ...base.ai, ...stored?.ai },
|
|
40
|
+
sla: { ...base.sla, ...stored?.sla },
|
|
41
|
+
autoClose,
|
|
42
|
+
// `autoClose` / `autoCloseDays` are projections of the block above, so they
|
|
43
|
+
// are recomputed here rather than trusted from whatever was persisted.
|
|
44
|
+
features: projectAutoClose(
|
|
45
|
+
normalizeFeatures({ ...base.features, ...stored?.features }),
|
|
46
|
+
autoClose
|
|
47
|
+
)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function readSupportSettingsState(payload, staffSlug) {
|
|
51
|
+
const staff = resolveStaffPrefSlug(payload, staffSlug);
|
|
52
|
+
const cached = settingsCache.get(staff);
|
|
53
|
+
if (cached && Date.now() - cached.ts < SETTINGS_TTL_MS) {
|
|
54
|
+
return cached.value;
|
|
55
|
+
}
|
|
56
|
+
let value = {
|
|
57
|
+
settings: mergeSupportSettings(null),
|
|
58
|
+
featuresConfigured: false
|
|
59
|
+
};
|
|
60
|
+
try {
|
|
61
|
+
const prefs = await dbFind(payload, "payload-preferences", {
|
|
62
|
+
// Sibling keys are AND-ed by Payload. The `user.relationTo` clause is the
|
|
63
|
+
// security boundary: without it any authenticated principal can plant a
|
|
64
|
+
// `support-settings` row and own the plugin's server settings.
|
|
65
|
+
where: { key: { equals: PREF_KEY }, "user.relationTo": { equals: staff } },
|
|
66
|
+
// The upsert is scoped per admin user, so several rows can share the key.
|
|
67
|
+
// Sorting makes "last write wins" deterministic instead of arbitrary.
|
|
68
|
+
sort: "-updatedAt",
|
|
69
|
+
limit: 1,
|
|
70
|
+
depth: 0,
|
|
71
|
+
overrideAccess: true
|
|
72
|
+
});
|
|
73
|
+
if (prefs.docs.length > 0) {
|
|
74
|
+
const stored = prefs.docs[0].value;
|
|
75
|
+
const featuresConfigured = !!stored.features && typeof stored.features === "object";
|
|
76
|
+
const settings = mergeSupportSettings(stored);
|
|
77
|
+
if (!featuresConfigured) {
|
|
78
|
+
settings.features.roundRobin = await readLegacyRoundRobin(payload, staff);
|
|
79
|
+
}
|
|
80
|
+
value = { settings, featuresConfigured };
|
|
81
|
+
} else {
|
|
82
|
+
await warnOnForeignSettingsRow(payload, staff);
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
if (settingsCache.size >= SETTINGS_CACHE_MAX && !settingsCache.has(staff)) settingsCache.clear();
|
|
87
|
+
settingsCache.set(staff, { value, ts: Date.now() });
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
async function warnOnForeignSettingsRow(payload, staff) {
|
|
91
|
+
if (warnedForeignSettingsRow.has(staff)) return;
|
|
92
|
+
try {
|
|
93
|
+
const any = await dbFind(payload, "payload-preferences", {
|
|
94
|
+
where: { key: { equals: PREF_KEY } },
|
|
95
|
+
limit: 1,
|
|
96
|
+
depth: 0,
|
|
97
|
+
overrideAccess: true
|
|
98
|
+
});
|
|
99
|
+
if (any.docs.length === 0) return;
|
|
100
|
+
if (warnedForeignSettingsRow.size >= SETTINGS_CACHE_MAX) warnedForeignSettingsRow.clear();
|
|
101
|
+
warnedForeignSettingsRow.add(staff);
|
|
102
|
+
console.warn(
|
|
103
|
+
`[support] A "${PREF_KEY}" preference row exists but none is owned by the "${staff}" collection: the plugin is running on its DEFAULT settings. Either the staff auth collection differs from \`admin.user\`, or the row was written by a principal that is not staff \u2014 in which case it is ignored on purpose.`
|
|
104
|
+
);
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function readSupportSettings(payload, staffSlug) {
|
|
109
|
+
return (await readSupportSettingsState(payload, staffSlug)).settings;
|
|
110
|
+
}
|
|
111
|
+
async function readLegacyRoundRobin(payload, staff) {
|
|
112
|
+
try {
|
|
113
|
+
const prefs = await dbFind(payload, "payload-preferences", {
|
|
114
|
+
where: { key: { equals: LEGACY_ROUND_ROBIN_KEY }, "user.relationTo": { equals: staff } },
|
|
115
|
+
limit: 1,
|
|
116
|
+
depth: 0,
|
|
117
|
+
overrideAccess: true
|
|
118
|
+
});
|
|
119
|
+
if (prefs.docs.length > 0) {
|
|
120
|
+
return prefs.docs[0].value?.enabled === true;
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
return DEFAULT_TICKETING_FEATURES.roundRobin;
|
|
125
|
+
}
|
|
126
|
+
async function readUserPrefs(payload, userId, staffSlug) {
|
|
127
|
+
try {
|
|
128
|
+
const key = `${USER_PREFS_KEY_PREFIX}-${userId}`;
|
|
129
|
+
const prefs = await dbFind(payload, "payload-preferences", {
|
|
130
|
+
where: {
|
|
131
|
+
key: { equals: key },
|
|
132
|
+
"user.relationTo": { equals: resolveStaffPrefSlug(payload, staffSlug) }
|
|
133
|
+
},
|
|
134
|
+
limit: 1,
|
|
135
|
+
depth: 0,
|
|
136
|
+
overrideAccess: true
|
|
137
|
+
});
|
|
138
|
+
if (prefs.docs.length > 0) {
|
|
139
|
+
const stored = prefs.docs[0].value;
|
|
140
|
+
return {
|
|
141
|
+
locale: stored.locale || DEFAULT_USER_PREFS.locale,
|
|
142
|
+
signature: stored.signature ?? DEFAULT_USER_PREFS.signature
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
return { ...DEFAULT_USER_PREFS };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { DEFAULT_SETTINGS, DEFAULT_USER_PREFS, SUPPORT_SETTINGS_PREF_KEY, SUPPORT_STAFF_SLUG_CONFIG_KEY, invalidateSupportSettingsCache, mergeSupportSettings, readSupportSettings, readSupportSettingsState, readUserPrefs, resolveStaffPrefSlug };
|
|
@@ -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"),
|