fix: improve empty screen for call history (#27231)

* Improve empty screen

* fix type error

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
Carina Wollendorfer
2026-01-26 10:09:22 +00:00
committed by GitHub
co-authored by CarinaWolli
parent f5d79a1470
commit 216e871b22
2 changed files with 89 additions and 19 deletions
@@ -1,6 +1,12 @@
"use client";
import { getCoreRowModel, getSortedRowModel, useReactTable, type ColumnDef } from "@tanstack/react-table";
import {
getCoreRowModel,
getSortedRowModel,
useReactTable,
type ColumnDef,
} from "@tanstack/react-table";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useMemo, useState, useReducer } from "react";
@@ -10,15 +16,23 @@ import {
convertFacetedValuesToMap,
useDataTable,
} from "@calcom/features/data-table";
import { DataTableWrapper, DataTableToolbar, DataTableFilters } from "~/data-table/components";
import {
DataTableWrapper,
DataTableToolbar,
DataTableFilters,
} from "~/data-table/components";
import { useSegments } from "@calcom/features/data-table/hooks/useSegments";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import type { CallDetailsState, CallDetailsAction } from "@calcom/features/ee/workflows/lib/types";
import type {
CallDetailsState,
CallDetailsAction,
} from "@calcom/features/ee/workflows/lib/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { RouterOutputs } from "@calcom/trpc/react";
import { Badge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import { CallDetailsSheet } from "@calcom/web/modules/ee/workflows/components/CallDetailsSheet";
@@ -47,7 +61,10 @@ const initialState: CallDetailsState = {
},
};
function reducer(state: CallDetailsState, action: CallDetailsAction): CallDetailsState {
function reducer(
state: CallDetailsState,
action: CallDetailsAction
): CallDetailsState {
switch (action.type) {
case "OPEN_CALL_DETAILS":
return { ...state, callDetailsSheet: action.payload };
@@ -65,7 +82,11 @@ function CallHistoryTable(props: CallHistoryProps) {
const pathname = usePathname();
if (!pathname) return null;
return (
<DataTableProvider tableIdentifier={pathname} useSegments={useSegments} defaultPageSize={25}>
<DataTableProvider
tableIdentifier={pathname}
useSegments={useSegments}
defaultPageSize={25}
>
<CallHistoryContent {...props} />
</DataTableProvider>
);
@@ -95,20 +116,29 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
return callsData.calls.map((call) => ({
id: call.call_id || Math.random().toString(),
time: call.start_timestamp ? new Date(call.start_timestamp).toISOString() : new Date().toISOString(),
time: call.start_timestamp
? new Date(call.start_timestamp).toISOString()
: new Date().toISOString(),
duration: Math.round((call.duration_ms || 0) / 1000),
channelType: (call.call_type || "phone_call") as "web_call" | "phone_call",
channelType: (call.call_type || "phone_call") as
| "web_call"
| "phone_call",
sessionId: call.call_id || t("unknown"),
endReason: call.disconnection_reason || t("unknown"),
sessionStatus:
call.call_status === "ended" ? "completed" : call.call_status === "ongoing" ? "ongoing" : "failed",
call.call_status === "ended"
? "completed"
: call.call_status === "ongoing"
? "ongoing"
: "failed",
userSentiment:
call.call_analysis?.user_sentiment?.toLowerCase() === "positive"
? "positive"
: call.call_analysis?.user_sentiment?.toLowerCase() === "negative"
? "negative"
: "neutral",
from: "from_number" in call ? call.from_number || t("unknown") : t("unknown"),
from:
"from_number" in call ? call.from_number || t("unknown") : t("unknown"),
to: "to_number" in call ? call.to_number || t("unknown") : t("unknown"),
callCreated: call.call_analysis?.call_successful ?? true,
inVoicemail: call.call_analysis?.in_voicemail ?? false,
@@ -141,7 +171,11 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
const seconds = row.original.duration;
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return <span>{`${minutes}:${remainingSeconds.toString().padStart(2, "0")}`}</span>;
return (
<span>{`${minutes}:${remainingSeconds
.toString()
.padStart(2, "0")}`}</span>
);
},
},
{
@@ -159,7 +193,9 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
accessorKey: "sessionId",
header: t("session_id"),
size: 210,
cell: ({ row }) => <code className="text-xs">{row.original.sessionId}</code>,
cell: ({ row }) => (
<code className="text-xs">{row.original.sessionId}</code>
),
},
{
id: "endReason",
@@ -178,7 +214,12 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
},
cell: ({ row }) => {
const status = row.original.sessionStatus;
const variant = status === "completed" ? "green" : status === "ongoing" ? "blue" : "red";
const variant =
status === "completed"
? "green"
: status === "ongoing"
? "blue"
: "red";
return <Badge variant={variant}>{status}</Badge>;
},
},
@@ -192,7 +233,12 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
},
cell: ({ row }) => {
const sentiment = row.original.userSentiment;
const variant = sentiment === "positive" ? "green" : sentiment === "negative" ? "red" : "gray";
const variant =
sentiment === "positive"
? "green"
: sentiment === "negative"
? "red"
: "gray";
return <Badge variant={variant}>{sentiment}</Badge>;
},
},
@@ -216,7 +262,11 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
cell: ({ row }) => {
const created = row.original.callCreated;
const variant = created ? "green" : "red";
return <Badge variant={variant}>{created ? t("successful") : t("unsuccessful")}</Badge>;
return (
<Badge variant={variant}>
{created ? t("successful") : t("unsuccessful")}
</Badge>
);
},
},
{
@@ -227,7 +277,9 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
cell: ({ row }) => {
const inVoicemail = row.original.inVoicemail;
const variant = inVoicemail ? "blue" : "gray";
return <Badge variant={variant}>{inVoicemail ? t("yes") : t("no")}</Badge>;
return (
<Badge variant={variant}>{inVoicemail ? t("yes") : t("no")}</Badge>
);
},
},
],
@@ -286,7 +338,9 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
paginationMode="standard"
rowClassName="cursor-pointer hover:bg-subtle"
onRowMouseclick={(row) => {
const callIndex = callHistoryData.findIndex((call) => call.id === row.original.id);
const callIndex = callHistoryData.findIndex(
(call) => call.id === row.original.id
);
if (callIndex !== -1 && callsData?.calls?.[callIndex]) {
dispatch({
type: "OPEN_CALL_DETAILS",
@@ -312,14 +366,29 @@ function CallHistoryContent({ org: _org }: CallHistoryProps) {
EmptyView={
<EmptyScreen
Icon="phone"
headline={searchTerm ? t("no_result_found_for", { searchTerm }) : t("no_call_history")}
headline={
searchTerm
? t("no_result_found_for", { searchTerm })
: t("no_call_history")
}
description={t("no_call_history_description")}
className="mb-16"
buttonRaw={
!searchTerm && (
<Button>
<Link href="/workflow/new?action=calAi&templateWorkflowId=wf-11">
{t("create_first_workflow")}
</Link>
</Button>
)
}
/>
}
/>
{state.callDetailsSheet.showModal && <CallDetailsSheet state={state} dispatch={dispatch} />}
{state.callDetailsSheet.showModal && (
<CallDetailsSheet state={state} dispatch={dispatch} />
)}
</>
);
}
@@ -1016,7 +1016,8 @@
"new_team_event_type_description": "Event types enable the team to share links that show available times on their calendars and allow people to make bookings with the team.",
"event_type_created_successfully": "{{eventTypeTitle}} event type created successfully",
"no_call_history": "No call history",
"no_call_history_description": "No matching call history found for the particular account/search query.",
"no_call_history_description": "Create your first Cal.ai workflow and view your call history here.",
"create_first_workflow": "Create a Cal.ai workflow",
"event_type_updated_successfully": "{{eventTypeTitle}} event type updated successfully",
"event_type_deleted_successfully": "Event type deleted successfully",
"hours": "Hours",