-
Notifications
You must be signed in to change notification settings - Fork 186
✨(app): Redesign project creation page with step-based navigation #3864
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
Redesigned the project new page to improve user experience with a clearer step-based navigation system and better component organization. Key changes: - Added step indicator showing current progress in project creation flow - Refactored InstallationSelector into smaller, focused sub-components (EmptyStateCard, HeaderActions, RepositoryList, InstallationDropdown) - Added JackAndOctcat illustration component for empty states - Improved styling consistency using CSS variables from @liam-hq/ui - Enhanced error handling and loading states - Added back navigation link to projects page 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
|
Finished running flow.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Updates to Preview Branch (feat/project-new-page-redesign) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis PR refactors the ProjectNewPage's project creation flow, replacing a dropdown-based InstallationSelector with a section-based UI featuring dedicated components for installation selection, empty states, repository listing, and a multi-step progress header with updated styling. Changes
Sequence DiagramsequenceDiagram
participant User
participant ProjectNewPage
participant InstallationSelector
participant HeaderActions
participant InstallationDropdown
participant EmptyStateCard
participant RepositoryList
User->>ProjectNewPage: Load project creation page
ProjectNewPage->>InstallationSelector: Render with needsRefresh prop
alt No installations or needs reauth
InstallationSelector->>EmptyStateCard: Render empty state
User->>EmptyStateCard: Click action button
EmptyStateCard->>InstallationSelector: onActionClick callback
else Has installations
InstallationSelector->>HeaderActions: Render
HeaderActions->>InstallationDropdown: Render with installations
User->>InstallationDropdown: Select installation
InstallationDropdown->>InstallationSelector: onSelect(installation)
InstallationSelector->>RepositoryList: Fetch & render repositories
RepositoryList-->>InstallationSelector: Show repository list
User->>RepositoryList: Select repository
end
note over InstallationSelector: If loading or no selection<br/>show RepositoryListSkeleton
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
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 |
Check changeset necessityStatus: NOT REQUIRED Reason:
Changeset (copy & paste):N/A – no changeset needed (app-only changes). |
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: 2
🧹 Nitpick comments (4)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css (1)
32-90: Consider using px directly for flex-basis.Line 78 uses a spacing variable for
flex-basis:flex: 0 0 var(--spacing-25, 100px);According to the coding guidelines, spacing CSS variables should be used only for margins and padding, not for width/height dimensions. While
flex-basisis not exactlywidth, it serves a similar purpose.Consider this refactor for consistency with the guidelines:
-.stepDivider { - flex: 0 0 var(--spacing-25, 100px); +.stepDivider { + flex: 0 0 100px;frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsx (1)
52-74: Consider filtering installations before mapping.The current implementation maps all installations and returns
nullfor those without a valid login. This could be optimized and made more defensive by filtering first:- <DropdownMenuContent className={styles.dropdownContent}> - {installations.map((installation) => { - const login = match(installation.account) - .with({ login: P.string }, (account) => account.login) - .otherwise(() => null) - - if (login === null) return null - - return ( - <DropdownMenuItem - key={installation.id} - onSelect={() => onSelect(installation)} - leftIcon={ - <GithubLogo - className={styles.menuItemIcon} - aria-hidden - focusable="false" - /> - } - > - {login} - </DropdownMenuItem> - ) - })} + <DropdownMenuContent className={styles.dropdownContent}> + {installations + .map((installation) => ({ + installation, + login: match(installation.account) + .with({ login: P.string }, (account) => account.login) + .otherwise(() => null), + })) + .filter(({ login }) => login !== null) + .map(({ installation, login }) => ( + <DropdownMenuItem + key={installation.id} + onSelect={() => onSelect(installation)} + leftIcon={ + <GithubLogo + className={styles.menuItemIcon} + aria-hidden + focusable="false" + /> + } + > + {login} + </DropdownMenuItem> + ))} </DropdownMenuContent>This approach:
- Eliminates scattered
return nullstatements- Makes the logic clearer by explicitly filtering invalid entries
- Slightly better performance (no React key generation for null returns)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx (1)
48-48: Consider removing redundant Boolean() conversion.Since
needsRefreshis already typed asboolean | undefined, theBoolean()wrapper is redundant. You can pass it directly:- needsRefresh={Boolean(needsRefresh)} + needsRefresh={needsRefresh ?? false}Or rely on the default value in InstallationSelector (which already defaults to
false):- needsRefresh={Boolean(needsRefresh)} + needsRefresh={needsRefresh}frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (1)
70-92: Consider capturing project creation errors with Sentry.Line 87 uses
console.errorfor error handling, but since Sentry is already integrated (imported on line 4), project creation failures should be captured for monitoring and debugging.} catch (error) { - console.error('Error adding project:', error) + Sentry.captureException(error, { + tags: { action: 'add_project' }, + extra: { repositoryName: repository.name }, + }) }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx(2 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx(4 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsx(1 hunks)frontend/apps/app/components/ProjectSessionsPage/ProjectSessionsPage.module.css(1 hunks)frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/JackAndOctcat.tsx(1 hunks)frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/index.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.module.css
📄 CodeRabbit inference engine (AGENTS.md)
Use CSS Modules named *.module.css and keep types via typed-css-modules
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.module.cssfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.cssfrontend/apps/app/components/ProjectSessionsPage/ProjectSessionsPage.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.module.css
frontend/apps/**
📄 CodeRabbit inference engine (AGENTS.md)
Next.js apps live under frontend/apps; target app-specific scripts and configs there
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.module.cssfrontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.cssfrontend/apps/app/components/ProjectSessionsPage/ProjectSessionsPage.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.module.cssfrontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx
frontend/**/*.{css,scss}
📄 CodeRabbit inference engine (CLAUDE.md)
Use CSS Modules for all styling
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.module.cssfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.cssfrontend/apps/app/components/ProjectSessionsPage/ProjectSessionsPage.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.module.css
frontend/**/*.{module.css,module.scss}
📄 CodeRabbit inference engine (CLAUDE.md)
frontend/**/*.{module.css,module.scss}: Use CSS Variables from the @liam-hq/ui package in styles
Use spacing CSS variables only for margins and padding; use size units (rem, px, etc.) for height/width
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.module.cssfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.cssfrontend/apps/app/components/ProjectSessionsPage/ProjectSessionsPage.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.module.css
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Name React component files in PascalCase and use TSX (e.g., App.tsx)
Files:
frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript/TSX across the codebase
Files:
frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsxfrontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
frontend/**/*.{ts,tsx}: Use named exports only (no default exports)
Import UI components from @liam-hq/ui when available
Import icons from @liam-hq/ui
Follow existing import patterns and tsconfig path aliases
Use consts instead of function declarations for simple utilities (e.g., const toggle = () => {})
Use runtime type validation with valibot for external data validation
Use early returns for readability
Files:
frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsxfrontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name utility files in camelCase (e.g., mergeSchema.ts)
Files:
frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/index.ts
🧬 Code graph analysis (6)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsx (3)
frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/JackAndOctcat.tsx (1)
JackAndOctcat(5-196)frontend/packages/ui/src/components/Button/Button.tsx (1)
Button(23-80)frontend/packages/ui/src/logos/GithubLogo.tsx (1)
GithubLogo(5-24)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (4)
frontend/internal-packages/github/src/types.ts (1)
Installation(3-3)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx (1)
HeaderActions(18-72)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsx (1)
EmptyStateCard(15-42)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsx (1)
RepositoryList(14-50)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsx (3)
frontend/internal-packages/github/src/types.ts (1)
Installation(3-3)frontend/packages/ui/src/components/Button/Button.tsx (1)
Button(23-80)frontend/packages/ui/src/logos/GithubLogo.tsx (1)
GithubLogo(5-24)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsx (1)
frontend/internal-packages/github/src/types.ts (1)
Repository(4-4)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx (5)
frontend/internal-packages/github/src/types.ts (1)
Installation(3-3)frontend/packages/ui/src/components/Button/Button.tsx (1)
Button(23-80)frontend/packages/ui/src/logos/GithubLogo.tsx (1)
GithubLogo(5-24)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsx (1)
InstallationDropdown(22-78)frontend/packages/ui/src/icons/index.ts (1)
Plus(55-55)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx (2)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (1)
InstallationSelector(28-148)frontend/packages/ui/src/icons/index.ts (2)
Link(41-41)ChevronLeft(12-12)
⏰ 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). (5)
- GitHub Check: frontend-ci
- GitHub Check: frontend-lint
- GitHub Check: Supabase Preview
- GitHub Check: security-review
- GitHub Check: codeql / languages (javascript) / Perform CodeQL for javascript
🔇 Additional comments (20)
frontend/apps/app/components/ProjectSessionsPage/ProjectSessionsPage.module.css (1)
77-77: LGTM — straightforward width adjustment for empty state description.The change appropriately increases max-width to accommodate longer description text. The use of
pxunits aligns with your coding guidelines (size units for max-width) and is consistent with other fixed widths in the file (e.g.,width: 782pxon line 12).As a nice-to-have improvement, consider whether this (and other fixed
pxvalues in the file) would benefit fromremunits for better scalability across different zoom levels and DPI settings. This is not blocking—just an optional consideration for future refactoring.frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.module.css (1)
1-25: LGTM!The CSS module follows the coding guidelines correctly:
- Uses CSS variables from
@liam-hq/uiwith appropriate fallbacks- Spacing variables are used for margins, padding, and gaps (as per guidelines)
- Fixed dimensions (width/height) use
pxunits directly (allowed per guidelines)- Layout is well-structured with flexbox
frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/JackAndOctcat.tsx (1)
1-196: LGTM!This SVG illustration component is well-implemented:
- Uses
useId()to generate SSR-safe unique IDs for SVG filters, preventing ID collisions- Properly typed with
ComponentPropsWithoutRef<'svg'>to accept standard SVG props- Includes accessibility attributes (
role="img",aria-label)- Follows naming conventions (PascalCase component, named export)
frontend/apps/app/components/ProjectsPage/components/EmptyProjectsState/index.ts (1)
2-4: LGTM!Standard barrel exports for the new illustration components.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx (2)
28-47: Verify button text for the no-installations scenario.The button text logic may be confusing for first-time users:
const actionText = needsRefresh ? 'Continue with GitHub' : 'Add Installation' if (!hasInstallations || needsRefresh) { // renders button with actionText }When
hasInstallationsisfalseandneedsRefreshisfalse, the button displays "Add Installation". This might be semantically unclear for first-time setup—"Add" typically implies adding to an existing collection. Consider whether "Connect with GitHub" or "Get Started with GitHub" would be clearer in this scenario.Alternatively, if this is intentional, the logic could be made more explicit:
const actionText = needsRefresh ? 'Continue with GitHub' : hasInstallations ? 'Add Installation' : 'Connect with GitHub'
52-71: LGTM!The dropdown + install button layout is well-structured. The
disabledstates correctly gate both the dropdown and the install button based onneedsRefreshandgithubAppUrlavailability.frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.module.css (1)
1-28: LGTM!CSS module correctly uses CSS variables from
@liam-hq/uiand follows the coding guidelines. Spacing variables are appropriately used for gaps and padding.frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css (2)
1-30: LGTM!The container and content layout styling is well-structured with proper use of CSS variables for spacing, font, and colors.
92-109: LGTM!The back link styling is well-implemented with proper hover states and uses CSS variables correctly for colors, spacing, and sizing.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.module.css (1)
1-20: LGTM!The empty state styling follows the coding guidelines correctly:
- Spacing variables used appropriately for gap
- Fixed dimensions (width/height) use
pxunits as allowed- CSS variables from
@liam-hq/uiused for colors and font sizingfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsx (1)
22-78: LGTM overall!The component is well-structured with proper TypeScript typing, accessibility attributes on icons, and correct use of the ts-pattern library for type-safe matching. The disabled state is properly handled throughout.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsx (1)
1-50: LGTM! Well-structured component with clear separation of concerns.The component correctly handles error states, empty states, and repository rendering. The conditional messaging based on
hasSelectedInstallationprovides good UX feedback. Implementation follows coding guidelines with named exports and early returns.frontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx (2)
25-44: LGTM! Clean multi-step header implementation.The step-based navigation provides clear visual guidance for the project creation flow. The hardcoded active state for Step 1 is appropriate for the current implementation scope.
50-53: LGTM! Proper navigation implementation.The back link correctly uses Next.js Link with appropriate aria attributes for the decorative icon.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsx (1)
15-41: LGTM! Clean empty state implementation.The component provides clear user guidance with appropriate illustrations and CTAs. The conditional text and button labels adapt well to the
connectvsreauthvariants.frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.module.css (1)
13-19: Verify that the fixed height doesn't cause overflow or responsiveness issues.The
panelContentclass uses a fixed height of610px, which could lead to overflow if the repository list or error messages exceed this height, or wasted space if content is minimal. Consider whether this should usemin-heightinstead to allow for dynamic content sizing.If the fixed height is intentional for consistent UI layout across states, please ensure that the content within this panel is scrollable when it exceeds the fixed height.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.module.css (1)
1-31: LGTM! Well-structured dropdown styles.The CSS module correctly uses spacing variables for padding and gaps, with appropriate fixed dimensions for icons and min-widths for the dropdown components. The flex layout properties ensure proper spacing and alignment.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (3)
45-52: LGTM! Handler separation supports future OAuth implementation.While
handleConnectGitHubcurrently just wrapshandleInstallApp, this separation is appropriate given the TODO comment in EmptyStateCard.tsx (line 10) indicating that re-authentication will eventually use a different OAuth flow.
54-68: LGTM! Proper guard against actions during refresh.The early return when
needsRefreshis true correctly prevents installation selection during re-authentication flows. The nested transitions properly manage the loading states.
113-147: LGTM! Well-structured component composition.The refactored structure clearly separates concerns with HeaderActions, EmptyStateCard, and RepositoryList components. The conditional rendering logic correctly handles loading, empty states, and repository display.
.../ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsx
Show resolved
Hide resolved
.../apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx
Outdated
Show resolved
Hide resolved
Refactored GitHub App URL handling to use a boolean flag instead of passing the full URL string, improving component interface clarity and reducing prop drilling. Changes: - Changed githubAppUrl prop to hasGithubAppUrl boolean in HeaderActions - Removed Sentry error logging for missing env var (not needed at component level) - Removed default value for actionDisabled prop in EmptyStateCard 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
|
@junkisai I think we can refine and adjust this a bit more from a UI perspective, so I'll move it to Draft for now. Sorry about that. 🙏 |
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.
Pull Request Overview
This PR redesigns the project creation page by introducing a step-based navigation system and refactoring the installation selector into smaller, focused components. The redesign enhances user experience with visual progress indicators, improved empty states with illustrations, and better separation of concerns in the component architecture.
Key Changes:
- Implemented a two-step progress indicator (Import Git Repository → Set Watch Schemas) with visual badges and active state highlighting
- Refactored the monolithic
InstallationSelectorinto four sub-components for better maintainability - Added
JackAndOctcatillustration component for empty states to improve visual feedback
Reviewed Changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
ProjectNewPage.tsx |
Added step-based header with progress indicators and back navigation link |
ProjectNewPage.module.css |
Comprehensive styling for the new layout with step indicators, header, and navigation elements |
InstallationSelector.tsx |
Major refactoring to use sub-components and improved state management with needsRefresh prop |
InstallationSelector.module.css |
Updated to panel-based layout with fixed height container |
EmptyStateCard.tsx |
New component handling empty states for connect/reauth scenarios |
HeaderActions.tsx |
New component managing installation selection dropdown and GitHub app connection buttons |
InstallationDropdown.tsx |
Extracted dropdown component for installation selection |
RepositoryList.tsx |
New component for displaying and managing repository selection |
JackAndOctcat.tsx |
SVG illustration component for empty states |
EmptyProjectsState/index.ts |
Added exports for new illustration components |
ProjectSessionsPage.module.css |
Minor adjustment to max-width value |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| const formData = new FormData() | ||
| formData.append('installationId', installation.id.toString()) | ||
| repositoriesAction(formData) | ||
| const hasInstallations = installations.length > 0 |
Copilot
AI
Oct 23, 2025
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.
[nitpick] The hasInstallations variable is computed on every render but could be memoized with useMemo to avoid unnecessary recalculations, especially if the installations array is large or the component re-renders frequently.
| const emptyStateVariant: EmptyStateVariant | null = needsRefresh | ||
| ? 'reauth' | ||
| : hasInstallations | ||
| ? null | ||
| : 'connect' |
Copilot
AI
Oct 23, 2025
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.
[nitpick] The emptyStateVariant computation could be memoized with useMemo since it depends on needsRefresh and hasInstallations. This would prevent unnecessary recalculations on every render.
| const emptyStateVariant: EmptyStateVariant | null = needsRefresh | |
| ? 'reauth' | |
| : hasInstallations | |
| ? null | |
| : 'connect' | |
| const emptyStateVariant: EmptyStateVariant | null = useMemo(() => { | |
| if (needsRefresh) return 'reauth'; | |
| if (hasInstallations) return null; | |
| return 'connect'; | |
| }, [needsRefresh, hasInstallations]); |
| const dropdownLabel = selectedInstallation | ||
| ? match(selectedInstallation.account) | ||
| .with({ login: P.string }, (item) => item.login) | ||
| .otherwise(() => 'Select installation') | ||
| : 'Select installation' |
Copilot
AI
Oct 23, 2025
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.
[nitpick] The dropdownLabel computation could be memoized with useMemo to avoid re-running the pattern match on every render when selectedInstallation hasn't changed.
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: 0
♻️ Duplicate comments (2)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (2)
42-42: Consider memoization for performance.This computation is recalculated on every render. While the cost is minimal, memoization would prevent unnecessary recalculations when
installationshasn't changed.
93-103: Consider memoization for performance.Both
emptyStateVariantanddropdownLabelare recomputed on every render. Memoizing them would prevent unnecessary recalculations.Apply this diff:
+ const emptyStateVariant: EmptyStateVariant | null = useMemo(() => { + if (needsRefresh) return 'reauth' + if (hasInstallations) return null + return 'connect' + }, [needsRefresh, hasInstallations]) + + const dropdownLabel = useMemo(() => { + if (!selectedInstallation) return 'Select installation' + return match(selectedInstallation.account) + .with({ login: P.string }, (item) => item.login) + .otherwise(() => 'Select installation') + }, [selectedInstallation]) - const emptyStateVariant: EmptyStateVariant | null = needsRefresh - ? 'reauth' - : hasInstallations - ? null - : 'connect' - - const dropdownLabel = selectedInstallation - ? match(selectedInstallation.account) - .with({ login: P.string }, (item) => item.login) - .otherwise(() => 'Select installation') - : 'Select installation'Don't forget to import
useMemofrom React.
🧹 Nitpick comments (2)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (1)
49-51: Consider removing the redundant wrapper.
handleConnectGitHubsimply wrapshandleInstallAppwithout adding any functionality. You can usehandleInstallAppdirectly in the component render (lines 114, 125) to reduce indirection.Apply this diff to simplify:
- const handleConnectGitHub = useCallback(() => { - handleInstallApp() - }, [handleInstallApp]) - const handleSelectInstallation = useCallback(Then update the usage sites (lines 114, 125):
- onConnectGitHub={handleConnectGitHub} + onConnectGitHub={handleInstallApp}- onActionClick={handleConnectGitHub} + onActionClick={handleInstallApp}frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx (1)
28-28: Consider extracting action text to a helper constant.The inline ternary for
actionTextis recomputed on every render. While the cost is minimal, extracting it to a small helper or usinguseMemowould improve clarity and follow the pattern of separating data computation from render logic.Apply this diff:
+const getActionText = (needsRefresh: boolean) => + needsRefresh ? 'Continue with GitHub' : 'Add Installation' + export const HeaderActions: FC<HeaderActionsProps> = ({ hasInstallations, needsRefresh, ... }) => { - const actionText = needsRefresh ? 'Continue with GitHub' : 'Add Installation' + const actionText = getActionText(needsRefresh)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx(3 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Name React component files in PascalCase and use TSX (e.g., App.tsx)
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript/TSX across the codebase
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx
frontend/apps/**
📄 CodeRabbit inference engine (AGENTS.md)
Next.js apps live under frontend/apps; target app-specific scripts and configs there
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
frontend/**/*.{ts,tsx}: Use named exports only (no default exports)
Import UI components from @liam-hq/ui when available
Import icons from @liam-hq/ui
Follow existing import patterns and tsconfig path aliases
Use consts instead of function declarations for simple utilities (e.g., const toggle = () => {})
Use runtime type validation with valibot for external data validation
Use early returns for readability
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx
🧬 Code graph analysis (2)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx (5)
frontend/internal-packages/github/src/types.ts (1)
Installation(3-3)frontend/packages/ui/src/components/Button/Button.tsx (1)
Button(23-80)frontend/packages/ui/src/logos/GithubLogo.tsx (1)
GithubLogo(5-24)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown.tsx (1)
InstallationDropdown(22-78)frontend/packages/ui/src/icons/index.ts (1)
Plus(55-55)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (4)
frontend/internal-packages/github/src/types.ts (1)
Installation(3-3)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx (1)
HeaderActions(18-72)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard.tsx (1)
EmptyStateCard(15-42)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList.tsx (1)
RepositoryList(14-50)
⏰ 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). (3)
- GitHub Check: Supabase Preview
- GitHub Check: Supabase Preview
- GitHub Check: Supabase Preview
🔇 Additional comments (8)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (4)
14-16: LGTM!The new component imports follow the coding guidelines for named exports and proper module organization.
22-22: LGTM!The prop rename from
disabledtoneedsRefreshimproves semantic clarity and better describes the authentication state.
53-67: Clarify the nested transitions pattern.The nested
startTransition(line 58) wrappingstartAddingProjectTransition(line 59) is unusual. Typically, you'd use a single transition for async state updates.The outer
startTransitionat line 35 appears to be declared but its purpose in this nesting isn't clear. Consider simplifying to a single transition unless there's a specific reason for the two-level nesting.If the nested transitions serve distinct purposes (e.g., different loading states), consider adding a comment explaining the pattern. Otherwise, simplify to:
const handleSelectInstallation = useCallback( (installation: Installation) => { if (needsRefresh) return setSelectedInstallation(installation) - startTransition(() => { - startAddingProjectTransition(() => { - const formData = new FormData() - formData.append('installationId', installation.id.toString()) - repositoriesAction(formData) - }) - }) + startAddingProjectTransition(() => { + const formData = new FormData() + formData.append('installationId', installation.id.toString()) + repositoriesAction(formData) + }) }, [needsRefresh, repositoriesAction], )
105-140: LGTM!The refactored rendering structure with
HeaderActions,EmptyStateCard, andRepositoryListcomponents provides clean separation of concerns and improves maintainability. The conditional rendering logic correctly handles loading, empty, and list states.frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions.tsx (4)
1-6: LGTM!Imports follow the coding guidelines: using named exports, importing UI components from
@liam-hq/ui, and proper module organization.
7-16: LGTM!The props type definition is clear and well-structured. All props are appropriately typed and named.
29-50: LGTM!The conditional logic correctly handles both the "no installations" and "needs refresh" states by presenting a single action button. The button is appropriately disabled when the GitHub App URL is unavailable, and the icon includes proper accessibility attributes.
52-72: LGTM!The default state correctly renders the installation dropdown and install button. Both elements properly handle the disabled state when
needsRefreshis true or when the GitHub App URL is unavailable. Accessibility attributes are correctly applied to icons.
…nent structure - Move component files into dedicated RepositoryListSkeleton directory - Add Storybook stories for visual testing - Add index.ts for clean imports - Maintain same component structure and styling
…ucture - Move RepositoryList component files to directory-based structure - Relocate RepositoryItem as a child component under RepositoryList - Add index.ts export file for RepositoryList - Maintain component encapsulation by nesting related components
…cture - Move HeaderActions.tsx and .module.css into HeaderActions/ directory - Add index.ts for clean exports - Add Storybook stories for component documentation - Generate CSS type definitions - Maintain existing component behavior and props interface This follows the project's directory-based component pattern for better organization.
…nt structure - Move InstallationDropdown.tsx to InstallationDropdown/index.tsx - Move InstallationDropdown.module.css to InstallationDropdown/ directory - Maintain consistent component organization pattern with other recent refactorings
junkisai
left a 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.
Thank you for the implementation!
I have a few comments.
Also, I've created a PR to manage components on a directory basis and add Storybook files, targeting this branch.
Please feel free to incorporate it if you'd like. 🙏
#3904
| Import Git Repository | ||
| </span> | ||
| </div> | ||
| <span className={styles.stepDivider} aria-hidden="true" /> |
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.
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.
Thank you so much🙏
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css
Outdated
Show resolved
Hide resolved
Co-authored-by: Junki Saito <[email protected]>
Change gap from var(--spacing-4) to var(--spacing-7) for better visual spacing in the project creation page header. Co-authored-by: Junki Saito <[email protected]>
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: 3
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.module.css
📄 CodeRabbit inference engine (AGENTS.md)
Use CSS Modules named *.module.css and keep types via typed-css-modules
Files:
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css
frontend/apps/**
📄 CodeRabbit inference engine (AGENTS.md)
Next.js apps live under frontend/apps; target app-specific scripts and configs there
Files:
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css
frontend/**/*.{css,scss}
📄 CodeRabbit inference engine (CLAUDE.md)
Use CSS Modules for all styling
Files:
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css
frontend/**/*.{module.css,module.scss}
📄 CodeRabbit inference engine (CLAUDE.md)
frontend/**/*.{module.css,module.scss}: Use CSS Variables from the @liam-hq/ui package in styles
Use spacing CSS variables only for margins and padding; use size units (rem, px, etc.) for height/width
Files:
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css
⏰ 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). (6)
- GitHub Check: Supabase Preview
- GitHub Check: Supabase Preview
- GitHub Check: frontend-ci
- GitHub Check: frontend-lint
- GitHub Check: codeql / languages (javascript) / Perform CodeQL for javascript
- GitHub Check: Supabase Preview
| width: var(--spacing-6, 24px); | ||
| height: var(--spacing-6, 24px); |
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.
🛠️ Refactor suggestion | 🟠 Major
Use size units for width/height instead of spacing variables.
Per coding guidelines, spacing CSS variables should only be used for margins and padding. Width and height properties should use explicit size units (px, rem, etc.).
Apply this diff:
.stepBadge {
display: flex;
align-items: center;
justify-content: center;
- width: var(--spacing-6, 24px);
- height: var(--spacing-6, 24px);
+ width: 24px;
+ height: 24px;
border-radius: var(--border-radius-full, 9999px);📝 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.
| width: var(--spacing-6, 24px); | |
| height: var(--spacing-6, 24px); | |
| width: 24px; | |
| height: 24px; |
🤖 Prompt for AI Agents
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css lines
54-55: width and height are using spacing variables; update them to explicit
size units instead (e.g., replace width: var(--spacing-6, 24px); and height:
var(--spacing-6, 24px); with explicit values like width: 24px; and height: 24px;
or the project’s preferred size variables/units) so width/height do not rely on
spacing CSS vars.
| } | ||
|
|
||
| .stepDivider { | ||
| flex: 0 0 var(--spacing-25, 100px); |
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.
🛠️ Refactor suggestion | 🟠 Major
Use size units for flex-basis instead of spacing variables.
Flex-basis is a size property and should use explicit units rather than spacing variables.
Apply this diff:
.stepDivider {
- flex: 0 0 var(--spacing-25, 100px);
+ flex: 0 0 100px;
height: 1px;📝 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.
| flex: 0 0 var(--spacing-25, 100px); | |
| .stepDivider { | |
| flex: 0 0 100px; | |
| height: 1px; | |
| } |
🤖 Prompt for AI Agents
In frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css around
line 79, the flex-basis uses a spacing variable (var(--spacing-25, 100px)) which
is a spacing token rather than an explicit size; update the declaration so
flex-basis is an explicit size value (e.g., use a px/rem value like 100px or
switch to a dedicated size token such as var(--size-25, 100px)) instead of the
spacing variable.
| width: var(--spacing-4, 16px); | ||
| height: var(--spacing-4, 16px); |
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.
🛠️ Refactor suggestion | 🟠 Major
Use size units for width/height instead of spacing variables.
Per coding guidelines, spacing CSS variables should only be used for margins and padding. Width and height properties should use explicit size units (px, rem, etc.).
Apply this diff:
.backIcon {
- width: var(--spacing-4, 16px);
- height: var(--spacing-4, 16px);
+ width: 16px;
+ height: 16px;
}🤖 Prompt for AI Agents
In frontend/apps/app/components/ProjectNewPage/ProjectNewPage.module.css around
lines 108 to 109, width and height currently use spacing CSS variables; replace
them with explicit size units per guidelines (e.g., use 16px or 1rem) so width
and height use concrete units instead of --spacing variables; update both
properties to the chosen unit and run a quick visual check to ensure layout
remains correct.
♻️(app): Refactor ProjectNewPage components to directory-based structure
junkisai
left a 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.
👍👍
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: 0
♻️ Duplicate comments (1)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsx (1)
21-24: Null return for empty/error states is already tracked.This issue was flagged in a previous review and acknowledged by the developer for separate resolution.
🧹 Nitpick comments (5)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx (2)
50-53: Consider moving the back link to the top of the page.The "Back to Projects" link is placed at the bottom of the content, which is unconventional for navigation. Users typically expect back/navigation links at the top of the page for easier access and better discoverability.
Apply this diff to move the back link to a more conventional position:
<div className={styles.content}> + <Link href={urlgen('projects')} className={styles.backLink}> + <ArrowLeft aria-hidden className={styles.backIcon} /> + <span>Back to Projects</span> + </Link> <header className={styles.header}> <h1 className={styles.title}>Add New Project</h1>And remove it from the bottom:
organizationId={organizationId} needsRefresh={needsRefresh} /> - <Link href={urlgen('projects')} className={styles.backLink}> - <ArrowLeft aria-hidden className={styles.backIcon} /> - <span>Back to Projects</span> - </Link> </div>
51-51: Consider specifying explicit size for the ArrowLeft icon.For consistency with other icon usage in the codebase, consider adding an explicit size attribute to the ArrowLeft icon.
Apply this diff:
- <ArrowLeft aria-hidden className={styles.backIcon} /> + <ArrowLeft aria-hidden className={styles.backIcon} size={16} />frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsx (2)
30-34: Redundant disabled prop on both DropdownMenuTrigger and Button.The
disabledprop is set on both theDropdownMenuTrigger(line 30) and the nestedButton(line 34). While this may be intentional for Radix UI compatibility, it creates redundancy that could be simplified if only one is necessary.If only the Button's disabled state is sufficient, apply this diff:
- <DropdownMenuRoot> - <DropdownMenuTrigger asChild disabled={disabled}> + <DropdownMenuRoot> + <DropdownMenuTrigger asChild> <Button size="md" variant="outline-secondary" disabled={disabled}
52-74: Add handling for filtered installations and empty states.The component silently filters out installations without a string login (line 57), which could lead to confusion. Additionally, there's no handling for when all installations are filtered out, resulting in an empty dropdown with no user feedback.
Consider adding:
- Console warning when installations are filtered
- Empty state message when no valid installations remain
- Or validate installations at the parent level before passing them down
Example implementation:
<DropdownMenuContent className={styles.dropdownContent}> - {installations.map((installation) => { + {installations.length === 0 ? ( + <div className={styles.emptyState}>No installations available</div> + ) : ( + installations.map((installation) => { const login = match(installation.account) .with({ login: P.string }, (account) => account.login) .otherwise(() => null) - if (login === null) return null + if (login === null) { + console.warn('Installation missing login:', installation.id) + return null + } return ( <DropdownMenuItem key={installation.id} onSelect={() => onSelect(installation)} leftIcon={ <GithubLogo className={styles.menuItemIcon} aria-hidden focusable="false" /> } > {login} </DropdownMenuItem> ) - })} + }) + )} </DropdownMenuContent>frontend/apps/app/components/ProjectsPage/components/NoProjects/index.ts (1)
1-1: Consider moving shared illustration to a common location.Re-exporting
JackAndOctcatfromProjectNewPageintoProjectsPagecreates tight coupling between these two page-level modules. If this illustration is used by both pages, consider moving it to a shared location (e.g.,components/shared/orcomponents/illustrations/) to better reflect its reusable nature and avoid cross-page dependencies.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx(2 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.stories.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.stories.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/index.ts(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/index.ts(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.stories.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/index.ts(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.stories.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/index.ts(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/index.ts(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.module.css(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.stories.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.tsx(1 hunks)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/index.ts(1 hunks)frontend/apps/app/components/ProjectsPage/components/NoProjects/index.ts(1 hunks)frontend/packages/ui/src/icons/index.stories.tsx(2 hunks)frontend/packages/ui/src/icons/index.ts(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.module.css
- frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.module.css
🧰 Additional context used
📓 Path-based instructions (9)
**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name utility files in camelCase (e.g., mergeSchema.ts)
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/index.tsfrontend/packages/ui/src/icons/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/index.tsfrontend/apps/app/components/ProjectsPage/components/NoProjects/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript/TSX across the codebase
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/index.tsfrontend/packages/ui/src/icons/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/index.tsfrontend/packages/ui/src/icons/index.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsxfrontend/apps/app/components/ProjectsPage/components/NoProjects/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/index.tsfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.stories.tsx
frontend/apps/**
📄 CodeRabbit inference engine (AGENTS.md)
Next.js apps live under frontend/apps; target app-specific scripts and configs there
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsxfrontend/apps/app/components/ProjectsPage/components/NoProjects/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.module.cssfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.stories.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
frontend/**/*.{ts,tsx}: Use named exports only (no default exports)
Import UI components from @liam-hq/ui when available
Import icons from @liam-hq/ui
Follow existing import patterns and tsconfig path aliases
Use consts instead of function declarations for simple utilities (e.g., const toggle = () => {})
Use runtime type validation with valibot for external data validation
Use early returns for readability
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/index.tsfrontend/packages/ui/src/icons/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/index.tsfrontend/packages/ui/src/icons/index.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsxfrontend/apps/app/components/ProjectsPage/components/NoProjects/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/index.tsfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/index.tsfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.stories.tsx
frontend/packages/**
📄 CodeRabbit inference engine (AGENTS.md)
Shared libraries and tools live under frontend/packages
Files:
frontend/packages/ui/src/icons/index.tsfrontend/packages/ui/src/icons/index.stories.tsx
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Name React component files in PascalCase and use TSX (e.g., App.tsx)
Files:
frontend/packages/ui/src/icons/index.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.stories.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.stories.tsxfrontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsxfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.stories.tsx
**/*.module.css
📄 CodeRabbit inference engine (AGENTS.md)
Use CSS Modules named *.module.css and keep types via typed-css-modules
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.module.css
frontend/**/*.{css,scss}
📄 CodeRabbit inference engine (CLAUDE.md)
Use CSS Modules for all styling
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.module.css
frontend/**/*.{module.css,module.scss}
📄 CodeRabbit inference engine (CLAUDE.md)
frontend/**/*.{module.css,module.scss}: Use CSS Variables from the @liam-hq/ui package in styles
Use spacing CSS variables only for margins and padding; use size units (rem, px, etc.) for height/width
Files:
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.module.cssfrontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.module.css
🧬 Code graph analysis (10)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsx (2)
frontend/packages/ui/src/components/Button/Button.tsx (1)
Button(23-80)frontend/packages/ui/src/icons/index.ts (1)
Plus(56-56)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsx (3)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsx (1)
JackAndOctcat(5-196)frontend/packages/ui/src/components/Button/Button.tsx (1)
Button(23-80)frontend/packages/ui/src/logos/GithubLogo.tsx (1)
GithubLogo(5-24)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsx (4)
frontend/internal-packages/github/src/types.ts (1)
Installation(3-3)frontend/packages/ui/src/components/Button/Button.tsx (1)
Button(23-80)frontend/packages/ui/src/logos/GithubLogo.tsx (1)
GithubLogo(5-24)frontend/packages/ui/src/icons/index.ts (1)
ChevronDown(12-12)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.stories.tsx (2)
frontend/internal-packages/github/src/types.ts (1)
Installation(3-3)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.tsx (1)
InstallationDropdown(22-78)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsx (2)
frontend/internal-packages/github/src/types.ts (1)
Repository(4-4)frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryItem/RepositoryItem.tsx (1)
RepositoryItem(11-29)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.stories.tsx (1)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsx (1)
EmptyStateCard(14-35)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.stories.tsx (1)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsx (1)
HeaderActions(11-31)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.stories.tsx (1)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.tsx (1)
RepositoryListSkeleton(9-21)
frontend/apps/app/components/ProjectNewPage/ProjectNewPage.tsx (2)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/InstallationSelector.tsx (1)
InstallationSelector(30-204)frontend/packages/ui/src/icons/index.ts (2)
Link(42-42)ArrowLeft(4-4)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.stories.tsx (1)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsx (1)
JackAndOctcat(5-196)
⏰ 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). (4)
- GitHub Check: Supabase Preview
- GitHub Check: frontend-lint
- GitHub Check: frontend-ci
- GitHub Check: Supabase Preview
🔇 Additional comments (30)
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.module.css (1)
1-18: Clean CSS implementation with proper use of design system tokens.The CSS module correctly applies @liam-hq/ui design system variables throughout, with appropriate use of spacing variables for gaps, color variables for text styling, and font sizing variables. The fallback values are well-chosen, and the class structure is semantic and maintainable.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/index.ts (1)
1-1: LGTM!Standard barrel export pattern that enables centralized imports for the RepositoryList module.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryList/RepositoryList.tsx (1)
1-40: Component structure and implementation look good.The component follows coding guidelines with named exports, proper TypeScript typing, and clean React patterns. The repository mapping logic correctly uses
repo.idas the key, and props are appropriately passed to child components.frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.module.css (1)
1-37: LGTM! Clean skeleton loader styling.The CSS module follows all coding guidelines correctly:
- Uses CSS variables from @liam-hq/ui package with appropriate fallbacks
- Applies spacing variables (
--spacing-4,--spacing-3) for gaps as per guidelines- Uses fixed units (px) for height/width dimensions
- Animation is smooth and appropriate for skeleton loading states
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/index.ts (1)
1-1: LGTM! Standard barrel export.Follows the named export pattern correctly and aligns with the project's module structure.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.stories.tsx (1)
1-21: LGTM! Well-structured Storybook story.The story setup follows Storybook best practices with appropriate meta configuration, decorator for width constraint, and proper TypeScript typing with the
satisfiesoperator.frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/RepositoryListSkeleton/RepositoryListSkeleton.tsx (1)
9-21: LGTM! Clean skeleton component implementation.The component is well-structured and follows all coding guidelines:
- Uses named exports correctly
- Proper TypeScript typing with FC
- CSS Module integration
- React best practices with key props in the map
The TODO comment about moving to a shared UI library is a good note for future consolidation and is appropriate to defer.
frontend/packages/ui/src/icons/index.ts (1)
4-4: LGTM!The ArrowLeft icon export is correctly added and maintains alphabetical ordering.
frontend/packages/ui/src/icons/index.stories.tsx (1)
4-4: LGTM!ArrowLeft is correctly integrated into the Storybook showcase, following the established pattern for other icons.
Also applies to: 88-88
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/index.ts (1)
1-1: LGTM!Standard barrel export pattern consistent with project conventions.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.tsx (1)
5-31: LGTM!The component is well-structured with clear prop types and proper accessibility handling for the decorative Plus icon.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/HeaderActions/HeaderActions.stories.tsx (1)
4-58: LGTM!The Storybook stories provide appropriate coverage with well-documented controls and a clean mock component.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/index.ts (1)
1-1: LGTM!Standard barrel export following project conventions.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.stories.tsx (5)
1-3: LGTM!Imports follow the coding guidelines and use named imports appropriately.
5-146: Comprehensive mock data for testing.The mock Installation objects are well-structured and cover multiple scenarios (Organization vs User accounts, different repository selections). This level of detail is excellent for visual testing in Storybook.
148-170: Well-configured Storybook meta.The meta configuration is properly typed and includes helpful descriptions for all props, which aids documentation and developer experience.
172-172: Default export required by Storybook.This default export is necessary for Storybook's file-based story configuration and is an acceptable exception to the "no default exports" guideline.
173-213: Excellent story coverage.The five story variations thoroughly cover the component's key states and edge cases, providing a comprehensive visual test suite.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/InstallationDropdown/InstallationDropdown.module.css (6)
1-8: LGTM!The
.dropdownTriggerclass correctly uses CSS variables from @liam-hq/ui with appropriate fallbacks. Spacing variables are properly used for padding and gap, while size units (px) are used for width as per coding guidelines.
10-12: LGTM!Appropriate use of px units for minimum width.
14-18: LGTM!Icon dimensions correctly use px units for width and height as specified in the coding guidelines.
20-22: LGTM!Preventing label shrinkage with
flex-shrink: 0ensures consistent label display.
24-26: LGTM!The spacer correctly uses flexbox to create flexible spacing within the dropdown trigger.
28-31: LGTM!Dropdown icon dimensions follow the same correct sizing pattern as other icons in this module.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.tsx (2)
5-7: LGTM: Excellent use ofuseIdfor SSR-safe filter IDs.Using React's
useIdhook to generate unique filter IDs ensures the SVG renders correctly when multiple instances exist on the page and during server-side rendering.
10-18: LGTM: Proper accessibility attributes.The SVG includes appropriate accessibility attributes (
role="img"andaria-label="Jack and Octocat") and correctly spreads props to allow className and other SVG attributes to be overridden.frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.stories.tsx (1)
4-34: LGTM: Well-structured Storybook configuration.The story configuration includes helpful argTypes documentation, a width-constraining decorator for consistent preview, and covers the main use cases (no installations, no repositories, and disabled state).
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/EmptyStateCard.tsx (2)
24-32: LGTM: Proper accessibility handling for decorative icon.The
GithubLogoicon correctly usesaria-hiddento hide it from screen readers (as it's decorative alongside the button text) andfocusable="false"to prevent keyboard focus on the SVG element.
7-7: TODO comment is appropriate for future enhancement.The TODO clearly documents the planned OAuth re-authentication handler. This is acceptable for tracking future work.
frontend/apps/app/components/ProjectNewPage/components/InstallationSelector/components/EmptyStateCard/JackAndOctcat/JackAndOctcat.stories.tsx (1)
4-14: LGTM: Appropriate minimal story for illustration component.For a pure SVG illustration component, a single
Defaultstory with centered layout is sufficient to enable visual inspection in Storybook.

Issue
Why is this change needed?
This redesign improves the user experience of the project creation flow by introducing a clear step-based navigation system and better component organization. The previous implementation lacked visual guidance for users going through the multi-step process of creating a project.
Not Done Yet
Design Review Notes
Key Improvements:
Step-based Navigation: Added a visual step indicator showing users their progress (Step 1: Import Git Repository, Step 2: Set Watch Schemas)
Component Refactoring: Split the monolithic InstallationSelector into smaller, focused sub-components:
EmptyStateCard: Handles empty states (no installations, needs reauth)HeaderActions: Manages installation selection and GitHub app connectionRepositoryList: Displays and handles repository selectionInstallationDropdown: Dedicated dropdown for installation selectionImproved Visual Design:
Better State Management:
disabledtoneedsRefreshfor clarity)Code Quality:
Summary by CodeRabbit
Release Notes
New Features
Style