mirror of
https://github.com/documenso/documenso.git
synced 2025-11-23 13:11:32 +10:00
feat: migrate templates and documents to envelope model
This commit is contained in:
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user