live: calls in progress, dashboard list, admin hang up and transfer

CallTracker groups channels by Linkedid from channel events (loaded from
CoreShowChannels on connect); call.updated/call.removed go to admins.
The dashboard lists calls now with state and timer, hang up (confirm)
and transfer to an internal number (AMI Redirect of the caller), both
audited. e2e m7-t4: 3 concurrent calls match core show channels.

Task: M7-T4
This commit is contained in:
iamdoubz committed 2026-10-18 15:06:57 -05:00
1 parent 4b6b95090a
commit f7fc4ed70e
14 files changed
+744 -12

No files matched your search

+25
View File
@@ -45,10 +45,32 @@ export type EmergencyCall = {
audienceUserIds: string[];
};
/** A call in progress (04 §5.1): every channel with the same Asterisk linkedid. */
export type ActiveCall = {
linkedid: string;
startedAt: string;
answeredAt?: string;
direction: 'inbound' | 'outbound' | 'internal';
from: { number: string; name?: string };
to: { number: string; name?: string };
did?: string;
state: 'ringing' | 'talking' | 'hold' | 'queued' | 'ivr' | 'voicemail' | 'parked';
/** Internal numbers involved. */
extensions: string[];
/** Asterisk channel names (what `core show channels` lists). */
channels: string[];
recording: boolean;
};
export type LiveSnapshot = {
extensions: ExtensionLive[];
/** Admins only. */
trunks?: TrunkLive[];
/** Admins only. */
calls?: ActiveCall[];
/** Queues and parked calls arrive with M8. */
queues?: never[];
parked?: never[];
ami: { connected: boolean };
};
@@ -60,6 +82,9 @@ export type LiveMessage =
| { type: 'trunk.updated'; data: TrunkLive }
| { type: 'trunk.removed'; data: { trunkId: string } }
| { type: 'emergency.call'; data: EmergencyCall }
/** Admins only. */
| { type: 'call.updated'; data: ActiveCall }
| { type: 'call.removed'; data: { linkedid: string } }
/** Only to the mailbox owner. */
| { type: 'voicemail.new'; data: VoicemailDto & { userId: string } }
/** A short message for one user (e.g. "forwarding refused"). */