Skip to content

feat: hide IP column by default and reuse VirtualizedLogsTable in my-usage page#1029

Merged
ding113 merged 2 commits into
devfrom
feat/ip-column-default-hidden-myusage-table
Apr 17, 2026
Merged

feat: hide IP column by default and reuse VirtualizedLogsTable in my-usage page#1029
ding113 merged 2 commits into
devfrom
feat/ip-column-default-hidden-myusage-table

Conversation

@ding113

@ding113 ding113 commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Summary

Hide the IP column by default in the request logs table (users can still toggle it via the column visibility dropdown), and replace the my-usage page's custom UsageLogsTable with the shared VirtualizedLogsTable component for consistent UX and reduced maintenance burden.

Problem

  1. IP column privacy/usability: PR feat: IP 记录 & 审计日志 & IP 归属地查询 #1027 introduced IP logging and display in the logs table. IP addresses should not be prominently displayed by default — they are sensitive data that most users don't need to see on every page load.

  2. Duplicated my-usage table logic: The my-usage page used a separate UsageLogsTable component with its own pagination, rendering, and auto-refresh logic, duplicating much of what VirtualizedLogsTable already handles. PR fix: stabilize usage log virtual scrolling #959 partially addressed this with virtual scrolling, but the my-usage page still had its own implementation rather than reusing the shared component.

Related PRs:

Solution

IP column hidden by default

  • Introduce a DEFAULT_HIDDEN_COLUMNS constant in column-visibility.ts (initially ["ip"])
  • When no user preference is stored, getHiddenColumns() returns the defaults instead of an empty array
  • Explicit user preferences (including reset-to-default) are persisted and always override the defaults
  • setHiddenColumns() now always writes to localStorage (even empty arrays) to distinguish "no preference" from "explicitly chose to show all"

my-usage page uses VirtualizedLogsTable

  • Replace the custom UsageLogsTable with the shared VirtualizedLogsTable component
  • User, Key, and Provider columns are always hidden (not applicable to self-service view)
  • Detail side-panel dialog is disabled (no decision chain or grouping info exposed to end users)
  • New getMyUsageLogsBatchFull server action scoped to the current key with allowReadOnlyAccess, returning the full UsageLogsBatchResult shape expected by VirtualizedLogsTable

VirtualizedLogsTable new props

  • fetchFn: custom data fetch function (defaults to getUsageLogsBatch)
  • queryKeyPrefix: custom react-query key prefix (defaults to "usage-logs-batch")
  • disableDetailDialog: renders a plain status badge without the Sheet trigger

Changes

File Change Lines
src/lib/column-visibility.ts Add DEFAULT_HIDDEN_COLUMNS, update getHiddenColumns/setHiddenColumns +7/-6
src/lib/column-visibility.test.ts Update tests for default-hidden behavior +26/-9
src/app/.../virtualized-logs-table.tsx Add fetchFn, queryKeyPrefix, disableDetailDialog props; extract StatusBadgeOnly +96/-47
src/app/.../usage-logs-section.tsx Replace UsageLogsTable with VirtualizedLogsTable; simplify filters and header +91/-187
src/app/.../usage-logs-section.test.tsx Rewrite test to verify VirtualizedLogsTable integration +34/-52
src/actions/my-usage.ts Add getMyUsageLogsBatchFull server action +27/-0

Testing

  • Unit tests updated: column-visibility.test.ts — verifies DEFAULT_HIDDEN_COLUMNS behavior, empty-array persistence, and toggle interactions with defaults
  • Unit tests rewritten: usage-logs-section.test.tsx — verifies VirtualizedLogsTable is rendered with correct hiddenColumns (user/key/provider), disableDetailDialog, custom fetchFn, and queryKeyPrefix
  • Manual testing: Verify IP column is hidden on first load of dashboard logs; toggle it visible via column dropdown; verify my-usage page shows virtualized table without user/key/provider columns and without detail dialog on status badge click

Description enhanced by Claude AI

Greptile Summary

This PR makes two focused improvements: it hides the IP column by default using a new DEFAULT_HIDDEN_COLUMNS constant in column-visibility.ts, and replaces the my-usage page's custom UsageLogsTable with the shared VirtualizedLogsTable by adding fetchFn, queryKeyPrefix, and disableDetailDialog props. A new getMyUsageLogsBatchFull server action scopes data to the current session's key and a shared parseDateRangeToTimestamps utility is extracted to @/lib/utils/date-range. The implementation is clean with no P0/P1 issues found.

Confidence Score: 5/5

Safe to merge; no P0 or P1 issues found. The IP-hiding default and my-usage VirtualizedLogsTable migration are both correctly implemented.

All findings are P2: a naming ambiguity for DEFAULT_VISIBLE_COLUMNS and a code-duplication opportunity between the extracted date-range utility and the older private helper in my-usage.ts. The authorization scoping in getMyUsageLogsBatchFull is correct (keyId forced from session, userId/keyId/providerId stripped at runtime), test coverage for the changed logic is thorough, and the serverTimeZone dependency in useMemo correctly recomputes tableFilters when the timezone loads.

No files require special attention.

Important Files Changed

Filename Overview
src/lib/column-visibility.ts Adds DEFAULT_HIDDEN_COLUMNS (["ip"]) and updates getHiddenColumns to return defaults when no preference stored; setHiddenColumns always writes to distinguish "no preference" from explicit empty; logic is correct.
src/lib/column-visibility.test.ts Tests comprehensively cover the new DEFAULT_HIDDEN_COLUMNS behavior, including empty-array persistence distinction and toggle interactions.
src/actions/my-usage.ts New getMyUsageLogsBatchFull server action correctly scopes to session.key.id and strips userId/keyId/providerId from client params as defense-in-depth; safe.
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx Adds fetchFn/queryKeyPrefix/disableDetailDialog props cleanly; extracts StatusBadgeOnly for the dialog-disabled path; STATUS_BADGE_FALLBACK constant simplifies getStatusBadgeClassName.
src/app/[locale]/my-usage/_components/usage-logs-section.tsx Replaces custom UsageLogsTable with VirtualizedLogsTable; fetches currencyCode/billingModelSource from getMyUsageMetadata; serverTimeZone is correctly plumbed as a useMemo dependency for date conversion.
src/lib/utils/date-range.ts New shared utility extracting date-to-timestamp conversion; correct regex validation, handles exclusive upper bound, defaults to UTC.
src/app/[locale]/my-usage/_components/usage-logs-section.test.tsx Tests verify VirtualizedLogsTable receives correct hiddenColumns, disableDetailDialog, fetchFn, and queryKeyPrefix; adequate coverage for the new integration.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[getHiddenColumns called] --> B{localStorage entry exists?}
    B -- No --> C[return DEFAULT_HIDDEN_COLUMNS copy]
    B -- Yes --> D[JSON.parse stored value]
    D --> E{Parse error?}
    E -- Yes --> C
    E -- No --> F[filter against all toggleable columns]
    F --> G[return filtered array]

    H[setHiddenColumns called] --> I[always write to localStorage\neven empty array]
    I --> J[distinguishes no-pref from show-all]

    K[resetColumns] --> L[setHiddenColumns with empty array]
    L --> M[next getHiddenColumns returns empty\nnot DEFAULT_HIDDEN_COLUMNS]

    subgraph my-usage page
        N[UsageLogsSection] --> O[VirtualizedLogsTable\nfetchFn=getMyUsageLogsBatchFull\nhiddenColumns=user,key,provider\ndisableDetailDialog=true]
        O --> P[getMyUsageLogsBatchFull\nserver action]
        P --> Q[strip userId/keyId/providerId\nfrom params]
        Q --> R[findUsageLogsBatch\nkeyId = session.key.id]
    end
Loading

Comments Outside Diff (2)

  1. src/lib/column-visibility.ts, line 76-78 (link)

    P2 Inconsistent fallback on parse error

    When localStorage contains corrupt data, the catch block returns [] (all columns visible), while the "no data stored" path returns [...DEFAULT_HIDDEN_COLUMNS] (IP hidden). A user whose storage is silently corrupted would see the IP column appear unexpectedly without ever having changed a preference.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/lib/column-visibility.ts
    Line: 76-78
    
    Comment:
    **Inconsistent fallback on parse error**
    
    When `localStorage` contains corrupt data, the `catch` block returns `[]` (all columns visible), while the "no data stored" path returns `[...DEFAULT_HIDDEN_COLUMNS]` (IP hidden). A user whose storage is silently corrupted would see the IP column appear unexpectedly without ever having changed a preference.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. src/lib/column-visibility.ts, line 147-149 (link)

    P2 Reset makes IP visible, overriding the new default

    resetColumns persists [], so getHiddenColumns returns [] afterward — IP becomes visible even though DEFAULT_HIDDEN_COLUMNS establishes IP as hidden for first-time users. Users clicking "Reset" may expect to return to the configured default (IP hidden), not an "all columns visible" state.

    If "reset to default" is the desired semantic, delete the stored key instead of writing []:

    If "show everything" is intentional (as described in the PR), consider renaming to showAllColumns or adding a comment to make the intent clear to future contributors.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/lib/column-visibility.ts
    Line: 147-149
    
    Comment:
    **Reset makes IP visible, overriding the new default**
    
    `resetColumns` persists `[]`, so `getHiddenColumns` returns `[]` afterward — IP becomes visible even though `DEFAULT_HIDDEN_COLUMNS` establishes IP as hidden for first-time users. Users clicking "Reset" may expect to return to the configured default (IP hidden), not an "all columns visible" state.
    
    If "reset to default" is the desired semantic, delete the stored key instead of writing `[]`:
    
    
    
    If "show everything" is intentional (as described in the PR), consider renaming to `showAllColumns` or adding a comment to make the intent clear to future contributors.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/lib/column-visibility.ts
Line: 27-37

Comment:
**`DEFAULT_VISIBLE_COLUMNS` name is misleading with `DEFAULT_HIDDEN_COLUMNS` now present**

`DEFAULT_VISIBLE_COLUMNS` includes `"ip"` but `"ip"` is also in `DEFAULT_HIDDEN_COLUMNS`, so this array actually represents all toggleable columns, not the columns that are visible by default. A future developer reading both constants side by side may assume `DEFAULT_VISIBLE_COLUMNS` describes the initial UI state. Consider renaming to `ALL_TOGGLEABLE_COLUMNS` (or similar) to clarify its role as a validation/enumeration list rather than a default-visibility declaration.

```suggestion
export const ALL_TOGGLEABLE_COLUMNS: LogsTableColumn[] = [
  "user",
  "key",
  "sessionId",
  "ip",
  "provider",
  "tokens",
  "cache",
  "performance",
  "cost",
];
```

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

---

This is a comment left during a code review.
Path: src/lib/utils/date-range.ts
Line: 1-31

Comment:
**Duplicate of `parseDateRangeInServerTimezone` still in `my-usage.ts`**

`parseDateRangeToTimestamps` was extracted as a shared utility, but the private `parseDateRangeInServerTimezone` function in `src/actions/my-usage.ts` (used by `getMyUsageLogs`, `getMyUsageLogsBatch`, and `getMyStatsSummary`) implements the same logic and also accepts `timezone` as a parameter. The two functions are equivalent and the older private helper could be replaced with this shared utility to eliminate the duplication.

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

Reviews (2): Last reviewed commit: "fix: address PR review comments" | Re-trigger Greptile

…usage page

- IP column is now hidden by default in the request logs table; users
  can toggle it via the column visibility dropdown
- my-usage page now uses the same VirtualizedLogsTable component as the
  dashboard logs page, with user/key/provider columns always hidden
- Detail side-panel dialog is disabled on my-usage page (no decision
  chain or grouping info exposed)
- New server action getMyUsageLogsBatchFull scoped to current key with
  allowReadOnlyAccess support
- VirtualizedLogsTable gains fetchFn, queryKeyPrefix, and
  disableDetailDialog props for reuse flexibility

Amp-Thread-ID: https://ampcode.com/threads/T-019d9b6e-07da-72af-8a57-3aee3dc20f44
Co-authored-by: Amp <amp@ampcode.com>
@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

新增 getMyUsageLogsBatchFull 后端操作,扩展 VirtualizedLogsTable 支持自定义 fetchFn/queryKeyPrefix/disableDetailDialog,UsageLogsSection 切换为该组件并使用日期范围解析,列可见性逻辑增加默认隐藏列并始终持久化设置。

Changes

Cohort / File(s) Summary
后端操作
src/actions/my-usage.ts
新增导出操作 getMyUsageLogsBatchFull(会话读取权限、授权失败返回、调用 findUsageLogsBatch 并强制使用 session.key.id),以及在 MyUsageMetadata 中加入 billingModelSource
虚拟化日志表组件
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
新增导出类型 LogsFetchFn,为组件添加 fetchFnqueryKeyPrefixdisableDetailDialog 属性;将 useInfiniteQuery 的 queryKey 改为基于 queryKeyPrefix,queryFn 使用传入或默认的批量获取函数;调整状态单元格渲染以支持仅徽章或带详情对话框的行为。
使用日志页面 UI
src/app/[locale]/my-usage/_components/usage-logs-section.tsx
将原先基于 useInfiniteQuery 的表格替换为 VirtualizedLogsTable,使用 getMyUsageLogsBatchFull 作为 fetchFn,新增 getMyUsageMetadata 调用与 parseDateRangeToTimestamps 转换,移除本地分页/聚合状态与旧的加载/错误逻辑,传入 hiddenColumns(隐藏 user/key/provider)与自动刷新设置。
测试更新
src/app/[locale]/my-usage/_components/usage-logs-section.test.tsx
移除对旧 infinite-query 及旧操作的模拟,改为模拟 getMyUsageLogsBatchFullgetMyUsageMetadata,并以虚拟化表格组件的 props 检查渲染与配置(包括 hiddenColumnsdisableDetailDialogfetchFnqueryKeyPrefix)。
列可见性逻辑与测试
src/lib/column-visibility.ts, src/lib/column-visibility.test.ts
新增导出常量 DEFAULT_HIDDEN_COLUMNS(包含 "ip");getHiddenColumns 在无存储或解析出错时返回默认隐藏列副本;setHiddenColumns 始终调用 localStorage.setItem 保存(即使为空数组);相应测试调整以匹配新行为。
工具函数
src/lib/utils/date-range.ts
新增 parseDateRangeToTimestamps(startDate?, endDate?, timezone?),将 YYYY-MM-DD 日期范围转换为时区敏感的 startTime/endTime 毫秒时间戳。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR 968: 与本次提交在 src/actions/my-usage.ts 上有直接重叠,包含 billingModelSource 与批量日志获取相关改动。
  • PR 966: 与 findUsageLogsBatch 及批量使用日志导出/获取接口有代码级重合,可能影响本次新增的 batch 调用。
  • PR 632: 修改过 VirtualizedLogsTable 的属性与隐藏列逻辑,与本次对该组件的 API 扩展和 hiddenColumns 用法直接相关。
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely describes the main changes: hiding the IP column by default and reusing VirtualizedLogsTable in the my-usage page, which directly aligns with the changeset.
Description check ✅ Passed The pull request description comprehensively explains the problem, solution, and changes made. It is directly related to the changeset and provides clear context about the rationale behind the modifications.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ip-column-default-hidden-myusage-table
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/ip-column-default-hidden-myusage-table

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 enhancement New feature or request area:UI size/L Large PR (< 1000 lines) labels Apr 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/lib/column-visibility.ts (1)

68-78: ⚠️ Potential issue | 🟡 Minor

JSON 解析失败时的回退与“无存储值”的回退不一致

localStorage 中不存在偏好时返回 [...DEFAULT_HIDDEN_COLUMNS](第 71 行),但当存储值损坏、JSON.parse 抛错时却回退到 [](第 77 行)。这意味着存储被污染的用户看到的默认可见列反而比全新用户更多(IP 列会显现),与本 PR 默认隐藏 IP 的意图相反。建议 catch 分支也返回 [...DEFAULT_HIDDEN_COLUMNS] 以保持一致。

建议修改
   } catch {
-    return [];
+    return [...DEFAULT_HIDDEN_COLUMNS];
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/column-visibility.ts` around lines 68 - 78, The fallback for
corrupted/malformed stored preferences is inconsistent: when no stored value the
code returns [...DEFAULT_HIDDEN_COLUMNS] but in the catch block it returns [] —
change the catch branch to return [...DEFAULT_HIDDEN_COLUMNS] instead so
JSON.parse errors yield the same default as missing storage; locate the function
that calls localStorage.getItem(getStorageKey(userId, tableId)), the
JSON.parse(...) and the catch block and update its catch return to use
DEFAULT_HIDDEN_COLUMNS (preserving the spread) while leaving the parsed.filter
logic that uses DEFAULT_VISIBLE_COLUMNS intact.
🧹 Nitpick comments (3)
src/app/[locale]/my-usage/_components/usage-logs-section.tsx (1)

55-79: 重复实现:parseDateRangesrc/actions/my-usage.tsparseDateRangeInServerTimezone 基本一致。

两边都在做「YYYY-MM-DD → 服务器时区起始/次日 0 点毫秒数」的转换,逻辑一致但各自实现,后续维护(例如时区回落策略、日期校验)容易发散。建议抽取到 @/lib/utils/timezone(或类似共享位置)并在两处复用。

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

In `@src/app/`[locale]/my-usage/_components/usage-logs-section.tsx around lines 55
- 79, Duplicate date-range parsing exists: extract the shared logic into a
common utility (e.g., export a function parseDateRangeInServerTimezone in a new
module like `@/lib/utils/timezone`) and replace both local implementations with
imports; ensure the utility implements the same behavior as the existing
parseDateRange and the existing parseDateRangeInServerTimezone (YYYY-MM-DD →
start-of-day ms and exclusive next-day ms in a given timezone), preserves the
timezone fallback behavior and input validation (the YYYY-MM-DD regex), and
update usages in src/app/[locale]/my-usage/_components/usage-logs-section.tsx
(parseDateRange) and src/actions/my-usage.ts (parseDateRangeInServerTimezone) to
call the new shared function.
src/actions/my-usage.ts (1)

649-666: 建议:对来自客户端的 params 做运行时白名单,避免依赖 Omit 的编译期保证。

Omit<UsageLogBatchFilters, "userId" | "keyId" | "providerId"> 仅在编译期生效。作为 Server Action,运行时的 params 来自客户端序列化数据,客户端完全可以额外传入 userId / providerId 字段,随 ...params 展开进入 findUsageLogsBatch。虽然随后 keyId: session.key.id 会将查询约束到当前用户自己的 Key(假设底层查询以 AND 语义组合条件),使得数据并不会越权泄露,但 userId / providerId 过滤条件仍会被生效,语义上超出接口声明的能力。建议显式解构白名单字段(或直接丢弃 userId/providerId)以与类型定义保持一致,增强防御性。

建议的改动
 export async function getMyUsageLogsBatchFull(
   params: Omit<UsageLogBatchFilters, "userId" | "keyId" | "providerId">
 ): Promise<ActionResult<UsageLogsBatchResult>> {
   try {
     const session = await getSession({ allowReadOnlyAccess: true });
     if (!session) return { ok: false, error: "Unauthorized" };

-    const result = await findUsageLogsBatch({
-      ...params,
-      keyId: session.key.id,
-    });
+    // 丢弃客户端可能伪造的越权字段,仅信任声明过的过滤条件
+    const { userId: _u, keyId: _k, providerId: _p, ...safeParams } =
+      params as UsageLogBatchFilters;
+    const result = await findUsageLogsBatch({
+      ...safeParams,
+      keyId: session.key.id,
+    });

     return { ok: true, data: result };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/my-usage.ts` around lines 649 - 666, The function
getMyUsageLogsBatchFull accepts client-provided params typed as
Omit<UsageLogBatchFilters, "userId" | "keyId" | "providerId"> but trusts the
runtime payload; to fix, explicitly whitelist and extract only the allowed
fields from params (e.g., page, limit, startDate, endDate, any other declared
filter fields) into a new sanitized object and do not pass through userId or
providerId, then call findUsageLogsBatch({ ...sanitizedParams, keyId:
session.key.id }); ensure you reference getMyUsageLogsBatchFull,
findUsageLogsBatch, UsageLogBatchFilters, and session.key.id when making the
change.
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx (1)

72-86: 小优化:判空条件冗余。

if (statusCode === null || !statusCode) 中,!statusCode 已覆盖 null/undefined/0,显式的 === null 可以去掉;末尾的 return 默认分支与第一条分支重复,可以直接把前置判空合并到末尾的 fallback 处以减少重复样式字符串。非阻塞。

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

In `@src/app/`[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
around lines 72 - 86, The getStatusBadgeClassName function has a redundant null
check and duplicated fallback branch; simplify by removing the initial if
(statusCode === null || !statusCode) branch and let the final return act as the
fallback for falsy values (null/undefined/0), keeping the existing 200–299,
400–499, and >=500 checks intact; update only the getStatusBadgeClassName
function so it checks the numeric ranges first and returns the shared gray
fallback string as the final return.
🤖 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/virtualized-logs-table.tsx:
- Around line 864-917: When disableDetailDialog is true the
ModelDisplayWithRedirect still receives onRedirectClick which only
setsDialogState and causes a silent no-op; change the rendering to omit the
onRedirectClick prop (or pass undefined) when disableDetailDialog is true so the
ModelDisplayWithRedirect renders non-interactive, and ensure cursor/aria
attributes reflect non-interactive state; likewise, apply the same conditional
removal for any ProviderChainPopover onChainItemClick usage to avoid
clickable-but-no-op elements (check symbols: disableDetailDialog,
ModelDisplayWithRedirect, onRedirectClick, setDialogState, ErrorDetailsDialog,
ProviderChainPopover).

---

Outside diff comments:
In `@src/lib/column-visibility.ts`:
- Around line 68-78: The fallback for corrupted/malformed stored preferences is
inconsistent: when no stored value the code returns [...DEFAULT_HIDDEN_COLUMNS]
but in the catch block it returns [] — change the catch branch to return
[...DEFAULT_HIDDEN_COLUMNS] instead so JSON.parse errors yield the same default
as missing storage; locate the function that calls
localStorage.getItem(getStorageKey(userId, tableId)), the JSON.parse(...) and
the catch block and update its catch return to use DEFAULT_HIDDEN_COLUMNS
(preserving the spread) while leaving the parsed.filter logic that uses
DEFAULT_VISIBLE_COLUMNS intact.

---

Nitpick comments:
In `@src/actions/my-usage.ts`:
- Around line 649-666: The function getMyUsageLogsBatchFull accepts
client-provided params typed as Omit<UsageLogBatchFilters, "userId" | "keyId" |
"providerId"> but trusts the runtime payload; to fix, explicitly whitelist and
extract only the allowed fields from params (e.g., page, limit, startDate,
endDate, any other declared filter fields) into a new sanitized object and do
not pass through userId or providerId, then call findUsageLogsBatch({
...sanitizedParams, keyId: session.key.id }); ensure you reference
getMyUsageLogsBatchFull, findUsageLogsBatch, UsageLogBatchFilters, and
session.key.id when making the change.

In `@src/app/`[locale]/dashboard/logs/_components/virtualized-logs-table.tsx:
- Around line 72-86: The getStatusBadgeClassName function has a redundant null
check and duplicated fallback branch; simplify by removing the initial if
(statusCode === null || !statusCode) branch and let the final return act as the
fallback for falsy values (null/undefined/0), keeping the existing 200–299,
400–499, and >=500 checks intact; update only the getStatusBadgeClassName
function so it checks the numeric ranges first and returns the shared gray
fallback string as the final return.

In `@src/app/`[locale]/my-usage/_components/usage-logs-section.tsx:
- Around line 55-79: Duplicate date-range parsing exists: extract the shared
logic into a common utility (e.g., export a function
parseDateRangeInServerTimezone in a new module like `@/lib/utils/timezone`) and
replace both local implementations with imports; ensure the utility implements
the same behavior as the existing parseDateRange and the existing
parseDateRangeInServerTimezone (YYYY-MM-DD → start-of-day ms and exclusive
next-day ms in a given timezone), preserves the timezone fallback behavior and
input validation (the YYYY-MM-DD regex), and update usages in
src/app/[locale]/my-usage/_components/usage-logs-section.tsx (parseDateRange)
and src/actions/my-usage.ts (parseDateRangeInServerTimezone) to call the new
shared function.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1886e7a-6222-4225-bda9-484d188d78d7

📥 Commits

Reviewing files that changed from the base of the PR and between 3746380 and 19a22ab.

📒 Files selected for processing (6)
  • src/actions/my-usage.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/my-usage/_components/usage-logs-section.test.tsx
  • src/app/[locale]/my-usage/_components/usage-logs-section.tsx
  • src/lib/column-visibility.test.ts
  • src/lib/column-visibility.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 19a22abf1d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +350 to +354
<VirtualizedLogsTable
filters={tableFilters}
hiddenColumns={MY_USAGE_HIDDEN_COLUMNS}
disableDetailDialog
fetchFn={myUsageFetchFn}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pass billing settings into VirtualizedLogsTable

The my-usage page now mounts VirtualizedLogsTable without currencyCode or billingModelSource, so it falls back to the component defaults (USD / original). In environments where system_settings.currencyDisplay or billing source is customized, this will render incorrect cost and model-billing context compared with the previous implementation that used server-resolved settings; users will see wrong billing information after this migration.

Useful? React with 👍 / 👎.

Comment thread src/actions/my-usage.ts
Comment on lines +656 to +659
const result = await findUsageLogsBatch({
...params,
keyId: session.key.id,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude warmup rows in my-usage batch fetch

This new action uses findUsageLogsBatch for my-usage, but that query path does not apply the warmup exclusion used by the previous key-scoped batch flow. As a result, when warmup interception is enabled, internal probe rows (blockedBy='warmup') can now appear in end-user my-usage logs even though they are not counted in quota/stats, creating inconsistent and confusing usage history.

Useful? React with 👍 / 👎.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the usage logs section in the "my-usage" page to utilize the shared VirtualizedLogsTable component, enhancing consistency and performance. It introduces a new server action, getMyUsageLogsBatchFull, and extends VirtualizedLogsTable with props for custom fetch functions, query keys, and a simplified status display. The PR also updates column visibility logic to support default hidden columns (specifically the IP column) and ensures that empty visibility preferences are explicitly persisted. Feedback highlights a potential regression where currency and billing model metadata are no longer dynamically passed to the table, and suggests a more explicit check for status codes in the badge rendering logic.

Comment on lines +350 to +358
<VirtualizedLogsTable
filters={tableFilters}
hiddenColumns={MY_USAGE_HIDDEN_COLUMNS}
disableDetailDialog
fetchFn={myUsageFetchFn}
queryKeyPrefix="my-usage-logs-batch"
autoRefreshEnabled={!!autoRefreshSeconds}
autoRefreshIntervalMs={autoRefreshSeconds ? autoRefreshSeconds * 1000 : undefined}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The VirtualizedLogsTable component is missing the currencyCode and billingModelSource props. Previously, these were dynamically retrieved from the log batch result. Without these props, the table will default to USD and original billing model, which may lead to incorrect data display for users with different settings. Please ensure these values are fetched (e.g., via getMyUsageMetadata) and passed to the table.

}

function getStatusBadgeClassName(statusCode: number | null): string {
if (statusCode === null || !statusCode) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The condition !statusCode will evaluate to true if statusCode is 0. While status code 0 usually indicates a network error or aborted request, it might be clearer to explicitly check for null or 0 if that is the intention, or just rely on statusCode === null if 0 should be handled differently.

Suggested change
if (statusCode === null || !statusCode) {
if (statusCode === null || statusCode === 0) {

- catch block in getHiddenColumns returns DEFAULT_HIDDEN_COLUMNS for
  consistency with missing-storage fallback
- getMyUsageLogsBatchFull strips userId/keyId/providerId from client
  params before spreading (defense-in-depth)
- getStatusBadgeClassName simplified with extracted fallback constant
  and proper null-safe checks
- onRedirectClick is undefined when disableDetailDialog is true to
  avoid clickable-but-no-op elements
- Extract parseDateRangeToTimestamps to shared @/lib/utils/date-range
- Pass currencyCode and billingModelSource to VirtualizedLogsTable in
  my-usage page via getMyUsageMetadata fetch

Amp-Thread-ID: https://ampcode.com/threads/T-019d9b6e-07da-72af-8a57-3aee3dc20f44
Co-authored-by: Amp <amp@ampcode.com>
@ding113
ding113 merged commit c1bc678 into dev Apr 17, 2026
8 of 9 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Claude Code Hub Roadmap Apr 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/app/[locale]/my-usage/_components/usage-logs-section.tsx (1)

95-101: 次要:getMyUsageMetadata 的 effect 缺少错误处理与竞态保护

当前对 getMyUsageMetadata() 的 Promise 只处理了成功分支,若请求 reject 会产生未捕获的 rejection;另外严格模式下 effect 会执行两次,虽依赖为 [] 不会重复触发业务问题,但如果未来新增依赖,缺少 "ignore on unmount" 标志可能导致旧响应覆盖新状态。可考虑补一个 .catchcancelled 标志保持与上方两个请求一致的健壮性。

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

In `@src/app/`[locale]/my-usage/_components/usage-logs-section.tsx around lines 95
- 101, The effect that calls getMyUsageMetadata() lacks rejection handling and a
cancellation guard: update the effect that invokes getMyUsageMetadata() so it
attaches a .catch(...) to log/handle errors and mirrors the other effects by
introducing a local "cancelled" (or "ignore") flag checked before calling
setCurrencyCode(...) and setBillingModelSource(...); ensure you flip the flag in
the cleanup function to prevent stale responses from updating state after
unmount.
src/lib/utils/date-range.ts (1)

20-28: 可简化:重复的正则与冗余的 if (match) 分支

第 20 行的 test 已确保 endDate 满足 YYYY-MM-DD,第 21 行再次 exec 后必然匹配,因此 if (match) 永远为真,可合并为一次 exec,并且可以直接基于字符串构造 next-day 而不走 Date.UTC。顺便也为 startDate 的无效日历日期(如 2024-02-30)保留了通过的可能,当前实现对非法日期会静默产生 NaN,若有需要可由调用方兜底。

♻️ 建议的简化
-  if (startDate && /^\d{4}-\d{2}-\d{2}$/.test(startDate)) {
-    startTime = fromZonedTime(`${startDate}T00:00:00`, tz).getTime();
-  }
-  if (endDate && /^\d{4}-\d{2}-\d{2}$/.test(endDate)) {
-    const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(endDate);
-    if (match) {
-      const next = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
-      next.setUTCDate(next.getUTCDate() + 1);
-      const nextStr = next.toISOString().slice(0, 10);
-      endTime = fromZonedTime(`${nextStr}T00:00:00`, tz).getTime();
-    }
-  }
+  const dateRe = /^(\d{4})-(\d{2})-(\d{2})$/;
+  if (startDate && dateRe.test(startDate)) {
+    startTime = fromZonedTime(`${startDate}T00:00:00`, tz).getTime();
+  }
+  const endMatch = endDate ? dateRe.exec(endDate) : null;
+  if (endMatch) {
+    const next = new Date(
+      Date.UTC(Number(endMatch[1]), Number(endMatch[2]) - 1, Number(endMatch[3]) + 1)
+    );
+    const nextStr = next.toISOString().slice(0, 10);
+    endTime = fromZonedTime(`${nextStr}T00:00:00`, tz).getTime();
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/utils/date-range.ts` around lines 20 - 28, The code duplicates regex
checking for endDate and has an unnecessary if (match); replace the two-step
test+exec with a single exec(/^(\\d{4})-(\\d{2})-(\\d{2})$/) call on endDate,
remove the redundant if branch, parse the captured year/month/day to construct a
Date for the day in UTC, add one day, convert to ISO date string and call
fromZonedTime to set endTime; additionally validate the resulting Date (isNaN)
and bail/skip setting endTime on invalid calendar dates to avoid producing NaN.
Reference symbols: endDate, endTime, fromZonedTime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/app/`[locale]/my-usage/_components/usage-logs-section.tsx:
- Around line 95-101: The effect that calls getMyUsageMetadata() lacks rejection
handling and a cancellation guard: update the effect that invokes
getMyUsageMetadata() so it attaches a .catch(...) to log/handle errors and
mirrors the other effects by introducing a local "cancelled" (or "ignore") flag
checked before calling setCurrencyCode(...) and setBillingModelSource(...);
ensure you flip the flag in the cleanup function to prevent stale responses from
updating state after unmount.

In `@src/lib/utils/date-range.ts`:
- Around line 20-28: The code duplicates regex checking for endDate and has an
unnecessary if (match); replace the two-step test+exec with a single
exec(/^(\\d{4})-(\\d{2})-(\\d{2})$/) call on endDate, remove the redundant if
branch, parse the captured year/month/day to construct a Date for the day in
UTC, add one day, convert to ISO date string and call fromZonedTime to set
endTime; additionally validate the resulting Date (isNaN) and bail/skip setting
endTime on invalid calendar dates to avoid producing NaN. Reference symbols:
endDate, endTime, fromZonedTime.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1629d85d-1311-419c-9c8a-357da34ef983

📥 Commits

Reviewing files that changed from the base of the PR and between 19a22ab and 5a083ec.

📒 Files selected for processing (7)
  • src/actions/my-usage.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/my-usage/_components/usage-logs-section.test.tsx
  • src/app/[locale]/my-usage/_components/usage-logs-section.tsx
  • src/lib/column-visibility.test.ts
  • src/lib/column-visibility.ts
  • src/lib/utils/date-range.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/app/[locale]/my-usage/_components/usage-logs-section.test.tsx
  • src/lib/column-visibility.test.ts
  • src/lib/column-visibility.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/actions/my-usage.ts

@github-actions

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

Comment thread src/actions/my-usage.ts Outdated
if (!session) return { ok: false, error: "Unauthorized" };

const result = await findUsageLogsBatch({
...params,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[High] [PERFORMANCE-ISSUE] getMyUsageLogsBatchFull forwards unvalidated limit

Why this is a problem: params.limit is forwarded directly into the DB query path. findUsageLogsBatch uses limit to compute fetchLimit = limit + 1 and then applies .limit(fetchLimit), so negative or very large limit values can trigger query errors or resource exhaustion.

Evidence:

  • src/actions/my-usage.ts: const result = await findUsageLogsBatch({ ...params, keyId: session.key.id, });
  • src/repository/usage-logs.ts: const { userId, keyId, providerId, cursor, limit = 50 } = filters;
  • src/repository/usage-logs.ts: const fetchLimit = limit + 1;
  • src/repository/usage-logs.ts: .limit(fetchLimit);

Suggested fix:

const resolvedLimit = params.limit && params.limit > 0 ? Math.min(params.limit, 100) : 50;

const result = await findUsageLogsBatch({
  ...params,
  limit: resolvedLimit,
  keyId: session.key.id,
});

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a083ec8cb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/actions/my-usage.ts
Comment on lines +659 to +662
const result = await findUsageLogsBatch({
...safeParams,
keyId: session.key.id,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Redact admin-only fields from my-usage batch response

getMyUsageLogsBatchFull now returns findUsageLogsBatch results verbatim, which include diagnostic fields like provider chain/error routing context and client metadata intended for the admin detail dialog. Although the my-usage UI disables that dialog, these fields are still delivered to the browser payload and can be inspected by end users, which is a data-exposure regression compared with the previous slim my-usage response.

Useful? React with 👍 / 👎.


const BATCH_SIZE = 20;
/** Columns always hidden on my-usage page (user/key/provider not available) */
const MY_USAGE_HIDDEN_COLUMNS: LogsTableColumn[] = ["user", "key", "provider"];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add IP to my-usage hidden columns

The migrated my-usage table only hides user/key/provider, so the shared table will render the IP column by default (hideIpColumn stays false). This exposes client IPs in my-usage where the old table did not show an IP column, and it also contradicts the privacy goal of making IP non-default visibility.

Useful? React with 👍 / 👎.

/>
<div className="rounded-lg border border-border/60 overflow-hidden">
<VirtualizedLogsTable
filters={tableFilters}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[High] [LOGIC-BUG] my-usage logs table defaults to USD currency formatting

Why this is a problem: VirtualizedLogsTable formats cost using its currencyCode prop, but UsageLogsSection does not pass one here. That means the table will always render with the component default (currencyCode = "USD"), which can diverge from the rest of the my-usage page when the system currency display is not USD.

Evidence:

  • src/app/[locale]/my-usage/_components/usage-logs-section.tsx: <VirtualizedLogsTable ... /> is rendered without currencyCode.
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx: default prop is currencyCode = "USD".

Suggested fix (one minimal option: load currency once and pass through):

// usage-logs-section.tsx
import { getMyUsageMetadata } from "@/actions/my-usage";
import type { CurrencyCode } from "@/lib/utils/currency";

const [currencyCode, setCurrencyCode] = useState<CurrencyCode>("USD");

useEffect(() => {
  void getMyUsageMetadata().then((result) => {
    if (result.ok) setCurrencyCode(result.data.currencyCode);
  });
}, []);

// ...
<VirtualizedLogsTable
  filters={tableFilters}
  currencyCode={currencyCode}
  hiddenColumns={MY_USAGE_HIDDEN_COLUMNS}
  disableDetailDialog
  fetchFn={myUsageFetchFn}
  queryKeyPrefix="my-usage-logs-batch"
  autoRefreshEnabled={!!autoRefreshSeconds}
  autoRefreshIntervalMs={autoRefreshSeconds ? autoRefreshSeconds * 1000 : undefined}
/>

useInfiniteQuery({
queryKey: ["usage-logs-batch", filters],
queryKey: [queryKeyPrefix, filters],
queryFn: async ({ pageParam }) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[Medium] [TEST-MISSING-CRITICAL] New VirtualizedLogsTable props are not covered by unit tests

Why this is a problem: This PR adds new behavioral branches (fetchFn, queryKeyPrefix, disableDetailDialog). Without tests that exercise these branches, regressions (wrong fetcher, cache key collisions, status cell behavior) are easy to introduce.

Evidence:

  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx: queryKey: [queryKeyPrefix, filters] and const fetcher = fetchFn ?? getUsageLogsBatch;
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx: disableDetailDialog ? <StatusBadgeOnly ... /> : <ErrorDetailsDialog ... />

Suggested fix (add focused unit tests):

// virtualized-logs-table.test.tsx

test("uses fetchFn and queryKeyPrefix when provided", async () => {
  mockIsLoading = false;
  mockIsError = false;
  mockError = null;
  mockHasNextPage = false;
  mockIsFetchingNextPage = false;
  mockLogs = [makeLog({ id: 1 })];
  useInfiniteQuerySpy.mockClear();

  const fetchFn = vi.fn().mockResolvedValue({
    ok: true,
    data: { logs: [], nextCursor: null, hasMore: false },
  });

  renderToStaticMarkup(
    <VirtualizedLogsTable
      filters={{}}
      autoRefreshEnabled={false}
      fetchFn={fetchFn}
      queryKeyPrefix="my-usage-logs-batch"
    />
  );

  const options = useInfiniteQuerySpy.mock.calls[0]?.[0] as {
    queryKey: unknown[];
    queryFn: (ctx: { pageParam?: unknown }) => Promise<unknown>;
  };

  expect(options.queryKey[0]).toBe("my-usage-logs-batch");
  await options.queryFn({ pageParam: undefined });
  expect(fetchFn).toHaveBeenCalledWith(expect.objectContaining({ limit: 50 }));
});

test("renders status badge only when disableDetailDialog=true", () => {
  mockIsLoading = false;
  mockIsError = false;
  mockError = null;
  mockHasNextPage = false;
  mockIsFetchingNextPage = false;
  mockLogs = [makeLog({ id: 1, statusCode: 200 })];

  const htmlWithDialog = renderToStaticMarkup(
    <VirtualizedLogsTable filters={{}} autoRefreshEnabled={false} />
  );
  expect(htmlWithDialog).toContain('data-slot="error-details-dialog"');

  const htmlNoDialog = renderToStaticMarkup(
    <VirtualizedLogsTable filters={{}} autoRefreshEnabled={false} disableDetailDialog />
  );
  expect(htmlNoDialog).not.toContain('data-slot="error-details-dialog"');
  expect(htmlNoDialog).toContain("200");
});

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

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 a new full-format my-usage logs fetch path and extends VirtualizedLogsTable with customizable fetching/query keys plus a status-only mode. Two high-risk gaps remain in the new my-usage wiring (limit validation and currency formatting), and the new VirtualizedLogsTable branches lack unit coverage.

PR Size: L

  • Lines changed: 582
  • Files changed: 6
  • Split suggestion: (1) column-visibility default behavior + tests, (2) VirtualizedLogsTable new props + tests, (3) my-usage page refactor + server action.

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 1 0 0
Security 0 1 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)

  • src/actions/my-usage.ts: getMyUsageLogsBatchFull forwards an unvalidated limit into findUsageLogsBatch (risk: query errors / resource exhaustion).
  • src/app/[locale]/my-usage/_components/usage-logs-section.tsx: my-usage VirtualizedLogsTable usage does not pass currencyCode, so cost formatting defaults to USD.
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx: new props (fetchFn, queryKeyPrefix, disableDetailDialog) are not exercised by unit tests.

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10)
  • Error handling
  • Type safety
  • Documentation accuracy
  • Test coverage
  • Code clarity

Automated review by Codex AI

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • Reviewed PR #1029 and applied label size/L.
  • Posted 3 inline comments (2 High, 1 Medium) on:
    • src/actions/my-usage.ts (unvalidated/clamp-less limit forwarded into findUsageLogsBatch)
    • src/app/[locale]/my-usage/_components/usage-logs-section.tsx (VirtualizedLogsTable cost formatting defaults to USD because currencyCode isn’t passed)
    • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx (new fetchFn / queryKeyPrefix / disableDetailDialog branches lack unit tests)
  • Submitted the required summary review via gh pr review --comment.

This was referenced Apr 18, 2026
@ding113
ding113 deleted the feat/ip-column-default-hidden-myusage-table branch May 13, 2026 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:UI enhancement New feature or request size/L Large PR (< 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant