@consilioweb/payload-support 2.0.1 → 3.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 +636 -402
- package/dist/components/RichTextEditor/index.d.ts +19 -0
- package/dist/components/TicketConversation/RewriteDropdown.d.ts +6 -0
- package/dist/components/TicketConversation/SkeletonText.d.ts +4 -0
- package/dist/components/TicketConversation/components/AISummaryPanel.d.ts +14 -0
- package/dist/components/TicketConversation/components/ActionPanels.d.ts +47 -0
- package/dist/components/TicketConversation/components/ActivityLog.d.ts +5 -0
- package/dist/components/TicketConversation/components/ClientBar.d.ts +5 -0
- package/dist/components/TicketConversation/components/ClientHistory.d.ts +24 -0
- package/dist/components/TicketConversation/components/CodeBlock.d.ts +26 -0
- package/dist/components/TicketConversation/components/CodeBlockInserter.d.ts +12 -0
- package/dist/components/TicketConversation/components/QuickActions.d.ts +32 -0
- package/dist/components/TicketConversation/components/TicketHeader.d.ts +19 -0
- package/dist/components/TicketConversation/components/TimeTrackingPanel.d.ts +24 -0
- package/dist/components/TicketConversation/config.d.ts +10 -0
- package/dist/components/TicketConversation/config.js +1 -40
- package/dist/components/TicketConversation/constants.d.ts +56 -0
- package/dist/components/TicketConversation/context.d.ts +53 -0
- package/dist/components/TicketConversation/hooks/useAI.d.ts +21 -0
- package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.d.ts +3 -0
- package/dist/components/TicketConversation/hooks/useFeatures.d.ts +23 -0
- package/dist/components/TicketConversation/hooks/useFeatures.js +61 -0
- package/dist/components/TicketConversation/hooks/useMessageActions.d.ts +20 -0
- package/dist/components/TicketConversation/hooks/useReply.d.ts +27 -0
- package/dist/components/TicketConversation/hooks/useTicketActions.d.ts +58 -0
- package/dist/components/TicketConversation/hooks/useTimeTracking.d.ts +18 -0
- package/dist/components/TicketConversation/hooks/useTranslation.d.ts +33 -0
- package/dist/components/TicketConversation/index.d.ts +4 -0
- package/dist/components/TicketConversation/index.js +2 -2
- package/dist/components/TicketConversation/types.d.ts +56 -0
- package/dist/components/TicketConversation/utils.d.ts +2 -0
- package/dist/index.cjs +392 -320
- package/dist/index.d.cts +100 -3
- package/dist/index.d.ts +100 -3
- package/dist/index.js +388 -321
- package/dist/utils/features.d.ts +123 -0
- package/dist/utils/features.js +147 -0
- package/dist/views/BillingView/client.d.ts +2 -0
- package/dist/views/BillingView/index.d.ts +4 -0
- package/dist/views/ChatView/client.d.ts +2 -0
- package/dist/views/ChatView/client.js +9 -9
- package/dist/views/ChatView/index.d.ts +4 -0
- package/dist/views/CrmView/client.d.ts +2 -0
- package/dist/views/CrmView/client.js +6 -9
- package/dist/views/CrmView/index.d.ts +4 -0
- package/dist/views/EmailTrackingView/client.d.ts +2 -0
- package/dist/views/EmailTrackingView/index.d.ts +4 -0
- package/dist/views/ImportConversationView/client.d.ts +2 -0
- package/dist/views/ImportConversationView/index.d.ts +4 -0
- package/dist/views/LogsView/client.d.ts +2 -0
- package/dist/views/LogsView/index.d.ts +4 -0
- package/dist/views/NewTicketView/client.d.ts +2 -0
- package/dist/views/NewTicketView/index.d.ts +4 -0
- package/dist/views/PendingEmailsView/client.d.ts +2 -0
- package/dist/views/PendingEmailsView/index.d.ts +4 -0
- package/dist/views/SupportDashboardView/client.d.ts +2 -0
- package/dist/views/SupportDashboardView/index.d.ts +4 -0
- package/dist/views/TicketDetailView/NextActionItem.d.ts +6 -0
- package/dist/views/TicketDetailView/RewriteDropdown.d.ts +7 -0
- package/dist/views/TicketDetailView/client.d.ts +2 -0
- package/dist/views/TicketDetailView/client.js +2 -2
- package/dist/views/TicketDetailView/constants.d.ts +13 -0
- package/dist/views/TicketDetailView/helpers.d.ts +2 -0
- package/dist/views/TicketDetailView/index.d.ts +4 -0
- package/dist/views/TicketDetailView/types.d.ts +40 -0
- package/dist/views/TicketInboxView/client.d.ts +2 -0
- package/dist/views/TicketInboxView/client.js +5 -5
- package/dist/views/TicketInboxView/index.d.ts +4 -0
- package/dist/views/TicketingSettingsView/client.d.ts +2 -0
- package/dist/views/TicketingSettingsView/client.js +5 -4
- package/dist/views/TicketingSettingsView/index.d.ts +4 -0
- package/dist/views/TimeDashboardView/client.d.ts +2 -0
- package/dist/views/TimeDashboardView/index.d.ts +4 -0
- package/dist/views/shared/AdminViewHeader.d.ts +13 -0
- package/dist/views/shared/ErrorBoundary.d.ts +17 -0
- package/dist/views/shared/Skeleton.d.ts +21 -0
- package/dist/views/shared/StatusPill.d.ts +8 -0
- package/dist/views/shared/adminTokens.d.ts +19 -0
- package/dist/views/shared/config.d.ts +8 -0
- package/dist/views/shared/config.js +1 -40
- package/dist/views/shared/dateLocale.d.ts +5 -0
- package/dist/views/shared/index.d.ts +12 -0
- package/dist/views/shared/index.js +2 -1
- package/dist/views/shared/sla.d.ts +14 -0
- package/dist/views.d.ts +13 -13
- package/package.json +16 -31
- package/src/collections/SupportClients.ts +29 -0
- package/src/collections/TicketCollaborators.ts +6 -1
- package/src/collections/TicketMessages.ts +46 -24
- package/src/collections/Tickets.ts +117 -79
- package/src/collections/TimeEntries.ts +57 -32
- package/src/components/TicketConversation/config.ts +16 -80
- package/src/components/TicketConversation/hooks/useFeatures.ts +109 -0
- package/src/components/TicketConversation/index.tsx +2 -2
- package/src/endpoints/ai.ts +2 -1
- package/src/endpoints/client-intelligence.ts +2 -1
- package/src/endpoints/process-scheduled.ts +13 -8
- package/src/endpoints/round-robin-config.ts +30 -20
- package/src/endpoints/settings.ts +28 -77
- package/src/index.ts +6 -2
- package/src/portal/LiveChat.tsx +33 -22
- package/src/portal/auth/tickets/detail/TicketPolling.tsx +5 -1
- package/src/types/lucide-react.d.ts +10 -2
- package/src/utils/aiAgent.ts +2 -1
- package/src/utils/aiProvider.ts +21 -0
- package/src/utils/features.ts +297 -0
- package/src/utils/fireWebhooks.ts +6 -0
- package/src/utils/generateTicketSynthesis.ts +2 -1
- package/src/utils/index.ts +4 -2
- package/src/utils/readSettings.ts +82 -9
- package/src/utils/webhookDispatcher.ts +19 -7
- package/src/views/ChatView/client.tsx +13 -9
- package/src/views/CrmView/client.tsx +10 -11
- package/src/views/TicketDetailView/client.tsx +2 -2
- package/src/views/TicketInboxView/client.tsx +10 -5
- package/src/views/TicketingSettingsView/client.tsx +8 -6
- package/src/views/shared/config.ts +14 -80
- package/src/views/shared/index.ts +3 -1
- package/dist/components/RichTextEditor/index.cjs +0 -279
- package/dist/components/TicketConversation/RewriteDropdown.cjs +0 -98
- package/dist/components/TicketConversation/SkeletonText.cjs +0 -21
- package/dist/components/TicketConversation/components/AISummaryPanel.cjs +0 -85
- package/dist/components/TicketConversation/components/ActionPanels.cjs +0 -193
- package/dist/components/TicketConversation/components/ActivityLog.cjs +0 -24
- package/dist/components/TicketConversation/components/ClientBar.cjs +0 -43
- package/dist/components/TicketConversation/components/ClientHistory.cjs +0 -138
- package/dist/components/TicketConversation/components/CodeBlock.cjs +0 -171
- package/dist/components/TicketConversation/components/CodeBlockInserter.cjs +0 -156
- package/dist/components/TicketConversation/components/QuickActions.cjs +0 -87
- package/dist/components/TicketConversation/components/TicketHeader.cjs +0 -93
- package/dist/components/TicketConversation/components/TimeTrackingPanel.cjs +0 -134
- package/dist/components/TicketConversation/config.cjs +0 -44
- package/dist/components/TicketConversation/constants.cjs +0 -127
- package/dist/components/TicketConversation/context.cjs +0 -13
- package/dist/components/TicketConversation/hooks/useAI.cjs +0 -196
- package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.cjs +0 -20
- package/dist/components/TicketConversation/hooks/useMessageActions.cjs +0 -136
- package/dist/components/TicketConversation/hooks/useReply.cjs +0 -188
- package/dist/components/TicketConversation/hooks/useTicketActions.cjs +0 -290
- package/dist/components/TicketConversation/hooks/useTimeTracking.cjs +0 -123
- package/dist/components/TicketConversation/hooks/useTranslation.cjs +0 -101
- package/dist/components/TicketConversation/index.cjs +0 -1114
- package/dist/components/TicketConversation/types.cjs +0 -2
- package/dist/components/TicketConversation/utils.cjs +0 -27
- package/dist/views/BillingView/client.cjs +0 -469
- package/dist/views/BillingView/index.cjs +0 -34
- package/dist/views/ChatView/client.cjs +0 -301
- package/dist/views/ChatView/index.cjs +0 -34
- package/dist/views/CrmView/client.cjs +0 -458
- package/dist/views/CrmView/index.cjs +0 -34
- package/dist/views/EmailTrackingView/client.cjs +0 -171
- package/dist/views/EmailTrackingView/index.cjs +0 -34
- package/dist/views/ImportConversationView/client.cjs +0 -238
- package/dist/views/ImportConversationView/index.cjs +0 -34
- package/dist/views/LogsView/client.cjs +0 -148
- package/dist/views/LogsView/index.cjs +0 -32
- package/dist/views/NewTicketView/client.cjs +0 -213
- package/dist/views/NewTicketView/index.cjs +0 -32
- package/dist/views/PendingEmailsView/client.cjs +0 -471
- package/dist/views/PendingEmailsView/index.cjs +0 -34
- package/dist/views/SupportDashboardView/client.cjs +0 -437
- package/dist/views/SupportDashboardView/index.cjs +0 -34
- package/dist/views/TicketDetailView/NextActionItem.cjs +0 -40
- package/dist/views/TicketDetailView/RewriteDropdown.cjs +0 -80
- package/dist/views/TicketDetailView/client.cjs +0 -1412
- package/dist/views/TicketDetailView/constants.cjs +0 -20
- package/dist/views/TicketDetailView/helpers.cjs +0 -23
- package/dist/views/TicketDetailView/index.cjs +0 -34
- package/dist/views/TicketDetailView/types.cjs +0 -2
- package/dist/views/TicketInboxView/client.cjs +0 -359
- package/dist/views/TicketInboxView/index.cjs +0 -32
- package/dist/views/TicketingSettingsView/client.cjs +0 -740
- package/dist/views/TicketingSettingsView/index.cjs +0 -34
- package/dist/views/TimeDashboardView/client.cjs +0 -180
- package/dist/views/TimeDashboardView/index.cjs +0 -34
- package/dist/views/shared/AdminViewHeader.cjs +0 -68
- package/dist/views/shared/ErrorBoundary.cjs +0 -55
- package/dist/views/shared/Skeleton.cjs +0 -77
- package/dist/views/shared/StatusPill.cjs +0 -47
- package/dist/views/shared/adminTokens.cjs +0 -34
- package/dist/views/shared/config.cjs +0 -44
- package/dist/views/shared/dateLocale.cjs +0 -5
- package/dist/views/shared/index.cjs +0 -72
- package/dist/views/shared/sla.cjs +0 -37
- package/dist/views.cjs +0 -86
- package/dist/views.d.cts +0 -13
- package/src/__tests__/aiSummaryRendering.test.ts +0 -21
- package/src/__tests__/authResponses.test.ts +0 -69
- package/src/__tests__/capabilities.test.ts +0 -71
- package/src/__tests__/generateTrackingToken.test.ts +0 -67
- package/src/__tests__/integration/ai-agent.test.ts +0 -46
- package/src/__tests__/integration/auth.test.ts +0 -43
- package/src/__tests__/integration/automation-rules.test.ts +0 -68
- package/src/__tests__/integration/buildTestPayload.ts +0 -52
- package/src/__tests__/integration/channels.test.ts +0 -52
- package/src/__tests__/integration/core-behaviors.test.ts +0 -81
- package/src/__tests__/integration/dashboard-volume.test.ts +0 -31
- package/src/__tests__/integration/digest.test.ts +0 -48
- package/src/__tests__/integration/invoice-pdf.test.ts +0 -30
- package/src/__tests__/integration/isolation.test.ts +0 -81
- package/src/__tests__/integration/mentions-invoice.test.ts +0 -50
- package/src/__tests__/integration/nps.test.ts +0 -37
- package/src/__tests__/integration/per-team-sla.test.ts +0 -44
- package/src/__tests__/integration/push.test.ts +0 -75
- package/src/__tests__/integration/sanitization.test.ts +0 -35
- package/src/__tests__/integration/sla-pause.test.ts +0 -39
- package/src/__tests__/integration/smoke.test.ts +0 -26
- package/src/__tests__/integration/snooze.test.ts +0 -68
- package/src/__tests__/integration/teams.test.ts +0 -38
- package/src/__tests__/rateLimiter.test.ts +0 -83
- package/src/__tests__/sanitizeHtml.test.ts +0 -165
- package/src/__tests__/sla.test.ts +0 -258
- package/src/__tests__/trackOpenEndpoint.test.ts +0 -54
- package/src/__tests__/translations.test.ts +0 -67
- package/src/__tests__/webhookSecurity.test.ts +0 -58
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface SkeletonProps {
|
|
3
|
+
width?: string | number;
|
|
4
|
+
height?: string | number;
|
|
5
|
+
borderRadius?: string;
|
|
6
|
+
style?: React.CSSProperties;
|
|
7
|
+
}
|
|
8
|
+
export declare function Skeleton({ width, height, borderRadius, style }: SkeletonProps): React.JSX.Element;
|
|
9
|
+
export declare function SkeletonText({ lines, width }: {
|
|
10
|
+
lines?: number;
|
|
11
|
+
width?: string | number;
|
|
12
|
+
}): React.JSX.Element;
|
|
13
|
+
export declare function SkeletonCard({ height }: {
|
|
14
|
+
height?: string;
|
|
15
|
+
}): React.JSX.Element;
|
|
16
|
+
export declare function SkeletonTable({ rows, columns }: {
|
|
17
|
+
rows?: number;
|
|
18
|
+
columns?: number;
|
|
19
|
+
}): React.JSX.Element;
|
|
20
|
+
export declare function SkeletonDashboard(): React.JSX.Element;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type TicketStatusVariant = 'open' | 'pending' | 'waiting_client' | 'resolved' | 'closed' | 'escalated' | 'sla-breach';
|
|
3
|
+
export interface StatusPillProps {
|
|
4
|
+
status: TicketStatusVariant | string;
|
|
5
|
+
label?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const StatusPill: React.FC<StatusPillProps>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
export declare const V: {
|
|
3
|
+
readonly text: "var(--theme-text)";
|
|
4
|
+
readonly textSecondary: "var(--theme-elevation-500)";
|
|
5
|
+
readonly bg: "var(--theme-elevation-50)";
|
|
6
|
+
readonly bgCard: "var(--theme-elevation-100)";
|
|
7
|
+
readonly border: "var(--theme-elevation-300)";
|
|
8
|
+
readonly blue: "#2563eb";
|
|
9
|
+
readonly amber: "#d97706";
|
|
10
|
+
readonly orange: "#ea580c";
|
|
11
|
+
readonly green: "#16a34a";
|
|
12
|
+
readonly red: "#dc2626";
|
|
13
|
+
readonly cyan: "#2563eb";
|
|
14
|
+
readonly yellow: "#d97706";
|
|
15
|
+
};
|
|
16
|
+
export declare const btnStyle: (bg: string, opts?: {
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
small?: boolean;
|
|
19
|
+
}) => React.CSSProperties;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ticketing feature flags — re-export of the shared implementation.
|
|
3
|
+
*
|
|
4
|
+
* See `utils/features.ts`. This module is kept so that
|
|
5
|
+
* `views/shared/index.ts` and existing view imports keep resolving.
|
|
6
|
+
*/
|
|
7
|
+
export { DEFAULT_TICKETING_FEATURES, DEFAULT_TICKETING_FEATURES as DEFAULT_FEATURES, FEATURES_CACHE_KEY, fetchFeatures, getFeatures, normalizeFeatures, saveFeatures, } from '../../utils/features.js';
|
|
8
|
+
export type { TicketingFeatures } from '../../utils/features.js';
|
|
@@ -1,40 +1 @@
|
|
|
1
|
-
|
|
2
|
-
timeTracking: true,
|
|
3
|
-
ai: true,
|
|
4
|
-
satisfaction: true,
|
|
5
|
-
chat: true,
|
|
6
|
-
emailTracking: true,
|
|
7
|
-
canned: true,
|
|
8
|
-
merge: true,
|
|
9
|
-
snooze: true,
|
|
10
|
-
externalMessages: true,
|
|
11
|
-
clientHistory: true,
|
|
12
|
-
activityLog: true,
|
|
13
|
-
splitTicket: true,
|
|
14
|
-
scheduledReplies: true,
|
|
15
|
-
autoClose: true,
|
|
16
|
-
autoCloseDays: 7,
|
|
17
|
-
roundRobin: false
|
|
18
|
-
};
|
|
19
|
-
const STORAGE_KEY = "ticketing_features";
|
|
20
|
-
function getFeatures() {
|
|
21
|
-
if (typeof window === "undefined") return DEFAULT_FEATURES;
|
|
22
|
-
try {
|
|
23
|
-
const stored = localStorage.getItem(STORAGE_KEY);
|
|
24
|
-
if (stored) {
|
|
25
|
-
const parsed = JSON.parse(stored);
|
|
26
|
-
return { ...DEFAULT_FEATURES, ...parsed };
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
}
|
|
30
|
-
return DEFAULT_FEATURES;
|
|
31
|
-
}
|
|
32
|
-
function saveFeatures(features) {
|
|
33
|
-
if (typeof window === "undefined") return;
|
|
34
|
-
try {
|
|
35
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(features));
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export { DEFAULT_FEATURES, getFeatures, saveFeatures };
|
|
1
|
+
export { DEFAULT_TICKETING_FEATURES as DEFAULT_FEATURES, DEFAULT_TICKETING_FEATURES, FEATURES_CACHE_KEY, fetchFeatures, getFeatures, normalizeFeatures, saveFeatures } from '../../utils/features.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { AdminErrorBoundary } from './ErrorBoundary.js';
|
|
2
|
+
export { V, btnStyle } from './adminTokens.js';
|
|
3
|
+
export { AdminViewHeader } from './AdminViewHeader.js';
|
|
4
|
+
export { Skeleton, SkeletonText, SkeletonCard, SkeletonTable, SkeletonDashboard } from './Skeleton.js';
|
|
5
|
+
export { getFeatures, saveFeatures, fetchFeatures, normalizeFeatures, DEFAULT_FEATURES } from './config.js';
|
|
6
|
+
export type { TicketingFeatures } from './config.js';
|
|
7
|
+
export { useFeatures } from '../../components/TicketConversation/hooks/useFeatures.js';
|
|
8
|
+
export type { UseFeaturesResult } from '../../components/TicketConversation/hooks/useFeatures.js';
|
|
9
|
+
export { StatusPill } from './StatusPill.js';
|
|
10
|
+
export type { StatusPillProps, TicketStatusVariant } from './StatusPill.js';
|
|
11
|
+
export { computeSlaState, formatSlaRemaining } from './sla.js';
|
|
12
|
+
export type { SlaState } from './sla.js';
|
|
@@ -2,6 +2,7 @@ export { AdminErrorBoundary } from './ErrorBoundary.js';
|
|
|
2
2
|
export { V, btnStyle } from './adminTokens.js';
|
|
3
3
|
export { AdminViewHeader } from './AdminViewHeader.js';
|
|
4
4
|
export { Skeleton, SkeletonCard, SkeletonDashboard, SkeletonTable, SkeletonText } from './Skeleton.js';
|
|
5
|
-
export { DEFAULT_FEATURES, getFeatures, saveFeatures } from './config.js';
|
|
5
|
+
export { DEFAULT_FEATURES, fetchFeatures, getFeatures, normalizeFeatures, saveFeatures } from './config.js';
|
|
6
|
+
export { useFeatures } from '../../components/TicketConversation/hooks/useFeatures.js';
|
|
6
7
|
export { StatusPill } from './StatusPill.js';
|
|
7
8
|
export { computeSlaState, formatSlaRemaining } from './sla.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SlaState = 'ok' | 'warn' | 'breach' | 'none' | 'met';
|
|
2
|
+
export interface SlaInput {
|
|
3
|
+
slaFirstResponseDue?: string | null;
|
|
4
|
+
slaFirstResponseBreached?: boolean | null;
|
|
5
|
+
firstResponseAt?: string | null;
|
|
6
|
+
slaResolutionDue?: string | null;
|
|
7
|
+
slaResolutionBreached?: boolean | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function computeSlaState(t: SlaInput, now?: Date): {
|
|
10
|
+
state: SlaState;
|
|
11
|
+
remainingMs: number | null;
|
|
12
|
+
due: string | null;
|
|
13
|
+
};
|
|
14
|
+
export declare function formatSlaRemaining(remainingMs: number | null): string;
|
package/dist/views.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export { default as TicketInboxView } from './views/TicketInboxView/index';
|
|
2
|
-
export { default as TicketDetailView } from './views/TicketDetailView/index';
|
|
3
|
-
export { default as SupportDashboardView } from './views/SupportDashboardView/index';
|
|
4
|
-
export { default as NewTicketView } from './views/NewTicketView/index';
|
|
5
|
-
export { default as TicketingSettingsView } from './views/TicketingSettingsView/index';
|
|
6
|
-
export { default as LogsView } from './views/LogsView/index';
|
|
7
|
-
export { default as ChatView } from './views/ChatView/index';
|
|
8
|
-
export { default as CrmView } from './views/CrmView/index';
|
|
9
|
-
export { default as PendingEmailsView } from './views/PendingEmailsView/index';
|
|
10
|
-
export { default as EmailTrackingView } from './views/EmailTrackingView/index';
|
|
11
|
-
export { default as BillingView } from './views/BillingView/index';
|
|
12
|
-
export { default as TimeDashboardView } from './views/TimeDashboardView/index';
|
|
13
|
-
export { default as ImportConversationView } from './views/ImportConversationView/index';
|
|
1
|
+
export { default as TicketInboxView } from './views/TicketInboxView/index.js';
|
|
2
|
+
export { default as TicketDetailView } from './views/TicketDetailView/index.js';
|
|
3
|
+
export { default as SupportDashboardView } from './views/SupportDashboardView/index.js';
|
|
4
|
+
export { default as NewTicketView } from './views/NewTicketView/index.js';
|
|
5
|
+
export { default as TicketingSettingsView } from './views/TicketingSettingsView/index.js';
|
|
6
|
+
export { default as LogsView } from './views/LogsView/index.js';
|
|
7
|
+
export { default as ChatView } from './views/ChatView/index.js';
|
|
8
|
+
export { default as CrmView } from './views/CrmView/index.js';
|
|
9
|
+
export { default as PendingEmailsView } from './views/PendingEmailsView/index.js';
|
|
10
|
+
export { default as EmailTrackingView } from './views/EmailTrackingView/index.js';
|
|
11
|
+
export { default as BillingView } from './views/BillingView/index.js';
|
|
12
|
+
export { default as TimeDashboardView } from './views/TimeDashboardView/index.js';
|
|
13
|
+
export { default as ImportConversationView } from './views/ImportConversationView/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@consilioweb/payload-support",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Payload CMS plugin — professional support & ticketing system with AI, SLA, time tracking, live chat, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -31,14 +31,11 @@
|
|
|
31
31
|
"import": {
|
|
32
32
|
"types": "./dist/views.d.ts",
|
|
33
33
|
"default": "./dist/views.js"
|
|
34
|
-
},
|
|
35
|
-
"require": {
|
|
36
|
-
"types": "./dist/views.d.cts",
|
|
37
|
-
"default": "./dist/views.cjs"
|
|
38
34
|
}
|
|
39
35
|
},
|
|
40
36
|
"./components/TicketConversation": {
|
|
41
37
|
"import": {
|
|
38
|
+
"types": "./dist/components/TicketConversation/index.d.ts",
|
|
42
39
|
"default": "./dist/components/TicketConversation/index.js"
|
|
43
40
|
}
|
|
44
41
|
}
|
|
@@ -46,6 +43,9 @@
|
|
|
46
43
|
"files": [
|
|
47
44
|
"dist",
|
|
48
45
|
"src",
|
|
46
|
+
"!src/__tests__",
|
|
47
|
+
"!src/**/*.test.ts",
|
|
48
|
+
"!src/**/*.test.tsx",
|
|
49
49
|
"README.md",
|
|
50
50
|
"LICENSE"
|
|
51
51
|
],
|
|
@@ -85,34 +85,19 @@
|
|
|
85
85
|
"author": "ConsilioWEB <contact@consilioweb.fr> (https://consilioweb.fr)",
|
|
86
86
|
"license": "MIT",
|
|
87
87
|
"peerDependencies": {
|
|
88
|
-
"@payloadcms/next": "^3.
|
|
89
|
-
"@payloadcms/translations": "^3.0.0",
|
|
90
|
-
"@payloadcms/ui": "^3.0.0",
|
|
88
|
+
"@payloadcms/next": "^3.37.0",
|
|
91
89
|
"lucide-react": ">=0.300.0",
|
|
92
|
-
"next": "^
|
|
93
|
-
"payload": "^3.
|
|
94
|
-
"react": "^
|
|
95
|
-
"react-dom": "^
|
|
96
|
-
},
|
|
97
|
-
"peerDependenciesMeta": {
|
|
98
|
-
"@payloadcms/ui": {
|
|
99
|
-
"optional": true
|
|
100
|
-
},
|
|
101
|
-
"@payloadcms/translations": {
|
|
102
|
-
"optional": true
|
|
103
|
-
},
|
|
104
|
-
"@payloadcms/next": {
|
|
105
|
-
"optional": true
|
|
106
|
-
},
|
|
107
|
-
"lucide-react": {
|
|
108
|
-
"optional": true
|
|
109
|
-
},
|
|
110
|
-
"next": {
|
|
111
|
-
"optional": true
|
|
112
|
-
}
|
|
90
|
+
"next": "^15.2.9 || ^16.0.0",
|
|
91
|
+
"payload": "^3.37.0",
|
|
92
|
+
"react": "^19.0.0",
|
|
93
|
+
"react-dom": "^19.0.0"
|
|
113
94
|
},
|
|
114
95
|
"engines": {
|
|
115
|
-
"node": ">=
|
|
96
|
+
"node": ">=20.9.0"
|
|
97
|
+
},
|
|
98
|
+
"publishConfig": {
|
|
99
|
+
"access": "public",
|
|
100
|
+
"provenance": true
|
|
116
101
|
},
|
|
117
102
|
"devDependencies": {
|
|
118
103
|
"@payloadcms/db-sqlite": "^3.86.0",
|
|
@@ -142,7 +127,7 @@
|
|
|
142
127
|
"web-push": "^3.6.7"
|
|
143
128
|
},
|
|
144
129
|
"scripts": {
|
|
145
|
-
"build": "tsup",
|
|
130
|
+
"build": "tsup && tsc -p tsconfig.types.json && node scripts/copy-subpath-types.mjs",
|
|
146
131
|
"typecheck": "tsc --noEmit",
|
|
147
132
|
"test": "vitest run",
|
|
148
133
|
"test:watch": "vitest",
|
|
@@ -91,12 +91,33 @@ function createEnforce2FA(slugs: CollectionSlugs): CollectionBeforeLoginHook {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Field-level write guard for the commercial / security fields.
|
|
96
|
+
*
|
|
97
|
+
* `access.update` on this collection intentionally lets a support-client PATCH
|
|
98
|
+
* their OWN document (the portal profile page writes firstName, phone, the
|
|
99
|
+
* notification toggles and the 2FA switch). Payload persists every other field of
|
|
100
|
+
* that same PATCH too, so without a field guard a client could self-promote
|
|
101
|
+
* (`tier`), rewrite the sales notes, or — worst — set `googleId`, which the Google
|
|
102
|
+
* OAuth callback resolves BEFORE the email and would hand them another account.
|
|
103
|
+
*
|
|
104
|
+
* Internal writers (2FA endpoints, OAuth linking, beforeLogin) all go through
|
|
105
|
+
* `overrideAccess: true`, which bypasses field access, so they are unaffected.
|
|
106
|
+
* `twoFactorEnabled` is deliberately NOT locked: it is a legitimate self-service
|
|
107
|
+
* toggle on the portal profile page.
|
|
108
|
+
*/
|
|
109
|
+
function createAdminOnlyFieldUpdate(slugs: CollectionSlugs) {
|
|
110
|
+
return ({ req }: { req: { user?: { collection?: string } | null } }) =>
|
|
111
|
+
req.user?.collection === slugs.users
|
|
112
|
+
}
|
|
113
|
+
|
|
94
114
|
// ─── Collection factory ──────────────────────────────────
|
|
95
115
|
|
|
96
116
|
export function createSupportClientsCollection(
|
|
97
117
|
slugs: CollectionSlugs,
|
|
98
118
|
options?: { capabilities?: SupportCapabilities },
|
|
99
119
|
): CollectionConfig {
|
|
120
|
+
const adminOnlyFieldUpdate = createAdminOnlyFieldUpdate(slugs)
|
|
100
121
|
return {
|
|
101
122
|
slug: slugs.supportClients,
|
|
102
123
|
labels: {
|
|
@@ -226,6 +247,7 @@ export function createSupportClientsCollection(
|
|
|
226
247
|
access: {
|
|
227
248
|
// Admin-only: never serialize commercial notes onto the client's own doc.
|
|
228
249
|
read: ({ req }) => req.user?.collection === slugs.users,
|
|
250
|
+
update: adminOnlyFieldUpdate,
|
|
229
251
|
},
|
|
230
252
|
admin: {
|
|
231
253
|
description: 'Notes commerciales (visible uniquement par les admins)',
|
|
@@ -235,6 +257,7 @@ export function createSupportClientsCollection(
|
|
|
235
257
|
name: 'tier',
|
|
236
258
|
type: 'select',
|
|
237
259
|
label: 'Plan',
|
|
260
|
+
access: { update: adminOnlyFieldUpdate },
|
|
238
261
|
options: [
|
|
239
262
|
{ label: 'Free', value: 'free' },
|
|
240
263
|
{ label: 'Basic', value: 'basic' },
|
|
@@ -248,6 +271,7 @@ export function createSupportClientsCollection(
|
|
|
248
271
|
type: 'relationship',
|
|
249
272
|
relationTo: slugs.users,
|
|
250
273
|
label: 'Agent referent',
|
|
274
|
+
access: { update: adminOnlyFieldUpdate },
|
|
251
275
|
admin: { position: 'sidebar' },
|
|
252
276
|
},
|
|
253
277
|
{
|
|
@@ -263,17 +287,20 @@ export function createSupportClientsCollection(
|
|
|
263
287
|
{
|
|
264
288
|
name: 'twoFactorCode',
|
|
265
289
|
type: 'text',
|
|
290
|
+
access: { update: adminOnlyFieldUpdate },
|
|
266
291
|
admin: { hidden: true },
|
|
267
292
|
},
|
|
268
293
|
{
|
|
269
294
|
name: 'twoFactorExpiry',
|
|
270
295
|
type: 'date',
|
|
296
|
+
access: { update: adminOnlyFieldUpdate },
|
|
271
297
|
admin: { hidden: true },
|
|
272
298
|
},
|
|
273
299
|
{
|
|
274
300
|
// Set by /support/2fa "verify"; gates login via the beforeLogin hook.
|
|
275
301
|
name: 'twoFactorVerifiedAt',
|
|
276
302
|
type: 'date',
|
|
303
|
+
access: { update: adminOnlyFieldUpdate },
|
|
277
304
|
admin: { hidden: true },
|
|
278
305
|
},
|
|
279
306
|
{
|
|
@@ -281,6 +308,7 @@ export function createSupportClientsCollection(
|
|
|
281
308
|
name: 'googleId',
|
|
282
309
|
type: 'text',
|
|
283
310
|
index: true,
|
|
311
|
+
access: { update: adminOnlyFieldUpdate },
|
|
284
312
|
admin: { hidden: true },
|
|
285
313
|
},
|
|
286
314
|
{
|
|
@@ -352,6 +380,7 @@ export function createSupportClientsCollection(
|
|
|
352
380
|
access: {
|
|
353
381
|
// Admin-only: never serialize internal notes onto the client's own doc.
|
|
354
382
|
read: ({ req }) => req.user?.collection === slugs.users,
|
|
383
|
+
update: adminOnlyFieldUpdate,
|
|
355
384
|
},
|
|
356
385
|
admin: {
|
|
357
386
|
description: 'Visible uniquement par les admins',
|
|
@@ -32,7 +32,12 @@ export function createTicketCollaboratorsCollection(slugs: CollectionSlugs): Col
|
|
|
32
32
|
}
|
|
33
33
|
return false
|
|
34
34
|
},
|
|
35
|
-
|
|
35
|
+
// Staff only. This collection is the source of truth for
|
|
36
|
+
// resolveAccessibleTicketIds(), which widens `read` on tickets, messages
|
|
37
|
+
// and the activity log — so letting any authenticated principal POST here
|
|
38
|
+
// let a support client grant themselves access to any ticket by id.
|
|
39
|
+
// The /invite endpoint is unaffected: it writes with overrideAccess: true.
|
|
40
|
+
create: ({ req }) => req.user?.collection === slugs.users,
|
|
36
41
|
update: () => false,
|
|
37
42
|
delete: ({ req }) => req.user?.collection === slugs.users,
|
|
38
43
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CollectionConfig, CollectionBeforeChangeHook, CollectionAfterChangeHook, Where } from 'payload'
|
|
2
|
+
import { APIError } from 'payload'
|
|
2
3
|
import type { CollectionSlugs } from '../utils/slugs'
|
|
3
4
|
import type { SupportCapabilities } from '../types'
|
|
4
5
|
import { escapeHtml, emailWrapper, emailButton, emailQuote, emailParagraph, emailRichContent, emailTrackingPixel } from '../utils/emailTemplate'
|
|
5
|
-
import { fireWebhooks } from '../utils/fireWebhooks'
|
|
6
6
|
import { createAdminNotification } from '../utils/adminNotification'
|
|
7
7
|
import { dispatchWebhook } from '../utils/webhookDispatcher'
|
|
8
8
|
import { readSupportSettings } from '../utils/readSettings'
|
|
@@ -13,6 +13,38 @@ import { queueClientNotification } from '../utils/notificationQueue'
|
|
|
13
13
|
import { sendPushToUser } from '../utils/push'
|
|
14
14
|
import { dbFind, dbCreate, dbFindByID } from '../utils/db'
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Cross-tenant write guard for support-clients.
|
|
18
|
+
*
|
|
19
|
+
* `access.create` only checks WHICH collection the actor belongs to, so any
|
|
20
|
+
* authenticated support-client could POST a message onto an arbitrary ticket id.
|
|
21
|
+
* That is not just a data-integrity issue: the `afterChange` notification hooks
|
|
22
|
+
* would then mail the injected body to the real ticket owner from the legitimate
|
|
23
|
+
* support address (phishing over a trusted channel).
|
|
24
|
+
*
|
|
25
|
+
* We reuse `resolveAccessibleTicketIds()` — the same boundary `access.read` uses —
|
|
26
|
+
* so read and write scopes cannot drift apart. Staff writes and every internal
|
|
27
|
+
* `overrideAccess` write are untouched: they carry no `support-clients` user.
|
|
28
|
+
*/
|
|
29
|
+
function createRestrictClientTicketTarget(slugs: CollectionSlugs): CollectionBeforeChangeHook {
|
|
30
|
+
return async ({ data, operation, req, originalDoc }) => {
|
|
31
|
+
if (req.user?.collection !== slugs.supportClients) return data
|
|
32
|
+
|
|
33
|
+
const raw = (data as { ticket?: unknown }).ticket
|
|
34
|
+
?? (operation === 'update' ? (originalDoc as { ticket?: unknown } | undefined)?.ticket : undefined)
|
|
35
|
+
const targetId = raw && typeof raw === 'object' ? (raw as { id?: unknown }).id : raw
|
|
36
|
+
if (targetId === undefined || targetId === null || targetId === '') {
|
|
37
|
+
throw new APIError('Ticket cible requis.', 400)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const accessible = await resolveAccessibleTicketIds(req.payload, slugs, req.user.id)
|
|
41
|
+
if (!accessible.some((id) => String(id) === String(targetId))) {
|
|
42
|
+
throw new APIError('Ticket inaccessible.', 403)
|
|
43
|
+
}
|
|
44
|
+
return data
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
16
48
|
function createAssignAuthor(slugs: CollectionSlugs): CollectionBeforeChangeHook {
|
|
17
49
|
return async ({ data, operation, req }) => {
|
|
18
50
|
if (operation === 'create' && req.user?.collection === slugs.supportClients) {
|
|
@@ -300,26 +332,12 @@ function createNotifyAdminOnClientMessage(slugs: CollectionSlugs, notificationSl
|
|
|
300
332
|
}
|
|
301
333
|
}
|
|
302
334
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (doc.isInternal) return doc
|
|
310
|
-
|
|
311
|
-
const ticketId = typeof doc.ticket === 'object' ? doc.ticket.id : doc.ticket
|
|
312
|
-
fireWebhooks(req.payload, slugs, 'ticket_replied', {
|
|
313
|
-
ticketId,
|
|
314
|
-
messageId: doc.id,
|
|
315
|
-
authorType: doc.authorType,
|
|
316
|
-
body: doc.body?.length > 500 ? doc.body.slice(0, 500) + '...' : doc.body,
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
return doc
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Single outbound-webhook dispatcher for replies. See the matching comment in
|
|
337
|
+
* Tickets.ts: a second, unsigned dispatcher used to fire on this exact hook with
|
|
338
|
+
* the same guards, doubling every delivery. The `body` excerpt of the legacy
|
|
339
|
+
* payload is carried over so existing consumers do not regress.
|
|
340
|
+
*/
|
|
323
341
|
function createDispatchWebhookOnReply(slugs: CollectionSlugs): CollectionAfterChangeHook {
|
|
324
342
|
return async ({ doc, operation, req }) => {
|
|
325
343
|
if (operation !== 'create') return doc
|
|
@@ -328,7 +346,12 @@ function createDispatchWebhookOnReply(slugs: CollectionSlugs): CollectionAfterCh
|
|
|
328
346
|
|
|
329
347
|
const ticketId = typeof doc.ticket === 'object' ? doc.ticket.id : doc.ticket
|
|
330
348
|
dispatchWebhook(
|
|
331
|
-
{
|
|
349
|
+
{
|
|
350
|
+
ticketId,
|
|
351
|
+
messageId: doc.id,
|
|
352
|
+
authorType: doc.authorType,
|
|
353
|
+
body: doc.body?.length > 500 ? doc.body.slice(0, 500) + '...' : doc.body,
|
|
354
|
+
},
|
|
332
355
|
'ticket_replied',
|
|
333
356
|
req.payload,
|
|
334
357
|
slugs,
|
|
@@ -534,7 +557,7 @@ export function createTicketMessagesCollection(slugs: CollectionSlugs, options?:
|
|
|
534
557
|
] : []),
|
|
535
558
|
],
|
|
536
559
|
hooks: {
|
|
537
|
-
beforeChange: [createSanitizeMessageHtml(), createResolveMentions(slugs), createAssignAuthor(slugs)],
|
|
560
|
+
beforeChange: [createRestrictClientTicketTarget(slugs), createSanitizeMessageHtml(), createResolveMentions(slugs), createAssignAuthor(slugs)],
|
|
538
561
|
afterChange: [
|
|
539
562
|
...(options?.capabilities?.sms ? [createSmsNotification(slugs, options.capabilities.sms)] : []),
|
|
540
563
|
createAutoUpdateStatus(slugs),
|
|
@@ -543,7 +566,6 @@ export function createTicketMessagesCollection(slugs: CollectionSlugs, options?:
|
|
|
543
566
|
createCheckSlaOnReply(slugs, notificationSlug),
|
|
544
567
|
createSyncTicketReplyToChat(slugs),
|
|
545
568
|
createNotifyAdminOnClientMessage(slugs, notificationSlug),
|
|
546
|
-
createFireMessageWebhooks(slugs),
|
|
547
569
|
createDispatchWebhookOnReply(slugs),
|
|
548
570
|
createNotifyMentions(slugs),
|
|
549
571
|
...(options?.capabilities?.threadCleanup ? [createThreadCleanup(options.capabilities.threadCleanup)] : []),
|