mirror of
https://github.com/docmost/docmost.git
synced 2025-11-13 09:32:40 +10:00
added recycle bin modal, updated api routes
This commit is contained in:
@ -14,6 +14,9 @@ import {
|
|||||||
movePage,
|
movePage,
|
||||||
getPageBreadcrumbs,
|
getPageBreadcrumbs,
|
||||||
getRecentChanges,
|
getRecentChanges,
|
||||||
|
getDeletedPages,
|
||||||
|
restorePage,
|
||||||
|
removePage,
|
||||||
} from "@/features/page/services/page-service";
|
} from "@/features/page/services/page-service";
|
||||||
import {
|
import {
|
||||||
IMovePage,
|
IMovePage,
|
||||||
@ -73,6 +76,18 @@ export function useUpdatePageMutation() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useRemovePageMutation() {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (pageId: string) => removePage(pageId),
|
||||||
|
onSuccess: () => {
|
||||||
|
notifications.show({ message: "Page deleted successfully" });
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
notifications.show({ message: "Failed to delete page", color: "red" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function useDeletePageMutation() {
|
export function useDeletePageMutation() {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (pageId: string) => deletePage(pageId),
|
mutationFn: (pageId: string) => deletePage(pageId),
|
||||||
@ -91,6 +106,18 @@ export function useMovePageMutation() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useRestorePageMutation() {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (pageId: string) => restorePage(pageId),
|
||||||
|
onSuccess: () => {
|
||||||
|
notifications.show({ message: "Page restored successfully" });
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
notifications.show({ message: "Failed to restore page", color: "red" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function useGetSidebarPagesQuery(
|
export function useGetSidebarPagesQuery(
|
||||||
data: SidebarPagesParams,
|
data: SidebarPagesParams,
|
||||||
): UseQueryResult<IPagination<IPage>, Error> {
|
): UseQueryResult<IPagination<IPage>, Error> {
|
||||||
@ -143,3 +170,12 @@ export function useRecentChangesQuery(
|
|||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useDeletedPagesQuery(
|
||||||
|
spaceId: string,
|
||||||
|
): UseQueryResult<IPagination<IPage>, Error> {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["deleted-pages", spaceId],
|
||||||
|
queryFn: () => getDeletedPages(spaceId),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -26,10 +26,19 @@ export async function updatePage(data: Partial<IPageInput>): Promise<IPage> {
|
|||||||
return req.data;
|
return req.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removePage(pageId: string): Promise<void> {
|
||||||
|
await api.post("/pages/remove", { pageId });
|
||||||
|
}
|
||||||
|
|
||||||
export async function deletePage(pageId: string): Promise<void> {
|
export async function deletePage(pageId: string): Promise<void> {
|
||||||
await api.post("/pages/delete", { pageId });
|
await api.post("/pages/delete", { pageId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getDeletedPages(spaceId: string): Promise<IPagination<IPage>> {
|
||||||
|
const req = await api.post("/pages/deleted", { spaceId });
|
||||||
|
return req.data;
|
||||||
|
}
|
||||||
|
|
||||||
export async function restorePage(pageId: string): Promise<void> {
|
export async function restorePage(pageId: string): Promise<void> {
|
||||||
await api.post("/pages/restore", { pageId });
|
await api.post("/pages/restore", { pageId });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { IMovePage, IPage } from "@/features/page/types/page.types.ts";
|
|||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
useCreatePageMutation,
|
useCreatePageMutation,
|
||||||
useDeletePageMutation,
|
useRemovePageMutation,
|
||||||
useMovePageMutation,
|
useMovePageMutation,
|
||||||
useUpdatePageMutation,
|
useUpdatePageMutation,
|
||||||
} from "@/features/page/queries/page-query.ts";
|
} from "@/features/page/queries/page-query.ts";
|
||||||
@ -27,7 +27,7 @@ export function useTreeMutation<T>(spaceId: string) {
|
|||||||
const tree = useMemo(() => new SimpleTree<SpaceTreeNode>(data), [data]);
|
const tree = useMemo(() => new SimpleTree<SpaceTreeNode>(data), [data]);
|
||||||
const createPageMutation = useCreatePageMutation();
|
const createPageMutation = useCreatePageMutation();
|
||||||
const updatePageMutation = useUpdatePageMutation();
|
const updatePageMutation = useUpdatePageMutation();
|
||||||
const deletePageMutation = useDeletePageMutation();
|
const deletePageMutation = useRemovePageMutation();
|
||||||
const movePageMutation = useMovePageMutation();
|
const movePageMutation = useMovePageMutation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { spaceSlug } = useParams();
|
const { spaceSlug } = useParams();
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { Modal, ScrollArea, rem } from "@mantine/core";
|
||||||
|
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"
|
||||||
|
|
||||||
|
interface RecycleBinModalProps {
|
||||||
|
spaceId: string;
|
||||||
|
opened: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RecycleBinModal({
|
||||||
|
spaceId,
|
||||||
|
opened,
|
||||||
|
onClose,
|
||||||
|
}: RecycleBinModalProps) {
|
||||||
|
const { data: space, isLoading } = useSpaceQuery(spaceId);
|
||||||
|
|
||||||
|
const spaceRules = space?.membership?.permissions;
|
||||||
|
const spaceAbility = useMemo(() => useSpaceAbility(spaceRules), [spaceRules]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal.Root
|
||||||
|
opened={opened}
|
||||||
|
onClose={onClose}
|
||||||
|
size={600}
|
||||||
|
padding="xl"
|
||||||
|
yOffset="10vh"
|
||||||
|
xOffset={0}
|
||||||
|
mah={400}
|
||||||
|
>
|
||||||
|
<Modal.Overlay />
|
||||||
|
<Modal.Content style={{ overflow: "hidden" }}>
|
||||||
|
<Modal.Header py={0}>
|
||||||
|
<Modal.Title fw={500}>{space?.name}</Modal.Title>
|
||||||
|
<Modal.CloseButton />
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<div style={{ height: rem("600px") }}>
|
||||||
|
<ScrollArea h="600" w="100%" scrollbarSize={5}>
|
||||||
|
<RecycledPagesList spaceId={space.id} />
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal.Content>
|
||||||
|
</Modal.Root>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
106
apps/client/src/features/space/components/recycled-pages.tsx
Normal file
106
apps/client/src/features/space/components/recycled-pages.tsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { useDeletedPagesQuery, useRestorePageMutation, useDeletePageMutation } from "@/features/page/queries/page-query.ts";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { ActionIcon, Menu, Table, Text } from "@mantine/core";
|
||||||
|
import { IconDots } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
interface RecycledPagesProps {
|
||||||
|
spaceId: string;
|
||||||
|
readOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RecycledPagesList({
|
||||||
|
spaceId,
|
||||||
|
}: RecycledPagesProps) {
|
||||||
|
const { data, isLoading } = useDeletedPagesQuery(spaceId);
|
||||||
|
const restorePageMutation = useRestorePageMutation();
|
||||||
|
const removePageMutation = useDeletePageMutation();
|
||||||
|
|
||||||
|
const handleRestorePage = async (pageId: string) => {
|
||||||
|
await restorePageMutation.mutateAsync(pageId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemovePage = async (pageId: string) => {
|
||||||
|
await removePageMutation.mutateAsync(pageId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openRemovePageModal = (pageId: string) =>
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Delete page permanently",
|
||||||
|
children: (
|
||||||
|
<Text size="sm">
|
||||||
|
Are you sure you want to permanently delete this page ?
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
centered: true,
|
||||||
|
labels: { confirm: "Delete", cancel: "Cancel" },
|
||||||
|
confirmProps: { color: "red" },
|
||||||
|
onConfirm: () => handleRemovePage(pageId),
|
||||||
|
});
|
||||||
|
|
||||||
|
const openRestorePageModal = (pageId: string) =>
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Restore page",
|
||||||
|
children: (
|
||||||
|
<Text size="sm">
|
||||||
|
"Restore this page ?"
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
centered: true,
|
||||||
|
labels: { confirm: "Restore", cancel: "Cancel" },
|
||||||
|
confirmProps: { color: "blue" },
|
||||||
|
onConfirm: () => handleRestorePage(pageId)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{data && (
|
||||||
|
<Table>
|
||||||
|
<Table.Thead>
|
||||||
|
<Table.Th>Deleted Pages</Table.Th>
|
||||||
|
</Table.Thead>
|
||||||
|
|
||||||
|
<Table.Tbody>
|
||||||
|
{data?.items.map((page) => (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td>
|
||||||
|
<div>
|
||||||
|
<Text fz="sm" fw={500}>
|
||||||
|
{page?.title}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Table.Td>
|
||||||
|
|
||||||
|
<Table.Td>
|
||||||
|
{(
|
||||||
|
<Menu>
|
||||||
|
<Menu.Target>
|
||||||
|
<ActionIcon variant="subtle" c="gray">
|
||||||
|
<IconDots size={20} stroke={2} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Menu.Target>
|
||||||
|
|
||||||
|
<Menu.Dropdown>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={() =>
|
||||||
|
openRestorePageModal(page.id)
|
||||||
|
}>
|
||||||
|
Restore Page
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item
|
||||||
|
onClick={() =>
|
||||||
|
openRemovePageModal
|
||||||
|
}>
|
||||||
|
Delete Page permanently
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.Dropdown>
|
||||||
|
</Menu>
|
||||||
|
)}
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
))}
|
||||||
|
</Table.Tbody>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -14,6 +14,7 @@ import {
|
|||||||
IconPlus,
|
IconPlus,
|
||||||
IconSearch,
|
IconSearch,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
|
IconTrash,
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
|
||||||
import classes from './space-sidebar.module.css';
|
import classes from './space-sidebar.module.css';
|
||||||
@ -25,6 +26,7 @@ import { Link, useLocation, useParams } from 'react-router-dom';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
import SpaceSettingsModal from '@/features/space/components/settings-modal.tsx';
|
import SpaceSettingsModal from '@/features/space/components/settings-modal.tsx';
|
||||||
|
import RecycleBinModal from "@/features/space/components/recycle-bin-modal.tsx";
|
||||||
import { useGetSpaceBySlugQuery } from '@/features/space/queries/space-query.ts';
|
import { useGetSpaceBySlugQuery } from '@/features/space/queries/space-query.ts';
|
||||||
import { getSpaceUrl } from '@/lib/config.ts';
|
import { getSpaceUrl } from '@/lib/config.ts';
|
||||||
import SpaceTree from '@/features/page/tree/components/space-tree.tsx';
|
import SpaceTree from '@/features/page/tree/components/space-tree.tsx';
|
||||||
@ -41,6 +43,8 @@ export function SpaceSidebar() {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [opened, { open: openSettings, close: closeSettings }] =
|
const [opened, { open: openSettings, close: closeSettings }] =
|
||||||
useDisclosure(false);
|
useDisclosure(false);
|
||||||
|
const [openedRecycleBin, { open: openRecycleBin, close: closeRecycleBin }] =
|
||||||
|
useDisclosure(false);
|
||||||
const { spaceSlug } = useParams();
|
const { spaceSlug } = useParams();
|
||||||
const { data: space, isLoading, isError } = useGetSpaceBySlugQuery(spaceSlug);
|
const { data: space, isLoading, isError } = useGetSpaceBySlugQuery(spaceSlug);
|
||||||
|
|
||||||
@ -113,6 +117,17 @@ export function SpaceSidebar() {
|
|||||||
</div>
|
</div>
|
||||||
</UnstyledButton>
|
</UnstyledButton>
|
||||||
|
|
||||||
|
<UnstyledButton className={classes.menu} onClick={openRecycleBin}>
|
||||||
|
<div className={classes.menuItemInner}>
|
||||||
|
<IconTrash
|
||||||
|
size={18}
|
||||||
|
className={classes.menuItemIcon}
|
||||||
|
stroke={2}
|
||||||
|
/>
|
||||||
|
<span>Recycle Bin</span>
|
||||||
|
</div>
|
||||||
|
</UnstyledButton>
|
||||||
|
|
||||||
{spaceAbility.can(
|
{spaceAbility.can(
|
||||||
SpaceCaslAction.Manage,
|
SpaceCaslAction.Manage,
|
||||||
SpaceCaslSubject.Page
|
SpaceCaslSubject.Page
|
||||||
@ -179,6 +194,12 @@ export function SpaceSidebar() {
|
|||||||
spaceId={space?.slug}
|
spaceId={space?.slug}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<RecycleBinModal
|
||||||
|
opened={openedRecycleBin}
|
||||||
|
onClose={closeRecycleBin}
|
||||||
|
spaceId={space?.slug}
|
||||||
|
/>
|
||||||
|
|
||||||
<SearchSpotlight spaceId={space.id} />
|
<SearchSpotlight spaceId={space.id} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
7
apps/server/src/core/page/dto/deleted-page.dto.ts
Normal file
7
apps/server/src/core/page/dto/deleted-page.dto.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class DeletedPageDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
spaceId: string;
|
||||||
|
}
|
||||||
@ -27,6 +27,7 @@ import {
|
|||||||
import SpaceAbilityFactory from '../casl/abilities/space-ability.factory';
|
import SpaceAbilityFactory from '../casl/abilities/space-ability.factory';
|
||||||
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
||||||
import { RecentPageDto } from './dto/recent-page.dto';
|
import { RecentPageDto } from './dto/recent-page.dto';
|
||||||
|
import { DeletedPageDto } from './dto/deleted-page.dto';
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Controller('pages')
|
@Controller('pages')
|
||||||
@ -112,6 +113,23 @@ export class PageController {
|
|||||||
await this.pageService.forceDelete(pageIdDto.pageId);
|
await this.pageService.forceDelete(pageIdDto.pageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post('remove')
|
||||||
|
async remove(@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.remove(pageIdDto.pageId);
|
||||||
|
}
|
||||||
|
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post('restore')
|
@Post('restore')
|
||||||
async restore(@Body() pageIdDto: PageIdDto) {
|
async restore(@Body() pageIdDto: PageIdDto) {
|
||||||
@ -144,6 +162,30 @@ export class PageController {
|
|||||||
return this.pageService.getRecentPages(user.id, pagination);
|
return this.pageService.getRecentPages(user.id, pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post('deleted')
|
||||||
|
async getDeletedPages(
|
||||||
|
@Body() deletedPageDto: DeletedPageDto,
|
||||||
|
@Body() pagination: PaginationOptions,
|
||||||
|
@AuthUser() user: User,
|
||||||
|
) {
|
||||||
|
if (deletedPageDto.spaceId) {
|
||||||
|
const ability = await this.spaceAbility.createForUser(
|
||||||
|
user,
|
||||||
|
deletedPageDto.spaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.pageService.getDeletedSpacePages(
|
||||||
|
deletedPageDto.spaceId,
|
||||||
|
pagination,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: scope to workspaces
|
// TODO: scope to workspaces
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Post('/history')
|
@Post('/history')
|
||||||
|
|||||||
@ -228,6 +228,7 @@ export class PageService {
|
|||||||
])
|
])
|
||||||
.select((eb) => this.withHasChildren(eb))
|
.select((eb) => this.withHasChildren(eb))
|
||||||
.where('id', '=', childPageId)
|
.where('id', '=', childPageId)
|
||||||
|
.where('deletedAt', 'is not', null)
|
||||||
.unionAll((exp) =>
|
.unionAll((exp) =>
|
||||||
exp
|
exp
|
||||||
.selectFrom('pages as p')
|
.selectFrom('pages as p')
|
||||||
@ -281,10 +282,21 @@ export class PageService {
|
|||||||
return await this.pageRepo.getRecentPages(userId, pagination);
|
return await this.pageRepo.getRecentPages(userId, pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDeletedSpacePages(
|
||||||
|
spaceId: string,
|
||||||
|
pagination: PaginationOptions,
|
||||||
|
): Promise<PaginationResult<Page>> {
|
||||||
|
return await this.pageRepo.getDeletedPagesInSpace(spaceId, pagination);
|
||||||
|
}
|
||||||
|
|
||||||
async forceDelete(pageId: string): Promise<void> {
|
async forceDelete(pageId: string): Promise<void> {
|
||||||
await this.pageRepo.deletePage(pageId);
|
await this.pageRepo.deletePage(pageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async remove(pageId: string): Promise<void> {
|
||||||
|
await this.pageRepo.removePage(pageId);
|
||||||
|
}
|
||||||
|
|
||||||
async restore(pageId: string): Promise<void> {
|
async restore(pageId: string): Promise<void> {
|
||||||
await this.pageRepo.restorePage(pageId);
|
await this.pageRepo.restorePage(pageId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export class PageRepo {
|
|||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
async deletePage(pageId: string): Promise<void> {
|
async removePage(pageId: string): Promise<void> {
|
||||||
let query = this.db.updateTable('pages').set({ deletedAt: new Date() });
|
let query = this.db.updateTable('pages').set({ deletedAt: new Date() });
|
||||||
|
|
||||||
if (isValidUUID(pageId)) {
|
if (isValidUUID(pageId)) {
|
||||||
@ -118,6 +118,18 @@ export class PageRepo {
|
|||||||
await query.execute();
|
await query.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deletePage(pageId: string): Promise<void> {
|
||||||
|
let query = this.db.deleteFrom('pages');
|
||||||
|
|
||||||
|
if (isValidUUID(pageId)) {
|
||||||
|
query = query.where('id', '=', pageId);
|
||||||
|
} else {
|
||||||
|
query = query.where('slugId', '=', pageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await query.execute();
|
||||||
|
}
|
||||||
|
|
||||||
async restorePage(pageId: string): Promise<void> {
|
async restorePage(pageId: string): Promise<void> {
|
||||||
let query = this.db.updateTable('pages').set({ deletedAt: null });
|
let query = this.db.updateTable('pages').set({ deletedAt: null });
|
||||||
|
|
||||||
@ -164,6 +176,23 @@ export class PageRepo {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDeletedPagesInSpace(spaceId: string, pagination: PaginationOptions) {
|
||||||
|
const query = this.db
|
||||||
|
.selectFrom('pages')
|
||||||
|
.select(this.baseFields)
|
||||||
|
.select((eb) => this.withSpace(eb))
|
||||||
|
.where('spaceId', '=', spaceId)
|
||||||
|
.where('deletedAt', 'is not', null)
|
||||||
|
.orderBy('updatedAt', 'desc');
|
||||||
|
|
||||||
|
const result = executeWithPagination(query, {
|
||||||
|
page: pagination.page,
|
||||||
|
perPage: pagination.limit,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
withSpace(eb: ExpressionBuilder<DB, 'pages'>) {
|
withSpace(eb: ExpressionBuilder<DB, 'pages'>) {
|
||||||
return jsonObjectFrom(
|
return jsonObjectFrom(
|
||||||
eb
|
eb
|
||||||
|
|||||||
Reference in New Issue
Block a user