+ {dailyTarget > 0 && (
+
+
+
+ {`${t('settings.costDashboard.dailyTarget')}: ${formatCurrency(dailyTarget, currency)}`}
+
+
+ )}
+
+
+
+
+
+ formatCurrency(v, currency)}
+ />
+ {
+ const item = props.payload?.[0]?.payload as ChartPoint | undefined;
+ if (!item) return null;
+ return (
+ 0
+ ? `${t('settings.costDashboard.dailyTarget')}: ${formatCurrency(dailyTarget, currency)}`
+ : undefined
+ }
+ />
+ );
+ }}
+ />
+ {dailyTarget > 0 && (
+
+ )}
+
+ {chartData.map(entry => (
+ |
+ ))}
+ {
+ if (!value) return null;
+ const cx = Number(x ?? 0) + Number(width ?? 0) / 2;
+ const cy = Math.max(0, Number(y ?? 0) - 6);
+ return (
+
+
+
+ {t('settings.costDashboard.todayBadge')}
+
+
+ );
+ }}
+ />
+
+
+
+
+
+ );
+};
+
+export default CostBarChart;
diff --git a/app/src/components/dashboard/CostDashboardPanel.test.tsx b/app/src/components/dashboard/CostDashboardPanel.test.tsx
new file mode 100644
index 000000000..69c1a5fc0
--- /dev/null
+++ b/app/src/components/dashboard/CostDashboardPanel.test.tsx
@@ -0,0 +1,125 @@
+import { configureStore } from '@reduxjs/toolkit';
+import { render, screen, waitFor } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { MemoryRouter } from 'react-router-dom';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { callCoreRpc } from '../../services/coreRpcClient';
+import CostDashboardPanel from './CostDashboardPanel';
+
+vi.mock('../../services/coreRpcClient', () => ({ callCoreRpc: vi.fn() }));
+
+vi.mock('recharts', async () => {
+ // Recharts uses ResponsiveContainer which needs DOM measurements that
+ // jsdom does not provide. Stub the few primitives the dashboard pulls in
+ // with passthrough divs so we can assert on the panel structure without
+ // pulling the real chart pipeline into the test runtime.
+ const passthrough = ({ children }: { children?: React.ReactNode }) => (
+