Skip to main content

Contact Center Analytics: Complete Technical Specification

This document is the unified, detailed specification for standardizing the contact center data models and mapping them to visualization widgets for both Real-Time Monitoring Dashboards and Historical Analysis Reports.


Part 1: Design Principles & Editable Formulas

A. General Standardization Rules

  • User & Group Terminology: Standardized on user and group (instead of agent and queue) because users and groups are shared between CRM modules and telephony logs.
  • Telemetry & CRM Integration: Standardized and preserved conference_id (for grouping multi-party calls), module_id, and record_id (for CRM-triggered calls).
  • Numeric Durations: Expose all durations as integers in seconds (e.g., talk_duration_seconds). Formatting (like conversion to HH:MM:SS strings) is offloaded to the BI layer (e.g. Power BI DAX or UI formatting). Exposing strings from the database/CubeJS layer breaks BI aggregations.
  • Case Consistency: Expose all dimensions and measures in snake_case (e.g., is_inbound, telephony_connection_id).

B. Editable KPI Formulas

Different organizations use different calculations for key metrics. Modify the definitions below in your Cube schema to match specific requirements.

1. Average Handle Time (AHT)

  • Option A: Talk Time includes Hold Time (Default Telephony Setup)

    Formula: Handle Time = Talk Time + Wrap Time Editable CubeJS Dimension:

    handle_duration_seconds: {
    sql: `COALESCE(${CUBE}.talk_duration_seconds, 0) + COALESCE(${CUBE}.wrap_duration_seconds, 0)`,
    type: `number`
    }
  • Option B: Talk Time and Hold Time are separate

    Formula: Handle Time = Talk Time + Hold Time + Wrap Time Editable CubeJS Dimension:

    handle_duration_seconds: {
    sql: `COALESCE(${CUBE}.talk_duration_seconds, 0) + COALESCE(${CUBE}.hold_duration_seconds, 0) + COALESCE(${CUBE}.wrap_duration_seconds, 0)`,
    type: `number`
    }

2. Service Level (SLA)

  • Option A: Answered-Only SLA (Default)

    Formula: SLA = Answered Calls within Target / Total Answered Calls Editable CubeJS Measure:

    service_level: {
    sql: `CASE WHEN ${answered_calls} = 0 THEN 0 ELSE (${answered_within_sla_count} * 100.0) / ${answered_calls} END`,
    type: `number`
    }
  • Option B: Answered + Abandoned SLA (Standard COPC)

    Formula: SLA = Answered Calls within Target / (Total Answered Calls + Abandoned Calls) Editable CubeJS Measure:

    service_level: {
    sql: `CASE WHEN (${answered_calls} + ${abandoned_calls}) = 0 THEN 0
    ELSE (${answered_within_sla_count} * 100.0) / (${answered_calls} + ${abandoned_calls}) END`,
    type: `number`
    }

Part 2: Model Definitions & Fields Mapping

Table 1: calls (Unique Call Stats)

Exposes call detail metadata. One row per telephony call.

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
ididDimensionnumberUnique primary key.${CUBE}.id (Primary Key)
call_history_idtelephony_call_idDimensionstringUnique session ID from telephony/3CX.${CUBE}."call_history_id"
call_typecall_typeDimensionstringinbound, outbound, campaign, internal.${CUBE}."call_type"
statusstatusDimensionstringOutcome status (answered, unanswered, missed, abandoned, dnc, preview).${CUBE}."status"
leadIdlead_idDimensionnumberAssociated CRM marketing lead/contact ID.${CUBE}."leadId"
conference_idconference_idDimensionnumberID grouping multiple calls under the same conference.${CUBE}."conference_id"
moduleIdmodule_idDimensionnumberCRM module identifier.${CUBE}."moduleId"
recordIdrecord_idDimensionnumberCRM record identifier within the module.${CUBE}."recordId"
hotlinedialed_numberDimensionstringNumber dialed by the caller.${CUBE}."hotline"
terminatedReasontermination_reasonDimensionstringReason for call termination.${CUBE}."terminatedReason"
rescheduleAttemptdial_attemptDimensionnumberDialer attempt count for campaign dials.${CUBE}."rescheduleAttempt"
groupIdgroup_idDimensionnumberGroup ID the call originally entered.${CUBE}."groupId"
start_timestart_timeDimensiontimeTimestamp when the call started.${CUBE}."start_time"
transcripttranscriptDimensionstringAI transcription text.${CUBE}."transcript"
summarysummaryDimensionstringText summary of the conversation.${CUBE}."summary"
call_time_hour_levelstart_hourDimensionstringHour of day (0-23) for hourly profiling.EXTRACT(HOUR FROM ${CUBE}."start_time")::text
counttotal_callsMeasurecountTotal count of unique calls.type: 'count'
campaignDialedCallCountcampaign_dialed_callsMeasurecountDistinctTotal dialed campaign calls (excluding DNC/preview).sql: ${CUBE}."call_history_id", filters: leadId IS NOT NULL AND status NOT IN ('dnc', 'preview')
campaignUniqueDialedCallCountcampaign_unique_leads_dialedMeasurecountDistinctUnique leads dialed.sql: ${CUBE}."leadId", filters: leadId IS NOT NULL AND status NOT IN ('dnc', 'preview')
campaignAnsweredCallCountcampaign_answered_callsMeasurecountDistinctAnswered campaign calls.sql: ${CUBE}."call_history_id", filters: leadId IS NOT NULL AND status = 'answered'
campaignUniqueAnsweredCallCountcampaign_unique_leads_answeredMeasurecountDistinctUnique leads answered.sql: ${CUBE}."leadId", filters: leadId IS NOT NULL AND status = 'answered'

Table 2: callParticipants (Performance & SLA)

[!NOTE] Formerly summarizeCalls. Summarizes legs into user and group sessions. Crucial for user performance boards and group SLA reports. Highlighted fields show editable formulas.

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
ididDimensionstringComposite primary key for unique combinations.CONCAT_WS('-', call_id, user_id, group_id, ivr_id, customer_number)
call_idcall_idDimensionnumberCall ID referencing calls.${CUBE}.call_id
agent_iduser_idDimensionnumberReference to handling user (joins to users).${CUBE}.user_id
group_idgroup_idDimensionnumberReference to handling group (joins to groups).${CUBE}.group_id
customer_numbercustomer_numberDimensionstringCustomer phone number.${CUBE}.customer_number
hotlinedialed_numberDimensionstringHotline number dialed.${CUBE}.dialed_number
directiondirectionDimensionstringinbound, outbound.${CUBE}.direction
start_timestart_timeDimensiontimeParticipant session start time.${CUBE}.start_time
is_answeredis_answeredDimensionbooleanWas call answered by user/group?${CUBE}.is_answered
is_abandonedis_abandonedDimensionbooleanWas call abandoned in group?${CUBE}.is_abandoned
is_sla_breachedis_sla_breachedDimensionbooleanTrue if wait duration exceeded SLA.${CUBE}.is_sla_breached
is_repeatedis_repeat_callDimensionbooleanTrue if customer called in last 24h.${CUBE}.is_repeat_call
talk_time_durationtalk_duration_secondsDimensionnumberTalk time in seconds (includes hold).${CUBE}.talk_duration_seconds
hold_time_durationhold_duration_secondsDimensionnumberTotal hold time in seconds.${CUBE}.hold_duration_seconds
wrap_time_durationwrap_duration_secondsDimensionnumberWrap-up time (ACW) in seconds.${CUBE}.wrap_duration_seconds
ring_time_durationring_duration_secondsDimensionnumberRinging time in seconds.${CUBE}.ring_duration_seconds
dial_time_durationdial_duration_secondsDimensionnumberDialing time in seconds.${CUBE}.dial_duration_seconds
queue_wait_durationqueue_wait_duration_secondsDimensionnumberGroup waiting duration in seconds.${CUBE}.queue_wait_duration_seconds
mute_time_durationmute_duration_secondsDimensionnumberMuted line duration in seconds.${CUBE}.mute_duration_seconds
[NEW]handle_duration_secondsDimensionnumberEDITABLE: Total handling time effort.COALESCE(${CUBE}.talk_duration_seconds, 0) + COALESCE(${CUBE}.wrap_duration_seconds, 0) (Talk includes Hold)
creation_methodcreation_methodDimensionstringclick-to-call, auto-dial, manual.${CUBE}.creation_method
termination_reasontermination_reasonDimensionstringReason for call termination.${CUBE}.termination_reason
disposition_iddisposition_idDimensionnumberCode categorizing call outcome.${CUBE}.disposition_id
recording_urlrecording_urlDimensionstringAudio recording URL link.${CUBE}.recording_url
total_call_counttotal_callsMeasurenumber (distinct)Distinct total call count.type: 'countDistinct', sql: ${CUBE}.call_id
answered_call_countanswered_callsMeasurenumber (distinct)Total answered calls.type: 'countDistinct', sql: CASE WHEN ${CUBE}.is_answered = true THEN ${CUBE}.call_id ELSE NULL END
unanswered_call_countunanswered_callsMeasurenumber (distinct)Total unanswered calls.type: 'countDistinct', sql: CASE WHEN ${CUBE}.is_answered = false THEN ${CUBE}.call_id ELSE NULL END
abandoned_call_countabandoned_callsMeasurenumber (distinct)Total abandoned calls in groups.type: 'countDistinct', sql: CASE WHEN ${CUBE}.is_abandoned = true AND ${CUBE}.group_id IS NOT NULL THEN ${CUBE}.call_id ELSE NULL END
repeated_call_countrepeat_calls_countMeasurenumber (distinct)Total repeat inbound calls.type: 'countDistinct', sql: CASE WHEN ${CUBE}.is_repeat_call = true AND ${CUBE}.direction = 'inbound' THEN ${CUBE}.call_id ELSE NULL END
answer_rateanswer_rateMeasurenumber (ratio)Percentage of calls answered.sql: CASE WHEN ${total_calls} = 0 THEN 0 ELSE (${answered_calls} * 100.0) / ${total_calls} END
abandon_rateabandon_rateMeasurenumber (ratio)Percentage of calls abandoned.sql: CASE WHEN ${total_calls} = 0 THEN 0 ELSE (${abandoned_calls} * 100.0) / ${total_calls} END
answered_within_sla_countanswered_within_sla_countMeasurenumber (distinct)Answered calls within SLA target.type: 'countDistinct', sql: CASE WHEN ${CUBE}.is_answered = true AND (${CUBE}.is_sla_breached = false OR ${CUBE}.is_sla_breached IS NULL) AND ${CUBE}.group_id IS NOT NULL THEN ${CUBE}.call_id END
service_levelservice_levelMeasurenumber (ratio)EDITABLE: SLA Percentage.sql: CASE WHEN ${answered_calls} = 0 THEN 0 ELSE (${answered_within_sla_count} * 100.0) / ${answered_calls} END
fcrfirst_call_resolution_rateMeasurenumber (ratio)First Call Resolution % (inbound).sql: CASE WHEN InboundCalls = 0 THEN 0 ELSE (InboundCalls - ${repeat_calls_count}) * 100.0 / InboundCalls END
talk_durationtotal_talk_durationMeasurenumber (sum)Total talk duration in seconds.type: 'sum', sql: ${CUBE}.talk_duration_seconds
avg_talk_durationavg_talk_durationMeasurenumber (avg)Average talk duration in seconds.type: 'avg', sql: ${CUBE}.talk_duration_seconds
hold_timetotal_hold_durationMeasurenumber (sum)Total hold duration in seconds.type: 'sum', sql: ${CUBE}.hold_duration_seconds
avg_hold_timeavg_hold_durationMeasurenumber (avg)Average hold duration in seconds.type: 'avg', sql: ${CUBE}.hold_duration_seconds`
wrap_timetotal_wrap_durationMeasurenumber (sum)Total wrap duration in seconds.type: 'sum', sql: ${CUBE}.wrap_duration_seconds
avg_wrap_timeavg_wrap_durationMeasurenumber (avg)Average wrap duration in seconds.type: 'avg', sql: ${CUBE}.wrap_duration_seconds
queue_waiting_durationtotal_queue_wait_durationMeasurenumber (sum)Total group wait duration in seconds.type: 'sum', sql: ${CUBE}.queue_wait_duration_seconds
avg_queue_waiting_timeavg_queue_wait_durationMeasurenumber (avg)Average wait duration in seconds.type: 'avg', sql: ${CUBE}.queue_wait_duration_seconds
[NEW]average_speed_of_answerMeasurenumber (avg)EDITABLE: ASA for answered calls.type: 'avg', sql: ${CUBE}.queue_wait_duration_seconds, filters: is_answered = true
[NEW]total_handle_durationMeasurenumber (sum)EDITABLE: Total handle time in seconds.type: 'sum', sql: ${CUBE}.handle_duration_seconds
[NEW]avg_handle_timeMeasurenumber (avg)EDITABLE: Average handle time (AHT).type: 'avg', sql: ${CUBE}.handle_duration_seconds

Table 3: callSegments (Raw Call Details / Legs)

Exposes individual timeline leg events for auditing call flow paths. One row per leg.

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
ididDimensionnumberUnique internal leg identifier.${CUBE}.id (Primary Key)
call_idcall_idDimensionnumberCall ID referencing parent call.${CUBE}.call_id
tcx_connection_idtelephony_connection_idDimensionnumberLeg event ID from telephony server.${CUBE}.tcx_connection_id
start_timestart_timeDimensiontimeTimestamp when the leg started.${CUBE}.start_time
end_timeend_timeDimensiontimeTimestamp when the leg ended.${CUBE}.end_time
statussegment_stateDimensionstringState of this leg (ringing, hold, talking, etc.).${CUBE}.status
source_numbersource_numberDimensionstringDialing source phone number/extension.${CUBE}.source_number
source_typesource_typeDimensionstringType of source (user, external, ivr, group).${CUBE}.source_type
destination_numberdestination_numberDimensionstringDestination phone number/extension.${CUBE}.destination_number
destination_typedestination_typeDimensionstringType of destination (user, external, ivr, group).${CUBE}.destination_type
is_Inboundis_inboundDimensionbooleanTrue if segment direction was inbound.${CUBE}.is_Inbound
userIduser_idDimensionnumberAssociated user ID handling this leg.${CUBE}."userId"
groupIdgroup_idDimensionnumberAssociated group ID handling this leg.${CUBE}."groupId"
creation_methodcreation_methodDimensionstringClick-to-call, dialer, manual, etc.${CUBE}.creation_method
termination_reasontermination_reasonDimensionstringTermination cause code for this leg.${CUBE}.termination_reason
recording_urlrecording_urlDimensionstringCall recording file URL.${CUBE}."recording_url"
dispositionIddisposition_idDimensionnumberDisposition ID for this leg.${CUBE}."dispositionId"
[NEW]segment_duration_secondsDimensionnumberCalculated duration of this leg.EXTRACT(EPOCH FROM (COALESCE(end_time, NOW()) - start_time))
countsegment_countMeasurecountTotal count of segment lines.type: 'count'
durationtotal_segment_durationMeasurenumber (sum)Total duration spent in this state.type: 'sum', sql: ${CUBE}.segment_duration_seconds
[NEW]avg_segment_durationMeasurenumber (avg)Average duration spent in this state.type: 'avg', sql: ${CUBE}.segment_duration_seconds

Table 4: workShifts (User Shift Clock-In/Clock-Out)

Tracks when users log in and out of their daily shifts.

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
shiftIdshift_idDimensionnumberPrimary key of the shift.${CUBE}."shiftId" (Primary Key)
userIduser_idDimensionnumberUser reference (joins to users).${CUBE}."userId"
startTimestart_timeDimensiontimeClock-in timestamp.${CUBE}."startTime"
endTimeend_timeDimensiontimeClock-out timestamp.${CUBE}."endTime"
[NEW]shift_duration_secondsDimensionnumberCalculated duration of the shift.EXTRACT(EPOCH FROM (COALESCE(${CUBE}."endTime", NOW()) - ${CUBE}."startTime"))
countshift_countMeasurecountCount of shift events.type: 'count'
shiftDurationtotal_shift_durationMeasurenumber (sum)Standardized: Sum of shift duration in seconds.type: 'sum', sql: ${CUBE}.shift_duration_seconds
[NEW]avg_shift_durationMeasurenumber (avg)Average shift duration in seconds.type: 'avg', sql: ${CUBE}.shift_duration_seconds

Table 5: statusLog (User Presence State Timeline)

Logs status changes (Available, Break, Meeting) during a shift. Exposes durations.

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
ididDimensionnumberLog row primary key.${CUBE}."id" (Primary Key)
shiftIdshift_idDimensionnumberParent shift reference.${CUBE}."shiftId"
statusIdstatus_idDimensionnumberReference to presence state (joins to presenceStatuses).${CUBE}."statusId"
timestampstatus_change_timeDimensiontimeTimestamp status was entered.${CUBE}."timestamp"
[NEW]next_status_change_timeDimensiontimeTimestamp status was exited.${CUBE}.next_timestamp
[NEW]status_duration_secondsDimensionnumberStandardized: Duration spent in this status state.GREATEST(EXTRACT(EPOCH FROM (LEAST(COALESCE(${CUBE}.next_timestamp, NOW()), COALESCE(${CUBE}."endTime", NOW())) - GREATEST(${CUBE}."timestamp", ${CUBE}."startTime"))), 0)
countstatus_log_countMeasurecountCount of state log updates.type: 'count'
durationtotal_status_durationMeasurenumber (sum)Sum of status duration in seconds.type: 'sum', sql: ${CUBE}.status_duration_seconds
avgDurationavg_status_durationMeasurenumber (avg)Average status duration in seconds.type: 'avg', sql: ${CUBE}.status_duration_seconds

Table 6: presenceStatuses (Presence Status Definitions)

Lookup table mapping statuses (Available, Busy, Break, Meeting) and approval rules.

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
statusIdstatus_idDimensionnumberUnique ID of status.${CUBE}."statusId" (Primary Key)
namestatus_nameDimensionstringDisplay name (Available, Busy, Lunch, Break).${CUBE}."name"
codestatus_codeDimensionstringSystem code representation (e.g., aux_break).${CUBE}."code"
colorcolor_codeDimensionstringUI color HEX code.${CUBE}."color"
requiresApprovalrequires_approvalDimensionbooleanTrue if supervisor approval is required.${CUBE}."requiresApproval"
tcxStatustelephony_statusDimensionstringTelephony state mapped (Available, Away, DND).${CUBE}."tcxStatus"
enableQueueLoginenable_group_loginDimensionbooleanTrue if this status logs user into groups.${CUBE}."enableQueueLogin"

Table 7: statusMonitor (Real-Time Presence Monitor)

Maintains the immediate presence state of all users. Used for live dashboards.

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
ididDimensionnumberPrimary Key.${CUBE}."id" (Primary Key)
userIduser_idDimensionnumberReference to user (joins to users).${CUBE}."userId"
currentStatusIdcurrent_status_idDimensionnumberCurrent status (joins to presenceStatuses).${CUBE}."currentStatusId"
previousStatusIdprevious_status_idDimensionnumberPrevious status.${CUBE}."previousStatusId"
statusUpdatedTimestatus_updated_timeDimensiontimeWhen current status was entered.${CUBE}."statusUpdatedTime"
[NEW]time_in_status_secondsDimensionnumberReal-time seconds in current status.EXTRACT(EPOCH FROM (NOW() - ${CUBE}."statusUpdatedTime"))

Table 8: queueLoginHistory (Group Login Logs)

Tracks when users log in and out of specific groups (queues).

Existing FieldProposed FieldDimension / MeasureTypeDescriptionSQL Formula / Cube Definition
ididDimensionnumberPrimary Key.${CUBE}."id" (Primary Key)
userIduser_idDimensionnumberReference to user (joins to users).${CUBE}."userId"
shiftIdshift_idDimensionnumberReference to shift (joins to workShifts).${CUBE}."shiftId"
queueIdgroup_idDimensionnumberReference to group (joins to groups).${CUBE}."queueId"
loginTimelogin_timeDimensiontimeGroup login timestamp.${CUBE}."loginTime"
logoutTimelogout_timeDimensiontimeGroup logout timestamp.${CUBE}."logoutTime"
[NEW]login_duration_secondsDimensionnumberDuration logged into group in seconds.EXTRACT(EPOCH FROM (COALESCE(${CUBE}."logoutTime", NOW()) - ${CUBE}."loginTime"))
countlogin_event_countMeasurecountCount of login events.type: 'count'
[NEW]total_login_durationMeasurenumber (sum)Sum of logged-in duration.type: 'sum', sql: ${CUBE}.login_duration_seconds
[NEW]avg_login_durationMeasurenumber (avg)Average logged-in duration.type: 'avg', sql: ${CUBE}.login_duration_seconds

Part 3: Widget Mapping Specifications

For each dashboard and report, the tables below map frontend widgets directly to their required backend models, fields, and logical filters.

A. Real-Time Monitoring Dashboards (Live Operations)

Dashboard A1: Inbound Queue & Live Calls Monitor

Exposes live group performance and active queue levels.

📋 Wireframe & Mock

Wireframe Layout (ASCII)

┌─────────────────────────────────────────────────────────────────────────────────┐
│ 🔴 LIVE QUEUE MONITOR [Today] [Filters ▼] [Export] [Refresh ⟳] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 42 │ │ 18 │ │ 92.5% │ │ 00:45 │ │
│ │ Calls Waiting│ │ Staff Online │ │ SLA Today │ │ Avg Wait │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ACTIVE QUEUE STAFFING GROUP LOGIN DURATION │
│ ┌────────────────────────────────────────┐ ┌──────────────────────────────┐ │
│ │ Agent │ Status │ Since │ Calls │ │ Support │ Sales │ Billing │ │
│ ├────────────────────────────────────────┤ │ ├──────────────────────────────┤ │
│ │ John Smith │ Available│ 09:30 │ 24 │ │ 8 agents │ 5 agt │ 3 agt │ │
│ │ Jane Doe │ Available│ 10:15 │ 18 │ │ SLA:95% │ 88% │ 91% │ │
│ │ Mike Jones │ On Break│ 14:00 │ 15 │ │ Waiting: │ Waiting│ Waiting │ │
│ │ Sarah Lee │ Available│ 08:45 │ 31 │ │ 12 calls │ 3 calls│ 1 call │ │
│ └────────────────────────────────────────┘ └──────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ LIVE CALL TIMELINES (Auto-refreshing every 5s) │
│ ┌────────────────────────────────────────────────────────────────────────────┐ │
│ │ From │ To │ State │ Duration │ Group │ Queue Wait │ Agent │ │
│ ├────────────────────────────────────────────────────────────────────────────┤ │
│ │ 555-1001 │ Queue │ Waiting │ 00:42 │ Support │ 00:42 │ — │ │
│ │ 555-1002 │ 1234 (J.) │ Talking │ 00:18 │ Support │ 00:05 │ John │ │
│ │ 555-1003 │ Queue │ Waiting │ 00:28 │ Sales │ 00:28 │ — │ │
│ │ 555-1004 │ 5678 (S.) │ On Hold │ 00:12 │ Support │ 00:03 │ Sarah│ │
│ └────────────────────────────────────────────────────────────────────────────┘ │
│ Last Updated: 14:32:05 UTC | Latency: <1s | Next Refresh: 14:32:10 │
└─────────────────────────────────────────────────────────────────────────────────┘
Widget TitleFrontend ComponentBackend ModelGrouping DimensionsMetrics / MeasuresActive FiltersDescription / Purpose
Live Calls WaitingstatboxWidget.tsxcallSegmentsNoneDistinct count of call_idsegment_state = 'queue_waiting' AND end_time IS NULLShows callers currently holding in queue.
Logged-in StaffstatboxWidget.tsxstatusMonitorNoneDistinct count of user_idcurrent_status_id.status_name != 'Offline'Number of ready/active users.
Live SLA TodaystatboxWidget.tsxcallParticipantsNoneservice_leveldirection = 'inbound' AND start_time >= CURRENT_DATEToday's running Service Level %.
Active Queue StaffingtableWidget.tsxstatusMonitor joined to users/presenceStatusesusers.username, presenceStatuses.status_nametime_in_status_secondspresenceStatuses.status_name != 'Offline'Live list of agent states sorted by duration.
Live Call TimelinestableWidget.tsxcallSegments joined to callscalls.telephony_call_id, source_number, segment_statesegment_duration_secondsend_time IS NULLLive scrollable list of active calls in progress.

Dashboard A2: Live Campaigns & Dialer Monitor

Monitors active outbound marketing campaigns and dialer pacing.

📋 Wireframe & Mock

┌─────────────────────────────────────────────────────────────────────────────────┐
│ 🚀 CAMPAIGNS & DIALER MONITOR [Today] [Campaign Filter ▼] [Refresh ⟳] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 3 │ │ 48/min │ │ 287 │ │ 156 │ │
│ │ Running Cpgn │ │ Dialer Speed │ │ Dials Today │ │ Connections │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ DIAL STATUS PIE CHART │ RUNNING CAMPAIGNS TABLE │
│ ┌──────────────────────────────┐ │ ┌────────────────────────────────────┐ │
│ │ │ │ │ Campaign │ Status │ Dials │ Ans │ │
│ │ Answered (156) │ │ ├────────────────────────────────────┤ │
│ │ 31% ●━━━━━ │ │ │ Summer Sale │ Active │ 145 │ 82 │ │
│ │ Busy (89) │ │ │ Q3 Renewal │ Active │ 98 │ 51 │ │
│ │ 18% ●━━ │ │ │ Trial Offer │ Paused │ 44 │ 23 │ │
│ │ No Answer (142) │ │ └────────────────────────────────────┘ │
│ │ 29% ●━━━━━ │ │ │
│ │ DNC/Invalid (60) │ │ CAMPAIGN DIALS DETAIL (Last 10 mins) │
│ │ 12% ●━━ │ │ ┌────────────────────────────────────┐ │
│ │ Not Reached (40) │ │ │ Call ID │ Lead │ Status │ Agent │ │
│ │ 10% ●━ │ │ │─────────│──────│──────────│────────│ │
│ │ Other (10) │ │ │ C-2841 │ 5821 │ Answered │ Sarah │ │
│ │ 0% ● │ │ │ C-2840 │ 5820 │ Busy │ — │ │
│ │ │ │ │ C-2839 │ 5819 │ Answered │ Mike │ │
│ └──────────────────────────────┘ │ │ C-2838 │ 5818 │ No Answ. │ — │ │
│ │ └────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
Widget TitleFrontend ComponentBackend ModelGrouping DimensionsMetrics / MeasuresActive FiltersDescription / Purpose
Running CampaignsstatboxWidget.tsxcalls joined to campaignsNoneDistinct count of campaign_idcampaigns.status = 'active'Counts active outbound campaigns.
Dialer SpeedstatboxWidget.tsxcallsNoneCount of starts / minlead_id IS NOT NULL AND status NOT IN ('dnc','preview') AND start_time >= NOW() - INTERVAL '1 minute'Pacing indicator of outbound calls.
Dial Status RatiosPieDonutChart.tsxcallsstatusCount of callslead_id IS NOT NULL AND start_time >= CURRENT_DATESplit of today's dialed outcomes (busy, answer, etc.).
Campaign Dials DetailtableWidget.tsxcalls joined to users/callParticipantstelephony_call_id, lead_id, status, dial_attempt, users.usernameNonestart_time >= CURRENT_DATE - INTERVAL '10 minutes'Recent calls detail list for audit.

Report B1: Agent Shift & Adherence Report

Audits shifts, auxiliary codes (meeting, break), and scheduled compliance.

📋 Wireframe & Mock

┌─────────────────────────────────────────────────────────────────────────────────┐
│ 👤 AGENT SHIFT & ADHERENCE REPORT [Date Range: 08/01 - 08/14] [Export ⬇] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 412 hrs │ │ 8.2 hrs/avg │ │ 78% Working │ │ 22% AUX Time │ │
│ │ Total Hours │ │ Per Shift │ │ vs Scheduled │ │ (Break/Mtg) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ TIME SPENT BY STATUS (Pie) │ AGENT AUX MATRIX (Pivot Table) │
│ ┌──────────────────────────────┐ │ ┌──────────────────────────────────────┐ │
│ │ │ │ │ Agent │Working│Break │Lunch│Mtg │ │
│ │ Available (78%) │ │ ├──────────────────────────────────────┤ │
│ │ ●━━━━━━━━━━━━━━━━━━━━━━ │ │ │ John Smith │ 38:24 │ 1:20│ 1:00│0:30│ │
│ │ Break (12%) │ │ │ Jane Doe │ 39:15 │ 0:45│ 1:00│0:30│ │
│ │ ●━━ │ │ │ Mike Jones │ 37:50 │ 2:10│ 1:00│0:30│ │
│ │ Lunch (8%) │ │ │ Sarah Lee │ 38:30 │ 1:00│ 1:00│0:45│ │
│ │ ●━━ │ │ │ Tom Brown │ 36:25 │ 2:35│ 1:00│0:45│ │
│ │ Meeting (2%) │ │ │ Average │ 38:09 │ 1:38│ 1:00│0:36│ │
│ │ ● │ │ └──────────────────────────────────────┘ │
│ │ │ │ ✅ All agents within compliance │
│ └──────────────────────────────┘ │ ⚠️ Mike Jones: High break time │
│ │ │
│ DETAILED SHIFT LOGS │ │
│ ┌────────────────────────────────┐ │ │
│ │ Agent │ Date │ In │Out │ │ │
│ ├────────────────────────────────┤ │ │
│ │ John S. │ 2026-08-14│09:00│18:00│ │
│ │ Jane D. │ 2026-08-14│08:00│17:15│ │
│ │ Mike J. │ 2026-08-14│10:00│19:00│ │
│ │ Sarah L. │ 2026-08-14│09:30│18:30│ │
│ └────────────────────────────────┘ │ │
└─────────────────────────────────────────────────────────────────────────────────┘
Widget TitleFrontend ComponentBackend ModelGrouping DimensionsMetrics / MeasuresActive FiltersDescription / Purpose
Hours WorkedstatboxWidget.tsxworkShiftsNonetotal_shift_durationSelected Time RangeTotal hours staffed.
Average Shift LengthstatboxWidget.tsxworkShiftsNoneavg_shift_durationSelected Time RangeAverage clocked shift time.
Time Spent in StatusPieDonutChart.tsxstatusLog joined to presenceStatusespresenceStatuses.status_nametotal_status_durationSelected Time RangeProportion of AUX vs Available time.
Agent AUX MatrixpivotTableWidget.tsxstatusLog joined to users/presenceStatusesRows: users.username
Columns: presenceStatuses.status_name
total_status_durationSelected Time RangePivot matrix of auxiliary state utilization by agent.
Detailed Shift LogstableWidget.tsxworkShifts joined to usersusers.username, start_time, end_timeshift_duration_secondsSelected Time RangeRaw list of clocked user shifts.

Report B2: Inbound Queues & SLA Performance Report

Trends SLA metrics, abandonment thresholds, and speed of answer.

📋 Wireframe & Mock

┌─────────────────────────────────────────────────────────────────────────────────┐
│ 📊 INBOUND QUEUES & SLA PERFORMANCE [Week] [Group Filter ▼] [PDF] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 92.8% │ │ 3.2% │ │ 156 │ │ 00:45 │ │
│ │ Total SLA │ │ Abandon Rate │ │ Total Calls │ │ Avg Wait │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ SLA TREND BY GROUP (Line Chart) │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ SLA % ▲ Support ——— Sales ---- Billing ╌╌╌╌╌ │ │
│ │ 100 ▼ │ │
│ │ 95 ├─ ──●─── ●──●──●──●── ● ← Support (95%) │ │
│ │ 90 ├──●──●──●──────────────────●────●─ ← Sales (88%) │ │
│ │ 85 ├──────────────●──●─●──●─●─── ──── ← Billing (92%) │ │
│ │ 80 ├────────────────────────────────── ← Target (80%) │ │
│ │ └────────────────────────────────────── (Mon-Sun this week) │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ABANDON WAIT CURVE (Sunburst) │ WAIT vs ASA TREND (2 Line Chart) │
│ ┌──────────────────────────────┐ │ ┌──────────────────────────────────┐ │
│ │ ┌─────────────────┐ │ │ │ Avg Wait (blue) ASA (orange) │ │
│ │ ╱ Support Queue ╲ │ │ │ │ 2:00 ▲ │ │
│ │ ╱ ├─0-1 min ╲ │ 12% │ │ 1:50 ─┼─────●─────●────●─────● │ │
│ │ ╱ ├─1-3 min ╲│ 18% │ │ 1:40 ─┼●───●────●────●───● │ │
│ │ ╱ ├─3-5 min ╲ │ 38% │ │ 1:30 ─┼─●────────────────── │ │
│ │ ╱ ├─5+ min ╲│ 32% │ │ 1:20 ─┴────────────────────── │ │
│ │ ╱ └─────────────────┘ │ │ Mon Tue Wed Thu Fri │ │
│ │ │ └──────────────────────────────────┘ │
│ │ Abandoned = 3.2% (5 of 156) │ Correlation: Wait time peaked Wed │
│ └──────────────────────────────────┘ ASA improved by 12s mid-week │
└─────────────────────────────────────────────────────────────────────────────────┘
Widget TitleFrontend ComponentBackend ModelGrouping DimensionsMetrics / MeasuresActive FiltersDescription / Purpose
Total Inbound SLAstatboxWidget.tsxcallParticipantsNoneservice_leveldirection = 'inbound'Total historical Service Level %.
Total Abandon RatestatboxWidget.tsxcallParticipantsNoneabandon_ratedirection = 'inbound'Percentage of group calls abandoned.
SLA Trend by GroupLineAreaChart.tsxcallParticipants joined to groupsstart_time (by Day/Week), groups.nameservice_leveldirection = 'inbound'SLA trend comparison over time.
Abandon Wait CurveSunburstWidget.tsxcallParticipants joined to groupsgroups.name $\rightarrow$ Wait Duration Bucketabandoned_callsdirection = 'inbound'Shows queue wait time before hangs.
Wait vs ASA TrendLineAreaChart.tsxcallParticipantsstart_time (by Day)avg_queue_wait_duration, average_speed_of_answerdirection = 'inbound'Visualizes speed of answer vs overall waits.

Report B3: Outbound Campaign Historical Report

Audits campaign outcomes, agent conversion, and lead lists.

📋 Wireframe & Mock

┌─────────────────────────────────────────────────────────────────────────────────┐
│ 🎯 CAMPAIGN HISTORICAL REPORT [Aug 2026] [Campaign Filter ▼] [Download ⬇] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 523 │ │ 287 │ │ 54.9% │ │ 156 │ │
│ │ Total Dials │ │ Unique Leads │ │ Connection % │ │ Connections │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ OUTCOME SPLIT (Donut Chart) │ CAMPAIGN MATRIX (Pivot Table) │
│ ┌──────────────────────────────┐ │ ┌──────────────────────────────────────┐ │
│ │ │ │ │ Campaign │Answered│Busy│No Ans│DNC│ │
│ │ Answered (156) 30% │ │ ├──────────────────────────────────────┤ │
│ │ ●────────────┐ │ │ │ Summer Sale │ 82 │ 28 │ 18 │17 │ │
│ │ Busy (89) 17% │ ●────┤ │ │ Q3 Renewal │ 51 │ 19 │ 15 │13 │ │
│ │ No Answer (142) 27%─┘ │ │ │ Trial Offer │ 23 │ 8 │ 7 │ 6 │ │
│ │ DNC (78) 15% │ │ │ Partner Promo │ 12 │ 4 │ 3 │ 3 │ │
│ │ Other (58) 11% │ │ │ TOTAL │ 168 │ 59 │ 43 │39 │ │
│ │ │ │ │ Total Dials │ 523 │ │
│ │ Click for raw dial list ▶ │ │ │ Connection % │ 32%│ │
│ └──────────────────────────────┘ │ └──────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ CRM MODULE SOURCE (Bar Chart) │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ Leads │ │ │
│ │ 300 ├─ │ │
│ │ 250 ├─ ┌─────────┐ │ │
│ │ 200 ├─ │ 287 │ ┌──────┐ │ │
│ │ 150 ├─ │ Leads │ │ 156 │ ┌────┐ ┌───┐ │ │
│ │ 100 ├─ │ Module │ │Sales │ │ 45 │ │ 8 │ │ │
│ │ 50 ├─ │ A │ │ Opp │ │Apps│ │EDI│ │ │
│ │ 0 └─────────────────────────────────────────────────────────────────│ │
│ │ Module A Module B Sales Opp Apps Internal EDI Feeds │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
Widget TitleFrontend ComponentBackend ModelGrouping DimensionsMetrics / MeasuresActive FiltersDescription / Purpose
Total Campaign DialsstatboxWidget.tsxcallsNonecampaign_dialed_callslead_id IS NOT NULLTotal dialed outbound leads.
Unique Leads ReachedstatboxWidget.tsxcallsNonecampaign_unique_leads_answeredlead_id IS NOT NULLCount of unique leads connected.
Outcome SplitPieDonutChart.tsxcallsstatustotal_callslead_id IS NOT NULLPercentage breakdown of outcomes.
Campaign MatrixpivotTableWidget.tsxcalls joined to campaignsRows: campaigns.name
Columns: status
total_callslead_id IS NOT NULLCall counts per campaign per status.
CRM Module SourceBarColumnChart.tsxcallsmodule_idtotal_callsmodule_id IS NOT NULLShows call counts triggered by CRM module source.

Report B4: Historical Time Profiles (Hour/Day Breakdowns)

Highlights high-traffic hourly profiles and day-of-week load volumes.

📋 Wireframe & Mock

┌─────────────────────────────────────────────────────────────────────────────────┐
│ 📅 HISTORICAL TIME PROFILES [Aug 2026] [Week View] [Heat Map] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ TRAFFIC HEATMAP (Calls by Hour × Day) │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Mon Tue Wed Thu Fri Sat Sun Legend │ │
│ │ 0-1 AM │ 🟦 🟦 🟦 🟦 🟦 🟦 🟦 ░░░░░░░ │ │
│ │ 1-2 AM │ 🟦 🟦 🟦 🟦 🟦 🟦 🟦 Low: 5-15 │ │
│ │ 2-3 AM │ 🟦 🟦 🟦 🟦 🟦 🟦 🟦 Medium: 16-50 │ │
│ │ ... │ ⋮ │ │
│ │ 9-10 AM │ 🟩 🟩 🟩 🟩 🟩 🟥 🟥 High: 51-100 │ │
│ │ 10-11 AM │ 🟩 🟩 🟨 🟩 🟩 🟥 🟥 Peak: 100+ │ │
│ │ 11-12 PM │ 🟨 🟨 🟥 🟨 🟥 🟥 🟥 │ │
│ │ 12-1 PM │ 🟥 🟥 🟥 🟥 🟥 🟨 🟨 Hotspot: Wed 11-1 (peak) │ │
│ │ 1-2 PM │ 🟩 🟩 🟨 🟩 🟩 🟦 🟦 Coldspot: Sat/Sun nights │ │
│ │ 2-3 PM │ 🟩 🟩 🟩 🟩 🟩 🟦 🟦 │ │
│ │ ... │ ⋮ │ │
│ │ 11-12 PM │ 🟦 🟦 🟦 🟦 🟦 🟦 🟦 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ TRAFFIC & AHT TRENDS (Dual Axis Chart) HOURLY DETAIL MATRIX │
│ ┌───────────────────────────────────────────┐ ┌───────────────────────────────┐ │
│ │ Calls ▲ AHT ▲ │ │ Hour │ Inbound │ Outbound │ │
│ │ 500 ├─●───●─────●─────● │ │ ─────────────────────────────│ │
│ │ 400 ├─●────────────●──── │ │ 09:00 │ 45 │ 28 │ │
│ │ 300 ├────────────────── ← Calls │ │ 10:00 │ 58 │ 41 │ │
│ │ 200 ├────────────────── │ │ 11:00 │ 72 │ 35 │ │
│ │ 100 ├────────────────── │ │ 12:00 │ 91 │ 42 │ │
│ │ ├──────────────────────────────────│ │ 13:00 │ 68 │ 38 │ │
│ │ 600s ├────●──●───●───●──●──● ← AHT │ │ 14:00 │ 52 │ 45 │ │
│ │ 500s ├───●────●───●───●────● │ │ 15:00 │ 49 │ 52 │ │
│ │ 400s ├────────────────────── │ │ Average AHT rising in │ │
│ │ └─────────────────────────────────│ │ peak hours (lunch effect) │ │
│ │ Mon Tue Wed Thu Fri Sat Sun │ └───────────────────────────────┘ │
│ └───────────────────────────────────────────┘ │
│ │
│ Insights: Peak traffic Wed-Fri 11am-1pm | AHT increases 15% in peak window │
│ Weekend traffic down 60% | Consider staffing allocation review │
└─────────────────────────────────────────────────────────────────────────────────┘
Widget TitleFrontend ComponentBackend ModelGrouping DimensionsMetrics / MeasuresActive FiltersDescription / Purpose
Traffic HeatmapHeatMapWidget.tsxcallsX-Axis: Day of Week
Y-Axis: start_hour
total_callsSelected Time RangeDisplays high-traffic time blocks.
Traffic & AHT TrendsLineAreaChart.tsxcallParticipantsstart_time (by Day)total_calls, avg_handle_timeSelected Time RangeTrends call volume vs agent handle time.
Hourly Detail MatrixpivotTableWidget.tsxcallParticipantsRows: start_hour
Columns: direction
total_calls, avg_handle_timeSelected Time RangePerformance metrics per hour of day.