mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
feat: deleted documents bin
This commit is contained in:
@ -162,7 +162,16 @@ export const DocumentDeleteDialog = ({
|
|||||||
</ul>
|
</ul>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
))
|
))
|
||||||
.exhaustive()}
|
// DocumentStatus.REJECTED isnt working currently so this is a fallback to prevent 500 error.
|
||||||
|
// The union should work but currently its not
|
||||||
|
.otherwise(() => (
|
||||||
|
<AlertDescription>
|
||||||
|
<Trans>
|
||||||
|
Please note that this action is <strong>irreversible</strong>. Once confirmed,
|
||||||
|
this document will be permanently deleted.
|
||||||
|
</Trans>
|
||||||
|
</AlertDescription>
|
||||||
|
))}
|
||||||
</Alert>
|
</Alert>
|
||||||
) : (
|
) : (
|
||||||
<Alert variant="warning" className="-mt-1">
|
<Alert variant="warning" className="-mt-1">
|
||||||
|
|||||||
@ -38,11 +38,6 @@ export const DocumentSigningRecipientProvider = ({
|
|||||||
recipient,
|
recipient,
|
||||||
targetSigner = null,
|
targetSigner = null,
|
||||||
}: DocumentSigningRecipientProviderProps) => {
|
}: DocumentSigningRecipientProviderProps) => {
|
||||||
// console.log({
|
|
||||||
// recipient,
|
|
||||||
// targetSigner,
|
|
||||||
// isAssistantMode: !!targetSigner,
|
|
||||||
// });
|
|
||||||
return (
|
return (
|
||||||
<DocumentSigningRecipientContext.Provider
|
<DocumentSigningRecipientContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import type { HTMLAttributes } from 'react';
|
|||||||
import type { MessageDescriptor } from '@lingui/core';
|
import type { MessageDescriptor } from '@lingui/core';
|
||||||
import { msg } from '@lingui/core/macro';
|
import { msg } from '@lingui/core/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { CheckCircle2, Clock, File, XCircle } from 'lucide-react';
|
import { CheckCircle2, Clock, File, Trash, XCircle } from 'lucide-react';
|
||||||
import type { LucideIcon } from 'lucide-react/dist/lucide-react';
|
import type { LucideIcon } from 'lucide-react/dist/lucide-react';
|
||||||
|
|
||||||
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
@ -36,11 +36,11 @@ export const FRIENDLY_STATUS_MAP: Record<ExtendedDocumentStatus, FriendlyStatus>
|
|||||||
icon: File,
|
icon: File,
|
||||||
color: 'text-yellow-500 dark:text-yellow-200',
|
color: 'text-yellow-500 dark:text-yellow-200',
|
||||||
},
|
},
|
||||||
REJECTED: {
|
DELETED: {
|
||||||
label: msg`Rejected`,
|
label: msg`Deleted`,
|
||||||
labelExtended: msg`Document rejected`,
|
labelExtended: msg`Document deleted`,
|
||||||
icon: XCircle,
|
icon: Trash,
|
||||||
color: 'text-red-500 dark:text-red-300',
|
color: 'text-red-700 dark:text-red-500',
|
||||||
},
|
},
|
||||||
INBOX: {
|
INBOX: {
|
||||||
label: msg`Inbox`,
|
label: msg`Inbox`,
|
||||||
@ -53,6 +53,12 @@ export const FRIENDLY_STATUS_MAP: Record<ExtendedDocumentStatus, FriendlyStatus>
|
|||||||
labelExtended: msg`Document All`,
|
labelExtended: msg`Document All`,
|
||||||
color: 'text-muted-foreground',
|
color: 'text-muted-foreground',
|
||||||
},
|
},
|
||||||
|
REJECTED: {
|
||||||
|
label: msg`Rejected`,
|
||||||
|
labelExtended: msg`Document rejected`,
|
||||||
|
icon: XCircle,
|
||||||
|
color: 'text-red-500 dark:text-red-300',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DocumentStatusProps = HTMLAttributes<HTMLSpanElement> & {
|
export type DocumentStatusProps = HTMLAttributes<HTMLSpanElement> & {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { msg } from '@lingui/core/macro';
|
import { msg } from '@lingui/core/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { Bird, CheckCircle2 } from 'lucide-react';
|
import { Bird, CheckCircle2, Trash } from 'lucide-react';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
@ -30,6 +30,11 @@ export const DocumentsTableEmptyState = ({ status }: DocumentsTableEmptyStatePro
|
|||||||
message: msg`You have not yet created or received any documents. To create a document please upload one.`,
|
message: msg`You have not yet created or received any documents. To create a document please upload one.`,
|
||||||
icon: Bird,
|
icon: Bird,
|
||||||
}))
|
}))
|
||||||
|
.with(ExtendedDocumentStatus.DELETED, () => ({
|
||||||
|
title: msg`Nothing in the trash`,
|
||||||
|
message: msg`There are no documents in the trash.`,
|
||||||
|
icon: Trash,
|
||||||
|
}))
|
||||||
.otherwise(() => ({
|
.otherwise(() => ({
|
||||||
title: msg`Nothing to do`,
|
title: msg`Nothing to do`,
|
||||||
message: msg`All documents have been processed. Any new documents that are sent or received will show here.`,
|
message: msg`All documents have been processed. Any new documents that are sent or received will show here.`,
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { Trans } from '@lingui/react/macro';
|
import { Trans } from '@lingui/react/macro';
|
||||||
import { useSearchParams } from 'react-router';
|
import { Link, useSearchParams } from 'react-router';
|
||||||
import { Link } from 'react-router';
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { formatAvatarUrl } from '@documenso/lib/utils/avatars';
|
import { formatAvatarUrl } from '@documenso/lib/utils/avatars';
|
||||||
@ -51,6 +50,7 @@ export default function DocumentsPage() {
|
|||||||
[ExtendedDocumentStatus.PENDING]: 0,
|
[ExtendedDocumentStatus.PENDING]: 0,
|
||||||
[ExtendedDocumentStatus.COMPLETED]: 0,
|
[ExtendedDocumentStatus.COMPLETED]: 0,
|
||||||
[ExtendedDocumentStatus.REJECTED]: 0,
|
[ExtendedDocumentStatus.REJECTED]: 0,
|
||||||
|
[ExtendedDocumentStatus.DELETED]: 0,
|
||||||
[ExtendedDocumentStatus.INBOX]: 0,
|
[ExtendedDocumentStatus.INBOX]: 0,
|
||||||
[ExtendedDocumentStatus.ALL]: 0,
|
[ExtendedDocumentStatus.ALL]: 0,
|
||||||
});
|
});
|
||||||
@ -114,13 +114,17 @@ export default function DocumentsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="-m-1 flex flex-wrap gap-x-4 gap-y-6 overflow-hidden p-1">
|
<div className="-m-1 flex flex-wrap gap-x-4 gap-y-6 overflow-hidden p-1">
|
||||||
<Tabs value={findDocumentSearchParams.status || 'ALL'} className="overflow-x-auto">
|
<Tabs
|
||||||
|
value={findDocumentSearchParams.status || ExtendedDocumentStatus.ALL}
|
||||||
|
className="overflow-x-auto"
|
||||||
|
>
|
||||||
<TabsList>
|
<TabsList>
|
||||||
{[
|
{[
|
||||||
ExtendedDocumentStatus.INBOX,
|
ExtendedDocumentStatus.INBOX,
|
||||||
ExtendedDocumentStatus.PENDING,
|
ExtendedDocumentStatus.PENDING,
|
||||||
ExtendedDocumentStatus.COMPLETED,
|
ExtendedDocumentStatus.COMPLETED,
|
||||||
ExtendedDocumentStatus.DRAFT,
|
ExtendedDocumentStatus.DRAFT,
|
||||||
|
ExtendedDocumentStatus.DELETED,
|
||||||
ExtendedDocumentStatus.ALL,
|
ExtendedDocumentStatus.ALL,
|
||||||
].map((value) => (
|
].map((value) => (
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export const getDocumentStats = async () => {
|
|||||||
[ExtendedDocumentStatus.COMPLETED]: 0,
|
[ExtendedDocumentStatus.COMPLETED]: 0,
|
||||||
[ExtendedDocumentStatus.REJECTED]: 0,
|
[ExtendedDocumentStatus.REJECTED]: 0,
|
||||||
[ExtendedDocumentStatus.ALL]: 0,
|
[ExtendedDocumentStatus.ALL]: 0,
|
||||||
|
[ExtendedDocumentStatus.DELETED]: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
counts.forEach((stat) => {
|
counts.forEach((stat) => {
|
||||||
|
|||||||
@ -136,18 +136,26 @@ export const findDocuments = async ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deletedDateRange =
|
||||||
|
status === ExtendedDocumentStatus.DELETED
|
||||||
|
? {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).toJSDate(),
|
||||||
|
lte: DateTime.now().toJSDate(),
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
let deletedFilter: Prisma.DocumentWhereInput = {
|
let deletedFilter: Prisma.DocumentWhereInput = {
|
||||||
AND: {
|
AND: {
|
||||||
OR: [
|
OR: [
|
||||||
{
|
{
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
deletedAt: null,
|
deletedAt: deletedDateRange,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
recipients: {
|
recipients: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
documentDeletedAt: null,
|
documentDeletedAt: deletedDateRange,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -162,19 +170,19 @@ export const findDocuments = async ({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
deletedAt: null,
|
deletedAt: deletedDateRange,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
user: {
|
user: {
|
||||||
email: team.teamEmail.email,
|
email: team.teamEmail.email,
|
||||||
},
|
},
|
||||||
deletedAt: null,
|
deletedAt: deletedDateRange,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
recipients: {
|
recipients: {
|
||||||
some: {
|
some: {
|
||||||
email: team.teamEmail.email,
|
email: team.teamEmail.email,
|
||||||
documentDeletedAt: null,
|
documentDeletedAt: deletedDateRange,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -182,7 +190,7 @@ export const findDocuments = async ({
|
|||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
deletedAt: null,
|
deletedAt: deletedDateRange,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -297,6 +305,14 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
status: ExtendedDocumentStatus.REJECTED,
|
||||||
|
recipients: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}))
|
}))
|
||||||
.with(ExtendedDocumentStatus.INBOX, () => ({
|
.with(ExtendedDocumentStatus.INBOX, () => ({
|
||||||
@ -368,7 +384,24 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
recipients: {
|
recipients: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
signingStatus: SigningStatus.REJECTED,
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
.with(ExtendedDocumentStatus.DELETED, () => ({
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).toJSDate(),
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
recipients: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -410,7 +443,7 @@ const findTeamDocumentsFilter = (
|
|||||||
status: ExtendedDocumentStatus,
|
status: ExtendedDocumentStatus,
|
||||||
team: Team & { teamEmail: TeamEmail | null },
|
team: Team & { teamEmail: TeamEmail | null },
|
||||||
visibilityFilters: Prisma.DocumentWhereInput[],
|
visibilityFilters: Prisma.DocumentWhereInput[],
|
||||||
) => {
|
): Prisma.DocumentWhereInput | null => {
|
||||||
const teamEmail = team.teamEmail?.email ?? null;
|
const teamEmail = team.teamEmail?.email ?? null;
|
||||||
|
|
||||||
return match<ExtendedDocumentStatus, Prisma.DocumentWhereInput | null>(status)
|
return match<ExtendedDocumentStatus, Prisma.DocumentWhereInput | null>(status)
|
||||||
@ -599,5 +632,32 @@ const findTeamDocumentsFilter = (
|
|||||||
|
|
||||||
return filter;
|
return filter;
|
||||||
})
|
})
|
||||||
|
.with(ExtendedDocumentStatus.DELETED, () => {
|
||||||
|
return {
|
||||||
|
OR: teamEmail
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
recipients: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
})
|
||||||
.exhaustive();
|
.exhaustive();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { TeamMemberRole } from '@prisma/client';
|
|
||||||
import type { Prisma, User } from '@prisma/client';
|
import type { Prisma, User } from '@prisma/client';
|
||||||
import { SigningStatus } from '@prisma/client';
|
import { DocumentVisibility, SigningStatus, TeamMemberRole } from '@prisma/client';
|
||||||
import { DocumentVisibility } from '@prisma/client';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
@ -17,7 +15,7 @@ export type GetStatsInput = {
|
|||||||
search?: string;
|
search?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStats = async ({ user, period, search = '', ...options }: GetStatsInput) => {
|
export const getStats = async ({ user, period, search, ...options }: GetStatsInput) => {
|
||||||
let createdAt: Prisma.DocumentWhereInput['createdAt'];
|
let createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||||
|
|
||||||
if (period) {
|
if (period) {
|
||||||
@ -30,7 +28,7 @@ export const getStats = async ({ user, period, search = '', ...options }: GetSta
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ownerCounts, notSignedCounts, hasSignedCounts] = await (options.team
|
const [ownerCounts, notSignedCounts, hasSignedCounts, deletedCounts] = await (options.team
|
||||||
? getTeamCounts({
|
? getTeamCounts({
|
||||||
...options.team,
|
...options.team,
|
||||||
createdAt,
|
createdAt,
|
||||||
@ -45,6 +43,7 @@ export const getStats = async ({ user, period, search = '', ...options }: GetSta
|
|||||||
[ExtendedDocumentStatus.PENDING]: 0,
|
[ExtendedDocumentStatus.PENDING]: 0,
|
||||||
[ExtendedDocumentStatus.COMPLETED]: 0,
|
[ExtendedDocumentStatus.COMPLETED]: 0,
|
||||||
[ExtendedDocumentStatus.REJECTED]: 0,
|
[ExtendedDocumentStatus.REJECTED]: 0,
|
||||||
|
[ExtendedDocumentStatus.DELETED]: 0,
|
||||||
[ExtendedDocumentStatus.INBOX]: 0,
|
[ExtendedDocumentStatus.INBOX]: 0,
|
||||||
[ExtendedDocumentStatus.ALL]: 0,
|
[ExtendedDocumentStatus.ALL]: 0,
|
||||||
};
|
};
|
||||||
@ -71,6 +70,8 @@ export const getStats = async ({ user, period, search = '', ...options }: GetSta
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
stats[ExtendedDocumentStatus.DELETED] = deletedCounts || 0;
|
||||||
|
|
||||||
Object.keys(stats).forEach((key) => {
|
Object.keys(stats).forEach((key) => {
|
||||||
if (key !== ExtendedDocumentStatus.ALL && isExtendedDocumentStatus(key)) {
|
if (key !== ExtendedDocumentStatus.ALL && isExtendedDocumentStatus(key)) {
|
||||||
stats[ExtendedDocumentStatus.ALL] += stats[key];
|
stats[ExtendedDocumentStatus.ALL] += stats[key];
|
||||||
@ -167,6 +168,32 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
|||||||
AND: [searchFilter],
|
AND: [searchFilter],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
// Deleted count
|
||||||
|
prisma.document.count({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).toJSDate(),
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
recipients: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
|
documentDeletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).toJSDate(),
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
AND: [searchFilter],
|
||||||
|
},
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -336,5 +363,40 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
|
|||||||
}),
|
}),
|
||||||
notSignedCountsGroupByArgs ? prisma.document.groupBy(notSignedCountsGroupByArgs) : [],
|
notSignedCountsGroupByArgs ? prisma.document.groupBy(notSignedCountsGroupByArgs) : [],
|
||||||
hasSignedCountsGroupByArgs ? prisma.document.groupBy(hasSignedCountsGroupByArgs) : [],
|
hasSignedCountsGroupByArgs ? prisma.document.groupBy(hasSignedCountsGroupByArgs) : [],
|
||||||
|
prisma.document.count({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
userId: userIdWhereClause,
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).toJSDate(),
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).toJSDate(),
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
recipients: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
documentDeletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).toJSDate(),
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
AND: [searchFilter],
|
||||||
|
},
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export const ExtendedDocumentStatus = {
|
|||||||
...DocumentStatus,
|
...DocumentStatus,
|
||||||
INBOX: 'INBOX',
|
INBOX: 'INBOX',
|
||||||
ALL: 'ALL',
|
ALL: 'ALL',
|
||||||
|
DELETED: 'DELETED',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type ExtendedDocumentStatus =
|
export type ExtendedDocumentStatus =
|
||||||
|
|||||||
@ -145,6 +145,7 @@ export const ZFindDocumentsInternalResponseSchema = ZFindResultResponse.extend({
|
|||||||
[ExtendedDocumentStatus.PENDING]: z.number(),
|
[ExtendedDocumentStatus.PENDING]: z.number(),
|
||||||
[ExtendedDocumentStatus.COMPLETED]: z.number(),
|
[ExtendedDocumentStatus.COMPLETED]: z.number(),
|
||||||
[ExtendedDocumentStatus.REJECTED]: z.number(),
|
[ExtendedDocumentStatus.REJECTED]: z.number(),
|
||||||
|
[ExtendedDocumentStatus.DELETED]: z.number(),
|
||||||
[ExtendedDocumentStatus.INBOX]: z.number(),
|
[ExtendedDocumentStatus.INBOX]: z.number(),
|
||||||
[ExtendedDocumentStatus.ALL]: z.number(),
|
[ExtendedDocumentStatus.ALL]: z.number(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user