Polish visual issues on the Console Home Page - #4617
Conversation
📝 WalkthroughWalkthroughThe home page replaces floating logos with animated framework template cards. It updates responsive styling, application navigation, avatar rendering, action buttons, quick-link breakpoints, translations, and related tests. ChangesHome experience
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant StartBuildingSection
participant FrameworkFlipCard
participant ApplicationTemplate
StartBuildingSection->>FrameworkFlipCard: Render one or three framework slots
FrameworkFlipCard->>ApplicationTemplate: Navigate with selected template
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
frontend/apps/console/src/features/home/components/FrameworkFlipCard.tsxESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. frontend/apps/console/src/features/home/components/NextStepsSection.tsxESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. frontend/apps/console/src/features/home/components/StartBuildingSection.tsxESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.
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.
Actionable comments posted: 3
🤖 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
`@frontend/apps/console/src/features/home/components/cards/InviteMembersCard.tsx`:
- Around line 57-65: Update the fallback construction in the users.map callback
to pass user.display ?? user.id to getInitials, preserving display-based
initials while providing a stable user.id fallback when display is missing.
In `@frontend/apps/console/src/features/home/components/FrameworkFlipCard.tsx`:
- Around line 76-101: Update the framework cards rendered by the frameworks.map
loop to derive active state from the animation and set inactive buttons to
tabIndex={-1} with aria-hidden, while keeping the active card
keyboard-focusable. Ensure the animation does not hide or advance away from a
card while it has focus.
In `@frontend/apps/console/src/features/home/components/StartBuildingSection.tsx`:
- Around line 111-129: Replace the clickable Box in the application-count action
with a semantic Button or link that invokes goToApplicationList and supports
keyboard activation. Preserve the existing inline layout, color, spacing,
cursor, and nowrap styling on the new control, while keeping the Typography
content and ArrowRight indicator unchanged.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d028a7b0-eafa-4b29-815b-25a1c11266ac
📒 Files selected for processing (11)
frontend/apps/console/src/features/home/components/FrameworkFlipCard.tsxfrontend/apps/console/src/features/home/components/HomeFloatingLogos.tsxfrontend/apps/console/src/features/home/components/StartBuildingSection.tsxfrontend/apps/console/src/features/home/components/__tests__/HomeFloatingLogos.test.tsxfrontend/apps/console/src/features/home/components/__tests__/StartBuildingSection.test.tsxfrontend/apps/console/src/features/home/components/cards/HomeNextStepCard.tsxfrontend/apps/console/src/features/home/components/cards/InviteMembersCard.tsxfrontend/apps/console/src/features/home/components/cards/__tests__/InviteMembersCard.test.tsxfrontend/apps/console/src/features/home/components/frameworks.tsfrontend/packages/configure-users/src/index.tsfrontend/packages/i18n/src/locales/en-US.ts
💤 Files with no reviewable changes (3)
- frontend/apps/console/src/features/home/components/tests/StartBuildingSection.test.tsx
- frontend/apps/console/src/features/home/components/HomeFloatingLogos.tsx
- frontend/apps/console/src/features/home/components/tests/HomeFloatingLogos.test.tsx
| {users.map((user) => { | ||
| const picture = user.attributes?.picture; | ||
|
|
||
| return ( | ||
| <motion.div key={user.id} variants={avatarVariants}> | ||
| <ResourceAvatar | ||
| value={typeof picture === 'string' ? picture : undefined} | ||
| size={32} | ||
| fallback={`${UserConstants.DEFAULT_AVATAR_PREFIX}${getInitials(user.display)}`} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use user.id when user.display is missing.
display is optional, but the fallback calls getInitials(user.display). Users without a display value do not receive a stable initials fallback. Use getInitials(user.display ?? user.id), matching the existing UsersList.tsx behavior.
Proposed fix
{users.map((user) => {
+ const display = user.display ?? user.id;
const picture = user.attributes?.picture;
return (
<motion.div key={user.id} variants={avatarVariants}>
<ResourceAvatar
value={typeof picture === 'string' ? picture : undefined}
size={32}
- fallback={`${UserConstants.DEFAULT_AVATAR_PREFIX}${getInitials(user.display)}`}
+ fallback={`${UserConstants.DEFAULT_AVATAR_PREFIX}${getInitials(display)}`}
/>
</motion.div>
);
})}📝 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.
| {users.map((user) => { | |
| const picture = user.attributes?.picture; | |
| return ( | |
| <motion.div key={user.id} variants={avatarVariants}> | |
| <ResourceAvatar | |
| value={typeof picture === 'string' ? picture : undefined} | |
| size={32} | |
| fallback={`${UserConstants.DEFAULT_AVATAR_PREFIX}${getInitials(user.display)}`} | |
| {users.map((user) => { | |
| const display = user.display ?? user.id; | |
| const picture = user.attributes?.picture; | |
| return ( | |
| <motion.div key={user.id} variants={avatarVariants}> | |
| <ResourceAvatar | |
| value={typeof picture === 'string' ? picture : undefined} | |
| size={32} | |
| fallback={`${UserConstants.DEFAULT_AVATAR_PREFIX}${getInitials(display)}`} |
🤖 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
`@frontend/apps/console/src/features/home/components/cards/InviteMembersCard.tsx`
around lines 57 - 65, Update the fallback construction in the users.map callback
to pass user.display ?? user.id to getInitials, preserving display-based
initials while providing a stable user.id fallback when display is missing.
| {frameworks.map(({Icon, label, templateId}, index) => ( | ||
| <Box | ||
| key={label} | ||
| component="button" | ||
| type="button" | ||
| onClick={() => onSelect(templateId)} | ||
| sx={{ | ||
| position: 'absolute', | ||
| inset: 0, | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| gap: 1.5, | ||
| px: 2, | ||
| border: '1px solid', | ||
| borderColor: isDark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.08)', | ||
| borderRadius: 1.5, | ||
| bgcolor: isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.03)', | ||
| cursor: 'pointer', | ||
| font: 'inherit', | ||
| backfaceVisibility: 'hidden', | ||
| animation: `${animationName} ${cycleSeconds}s infinite backwards`, | ||
| animationDelay: `${index * staggerSeconds}s`, | ||
| '&:hover': { | ||
| borderColor: 'primary.main', | ||
| }, | ||
| }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Remove inactive cards from keyboard navigation.
At Line 76, every framework card remains a focusable native button. opacity: 0 and pointerEvents: 'none' do not remove an inactive button from the tab order. Keyboard users can focus and activate cards that are not visible.
Synchronize the active card state with the animation. Set inactive cards to tabIndex={-1} and hide them from assistive technology. Pause or preserve the active card while it has focus.
🤖 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 `@frontend/apps/console/src/features/home/components/FrameworkFlipCard.tsx`
around lines 76 - 101, Update the framework cards rendered by the frameworks.map
loop to derive active state from the animation and set inactive buttons to
tabIndex={-1} with aria-hidden, while keeping the active card
keyboard-focusable. Ensure the animation does not hide or advance away from a
card while it has focus.
| <Box | ||
| onClick={goToApplicationList} | ||
| sx={{ | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| gap: 0.25, | ||
| color: 'primary.light', | ||
| cursor: 'pointer', | ||
| whiteSpace: 'nowrap', | ||
| }} | ||
| > | ||
| <Typography variant="body2" color="inherit"> | ||
| {t('start_building.hero.status.app_count', { | ||
| count: totalApps, | ||
| defaultValue: '{{count}} application', | ||
| })} | ||
| </Typography> | ||
| <ArrowRight size={16} /> | ||
| </Box> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Use a semantic control for the application-count action.
At Line 111, this Box only handles pointer clicks. Keyboard users cannot focus it or activate the application-list navigation.
Use a Button or link component that calls goToApplicationList. Preserve the current inline styling on that semantic control.
🤖 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 `@frontend/apps/console/src/features/home/components/StartBuildingSection.tsx`
around lines 111 - 129, Replace the clickable Box in the application-count
action with a semantic Button or link that invokes goToApplicationList and
supports keyboard activation. Preserve the existing inline layout, color,
spacing, cursor, and nowrap styling on the new control, while keeping the
Typography content and ArrowRight indicator unchanged.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
ab72548 to
d818e30
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Replaces the scrolling background logo decoration with a compact, responsive SDK flip-card widget that cycles through ThunderID's supported frameworks and links to their application templates. Also aligns the "Add Users" quick-link card's avatars with the same photo-or-initials rendering used on the Users list page.
d818e30 to
bf3d34c
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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
`@frontend/apps/console/src/features/home/components/__tests__/FrameworkFlipCard.test.tsx`:
- Around line 47-51: Update the FrameworkFlipCard navigation assertion to import
and use RouteConfig.applications.types() when constructing the expected route
prefix, replacing the hardcoded "/applications/types" literal while preserving
the selected template query matching.
- Around line 37-45: Update the FrameworkFlipCard tests to target a stable
selector for individual slots, then assert the exact expected default slot count
in the default-render test and exactly one matching slot when slotCount={1}.
Ensure the selector identifies only slot elements rather than generic nested
divs.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d06f1683-bb2e-4467-8df6-4754380356eb
📒 Files selected for processing (12)
frontend/apps/console/src/features/home/components/FrameworkFlipCard.tsxfrontend/apps/console/src/features/home/components/HomeFloatingLogos.tsxfrontend/apps/console/src/features/home/components/NextStepsSection.tsxfrontend/apps/console/src/features/home/components/StartBuildingSection.tsxfrontend/apps/console/src/features/home/components/__tests__/FrameworkFlipCard.test.tsxfrontend/apps/console/src/features/home/components/__tests__/HomeFloatingLogos.test.tsxfrontend/apps/console/src/features/home/components/__tests__/StartBuildingSection.test.tsxfrontend/apps/console/src/features/home/components/cards/HomeNextStepCard.tsxfrontend/apps/console/src/features/home/components/cards/InviteMembersCard.tsxfrontend/apps/console/src/features/home/components/cards/__tests__/InviteMembersCard.test.tsxfrontend/packages/configure-users/src/index.tsfrontend/packages/i18n/src/locales/en-US.ts
💤 Files with no reviewable changes (2)
- frontend/apps/console/src/features/home/components/HomeFloatingLogos.tsx
- frontend/apps/console/src/features/home/components/tests/HomeFloatingLogos.test.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
- frontend/packages/configure-users/src/index.ts
- frontend/packages/i18n/src/locales/en-US.ts
- frontend/apps/console/src/features/home/components/cards/HomeNextStepCard.tsx
- frontend/apps/console/src/features/home/components/cards/InviteMembersCard.tsx
- frontend/apps/console/src/features/home/components/NextStepsSection.tsx
- frontend/apps/console/src/features/home/components/tests/StartBuildingSection.test.tsx
- frontend/apps/console/src/features/home/components/StartBuildingSection.tsx
- frontend/apps/console/src/features/home/components/FrameworkFlipCard.tsx
| it('renders the default number of slots', () => { | ||
| render(<FrameworkFlipCard />); | ||
| expect(screen.getAllByRole('button').length).toBeGreaterThan(0); | ||
| }); | ||
|
|
||
| it('renders only a single slot when slotCount is 1', () => { | ||
| const {container} = render(<FrameworkFlipCard slotCount={1} />); | ||
| expect(container.querySelectorAll(':scope > div > div').length).toBeGreaterThan(0); | ||
| }); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Assert the slot count exactly.
Both assertions only verify that at least one element renders. They pass if the default count changes or if slotCount={1} still renders multiple slots. Add a stable slot selector and assert the expected default count and exactly one slot.
🤖 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
`@frontend/apps/console/src/features/home/components/__tests__/FrameworkFlipCard.test.tsx`
around lines 37 - 45, Update the FrameworkFlipCard tests to target a stable
selector for individual slots, then assert the exact expected default slot count
in the default-render test and exactly one matching slot when slotCount={1}.
Ensure the selector identifies only slot elements rather than generic nested
divs.
| it('navigates to the application types page with the selected template on click', () => { | ||
| render(<FrameworkFlipCard />); | ||
| const buttons = screen.getAllByRole('button'); | ||
| fireEvent.click(buttons[0]); | ||
| expect(mockNavigate).toHaveBeenCalledWith(expect.stringMatching(/^\/applications\/types\?type=.+/)); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Resolve the expected route through RouteConfig.
The literal route duplicates the configured destination. Import RouteConfig and build the expected navigation prefix from RouteConfig.applications.types() so this test changes with the route configuration.
As per coding guidelines, "Never hardcode route paths in frontend app code."
🤖 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
`@frontend/apps/console/src/features/home/components/__tests__/FrameworkFlipCard.test.tsx`
around lines 47 - 51, Update the FrameworkFlipCard navigation assertion to
import and use RouteConfig.applications.types() when constructing the expected
route prefix, replacing the hardcoded "/applications/types" literal while
preserving the selected template query matching.
Source: Coding guidelines
Purpose
The Home page's "Integrate ThunderID into your application" banner used a purely decorative, always-scrolling background of technology logos and didn't reflect the SDKs ThunderID actually ships. The "Add Users" quick-link card also rendered every member as flat initials, never showing their real profile photo even when one was set.
Approach
HomeFloatingLogosbackground withFrameworkFlipCard, a compact widget that flips between ThunderID's real supported SDKs (React, Next.js, Vue, Nuxt, Express, JavaScript), pulling icons from@thunderid/components. Each card links to its application template (applications/types?type=<ID>).lg, one card atlg–xl, three atxl+, so it never squeezes the banner's title/actions at in-between viewport widths.Chipto inline text with anArrowRighticon, and fixed it to navigate to the actual application listing instead of the create-flow picker.ArrowRight, for a lighter, consistent call-to-action style.InviteMembersCard's avatar preview to useResourceAvatar(the same component the Users list page uses), so members with a profile photo show it instead of always falling back to initials.Related Issues
Related PRs
Checklist
breaking changelabel added.Security checks
Summary by CodeRabbit