diff --git a/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts b/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts index bad3c5b8..53ec2d8a 100644 --- a/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts +++ b/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts @@ -13,6 +13,7 @@ import { IMovePage, IPage } from "@/features/page/types/page.types.ts"; import { useNavigate, useParams } from "react-router-dom"; import { useCreatePageMutation, + // useDeletePageMutation, useRemovePageMutation, useMovePageMutation, useUpdatePageMutation, @@ -27,7 +28,8 @@ export function useTreeMutation(spaceId: string) { const tree = useMemo(() => new SimpleTree(data), [data]); const createPageMutation = useCreatePageMutation(); const updatePageMutation = useUpdatePageMutation(); - const deletePageMutation = useRemovePageMutation(); + // const deletePageMutation = useDeletePageMutation(); + const removePageMutation = useRemovePageMutation(); const movePageMutation = useMovePageMutation(); const navigate = useNavigate(); const { spaceSlug } = useParams(); @@ -180,7 +182,7 @@ export function useTreeMutation(spaceId: string) { const onDelete: DeleteHandler = async (args: { ids: string[] }) => { try { - await deletePageMutation.mutateAsync(args.ids[0]); + await removePageMutation.mutateAsync(args.ids[0]); if (tree.find(args.ids[0])) { tree.drop({ id: args.ids[0] }); diff --git a/apps/client/src/features/space/components/recycle-bin-modal.tsx b/apps/client/src/features/space/components/recycle-bin-modal.tsx index 7c912005..1d1262c5 100644 --- a/apps/client/src/features/space/components/recycle-bin-modal.tsx +++ b/apps/client/src/features/space/components/recycle-bin-modal.tsx @@ -3,6 +3,10 @@ import React, { useMemo } from "react"; import { useSpaceQuery } from "@/features/space/queries/space-query.ts"; import { useSpaceAbility } from "@/features/space/permissions/use-space-ability.ts"; import RecycledPagesList from "@/features/space/components/recycled-pages.tsx" +import { + SpaceCaslAction, + SpaceCaslSubject, +} from "@/features/space/permissions/permissions.type.ts"; interface RecycleBinModalProps { spaceId: string; @@ -40,7 +44,13 @@ export default function RecycleBinModal({
- +
diff --git a/apps/client/src/features/space/components/recycled-pages.tsx b/apps/client/src/features/space/components/recycled-pages.tsx index e0694768..22e288a5 100644 --- a/apps/client/src/features/space/components/recycled-pages.tsx +++ b/apps/client/src/features/space/components/recycled-pages.tsx @@ -10,6 +10,7 @@ interface RecycledPagesProps { export default function RecycledPagesList({ spaceId, + readOnly, }: RecycledPagesProps) { const { data, isLoading } = useDeletedPagesQuery(spaceId); const restorePageMutation = useRestorePageMutation(); @@ -65,13 +66,13 @@ export default function RecycledPagesList({
- {page?.title} + {page?.title || "Untitled"}
- {( + {!readOnly && ( @@ -88,7 +89,7 @@ export default function RecycledPagesList({ - openRemovePageModal + openRemovePageModal(page.id) }> Delete Page permanently diff --git a/apps/server/src/core/page/page.controller.ts b/apps/server/src/core/page/page.controller.ts index dc939b36..6ef19549 100644 --- a/apps/server/src/core/page/page.controller.ts +++ b/apps/server/src/core/page/page.controller.ts @@ -12,6 +12,7 @@ import { PageService } from './services/page.service'; import { CreatePageDto } from './dto/create-page.dto'; import { UpdatePageDto } from './dto/update-page.dto'; import { MovePageDto } from './dto/move-page.dto'; +// import { RestorePageDto } from './dto/restore-page.dto'; import { PageHistoryIdDto, PageIdDto, PageInfoDto } from './dto/page.dto'; import { PageHistoryService } from './services/page-history.service'; import { AuthUser } from '../../common/decorators/auth-user.decorator'; @@ -132,8 +133,19 @@ export class PageController { @HttpCode(HttpStatus.OK) @Post('restore') - async restore(@Body() pageIdDto: PageIdDto) { - await this.pageService.restore(pageIdDto.pageId); + async restore(@Body() pageIdDto: PageIdDto, @AuthUser() user: User) { + const page = await this.pageRepo.findById(pageIdDto.pageId); + + if (!page) { + throw new NotFoundException('Page not found'); + } + + const ability = await this.spaceAbility.createForUser(user, page.spaceId); + if (ability.cannot(SpaceCaslAction.Manage, SpaceCaslSubject.Page)) { + throw new ForbiddenException(); + } + + await this.pageService.restore(pageIdDto.pageId); } @HttpCode(HttpStatus.OK) diff --git a/apps/server/src/core/page/services/page.service.ts b/apps/server/src/core/page/services/page.service.ts index 44c1ba51..4bbc1474 100644 --- a/apps/server/src/core/page/services/page.service.ts +++ b/apps/server/src/core/page/services/page.service.ts @@ -162,9 +162,11 @@ export class PageService { 'parentPageId', 'spaceId', 'creatorId', + 'deletedAt', ]) .select((eb) => this.withHasChildren(eb)) .orderBy('position', 'asc') + .where('deletedAt', 'is', null) .where('spaceId', '=', spaceId); if (pageId) { diff --git a/apps/server/src/database/repos/page/page.repo.ts b/apps/server/src/database/repos/page/page.repo.ts index 5979a3aa..8978cd66 100644 --- a/apps/server/src/database/repos/page/page.repo.ts +++ b/apps/server/src/database/repos/page/page.repo.ts @@ -148,6 +148,7 @@ export class PageRepo { .select(this.baseFields) .select((eb) => this.withSpace(eb)) .where('spaceId', '=', spaceId) + .where('deletedAt', 'is not', null) .orderBy('updatedAt', 'desc'); const result = executeWithPagination(query, { @@ -166,6 +167,7 @@ export class PageRepo { .select(this.baseFields) .select((eb) => this.withSpace(eb)) .where('spaceId', 'in', userSpaceIds) + .where('deletedAt', 'is not', null) .orderBy('updatedAt', 'desc'); const result = executeWithPagination(query, {