Skip to content

Commit 28a206b

Browse files
athul-rsclaude
andauthored
UN-3380 [FIX] Dashboard UI/UX improvements (#1897)
* UN-3380 [FIX] Dashboard UI/UX improvements - Fix side padding to 14px for consistency with rest of product - Add white background to tab content area - Remove duplicate refresh button from Usage by Deployment table - Wire global refresh to also trigger deployment data refetch - Add sort on Last Run column in deployment usage table - Upgrade Subscription tab with area chart, summary stats, and last 30 active days window Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * UN-3380 [FIX] Address review feedback: useEffect cleanup and PropTypes - Add cleanup function to refetchRef useEffect to prevent stale closures - Narrow refetchRef PropTypes from object to shape({ current: func }) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * UN-3380 [FIX] Wrap dashboard content in white container matching island-layout Follows the same pattern as API Deployments and other pages where the entire content area (topbar, tabs, tab content) sits inside a white container box. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * UN-3380 [FIX] Align tabs bar with dashboard header Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * UN-3380 [FIX] Compact token formatting and cleaner execution column - Tokens: show as 12.6M/4.6K with full value on hover - Executions: inline compact display instead of spilling Tag components - Tabs nav alignment with dashboard header padding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * UN-3380 [FIX] Scope tab CSS to top-level tabs only, avoid nested leak Use direct child selectors on .metrics-dashboard-container to prevent padding from leaking into the nested deployment-type-tabs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * UN-3380 [FIX] Fix biome: CSS selector formatting and Header block statement Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fe023c0 commit 28a206b

4 files changed

Lines changed: 343 additions & 140 deletions

File tree

frontend/src/components/custom-tools/header/Header.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,9 @@ function Header({
315315
setEditModalOpen(false);
316316
setAlertDetails({ type: "success", content: "Updated successfully" });
317317
} catch (err) {
318-
if (err?.errorFields) return;
318+
if (err?.errorFields) {
319+
return;
320+
}
319321
setAlertDetails(handleException(err, "Failed to update"));
320322
}
321323
}, [editForm, handleUpdateTool, setAlertDetails, handleException]);

frontend/src/components/metrics-dashboard/LLMUsageTable.jsx

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import {
22
CheckCircleOutlined,
33
CloseCircleOutlined,
44
InfoCircleOutlined,
5-
ReloadOutlined,
65
} from "@ant-design/icons";
76
import {
87
Alert,
9-
Button,
108
Card,
119
Empty,
1210
Spin,
@@ -17,7 +15,7 @@ import {
1715
Typography,
1816
} from "antd";
1917
import PropTypes from "prop-types";
20-
import { useState } from "react";
18+
import { useEffect, useState } from "react";
2119

2220
import { ApiDeployments, ETLIcon, Task, Workflows } from "../../assets/index";
2321
import { useDeploymentUsage } from "../../hooks/useMetricsData";
@@ -26,6 +24,24 @@ import "./MetricsDashboard.css";
2624

2725
const { Text } = Typography;
2826

27+
/**
28+
* Format large numbers with K/M/B suffixes.
29+
* Shows full value on hover via Tooltip.
30+
*/
31+
function formatCompactNumber(value) {
32+
const num = value || 0;
33+
if (num >= 1_000_000_000) {
34+
return `${(num / 1_000_000_000).toFixed(1)}B`;
35+
}
36+
if (num >= 1_000_000) {
37+
return `${(num / 1_000_000).toFixed(1)}M`;
38+
}
39+
if (num >= 10_000) {
40+
return `${(num / 1_000).toFixed(1)}K`;
41+
}
42+
return num.toLocaleString();
43+
}
44+
2945
const columns = [
3046
{
3147
title: "Deployment",
@@ -47,8 +63,12 @@ const columns = [
4763
key: "total_tokens",
4864
sorter: (a, b) => a.total_tokens - b.total_tokens,
4965
defaultSortOrder: "descend",
50-
render: (value) => (value || 0).toLocaleString(),
51-
width: 130,
66+
render: (value) => (
67+
<Tooltip title={(value || 0).toLocaleString()}>
68+
{formatCompactNumber(value)}
69+
</Tooltip>
70+
),
71+
width: 120,
5272
},
5373
{
5474
title: "LLM Calls",
@@ -68,26 +88,26 @@ const columns = [
6888
const completed = record.completed_executions || 0;
6989
const failed = record.failed_executions || 0;
7090
return (
71-
<span>
72-
{total.toLocaleString()}{" "}
73-
{completed > 0 && (
74-
<Tooltip title={`${completed} completed`}>
75-
<Tag color="success" className="llm-usage-tag-compact">
76-
<CheckCircleOutlined /> {completed}
77-
</Tag>
78-
</Tooltip>
79-
)}
80-
{failed > 0 && (
81-
<Tooltip title={`${failed} failed`}>
82-
<Tag color="error">
83-
<CloseCircleOutlined /> {failed}
84-
</Tag>
85-
</Tooltip>
86-
)}
87-
</span>
91+
<Tooltip
92+
title={`${completed.toLocaleString()} completed, ${failed.toLocaleString()} failed`}
93+
>
94+
<span className="execution-compact">
95+
<span>{total.toLocaleString()}</span>
96+
{completed > 0 && (
97+
<span className="execution-success">
98+
<CheckCircleOutlined /> {formatCompactNumber(completed)}
99+
</span>
100+
)}
101+
{failed > 0 && (
102+
<span className="execution-error">
103+
<CloseCircleOutlined /> {formatCompactNumber(failed)}
104+
</span>
105+
)}
106+
</span>
107+
</Tooltip>
88108
);
89109
},
90-
width: 180,
110+
width: 170,
91111
},
92112
{
93113
title: "Pages",
@@ -110,12 +130,14 @@ const columns = [
110130
title: "Last Run",
111131
dataIndex: "last_execution_at",
112132
key: "last_execution_at",
133+
sorter: (a, b) =>
134+
(a.last_execution_at || "").localeCompare(b.last_execution_at || ""),
113135
render: (value) => (value ? value.split("T")[0] : "-"),
114136
width: 110,
115137
},
116138
];
117139

118-
function DeploymentUsageTable({ startDate, endDate }) {
140+
function DeploymentUsageTable({ startDate, endDate, refetchRef }) {
119141
const [activeType, setActiveType] = useState("API");
120142

121143
const { data, loading, error, refetch } = useDeploymentUsage(
@@ -124,6 +146,18 @@ function DeploymentUsageTable({ startDate, endDate }) {
124146
endDate,
125147
);
126148

149+
// Expose refetch to parent via ref
150+
useEffect(() => {
151+
if (refetchRef) {
152+
refetchRef.current = refetch;
153+
}
154+
return () => {
155+
if (refetchRef) {
156+
refetchRef.current = null;
157+
}
158+
};
159+
}, [refetch, refetchRef]);
160+
127161
const handleTabChange = (key) => {
128162
setActiveType(key);
129163
};
@@ -167,17 +201,6 @@ function DeploymentUsageTable({ startDate, endDate }) {
167201
},
168202
];
169203

170-
const refreshButton = (
171-
<Tooltip title="Refresh (bypasses cache)">
172-
<Button
173-
icon={<ReloadOutlined />}
174-
size="small"
175-
onClick={refetch}
176-
loading={loading}
177-
/>
178-
</Tooltip>
179-
);
180-
181204
const renderContent = () => {
182205
if (loading) {
183206
return (
@@ -222,7 +245,6 @@ function DeploymentUsageTable({ startDate, endDate }) {
222245
<Text strong className="llm-usage-title">
223246
Usage by Deployment
224247
</Text>
225-
{refreshButton}
226248
</div>
227249
<Tabs
228250
items={deploymentTabs}
@@ -244,11 +266,13 @@ function DeploymentUsageTable({ startDate, endDate }) {
244266
DeploymentUsageTable.propTypes = {
245267
startDate: PropTypes.string,
246268
endDate: PropTypes.string,
269+
refetchRef: PropTypes.shape({ current: PropTypes.func }),
247270
};
248271

249272
DeploymentUsageTable.defaultProps = {
250273
startDate: null,
251274
endDate: null,
275+
refetchRef: null,
252276
};
253277

254278
export { DeploymentUsageTable };

0 commit comments

Comments
 (0)