Skip to content

fix(my-usage): UX improvements for quota and statistics cards#794

Merged
ding113 merged 6 commits intoding113:devfrom
miraserver:fix/my-usage-ux
Feb 15, 2026
Merged

fix(my-usage): UX improvements for quota and statistics cards#794
ding113 merged 6 commits intoding113:devfrom
miraserver:fix/my-usage-ux

Conversation

@miraserver
Copy link
Contributor

@miraserver miraserver commented Feb 15, 2026

Summary

  • Replace "Unlimited" text with infinity icon in quota cards for a cleaner look
  • Use currency symbol instead of currency code in quota display
  • Use Badge component for provider group values for visual consistency
  • Add pagination to model breakdown in statistics summary card (5 items per page)
  • Suppress biome exhaustive-deps lint for intentional stats-change page reset

Related Issues:

Test plan

  • Verify infinity icon renders in quota cards when limit is unlimited
  • Verify currency symbol (e.g. $) is shown instead of code (e.g. USD) in quota rows
  • Verify provider group values display as Badge components
  • Verify model breakdown pagination works (next/prev, page indicator, reset on data change)
  • bun run typecheck passes
  • bun run build succeeds

Description enhanced by Claude AI

Generated with Claude Code

Greptile Summary

This PR delivers several UX improvements to the my-usage page's quota and statistics cards:

  • Quota cards redesign: Replaces the previous Card-based grid layout with a more compact row-based design using inline progress bars, an infinity icon (InfinityIcon) for unlimited quotas, and formatCurrency() for proper currency symbol display (e.g., $ instead of USD).
  • Provider group badges: Switches from plain text to Badge components with tooltip-enabled abbreviated model/client names, improving visual density and consistency.
  • Model breakdown pagination: Adds client-side pagination (5 items per page) to the statistics summary card's model breakdown section, with page reset on date range changes.
  • i18n: Adds pagination translation keys (breakdownPrevPage, breakdownNextPage, breakdownPageIndicator) across all 5 locale files.
  • Collapsible card: Minor cleanup aliasing the Infinity import to InfinityIcon to avoid shadowing the global.

The changes align with the existing codebase patterns (using the shared formatCurrency utility, Badge/Tooltip components) and extend the currency symbol formatting from #717 to the quota cards.

Confidence Score: 4/5

  • This PR is safe to merge — it's a UI-only refactor with no backend or data logic changes.
  • All changes are confined to client-side presentation components and i18n files. The pagination logic is straightforward, the currency formatting uses the existing shared utility, and the abbreviation helpers are well-guarded against edge cases. One minor style nit on constant ordering. No security, data integrity, or functional risks.
  • No files require special attention. statistics-summary-card.tsx and provider-group-info.tsx have the most substantial changes but both are well-structured.

Important Files Changed

Filename Overview
src/app/[locale]/my-usage/_components/statistics-summary-card.tsx Adds model breakdown pagination (5 per page) with page reset on date range change. Clean extraction into ModelBreakdownColumn component. MODEL_BREAKDOWN_PAGE_SIZE is defined after usage but is safe at runtime.
src/app/[locale]/my-usage/_components/quota-cards.tsx Major refactor: replaces Card-based layout with compact row layout using inline progress bars, infinity icon for unlimited quotas, and formatCurrency for currency symbols. Cleaner and more compact design.
src/app/[locale]/my-usage/_components/provider-group-info.tsx Adds Badge-based display for provider groups and access restrictions with abbreviated model/client names in tooltips. New abbreviateModel and abbreviateClient helper functions added.
src/app/[locale]/my-usage/_components/collapsible-quota-card.tsx Trivial rename: Infinity import aliased to InfinityIcon to avoid shadowing the global Infinity. No logic changes.
messages/en/myUsage.json Adds three new i18n keys for pagination controls in model breakdown section.

Flowchart

flowchart TD
    A[StatisticsSummaryCard] -->|date range change| B[Reset breakdownPage to 1]
    A -->|loadStats| C[Fetch MyStatsSummary]
    C --> D{stats available?}
    D -->|yes| E[Compute maxBreakdownLen]
    E --> F[Slice keyModelBreakdown & userModelBreakdown]
    F --> G[ModelBreakdownColumn key]
    F --> H[ModelBreakdownColumn user]
    G --> I[ModelBreakdownRow x N]
    H --> J[ModelBreakdownRow x N]
    D -->|no| K[Show noData]

    L[QuotaCards] --> M[QuotaBlock per quota item]
    M --> N[QuotaRow keyLevel]
    M --> O[QuotaRow userLevel]
    N --> P{unlimited?}
    P -->|yes| Q[InfinityIcon + muted bar]
    P -->|no| R[Progress bar + formatted value]
    O --> P

    S[ProviderGroupInfo] --> T[Badge for groups]
    S --> U{hasModels?}
    U -->|yes| V[Tooltip + Badge with abbreviateModel]
    U -->|no| W[noRestrictions text]
Loading

Last reviewed commit: 3007012

John Doe and others added 5 commits February 15, 2026 16:50
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
…ards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… reset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @miraserver, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers several user experience enhancements to the 'My Usage' section. It refines the presentation of quota information by introducing more intuitive visual cues like infinity icons and currency symbols. Additionally, it improves the clarity and navigability of provider group details through the use of badges and tooltips, and makes the model breakdown statistics more user-friendly by implementing pagination.

Highlights

  • Quota Card Visuals: Replaced 'Unlimited' text with an infinity icon and updated currency displays to use symbols instead of codes for improved readability.
  • Provider Group Display: Implemented Badge components for displaying provider group values and restrictions, enhancing visual consistency and providing full names via tooltips.
  • Statistics Card Pagination: Added pagination to the model breakdown section within the statistics summary card, limiting items to 5 per page for better manageability.
  • Lint Rule Suppression: Suppressed a biome lint rule (exhaustive-deps) for a specific useEffect hook to allow intentional page reset when statistics data changes.
Changelog
  • src/app/[locale]/my-usage/_components/provider-group-info.tsx
    • Added abbreviateModel and abbreviateClient utility functions.
    • Imported Badge, Tooltip, TooltipContent, and TooltipTrigger components.
    • Refactored provider group displays to utilize Badge components.
    • Updated model and client restriction displays to use Badge components with Tooltip for abbreviated and full names.
  • src/app/[locale]/my-usage/_components/quota-cards.tsx
    • Imported Infinity icon and formatCurrency utility function.
    • Refactored the quota card rendering into new QuotaBlock and QuotaRow components.
    • Modified QuotaRow to display an Infinity icon for unlimited limits.
    • Updated QuotaRow to use formatCurrency for displaying monetary values with currency symbols.
    • Adjusted the QuotaCardsSkeleton component to align with the new component structure.
  • src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
    • Imported ChevronLeft and ChevronRight icons for pagination.
    • Imported ModelBreakdownItem type.
    • Implemented breakdownPage state and an useEffect to reset the page on stats data changes, with a biome-ignore comment.
    • Defined MODEL_BREAKDOWN_PAGE_SIZE constant with a value of 5.
    • Added logic to slice model breakdown data for paginated display.
    • Introduced a new ModelBreakdownColumn component to render paginated breakdown items.
    • Added pagination controls (previous/next buttons and page indicator) to the model breakdown section.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

📝 Walkthrough

Walkthrough

对 my-usage 模块的多处 UI 与交互进行重构:Provider/Group 显示改为 Badge+Tooltip,Quota 卡片重构为 QuotaBlock/QuotaRow 并调整货币/无限值显示,StatisticsSummaryCard 添加客户端分页及翻页控件,新增多语言分页文案。

Changes

Cohort / File(s) Summary
提供者组信息 UI
src/app/[locale]/my-usage/_components/provider-group-info.tsx
BadgeTooltip(含 TooltipTrigger/TooltipContent)替换纯文本展示;新增 abbreviateModel / abbreviateClient 辅助函数;条件渲染模型与客户端列表及无限制文案。
配额卡片重构
src/app/[locale]/my-usage/_components/quota-cards.tsx
移除原基于 Card 的组合,新增 QuotaBlock(导出)和内部 QuotaRow,统一货币格式化(formatCurrency)、无限值显示与进度文案;更新骨架加载 UI;调整每项货币计算并改用 InfinityIcon
统计摘要分页
src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
为模型分解添加客户端分页状态(breakdownPage)、常量 MODEL_BREAKDOWN_PAGE_SIZEModelBreakdownColumn 组件及左右翻页按钮(ChevronLeft/ChevronRight);引入并使用 ModelBreakdownItem 类型。
局部导入/符号别名调整
src/app/[locale]/my-usage/_components/collapsible-quota-card.tsx
Infinity 导入重命名为 InfinityIcon 并相应替换使用位置(行为无变化)。
本地化文案(分页)
messages/en/myUsage.json, messages/ja/myUsage.json, messages/ru/myUsage.json, messages/zh-CN/myUsage.json, messages/zh-TW/myUsage.json
为统计分解分页添加三条文案键:breakdownPrevPagebreakdownNextPagebreakdownPageIndicator

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地总结了主要改动:为配额卡和统计信息卡添加UX改进,包括无限图标、货币符号、徽章组件和分页功能。
Description check ✅ Passed 描述详细说明了所有主要更改,包括无限文本替换为图标、货币符号改进、徽章组件应用和分页功能,并提供了相关问题链接和测试计划。
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into dev

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the size/S Small PR (< 200 lines) label Feb 15, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces several UX improvements to the usage and statistics cards, such as visual consistency with Badge components, currency symbol formatting, and pagination for model breakdowns. Key improvements include the use of tooltips for abbreviated model names and a cleaner layout for quota information. However, there are robustness issues in the abbreviation logic that could lead to crashes with malformed input, and the pagination reset logic should be refined to avoid disrupting users during auto-refreshes. Additionally, a regression in the empty state display for statistics should be addressed.


const prefix = letterParts
.slice(0, 3)
.map((w) => w[0].toUpperCase())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The map operation will throw an error if letterParts contains an empty string (e.g., if the model name has double hyphens), as w[0] would be undefined. It's safer to use optional chaining or filter out empty strings.

Suggested change
.map((w) => w[0].toUpperCase())
.map((w) => w[0]?.toUpperCase())
.filter(Boolean)

}
return parts
.slice(0, 3)
.map((w) => w[0].toUpperCase())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to abbreviateModel, this map operation can crash if parts contains empty strings due to multiple spaces or hyphens in the name.

Suggested change
.map((w) => w[0].toUpperCase())
.filter(Boolean)
.map((w) => w[0].toUpperCase())

Comment on lines 122 to 125
// biome-ignore lint/correctness/useExhaustiveDependencies: intentional reset on stats identity change
useEffect(() => {
setBreakdownPage(1);
}, [stats]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Resetting the breakdown page whenever stats changes causes the UI to jump back to the first page every time the auto-refresh occurs (every 30 seconds). This is disruptive if the user is inspecting items on a later page. The page should only reset when the date range changes.

Suggested change
// biome-ignore lint/correctness/useExhaustiveDependencies: intentional reset on stats identity change
useEffect(() => {
setBreakdownPage(1);
}, [stats]);
// Reset breakdown page when date range changes
useEffect(() => {
setBreakdownPage(1);
}, [dateRange.startDate, dateRange.endDate]);

Comment on lines 247 to 260
{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>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The 'No data' message for the model breakdown was removed. If there is no data for the selected period, the columns will now be completely empty. Consider restoring the empty state message and keeping the headers visible for consistency.

Suggested change
{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>
)}
<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}
totalCost={stats.totalCost}
keyPrefix="key"
pageOffset={sliceStart}
/>
) : (
<p className="text-sm text-muted-foreground py-2">{t("noData")}</p>
)}
</div>

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 133 to 144
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>
))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping TooltipTrigger with an extra <span> is unnecessary. The asChild prop makes TooltipTrigger pass its props to its child, so you can directly wrap Badge.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/my-usage/_components/provider-group-info.tsx
Line: 133:144

Comment:
Wrapping `TooltipTrigger` with an extra `<span>` is unnecessary. The `asChild` prop makes `TooltipTrigger` pass its props to its child, so you can directly wrap `Badge`.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 156 to 167
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>
))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue - the extra <span> wrapper is unnecessary when using asChild prop on TooltipTrigger.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/my-usage/_components/provider-group-info.tsx
Line: 156:167

Comment:
Same issue - the extra `<span>` wrapper is unnecessary when using `asChild` prop on `TooltipTrigger`.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@github-actions github-actions bot added enhancement New feature or request area:UI area:UX labels Feb 15, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/app/`[locale]/my-usage/_components/provider-group-info.tsx:
- Around line 53-62: abbreviateClient can produce empty-string array elements
when the input starts with separators, causing w[0] to be undefined; update the
function (abbreviateClient) to filter out empty parts before slicing/mapping
(e.g., use parts.filter(Boolean) or equivalent) and ensure a safe fallback when
the resulting array is empty (return an empty string or a sensible default) so
the map and w[0] access cannot throw.
- Around line 9-51: abbreviateModel can crash when name.split("-") yields empty
strings (e.g., "model--v2"); filter out empty parts before processing or guard
against empty strings when building letterParts so you never call w[0] on an
empty string. Specifically, update the parts handling in abbreviateModel (the
parts array and the for loop that pushes into letterParts) to ignore "" entries
(or trim them) and ensure the prefix mapping (.slice(0,3).map(w =>
w[0].toUpperCase())) only runs on non-empty words; this prevents the TypeError
and preserves existing abbreviation logic (versionMixed/versionNums/prefix
assembly).

In `@src/app/`[locale]/my-usage/_components/quota-cards.tsx:
- Around line 182-183: The label span in quota-cards.tsx currently uses a fixed
width class ("w-8") which causes translations to overflow; in the <span
...>{label}</span> replace the fixed width with a responsive set such as "w-auto
min-w-8" or "max-w-fit" and add overflow handling classes like
"whitespace-nowrap overflow-hidden text-ellipsis" (keep existing "shrink-0
text-[11px] text-muted-foreground") so the label adapts to longer translations
without layout breakage.

In `@src/app/`[locale]/my-usage/_components/statistics-summary-card.tsx:
- Around line 278-302: The pagination controls lack accessibility and i18n: add
descriptive aria-labels to the icon Buttons (the ones rendering ChevronLeft and
ChevronRight within the pagination block using Button and handlers
setBreakdownPage) and replace the hardcoded page indicator "{breakdownPage} /
{breakdownTotalPages}" with an i18n string via t(), e.g. a translated pattern
like t('pagination.page_of', { page: breakdownPage, total: breakdownTotalPages
}); ensure the aria-labels also use t() (e.g. t('pagination.previous') /
t('pagination.next')) so all user-facing text is localized and screen-reader
friendly.
🧹 Nitpick comments (2)
src/app/[locale]/my-usage/_components/statistics-summary-card.tsx (1)

247-275: 分页列数不对称时布局可能跳动。

keyModelBreakdownuserModelBreakdown 长度不同时,较短一方在后续页面可能为空(pageItems.length > 0 为 false),导致该列整体不渲染,grid 布局从双列变单列。如果需要保持布局稳定,可以在无数据时渲染一个占位空容器,或始终渲染列标题。

src/app/[locale]/my-usage/_components/quota-cards.tsx (1)

3-3: Infinity 导入遮蔽了全局 Infinity 常量

lucide-react 导入的 Infinity 组件遮蔽了 JavaScript 全局常量 Infinity。当前代码中使用 Number.isFinite 因此不会出问题,但如果后续有人在此文件中写 value === Infinity 会产生难以排查的 bug。建议重命名导入以消除歧义。

建议修改
-import { Infinity } from "lucide-react";
+import { Infinity as InfinityIcon } from "lucide-react";

同时将 Line 198 的引用一并更新:

-          {unlimited ? <Infinity className="inline h-3.5 w-3.5" /> : limitDisplay}
+          {unlimited ? <InfinityIcon className="inline h-3.5 w-3.5" /> : limitDisplay}

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] TEST-MISSING-CRITICAL - The new abbreviateModel() (lines 10-48) and abbreviateClient() (lines 50-60) functions contain complex parsing logic with multiple regex patterns but have no unit test coverage. These functions handle various model name formats and edge cases (version numbers, date-like strings, mixed alphanumeric patterns). According to CLAUDE.md, new features require 80% test coverage.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

This PR introduces UX improvements for the My Usage page, including infinity icons for unlimited quotas, currency symbols instead of codes, Badge components for provider groups, and pagination for model breakdown. The changes are focused on three files in the my-usage components directory.

PR Size: S

  • Lines changed: 495 (328 additions, 167 deletions)
  • Files changed: 3

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 1 0
Simplification 0 0 0 0

Critical Issues (Must Fix)

None.

High Priority Issues (Should Fix)

None.

Medium Priority Issues

  • TEST-MISSING-CRITICAL: The new abbreviateModel() and abbreviateClient() functions lack unit tests. These parsing functions contain complex regex logic handling edge cases (version numbers, date-like strings, mixed alphanumeric patterns) that should be tested per CLAUDE.md's 80% test coverage requirement.

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - 1 medium issue identified
  • Code clarity - Good

Automated review by Claude AI

- 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>
@miraserver
Copy link
Contributor Author

Review Fixes Applied

Addressed all comments from Gemini, CodeRabbit, and Greptile reviews:

Bugs Fixed

  • abbreviateModel/abbreviateClient crash on empty split parts ("model--v2", "-client") — added .filter(Boolean) (provider-group-info.tsx:10,54)
  • Pagination reset on auto-refresh — changed dep from [stats] to [dateRange.startDate, dateRange.endDate] (statistics-summary-card.tsx:121-125)
  • Missing noData fallback in model breakdown — columns always render with {t("noData")} when empty (statistics-summary-card.tsx:247-275)

I18N

  • Pagination hardcoded text — replaced with t("breakdownPageIndicator") + aria-label on buttons (statistics-summary-card.tsx:278-302)
  • Translation keys added to all 5 languages: breakdownPrevPage, breakdownNextPage, breakdownPageIndicator
  • Quota label overflow for Russian/Japanese — w-8 replaced with w-auto whitespace-nowrap (quota-cards.tsx:182)

Code Quality

  • Infinity shadowing global — renamed to InfinityIcon in both quota-cards.tsx and collapsible-quota-card.tsx
  • Redundant <span> wrappers removed from TooltipTrigger asChild (provider-group-info.tsx:133-144, 156-167)

Verification

  • tsgo --noEmit — clean
  • biome check on changed files — clean
  • next build — clean

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

);
}

const MODEL_BREAKDOWN_PAGE_SIZE = 5;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constant defined after usage
MODEL_BREAKDOWN_PAGE_SIZE is declared on line 322 but referenced on line 134. While this works at runtime (the module fully evaluates before React calls any component), declaring constants before their usage is conventional and improves readability. Consider moving this constant above the StatisticsSummaryCard component.

Suggested change
const MODEL_BREAKDOWN_PAGE_SIZE = 5;
const MODEL_BREAKDOWN_PAGE_SIZE = 5;

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/my-usage/_components/statistics-summary-card.tsx
Line: 322:322

Comment:
**Constant defined after usage**
`MODEL_BREAKDOWN_PAGE_SIZE` is declared on line 322 but referenced on line 134. While this works at runtime (the module fully evaluates before React calls any component), declaring constants before their usage is conventional and improves readability. Consider moving this constant above the `StatisticsSummaryCard` component.

```suggestion
const MODEL_BREAKDOWN_PAGE_SIZE = 5;
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/app/`[locale]/my-usage/_components/statistics-summary-card.tsx:
- Around line 119-138: The current breakdownPage can become larger than
breakdownTotalPages after an auto-refresh; add a clamp so the active page never
exceeds available pages. After computing breakdownTotalPages (using
MODEL_BREAKDOWN_PAGE_SIZE and stats), derive a clampedPage =
Math.min(Math.max(breakdownPage, 1), Math.max(1, breakdownTotalPages)) and use
that for sliceStart/sliceEnd and for pagination controls instead of raw
breakdownPage; also add a useEffect watching breakdownTotalPages (or stats) that
calls setBreakdownPage(clampedPage) when breakdownPage > breakdownTotalPages to
proactively reset the state. Update references that build keyPageItems and
userPageItems to slice using clampedPage.

@ding113 ding113 merged commit 084c940 into ding113:dev Feb 15, 2026
10 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Feb 15, 2026
ding113 added a commit that referenced this pull request Feb 15, 2026
* fix(proxy): extract model from Gemini Vertex AI publishers path for correct billing

When Gemini requests use the Vertex AI URL format
/v1/publishers/google/models/{model}:generateContent, the system
failed to extract the model name, falling back to a hardcoded
"gemini-2.5-flash" default and causing incorrect billing.

Add publishers path regex to extractModelFromPath() and
detectFormatByEndpoint() to handle this URL pattern.

* fix(proxy): correct Host header to match actual request target in standard path

buildHeaders() derives Host from provider.url, but the actual fetch target
(proxyUrl) may use a different host when activeEndpoint.baseUrl differs or
MCP passthrough overrides the base URL. This causes undici TLS certificate
validation failures. After proxyUrl is computed, re-derive Host from it.

* perf(logs): hide stats summary panel when no filters are active

Skip rendering UsageLogsStatsPanel and its aggregation query when all
filter conditions are empty, preventing full-table scans that cause
CPU overload.

* fix(proxy): remove deterministic session ID to prevent collision across conversations (#793)

generateDeterministicSessionId() hashes (UA, IP, API key prefix) with no time
dimension, producing identical session IDs for the same user hours apart. This
merges unrelated conversations into one session, polluting usage logs, session
tracking, and concurrent session limits.

The existing fallback in getOrCreateSessionId() (content hash -> random ID)
already provides correct session continuity without collision risk.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* perf(logs): hide stats panel in virtualized view when no filters active

Apply the same hasStatsFilters guard from the old view to the
virtualized logs view, preventing an unconditional full-table
aggregation query on page load. Also remove the unused legacy
usage-logs-view.tsx which is no longer imported anywhere.

* fix(my-usage): UX improvements for quota and statistics cards (#794)

* style(my-usage): use Badge for provider group values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* 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>

* style(my-usage): replace unlimited text with infinity icon in quota cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(my-usage): paginate model breakdown in statistics summary card

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(my-usage): suppress biome exhaustive-deps for intentional stats reset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* 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>

---------

Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: miraserver <20286838+miraserver@users.noreply.github.com>
Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:UI area:UX enhancement New feature or request size/S Small PR (< 200 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants