feat: restore deleted document

This commit is contained in:
Ephraim Atta-Duncan
2024-06-13 15:57:16 +00:00
committed by Mythie
parent 1a55f4253b
commit feef4b1a12
11 changed files with 462 additions and 8 deletions

View File

@ -26,6 +26,9 @@ export const DocumentPageViewInformation = ({
const documentInformation = useMemo(() => {
let createdValue = DateTime.fromJSDate(document.createdAt).toFormat('MMMM d, yyyy');
let deletedValue =
document.deletedAt && DateTime.fromJSDate(document.deletedAt).toFormat('MMMM d, yyyy');
let lastModifiedValue = DateTime.fromJSDate(document.updatedAt).toRelative();
if (!isMounted) {
@ -34,9 +37,13 @@ export const DocumentPageViewInformation = ({
.toFormat('MMMM d, yyyy');
lastModifiedValue = DateTime.fromJSDate(document.updatedAt).setLocale(locale).toRelative();
deletedValue =
document.deletedAt &&
DateTime.fromJSDate(document.deletedAt).setLocale(locale).toFormat('MMMM d, yyyy');
}
return [
const info = [
{
description: 'Uploaded by',
value: userId === document.userId ? 'You' : document.User.name ?? document.User.email,
@ -50,6 +57,15 @@ export const DocumentPageViewInformation = ({
value: lastModifiedValue,
},
];
if (deletedValue) {
info.push({
description: 'Deleted',
value: deletedValue,
});
}
return info;
}, [isMounted, document, locale, userId]);
return (

View File

@ -5,6 +5,7 @@ import { useState } from 'react';
import Link from 'next/link';
import {
ArchiveRestore,
CheckCircle,
Copy,
Download,
@ -39,6 +40,7 @@ import { ResendDocumentActionItem } from './_action-items/resend-document';
import { DeleteDocumentDialog } from './delete-document-dialog';
import { DuplicateDocumentDialog } from './duplicate-document-dialog';
import { MoveDocumentDialog } from './move-document-dialog';
import { RestoreDocumentDialog } from './restore-document-dialog';
export type DataTableActionDropdownProps = {
row: Document & {
@ -56,6 +58,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
const [isMoveDialogOpen, setMoveDialogOpen] = useState(false);
const [isRestoreDialogOpen, setRestoreDialogOpen] = useState(false);
if (!session) {
return null;
@ -71,6 +74,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
const isCurrentTeamDocument = team && row.team?.url === team.url;
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);
const isDeletedDocument = row.deletedAt !== null;
const documentsPath = formatDocumentsPath(team?.url);
@ -174,13 +178,23 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
Void
</DropdownMenuItem> */}
<DropdownMenuItem
onClick={() => setDeleteDialogOpen(true)}
disabled={Boolean(!canManageDocument && team?.teamEmail)}
>
<Trash2 className="mr-2 h-4 w-4" />
{canManageDocument ? 'Delete' : 'Hide'}
</DropdownMenuItem>
{isDeletedDocument ? (
<DropdownMenuItem
onClick={() => setRestoreDialogOpen(true)}
disabled={Boolean(!canManageDocument)}
>
<ArchiveRestore className="mr-2 h-4 w-4" />
Restore
</DropdownMenuItem>
) : (
<DropdownMenuItem
onClick={() => setDeleteDialogOpen(true)}
disabled={Boolean(!canManageDocument && team?.teamEmail)}
>
<Trash2 className="mr-2 h-4 w-4" />
{canManageDocument ? 'Delete' : 'Hide'}
</DropdownMenuItem>
)}
<DropdownMenuLabel>Share</DropdownMenuLabel>
@ -216,6 +230,16 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
onOpenChange={setMoveDialogOpen}
/>
<RestoreDocumentDialog
id={row.id}
status={row.status}
documentTitle={row.title}
open={isRestoreDialogOpen}
onOpenChange={setRestoreDialogOpen}
teamId={team?.id}
canManageDocument={canManageDocument}
/>
{isDuplicateDialogOpen && (
<DuplicateDocumentDialog
id={row.id}

View File

@ -0,0 +1,90 @@
import { useRouter } from 'next/navigation';
import type { DocumentStatus } from '@documenso/prisma/client';
import { trpc as trpcReact } from '@documenso/trpc/react';
import {
AlertDialog,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@documenso/ui/primitives/alert-dialog';
import { Button } from '@documenso/ui/primitives/button';
import { useToast } from '@documenso/ui/primitives/use-toast';
type RestoreDocumentDialogProps = {
id: number;
open: boolean;
onOpenChange: (_open: boolean) => void;
status: DocumentStatus;
documentTitle: string;
teamId?: number;
canManageDocument: boolean;
};
export function RestoreDocumentDialog({
id,
teamId,
open,
onOpenChange,
documentTitle,
canManageDocument,
}: RestoreDocumentDialogProps) {
const router = useRouter();
const { toast } = useToast();
const { mutateAsync: restoreDocument, isLoading } =
trpcReact.document.restoreDocument.useMutation({
onSuccess: () => {
router.refresh();
toast({
title: 'Document restored',
description: `"${documentTitle}" has been successfully restored`,
duration: 5000,
});
onOpenChange(false);
},
});
const onRestore = async () => {
try {
await restoreDocument({ id, teamId });
} catch {
toast({
title: 'Something went wrong',
description: 'This document could not be restored at this time. Please try again.',
variant: 'destructive',
duration: 7500,
});
}
};
return (
<AlertDialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
You are about to restore the document <strong>"{documentTitle}"</strong>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<Button
type="button"
loading={isLoading}
onClick={onRestore}
disabled={!canManageDocument}
>
Restore
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}

View File

@ -154,6 +154,7 @@ export const DocumentHistorySheet = ({
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED },
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED },
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED },
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RESTORED },
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED },
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED },
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT },