Skip to content

feat: added breadcrumbs to page detail header#13

Merged
danships merged 3 commits intomainfrom
feat/page-breadcrumbs
Jan 14, 2026
Merged

feat: added breadcrumbs to page detail header#13
danships merged 3 commits intomainfrom
feat/page-breadcrumbs

Conversation

@danships
Copy link
Owner

@danships danships commented Nov 7, 2025

Summary by CodeRabbit

  • New Features
    • Added breadcrumb navigation trail to page headers, displaying the full hierarchy from root to current page.
    • Breadcrumbs support emoji indicators and are fully navigable, allowing users to jump to parent pages via clickable links.

@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Introduces page breadcrumbs feature by adding a new API endpoint that traverses parent pages with cycle protection, a React hook for data fetching, a visual breadcrumb component, and integration into the page detail view.

Changes

Cohort / File(s) Summary
API Endpoint
src/app/api/v1/pages/[id]/breadcrumbs/route.ts
New GET route handler that fetches page breadcrumbs by traversing the parent chain upward from the specified page ID, accumulating pages with id, name, emoji, parentId, createdAt, and lastUpdated. Includes cycle detection via visitedIds set and stops on missing/invalid parents. Returns breadcrumb list in root-to-current order.
Type Definitions
src/types/api/endpoints/get-page-breadcrumbs.ts, src/types/api/endpoints/index.ts
Introduces endpoint constant GET_PAGE_BREADCRUMBS_ENDPOINT, parameter schema validating non-empty id string, and response schema as array of pages. Re-exports new module in endpoints index.
React Hook
src/lib/hooks/api/use-page-breadcrumbs.ts
New hook usePageBreadcrumbs(pageId) wraps useSWR to fetch breadcrumbs data, constructing endpoint URL dynamically and skipping fetch when pageId is null.
Breadcrumb Component
src/components/molecules/page-breadcrumb.tsx
New React component rendering Mantine Breadcrumbs trail from pages array, displaying emoji (if present) and page name, with non-final items as links to /pages/{id}.
Page Integration
src/pages/[id]/page.tsx
Integrates breadcrumbs by calling usePageBreadcrumbs hook and rendering PageBreadcrumb component in page header when data loads.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PageComponent as Page Component<br/>(src/pages/[id]/page.tsx)
    participant Hook as usePageBreadcrumbs Hook
    participant SWR as SWR Client
    participant API as GET /api/v1/pages/[id]/<br/>breadcrumbs
    participant Repository as Repository
    participant BreadcrumbComponent as PageBreadcrumb<br/>Component

    User->>PageComponent: Navigate to /pages/[id]
    PageComponent->>Hook: usePageBreadcrumbs(pageId)
    Hook->>SWR: useSWR(endpoint, fetcher)
    SWR->>API: Fetch breadcrumbs
    API->>Repository: Get page by ID
    loop Traverse parent chain
        API->>Repository: Get parent page
        alt Parent exists & not visited
            API->>API: Add to breadcrumbs list
        else Cycle or missing parent
            API->>API: Stop traversal
        end
    end
    API->>API: Reverse list (root-to-current)
    API-->>SWR: Return breadcrumbs array
    SWR-->>Hook: isLoading=false, data=breadcrumbs
    Hook-->>PageComponent: Return breadcrumbs + isLoading
    PageComponent->>BreadcrumbComponent: Render if breadcrumbs exist
    BreadcrumbComponent-->>User: Display breadcrumb trail
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • API route parent traversal logic: Verify cycle detection via visitedIds set is complete and handles all edge cases (e.g., deleted parent, type validation)
  • Boundary conditions: Confirm behavior when root page is reached and when invalid parent references exist
  • Type safety: Ensure GetPageBreadcrumbsResponse type flows correctly through hook to component
  • User scope enforcement: Verify addUserIdToQuery properly isolates data to current user

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: added breadcrumbs to page detail header' accurately summarizes the main change, which is adding a breadcrumb component to the page detail view's header section.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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 (2)
src/components/molecules/page-breadcrumb.tsx (1)

19-24: Consider adding spacing between emoji and name.

The emoji and name are rendered in adjacent spans without spacing, which might appear cramped visually.

Apply this diff to add spacing:

         const content = (
           <>
-            {page.emoji && <span>{page.emoji}</span>}
+            {page.emoji && <span style={{ marginRight: '0.25em' }}>{page.emoji}</span>}
             <span>{page.name}</span>
           </>
         );

Alternatively, use Mantine's Group component with gap prop for more consistent spacing with the design system.

src/app/api/v1/pages/[id]/breadcrumbs/route.ts (1)

59-59: Consider browser compatibility of toReversed().

The toReversed() method is an ES2023 feature. Given Next.js 15.5.6's modern target environment, this should be fine. However, if you need to support older browsers, consider using the classic reverse() pattern.

Alternative approach if broader compatibility is needed:

-    return breadcrumbs.toReversed();
+    return [...breadcrumbs].reverse();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4d588f and b34cc22.

📒 Files selected for processing (6)
  • src/app/api/v1/pages/[id]/breadcrumbs/route.ts (1 hunks)
  • src/app/pages/[id]/page.tsx (3 hunks)
  • src/components/molecules/page-breadcrumb.tsx (1 hunks)
  • src/lib/hooks/api/use-page-breadcrumbs.ts (1 hunks)
  • src/types/api/endpoints/get-page-breadcrumbs.ts (1 hunks)
  • src/types/api/endpoints/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/types/api/endpoints/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define Zod schemas and exported types for new endpoints under src/types/api/endpoints/.

Files:

  • src/types/api/endpoints/get-page-breadcrumbs.ts
  • src/types/api/endpoints/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Prefer type aliases over interface in TypeScript.

Files:

  • src/types/api/endpoints/get-page-breadcrumbs.ts
  • src/app/pages/[id]/page.tsx
  • src/app/api/v1/pages/[id]/breadcrumbs/route.ts
  • src/lib/hooks/api/use-page-breadcrumbs.ts
  • src/types/api/endpoints/index.ts
  • src/components/molecules/page-breadcrumb.tsx
src/app/!(api)/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

On the client, use useAuth() to access user/session state via the auth provider.

Files:

  • src/app/pages/[id]/page.tsx
src/app/!(api)/**

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Front-end code should call backend endpoints through src/lib/api/client.ts rather than raw fetch/axios calls.

Files:

  • src/app/pages/[id]/page.tsx
src/app/api/**/route.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/app/api/**/route.ts: Place Next.js API route handlers at src/app/api//route.ts and export functions named GET, POST, etc.
Use apiRoute<Resp, Query, Body>(options, handler) in all API route handlers to enforce Zod validation and typed session.
In routes, import and use expectedQuerySchema/expectedBodySchema from src/types/api when validating input.
Obtain repositories via getWorkspaceRepository()/getContainerRepository() from src/lib/database in API routes.
Always scope database queries with addUserIdToQuery(..., session.user.id) to prevent cross-user data leaks.
Routes must only live under src/app/api/*; do not place route handlers elsewhere.

Files:

  • src/app/api/v1/pages/[id]/breadcrumbs/route.ts
🧠 Learnings (4)
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
Repo: danships/thoth PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/types/api/endpoints/**/*.ts : Define Zod schemas and exported types for new endpoints under src/types/api/endpoints/.

Applied to files:

  • src/types/api/endpoints/get-page-breadcrumbs.ts
  • src/types/api/endpoints/index.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
Repo: danships/thoth PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : Place Next.js API route handlers at src/app/api/<route>/route.ts and export functions named GET, POST, etc.

Applied to files:

  • src/app/api/v1/pages/[id]/breadcrumbs/route.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
Repo: danships/thoth PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : Routes must only live under src/app/api/*; do not place route handlers elsewhere.

Applied to files:

  • src/app/api/v1/pages/[id]/breadcrumbs/route.ts
📚 Learning: 2025-10-24T20:04:43.029Z
Learnt from: CR
Repo: danships/thoth PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-24T20:04:43.029Z
Learning: Applies to src/app/api/**/route.ts : In routes, import and use expectedQuerySchema/expectedBodySchema from src/types/api when validating input.

Applied to files:

  • src/app/api/v1/pages/[id]/breadcrumbs/route.ts
🧬 Code graph analysis (5)
src/types/api/endpoints/get-page-breadcrumbs.ts (1)
src/types/api/entities.ts (1)
  • pageSchema (5-12)
src/app/pages/[id]/page.tsx (2)
src/lib/hooks/api/use-page-breadcrumbs.ts (1)
  • usePageBreadcrumbs (5-6)
src/components/molecules/page-breadcrumb.tsx (1)
  • PageBreadcrumb (11-42)
src/app/api/v1/pages/[id]/breadcrumbs/route.ts (6)
src/lib/api/route-wrapper.ts (1)
  • apiRoute (15-128)
src/types/api/endpoints/get-page-breadcrumbs.ts (3)
  • GetPageBreadcrumbsResponse (14-14)
  • GetPageBreadcrumbsParameters (10-10)
  • getPageBreadcrumbsParametersSchema (6-8)
src/lib/database/index.ts (1)
  • getContainerRepository (22-25)
src/types/api/entities.ts (1)
  • Page (13-13)
src/lib/database/retrievers/page-retriever.ts (1)
  • pageRetriever (22-22)
src/lib/database/helpers.ts (1)
  • addUserIdToQuery (3-5)
src/lib/hooks/api/use-page-breadcrumbs.ts (2)
src/types/api/endpoints/get-page-breadcrumbs.ts (2)
  • GetPageBreadcrumbsResponse (14-14)
  • GET_PAGE_BREADCRUMBS_ENDPOINT (4-4)
src/lib/swr/fetcher.ts (1)
  • swrFetcher (3-3)
src/components/molecules/page-breadcrumb.tsx (1)
src/types/api/entities.ts (1)
  • Page (13-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci
🔇 Additional comments (12)
src/types/api/endpoints/index.ts (1)

10-10: LGTM!

The export follows the established pattern and correctly exposes the new breadcrumbs endpoint types.

src/app/pages/[id]/page.tsx (3)

17-18: LGTM!

The imports are correctly structured and follow the existing patterns in the file.


31-31: LGTM!

The hook usage follows the same pattern as other data fetching hooks in this component.


156-156: LGTM!

The conditional rendering properly guards against loading states and empty breadcrumbs. Note that the breadcrumbs.length > 0 check is redundant since PageBreadcrumb already handles empty arrays, but this explicit check improves readability and avoids unnecessary component instantiation.

src/components/molecules/page-breadcrumb.tsx (2)

11-14: LGTM!

The early return guard is a good practice for handling edge cases.


26-38: LGTM!

The breadcrumb navigation logic correctly distinguishes between the current page (non-clickable) and parent pages (clickable links).

src/lib/hooks/api/use-page-breadcrumbs.ts (1)

5-6: LGTM!

The hook correctly implements the SWR pattern with proper conditional fetching and type safety.

src/app/api/v1/pages/[id]/breadcrumbs/route.ts (3)

8-11: LGTM!

The route setup correctly uses the apiRoute wrapper with proper schema validation as per coding guidelines.


13-18: LGTM!

Proper initialization and user-scoped page retrieval.


22-26: LGTM!

Essential protection against circular parent references that could cause infinite loops.

src/types/api/endpoints/get-page-breadcrumbs.ts (2)

6-14: LGTM!

The schemas and type definitions follow the coding guidelines and correctly model the endpoint's contract. As per coding guidelines.


4-4: The endpoint path is correct. The apiClient is configured with baseURL: '/api/v1', which automatically prepends this prefix to all requests. The endpoint constant '/pages/:id/breadcrumbs' will correctly resolve to /api/v1/pages/:id/breadcrumbs when used with the configured client.

Likely an incorrect or invalid review comment.

Comment on lines 44 to 49
const parentPage = await containerRepository.getOneByQuery(
addUserIdToQuery(containerRepository.createQuery().eq('id', currentPage.parentId), session.user.id).eq(
'type',
'page'
)
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Refactor to use pageRetriever for consistency.

This code manually queries the containerRepository, while line 18 uses pageRetriever.retrievePage(). Using the retriever consistently would simplify this code and reduce duplication of query logic.

Apply this diff to use the retriever:

       // Fetch the parent page
-      const parentPage = await containerRepository.getOneByQuery(
-        addUserIdToQuery(containerRepository.createQuery().eq('id', currentPage.parentId), session.user.id).eq(
-          'type',
-          'page'
-        )
-      );
+      const parentPage = await pageRetriever.retrievePage(currentPage.parentId, session.user.id);

Then simplify the check:

-      if (!parentPage || parentPage.type !== 'page') {
+      if (!parentPage) {
         break;
       }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/app/api/v1/pages/[id]/breadcrumbs/route.ts around lines 44 to 49, the
code directly queries containerRepository for the parent page while earlier
(line 18) it uses pageRetriever.retrievePage(); replace the manual
containerRepository.getOneByQuery call with
pageRetriever.retrievePage(session.user.id, currentPage.parentId) (or the
retriever's equivalent signature) to centralize query logic, then simplify the
subsequent check to rely on the retriever's return (e.g., if (!parentPage)
return null or handle not-found) instead of reapplying query predicates; ensure
you pass user id/session correctly and preserve the original type check if the
retriever doesn’t already enforce it.

@danships danships merged commit e08f235 into main Jan 14, 2026
2 checks passed
@danships danships deleted the feat/page-breadcrumbs branch January 14, 2026 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant