mirror of
https://github.com/documenso/documenso.git
synced 2025-11-16 01:32:06 +10:00
feat: migrate templates and documents to envelope model
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { DocumentStatus } from '@prisma/client';
|
||||
import { DocumentStatus, EnvelopeType } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { kyselyPrisma, sql } from '@documenso/prisma';
|
||||
@ -7,18 +7,19 @@ import { addZeroMonth } from '../add-zero-month';
|
||||
|
||||
export const getCompletedDocumentsMonthly = async (type: 'count' | 'cumulative' = 'count') => {
|
||||
const qb = kyselyPrisma.$kysely
|
||||
.selectFrom('Document')
|
||||
.selectFrom('Envelope')
|
||||
.select(({ fn }) => [
|
||||
fn<Date>('DATE_TRUNC', [sql.lit('MONTH'), 'Document.updatedAt']).as('month'),
|
||||
fn<Date>('DATE_TRUNC', [sql.lit('MONTH'), 'Envelope.updatedAt']).as('month'),
|
||||
fn.count('id').as('count'),
|
||||
fn
|
||||
.sum(fn.count('id'))
|
||||
// Feels like a bug in the Kysely extension but I just can not do this orderBy in a type-safe manner
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
|
||||
.over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'Document.updatedAt']) as any))
|
||||
.over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'Envelope.updatedAt']) as any))
|
||||
.as('cume_count'),
|
||||
])
|
||||
.where(() => sql`"Document"."status" = ${DocumentStatus.COMPLETED}::"DocumentStatus"`)
|
||||
.where(() => sql`"Envelope"."status" = ${DocumentStatus.COMPLETED}::"DocumentStatus"`)
|
||||
.where(() => sql`"Envelope"."type" = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`)
|
||||
.groupBy('month')
|
||||
.orderBy('month', 'desc')
|
||||
.limit(12);
|
||||
|
||||
@ -3,7 +3,6 @@ import { useState } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Document } from '@prisma/client';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
@ -22,10 +21,10 @@ import { Input } from '@documenso/ui/primitives/input';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
export type AdminDocumentDeleteDialogProps = {
|
||||
document: Document;
|
||||
envelopeId: string;
|
||||
};
|
||||
|
||||
export const AdminDocumentDeleteDialog = ({ document }: AdminDocumentDeleteDialogProps) => {
|
||||
export const AdminDocumentDeleteDialog = ({ envelopeId }: AdminDocumentDeleteDialogProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
@ -42,7 +41,7 @@ export const AdminDocumentDeleteDialog = ({ document }: AdminDocumentDeleteDialo
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteDocument({ id: document.id, reason });
|
||||
await deleteDocument({ id: envelopeId, reason });
|
||||
|
||||
toast({
|
||||
title: _(msg`Document deleted`),
|
||||
|
||||
@ -81,7 +81,7 @@ export const DocumentMoveToFolderDialog = ({
|
||||
},
|
||||
);
|
||||
|
||||
const { mutateAsync: moveDocumentToFolder } = trpc.folder.moveDocumentToFolder.useMutation();
|
||||
const { mutateAsync: updateDocument } = trpc.document.update.useMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
@ -94,9 +94,11 @@ export const DocumentMoveToFolderDialog = ({
|
||||
|
||||
const onSubmit = async (data: TMoveDocumentFormSchema) => {
|
||||
try {
|
||||
await moveDocumentToFolder({
|
||||
await updateDocument({
|
||||
documentId,
|
||||
folderId: data.folderId ?? null,
|
||||
data: {
|
||||
folderId: data.folderId ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
const documentsPath = formatDocumentsPath(team.url);
|
||||
|
||||
@ -4,15 +4,15 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { type Recipient, SigningStatus } from '@prisma/client';
|
||||
import { type Recipient, SigningStatus, type Team, type User } from '@prisma/client';
|
||||
import { History } from 'lucide-react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import * as z from 'zod';
|
||||
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
||||
import type { TDocumentMany as TDocumentRow } from '@documenso/lib/types/document';
|
||||
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
||||
import type { Document } from '@documenso/prisma/types/document-legacy-schema';
|
||||
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@ -43,7 +43,11 @@ import { StackAvatar } from '../general/stack-avatar';
|
||||
const FORM_ID = 'resend-email';
|
||||
|
||||
export type DocumentResendDialogProps = {
|
||||
document: TDocumentRow;
|
||||
document: Pick<Document, 'id' | 'userId' | 'teamId' | 'status'> & {
|
||||
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||
recipients: Recipient[];
|
||||
team: Pick<Team, 'id' | 'url'> | null;
|
||||
};
|
||||
recipients: Recipient[];
|
||||
};
|
||||
|
||||
|
||||
@ -4,14 +4,14 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Plural, Trans } from '@lingui/react/macro';
|
||||
import type { Template, TemplateDirectLink } from '@prisma/client';
|
||||
import { TemplateType } from '@prisma/client';
|
||||
import { type TemplateDirectLink, TemplateType } from '@prisma/client';
|
||||
import type * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { CheckCircle2Icon, CircleIcon } from 'lucide-react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { P, match } from 'ts-pattern';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { type Template } from '@documenso/prisma/types/template-legacy-schema';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import {
|
||||
MAX_TEMPLATE_PUBLIC_DESCRIPTION_LENGTH,
|
||||
|
||||
@ -54,7 +54,7 @@ export const TemplateCreateDialog = ({ folderId }: TemplateCreateDialogProps) =>
|
||||
try {
|
||||
const response = await putPdfFile(file);
|
||||
|
||||
const { id } = await createTemplate({
|
||||
const { legacyTemplateId: id } = await createTemplate({
|
||||
title: file.name,
|
||||
templateDocumentDataId: response.id,
|
||||
folderId: folderId,
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Recipient, Template, TemplateDirectLink } from '@prisma/client';
|
||||
import type { Recipient, TemplateDirectLink } from '@prisma/client';
|
||||
import { LinkIcon } from 'lucide-react';
|
||||
|
||||
import type { Template } from '@documenso/prisma/types/template-legacy-schema';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
|
||||
import { TemplateDirectLinkDialog } from '~/components/dialogs/template-direct-link-dialog';
|
||||
|
||||
@ -3,12 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import {
|
||||
type Recipient,
|
||||
RecipientRole,
|
||||
type Template,
|
||||
type TemplateDirectLink,
|
||||
} from '@prisma/client';
|
||||
import { type Recipient, RecipientRole, type TemplateDirectLink } from '@prisma/client';
|
||||
import { CircleDotIcon, CircleIcon, ClipboardCopyIcon, InfoIcon, LoaderIcon } from 'lucide-react';
|
||||
import { Link, useRevalidator } from 'react-router';
|
||||
import { P, match } from 'ts-pattern';
|
||||
@ -20,6 +15,7 @@ import { DIRECT_TEMPLATE_RECIPIENT_EMAIL } from '@documenso/lib/constants/direct
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import { DIRECT_TEMPLATE_DOCUMENTATION } from '@documenso/lib/constants/template';
|
||||
import { formatDirectTemplatePath } from '@documenso/lib/utils/templates';
|
||||
import type { Template } from '@documenso/prisma/types/template-legacy-schema';
|
||||
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||
import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||
|
||||
@ -83,7 +83,7 @@ export function TemplateMoveToFolderDialog({
|
||||
},
|
||||
);
|
||||
|
||||
const { mutateAsync: moveTemplateToFolder } = trpc.folder.moveTemplateToFolder.useMutation();
|
||||
const { mutateAsync: updateTemplate } = trpc.template.updateTemplate.useMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
@ -96,9 +96,11 @@ export function TemplateMoveToFolderDialog({
|
||||
|
||||
const onSubmit = async (data: TMoveTemplateFormSchema) => {
|
||||
try {
|
||||
await moveTemplateToFolder({
|
||||
await updateTemplate({
|
||||
templateId,
|
||||
folderId: data.folderId ?? null,
|
||||
data: {
|
||||
folderId: data.folderId ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
toast({
|
||||
|
||||
@ -118,6 +118,9 @@ export const ConfigureFieldsView = ({
|
||||
sendStatus: signer.disabled ? SendStatus.SENT : SendStatus.NOT_SENT,
|
||||
readStatus: signer.disabled ? ReadStatus.OPENED : ReadStatus.NOT_OPENED,
|
||||
signingStatus: signer.disabled ? SigningStatus.SIGNED : SigningStatus.NOT_SIGNED,
|
||||
|
||||
// Todo: Envelopes - Dummy data
|
||||
envelopeId: '',
|
||||
}));
|
||||
}, [configData.signers]);
|
||||
|
||||
|
||||
@ -15,12 +15,14 @@ import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
|
||||
|
||||
import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn';
|
||||
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
||||
import type { DocumentField } from '@documenso/lib/server-only/field/get-fields-for-document';
|
||||
import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers';
|
||||
import { validateFieldsInserted } from '@documenso/lib/utils/fields';
|
||||
import type { RecipientWithFields } from '@documenso/prisma/types/recipient-with-fields';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { DocumentReadOnlyFields } from '@documenso/ui/components/document/document-read-only-fields';
|
||||
import {
|
||||
type DocumentField,
|
||||
DocumentReadOnlyFields,
|
||||
} from '@documenso/ui/components/document/document-read-only-fields';
|
||||
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { type Document, FieldType, type Passkey, type Recipient } from '@prisma/client';
|
||||
import { type Envelope, FieldType, type Passkey, type Recipient } from '@prisma/client';
|
||||
|
||||
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
|
||||
import { MAXIMUM_PASSKEYS } from '@documenso/lib/constants/auth';
|
||||
@ -26,9 +26,9 @@ type PasskeyData = {
|
||||
|
||||
export type DocumentSigningAuthContextValue = {
|
||||
executeActionAuthProcedure: (_value: ExecuteActionAuthProcedureOptions) => Promise<void>;
|
||||
documentAuthOptions: Document['authOptions'];
|
||||
documentAuthOptions: Envelope['authOptions'];
|
||||
documentAuthOption: TDocumentAuthOptions;
|
||||
setDocumentAuthOptions: (_value: Document['authOptions']) => void;
|
||||
setDocumentAuthOptions: (_value: Envelope['authOptions']) => void;
|
||||
recipient: Recipient;
|
||||
recipientAuthOption: TRecipientAuthOptions;
|
||||
setRecipient: (_value: Recipient) => void;
|
||||
@ -61,7 +61,7 @@ export const useRequiredDocumentSigningAuthContext = () => {
|
||||
};
|
||||
|
||||
export interface DocumentSigningAuthProviderProps {
|
||||
documentAuthOptions: Document['authOptions'];
|
||||
documentAuthOptions: Envelope['authOptions'];
|
||||
recipient: Recipient;
|
||||
user?: SessionUser | null;
|
||||
children: React.ReactNode;
|
||||
|
||||
@ -3,12 +3,12 @@ import { useEffect, useState } from 'react';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Document } from '@prisma/client';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { useSearchParams } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { Document } from '@documenso/prisma/types/document-legacy-schema';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
|
||||
@ -4,6 +4,7 @@ import { Trans } from '@lingui/react/macro';
|
||||
import type { DocumentData } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
@ -23,6 +24,7 @@ export type DocumentCertificateQRViewProps = {
|
||||
title: string;
|
||||
documentData: DocumentData;
|
||||
password?: string | null;
|
||||
documentTeamUrl: string;
|
||||
recipientCount?: number;
|
||||
completedDate?: Date;
|
||||
};
|
||||
@ -32,29 +34,30 @@ export const DocumentCertificateQRView = ({
|
||||
title,
|
||||
documentData,
|
||||
password,
|
||||
documentTeamUrl,
|
||||
recipientCount = 0,
|
||||
completedDate,
|
||||
}: DocumentCertificateQRViewProps) => {
|
||||
const { data: documentUrl } = trpc.shareLink.getDocumentInternalUrlForQRCode.useQuery({
|
||||
const { data: documentViaUser } = trpc.document.get.useQuery({
|
||||
documentId,
|
||||
});
|
||||
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(() => !!documentUrl);
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(() => !!documentViaUser);
|
||||
|
||||
const formattedDate = completedDate
|
||||
? DateTime.fromJSDate(completedDate).toLocaleString(DateTime.DATETIME_MED)
|
||||
: '';
|
||||
|
||||
useEffect(() => {
|
||||
if (documentUrl) {
|
||||
if (documentViaUser) {
|
||||
setIsDialogOpen(true);
|
||||
}
|
||||
}, [documentUrl]);
|
||||
}, [documentViaUser]);
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-screen-md">
|
||||
{/* Dialog for internal document link */}
|
||||
{documentUrl && (
|
||||
{documentViaUser && (
|
||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
@ -72,7 +75,11 @@ export const DocumentCertificateQRView = ({
|
||||
|
||||
<DialogFooter className="flex flex-row justify-end gap-2">
|
||||
<Button asChild>
|
||||
<a href={documentUrl} target="_blank" rel="noopener noreferrer">
|
||||
<a
|
||||
href={`${formatDocumentsPath(documentTeamUrl)}/${documentViaUser.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Trans>Go to document</Trans>
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
@ -64,7 +64,7 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
|
||||
|
||||
const response = await putPdfFile(file);
|
||||
|
||||
const { id } = await createDocument({
|
||||
const { legacyDocumentId: id } = await createDocument({
|
||||
title: file.name,
|
||||
documentDataId: response.id,
|
||||
timezone: userTimezone, // Note: When migrating to v2 document upload remember to pass this through as a 'userTimezone' field.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Document, Recipient, Team, User } from '@prisma/client';
|
||||
import type { Recipient, Team, User } from '@prisma/client';
|
||||
import { DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
import { CheckCircle, Download, EyeIcon, Pencil } from 'lucide-react';
|
||||
import { Link } from 'react-router';
|
||||
@ -11,6 +11,7 @@ import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
import type { Document } from '@documenso/prisma/types/document-legacy-schema';
|
||||
import { trpc as trpcClient } from '@documenso/trpc/client';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
@ -3,7 +3,7 @@ import { useState } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Document, Recipient, Team, User } from '@prisma/client';
|
||||
import type { Recipient, Team, User } from '@prisma/client';
|
||||
import { DocumentStatus } from '@prisma/client';
|
||||
import {
|
||||
Copy,
|
||||
@ -19,6 +19,7 @@ import { Link, useNavigate } from 'react-router';
|
||||
|
||||
import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import type { TDocument } from '@documenso/lib/types/document';
|
||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
import { trpc as trpcClient } from '@documenso/trpc/client';
|
||||
@ -39,7 +40,7 @@ import { DocumentRecipientLinkCopyDialog } from '~/components/general/document/d
|
||||
import { useCurrentTeam } from '~/providers/team';
|
||||
|
||||
export type DocumentPageViewDropdownProps = {
|
||||
document: Document & {
|
||||
document: TDocument & {
|
||||
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||
recipients: Recipient[];
|
||||
team: Pick<Team, 'id' | 'url'> | null;
|
||||
|
||||
@ -3,10 +3,11 @@ import { useMemo } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Document, Recipient, User } from '@prisma/client';
|
||||
import type { Recipient, User } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
|
||||
import type { Document } from '@documenso/prisma/types/document-legacy-schema';
|
||||
|
||||
export type DocumentPageViewInformationProps = {
|
||||
userId: number;
|
||||
|
||||
@ -2,7 +2,7 @@ import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
import type { Document, Recipient } from '@prisma/client';
|
||||
import type { Recipient } from '@prisma/client';
|
||||
import {
|
||||
AlertTriangle,
|
||||
CheckIcon,
|
||||
@ -19,6 +19,7 @@ import { match } from 'ts-pattern';
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||
import type { Document } from '@documenso/prisma/types/document-legacy-schema';
|
||||
import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button';
|
||||
import { SignatureIcon } from '@documenso/ui/icons/signature';
|
||||
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
|
||||
|
||||
@ -75,7 +75,7 @@ export const DocumentUploadDropzone = ({ className }: DocumentUploadDropzoneProp
|
||||
|
||||
const response = await putPdfFile(file);
|
||||
|
||||
const { id } = await createDocument({
|
||||
const { legacyDocumentId: id } = await createDocument({
|
||||
title: file.name,
|
||||
documentDataId: response.id,
|
||||
timezone: userTimezone, // Note: When migrating to v2 document upload remember to pass this through as a 'userTimezone' field.
|
||||
|
||||
@ -42,7 +42,7 @@ export const TemplateDropZoneWrapper = ({ children, className }: TemplateDropZon
|
||||
|
||||
const documentData = await putPdfFile(file);
|
||||
|
||||
const { id } = await createTemplate({
|
||||
const { legacyTemplateId: id } = await createTemplate({
|
||||
title: file.name,
|
||||
templateDocumentDataId: documentData.id,
|
||||
folderId: folderId ?? undefined,
|
||||
|
||||
@ -3,10 +3,11 @@ import { useMemo } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Template, User } from '@prisma/client';
|
||||
import type { User } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
|
||||
import type { Template } from '@documenso/prisma/types/template-legacy-schema';
|
||||
|
||||
export type TemplatePageViewInformationProps = {
|
||||
userId: number;
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Recipient, Template } from '@prisma/client';
|
||||
import type { Recipient } from '@prisma/client';
|
||||
import { PenIcon, PlusIcon } from 'lucide-react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import { isTemplateRecipientEmailPlaceholder } from '@documenso/lib/constants/template';
|
||||
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
|
||||
import type { Template } from '@documenso/prisma/types/template-legacy-schema';
|
||||
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
|
||||
|
||||
export type TemplatePageViewRecipientsProps = {
|
||||
|
||||
@ -2,7 +2,7 @@ import { useEffect, useMemo, useState, useTransition } from 'react';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import type { Document, Role, Subscription } from '@prisma/client';
|
||||
import type { Role, Subscription } from '@prisma/client';
|
||||
import { Edit, Loader } from 'lucide-react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
@ -20,7 +20,7 @@ type UserData = {
|
||||
email: string;
|
||||
roles: Role[];
|
||||
subscriptions?: SubscriptionLite[] | null;
|
||||
documents: DocumentLite[];
|
||||
documentCount: number;
|
||||
};
|
||||
|
||||
type SubscriptionLite = Pick<
|
||||
@ -28,8 +28,6 @@ type SubscriptionLite = Pick<
|
||||
'id' | 'status' | 'planId' | 'priceId' | 'createdAt' | 'periodEnd'
|
||||
>;
|
||||
|
||||
type DocumentLite = Pick<Document, 'id'>;
|
||||
|
||||
type AdminDashboardUsersTableProps = {
|
||||
users: UserData[];
|
||||
totalPages: number;
|
||||
@ -74,10 +72,7 @@ export const AdminDashboardUsersTable = ({
|
||||
},
|
||||
{
|
||||
header: _(msg`Documents`),
|
||||
accessorKey: 'documents',
|
||||
cell: ({ row }) => {
|
||||
return <div>{row.original.documents?.length}</div>;
|
||||
},
|
||||
accessorKey: 'documentCount',
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
|
||||
@ -3,8 +3,7 @@ import { useMemo, useState } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { TemplateDirectLink } from '@prisma/client';
|
||||
import { TemplateType } from '@prisma/client';
|
||||
import { type TemplateDirectLink, TemplateType } from '@prisma/client';
|
||||
import { EditIcon, FileIcon, LinkIcon, MoreHorizontalIcon, Trash2Icon } from 'lucide-react';
|
||||
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Recipient, Template, TemplateDirectLink } from '@prisma/client';
|
||||
import type { Recipient, TemplateDirectLink } from '@prisma/client';
|
||||
import { Copy, Edit, FolderIcon, MoreHorizontal, Share2Icon, Trash2, Upload } from 'lucide-react';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import type { Template } from '@documenso/prisma/types/template-legacy-schema';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { SigningStatus } from '@prisma/client';
|
||||
import { EnvelopeType, SigningStatus } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
import { Link, redirect } from 'react-router';
|
||||
|
||||
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||
import { unsafeGetEntireEnvelope } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import {
|
||||
Accordion,
|
||||
@ -36,13 +36,19 @@ export async function loader({ params }: Route.LoaderArgs) {
|
||||
throw redirect('/admin/documents');
|
||||
}
|
||||
|
||||
const document = await getEntireDocument({ id });
|
||||
const envelope = await unsafeGetEntireEnvelope({
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id,
|
||||
},
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
});
|
||||
|
||||
return { document };
|
||||
return { envelope };
|
||||
}
|
||||
|
||||
export default function AdminDocumentDetailsPage({ loaderData }: Route.ComponentProps) {
|
||||
const { document } = loaderData;
|
||||
const { envelope } = loaderData;
|
||||
|
||||
const { _, i18n } = useLingui();
|
||||
const { toast } = useToast();
|
||||
@ -68,11 +74,11 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
|
||||
<div>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<h1 className="text-2xl font-semibold">{document.title}</h1>
|
||||
<DocumentStatus status={document.status} />
|
||||
<h1 className="text-2xl font-semibold">{envelope.title}</h1>
|
||||
<DocumentStatus status={envelope.status} />
|
||||
</div>
|
||||
|
||||
{document.deletedAt && (
|
||||
{envelope.deletedAt && (
|
||||
<Badge size="large" variant="destructive">
|
||||
<Trans>Deleted</Trans>
|
||||
</Badge>
|
||||
@ -81,11 +87,11 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
|
||||
|
||||
<div className="text-muted-foreground mt-4 text-sm">
|
||||
<div>
|
||||
<Trans>Created on</Trans>: {i18n.date(document.createdAt, DateTime.DATETIME_MED)}
|
||||
<Trans>Created on</Trans>: {i18n.date(envelope.createdAt, DateTime.DATETIME_MED)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Trans>Last updated at</Trans>: {i18n.date(document.updatedAt, DateTime.DATETIME_MED)}
|
||||
<Trans>Last updated at</Trans>: {i18n.date(envelope.updatedAt, DateTime.DATETIME_MED)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -102,12 +108,12 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
|
||||
<Button
|
||||
variant="outline"
|
||||
loading={isResealDocumentLoading}
|
||||
disabled={document.recipients.some(
|
||||
disabled={envelope.recipients.some(
|
||||
(recipient) =>
|
||||
recipient.signingStatus !== SigningStatus.SIGNED &&
|
||||
recipient.signingStatus !== SigningStatus.REJECTED,
|
||||
)}
|
||||
onClick={() => resealDocument({ id: document.id })}
|
||||
onClick={() => resealDocument({ id: envelope.id })}
|
||||
>
|
||||
<Trans>Reseal document</Trans>
|
||||
</Button>
|
||||
@ -123,7 +129,7 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
|
||||
</TooltipProvider>
|
||||
|
||||
<Button variant="outline" asChild>
|
||||
<Link to={`/admin/users/${document.userId}`}>
|
||||
<Link to={`/admin/users/${envelope.userId}`}>
|
||||
<Trans>Go to owner</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
@ -136,7 +142,7 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
|
||||
|
||||
<div className="mt-4">
|
||||
<Accordion type="multiple" className="space-y-4">
|
||||
{document.recipients.map((recipient) => (
|
||||
{envelope.recipients.map((recipient) => (
|
||||
<AccordionItem
|
||||
key={recipient.id}
|
||||
value={recipient.id.toString()}
|
||||
@ -161,7 +167,7 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
|
||||
|
||||
<hr className="my-4" />
|
||||
|
||||
{document && <AdminDocumentDeleteDialog document={document} />}
|
||||
{envelope && <AdminDocumentDeleteDialog envelopeId={envelope.id} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,7 +12,10 @@ import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
import { DocumentVisibility } from '@documenso/lib/types/document-visibility';
|
||||
import { logDocumentAccess } from '@documenso/lib/utils/logger';
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
import { DocumentReadOnlyFields } from '@documenso/ui/components/document/document-read-only-fields';
|
||||
import {
|
||||
DocumentReadOnlyFields,
|
||||
mapFieldsWithRecipients,
|
||||
} from '@documenso/ui/components/document/document-read-only-fields';
|
||||
import { Badge } from '@documenso/ui/primitives/badge';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
|
||||
@ -54,7 +57,10 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
||||
}
|
||||
|
||||
const document = await getDocumentWithDetailsById({
|
||||
documentId,
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
}).catch(() => null);
|
||||
@ -171,7 +177,7 @@ export default function DocumentPage() {
|
||||
|
||||
{document.status !== DocumentStatus.COMPLETED && (
|
||||
<DocumentReadOnlyFields
|
||||
fields={document.fields}
|
||||
fields={mapFieldsWithRecipients(document.fields, recipients)}
|
||||
documentMeta={documentMeta || undefined}
|
||||
showRecipientTooltip={true}
|
||||
showRecipientColors={true}
|
||||
|
||||
@ -42,7 +42,10 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
||||
}
|
||||
|
||||
const document = await getDocumentWithDetailsById({
|
||||
documentId,
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
}).catch(() => null);
|
||||
|
||||
@ -2,15 +2,16 @@ import type { MessageDescriptor } from '@lingui/core';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Recipient } from '@prisma/client';
|
||||
import { EnvelopeType, type Recipient } from '@prisma/client';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { DateTime } from 'luxon';
|
||||
import { Link, redirect } from 'react-router';
|
||||
|
||||
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
||||
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
||||
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
|
||||
import { logDocumentAccess } from '@documenso/lib/utils/logger';
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
import { Card } from '@documenso/ui/primitives/card';
|
||||
@ -40,13 +41,17 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
||||
throw redirect(documentRootPath);
|
||||
}
|
||||
|
||||
const document = await getDocumentById({
|
||||
documentId,
|
||||
const envelope = await getEnvelopeById({
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
teamId: team.id,
|
||||
}).catch(() => null);
|
||||
|
||||
if (!document || !document.documentData) {
|
||||
if (!envelope) {
|
||||
throw redirect(documentRootPath);
|
||||
}
|
||||
|
||||
@ -63,7 +68,19 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
||||
});
|
||||
|
||||
return {
|
||||
document,
|
||||
// Only return necessary data
|
||||
document: {
|
||||
id: mapSecondaryIdToDocumentId(envelope.secondaryId),
|
||||
title: envelope.title,
|
||||
status: envelope.status,
|
||||
user: {
|
||||
name: envelope.user.name,
|
||||
email: envelope.user.email,
|
||||
},
|
||||
createdAt: envelope.createdAt,
|
||||
updatedAt: envelope.updatedAt,
|
||||
documentMeta: envelope.documentMeta,
|
||||
},
|
||||
recipients,
|
||||
documentRootPath,
|
||||
};
|
||||
|
||||
@ -2,8 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { TemplateDirectLink } from '@prisma/client';
|
||||
import { TemplateType } from '@prisma/client';
|
||||
import { type TemplateDirectLink, TemplateType } from '@prisma/client';
|
||||
|
||||
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
import { redirect } from 'react-router';
|
||||
|
||||
import { DOCUMENT_STATUS } from '@documenso/lib/constants/document';
|
||||
import { APP_I18N_OPTIONS, ZSupportedLanguageCodeSchema } from '@documenso/lib/constants/i18n';
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||
import { unsafeGetEntireEnvelope } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
||||
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
|
||||
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
|
||||
import { getTranslations } from '@documenso/lib/utils/i18n';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
|
||||
@ -39,20 +41,24 @@ export async function loader({ request }: Route.LoaderArgs) {
|
||||
|
||||
const documentId = Number(rawDocumentId);
|
||||
|
||||
const document = await getEntireDocument({
|
||||
id: documentId,
|
||||
const envelope = await unsafeGetEntireEnvelope({
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
}).catch(() => null);
|
||||
|
||||
if (!document) {
|
||||
if (!envelope) {
|
||||
throw redirect('/');
|
||||
}
|
||||
|
||||
const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language);
|
||||
const documentLanguage = ZSupportedLanguageCodeSchema.parse(envelope.documentMeta?.language);
|
||||
|
||||
const { data: auditLogs } = await findDocumentAuditLogs({
|
||||
documentId: documentId,
|
||||
userId: document.userId,
|
||||
teamId: document.teamId,
|
||||
userId: envelope.userId,
|
||||
teamId: envelope.teamId,
|
||||
perPage: 100_000,
|
||||
});
|
||||
|
||||
@ -60,7 +66,20 @@ export async function loader({ request }: Route.LoaderArgs) {
|
||||
|
||||
return {
|
||||
auditLogs,
|
||||
document,
|
||||
document: {
|
||||
id: mapSecondaryIdToDocumentId(envelope.secondaryId),
|
||||
title: envelope.title,
|
||||
status: envelope.status,
|
||||
user: {
|
||||
name: envelope.user.name,
|
||||
email: envelope.user.email,
|
||||
},
|
||||
recipients: envelope.recipients,
|
||||
createdAt: envelope.createdAt,
|
||||
updatedAt: envelope.updatedAt,
|
||||
deletedAt: envelope.deletedAt,
|
||||
documentMeta: envelope.documentMeta,
|
||||
},
|
||||
documentLanguage,
|
||||
messages,
|
||||
};
|
||||
@ -90,6 +109,7 @@ export default function AuditLog({ loaderData }: Route.ComponentProps) {
|
||||
<Card>
|
||||
<CardContent className="grid grid-cols-2 gap-4 p-6 text-sm print:text-xs">
|
||||
<p>
|
||||
{/* Todo: Envelopes - Should we should envelope ID instead here? */}
|
||||
<span className="font-medium">{_(msg`Document ID`)}</span>
|
||||
|
||||
<span className="mt-1 block break-words">{document.id}</span>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { FieldType, SigningStatus } from '@prisma/client';
|
||||
import { EnvelopeType, FieldType, SigningStatus } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
import { redirect } from 'react-router';
|
||||
import { prop, sortBy } from 'remeda';
|
||||
@ -14,12 +14,13 @@ import {
|
||||
RECIPIENT_ROLES_DESCRIPTION,
|
||||
RECIPIENT_ROLE_SIGNING_REASONS,
|
||||
} from '@documenso/lib/constants/recipient-roles';
|
||||
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||
import { unsafeGetEntireEnvelope } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
||||
import { getDocumentCertificateAuditLogs } from '@documenso/lib/server-only/document/get-document-certificate-audit-logs';
|
||||
import { getOrganisationClaimByTeamId } from '@documenso/lib/server-only/organisation/get-organisation-claims';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
||||
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
|
||||
import { getTranslations } from '@documenso/lib/utils/i18n';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
import {
|
||||
@ -55,26 +56,45 @@ export async function loader({ request }: Route.LoaderArgs) {
|
||||
|
||||
const documentId = Number(rawDocumentId);
|
||||
|
||||
const document = await getEntireDocument({
|
||||
id: documentId,
|
||||
const envelope = await unsafeGetEntireEnvelope({
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
}).catch(() => null);
|
||||
|
||||
if (!document) {
|
||||
if (!envelope) {
|
||||
throw redirect('/');
|
||||
}
|
||||
|
||||
const organisationClaim = await getOrganisationClaimByTeamId({ teamId: document.teamId });
|
||||
const organisationClaim = await getOrganisationClaimByTeamId({ teamId: envelope.teamId });
|
||||
|
||||
const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language);
|
||||
const documentLanguage = ZSupportedLanguageCodeSchema.parse(envelope.documentMeta?.language);
|
||||
|
||||
const auditLogs = await getDocumentCertificateAuditLogs({
|
||||
id: documentId,
|
||||
envelopeId: envelope.id,
|
||||
});
|
||||
|
||||
const messages = await getTranslations(documentLanguage);
|
||||
|
||||
return {
|
||||
document,
|
||||
document: {
|
||||
id: mapSecondaryIdToDocumentId(envelope.secondaryId),
|
||||
title: envelope.title,
|
||||
status: envelope.status,
|
||||
user: {
|
||||
name: envelope.user.name,
|
||||
email: envelope.user.email,
|
||||
},
|
||||
qrToken: envelope.qrToken,
|
||||
authOptions: envelope.authOptions,
|
||||
recipients: envelope.recipients,
|
||||
createdAt: envelope.createdAt,
|
||||
updatedAt: envelope.updatedAt,
|
||||
deletedAt: envelope.deletedAt,
|
||||
documentMeta: envelope.documentMeta,
|
||||
},
|
||||
hidePoweredBy: organisationClaim.flags.hidePoweredBy,
|
||||
documentLanguage,
|
||||
auditLogs,
|
||||
|
||||
@ -3,7 +3,7 @@ import { useEffect } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { type Document, DocumentStatus, FieldType, RecipientRole } from '@prisma/client';
|
||||
import { DocumentStatus, FieldType, RecipientRole } from '@prisma/client';
|
||||
import { CheckCircle2, Clock8, FileSearch } from 'lucide-react';
|
||||
import { Link, useRevalidator } from 'react-router';
|
||||
import { match } from 'ts-pattern';
|
||||
@ -19,6 +19,7 @@ import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get
|
||||
import { getUserByEmail } from '@documenso/lib/server-only/user/get-user-by-email';
|
||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||
import { env } from '@documenso/lib/utils/env';
|
||||
import type { Document } from '@documenso/prisma/types/document-legacy-schema';
|
||||
import DocumentDialog from '@documenso/ui/components/document/document-dialog';
|
||||
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
|
||||
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Team } from '@prisma/client';
|
||||
import { DocumentStatus } from '@prisma/client';
|
||||
import { DocumentStatus, EnvelopeType } from '@prisma/client';
|
||||
import { Link, redirect } from 'react-router';
|
||||
|
||||
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
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 { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
||||
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
@ -40,12 +40,16 @@ export async function loader({ params, request }: Route.LoaderArgs) {
|
||||
let team: Team | null = null;
|
||||
|
||||
if (user) {
|
||||
isOwnerOrTeamMember = await getDocumentById({
|
||||
documentId: document.id,
|
||||
isOwnerOrTeamMember = await getEnvelopeById({
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id: document.id,
|
||||
},
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
userId: user.id,
|
||||
teamId: document.teamId ?? undefined,
|
||||
})
|
||||
.then((document) => !!document)
|
||||
.then((envelope) => !!envelope)
|
||||
.catch(() => false);
|
||||
|
||||
if (document.teamId) {
|
||||
|
||||
@ -81,9 +81,9 @@ export default function SharePage() {
|
||||
<DocumentCertificateQRView
|
||||
documentId={document.id}
|
||||
title={document.title}
|
||||
documentData={document.documentData}
|
||||
password={document.documentMeta?.password}
|
||||
recipientCount={document.recipients?.length ?? 0}
|
||||
documentTeamUrl={document.documentTeamUrl}
|
||||
documentData={document.documentData} // Todo: Envelopes
|
||||
recipientCount={document.recipientCount}
|
||||
completedDate={document.completedAt ?? undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -54,7 +54,10 @@ export const loader = async ({ request, params }: Route.LoaderArgs) => {
|
||||
}
|
||||
|
||||
const document = await getDocumentWithDetailsById({
|
||||
documentId,
|
||||
id: {
|
||||
type: 'documentId',
|
||||
id: documentId,
|
||||
},
|
||||
userId: result?.userId,
|
||||
teamId: result?.teamId ?? undefined,
|
||||
}).catch(() => null);
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import type { Context } from 'hono';
|
||||
|
||||
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
import {
|
||||
mapDocumentIdToSecondaryId,
|
||||
mapTemplateIdToSecondaryId,
|
||||
} from '@documenso/lib/utils/envelope';
|
||||
import { buildTeamWhereQuery } from '@documenso/lib/utils/teams';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
@ -104,9 +109,10 @@ async function hasAccessToDocument(c: Context, documentId: number): Promise<stri
|
||||
|
||||
const userId = session.user.id;
|
||||
|
||||
const document = await prisma.document.findUnique({
|
||||
const envelope = await prisma.envelope.findUnique({
|
||||
where: {
|
||||
id: documentId,
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
secondaryId: mapDocumentIdToSecondaryId(documentId),
|
||||
team: buildTeamWhereQuery({
|
||||
userId,
|
||||
teamId: undefined,
|
||||
@ -121,7 +127,7 @@ async function hasAccessToDocument(c: Context, documentId: number): Promise<stri
|
||||
},
|
||||
});
|
||||
|
||||
return document ? document.team.url : null;
|
||||
return envelope ? envelope.team.url : null;
|
||||
}
|
||||
|
||||
async function hasAccessToFolder(c: Context, folderId: string): Promise<string | null> {
|
||||
@ -154,9 +160,10 @@ async function hasAccessToTemplate(c: Context, templateId: number): Promise<stri
|
||||
|
||||
const userId = session.user.id;
|
||||
|
||||
const template = await prisma.template.findUnique({
|
||||
const envelope = await prisma.envelope.findUnique({
|
||||
where: {
|
||||
id: templateId,
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
secondaryId: mapTemplateIdToSecondaryId(templateId),
|
||||
team: buildTeamWhereQuery({
|
||||
userId,
|
||||
teamId: undefined,
|
||||
@ -171,5 +178,5 @@ async function hasAccessToTemplate(c: Context, templateId: number): Promise<stri
|
||||
},
|
||||
});
|
||||
|
||||
return template ? template.team.url : null;
|
||||
return envelope ? envelope.team.url : null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user