@consilioweb/payload-support 0.9.11 → 0.9.13

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/dist/index.cjs CHANGED
@@ -2975,9 +2975,36 @@ function createBillingEndpoint(slugs) {
2975
2975
  return Response.json({ error: "Missing from/to params" }, { status: 400 });
2976
2976
  }
2977
2977
  const projectId = url.searchParams.get("projectId");
2978
+ const toExclusive = new Date(to);
2979
+ toExclusive.setDate(toExclusive.getDate() + 1);
2980
+ const toExclusiveIso = toExclusive.toISOString();
2978
2981
  const ticketWhere = {
2979
- billable: { equals: true },
2980
- ...projectId ? { project: { equals: Number(projectId) } } : {}
2982
+ and: [
2983
+ { billable: { equals: true } },
2984
+ ...projectId ? [{ project: { equals: Number(projectId) } }] : [],
2985
+ {
2986
+ or: [
2987
+ {
2988
+ and: [
2989
+ { updatedAt: { greater_than_equal: from } },
2990
+ { updatedAt: { less_than: toExclusiveIso } }
2991
+ ]
2992
+ },
2993
+ {
2994
+ and: [
2995
+ { createdAt: { greater_than_equal: from } },
2996
+ { createdAt: { less_than: toExclusiveIso } }
2997
+ ]
2998
+ },
2999
+ {
3000
+ and: [
3001
+ { resolvedAt: { greater_than_equal: from } },
3002
+ { resolvedAt: { less_than: toExclusiveIso } }
3003
+ ]
3004
+ }
3005
+ ]
3006
+ }
3007
+ ]
2981
3008
  };
2982
3009
  const allTickets = [];
2983
3010
  let ticketPage = 1;
@@ -3030,8 +3057,8 @@ function createBillingEndpoint(slugs) {
3030
3057
  const projectGroups = /* @__PURE__ */ new Map();
3031
3058
  for (const ticket of allTickets) {
3032
3059
  const t = ticket;
3033
- const ticketEntries = entriesByTicket.get(t.id);
3034
- if (!ticketEntries || ticketEntries.length === 0) continue;
3060
+ const ticketEntries = entriesByTicket.get(t.id) || [];
3061
+ const hasNoTimeEntries = ticketEntries.length === 0;
3035
3062
  const project = typeof t.project === "object" && t.project ? { id: t.project.id, name: t.project.name || "Sans nom" } : null;
3036
3063
  const projectKey = project ? String(project.id) : "no-project";
3037
3064
  if (!projectGroups.has(projectKey)) {
@@ -3059,9 +3086,14 @@ function createBillingEndpoint(slugs) {
3059
3086
  id: t.id,
3060
3087
  ticketNumber: t.ticketNumber || "",
3061
3088
  subject: t.subject || "",
3089
+ status: t.status || "",
3062
3090
  entries: ticketEntries,
3063
3091
  totalMinutes: ticketTotalMinutes,
3064
- billedAmount
3092
+ billedAmount,
3093
+ hasNoTimeEntries,
3094
+ aiSummary: t.aiSummary || null,
3095
+ aiSummaryGeneratedAt: t.aiSummaryGeneratedAt || null,
3096
+ aiSummaryStatus: t.aiSummaryStatus || null
3065
3097
  });
3066
3098
  projectGroups.get(projectKey).totalMinutes += ticketTotalMinutes;
3067
3099
  if (billedAmount) projectGroups.get(projectKey).totalBilledAmount += billedAmount;
@@ -3069,7 +3101,16 @@ function createBillingEndpoint(slugs) {
3069
3101
  const groups = Array.from(projectGroups.values());
3070
3102
  const grandTotalMinutes = groups.reduce((sum, g) => sum + g.totalMinutes, 0);
3071
3103
  const grandTotalBilledAmount = groups.reduce((sum, g) => sum + g.totalBilledAmount, 0);
3072
- return new Response(JSON.stringify({ groups, grandTotalMinutes, grandTotalBilledAmount }), {
3104
+ const ticketsWithoutTime = groups.reduce(
3105
+ (sum, g) => sum + g.tickets.filter((t) => t.hasNoTimeEntries).length,
3106
+ 0
3107
+ );
3108
+ return new Response(JSON.stringify({
3109
+ groups,
3110
+ grandTotalMinutes,
3111
+ grandTotalBilledAmount,
3112
+ ticketsWithoutTime
3113
+ }), {
3073
3114
  headers: {
3074
3115
  "Content-Type": "application/json",
3075
3116
  "Cache-Control": "private, max-age=300, stale-while-revalidate=600"
@@ -3085,6 +3126,177 @@ function createBillingEndpoint(slugs) {
3085
3126
  };
3086
3127
  }
3087
3128
 
3129
+ // src/utils/generateTicketSynthesis.ts
3130
+ function getClient3(aiSettings) {
3131
+ const Anthropic = __require("@anthropic-ai/sdk").default;
3132
+ if (aiSettings.provider === "ollama") {
3133
+ const baseURL = process.env.OLLAMA_API_URL || "https://ollama.orkelis.app/v1";
3134
+ return new Anthropic({ apiKey: "ollama", baseURL });
3135
+ }
3136
+ return new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
3137
+ }
3138
+ function getModel3(aiSettings) {
3139
+ return aiSettings.model || "claude-haiku-4-5-20251001";
3140
+ }
3141
+ async function generateTicketSynthesis(args) {
3142
+ const { payload, slugs, ticketId } = args;
3143
+ const settings = await readSupportSettings(payload);
3144
+ if (settings.ai.enableSynthesis === false) {
3145
+ return { summary: "", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), status: "skipped", reason: "synthesis disabled" };
3146
+ }
3147
+ const ticket = await payload.findByID({
3148
+ collection: slugs.tickets,
3149
+ id: ticketId,
3150
+ depth: 1,
3151
+ overrideAccess: true
3152
+ });
3153
+ if (!ticket) {
3154
+ return { summary: "", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), status: "error", reason: "ticket not found" };
3155
+ }
3156
+ await payload.update({
3157
+ collection: slugs.tickets,
3158
+ id: ticketId,
3159
+ data: { aiSummaryStatus: "pending" },
3160
+ overrideAccess: true
3161
+ }).catch(() => {
3162
+ });
3163
+ const messagesResult = await payload.find({
3164
+ collection: slugs.ticketMessages,
3165
+ where: { ticket: { equals: ticketId } },
3166
+ sort: "createdAt",
3167
+ limit: 500,
3168
+ depth: 0,
3169
+ overrideAccess: true
3170
+ });
3171
+ const messages = messagesResult.docs;
3172
+ if (messages.length === 0) {
3173
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3174
+ await payload.update({
3175
+ collection: slugs.tickets,
3176
+ id: ticketId,
3177
+ data: {
3178
+ aiSummary: "(Aucun message dans ce ticket)",
3179
+ aiSummaryGeneratedAt: generatedAt,
3180
+ aiSummaryStatus: "done"
3181
+ },
3182
+ overrideAccess: true
3183
+ });
3184
+ return { summary: "(Aucun message dans ce ticket)", generatedAt, status: "done" };
3185
+ }
3186
+ const conversation = messages.map((m) => {
3187
+ const author = m.authorType === "admin" ? "Support" : "Client";
3188
+ const date = m.createdAt ? new Date(m.createdAt).toLocaleDateString("fr-FR", {
3189
+ day: "numeric",
3190
+ month: "short",
3191
+ hour: "2-digit",
3192
+ minute: "2-digit",
3193
+ timeZone: "Europe/Paris"
3194
+ }) : "";
3195
+ return `[${date}] ${author}: ${m.body || ""}`;
3196
+ }).join("\n\n");
3197
+ const clientObj = typeof ticket.client === "object" && ticket.client ? ticket.client : null;
3198
+ const clientCompany = clientObj?.company || "";
3199
+ const clientName = clientObj ? [clientObj.firstName, clientObj.lastName].filter(Boolean).join(" ") : "";
3200
+ const prompt = `Tu es un consultant technique qui prepare un recap factuel pour une facturation client.
3201
+
3202
+ Sujet du ticket : ${ticket.subject || "(sans sujet)"}
3203
+ Client : ${clientName || "Inconnu"}${clientCompany ? ` \u2014 ${clientCompany}` : ""}
3204
+
3205
+ Conversation complete du ticket :
3206
+ ${conversation}
3207
+
3208
+ Genere un recap factuel sous forme d'une liste a puces courtes et actionnables, decrivant CE QUI A ETE FAIT cote support pendant ce ticket. C'est destine a etre colle dans un devis ou une facture.
3209
+
3210
+ Regles strictes :
3211
+ - Une puce = une action realisee, formulee en groupe nominal court (ex : "Diagnostic configuration DNS et authentification Mailchimp")
3212
+ - Pas de phrases completes, pas de "j'ai fait", pas de pronoms
3213
+ - Pas de salutations, pas d'introduction, pas de conclusion
3214
+ - Pas de markdown autre que les puces "- "
3215
+ - 5 a 10 puces maximum, ordonnees chronologiquement
3216
+ - Ne mentionne PAS le client par son nom dans les puces
3217
+ - Si le ticket n'a pas abouti, decris quand meme le travail d'analyse realise
3218
+
3219
+ Reponds UNIQUEMENT avec la liste de puces, rien d'autre.`;
3220
+ const anthropic = getClient3(settings.ai);
3221
+ const model = getModel3(settings.ai);
3222
+ try {
3223
+ const res = await anthropic.messages.create({
3224
+ model,
3225
+ max_tokens: 600,
3226
+ messages: [{ role: "user", content: prompt }]
3227
+ });
3228
+ const summary = res.content[0]?.type === "text" ? res.content[0].text.trim() : "";
3229
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3230
+ await payload.update({
3231
+ collection: slugs.tickets,
3232
+ id: ticketId,
3233
+ data: {
3234
+ aiSummary: summary,
3235
+ aiSummaryGeneratedAt: generatedAt,
3236
+ aiSummaryStatus: "done"
3237
+ },
3238
+ overrideAccess: true
3239
+ });
3240
+ return { summary, generatedAt, status: "done" };
3241
+ } catch (err) {
3242
+ const message = err instanceof Error ? err.message : String(err);
3243
+ await payload.update({
3244
+ collection: slugs.tickets,
3245
+ id: ticketId,
3246
+ data: { aiSummaryStatus: "error" },
3247
+ overrideAccess: true
3248
+ }).catch(() => {
3249
+ });
3250
+ return { summary: "", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), status: "error", reason: message };
3251
+ }
3252
+ }
3253
+
3254
+ // src/endpoints/ticket-synthesis.ts
3255
+ function createTicketSynthesisEndpoint(slugs) {
3256
+ return {
3257
+ path: "/support/ticket-synthesis",
3258
+ method: "post",
3259
+ handler: async (req) => {
3260
+ try {
3261
+ requireAdmin(req, slugs);
3262
+ const payload = req.payload;
3263
+ const url = new URL(req.url || "", "http://localhost");
3264
+ const ticketIdRaw = url.searchParams.get("ticketId");
3265
+ const force = url.searchParams.get("force") === "true";
3266
+ if (!ticketIdRaw) {
3267
+ return Response.json({ error: "ticketId required" }, { status: 400 });
3268
+ }
3269
+ const ticketId = Number(ticketIdRaw);
3270
+ if (Number.isNaN(ticketId)) {
3271
+ return Response.json({ error: "ticketId must be a number" }, { status: 400 });
3272
+ }
3273
+ if (!force) {
3274
+ const existing = await payload.findByID({
3275
+ collection: slugs.tickets,
3276
+ id: ticketId,
3277
+ depth: 0,
3278
+ overrideAccess: true
3279
+ });
3280
+ if (existing?.aiSummary && existing.aiSummaryStatus === "done") {
3281
+ return Response.json({
3282
+ summary: existing.aiSummary,
3283
+ generatedAt: existing.aiSummaryGeneratedAt,
3284
+ status: "cached"
3285
+ });
3286
+ }
3287
+ }
3288
+ const result = await generateTicketSynthesis({ payload, slugs, ticketId });
3289
+ return Response.json(result);
3290
+ } catch (err) {
3291
+ const authResponse = handleAuthError(err);
3292
+ if (authResponse) return authResponse;
3293
+ console.error("[support/ticket-synthesis] Error:", err);
3294
+ return Response.json({ error: "Internal server error" }, { status: 500 });
3295
+ }
3296
+ }
3297
+ };
3298
+ }
3299
+
3088
3300
  // src/endpoints/email-stats.ts
3089
3301
  function createEmailStatsEndpoint(slugs) {
3090
3302
  return {
@@ -5023,6 +5235,7 @@ function createSupportEndpoints(slugs, options) {
5023
5235
  if (!f || f.ai !== false) {
5024
5236
  endpoints.push(createAiEndpoint(slugs));
5025
5237
  endpoints.push(...createClientIntelligenceEndpoint(slugs));
5238
+ endpoints.push(createTicketSynthesisEndpoint(slugs));
5026
5239
  }
5027
5240
  if (!f || f.bulkActions !== false) endpoints.push(createBulkActionEndpoint(slugs));
5028
5241
  if (!f || f.merge !== false) endpoints.push(createMergeTicketsEndpoint(slugs));
@@ -5679,6 +5892,32 @@ function createTrackSLA(slugs) {
5679
5892
  return doc;
5680
5893
  };
5681
5894
  }
5895
+ function createTrackAiSummaryOnResolve(slugs) {
5896
+ return async ({ doc, previousDoc, operation, req }) => {
5897
+ if (operation !== "update" || !previousDoc) return doc;
5898
+ const wasResolved = previousDoc.status === "resolved";
5899
+ const isResolved = doc.status === "resolved";
5900
+ if (wasResolved && !isResolved && doc.aiSummary) {
5901
+ try {
5902
+ await req.payload.update({
5903
+ collection: slugs.tickets,
5904
+ id: doc.id,
5905
+ data: { aiSummary: null, aiSummaryGeneratedAt: null, aiSummaryStatus: null },
5906
+ overrideAccess: true
5907
+ });
5908
+ } catch (err) {
5909
+ console.error("[support] Failed to clear ai summary on reopen:", err);
5910
+ }
5911
+ return doc;
5912
+ }
5913
+ if (!wasResolved && isResolved && !doc.aiSummary) {
5914
+ setImmediate(() => {
5915
+ generateTicketSynthesis({ payload: req.payload, slugs, ticketId: doc.id }).catch((err) => console.error("[support] Background ai synthesis failed:", err));
5916
+ });
5917
+ }
5918
+ return doc;
5919
+ };
5920
+ }
5682
5921
  function createLogTicketActivity(slugs) {
5683
5922
  return async ({ doc, previousDoc, operation, req }) => {
5684
5923
  if (operation !== "update" || !previousDoc) return doc;
@@ -6083,6 +6322,49 @@ function createTicketsCollection(slugs, options) {
6083
6322
  admin: { initCollapsed: true },
6084
6323
  fields: billingFields
6085
6324
  },
6325
+ // AI Synthesis collapsible — auto-filled when ticket is resolved
6326
+ {
6327
+ type: "collapsible",
6328
+ label: "Synthese IA",
6329
+ admin: {
6330
+ initCollapsed: true,
6331
+ description: "Recap factuel genere automatiquement au passage en resolu. Sert au copier-coller dans devis/factures."
6332
+ },
6333
+ fields: [
6334
+ {
6335
+ name: "aiSummary",
6336
+ type: "textarea",
6337
+ label: "Synthese",
6338
+ admin: {
6339
+ readOnly: true,
6340
+ rows: 8,
6341
+ description: "Vide tant que le ticket n'est pas resolu. Effacee si le ticket est reouvert."
6342
+ }
6343
+ },
6344
+ {
6345
+ type: "row",
6346
+ fields: [
6347
+ {
6348
+ name: "aiSummaryGeneratedAt",
6349
+ type: "date",
6350
+ label: "Genere le",
6351
+ admin: { readOnly: true, width: "50%", date: { displayFormat: "dd/MM/yyyy HH:mm" } }
6352
+ },
6353
+ {
6354
+ name: "aiSummaryStatus",
6355
+ type: "select",
6356
+ label: "Statut",
6357
+ options: [
6358
+ { label: "En cours", value: "pending" },
6359
+ { label: "Genere", value: "done" },
6360
+ { label: "Erreur", value: "error" }
6361
+ ],
6362
+ admin: { readOnly: true, width: "50%" }
6363
+ }
6364
+ ]
6365
+ }
6366
+ ]
6367
+ },
6086
6368
  // SLA & Delais
6087
6369
  {
6088
6370
  type: "collapsible",
@@ -6207,6 +6489,7 @@ function createTicketsCollection(slugs, options) {
6207
6489
  ],
6208
6490
  afterChange: [
6209
6491
  createTrackSLA(slugs),
6492
+ createTrackAiSummaryOnResolve(slugs),
6210
6493
  createAutoCalculateSLA(slugs),
6211
6494
  createAssignSlaDeadlines(slugs, notificationSlug),
6212
6495
  createCheckSlaOnResolve(slugs, notificationSlug),
@@ -8188,6 +8471,7 @@ exports.createTicketsCollection = createTicketsCollection;
8188
8471
  exports.createTimeEntriesCollection = createTimeEntriesCollection;
8189
8472
  exports.createWebhookEndpointsCollection = createWebhookEndpointsCollection;
8190
8473
  exports.dispatchWebhook = dispatchWebhook;
8474
+ exports.generateTicketSynthesis = generateTicketSynthesis;
8191
8475
  exports.readSupportSettings = readSupportSettings;
8192
8476
  exports.readUserPrefs = readUserPrefs;
8193
8477
  exports.resolveSlugs = resolveSlugs;
package/dist/index.d.cts CHANGED
@@ -309,6 +309,23 @@ type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 's
309
309
  */
310
310
  declare function dispatchWebhook(data: Record<string, unknown>, event: WebhookEvent, payload: BasePayload, slugs: CollectionSlugs): void;
311
311
 
312
+ interface TicketSynthesisResult {
313
+ summary: string;
314
+ generatedAt: string;
315
+ status: 'done' | 'error' | 'skipped';
316
+ reason?: string;
317
+ }
318
+ /**
319
+ * Generates a billing-oriented bullet-point synthesis for a single support ticket.
320
+ * Persists the result on the ticket (aiSummary, aiSummaryGeneratedAt, aiSummaryStatus).
321
+ * Designed to be called from an HTTP endpoint OR from a Payload hook (async fire-and-forget).
322
+ */
323
+ declare function generateTicketSynthesis(args: {
324
+ payload: Payload;
325
+ slugs: CollectionSlugs;
326
+ ticketId: number | string;
327
+ }): Promise<TicketSynthesisResult>;
328
+
312
329
  /**
313
330
  * Calculate a business-hours deadline from a start date.
314
331
  * Business hours: Mon-Fri, 9:00-18:00 (Europe/Paris).
@@ -381,4 +398,4 @@ declare function createMacrosCollection(slugs: CollectionSlugs): CollectionConfi
381
398
 
382
399
  declare function createTicketStatusesCollection(slugs: CollectionSlugs): CollectionConfig;
383
400
 
384
- export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_USER_PREFS, type EmailConfig, type MessageData, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type TicketData, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createMacrosCollection, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createWebhookEndpointsCollection, dispatchWebhook, readSupportSettings, readUserPrefs, resolveSlugs, supportPlugin };
401
+ export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_USER_PREFS, type EmailConfig, type MessageData, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type TicketData, type TicketSynthesisResult, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createMacrosCollection, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createWebhookEndpointsCollection, dispatchWebhook, generateTicketSynthesis, readSupportSettings, readUserPrefs, resolveSlugs, supportPlugin };
package/dist/index.d.ts CHANGED
@@ -309,6 +309,23 @@ type WebhookEvent = 'ticket_created' | 'ticket_resolved' | 'ticket_replied' | 's
309
309
  */
310
310
  declare function dispatchWebhook(data: Record<string, unknown>, event: WebhookEvent, payload: BasePayload, slugs: CollectionSlugs): void;
311
311
 
312
+ interface TicketSynthesisResult {
313
+ summary: string;
314
+ generatedAt: string;
315
+ status: 'done' | 'error' | 'skipped';
316
+ reason?: string;
317
+ }
318
+ /**
319
+ * Generates a billing-oriented bullet-point synthesis for a single support ticket.
320
+ * Persists the result on the ticket (aiSummary, aiSummaryGeneratedAt, aiSummaryStatus).
321
+ * Designed to be called from an HTTP endpoint OR from a Payload hook (async fire-and-forget).
322
+ */
323
+ declare function generateTicketSynthesis(args: {
324
+ payload: Payload;
325
+ slugs: CollectionSlugs;
326
+ ticketId: number | string;
327
+ }): Promise<TicketSynthesisResult>;
328
+
312
329
  /**
313
330
  * Calculate a business-hours deadline from a start date.
314
331
  * Business hours: Mon-Fri, 9:00-18:00 (Europe/Paris).
@@ -381,4 +398,4 @@ declare function createMacrosCollection(slugs: CollectionSlugs): CollectionConfi
381
398
 
382
399
  declare function createTicketStatusesCollection(slugs: CollectionSlugs): CollectionConfig;
383
400
 
384
- export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_USER_PREFS, type EmailConfig, type MessageData, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type TicketData, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createMacrosCollection, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createWebhookEndpointsCollection, dispatchWebhook, readSupportSettings, readUserPrefs, resolveSlugs, supportPlugin };
401
+ export { type AIProviderConfig, type ActivityEntryData, type CannedResponseData, type ClientData, type CollectionSlugs, DEFAULT_FEATURES, DEFAULT_SETTINGS, DEFAULT_SLUGS, DEFAULT_USER_PREFS, type EmailConfig, type MessageData, type SatisfactionSurveyData, type SupportFeatures, type SupportPluginConfig, type SupportSettings, type TicketData, type TicketSynthesisResult, type TimeEntryData, type UserPrefs, calculateBusinessHoursDeadline, createAdminNotification, createAssignSlaDeadlines, createAuthLogsCollection, createCannedResponsesCollection, createChatMessagesCollection, createCheckSlaOnReply, createCheckSlaOnResolve, createEmailLogsCollection, createKnowledgeBaseCollection, createMacrosCollection, createPendingEmailsCollection, createSatisfactionSurveysCollection, createSlaPoliciesCollection, createSupportClientsCollection, createTicketActivityLogCollection, createTicketMessagesCollection, createTicketStatusEmail, createTicketStatusesCollection, createTicketsCollection, createTimeEntriesCollection, createWebhookEndpointsCollection, dispatchWebhook, generateTicketSynthesis, readSupportSettings, readUserPrefs, resolveSlugs, supportPlugin };