Conversation
WalkthroughAdds “home page” support across client and server: UI toggle in PageHeaderMenu, props plumbed through PageHeader and page container, conditional space card navigation, ISpace type update, server DTO/service validation and persistence for homePageId, and database type additions for Spaces and related tables. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant PH as PageHeaderMenu (Client)
participant C as Client API Layer
participant S as SpaceService (Server)
participant PR as PageRepo
participant SR as SpaceRepo
U->>PH: Click "Home" toggle
PH->>C: mutate updateSpace({ spaceId, homePageId or null })
rect rgba(200,230,255,0.3)
C->>S: PATCH /spaces/:id { homePageId }
S->>S: Validate DTO (UUID/optional)
alt homePageId provided
S->>PR: getById(homePageId)
PR-->>S: page | null
alt page not found
S-->>C: 400 BadRequest (invalid homePageId)
C-->>PH: error
note right of PH: Keep local state unchanged
else page found
S->>SR: updateSpace({ id, homePageId })
SR-->>S: updated space
S-->>C: 200 OK { space }
C-->>PH: success
PH->>PH: setIsHomePage(true/false)
end
else homePageId null/omitted
S->>SR: updateSpace({ id, homePageId: null })
SR-->>S: updated space
S-->>C: 200 OK
C-->>PH: success
PH->>PH: setIsHomePage(false)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/server/src/core/space/services/space.service.ts (1)
108-117: Add validation for homePageId before persisting.The service persists
homePageIdwithout validating that:
- The page actually exists
- The page belongs to the space being updated
This could allow setting a non-existent page or a page from a different space as the home page, leading to broken navigation and data integrity issues.
Add validation before the update. Example implementation:
async updateSpace( updateSpaceDto: UpdateSpaceDto, workspaceId: string, ): Promise<Space> { if (updateSpaceDto?.slug) { const slugExists = await this.spaceRepo.slugExists( updateSpaceDto.slug, workspaceId, ); if (slugExists) { throw new BadRequestException( 'Space slug exists. Please use a unique space slug', ); } } // Validate homePageId if provided if (updateSpaceDto?.homePageId) { const page = await this.pageRepo.findById( updateSpaceDto.homePageId, workspaceId, ); if (!page || page.spaceId !== updateSpaceDto.spaceId) { throw new BadRequestException( 'Invalid home page: page does not exist or does not belong to this space', ); } } return await this.spaceRepo.updateSpace( { name: updateSpaceDto.name, description: updateSpaceDto.description, slug: updateSpaceDto.slug, homePageId: updateSpaceDto.homePageId }, updateSpaceDto.spaceId, workspaceId, ); }apps/client/src/features/page/components/header/page-header-menu.tsx (1)
55-105: Guard optional props before mutating space state
isHome,spaceId, andpageIdare all optional yet the new logic assumes they are defined. Today this compiles because the props are typed as optional, but at runtime we immediately feed them intouseState<boolean>(isHome)andsetIsHomePage(isHome)(Line 67 / Line 103). WhenisHomeisundefined(the default for spaces without a home page), React receivesundefinedwhere a boolean is required. TypeScript also rejects this understrictNullChecks, so the build will break.Additionally, when the user marks a page as the home page, we send
{ homePageId: pageId }. IfpageIdorspaceIdis missing (which can happen because they are optional), we end up hitting the mutation withundefined, leading to a backend validation error and leaving the UI out of sync.Please normalise the props before use (default
isHometofalse) and bail out early if eitherspaceIdorpageIdis missing so we never fire an invalid update. Example fix:-const [isHomePage, setIsHomePage] = useState<boolean>(isHome); +const [isHomePage, setIsHomePage] = useState<boolean>(Boolean(isHome)); … -const handleUpdateHomePage = async () => { - const newHomePageValue = isHomePage ? null : pageId; - const spaceData: Partial<ISpace> = { - spaceId: spaceId, - homePageId: newHomePageValue, - }; - - await updateSpaceMutation.mutateAsync(spaceData); - setIsHomePage(!isHomePage); -}; +const handleUpdateHomePage = async () => { + if (!spaceId || !pageId) { + return; + } + + const newHomePageValue = isHomePage ? null : pageId; + await updateSpaceMutation.mutateAsync({ + spaceId, + homePageId: newHomePageValue, + }); + setIsHomePage((current) => !current); +}; … -useEffect(() => { - setIsHomePage(isHome); -}, [isHome]); +useEffect(() => { + setIsHomePage(Boolean(isHome)); +}, [isHome]);This keeps state strictly boolean and prevents accidental mutation calls with undefined identifiers.
♻️ Duplicate comments (1)
apps/server/src/database/migrations/20251005T160503-20251005-add-home-page.ts (1)
1-7: Remove duplicate empty migration.Same issue as
20251005T160001-20251005-add-home-page.ts: this is another empty placeholder migration with no logic. Having three migration files for the same feature (two empty, one functional) creates unnecessary clutter.Remove this file and consolidate to a single functional migration.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/client/src/features/page/components/header/page-header-menu.tsx(5 hunks)apps/client/src/features/page/components/header/page-header.tsx(1 hunks)apps/client/src/features/space/components/space-grid.tsx(1 hunks)apps/client/src/features/space/types/space.types.ts(1 hunks)apps/client/src/pages/page/page.tsx(2 hunks)apps/server/src/core/space/dto/update-space.dto.ts(1 hunks)apps/server/src/core/space/services/space.service.ts(1 hunks)apps/server/src/database/migrations/20251005T151245-add-home-page.ts.ts(1 hunks)apps/server/src/database/migrations/20251005T160001-20251005-add-home-page.ts(1 hunks)apps/server/src/database/migrations/20251005T160503-20251005-add-home-page.ts(1 hunks)apps/server/src/database/types/db.d.ts(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
apps/server/src/database/migrations/20251005T151245-add-home-page.ts.ts (2)
apps/server/src/database/migrations/20251005T160001-20251005-add-home-page.ts (2)
up(3-4)down(6-7)apps/server/src/database/migrations/20251005T160503-20251005-add-home-page.ts (2)
up(3-4)down(6-7)
apps/client/src/pages/page/page.tsx (3)
apps/client/src/features/page/queries/page-query.ts (1)
usePageQuery(43-64)apps/client/src/lib/utils.tsx (1)
extractPageSlugId(15-24)apps/client/src/features/space/queries/space-query.ts (1)
useGetSpaceBySlugQuery(98-107)
apps/client/src/features/page/components/header/page-header.tsx (1)
apps/client/src/features/page/components/header/page-header-menu.tsx (1)
PageHeaderMenu(58-165)
apps/server/src/core/space/dto/update-space.dto.ts (1)
apps/server/src/core/space/dto/create-space.dto.ts (1)
CreateSpaceDto(10-25)
apps/server/src/database/migrations/20251005T160503-20251005-add-home-page.ts (2)
apps/server/src/database/migrations/20251005T151245-add-home-page.ts.ts (2)
up(3-8)down(10-15)apps/server/src/database/migrations/20251005T160001-20251005-add-home-page.ts (2)
up(3-4)down(6-7)
apps/client/src/features/space/components/space-grid.tsx (1)
apps/client/src/lib/config.ts (1)
getSpaceUrl(55-57)
apps/client/src/features/page/components/header/page-header-menu.tsx (3)
apps/client/src/features/editor/atoms/editor-atoms.ts (1)
yjsConnectionStatusAtom(10-10)apps/client/src/features/space/queries/space-query.ts (1)
useUpdateSpaceMutation(109-137)apps/client/src/features/space/types/space.types.ts (1)
ISpace(8-22)
apps/server/src/database/migrations/20251005T160001-20251005-add-home-page.ts (2)
apps/server/src/database/migrations/20251005T151245-add-home-page.ts.ts (2)
up(3-8)down(10-15)apps/server/src/database/migrations/20251005T160503-20251005-add-home-page.ts (2)
up(3-4)down(6-7)
🔇 Additional comments (2)
apps/client/src/features/space/types/space.types.ts (1)
21-21: LGTM!The addition of the optional
homePageIdfield to theISpaceinterface is correct and aligns with the server-side schema changes.apps/client/src/pages/page/page.tsx (1)
65-67: LGTM!The props passed to
MemoizedPageHeadercorrectly provide the home page context and necessary identifiers for the header component to function properly.
apps/server/src/database/migrations/20251005T160001-20251005-add-home-page.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/client/src/features/space/components/space-grid.tsx(1 hunks)apps/client/src/pages/page/page.tsx(2 hunks)apps/server/src/core/space/services/space.service.ts(2 hunks)apps/server/src/database/migrations/20251005T151245-add-home-page.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/client/src/pages/page/page.tsx
- apps/client/src/features/space/components/space-grid.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/server/src/core/space/services/space.service.ts (2)
apps/server/src/database/repos/page/page.repo.ts (1)
Injectable(18-425)apps/server/src/database/repos/space/space.repo.ts (1)
Injectable(16-159)
🔇 Additional comments (3)
apps/server/src/core/space/services/space.service.ts (3)
20-20: LGTM!PageRepo import is correct and necessary for the home page validation logic.
26-26: LGTM!PageRepo injection is correct and follows NestJS dependency injection patterns.
124-124: No action required: UpdateSpaceDto.homePageId already supports explicit null
UpdateSpaceDto.homePageId is typed asstring | null, so clients can clear the home page by passingnull.
|
Disclaimer: I am not a maintainer I love that feature! But I don't think the button is placed that well. The bar already is overloaded and setting a home page probably isn't going to be an often used action. So imho I think it would better fit in the dot menu instead. |
|
@Vito0912 Yeah thats totally doable to, i thought where it is now is good but we will see what the maintainer says. |
8ca23ad to
0914fae
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
apps/client/src/features/page/components/header/page-header-menu.tsx (3)
48-48: Remove unused import.The
updateSpacefunction is imported but never used in this component. The mutation hookuseUpdateSpaceMutationis used instead on line 69.Apply this diff to remove the unused import:
-import { updateSpace } from "@/features/space/services/space-service";
98-99: Wrap mutation in try-catch to prevent unhandled errors.While the mutation hook handles error notifications, the function should catch potential errors to prevent unhandled promise rejections and ensure graceful degradation.
Apply this diff:
+ try { await updateSpaceMutation.mutateAsync(spaceData); setIsHomePage(!isHomePage); + } catch (error) { + // Error notification already shown by mutation hook + console.error('Failed to update home page:', error); + }
130-134: Use theme colors instead of hard-coded color strings.The
color: "lightblue"is not theme-aware and may have poor contrast in dark mode or other themes. Use Mantine's theme colors for better consistency.Apply this diff:
<IconHome size={20} stroke={2} - {...(isHomePage ? { color: "lightblue" } : {})} + {...(isHomePage ? { color: "var(--mantine-color-blue-5)" } : {})} />Or alternatively, use Mantine's
cprop for better theming:- <ActionIcon + <ActionIcon variant="default" style={{ border: "none" }} onClick={handleUpdateHomePage} + {...(isHomePage ? { color: "blue" } : {})} > <IconHome size={20} stroke={2} - {...(isHomePage ? { color: "lightblue" } : {})} /> </ActionIcon>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/client/src/features/page/components/header/page-header-menu.tsx(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/client/src/features/page/components/header/page-header-menu.tsx (3)
apps/client/src/features/editor/atoms/editor-atoms.ts (1)
yjsConnectionStatusAtom(10-10)apps/client/src/features/space/queries/space-query.ts (1)
useUpdateSpaceMutation(109-137)apps/client/src/features/space/types/space.types.ts (1)
ISpace(8-22)
🔇 Additional comments (1)
apps/client/src/features/page/components/header/page-header-menu.tsx (1)
120-136: Consider the UX feedback about button placement.A community member (Vito0912) raised a valid concern that the page header is crowded and setting a home page is an infrequent action. They suggested moving this control to the overflow menu (the three-dot menu) instead of the visible header bar.
This is primarily a UX/design decision, but worth considering whether the home page toggle would be better placed in the
PageActionMenucomponent alongside other page actions.Current placement pros:
- More discoverable for users
- Quicker access
Overflow menu pros:
- Reduces header clutter
- Groups related page settings together
- Aligns with infrequent usage pattern
Please confirm the intended placement with the maintainers before merging.
| const { t } = useTranslation(); | ||
| const toggleAside = useToggleAside(); | ||
| const [yjsConnectionStatus] = useAtom(yjsConnectionStatusAtom); | ||
| const [isHomePage, setIsHomePage] = useState<boolean>(isHome); |
There was a problem hiding this comment.
State may become stale if prop changes.
The isHomePage state is initialized from the isHome prop but won't update if the prop changes later. If the home page is updated elsewhere (e.g., via another component or tab), this component will show stale state.
Consider one of these solutions:
Solution 1 (preferred): Derive the value directly from the prop
- const [isHomePage, setIsHomePage] = useState<boolean>(isHome);Then use isHome directly in the component and update the mutation's success callback to rely on query cache updates.
Solution 2: Sync state with prop using useEffect
+ useEffect(() => {
+ setIsHomePage(isHome);
+ }, [isHome]);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/client/src/features/page/components/header/page-header-menu.tsx around
line 67, the component initializes local state const [isHomePage, setIsHomePage]
= useState<boolean>(isHome) which will become stale if the isHome prop changes;
fix by removing the derived local state and use the isHome prop directly
throughout the component (and update the mutation success handler to rely on
query-cache updates or explicitly update cache/ui there), or if you must keep
local state, add a useEffect(() => setIsHomePage(isHome), [isHome]) so the state
stays in sync with the prop and adjust any handlers to update both local state
and cache consistently.
| const handleUpdateHomePage = async () => { | ||
| const newHomePageValue = isHomePage ? null : pageId; | ||
| const spaceData: Partial<ISpace> = { | ||
| spaceId: spaceId, | ||
| homePageId: newHomePageValue, | ||
| }; | ||
|
|
||
| await updateSpaceMutation.mutateAsync(spaceData); | ||
| setIsHomePage(!isHomePage); | ||
| }; |
There was a problem hiding this comment.
Add validation for required props.
The function uses spaceId and pageId without checking if they're defined. If these props are missing, the mutation will fail with unclear errors.
Apply this diff to add prop validation:
const handleUpdateHomePage = async () => {
+ if (!spaceId || !pageId) {
+ notifications.show({
+ message: t("Unable to update home page"),
+ color: "red"
+ });
+ return;
+ }
+
const newHomePageValue = isHomePage ? null : pageId;
const spaceData: Partial<ISpace> = {
spaceId: spaceId,
homePageId: newHomePageValue,
};
await updateSpaceMutation.mutateAsync(spaceData);
setIsHomePage(!isHomePage);
};🤖 Prompt for AI Agents
In apps/client/src/features/page/components/header/page-header-menu.tsx around
lines 91 to 100, validate that required props spaceId and pageId are present
before calling updateSpaceMutation: check if spaceId is defined (and pageId when
setting home) and if not, abort the handler early (return) and surface a clear
error (e.g., show a user-facing message or log a descriptive error) rather than
calling mutateAsync with undefined values; only build spaceData and call
updateSpaceMutation.mutateAsync when validation passes and keep toggling
isHomePage after a successful mutation.
Screen.Recording.2025-10-07.at.1.11.14.PM.mov
This PR gives the user and ability to mark any specific page in a space as the home page as requested in this ticket #1646
-A user can mark a page in the space as the homepage with a whole icon that I added in the 'page-header' component.
-Every time a user has a home page for a space it directs them to that page instead of the overview page.
-If a home page is not set the user gets directed to the overview page instead.
https://github.com/user-attachments/assets/943e59f8-82f3-4773-983c-17d323f5673a.
Summary by CodeRabbit