Skip to content

fix(logs): compact stats panel and collapse filters to reduce above-fold space#788

Open
NieiR wants to merge 7 commits intoding113:devfrom
NieiR:fix/766-logs-page-layout
Open

fix(logs): compact stats panel and collapse filters to reduce above-fold space#788
NieiR wants to merge 7 commits intoding113:devfrom
NieiR:fix/766-logs-page-layout

Conversation

@NieiR
Copy link
Contributor

@NieiR NieiR commented Feb 15, 2026

关联 Issue

Closes #766

变更内容

基于上游 v0.5.8 rebase(包含 #795#794#793 等)。在上游 hasStatsFilters 性能优化(无筛选条件时隐藏统计面板,避免全表聚合查询)的基础上,叠加 #766 要求的首屏空间优化。

具体改动

  • 统计面板紧凑化:从 4 卡片网格(~150px)改为单行内联显示(~40px),指标间用竖线分隔,Token/Cache 子项用括号内联展示
  • 筛选条件默认折叠:使用 Collapsible 组件包裹,默认折叠为单行条,展开后与原布局一致;折叠态显示激活筛选数量 Badge
  • 活跃会话空状态压缩:为 ActiveSessionsList 新增 compactEmpty prop(默认 false,不影响其他调用方),无活跃会话时压缩为单行显示

与上游的兼容性

  • 保留 hasStatsFilters 性能守卫(来自 29d05b22)——仅在有筛选条件时才渲染统计面板
  • 统计面板渲染时使用紧凑布局——性能优化与 UX 优化共存
  • 无新增 i18n key,全部复用已有翻译
  • 未触及 API / 数据层

影响范围

维度 风险
ActiveSessionsList 新增 prop 默认 false,其他调用方行为不变
统计面板样式 仅影响日志页,数据源和接口不变
筛选条件折叠 展开后与原来完全一致,筛选逻辑未动
i18n 无新增 key,全部复用已有翻译
API / 数据层 未触及

涉及文件

  • src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-sections.tsx
  • src/components/customs/active-sessions-list.tsx

检查清单

  • 目标分支为 dev
  • bun run typecheck 通过
  • bun run lint 通过
  • bun run build 通过
  • 无新增 i18n key(复用已有翻译)
  • 无破坏性变更
  • 已基于上游 v0.5.8 rebase

效果图

Snipaste_2026-02-16_01-24-42

Greptile Summary

This PR successfully reduces above-fold space on the logs page by compacting the stats panel from a 4-card grid (~150px) to an inline single row (~40px), collapsing the filter panel by default with badge count indicator, and compressing the empty active sessions state to a single line. The changes preserve all existing functionality while improving screen real estate usage.

Key changes:

  • Stats panel refactored to inline display with pipe separators and parenthetical sub-metrics
  • Filter panel wrapped in Collapsible with auto-expand when URL filters are detected
  • ActiveSessionsList gains optional compactEmpty prop (default false, backward compatible)
  • Import ordering standardized across provider settings files
  • Chinese typography fix (full-width parentheses in zh-TW)

Compatibility:

  • Maintains upstream hasStatsFilters performance guard from v0.5.8
  • No new i18n keys required (reuses existing translations)
  • No API or data layer changes
  • All existing callers of ActiveSessionsList unaffected by default prop value

Confidence Score: 4/5

  • This PR is safe to merge with one minor style suggestion
  • The implementation is solid with backward-compatible changes, proper memoization, and auto-expand UX enhancement. Previous review threads have been addressed. Score reduced from 5 to 4 due to template literal className that could break Tailwind tooling (minor style issue), but this is a non-blocking suggestion and the code will function correctly.
  • Only src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx has a minor style suggestion regarding template literal className on line 328

Important Files Changed

Filename Overview
src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx Refactored from card-based grid layout to compact inline layout; removed cn utility import (no longer needed); all functionality preserved
src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx Added collapsible filter panel with auto-expand on URL filters, badge count display, and memoized statsFilters; template literal className on line 328
src/components/customs/active-sessions-list.tsx Added compactEmpty prop with default false; backward compatible with all existing callers
src/app/[locale]/dashboard/logs/_components/usage-logs-sections.tsx Added compactEmpty prop to ActiveSessionsList; minimal change, single line addition

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Logs Page] --> B[Active Sessions Section]
    A --> C[Stats Panel]
    A --> D[Filter Panel]
    A --> E[Usage Records Table]
    
    B -->|compactEmpty=true| B1{Has Active Sessions?}
    B1 -->|Yes| B2[Full Card Layout]
    B1 -->|No| B3[Compact Single Line]
    
    C -->|hasStatsFilters?| C1{Has Filter Criteria?}
    C1 -->|Yes| C2[Inline Stats Display]
    C1 -->|No| C3[Hidden]
    
    D --> D1[Collapsible Component]
    D1 -->|URL Filters| D2{activeFilterCount > 0?}
    D2 -->|Yes| D3[Auto-expand on mount]
    D2 -->|No| D4[Collapsed by default]
    D1 -->|Collapsed| D5[Single line + Badge count]
    D1 -->|Expanded| D6[Full filter form]
    
    C2 --> C4[4 inline stats separated by pipes]
    C4 --> C5[Requests, Cost, Tokens, Cache]
    
    style B3 fill:#e1f5e1
    style C2 fill:#e1f5e1
    style D3 fill:#e1f5e1
    style D5 fill:#e1f5e1
Loading

Last reviewed commit: 5e2c8d9

@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

本次变更重构并紧凑化日志页面 UI:添加可折叠的筛选区并显示活跃筛选计数、将统计面板改为紧凑内联布局、并为活跃会话列表增加空状态的紧凑渲染选项(compactEmpty)。

Changes

Cohort / File(s) Summary
筛选条件可折叠模块
src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
引入可折叠的 Filter 区(Collapsible、Trigger、ChevronDown),增加 isFiltersOpen 状态、activeFilterCount 计算与 Badge,支持 URL 驱动的初始自动展开。
统计面板重设计
src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx
移除原有 glass/card 外层与网格结构,改为单行内联布局;新增 StatItem/Separator、简化骨架与条件渲染,保留原有数据加载逻辑。
活跃会话列表紧凑模式
src/components/customs/active-sessions-list.tsx, src/app/[locale]/dashboard/logs/_components/usage-logs-sections.tsx
ActiveSessionsList 增加公开 prop compactEmpty?: boolean,当数据为空且开启该 prop 时渲染单行紧凑空状态,而非完整列表容器。
本地化文本修正
messages/zh-TW/dashboard.json
将两处括号替换为全角中文括号(“( )” → “( )”)。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确总结了本 PR 的核心改动:压缩统计面板和折叠筛选器以减少首屏空间占用。
Linked Issues check ✅ Passed PR 完全满足 Issue #766 的要求:默认折叠筛选条件框、减少垂直空间占用、使请求记录首屏可见。
Out of Scope Changes check ✅ Passed 所有代码变更均在 PR 目标范围内:UI 布局优化、组件重构、国际化调整,未涉及 API 或数据层变更。
Description check ✅ Passed PR描述清晰地关联了Issue #766,详细说明了变更内容(统计面板紧凑化、筛选条件折叠、活跃会话空状态压缩)和影响范围,与实际代码改动相符。

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @NieiR, 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!

此拉取请求旨在优化使用记录页面的布局,通过减少统计汇总、筛选条件和活跃会话三个主要板块在首屏所占用的空间,使用户无需滚动即可查看到请求记录。这些改进提升了页面的视觉紧凑性和用户体验。

Highlights

  • 统计面板紧凑化: 将统计面板从四卡片网格布局(约150px高)改为单行内联显示(约40px高),指标间用竖线分隔,Token/Cache 子项以内联括号形式展示。
  • 筛选条件默认折叠: 筛选条件区域现在默认折叠,显示为单行条,并带有激活筛选数量的徽章。展开后布局与原先一致。
  • 活跃会话空状态压缩: 为 ActiveSessionsList 组件新增 compactEmpty 属性,当无活跃会话时,将其空状态显示压缩为单行。
  • 修复统计面板不必要的重请求: 通过使用 useMemo 确保传递给 UsageLogsStatsPanelfilters 对象引用稳定,避免了因 JSX 对象字面量导致的不必要统计数据重新请求。
Changelog
  • src/app/[locale]/dashboard/logs/_components/usage-logs-sections.tsx
    • UsageLogsActiveSessionsSection 中的 ActiveSessionsList 组件添加了 compactEmpty 属性。
  • src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx
    • 移除了不再使用的 cn 导入。
    • 删除了与玻璃拟态UI和稳定过滤器键相关的注释。
    • 重构了 UsageLogsStatsPanel 的渲染逻辑,将其从多卡片网格布局改为紧凑的单行显示。
    • 引入了 StatItemSeparator 辅助组件以支持新的紧凑布局。
  • src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
    • 导入了 ChevronDownBadgeCollapsibleCollapsibleContentCollapsibleTrigger 组件。
    • 添加了 isFiltersOpen 状态来控制筛选器部分的折叠/展开。
    • 实现了 activeFilterCount 的 memoization,用于计算并显示激活的筛选器数量。
    • 实现了 statsFilters 的 memoization,以确保传递给 UsageLogsStatsPanel 的过滤器对象引用稳定。
    • 将筛选条件区域封装在 Collapsible 组件中,并添加了一个触发按钮,该按钮在折叠状态下显示激活筛选器数量的徽章。
    • 将 memoized 的 statsFilters 传递给 UsageLogsStatsPanel 组件。
  • src/components/customs/active-sessions-list.tsx
    • ActiveSessionsListProps 接口中添加了 compactEmpty 属性。
    • compactEmpty 为真且没有活跃会话时,实现了紧凑的单行空状态显示。
Activity
  • 目前没有记录到人工活动。
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.

@github-actions github-actions bot added bug Something isn't working area:UI labels Feb 15, 2026
@github-actions github-actions bot added the size/M Medium PR (< 500 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

本次 PR 很好地优化了使用记录页面的布局,通过紧凑化统计面板、折叠筛选条件和压缩空状态,显著减少了首屏空间占用,提升了用户体验。同时,通过 useMemo 修复了统计面板不必要的重请求问题,这是一个重要的性能优化。代码实现清晰,结构良好。我有一个关于代码复用的建议,以进一步提高代码的可维护性。

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 optimizes the usage logs page layout to reduce above-fold space consumption. The changes are well-structured, focused, and follow React best practices.

PR Size: M

  • Lines changed: 323 (154 additions, 169 deletions)
  • Files changed: 4

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 0 0
Simplification 0 0 0 0

Key Changes Reviewed

  1. Stats Panel Compact Layout ()

    • Refactored from 4-card grid to single-line inline display
    • Removed unused import
    • New and helper components are clean and focused
  2. Collapsible Filters ()

    • Added wrapper with badge
    • Proper use of for to prevent unnecessary re-renders
    • Correct dependency array handling for calculation
  3. Active Sessions Compact Empty State ()

    • New prop (default ) maintains backward compatibility
    • Early return pattern for compact empty state is clean
  4. Integration ()

    • Simple prop addition to enable compact mode

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Proper error states maintained
  • Type safety - TypeScript types are correct
  • Documentation accuracy - Comments updated appropriately
  • Test coverage - N/A (UI changes, existing tests sufficient)
  • Code clarity - Well-structured and readable

Conclusion

No issues found. The PR is well-implemented, follows React best practices, and maintains backward compatibility. The changes are focused and achieve the stated goal of reducing above-fold space consumption.


Automated review by Claude AI

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 optimizes the usage logs page layout to reduce above-fold space consumption. The changes are well-structured, focused, and follow React best practices.

PR Size: M

  • Lines changed: 323 (154 additions, 169 deletions)
  • Files changed: 4

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 0 0
Simplification 0 0 0 0

Key Changes Reviewed

Stats Panel Compact Layout

  • Refactored from 4-card grid to single-line inline display
  • Removed unused cn import
  • New StatItem and Separator helper components are clean

Collapsible Filters

  • Added Collapsible wrapper with activeFilterCount badge
  • Proper use of useMemo for statsFilters to prevent re-renders
  • Correct dependency array handling for activeFilterCount

Active Sessions Compact Empty State

  • New compactEmpty prop (default false) maintains backward compatibility
  • Early return pattern for compact empty state is clean

Integration

  • Simple prop addition to enable compact mode

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Proper error states maintained
  • Type safety - TypeScript types are correct
  • Documentation accuracy - Comments updated appropriately
  • Test coverage - N/A (UI changes, existing tests sufficient)
  • Code clarity - Well-structured and readable

Conclusion

No issues found. The PR is well-implemented, follows React best practices, and maintains backward compatibility. The changes are focused and achieve the stated goal of reducing above-fold space consumption.

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.

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@github-actions github-actions bot mentioned this pull request Feb 15, 2026
@NieiR NieiR force-pushed the fix/766-logs-page-layout branch from b565894 to 73c3102 Compare February 15, 2026 17:09
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.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 15, 2026

Additional Comments (1)

src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
statsFilters is not memoized despite PR description

The PR description states "改用 useMemo 保证引用稳定" (use useMemo to ensure stable reference), but statsFilters is still a plain object literal that gets recreated on every render. This means a new object reference is passed to UsageLogsStatsPanel each time.

In practice this doesn't cause redundant API calls because UsageLogsStatsPanel internally uses JSON.stringify(filters) as a change-detection key. However, it does cause unnecessary re-renders of UsageLogsStatsPanel itself (React will re-render the child because the prop reference changed). Consider wrapping in useMemo to match the stated intent:

  const statsFilters = useMemo(() => ({
    userId: filters.userId,
    keyId: filters.keyId,
    providerId: filters.providerId,
    sessionId: filters.sessionId,
    startTime: filters.startTime,
    endTime: filters.endTime,
    statusCode: filters.statusCode,
    excludeStatusCode200: filters.excludeStatusCode200,
    model: filters.model,
    endpoint: filters.endpoint,
    minRetryCount: filters.minRetryCount,
  }), [filters]);

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]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Line: 266:278

Comment:
**`statsFilters` is not memoized despite PR description**

The PR description states "改用 `useMemo` 保证引用稳定" (use `useMemo` to ensure stable reference), but `statsFilters` is still a plain object literal that gets recreated on every render. This means a new object reference is passed to `UsageLogsStatsPanel` each time.

In practice this doesn't cause redundant API calls because `UsageLogsStatsPanel` internally uses `JSON.stringify(filters)` as a change-detection key. However, it does cause unnecessary re-renders of `UsageLogsStatsPanel` itself (React will re-render the child because the prop reference changed). Consider wrapping in `useMemo` to match the stated intent:

```suggestion
  const statsFilters = useMemo(() => ({
    userId: filters.userId,
    keyId: filters.keyId,
    providerId: filters.providerId,
    sessionId: filters.sessionId,
    startTime: filters.startTime,
    endTime: filters.endTime,
    statusCode: filters.statusCode,
    excludeStatusCode200: filters.excludeStatusCode200,
    model: filters.model,
    endpoint: filters.endpoint,
    minRetryCount: filters.minRetryCount,
  }), [filters]);
```

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/customs/active-sessions-list.tsx (1)

14-20: ⚠️ Potential issue | 🟡 Minor

fetchActiveSessions 中的错误信息为硬编码中文字符串

Line 17 的 "获取活跃 Session 失败" 是硬编码中文。虽然这是预先存在的代码且错误不会直接渲染到 UI(useQueryerror 未在组件中展示),但如果未来 error boundary 或日志系统展示此消息,将无法国际化。

此为已有问题,非本 PR 引入,可后续处理。

🧹 Nitpick comments (3)
src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx (2)

36-60: filtersKey 去重机制被 loadStats 依赖破坏

loadStatsuseCallback 依赖了 filters 对象引用(line 55),而 useEffect 同时依赖 filtersKeyloadStats(line 60)。当父组件未正确 memoize filters 时,即使 filtersKey(JSON 字符串)不变,filters 引用变化也会导致 loadStats 重建,进而触发 effect 重新执行,造成重复请求。

虽然 PR 在父组件中加了 useMemo 来稳定引用,但本组件自身的防护是不完整的。更健壮的做法是让 loadStats 内部基于 filtersKey 解析 filters,或使用 ref 模式彻底解耦:

建议的修复方案
- const filtersKey = JSON.stringify(filters);
-
- const loadStats = useCallback(async () => {
-   setIsLoading(true);
-   setError(null);
-
-   try {
-     const result = await getUsageLogsStats(filters);
+ const filtersKey = JSON.stringify(filters);
+ const filtersRef = useRef(filters);
+ filtersRef.current = filters;
+
+ const loadStats = useCallback(async () => {
+   setIsLoading(true);
+   setError(null);
+
+   try {
+     const result = await getUsageLogsStats(filtersRef.current);
      if (result.ok && result.data) {
        setStats(result.data);
      } else {
        setError(!result.ok ? result.error : t("logs.error.loadFailed"));
      }
    } catch (err) {
      console.error("Failed to load usage logs stats:", err);
      setError(t("logs.error.loadFailed"));
    } finally {
      setIsLoading(false);
    }
- }, [filters, t]);
+ }, [t]);

  // biome-ignore lint/correctness/useExhaustiveDependencies: filtersKey is used to detect filter changes
  useEffect(() => {
    loadStats();
  }, [filtersKey, loadStats]);

需要在 line 5 的 import 中添加 useRef


133-135: Separator 命名与 UI 组件库同名,建议区分

项目中 @/components/ui/separator.tsx 已导出 Separator 组件。此处的本地函数虽未产生运行时冲突,但同名易造成混淆——未来如果需要在本文件引入 UI 库的 Separator,会产生命名冲突。建议重命名为 StatDivider 或类似名称。

src/components/customs/active-sessions-list.tsx (1)

45-53: 建议:compactEmpty 放置位置与其他 boolean props 不一致

compactEmpty = false 在解构中位于 maxHeight 之后、showTokensCost 之前(line 50),但在接口定义中位于 showTokensCost 之后(line 33-34)。建议保持解构顺序与接口定义顺序一致,提升可读性。

建议调整解构顺序
 export function ActiveSessionsList({
   currencyCode = "USD",
   maxItems,
   showHeader = true,
   maxHeight = "200px",
-  compactEmpty = false,
   showTokensCost = true,
+  compactEmpty = false,
   className = "",
 }: ActiveSessionsListProps) {

…old space

Rebased on upstream v0.5.8 (includes hasStatsFilters perf guard).
Retains our UX optimizations for issue ding113#766:
- Stats panel: compact single-line inline layout (~40px vs ~150px)
- Filter criteria: collapsed by default with active filter count badge
- Active sessions: compactEmpty single-line when no sessions
@NieiR NieiR force-pushed the fix/766-logs-page-layout branch from 73c3102 to 5cc234d Compare February 15, 2026 17:14
…ailure (ding113#800)

Add parentheses around date arithmetic in buildDateCondition to prevent
AT TIME ZONE from binding to INTERVAL instead of the full expression.
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.

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@NieiR
Copy link
Contributor Author

NieiR commented Feb 19, 2026

Response to CodeRabbit nitpick suggestions

1. filtersKey dedup mechanism — Adopted

statsFilters was a plain object literal recreated on every render, causing loadStats to rebuild on every parent re-render (e.g., toggling auto-refresh, expanding filters), which defeated the filtersKey JSON-string dedup.

Fixed by wrapping statsFilters in useMemo([filters]). Since filters is already memoized from useSearchParams, statsFilters now has a stable reference, and loadStats only rebuilds when filters actually change.

2. Separator naming conflict — Not adopted

This is a file-private function (not exported). It will never conflict with @/components/ui/separator at runtime. Renaming for a hypothetical future import is speculative — if that need arises, the rename is trivial at that point.

3. compactEmpty destructuring order — Not adopted

Destructuring order has no semantic or readability impact. Enforcing interface-definition order is a stylistic preference, not a maintainability concern.

4. Hardcoded Chinese error string in fetchActiveSessions — Acknowledged

As CodeRabbit noted, this is pre-existing code not introduced by this PR. Will address separately if needed.

…auto-expand on URL filters

- Wrap statsFilters in useMemo to prevent unnecessary API re-requests
  when parent re-renders without filter changes
- Replace Object.values filter count with explicit per-field counting,
  grouping statusCode/excludeStatusCode200 as one filter (consistent
  with usage-logs-filters.tsx)
- Auto-expand collapsible filter panel when URL-based filters are
  detected on initial page load
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/`[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx:
- Around line 284-296: activeFilterCount currently only counts the time filter
when both filters.startTime and filters.endTime are present, which can mismatch
hasStatsFilters and timeActiveCount; change the time check in
usage-logs-view-virtualized.tsx (activeFilterCount) to treat a partial time
range as active (e.g., count if filters.startTime !== undefined ||
filters.endTime !== undefined), and also add symmetry by either updating the
timeActiveCount logic in usage-logs-filters.tsx or normalize URL/filters earlier
(coerce to both undefined if only one of filters.startTime/filters.endTime is
present) so both places use the same presence rule.

Comment on lines +284 to +296
const activeFilterCount = useMemo(() => {
let count = 0;
if (filters.userId !== undefined) count++;
if (filters.keyId !== undefined) count++;
if (filters.providerId !== undefined) count++;
if (filters.sessionId) count++;
if (filters.startTime && filters.endTime) count++;
if (filters.statusCode !== undefined || filters.excludeStatusCode200) count++;
if (filters.model) count++;
if (filters.endpoint) count++;
if (filters.minRetryCount !== undefined && filters.minRetryCount > 0) count++;
return count;
}, [filters]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the actual content at the specified lines in usage-logs-view-virtualized.tsx
echo "=== Checking usage-logs-view-virtualized.tsx lines 265-296 ==="
rg -n "activeFilterCount|hasStatsFilters|statsFilters" src/app/*/dashboard/logs/_components/usage-logs-view-virtualized.tsx -A 15 -B 2

Repository: ding113/claude-code-hub

Length of output: 3481


🏁 Script executed:

#!/bin/bash
# Look for usage-logs-filters.tsx to understand filter behavior
echo "=== Finding usage-logs-filters file ==="
fd "usage-logs-filters" --type f

Repository: ding113/claude-code-hub

Length of output: 173


🏁 Script executed:

#!/bin/bash
# Check for the i18n key t("title.filterCriteria")
echo "=== Searching for i18n key ==="
rg "title.filterCriteria" --type ts --type tsx

Repository: ding113/claude-code-hub

Length of output: 142


🏁 Script executed:

#!/bin/bash
# Check locale files for the key
echo "=== Checking locale files ==="
fd ".*\.json" -t f | grep -i locale | head -20

Repository: ding113/claude-code-hub

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Check usage-logs-filters.tsx for startTime/endTime handling
echo "=== Checking filter form logic ==="
cat src/app/*/dashboard/logs/_components/usage-logs-filters.tsx | head -200

Repository: ding113/claude-code-hub

Length of output: 6463


🏁 Script executed:

#!/bin/bash
# Find locale directory
echo "=== Finding locale directory ==="
fd "messages" --type d
fd "locales" --type d
find . -type d -name "*locale*" | head -10

Repository: ding113/claude-code-hub

Length of output: 186


🏁 Script executed:

#!/bin/bash
# Search for i18n keys more carefully
echo "=== Searching for filterCriteria key ==="
rg "filterCriteria" -t json
rg "filterCriteria"

Repository: ding113/claude-code-hub

Length of output: 1928


🏁 Script executed:

#!/bin/bash
# Check the type definitions for filters
echo "=== Checking filters type ==="
rg -n "type.*Filter" src/app/*/dashboard/logs/_components/usage-logs-view-virtualized.tsx -B 2 -A 10

Repository: ding113/claude-code-hub

Length of output: 636


🏁 Script executed:

#!/bin/bash
# Check the exact structure of dashboard.json to see if "title" nesting exists
echo "=== Checking dashboard.json structure for filterCriteria ==="
grep -A 2 -B 2 "filterCriteria" messages/en/dashboard.json

Repository: ding113/claude-code-hub

Length of output: 372


🏁 Script executed:

#!/bin/bash
# Check the full context around filterCriteria in locale files
echo "=== Full context in zh-CN dashboard.json ==="
cat messages/zh-CN/dashboard.json | head -100

Repository: ding113/claude-code-hub

Length of output: 2895


🏁 Script executed:

#!/bin/bash
# Look at TimeFilters component to understand startTime/endTime behavior
echo "=== Checking TimeFilters component ==="
fd "time-filters" --type f

Repository: ding113/claude-code-hub

Length of output: 174


🏁 Script executed:

#!/bin/bash
# Check TimeFilters component to see if startTime and endTime can be set independently
echo "=== Checking TimeFilters implementation ==="
cat src/app/*/dashboard/logs/_components/filters/time-filters.tsx

Repository: ding113/claude-code-hub

Length of output: 6184


🏁 Script executed:

#!/bin/bash
# Also check if there are any validations that enforce both being set together
echo "=== Searching for startTime/endTime validation logic ==="
rg -n "startTime|endTime" src/app/*/dashboard/logs/_components/filters/time-filters.tsx -B 2 -A 2

Repository: ding113/claude-code-hub

Length of output: 3783


第 290 行的时间范围判断逻辑存在潜在不一致

第 290 行要求 filters.startTime && filters.endTime 同时存在才将时间范围计为 1 个过滤器,但第 282 行的 hasStatsFilters 基于单个字段检查(使用 Object.values().some(v => v !== undefined && v !== false))。虽然 TimeFilters 组件的日期选择器确保两个字段通常成对设置/清除,但在 URL 参数被直接修改的情况下,可能出现仅 startTime 被设置而 endTime 未设置的状态,导致统计面板显示(hasStatsFilters = true)而 badge 计数为 0(activeFilterCount = 0)的视觉不一致。

同时,usage-logs-filters.tsx 中的 timeActiveCount(第 107-111 行)使用相同的逻辑,保持了一致性,但这种设计仍应明确文档说明或加强 URL 参数验证,确保时间范围始终成对出现。

验证结果:i18n 键 t("title.filterCriteria") 已存在于全部 5 种语言的 messages/[locale]/dashboard.json 中,无需修改。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
around lines 284 - 296, activeFilterCount currently only counts the time filter
when both filters.startTime and filters.endTime are present, which can mismatch
hasStatsFilters and timeActiveCount; change the time check in
usage-logs-view-virtualized.tsx (activeFilterCount) to treat a partial time
range as active (e.g., count if filters.startTime !== undefined ||
filters.endTime !== undefined), and also add symmetry by either updating the
timeActiveCount logic in usage-logs-filters.tsx or normalize URL/filters earlier
(coerce to both undefined if only one of filters.startTime/filters.endTime is
present) so both places use the same presence rule.

Half-width parentheses were introduced in ding113#798, breaking the
zh-tw-dashboard-parentheses test.
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.

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +133 to +135
function Separator() {
return <span className="text-border hidden sm:inline">|</span>;
}
Copy link

Choose a reason for hiding this comment

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

separator color should use proper border color variable

The separator uses text-border which applies the border color to text, creating a grey separator. However, for semantic correctness and proper theming, use text-muted-foreground or create a proper divider element with border styling.

Suggested change
function Separator() {
return <span className="text-border hidden sm:inline">|</span>;
}
function Separator() {
return <span className="text-muted-foreground/30 hidden sm:inline">|</span>;
}

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]/dashboard/logs/_components/usage-logs-stats-panel.tsx
Line: 133-135

Comment:
separator color should use proper border color variable

The separator uses `text-border` which applies the border color to text, creating a grey separator. However, for semantic correctness and proper theming, use `text-muted-foreground` or create a proper divider element with border styling.

```suggestion
function Separator() {
  return <span className="text-muted-foreground/30 hidden sm:inline">|</span>;
}
```

<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 +327 to +329
<ChevronDown
className={`ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200 ${isFiltersOpen ? "rotate-180" : ""}`}
/>
Copy link

Choose a reason for hiding this comment

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

avoid inline template literal for dynamic class

String interpolation with template literals for dynamic classes can break certain CSS tooling (like Tailwind's purge scanner in older configs). Use cn() utility or conditional class approach:

Suggested change
<ChevronDown
className={`ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200 ${isFiltersOpen ? "rotate-180" : ""}`}
/>
<ChevronDown
className={cn(
"ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200",
isFiltersOpen && "rotate-180"
)}
/>

You'll need to import cn from @/lib/utils.

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]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Line: 327-329

Comment:
avoid inline template literal for dynamic class

String interpolation with template literals for dynamic classes can break certain CSS tooling (like Tailwind's purge scanner in older configs). Use `cn()` utility or conditional class approach:

```suggestion
              <ChevronDown
                className={cn(
                  "ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200",
                  isFiltersOpen && "rotate-180"
                )}
              />
```

You'll need to import `cn` from `@/lib/utils`.

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

@NieiR NieiR force-pushed the fix/766-logs-page-layout branch from 5e39dca to 5e2c8d9 Compare February 21, 2026 00:48
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.

8 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

<Badge variant="secondary">{activeFilterCount}</Badge>
) : null}
<ChevronDown
className={`ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200 ${isFiltersOpen ? "rotate-180" : ""}`}
Copy link

Choose a reason for hiding this comment

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

template literal with string interpolation can break Tailwind's class detection in some tooling configurations; consider using conditional classes or cn() utility

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]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Line: 328

Comment:
template literal with string interpolation can break Tailwind's class detection in some tooling configurations; consider using conditional classes or `cn()` utility

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:UI bug Something isn't working size/M Medium PR (< 500 lines)

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant