mirror of
https://github.com/documenso/documenso.git
synced 2025-11-11 04:52:41 +10:00
feat: move document to team (#1210)
Introduces a new dialog component allowing users to move documents between teams with included audit logging.
This commit is contained in:
@ -12,6 +12,7 @@ import {
|
|||||||
EyeIcon,
|
EyeIcon,
|
||||||
Loader,
|
Loader,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
|
MoveRight,
|
||||||
Pencil,
|
Pencil,
|
||||||
Share,
|
Share,
|
||||||
Trash2,
|
Trash2,
|
||||||
@ -37,6 +38,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
|
|||||||
import { ResendDocumentActionItem } from './_action-items/resend-document';
|
import { ResendDocumentActionItem } from './_action-items/resend-document';
|
||||||
import { DeleteDocumentDialog } from './delete-document-dialog';
|
import { DeleteDocumentDialog } from './delete-document-dialog';
|
||||||
import { DuplicateDocumentDialog } from './duplicate-document-dialog';
|
import { DuplicateDocumentDialog } from './duplicate-document-dialog';
|
||||||
|
import { MoveDocumentDialog } from './move-document-dialog';
|
||||||
|
|
||||||
export type DataTableActionDropdownProps = {
|
export type DataTableActionDropdownProps = {
|
||||||
row: Document & {
|
row: Document & {
|
||||||
@ -53,6 +55,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
|
|
||||||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
||||||
|
const [isMoveDialogOpen, setMoveDialogOpen] = useState(false);
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return null;
|
return null;
|
||||||
@ -157,6 +160,14 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
Duplicate
|
Duplicate
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
{/* We don't want to allow teams moving documents across at the moment. */}
|
||||||
|
{!team && (
|
||||||
|
<DropdownMenuItem onClick={() => setMoveDialogOpen(true)}>
|
||||||
|
<MoveRight className="mr-2 h-4 w-4" />
|
||||||
|
Move to Team
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* No point displaying this if there's no functionality. */}
|
{/* No point displaying this if there's no functionality. */}
|
||||||
{/* <DropdownMenuItem disabled>
|
{/* <DropdownMenuItem disabled>
|
||||||
<XCircle className="mr-2 h-4 w-4" />
|
<XCircle className="mr-2 h-4 w-4" />
|
||||||
@ -199,6 +210,12 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
canManageDocument={canManageDocument}
|
canManageDocument={canManageDocument}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<MoveDocumentDialog
|
||||||
|
documentId={row.id}
|
||||||
|
open={isMoveDialogOpen}
|
||||||
|
onOpenChange={setMoveDialogOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
{isDuplicateDialogOpen && (
|
{isDuplicateDialogOpen && (
|
||||||
<DuplicateDocumentDialog
|
<DuplicateDocumentDialog
|
||||||
id={row.id}
|
id={row.id}
|
||||||
|
|||||||
117
apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx
Normal file
117
apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||||
|
import { trpc } from '@documenso/trpc/react';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@documenso/ui/primitives/dialog';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@documenso/ui/primitives/select';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
type MoveDocumentDialogProps = {
|
||||||
|
documentId: number;
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (_open: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MoveDocumentDialog = ({ documentId, open, onOpenChange }: MoveDocumentDialogProps) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [selectedTeamId, setSelectedTeamId] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const { data: teams, isLoading: isLoadingTeams } = trpc.team.getTeams.useQuery();
|
||||||
|
const { mutateAsync: moveDocument, isLoading } = trpc.document.moveDocumentToTeam.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
router.refresh();
|
||||||
|
toast({
|
||||||
|
title: 'Document moved',
|
||||||
|
description: 'The document has been successfully moved to the selected team.',
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
onOpenChange(false);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: error.message || 'An error occurred while moving the document.',
|
||||||
|
variant: 'destructive',
|
||||||
|
duration: 7500,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onMove = async () => {
|
||||||
|
if (!selectedTeamId) return;
|
||||||
|
await moveDocument({ documentId, teamId: selectedTeamId });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Move Document to Team</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Select a team to move this document to. This action cannot be undone.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<Select onValueChange={(value) => setSelectedTeamId(Number(value))}>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a team" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{isLoadingTeams ? (
|
||||||
|
<SelectItem value="loading" disabled>
|
||||||
|
Loading teams...
|
||||||
|
</SelectItem>
|
||||||
|
) : (
|
||||||
|
teams?.map((team) => (
|
||||||
|
<SelectItem key={team.id} value={team.id.toString()}>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<Avatar className="h-8 w-8">
|
||||||
|
{team.avatarImageId && (
|
||||||
|
<AvatarImage
|
||||||
|
src={`${NEXT_PUBLIC_WEBAPP_URL()}/api/avatar/${team.avatarImageId}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<AvatarFallback className="text-sm text-gray-400">
|
||||||
|
{team.name.slice(0, 1).toUpperCase()}
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
|
||||||
|
<span>{team.name}</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="secondary" onClick={() => onOpenChange(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={onMove} loading={isLoading} disabled={!selectedTeamId || isLoading}>
|
||||||
|
{isLoading ? 'Moving...' : 'Move'}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -157,6 +157,7 @@ export const DocumentHistorySheet = ({
|
|||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED },
|
||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED },
|
||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT },
|
||||||
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM },
|
||||||
() => null,
|
() => null,
|
||||||
)
|
)
|
||||||
.with(
|
.with(
|
||||||
@ -304,7 +305,6 @@ export const DocumentHistorySheet = ({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|
||||||
.exhaustive()}
|
.exhaustive()}
|
||||||
|
|
||||||
{isUserDetailsVisible && (
|
{isUserDetailsVisible && (
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export const findDocumentAuditLogs = async ({
|
|||||||
const orderByColumn = orderBy?.column ?? 'createdAt';
|
const orderByColumn = orderBy?.column ?? 'createdAt';
|
||||||
const orderByDirection = orderBy?.direction ?? 'desc';
|
const orderByDirection = orderBy?.direction ?? 'desc';
|
||||||
|
|
||||||
await prisma.document.findFirstOrThrow({
|
const documentFilter = await prisma.document.findFirstOrThrow({
|
||||||
where: {
|
where: {
|
||||||
id: documentId,
|
id: documentId,
|
||||||
OR: [
|
OR: [
|
||||||
@ -67,6 +67,7 @@ export const findDocumentAuditLogs = async ({
|
|||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
|
||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
|
||||||
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
83
packages/lib/server-only/document/move-document-to-team.ts
Normal file
83
packages/lib/server-only/document/move-document-to-team.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { TRPCError } from '@trpc/server';
|
||||||
|
|
||||||
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
|
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||||
|
|
||||||
|
export type MoveDocumentToTeamOptions = {
|
||||||
|
documentId: number;
|
||||||
|
teamId: number;
|
||||||
|
userId: number;
|
||||||
|
requestMetadata?: RequestMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const moveDocumentToTeam = async ({
|
||||||
|
documentId,
|
||||||
|
teamId,
|
||||||
|
userId,
|
||||||
|
requestMetadata,
|
||||||
|
}: MoveDocumentToTeamOptions) => {
|
||||||
|
return await prisma.$transaction(async (tx) => {
|
||||||
|
const user = await tx.user.findUniqueOrThrow({
|
||||||
|
where: { id: userId },
|
||||||
|
});
|
||||||
|
|
||||||
|
const document = await tx.document.findFirst({
|
||||||
|
where: {
|
||||||
|
id: documentId,
|
||||||
|
userId,
|
||||||
|
teamId: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!document) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'NOT_FOUND',
|
||||||
|
message: 'Document not found or already associated with a team.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const team = await tx.team.findFirst({
|
||||||
|
where: {
|
||||||
|
id: teamId,
|
||||||
|
members: {
|
||||||
|
some: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!team) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'FORBIDDEN',
|
||||||
|
message: 'You are not a member of this team.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedDocument = await tx.document.update({
|
||||||
|
where: { id: documentId },
|
||||||
|
data: { teamId },
|
||||||
|
});
|
||||||
|
|
||||||
|
const log = await tx.documentAuditLog.create({
|
||||||
|
data: createDocumentAuditLogData({
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM,
|
||||||
|
documentId: updatedDocument.id,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
movedByUserId: userId,
|
||||||
|
fromPersonalAccount: true,
|
||||||
|
toTeamId: teamId,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(log);
|
||||||
|
|
||||||
|
return updatedDocument;
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -35,6 +35,7 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
|
|||||||
'DOCUMENT_RECIPIENT_COMPLETED', // When a recipient completes all their required tasks for the document.
|
'DOCUMENT_RECIPIENT_COMPLETED', // When a recipient completes all their required tasks for the document.
|
||||||
'DOCUMENT_SENT', // When the document transitions from DRAFT to PENDING.
|
'DOCUMENT_SENT', // When the document transitions from DRAFT to PENDING.
|
||||||
'DOCUMENT_TITLE_UPDATED', // When the document title is updated.
|
'DOCUMENT_TITLE_UPDATED', // When the document title is updated.
|
||||||
|
'DOCUMENT_MOVED_TO_TEAM', // When the document is moved to a team.
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const ZDocumentAuditLogEmailTypeSchema = z.enum([
|
export const ZDocumentAuditLogEmailTypeSchema = z.enum([
|
||||||
@ -410,6 +411,18 @@ export const ZDocumentAuditLogEventRecipientRemovedSchema = z.object({
|
|||||||
data: ZBaseRecipientDataSchema,
|
data: ZBaseRecipientDataSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event: Document moved to team.
|
||||||
|
*/
|
||||||
|
export const ZDocumentAuditLogEventDocumentMovedToTeamSchema = z.object({
|
||||||
|
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM),
|
||||||
|
data: z.object({
|
||||||
|
movedByUserId: z.number(),
|
||||||
|
fromPersonalAccount: z.boolean(),
|
||||||
|
toTeamId: z.number(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
export const ZDocumentAuditLogBaseSchema = z.object({
|
export const ZDocumentAuditLogBaseSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
@ -427,6 +440,7 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
|
|||||||
ZDocumentAuditLogEventDocumentCompletedSchema,
|
ZDocumentAuditLogEventDocumentCompletedSchema,
|
||||||
ZDocumentAuditLogEventDocumentCreatedSchema,
|
ZDocumentAuditLogEventDocumentCreatedSchema,
|
||||||
ZDocumentAuditLogEventDocumentDeletedSchema,
|
ZDocumentAuditLogEventDocumentDeletedSchema,
|
||||||
|
ZDocumentAuditLogEventDocumentMovedToTeamSchema,
|
||||||
ZDocumentAuditLogEventDocumentFieldInsertedSchema,
|
ZDocumentAuditLogEventDocumentFieldInsertedSchema,
|
||||||
ZDocumentAuditLogEventDocumentFieldUninsertedSchema,
|
ZDocumentAuditLogEventDocumentFieldUninsertedSchema,
|
||||||
ZDocumentAuditLogEventDocumentGlobalAuthAccessUpdatedSchema,
|
ZDocumentAuditLogEventDocumentGlobalAuthAccessUpdatedSchema,
|
||||||
|
|||||||
@ -336,6 +336,10 @@ export const formatDocumentAuditLogAction = (auditLog: TDocumentAuditLog, userId
|
|||||||
anonymous: 'Document sent',
|
anonymous: 'Document sent',
|
||||||
identified: 'sent the document',
|
identified: 'sent the document',
|
||||||
}))
|
}))
|
||||||
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM }, () => ({
|
||||||
|
anonymous: 'Document moved to team',
|
||||||
|
identified: 'moved the document to team',
|
||||||
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED }, ({ data }) => {
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED }, ({ data }) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
const action = RECIPIENT_ROLES_DESCRIPTION[data.recipientRole as RecipientRole]?.actioned;
|
const action = RECIPIENT_ROLES_DESCRIPTION[data.recipientRole as RecipientRole]?.actioned;
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-
|
|||||||
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
||||||
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||||
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
||||||
|
import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
|
||||||
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
||||||
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
|
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
|
||||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||||
@ -32,6 +33,7 @@ import {
|
|||||||
ZGetDocumentByIdQuerySchema,
|
ZGetDocumentByIdQuerySchema,
|
||||||
ZGetDocumentByTokenQuerySchema,
|
ZGetDocumentByTokenQuerySchema,
|
||||||
ZGetDocumentWithDetailsByIdQuerySchema,
|
ZGetDocumentWithDetailsByIdQuerySchema,
|
||||||
|
ZMoveDocumentsToTeamSchema,
|
||||||
ZResendDocumentMutationSchema,
|
ZResendDocumentMutationSchema,
|
||||||
ZSearchDocumentsMutationSchema,
|
ZSearchDocumentsMutationSchema,
|
||||||
ZSendDocumentMutationSchema,
|
ZSendDocumentMutationSchema,
|
||||||
@ -158,6 +160,33 @@ export const documentRouter = router({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
moveDocumentToTeam: authenticatedProcedure
|
||||||
|
.input(ZMoveDocumentsToTeamSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const { documentId, teamId } = input;
|
||||||
|
const userId = ctx.user.id;
|
||||||
|
|
||||||
|
return await moveDocumentToTeam({
|
||||||
|
documentId,
|
||||||
|
teamId,
|
||||||
|
userId,
|
||||||
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
if (err instanceof TRPCError) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'We were unable to move this document. Please try again later.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
findDocumentAuditLogs: authenticatedProcedure
|
findDocumentAuditLogs: authenticatedProcedure
|
||||||
.input(ZFindDocumentAuditLogsQuerySchema)
|
.input(ZFindDocumentAuditLogsQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
|||||||
@ -168,3 +168,8 @@ export const ZDownloadAuditLogsMutationSchema = z.object({
|
|||||||
documentId: z.number(),
|
documentId: z.number(),
|
||||||
teamId: z.number().optional(),
|
teamId: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const ZMoveDocumentsToTeamSchema = z.object({
|
||||||
|
documentId: z.number(),
|
||||||
|
teamId: z.number(),
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user