feat: added breadcrumbs to page detail header#13
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughIntroduces 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–25 minutes
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 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
Groupcomponent withgapprop for more consistent spacing with the design system.src/app/api/v1/pages/[id]/breadcrumbs/route.ts (1)
59-59: Consider browser compatibility oftoReversed().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 classicreverse()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
📒 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.tssrc/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.tssrc/app/pages/[id]/page.tsxsrc/app/api/v1/pages/[id]/breadcrumbs/route.tssrc/lib/hooks/api/use-page-breadcrumbs.tssrc/types/api/endpoints/index.tssrc/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.tssrc/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 > 0check is redundant sincePageBreadcrumbalready 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
apiRoutewrapper 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 withbaseURL: '/api/v1', which automatically prepends this prefix to all requests. The endpoint constant'/pages/:id/breadcrumbs'will correctly resolve to/api/v1/pages/:id/breadcrumbswhen used with the configured client.Likely an incorrect or invalid review comment.
| const parentPage = await containerRepository.getOneByQuery( | ||
| addUserIdToQuery(containerRepository.createQuery().eq('id', currentPage.parentId), session.user.id).eq( | ||
| 'type', | ||
| 'page' | ||
| ) | ||
| ); |
There was a problem hiding this comment.
🛠️ 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.
Summary by CodeRabbit