fix: ResponsiveContainer -1 error & projects sort order#242
fix: ResponsiveContainer -1 error & projects sort order#242SurviveM merged 11 commits intoawsl-project:mainfrom
Conversation
- Use fixed pixel height instead of percentage for ResponsiveContainer - Add minHeight guard to ChartContainer's ResponsiveContainer - Upgrade bytedance/sonic v1.14.2 → v1.15.0 for Go 1.26.0 compat - Add keepPreviousData to usage stats query Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes awsl-project#204 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 Walkthrough总体概览本次更改通过以下方式修复了两个问题:为项目列表添加按创建时间的排序逻辑,优化图表容器的尺寸配置(添加显式宽高和最小高度),改进数据查询的缓存表现,以及微调 UI 组件布局。 变更内容
预计代码审查工作量🎯 3 (中等) | ⏱️ ~20 分钟 相关 PR
建议审查人
庆祝诗
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
web/src/pages/stats/index.tsx (3)
1022-1022: 多余的 JSX 空白字符字面量
</ComposedChart>{' '}中的{' '}是无意义的空格字面量,建议删除。🧹 建议修复
- </ComposedChart>{' '} + </ComposedChart>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/stats/index.tsx` at line 1022, Remove the meaningless whitespace literal after the ComposedChart closing tag: locate the closing JSX tag </ComposedChart> in the stats page component and delete the trailing {' '} token so the JSX no longer contains the redundant space literal; no other logic changes are needed (keep the rest of the surrounding JSX as-is).
729-739: 统计页的项目筛选列表未同步排序
projects/index.tsx已按createdAt升序排序(修复 issue#204),但此处的项目筛选 chips 仍直接遍历原始projects(API 返回顺序),导致两处展示顺序不一致。♻️ 建议在筛选列表中应用相同排序
-{projects && projects.length > 0 && ( +{projects && projects.length > 0 && (() => { + const sorted = projects.slice().sort((a, b) => { + const tA = new Date(a.createdAt).getTime() || 0; + const tB = new Date(b.createdAt).getTime() || 0; + return tA !== tB ? tA - tB : a.id - b.id; + }); + return ( <FilterSection label={t('stats.project')} showClear={projectId !== 'all'} onClear={() => setProjectId('all')} > - {projects.map((p) => ( + {sorted.map((p) => ( <FilterChip key={p.id} selected={projectId === String(p.id)} onClick={() => setProjectId(String(p.id))} > {p.name} </FilterChip> ))} </FilterSection> -)} + ); +})()}或者将排序逻辑提取为与
projects/index.tsx共享的 hook/util。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/stats/index.tsx` around lines 729 - 739, The project filter chips are rendered from the unsorted projects array (projects.map) causing order mismatch with projects/index.tsx which sorts by createdAt; update the rendering in the stats page to use the same sort (e.g., sort projects by createdAt ascending before mapping to FilterChip, or move the sorting into a shared utility/hook used by both pages) so FilterChip, projectId and setProjectId usage remains identical but the list order matches projects/index.tsx.
481-481:keepPreviousData下切换筛选条件无加载反馈启用
keepPreviousData后,切换筛选条件时isLoading保持false(占位数据存在),图表会静默显示旧数据直到新数据到达,用户无法感知正在刷新。♻️ 建议利用 `isFetching` 展示轻量加载状态
-const { data: stats, isLoading } = useUsageStats(filter); +const { data: stats, isLoading, isFetching, isPlaceholderData } = useUsageStats(filter);然后在图表卡片上叠加半透明遮罩或微型 spinner:
-<Card className="border-border/50 bg-card/50 backdrop-blur-sm"> +<Card className={cn('border-border/50 bg-card/50 backdrop-blur-sm', isFetching && 'opacity-60 pointer-events-none')}>Also applies to: 882-885
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/stats/index.tsx` at line 481, The current use of useUsageStats(filter) with keepPreviousData causes isLoading to stay false during filter switches, so add useUsageStats's isFetching (e.g., const { data: stats, isLoading, isFetching } = useUsageStats(filter)) and use isFetching to drive a lightweight loading UI; update the chart/card components that render stats to show a semi-transparent overlay or small spinner when isFetching is true (while still rendering previous stats) so users see a refresh indicator — adjust the chart container(s) where stats is used to conditionally render the overlay based on isFetching.web/src/pages/projects/index.tsx (1)
48-59: 排序比较器中每项Date对象重复实例化
new Date(a.createdAt)在Number.isFinite(...)检查和.getTime()取值时各创建一次,可合并为一次。♻️ 建议优化
return projects.slice().sort((a, b) => { - const timeA = Number.isFinite(new Date(a.createdAt).getTime()) - ? new Date(a.createdAt).getTime() - : 0; - const timeB = Number.isFinite(new Date(b.createdAt).getTime()) - ? new Date(b.createdAt).getTime() - : 0; + const rawA = new Date(a.createdAt).getTime(); + const rawB = new Date(b.createdAt).getTime(); + const timeA = Number.isFinite(rawA) ? rawA : 0; + const timeB = Number.isFinite(rawB) ? rawB : 0; if (timeA !== timeB) { return timeA - timeB; } return a.id - b.id; });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/pages/projects/index.tsx` around lines 48 - 59, The sort comparator repeatedly constructs Date objects for each side (new Date(a.createdAt) and new Date(b.createdAt)) twice; to fix, create and reuse a single Date instance per item inside the comparator (e.g., const dateA = new Date(a.createdAt) and const dateB = new Date(b.createdAt)), compute their getTime() once, validate with Number.isFinite and fall back to 0, then compare those cached times and finally a.id - b.id; update the comparator in the projects.slice().sort(...) callback to use these local dateA/dateB and timeA/timeB variables.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
web/src/components/layout/app-sidebar/nav-user.tsxweb/src/components/ui/chart.tsxweb/src/hooks/queries/use-usage-stats.tsweb/src/pages/projects/index.tsxweb/src/pages/stats/index.tsx
🔇 Additional comments (4)
web/src/hooks/queries/use-usage-stats.ts (1)
120-120:keepPreviousData用法正确,符合 TanStack Query v5 规范v5 中将此功能从
keepPreviousData: true(v4 选项)迁移至placeholderData: keepPreviousData,使用方式完全正确。web/src/components/ui/chart.tsx (1)
66-68: 修复正确,minHeight={1}有效防止-1错误
ChartContainer的包裹 div 上有硬编码的aspect-video类(aspect-ratio: 16/9),可为height="100%"提供可测量的父容器高度。同时,children的 TypeScript 类型(ResponsiveContainer['children'])从类型层面防止了误传另一个ResponsiveContainer导致的嵌套问题。web/src/pages/stats/index.tsx (1)
901-902: 固定像素高度修复正确
height={400}(数值型)让ResponsiveContainer直接使用 400px 固定高度而非测量父容器百分比,从根本上规避了-1错误。外层 div 的h-[400px]保证布局空间,min-h-[400px]防止容器坍缩,三者配合合理。web/src/pages/projects/index.tsx (1)
44-60: 排序实现逻辑正确
slice()避免原数组变异,createdAt升序 +id稳定兜底,Number.isFinite正确处理无效日期,[projects]依赖项设置准确。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@web/src/pages/projects/index.tsx`:
- Around line 48-59: The sort comparator repeatedly constructs Date objects for
each side (new Date(a.createdAt) and new Date(b.createdAt)) twice; to fix,
create and reuse a single Date instance per item inside the comparator (e.g.,
const dateA = new Date(a.createdAt) and const dateB = new Date(b.createdAt)),
compute their getTime() once, validate with Number.isFinite and fall back to 0,
then compare those cached times and finally a.id - b.id; update the comparator
in the projects.slice().sort(...) callback to use these local dateA/dateB and
timeA/timeB variables.
In `@web/src/pages/stats/index.tsx`:
- Line 1022: Remove the meaningless whitespace literal after the ComposedChart
closing tag: locate the closing JSX tag </ComposedChart> in the stats page
component and delete the trailing {' '} token so the JSX no longer contains the
redundant space literal; no other logic changes are needed (keep the rest of the
surrounding JSX as-is).
- Around line 729-739: The project filter chips are rendered from the unsorted
projects array (projects.map) causing order mismatch with projects/index.tsx
which sorts by createdAt; update the rendering in the stats page to use the same
sort (e.g., sort projects by createdAt ascending before mapping to FilterChip,
or move the sorting into a shared utility/hook used by both pages) so
FilterChip, projectId and setProjectId usage remains identical but the list
order matches projects/index.tsx.
- Line 481: The current use of useUsageStats(filter) with keepPreviousData
causes isLoading to stay false during filter switches, so add useUsageStats's
isFetching (e.g., const { data: stats, isLoading, isFetching } =
useUsageStats(filter)) and use isFetching to drive a lightweight loading UI;
update the chart/card components that render stats to show a semi-transparent
overlay or small spinner when isFetching is true (while still rendering previous
stats) so users see a refresh indicator — adjust the chart container(s) where
stats is used to conditionally render the overlay based on isFetching.
Summary
ResponsiveContainerreportingwidth(-1) height(-1)by using fixed pixel height instead of percentageminHeightguard toChartContainer'sResponsiveContainercreatedAtto ensure consistent display order on refreshbytedance/sonicv1.14.2 → v1.15.0 for Go 1.26.0 compatibilitykeepPreviousDatato usage stats queryCloses #204
Closes #220
🤖 Generated with Claude Code
Summary by CodeRabbit
发布说明
新功能
Bug 修复
样式