-
Notifications
You must be signed in to change notification settings - Fork 1
refactor(member): redesign page with Bento grid layout and compact styling #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…yling - Add MemberStatsCard component for stats display (roles, teams, points) - Replace Card components with bordered divs for Bento aesthetic - Implement 12-column grid layout with compact spacing (px-4 py-3, gap-3) - Update header with smaller avatar and simplified UI - Add subtle hover states (hover:bg-accent/30, hover:border-border) - Redesign effort and goals charts with cleaner borders - Compact role cards with vertical sections and hover effects - Simplify team section triggering with button instead of Card header - Inline chart logic in main page component - Remove member-charts-section wrapper component
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis pull request redesigns the member detail page by decomposing MemberChartsSection into standalone chart components, replacing Card-based layouts with div-based designs, adding a new MemberStatsCard, simplifying the MemberHeader props, and migrating chart rendering from DashboardMetrics to Recharts components. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/member/[id]/_components/member-effort-chart.tsx (1)
51-57: Potential key collision if role titles are not unique.Using
role.titleas the chartConfig key could cause data loss if two roles share the same title. Consider usingrole.idas the key instead.🔎 Proposed fix
- const chartConfig: ChartConfig = rolesWithEffort.reduce((acc, role) => { - acc[role.title] = { - label: role.title, - color: role.color, - }; - return acc; - }, {} as ChartConfig); + const chartConfig: ChartConfig = rolesWithEffort.reduce((acc, role) => { + acc[role.id] = { + label: role.title, + color: role.color, + }; + return acc; + }, {} as ChartConfig);You would also need to update the
chartDatamapping to userole.idas the name key if making this change, ensuring consistency between the config and data.
🧹 Nitpick comments (3)
src/app/member/[id]/_components/member-goals-chart.tsx (1)
76-89: Consider addingPolarRadiusAxisfor better chart readability.The radar chart lacks a radial axis, which would help users understand the scale of progress values (0-100%). Without it, the chart shows relative proportions but not absolute progress.
🔎 Proposed improvement
import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"; +import { PolarRadiusAxis } from "recharts"; ... <RadarChart data={chartData}> <ChartTooltip cursor={false} content={<ChartTooltipContent />} /> <PolarAngleAxis dataKey="goal" /> <PolarGrid /> + <PolarRadiusAxis angle={30} domain={[0, 100]} tick={false} axisLine={false} /> <Radarsrc/app/member/[id]/_components/member-header.tsx (1)
47-52: Consider usingasChildpattern for Link+Button composition.Nesting a
Buttoninside aLinkcreates two focusable elements. The recommended pattern in this codebase (per Radix UI conventions) is to useasChildon the Link or render the Button with an anchor element.🔎 Alternative approach
- <Link href={`/check-in/${member.id}`} className="shrink-0"> - <Button variant="default" size="sm" className="gap-1.5"> - <ClipboardCheck className="h-3.5 w-3.5" /> - Check-in - </Button> - </Link> + <Button variant="default" size="sm" className="gap-1.5 shrink-0" asChild> + <Link href={`/check-in/${member.id}`}> + <ClipboardCheck className="h-3.5 w-3.5" /> + Check-in + </Link> + </Button>src/app/member/[id]/_components/member-stats-card.tsx (1)
42-42: Add explicit React import forReact.ReactNodetype.The
React.ReactNodetype is used on line 42, butReactis not imported. While this may work in some setups due to the new JSX transform, explicit imports are safer for type annotations.🔎 Proposed fix
"use client"; +import type React from "react"; import { Briefcase, Target, Users } from "lucide-react";
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/app/member/[id]/_components/member-charts-section.tsx(0 hunks)src/app/member/[id]/_components/member-effort-chart.tsx(3 hunks)src/app/member/[id]/_components/member-goals-chart.tsx(3 hunks)src/app/member/[id]/_components/member-header.tsx(2 hunks)src/app/member/[id]/_components/member-page-client.tsx(3 hunks)src/app/member/[id]/_components/member-role-card.tsx(1 hunks)src/app/member/[id]/_components/member-stats-card.tsx(1 hunks)src/app/member/[id]/_components/team-section.tsx(3 hunks)
💤 Files with no reviewable changes (1)
- src/app/member/[id]/_components/member-charts-section.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use the tRPC dual API pattern: for server components, directly call tRPC server API (10x faster); for client components, use React hooks with TanStack Query
Use inline type imports with @trivago/prettier-plugin-sort-imports for consistent import orderingUse TypeScript 5.9 with strict type checking for all frontend and backend code
Files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsxsrc/app/member/[id]/_components/team-section.tsxsrc/app/member/[id]/_components/member-header.tsx
src/**/*.tsx
📄 CodeRabbit inference engine (GEMINI.md)
Prefer Server Components for initial data fetching; use Client Components ('use client') only for interactivity
Files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsxsrc/app/member/[id]/_components/team-section.tsxsrc/app/member/[id]/_components/member-header.tsx
src/**/*/*.tsx
📄 CodeRabbit inference engine (GEMINI.md)
Client Components must use
import { api } from '@/trpc/react'for standard HTTP/Hooks wrapper
Files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsxsrc/app/member/[id]/_components/team-section.tsxsrc/app/member/[id]/_components/member-header.tsx
**/*.tsx
📄 CodeRabbit inference engine (GEMINI.md)
Use Tailwind CSS 4 for styling with shadcn/ui and Radix UI primitive components
Files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsxsrc/app/member/[id]/_components/team-section.tsxsrc/app/member/[id]/_components/member-header.tsx
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/dashboard/**/*.tsx : Consolidate dashboard metric card duplication: add readOnly mode to dashboard-metric-card.tsx instead of maintaining separate public-dashboard-metric-card.tsx component
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/dashboard/**/*.{ts,tsx} : Three-stage metrics transformation pipeline: Stage 1 (API → DataPoints via DataIngestionTransformer), Stage 2 (DataPoints → ChartConfig via ChartTransformer), Stage 3 (ChartConfig → UI via DashboardMetricChart with Recharts)
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/teams/[teamId]/**/*.tsx : Consolidate role node duplication: extract shared RoleNodeTemplate from role-node.tsx and public-role-node.tsx with isEditable prop instead of maintaining separate components
Applied to files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsxsrc/app/member/[id]/_components/team-section.tsxsrc/app/member/[id]/_components/member-header.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/dashboard/**/*.tsx : Consolidate dashboard metric card duplication: add readOnly mode to dashboard-metric-card.tsx instead of maintaining separate public-dashboard-metric-card.tsx component
Applied to files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsxsrc/app/member/[id]/_components/team-section.tsxsrc/app/member/[id]/_components/member-header.tsx
📚 Learning: 2025-12-20T22:12:00.566Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-20T22:12:00.566Z
Learning: Applies to src/app/teams/[teamId]/**/*.tsx : React Flow nodes must store minimal data (e.g., just `roleId`); fetch full Role data from TanStack Query cache in the Node component to keep canvas and sidebars in sync
Applied to files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsxsrc/app/member/[id]/_components/team-section.tsxsrc/app/member/[id]/_components/member-header.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/metric/_components/**/*.tsx : Consider factory pattern for metric dialogs to reduce the 5 nearly identical wrapper components (currently duplicated across providers)
Applied to files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/metric/_components/**/*.tsx : Use MetricDialogBase from base/ for shared metric dialog functionality to reduce duplication across provider-specific dialogs
Applied to files:
src/app/member/[id]/_components/member-stats-card.tsxsrc/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/dashboard/**/*.{ts,tsx} : Three-stage metrics transformation pipeline: Stage 1 (API → DataPoints via DataIngestionTransformer), Stage 2 (DataPoints → ChartConfig via ChartTransformer), Stage 3 (ChartConfig → UI via DashboardMetricChart with Recharts)
Applied to files:
src/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/member-effort-chart.tsxsrc/app/member/[id]/_components/member-goals-chart.tsx
📚 Learning: 2025-12-20T22:12:00.566Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-20T22:12:00.566Z
Learning: Applies to src/app/teams/[teamId]/**/*.tsx : Avoid modifying `enrichNodesWithRoleData` flow without understanding the complete canvas serialization logic for saving/loading React Flow nodes to the database
Applied to files:
src/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/team-section.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/teams/[teamId]/**/*.{ts,tsx} : For new canvas node types: create component in teams/[teamId]/_components/, add to TeamNode union in types/canvas.ts, register in nodeTypes in team-canvas.tsx, and update serialization in canvas-serialization.ts
Applied to files:
src/app/member/[id]/_components/member-page-client.tsxsrc/app/member/[id]/_components/team-section.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/teams/[teamId]/**/*.{ts,tsx} : Cache-first node pattern: Role nodes store only roleId; fetch display data from TanStack Query cache using useRoleData hook rather than storing denormalized data
Applied to files:
src/app/member/[id]/_components/member-role-card.tsxsrc/app/member/[id]/_components/team-section.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/metric/_components/**/*.tsx : Metric dialogs should follow the pattern: [Provider]MetricDialog.tsx (dialog wrapper) + [Provider]MetricContent.tsx (form content) and be registered in src/app/metric/_components/index.ts
Applied to files:
src/app/member/[id]/_components/member-role-card.tsx
📚 Learning: 2025-12-20T22:12:00.566Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-20T22:12:00.566Z
Learning: Applies to src/**/*.tsx : Prefer Server Components for initial data fetching; use Client Components ('use client') only for interactivity
Applied to files:
src/app/member/[id]/_components/member-effort-chart.tsx
📚 Learning: 2025-12-20T22:12:00.566Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-20T22:12:00.566Z
Learning: Applies to src/**/*/*.tsx : Client Components must use `import { api } from '@/trpc/react'` for standard HTTP/Hooks wrapper
Applied to files:
src/app/member/[id]/_components/member-effort-chart.tsx
📚 Learning: 2025-12-20T16:32:46.818Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-20T16:32:46.818Z
Learning: Applies to src/app/teams/[teamId]/**/*.{ts,tsx} : Use Zustand store with Context pattern (TeamStoreContext) and access via useTeamStore hook. For callbacks that need current state, use useTeamStoreApi() to avoid stale closures.
Applied to files:
src/app/member/[id]/_components/team-section.tsx
📚 Learning: 2025-12-20T22:12:00.566Z
Learnt from: CR
Repo: drifter089/orgOS PR: 0
File: GEMINI.md:0-0
Timestamp: 2025-12-20T22:12:00.566Z
Learning: Applies to src/app/teams/[teamId]/**/*.ts : Canvas changes must be debounced (2s) and saved via `editSession` logic to handle concurrent team edits safely
Applied to files:
src/app/member/[id]/_components/team-section.tsx
🧬 Code graph analysis (3)
src/app/member/[id]/_components/member-page-client.tsx (6)
src/app/member/[id]/_components/member-header.tsx (1)
MemberHeader(16-55)src/components/ui/skeleton.tsx (1)
Skeleton(13-13)src/app/member/[id]/_components/member-stats-card.tsx (1)
MemberStatsCard(11-35)src/app/member/[id]/_components/member-effort-chart.tsx (1)
MemberEffortChart(22-129)src/app/member/[id]/_components/member-goals-chart.tsx (1)
MemberGoalsChart(23-94)src/app/member/[id]/_components/team-section.tsx (1)
TeamSection(25-86)
src/app/member/[id]/_components/team-section.tsx (1)
src/components/ui/collapsible.tsx (2)
CollapsibleTrigger(33-33)CollapsibleContent(33-33)
src/app/member/[id]/_components/member-header.tsx (3)
src/trpc/react.tsx (1)
RouterOutputs(42-42)src/components/ui/avatar.tsx (2)
Avatar(54-54)AvatarFallback(54-54)src/components/ui/button.tsx (1)
Button(62-62)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (12)
src/app/member/[id]/_components/member-goals-chart.tsx (2)
23-42: LGTM!The empty state handling is clean with consistent styling. The early return pattern keeps the component readable.
44-54: LGTM!Good defensive coding with the progress value clamping (
Math.max(0, Math.min(100, ...))), ensuring the radar chart renders correctly even with unexpected input values.src/app/member/[id]/_components/member-header.tsx (2)
16-26: LGTM!The fallback logic for initials and userName handles edge cases well, with a sensible cascade from full name → email → default value.
44-44: Remove the optional chaining from line 20—The
Membertype definesemail: string(required, non-null), so line 44's unconditional render of{member.email}is correct. Line 20's defensivemember.email?.[0]?.toUpperCase()is unnecessary. Simplify tomember.email[0].toUpperCase().src/app/member/[id]/_components/team-section.tsx (2)
41-63: LGTM!Good accessibility implementation with dynamic
aria-labelfor the expand/collapse button. The visual feedback with chevron icons and the compact header layout align well with the Bento grid aesthetic.
67-81: LGTM!The responsive grid layout (
grid-cols-1 lg:grid-cols-2) and consistent gap spacing work well for the role cards.src/app/member/[id]/_components/member-stats-card.tsx (1)
11-35: LGTM!Clean component structure with good separation of concerns. The
StatBlockextraction keeps the main component readable, and the pluralization logic handles edge cases correctly.src/app/member/[id]/_components/member-effort-chart.tsx (2)
93-121: LGTM!The custom center label implementation using SVG
<text>and<tspan>elements is well-structured and provides a clear total display. The viewBox guard ensures safe property access.
30-43: LGTM!Consistent empty state styling matching the other chart components in this PR.
src/app/member/[id]/_components/member-role-card.tsx (1)
1-72: Clean Bento grid refactor with proper sanitization.The restructuring from Card-based to div-based layout successfully achieves the minimalist Bento aesthetic described in the PR objectives. The stripHtml sanitization on line 18 properly prevents XSS when rendering
role.purpose. The conditional rendering logic for purpose, charts, and KPIs is clear and handles all edge cases appropriately.src/app/member/[id]/_components/member-page-client.tsx (2)
23-42: Well-structured loading state.The expanded skeleton layout properly reflects the final multi-block responsive grid structure, providing users with an accurate preview of the content layout during loading.
132-175: Excellent responsive grid layout with clear structure.The 12-column grid layout with explicit block comments (Header, Stats, Charts, Team Sections) makes the page structure immediately clear. The gap-3 spacing and responsive breakpoints align well with the compact Bento grid design goals.
| // Process goals data for the radar chart | ||
| const goalsData = roles | ||
| .filter((role) => { | ||
| if (!role.metricId) return false; | ||
| const chart = chartsByMetricId.get(role.metricId); | ||
| return chart?.goalProgress != null; | ||
| }) | ||
| .map((role) => { | ||
| const chart = chartsByMetricId.get(role.metricId!)!; | ||
| return { | ||
| goalName: chart.metric.name ?? role.metric?.name ?? "Unknown", | ||
| progressPercent: chart.goalProgress!.progressPercent, | ||
| status: chart.goalProgress!.status, | ||
| }; | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe non-null assertions in goalsData preprocessing.
The chained .filter().map() pattern uses multiple non-null assertion operators (!) that bypass TypeScript's type safety:
- Line 124:
chartsByMetricId.get(role.metricId!)!— double assertion assumes bothmetricIdexists and.get()returns a value - Lines 127-128:
chart.goalProgress!— assertion assumesgoalProgressis non-null
While the filter checks these conditions, TypeScript's type narrowing doesn't carry from .filter() to .map() in chains. This creates brittle code that could crash if the Map structure changes or filter logic is modified.
🔎 Safer implementation without non-null assertions
- // Process goals data for the radar chart
- const goalsData = roles
- .filter((role) => {
- if (!role.metricId) return false;
- const chart = chartsByMetricId.get(role.metricId);
- return chart?.goalProgress != null;
- })
- .map((role) => {
- const chart = chartsByMetricId.get(role.metricId!)!;
- return {
- goalName: chart.metric.name ?? role.metric?.name ?? "Unknown",
- progressPercent: chart.goalProgress!.progressPercent,
- status: chart.goalProgress!.status,
- };
- });
+ // Process goals data for the radar chart
+ const goalsData = roles
+ .map((role) => {
+ if (!role.metricId) return null;
+ const chart = chartsByMetricId.get(role.metricId);
+ if (!chart?.goalProgress) return null;
+ return {
+ goalName: chart.metric.name ?? role.metric?.name ?? "Unknown",
+ progressPercent: chart.goalProgress.progressPercent,
+ status: chart.goalProgress.status,
+ };
+ })
+ .filter((goal): goal is NonNullable<typeof goal> => goal !== null);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Process goals data for the radar chart | |
| const goalsData = roles | |
| .filter((role) => { | |
| if (!role.metricId) return false; | |
| const chart = chartsByMetricId.get(role.metricId); | |
| return chart?.goalProgress != null; | |
| }) | |
| .map((role) => { | |
| const chart = chartsByMetricId.get(role.metricId!)!; | |
| return { | |
| goalName: chart.metric.name ?? role.metric?.name ?? "Unknown", | |
| progressPercent: chart.goalProgress!.progressPercent, | |
| status: chart.goalProgress!.status, | |
| }; | |
| }); | |
| // Process goals data for the radar chart | |
| const goalsData = roles | |
| .map((role) => { | |
| if (!role.metricId) return null; | |
| const chart = chartsByMetricId.get(role.metricId); | |
| if (!chart?.goalProgress) return null; | |
| return { | |
| goalName: chart.metric.name ?? role.metric?.name ?? "Unknown", | |
| progressPercent: chart.goalProgress.progressPercent, | |
| status: chart.goalProgress.status, | |
| }; | |
| }) | |
| .filter((goal): goal is NonNullable<typeof goal> => goal !== null); |
🤖 Prompt for AI Agents
In src/app/member/[id]/_components/member-page-client.tsx around lines 116 to
130, replace the current chained filter/map that uses non-null assertion
operators with a single pass that explicitly checks for missing values: iterate
roles (e.g., with reduce or flatMap), for each role do const metricId =
role.metricId; if (!metricId) continue/skip; const chart =
chartsByMetricId.get(metricId); if (!chart || chart.goalProgress == null)
continue/skip; then push an object using chart.metric?.name ?? role.metric?.name
?? "Unknown" and chart.goalProgress.progressPercent and
chart.goalProgress.status (no ! assertions). This preserves the original output
but avoids unsafe non-null assertions and keeps TypeScript type-safety.
Summary
Redesigned the member profile page with a Japanese Bento grid-inspired, minimalist UI featuring compact spacing, clean borders, and subtle hover states.
Changes
gap-3spacing for compact presentationDesign Details
border-border/60andhover:bg-accent/30for subtle interactionspx-4 py-3throughout all componentsSummary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.