diff --git a/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.test.tsx b/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.test.tsx
index 822fa65..7c76895 100644
--- a/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.test.tsx
+++ b/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.test.tsx
@@ -17,8 +17,6 @@ const theme = {
const renderCard = (props: {
message: string;
user_created: boolean;
- tokens_in?: number | null;
- tokens_out?: number | null;
}) =>
render(
@@ -26,38 +24,23 @@ const renderCard = (props: {
);
-describe('ConversationDetailCard token usage', () => {
- it('shows em dash when assistant tokens are missing', () => {
+describe('ConversationDetailCard', () => {
+ it('renders assistant message without token meta', () => {
renderCard({
message: 'Hello from the model',
user_created: false,
- tokens_in: null,
- tokens_out: null,
});
- expect(screen.getByTestId('message-token-usage')).toHaveTextContent(
- 'Tokens in — · out —'
- );
+ expect(screen.getByText('Hello from the model')).toBeInTheDocument();
+ expect(screen.queryByTestId('message-token-usage')).not.toBeInTheDocument();
+ expect(screen.queryByText(/Tokens in/i)).not.toBeInTheDocument();
});
- it('shows reported token counts for assistant messages', () => {
- renderCard({
- message: 'Hello from the model',
- user_created: false,
- tokens_in: 12,
- tokens_out: 34,
- });
- expect(screen.getByTestId('message-token-usage')).toHaveTextContent(
- 'Tokens in 12 · out 34'
- );
- });
-
- it('hides token meta on user messages', () => {
+ it('renders user message without token meta', () => {
renderCard({
message: 'Hi',
user_created: true,
- tokens_in: 1,
- tokens_out: 2,
});
+ expect(screen.getByText('Hi')).toBeInTheDocument();
expect(screen.queryByTestId('message-token-usage')).not.toBeInTheDocument();
});
});
diff --git a/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.tsx b/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.tsx
index 7129ab9..fabe98c 100644
--- a/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.tsx
+++ b/llm-fe/src/llm-fe/components/ConversationDetailCard/ConversationDetailCard.tsx
@@ -1,7 +1,6 @@
import React from "react";
import Markdown from "markdown-to-jsx";
import styled, { keyframes } from "styled-components";
-import { formatTokenCount } from "../../utils/finance";
const fadeIn = keyframes`
from { opacity: 0; transform: translateY(10px); }
@@ -68,15 +67,6 @@ const Bubble = styled.div<{ $isUser: boolean }>`
}
`;
-const TokenMeta = styled.div<{ $isUser: boolean }>`
- margin-top: 0.35rem;
- max-width: 80%;
- font-size: 0.75rem;
- opacity: 0.65;
- color: ${({ theme }) => theme.colors.text};
- text-align: ${(props) => (props.$isUser ? "right" : "left")};
-`;
-
const LoadingDot = styled.div`
width: 8px;
height: 8px;
@@ -104,8 +94,6 @@ const LoadingContainer = styled.div`
type ConversationDetailCardProps = {
message: string;
user_created: boolean;
- tokens_in?: number | null;
- tokens_out?: number | null;
};
const MyPlot = ({ format, image }: { format: string; image: string }) => {
@@ -130,8 +118,6 @@ const MyError = ({ content }: { content: string }) => {
const ConversationDetailCard = ({
message,
user_created,
- tokens_in = null,
- tokens_out = null,
}: ConversationDetailCardProps): JSX.Element => {
if (message.length === 0) {
return (
@@ -171,8 +157,6 @@ const ConversationDetailCard = ({
}
} catch { }
- const showTokens = !user_created;
-
return (
@@ -193,11 +177,6 @@ const ConversationDetailCard = ({
{contentToAdd}
- {showTokens ? (
-
- Tokens in {formatTokenCount(tokens_in)} · out {formatTokenCount(tokens_out)}
-
- ) : null}
);
};
diff --git a/llm-fe/src/llm-fe/components/UsageSummaryBar/UsageSummaryBar.tsx b/llm-fe/src/llm-fe/components/UsageSummaryBar/UsageSummaryBar.tsx
deleted file mode 100644
index a97d45c..0000000
--- a/llm-fe/src/llm-fe/components/UsageSummaryBar/UsageSummaryBar.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React, { useCallback, useEffect, useState } from 'react';
-import styled from 'styled-components';
-import { axiosInstance } from '../../../axiosApi';
-import {
- formatTokenCount,
- SubscriptionMe,
-} from '../../utils/finance';
-
-const Bar = styled.div`
- display: flex;
- flex-wrap: wrap;
- gap: 0.75rem 1.25rem;
- align-items: center;
- padding: 0.65rem 1rem;
- margin-bottom: 0.75rem;
- border-radius: 0.75rem;
- background: ${({ theme }) => theme.colors.cardBackground};
- border: 1px solid ${({ theme }) => theme.colors.cardBorder};
- color: ${({ theme }) => theme.colors.text};
- font-size: 0.85rem;
- opacity: 0.95;
-`;
-
-const Chip = styled.span`
- white-space: nowrap;
-`;
-
-const UsageSummaryBar = (): JSX.Element | null => {
- const [subscription, setSubscription] = useState(null);
-
- const load = useCallback(async () => {
- try {
- const response = await axiosInstance.get('/finance/subscription/');
- setSubscription(response.data || null);
- } catch {
- setSubscription(null);
- }
- }, []);
-
- useEffect(() => {
- load();
- }, [load]);
-
- if (!subscription?.plan) {
- return null;
- }
-
- const usage = subscription.usage;
- const promptsLabel =
- usage.prompts_remaining === null || usage.prompt_quota === null
- ? '—'
- : `${usage.prompts_remaining}/${usage.prompt_quota}`;
-
- return (
-
-
- {subscription.plan.name}
-
-
- Prompts left ({usage.window_hours}h): {promptsLabel}
-
-
- Period tokens in {formatTokenCount(usage.tokens_in_period)} · out{' '}
- {formatTokenCount(usage.tokens_out_period)}
-
-
- );
-};
-
-export default UsageSummaryBar;
diff --git a/llm-fe/src/llm-fe/components/UsageSummaryCard/UsageSummaryCard.tsx b/llm-fe/src/llm-fe/components/UsageSummaryCard/UsageSummaryCard.tsx
new file mode 100644
index 0000000..9b7190c
--- /dev/null
+++ b/llm-fe/src/llm-fe/components/UsageSummaryCard/UsageSummaryCard.tsx
@@ -0,0 +1,117 @@
+import React, { useCallback, useEffect, useState } from 'react';
+import styled from 'styled-components';
+import { axiosInstance } from '../../../axiosApi';
+import {
+ formatTokenCount,
+ SubscriptionMe,
+} from '../../utils/finance';
+
+const GlassCard = styled.div`
+ background: ${({ theme }) => theme.colors.cardBackground};
+ backdrop-filter: blur(10px);
+ border: 1px solid ${({ theme }) => theme.colors.cardBorder};
+ border-radius: 1rem;
+ padding: 2rem;
+ width: 100%;
+ max-width: 1000px;
+ margin-bottom: 2rem;
+ box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
+`;
+
+const CardTitle = styled.h2`
+ font-size: 1.8rem;
+ margin-bottom: 1.5rem;
+ color: ${({ theme }) => theme.colors.text};
+ border-bottom: 1px solid ${({ theme }) => theme.colors.cardBorder};
+ padding-bottom: 1rem;
+`;
+
+const SettingRow = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem 0;
+ border-bottom: 1px solid ${({ theme }) => theme.colors.cardBorder};
+
+ &:last-child {
+ border-bottom: none;
+ }
+`;
+
+const SettingLabel = styled.span`
+ font-size: 1.1rem;
+ color: ${({ theme }) => theme.colors.text};
+ font-weight: 500;
+`;
+
+const SettingValue = styled.span`
+ font-size: 1.1rem;
+ color: ${({ theme }) => theme.colors.text};
+ opacity: 0.9;
+ text-align: right;
+`;
+
+const BodyText = styled.p`
+ margin: 0;
+ color: ${({ theme }) => theme.colors.text};
+ opacity: 0.75;
+ line-height: 1.5;
+`;
+
+const UsageSummaryCard = (): JSX.Element => {
+ const [subscription, setSubscription] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ const load = useCallback(async () => {
+ setLoading(true);
+ try {
+ const response = await axiosInstance.get('/finance/subscription/');
+ setSubscription(response.data || null);
+ } catch {
+ setSubscription(null);
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ load();
+ }, [load]);
+
+ const usage = subscription?.usage;
+ const promptsLabel =
+ !usage || usage.prompts_remaining === null || usage.prompt_quota === null
+ ? '—'
+ : `${usage.prompts_remaining}/${usage.prompt_quota}`;
+
+ return (
+
+ Plan & usage
+ {loading ? (
+ Loading usage…
+ ) : !subscription?.plan ? (
+ No active plan yet.
+ ) : (
+ <>
+
+ Plan
+ {subscription.plan.name}
+
+
+ Prompts left ({usage?.window_hours ?? '—'}h)
+ {promptsLabel}
+
+
+ Period tokens
+
+ in {formatTokenCount(usage?.tokens_in_period ?? null)} · out{' '}
+ {formatTokenCount(usage?.tokens_out_period ?? null)}
+
+
+ >
+ )}
+
+ );
+};
+
+export default UsageSummaryCard;
diff --git a/llm-fe/src/llm-fe/pages/Account2/Account2.tsx b/llm-fe/src/llm-fe/pages/Account2/Account2.tsx
index 585d82f..790b418 100644
--- a/llm-fe/src/llm-fe/pages/Account2/Account2.tsx
+++ b/llm-fe/src/llm-fe/pages/Account2/Account2.tsx
@@ -9,6 +9,7 @@ import Header2 from "../../components/Header2/Header2";
import ParticleBackground from "../../components/ParticleBackground/ParticleBackground";
import styled from "styled-components";
import ThemeSettingsCard from "../../components/ThemeSettingsCard/ThemeSettingsCard";
+import UsageSummaryCard from "../../components/UsageSummaryCard/UsageSummaryCard";
import BillingSection from "../../components/BillingSection/BillingSection";
// Styled Components
@@ -408,6 +409,7 @@ const AccountPage = (): JSX.Element => {
return (
<>
+
{account?.is_company_manager ? (
diff --git a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx
index 09116fb..1375940 100644
--- a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx
+++ b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx
@@ -16,7 +16,6 @@ import { MessageContext } from "../../contexts/MessageContext";
import ParticleBackground from "../../components/ParticleBackground/ParticleBackground";
import Header2 from "../../components/Header2/Header2";
-import UsageSummaryBar from "../../components/UsageSummaryBar/UsageSummaryBar";
import { AnalyticsEvents, trackEvent } from "../../utils/analytics";
// Styled Components
@@ -517,7 +516,6 @@ const AsyncDashboardInner = (): JSX.Element => {
-
{conversationDetails.length > 0 ? (
conversationDetails.map((convo_detail, index) =>
@@ -525,16 +523,12 @@ const AsyncDashboardInner = (): JSX.Element => {
) : (
)