Skip to content

perf(web): improve frontend table rendering and pinned columns/UI table - #5405

Merged
t0ng7u merged 39 commits into
mainfrom
perf/ui-table
Jun 10, 2026
Merged

perf(web): improve frontend table rendering and pinned columns/UI table#5405
t0ng7u merged 39 commits into
mainfrom
perf/ui-table

Conversation

@QuentinHsu

@QuentinHsu QuentinHsu commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

概述

  • 优化 web/default 前端表格体系,统一数据表格、静态表格和分页相关实现。
  • 改善模型定价、渠道、令牌、用户、订阅、兑换码、日志等列表页面的表格布局和滚动体验。

改动说明

  • 集中整理 data-table 公共组件、hook、分页组件和静态表格实现,减少业务页面重复逻辑。
  • 优化固定高度表格、分页状态复用、空状态行和列过滤相关实现。
  • 修复固定列背景透明导致滚动内容透出的样式问题。
  • 清理未使用导出和过期 lint override,并移除 code-block 对未声明 hast 类型的直接依赖。

效果

  • 表格公共 API 更收敛,后续维护成本更低。
  • 固定列、分页和列表查看体验更稳定。
  • 已验证 bun run typecheck 通过。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • Closes # (如有)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

(请在此粘贴截图、关键日志或测试报告,以证明变更生效)

Summary by CodeRabbit

  • New Features

    • New shared data-table components, hooks, and utilities for consistent table rendering and sizing.
    • Section pages gain a fixedContent layout option for improved scrolling.
  • Refactor

    • Replaced many bespoke table implementations across the app with standardized data-table components and centralized exports; pages/dialogs now use shared pagination and row/view abstractions.
  • Documentation

    • Added README for the data-table component set.
  • Localization

    • Added "Go to page" and "Received amount" translations for multiple locales.
  • UI

    • Status badges no longer show a leading dot by default; provider-style badges introduced.

QuentinHsu added 25 commits June 9, 2026 14:52
- route all TanStack table setup through a shared data-table hook to remove repeated state and row model wiring.
- move table rendering, static table wrappers, empty states, and primitive exports behind the data-table module.
- update feature tables and configuration editors to share the same table UX while preserving their existing workflows.
- remove unused data-table exports and dead static table helper types.
- keep internal table header, skeleton, empty state, and faceted filter helpers private to the data-table module.
- route feature imports through the data-table barrel to avoid subpath coupling.
- centralize static table headers, bodies, empty states, and shared class names behind the data-table package.
- migrate settings, pricing, channel, key, subscription, and model tables to the shared table APIs.
- remove data-table exports for low-level table primitives so feature code uses one supported abstraction.
- make shared data table pages fill available height and scroll row data inside the table body.
- add a fixed content layout mode so selected list pages avoid page-level scrolling.
- apply the fixed table behavior to keys, logs, channels, models, users, redemptions, and subscriptions.
- show total row counts instead of redundant page range text.
- tighten visible page buttons so pagination fits constrained table widths.
- align pagination controls and tune text hierarchy for clearer scanning.
- keep model pricing columns at fixed widths so headers do not collapse in narrow layouts.
- truncate long model names and pricing summaries within their cells instead of squeezing adjacent columns.
- split table body rendering into focused helpers for loading, empty, and row states.
- extract static table row and cell class resolution to reduce branching in the main component.
- reuse a single pagination page-size option list to avoid duplicated constants.
- reuse dynamic pricing field metadata instead of rebuilding it inside table columns.
- precompute formatted dynamic prices per tier and group to avoid repeated entry mapping for each cell.
- simplify select option construction in related dialogs while preserving the same choices.
- reuse translated endpoint select options between trigger data and menu items.
- precompute dynamic pricing maps per group so table cells only resolve formatted values.
- add local dynamic pricing type aliases to keep helper signatures readable.
- rely on Shiki transformer contextual typing for line nodes.
- allow frontend typecheck to pass without an undeclared hast package.
- return only the TanStack table instance from useDataTable.
- keep internal state handling private because callers do not consume it directly.
- stop exporting the internal StaticDataTableEmptyRow helper.
- keep the public static table API focused on the table component and column type.
- apply pinned column background classes after custom column classes.
- use an opaque hover background so scrolled content cannot show through fixed cells.
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa79a14a-bf01-4981-9a55-9c734ee822d6

📥 Commits

Reviewing files that changed from the base of the PR and between c57009f and 8a44183.

📒 Files selected for processing (19)
  • web/default/src/components/data-table/hooks/use-debounced-column-filter.ts
  • web/default/src/components/data-table/toolbar/toolbar.tsx
  • web/default/src/components/masked-value-display.tsx
  • web/default/src/components/provider-badge.tsx
  • web/default/src/components/status-badge.tsx
  • web/default/src/components/ui/table.tsx
  • web/default/src/features/channels/components/channels-columns.tsx
  • web/default/src/features/channels/components/channels-table.tsx
  • web/default/src/features/keys/components/api-keys-cells.tsx
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/redemption-codes/components/redemptions-columns.tsx
  • web/default/src/features/subscriptions/components/dialogs/user-subscriptions-dialog.tsx
  • web/default/src/features/subscriptions/components/subscriptions-columns.tsx
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
✅ Files skipped from review due to trivial changes (4)
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/components/masked-value-display.tsx
  • web/default/src/i18n/locales/ru.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • web/default/src/features/subscriptions/components/dialogs/user-subscriptions-dialog.tsx
  • web/default/src/components/status-badge.tsx
  • web/default/src/i18n/locales/vi.json
  • web/default/src/features/keys/components/api-keys-cells.tsx
  • web/default/src/i18n/locales/ja.json
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/channels/components/channels-table.tsx

Walkthrough

Adds a shared data-table core (types, DataTableView, DataTableRow/header/colgroup, pinning, sizing, pagination), hooks (useDataTable, useDebouncedColumnFilter), StaticDataTable + classnames, updates barrel exports and README, and migrates many pages/dialogs/settings from manual Table/useReactTable to the new abstractions; layout and locale tweaks included.

Changes

Unified Table Abstraction

Layer / File(s) Summary
Core table primitives and types
web/default/src/components/data-table/core/types.ts, core/data-table-view.tsx, core/data-table-row.tsx, core/data-table-header.tsx, core/data-table-colgroup.tsx, core/column-pinning.ts, core/table-sizing.ts, core/pagination.tsx
Introduces DataTableView (unified and split-header layouts), DataTableRow, DataTableHeader/Colgroup, pinned-column helpers, table sizing, pagination constants/select items, and public core types.
Hooks, static table, classnames and barrel exports
web/default/src/components/data-table/hooks/use-data-table.ts, hooks/use-debounced-column-filter.ts, static/static-data-table.tsx, static/static-data-table-classnames.ts, index.ts, README.md
Adds useDataTable (controllable state + TanStack wiring), useDebouncedColumnFilter (IME-aware debounced input handlers), StaticDataTable and its classname map, updates data-table barrel exports and README.
DataTablePage refactor and SectionPageLayout fixedContent
web/default/src/components/data-table/layout/data-table-page.tsx, web/default/src/components/layout/components/section-page-layout.tsx
DataTablePage delegates desktop rendering to DataTableView, centralizes pagination rendering, adds fixedHeight/getColumnClassName/pinnedColumns/renderRow helpers; SectionPageLayout adds fixedContent?: boolean to control content overflow behavior.
List pages → useDataTable migrations
web/default/src/features/channels/components/channels-table.tsx, web/default/src/features/keys/components/api-keys-table.tsx, web/default/src/features/models/components/models-table.tsx, web/default/src/features/models/components/deployments-table.tsx, web/default/src/features/redemption-codes/components/redemptions-table.tsx, web/default/src/features/subscriptions/components/subscriptions-table.tsx, web/default/src/features/users/components/users-table.tsx, web/default/src/features/usage-logs/components/usage-logs-table.tsx, web/default/src/features/pricing/components/pricing-table.tsx
List tables migrate from useReactTable to useDataTable, removing local sorting/visibility/selection state and effect-based ensurePageInRange; useDebouncedColumnFilter replaces manual debounce/effect filter wiring.
Dialog/editor migrations to DataTableView
web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx, web/default/src/features/models/components/dialogs/upstream-conflict-dialog.tsx, web/default/src/features/system-settings/models/channel-selector-dialog.tsx, web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
Dialogs refactored to useDataTable + DataTableView; selection and pagination preserved; endpoint select memoized and PAGE_SIZE_OPTIONS centralized where applied.
StaticDataTable migrations across settings and dialogs
web/default/src/features/channels/components/dialogs/multi-key-manage-dialog.tsx, web/default/src/features/models/components/dialogs/prefill-group-management-dialog.tsx, web/default/src/features/system-settings/models/conflict-confirm-dialog.tsx, web/default/src/features/system-settings/auth/custom-oauth/components/provider-table.tsx, web/default/src/features/subscriptions/components/dialogs/user-subscriptions-dialog.tsx, web/default/src/features/system-settings/content/announcements-section.tsx, web/default/src/features/system-settings/content/api-info-section.tsx, web/default/src/features/system-settings/content/chat-settings-visual-editor.tsx, web/default/src/features/system-settings/content/faq-section.tsx, web/default/src/features/system-settings/content/uptime-kuma-section.tsx, web/default/src/features/system-settings/general/channel-affinity/index.tsx, web/default/src/features/system-settings/integrations/*-visual-editor.tsx, web/default/src/features/system-settings/models/group-ratio-visual-editor.tsx, web/default/src/features/system-settings/models/tool-price-settings.tsx, web/default/src/features/system-settings/request-limits/rate-limit-visual-editor.tsx
Replaces manual Table markup with StaticDataTable configured via data, getRowKey, emptyContent, and columns; preserves existing actions, selection, and formatting via column cell renderers.
Pricing & model details conversion
web/default/src/features/pricing/components/dynamic-pricing-breakdown.tsx, web/default/src/features/pricing/components/model-details-api.tsx, web/default/src/features/pricing/components/model-details-apps.tsx, web/default/src/features/pricing/components/model-details-modalities.tsx, web/default/src/features/pricing/components/model-details-performance.tsx, web/default/src/features/pricing/components/model-details.tsx, web/default/src/features/pricing/components/pricing-columns.tsx
Pricing and model-detail views moved to StaticDataTable; model-details adds helpers to compute dynamic price fields and formattedPricesByTier maps, with dynamic columns generated per model/group.
Feature index pages adopt fixedContent layout
web/default/src/features/channels/index.tsx, web/default/src/features/keys/index.tsx, web/default/src/features/models/index.tsx, web/default/src/features/redemption-codes/index.tsx, web/default/src/features/subscriptions/index.tsx, web/default/src/features/usage-logs/index.tsx, web/default/src/features/users/index.tsx
Index pages now pass fixedContent to SectionPageLayout and use flex column containers (flex h-full min-h-0 flex-col gap-4) with inner min-h-0 flex-1 wrappers for full-height table layouts.
Column sizing, presentation, provider/status badges, and small cleanups
web/default/src/features/keys/components/api-keys-columns.tsx, web/default/src/features/models/components/models-columns.tsx, web/default/src/features/redemption-codes/components/redemptions-columns.tsx, web/default/src/features/system-settings/models/model-ratio-table-columns.tsx, web/default/src/features/users/components/users-columns.tsx, web/default/src/components/status-badge.tsx, web/default/src/components/provider-badge.tsx, web/default/src/components/long-text.tsx, web/default/src/components/ai-elements/code-block.tsx, web/default/src/lib/utils.ts, web/default/src/i18n/locales/*
Adds explicit size values to many columns, introduces ProviderBadge, sets StatusBadge default showDot to false and adds data-slot, removes a few eslint/type annotations/comments, updates timestamp truncation/classnames, and adds pagination/Received amount i18n entries across locales.
Usage-logs mobile card formatting
web/default/src/features/usage-logs/components/usage-logs-mobile-card.tsx
Reorders Tailwind class tokens and consolidates className compositions in mobile card JSX without functional changes.

Estimated code review effort: 🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • Calcium-Ion
  • seefs001
  • panjinhe

"A rabbit hops through table lands so fine,
Shared abstractions now in each design,
Fixed heights and rows in columns arranged,
Pagination options neatly exchanged. 🐰✨"

✨ 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 perf/ui-table

- group table primitives, page composition, toolbar controls, static tables, and hooks by responsibility.
- split shared view types, row rendering, header rendering, and pinned-column styling out of the main table view.
- keep the public data-table barrel stable while documenting the new ownership boundaries.

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

Actionable comments posted: 13

Caution

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

⚠️ Outside diff range comments (2)
web/default/src/features/keys/components/api-keys-table.tsx (1)

194-214: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Status filtering is still page-local on a server-paginated table.

Line 200 registers a status filter, but the query never includes it and the migrated useDataTable call leaves filtering client-side. With manualPagination and a server total, the dropdown only filters the current page, so totals/page navigation stay wrong and filtered pages can appear empty. Either send status to getApiKeys/searchApiKeys and set manualFiltering: true, or remove the status filter until the backend supports it.

Suggested direction
+ const statusFilter =
+   (columnFilters.find((f) => f.id === 'status')?.value as string[]) || []
+ const activeStatus =
+   statusFilter.length > 0 && !statusFilter.includes('all')
+     ? statusFilter[0]
+     : undefined
+
   const { data, isLoading, isFetching } = useQuery({
     queryKey: [
       'keys',
       pagination.pageIndex + 1,
       pagination.pageSize,
       globalFilter,
       tokenFilter,
+      activeStatus,
       refreshTrigger,
     ],
     queryFn: async () => {
       const result = shouldSearch
         ? await searchApiKeys({
             keyword: globalFilter,
             token: tokenFilter,
+            status: activeStatus,
             p: pagination.pageIndex + 1,
             size: pagination.pageSize,
           })
         : await getApiKeys({
+            status: activeStatus,
             p: pagination.pageIndex + 1,
             size: pagination.pageSize,
           })
   const { table } = useDataTable({
     data: apiKeys,
     columns,
     enableRowSelection: true,
     columnFilters,
     globalFilter,
     pagination,
     globalFilterFn: () => true,
     onPaginationChange,
     onGlobalFilterChange,
     onColumnFiltersChange,
     manualPagination: true,
+    manualFiltering: true,
     totalCount: data?.total || 0,
     ensurePageInRange,
   })

Also applies to: 218-276

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/keys/components/api-keys-table.tsx` around lines 194
- 214, The status column filter registered in useTableUrlState (columnId:
'status') is still applied client-side because the useDataTable call was
migrated without enabling server-side filtering; as a result status only filters
the current page. Fix by wiring the status filter into the server query (pass
the status value from columnFilters or the derived state into
getApiKeys/searchApiKeys) and enable manualFiltering: true (or remove the status
filter entirely until backend support exists). Update the code paths around
useTableUrlState, useDebouncedColumnFilter (for _tokenSearch/tokenFilter) and
the useDataTable invocation so the server receives { status } alongside
pagination and globalFilter when shouldSearch is true.
web/default/src/features/redemption-codes/components/redemptions-table.tsx (1)

79-89: ⚠️ Potential issue | 🟠 Major

Keep useDataTable in manual (server) pagination mode when using globalFilter

redemptions-table.tsx always requests a single server page (p / page_size) for both getRedemptions and searchRedemptions, but useDataTable currently flips to client pagination with manualPagination: !globalFilter—so when globalFilter is set, the table can re-slice an already server-paged dataset and effectively hide later result pages.

Suggested fix
-    manualPagination: !globalFilter,
+    manualPagination: true,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/redemption-codes/components/redemptions-table.tsx`
around lines 79 - 89, The table flips to client-side pagination when a
globalFilter exists causing server-paged results to be re-sliced; keep
useDataTable in manual (server) pagination mode whenever server endpoints are
used. Update the useDataTable call so manualPagination is true when using
getRedemptions/searchRedemptions (use manualPagination: true instead of
manualPagination: !globalFilter), and ensure the queryFn (which calls
getRedemptions/searchRedemptions using pagination and p/page_size) continues to
request the correct server page parameters (pagination, globalFilter) so the
table relies on server paging rather than re-slicing results.
🧹 Nitpick comments (7)
web/default/src/features/pricing/components/model-details.tsx (1)

711-714: ⚡ Quick win

Use nullish fallback for group ratios instead of truthy fallback.

|| 1 treats 0 as missing, which can silently change configured zero-multiplier pricing to 1x in rendered tables.

Suggested fix
-        const ratio = props.groupRatio[group] || 1
+        const ratio = props.groupRatio[group] ?? 1
...
-            const ratio = props.groupRatio[group] || 1
+            const ratio = props.groupRatio[group] ?? 1
...
-            cell: (group) => `${props.groupRatio[group] || 1}x`,
+            cell: (group) => `${props.groupRatio[group] ?? 1}x`,

Also applies to: 829-829

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/pricing/components/model-details.tsx` around lines
711 - 714, The code uses a truthy fallback when reading group multipliers which
treats 0 as missing (e.g., in formattedPricesByGroup): replace uses of
"props.groupRatio[group] || 1" with a nullish-coalescing fallback so only
null/undefined fall back to 1 (use "props.groupRatio[group] ?? 1"); update the
same pattern wherever groupRatio is read (including the other occurrence around
the block referenced) to preserve explicit zero multipliers.
web/default/src/components/data-table/static-data-table.tsx (2)

59-74: ⚡ Quick win

Avoid destructuring component props in TSX components.

Both components destructure props directly; switch to props.xxx access for consistency with project conventions.

Proposed refactor
-export function StaticDataTable<TData = unknown>({
-  children,
-  columns,
-  data,
-  getRowKey,
-  getRowClassName,
-  renderRow,
-  empty,
-  emptyContent,
-  emptyClassName,
-  headerRowClassName,
-  className,
-  tableClassName,
-  containerProps,
-  tableProps,
-}: StaticDataTableProps<TData>) {
+export function StaticDataTable<TData = unknown>(
+  props: StaticDataTableProps<TData>
+) {
+  const children = props.children
+  const columns = props.columns
+  const data = props.data
+  const getRowKey = props.getRowKey
+  const getRowClassName = props.getRowClassName
+  const renderRow = props.renderRow
+  const empty = props.empty
+  const emptyContent = props.emptyContent
+  const emptyClassName = props.emptyClassName
+  const headerRowClassName = props.headerRowClassName
+  const className = props.className
+  const tableClassName = props.tableClassName
+  const containerProps = props.containerProps
+  const tableProps = props.tableProps
   // ...
 }

As per coding guidelines, “Do not destructure component props; use props.xxx directly instead for clarity”.

Also applies to: 174-178

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/static-data-table.tsx` around lines 59
- 74, The StaticDataTable function currently destructures props in its parameter
list; change the signature to accept a single props parameter (e.g., props:
StaticDataTableProps<TData>) and update all uses to props.children,
props.columns, props.data, props.getRowKey, props.getRowClassName,
props.renderRow, props.empty, props.emptyContent, props.emptyClassName,
props.headerRowClassName, props.className, props.tableClassName,
props.containerProps, and props.tableProps instead of the destructured names.
Apply the same refactor to the other component in this file that destructures
props (the one around lines 174-178) so both components follow the project's
convention of using props.xxx access.

Source: Coding guidelines


86-112: ⚡ Quick win

Replace the 2-level nested ternary with explicit branching.

The current nested ternary hurts readability and violates the project rule; use if/else (or extracted helpers) for content composition.

Proposed refactor
-  const content = columns ? (
-    <>
-      <TableHeader>...</TableHeader>
-      <TableBody>
-        {isEmpty ? (
-          <StaticDataTableEmptyRow ...>{emptyContent}</StaticDataTableEmptyRow>
-        ) : (
-          bodyRows
-        )}
-      </TableBody>
-    </>
-  ) : (
-    bodyRows
-  )
+  let content: React.ReactNode
+  if (!columns) {
+    content = bodyRows
+  } else {
+    const bodyContent = isEmpty ? (
+      <StaticDataTableEmptyRow colSpan={columns.length} className={emptyClassName}>
+        {emptyContent}
+      </StaticDataTableEmptyRow>
+    ) : (
+      bodyRows
+    )
+
+    content = (
+      <>
+        <TableHeader>...</TableHeader>
+        <TableBody>{bodyContent}</TableBody>
+      </>
+    )
+  }

As per coding guidelines, “Prohibit nested ternary expressions with 2 or more levels; use if-else, early returns, or extract functions instead”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/static-data-table.tsx` around lines 86
- 112, The definition of the JSX variable "content" uses a nested ternary
(columns ? ... : bodyRows with an inner isEmpty ? ... : bodyRows) which reduces
readability; replace this with explicit branching by computing "content" with an
if/else (or extracting a helper function) that first checks "columns" and
returns the header/body JSX when present, and otherwise returns "bodyRows"; when
columns exist, build the TableHeader/TableBody block and inside it use an
if/else on "isEmpty" to choose between rendering StaticDataTableEmptyRow
(passing colSpan={columns.length} and emptyClassName) or "bodyRows"; keep the
same props and keys (column.id, column.className, column.header,
headerRowClassName) but eliminate the nested ternary.

Source: Coding guidelines

web/default/src/components/data-table/use-debounced-column-filter.ts (1)

30-35: ⚡ Quick win

Add an explicit return type for the exported hook.

The exported API currently relies on inference; define a result type and annotate the hook return type explicitly.

Proposed refactor
+type UseDebouncedColumnFilterResult = {
+  value: string
+  inputValue: string
+  setInputValue: React.Dispatch<React.SetStateAction<string>>
+}
+
 export function useDebouncedColumnFilter({
   columnFilters,
   columnId,
   onColumnFiltersChange,
   delay = 500,
-}: UseDebouncedColumnFilterOptions) {
+}: UseDebouncedColumnFilterOptions): UseDebouncedColumnFilterResult {

As per coding guidelines, “Explicitly annotate parameter and return value types”.

Also applies to: 60-65

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/use-debounced-column-filter.ts` around
lines 30 - 35, The hook useDebouncedColumnFilter currently relies on inferred
return types; add an explicit return type annotation (e.g., define and export an
interface/type for the hook result and annotate useDebouncedColumnFilter(...):
YourReturnType) and do the same for the other exported hook mentioned around the
60-65 range; reference the existing UseDebouncedColumnFilterOptions parameter
type when creating the result type to ensure properties returned (callbacks,
state, cleanup) are correctly typed and update the function signature to use
that explicit return type.

Source: Coding guidelines

web/default/src/features/system-settings/general/channel-affinity/index.tsx (1)

542-652: ⚡ Quick win

Consider adding getRowKey for stable React keys.

This StaticDataTable does not provide a getRowKey prop, which may cause React to fall back to array indices for keys. For consistency with similar tables in this PR (e.g., amount-discount-visual-editor, creem-products-visual-editor) and to ensure stable keys if rules are reordered, add getRowKey={(rule) => rule.id ?? rule.name ?? ''} or similar.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/system-settings/general/channel-affinity/index.tsx`
around lines 542 - 652, The table lacks a stable row key which can cause React
to use array indices; add a getRowKey prop to the StaticDataTable (the component
instance rendering rules) that returns a stable identifier for each rule, e.g. a
function that chooses rule.id if present, otherwise rule.name, otherwise an
empty string; update the StaticDataTable props near the columns prop so
getRowKey is used consistently with other tables like
amount-discount-visual-editor.
web/default/src/features/system-settings/integrations/waffo-settings-section.tsx (1)

329-396: ⚡ Quick win

Consider adding getRowKey for stable React keys.

Similar to the channel-affinity table, this StaticDataTable lacks a getRowKey prop. While array indices may work for this stable list, providing an explicit key function (e.g., getRowKey={(method, idx) => ${method.payMethodType}-${method.payMethodName}-${idx}}) ensures consistency with other StaticDataTable usages in this PR and avoids potential React key warnings if the array is reordered.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/system-settings/integrations/waffo-settings-section.tsx`
around lines 329 - 396, The StaticDataTable rendering payMethods should supply a
stable getRowKey to avoid React key instability; update the StaticDataTable
props (where StaticDataTable is used with data={payMethods}) to add getRowKey
that returns a unique string per row (for example combining payMethodType and
payMethodName and index) so keys remain stable during reorder and match other
usages in this PR; keep existing handlers (openEdit, onPayMethodsChange)
unchanged.
web/default/src/features/system-settings/models/conflict-confirm-dialog.tsx (1)

69-104: ⚡ Quick win

Consider adding getRowKey for stable React keys.

This StaticDataTable does not specify a getRowKey prop. While conflict items are likely stable during the dialog's lifetime, adding getRowKey={(conflict, idx) => ${conflict.channel}-${conflict.model}-${idx}} aligns with best practices demonstrated elsewhere in this PR and prevents potential React key warnings.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/system-settings/models/conflict-confirm-dialog.tsx`
around lines 69 - 104, The StaticDataTable usage lacks a getRowKey prop which
can cause unstable React keys; add getRowKey to the StaticDataTable invocation
(the one rendering data={conflicts}) and return a stable key using the row
values and index, e.g. a template combining conflict.channel, conflict.model and
idx (referencing the conflicts array and the conflict parameter) so keys are
stable across renders and avoid React key warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/default/src/components/data-table/data-table-view.tsx`:
- Around line 333-341: Pinned columns all receive a fixed "left-0"/"right-0" so
multiple pinned columns on the same side overlap; change the positioning logic
in data-table-view by computing a per-column offset (e.g., derive a cumulative
width for each pinned column from the pinned columns array) and use that offset
instead of "left-0"/"right-0" when rendering pinnedColumn: update the code that
builds edgeClassName and the cn(...) call to remove the hardcoded left-0/right-0
and instead apply a dynamic inline style or CSS variable using the computed
offset (use the pinnedColumn identifier or a helper like
getPinnedOffset(pinnedColumn) to locate where to compute offsets and apply
style).
- Around line 256-263: The custom-row branch in rows.map calls
props.renderRow(...) but doesn't ensure a stable React key, which can break
reconciliation; update the rows.map logic where props.renderRow is used to
ensure the returned element has a key derived from the row (e.g., row.id ||
row._id || row.key) and fallback to the row index only if none exists, by
wrapping or cloning the node returned from props.renderRow(row, {...}) to inject
that key; keep renderDefaultRow(path) unchanged and continue using
getColumnClassName for class names.

In `@web/default/src/components/data-table/pagination.tsx`:
- Line 137: The screen-reader-only label "Go to page {pageNumber}" is hardcoded;
update the pagination component to use useTranslation() and replace the literal
with t('pagination.goToPage', { page: pageNumber }) (or similar key) so the SR
text is localized; locate the span with className 'sr-only' in the pagination
component (e.g., Pagination or DataTablePagination) and change its content to
the translated string, ensuring the t() import and hook call are added if not
already present and the interpolation key matches your i18n resource bundle.

In
`@web/default/src/features/models/components/dialogs/prefill-group-management-dialog.tsx`:
- Around line 360-362: The new desktop table in PrefillGroupManagementDialog
contains hardcoded user-facing strings (e.g., "No description provided", the
empty-state item text, and the sr-only action labels) so update the component to
use the i18n hook and wrap these literals with t(...); import/use
useTranslation() and replace the hardcoded texts in the table rows and action
labels (inside PrefillGroupManagementDialog / the desktop table render
functions) with calls like t('...') using appropriate translation keys to
maintain locale behavior.

In
`@web/default/src/features/models/components/dialogs/upstream-conflict-dialog.tsx`:
- Around line 333-343: The table's selection is bound to the full conflictRows
model while the UI renders only paginatedRows, so the header checkbox can select
hidden rows; fix by wiring the table to the same row set being rendered—call
useDataTable with the paginatedRows (or enable TanStack pagination/filtering by
setting withPaginationRowModel/withFilteredRowModel true) so selection state and
getRowId in useDataTable match what is displayed, and ensure
handleApplyOverwrite reads from the table's current selection (rowSelection)
rather than the full conflictRows list.

In `@web/default/src/features/pricing/components/dynamic-pricing-breakdown.tsx`:
- Around line 309-313: The desktop row-matching uses normalizedMatchedTierLabel
and normalizeTierLabel(tier.label) but the mobile match branch still uses strict
equality; update the mobile matching logic (the code that compares tier.label
for mobile) to use the same normalization strategy—i.e., compute
normalizeTierLabel(tier.label) and compare it to normalizedMatchedTierLabel (or
normalize both sides) so getRowClassName and the mobile matcher use the same
normalizeTierLabel function and normalizedMatchedTierLabel variable for
consistency.

In `@web/default/src/features/pricing/components/pricing-table.tsx`:
- Around line 97-103: The row currently only handles mouse clicks (renderRow ->
DataTableRow with onClick={() => handleRowClick(row.original)}) and shows
pointer styling even when non-interactive; make rows keyboard-accessible and
only show pointer when actionable by: add tabIndex={0}, role="button", and an
onKeyDown handler on DataTableRow that calls handleRowClick(row.original) when
Enter or Space are pressed, and change the className to conditionally include
'cursor-pointer' only if onModelClick/handleRowClick is present so
non-actionable rows don't advertise interactivity.
- Around line 66-75: The table currently advertises sortable headers (via
usePricingColumns -> DataTableColumnHeader for "model_name" and "price") while
useDataTable is called with withSortedRowModel: false, causing a UI/behavior
mismatch; fix by either enabling sorting (set withSortedRowModel: true and
ensure getSortedRowModel is wired) or explicitly disable sorting for those
columns in usePricingColumns so DataTableColumnHeader doesn't render clickable
sort affordances; additionally make the clickable-row behavior consistent and
accessible by updating the row element that uses onModelClick to include pointer
cursor styling and keyboard/ARIA support (add role="button", tabIndex=0 and
onKeyDown handling for Enter/Space) so row clicks and keyboard activation both
invoke onModelClick.

In
`@web/default/src/features/subscriptions/components/dialogs/user-subscriptions-dialog.tsx`:
- Line 251: The new table column config uses a hardcoded header string "ID"
which bypasses i18n; update the column definition in
user-subscriptions-dialog.tsx (where header: 'ID' is set) to call the
translation function from useTranslation, e.g., replace header: 'ID' with
header: t('ID'), ensuring the component imports/uses the t function (from
useTranslation()) already used elsewhere in the file so the column header
follows the same i18n flow as the rest of the dialog.

In
`@web/default/src/features/system-settings/auth/custom-oauth/components/provider-table.tsx`:
- Around line 117-129: The row action Buttons (the Button elements that call
props.onEdit(provider) and setDeleteTarget(provider)) currently render icon-only
controls; add accessible names by adding an aria-label (e.g., aria-label={`Edit
${provider.name}`} and aria-label={`Delete ${provider.name}`}) to those Button
components and mark the SVG icons (Pencil and Trash2) as decorative with
aria-hidden="true" so screen readers get the label while icons remain
decorative; ensure the same pattern is applied to any other icon-only action
buttons in this component.

In `@web/default/src/features/system-settings/content/announcements-section.tsx`:
- Around line 355-372: The header and row selection Checkboxes in the
announcements table lack accessible names; update the Checkbox components used
in the header (checked based on selectedIds/announcements and
onCheckedChange={toggleSelectAll}) and the row cell Checkbox (checked based on
selectedIds.includes(announcement.id) and onCheckedChange calling
toggleSelectOne) to include descriptive aria-label props (e.g.,
aria-label="Select all announcements" for the header and aria-label={`Select
announcement ${announcement.id}`} or a localized label for rows), and ensure any
icon-only buttons in the same table columns have either visible text or
aria-label and that decorative icons are marked aria-hidden="true" to meet
screen-reader accessibility requirements.

In `@web/default/src/features/system-settings/general/channel-affinity/index.tsx`:
- Around line 605-608: The cell renderer and the clear-cache button must guard
for a defined non-empty rule.name before using it; update the cell logic (the
cell: (rule) => ... expression) to check rule.include_rule_name && rule.name (or
rule.name?.length) before indexing cacheStats.by_rule_name, and update code that
calls setClearRuleName to pass rule.name only when it is truthy (otherwise
handle as disabled/'N/A'); specifically add checks around
cacheStats.by_rule_name[rule.name] and the call to setClearRuleName to avoid
using undefined/empty rule.name when rule.include_rule_name is true.

In
`@web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx`:
- Around line 618-619: The empty-state logic uses
table.getRowModel().rows.length (page-level count) which hides pagination when
filters reduce the current page but other pages still have matches; change
checks to use the filtered row count instead, e.g., replace uses of
table.getRowModel().rows.length in hasRows and the related empty-state branches
(and the same checks around lines 658-715) with
table.getFilteredRowModel().rows.length (or
table.getFilteredRowModel().flatRows.length if you need the full filtered row
list) so the UI reflects global filtered results rather than just the active
page.

---

Outside diff comments:
In `@web/default/src/features/keys/components/api-keys-table.tsx`:
- Around line 194-214: The status column filter registered in useTableUrlState
(columnId: 'status') is still applied client-side because the useDataTable call
was migrated without enabling server-side filtering; as a result status only
filters the current page. Fix by wiring the status filter into the server query
(pass the status value from columnFilters or the derived state into
getApiKeys/searchApiKeys) and enable manualFiltering: true (or remove the status
filter entirely until backend support exists). Update the code paths around
useTableUrlState, useDebouncedColumnFilter (for _tokenSearch/tokenFilter) and
the useDataTable invocation so the server receives { status } alongside
pagination and globalFilter when shouldSearch is true.

In `@web/default/src/features/redemption-codes/components/redemptions-table.tsx`:
- Around line 79-89: The table flips to client-side pagination when a
globalFilter exists causing server-paged results to be re-sliced; keep
useDataTable in manual (server) pagination mode whenever server endpoints are
used. Update the useDataTable call so manualPagination is true when using
getRedemptions/searchRedemptions (use manualPagination: true instead of
manualPagination: !globalFilter), and ensure the queryFn (which calls
getRedemptions/searchRedemptions using pagination and p/page_size) continues to
request the correct server page parameters (pagination, globalFilter) so the
table relies on server paging rather than re-slicing results.

---

Nitpick comments:
In `@web/default/src/components/data-table/static-data-table.tsx`:
- Around line 59-74: The StaticDataTable function currently destructures props
in its parameter list; change the signature to accept a single props parameter
(e.g., props: StaticDataTableProps<TData>) and update all uses to
props.children, props.columns, props.data, props.getRowKey,
props.getRowClassName, props.renderRow, props.empty, props.emptyContent,
props.emptyClassName, props.headerRowClassName, props.className,
props.tableClassName, props.containerProps, and props.tableProps instead of the
destructured names. Apply the same refactor to the other component in this file
that destructures props (the one around lines 174-178) so both components follow
the project's convention of using props.xxx access.
- Around line 86-112: The definition of the JSX variable "content" uses a nested
ternary (columns ? ... : bodyRows with an inner isEmpty ? ... : bodyRows) which
reduces readability; replace this with explicit branching by computing "content"
with an if/else (or extracting a helper function) that first checks "columns"
and returns the header/body JSX when present, and otherwise returns "bodyRows";
when columns exist, build the TableHeader/TableBody block and inside it use an
if/else on "isEmpty" to choose between rendering StaticDataTableEmptyRow
(passing colSpan={columns.length} and emptyClassName) or "bodyRows"; keep the
same props and keys (column.id, column.className, column.header,
headerRowClassName) but eliminate the nested ternary.

In `@web/default/src/components/data-table/use-debounced-column-filter.ts`:
- Around line 30-35: The hook useDebouncedColumnFilter currently relies on
inferred return types; add an explicit return type annotation (e.g., define and
export an interface/type for the hook result and annotate
useDebouncedColumnFilter(...): YourReturnType) and do the same for the other
exported hook mentioned around the 60-65 range; reference the existing
UseDebouncedColumnFilterOptions parameter type when creating the result type to
ensure properties returned (callbacks, state, cleanup) are correctly typed and
update the function signature to use that explicit return type.

In `@web/default/src/features/pricing/components/model-details.tsx`:
- Around line 711-714: The code uses a truthy fallback when reading group
multipliers which treats 0 as missing (e.g., in formattedPricesByGroup): replace
uses of "props.groupRatio[group] || 1" with a nullish-coalescing fallback so
only null/undefined fall back to 1 (use "props.groupRatio[group] ?? 1"); update
the same pattern wherever groupRatio is read (including the other occurrence
around the block referenced) to preserve explicit zero multipliers.

In `@web/default/src/features/system-settings/general/channel-affinity/index.tsx`:
- Around line 542-652: The table lacks a stable row key which can cause React to
use array indices; add a getRowKey prop to the StaticDataTable (the component
instance rendering rules) that returns a stable identifier for each rule, e.g. a
function that chooses rule.id if present, otherwise rule.name, otherwise an
empty string; update the StaticDataTable props near the columns prop so
getRowKey is used consistently with other tables like
amount-discount-visual-editor.

In
`@web/default/src/features/system-settings/integrations/waffo-settings-section.tsx`:
- Around line 329-396: The StaticDataTable rendering payMethods should supply a
stable getRowKey to avoid React key instability; update the StaticDataTable
props (where StaticDataTable is used with data={payMethods}) to add getRowKey
that returns a unique string per row (for example combining payMethodType and
payMethodName and index) so keys remain stable during reorder and match other
usages in this PR; keep existing handlers (openEdit, onPayMethodsChange)
unchanged.

In `@web/default/src/features/system-settings/models/conflict-confirm-dialog.tsx`:
- Around line 69-104: The StaticDataTable usage lacks a getRowKey prop which can
cause unstable React keys; add getRowKey to the StaticDataTable invocation (the
one rendering data={conflicts}) and return a stable key using the row values and
index, e.g. a template combining conflict.channel, conflict.model and idx
(referencing the conflicts array and the conflict parameter) so keys are stable
across renders and avoid React key warnings.
🪄 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: 77284c4e-5beb-4c33-937e-6cd805f99760

📥 Commits

Reviewing files that changed from the base of the PR and between d2576dd and d73f6b4.

📒 Files selected for processing (64)
  • web/default/src/components/ai-elements/code-block.tsx
  • web/default/src/components/data-table/data-table-page.tsx
  • web/default/src/components/data-table/data-table-view.tsx
  • web/default/src/components/data-table/index.ts
  • web/default/src/components/data-table/pagination.tsx
  • web/default/src/components/data-table/static-data-table-classnames.ts
  • web/default/src/components/data-table/static-data-table.tsx
  • web/default/src/components/data-table/use-data-table.ts
  • web/default/src/components/data-table/use-debounced-column-filter.ts
  • web/default/src/components/layout/components/section-page-layout.tsx
  • web/default/src/components/long-text.tsx
  • web/default/src/features/channels/components/channels-columns.tsx
  • web/default/src/features/channels/components/channels-table.tsx
  • web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
  • web/default/src/features/channels/components/dialogs/multi-key-manage-dialog.tsx
  • web/default/src/features/channels/index.tsx
  • web/default/src/features/keys/components/api-keys-table.tsx
  • web/default/src/features/keys/index.tsx
  • web/default/src/features/models/components/deployments-columns.tsx
  • web/default/src/features/models/components/deployments-table.tsx
  • web/default/src/features/models/components/dialogs/prefill-group-management-dialog.tsx
  • web/default/src/features/models/components/dialogs/upstream-conflict-dialog.tsx
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/models/components/models-table.tsx
  • web/default/src/features/models/index.tsx
  • web/default/src/features/pricing/components/dynamic-pricing-breakdown.tsx
  • web/default/src/features/pricing/components/model-details-api.tsx
  • web/default/src/features/pricing/components/model-details-apps.tsx
  • web/default/src/features/pricing/components/model-details-modalities.tsx
  • web/default/src/features/pricing/components/model-details-performance.tsx
  • web/default/src/features/pricing/components/model-details.tsx
  • web/default/src/features/pricing/components/pricing-columns.tsx
  • web/default/src/features/pricing/components/pricing-table.tsx
  • web/default/src/features/redemption-codes/components/redemptions-table.tsx
  • web/default/src/features/redemption-codes/index.tsx
  • web/default/src/features/subscriptions/components/dialogs/user-subscriptions-dialog.tsx
  • web/default/src/features/subscriptions/components/subscriptions-table.tsx
  • web/default/src/features/subscriptions/index.tsx
  • web/default/src/features/system-settings/auth/custom-oauth/components/provider-table.tsx
  • web/default/src/features/system-settings/content/announcements-section.tsx
  • web/default/src/features/system-settings/content/api-info-section.tsx
  • web/default/src/features/system-settings/content/chat-settings-visual-editor.tsx
  • web/default/src/features/system-settings/content/faq-section.tsx
  • web/default/src/features/system-settings/content/uptime-kuma-section.tsx
  • web/default/src/features/system-settings/general/channel-affinity/index.tsx
  • web/default/src/features/system-settings/integrations/amount-discount-visual-editor.tsx
  • web/default/src/features/system-settings/integrations/creem-products-visual-editor.tsx
  • web/default/src/features/system-settings/integrations/payment-methods-visual-editor.tsx
  • web/default/src/features/system-settings/integrations/waffo-settings-section.tsx
  • web/default/src/features/system-settings/models/channel-selector-dialog.tsx
  • web/default/src/features/system-settings/models/conflict-confirm-dialog.tsx
  • web/default/src/features/system-settings/models/group-ratio-visual-editor.tsx
  • web/default/src/features/system-settings/models/model-ratio-table-columns.tsx
  • web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
  • web/default/src/features/system-settings/models/tiered-pricing-editor.tsx
  • web/default/src/features/system-settings/models/tool-price-settings.tsx
  • web/default/src/features/system-settings/models/upstream-ratio-sync-table.tsx
  • web/default/src/features/system-settings/request-limits/rate-limit-visual-editor.tsx
  • web/default/src/features/usage-logs/components/usage-logs-mobile-card.tsx
  • web/default/src/features/usage-logs/components/usage-logs-table.tsx
  • web/default/src/features/usage-logs/index.tsx
  • web/default/src/features/users/components/users-table.tsx
  • web/default/src/features/users/index.tsx
  • web/default/src/lib/utils.ts
💤 Files with no reviewable changes (1)
  • web/default/src/components/long-text.tsx

Comment on lines +256 to +263
return rows.map((row) =>
props.renderRow
? props.renderRow(row, {
getCellClassName: (columnId, className) =>
cn(getColumnClassName(columnId, 'cell'), className),
})
: renderDefaultRow(props, row, getColumnClassName)
)

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce a stable key when renderRow is used.

The custom row branch returns raw nodes from props.renderRow(...) without a guaranteed key, which can cause incorrect row reconciliation after sorting/pagination.

Proposed fix
-  return rows.map((row) =>
-    props.renderRow
-      ? props.renderRow(row, {
-          getCellClassName: (columnId, className) =>
-            cn(getColumnClassName(columnId, 'cell'), className),
-        })
-      : renderDefaultRow(props, row, getColumnClassName)
-  )
+  return rows.map((row) =>
+    props.renderRow ? (
+      <React.Fragment key={row.id}>
+        {props.renderRow(row, {
+          getCellClassName: (columnId, className) =>
+            cn(getColumnClassName(columnId, 'cell'), className),
+        })}
+      </React.Fragment>
+    ) : (
+      renderDefaultRow(props, row, getColumnClassName)
+    )
+  )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/data-table-view.tsx` around lines 256 -
263, The custom-row branch in rows.map calls props.renderRow(...) but doesn't
ensure a stable React key, which can break reconciliation; update the rows.map
logic where props.renderRow is used to ensure the returned element has a key
derived from the row (e.g., row.id || row._id || row.key) and fallback to the
row index only if none exists, by wrapping or cloning the node returned from
props.renderRow(row, {...}) to inject that key; keep renderDefaultRow(path)
unchanged and continue using getColumnClassName for class names.

Comment on lines +333 to +341
const edgeClassName =
pinnedColumn.side === 'left'
? 'border-r shadow-[8px_0_10px_-10px_hsl(var(--foreground))]'
: 'border-l shadow-[-8px_0_10px_-10px_hsl(var(--foreground))]'

return cn(
'sticky whitespace-nowrap',
pinnedColumn.side === 'left' ? 'left-0' : 'right-0',
edgeClassName,

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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Pinned columns on the same side will overlap.

Every pinned-left cell gets left-0 (and pinned-right gets right-0), so two pinned columns on the same side stack at the same position instead of offsetting by preceding pinned column widths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/data-table-view.tsx` around lines 333 -
341, Pinned columns all receive a fixed "left-0"/"right-0" so multiple pinned
columns on the same side overlap; change the positioning logic in
data-table-view by computing a per-column offset (e.g., derive a cumulative
width for each pinned column from the pinned columns array) and use that offset
instead of "left-0"/"right-0" when rendering pinnedColumn: update the code that
builds edgeClassName and the cn(...) call to remove the hardcoded left-0/right-0
and instead apply a dynamic inline style or CSS variable using the computed
offset (use the pinnedColumn identifier or a helper like
getPinnedOffset(pinnedColumn) to locate where to compute offsets and apply
style).

Comment thread web/default/src/components/data-table/core/pagination.tsx Outdated
Comment on lines +360 to +362
<p className='text-muted-foreground text-xs italic'>
No description provided
</p>

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize newly added table copy and action labels.

The new desktop table path introduces hardcoded user-facing strings (No description provided, empty-state item text, and sr-only action labels). Wrap these with t(...) to keep locale behavior consistent.

As per coding guidelines, "All user-facing text content must support i18n using the t() function from useTranslation() in React components".

Also applies to: 409-411, 435-436, 444-445

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/models/components/dialogs/prefill-group-management-dialog.tsx`
around lines 360 - 362, The new desktop table in PrefillGroupManagementDialog
contains hardcoded user-facing strings (e.g., "No description provided", the
empty-state item text, and the sr-only action labels) so update the component to
use the i18n hook and wrap these literals with t(...); import/use
useTranslation() and replace the hardcoded texts in the table rows and action
labels (inside PrefillGroupManagementDialog / the desktop table render
functions) with calls like t('...') using appropriate translation keys to
maintain locale behavior.

Source: Coding guidelines

Comment on lines +333 to +343
const { table } = useDataTable({
data: conflictRows,
columns,
state: {
rowSelection,
},
rowSelection,
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
getRowId: (row) => row.id,
withFilteredRowModel: false,
withPaginationRowModel: false,
withSortedRowModel: false,
withFacetedRowModel: false,

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Page-scoped selection is no longer aligned with the rows being rendered.

Line 531 renders only paginatedRows, but the table on Line 333 still owns every conflictRows entry and has pagination row modeling disabled. That means the header checkbox keeps operating on TanStack’s full row model, so “select all” can select hidden conflicts from other pages/searches and handleApplyOverwrite() will submit them in the overwrite payload.

Also applies to: 529-536

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/models/components/dialogs/upstream-conflict-dialog.tsx`
around lines 333 - 343, The table's selection is bound to the full conflictRows
model while the UI renders only paginatedRows, so the header checkbox can select
hidden rows; fix by wiring the table to the same row set being rendered—call
useDataTable with the paginatedRows (or enable TanStack pagination/filtering by
setting withPaginationRowModel/withFilteredRowModel true) so selection state and
getRowId in useDataTable match what is displayed, and ensure
handleApplyOverwrite reads from the table's current selection (rowSelection)
rather than the full conflictRows list.

Comment on lines +117 to +129
<Button
variant='ghost'
size='sm'
onClick={() => props.onEdit(provider)}
>
<Pencil className='h-4 w-4' />
</Button>
<Button
variant='ghost'
size='sm'
onClick={() => setDeleteTarget(provider)}
>
<Trash2 className='text-destructive h-4 w-4' />

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Shared migration issue: icon-only row action buttons lack accessible names.

Across these migrated tables, edit/delete controls rely on icons without text equivalents. Add aria-label (and keep icons decorative) consistently to restore accessible action semantics.

As per coding guidelines, "Ensure keyboard operability and logical focus order; use ARIA attributes when necessary (aria-label, aria-expanded, aria-hidden); add aria-hidden=\"true\" to decorative icons; provide text equivalents for important information".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/system-settings/auth/custom-oauth/components/provider-table.tsx`
around lines 117 - 129, The row action Buttons (the Button elements that call
props.onEdit(provider) and setDeleteTarget(provider)) currently render icon-only
controls; add accessible names by adding an aria-label (e.g., aria-label={`Edit
${provider.name}`} and aria-label={`Delete ${provider.name}`}) to those Button
components and mark the SVG icons (Pencil and Trash2) as decorative with
aria-hidden="true" so screen readers get the label while icons remain
decorative; ensure the same pattern is applied to any other icon-only action
buttons in this component.

Source: Coding guidelines

Comment on lines +355 to +372
header: (
<Checkbox
checked={
selectedIds.length === announcements.length &&
announcements.length > 0
}
onCheckedChange={toggleSelectAll}
/>
),
className: 'w-12',
cell: (announcement) => (
<Checkbox
checked={selectedIds.includes(announcement.id)}
onCheckedChange={(checked) =>
toggleSelectOne(announcement.id, checked as boolean)
}
/>
),

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Shared root cause: migrated StaticDataTable controls are missing accessible names.

Across these table migrations, icon-only buttons and selection controls were introduced without aria-labels (and icons are not marked decorative). This blocks clear screen-reader interaction for row actions and selection. Apply one consistent pattern for all action/selection controls in these table columns.

As per coding guidelines, “Ensure keyboard operability and logical focus order; use ARIA attributes when necessary (aria-label, aria-expanded, aria-hidden); add aria-hidden="true" to decorative icons; provide text equivalents for important information”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/system-settings/content/announcements-section.tsx`
around lines 355 - 372, The header and row selection Checkboxes in the
announcements table lack accessible names; update the Checkbox components used
in the header (checked based on selectedIds/announcements and
onCheckedChange={toggleSelectAll}) and the row cell Checkbox (checked based on
selectedIds.includes(announcement.id) and onCheckedChange calling
toggleSelectOne) to include descriptive aria-label props (e.g.,
aria-label="Select all announcements" for the header and aria-label={`Select
announcement ${announcement.id}`} or a localized label for rows), and ensure any
icon-only buttons in the same table columns have either visible text or
aria-label and that decorative icons are marked aria-hidden="true" to meet
screen-reader accessibility requirements.

Source: Coding guidelines

Comment on lines +605 to +608
cell: (rule) =>
rule.include_rule_name && cacheStats?.by_rule_name
? cacheStats.by_rule_name[rule.name] || 0
: 'N/A',

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against undefined rule.name when rule.include_rule_name is true.

Line 605 accesses cacheStats.by_rule_name[rule.name] and line 622 passes rule.name to setClearRuleName, but these cells only check rule.include_rule_name without verifying rule.name is defined. Line 552 shows rule.name || '-', confirming that rule.name can be falsy. If rule.include_rule_name is true but rule.name is undefined/empty, the cache lookup and clear-cache button will not work correctly.

🛡️ Recommended guard
 {
   id: 'cache',
   header: t('Cache'),
   cell: (rule) =>
-    rule.include_rule_name && cacheStats?.by_rule_name
+    rule.include_rule_name && rule.name && cacheStats?.by_rule_name
       ? cacheStats.by_rule_name[rule.name] || 0
       : 'N/A',
 },

And for the button:

-{rule.include_rule_name && (
+{rule.include_rule_name && rule.name && (
   <Button

Also applies to: 617-627

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/system-settings/general/channel-affinity/index.tsx`
around lines 605 - 608, The cell renderer and the clear-cache button must guard
for a defined non-empty rule.name before using it; update the cell logic (the
cell: (rule) => ... expression) to check rule.include_rule_name && rule.name (or
rule.name?.length) before indexing cacheStats.by_rule_name, and update code that
calls setClearRuleName to pass rule.name only when it is truthy (otherwise
handle as disabled/'N/A'); specifically add checks around
cacheStats.by_rule_name[rule.name] and the call to setClearRuleName to avoid
using undefined/empty rule.name when rule.include_rule_name is true.

Comment on lines +618 to +619
const hasRows = table.getRowModel().rows.length > 0

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the filtered row count for the empty state, not the current page count.

table.getRowModel().rows.length only reflects the active page. When the user is on a later page and a filter shrinks the result set, this branch shows “No models match your search” and hides pagination even though earlier pages still contain matches.

Suggested fix
-  const hasRows = table.getRowModel().rows.length > 0
+  const hasFilteredRows = table.getPrePaginationRowModel().rows.length > 0
...
-          {!hasRows ? (
+          {!hasFilteredRows ? (
             <div className='text-muted-foreground rounded-lg border border-dashed p-8 text-center'>
               {table.getState().globalFilter
                 ? t('No models match your search')
                 : t('No models configured. Use Add model to get started.')}
             </div>
           ) : (
             <DataTableView
               table={table}
               containerClassName='min-h-0 flex-1 rounded-md'
               tableContainerClassName='h-full'
               tableClassName='min-w-[852px] table-fixed'
               tableHeaderClassName='[&_tr]:border-b-0'
               splitHeaderScrollClassName='h-full'
               bodyContainerClassName='[scrollbar-gutter:stable]'
               splitHeader
               pinnedColumns={[
                 {
                   columnId: 'actions',
                   side: 'right',
                   className: 'w-24 min-w-24',
                 },
               ]}
               colgroup={
                 <colgroup>
                   <col className='w-9' />
                   <col className='w-[300px]' />
                   <col className='w-[120px]' />
                   <col className='w-[300px]' />
                   <col className='w-24' />
                 </colgroup>
               }
               renderRow={(row, { getCellClassName }) => (
                 <DataTableRow
                   key={row.id}
                   row={row}
                   className={
                     editData?.name === row.original.name
                       ? 'bg-muted/45 hover:bg-muted/50 data-[state=selected]:bg-muted group'
                       : 'group'
                   }
                   getColumnClassName={(columnId) =>
                     columnId === 'actions' &&
                     editData?.name === row.original.name
                       ? getCellClassName(columnId, 'bg-muted/45')
                       : getCellClassName(columnId)
                   }
                   onClick={(event) => {
                     const target = event.target as HTMLElement
                     if (target.closest('button, [role="checkbox"]')) return
                     handleEdit(row.original)
                   }}
                 />
               )}
             />
           )}
 
-          {hasRows && <DataTablePagination table={table} />}
+          {hasFilteredRows && <DataTablePagination table={table} />}

Also applies to: 658-715

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx`
around lines 618 - 619, The empty-state logic uses
table.getRowModel().rows.length (page-level count) which hides pagination when
filters reduce the current page but other pages still have matches; change
checks to use the filtered row count instead, e.g., replace uses of
table.getRowModel().rows.length in hasRows and the related empty-state branches
(and the same checks around lines 658-715) with
table.getFilteredRowModel().rows.length (or
table.getFilteredRowModel().flatRows.length if you need the full filtered row
list) so the UI reflects global filtered results rather than just the active
page.

- derive default colgroup widths from visible columns when split headers or header sizing are enabled.
- apply a fixed table layout with computed minimum width so header and body columns stay aligned.
- keep split-header containers from leaking horizontal overflow and avoid extra pinned-column borders.
- assign fixed widths to selection columns so shared colgroup sizing keeps checkbox cells compact.
- size id columns in redemption and user tables to keep split headers aligned with body rows.
- render compact provider avatars in the metadata icon column instead of wide wordmarks.
- position icons in a fixed-size wrapper so they line up with the existing icon header alignment.
- give the invite info and created-at columns explicit widths so table sizing reserves enough space.
- allow invite badges to wrap within the cell instead of spilling into adjacent columns.

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

Caution

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

⚠️ Outside diff range comments (1)
web/default/src/components/data-table/core/data-table-view.tsx (1)

149-170: ⚠️ Potential issue | 🟠 Major

Fix accessibility semantics in split-header table mode

In DataTableView split-header mode, the header and body render as two separate <table> elements: the header table contains <thead>/<th>, while the body table renders only <tbody>/<td> with no headers/scope/ARIA association. TableHead/TableCell don’t add any header linkage, and DataTableHeader doesn’t set <th> ids—so body cells can’t reliably announce their column headers. DataTablePage enables this by default via fixedHeight !== false. Use a single <table> (sticky header) or assign stable ids to header <th> and set headers (and/or scope="col") on the corresponding body <td>.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/core/data-table-view.tsx` around lines
149 - 170, DataTableView is rendering header and body as separate <table>
elements in split-header mode (enabled via DataTablePage fixedHeight !== false),
which breaks header/body semantic linkage; fix by either rendering a single
table with a sticky <thead> when split-header is active (move the body Host into
the same Table) or, if keeping separate tables, ensure DataTableHeader/TableHead
assigns stable id attributes to each <th> (e.g., based on column keys) and
update renderTableBody/TableCell to emit matching headers attributes on each
<td> (and add scope="col" to header <th>), so assistive tech can associate body
cells with their column headers. Ensure ids are stable across renders and derive
from unique column identifiers used by DataTableHeader/renderTableBody.
🧹 Nitpick comments (3)
web/default/src/features/models/components/models-columns.tsx (1)

44-48: ⚡ Quick win

Consider adding a comment explaining the icon transformation.

The helper extracts the base icon key and appends .Avatar.type={'platform'}, but the purpose of this "compact" transformation isn't immediately clear. A brief comment would help future maintainers understand why this specific suffix is used for model icons.

📝 Suggested documentation
+// Derives a compact provider avatar icon from the model's icon key
+// by taking the base segment and rendering it as a platform avatar
 function getCompactModelIcon(iconKey: string) {
   const baseIconKey = iconKey.split('.')[0]
   return getLobeIcon(`${baseIconKey}.Avatar.type={'platform'}`, 20)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/models/components/models-columns.tsx` around lines
44 - 48, Add a short inline comment above the getCompactModelIcon function
explaining that it normalizes an icon key to its base (splitting on '.') and
forces the Avatar variant to the compact "platform" type by appending the suffix
`${baseIconKey}.Avatar.type={'platform'}` before calling getLobeIcon, so future
maintainers understand this transformation and why the compact/platform avatar
is used for model icons.
web/default/src/components/data-table/core/data-table-row.tsx (1)

30-35: ⚡ Quick win

Avoid destructuring component props in this TSX component.

Please switch to a single props parameter and access fields via props.xxx to match the repo’s component style contract.

As per coding guidelines, "Do not destructure component props; use props.xxx directly instead for clarity."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/core/data-table-row.tsx` around lines
30 - 35, The DataTableRow component currently destructures its props in the
function signature; change the signature to accept a single props parameter and
update all internal references to use props.xxx (e.g., props.row,
props.className, props.getColumnClassName, props.rowProps) and keep the type as
DataTableRowProps<TData>; ensure any usage inside the function body and JSX is
updated accordingly to avoid destructuring while preserving behavior of
DataTableRow and its exported type.

Source: Coding guidelines

web/default/src/components/data-table/core/data-table-header.tsx (1)

31-37: ⚡ Quick win

Use props.xxx access instead of destructuring component props.

This component destructures props at the function boundary; this breaks the TSX props-access rule used in this repo.

♻️ Suggested refactor
-export function DataTableHeader<TData>({
-  table,
-  applyHeaderSize,
-  className,
-  rowClassName,
-  getColumnClassName,
-}: DataTableHeaderProps<TData>) {
+export function DataTableHeader<TData>(props: DataTableHeaderProps<TData>) {
   return (
-    <TableHeader className={className}>
-      {table.getHeaderGroups().map((headerGroup) => (
-        <TableRow key={headerGroup.id} className={rowClassName}>
+    <TableHeader className={props.className}>
+      {props.table.getHeaderGroups().map((headerGroup) => (
+        <TableRow key={headerGroup.id} className={props.rowClassName}>
           {headerGroup.headers.map((header) => (
             <TableHead
               key={header.id}
               colSpan={header.colSpan}
-              className={getColumnClassName?.(header.column.id, 'header')}
-              style={applyHeaderSize ? { width: header.getSize() } : undefined}
+              className={props.getColumnClassName?.(header.column.id, 'header')}
+              style={props.applyHeaderSize ? { width: header.getSize() } : undefined}
             >

As per coding guidelines, "Do not destructure component props; use props.xxx directly instead for clarity."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/core/data-table-header.tsx` around
lines 31 - 37, The DataTableHeader component currently destructures props in the
function signature (table, applyHeaderSize, className, rowClassName,
getColumnClassName) which violates the TSX props-access rule; change the
function to accept a single parameter (props: DataTableHeaderProps<TData>) and
replace all direct uses of the destructured identifiers inside the component
with props.table, props.applyHeaderSize, props.className, props.rowClassName,
and props.getColumnClassName, respectively, ensuring any defaulting or renaming
is handled inside the function body rather than at the signature.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@web/default/src/components/data-table/core/data-table-view.tsx`:
- Around line 149-170: DataTableView is rendering header and body as separate
<table> elements in split-header mode (enabled via DataTablePage fixedHeight !==
false), which breaks header/body semantic linkage; fix by either rendering a
single table with a sticky <thead> when split-header is active (move the body
Host into the same Table) or, if keeping separate tables, ensure
DataTableHeader/TableHead assigns stable id attributes to each <th> (e.g., based
on column keys) and update renderTableBody/TableCell to emit matching headers
attributes on each <td> (and add scope="col" to header <th>), so assistive tech
can associate body cells with their column headers. Ensure ids are stable across
renders and derive from unique column identifiers used by
DataTableHeader/renderTableBody.

---

Nitpick comments:
In `@web/default/src/components/data-table/core/data-table-header.tsx`:
- Around line 31-37: The DataTableHeader component currently destructures props
in the function signature (table, applyHeaderSize, className, rowClassName,
getColumnClassName) which violates the TSX props-access rule; change the
function to accept a single parameter (props: DataTableHeaderProps<TData>) and
replace all direct uses of the destructured identifiers inside the component
with props.table, props.applyHeaderSize, props.className, props.rowClassName,
and props.getColumnClassName, respectively, ensuring any defaulting or renaming
is handled inside the function body rather than at the signature.

In `@web/default/src/components/data-table/core/data-table-row.tsx`:
- Around line 30-35: The DataTableRow component currently destructures its props
in the function signature; change the signature to accept a single props
parameter and update all internal references to use props.xxx (e.g., props.row,
props.className, props.getColumnClassName, props.rowProps) and keep the type as
DataTableRowProps<TData>; ensure any usage inside the function body and JSX is
updated accordingly to avoid destructuring while preserving behavior of
DataTableRow and its exported type.

In `@web/default/src/features/models/components/models-columns.tsx`:
- Around line 44-48: Add a short inline comment above the getCompactModelIcon
function explaining that it normalizes an icon key to its base (splitting on
'.') and forces the Avatar variant to the compact "platform" type by appending
the suffix `${baseIconKey}.Avatar.type={'platform'}` before calling getLobeIcon,
so future maintainers understand this transformation and why the
compact/platform avatar is used for model icons.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c4a030a3-486f-47dc-8a92-0c1b5b572b39

📥 Commits

Reviewing files that changed from the base of the PR and between d73f6b4 and 9691ca0.

📒 Files selected for processing (29)
  • web/default/src/components/data-table/README.md
  • web/default/src/components/data-table/core/column-header.tsx
  • web/default/src/components/data-table/core/column-pinning.ts
  • web/default/src/components/data-table/core/data-table-colgroup.tsx
  • web/default/src/components/data-table/core/data-table-header.tsx
  • web/default/src/components/data-table/core/data-table-row.tsx
  • web/default/src/components/data-table/core/data-table-view.tsx
  • web/default/src/components/data-table/core/pagination.tsx
  • web/default/src/components/data-table/core/table-empty.tsx
  • web/default/src/components/data-table/core/table-sizing.ts
  • web/default/src/components/data-table/core/table-skeleton.tsx
  • web/default/src/components/data-table/core/types.ts
  • web/default/src/components/data-table/hooks/use-data-table.ts
  • web/default/src/components/data-table/hooks/use-debounced-column-filter.ts
  • web/default/src/components/data-table/index.ts
  • web/default/src/components/data-table/layout/data-table-page.tsx
  • web/default/src/components/data-table/layout/mobile-card-list.tsx
  • web/default/src/components/data-table/static/static-data-table-classnames.ts
  • web/default/src/components/data-table/static/static-data-table.tsx
  • web/default/src/components/data-table/toolbar/bulk-actions.tsx
  • web/default/src/components/data-table/toolbar/faceted-filter.tsx
  • web/default/src/components/data-table/toolbar/toolbar.tsx
  • web/default/src/components/data-table/toolbar/view-options.tsx
  • web/default/src/components/status-badge.tsx
  • web/default/src/features/keys/components/api-keys-columns.tsx
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/redemption-codes/components/redemptions-columns.tsx
  • web/default/src/features/system-settings/models/model-ratio-table-columns.tsx
  • web/default/src/features/users/components/users-columns.tsx
💤 Files with no reviewable changes (5)
  • web/default/src/components/data-table/static/static-data-table-classnames.ts
  • web/default/src/components/data-table/hooks/use-debounced-column-filter.ts
  • web/default/src/components/data-table/hooks/use-data-table.ts
  • web/default/src/components/data-table/static/static-data-table.tsx
  • web/default/src/components/data-table/core/pagination.tsx
✅ Files skipped from review due to trivial changes (4)
  • web/default/src/components/data-table/README.md
  • web/default/src/features/keys/components/api-keys-columns.tsx
  • web/default/src/features/redemption-codes/components/redemptions-columns.tsx
  • web/default/src/features/system-settings/models/model-ratio-table-columns.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/default/src/components/data-table/index.ts

- reuse the pinned column lookup while table props stay stable to reduce repeated per-render work.
- share the resolved column class handler across unified and split-header table layouts.
- localize page-number screen reader labels so pagination remains accessible in every locale.
- make StaticDataTable distinguish data-driven and children-only usage through explicit prop shapes.
- remove unsupported columns-without-data fallback after confirming no repository callers rely on it.
- default manual table modes away from unused local row models to reduce repeated table work.
- use an opaque muted background for the active action column so sticky cells do not reveal scrolled content underneath.

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

🧹 Nitpick comments (1)
web/default/src/components/data-table/static/static-data-table.tsx (1)

74-74: ⚡ Quick win

Use props.xxx instead of destructuring component props.

Line 74, Line 92, and Line 150 destructure component props, which conflicts with this repo’s TSX component convention. Please switch to direct props access in these components.

As per coding guidelines, “Do not destructure component props; use props.xxx directly instead for clarity.”

Also applies to: 92-102, 150-156

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/data-table/static/static-data-table.tsx` at line
74, The component currently destructures props (e.g., const { className,
tableClassName, containerProps, tableProps } = props and similar destructures at
the other locations); change these to use direct props access everywhere
(replace usages of className with props.className, tableClassName with
props.tableClassName, containerProps with props.containerProps, tableProps with
props.tableProps and likewise for the other destructured identifiers found
around the 92-102 and 150-156 regions) so the component follows the repository
TSX convention of not destructuring props.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@web/default/src/components/data-table/static/static-data-table.tsx`:
- Line 74: The component currently destructures props (e.g., const { className,
tableClassName, containerProps, tableProps } = props and similar destructures at
the other locations); change these to use direct props access everywhere
(replace usages of className with props.className, tableClassName with
props.tableClassName, containerProps with props.containerProps, tableProps with
props.tableProps and likewise for the other destructured identifiers found
around the 92-102 and 150-156 regions) so the component follows the repository
TSX convention of not destructuring props.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f9b07f02-3058-4b4f-82da-b9d356472505

📥 Commits

Reviewing files that changed from the base of the PR and between 9691ca0 and 6799f27.

📒 Files selected for processing (14)
  • web/default/src/components/data-table/core/column-pinning.ts
  • web/default/src/components/data-table/core/data-table-view.tsx
  • web/default/src/components/data-table/core/pagination.tsx
  • web/default/src/components/data-table/hooks/use-data-table.ts
  • web/default/src/components/data-table/static/static-data-table.tsx
  • web/default/src/features/channels/components/channels-table.tsx
  • web/default/src/features/models/components/deployments-table.tsx
  • web/default/src/features/models/components/models-table.tsx
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
💤 Files with no reviewable changes (3)
  • web/default/src/features/models/components/deployments-table.tsx
  • web/default/src/features/channels/components/channels-table.tsx
  • web/default/src/features/models/components/models-table.tsx
✅ Files skipped from review due to trivial changes (5)
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/vi.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/default/src/components/data-table/core/pagination.tsx
  • web/default/src/components/data-table/hooks/use-data-table.ts
  • web/default/src/components/data-table/core/data-table-view.tsx

- apply stable header sizing to remaining desktop data table pages so constrained layouts scroll instead of compressing cells.
- add explicit widths for key, quota, badge, and timestamp columns that contain fixed-format content.
- constrain masked values and timestamp cells with truncation to keep content inside its assigned column.
- remove extra inline padding from masked table text buttons so values start at the cell edge.
- tag status badges and offset leading badges inside table cells to match header text alignment.
- widen redemption and subscription table columns so masked codes, timestamps, and localized headers fit.
- localize subscription ID headers and add Received amount translations across supported locales.
- add a shared provider badge component for icon and status label layout.
- reuse it in channel type and model vendor columns so OpenAI icons align consistently.
# Conflicts:
#	web/default/src/features/channels/components/channels-table.tsx
@t0ng7u
t0ng7u merged commit 6f41542 into main Jun 10, 2026
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 11, 2026
11 tasks
tongkaiteng pushed a commit to tongkaiteng/new-api that referenced this pull request Jun 12, 2026
…le (QuantumNous#5405)

* refactor(web): centralize data table implementation

- route all TanStack table setup through a shared data-table hook to remove repeated state and row model wiring.
- move table rendering, static table wrappers, empty states, and primitive exports behind the data-table module.
- update feature tables and configuration editors to share the same table UX while preserving their existing workflows.

* refactor(web): trim data table public API

- remove unused data-table exports and dead static table helper types.
- keep internal table header, skeleton, empty state, and faceted filter helpers private to the data-table module.
- route feature imports through the data-table barrel to avoid subpath coupling.

* refactor(web): unify table rendering components

- centralize static table headers, bodies, empty states, and shared class names behind the data-table package.
- migrate settings, pricing, channel, key, subscription, and model tables to the shared table APIs.
- remove data-table exports for low-level table primitives so feature code uses one supported abstraction.

* perf(web): keep list tables fixed within page content

- make shared data table pages fill available height and scroll row data inside the table body.
- add a fixed content layout mode so selected list pages avoid page-level scrolling.
- apply the fixed table behavior to keys, logs, channels, models, users, redemptions, and subscriptions.

* perf(web): refine table pagination controls

- show total row counts instead of redundant page range text.
- tighten visible page buttons so pagination fits constrained table widths.
- align pagination controls and tune text hierarchy for clearer scanning.

* perf(web): stabilize model pricing table columns

- keep model pricing columns at fixed widths so headers do not collapse in narrow layouts.
- truncate long model names and pricing summaries within their cells instead of squeezing adjacent columns.

* refactor(web): simplify data table rendering internals

- split table body rendering into focused helpers for loading, empty, and row states.
- extract static table row and cell class resolution to reduce branching in the main component.
- reuse a single pagination page-size option list to avoid duplicated constants.

* perf(pricing): reduce dynamic pricing table render work

- reuse dynamic pricing field metadata instead of rebuilding it inside table columns.
- precompute formatted dynamic prices per tier and group to avoid repeated entry mapping for each cell.
- simplify select option construction in related dialogs while preserving the same choices.

* refactor(web): streamline pricing table rendering

- reuse translated endpoint select options between trigger data and menu items.
- precompute dynamic pricing maps per group so table cells only resolve formatted values.
- add local dynamic pricing type aliases to keep helper signatures readable.

* refactor(web): merge pricing table imports

* refactor(web): merge upstream ratio table imports

* refactor(web): merge channel selector table imports

* refactor(web): simplify tiered pricing select items

* refactor(web): reuse model ratio row state

* refactor(web): rely on table view row defaults

* refactor(web): reuse pagination state values

* refactor(web): hoist pagination size select items

* refactor(web): clarify static table body rows

* refactor(web): extract table page pagination rendering

* fix(web): remove direct hast type dependency

- rely on Shiki transformer contextual typing for line nodes.
- allow frontend typecheck to pass without an undeclared hast package.

* refactor(web): trim data table hook return API

- return only the TanStack table instance from useDataTable.
- keep internal state handling private because callers do not consume it directly.

* refactor(web): keep static table empty row private

- stop exporting the internal StaticDataTableEmptyRow helper.
- keep the public static table API focused on the table component and column type.

* refactor(web): hide data table view props from barrel

* refactor(web): remove stale long text lint override

* fix(web): keep pinned table columns opaque

- apply pinned column background classes after custom column classes.
- use an opaque hover background so scrolled content cannot show through fixed cells.

* refactor(data-table): organize shared table components

- group table primitives, page composition, toolbar controls, static tables, and hooks by responsibility.
- split shared view types, row rendering, header rendering, and pinned-column styling out of the main table view.
- keep the public data-table barrel stable while documenting the new ownership boundaries.

* fix(web): stabilize split table column sizing

- derive default colgroup widths from visible columns when split headers or header sizing are enabled.
- apply a fixed table layout with computed minimum width so header and body columns stay aligned.
- keep split-header containers from leaking horizontal overflow and avoid extra pinned-column borders.

* fix(web): set stable table utility column widths

- assign fixed widths to selection columns so shared colgroup sizing keeps checkbox cells compact.
- size id columns in redemption and user tables to keep split headers aligned with body rows.

* fix(web): align model metadata icon cells

- render compact provider avatars in the metadata icon column instead of wide wordmarks.
- position icons in a fixed-size wrapper so they line up with the existing icon header alignment.

* fix(status-badge): hide status dot by default

* fix(web): prevent user invite info overlap

- give the invite info and created-at columns explicit widths so table sizing reserves enough space.
- allow invite badges to wrap within the cell instead of spilling into adjacent columns.

* perf(data-table): cache pinned column class resolution

- reuse the pinned column lookup while table props stay stable to reduce repeated per-render work.
- share the resolved column class handler across unified and split-header table layouts.
- localize page-number screen reader labels so pagination remains accessible in every locale.

* refactor(data-table): tighten static table modes

- make StaticDataTable distinguish data-driven and children-only usage through explicit prop shapes.
- remove unsupported columns-without-data fallback after confirming no repository callers rely on it.
- default manual table modes away from unused local row models to reduce repeated table work.

* fix(data-table): make pinned edit column opaque

- use an opaque muted background for the active action column so sticky cells do not reveal scrolled content underneath.

* fix(data-table): prevent narrow column overlap

- apply stable header sizing to remaining desktop data table pages so constrained layouts scroll instead of compressing cells.
- add explicit widths for key, quota, badge, and timestamp columns that contain fixed-format content.
- constrain masked values and timestamp cells with truncation to keep content inside its assigned column.

* fix(table): align table cell content with headers

- remove extra inline padding from masked table text buttons so values start at the cell edge.
- tag status badges and offset leading badges inside table cells to match header text alignment.

* fix(table): prevent admin list column overflow

- widen redemption and subscription table columns so masked codes, timestamps, and localized headers fit.
- localize subscription ID headers and add Received amount translations across supported locales.

* fix(provider-badge): unify provider icon spacing

- add a shared provider badge component for icon and status label layout.
- reuse it in channel type and model vendor columns so OpenAI icons align consistently.
@Calcium-Ion
Calcium-Ion deleted the perf/ui-table branch June 13, 2026 08:34
ruanhangjian pushed a commit to ruanhangjian/new-api that referenced this pull request Jul 11, 2026
…le (QuantumNous#5405)

* refactor(web): centralize data table implementation

- route all TanStack table setup through a shared data-table hook to remove repeated state and row model wiring.
- move table rendering, static table wrappers, empty states, and primitive exports behind the data-table module.
- update feature tables and configuration editors to share the same table UX while preserving their existing workflows.

* refactor(web): trim data table public API

- remove unused data-table exports and dead static table helper types.
- keep internal table header, skeleton, empty state, and faceted filter helpers private to the data-table module.
- route feature imports through the data-table barrel to avoid subpath coupling.

* refactor(web): unify table rendering components

- centralize static table headers, bodies, empty states, and shared class names behind the data-table package.
- migrate settings, pricing, channel, key, subscription, and model tables to the shared table APIs.
- remove data-table exports for low-level table primitives so feature code uses one supported abstraction.

* perf(web): keep list tables fixed within page content

- make shared data table pages fill available height and scroll row data inside the table body.
- add a fixed content layout mode so selected list pages avoid page-level scrolling.
- apply the fixed table behavior to keys, logs, channels, models, users, redemptions, and subscriptions.

* perf(web): refine table pagination controls

- show total row counts instead of redundant page range text.
- tighten visible page buttons so pagination fits constrained table widths.
- align pagination controls and tune text hierarchy for clearer scanning.

* perf(web): stabilize model pricing table columns

- keep model pricing columns at fixed widths so headers do not collapse in narrow layouts.
- truncate long model names and pricing summaries within their cells instead of squeezing adjacent columns.

* refactor(web): simplify data table rendering internals

- split table body rendering into focused helpers for loading, empty, and row states.
- extract static table row and cell class resolution to reduce branching in the main component.
- reuse a single pagination page-size option list to avoid duplicated constants.

* perf(pricing): reduce dynamic pricing table render work

- reuse dynamic pricing field metadata instead of rebuilding it inside table columns.
- precompute formatted dynamic prices per tier and group to avoid repeated entry mapping for each cell.
- simplify select option construction in related dialogs while preserving the same choices.

* refactor(web): streamline pricing table rendering

- reuse translated endpoint select options between trigger data and menu items.
- precompute dynamic pricing maps per group so table cells only resolve formatted values.
- add local dynamic pricing type aliases to keep helper signatures readable.

* refactor(web): merge pricing table imports

* refactor(web): merge upstream ratio table imports

* refactor(web): merge channel selector table imports

* refactor(web): simplify tiered pricing select items

* refactor(web): reuse model ratio row state

* refactor(web): rely on table view row defaults

* refactor(web): reuse pagination state values

* refactor(web): hoist pagination size select items

* refactor(web): clarify static table body rows

* refactor(web): extract table page pagination rendering

* fix(web): remove direct hast type dependency

- rely on Shiki transformer contextual typing for line nodes.
- allow frontend typecheck to pass without an undeclared hast package.

* refactor(web): trim data table hook return API

- return only the TanStack table instance from useDataTable.
- keep internal state handling private because callers do not consume it directly.

* refactor(web): keep static table empty row private

- stop exporting the internal StaticDataTableEmptyRow helper.
- keep the public static table API focused on the table component and column type.

* refactor(web): hide data table view props from barrel

* refactor(web): remove stale long text lint override

* fix(web): keep pinned table columns opaque

- apply pinned column background classes after custom column classes.
- use an opaque hover background so scrolled content cannot show through fixed cells.

* refactor(data-table): organize shared table components

- group table primitives, page composition, toolbar controls, static tables, and hooks by responsibility.
- split shared view types, row rendering, header rendering, and pinned-column styling out of the main table view.
- keep the public data-table barrel stable while documenting the new ownership boundaries.

* fix(web): stabilize split table column sizing

- derive default colgroup widths from visible columns when split headers or header sizing are enabled.
- apply a fixed table layout with computed minimum width so header and body columns stay aligned.
- keep split-header containers from leaking horizontal overflow and avoid extra pinned-column borders.

* fix(web): set stable table utility column widths

- assign fixed widths to selection columns so shared colgroup sizing keeps checkbox cells compact.
- size id columns in redemption and user tables to keep split headers aligned with body rows.

* fix(web): align model metadata icon cells

- render compact provider avatars in the metadata icon column instead of wide wordmarks.
- position icons in a fixed-size wrapper so they line up with the existing icon header alignment.

* fix(status-badge): hide status dot by default

* fix(web): prevent user invite info overlap

- give the invite info and created-at columns explicit widths so table sizing reserves enough space.
- allow invite badges to wrap within the cell instead of spilling into adjacent columns.

* perf(data-table): cache pinned column class resolution

- reuse the pinned column lookup while table props stay stable to reduce repeated per-render work.
- share the resolved column class handler across unified and split-header table layouts.
- localize page-number screen reader labels so pagination remains accessible in every locale.

* refactor(data-table): tighten static table modes

- make StaticDataTable distinguish data-driven and children-only usage through explicit prop shapes.
- remove unsupported columns-without-data fallback after confirming no repository callers rely on it.
- default manual table modes away from unused local row models to reduce repeated table work.

* fix(data-table): make pinned edit column opaque

- use an opaque muted background for the active action column so sticky cells do not reveal scrolled content underneath.

* fix(data-table): prevent narrow column overlap

- apply stable header sizing to remaining desktop data table pages so constrained layouts scroll instead of compressing cells.
- add explicit widths for key, quota, badge, and timestamp columns that contain fixed-format content.
- constrain masked values and timestamp cells with truncation to keep content inside its assigned column.

* fix(table): align table cell content with headers

- remove extra inline padding from masked table text buttons so values start at the cell edge.
- tag status badges and offset leading badges inside table cells to match header text alignment.

* fix(table): prevent admin list column overflow

- widen redemption and subscription table columns so masked codes, timestamps, and localized headers fit.
- localize subscription ID headers and add Received amount translations across supported locales.

* fix(provider-badge): unify provider icon spacing

- add a shared provider badge component for icon and status label layout.
- reuse it in channel type and model vendor columns so OpenAI icons align consistently.
zhaodechao2008 pushed a commit to zhaodechao2008/new-api that referenced this pull request Jul 27, 2026
…le (QuantumNous#5405)

* refactor(web): centralize data table implementation

- route all TanStack table setup through a shared data-table hook to remove repeated state and row model wiring.
- move table rendering, static table wrappers, empty states, and primitive exports behind the data-table module.
- update feature tables and configuration editors to share the same table UX while preserving their existing workflows.

* refactor(web): trim data table public API

- remove unused data-table exports and dead static table helper types.
- keep internal table header, skeleton, empty state, and faceted filter helpers private to the data-table module.
- route feature imports through the data-table barrel to avoid subpath coupling.

* refactor(web): unify table rendering components

- centralize static table headers, bodies, empty states, and shared class names behind the data-table package.
- migrate settings, pricing, channel, key, subscription, and model tables to the shared table APIs.
- remove data-table exports for low-level table primitives so feature code uses one supported abstraction.

* perf(web): keep list tables fixed within page content

- make shared data table pages fill available height and scroll row data inside the table body.
- add a fixed content layout mode so selected list pages avoid page-level scrolling.
- apply the fixed table behavior to keys, logs, channels, models, users, redemptions, and subscriptions.

* perf(web): refine table pagination controls

- show total row counts instead of redundant page range text.
- tighten visible page buttons so pagination fits constrained table widths.
- align pagination controls and tune text hierarchy for clearer scanning.

* perf(web): stabilize model pricing table columns

- keep model pricing columns at fixed widths so headers do not collapse in narrow layouts.
- truncate long model names and pricing summaries within their cells instead of squeezing adjacent columns.

* refactor(web): simplify data table rendering internals

- split table body rendering into focused helpers for loading, empty, and row states.
- extract static table row and cell class resolution to reduce branching in the main component.
- reuse a single pagination page-size option list to avoid duplicated constants.

* perf(pricing): reduce dynamic pricing table render work

- reuse dynamic pricing field metadata instead of rebuilding it inside table columns.
- precompute formatted dynamic prices per tier and group to avoid repeated entry mapping for each cell.
- simplify select option construction in related dialogs while preserving the same choices.

* refactor(web): streamline pricing table rendering

- reuse translated endpoint select options between trigger data and menu items.
- precompute dynamic pricing maps per group so table cells only resolve formatted values.
- add local dynamic pricing type aliases to keep helper signatures readable.

* refactor(web): merge pricing table imports

* refactor(web): merge upstream ratio table imports

* refactor(web): merge channel selector table imports

* refactor(web): simplify tiered pricing select items

* refactor(web): reuse model ratio row state

* refactor(web): rely on table view row defaults

* refactor(web): reuse pagination state values

* refactor(web): hoist pagination size select items

* refactor(web): clarify static table body rows

* refactor(web): extract table page pagination rendering

* fix(web): remove direct hast type dependency

- rely on Shiki transformer contextual typing for line nodes.
- allow frontend typecheck to pass without an undeclared hast package.

* refactor(web): trim data table hook return API

- return only the TanStack table instance from useDataTable.
- keep internal state handling private because callers do not consume it directly.

* refactor(web): keep static table empty row private

- stop exporting the internal StaticDataTableEmptyRow helper.
- keep the public static table API focused on the table component and column type.

* refactor(web): hide data table view props from barrel

* refactor(web): remove stale long text lint override

* fix(web): keep pinned table columns opaque

- apply pinned column background classes after custom column classes.
- use an opaque hover background so scrolled content cannot show through fixed cells.

* refactor(data-table): organize shared table components

- group table primitives, page composition, toolbar controls, static tables, and hooks by responsibility.
- split shared view types, row rendering, header rendering, and pinned-column styling out of the main table view.
- keep the public data-table barrel stable while documenting the new ownership boundaries.

* fix(web): stabilize split table column sizing

- derive default colgroup widths from visible columns when split headers or header sizing are enabled.
- apply a fixed table layout with computed minimum width so header and body columns stay aligned.
- keep split-header containers from leaking horizontal overflow and avoid extra pinned-column borders.

* fix(web): set stable table utility column widths

- assign fixed widths to selection columns so shared colgroup sizing keeps checkbox cells compact.
- size id columns in redemption and user tables to keep split headers aligned with body rows.

* fix(web): align model metadata icon cells

- render compact provider avatars in the metadata icon column instead of wide wordmarks.
- position icons in a fixed-size wrapper so they line up with the existing icon header alignment.

* fix(status-badge): hide status dot by default

* fix(web): prevent user invite info overlap

- give the invite info and created-at columns explicit widths so table sizing reserves enough space.
- allow invite badges to wrap within the cell instead of spilling into adjacent columns.

* perf(data-table): cache pinned column class resolution

- reuse the pinned column lookup while table props stay stable to reduce repeated per-render work.
- share the resolved column class handler across unified and split-header table layouts.
- localize page-number screen reader labels so pagination remains accessible in every locale.

* refactor(data-table): tighten static table modes

- make StaticDataTable distinguish data-driven and children-only usage through explicit prop shapes.
- remove unsupported columns-without-data fallback after confirming no repository callers rely on it.
- default manual table modes away from unused local row models to reduce repeated table work.

* fix(data-table): make pinned edit column opaque

- use an opaque muted background for the active action column so sticky cells do not reveal scrolled content underneath.

* fix(data-table): prevent narrow column overlap

- apply stable header sizing to remaining desktop data table pages so constrained layouts scroll instead of compressing cells.
- add explicit widths for key, quota, badge, and timestamp columns that contain fixed-format content.
- constrain masked values and timestamp cells with truncation to keep content inside its assigned column.

* fix(table): align table cell content with headers

- remove extra inline padding from masked table text buttons so values start at the cell edge.
- tag status badges and offset leading badges inside table cells to match header text alignment.

* fix(table): prevent admin list column overflow

- widen redemption and subscription table columns so masked codes, timestamps, and localized headers fit.
- localize subscription ID headers and add Received amount translations across supported locales.

* fix(provider-badge): unify provider icon spacing

- add a shared provider badge component for icon and status label layout.
- reuse it in channel type and model vendor columns so OpenAI icons align consistently.
330079598 pushed a commit to 330079598/new-api that referenced this pull request Aug 19, 2026
…le (QuantumNous#5405)

* refactor(web): centralize data table implementation

- route all TanStack table setup through a shared data-table hook to remove repeated state and row model wiring.
- move table rendering, static table wrappers, empty states, and primitive exports behind the data-table module.
- update feature tables and configuration editors to share the same table UX while preserving their existing workflows.

* refactor(web): trim data table public API

- remove unused data-table exports and dead static table helper types.
- keep internal table header, skeleton, empty state, and faceted filter helpers private to the data-table module.
- route feature imports through the data-table barrel to avoid subpath coupling.

* refactor(web): unify table rendering components

- centralize static table headers, bodies, empty states, and shared class names behind the data-table package.
- migrate settings, pricing, channel, key, subscription, and model tables to the shared table APIs.
- remove data-table exports for low-level table primitives so feature code uses one supported abstraction.

* perf(web): keep list tables fixed within page content

- make shared data table pages fill available height and scroll row data inside the table body.
- add a fixed content layout mode so selected list pages avoid page-level scrolling.
- apply the fixed table behavior to keys, logs, channels, models, users, redemptions, and subscriptions.

* perf(web): refine table pagination controls

- show total row counts instead of redundant page range text.
- tighten visible page buttons so pagination fits constrained table widths.
- align pagination controls and tune text hierarchy for clearer scanning.

* perf(web): stabilize model pricing table columns

- keep model pricing columns at fixed widths so headers do not collapse in narrow layouts.
- truncate long model names and pricing summaries within their cells instead of squeezing adjacent columns.

* refactor(web): simplify data table rendering internals

- split table body rendering into focused helpers for loading, empty, and row states.
- extract static table row and cell class resolution to reduce branching in the main component.
- reuse a single pagination page-size option list to avoid duplicated constants.

* perf(pricing): reduce dynamic pricing table render work

- reuse dynamic pricing field metadata instead of rebuilding it inside table columns.
- precompute formatted dynamic prices per tier and group to avoid repeated entry mapping for each cell.
- simplify select option construction in related dialogs while preserving the same choices.

* refactor(web): streamline pricing table rendering

- reuse translated endpoint select options between trigger data and menu items.
- precompute dynamic pricing maps per group so table cells only resolve formatted values.
- add local dynamic pricing type aliases to keep helper signatures readable.

* refactor(web): merge pricing table imports

* refactor(web): merge upstream ratio table imports

* refactor(web): merge channel selector table imports

* refactor(web): simplify tiered pricing select items

* refactor(web): reuse model ratio row state

* refactor(web): rely on table view row defaults

* refactor(web): reuse pagination state values

* refactor(web): hoist pagination size select items

* refactor(web): clarify static table body rows

* refactor(web): extract table page pagination rendering

* fix(web): remove direct hast type dependency

- rely on Shiki transformer contextual typing for line nodes.
- allow frontend typecheck to pass without an undeclared hast package.

* refactor(web): trim data table hook return API

- return only the TanStack table instance from useDataTable.
- keep internal state handling private because callers do not consume it directly.

* refactor(web): keep static table empty row private

- stop exporting the internal StaticDataTableEmptyRow helper.
- keep the public static table API focused on the table component and column type.

* refactor(web): hide data table view props from barrel

* refactor(web): remove stale long text lint override

* fix(web): keep pinned table columns opaque

- apply pinned column background classes after custom column classes.
- use an opaque hover background so scrolled content cannot show through fixed cells.

* refactor(data-table): organize shared table components

- group table primitives, page composition, toolbar controls, static tables, and hooks by responsibility.
- split shared view types, row rendering, header rendering, and pinned-column styling out of the main table view.
- keep the public data-table barrel stable while documenting the new ownership boundaries.

* fix(web): stabilize split table column sizing

- derive default colgroup widths from visible columns when split headers or header sizing are enabled.
- apply a fixed table layout with computed minimum width so header and body columns stay aligned.
- keep split-header containers from leaking horizontal overflow and avoid extra pinned-column borders.

* fix(web): set stable table utility column widths

- assign fixed widths to selection columns so shared colgroup sizing keeps checkbox cells compact.
- size id columns in redemption and user tables to keep split headers aligned with body rows.

* fix(web): align model metadata icon cells

- render compact provider avatars in the metadata icon column instead of wide wordmarks.
- position icons in a fixed-size wrapper so they line up with the existing icon header alignment.

* fix(status-badge): hide status dot by default

* fix(web): prevent user invite info overlap

- give the invite info and created-at columns explicit widths so table sizing reserves enough space.
- allow invite badges to wrap within the cell instead of spilling into adjacent columns.

* perf(data-table): cache pinned column class resolution

- reuse the pinned column lookup while table props stay stable to reduce repeated per-render work.
- share the resolved column class handler across unified and split-header table layouts.
- localize page-number screen reader labels so pagination remains accessible in every locale.

* refactor(data-table): tighten static table modes

- make StaticDataTable distinguish data-driven and children-only usage through explicit prop shapes.
- remove unsupported columns-without-data fallback after confirming no repository callers rely on it.
- default manual table modes away from unused local row models to reduce repeated table work.

* fix(data-table): make pinned edit column opaque

- use an opaque muted background for the active action column so sticky cells do not reveal scrolled content underneath.

* fix(data-table): prevent narrow column overlap

- apply stable header sizing to remaining desktop data table pages so constrained layouts scroll instead of compressing cells.
- add explicit widths for key, quota, badge, and timestamp columns that contain fixed-format content.
- constrain masked values and timestamp cells with truncation to keep content inside its assigned column.

* fix(table): align table cell content with headers

- remove extra inline padding from masked table text buttons so values start at the cell edge.
- tag status badges and offset leading badges inside table cells to match header text alignment.

* fix(table): prevent admin list column overflow

- widen redemption and subscription table columns so masked codes, timestamps, and localized headers fit.
- localize subscription ID headers and add Received amount translations across supported locales.

* fix(provider-badge): unify provider icon spacing

- add a shared provider badge component for icon and status label layout.
- reuse it in channel type and model vendor columns so OpenAI icons align consistently.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants