-
-
Notifications
You must be signed in to change notification settings - Fork 312
refactor: update breadcrumbs component to support dynamic title rende… #2573
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
base: main
Are you sure you want to change the base?
refactor: update breadcrumbs component to support dynamic title rende… #2573
Conversation
Summary by CodeRabbitRelease Notes
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughReplaces a path-parsing breadcrumb component with a context-driven system: adds BreadcrumbContext, useBreadcrumbs hook, BreadcrumbProvider/BreadcrumbRoot, a BreadCrumbsWrapper that renders current-path breadcrumbs, a BreadCrumbRenderer (items prop), PageLayout to register titles, utilities, tests, and updates many dynamic layouts to use PageLayout and cached metadata helpers. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (3)
frontend/src/hooks/useBreadcrumbs.ts (1)
26-27: Consider making route pattern detection more maintainable.The nested repository route detection uses a hardcoded check with magic values. Consider extracting this as a constant or pattern matcher for better maintainability.
+const NESTED_REPO_PATTERN = { + segments: 4, + positions: { organization: 0, repositories: 2 }, + values: { organization: 'organizations', repositories: 'repositories' } +} + const isNestedRepoRoute = - segments.length === 4 && segments[0] === 'organizations' && segments[2] === 'repositories' + segments.length === NESTED_REPO_PATTERN.segments && + segments[0] === NESTED_REPO_PATTERN.values.organization && + segments[2] === NESTED_REPO_PATTERN.values.repositoriesfrontend/src/components/BreadCrumbsWrapper.tsx (1)
19-78: LGTM! Solid implementation with good edge case handling.The BreadCrumbsWrapper component correctly:
- ✅ Returns null for home page and PageLayout-managed routes
- ✅ Generates breadcrumb items from path segments with proper formatting (
upperFirst+ dash replacement)- ✅ Builds progressive paths for each breadcrumb link
- ✅ Renders with consistent styling using HeroUI and FontAwesome
The logic aligns well with the
useBreadcrumbsfallback behavior and has comprehensive test coverage.Optional future refactor: The rendering logic (lines 36-77) is nearly identical to
BreadCrumbRenderer(lines 10-53 infrontend/src/components/BreadCrumbs.tsx). Consider extracting the common rendering logic to reduce duplication:// Shared base component function BreadcrumbsBase({ items }: { items: BreadCrumbItem[] }) { return ( <div className="mt-16 w-full pt-4"> {/* ... shared rendering logic ... */} </div> ) } // BreadCrumbsWrapper would then: export default function BreadCrumbsWrapper() { // ... route detection and item generation ... return <BreadcrumbsBase items={items} /> }This would centralize the styling and structure, making future updates easier.
frontend/__tests__/unit/components/BreadCrumbs.test.tsx (1)
37-43: Update the last-item test to assert current-page semantics.Once the renderer stops using
isDisabled, this case should verify the breadcrumb exposesaria-current="page"(and rename the test accordingly) rather than asserting it’s disabled. That keeps the test aligned with the accessibility contract we want. (heroui.com)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/src/types/__generated__/repositoryQueries.generated.tsis excluded by!**/__generated__/**
📒 Files selected for processing (20)
cspell/custom-dict.txt(3 hunks)frontend/__tests__/unit/components/BreadCrumbs.test.tsx(1 hunks)frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx(1 hunks)frontend/__tests__/unit/components/PageLayout.test.tsx(1 hunks)frontend/__tests__/unit/hooks/useBreadcrumbs.test.ts(1 hunks)frontend/__tests__/unit/pages/ChapterDetails.test.tsx(1 hunks)frontend/__tests__/unit/pages/CommitteeDetails.test.tsx(2 hunks)frontend/__tests__/unit/pages/OrganizationDetails.test.tsx(2 hunks)frontend/src/app/chapters/[chapterKey]/page.tsx(2 hunks)frontend/src/app/committees/[committeeKey]/page.tsx(2 hunks)frontend/src/app/layout.tsx(2 hunks)frontend/src/app/members/[memberKey]/page.tsx(2 hunks)frontend/src/app/organizations/[organizationKey]/page.tsx(2 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx(3 hunks)frontend/src/app/projects/[projectKey]/page.tsx(2 hunks)frontend/src/components/BreadCrumbs.tsx(2 hunks)frontend/src/components/BreadCrumbsWrapper.tsx(1 hunks)frontend/src/components/PageLayout.tsx(1 hunks)frontend/src/hooks/useBreadcrumbs.ts(1 hunks)frontend/src/server/queries/repositoryQueries.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/committees/[committeeKey]/page.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.
Applied to files:
frontend/__tests__/unit/components/PageLayout.test.tsxfrontend/__tests__/unit/pages/OrganizationDetails.test.tsxfrontend/__tests__/unit/pages/CommitteeDetails.test.tsxfrontend/__tests__/unit/pages/ChapterDetails.test.tsxfrontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsxfrontend/__tests__/unit/components/BreadCrumbs.test.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/__tests__/unit/pages/OrganizationDetails.test.tsxfrontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsxfrontend/__tests__/unit/pages/CommitteeDetails.test.tsxfrontend/__tests__/unit/pages/ChapterDetails.test.tsxfrontend/src/components/BreadCrumbs.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/components/BreadCrumbsWrapper.tsxfrontend/__tests__/unit/pages/CommitteeDetails.test.tsxfrontend/__tests__/unit/pages/ChapterDetails.test.tsxfrontend/src/components/BreadCrumbs.tsxfrontend/src/components/PageLayout.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/src/server/queries/repositoryQueries.ts
🧬 Code graph analysis (13)
frontend/src/app/committees/[committeeKey]/page.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
frontend/__tests__/unit/components/PageLayout.test.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
frontend/src/app/projects/[projectKey]/page.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
frontend/__tests__/unit/hooks/useBreadcrumbs.test.ts (1)
frontend/src/hooks/useBreadcrumbs.ts (1)
useBreadcrumbs(18-64)
frontend/src/app/members/[memberKey]/page.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx (1)
frontend/src/components/BreadCrumbsWrapper.tsx (1)
BreadCrumbsWrapper(19-78)
frontend/src/app/layout.tsx (1)
frontend/src/components/BreadCrumbsWrapper.tsx (1)
BreadCrumbsWrapper(19-78)
frontend/src/components/BreadCrumbs.tsx (1)
frontend/src/hooks/useBreadcrumbs.ts (1)
BreadCrumbItem(4-7)
frontend/src/components/PageLayout.tsx (2)
frontend/src/hooks/useBreadcrumbs.ts (1)
useBreadcrumbs(18-64)frontend/src/components/BreadCrumbs.tsx (1)
BreadCrumbRenderer(11-54)
frontend/__tests__/unit/components/BreadCrumbs.test.tsx (1)
frontend/src/components/BreadCrumbs.tsx (1)
BreadCrumbRenderer(11-54)
frontend/src/app/organizations/[organizationKey]/page.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
frontend/src/app/chapters/[chapterKey]/page.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
🔇 Additional comments (18)
cspell/custom-dict.txt (1)
9-9: Verify the necessity of bothCyclonedxandcyclonedxcasing variants.All four new dictionary entries are correctly placed in alphabetical order. However, the addition of both
Cyclonedx(line 9, capitalized) andcyclonedx(line 58, lowercase) appears redundant if both are intended to match the same tool name. Most spell-checkers handle case-insensitive matching, so a single entry may be sufficient.Additionally, the standard spelling is
CycloneDX(mixed case), notCyclonedx. If this tool is referenced in the breadcrumb-related code, consider whether the dictionary entries match the actual casing used there.Please confirm:
- Whether both casing variants are necessary for the spell-checker configuration in this project.
- Whether these words are actually used in the breadcrumb component changes, tests, or documentation, and if so, in what casing.
Also applies to: 17-17, 48-48, 58-58
frontend/__tests__/unit/components/PageLayout.test.tsx (1)
1-137: LGTM! Comprehensive test coverage.The test suite provides excellent coverage of the PageLayout component functionality, including:
- Children rendering
- Breadcrumb positioning in the DOM
- Various breadcrumbData field types (projectName, memberName, chapterName, committeeName, orgName/repoName)
- Edge cases (undefined breadcrumbData, mismatched field types)
The use of
compareDocumentPositionto verify breadcrumb placement is a solid approach.frontend/src/app/layout.tsx (1)
9-9: LGTM! Clean component swap.The replacement of BreadCrumbs with BreadCrumbsWrapper aligns with the broader breadcrumb refactoring described in the PR objectives.
Also applies to: 77-77
frontend/src/server/queries/repositoryQueries.ts (1)
34-34: LGTM! Supports breadcrumb display names.Adding
organization.nameto the query enables breadcrumbs to display the full organization name instead of relying on the slug-formattedloginfield.frontend/src/app/projects/[projectKey]/page.tsx (1)
21-21: LGTM! Consistent PageLayout integration.The wrapping of DetailsCard with PageLayout and the passing of
breadcrumbData={{ projectName: project.name }}follows the established pattern and enables proper breadcrumb rendering with the actual project name.Also applies to: 93-113
frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (1)
25-25: LGTM! Improved test robustness.The changes adapt the test for the PageLayout integration:
- Mocking
usePathnamesupports the breadcrumb generation logic- Using
getByRole('heading', { name: 'Test Committee' })is more semantically correct and resilient than text-based queriesAlso applies to: 56-56
frontend/src/app/members/[memberKey]/page.tsx (1)
29-29: LGTM! Proper fallback handling.The PageLayout integration is clean and includes a sensible fallback:
memberName: user?.name || user?.loginensures the breadcrumb displays either the user's full name or their login if no name is set.Also applies to: 235-249
frontend/__tests__/unit/pages/OrganizationDetails.test.tsx (1)
31-31: LGTM! Consistent test improvements.The changes mirror the pattern in CommitteeDetails.test.tsx:
usePathnamemock enables breadcrumb generation- Role-based heading query is more robust than text matching
Also applies to: 74-74
frontend/src/hooks/useBreadcrumbs.ts (1)
50-58: The current breadcrumbData logic is correct and intentional.The OR-chaining behavior in lines 50-58 is explicitly tested and expected. The test "handles partial breadcrumbData (uses first matching field)" at line 212 in
useBreadcrumbs.test.tsvalidates that when a breadcrumbData field is provided, the first truthy value from the OR-chain is used regardless of route type. The same scenario is tested inPageLayout.test.tsxat line 125 under the title "handles partial breadcrumbData (wrong field for route type)," confirming this is intentional design.Additionally, the codebase design prevents the problematic scenario from occurring: every page passes only the appropriate field for its route type (e.g.,
projectNamefor project routes,memberNamefor member routes). No instances exist where multiple conflicting fields are passed simultaneously.frontend/src/app/committees/[committeeKey]/page.tsx (1)
84-94: LGTM!The PageLayout integration is clean and consistent with the breadcrumb system. The
committeeNameis correctly passed from the GraphQL-fetched data, and the early return at lines 48-55 ensurescommitteeis not null.frontend/__tests__/unit/pages/ChapterDetails.test.tsx (1)
31-31: LGTM!Adding the
usePathnamemock is necessary for the PageLayout integration in ChapterDetailsPage, as theuseBreadcrumbshook (used by PageLayout) depends onusePathnameto generate breadcrumb items. The mocked path/chapters/test-chaptercorrectly matches thechapterKeyfrom the existinguseParamsmock.frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx (1)
1-148: LGTM! Comprehensive test coverage.The test suite provides excellent coverage of BreadCrumbsWrapper functionality:
- ✅ Route detection logic (hiding on home and detail pages)
- ✅ Breadcrumb rendering on list pages
- ✅ Auto-generation logic (capitalization, dash replacement)
- ✅ Progressive path building for links
- ✅ Edge cases (trailing slashes, special characters)
The test structure is clear with well-organized describe blocks, and the assertions properly validate both visibility and rendering behavior.
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (1)
113-145: PageLayout integration looks good.The PageLayout wrapper is correctly implemented with appropriate fallback chains for organization and repository names. The optional chaining at lines 116-118 properly handles cases where
repository.organizationmight be null.frontend/src/app/chapters/[chapterKey]/page.tsx (1)
64-77: LGTM!The PageLayout integration is clean and follows the established pattern. The
chapterNameis correctly passed from the GraphQL-fetched chapter data.frontend/src/components/PageLayout.tsx (1)
1-28: LGTM! Clean component design.The PageLayout component follows React best practices:
- ✅ Proper separation of concerns (delegates breadcrumb logic to
useBreadcrumbshook)- ✅ Client component correctly marked with
'use client'directive (required for Next.js 15 app router withusePathname)- ✅ Fragment wrapper avoids unnecessary DOM nesting
- ✅ Composition pattern with
childrenprop for flexible page contentThe interface design is pragmatic, allowing optional breadcrumb data fields that are resolved by the
useBreadcrumbshook based on the current route.frontend/src/components/BreadCrumbsWrapper.tsx (1)
10-17: Route pattern matching looks correct.The
ROUTES_WITH_PAGE_LAYOUTpatterns properly identify detail pages that use PageLayout component and should not show path-based breadcrumbs. The regex patterns correctly match:
- Single-segment detail pages:
/members/[key],/projects/[key], etc.- Nested repository pages:
/organizations/[key]/repositories/[key]This prevents double breadcrumb rendering on pages that have entity-specific breadcrumbs from PageLayout.
frontend/src/app/organizations/[organizationKey]/page.tsx (1)
117-131: Verify TypeScript satisfaction and type safety across state/useEffect.The code logic is correct at runtime: the guard at line 54 prevents reaching the return statement unless
graphQLData?.organizationexists, which triggerssetOrganization()in the useEffect. However, TypeScript may not recognize this connection becauseorganizationis a separate state variable initialized asnull(line 21), and the guard checksgraphQLData?.organizationrather than directly narrowing theorganizationstate variable.The type-safety concern raised in the original review is valid—accessing
organization.name,organization.description, andorganization.statsat lines 117-131 could trigger TypeScript errors depending on the configuration and how state narrowing is handled.Manual verification needed: Confirm whether TypeScript reports any errors for
organizationproperty access at lines 67-70, 79-91, and 115-131 in a proper build environment with TypeScript configured for the project.frontend/__tests__/unit/hooks/useBreadcrumbs.test.ts (1)
14-241: Great coverage of breadcrumb scenarios.The suite hits every branch of the hook, including nested repo routes, fallbacks, and trailing-slash handling. That gives strong confidence in the refactor.
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx
Outdated
Show resolved
Hide resolved
|
@coderabbitai generate docstrings |
✅ Actions performedInitiated docstring generation; will generate only if new commits exist. |
|
Warning Docstrings generation - IN PROGRESS Generating docstrings for this pull request |
✅ Actions performedInitiated docstring generation; will generate only if new commits exist. |
…rmatting - Replace isDisabled with aria-current="page" for proper accessibility - Use consistent split/map/join capitalization for repository names
…rmatting - Replace isDisabled with aria-current="page" for proper accessibility - Use consistent split/map/join capitalization for repository names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx
🧬 Code graph analysis (1)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (1)
frontend/src/components/PageLayout.tsx (1)
PageLayout(19-28)
🔇 Additional comments (3)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (3)
10-10: LGTM!The import is correctly placed and used for formatting breadcrumb names.
20-20: LGTM!The PageLayout import is correctly placed and consistent with the breadcrumb refactoring across other detail pages.
130-146: LGTM!The DetailsCard props are correctly preserved, and wrapping it in PageLayout successfully adds breadcrumb functionality without affecting the existing detail page content.
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx
Outdated
Show resolved
Hide resolved
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
🧹 Nitpick comments (1)
frontend/src/components/BreadCrumbs.tsx (1)
11-57: Consider adding a guard for empty items.While the parent components should ensure
itemsis never empty, a defensive check would make the component more resilient:export default function BreadCrumbRenderer({ items }: BreadCrumbRendererProps) { + if (items.length === 0) { + return null + } + return ( <div className="mt-16 w-full pt-4">This is optional since the
useBreadcrumbshook should always provide at least a Home item according to the PR objectives.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/src/components/BreadCrumbs.tsx(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/src/components/BreadCrumbs.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/components/BreadCrumbs.tsx
🧬 Code graph analysis (1)
frontend/src/components/BreadCrumbs.tsx (1)
frontend/src/hooks/useBreadcrumbs.ts (1)
BreadCrumbItem(4-7)
🔇 Additional comments (2)
frontend/src/components/BreadCrumbs.tsx (2)
1-9: LGTM! Clean imports and interface definition.The type-only import on line 4 follows TypeScript best practices, and the rename of
BreadcrumbItemtoHeroUIBreadcrumbItemon line 3 properly avoids naming collisions with the customBreadCrumbItemtype. The interface is focused and well-typed.
30-52: Excellent refactor—previous accessibility issue properly resolved.The component now correctly implements breadcrumb semantics:
- Last item uses
aria-current="page"(line 38) instead of the previousaria-disabled="true"- No
isDisabledprop onHeroUIBreadcrumbItem(line 34)- Clean separation of concerns with logic moved to the
useBreadcrumbshook- Key using
item.pathis appropriate since breadcrumb paths are unique by natureThe refactor transforms this into a focused, pure renderer component.
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
🧹 Nitpick comments (1)
frontend/__tests__/unit/pages/ChapterDetails.test.tsx (1)
34-144: Consider adding a breadcrumb rendering test.While the breadcrumb system is tested elsewhere, you might consider adding a test in this suite to verify that the chapter name appears in the breadcrumbs when rendered through PageLayout. This would provide integration-level coverage for the breadcrumb feature in the chapter details context.
Example test structure:
test('renders chapter name in breadcrumbs', async () => { render(<ChapterDetailsPage />) await waitFor(() => { // Verify breadcrumb contains chapter name from mockChapterDetailsData expect(screen.getByText(mockChapterDetailsData.chapter.name)).toBeInTheDocument() }) })
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/__tests__/unit/pages/ChapterDetails.test.tsx(1 hunks)frontend/src/app/members/[memberKey]/page.tsx(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/app/members/[memberKey]/page.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/__tests__/unit/pages/ChapterDetails.test.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.
Applied to files:
frontend/__tests__/unit/pages/ChapterDetails.test.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/__tests__/unit/pages/ChapterDetails.test.tsx
🔇 Additional comments (1)
frontend/__tests__/unit/pages/ChapterDetails.test.tsx (1)
31-31: LGTM! Mock addition supports breadcrumb integration.The
usePathnamemock is correctly added to support the new breadcrumb system that PageLayout uses when wrapping ChapterDetailsPage. The returned path '/chapters/test-chapter' is consistent with the test data'schapterKey.
kasya
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 @Kunal1522 !
It works as expected for most part.. but I do have some concerns for the code. Left a few comments below ⬇️
frontend/src/hooks/useBreadcrumbs.ts
Outdated
| const isNestedRepoRoute = | ||
| segments.length === 4 && segments[0] === 'organizations' && segments[2] === 'repositories' |
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.
Where is this 4 coming from? What if we have more then 4 nested items? 🤔
We want to make it as dynamic as possible
frontend/src/hooks/useBreadcrumbs.ts
Outdated
| path: string | ||
| } | ||
|
|
||
| interface BreadcrumbData { |
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.
BreadcrumbData interface is duplicated in two files, violating DRY.
It's added here and in PageLayout.tsx.
Also, we are trying to get away from using interface and want to use type instead. Please move this into types/ folder and use where needed to avoid code duplication
frontend/src/hooks/useBreadcrumbs.ts
Outdated
| if (isNestedRepoRoute) { | ||
| breadcrumbs.push({ title: 'Organizations', path: '/organizations' }) | ||
|
|
||
| const orgTitle = breadcrumbData?.orgName || upperFirst(segments[1]).replaceAll('-', ' ') |
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.
I can see you use the same logic several times. Can we move this into a function instead? Lines 32, 38, 48.
| const ROUTES_WITH_PAGE_LAYOUT = [ | ||
| /^\/members\/[^/]+$/, | ||
| /^\/projects\/[^/]+$/, | ||
| /^\/chapters\/[^/]+$/, | ||
| /^\/committees\/[^/]+$/, | ||
| /^\/organizations\/[^/]+$/, | ||
| /^\/organizations\/[^/]+\/repositories\/[^/]+$/, | ||
| ] |
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.
I'm also not super happy with these hardcoded values. If we ever change our project structure - this might fail. Is there a better way to find a match? 🤔
| import Link from 'next/link' | ||
| import { usePathname } from 'next/navigation' | ||
|
|
||
| const ROUTES_WITH_PAGE_LAYOUT = [ |
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.
I also think this route detection logic is somewhat duplicated: regex patterns in here vs string matching in useBreadcrumbs 🤔
| }) | ||
|
|
||
| test('returns null for member detail pages', () => { | ||
| ;(usePathname as jest.Mock).mockReturnValue('/members/bjornkimminich') |
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.
Can we use users/project names that we already added to cspell to not add new items? Or just some fake ones, that would be even better.
|
@Kunal1522 I pushed some changes to address issues I found while reviewing your PR. The one I noticed was not connected to your changes, but other 2 were from running Could you also address 5 newly added issues found by SonarQube? Here's the list |
|
@Kunal1522 Hi! Are you planning to address the comments in this PR? The deadline for this issue passed a while ago (Nov 8th). If the updates aren’t made within the next day, I’ll need to reassign the issue to someone else. Thanks! |
|
@Kunal1522 I haven't reassigned this to anyone else. It's Nov 26th so let me know if you can get back to this tomorrow. Otherwise, I'll reassign! Hope the exams went well. |
|
thanks i appreciate that .yeah i have started reworking on it now maybe will raise pr in 1-2 days |
…x patterns, and simplify dynamic/static route handling
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 (8)
frontend/src/components/RepositoryCard.tsx (2)
25-31: Composite key is an improvement; consider fallback collisionsUsing
${repository.organization?.login ?? 'unknown'}-${repository.key}is a good step toward more stable keys, especially ifrepository.keyis scoped per organization. If there’s any chance of multiple repositories with missingorganizationappearing in the same list, they’ll all share the'unknown'prefix, so you might still get duplicate keys. If available, consider a globally unique field (e.g.,repository.id) or extending the composite key to avoid that edge case.
40-43: Guard against missing organization when constructing the navigation URL
details.organization?.logincan evaluate toundefined; interpolated in a template literal this becomes the string"undefined", so the click could navigate to/organizations/undefined/repositories/${details.key}instead of a valid org route. Ifdetails.organizationis guaranteed to be present for this component, it’s safer to reflect that in the type (makeorganization.loginrequired here) and drop the optional chaining. Otherwise, add a guard before callingrouter.push(or render a non-clickable title when the organization is missing) to avoid broken links.frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx (1)
67-90: Address SonarQube issues: consolidate push calls and reduce nesting depth.The static analysis flags two issues in this test:
- Multiple
Array#push()calls (lines 69-77) should be consolidated- Nesting depth exceeds 4 levels due to
forEachinsideact()(line 89)Apply this diff to address both issues:
- const unregisterFns: (() => void)[] = [] - act(() => { - unregisterFns.push( - registerBreadcrumb({ title: 'Test Organization', path: '/organizations/test-org' }) - ) - unregisterFns.push( - registerBreadcrumb({ - title: 'Test Repository', - path: '/organizations/test-org/repositories/test-repo', - }) - ) - }) + let unregisterOrg: () => void + let unregisterRepo: () => void + act(() => { + unregisterOrg = registerBreadcrumb({ title: 'Test Organization', path: '/organizations/test-org' }) + unregisterRepo = registerBreadcrumb({ + title: 'Test Repository', + path: '/organizations/test-org/repositories/test-repo', + }) + }) // Note: 'repositories' segment is hidden (in HIDDEN_SEGMENTS) expect(result.current).toEqual([ { title: 'Home', path: '/' }, { title: 'Organizations', path: '/organizations' }, { title: 'Test Organization', path: '/organizations/test-org' }, { title: 'Test Repository', path: '/organizations/test-org/repositories/test-repo' }, ]) act(() => { - unregisterFns.forEach((fn) => fn()) + unregisterOrg() + unregisterRepo() })frontend/src/hooks/useBreadcrumbs.ts (2)
6-6: Useexport...fromsyntax for cleaner re-export.Per SonarCloud suggestion, prefer direct re-export syntax.
-export type { BreadcrumbItem } +export type { BreadcrumbItem } from 'types/breadcrumb'Then remove the import on line 3:
-import type { BreadcrumbItem } from 'types/breadcrumb'
8-8: UseSetfor HIDDEN_SEGMENTS for efficient lookup.
Set.has()is O(1) compared toArray.includes()which is O(n). While the performance difference is negligible for a single-element collection, usingSetis idiomatic and aligns with the SonarCloud suggestion.-const HIDDEN_SEGMENTS = ['repositories'] +const HIDDEN_SEGMENTS = new Set(['repositories'])And update line 31:
- if (HIDDEN_SEGMENTS.includes(segment)) { + if (HIDDEN_SEGMENTS.has(segment)) {frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
34-40: Mark props as read-only per SonarCloud recommendation.The component props should be marked as
Readonlyfor immutability and consistency with React best practices.export default async function RepositoryDetailsLayout({ children, params, -}: { +}: Readonly<{ children: React.ReactNode params: Promise<{ repositoryKey: string; organizationKey: string }> -}) { +}>) {frontend/src/contexts/BreadcrumbContext.tsx (2)
57-69: Mark props as read-only per static analysis.SonarCloud correctly flags that
BreadcrumbProviderPropsshould be marked read-only to match the pattern used elsewhere (e.g.,BreadCrumbRendererProps).Additionally, the
[item]dependency could cause re-registration on every render if the parent creates a new object reference. Consider stabilizing withuseMemoat call sites or using primitive values.Apply this diff:
-type BreadcrumbProviderProps = { +type BreadcrumbProviderProps = Readonly<{ item: BreadcrumbItem children: ReactNode -} +}>
71-78: Mark inline props type as read-only.Per SonarCloud and project conventions, wrap the inline props type in
Readonly<>.Apply this diff:
-export function BreadcrumbRoot({ children }: { children: ReactNode }) { +export function BreadcrumbRoot({ children }: Readonly<{ children: ReactNode }>) {
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx(1 hunks)frontend/__tests__/unit/components/PageLayout.test.tsx(1 hunks)frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx(1 hunks)frontend/__tests__/unit/utils/breadcrumb.test.ts(1 hunks)frontend/src/app/chapters/[chapterKey]/layout.tsx(1 hunks)frontend/src/app/chapters/[chapterKey]/page.tsx(2 hunks)frontend/src/app/committees/[committeeKey]/page.tsx(2 hunks)frontend/src/app/layout.tsx(2 hunks)frontend/src/app/members/[memberKey]/layout.tsx(2 hunks)frontend/src/app/organizations/[organizationKey]/layout.tsx(3 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx(2 hunks)frontend/src/app/projects/[projectKey]/layout.tsx(0 hunks)frontend/src/app/projects/[projectKey]/page.tsx(1 hunks)frontend/src/components/BreadCrumbs.tsx(2 hunks)frontend/src/components/BreadCrumbsWrapper.tsx(1 hunks)frontend/src/components/PageLayout.tsx(1 hunks)frontend/src/components/RepositoryCard.tsx(2 hunks)frontend/src/contexts/BreadcrumbContext.tsx(1 hunks)frontend/src/hooks/useBreadcrumbs.ts(1 hunks)frontend/src/types/breadcrumb.ts(1 hunks)frontend/src/utils/breadcrumb.ts(1 hunks)frontend/src/wrappers/testUtil.tsx(1 hunks)
💤 Files with no reviewable changes (1)
- frontend/src/app/projects/[projectKey]/layout.tsx
✅ Files skipped from review due to trivial changes (1)
- frontend/src/app/chapters/[chapterKey]/layout.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/tests/unit/components/PageLayout.test.tsx
- frontend/src/app/projects/[projectKey]/page.tsx
- frontend/src/app/chapters/[chapterKey]/page.tsx
- frontend/src/components/PageLayout.tsx
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-11-17T17:30:42.139Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:169-171
Timestamp: 2025-11-17T17:30:42.139Z
Learning: In the OWASP/Nest frontend tests (PR #2671 context), wrapper functions like `expectChaptersCountEqualsThree` that simply call another helper with a fixed parameter (e.g., `expectChaptersCountEquals(3)`) are intentionally used to avoid arrow function callbacks in `waitFor` calls. This pattern prevents adding nesting depth that would trigger rule typescript:S2004. Example: `await waitFor(expectChaptersCountEqualsThree)` avoids the extra nesting from `await waitFor(() => expectChaptersCountEquals(3))`.
Applied to files:
frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/committees/[committeeKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/components/BreadCrumbsWrapper.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.
Applied to files:
frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx
📚 Learning: 2025-11-17T16:47:05.578Z
Learnt from: anurag2787
Repo: OWASP/Nest PR: 2671
File: frontend/__tests__/unit/components/MultiSearch.test.tsx:427-427
Timestamp: 2025-11-17T16:47:05.578Z
Learning: In the frontend test files for the OWASP/Nest repository, `expect(true).toBe(true)` no-op assertions may be intentionally added as workarounds when ESLint rule jest/expect-expect doesn't detect expectations inside helper functions or waitFor callbacks. These can be resolved by configuring the ESLint rule's assertFunctionNames option to recognize custom assertion helpers instead of flagging them as redundant.
Applied to files:
frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx
🧬 Code graph analysis (13)
frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx (2)
frontend/src/contexts/BreadcrumbContext.tsx (2)
BreadcrumbRoot(71-78)registerBreadcrumb(20-28)frontend/src/hooks/useBreadcrumbs.ts (1)
useBreadcrumbs(49-54)
frontend/src/types/breadcrumb.ts (1)
frontend/src/hooks/useBreadcrumbs.ts (1)
BreadcrumbItem(6-6)
frontend/src/app/committees/[committeeKey]/page.tsx (2)
frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)backend/apps/owasp/api/internal/queries/committee.py (1)
committee(14-28)
frontend/src/wrappers/testUtil.tsx (1)
frontend/src/contexts/BreadcrumbContext.tsx (1)
BreadcrumbRoot(71-78)
frontend/src/contexts/BreadcrumbContext.tsx (2)
frontend/src/hooks/useBreadcrumbs.ts (1)
BreadcrumbItem(6-6)frontend/src/types/breadcrumb.ts (1)
BreadcrumbItem(1-4)
frontend/src/app/members/[memberKey]/layout.tsx (2)
frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)frontend/src/utils/structuredData.ts (1)
generateProfilePageStructuredData(30-76)
frontend/src/components/BreadCrumbs.tsx (2)
frontend/src/hooks/useBreadcrumbs.ts (1)
BreadcrumbItem(6-6)frontend/src/types/breadcrumb.ts (1)
BreadcrumbItem(1-4)
frontend/src/components/BreadCrumbsWrapper.tsx (2)
frontend/src/hooks/useBreadcrumbs.ts (1)
useBreadcrumbs(49-54)frontend/src/types/link.ts (1)
Link(1-7)
frontend/src/hooks/useBreadcrumbs.ts (3)
frontend/src/types/breadcrumb.ts (1)
BreadcrumbItem(1-4)frontend/src/utils/breadcrumb.ts (1)
formatBreadcrumbTitle(3-9)frontend/src/contexts/BreadcrumbContext.tsx (1)
useBreadcrumb(39-55)
frontend/__tests__/unit/utils/breadcrumb.test.ts (1)
frontend/src/utils/breadcrumb.ts (1)
formatBreadcrumbTitle(3-9)
frontend/src/app/layout.tsx (2)
frontend/src/contexts/BreadcrumbContext.tsx (1)
BreadcrumbRoot(71-78)frontend/src/components/BreadCrumbsWrapper.tsx (1)
BreadCrumbsWrapper(10-58)
frontend/src/app/organizations/[organizationKey]/layout.tsx (3)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/organizationQueries.generated.ts (1)
GetOrganizationMetadataDocument(20-20)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
frontend/src/components/RepositoryCard.tsx (1)
backend/apps/github/api/internal/queries/repository.py (1)
repository(14-35)
🪛 GitHub Check: SonarCloud Code Analysis
frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx
[warning] 72-72: Do not call Array#push() multiple times.
[failure] 89-89: Refactor this code to not nest functions more than 4 levels deep.
frontend/src/contexts/BreadcrumbContext.tsx
[warning] 62-62: Mark the props of the component as read-only.
[warning] 71-71: Mark the props of the component as read-only.
frontend/src/hooks/useBreadcrumbs.ts
[warning] 6-6: Use export…from to re-export BreadcrumbItem.
[warning] 8-8: HIDDEN_SEGMENTS should be a Set, and use HIDDEN_SEGMENTS.has() to check existence or non-existence.
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx
[warning] 34-40: Mark the props of the component as read-only.
frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx
[warning] 18-18: Mark the props of the component as read-only.
🔇 Additional comments (21)
frontend/src/types/breadcrumb.ts (1)
1-4: LGTM!The
BreadcrumbItemtype is well-defined with appropriate use ofReadonlyfor immutability. The structure is clean and suitable for breadcrumb data.frontend/src/utils/breadcrumb.ts (1)
3-9: LGTM!The
formatBreadcrumbTitlefunction correctly transforms hyphenated slugs into human-readable titles. The use ofupperFirstpreserves acronyms (e.g., "OWASP-ZAP" → "OWASP ZAP") which is appropriate for breadcrumb display.frontend/src/wrappers/testUtil.tsx (1)
8-12: LGTM!Wrapping the test renderer with
BreadcrumbRootensures that breadcrumb context is consistently available across all tests, which aligns well with the new breadcrumb system architecture.frontend/src/app/committees/[committeeKey]/page.tsx (1)
84-94: LGTM!The
PageLayoutwrapper correctly integrates breadcrumb functionality while preserving all existing component behavior. Usingcommittee.nameas the title appropriately displays the real entity name instead of the URL slug.frontend/src/app/members/[memberKey]/layout.tsx (1)
59-59: LGTM!The title fallback chain
data?.user?.name || data?.user?.login || ''is appropriate. Note that the empty string fallback is technically unreachable due to the guard clause on line 54, but it serves as a defensive safeguard.frontend/__tests__/unit/utils/breadcrumb.test.ts (1)
3-37: Test coverage is comprehensive.The test suite covers all important edge cases including empty input, single/multiple hyphens, numbers, and case preservation. Good work on the thorough testing.
However, per previous feedback from kasya, consider using test data that's either already in cspell or generic fake names. Line 19 uses
'OWASP-ZAP'which might require adding to cspell. The other test values like'test-org-repo'and'test-multi-word-slug-example'appear generic enough.frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx (1)
26-62: LGTM!The test suite provides good coverage of the core BreadCrumbsWrapper functionality:
- Conditional rendering based on route
- Breadcrumb registration and display
- Title override behavior
The test structure is clean and the assertions are appropriate.
frontend/src/app/organizations/[organizationKey]/layout.tsx (1)
103-108: Reconsider the duplicate query concern — Apollo Client deduplicates in-flight queries by default.Apollo Client has
queryDeduplicationenabled by default and will automatically prevent duplicate network requests for identical in-flight queries (same operation and variables). The primary concern should be whether these queries execute simultaneously during server rendering.If both
generateMetadataand the layout component execute during the same server render cycle, Apollo Client's deduplication will likely handle the duplicate. However, if they execute sequentially or at different times, a manual deduplication approach using React'scache()helper is worth considering for clarity and guaranteed deduplication at the server-side request level.The suggested approaches (React
cache(), passing data down) remain valid for explicit control over data fetching, but verify the actual execution order and Apollo Client configuration in your setup before refactoring.frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx (2)
41-60: Good test coverage for registered title precedence.The test correctly verifies that registered titles override auto-generated ones, and properly cleans up by calling
unregister()in the teardown. This ensures no state leakage between tests.
94-113: Edge cases well covered.Tests for null pathname fallback and hyphenated URL formatting are essential for robustness. Good coverage of the
formatBreadcrumbTitleintegration.frontend/src/components/BreadCrumbsWrapper.tsx (2)
10-14: Minor redundancy in root path check.The
pathname === '/'check is likely redundant sinceuseBreadcrumbs()returns only[{ title: 'Home', path: '/' }]for root, makingitems.length <= 1sufficient. However, keeping both provides explicit intent and negligible overhead.
16-56: Clean breadcrumb rendering implementation.Good use of HeroUI Breadcrumbs with proper accessibility (
aria-label), dark mode support, and the last-item non-clickable pattern usingisDisabledcombined with a span. The separator styling is consistent.frontend/src/hooks/useBreadcrumbs.ts (1)
10-47: Well-structured breadcrumb building logic.The
buildBreadcrumbItemsfunction is pure, handles null pathname gracefully, and correctly merges registered items with auto-generated fallbacks. The use of aMapfor O(1) lookups is appropriate.frontend/src/app/layout.tsx (1)
75-84: Clean integration of breadcrumb context and wrapper.The
BreadcrumbRootcorrectly wraps the layout content to provide the breadcrumb registry context, andBreadCrumbsWrapperis positioned appropriately after theHeader. This structure ensures all child routes can register their breadcrumbs.frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (2)
46-48: Verify necessity offormatBreadcrumbTitleon repository name.
formatBreadcrumbTitleconverts hyphenated slugs to title case (e.g., "test-repo" → "Test Repo"). Ifdata.repository.namealready contains the proper display name from the backend (e.g., "Test Repository"), applying this transformation may be unnecessary or even incorrect for names with intentional casing like "OWASP ZAP".Consider conditionally applying formatting only to the fallback:
- const repoName = data?.repository?.name - ? formatBreadcrumbTitle(data.repository.name) - : formatBreadcrumbTitle(repositoryKey) + const repoName = data?.repository?.name ?? formatBreadcrumbTitle(repositoryKey)
42-45: Duplicate Apollo Client query execution.The same
GetRepositoryMetadataDocumentquery executes in bothgenerateMetadataand the layout function. Unlike Next.js's automatic deduplication offetch()requests, Apollo Client queries do not benefit from this built-in mechanism and will result in two separate network calls during the same server render.Consider extracting the query into a function wrapped with
React.cache()to ensure a single execution per render, or leverage Apollo's normalized cache with an appropriate cache policy.frontend/src/components/BreadCrumbs.tsx (2)
7-9: Props type correctly marked as readonly.The
BreadCrumbRendererPropstype properly usesReadonly<>wrapper andreadonlymodifier for the items array, ensuring immutability.
30-52: Breadcrumb rendering logic is well-implemented.The component correctly handles the distinction between navigable items (rendered as
Link) and the current page (rendered asspanwitharia-current="page"). Usingitem.pathas the key is appropriate since paths should be unique within a breadcrumb trail.frontend/src/contexts/BreadcrumbContext.tsx (3)
11-28: Registry pattern is well-suited for client-side breadcrumb management.The module-level singleton with Map-based storage and listener notification follows an external store pattern that works well with React's
useSyncExternalStoresemantics. Usingpathas the key naturally prevents duplicate breadcrumbs for the same route.
30-37: Sorting by path length works for typical hierarchical routes.The sorting logic correctly prioritizes the root path and then orders by path length, which aligns with typical URL hierarchies where parent routes have shorter paths. For cases where paths have equal length but different depths (e.g.,
/a/bvs/x/y), the order would be arbitrary, but this is unlikely to be an issue in practice for breadcrumbs on the same page.
39-55: Subscription pattern in useBreadcrumb is implemented correctly.The hook properly uses a lazy initializer for
useState, adds the listener immediately, syncs on mount, and cleans up on unmount. This follows the recommended pattern for subscribing to external stores in React.
…mprove breadcrumb registration logic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/src/hooks/useBreadcrumbs.ts (1)
8-8: Consider making hidden segments configurable.The
HIDDEN_SEGMENTSset is currently hardcoded with'repositories'. If additional segments need hiding in the future, this requires code changes.Consider accepting hidden segments as a parameter or loading from a config:
// Option 1: Parameter (if different routes need different rules) function buildBreadcrumbItems( pathname: string | null, registeredItems: BreadcrumbItem[], hiddenSegments: ReadonlySet<string> = HIDDEN_SEGMENTS ): BreadcrumbItem[] { // ... use hiddenSegments instead of HIDDEN_SEGMENTS } // Option 2: Centralized config file // config/breadcrumbs.ts export const BREADCRUMB_CONFIG = { hiddenSegments: ['repositories'] as const, } as const
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/__tests__/unit/components/BreadCrumbsWrapper.test.tsx(1 hunks)frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx(1 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx(2 hunks)frontend/src/contexts/BreadcrumbContext.tsx(1 hunks)frontend/src/hooks/useBreadcrumbs.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/tests/unit/hooks/useBreadcrumbs.test.tsx
- frontend/tests/unit/components/BreadCrumbsWrapper.test.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/hooks/useBreadcrumbs.ts (3)
frontend/src/types/breadcrumb.ts (1)
BreadcrumbItem(1-4)frontend/src/utils/breadcrumb.ts (1)
formatBreadcrumbTitle(3-9)frontend/src/contexts/BreadcrumbContext.tsx (1)
useBreadcrumb(39-55)
frontend/src/contexts/BreadcrumbContext.tsx (1)
frontend/src/types/breadcrumb.ts (1)
BreadcrumbItem(1-4)
🔇 Additional comments (2)
frontend/src/hooks/useBreadcrumbs.ts (1)
1-54: Implementation looks solid.The breadcrumb building logic correctly handles pathname parsing, registered item lookup, hidden segment filtering, and fallback formatting. The hook properly integrates with Next.js navigation and the breadcrumb context.
frontend/src/contexts/BreadcrumbContext.tsx (1)
1-79: Well-designed breadcrumb registry system.The implementation follows solid patterns:
- Pub/sub with Map/Set for efficient lookups and notifications
- Proper cleanup functions that capture correct references
- Lazy state initialization in
useBreadcrumbto optimize initial renders- Dependency array
[item.path, item.title]correctly addresses the inline object re-registration concern from past reviewsThe registry correctly handles registration/unregistration lifecycle, sorting prioritizes root, and listeners are notified synchronously on changes.
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx
Outdated
Show resolved
Hide resolved
…ts and enhance layout structure
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: 4
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/src/app/chapters/[chapterKey]/layout.tsx(2 hunks)frontend/src/app/committees/[committeeKey]/layout.tsx(2 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx(3 hunks)frontend/src/app/projects/[projectKey]/layout.tsx(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (4)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/repositoryQueries.generated.ts (1)
GetRepositoryMetadataDocument(22-22)frontend/src/utils/breadcrumb.ts (1)
formatBreadcrumbTitle(3-9)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
frontend/src/app/projects/[projectKey]/layout.tsx (3)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/projectQueries.generated.ts (1)
GetProjectMetadataDocument(37-37)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
frontend/src/app/chapters/[chapterKey]/layout.tsx (3)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/app/projects/[projectKey]/layout.tsx (1)
generateMetadata(17-36)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
frontend/src/app/committees/[committeeKey]/layout.tsx (9)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/committeeQueries.generated.ts (1)
GetCommitteeMetadataDocument(20-20)frontend/src/app/chapters/[chapterKey]/layout.tsx (1)
generateMetadata(17-34)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
generateMetadata(18-38)frontend/src/app/projects/[projectKey]/layout.tsx (1)
generateMetadata(17-36)frontend/src/app/members/[memberKey]/layout.tsx (1)
generateMetadata(13-36)frontend/src/app/organizations/[organizationKey]/layout.tsx (1)
generateMetadata(12-34)frontend/src/app/snapshots/[id]/layout.tsx (1)
generateMetadata(7-27)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
🪛 GitHub Check: SonarCloud Code Analysis
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx
[warning] 3-3: 'react' imported multiple times.
[warning] 2-2: 'react' imported multiple times.
frontend/src/app/projects/[projectKey]/layout.tsx
[warning] 3-3: 'react' imported multiple times.
[warning] 2-2: 'react' imported multiple times.
frontend/src/app/chapters/[chapterKey]/layout.tsx
[warning] 3-3: 'react' imported multiple times.
[warning] 2-2: 'react' imported multiple times.
frontend/src/app/committees/[committeeKey]/layout.tsx
[warning] 2-2: 'react' imported multiple times.
[warning] 3-3: 'react' imported multiple times.
🔇 Additional comments (5)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (2)
10-16: Good use ofcache()for request deduplication.Wrapping the Apollo query in React's
cache()ensuresgenerateMetadataand the layout component share a single request per render cycle, addressing the duplicate query concern.
49-60: Inconsistent fallback behavior compared to other layouts.This layout always renders
PageLayoutwith a fallback title (formatted URL slug), whereasprojects,chapters, andcommitteeslayouts return barechildrenwhen data is missing. Consider aligning the approach for consistency—either all layouts should providePageLayoutwith fallback titles, or all should skip it when data is unavailable.frontend/src/app/projects/[projectKey]/layout.tsx (1)
9-15: LGTM on the cached data fetch pattern.The
cache()wrapper properly deduplicates the GraphQL query betweengenerateMetadataand the layout component. The async layout pattern and conditional PageLayout wrapping are correct.Also applies to: 38-53
frontend/src/app/chapters/[chapterKey]/layout.tsx (1)
9-15: Implementation looks good.Correctly uses
cache()for query deduplication. The async layout pattern with conditionalPageLayoutwrapping matches the project layout approach.Also applies to: 36-51
frontend/src/app/committees/[committeeKey]/layout.tsx (1)
9-15: Implementation is correct and consistent.The cached data fetching and async layout pattern align well with the other detail layouts (projects, chapters). Query deduplication via
cache()ensures efficient data loading.Also applies to: 36-51
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx
Outdated
Show resolved
Hide resolved
…y in layout components
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/app/committees/[committeeKey]/layout.tsx (1)
25-32: Return empty object instead of null for metadata.While
nullis technically valid, Next.js best practices recommend returning an empty object{}when metadata cannot be generated.Apply this diff:
return committee ? generateSeoMetadata({ canonicalPath: `/committees/${committeeKey}`, description: committee.summary ?? `${committee.name} details`, keywords: ['owasp', 'security', 'committee', committeeKey, committee.name], title: committee.name, }) - : null + : {}
♻️ Duplicate comments (1)
frontend/src/app/chapters/[chapterKey]/layout.tsx (1)
2-2: LGTM! Duplicate import resolved.The consolidated import addresses the previous SonarCloud warning and reduces redundancy.
🧹 Nitpick comments (1)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
2-7: Optional: switch to type-only React import for cleaner bundle.Since React 19 with the modern JSX transform doesn’t require
Reactat runtime, you can avoid a runtimereactimport by using a type-only import forReactNodeand a named import forcache:-import React, { cache } from 'react' +import type { ReactNode } from 'react' +import { cache } from 'react' … -export default async function RepositoryDetailsLayout({ - children, +export default async function RepositoryDetailsLayout({ + children, params, }: Readonly<{ - children: React.ReactNode + children: ReactNode params: Promise<{ repositoryKey: string; organizationKey: string }> }>) {This keeps types intact while trimming an unnecessary runtime dependency.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/src/app/chapters/[chapterKey]/layout.tsx(2 hunks)frontend/src/app/committees/[committeeKey]/layout.tsx(2 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx(3 hunks)frontend/src/app/projects/[projectKey]/layout.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/app/projects/[projectKey]/layout.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/app/chapters/[chapterKey]/layout.tsx (3)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/chapterQueries.generated.ts (1)
GetChapterMetadataDocument(20-20)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (4)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/repositoryQueries.generated.ts (1)
GetRepositoryMetadataDocument(22-22)frontend/src/utils/breadcrumb.ts (1)
formatBreadcrumbTitle(3-9)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
🔇 Additional comments (5)
frontend/src/app/committees/[committeeKey]/layout.tsx (2)
2-2: Duplicate import resolved.The duplicate React import issue from the previous review has been correctly addressed by consolidating into a single import statement.
49-49: Verify PageLayout accepts title prop.Ensure that the
PageLayoutcomponent correctly accepts and handles thetitleprop, and that this integration properly feeds into the breadcrumb system as intended by the PR objectives.frontend/src/app/chapters/[chapterKey]/layout.tsx (1)
45-47: Verify the fallback behavior when chapter data is absent.When
!data?.chapter, the layout returnschildrendirectly without wrapping inPageLayout. This means breadcrumbs won't be displayed for non-existent chapters. Please confirm this is the intended behavior for 404 or error scenarios.If breadcrumbs should still appear (e.g., showing a generic "Chapter" label), consider providing a fallback:
if (!data?.chapter) { - return children + return <PageLayout title="Chapter">{children}</PageLayout> }frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (2)
9-15: Good use ofcacheto deduplicate repository metadata queries.Wrapping the Apollo query in
getRepositoryMetadatawith React’scacheand reusing it in bothgenerateMetadataand the layout cleanly eliminates duplicate network requests per render and centralizes the fetch logic. The argument list(organizationKey, repositoryKey)is a stable cache key, and returning onlydatakeeps call sites simple.Also applies to: 26-27, 47-47
39-59: Layout integration withPageLayoutand breadcrumb title logic looks solid.Deriving
repoNamefromdata.repository.nameand falling back to a prettifiedrepositoryKeyviaformatBreadcrumbTitle, then passing{ title: repoName, path: /organizations/${organizationKey}/repositories/${repositoryKey} }intoPageLayout, matches the new breadcrumb design (real entity names with sensible slug fallback) and keeps the route path explicit for the breadcrumb provider.Also applies to: 48-50
|
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
🧹 Nitpick comments (2)
frontend/src/app/projects/[projectKey]/layout.tsx (1)
41-56: LGTM, but consider extracting the shared layout pattern.The async layout with conditional
PageLayoutwrapping works correctly. However, this exact pattern (cached data fetch → conditional PageLayout wrapper) is duplicated across projects, committees, chapters, and repositories layouts. Consider abstracting this into a reusable higher-order component or utility function to reduce duplication and simplify future maintenance.Example abstraction:
// utils/createEntityLayout.tsx export function createEntityLayout<T>({ getMetadata, extractName, }: { getMetadata: (key: string) => Promise<T | null> extractName: (data: T) => string }) { return async function EntityLayout({ children, params, ... }: LayoutProps) { const { key } = await params const data = await getMetadata(key) if (!data) return children return <PageLayout title={extractName(data)}>{children}</PageLayout> } }frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
52-64: Consider standardizing title formatting and path prop usage.This layout differs from projects/committees/chapters layouts in two ways:
- Title formatting: Uses
formatBreadcrumbTitleto convert repository names to Title Case (e.g., "my-repo" → "My Repo"), while other layouts use raw entity names directly.- Explicit path prop: Passes
pathtoPageLayout, while other layouts rely onPageLayout's internalusePathname()fallback.These inconsistencies may lead to:
- Different breadcrumb title casing across entity types (formatted repositories vs. raw project/chapter/committee names)
- Potential confusion about when explicit paths are needed
Recommendation: Establish a consistent pattern:
- Either apply
formatBreadcrumbTitleto all entity layouts (if names can contain dashes/special characters), or remove it from repositories (if GraphQL names are already formatted).- Document when explicit
pathprops are necessary (e.g., for nested routes that need non-standard breadcrumb paths) vs. whenusePathname()suffices.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/src/app/chapters/[chapterKey]/layout.tsx(2 hunks)frontend/src/app/committees/[committeeKey]/layout.tsx(2 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx(3 hunks)frontend/src/app/projects/[projectKey]/layout.tsx(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-30T12:52:32.214Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2155
File: frontend/graphql-codegen.ts:52-52
Timestamp: 2025-08-30T12:52:32.214Z
Learning: When using GraphQL Codegen with async configuration that needs to fetch data (like CSRF tokens), export the async function directly instead of using top-level await: `export default async function()` rather than `export default await function()`.
Applied to files:
frontend/src/app/chapters/[chapterKey]/layout.tsx
🧬 Code graph analysis (2)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (4)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/repositoryQueries.generated.ts (1)
GetRepositoryMetadataDocument(22-22)frontend/src/utils/breadcrumb.ts (1)
formatBreadcrumbTitle(3-9)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
frontend/src/app/chapters/[chapterKey]/layout.tsx (9)
frontend/src/server/apolloClient.ts (1)
apolloClient(36-37)frontend/src/types/__generated__/chapterQueries.generated.ts (1)
GetChapterMetadataDocument(20-20)frontend/src/app/committees/[committeeKey]/layout.tsx (1)
generateMetadata(20-37)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
generateMetadata(21-41)frontend/src/app/projects/[projectKey]/layout.tsx (1)
generateMetadata(20-39)frontend/src/app/members/[memberKey]/layout.tsx (1)
generateMetadata(13-36)frontend/src/app/organizations/[organizationKey]/layout.tsx (1)
generateMetadata(12-34)frontend/src/app/snapshots/[id]/layout.tsx (1)
generateMetadata(7-27)frontend/src/components/PageLayout.tsx (1)
PageLayout(13-18)
🔇 Additional comments (4)
frontend/src/app/projects/[projectKey]/layout.tsx (1)
8-18: LGTM: Cached metadata helper follows React 19/Next.js 15 best practices.The
getProjectMetadatahelper correctly uses React'scacheto deduplicate requests acrossgenerateMetadataand the layout component, and the try-catch ensures graceful degradation when data fetching fails.frontend/src/app/committees/[committeeKey]/layout.tsx (1)
8-54: LGTM: Implementation matches the established pattern.The cached helper and conditional layout wrapping are correctly implemented, consistent with the projects layout. The same code duplication opportunity noted in
frontend/src/app/projects/[projectKey]/layout.tsxapplies here.frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
9-19: LGTM: Cached helper follows best practices.The
getRepositoryMetadataimplementation correctly uses React'scachewith proper error handling, consistent with the pattern established in other entity layouts.frontend/src/app/chapters/[chapterKey]/layout.tsx (1)
8-54: LGTM: Implementation follows the established pattern.The cached helper and async layout are correctly implemented, consistent with projects and committees layouts. Note the code duplication opportunity mentioned in
frontend/src/app/projects/[projectKey]/layout.tsxand the title formatting inconsistency flagged infrontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx.




Tasks
/repositoriesto appear in breadcrumb as the page does not really existBreadCrumbs.tsx,BreadCrumbsWrapper.tsxResolves #1425
Proposed change
Display actual entity names in breadcrumbs instead of URL slugs.
Currently, breadcrumbs show URL-formatted text like "Bjornkimminich" instead of the real name "Björn Kimminich" from the database. This PR fixes that by having detail pages pass their GraphQL data to the breadcrumb system through a simple interface.
Pages now pass entity names like this:
The breadcrumb checks which field is set and displays that name. If no data is provided, it falls back to formatting the URL slug nicely.
Also refactored breadcrumb logic into reusable components (
BreadCrumbs,BreadCrumbsWrapper,useBreadcrumbshook) and added 72 tests.Detailed PR
Problem
Breadcrumbs were showing URL slugs instead of actual entity names from the database.
Solution
Created Reusable Breadcrumb System
Three main pieces:
BreadCrumbs.tsx- Pure UI component that renders breadcrumb items.BreadCrumbsWrapper.tsx- Renders breadcrumbs. It takes an array of items and displays them nicely with proper styling and navigation links.useBreadcrumbshook - Contains all the logic for building breadcrumbs. It handles both simple routes (/projects/zap) and nested routes (/organizations/x/repositories/y).Pages Pass Real Names via Props
Each detail page already fetches GraphQL data. Now they pass the entity name to
PageLayout:The interface accepts different entity types:
The hook checks which field is present and uses it. If none is provided, it falls back to formatting the URL slug.
Special Handling for Nested Routes
Repository routes (
/organizations/[org]/repositories/[repo]) get special detection. The breadcrumb displays actual org and repo names, and skips the "Repositories" segment since that page doesn't exist.Testing
Added 72 tests covering rendering, auto-generation, real name display, fallbacks, and edge cases. All pass.
BEFORE
AFTER
BEFORE
AFTER
Checklist
make check-testlocally; all checks and tests passed.