@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
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import type { Endpoint } from 'payload'
|
|
2
2
|
import type { CollectionSlugs } from '../utils/slugs'
|
|
3
|
+
import { FIXED_SLUGS } from '../utils/slugs'
|
|
3
4
|
import { requireClient, handleAuthError } from '../utils/auth'
|
|
4
5
|
import { dbFind, dbFindByID } from '../utils/db'
|
|
5
6
|
|
|
7
|
+
const EMPTY = { docs: [] as any[] }
|
|
8
|
+
|
|
6
9
|
/**
|
|
7
10
|
* GET /api/support/export-data
|
|
8
11
|
* RGPD Data Export — allows support clients to download all their personal data.
|
|
12
|
+
*
|
|
13
|
+
* The payload is split in two. `providedByYou` is what article 20
|
|
14
|
+
* (portability) covers: what the person or their correspondents typed.
|
|
15
|
+
* `derivedData` is what the plugin computed ABOUT them — the AI client
|
|
16
|
+
* summary. Article 20 does not cover derived data; article 15 (access) does,
|
|
17
|
+
* and article 17 (erasure) does too, which is why `delete-account` removes
|
|
18
|
+
* `client-summaries` as well. Merging the two sections would blur that.
|
|
9
19
|
*/
|
|
10
20
|
export function createExportDataEndpoint(slugs: CollectionSlugs): Endpoint {
|
|
11
21
|
return {
|
|
@@ -17,7 +27,20 @@ export function createExportDataEndpoint(slugs: CollectionSlugs): Endpoint {
|
|
|
17
27
|
|
|
18
28
|
requireClient(req, slugs)
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
// Feature-flagged collections are not registered when their flag is
|
|
31
|
+
// off; addressing one that does not exist throws.
|
|
32
|
+
const isRegistered = (slug: string): boolean =>
|
|
33
|
+
Boolean((payload.collections as Record<string, unknown> | undefined)?.[slug])
|
|
34
|
+
|
|
35
|
+
const [
|
|
36
|
+
clientData,
|
|
37
|
+
ticketsResult,
|
|
38
|
+
messagesResult,
|
|
39
|
+
surveysResult,
|
|
40
|
+
chatResult,
|
|
41
|
+
feedbackResult,
|
|
42
|
+
summariesResult,
|
|
43
|
+
] = await Promise.all([
|
|
21
44
|
dbFindByID(payload, slugs.supportClients, {
|
|
22
45
|
id: req.user.id,
|
|
23
46
|
depth: 0,
|
|
@@ -44,12 +67,46 @@ export function createExportDataEndpoint(slugs: CollectionSlugs): Endpoint {
|
|
|
44
67
|
depth: 0,
|
|
45
68
|
overrideAccess: true,
|
|
46
69
|
}),
|
|
70
|
+
isRegistered(slugs.chatMessages)
|
|
71
|
+
? dbFind(payload, slugs.chatMessages, {
|
|
72
|
+
where: { client: { equals: req.user.id } },
|
|
73
|
+
limit: 5000,
|
|
74
|
+
depth: 0,
|
|
75
|
+
overrideAccess: true,
|
|
76
|
+
})
|
|
77
|
+
: EMPTY,
|
|
78
|
+
isRegistered(FIXED_SLUGS.ticketFeedback)
|
|
79
|
+
? dbFind(payload, FIXED_SLUGS.ticketFeedback, {
|
|
80
|
+
where: { client: { equals: req.user.id } },
|
|
81
|
+
limit: 500,
|
|
82
|
+
depth: 0,
|
|
83
|
+
overrideAccess: true,
|
|
84
|
+
})
|
|
85
|
+
: EMPTY,
|
|
86
|
+
isRegistered(FIXED_SLUGS.clientSummaries)
|
|
87
|
+
? dbFind(payload, FIXED_SLUGS.clientSummaries, {
|
|
88
|
+
where: { client: { equals: req.user.id } },
|
|
89
|
+
limit: 100,
|
|
90
|
+
depth: 0,
|
|
91
|
+
overrideAccess: true,
|
|
92
|
+
})
|
|
93
|
+
: EMPTY,
|
|
47
94
|
])
|
|
48
95
|
|
|
96
|
+
const ticketIds = ticketsResult.docs.map((t: any) => t.id)
|
|
97
|
+
|
|
98
|
+
// Time entries hang off the tickets, not off the client.
|
|
99
|
+
const timeResult = ticketIds.length > 0 && isRegistered(slugs.timeEntries)
|
|
100
|
+
? await dbFind(payload, slugs.timeEntries, {
|
|
101
|
+
where: { ticket: { in: ticketIds } },
|
|
102
|
+
limit: 5000,
|
|
103
|
+
depth: 0,
|
|
104
|
+
overrideAccess: true,
|
|
105
|
+
})
|
|
106
|
+
: EMPTY
|
|
107
|
+
|
|
49
108
|
const c = clientData as any
|
|
50
|
-
const
|
|
51
|
-
exportDate: new Date().toISOString(),
|
|
52
|
-
exportType: 'RGPD - Export des données personnelles',
|
|
109
|
+
const providedByYou = {
|
|
53
110
|
profile: {
|
|
54
111
|
email: c.email,
|
|
55
112
|
firstName: c.firstName,
|
|
@@ -79,6 +136,49 @@ export function createExportDataEndpoint(slugs: CollectionSlugs): Endpoint {
|
|
|
79
136
|
comment: s.comment,
|
|
80
137
|
createdAt: s.createdAt,
|
|
81
138
|
})),
|
|
139
|
+
feedback: feedbackResult.docs.map((f: any) => ({
|
|
140
|
+
ticketId: f.ticket,
|
|
141
|
+
rating: f.rating,
|
|
142
|
+
comment: f.comment,
|
|
143
|
+
submittedFrom: f.submittedFrom,
|
|
144
|
+
createdAt: f.createdAt,
|
|
145
|
+
})),
|
|
146
|
+
chatMessages: chatResult.docs.map((m: any) => ({
|
|
147
|
+
session: m.session,
|
|
148
|
+
senderType: m.senderType,
|
|
149
|
+
message: m.message,
|
|
150
|
+
createdAt: m.createdAt,
|
|
151
|
+
})),
|
|
152
|
+
timeEntries: timeResult.docs.map((e: any) => ({
|
|
153
|
+
ticketId: e.ticket,
|
|
154
|
+
duration: e.duration,
|
|
155
|
+
date: e.date,
|
|
156
|
+
description: e.description,
|
|
157
|
+
billable: e.billable,
|
|
158
|
+
})),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const exportData = {
|
|
162
|
+
exportDate: new Date().toISOString(),
|
|
163
|
+
exportType: 'RGPD - Export des données personnelles',
|
|
164
|
+
providedByYou: {
|
|
165
|
+
legalBasis: 'Art. 20 RGPD - droit à la portabilité',
|
|
166
|
+
...providedByYou,
|
|
167
|
+
},
|
|
168
|
+
derivedData: {
|
|
169
|
+
legalBasis: 'Art. 15 RGPD - droit d\'accès (données dérivées, hors portabilité)',
|
|
170
|
+
clientSummaries: summariesResult.docs.map((s: any) => ({
|
|
171
|
+
summary: s.summary,
|
|
172
|
+
recurringTopics: s.recurringTopics,
|
|
173
|
+
patterns: s.patterns,
|
|
174
|
+
keyFacts: s.keyFacts,
|
|
175
|
+
ticketCount: s.ticketCount,
|
|
176
|
+
messageCount: s.messageCount,
|
|
177
|
+
averageSatisfaction: s.averageSatisfaction,
|
|
178
|
+
generatedAt: s.generatedAt,
|
|
179
|
+
aiModel: s.aiModel,
|
|
180
|
+
})),
|
|
181
|
+
},
|
|
82
182
|
}
|
|
83
183
|
|
|
84
184
|
const json = JSON.stringify(exportData, null, 2)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Endpoint } from 'payload'
|
|
2
2
|
import type { CollectionSlugs } from '../utils/slugs'
|
|
3
3
|
import { requireAdmin, handleAuthError } from '../utils/auth'
|
|
4
|
-
import {
|
|
4
|
+
import { purgeOlderThan, purgeableCollections } from '../utils/retention'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* DELETE /api/support/purge-logs?collection=email-logs&days=30
|
|
@@ -22,25 +22,16 @@ export function createPurgeLogsEndpoint(slugs: CollectionSlugs): Endpoint {
|
|
|
22
22
|
const days = Number(url.searchParams.get('days') || '0')
|
|
23
23
|
|
|
24
24
|
// Map collection param to slug
|
|
25
|
-
const allowedCollections
|
|
26
|
-
'email-logs': slugs.emailLogs,
|
|
27
|
-
'auth-logs': slugs.authLogs,
|
|
28
|
-
}
|
|
25
|
+
const allowedCollections = purgeableCollections(slugs)
|
|
29
26
|
|
|
30
27
|
if (!collection || !allowedCollections[collection]) {
|
|
31
28
|
return Response.json({ error: 'Invalid collection. Use email-logs or auth-logs.' }, { status: 400 })
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
? { createdAt: { less_than: cutoff } }
|
|
39
|
-
: { id: { exists: true } },
|
|
40
|
-
overrideAccess: true,
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const count = Array.isArray(result.docs) ? result.docs.length : 0
|
|
31
|
+
// `days=0` wipes the journal entirely. It stays available here — an
|
|
32
|
+
// explicit, admin-only, one-off action — and is refused by the
|
|
33
|
+
// scheduled task, which must never be able to do it by accident.
|
|
34
|
+
const count = await purgeOlderThan(payload, allowedCollections[collection], days, req)
|
|
44
35
|
|
|
45
36
|
return Response.json({
|
|
46
37
|
purged: count,
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,8 @@ export type { SupportSettings, SupportSettingsState, UserPrefs } from './utils/r
|
|
|
28
28
|
export { DEFAULT_TICKETING_FEATURES, normalizeFeatures, projectAutoClose, stripProjectedFeatures } from './utils/features'
|
|
29
29
|
export type { TicketingFeatures } from './utils/features'
|
|
30
30
|
export { MemoryRateLimitStore, PayloadRateLimitStore, RateLimiter } from './utils/rateLimiter'
|
|
31
|
+
export { DEFAULT_RETENTION, PURGE_LOGS_TASK_SLUG, purgeOlderThan, purgeableCollections, runScheduledPurge } from './utils/retention'
|
|
32
|
+
export type { RetentionConfig, ScheduledPurgeResult } from './utils/retention'
|
|
31
33
|
export type { RateLimitEntry, RateLimitStore } from './utils/rateLimiter'
|
|
32
34
|
export { DEFAULT_INBOUND_EMAIL_LIMITS, validateInboundEmailPayload, verifySecret } from './utils/webhookSecurity'
|
|
33
35
|
export type { InboundEmailInput, InboundEmailLimits, InboundEmailValidationError } from './utils/webhookSecurity'
|
package/src/plugin.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Config, Plugin, AdminViewConfig } from 'payload'
|
|
1
|
+
import type { Config, Plugin, AdminViewConfig, PayloadRequest } from 'payload'
|
|
2
2
|
import type { SupportPluginConfig, SupportFeatures } from './types'
|
|
3
3
|
import { DEFAULT_FEATURES } from './types'
|
|
4
4
|
import { resolveSlugs } from './utils/slugs'
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
createSupportCountersCollection,
|
|
32
32
|
} from './collections'
|
|
33
33
|
import { PayloadRateLimitStore } from './utils/rateLimiter'
|
|
34
|
+
import { DEFAULT_RETENTION, PURGE_LOGS_TASK_SLUG, runScheduledPurge } from './utils/retention'
|
|
34
35
|
import { SUPPORT_STAFF_SLUG_CONFIG_KEY } from './utils/readSettings'
|
|
35
36
|
|
|
36
37
|
function viewConfig(component: string, path: string): AdminViewConfig {
|
|
@@ -168,8 +169,57 @@ export function supportPlugin(config?: SupportPluginConfig): Plugin {
|
|
|
168
169
|
capabilities: config?.capabilities,
|
|
169
170
|
})
|
|
170
171
|
|
|
172
|
+
// ─── Retention job ───────────────────────────────────
|
|
173
|
+
//
|
|
174
|
+
// `DELETE /api/support/purge-logs` existed but nothing ever called it, so
|
|
175
|
+
// the two journals grew forever — a retention policy nobody applies is not
|
|
176
|
+
// a retention policy. This registers a daily Payload Jobs task instead of
|
|
177
|
+
// a setInterval: an interval does not survive a serverless deploy and runs
|
|
178
|
+
// N times in parallel behind N instances.
|
|
179
|
+
// The task is OPT-IN: nothing is registered unless `retention` is set.
|
|
180
|
+
//
|
|
181
|
+
// Registering it by default was tempting — a retention policy nobody opts
|
|
182
|
+
// into is how the manual purge endpoint ended up never being called. But
|
|
183
|
+
// declaring a task turns Payload's job queue on, which adds `payload-jobs`
|
|
184
|
+
// and `payload-jobs-stats` to an app that had none: two tables, i.e. a
|
|
185
|
+
// schema change imposed on every consumer of a *support* plugin, by a minor
|
|
186
|
+
// release. And it would buy them nothing, because a scheduled task only
|
|
187
|
+
// fires when the host also runs a job runner (`payload jobs:run`, a cron,
|
|
188
|
+
// or `jobs.autoRun`). Defaulting it on therefore costs two tables and
|
|
189
|
+
// delivers no purge until the integrator does the other half of the work
|
|
190
|
+
// anyway — at which point they can just as well pass `retention`.
|
|
191
|
+
//
|
|
192
|
+
// The README states the trade-off where the option is documented, so the
|
|
193
|
+
// choice is visible rather than silent. `admin-ui-pro` made the same call
|
|
194
|
+
// for the same reason.
|
|
195
|
+
const retention = config?.retention === false ? undefined : config?.retention
|
|
196
|
+
const retentionEnabled = Boolean(retention)
|
|
197
|
+
const purgeTask = {
|
|
198
|
+
slug: PURGE_LOGS_TASK_SLUG,
|
|
199
|
+
label: 'Support — purge des journaux',
|
|
200
|
+
schedule: [
|
|
201
|
+
{
|
|
202
|
+
cron: retention?.cron ?? DEFAULT_RETENTION.cron,
|
|
203
|
+
queue: retention?.queue ?? DEFAULT_RETENTION.queue,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
handler: async ({ req }: { req: PayloadRequest }) => {
|
|
207
|
+
const result = await runScheduledPurge(req.payload, slugs, retention, req)
|
|
208
|
+
return { output: result }
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const existingJobs = incomingConfig.jobs
|
|
213
|
+
const jobs = retentionEnabled
|
|
214
|
+
? ({
|
|
215
|
+
...existingJobs,
|
|
216
|
+
tasks: [...(existingJobs?.tasks ?? []), purgeTask],
|
|
217
|
+
} as Config['jobs'])
|
|
218
|
+
: existingJobs
|
|
219
|
+
|
|
171
220
|
return {
|
|
172
221
|
...incomingConfig,
|
|
222
|
+
jobs,
|
|
173
223
|
// Publish the resolved staff collection so the server-side readers share
|
|
174
224
|
// ONE source of truth with the writers. `requireAdmin` compares against
|
|
175
225
|
// `slugs.users`; the `payload-preferences` reads used to scope themselves
|
package/src/portal/LiveChat.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import React, { useState, useEffect, useRef, useCallback } from 'react'
|
|
4
|
-
import { MessageCircle, Send, X, Minimize2, ArrowLeft } from '
|
|
4
|
+
import { MessageCircle, Send, X, Minimize2, ArrowLeft } from './icons'
|
|
5
5
|
import type { ChatMessage, LiveChatSession, Screen } from './types'
|
|
6
6
|
import { loadSession, saveSession, clearSession } from './helpers'
|
|
7
7
|
|
|
@@ -561,8 +561,9 @@ export default function ProfilePage() {
|
|
|
561
561
|
|
|
562
562
|
<div className="p-6 sm:p-8">
|
|
563
563
|
<p className="mb-4 text-sm text-slate-500 dark:text-slate-400 leading-relaxed">
|
|
564
|
-
Conformement au RGPD, vous pouvez télécharger l'ensemble de vos données personnelles
|
|
565
|
-
(profil, tickets, messages, enquetes de
|
|
564
|
+
Conformement au RGPD, vous pouvez télécharger l'ensemble de vos données personnelles :
|
|
565
|
+
celles que vous avez fournies (profil, tickets, messages, temps passe, enquetes de
|
|
566
|
+
satisfaction, chat) et celles que nous avons deduites de votre historique (synthese IA).
|
|
566
567
|
</p>
|
|
567
568
|
<a
|
|
568
569
|
href="/api/support/export-data"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inlined Lucide icons.
|
|
3
|
+
*
|
|
4
|
+
* The plugin used to require `lucide-react` as a peer dependency for exactly
|
|
5
|
+
* 5 icons across the portal live-chat widget. That made every integrator install an icon set,
|
|
6
|
+
* and a missing install a build-time failure, for 5 paths totalling a few
|
|
7
|
+
* hundred bytes. They are inlined here instead, and `lucide-react` is no longer
|
|
8
|
+
* a peer dependency.
|
|
9
|
+
*
|
|
10
|
+
* The geometry below is copied from lucide-react v1.41.0 and, like the rest of
|
|
11
|
+
* Lucide, is under the ISC licence:
|
|
12
|
+
*
|
|
13
|
+
* ISC License
|
|
14
|
+
* Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as
|
|
15
|
+
* part of Feather (MIT). All other copyright (c) for Lucide are held by
|
|
16
|
+
* Lucide Contributors 2022.
|
|
17
|
+
*
|
|
18
|
+
* Permission to use, copy, modify, and/or distribute this software for any
|
|
19
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
20
|
+
* copyright notice and this permission notice appear in all copies.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
23
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
24
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
25
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
26
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
27
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
28
|
+
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import React from 'react'
|
|
32
|
+
|
|
33
|
+
export interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, 'width' | 'height'> {
|
|
34
|
+
/** Rendered width and height. Lucide's own default is 24. */
|
|
35
|
+
size?: number | string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type IconNode = ReadonlyArray<readonly [string, Record<string, string | number>]>
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Icons are decorative by default (`aria-hidden`): every control that has
|
|
42
|
+
* nothing but an icon inside carries its own `aria-label`. Pass
|
|
43
|
+
* `aria-hidden={false}` plus a label to make one meaningful on its own.
|
|
44
|
+
*/
|
|
45
|
+
function createIcon(displayName: string, nodes: IconNode): React.FC<IconProps> {
|
|
46
|
+
const Icon: React.FC<IconProps> = ({ size = 24, strokeWidth = 2, ...rest }) => (
|
|
47
|
+
<svg
|
|
48
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
49
|
+
width={size}
|
|
50
|
+
height={size}
|
|
51
|
+
viewBox="0 0 24 24"
|
|
52
|
+
fill="none"
|
|
53
|
+
stroke="currentColor"
|
|
54
|
+
strokeWidth={strokeWidth}
|
|
55
|
+
strokeLinecap="round"
|
|
56
|
+
strokeLinejoin="round"
|
|
57
|
+
aria-hidden="true"
|
|
58
|
+
{...rest}
|
|
59
|
+
>
|
|
60
|
+
{nodes.map(([tag, attrs], index) => React.createElement(tag, { key: index, ...attrs }))}
|
|
61
|
+
</svg>
|
|
62
|
+
)
|
|
63
|
+
Icon.displayName = displayName
|
|
64
|
+
return Icon
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const ArrowLeft = createIcon('ArrowLeft', [
|
|
68
|
+
['path', { d: "m12 19-7-7 7-7" }],
|
|
69
|
+
['path', { d: "M19 12H5" }],
|
|
70
|
+
] as const)
|
|
71
|
+
|
|
72
|
+
export const MessageCircle = createIcon('MessageCircle', [
|
|
73
|
+
['path', { d: "M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" }],
|
|
74
|
+
] as const)
|
|
75
|
+
|
|
76
|
+
export const Minimize2 = createIcon('Minimize2', [
|
|
77
|
+
['path', { d: "m14 10 7-7" }],
|
|
78
|
+
['path', { d: "M20 10h-6V4" }],
|
|
79
|
+
['path', { d: "m3 21 7-7" }],
|
|
80
|
+
['path', { d: "M4 14h6v6" }],
|
|
81
|
+
] as const)
|
|
82
|
+
|
|
83
|
+
export const Send = createIcon('Send', [
|
|
84
|
+
['path', { d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z" }],
|
|
85
|
+
['path', { d: "m21.854 2.147-10.94 10.939" }],
|
|
86
|
+
] as const)
|
|
87
|
+
|
|
88
|
+
export const X = createIcon('X', [
|
|
89
|
+
['path', { d: "M18 6 6 18" }],
|
|
90
|
+
['path', { d: "m6 6 12 12" }],
|
|
91
|
+
] as const)
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.subtitle {
|
|
26
|
-
color: var(--theme-elevation-
|
|
26
|
+
color: var(--theme-elevation-650);
|
|
27
27
|
margin: 4px 0 0;
|
|
28
28
|
font-size: 14px;
|
|
29
29
|
}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
.label {
|
|
60
60
|
font-size: 12px;
|
|
61
61
|
font-weight: 700;
|
|
62
|
-
color: var(--theme-elevation-
|
|
62
|
+
color: var(--theme-elevation-650);
|
|
63
63
|
margin-bottom: 4px;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
border: 1px solid var(--theme-elevation-300);
|
|
135
135
|
border-radius: 8px;
|
|
136
136
|
background: var(--theme-elevation-100);
|
|
137
|
-
color: var(--theme-elevation-
|
|
137
|
+
color: var(--theme-elevation-650);
|
|
138
138
|
font-size: 14px;
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
.groupClient {
|
|
165
|
-
color: var(--theme-elevation-
|
|
165
|
+
color: var(--theme-elevation-650);
|
|
166
166
|
margin-left: 10px;
|
|
167
167
|
font-size: 13px;
|
|
168
168
|
}
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
|
|
191
191
|
.groupAmountStrike {
|
|
192
192
|
font-size: 11px;
|
|
193
|
-
color: var(--theme-elevation-
|
|
193
|
+
color: var(--theme-elevation-650);
|
|
194
194
|
text-decoration: line-through;
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
text-transform: uppercase;
|
|
209
209
|
letter-spacing: 0.5px;
|
|
210
210
|
text-align: center;
|
|
211
|
-
color: var(--theme-elevation-
|
|
211
|
+
color: var(--theme-elevation-650);
|
|
212
212
|
background: var(--theme-elevation-50);
|
|
213
213
|
border-bottom: 1px solid var(--theme-elevation-300);
|
|
214
214
|
}
|
|
@@ -268,7 +268,7 @@
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
.secondary {
|
|
271
|
-
color: var(--theme-elevation-
|
|
271
|
+
color: var(--theme-elevation-650);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
.billedAmount {
|
|
@@ -326,7 +326,7 @@
|
|
|
326
326
|
border-radius: 4px;
|
|
327
327
|
border: 1px solid var(--theme-elevation-300);
|
|
328
328
|
background: var(--theme-elevation-100);
|
|
329
|
-
color: var(--theme-elevation-
|
|
329
|
+
color: var(--theme-elevation-650);
|
|
330
330
|
font-size: 10px;
|
|
331
331
|
font-weight: 600;
|
|
332
332
|
cursor: pointer;
|
|
@@ -367,7 +367,7 @@
|
|
|
367
367
|
|
|
368
368
|
.summaryMeta {
|
|
369
369
|
font-size: 11px;
|
|
370
|
-
color: var(--theme-elevation-
|
|
370
|
+
color: var(--theme-elevation-650);
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
.summaryAction {
|
|
@@ -403,7 +403,7 @@
|
|
|
403
403
|
background: var(--theme-elevation-100);
|
|
404
404
|
border: 1px dashed var(--theme-elevation-300);
|
|
405
405
|
border-radius: 6px;
|
|
406
|
-
color: var(--theme-elevation-
|
|
406
|
+
color: var(--theme-elevation-650);
|
|
407
407
|
font-size: 12px;
|
|
408
408
|
font-style: italic;
|
|
409
409
|
}
|
|
@@ -423,7 +423,7 @@
|
|
|
423
423
|
|
|
424
424
|
.totalMeta {
|
|
425
425
|
font-size: 13px;
|
|
426
|
-
color: var(--theme-elevation-
|
|
426
|
+
color: var(--theme-elevation-650);
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
.totalChecked {
|
|
@@ -29,7 +29,7 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui,
|
|
|
29
29
|
|
|
30
30
|
.subtitle {
|
|
31
31
|
font-size: 13px;
|
|
32
|
-
color: var(--theme-elevation-
|
|
32
|
+
color: var(--theme-elevation-650);
|
|
33
33
|
margin: 4px 0 0;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -67,7 +67,7 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui,
|
|
|
67
67
|
letter-spacing: 0.04em;
|
|
68
68
|
border: none;
|
|
69
69
|
cursor: pointer;
|
|
70
|
-
color: var(--theme-elevation-
|
|
70
|
+
color: var(--theme-elevation-650);
|
|
71
71
|
background: none;
|
|
72
72
|
position: relative;
|
|
73
73
|
transition: color 100ms;
|
|
@@ -175,7 +175,7 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui,
|
|
|
175
175
|
|
|
176
176
|
.sessionPreview {
|
|
177
177
|
font-size: 12px;
|
|
178
|
-
color: var(--theme-elevation-
|
|
178
|
+
color: var(--theme-elevation-650);
|
|
179
179
|
white-space: nowrap;
|
|
180
180
|
overflow: hidden;
|
|
181
181
|
text-overflow: ellipsis;
|
|
@@ -233,7 +233,7 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui,
|
|
|
233
233
|
border-radius: 6px;
|
|
234
234
|
border: 1px solid var(--theme-elevation-200);
|
|
235
235
|
background: none;
|
|
236
|
-
color: var(--theme-elevation-
|
|
236
|
+
color: var(--theme-elevation-650);
|
|
237
237
|
font-weight: 600;
|
|
238
238
|
font-size: 12px;
|
|
239
239
|
cursor: pointer;
|
|
@@ -301,7 +301,7 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui,
|
|
|
301
301
|
border-color: var(--theme-elevation-200);
|
|
302
302
|
padding: 6px 14px;
|
|
303
303
|
font-size: 12px;
|
|
304
|
-
color: var(--theme-elevation-
|
|
304
|
+
color: var(--theme-elevation-650);
|
|
305
305
|
border-radius: 8px;
|
|
306
306
|
max-width: 80%;
|
|
307
307
|
}
|
|
@@ -339,7 +339,7 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui,
|
|
|
339
339
|
font-weight: 700;
|
|
340
340
|
margin-bottom: 4px;
|
|
341
341
|
opacity: 0.7;
|
|
342
|
-
color: var(--theme-elevation-
|
|
342
|
+
color: var(--theme-elevation-650);
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
.bubbleBody {
|
|
@@ -373,7 +373,7 @@ $font-stack: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui,
|
|
|
373
373
|
font-size: 12px;
|
|
374
374
|
font-weight: 600;
|
|
375
375
|
background: none;
|
|
376
|
-
color: var(--theme-elevation-
|
|
376
|
+
color: var(--theme-elevation-650);
|
|
377
377
|
cursor: pointer;
|
|
378
378
|
transition: all 80ms;
|
|
379
379
|
|
|
@@ -34,7 +34,7 @@ $red: #dc2626;
|
|
|
34
34
|
|
|
35
35
|
.subtitle {
|
|
36
36
|
font-size: 13px;
|
|
37
|
-
color: var(--theme-elevation-
|
|
37
|
+
color: var(--theme-elevation-650);
|
|
38
38
|
font-weight: 400;
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -125,7 +125,7 @@ $red: #dc2626;
|
|
|
125
125
|
|
|
126
126
|
.clientName {
|
|
127
127
|
font-size: 12px;
|
|
128
|
-
color: var(--theme-elevation-
|
|
128
|
+
color: var(--theme-elevation-650);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
.clientEmail {
|
|
@@ -181,7 +181,7 @@ $red: #dc2626;
|
|
|
181
181
|
|
|
182
182
|
.clientHeaderContact {
|
|
183
183
|
font-size: 14px;
|
|
184
|
-
color: var(--theme-elevation-
|
|
184
|
+
color: var(--theme-elevation-650);
|
|
185
185
|
margin-top: 4px;
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -199,7 +199,7 @@ $red: #dc2626;
|
|
|
199
199
|
background: var(--theme-elevation-50);
|
|
200
200
|
border-radius: 6px;
|
|
201
201
|
font-size: 12px;
|
|
202
|
-
color: var(--theme-elevation-
|
|
202
|
+
color: var(--theme-elevation-650);
|
|
203
203
|
font-style: italic;
|
|
204
204
|
}
|
|
205
205
|
|
|
@@ -281,7 +281,7 @@ $red: #dc2626;
|
|
|
281
281
|
|
|
282
282
|
.mergePanelDesc {
|
|
283
283
|
font-size: 12px;
|
|
284
|
-
color: var(--theme-elevation-
|
|
284
|
+
color: var(--theme-elevation-650);
|
|
285
285
|
margin: 0 0 10px;
|
|
286
286
|
line-height: 1.5;
|
|
287
287
|
}
|
|
@@ -328,13 +328,13 @@ $red: #dc2626;
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
.mergeResultName {
|
|
331
|
-
color: var(--theme-elevation-
|
|
331
|
+
color: var(--theme-elevation-650);
|
|
332
332
|
margin-left: 8px;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
.mergeEmpty {
|
|
336
336
|
font-size: 12px;
|
|
337
|
-
color: var(--theme-elevation-
|
|
337
|
+
color: var(--theme-elevation-650);
|
|
338
338
|
font-style: italic;
|
|
339
339
|
margin: 0;
|
|
340
340
|
}
|
|
@@ -355,7 +355,7 @@ $red: #dc2626;
|
|
|
355
355
|
font-size: 10px;
|
|
356
356
|
font-weight: 700;
|
|
357
357
|
text-transform: uppercase;
|
|
358
|
-
color: var(--theme-elevation-
|
|
358
|
+
color: var(--theme-elevation-650);
|
|
359
359
|
letter-spacing: 0.03em;
|
|
360
360
|
}
|
|
361
361
|
|
|
@@ -477,7 +477,7 @@ $red: #dc2626;
|
|
|
477
477
|
font-size: 10px;
|
|
478
478
|
font-weight: 600;
|
|
479
479
|
text-transform: uppercase;
|
|
480
|
-
color: var(--theme-elevation-
|
|
480
|
+
color: var(--theme-elevation-650);
|
|
481
481
|
border-bottom: 1px solid var(--theme-elevation-200);
|
|
482
482
|
letter-spacing: 0.03em;
|
|
483
483
|
}
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
font-weight: 700;
|
|
43
43
|
text-transform: uppercase;
|
|
44
44
|
letter-spacing: 0.06em;
|
|
45
|
-
color: var(--theme-elevation-
|
|
45
|
+
color: var(--theme-elevation-650);
|
|
46
46
|
margin-bottom: 6px;
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
.statUnit {
|
|
56
56
|
font-size: 14px;
|
|
57
57
|
font-weight: 500;
|
|
58
|
-
color: var(--theme-elevation-
|
|
58
|
+
color: var(--theme-elevation-650);
|
|
59
59
|
margin-left: 2px;
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
padding: 8px 14px;
|
|
79
79
|
font-size: 13px;
|
|
80
80
|
font-weight: 600;
|
|
81
|
-
color: var(--theme-elevation-
|
|
81
|
+
color: var(--theme-elevation-650);
|
|
82
82
|
background: none;
|
|
83
83
|
border: none;
|
|
84
84
|
border-bottom: 2px solid transparent;
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
background: none;
|
|
105
105
|
font-size: 12px;
|
|
106
106
|
font-weight: 600;
|
|
107
|
-
color: var(--theme-elevation-
|
|
107
|
+
color: var(--theme-elevation-650);
|
|
108
108
|
cursor: pointer;
|
|
109
109
|
transition: all 80ms;
|
|
110
110
|
&:hover { background: var(--theme-elevation-100); }
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
font-weight: 700;
|
|
142
142
|
text-transform: uppercase;
|
|
143
143
|
letter-spacing: 0.04em;
|
|
144
|
-
color: var(--theme-elevation-
|
|
144
|
+
color: var(--theme-elevation-650);
|
|
145
145
|
border-bottom: 1px solid var(--theme-elevation-200);
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
background: none;
|
|
218
218
|
font-size: 12px;
|
|
219
219
|
font-weight: 600;
|
|
220
|
-
color: var(--theme-elevation-
|
|
220
|
+
color: var(--theme-elevation-650);
|
|
221
221
|
cursor: pointer;
|
|
222
222
|
&:hover { background: var(--theme-elevation-100); }
|
|
223
223
|
&:disabled { opacity: 0.4; cursor: not-allowed; }
|