Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/services/admin/admin-telephony-workspace.server.ts
T

275 lines
8.1 KiB
TypeScript

import type { AdminTelephonyAction } from "@hivegrid/view-models";
import { writeAdminSalesAudit } from "@hivegrid/db/admin-sales-core.server";
import type { AdminTelephonyOperatorContext } from "./admin-telephony-connections.server";
import { adminPath } from "~/utils/app/CanonicalUrlUtils";
import { db } from "~/utils/db.server";
export function adminTelephonyWorkspaceHrefs() {
return {
calls: adminPath("sales/calls", { mode: "host-aware" }),
settings: adminPath("settings/telephony", {
mode: "host-aware",
}),
newLead: adminPath("leads/new", { mode: "host-aware" }),
};
}
export async function readAdminTelephonyWorkspace(
context: AdminTelephonyOperatorContext,
selectedCallId?: string | null
) {
const ownerWhere = context.canReadAllCommunication
? {}
: { adminUserId: context.adminUserId };
const [calls, dispositions, contacts, policy, dialerLists] =
await Promise.all([
db.adminCallSession.findMany({
where: ownerWhere,
orderBy: [{ startedAt: "desc" }, { id: "asc" }],
take: 100,
include: {
contact: {
select: { firstName: true, lastName: true },
},
lead: {
select: {
firstName: true,
lastName: true,
email: true,
},
},
deal: { select: { title: true } },
recording: {
select: { status: true, retainedUntil: true },
},
matchCandidates: {
orderBy: [
{ confidenceBasisPoints: "desc" },
{ createdAt: "asc" },
],
include: {
contact: {
select: {
firstName: true,
lastName: true,
deals: {
where: { deal: { status: "open" } },
orderBy: [
{ isPrimary: "desc" },
{ createdAt: "asc" },
],
take: 1,
select: { dealId: true },
},
},
},
lead: {
select: {
firstName: true,
lastName: true,
email: true,
},
},
},
},
},
}),
db.adminCallDisposition.findMany({
where: { active: true, archivedAt: null },
orderBy: [{ position: "asc" }, { code: "asc" }],
select: { code: true },
}),
db.adminContact.findMany({
where: {
archivedAt: null,
normalizedPhone: { not: null },
},
orderBy: [{ lastName: "asc" }, { firstName: "asc" }, { id: "asc" }],
take: 500,
select: {
id: true,
firstName: true,
lastName: true,
normalizedPhone: true,
doNotCall: true,
deals: {
where: { deal: { status: "open" } },
orderBy: [{ isPrimary: "desc" }, { createdAt: "asc" }],
take: 1,
select: { dealId: true },
},
},
}),
db.adminTelephonyPolicy.findUnique({
where: { adminUserId: context.adminUserId },
}),
db.adminDialerList.findMany({
where: {
...ownerWhere,
archivedAt: null,
},
orderBy: [{ updatedAt: "desc" }, { id: "asc" }],
take: 50,
include: {
entries: {
orderBy: [{ position: "asc" }, { id: "asc" }],
include: {
contact: {
select: {
id: true,
firstName: true,
lastName: true,
normalizedPhone: true,
},
},
},
},
},
}),
]);
const foreignCalls = calls.filter(
(call) => call.adminUserId !== context.adminUserId
);
if (foreignCalls.length > 0) {
await writeAdminSalesAudit(db, {
actor: context.actor("Fremde Team-Anrufe aufgelistet"),
action: "admin.sales.telephony.foreignCalls.read",
targetType: "AdminCallSession",
targetId: foreignCalls[0]!.id,
after: {
foreignTargets: foreignCalls.map((call) => ({
targetId: call.id,
dataOwnerAdminUserId: call.adminUserId,
})),
accessKind: "read",
},
});
}
const selectedCall = selectedCallId
? calls.find((call) => call.id === selectedCallId)
: null;
if (
selectedCall &&
selectedCall.adminUserId !== context.adminUserId
) {
await writeAdminSalesAudit(db, {
actor: context.actor("Fremden Team-Anruf geöffnet"),
action: "admin.sales.telephony.foreignCall.read",
targetType: "AdminCallSession",
targetId: selectedCall.id,
after: {
dataOwnerAdminUserId: selectedCall.adminUserId,
accessKind: "read",
},
});
}
return {
calls,
selectedCallId,
dispositions,
contacts,
policy,
dialerLists,
permissions: {
canCall: context.canCall,
canManageSettings: context.canManageSettings,
canManageRecording: context.canManageRecording,
},
};
}
export async function applyAdminTelephonyWorkspaceAction(
context: AdminTelephonyOperatorContext,
action: AdminTelephonyAction
) {
const actor = context.actor(action.reason);
switch (action.intent) {
case "save-outcome":
return context.actions.saveOutcome({
adminUserId: context.adminUserId,
callSessionId: action.callSessionId,
dispositionCode: action.dispositionCode,
note: action.note,
callbackTask:
action.callbackTitle && action.callbackDueAt
? {
title: action.callbackTitle,
dueAt: new Date(action.callbackDueAt),
timeZone: "Europe/Berlin",
priority: "high",
}
: null,
actor,
});
case "confirm-match":
return context.actions.confirmMatch({
adminUserId: context.adminUserId,
callSessionId: action.callSessionId,
contactId: action.contactId,
leadId: action.leadId,
dealId: action.dealId,
actor,
});
case "queue-call":
return context.actions.queueOutboundCall({
adminUserId: context.adminUserId,
contactId: action.contactId,
dealId: action.dealId,
dialerEntryId: action.dialerEntryId,
idempotencyKey: action.idempotencyKey,
actor,
});
case "create-dialer":
return context.actions.createDialerList({
adminUserId: context.adminUserId,
name: action.name,
contactIds: action.contactIds,
actor,
});
case "next-dialer":
return context.actions.activateNextDialerEntry({
adminUserId: context.adminUserId,
dialerListId: action.dialerListId,
actor,
});
case "finish-dialer":
return context.actions.finishDialerEntry({
adminUserId: context.adminUserId,
dialerEntryId: action.dialerEntryId,
outcome: action.outcome,
callSessionId: action.callSessionId,
actor,
});
case "update-policy":
return context.actions.updatePolicy({
adminUserId: context.adminUserId,
outboundEnabled: action.outboundEnabled,
dailyCallLimit: action.dailyCallLimit,
workDayStartMinutes: action.workDayStartMinutes,
workDayEndMinutes: action.workDayEndMinutes,
workDays: action.workDays,
timeZone: action.timeZone,
dncEnforced: true,
actor,
});
case "update-recording-policy":
return context.recordings.updatePolicy({
adminUserId: context.adminUserId,
enabled: action.enabled,
accepted: action.accepted,
consentText: action.consentText,
consentVersion: action.consentVersion,
actor,
});
case "confirm-recording-consent":
return context.recordings.confirmCallConsent({
adminUserId: context.adminUserId,
callSessionId: action.callSessionId,
confirmed: action.confirmed,
actor,
});
}
}