@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.
- 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/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 +6 -2
- package/scripts/uninstall-data.mjs +178 -0
- package/scripts/uninstall.mjs +184 -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,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import React, { useState, useEffect } from 'react';
|
|
4
|
-
import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from '
|
|
3
|
+
import React, { useState, useEffect, useId } from 'react';
|
|
4
|
+
import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from '../shared/icons.js';
|
|
5
5
|
import { btnStyle, V } from '../shared/adminTokens.js';
|
|
6
6
|
import { AdminViewHeader } from '../shared/AdminViewHeader.js';
|
|
7
7
|
import { DEFAULT_FEATURES } from '../../components/TicketConversation/config.js';
|
|
@@ -196,13 +196,16 @@ const CollapsibleSection = ({ title, icon, color, defaultOpen = true, children,
|
|
|
196
196
|
open && /* @__PURE__ */ jsx("div", { className: ts.sectionBody, children })
|
|
197
197
|
] });
|
|
198
198
|
};
|
|
199
|
-
const FieldRow = ({ label, description, children }) =>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
] })
|
|
199
|
+
const FieldRow = ({ label, description, children }) => {
|
|
200
|
+
const id = useId();
|
|
201
|
+
return /* @__PURE__ */ jsxs("div", { className: ts.fieldRow, children: [
|
|
202
|
+
/* @__PURE__ */ jsxs("div", { className: ts.fieldLabel, children: [
|
|
203
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, children: label }),
|
|
204
|
+
description && /* @__PURE__ */ jsx("div", { className: ts.fieldDescription, children: description })
|
|
205
|
+
] }),
|
|
206
|
+
/* @__PURE__ */ jsx("div", { className: ts.fieldContent, children: typeof children === "function" ? children(id) : children })
|
|
207
|
+
] });
|
|
208
|
+
};
|
|
206
209
|
const TicketingSettingsClient = () => {
|
|
207
210
|
const { t } = useTranslation();
|
|
208
211
|
const FEATURE_LIST = React.useMemo(() => getFeatureList(t), [t]);
|
|
@@ -283,7 +286,7 @@ const TicketingSettingsClient = () => {
|
|
|
283
286
|
title: t("settingsView.configTitle"),
|
|
284
287
|
subtitle: t("settings.subtitle", { enabled: String(enabledCount), total: String(totalCount) }),
|
|
285
288
|
actions: /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
286
|
-
/* @__PURE__ */ jsx("button", { onClick: handleReset, style: btnStyle(
|
|
289
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: handleReset, style: btnStyle(V.neutralBg, { small: true, fg: V.neutralFg }), children: t("settingsView.reset") }),
|
|
287
290
|
/* @__PURE__ */ jsx(
|
|
288
291
|
"button",
|
|
289
292
|
{
|
|
@@ -358,9 +361,10 @@ const TicketingSettingsClient = () => {
|
|
|
358
361
|
defaultOpen: false,
|
|
359
362
|
children: [
|
|
360
363
|
/* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.emailDescription") }),
|
|
361
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderAddress"), description: t("settingsView.senderAddressDesc"), children: /* @__PURE__ */ jsx(
|
|
364
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderAddress"), description: t("settingsView.senderAddressDesc"), children: (id) => /* @__PURE__ */ jsx(
|
|
362
365
|
"input",
|
|
363
366
|
{
|
|
367
|
+
id,
|
|
364
368
|
type: "email",
|
|
365
369
|
value: settings.email.fromAddress,
|
|
366
370
|
onChange: (e) => updateEmail("fromAddress", e.target.value),
|
|
@@ -368,9 +372,10 @@ const TicketingSettingsClient = () => {
|
|
|
368
372
|
className: ts.input
|
|
369
373
|
}
|
|
370
374
|
) }),
|
|
371
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderName"), description: t("settingsView.senderNameDesc"), children: /* @__PURE__ */ jsx(
|
|
375
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.senderName"), description: t("settingsView.senderNameDesc"), children: (id) => /* @__PURE__ */ jsx(
|
|
372
376
|
"input",
|
|
373
377
|
{
|
|
378
|
+
id,
|
|
374
379
|
type: "text",
|
|
375
380
|
value: settings.email.fromName,
|
|
376
381
|
onChange: (e) => updateEmail("fromName", e.target.value),
|
|
@@ -378,9 +383,10 @@ const TicketingSettingsClient = () => {
|
|
|
378
383
|
className: ts.input
|
|
379
384
|
}
|
|
380
385
|
) }),
|
|
381
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.replyTo"), description: t("settingsView.replyToDesc"), children: /* @__PURE__ */ jsx(
|
|
386
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.replyTo"), description: t("settingsView.replyToDesc"), children: (id) => /* @__PURE__ */ jsx(
|
|
382
387
|
"input",
|
|
383
388
|
{
|
|
389
|
+
id,
|
|
384
390
|
type: "email",
|
|
385
391
|
value: settings.email.replyToAddress,
|
|
386
392
|
onChange: (e) => updateEmail("replyToAddress", e.target.value),
|
|
@@ -389,18 +395,20 @@ const TicketingSettingsClient = () => {
|
|
|
389
395
|
}
|
|
390
396
|
) }),
|
|
391
397
|
/* @__PURE__ */ jsx("div", { className: ts.separator }),
|
|
392
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpServer"), description: t("settingsView.smtpServerDesc"), children: /* @__PURE__ */ jsx(
|
|
398
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpServer"), description: t("settingsView.smtpServerDesc"), children: (id) => /* @__PURE__ */ jsx(
|
|
393
399
|
"input",
|
|
394
400
|
{
|
|
401
|
+
id,
|
|
395
402
|
type: "text",
|
|
396
403
|
value: smtpHost,
|
|
397
404
|
readOnly: true,
|
|
398
405
|
className: ts.inputReadonly
|
|
399
406
|
}
|
|
400
407
|
) }),
|
|
401
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpPort"), children: /* @__PURE__ */ jsx(
|
|
408
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.smtpPort"), children: (id) => /* @__PURE__ */ jsx(
|
|
402
409
|
"input",
|
|
403
410
|
{
|
|
411
|
+
id,
|
|
404
412
|
type: "text",
|
|
405
413
|
value: smtpPort,
|
|
406
414
|
readOnly: true,
|
|
@@ -421,9 +429,10 @@ const TicketingSettingsClient = () => {
|
|
|
421
429
|
badge: features.ai ? /* @__PURE__ */ jsx("span", { className: ts.badge, style: { backgroundColor: "#dcfce7", color: "#166534" }, children: t("settingsView.aiBadgeActive") }) : /* @__PURE__ */ jsx("span", { className: ts.badge, style: { backgroundColor: "#fee2e2", color: "#991b1b" }, children: t("settingsView.aiBadgeInactive") }),
|
|
422
430
|
children: [
|
|
423
431
|
/* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.aiDescription") }),
|
|
424
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiProvider"), description: t("settingsView.aiProviderDesc"), children: /* @__PURE__ */ jsxs(
|
|
432
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiProvider"), description: t("settingsView.aiProviderDesc"), children: (id) => /* @__PURE__ */ jsxs(
|
|
425
433
|
"select",
|
|
426
434
|
{
|
|
435
|
+
id,
|
|
427
436
|
value: settings.ai.provider,
|
|
428
437
|
onChange: (e) => updateAI("provider", e.target.value),
|
|
429
438
|
className: ts.select,
|
|
@@ -435,10 +444,11 @@ const TicketingSettingsClient = () => {
|
|
|
435
444
|
]
|
|
436
445
|
}
|
|
437
446
|
) }),
|
|
438
|
-
settings.ai.provider !== "ollama" && /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiApiKey"), description: t("settingsView.aiApiKeyDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.apiKeyRow, children: [
|
|
447
|
+
settings.ai.provider !== "ollama" && /* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiApiKey"), description: t("settingsView.aiApiKeyDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.apiKeyRow, children: [
|
|
439
448
|
/* @__PURE__ */ jsx(
|
|
440
449
|
"input",
|
|
441
450
|
{
|
|
451
|
+
id,
|
|
442
452
|
type: showApiKey ? "text" : "password",
|
|
443
453
|
value: settings.ai.apiKey,
|
|
444
454
|
onChange: (e) => updateAI("apiKey", e.target.value),
|
|
@@ -456,9 +466,10 @@ const TicketingSettingsClient = () => {
|
|
|
456
466
|
}
|
|
457
467
|
)
|
|
458
468
|
] }) }),
|
|
459
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiModel"), description: t("settingsView.aiModelDesc"), children: /* @__PURE__ */ jsx(
|
|
469
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.aiModel"), description: t("settingsView.aiModelDesc"), children: (id) => /* @__PURE__ */ jsx(
|
|
460
470
|
"input",
|
|
461
471
|
{
|
|
472
|
+
id,
|
|
462
473
|
type: "text",
|
|
463
474
|
value: settings.ai.model,
|
|
464
475
|
onChange: (e) => updateAI("model", e.target.value),
|
|
@@ -500,10 +511,11 @@ const TicketingSettingsClient = () => {
|
|
|
500
511
|
defaultOpen: false,
|
|
501
512
|
children: [
|
|
502
513
|
/* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.slaDescription") }),
|
|
503
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaFirstResponse"), description: t("settingsView.slaFirstResponseDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
514
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaFirstResponse"), description: t("settingsView.slaFirstResponseDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
504
515
|
/* @__PURE__ */ jsx(
|
|
505
516
|
"input",
|
|
506
517
|
{
|
|
518
|
+
id,
|
|
507
519
|
type: "number",
|
|
508
520
|
min: 1,
|
|
509
521
|
value: settings.sla.firstResponseMinutes,
|
|
@@ -520,10 +532,11 @@ const TicketingSettingsClient = () => {
|
|
|
520
532
|
")"
|
|
521
533
|
] })
|
|
522
534
|
] }) }),
|
|
523
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaResolution"), description: t("settingsView.slaResolutionDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
535
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.slaResolution"), description: t("settingsView.slaResolutionDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
524
536
|
/* @__PURE__ */ jsx(
|
|
525
537
|
"input",
|
|
526
538
|
{
|
|
539
|
+
id,
|
|
527
540
|
type: "number",
|
|
528
541
|
min: 1,
|
|
529
542
|
value: settings.sla.resolutionMinutes,
|
|
@@ -556,9 +569,10 @@ const TicketingSettingsClient = () => {
|
|
|
556
569
|
] })
|
|
557
570
|
] }),
|
|
558
571
|
/* @__PURE__ */ jsx("div", { className: ts.separator }),
|
|
559
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.escalationEmail"), description: t("settingsView.escalationEmailDesc"), children: /* @__PURE__ */ jsx(
|
|
572
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.escalationEmail"), description: t("settingsView.escalationEmailDesc"), children: (id) => /* @__PURE__ */ jsx(
|
|
560
573
|
"input",
|
|
561
574
|
{
|
|
575
|
+
id,
|
|
562
576
|
type: "email",
|
|
563
577
|
value: settings.sla.escalationEmail,
|
|
564
578
|
onChange: (e) => updateSLA("escalationEmail", e.target.value),
|
|
@@ -594,10 +608,11 @@ const TicketingSettingsClient = () => {
|
|
|
594
608
|
/* @__PURE__ */ jsx("span", { className: ts.inlineLabel, children: t("settingsView.autoCloseEnable") })
|
|
595
609
|
] }),
|
|
596
610
|
settings.autoClose.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
597
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseDelay"), description: t("settingsView.autoCloseDelayDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
611
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseDelay"), description: t("settingsView.autoCloseDelayDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
598
612
|
/* @__PURE__ */ jsx(
|
|
599
613
|
"input",
|
|
600
614
|
{
|
|
615
|
+
id,
|
|
601
616
|
type: "number",
|
|
602
617
|
min: 1,
|
|
603
618
|
max: 90,
|
|
@@ -608,10 +623,11 @@ const TicketingSettingsClient = () => {
|
|
|
608
623
|
),
|
|
609
624
|
/* @__PURE__ */ jsx("span", { className: ts.slaHint, children: t("settingsView.days") })
|
|
610
625
|
] }) }),
|
|
611
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseReminder"), description: t("settingsView.autoCloseReminderDesc"), children: /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
626
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.autoCloseReminder"), description: t("settingsView.autoCloseReminderDesc"), children: (id) => /* @__PURE__ */ jsxs("div", { className: ts.slaInline, children: [
|
|
612
627
|
/* @__PURE__ */ jsx(
|
|
613
628
|
"input",
|
|
614
629
|
{
|
|
630
|
+
id,
|
|
615
631
|
type: "number",
|
|
616
632
|
min: 1,
|
|
617
633
|
max: settings.autoClose.daysBeforeClose - 1,
|
|
@@ -646,9 +662,10 @@ const TicketingSettingsClient = () => {
|
|
|
646
662
|
defaultOpen: false,
|
|
647
663
|
children: [
|
|
648
664
|
/* @__PURE__ */ jsx("p", { className: ts.sectionDescription, children: t("settingsView.localeDescription") }),
|
|
649
|
-
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.language"), description: t("settingsView.languageDesc"), children: /* @__PURE__ */ jsxs(
|
|
665
|
+
/* @__PURE__ */ jsx(FieldRow, { label: t("settingsView.language"), description: t("settingsView.languageDesc"), children: (id) => /* @__PURE__ */ jsxs(
|
|
650
666
|
"select",
|
|
651
667
|
{
|
|
668
|
+
id,
|
|
652
669
|
value: settings.locale.language,
|
|
653
670
|
onChange: (e) => updateLocale("language", e.target.value),
|
|
654
671
|
className: ts.select,
|
|
@@ -673,6 +690,7 @@ const TicketingSettingsClient = () => {
|
|
|
673
690
|
/* @__PURE__ */ jsx(
|
|
674
691
|
"textarea",
|
|
675
692
|
{
|
|
693
|
+
"aria-label": t("settingsView.signatureTitle"),
|
|
676
694
|
value: signature,
|
|
677
695
|
onChange: (e) => {
|
|
678
696
|
setSignature(e.target.value);
|
|
@@ -710,7 +728,7 @@ const TicketingSettingsClient = () => {
|
|
|
710
728
|
alert(t("import.errorTitle"));
|
|
711
729
|
}
|
|
712
730
|
},
|
|
713
|
-
style: { ...btnStyle(opt.days === 0 ?
|
|
731
|
+
style: { ...btnStyle(opt.days === 0 ? V.red : V.neutralBg, { small: true, fg: opt.days === 0 ? void 0 : V.neutralFg }), fontSize: 11 },
|
|
714
732
|
children: opt.label
|
|
715
733
|
},
|
|
716
734
|
opt.days
|
|
@@ -718,7 +736,7 @@ const TicketingSettingsClient = () => {
|
|
|
718
736
|
] }, col)) })
|
|
719
737
|
] }),
|
|
720
738
|
/* @__PURE__ */ jsxs("div", { className: ts.bottomBar, children: [
|
|
721
|
-
/* @__PURE__ */ jsx("button", { onClick: handleReset, style: btnStyle(
|
|
739
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: handleReset, style: btnStyle(V.neutralBg, { small: true, fg: V.neutralFg }), children: t("settingsView.resetAll") }),
|
|
722
740
|
/* @__PURE__ */ jsx(
|
|
723
741
|
"button",
|
|
724
742
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import React, { useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import React, { useId, useState, useCallback, useEffect } from 'react';
|
|
4
4
|
import { SkeletonDashboard } from '../shared/Skeleton.js';
|
|
5
5
|
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
|
|
6
6
|
import styles from '../../styles/TimeDashboard.module.scss';
|
|
@@ -25,6 +25,9 @@ function getMonthRange(offset) {
|
|
|
25
25
|
const MONTHS_FR = ["Jan", "Fev", "Mar", "Avr", "Mai", "Jun", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"];
|
|
26
26
|
const TimeDashboardClient = () => {
|
|
27
27
|
const { t } = useTranslation();
|
|
28
|
+
const fromId = useId();
|
|
29
|
+
const toId = useId();
|
|
30
|
+
const groupById = useId();
|
|
28
31
|
const [entries, setEntries] = useState([]);
|
|
29
32
|
const [loading, setLoading] = useState(true);
|
|
30
33
|
const [from, setFrom] = useState(() => getMonthRange(0).from);
|
|
@@ -108,16 +111,16 @@ const TimeDashboardClient = () => {
|
|
|
108
111
|
] }),
|
|
109
112
|
/* @__PURE__ */ jsxs("div", { className: styles.filterRow, children: [
|
|
110
113
|
/* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
|
|
111
|
-
/* @__PURE__ */ jsx("label", { className: styles.label, children: t("timeDashboard.filters.from") }),
|
|
112
|
-
/* @__PURE__ */ jsx("input", { type: "date", value: from, onChange: (e) => setFrom(e.target.value), className: styles.input })
|
|
114
|
+
/* @__PURE__ */ jsx("label", { className: styles.label, htmlFor: fromId, children: t("timeDashboard.filters.from") }),
|
|
115
|
+
/* @__PURE__ */ jsx("input", { id: fromId, type: "date", value: from, onChange: (e) => setFrom(e.target.value), className: styles.input })
|
|
113
116
|
] }),
|
|
114
117
|
/* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
|
|
115
|
-
/* @__PURE__ */ jsx("label", { className: styles.label, children: t("timeDashboard.filters.to") }),
|
|
116
|
-
/* @__PURE__ */ jsx("input", { type: "date", value: to, onChange: (e) => setTo(e.target.value), className: styles.input })
|
|
118
|
+
/* @__PURE__ */ jsx("label", { className: styles.label, htmlFor: toId, children: t("timeDashboard.filters.to") }),
|
|
119
|
+
/* @__PURE__ */ jsx("input", { id: toId, type: "date", value: to, onChange: (e) => setTo(e.target.value), className: styles.input })
|
|
117
120
|
] }),
|
|
118
121
|
/* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
|
|
119
|
-
/* @__PURE__ */ jsx("label", { className: styles.label, children: t("timeDashboard.filters.groupBy") }),
|
|
120
|
-
/* @__PURE__ */ jsxs("select", { value: groupBy, onChange: (e) => setGroupBy(e.target.value), className: styles.select, children: [
|
|
122
|
+
/* @__PURE__ */ jsx("label", { className: styles.label, htmlFor: groupById, children: t("timeDashboard.filters.groupBy") }),
|
|
123
|
+
/* @__PURE__ */ jsxs("select", { id: groupById, value: groupBy, onChange: (e) => setGroupBy(e.target.value), className: styles.select, children: [
|
|
121
124
|
/* @__PURE__ */ jsx("option", { value: "day", children: t("timeDashboard.filters.day") }),
|
|
122
125
|
/* @__PURE__ */ jsx("option", { value: "week", children: t("timeDashboard.filters.week") }),
|
|
123
126
|
/* @__PURE__ */ jsx("option", { value: "project", children: t("timeDashboard.filters.project") })
|
|
@@ -3,15 +3,50 @@ interface ErrorBoundaryProps {
|
|
|
3
3
|
children: React.ReactNode;
|
|
4
4
|
fallback?: React.ReactNode;
|
|
5
5
|
viewName?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Values that identify the input the subtree renders from (a ticket id, a
|
|
8
|
+
* filter, a page number...). When any of them changes the boundary clears
|
|
9
|
+
* the error on its own, because the new input may well render fine.
|
|
10
|
+
*/
|
|
11
|
+
resetKeys?: unknown[];
|
|
6
12
|
}
|
|
7
13
|
interface ErrorBoundaryState {
|
|
8
14
|
hasError: boolean;
|
|
9
15
|
error: Error | null;
|
|
16
|
+
/**
|
|
17
|
+
* Bumped on every reset. It is used as the `key` of the wrapper around the
|
|
18
|
+
* children so that a retry REMOUNTS the subtree. Clearing `hasError` alone
|
|
19
|
+
* keeps the broken component instance and its state, so anything that threw
|
|
20
|
+
* from a bad piece of state throws again on the very next render.
|
|
21
|
+
*/
|
|
22
|
+
resetCount: number;
|
|
10
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Shallow comparison of two reset-key arrays. A missing array is treated as
|
|
26
|
+
* empty, so a boundary declared without `resetKeys` never auto-resets.
|
|
27
|
+
*/
|
|
28
|
+
export declare function haveResetKeysChanged(previous?: unknown[], next?: unknown[]): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Default fallback UI.
|
|
31
|
+
*
|
|
32
|
+
* Exported so it can be rendered (and asserted on) in isolation. It is the
|
|
33
|
+
* only screen a user sees when a view is broken, so it must stay readable in
|
|
34
|
+
* both themes: every colour comes from a Payload theme token, never a hex
|
|
35
|
+
* literal that only works on a light background.
|
|
36
|
+
*
|
|
37
|
+
* The thrown message is deliberately NOT displayed — it can carry a stack
|
|
38
|
+
* fragment, an internal path or a raw API payload. It is logged to the console
|
|
39
|
+
* by `componentDidCatch` instead.
|
|
40
|
+
*/
|
|
41
|
+
export declare function AdminErrorFallback({ onRetry }: {
|
|
42
|
+
onRetry: () => void;
|
|
43
|
+
}): React.ReactElement;
|
|
11
44
|
export declare class AdminErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
12
45
|
constructor(props: ErrorBoundaryProps);
|
|
13
|
-
static getDerivedStateFromError(error: Error): ErrorBoundaryState
|
|
46
|
+
static getDerivedStateFromError(error: Error): Pick<ErrorBoundaryState, 'hasError' | 'error'>;
|
|
14
47
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
|
|
48
|
+
componentDidUpdate(prevProps: ErrorBoundaryProps): void;
|
|
49
|
+
reset: () => void;
|
|
15
50
|
render(): React.ReactNode;
|
|
16
51
|
}
|
|
17
52
|
export {};
|
|
@@ -1,11 +1,53 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import React from 'react';
|
|
4
|
+
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
|
|
4
5
|
|
|
6
|
+
function haveResetKeysChanged(previous, next) {
|
|
7
|
+
const a = previous ?? [];
|
|
8
|
+
const b = next ?? [];
|
|
9
|
+
if (a.length !== b.length) return true;
|
|
10
|
+
return a.some((value, index) => !Object.is(value, b[index]));
|
|
11
|
+
}
|
|
12
|
+
function AdminErrorFallback({ onRetry }) {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
return /* @__PURE__ */ jsxs(
|
|
15
|
+
"div",
|
|
16
|
+
{
|
|
17
|
+
role: "alert",
|
|
18
|
+
style: {
|
|
19
|
+
padding: "40px",
|
|
20
|
+
textAlign: "center",
|
|
21
|
+
color: "var(--theme-error-550)"
|
|
22
|
+
},
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsx("h2", { style: { marginBottom: "16px", fontSize: "18px" }, children: t("errorBoundary.title") }),
|
|
25
|
+
/* @__PURE__ */ jsx("p", { style: { marginBottom: "24px", color: "var(--theme-elevation-650)", fontSize: "14px" }, children: t("errorBoundary.description") }),
|
|
26
|
+
/* @__PURE__ */ jsx(
|
|
27
|
+
"button",
|
|
28
|
+
{
|
|
29
|
+
type: "button",
|
|
30
|
+
onClick: onRetry,
|
|
31
|
+
style: {
|
|
32
|
+
padding: "8px 20px",
|
|
33
|
+
backgroundColor: "var(--theme-success-550)",
|
|
34
|
+
color: "var(--theme-elevation-0)",
|
|
35
|
+
border: "none",
|
|
36
|
+
borderRadius: "6px",
|
|
37
|
+
cursor: "pointer",
|
|
38
|
+
fontSize: "14px"
|
|
39
|
+
},
|
|
40
|
+
children: t("errorBoundary.retry")
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
5
47
|
class AdminErrorBoundary extends React.Component {
|
|
6
48
|
constructor(props) {
|
|
7
49
|
super(props);
|
|
8
|
-
this.state = { hasError: false, error: null };
|
|
50
|
+
this.state = { hasError: false, error: null, resetCount: 0 };
|
|
9
51
|
}
|
|
10
52
|
static getDerivedStateFromError(error) {
|
|
11
53
|
return { hasError: true, error };
|
|
@@ -13,38 +55,27 @@ class AdminErrorBoundary extends React.Component {
|
|
|
13
55
|
componentDidCatch(error, errorInfo) {
|
|
14
56
|
console.error(`[${this.props.viewName || "AdminView"}] Error:`, error, errorInfo);
|
|
15
57
|
}
|
|
58
|
+
componentDidUpdate(prevProps) {
|
|
59
|
+
if (!this.state.hasError) return;
|
|
60
|
+
if (!haveResetKeysChanged(prevProps.resetKeys, this.props.resetKeys)) return;
|
|
61
|
+
this.reset();
|
|
62
|
+
}
|
|
63
|
+
reset = () => {
|
|
64
|
+
this.setState((prev) => ({
|
|
65
|
+
hasError: false,
|
|
66
|
+
error: null,
|
|
67
|
+
resetCount: prev.resetCount + 1
|
|
68
|
+
}));
|
|
69
|
+
};
|
|
16
70
|
render() {
|
|
17
71
|
if (this.state.hasError) {
|
|
18
72
|
if (this.props.fallback) {
|
|
19
73
|
return this.props.fallback;
|
|
20
74
|
}
|
|
21
|
-
return /* @__PURE__ */
|
|
22
|
-
padding: "40px",
|
|
23
|
-
textAlign: "center",
|
|
24
|
-
color: "#dc2626"
|
|
25
|
-
}, children: [
|
|
26
|
-
/* @__PURE__ */ jsx("h2", { style: { marginBottom: "16px", fontSize: "18px" }, children: "Une erreur est survenue" }),
|
|
27
|
-
/* @__PURE__ */ jsx("p", { style: { marginBottom: "24px", color: "#6b7280", fontSize: "14px" }, children: this.state.error?.message || "Erreur inattendue" }),
|
|
28
|
-
/* @__PURE__ */ jsx(
|
|
29
|
-
"button",
|
|
30
|
-
{
|
|
31
|
-
onClick: () => this.setState({ hasError: false, error: null }),
|
|
32
|
-
style: {
|
|
33
|
-
padding: "8px 20px",
|
|
34
|
-
backgroundColor: "#2563eb",
|
|
35
|
-
color: "#fff",
|
|
36
|
-
border: "none",
|
|
37
|
-
borderRadius: "6px",
|
|
38
|
-
cursor: "pointer",
|
|
39
|
-
fontSize: "14px"
|
|
40
|
-
},
|
|
41
|
-
children: "Reessayer"
|
|
42
|
-
}
|
|
43
|
-
)
|
|
44
|
-
] });
|
|
75
|
+
return /* @__PURE__ */ jsx(AdminErrorFallback, { onRetry: this.reset });
|
|
45
76
|
}
|
|
46
|
-
return this.props.children;
|
|
77
|
+
return /* @__PURE__ */ jsx(React.Fragment, { children: this.props.children }, this.state.resetCount);
|
|
47
78
|
}
|
|
48
79
|
}
|
|
49
80
|
|
|
50
|
-
export { AdminErrorBoundary };
|
|
81
|
+
export { AdminErrorBoundary, AdminErrorFallback, haveResetKeysChanged };
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
export declare const V: {
|
|
3
3
|
readonly text: "var(--theme-text)";
|
|
4
|
-
readonly textSecondary: "var(--theme-elevation-
|
|
4
|
+
readonly textSecondary: "var(--theme-elevation-650)";
|
|
5
5
|
readonly bg: "var(--theme-elevation-50)";
|
|
6
6
|
readonly bgCard: "var(--theme-elevation-100)";
|
|
7
7
|
readonly border: "var(--theme-elevation-300)";
|
|
8
8
|
readonly blue: "#2563eb";
|
|
9
|
-
readonly amber: "#
|
|
10
|
-
readonly orange: "#
|
|
11
|
-
readonly green: "#
|
|
9
|
+
readonly amber: "#b45309";
|
|
10
|
+
readonly orange: "#c2410c";
|
|
11
|
+
readonly green: "#15803d";
|
|
12
12
|
readonly red: "#dc2626";
|
|
13
|
+
readonly neutralBg: "var(--theme-elevation-150)";
|
|
14
|
+
readonly neutralFg: "var(--theme-text)";
|
|
13
15
|
readonly cyan: "#2563eb";
|
|
14
|
-
readonly yellow: "#
|
|
16
|
+
readonly yellow: "#b45309";
|
|
15
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Button style factory.
|
|
20
|
+
*
|
|
21
|
+
* `bg` is expected to be one of the fixed hex values of `V`; the default
|
|
22
|
+
* foreground is white, which reaches AA on all of them. Pass `fg` whenever
|
|
23
|
+
* `bg` is a theme variable, since white only works on one of the two themes.
|
|
24
|
+
*/
|
|
16
25
|
export declare const btnStyle: (bg: string, opts?: {
|
|
17
26
|
disabled?: boolean;
|
|
18
27
|
small?: boolean;
|
|
28
|
+
fg?: string;
|
|
19
29
|
}) => React.CSSProperties;
|
|
@@ -1,25 +1,41 @@
|
|
|
1
1
|
const V = {
|
|
2
2
|
text: "var(--theme-text)",
|
|
3
|
-
|
|
3
|
+
// elevation-650, not elevation-500: Payload's dark theme does not redefine
|
|
4
|
+
// the 500 step (colors.scss jumps from 450 to 550), so elevation-500 stays
|
|
5
|
+
// rgb(128,128,128) in both themes — 3.62:1 on a light background and 4.03:1
|
|
6
|
+
// on a dark one, below WCAG AA in both. elevation-650 gives 6.63:1 / 9.03:1.
|
|
7
|
+
textSecondary: "var(--theme-elevation-650)",
|
|
4
8
|
bg: "var(--theme-elevation-50)",
|
|
5
9
|
bgCard: "var(--theme-elevation-100)",
|
|
6
10
|
border: "var(--theme-elevation-300)",
|
|
7
|
-
// Professional palette
|
|
11
|
+
// Professional palette. These are fixed hex values (they do not follow the
|
|
12
|
+
// admin theme), so they are only ever used as a background behind white
|
|
13
|
+
// text; each one is dark enough for white to reach at least 4.5:1.
|
|
8
14
|
blue: "#2563eb",
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
// white on it: 5.17:1
|
|
16
|
+
amber: "#b45309",
|
|
17
|
+
// white on it: 5.02:1
|
|
18
|
+
orange: "#c2410c",
|
|
19
|
+
// white on it: 5.18:1
|
|
20
|
+
green: "#15803d",
|
|
21
|
+
// white on it: 5.02:1
|
|
12
22
|
red: "#dc2626",
|
|
23
|
+
// white on it: 4.83:1
|
|
24
|
+
// Neutral / secondary button. Both halves follow the admin theme, so they
|
|
25
|
+
// must be used together — white on a theme token is unreadable in one of
|
|
26
|
+
// the two themes whichever token is picked.
|
|
27
|
+
neutralBg: "var(--theme-elevation-150)",
|
|
28
|
+
neutralFg: "var(--theme-text)",
|
|
13
29
|
// Legacy aliases (backward compat for views not yet updated)
|
|
14
30
|
cyan: "#2563eb",
|
|
15
|
-
yellow: "#
|
|
31
|
+
yellow: "#b45309"
|
|
16
32
|
};
|
|
17
33
|
const btnStyle = (bg, opts) => ({
|
|
18
34
|
padding: opts?.small ? "6px 12px" : "8px 14px",
|
|
19
35
|
borderRadius: 6,
|
|
20
36
|
border: `1px solid var(--theme-elevation-300)`,
|
|
21
37
|
backgroundColor: bg,
|
|
22
|
-
color: "#fff",
|
|
38
|
+
color: opts?.fg ?? "#fff",
|
|
23
39
|
fontWeight: 600,
|
|
24
40
|
fontSize: opts?.small ? 12 : 13,
|
|
25
41
|
cursor: opts?.disabled ? "not-allowed" : "pointer",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inlined Lucide icons.
|
|
3
|
+
*
|
|
4
|
+
* The plugin used to require `lucide-react` as a peer dependency for exactly
|
|
5
|
+
* 15 icons across two admin views. That made every integrator install an icon set,
|
|
6
|
+
* and a missing install a build-time failure, for 15 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
|
+
import React from 'react';
|
|
31
|
+
export interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, 'width' | 'height'> {
|
|
32
|
+
/** Rendered width and height. Lucide's own default is 24. */
|
|
33
|
+
size?: number | string;
|
|
34
|
+
}
|
|
35
|
+
export declare const Bot: React.FC<IconProps>;
|
|
36
|
+
export declare const ChevronDown: React.FC<IconProps>;
|
|
37
|
+
export declare const ChevronUp: React.FC<IconProps>;
|
|
38
|
+
export declare const Clock: React.FC<IconProps>;
|
|
39
|
+
export declare const FileSignature: React.FC<IconProps>;
|
|
40
|
+
export declare const Globe: React.FC<IconProps>;
|
|
41
|
+
export declare const Inbox: React.FC<IconProps>;
|
|
42
|
+
export declare const Link2: React.FC<IconProps>;
|
|
43
|
+
export declare const Mail: React.FC<IconProps>;
|
|
44
|
+
export declare const Paperclip: React.FC<IconProps>;
|
|
45
|
+
export declare const Plus: React.FC<IconProps>;
|
|
46
|
+
export declare const Search: React.FC<IconProps>;
|
|
47
|
+
export declare const Settings: React.FC<IconProps>;
|
|
48
|
+
export declare const Timer: React.FC<IconProps>;
|
|
49
|
+
export declare const X: React.FC<IconProps>;
|