From 2654f10ac18b286ffa0a967cc829750249bc2541 Mon Sep 17 00:00:00 2001
From: John Doe <johndoe@example.com>
Date: Sun, 15 Feb 2026 15:59:57 +0300
Subject: [PATCH 1/6] style(my-usage): use Badge for provider group values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 .../_components/provider-group-info.tsx       | 129 +++++++++++++++---
 1 file changed, 113 insertions(+), 16 deletions(-)

diff --git a/src/app/[locale]/my-usage/_components/provider-group-info.tsx b/src/app/[locale]/my-usage/_components/provider-group-info.tsx
index fab11b69b..0f2fec1da 100644
--- a/src/app/[locale]/my-usage/_components/provider-group-info.tsx
+++ b/src/app/[locale]/my-usage/_components/provider-group-info.tsx
@@ -2,8 +2,65 @@
 
 import { Layers, ShieldCheck } from "lucide-react";
 import { useTranslations } from "next-intl";
+import { Badge } from "@/components/ui/badge";
+import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
 import { cn } from "@/lib/utils";
 
+function abbreviateModel(name: string): string {
+  const parts = name.split("-");
+
+  if (parts.length === 1) {
+    return parts[0].length <= 4 ? parts[0].toUpperCase() : parts[0].slice(0, 2).toUpperCase();
+  }
+
+  const letterParts: string[] = [];
+  let versionMixed = "";
+  const versionNums: string[] = [];
+
+  for (const part of parts) {
+    if (/^\d{8,}$/.test(part)) continue;
+    if (/^[a-zA-Z]+$/.test(part)) {
+      letterParts.push(part);
+    } else if (/^\d+\.\d+$/.test(part)) {
+      versionMixed = part;
+    } else if (/^\d+[a-zA-Z]/.test(part)) {
+      versionMixed = part;
+    } else if (/^\d+$/.test(part)) {
+      versionNums.push(part);
+    } else {
+      letterParts.push(part);
+    }
+  }
+
+  const prefix = letterParts
+    .slice(0, 3)
+    .map((w) => w[0].toUpperCase())
+    .join("");
+
+  let version = "";
+  if (versionMixed) {
+    version = versionMixed;
+  } else if (versionNums.length > 0) {
+    version = versionNums.slice(0, 2).join(".");
+  }
+
+  if (version && prefix) {
+    return `${prefix}-${version}`;
+  }
+  return prefix || name.toUpperCase().substring(0, 3);
+}
+
+function abbreviateClient(name: string): string {
+  const parts = name.split(/[-\s]+/);
+  if (parts.length === 1) {
+    return name.slice(0, 2).toUpperCase();
+  }
+  return parts
+    .slice(0, 3)
+    .map((w) => w[0].toUpperCase())
+    .join("");
+}
+
 interface ProviderGroupInfoProps {
   keyProviderGroup: string | null;
   userProviderGroup: string | null;
@@ -26,10 +83,8 @@ export function ProviderGroupInfo({
   const userDisplay = userProviderGroup ?? tGroup("allProviders");
   const inherited = !keyProviderGroup && !!userProviderGroup;
 
-  const modelsDisplay =
-    userAllowedModels.length > 0 ? userAllowedModels.join(", ") : tRestrictions("noRestrictions");
-  const clientsDisplay =
-    userAllowedClients.length > 0 ? userAllowedClients.join(", ") : tRestrictions("noRestrictions");
+  const hasModels = userAllowedModels.length > 0;
+  const hasClients = userAllowedClients.length > 0;
 
   return (
     <div
@@ -45,16 +100,20 @@ export function ProviderGroupInfo({
           <span>{tGroup("title")}</span>
         </div>
         <div className="space-y-1">
-          <div className="flex items-baseline gap-1.5">
-            <span className="text-xs text-muted-foreground">{tGroup("keyGroup")}:</span>
-            <span className="text-sm font-semibold text-foreground">{keyDisplay}</span>
+          <div className="flex flex-wrap items-center gap-1.5">
+            <span className="shrink-0 text-xs text-muted-foreground">{tGroup("keyGroup")}:</span>
+            <Badge variant="outline" className="cursor-default text-xs">
+              {keyDisplay}
+            </Badge>
             {inherited && (
               <span className="text-xs text-muted-foreground">({tGroup("inheritedFromUser")})</span>
             )}
           </div>
-          <div className="flex items-baseline gap-1.5">
-            <span className="text-xs text-muted-foreground">{tGroup("userGroup")}:</span>
-            <span className="text-sm font-semibold text-foreground">{userDisplay}</span>
+          <div className="flex flex-wrap items-center gap-1.5">
+            <span className="shrink-0 text-xs text-muted-foreground">{tGroup("userGroup")}:</span>
+            <Badge variant="outline" className="cursor-default text-xs">
+              {userDisplay}
+            </Badge>
           </div>
         </div>
       </div>
@@ -66,13 +125,51 @@ export function ProviderGroupInfo({
           <span>{tRestrictions("title")}</span>
         </div>
         <div className="space-y-1">
-          <div className="flex items-baseline gap-1.5">
-            <span className="text-xs text-muted-foreground">{tRestrictions("models")}:</span>
-            <span className="text-sm font-semibold text-foreground">{modelsDisplay}</span>
+          <div className="flex flex-wrap items-center gap-1.5">
+            <span className="shrink-0 text-xs text-muted-foreground">
+              {tRestrictions("models")}:
+            </span>
+            {hasModels ? (
+              userAllowedModels.map((name) => (
+                <Tooltip key={name}>
+                  <TooltipTrigger asChild>
+                    <span>
+                      <Badge variant="outline" className="cursor-default font-mono text-xs">
+                        {abbreviateModel(name)}
+                      </Badge>
+                    </span>
+                  </TooltipTrigger>
+                  <TooltipContent>{name}</TooltipContent>
+                </Tooltip>
+              ))
+            ) : (
+              <span className="text-sm font-semibold text-foreground">
+                {tRestrictions("noRestrictions")}
+              </span>
+            )}
           </div>
-          <div className="flex items-baseline gap-1.5">
-            <span className="text-xs text-muted-foreground">{tRestrictions("clients")}:</span>
-            <span className="text-sm font-semibold text-foreground">{clientsDisplay}</span>
+          <div className="flex flex-wrap items-center gap-1.5">
+            <span className="shrink-0 text-xs text-muted-foreground">
+              {tRestrictions("clients")}:
+            </span>
+            {hasClients ? (
+              userAllowedClients.map((name) => (
+                <Tooltip key={name}>
+                  <TooltipTrigger asChild>
+                    <span>
+                      <Badge variant="outline" className="cursor-default font-mono text-xs">
+                        {abbreviateClient(name)}
+                      </Badge>
+                    </span>
+                  </TooltipTrigger>
+                  <TooltipContent>{name}</TooltipContent>
+                </Tooltip>
+              ))
+            ) : (
+              <span className="text-sm font-semibold text-foreground">
+                {tRestrictions("noRestrictions")}
+              </span>
+            )}
           </div>
         </div>
       </div>

From b8dc8caf3ee72606ee6fc9fc37315fae872ece5a Mon Sep 17 00:00:00 2001
From: John Doe <johndoe@example.com>
Date: Sun, 15 Feb 2026 16:28:19 +0300
Subject: [PATCH 2/6] fix(my-usage): use currency symbol instead of code in
 quota cards

Replace manual `${currency} ${num.toFixed(2)}` formatting with
`formatCurrency()` so quota values display "$3.50" instead of "USD 3.50",
consistent with all other currency displays in the app.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 .../my-usage/_components/quota-cards.tsx      | 193 +++++++++---------
 1 file changed, 95 insertions(+), 98 deletions(-)

diff --git a/src/app/[locale]/my-usage/_components/quota-cards.tsx b/src/app/[locale]/my-usage/_components/quota-cards.tsx
index 68a8a3a30..37af9c866 100644
--- a/src/app/[locale]/my-usage/_components/quota-cards.tsx
+++ b/src/app/[locale]/my-usage/_components/quota-cards.tsx
@@ -3,11 +3,11 @@
 import { useTranslations } from "next-intl";
 import { useMemo } from "react";
 import type { MyUsageQuota } from "@/actions/my-usage";
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
 import { Progress } from "@/components/ui/progress";
 import { Skeleton } from "@/components/ui/skeleton";
 import type { CurrencyCode } from "@/lib/utils";
 import { cn } from "@/lib/utils";
+import { formatCurrency } from "@/lib/utils/currency";
 import { calculateUsagePercent, isUnlimited } from "@/lib/utils/limit-helpers";
 
 interface QuotaCardsProps {
@@ -79,144 +79,141 @@ export function QuotaCards({ quota, loading = false, currencyCode = "USD" }: Quo
   }
 
   return (
-    <div className="space-y-3">
-      <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
-        {items.map((item) => {
-          const keyPct = calculateUsagePercent(item.keyCurrent, item.keyLimit);
-          const userPct = calculateUsagePercent(item.userCurrent ?? 0, item.userLimit);
-
-          const keyTone = getTone(keyPct);
-          const userTone = getTone(userPct);
-          const hasUserData = item.userLimit !== null || item.userCurrent !== null;
-
-          return (
-            <Card key={item.key} className="border-border/70">
-              <CardHeader className="pb-3">
-                <CardTitle className="text-sm font-semibold text-muted-foreground">
-                  {item.title}
-                </CardTitle>
-              </CardHeader>
-              <CardContent className="space-y-3">
-                <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
-                  <QuotaColumn
-                    label={t("keyLevel")}
-                    current={item.keyCurrent}
-                    limit={item.keyLimit}
-                    percent={keyPct}
-                    tone={keyTone}
-                    currency={item.key === "concurrent" ? undefined : currencyCode}
-                  />
-                  <QuotaColumn
-                    label={t("userLevel")}
-                    current={item.userCurrent ?? 0}
-                    limit={item.userLimit}
-                    percent={userPct}
-                    tone={userTone}
-                    currency={item.key === "concurrent" ? undefined : currencyCode}
-                    muted={!hasUserData}
-                  />
-                </div>
-              </CardContent>
-            </Card>
-          );
-        })}
-        {items.length === 0 && !loading ? (
-          <Card>
-            <CardContent className="py-6 text-center text-sm text-muted-foreground">
-              {t("empty")}
-            </CardContent>
-          </Card>
-        ) : null}
-      </div>
+    <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
+      {items.map((item) => {
+        const isCurrency = item.key !== "concurrent";
+        const currency = isCurrency ? currencyCode : undefined;
+
+        return (
+          <QuotaBlock
+            key={item.key}
+            title={item.title}
+            keyCurrent={item.keyCurrent}
+            keyLimit={item.keyLimit}
+            userCurrent={item.userCurrent ?? 0}
+            userLimit={item.userLimit}
+            currency={currency}
+          />
+        );
+      })}
+      {items.length === 0 && !loading ? (
+        <div className="col-span-full py-6 text-center text-sm text-muted-foreground">
+          {t("empty")}
+        </div>
+      ) : null}
     </div>
   );
 }
 
-function QuotaCardsSkeleton({ label }: { label: string }) {
+function QuotaBlock({
+  title,
+  keyCurrent,
+  keyLimit,
+  userCurrent,
+  userLimit,
+  currency,
+}: {
+  title: string;
+  keyCurrent: number;
+  keyLimit: number | null;
+  userCurrent: number;
+  userLimit: number | null;
+  currency?: CurrencyCode;
+}) {
+  const t = useTranslations("myUsage.quota");
+
+  const keyPct = calculateUsagePercent(keyCurrent, keyLimit);
+  const userPct = calculateUsagePercent(userCurrent, userLimit);
+
   return (
-    <div className="space-y-3" aria-busy="true">
-      <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
-        {Array.from({ length: 6 }).map((_, index) => (
-          <Card key={index} className="border-border/70">
-            <CardHeader className="pb-3">
-              <Skeleton className="h-4 w-20" />
-            </CardHeader>
-            <CardContent className="space-y-3">
-              <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
-                <Skeleton className="h-16 w-full" />
-                <Skeleton className="h-16 w-full" />
-              </div>
-            </CardContent>
-          </Card>
-        ))}
-      </div>
-      <div className="flex items-center gap-2 text-xs text-muted-foreground">
-        <Skeleton className="h-3 w-3 rounded-full" />
-        <span>{label}</span>
-      </div>
+    <div className="space-y-2 rounded-md border bg-card/50 p-3">
+      <div className="text-xs font-semibold text-muted-foreground">{title}</div>
+      <QuotaRow
+        label={t("keyLevel")}
+        current={keyCurrent}
+        limit={keyLimit}
+        percent={keyPct}
+        currency={currency}
+      />
+      <QuotaRow
+        label={t("userLevel")}
+        current={userCurrent}
+        limit={userLimit}
+        percent={userPct}
+        currency={currency}
+      />
     </div>
   );
 }
 
-function QuotaColumn({
+function QuotaRow({
   label,
   current,
   limit,
   percent,
-  tone,
   currency,
-  muted = false,
 }: {
   label: string;
   current: number;
   limit: number | null;
   percent: number | null;
-  tone: "default" | "warn" | "danger";
-  currency?: string;
-  muted?: boolean;
+  currency?: CurrencyCode;
 }) {
   const t = useTranslations("myUsage.quota");
+  const unlimited = isUnlimited(limit);
+  const tone = getTone(percent);
 
   const formatValue = (value: number) => {
     const num = Number(value);
-    if (!Number.isFinite(num)) {
-      return currency ? `${currency} 0.00` : "0";
-    }
-    return currency ? `${currency} ${num.toFixed(2)}` : String(num);
+    if (!Number.isFinite(num)) return currency ? formatCurrency(0, currency) : "0";
+    return currency ? formatCurrency(num, currency) : String(num);
   };
 
-  const unlimited = isUnlimited(limit);
+  const limitDisplay = unlimited ? t("unlimited") : formatValue(limit as number);
+  const ariaLabel = `${label}: ${formatValue(current)}${!unlimited ? ` / ${limitDisplay}` : ""}`;
 
-  const progressClass = cn("h-2", {
+  const progressClass = cn("h-1.5 flex-1", {
     "bg-destructive/10 [&>div]:bg-destructive": tone === "danger",
     "bg-amber-500/10 [&>div]:bg-amber-500": tone === "warn",
   });
 
-  const limitDisplay = unlimited ? t("unlimited") : formatValue(limit as number);
-  const ariaLabel = `${label}: ${formatValue(current)}${!unlimited ? ` / ${limitDisplay}` : ""}`;
-
   return (
-    <div className={cn("space-y-2 rounded-md border bg-card/50 p-3", muted && "opacity-70")}>
-      {/* Label */}
-      <div className="text-xs font-medium text-muted-foreground">{label}</div>
-
-      {/* Values - split into two lines to avoid overlap */}
-      <div className="space-y-0.5">
-        <div className="text-sm font-mono font-medium text-foreground">{formatValue(current)}</div>
-        <div className="text-xs text-muted-foreground">/ {limitDisplay}</div>
-      </div>
-
-      {/* Progress bar or placeholder */}
+    <div className="flex items-center gap-2">
+      <span className="w-8 shrink-0 text-[11px] text-muted-foreground">{label}</span>
       {!unlimited ? (
         <Progress value={percent ?? 0} className={progressClass} aria-label={ariaLabel} />
       ) : (
         <div
-          className="h-2 rounded-full bg-muted/50"
+          className="h-1.5 flex-1 rounded-full bg-muted/50"
           role="progressbar"
           aria-label={`${label}: ${t("unlimited")}`}
           aria-valuetext={t("unlimited")}
         />
       )}
+      <span className="shrink-0 text-right font-mono text-xs text-foreground">
+        {formatValue(current)}
+        <span className="text-muted-foreground"> / {limitDisplay}</span>
+      </span>
+    </div>
+  );
+}
+
+function QuotaCardsSkeleton({ label }: { label: string }) {
+  return (
+    <div className="space-y-3" aria-busy="true">
+      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
+        {Array.from({ length: 6 }).map((_, index) => (
+          <div key={index} className="space-y-2 rounded-md border bg-card/50 p-3">
+            <Skeleton className="h-3 w-16" />
+            <Skeleton className="h-4 w-full" />
+            <Skeleton className="h-4 w-full" />
+          </div>
+        ))}
+      </div>
+      <div className="flex items-center gap-2 text-xs text-muted-foreground">
+        <Skeleton className="h-3 w-3 rounded-full" />
+        <span>{label}</span>
+      </div>
     </div>
   );
 }

From 91a571e92240ca26b0b620702289208535ceff5f Mon Sep 17 00:00:00 2001
From: John Doe <johndoe@example.com>
Date: Sun, 15 Feb 2026 16:45:22 +0300
Subject: [PATCH 3/6] style(my-usage): replace unlimited text with infinity
 icon in quota cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 src/app/[locale]/my-usage/_components/quota-cards.tsx | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/app/[locale]/my-usage/_components/quota-cards.tsx b/src/app/[locale]/my-usage/_components/quota-cards.tsx
index 37af9c866..fd44b5580 100644
--- a/src/app/[locale]/my-usage/_components/quota-cards.tsx
+++ b/src/app/[locale]/my-usage/_components/quota-cards.tsx
@@ -1,5 +1,6 @@
 "use client";
 
+import { Infinity } from "lucide-react";
 import { useTranslations } from "next-intl";
 import { useMemo } from "react";
 import type { MyUsageQuota } from "@/actions/my-usage";
@@ -192,7 +193,10 @@ function QuotaRow({
       )}
       <span className="shrink-0 text-right font-mono text-xs text-foreground">
         {formatValue(current)}
-        <span className="text-muted-foreground"> / {limitDisplay}</span>
+        <span className="text-muted-foreground">
+          {" / "}
+          {unlimited ? <Infinity className="inline h-3.5 w-3.5" /> : limitDisplay}
+        </span>
       </span>
     </div>
   );

From fe1fe4efdbc59650d7ee1d699325c3aca9861230 Mon Sep 17 00:00:00 2001
From: John Doe <johndoe@example.com>
Date: Sun, 15 Feb 2026 16:47:01 +0300
Subject: [PATCH 4/6] fix(my-usage): paginate model breakdown in statistics
 summary card

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 .../_components/statistics-summary-card.tsx   | 168 ++++++++++++------
 1 file changed, 115 insertions(+), 53 deletions(-)

diff --git a/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx b/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
index 1d0052018..94733d2fe 100644
--- a/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
+++ b/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
@@ -6,6 +6,8 @@ import {
   ArrowDownRight,
   ArrowUpRight,
   BarChart3,
+  ChevronLeft,
+  ChevronRight,
   Coins,
   Database,
   Hash,
@@ -15,7 +17,11 @@ import {
 } from "lucide-react";
 import { useTranslations } from "next-intl";
 import { useCallback, useEffect, useRef, useState } from "react";
-import { getMyStatsSummary, type MyStatsSummary } from "@/actions/my-usage";
+import {
+  getMyStatsSummary,
+  type ModelBreakdownItem,
+  type MyStatsSummary,
+} from "@/actions/my-usage";
 import { Button } from "@/components/ui/button";
 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
 import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
@@ -110,9 +116,26 @@ export function StatisticsSummaryCard({
     setDateRange(range);
   }, []);
 
+  const [breakdownPage, setBreakdownPage] = useState(1);
+
+  // Reset breakdown page when stats change (date range switch, refresh)
+  useEffect(() => {
+    setBreakdownPage(1);
+  }, [stats]);
+
   const isLoading = loading || refreshing;
   const currencyCode = stats?.currencyCode ?? "USD";
 
+  const maxBreakdownLen = Math.max(
+    stats?.keyModelBreakdown.length ?? 0,
+    stats?.userModelBreakdown.length ?? 0
+  );
+  const breakdownTotalPages = Math.ceil(maxBreakdownLen / MODEL_BREAKDOWN_PAGE_SIZE);
+  const sliceStart = (breakdownPage - 1) * MODEL_BREAKDOWN_PAGE_SIZE;
+  const sliceEnd = breakdownPage * MODEL_BREAKDOWN_PAGE_SIZE;
+  const keyPageItems = stats?.keyModelBreakdown.slice(sliceStart, sliceEnd) ?? [];
+  const userPageItems = stats?.userModelBreakdown.slice(sliceStart, sliceEnd) ?? [];
+
   return (
     <Card className={className}>
       <CardHeader className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between space-y-0 pb-4">
@@ -220,60 +243,62 @@ export function StatisticsSummaryCard({
             <div className="space-y-3">
               <p className="text-sm font-medium text-muted-foreground">{t("modelBreakdown")}</p>
               <div className="grid gap-4 md:grid-cols-2">
-                {/* Key Stats */}
-                <div className="space-y-2">
-                  <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
-                    {t("keyStats")}
-                  </p>
-                  {stats.keyModelBreakdown.length > 0 ? (
-                    <div className="space-y-2">
-                      {stats.keyModelBreakdown.map((item, index) => (
-                        <ModelBreakdownRow
-                          key={`key-${item.model ?? "unknown"}-${index}`}
-                          model={item.model}
-                          requests={item.requests}
-                          cost={item.cost}
-                          inputTokens={item.inputTokens}
-                          outputTokens={item.outputTokens}
-                          cacheCreationTokens={item.cacheCreationTokens}
-                          cacheReadTokens={item.cacheReadTokens}
-                          currencyCode={currencyCode}
-                          totalCost={stats.totalCost}
-                        />
-                      ))}
-                    </div>
-                  ) : (
-                    <p className="text-sm text-muted-foreground py-2">{t("noData")}</p>
-                  )}
-                </div>
+                {keyPageItems.length > 0 && (
+                  <div className="space-y-2">
+                    <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
+                      {t("keyStats")}
+                    </p>
+                    <ModelBreakdownColumn
+                      pageItems={keyPageItems}
+                      currencyCode={currencyCode}
+                      totalCost={stats.totalCost}
+                      keyPrefix="key"
+                      pageOffset={sliceStart}
+                    />
+                  </div>
+                )}
+
+                {userPageItems.length > 0 && (
+                  <div className="space-y-2">
+                    <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
+                      {t("userStats")}
+                    </p>
+                    <ModelBreakdownColumn
+                      pageItems={userPageItems}
+                      currencyCode={currencyCode}
+                      totalCost={stats.totalCost}
+                      keyPrefix="user"
+                      pageOffset={sliceStart}
+                    />
+                  </div>
+                )}
+              </div>
 
-                {/* User Stats */}
-                <div className="space-y-2">
-                  <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
-                    {t("userStats")}
-                  </p>
-                  {stats.userModelBreakdown.length > 0 ? (
-                    <div className="space-y-2">
-                      {stats.userModelBreakdown.map((item, index) => (
-                        <ModelBreakdownRow
-                          key={`user-${item.model ?? "unknown"}-${index}`}
-                          model={item.model}
-                          requests={item.requests}
-                          cost={item.cost}
-                          inputTokens={item.inputTokens}
-                          outputTokens={item.outputTokens}
-                          cacheCreationTokens={item.cacheCreationTokens}
-                          cacheReadTokens={item.cacheReadTokens}
-                          currencyCode={currencyCode}
-                          totalCost={stats.totalCost}
-                        />
-                      ))}
-                    </div>
-                  ) : (
-                    <p className="text-sm text-muted-foreground py-2">{t("noData")}</p>
-                  )}
+              {breakdownTotalPages > 1 && (
+                <div className="flex items-center justify-between pt-1">
+                  <Button
+                    size="icon"
+                    variant="ghost"
+                    className="h-7 w-7"
+                    disabled={breakdownPage <= 1}
+                    onClick={() => setBreakdownPage((p) => p - 1)}
+                  >
+                    <ChevronLeft className="h-4 w-4" />
+                  </Button>
+                  <span className="text-xs text-muted-foreground">
+                    {breakdownPage} / {breakdownTotalPages}
+                  </span>
+                  <Button
+                    size="icon"
+                    variant="ghost"
+                    className="h-7 w-7"
+                    disabled={breakdownPage >= breakdownTotalPages}
+                    onClick={() => setBreakdownPage((p) => p + 1)}
+                  >
+                    <ChevronRight className="h-4 w-4" />
+                  </Button>
                 </div>
-              </div>
+              )}
             </div>
           </>
         ) : (
@@ -284,6 +309,43 @@ export function StatisticsSummaryCard({
   );
 }
 
+const MODEL_BREAKDOWN_PAGE_SIZE = 5;
+
+interface ModelBreakdownColumnProps {
+  pageItems: ModelBreakdownItem[];
+  currencyCode: CurrencyCode;
+  totalCost: number;
+  keyPrefix: string;
+  pageOffset: number;
+}
+
+function ModelBreakdownColumn({
+  pageItems,
+  currencyCode,
+  totalCost,
+  keyPrefix,
+  pageOffset,
+}: ModelBreakdownColumnProps) {
+  return (
+    <div className="space-y-2">
+      {pageItems.map((item, index) => (
+        <ModelBreakdownRow
+          key={`${keyPrefix}-${item.model ?? "unknown"}-${pageOffset + index}`}
+          model={item.model}
+          requests={item.requests}
+          cost={item.cost}
+          inputTokens={item.inputTokens}
+          outputTokens={item.outputTokens}
+          cacheCreationTokens={item.cacheCreationTokens}
+          cacheReadTokens={item.cacheReadTokens}
+          currencyCode={currencyCode}
+          totalCost={totalCost}
+        />
+      ))}
+    </div>
+  );
+}
+
 interface ModelBreakdownRowProps {
   model: string | null;
   requests: number;

From 1576a25f3298dba8f63e05be169c96d3f7e2c3c6 Mon Sep 17 00:00:00 2001
From: John Doe <johndoe@example.com>
Date: Sun, 15 Feb 2026 16:54:57 +0300
Subject: [PATCH 5/6] chore(my-usage): suppress biome exhaustive-deps for
 intentional stats reset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 .../[locale]/my-usage/_components/statistics-summary-card.tsx    | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx b/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
index 94733d2fe..df0196243 100644
--- a/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
+++ b/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
@@ -119,6 +119,7 @@ export function StatisticsSummaryCard({
   const [breakdownPage, setBreakdownPage] = useState(1);
 
   // Reset breakdown page when stats change (date range switch, refresh)
+  // biome-ignore lint/correctness/useExhaustiveDependencies: intentional reset on stats identity change
   useEffect(() => {
     setBreakdownPage(1);
   }, [stats]);

From 3007012db2f5a800354a0da3c9e1eae94e293db7 Mon Sep 17 00:00:00 2001
From: John Doe <johndoe@example.com>
Date: Sun, 15 Feb 2026 17:10:51 +0300
Subject: [PATCH 6/6] fix(my-usage): address PR #794 review comments

- Fix abbreviateModel/abbreviateClient crash on empty split parts
- Fix pagination reset on auto-refresh by using dateRange deps
- Restore noData fallback in model breakdown columns
- Add i18n for pagination controls with aria-labels (5 langs)
- Fix quota label overflow for long translations (w-8 -> w-auto)
- Rename Infinity -> InfinityIcon to avoid shadowing global
- Remove redundant span wrappers in TooltipTrigger asChild

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 messages/en/myUsage.json                      |  3 ++
 messages/ja/myUsage.json                      |  3 ++
 messages/ru/myUsage.json                      |  3 ++
 messages/zh-CN/myUsage.json                   |  3 ++
 messages/zh-TW/myUsage.json                   |  3 ++
 .../_components/collapsible-quota-card.tsx    |  8 ++--
 .../_components/provider-group-info.tsx       | 20 ++++-----
 .../my-usage/_components/quota-cards.tsx      |  8 ++--
 .../_components/statistics-summary-card.tsx   | 45 +++++++++++--------
 9 files changed, 59 insertions(+), 37 deletions(-)

diff --git a/messages/en/myUsage.json b/messages/en/myUsage.json
index 0ebe076a2..0e39e8496 100644
--- a/messages/en/myUsage.json
+++ b/messages/en/myUsage.json
@@ -92,6 +92,9 @@
     "keyStats": "Key",
     "userStats": "User",
     "noData": "No data for selected period",
+    "breakdownPrevPage": "Previous page",
+    "breakdownNextPage": "Next page",
+    "breakdownPageIndicator": "{current} / {total}",
     "unknownModel": "Unknown",
     "modal": {
       "requests": "Requests",
diff --git a/messages/ja/myUsage.json b/messages/ja/myUsage.json
index 4d0b1bb7e..901e10ab6 100644
--- a/messages/ja/myUsage.json
+++ b/messages/ja/myUsage.json
@@ -92,6 +92,9 @@
     "keyStats": "キー",
     "userStats": "ユーザー",
     "noData": "選択期間のデータがありません",
+    "breakdownPrevPage": "前のページ",
+    "breakdownNextPage": "次のページ",
+    "breakdownPageIndicator": "{current} / {total}",
     "unknownModel": "不明",
     "modal": {
       "requests": "リクエスト",
diff --git a/messages/ru/myUsage.json b/messages/ru/myUsage.json
index bb3b61bd7..5ccfec871 100644
--- a/messages/ru/myUsage.json
+++ b/messages/ru/myUsage.json
@@ -92,6 +92,9 @@
     "keyStats": "Ключ",
     "userStats": "Пользователь",
     "noData": "Нет данных за выбранный период",
+    "breakdownPrevPage": "Предыдущая страница",
+    "breakdownNextPage": "Следующая страница",
+    "breakdownPageIndicator": "{current} / {total}",
     "unknownModel": "Неизвестно",
     "modal": {
       "requests": "Запросов",
diff --git a/messages/zh-CN/myUsage.json b/messages/zh-CN/myUsage.json
index 9eaaf7925..6cf939337 100644
--- a/messages/zh-CN/myUsage.json
+++ b/messages/zh-CN/myUsage.json
@@ -92,6 +92,9 @@
     "keyStats": "密钥",
     "userStats": "用户",
     "noData": "所选时段无数据",
+    "breakdownPrevPage": "上一页",
+    "breakdownNextPage": "下一页",
+    "breakdownPageIndicator": "{current} / {total}",
     "unknownModel": "未知",
     "modal": {
       "requests": "请求",
diff --git a/messages/zh-TW/myUsage.json b/messages/zh-TW/myUsage.json
index b5247a160..f803a617b 100644
--- a/messages/zh-TW/myUsage.json
+++ b/messages/zh-TW/myUsage.json
@@ -92,6 +92,9 @@
     "keyStats": "金鑰",
     "userStats": "使用者",
     "noData": "所選時段無資料",
+    "breakdownPrevPage": "上一頁",
+    "breakdownNextPage": "下一頁",
+    "breakdownPageIndicator": "{current} / {total}",
     "unknownModel": "不明",
     "modal": {
       "requests": "請求",
diff --git a/src/app/[locale]/my-usage/_components/collapsible-quota-card.tsx b/src/app/[locale]/my-usage/_components/collapsible-quota-card.tsx
index ee7e0db50..4d172d1fd 100644
--- a/src/app/[locale]/my-usage/_components/collapsible-quota-card.tsx
+++ b/src/app/[locale]/my-usage/_components/collapsible-quota-card.tsx
@@ -1,6 +1,6 @@
 "use client";
 
-import { AlertTriangle, ChevronDown, Infinity, PieChart } from "lucide-react";
+import { AlertTriangle, ChevronDown, Infinity as InfinityIcon, PieChart } from "lucide-react";
 import { useTranslations } from "next-intl";
 import { useState } from "react";
 import type { MyUsageQuota } from "@/actions/my-usage";
@@ -94,7 +94,7 @@ export function CollapsibleQuotaCard({
                 <div className="flex items-center gap-1.5">
                   <span className="text-muted-foreground">{t("daily")}:</span>
                   {dailyPct === null ? (
-                    <Infinity className="h-4 w-4 text-muted-foreground" />
+                    <InfinityIcon className="h-4 w-4 text-muted-foreground" />
                   ) : (
                     <>
                       <span className={cn("font-semibold", getPercentColor(dailyPct))}>
@@ -108,7 +108,7 @@ export function CollapsibleQuotaCard({
                 <div className="flex items-center gap-1.5">
                   <span className="text-muted-foreground">{t("monthly")}:</span>
                   {monthlyPct === null ? (
-                    <Infinity className="h-4 w-4 text-muted-foreground" />
+                    <InfinityIcon className="h-4 w-4 text-muted-foreground" />
                   ) : (
                     <>
                       <span className={cn("font-semibold", getPercentColor(monthlyPct))}>
@@ -122,7 +122,7 @@ export function CollapsibleQuotaCard({
                 <div className="flex items-center gap-1.5">
                   <span className="text-muted-foreground">{t("total")}:</span>
                   {totalPct === null ? (
-                    <Infinity className="h-4 w-4 text-muted-foreground" />
+                    <InfinityIcon className="h-4 w-4 text-muted-foreground" />
                   ) : (
                     <>
                       <span className={cn("font-semibold", getPercentColor(totalPct))}>
diff --git a/src/app/[locale]/my-usage/_components/provider-group-info.tsx b/src/app/[locale]/my-usage/_components/provider-group-info.tsx
index 0f2fec1da..227087f1b 100644
--- a/src/app/[locale]/my-usage/_components/provider-group-info.tsx
+++ b/src/app/[locale]/my-usage/_components/provider-group-info.tsx
@@ -7,7 +7,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
 import { cn } from "@/lib/utils";
 
 function abbreviateModel(name: string): string {
-  const parts = name.split("-");
+  const parts = name.split("-").filter(Boolean);
 
   if (parts.length === 1) {
     return parts[0].length <= 4 ? parts[0].toUpperCase() : parts[0].slice(0, 2).toUpperCase();
@@ -51,7 +51,7 @@ function abbreviateModel(name: string): string {
 }
 
 function abbreviateClient(name: string): string {
-  const parts = name.split(/[-\s]+/);
+  const parts = name.split(/[-\s]+/).filter(Boolean);
   if (parts.length === 1) {
     return name.slice(0, 2).toUpperCase();
   }
@@ -133,11 +133,9 @@ export function ProviderGroupInfo({
               userAllowedModels.map((name) => (
                 <Tooltip key={name}>
                   <TooltipTrigger asChild>
-                    <span>
-                      <Badge variant="outline" className="cursor-default font-mono text-xs">
-                        {abbreviateModel(name)}
-                      </Badge>
-                    </span>
+                    <Badge variant="outline" className="cursor-default font-mono text-xs">
+                      {abbreviateModel(name)}
+                    </Badge>
                   </TooltipTrigger>
                   <TooltipContent>{name}</TooltipContent>
                 </Tooltip>
@@ -156,11 +154,9 @@ export function ProviderGroupInfo({
               userAllowedClients.map((name) => (
                 <Tooltip key={name}>
                   <TooltipTrigger asChild>
-                    <span>
-                      <Badge variant="outline" className="cursor-default font-mono text-xs">
-                        {abbreviateClient(name)}
-                      </Badge>
-                    </span>
+                    <Badge variant="outline" className="cursor-default font-mono text-xs">
+                      {abbreviateClient(name)}
+                    </Badge>
                   </TooltipTrigger>
                   <TooltipContent>{name}</TooltipContent>
                 </Tooltip>
diff --git a/src/app/[locale]/my-usage/_components/quota-cards.tsx b/src/app/[locale]/my-usage/_components/quota-cards.tsx
index fd44b5580..9f7496fc9 100644
--- a/src/app/[locale]/my-usage/_components/quota-cards.tsx
+++ b/src/app/[locale]/my-usage/_components/quota-cards.tsx
@@ -1,6 +1,6 @@
 "use client";
 
-import { Infinity } from "lucide-react";
+import { Infinity as InfinityIcon } from "lucide-react";
 import { useTranslations } from "next-intl";
 import { useMemo } from "react";
 import type { MyUsageQuota } from "@/actions/my-usage";
@@ -180,7 +180,9 @@ function QuotaRow({
 
   return (
     <div className="flex items-center gap-2">
-      <span className="w-8 shrink-0 text-[11px] text-muted-foreground">{label}</span>
+      <span className="w-auto shrink-0 whitespace-nowrap text-[11px] text-muted-foreground">
+        {label}
+      </span>
       {!unlimited ? (
         <Progress value={percent ?? 0} className={progressClass} aria-label={ariaLabel} />
       ) : (
@@ -195,7 +197,7 @@ function QuotaRow({
         {formatValue(current)}
         <span className="text-muted-foreground">
           {" / "}
-          {unlimited ? <Infinity className="inline h-3.5 w-3.5" /> : limitDisplay}
+          {unlimited ? <InfinityIcon className="inline h-3.5 w-3.5" /> : limitDisplay}
         </span>
       </span>
     </div>
diff --git a/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx b/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
index df0196243..a73947cea 100644
--- a/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
+++ b/src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
@@ -118,11 +118,11 @@ export function StatisticsSummaryCard({
 
   const [breakdownPage, setBreakdownPage] = useState(1);
 
-  // Reset breakdown page when stats change (date range switch, refresh)
-  // biome-ignore lint/correctness/useExhaustiveDependencies: intentional reset on stats identity change
+  // Reset breakdown page when date range changes
+  // biome-ignore lint/correctness/useExhaustiveDependencies: deps used as reset trigger on date range change
   useEffect(() => {
     setBreakdownPage(1);
-  }, [stats]);
+  }, [dateRange.startDate, dateRange.endDate]);
 
   const isLoading = loading || refreshing;
   const currencyCode = stats?.currencyCode ?? "USD";
@@ -244,11 +244,11 @@ export function StatisticsSummaryCard({
             <div className="space-y-3">
               <p className="text-sm font-medium text-muted-foreground">{t("modelBreakdown")}</p>
               <div className="grid gap-4 md:grid-cols-2">
-                {keyPageItems.length > 0 && (
-                  <div className="space-y-2">
-                    <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
-                      {t("keyStats")}
-                    </p>
+                <div className="space-y-2">
+                  <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
+                    {t("keyStats")}
+                  </p>
+                  {keyPageItems.length > 0 ? (
                     <ModelBreakdownColumn
                       pageItems={keyPageItems}
                       currencyCode={currencyCode}
@@ -256,14 +256,16 @@ export function StatisticsSummaryCard({
                       keyPrefix="key"
                       pageOffset={sliceStart}
                     />
-                  </div>
-                )}
+                  ) : (
+                    <p className="text-sm text-muted-foreground">{t("noData")}</p>
+                  )}
+                </div>
 
-                {userPageItems.length > 0 && (
-                  <div className="space-y-2">
-                    <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
-                      {t("userStats")}
-                    </p>
+                <div className="space-y-2">
+                  <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
+                    {t("userStats")}
+                  </p>
+                  {userPageItems.length > 0 ? (
                     <ModelBreakdownColumn
                       pageItems={userPageItems}
                       currencyCode={currencyCode}
@@ -271,8 +273,10 @@ export function StatisticsSummaryCard({
                       keyPrefix="user"
                       pageOffset={sliceStart}
                     />
-                  </div>
-                )}
+                  ) : (
+                    <p className="text-sm text-muted-foreground">{t("noData")}</p>
+                  )}
+                </div>
               </div>
 
               {breakdownTotalPages > 1 && (
@@ -281,18 +285,23 @@ export function StatisticsSummaryCard({
                     size="icon"
                     variant="ghost"
                     className="h-7 w-7"
+                    aria-label={t("breakdownPrevPage")}
                     disabled={breakdownPage <= 1}
                     onClick={() => setBreakdownPage((p) => p - 1)}
                   >
                     <ChevronLeft className="h-4 w-4" />
                   </Button>
                   <span className="text-xs text-muted-foreground">
-                    {breakdownPage} / {breakdownTotalPages}
+                    {t("breakdownPageIndicator", {
+                      current: breakdownPage,
+                      total: breakdownTotalPages,
+                    })}
                   </span>
                   <Button
                     size="icon"
                     variant="ghost"
                     className="h-7 w-7"
+                    aria-label={t("breakdownNextPage")}
                     disabled={breakdownPage >= breakdownTotalPages}
                     onClick={() => setBreakdownPage((p) => p + 1)}
                   >
