mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 16:51:38 +10:00
Merge branch 'main' into staging
This commit is contained in:
@ -93,6 +93,8 @@ NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS=
|
|||||||
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
|
||||||
# REQUIRED: Defines the email address to use as the from address.
|
# REQUIRED: Defines the email address to use as the from address.
|
||||||
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@documenso.com"
|
NEXT_PRIVATE_SMTP_FROM_ADDRESS="noreply@documenso.com"
|
||||||
|
# OPTIONAL: Defines the service for nodemailer
|
||||||
|
NEXT_PRIVATE_SMTP_SERVICE=
|
||||||
# OPTIONAL: The API key to use for Resend.com
|
# OPTIONAL: The API key to use for Resend.com
|
||||||
NEXT_PRIVATE_RESEND_API_KEY=
|
NEXT_PRIVATE_RESEND_API_KEY=
|
||||||
# OPTIONAL: The API key to use for MailChannels.
|
# OPTIONAL: The API key to use for MailChannels.
|
||||||
|
|||||||
@ -38,11 +38,17 @@ You will be prompted to enter some information, such as the certificate's Common
|
|||||||
Combine the private key and the self-signed certificate to create a `.p12` certificate. Use the following command:
|
Combine the private key and the self-signed certificate to create a `.p12` certificate. Use the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt
|
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt -legacy
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="warning">
|
<Callout type="warning">
|
||||||
If you get the error "Error: Failed to get private key bags", add the `-legacy` flag to the command `openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt -legacy`.
|
When running the application in Docker, you may encounter permission issues when attempting to sign documents using your certificate (.p12) file. This happens because the application runs as a non-root user inside the container and needs read access to the certificate.
|
||||||
|
|
||||||
|
To resolve this, you'll need to update the certificate file permissions to allow the container user 1001, which runs NextJS, to read it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chown 1001 certificate.p12
|
||||||
|
```
|
||||||
|
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
@ -54,8 +60,8 @@ Note that for local development, the password can be left empty.
|
|||||||
|
|
||||||
### Add Certificate to the Project
|
### Add Certificate to the Project
|
||||||
|
|
||||||
Finally, add the certificate to the project. Place the `certificate.p12` file in the `/apps/web/resources` directory. If the directory doesn't exist, create it.
|
Use the `NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH` environment variable to point at the certificate you created.
|
||||||
|
|
||||||
The final file path should be `/apps/web/resources/certificate.p12`.
|
Details about environment variables associated with certificates can be found [here](/developers/self-hosting/signing-certificate#configure-documenso-to-use-the-certificate).
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|||||||
@ -133,7 +133,7 @@ volumes:
|
|||||||
After updating the volume binding, save the `compose.yml` file and run the following command to start the containers:
|
After updating the volume binding, save the `compose.yml` file and run the following command to start the containers:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose --env-file ./.env -d up
|
docker-compose --env-file ./.env up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
The command will start the PostgreSQL database and the Documenso application containers.
|
The command will start the PostgreSQL database and the Documenso application containers.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/marketing",
|
"name": "@documenso/marketing",
|
||||||
"version": "1.7.2-rc.3",
|
"version": "1.7.2-rc.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
23
apps/marketing/src/app/(marketing)/[content]/content.tsx
Normal file
23
apps/marketing/src/app/(marketing)/[content]/content.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
import type { DocumentTypes } from 'contentlayer/generated';
|
||||||
|
import type { MDXComponents } from 'mdx/types';
|
||||||
|
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||||
|
|
||||||
|
const mdxComponents: MDXComponents = {
|
||||||
|
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
||||||
|
<Image {...props} alt={props.alt ?? ''} />
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ContentPageContentProps = {
|
||||||
|
document: DocumentTypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ContentPageContent = ({ document }: ContentPageContentProps) => {
|
||||||
|
const MDXContent = useMDXComponent(document.body.code);
|
||||||
|
|
||||||
|
return <MDXContent components={mdxComponents} />;
|
||||||
|
};
|
||||||
@ -1,12 +1,11 @@
|
|||||||
import Image from 'next/image';
|
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
import { allDocuments } from 'contentlayer/generated';
|
import { allDocuments } from 'contentlayer/generated';
|
||||||
import type { MDXComponents } from 'mdx/types';
|
|
||||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
|
||||||
|
|
||||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
|
|
||||||
|
import { ContentPageContent } from './content';
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
export const generateMetadata = ({ params }: { params: { content: string } }) => {
|
export const generateMetadata = ({ params }: { params: { content: string } }) => {
|
||||||
@ -19,12 +18,6 @@ export const generateMetadata = ({ params }: { params: { content: string } }) =>
|
|||||||
return { title: document.title };
|
return { title: document.title };
|
||||||
};
|
};
|
||||||
|
|
||||||
const mdxComponents: MDXComponents = {
|
|
||||||
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
|
||||||
<Image {...props} alt={props.alt ?? ''} />
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic catch all page for the root level that checks for content layer documents.
|
* A generic catch all page for the root level that checks for content layer documents.
|
||||||
*
|
*
|
||||||
@ -39,11 +32,9 @@ export default async function ContentPage({ params }: { params: { content: strin
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const MDXContent = useMDXComponent(post.body.code);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className="prose dark:prose-invert mx-auto">
|
<article className="prose dark:prose-invert mx-auto">
|
||||||
<MDXContent components={mdxComponents} />
|
<ContentPageContent document={post} />
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
23
apps/marketing/src/app/(marketing)/blog/[post]/content.tsx
Normal file
23
apps/marketing/src/app/(marketing)/blog/[post]/content.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
import type { BlogPost } from 'contentlayer/generated';
|
||||||
|
import type { MDXComponents } from 'mdx/types';
|
||||||
|
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||||
|
|
||||||
|
const mdxComponents: MDXComponents = {
|
||||||
|
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
||||||
|
<Image {...props} alt={props.alt ?? ''} />
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BlogPostContentProps = {
|
||||||
|
post: BlogPost;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BlogPostContent = ({ post }: BlogPostContentProps) => {
|
||||||
|
const MdxContent = useMDXComponent(post.body.code);
|
||||||
|
|
||||||
|
return <MdxContent components={mdxComponents} />;
|
||||||
|
};
|
||||||
@ -1,16 +1,15 @@
|
|||||||
import Image from 'next/image';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
import { allBlogPosts } from 'contentlayer/generated';
|
import { allBlogPosts } from 'contentlayer/generated';
|
||||||
import { ChevronLeft } from 'lucide-react';
|
import { ChevronLeft } from 'lucide-react';
|
||||||
import type { MDXComponents } from 'mdx/types';
|
|
||||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
|
||||||
|
|
||||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
|
|
||||||
import { CallToAction } from '~/components/(marketing)/call-to-action';
|
import { CallToAction } from '~/components/(marketing)/call-to-action';
|
||||||
|
|
||||||
|
import { BlogPostContent } from './content';
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
export const generateMetadata = ({ params }: { params: { post: string } }) => {
|
export const generateMetadata = ({ params }: { params: { post: string } }) => {
|
||||||
@ -42,12 +41,6 @@ export const generateMetadata = ({ params }: { params: { post: string } }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mdxComponents: MDXComponents = {
|
|
||||||
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
|
||||||
<Image {...props} alt={props.alt ?? ''} />
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function BlogPostPage({ params }: { params: { post: string } }) {
|
export default async function BlogPostPage({ params }: { params: { post: string } }) {
|
||||||
await setupI18nSSR();
|
await setupI18nSSR();
|
||||||
|
|
||||||
@ -57,8 +50,6 @@ export default async function BlogPostPage({ params }: { params: { post: string
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const MDXContent = useMDXComponent(post.body.code);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<article className="prose dark:prose-invert mx-auto py-8">
|
<article className="prose dark:prose-invert mx-auto py-8">
|
||||||
@ -87,7 +78,7 @@ export default async function BlogPostPage({ params }: { params: { post: string
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MDXContent components={mdxComponents} />
|
<BlogPostContent post={post} />
|
||||||
|
|
||||||
{post.tags.length > 0 && (
|
{post.tags.length > 0 && (
|
||||||
<ul className="not-prose flex list-none flex-row space-x-2 px-0">
|
<ul className="not-prose flex list-none flex-row space-x-2 px-0">
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
@ -130,9 +131,9 @@ const fetchEarlyAdopters = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function OpenPage() {
|
export default async function OpenPage() {
|
||||||
const { i18n } = await setupI18nSSR();
|
await setupI18nSSR();
|
||||||
|
|
||||||
const { _ } = i18n;
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const [
|
const [
|
||||||
{ forks_count: forksCount, stargazers_count: stargazersCount },
|
{ forks_count: forksCount, stargazers_count: stargazersCount },
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.7.2-rc.3",
|
"version": "1.7.2-rc.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -33,6 +33,8 @@ import {
|
|||||||
} from '@documenso/ui/primitives/dropdown-menu';
|
} from '@documenso/ui/primitives/dropdown-menu';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
import { DocumentRecipientLinkCopyDialog } from '~/components/document/document-recipient-link-copy-dialog';
|
||||||
|
|
||||||
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';
|
||||||
@ -62,6 +64,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
|
|||||||
|
|
||||||
const isOwner = document.User.id === session.user.id;
|
const isOwner = document.User.id === session.user.id;
|
||||||
const isDraft = document.status === DocumentStatus.DRAFT;
|
const isDraft = document.status === DocumentStatus.DRAFT;
|
||||||
|
const isPending = document.status === DocumentStatus.PENDING;
|
||||||
const isDeleted = document.deletedAt !== null;
|
const isDeleted = document.deletedAt !== null;
|
||||||
const isComplete = document.status === DocumentStatus.COMPLETED;
|
const isComplete = document.status === DocumentStatus.COMPLETED;
|
||||||
const isCurrentTeamDocument = team && document.team?.url === team.url;
|
const isCurrentTeamDocument = team && document.team?.url === team.url;
|
||||||
@ -145,6 +148,21 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
|
|||||||
<Trans>Share</Trans>
|
<Trans>Share</Trans>
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
|
|
||||||
|
{canManageDocument && (
|
||||||
|
<DocumentRecipientLinkCopyDialog
|
||||||
|
recipients={document.Recipient}
|
||||||
|
trigger={
|
||||||
|
<DropdownMenuItem
|
||||||
|
disabled={!isPending || isDeleted}
|
||||||
|
onSelect={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<Copy className="mr-2 h-4 w-4" />
|
||||||
|
<Trans>Signing Links</Trans>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<ResendDocumentActionItem
|
<ResendDocumentActionItem
|
||||||
document={document}
|
document={document}
|
||||||
recipients={nonSignedRecipients}
|
recipients={nonSignedRecipients}
|
||||||
|
|||||||
@ -143,17 +143,11 @@ export const DocumentPageViewRecentActivity = ({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Todo: Translations. */}
|
|
||||||
<p
|
<p
|
||||||
className="text-muted-foreground dark:text-muted-foreground/70 flex-auto truncate py-0.5 text-xs leading-5"
|
className="text-muted-foreground dark:text-muted-foreground/70 flex-auto truncate py-0.5 text-xs leading-5"
|
||||||
title={`${formatDocumentAuditLogAction(auditLog, userId).prefix} ${
|
title={formatDocumentAuditLogAction(_, auditLog, userId).description}
|
||||||
formatDocumentAuditLogAction(auditLog, userId).description
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<span className="text-foreground font-medium">
|
{formatDocumentAuditLogAction(_, auditLog, userId).description}
|
||||||
{formatDocumentAuditLogAction(auditLog, userId).prefix}
|
|
||||||
</span>{' '}
|
|
||||||
{formatDocumentAuditLogAction(auditLog, userId).description}
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<time className="text-muted-foreground dark:text-muted-foreground/70 flex-none py-0.5 text-xs leading-5">
|
<time className="text-muted-foreground dark:text-muted-foreground/70 flex-none py-0.5 text-xs leading-5">
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
@ -6,11 +8,14 @@ import { CheckIcon, Clock, MailIcon, MailOpenIcon, PenIcon, PlusIcon } from 'luc
|
|||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||||
|
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||||
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
||||||
import type { Document, Recipient } from '@documenso/prisma/client';
|
import type { Document, Recipient } from '@documenso/prisma/client';
|
||||||
|
import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button';
|
||||||
import { SignatureIcon } from '@documenso/ui/icons/signature';
|
import { SignatureIcon } from '@documenso/ui/icons/signature';
|
||||||
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
|
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
|
||||||
import { Badge } from '@documenso/ui/primitives/badge';
|
import { Badge } from '@documenso/ui/primitives/badge';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
export type DocumentPageViewRecipientsProps = {
|
export type DocumentPageViewRecipientsProps = {
|
||||||
document: Document & {
|
document: Document & {
|
||||||
@ -24,6 +29,7 @@ export const DocumentPageViewRecipients = ({
|
|||||||
documentRootPath,
|
documentRootPath,
|
||||||
}: DocumentPageViewRecipientsProps) => {
|
}: DocumentPageViewRecipientsProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
const recipients = document.Recipient;
|
const recipients = document.Recipient;
|
||||||
|
|
||||||
@ -68,6 +74,7 @@ export const DocumentPageViewRecipients = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
{document.status !== DocumentStatus.DRAFT &&
|
{document.status !== DocumentStatus.DRAFT &&
|
||||||
recipient.signingStatus === SigningStatus.SIGNED && (
|
recipient.signingStatus === SigningStatus.SIGNED && (
|
||||||
<Badge variant="default">
|
<Badge variant="default">
|
||||||
@ -115,6 +122,21 @@ export const DocumentPageViewRecipients = ({
|
|||||||
<Trans>Pending</Trans>
|
<Trans>Pending</Trans>
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{document.status === DocumentStatus.PENDING &&
|
||||||
|
recipient.signingStatus === SigningStatus.NOT_SIGNED &&
|
||||||
|
recipient.role !== RecipientRole.CC && (
|
||||||
|
<CopyTextButton
|
||||||
|
value={formatSigningLink(recipient.token)}
|
||||||
|
onCopySuccess={() => {
|
||||||
|
toast({
|
||||||
|
title: _(msg`Copied to clipboard`),
|
||||||
|
description: _(msg`The signing link has been copied to your clipboard.`),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
|||||||
import { StackAvatarsWithTooltip } from '~/components/(dashboard)/avatar/stack-avatars-with-tooltip';
|
import { StackAvatarsWithTooltip } from '~/components/(dashboard)/avatar/stack-avatars-with-tooltip';
|
||||||
import { DocumentHistorySheet } from '~/components/document/document-history-sheet';
|
import { DocumentHistorySheet } from '~/components/document/document-history-sheet';
|
||||||
import { DocumentReadOnlyFields } from '~/components/document/document-read-only-fields';
|
import { DocumentReadOnlyFields } from '~/components/document/document-read-only-fields';
|
||||||
|
import { DocumentRecipientLinkCopyDialog } from '~/components/document/document-recipient-link-copy-dialog';
|
||||||
import {
|
import {
|
||||||
DocumentStatus as DocumentStatusComponent,
|
DocumentStatus as DocumentStatusComponent,
|
||||||
FRIENDLY_STATUS_MAP,
|
FRIENDLY_STATUS_MAP,
|
||||||
@ -134,6 +135,10 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
||||||
|
{document.status === DocumentStatus.PENDING && (
|
||||||
|
<DocumentRecipientLinkCopyDialog recipients={recipients} />
|
||||||
|
)}
|
||||||
|
|
||||||
<Link href={documentRootPath} className="flex items-center text-[#7AC455] hover:opacity-80">
|
<Link href={documentRootPath} className="flex items-center text-[#7AC455] hover:opacity-80">
|
||||||
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
||||||
<Trans>Documents</Trans>
|
<Trans>Documents</Trans>
|
||||||
|
|||||||
@ -58,10 +58,6 @@ export const DocumentLogsDataTable = ({ documentId }: DocumentLogsDataTableProps
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const uppercaseFistLetter = (text: string) => {
|
|
||||||
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const results = data ?? {
|
const results = data ?? {
|
||||||
data: [],
|
data: [],
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
@ -103,9 +99,7 @@ export const DocumentLogsDataTable = ({ documentId }: DocumentLogsDataTableProps
|
|||||||
{
|
{
|
||||||
header: _(msg`Action`),
|
header: _(msg`Action`),
|
||||||
accessorKey: 'type',
|
accessorKey: 'type',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => <span>{formatDocumentAuditLogAction(_, row.original).description}</span>,
|
||||||
<span>{uppercaseFistLetter(formatDocumentAuditLogAction(row.original).description)}</span>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'IP Address',
|
header: 'IP Address',
|
||||||
|
|||||||
@ -37,6 +37,8 @@ import {
|
|||||||
} from '@documenso/ui/primitives/dropdown-menu';
|
} from '@documenso/ui/primitives/dropdown-menu';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
import { DocumentRecipientLinkCopyDialog } from '~/components/document/document-recipient-link-copy-dialog';
|
||||||
|
|
||||||
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';
|
||||||
@ -69,7 +71,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
const isOwner = row.User.id === session.user.id;
|
const isOwner = row.User.id === session.user.id;
|
||||||
// const isRecipient = !!recipient;
|
// const isRecipient = !!recipient;
|
||||||
const isDraft = row.status === DocumentStatus.DRAFT;
|
const isDraft = row.status === DocumentStatus.DRAFT;
|
||||||
// const isPending = row.status === DocumentStatus.PENDING;
|
const isPending = row.status === DocumentStatus.PENDING;
|
||||||
const isComplete = row.status === DocumentStatus.COMPLETED;
|
const isComplete = row.status === DocumentStatus.COMPLETED;
|
||||||
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
|
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
|
||||||
const isCurrentTeamDocument = team && row.team?.url === team.url;
|
const isCurrentTeamDocument = team && row.team?.url === team.url;
|
||||||
@ -191,6 +193,20 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
<Trans>Share</Trans>
|
<Trans>Share</Trans>
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
|
|
||||||
|
{canManageDocument && (
|
||||||
|
<DocumentRecipientLinkCopyDialog
|
||||||
|
recipients={row.Recipient}
|
||||||
|
trigger={
|
||||||
|
<DropdownMenuItem disabled={!isPending} asChild onSelect={(e) => e.preventDefault()}>
|
||||||
|
<div>
|
||||||
|
<Copy className="mr-2 h-4 w-4" />
|
||||||
|
<Trans>Signing Links</Trans>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<ResendDocumentActionItem document={row} recipients={nonSignedRecipients} team={team} />
|
<ResendDocumentActionItem document={row} recipients={nonSignedRecipients} team={team} />
|
||||||
|
|
||||||
<DocumentShareButton
|
<DocumentShareButton
|
||||||
|
|||||||
@ -44,11 +44,11 @@ export const BillingPlans = ({ prices }: BillingPlansProps) => {
|
|||||||
const isMounted = useIsMounted();
|
const isMounted = useIsMounted();
|
||||||
|
|
||||||
const [interval, setInterval] = useState<Interval>('month');
|
const [interval, setInterval] = useState<Interval>('month');
|
||||||
const [isFetchingCheckoutSession, setIsFetchingCheckoutSession] = useState(false);
|
const [checkoutSessionPriceId, setCheckoutSessionPriceId] = useState<string | null>(null);
|
||||||
|
|
||||||
const onSubscribeClick = async (priceId: string) => {
|
const onSubscribeClick = async (priceId: string) => {
|
||||||
try {
|
try {
|
||||||
setIsFetchingCheckoutSession(true);
|
setCheckoutSessionPriceId(priceId);
|
||||||
|
|
||||||
const url = await createCheckout({ priceId });
|
const url = await createCheckout({ priceId });
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ export const BillingPlans = ({ prices }: BillingPlansProps) => {
|
|||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsFetchingCheckoutSession(false);
|
setCheckoutSessionPriceId(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,7 +122,8 @@ export const BillingPlans = ({ prices }: BillingPlansProps) => {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
loading={isFetchingCheckoutSession}
|
disabled={checkoutSessionPriceId !== null}
|
||||||
|
loading={checkoutSessionPriceId === price.id}
|
||||||
onClick={() => void onSubscribeClick(price.id)}
|
onClick={() => void onSubscribeClick(price.id)}
|
||||||
>
|
>
|
||||||
<Trans>Subscribe</Trans>
|
<Trans>Subscribe</Trans>
|
||||||
|
|||||||
14
apps/web/src/app/(dashboard)/templates/[id]/edit/page.tsx
Normal file
14
apps/web/src/app/(dashboard)/templates/[id]/edit/page.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
|
|
||||||
|
import type { TemplateEditPageViewProps } from './template-edit-page-view';
|
||||||
|
import { TemplateEditPageView } from './template-edit-page-view';
|
||||||
|
|
||||||
|
type TemplateEditPageProps = Pick<TemplateEditPageViewProps, 'params'>;
|
||||||
|
|
||||||
|
export default async function TemplateEditPage({ params }: TemplateEditPageProps) {
|
||||||
|
await setupI18nSSR();
|
||||||
|
|
||||||
|
return <TemplateEditPageView params={params} />;
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
|
import { Trans } from '@lingui/macro';
|
||||||
|
import { ChevronLeft } from 'lucide-react';
|
||||||
|
|
||||||
|
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||||
|
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
|
import { getTemplateWithDetailsById } from '@documenso/lib/server-only/template/get-template-with-details-by-id';
|
||||||
|
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
|
||||||
|
import type { Team } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { TemplateType } from '~/components/formatter/template-type';
|
||||||
|
|
||||||
|
import { TemplateDirectLinkBadge } from '../../template-direct-link-badge';
|
||||||
|
import { TemplateDirectLinkDialogWrapper } from '../template-direct-link-dialog-wrapper';
|
||||||
|
import { EditTemplateForm } from './edit-template';
|
||||||
|
|
||||||
|
export type TemplateEditPageViewProps = {
|
||||||
|
params: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
team?: Team;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TemplateEditPageView = async ({ params, team }: TemplateEditPageViewProps) => {
|
||||||
|
const { id } = params;
|
||||||
|
|
||||||
|
const templateId = Number(id);
|
||||||
|
const templateRootPath = formatTemplatesPath(team?.url);
|
||||||
|
|
||||||
|
if (!templateId || Number.isNaN(templateId)) {
|
||||||
|
redirect(templateRootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
|
const template = await getTemplateWithDetailsById({
|
||||||
|
id: templateId,
|
||||||
|
userId: user.id,
|
||||||
|
}).catch(() => null);
|
||||||
|
|
||||||
|
if (!template || !template.templateDocumentData) {
|
||||||
|
redirect(templateRootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTemplateEnterprise = await isUserEnterprise({
|
||||||
|
userId: user.id,
|
||||||
|
teamId: team?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto -mt-4 max-w-screen-xl px-4 md:px-8">
|
||||||
|
<div className="flex flex-col justify-between sm:flex-row">
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
href={`${templateRootPath}/${templateId}`}
|
||||||
|
className="flex items-center text-[#7AC455] hover:opacity-80"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
||||||
|
<Trans>Template</Trans>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
||||||
|
{template.title}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="mt-2.5 flex items-center">
|
||||||
|
<TemplateType inheritColor className="text-muted-foreground" type={template.type} />
|
||||||
|
|
||||||
|
{template.directLink?.token && (
|
||||||
|
<TemplateDirectLinkBadge
|
||||||
|
className="ml-4"
|
||||||
|
token={template.directLink.token}
|
||||||
|
enabled={template.directLink.enabled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 sm:mt-0 sm:self-end">
|
||||||
|
<TemplateDirectLinkDialogWrapper template={template} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditTemplateForm
|
||||||
|
className="mt-6"
|
||||||
|
initialTemplate={template}
|
||||||
|
templateRootPath={templateRootPath}
|
||||||
|
isEnterprise={isTemplateEnterprise}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
|
|
||||||
import type { TemplatePageViewProps } from './template-page-view';
|
|
||||||
import { TemplatePageView } from './template-page-view';
|
import { TemplatePageView } from './template-page-view';
|
||||||
|
|
||||||
type TemplatePageProps = Pick<TemplatePageViewProps, 'params'>;
|
export type TemplatePageProps = {
|
||||||
|
params: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default async function TemplatePage({ params }: TemplatePageProps) {
|
export default async function TemplatePage({ params }: TemplatePageProps) {
|
||||||
await setupI18nSSR();
|
await setupI18nSSR();
|
||||||
|
|||||||
@ -10,11 +10,13 @@ import { Button } from '@documenso/ui/primitives/button';
|
|||||||
|
|
||||||
import { TemplateDirectLinkDialog } from '../template-direct-link-dialog';
|
import { TemplateDirectLinkDialog } from '../template-direct-link-dialog';
|
||||||
|
|
||||||
export type TemplatePageViewProps = {
|
export type TemplateDirectLinkDialogWrapperProps = {
|
||||||
template: Template & { directLink?: TemplateDirectLink | null; Recipient: Recipient[] };
|
template: Template & { directLink?: TemplateDirectLink | null; Recipient: Recipient[] };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TemplateDirectLinkDialogWrapper = ({ template }: TemplatePageViewProps) => {
|
export const TemplateDirectLinkDialogWrapper = ({
|
||||||
|
template,
|
||||||
|
}: TemplateDirectLinkDialogWrapperProps) => {
|
||||||
const [isTemplateDirectLinkOpen, setTemplateDirectLinkOpen] = useState(false);
|
const [isTemplateDirectLinkOpen, setTemplateDirectLinkOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -0,0 +1,281 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
|
import type { MessageDescriptor } from '@lingui/core';
|
||||||
|
import { Trans, msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
|
import { InfoIcon } from 'lucide-react';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||||
|
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||||
|
import type { Team } from '@documenso/prisma/client';
|
||||||
|
import { DocumentSource, DocumentStatus as DocumentStatusEnum } from '@documenso/prisma/client';
|
||||||
|
import { trpc } from '@documenso/trpc/react';
|
||||||
|
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
|
||||||
|
import { DataTable } from '@documenso/ui/primitives/data-table';
|
||||||
|
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
|
||||||
|
import { SelectItem } from '@documenso/ui/primitives/select';
|
||||||
|
import { Skeleton } from '@documenso/ui/primitives/skeleton';
|
||||||
|
import { TableCell } from '@documenso/ui/primitives/table';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
|
||||||
|
|
||||||
|
import { StackAvatarsWithTooltip } from '~/components/(dashboard)/avatar/stack-avatars-with-tooltip';
|
||||||
|
import { DocumentSearch } from '~/components/(dashboard)/document-search/document-search';
|
||||||
|
import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-selector';
|
||||||
|
import { DocumentStatus } from '~/components/formatter/document-status';
|
||||||
|
import { SearchParamSelector } from '~/components/forms/search-param-selector';
|
||||||
|
|
||||||
|
import { DataTableActionButton } from '../../documents/data-table-action-button';
|
||||||
|
import { DataTableActionDropdown } from '../../documents/data-table-action-dropdown';
|
||||||
|
import { DataTableTitle } from '../../documents/data-table-title';
|
||||||
|
|
||||||
|
const DOCUMENT_SOURCE_LABELS: { [key in DocumentSource]: MessageDescriptor } = {
|
||||||
|
DOCUMENT: msg`Document`,
|
||||||
|
TEMPLATE: msg`Template`,
|
||||||
|
TEMPLATE_DIRECT_LINK: msg`Direct link`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ZTemplateSearchParamsSchema = ZBaseTableSearchParamsSchema.extend({
|
||||||
|
source: z
|
||||||
|
.nativeEnum(DocumentSource)
|
||||||
|
.optional()
|
||||||
|
.catch(() => undefined),
|
||||||
|
status: z
|
||||||
|
.nativeEnum(DocumentStatusEnum)
|
||||||
|
.optional()
|
||||||
|
.catch(() => undefined),
|
||||||
|
search: z.coerce
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.catch(() => undefined),
|
||||||
|
});
|
||||||
|
|
||||||
|
type TemplatePageViewDocumentsTableProps = {
|
||||||
|
templateId: number;
|
||||||
|
team?: Team;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TemplatePageViewDocumentsTable = ({
|
||||||
|
templateId,
|
||||||
|
team,
|
||||||
|
}: TemplatePageViewDocumentsTableProps) => {
|
||||||
|
const { _, i18n } = useLingui();
|
||||||
|
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const updateSearchParams = useUpdateSearchParams();
|
||||||
|
|
||||||
|
const parsedSearchParams = ZTemplateSearchParamsSchema.parse(
|
||||||
|
Object.fromEntries(searchParams ?? []),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data, isLoading, isInitialLoading, isLoadingError } =
|
||||||
|
trpc.document.findDocuments.useQuery(
|
||||||
|
{
|
||||||
|
templateId,
|
||||||
|
teamId: team?.id,
|
||||||
|
page: parsedSearchParams.page,
|
||||||
|
perPage: parsedSearchParams.perPage,
|
||||||
|
search: parsedSearchParams.search,
|
||||||
|
source: parsedSearchParams.source,
|
||||||
|
status: parsedSearchParams.status,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keepPreviousData: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const onPaginationChange = (page: number, perPage: number) => {
|
||||||
|
updateSearchParams({
|
||||||
|
page,
|
||||||
|
perPage,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const results = data ?? {
|
||||||
|
data: [],
|
||||||
|
perPage: 10,
|
||||||
|
currentPage: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
header: _(msg`Created`),
|
||||||
|
accessorKey: 'createdAt',
|
||||||
|
cell: ({ row }) =>
|
||||||
|
i18n.date(row.original.createdAt, { ...DateTime.DATETIME_SHORT, hourCycle: 'h12' }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: _(msg`Title`),
|
||||||
|
cell: ({ row }) => <DataTableTitle row={row.original} teamUrl={team?.url} />,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
header: _(msg`Recipient`),
|
||||||
|
accessorKey: 'recipient',
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<StackAvatarsWithTooltip
|
||||||
|
recipients={row.original.Recipient}
|
||||||
|
documentStatus={row.original.status}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: _(msg`Status`),
|
||||||
|
accessorKey: 'status',
|
||||||
|
cell: ({ row }) => <DocumentStatus status={row.getValue('status')} />,
|
||||||
|
size: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: () => (
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<Trans>Source</Trans>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="mx-2 h-4 w-4" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
|
||||||
|
<TooltipContent className="text-foreground max-w-md space-y-2 !p-0">
|
||||||
|
<ul className="text-muted-foreground space-y-0.5 divide-y [&>li]:p-4">
|
||||||
|
<li>
|
||||||
|
<h2 className="mb-2 flex flex-row items-center font-semibold">
|
||||||
|
<Trans>Template</Trans>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<Trans>
|
||||||
|
This document was created by you or a team member using the template above.
|
||||||
|
</Trans>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<h2 className="mb-2 flex flex-row items-center font-semibold">
|
||||||
|
<Trans>Direct Link</Trans>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<Trans>This document was created using a direct link.</Trans>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
accessorKey: 'type',
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
{_(DOCUMENT_SOURCE_LABELS[row.original.source])}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actions',
|
||||||
|
header: _(msg`Actions`),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<DataTableActionButton team={team} row={row.original} />
|
||||||
|
|
||||||
|
<DataTableActionDropdown team={team} row={row.original} />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
] satisfies DataTableColumnDef<(typeof results)['data'][number]>[];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="mb-4 flex flex-row space-x-4">
|
||||||
|
<DocumentSearch />
|
||||||
|
|
||||||
|
<SearchParamSelector
|
||||||
|
paramKey="status"
|
||||||
|
isValueValid={(value) =>
|
||||||
|
[...DocumentStatusEnum.COMPLETED].includes(value as unknown as string)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectItem value="all">
|
||||||
|
<Trans>Any Status</Trans>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={DocumentStatusEnum.COMPLETED}>
|
||||||
|
<Trans>Completed</Trans>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={DocumentStatusEnum.PENDING}>
|
||||||
|
<Trans>Pending</Trans>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={DocumentStatusEnum.DRAFT}>
|
||||||
|
<Trans>Draft</Trans>
|
||||||
|
</SelectItem>
|
||||||
|
</SearchParamSelector>
|
||||||
|
|
||||||
|
<SearchParamSelector
|
||||||
|
paramKey="source"
|
||||||
|
isValueValid={(value) =>
|
||||||
|
[...DocumentSource.TEMPLATE].includes(value as unknown as string)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectItem value="all">
|
||||||
|
<Trans>Any Source</Trans>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={DocumentSource.TEMPLATE}>
|
||||||
|
<Trans>Template</Trans>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={DocumentSource.TEMPLATE_DIRECT_LINK}>
|
||||||
|
<Trans>Direct Link</Trans>
|
||||||
|
</SelectItem>
|
||||||
|
</SearchParamSelector>
|
||||||
|
|
||||||
|
<PeriodSelector />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={results.data}
|
||||||
|
perPage={results.perPage}
|
||||||
|
currentPage={results.currentPage}
|
||||||
|
totalPages={results.totalPages}
|
||||||
|
onPaginationChange={onPaginationChange}
|
||||||
|
error={{
|
||||||
|
enable: isLoadingError,
|
||||||
|
}}
|
||||||
|
skeleton={{
|
||||||
|
enable: isLoading && isInitialLoading,
|
||||||
|
rows: 3,
|
||||||
|
component: (
|
||||||
|
<>
|
||||||
|
<TableCell>
|
||||||
|
<Skeleton className="h-4 w-12 rounded-full" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Skeleton className="h-4 w-24 rounded-full" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="py-4 pr-4">
|
||||||
|
<Skeleton className="h-12 w-12 flex-shrink-0 rounded-full" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Skeleton className="h-4 w-12 rounded-full" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Skeleton className="h-4 w-12 rounded-full" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex flex-row justify-end space-x-2">
|
||||||
|
<Skeleton className="h-10 w-20 rounded" />
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(table) => <DataTablePagination additionalInformation="VisibleCount" table={table} />}
|
||||||
|
</DataTable>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { Trans, msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
|
||||||
|
import type { Template, User } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
export type TemplatePageViewInformationProps = {
|
||||||
|
userId: number;
|
||||||
|
template: Template & {
|
||||||
|
User: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TemplatePageViewInformation = ({
|
||||||
|
template,
|
||||||
|
userId,
|
||||||
|
}: TemplatePageViewInformationProps) => {
|
||||||
|
const isMounted = useIsMounted();
|
||||||
|
|
||||||
|
const { _, i18n } = useLingui();
|
||||||
|
|
||||||
|
const templateInformation = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
description: msg`Uploaded by`,
|
||||||
|
value: userId === template.userId ? _(msg`You`) : template.User.name ?? template.User.email,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: msg`Created`,
|
||||||
|
value: i18n.date(template.createdAt, { dateStyle: 'medium' }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: msg`Last modified`,
|
||||||
|
value: DateTime.fromJSDate(template.updatedAt)
|
||||||
|
.setLocale(i18n.locales?.[0] || i18n.locale)
|
||||||
|
.toRelative(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isMounted, template, userId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="dark:bg-background text-foreground border-border bg-widget flex flex-col rounded-xl border">
|
||||||
|
<h1 className="px-4 py-3 font-medium">
|
||||||
|
<Trans>Information</Trans>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<ul className="divide-y border-t">
|
||||||
|
{templateInformation.map((item, i) => (
|
||||||
|
<li
|
||||||
|
key={i}
|
||||||
|
className="flex items-center justify-between px-4 py-2.5 text-sm last:border-b"
|
||||||
|
>
|
||||||
|
<span className="text-muted-foreground">{_(item.description)}</span>
|
||||||
|
<span>{item.value}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import { Trans } from '@lingui/macro';
|
||||||
|
import { Loader } from 'lucide-react';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
import { DocumentSource } from '@documenso/prisma/client';
|
||||||
|
import { trpc } from '@documenso/trpc/react';
|
||||||
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
|
||||||
|
export type TemplatePageViewRecentActivityProps = {
|
||||||
|
templateId: number;
|
||||||
|
teamId?: number;
|
||||||
|
documentRootPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TemplatePageViewRecentActivity = ({
|
||||||
|
templateId,
|
||||||
|
teamId,
|
||||||
|
documentRootPath,
|
||||||
|
}: TemplatePageViewRecentActivityProps) => {
|
||||||
|
const { data, isLoading, isLoadingError, refetch } = trpc.document.findDocuments.useQuery({
|
||||||
|
templateId,
|
||||||
|
teamId,
|
||||||
|
orderBy: {
|
||||||
|
column: 'createdAt',
|
||||||
|
direction: 'asc',
|
||||||
|
},
|
||||||
|
perPage: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = data ?? {
|
||||||
|
data: [],
|
||||||
|
perPage: 10,
|
||||||
|
currentPage: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
|
||||||
|
<div className="flex flex-row items-center justify-between border-b px-4 py-3">
|
||||||
|
<h1 className="text-foreground font-medium">
|
||||||
|
<Trans>Recent documents</Trans>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Can add dropdown menu here for additional options. */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLoading && (
|
||||||
|
<div className="flex h-full items-center justify-center py-16">
|
||||||
|
<Loader className="text-muted-foreground h-6 w-6 animate-spin" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isLoadingError && (
|
||||||
|
<div className="flex h-full flex-col items-center justify-center py-16">
|
||||||
|
<p className="text-foreground/80 text-sm">
|
||||||
|
<Trans>Unable to load documents</Trans>
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={async () => refetch()}
|
||||||
|
className="text-foreground/70 hover:text-muted-foreground mt-2 text-sm"
|
||||||
|
>
|
||||||
|
<Trans>Click here to retry</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{data && (
|
||||||
|
<>
|
||||||
|
<ul role="list" className="space-y-6 p-4">
|
||||||
|
{data.data.length > 0 && results.totalPages > 1 && (
|
||||||
|
<li className="relative flex gap-x-4">
|
||||||
|
<div className="absolute -bottom-6 left-0 top-0 flex w-6 justify-center">
|
||||||
|
<div className="bg-border w-px" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-widget relative flex h-6 w-6 flex-none items-center justify-center">
|
||||||
|
<div className="bg-widget h-1.5 w-1.5 rounded-full ring-1 ring-gray-300 dark:ring-neutral-600" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: document.getElementById('documents')?.offsetTop,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="text-foreground/70 hover:text-muted-foreground flex items-center text-xs"
|
||||||
|
>
|
||||||
|
<Trans>View more</Trans>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{results.data.length === 0 && (
|
||||||
|
<div className="flex items-center justify-center py-4">
|
||||||
|
<p className="text-muted-foreground/70 text-sm">
|
||||||
|
<Trans>No recent documents</Trans>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{results.data.map((document, documentIndex) => (
|
||||||
|
<li key={document.id} className="relative flex gap-x-4">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
documentIndex === results.data.length - 1 ? 'h-6' : '-bottom-6',
|
||||||
|
'absolute left-0 top-0 flex w-6 justify-center',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="bg-border w-px" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-widget text-foreground/40 relative flex h-6 w-6 flex-none items-center justify-center">
|
||||||
|
<div className="bg-widget h-1.5 w-1.5 rounded-full ring-1 ring-gray-300 dark:ring-neutral-600" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={`${documentRootPath}/${document.id}`}
|
||||||
|
className="text-muted-foreground dark:text-muted-foreground/70 flex-auto truncate py-0.5 text-xs leading-5"
|
||||||
|
>
|
||||||
|
{match(document.source)
|
||||||
|
.with(DocumentSource.DOCUMENT, DocumentSource.TEMPLATE, () => (
|
||||||
|
<Trans>
|
||||||
|
Document created by <span className="font-bold">{document.User.name}</span>
|
||||||
|
</Trans>
|
||||||
|
))
|
||||||
|
.with(DocumentSource.TEMPLATE_DIRECT_LINK, () => (
|
||||||
|
<Trans>
|
||||||
|
Document created using a <span className="font-bold">direct link</span>
|
||||||
|
</Trans>
|
||||||
|
))
|
||||||
|
.exhaustive()}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<time className="text-muted-foreground dark:text-muted-foreground/70 flex-none py-0.5 text-xs leading-5">
|
||||||
|
{DateTime.fromJSDate(document.createdAt).toRelative({ style: 'short' })}
|
||||||
|
</time>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className="mx-4 mb-4"
|
||||||
|
onClick={() => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: document.getElementById('documents')?.offsetTop,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trans>View all related documents</Trans>
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import { Trans, msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
|
import { PenIcon, PlusIcon } from 'lucide-react';
|
||||||
|
|
||||||
|
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||||
|
import type { Recipient, Template } from '@documenso/prisma/client';
|
||||||
|
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
|
||||||
|
|
||||||
|
export type TemplatePageViewRecipientsProps = {
|
||||||
|
template: Template & {
|
||||||
|
Recipient: Recipient[];
|
||||||
|
};
|
||||||
|
templateRootPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TemplatePageViewRecipients = ({
|
||||||
|
template,
|
||||||
|
templateRootPath,
|
||||||
|
}: TemplatePageViewRecipientsProps) => {
|
||||||
|
const { _ } = useLingui();
|
||||||
|
|
||||||
|
const recipients = template.Recipient;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
|
||||||
|
<div className="flex flex-row items-center justify-between px-4 py-3">
|
||||||
|
<h1 className="text-foreground font-medium">
|
||||||
|
<Trans>Recipients</Trans>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href={`${templateRootPath}/${template.id}/edit?step=signers`}
|
||||||
|
title={_(msg`Modify recipients`)}
|
||||||
|
className="flex flex-row items-center justify-between"
|
||||||
|
>
|
||||||
|
{recipients.length === 0 ? (
|
||||||
|
<PlusIcon className="ml-2 h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<PenIcon className="ml-2 h-3 w-3" />
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="text-muted-foreground divide-y border-t">
|
||||||
|
{recipients.length === 0 && (
|
||||||
|
<li className="flex flex-col items-center justify-center py-6 text-sm">
|
||||||
|
<Trans>No recipients</Trans>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{recipients.map((recipient) => (
|
||||||
|
<li key={recipient.id} className="flex items-center justify-between px-4 py-2.5 text-sm">
|
||||||
|
<AvatarWithText
|
||||||
|
avatarFallback={recipient.email.slice(0, 1).toUpperCase()}
|
||||||
|
primaryText={<p className="text-muted-foreground text-sm">{recipient.email}</p>}
|
||||||
|
secondaryText={
|
||||||
|
<p className="text-muted-foreground/70 text-xs">
|
||||||
|
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,22 +1,28 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
import { Trans } from '@lingui/macro';
|
import { Trans } from '@lingui/macro';
|
||||||
import { ChevronLeft } from 'lucide-react';
|
import { ChevronLeft, LucideEdit } from 'lucide-react';
|
||||||
|
|
||||||
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
|
||||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
import { getTemplateWithDetailsById } from '@documenso/lib/server-only/template/get-template-with-details-by-id';
|
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
|
||||||
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams';
|
||||||
import type { Team } from '@documenso/prisma/client';
|
import { DocumentSigningOrder, SigningStatus, type Team } from '@documenso/prisma/client';
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||||
|
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
||||||
|
|
||||||
|
import { DocumentReadOnlyFields } from '~/components/document/document-read-only-fields';
|
||||||
import { TemplateType } from '~/components/formatter/template-type';
|
import { TemplateType } from '~/components/formatter/template-type';
|
||||||
|
|
||||||
|
import { DataTableActionDropdown } from '../data-table-action-dropdown';
|
||||||
import { TemplateDirectLinkBadge } from '../template-direct-link-badge';
|
import { TemplateDirectLinkBadge } from '../template-direct-link-badge';
|
||||||
import { EditTemplateForm } from './edit-template';
|
import { UseTemplateDialog } from '../use-template-dialog';
|
||||||
import { TemplateDirectLinkDialogWrapper } from './template-direct-link-dialog-wrapper';
|
import { TemplateDirectLinkDialogWrapper } from './template-direct-link-dialog-wrapper';
|
||||||
|
import { TemplatePageViewDocumentsTable } from './template-page-view-documents-table';
|
||||||
|
import { TemplatePageViewInformation } from './template-page-view-information';
|
||||||
|
import { TemplatePageViewRecentActivity } from './template-page-view-recent-activity';
|
||||||
|
import { TemplatePageViewRecipients } from './template-page-view-recipients';
|
||||||
|
|
||||||
export type TemplatePageViewProps = {
|
export type TemplatePageViewProps = {
|
||||||
params: {
|
params: {
|
||||||
@ -30,6 +36,7 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
|
|
||||||
const templateId = Number(id);
|
const templateId = Number(id);
|
||||||
const templateRootPath = formatTemplatesPath(team?.url);
|
const templateRootPath = formatTemplatesPath(team?.url);
|
||||||
|
const documentRootPath = formatDocumentsPath(team?.url);
|
||||||
|
|
||||||
if (!templateId || Number.isNaN(templateId)) {
|
if (!templateId || Number.isNaN(templateId)) {
|
||||||
redirect(templateRootPath);
|
redirect(templateRootPath);
|
||||||
@ -37,29 +44,51 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
|
|
||||||
const { user } = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
const template = await getTemplateWithDetailsById({
|
const template = await getTemplateById({
|
||||||
id: templateId,
|
id: templateId,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
teamId: team?.id,
|
||||||
}).catch(() => null);
|
}).catch(() => null);
|
||||||
|
|
||||||
if (!template || !template.templateDocumentData) {
|
if (!template || !template.templateDocumentData || (template?.teamId && !team?.url)) {
|
||||||
redirect(templateRootPath);
|
redirect(templateRootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isTemplateEnterprise = await isUserEnterprise({
|
const { templateDocumentData, Field, Recipient: recipients, templateMeta } = template;
|
||||||
userId: user.id,
|
|
||||||
teamId: team?.id,
|
// Remap to fit the DocumentReadOnlyFields component.
|
||||||
|
const readOnlyFields = Field.map((field) => {
|
||||||
|
const recipient = recipients.find((recipient) => recipient.id === field.recipientId) || {
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
signingStatus: SigningStatus.NOT_SIGNED,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
Recipient: recipient,
|
||||||
|
Signature: null,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockedDocumentMeta = templateMeta
|
||||||
|
? {
|
||||||
|
typedSignatureEnabled: false,
|
||||||
|
...templateMeta,
|
||||||
|
signingOrder: templateMeta.signingOrder || DocumentSigningOrder.SEQUENTIAL,
|
||||||
|
documentId: 0,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto -mt-4 max-w-screen-xl px-4 md:px-8">
|
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
||||||
<div className="flex flex-col justify-between sm:flex-row">
|
<Link href={templateRootPath} className="flex items-center text-[#7AC455] hover:opacity-80">
|
||||||
<div>
|
|
||||||
<Link href="/templates" className="flex items-center text-[#7AC455] hover:opacity-80">
|
|
||||||
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
||||||
<Trans>Templates</Trans>
|
<Trans>Templates</Trans>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-between truncate">
|
||||||
|
<div>
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
||||||
{template.title}
|
{template.title}
|
||||||
</h1>
|
</h1>
|
||||||
@ -77,17 +106,97 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 sm:mt-0 sm:self-end">
|
<div className="mt-2 flex flex-row space-x-4 sm:mt-0 sm:self-end">
|
||||||
<TemplateDirectLinkDialogWrapper template={template} />
|
<TemplateDirectLinkDialogWrapper template={template} />
|
||||||
|
|
||||||
|
<Button className="w-full" asChild>
|
||||||
|
<Link href={`${templateRootPath}/${template.id}/edit`}>
|
||||||
|
<LucideEdit className="mr-1.5 h-3.5 w-3.5" />
|
||||||
|
<Trans>Edit Template</Trans>
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EditTemplateForm
|
<div className="mt-6 grid w-full grid-cols-12 gap-8">
|
||||||
className="mt-6"
|
<Card
|
||||||
initialTemplate={template}
|
className="relative col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7"
|
||||||
templateRootPath={templateRootPath}
|
gradient
|
||||||
isEnterprise={isTemplateEnterprise}
|
>
|
||||||
|
<CardContent className="p-2">
|
||||||
|
<LazyPDFViewer
|
||||||
|
document={template}
|
||||||
|
key={template.id}
|
||||||
|
documentData={templateDocumentData}
|
||||||
/>
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<DocumentReadOnlyFields
|
||||||
|
fields={readOnlyFields}
|
||||||
|
showFieldStatus={false}
|
||||||
|
documentMeta={mockedDocumentMeta}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="col-span-12 lg:col-span-6 xl:col-span-5">
|
||||||
|
<div className="space-y-6">
|
||||||
|
<section className="border-border bg-widget flex flex-col rounded-xl border pb-4 pt-6">
|
||||||
|
<div className="flex flex-row items-center justify-between px-4">
|
||||||
|
<h3 className="text-foreground text-2xl font-semibold">
|
||||||
|
<Trans>Template</Trans>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<DataTableActionDropdown
|
||||||
|
row={template}
|
||||||
|
teamId={team?.id}
|
||||||
|
templateRootPath={templateRootPath}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-muted-foreground mt-2 px-4 text-sm ">
|
||||||
|
<Trans>Manage and view template</Trans>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-4 border-t px-4 pt-4">
|
||||||
|
<UseTemplateDialog
|
||||||
|
templateId={template.id}
|
||||||
|
templateSigningOrder={template.templateMeta?.signingOrder}
|
||||||
|
recipients={template.Recipient}
|
||||||
|
documentRootPath={documentRootPath}
|
||||||
|
trigger={
|
||||||
|
<Button className="w-full">
|
||||||
|
<Trans>Use</Trans>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Template information section. */}
|
||||||
|
<TemplatePageViewInformation template={template} userId={user.id} />
|
||||||
|
|
||||||
|
{/* Recipients section. */}
|
||||||
|
<TemplatePageViewRecipients template={template} templateRootPath={templateRootPath} />
|
||||||
|
|
||||||
|
{/* Recent activity section. */}
|
||||||
|
<TemplatePageViewRecentActivity
|
||||||
|
documentRootPath={documentRootPath}
|
||||||
|
templateId={template.id}
|
||||||
|
teamId={team?.id}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-16" id="documents">
|
||||||
|
<h1 className="mb-4 text-2xl font-bold">
|
||||||
|
<Trans>Documents created from template</Trans>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<TemplatePageViewDocumentsTable team={team} templateId={template.id} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { Trans } from '@lingui/macro';
|
|||||||
import { Copy, Edit, MoreHorizontal, MoveRight, Share2Icon, Trash2 } from 'lucide-react';
|
import { Copy, Edit, MoreHorizontal, MoveRight, Share2Icon, Trash2 } from 'lucide-react';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
|
|
||||||
import { type FindTemplateRow } from '@documenso/lib/server-only/template/find-templates';
|
import type { Recipient, Template, TemplateDirectLink } from '@documenso/prisma/client';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@ -23,7 +23,10 @@ import { MoveTemplateDialog } from './move-template-dialog';
|
|||||||
import { TemplateDirectLinkDialog } from './template-direct-link-dialog';
|
import { TemplateDirectLinkDialog } from './template-direct-link-dialog';
|
||||||
|
|
||||||
export type DataTableActionDropdownProps = {
|
export type DataTableActionDropdownProps = {
|
||||||
row: FindTemplateRow;
|
row: Template & {
|
||||||
|
directLink?: Pick<TemplateDirectLink, 'token' | 'enabled'> | null;
|
||||||
|
Recipient: Recipient[];
|
||||||
|
};
|
||||||
templateRootPath: string;
|
templateRootPath: string;
|
||||||
teamId?: number;
|
teamId?: number;
|
||||||
};
|
};
|
||||||
@ -57,7 +60,7 @@ export const DataTableActionDropdown = ({
|
|||||||
<DropdownMenuLabel>Action</DropdownMenuLabel>
|
<DropdownMenuLabel>Action</DropdownMenuLabel>
|
||||||
|
|
||||||
<DropdownMenuItem disabled={!isOwner && !isTeamTemplate} asChild>
|
<DropdownMenuItem disabled={!isOwner && !isTeamTemplate} asChild>
|
||||||
<Link href={`${templateRootPath}/${row.id}`}>
|
<Link href={`${templateRootPath}/${row.id}/edit`}>
|
||||||
<Edit className="mr-2 h-4 w-4" />
|
<Edit className="mr-2 h-4 w-4" />
|
||||||
<Trans>Edit</Trans>
|
<Trans>Edit</Trans>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -124,7 +124,7 @@ export const TemplatesDataTable = ({
|
|||||||
accessorKey: 'type',
|
accessorKey: 'type',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<TemplateType type="PRIVATE" />
|
<TemplateType type={row.original.type} />
|
||||||
|
|
||||||
{row.original.directLink?.token && (
|
{row.original.directLink?.token && (
|
||||||
<TemplateDirectLinkBadge
|
<TemplateDirectLinkBadge
|
||||||
|
|||||||
@ -73,7 +73,7 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
|||||||
|
|
||||||
setShowNewTemplateDialog(false);
|
setShowNewTemplateDialog(false);
|
||||||
|
|
||||||
router.push(`${templateRootPath}/${id}`);
|
router.push(`${templateRootPath}/${id}/edit`);
|
||||||
} catch {
|
} catch {
|
||||||
toast({
|
toast({
|
||||||
title: _(msg`Something went wrong`),
|
title: _(msg`Something went wrong`),
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@ -92,6 +94,7 @@ export type UseTemplateDialogProps = {
|
|||||||
templateSigningOrder?: DocumentSigningOrder | null;
|
templateSigningOrder?: DocumentSigningOrder | null;
|
||||||
recipients: Recipient[];
|
recipients: Recipient[];
|
||||||
documentRootPath: string;
|
documentRootPath: string;
|
||||||
|
trigger?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function UseTemplateDialog({
|
export function UseTemplateDialog({
|
||||||
@ -99,6 +102,7 @@ export function UseTemplateDialog({
|
|||||||
documentRootPath,
|
documentRootPath,
|
||||||
templateId,
|
templateId,
|
||||||
templateSigningOrder,
|
templateSigningOrder,
|
||||||
|
trigger,
|
||||||
}: UseTemplateDialogProps) {
|
}: UseTemplateDialogProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -186,10 +190,12 @@ export function UseTemplateDialog({
|
|||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
<Dialog open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
|
{trigger || (
|
||||||
<Button variant="outline" className="bg-background">
|
<Button variant="outline" className="bg-background">
|
||||||
<Plus className="-ml-1 mr-2 h-4 w-4" />
|
<Plus className="-ml-1 mr-2 h-4 w-4" />
|
||||||
<Trans>Use Template</Trans>
|
<Trans>Use Template</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-lg">
|
<DialogContent className="sm:max-w-lg">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
'use client';
|
import { msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import type { DateTimeFormatOptions } from 'luxon';
|
import type { DateTimeFormatOptions } from 'luxon';
|
||||||
import { UAParser } from 'ua-parser-js';
|
import { UAParser } from 'ua-parser-js';
|
||||||
@ -25,7 +25,12 @@ const dateFormat: DateTimeFormatOptions = {
|
|||||||
hourCycle: 'h12',
|
hourCycle: 'h12',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT USE TRANS. YOU MUST USE _ FOR THIS FILE AND ALL CHILDREN COMPONENTS.
|
||||||
|
*/
|
||||||
export const AuditLogDataTable = ({ logs }: AuditLogDataTableProps) => {
|
export const AuditLogDataTable = ({ logs }: AuditLogDataTableProps) => {
|
||||||
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const parser = new UAParser();
|
const parser = new UAParser();
|
||||||
|
|
||||||
const uppercaseFistLetter = (text: string) => {
|
const uppercaseFistLetter = (text: string) => {
|
||||||
@ -36,11 +41,11 @@ export const AuditLogDataTable = ({ logs }: AuditLogDataTableProps) => {
|
|||||||
<Table overflowHidden>
|
<Table overflowHidden>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Time</TableHead>
|
<TableHead>{_(msg`Time`)}</TableHead>
|
||||||
<TableHead>User</TableHead>
|
<TableHead>{_(msg`User`)}</TableHead>
|
||||||
<TableHead>Action</TableHead>
|
<TableHead>{_(msg`Action`)}</TableHead>
|
||||||
<TableHead>IP Address</TableHead>
|
<TableHead>{_(msg`IP Address`)}</TableHead>
|
||||||
<TableHead>Browser</TableHead>
|
<TableHead>{_(msg`Browser`)}</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
|
|
||||||
@ -74,7 +79,7 @@ export const AuditLogDataTable = ({ logs }: AuditLogDataTableProps) => {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{uppercaseFistLetter(formatDocumentAuditLogAction(log).description)}
|
{uppercaseFistLetter(formatDocumentAuditLogAction(_, log).description)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>{log.ipAddress}</TableCell>
|
<TableCell>{log.ipAddress}</TableCell>
|
||||||
|
|||||||
@ -2,13 +2,18 @@ import React from 'react';
|
|||||||
|
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
|
import { msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
import { APP_I18N_OPTIONS } from '@documenso/lib/constants/i18n';
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
import { RECIPIENT_ROLES_DESCRIPTION_ENG } from '@documenso/lib/constants/recipient-roles';
|
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 { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||||
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
||||||
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
|
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
|
||||||
|
import { dynamicActivate } from '@documenso/lib/utils/i18n';
|
||||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||||
|
|
||||||
import { Logo } from '~/components/branding/logo';
|
import { Logo } from '~/components/branding/logo';
|
||||||
@ -21,7 +26,17 @@ type AuditLogProps = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT USE TRANS. YOU MUST USE _ FOR THIS FILE AND ALL CHILDREN COMPONENTS.
|
||||||
|
*
|
||||||
|
* Cannot use dynamicActivate by itself to translate this specific page and all
|
||||||
|
* children components because `not-found.tsx` page runs and overrides the i18n.
|
||||||
|
*/
|
||||||
export default async function AuditLog({ searchParams }: AuditLogProps) {
|
export default async function AuditLog({ searchParams }: AuditLogProps) {
|
||||||
|
const { i18n } = await setupI18nSSR();
|
||||||
|
|
||||||
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const { d } = searchParams;
|
const { d } = searchParams;
|
||||||
|
|
||||||
if (typeof d !== 'string' || !d) {
|
if (typeof d !== 'string' || !d) {
|
||||||
@ -44,6 +59,10 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
return redirect('/');
|
return redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language);
|
||||||
|
|
||||||
|
await dynamicActivate(i18n, documentLanguage);
|
||||||
|
|
||||||
const { data: auditLogs } = await findDocumentAuditLogs({
|
const { data: auditLogs } = await findDocumentAuditLogs({
|
||||||
documentId: documentId,
|
documentId: documentId,
|
||||||
userId: document.userId,
|
userId: document.userId,
|
||||||
@ -53,31 +72,35 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="print-provider pointer-events-none mx-auto max-w-screen-md">
|
<div className="print-provider pointer-events-none mx-auto max-w-screen-md">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<h1 className="my-8 text-2xl font-bold">Version History</h1>
|
<h1 className="my-8 text-2xl font-bold">{_(msg`Version History`)}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="grid grid-cols-2 gap-4 p-6 text-sm print:text-xs">
|
<CardContent className="grid grid-cols-2 gap-4 p-6 text-sm print:text-xs">
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Document ID</span>
|
<span className="font-medium">{_(msg`Document ID`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block break-words">{document.id}</span>
|
<span className="mt-1 block break-words">{document.id}</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Enclosed Document</span>
|
<span className="font-medium">{_(msg`Enclosed Document`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block break-words">{document.title}</span>
|
<span className="mt-1 block break-words">{document.title}</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Status</span>
|
<span className="font-medium">{_(msg`Status`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block">{document.deletedAt ? 'DELETED' : document.status}</span>
|
<span className="mt-1 block">
|
||||||
|
{_(
|
||||||
|
document.deletedAt ? msg`Deleted` : DOCUMENT_STATUS[document.status].description,
|
||||||
|
).toUpperCase()}
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Owner</span>
|
<span className="font-medium">{_(msg`Owner`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block break-words">
|
<span className="mt-1 block break-words">
|
||||||
{document.User.name} ({document.User.email})
|
{document.User.name} ({document.User.email})
|
||||||
@ -85,7 +108,7 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Created At</span>
|
<span className="font-medium">{_(msg`Created At`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block">
|
<span className="mt-1 block">
|
||||||
{DateTime.fromJSDate(document.createdAt)
|
{DateTime.fromJSDate(document.createdAt)
|
||||||
@ -95,7 +118,7 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Last Updated</span>
|
<span className="font-medium">{_(msg`Last Updated`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block">
|
<span className="mt-1 block">
|
||||||
{DateTime.fromJSDate(document.updatedAt)
|
{DateTime.fromJSDate(document.updatedAt)
|
||||||
@ -105,7 +128,7 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">Time Zone</span>
|
<span className="font-medium">{_(msg`Time Zone`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block break-words">
|
<span className="mt-1 block break-words">
|
||||||
{document.documentMeta?.timezone ?? 'N/A'}
|
{document.documentMeta?.timezone ?? 'N/A'}
|
||||||
@ -113,13 +136,13 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium">Recipients</p>
|
<p className="font-medium">{_(msg`Recipients`)}</p>
|
||||||
|
|
||||||
<ul className="mt-1 list-inside list-disc">
|
<ul className="mt-1 list-inside list-disc">
|
||||||
{document.Recipient.map((recipient) => (
|
{document.Recipient.map((recipient) => (
|
||||||
<li key={recipient.id}>
|
<li key={recipient.id}>
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
[{RECIPIENT_ROLES_DESCRIPTION_ENG[recipient.role].roleName}]
|
[{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}]
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
{recipient.name} ({recipient.email})
|
{recipient.name} ({recipient.email})
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -2,20 +2,24 @@ import React from 'react';
|
|||||||
|
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
|
import { msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
import { UAParser } from 'ua-parser-js';
|
import { UAParser } from 'ua-parser-js';
|
||||||
|
|
||||||
import { APP_I18N_OPTIONS } from '@documenso/lib/constants/i18n';
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
|
import { APP_I18N_OPTIONS, ZSupportedLanguageCodeSchema } from '@documenso/lib/constants/i18n';
|
||||||
import {
|
import {
|
||||||
RECIPIENT_ROLES_DESCRIPTION_ENG,
|
RECIPIENT_ROLES_DESCRIPTION,
|
||||||
RECIPIENT_ROLE_SIGNING_REASONS_ENG,
|
RECIPIENT_ROLE_SIGNING_REASONS,
|
||||||
} from '@documenso/lib/constants/recipient-roles';
|
} from '@documenso/lib/constants/recipient-roles';
|
||||||
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
|
||||||
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
||||||
import { getDocumentCertificateAuditLogs } from '@documenso/lib/server-only/document/get-document-certificate-audit-logs';
|
import { getDocumentCertificateAuditLogs } from '@documenso/lib/server-only/document/get-document-certificate-audit-logs';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
||||||
|
import { dynamicActivate } from '@documenso/lib/utils/i18n';
|
||||||
import { FieldType } from '@documenso/prisma/client';
|
import { FieldType } from '@documenso/prisma/client';
|
||||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||||
import {
|
import {
|
||||||
@ -36,11 +40,21 @@ type SigningCertificateProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FRIENDLY_SIGNING_REASONS = {
|
const FRIENDLY_SIGNING_REASONS = {
|
||||||
['__OWNER__']: `I am the owner of this document`,
|
['__OWNER__']: msg`I am the owner of this document`,
|
||||||
...RECIPIENT_ROLE_SIGNING_REASONS_ENG,
|
...RECIPIENT_ROLE_SIGNING_REASONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT USE TRANS. YOU MUST USE _ FOR THIS FILE AND ALL CHILDREN COMPONENTS.
|
||||||
|
*
|
||||||
|
* Cannot use dynamicActivate by itself to translate this specific page and all
|
||||||
|
* children components because `not-found.tsx` page runs and overrides the i18n.
|
||||||
|
*/
|
||||||
export default async function SigningCertificate({ searchParams }: SigningCertificateProps) {
|
export default async function SigningCertificate({ searchParams }: SigningCertificateProps) {
|
||||||
|
const { i18n } = await setupI18nSSR();
|
||||||
|
|
||||||
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const { d } = searchParams;
|
const { d } = searchParams;
|
||||||
|
|
||||||
if (typeof d !== 'string' || !d) {
|
if (typeof d !== 'string' || !d) {
|
||||||
@ -63,6 +77,10 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
return redirect('/');
|
return redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language);
|
||||||
|
|
||||||
|
await dynamicActivate(i18n, documentLanguage);
|
||||||
|
|
||||||
const auditLogs = await getDocumentCertificateAuditLogs({
|
const auditLogs = await getDocumentCertificateAuditLogs({
|
||||||
id: documentId,
|
id: documentId,
|
||||||
});
|
});
|
||||||
@ -98,17 +116,17 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
});
|
});
|
||||||
|
|
||||||
let authLevel = match(extractedAuthMethods.derivedRecipientActionAuth)
|
let authLevel = match(extractedAuthMethods.derivedRecipientActionAuth)
|
||||||
.with('ACCOUNT', () => 'Account Re-Authentication')
|
.with('ACCOUNT', () => _(msg`Account Re-Authentication`))
|
||||||
.with('TWO_FACTOR_AUTH', () => 'Two-Factor Re-Authentication')
|
.with('TWO_FACTOR_AUTH', () => _(msg`Two-Factor Re-Authentication`))
|
||||||
.with('PASSKEY', () => 'Passkey Re-Authentication')
|
.with('PASSKEY', () => _(msg`Passkey Re-Authentication`))
|
||||||
.with('EXPLICIT_NONE', () => 'Email')
|
.with('EXPLICIT_NONE', () => _(msg`Email`))
|
||||||
.with(null, () => null)
|
.with(null, () => null)
|
||||||
.exhaustive();
|
.exhaustive();
|
||||||
|
|
||||||
if (!authLevel) {
|
if (!authLevel) {
|
||||||
authLevel = match(extractedAuthMethods.derivedRecipientAccessAuth)
|
authLevel = match(extractedAuthMethods.derivedRecipientAccessAuth)
|
||||||
.with('ACCOUNT', () => 'Account Authentication')
|
.with('ACCOUNT', () => _(msg`Account Authentication`))
|
||||||
.with(null, () => 'Email')
|
.with(null, () => _(msg`Email`))
|
||||||
.exhaustive();
|
.exhaustive();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +165,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
return (
|
return (
|
||||||
<div className="print-provider pointer-events-none mx-auto max-w-screen-md">
|
<div className="print-provider pointer-events-none mx-auto max-w-screen-md">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<h1 className="my-8 text-2xl font-bold">Signing Certificate</h1>
|
<h1 className="my-8 text-2xl font-bold">{_(msg`Signing Certificate`)}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@ -155,9 +173,9 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
<Table overflowHidden>
|
<Table overflowHidden>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Signer Events</TableHead>
|
<TableHead>{_(msg`Signer Events`)}</TableHead>
|
||||||
<TableHead>Signature</TableHead>
|
<TableHead>{_(msg`Signature`)}</TableHead>
|
||||||
<TableHead>Details</TableHead>
|
<TableHead>{_(msg`Details`)}</TableHead>
|
||||||
{/* <TableHead>Security</TableHead> */}
|
{/* <TableHead>Security</TableHead> */}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@ -173,11 +191,11 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
<div className="hyphens-auto break-words font-medium">{recipient.name}</div>
|
<div className="hyphens-auto break-words font-medium">{recipient.name}</div>
|
||||||
<div className="break-all">{recipient.email}</div>
|
<div className="break-all">{recipient.email}</div>
|
||||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||||
{RECIPIENT_ROLES_DESCRIPTION_ENG[recipient.role].roleName}
|
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||||
<span className="font-medium">Authentication Level:</span>{' '}
|
<span className="font-medium">{_(msg`Authentication Level`)}:</span>{' '}
|
||||||
<span className="block">{getAuthenticationLevel(recipient.id)}</span>
|
<span className="block">{getAuthenticationLevel(recipient.id)}</span>
|
||||||
</p>
|
</p>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@ -199,21 +217,21 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||||
<span className="font-medium">Signature ID:</span>{' '}
|
<span className="font-medium">{_(msg`Signature ID`)}:</span>{' '}
|
||||||
<span className="block font-mono uppercase">
|
<span className="block font-mono uppercase">
|
||||||
{signature.secondaryId}
|
{signature.secondaryId}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||||
<span className="font-medium">IP Address:</span>{' '}
|
<span className="font-medium">{_(msg`IP Address`)}:</span>{' '}
|
||||||
<span className="inline-block">
|
<span className="inline-block">
|
||||||
{logs.DOCUMENT_RECIPIENT_COMPLETED[0]?.ipAddress ?? 'Unknown'}
|
{logs.DOCUMENT_RECIPIENT_COMPLETED[0]?.ipAddress ?? _(msg`Unknown`)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-1 text-sm print:text-xs">
|
<p className="text-muted-foreground mt-1 text-sm print:text-xs">
|
||||||
<span className="font-medium">Device:</span>{' '}
|
<span className="font-medium">{_(msg`Device`)}:</span>{' '}
|
||||||
<span className="inline-block">
|
<span className="inline-block">
|
||||||
{getDevice(logs.DOCUMENT_RECIPIENT_COMPLETED[0]?.userAgent)}
|
{getDevice(logs.DOCUMENT_RECIPIENT_COMPLETED[0]?.userAgent)}
|
||||||
</span>
|
</span>
|
||||||
@ -227,44 +245,46 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
<TableCell truncate={false} className="w-[min-content] align-top">
|
<TableCell truncate={false} className="w-[min-content] align-top">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-muted-foreground text-sm print:text-xs">
|
<p className="text-muted-foreground text-sm print:text-xs">
|
||||||
<span className="font-medium">Sent:</span>{' '}
|
<span className="font-medium">{_(msg`Sent`)}:</span>{' '}
|
||||||
<span className="inline-block">
|
<span className="inline-block">
|
||||||
{logs.EMAIL_SENT[0]
|
{logs.EMAIL_SENT[0]
|
||||||
? DateTime.fromJSDate(logs.EMAIL_SENT[0].createdAt)
|
? DateTime.fromJSDate(logs.EMAIL_SENT[0].createdAt)
|
||||||
.setLocale(APP_I18N_OPTIONS.defaultLocale)
|
.setLocale(APP_I18N_OPTIONS.defaultLocale)
|
||||||
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
|
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
|
||||||
: 'Unknown'}
|
: _(msg`Unknown`)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-muted-foreground text-sm print:text-xs">
|
<p className="text-muted-foreground text-sm print:text-xs">
|
||||||
<span className="font-medium">Viewed:</span>{' '}
|
<span className="font-medium">{_(msg`Viewed`)}:</span>{' '}
|
||||||
<span className="inline-block">
|
<span className="inline-block">
|
||||||
{logs.DOCUMENT_OPENED[0]
|
{logs.DOCUMENT_OPENED[0]
|
||||||
? DateTime.fromJSDate(logs.DOCUMENT_OPENED[0].createdAt)
|
? DateTime.fromJSDate(logs.DOCUMENT_OPENED[0].createdAt)
|
||||||
.setLocale(APP_I18N_OPTIONS.defaultLocale)
|
.setLocale(APP_I18N_OPTIONS.defaultLocale)
|
||||||
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
|
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
|
||||||
: 'Unknown'}
|
: _(msg`Unknown`)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-muted-foreground text-sm print:text-xs">
|
<p className="text-muted-foreground text-sm print:text-xs">
|
||||||
<span className="font-medium">Signed:</span>{' '}
|
<span className="font-medium">{_(msg`Signed`)}:</span>{' '}
|
||||||
<span className="inline-block">
|
<span className="inline-block">
|
||||||
{logs.DOCUMENT_RECIPIENT_COMPLETED[0]
|
{logs.DOCUMENT_RECIPIENT_COMPLETED[0]
|
||||||
? DateTime.fromJSDate(logs.DOCUMENT_RECIPIENT_COMPLETED[0].createdAt)
|
? DateTime.fromJSDate(logs.DOCUMENT_RECIPIENT_COMPLETED[0].createdAt)
|
||||||
.setLocale(APP_I18N_OPTIONS.defaultLocale)
|
.setLocale(APP_I18N_OPTIONS.defaultLocale)
|
||||||
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
|
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
|
||||||
: 'Unknown'}
|
: _(msg`Unknown`)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-muted-foreground text-sm print:text-xs">
|
<p className="text-muted-foreground text-sm print:text-xs">
|
||||||
<span className="font-medium">Reason:</span>{' '}
|
<span className="font-medium">{_(msg`Reason`)}:</span>{' '}
|
||||||
<span className="inline-block">
|
<span className="inline-block">
|
||||||
{isOwner(recipient.email)
|
{_(
|
||||||
|
isOwner(recipient.email)
|
||||||
? FRIENDLY_SIGNING_REASONS['__OWNER__']
|
? FRIENDLY_SIGNING_REASONS['__OWNER__']
|
||||||
: FRIENDLY_SIGNING_REASONS[recipient.role]}
|
: FRIENDLY_SIGNING_REASONS[recipient.role],
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -280,7 +300,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
<div className="my-8 flex-row-reverse">
|
<div className="my-8 flex-row-reverse">
|
||||||
<div className="flex items-end justify-end gap-x-4">
|
<div className="flex items-end justify-end gap-x-4">
|
||||||
<p className="flex-shrink-0 text-sm font-medium print:text-xs">
|
<p className="flex-shrink-0 text-sm font-medium print:text-xs">
|
||||||
Signing certificate provided by:
|
{_(msg`Signing certificate provided by`)}:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Logo className="max-h-6 print:max-h-4" />
|
<Logo className="max-h-6 print:max-h-4" />
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||||
|
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
|
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||||
|
|
||||||
|
import type { TemplateEditPageViewProps } from '~/app/(dashboard)/templates/[id]/edit/template-edit-page-view';
|
||||||
|
import { TemplateEditPageView } from '~/app/(dashboard)/templates/[id]/edit/template-edit-page-view';
|
||||||
|
|
||||||
|
export type TeamsTemplateEditPageProps = {
|
||||||
|
params: TemplateEditPageViewProps['params'] & {
|
||||||
|
teamUrl: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function TeamsTemplateEditPage({ params }: TeamsTemplateEditPageProps) {
|
||||||
|
await setupI18nSSR();
|
||||||
|
|
||||||
|
const { teamUrl } = params;
|
||||||
|
|
||||||
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
|
const team = await getTeamByUrl({ userId: user.id, teamUrl });
|
||||||
|
|
||||||
|
return <TemplateEditPageView params={params} team={team} />;
|
||||||
|
}
|
||||||
@ -12,7 +12,7 @@ import { UAParser } from 'ua-parser-js';
|
|||||||
import { DOCUMENT_AUDIT_LOG_EMAIL_FORMAT } from '@documenso/lib/constants/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_EMAIL_FORMAT } from '@documenso/lib/constants/document-audit-logs';
|
||||||
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
|
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import { formatDocumentAuditLogActionString } from '@documenso/lib/utils/document-audit-logs';
|
import { formatDocumentAuditLogAction } from '@documenso/lib/utils/document-audit-logs';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
||||||
@ -37,7 +37,7 @@ export const DocumentHistorySheet = ({
|
|||||||
onMenuOpenChange,
|
onMenuOpenChange,
|
||||||
children,
|
children,
|
||||||
}: DocumentHistorySheetProps) => {
|
}: DocumentHistorySheetProps) => {
|
||||||
const { i18n } = useLingui();
|
const { _, i18n } = useLingui();
|
||||||
|
|
||||||
const [isUserDetailsVisible, setIsUserDetailsVisible] = useState(false);
|
const [isUserDetailsVisible, setIsUserDetailsVisible] = useState(false);
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ export const DocumentHistorySheet = ({
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-foreground text-xs font-bold">
|
<p className="text-foreground text-xs font-bold">
|
||||||
{formatDocumentAuditLogActionString(auditLog, userId)}
|
{formatDocumentAuditLogAction(_, auditLog, userId).description}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-foreground/50 text-xs">
|
<p className="text-foreground/50 text-xs">
|
||||||
{DateTime.fromJSDate(auditLog.createdAt)
|
{DateTime.fromJSDate(auditLog.createdAt)
|
||||||
|
|||||||
@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { Trans } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { EyeOffIcon } from 'lucide-react';
|
import { Clock, EyeOffIcon } from 'lucide-react';
|
||||||
import { P, match } from 'ts-pattern';
|
import { P, match } from 'ts-pattern';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -18,8 +19,10 @@ import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
|
|||||||
import type { DocumentMeta } from '@documenso/prisma/client';
|
import type { DocumentMeta } from '@documenso/prisma/client';
|
||||||
import { FieldType, SigningStatus } from '@documenso/prisma/client';
|
import { FieldType, SigningStatus } from '@documenso/prisma/client';
|
||||||
import { FieldRootContainer } from '@documenso/ui/components/field/field';
|
import { FieldRootContainer } from '@documenso/ui/components/field/field';
|
||||||
|
import { SignatureIcon } from '@documenso/ui/icons/signature';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
||||||
|
import { Badge } from '@documenso/ui/primitives/badge';
|
||||||
import { FRIENDLY_FIELD_TYPE } from '@documenso/ui/primitives/document-flow/types';
|
import { FRIENDLY_FIELD_TYPE } from '@documenso/ui/primitives/document-flow/types';
|
||||||
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
|
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
|
||||||
import { PopoverHover } from '@documenso/ui/primitives/popover';
|
import { PopoverHover } from '@documenso/ui/primitives/popover';
|
||||||
@ -27,9 +30,14 @@ import { PopoverHover } from '@documenso/ui/primitives/popover';
|
|||||||
export type DocumentReadOnlyFieldsProps = {
|
export type DocumentReadOnlyFieldsProps = {
|
||||||
fields: DocumentField[];
|
fields: DocumentField[];
|
||||||
documentMeta?: DocumentMeta;
|
documentMeta?: DocumentMeta;
|
||||||
|
showFieldStatus?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DocumentReadOnlyFields = ({ documentMeta, fields }: DocumentReadOnlyFieldsProps) => {
|
export const DocumentReadOnlyFields = ({
|
||||||
|
documentMeta,
|
||||||
|
fields,
|
||||||
|
showFieldStatus = true,
|
||||||
|
}: DocumentReadOnlyFieldsProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const [hiddenFieldIds, setHiddenFieldIds] = useState<Record<string, boolean>>({});
|
const [hiddenFieldIds, setHiddenFieldIds] = useState<Record<string, boolean>>({});
|
||||||
@ -58,15 +66,37 @@ export const DocumentReadOnlyFields = ({ documentMeta, fields }: DocumentReadOnl
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
}
|
}
|
||||||
contentProps={{
|
contentProps={{
|
||||||
className: 'relative flex w-fit flex-col p-2.5 text-sm',
|
className: 'relative flex w-fit flex-col p-4 text-sm',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p className="font-semibold">
|
{showFieldStatus && (
|
||||||
{field.Recipient.signingStatus === SigningStatus.SIGNED ? 'Signed' : 'Pending'}{' '}
|
<Badge
|
||||||
{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type]).toLowerCase()} field
|
className="mx-auto mb-1 py-0.5"
|
||||||
|
variant={
|
||||||
|
field.Recipient.signingStatus === SigningStatus.SIGNED
|
||||||
|
? 'default'
|
||||||
|
: 'secondary'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{field.Recipient.signingStatus === SigningStatus.SIGNED ? (
|
||||||
|
<>
|
||||||
|
<SignatureIcon className="mr-1 h-3 w-3" />
|
||||||
|
<Trans>Signed</Trans>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Clock className="mr-1 h-3 w-3" />
|
||||||
|
<Trans>Pending</Trans>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p className="text-center font-semibold">
|
||||||
|
<span>{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type])} field</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-muted-foreground text-xs">
|
<p className="text-muted-foreground mt-1 text-center text-xs">
|
||||||
{field.Recipient.name
|
{field.Recipient.name
|
||||||
? `${field.Recipient.name} (${field.Recipient.email})`
|
? `${field.Recipient.name} (${field.Recipient.email})`
|
||||||
: field.Recipient.email}{' '}
|
: field.Recipient.email}{' '}
|
||||||
|
|||||||
@ -0,0 +1,151 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
|
import { Trans, msg } from '@lingui/macro';
|
||||||
|
import { useLingui } from '@lingui/react';
|
||||||
|
|
||||||
|
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||||
|
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||||
|
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||||
|
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||||
|
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||||
|
import type { Recipient } from '@documenso/prisma/client';
|
||||||
|
import { RecipientRole } from '@documenso/prisma/client';
|
||||||
|
import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button';
|
||||||
|
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from '@documenso/ui/primitives/dialog';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
export type DocumentRecipientLinkCopyDialogProps = {
|
||||||
|
trigger?: React.ReactNode;
|
||||||
|
recipients: Recipient[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DocumentRecipientLinkCopyDialog = ({
|
||||||
|
trigger,
|
||||||
|
recipients,
|
||||||
|
}: DocumentRecipientLinkCopyDialogProps) => {
|
||||||
|
const { _ } = useLingui();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [, copy] = useCopyToClipboard();
|
||||||
|
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const updateSearchParams = useUpdateSearchParams();
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const actionSearchParam = searchParams?.get('action');
|
||||||
|
|
||||||
|
const onBulkCopy = async () => {
|
||||||
|
const generatedString = recipients
|
||||||
|
.filter((recipient) => recipient.role !== RecipientRole.CC)
|
||||||
|
.map((recipient) => `${recipient.email}\n${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`)
|
||||||
|
.join('\n\n');
|
||||||
|
|
||||||
|
await copy(generatedString).then(() => {
|
||||||
|
toast({
|
||||||
|
title: _(msg`Copied to clipboard`),
|
||||||
|
description: _(msg`All signing links have been copied to your clipboard.`),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (actionSearchParam === 'view-signing-links') {
|
||||||
|
setOpen(true);
|
||||||
|
updateSearchParams({ action: null });
|
||||||
|
}
|
||||||
|
}, [actionSearchParam, open, setOpen, updateSearchParams]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={(value) => setOpen(value)}>
|
||||||
|
<DialogTrigger asChild onClick={(e) => e.stopPropagation()}>
|
||||||
|
{trigger}
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogContent position="center">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="pb-0.5">
|
||||||
|
<Trans>Copy Signing Links</Trans>
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<DialogDescription>
|
||||||
|
<Trans>
|
||||||
|
You can copy and share these links to recipients so they can action the document.
|
||||||
|
</Trans>
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<ul className="text-muted-foreground divide-y rounded-lg border">
|
||||||
|
{recipients.length === 0 && (
|
||||||
|
<li className="flex flex-col items-center justify-center py-6 text-sm">
|
||||||
|
<Trans>No recipients</Trans>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{recipients.map((recipient) => (
|
||||||
|
<li key={recipient.id} className="flex items-center justify-between px-4 py-3 text-sm">
|
||||||
|
<AvatarWithText
|
||||||
|
avatarFallback={recipient.email.slice(0, 1).toUpperCase()}
|
||||||
|
primaryText={<p className="text-muted-foreground text-sm">{recipient.email}</p>}
|
||||||
|
secondaryText={
|
||||||
|
<p className="text-muted-foreground/70 text-xs">
|
||||||
|
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{recipient.role !== RecipientRole.CC && (
|
||||||
|
<CopyTextButton
|
||||||
|
value={formatSigningLink(recipient.token)}
|
||||||
|
onCopySuccess={() => {
|
||||||
|
toast({
|
||||||
|
title: _(msg`Copied to clipboard`),
|
||||||
|
description: _(msg`The signing link has been copied to your clipboard.`),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
badgeContentUncopied={
|
||||||
|
<p className="ml-1 text-xs">
|
||||||
|
<Trans>Copy</Trans>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
badgeContentCopied={
|
||||||
|
<p className="ml-1 text-xs">
|
||||||
|
<Trans>Copied</Trans>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button type="button" variant="secondary">
|
||||||
|
<Trans>Close</Trans>
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
|
||||||
|
<Button type="button" onClick={onBulkCopy}>
|
||||||
|
<Trans>Bulk Copy</Trans>
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
50
apps/web/src/components/forms/search-param-selector.tsx
Normal file
50
apps/web/src/components/forms/search-param-selector.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
|
import { Select, SelectContent, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
|
||||||
|
|
||||||
|
export type SearchParamSelector = {
|
||||||
|
paramKey: string;
|
||||||
|
isValueValid: (value: unknown) => boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SearchParamSelector = ({ children, paramKey, isValueValid }: SearchParamSelector) => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const value = useMemo(() => {
|
||||||
|
const p = searchParams?.get(paramKey) ?? 'all';
|
||||||
|
|
||||||
|
return isValueValid(p) ? p : 'all';
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
const onValueChange = (newValue: string) => {
|
||||||
|
if (!pathname) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams(searchParams?.toString());
|
||||||
|
|
||||||
|
params.set(paramKey, newValue);
|
||||||
|
|
||||||
|
if (newValue === '' || newValue === 'all') {
|
||||||
|
params.delete(paramKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push(`${pathname}?${params.toString()}`, { scroll: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select defaultValue={value} onValueChange={onValueChange}>
|
||||||
|
<SelectTrigger className="text-muted-foreground max-w-[200px]">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
|
||||||
|
<SelectContent position="popper">{children}</SelectContent>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -50,6 +50,7 @@ services:
|
|||||||
- NEXT_PRIVATE_SMTP_SECURE=${NEXT_PRIVATE_SMTP_SECURE}
|
- NEXT_PRIVATE_SMTP_SECURE=${NEXT_PRIVATE_SMTP_SECURE}
|
||||||
- NEXT_PRIVATE_SMTP_FROM_NAME=${NEXT_PRIVATE_SMTP_FROM_NAME:?err}
|
- NEXT_PRIVATE_SMTP_FROM_NAME=${NEXT_PRIVATE_SMTP_FROM_NAME:?err}
|
||||||
- NEXT_PRIVATE_SMTP_FROM_ADDRESS=${NEXT_PRIVATE_SMTP_FROM_ADDRESS:?err}
|
- NEXT_PRIVATE_SMTP_FROM_ADDRESS=${NEXT_PRIVATE_SMTP_FROM_ADDRESS:?err}
|
||||||
|
- NEXT_PRIVATE_SMTP_SERVICE=${NEXT_PRIVATE_SMTP_SERVICE}
|
||||||
- NEXT_PRIVATE_RESEND_API_KEY=${NEXT_PRIVATE_RESEND_API_KEY}
|
- NEXT_PRIVATE_RESEND_API_KEY=${NEXT_PRIVATE_RESEND_API_KEY}
|
||||||
- NEXT_PRIVATE_MAILCHANNELS_API_KEY=${NEXT_PRIVATE_MAILCHANNELS_API_KEY}
|
- NEXT_PRIVATE_MAILCHANNELS_API_KEY=${NEXT_PRIVATE_MAILCHANNELS_API_KEY}
|
||||||
- NEXT_PRIVATE_MAILCHANNELS_ENDPOINT=${NEXT_PRIVATE_MAILCHANNELS_ENDPOINT}
|
- NEXT_PRIVATE_MAILCHANNELS_ENDPOINT=${NEXT_PRIVATE_MAILCHANNELS_ENDPOINT}
|
||||||
@ -60,6 +61,7 @@ services:
|
|||||||
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
|
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
|
||||||
- NEXT_PUBLIC_DISABLE_SIGNUP=${NEXT_PUBLIC_DISABLE_SIGNUP}
|
- NEXT_PUBLIC_DISABLE_SIGNUP=${NEXT_PUBLIC_DISABLE_SIGNUP}
|
||||||
- NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=${NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH:-/opt/documenso/cert.p12}
|
- NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=${NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH:-/opt/documenso/cert.p12}
|
||||||
|
- NEXT_PRIVATE_SIGNING_PASSPHRASE=${NEXT_PRIVATE_SIGNING_PASSPHRASE}
|
||||||
ports:
|
ports:
|
||||||
- ${PORT:-3000}:${PORT:-3000}
|
- ${PORT:-3000}:${PORT:-3000}
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.7.2-rc.3",
|
"version": "1.7.2-rc.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.7.2-rc.3",
|
"version": "1.7.2-rc.4",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
@ -80,7 +80,7 @@
|
|||||||
},
|
},
|
||||||
"apps/marketing": {
|
"apps/marketing": {
|
||||||
"name": "@documenso/marketing",
|
"name": "@documenso/marketing",
|
||||||
"version": "1.7.2-rc.3",
|
"version": "1.7.2-rc.4",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/assets": "*",
|
"@documenso/assets": "*",
|
||||||
@ -441,7 +441,7 @@
|
|||||||
},
|
},
|
||||||
"apps/web": {
|
"apps/web": {
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.7.2-rc.3",
|
"version": "1.7.2-rc.4",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/api": "*",
|
"@documenso/api": "*",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.7.2-rc.3",
|
"version": "1.7.2-rc.4",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"build:web": "turbo run build --filter=@documenso/web",
|
"build:web": "turbo run build --filter=@documenso/web",
|
||||||
|
|||||||
@ -32,7 +32,7 @@ test.describe('[EE_ONLY]', () => {
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
redirectPath: `/templates/${template.id}`,
|
redirectPath: `/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set EE action auth.
|
// Set EE action auth.
|
||||||
@ -74,7 +74,7 @@ test.describe('[EE_ONLY]', () => {
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: teamMemberUser.email,
|
email: teamMemberUser.email,
|
||||||
redirectPath: `/t/${team.url}/templates/${template.id}`,
|
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set EE action auth.
|
// Set EE action auth.
|
||||||
@ -110,7 +110,7 @@ test.describe('[EE_ONLY]', () => {
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: teamMemberUser.email,
|
email: teamMemberUser.email,
|
||||||
redirectPath: `/templates/${template.id}`,
|
redirectPath: `/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global action auth should not be visible.
|
// Global action auth should not be visible.
|
||||||
@ -132,7 +132,7 @@ test('[TEMPLATE_FLOW]: add settings', async ({ page }) => {
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
redirectPath: `/templates/${template.id}`,
|
redirectPath: `/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set title.
|
// Set title.
|
||||||
|
|||||||
@ -31,7 +31,7 @@ test.describe('[EE_ONLY]', () => {
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
redirectPath: `/templates/${template.id}`,
|
redirectPath: `/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Save the settings by going to the next step.
|
// Save the settings by going to the next step.
|
||||||
@ -81,7 +81,7 @@ test('[TEMPLATE_FLOW]: add placeholder', async ({ page }) => {
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
redirectPath: `/templates/${template.id}`,
|
redirectPath: `/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Save the settings by going to the next step.
|
// Save the settings by going to the next step.
|
||||||
|
|||||||
@ -37,7 +37,7 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
redirectPath: `/templates/${template.id}`,
|
redirectPath: `/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set template title.
|
// Set template title.
|
||||||
@ -172,7 +172,7 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
|
|||||||
await apiSignin({
|
await apiSignin({
|
||||||
page,
|
page,
|
||||||
email: owner.email,
|
email: owner.email,
|
||||||
redirectPath: `/t/${team.url}/templates/${template.id}`,
|
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set template title.
|
// Set template title.
|
||||||
|
|||||||
@ -1,10 +1,56 @@
|
|||||||
|
import type { Transporter } from 'nodemailer';
|
||||||
import { createTransport } from 'nodemailer';
|
import { createTransport } from 'nodemailer';
|
||||||
|
|
||||||
import { ResendTransport } from '@documenso/nodemailer-resend';
|
import { ResendTransport } from '@documenso/nodemailer-resend';
|
||||||
|
|
||||||
import { MailChannelsTransport } from './transports/mailchannels';
|
import { MailChannelsTransport } from './transports/mailchannels';
|
||||||
|
|
||||||
const getTransport = () => {
|
/**
|
||||||
|
* Creates a Nodemailer transport object for sending emails.
|
||||||
|
*
|
||||||
|
* This function uses various environment variables to configure the appropriate
|
||||||
|
* email transport mechanism. It supports multiple types of email transports,
|
||||||
|
* including MailChannels, Resend, and different SMTP configurations.
|
||||||
|
*
|
||||||
|
* @returns {Transporter} A configured Nodemailer transporter instance.
|
||||||
|
*
|
||||||
|
* Supported Transports:
|
||||||
|
* - **mailchannels**: Uses MailChannelsTransport, requiring:
|
||||||
|
* - `NEXT_PRIVATE_MAILCHANNELS_API_KEY`: API key for MailChannels
|
||||||
|
* - `NEXT_PRIVATE_MAILCHANNELS_ENDPOINT`: Endpoint for MailChannels (optional)
|
||||||
|
* - **resend**: Uses ResendTransport, requiring:
|
||||||
|
* - `NEXT_PRIVATE_RESEND_API_KEY`: API key for Resend
|
||||||
|
* - **smtp-api**: Uses a custom SMTP API configuration, requiring:
|
||||||
|
* - `NEXT_PRIVATE_SMTP_HOST`: The SMTP server host
|
||||||
|
* - `NEXT_PRIVATE_SMTP_APIKEY`: The API key for SMTP authentication
|
||||||
|
* - `NEXT_PRIVATE_SMTP_APIKEY_USER`: The username for SMTP authentication (default: 'apikey')
|
||||||
|
* - **smtp-auth** (default): Uses a standard SMTP configuration, requiring:
|
||||||
|
* - `NEXT_PRIVATE_SMTP_HOST`: The SMTP server host (default: 'localhost:2500')
|
||||||
|
* - `NEXT_PRIVATE_SMTP_PORT`: The port to connect to (default: 587)
|
||||||
|
* - `NEXT_PRIVATE_SMTP_SECURE`: Whether to use SSL/TLS (default: false)
|
||||||
|
* - `NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS`: Whether to ignore TLS (default: false)
|
||||||
|
* - `NEXT_PRIVATE_SMTP_USERNAME`: The username for SMTP authentication
|
||||||
|
* - `NEXT_PRIVATE_SMTP_PASSWORD`: The password for SMTP authentication
|
||||||
|
* - `NEXT_PRIVATE_SMTP_SERVICE`: The SMTP service provider (e.g., "gmail"). This option is used
|
||||||
|
* when integrating with well-known services (like Gmail), enabling simplified configuration.
|
||||||
|
*
|
||||||
|
* Example Usage:
|
||||||
|
* ```env
|
||||||
|
* NEXT_PRIVATE_SMTP_TRANSPORT='smtp-auth';
|
||||||
|
* NEXT_PRIVATE_SMTP_HOST='smtp.example.com';
|
||||||
|
* NEXT_PRIVATE_SMTP_PORT=587;
|
||||||
|
* NEXT_PRIVATE_SMTP_SERVICE='gmail';
|
||||||
|
* NEXT_PRIVATE_SMTP_SECURE='true';
|
||||||
|
* NEXT_PRIVATE_SMTP_USERNAME='your-email@gmail.com';
|
||||||
|
* NEXT_PRIVATE_SMTP_PASSWORD='your-password';
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* - Ensure that the required environment variables for each transport type are set.
|
||||||
|
* - If `NEXT_PRIVATE_SMTP_TRANSPORT` is not specified, the default is `smtp-auth`.
|
||||||
|
* - `NEXT_PRIVATE_SMTP_SERVICE` is optional and used specifically for well-known services like Gmail.
|
||||||
|
*/
|
||||||
|
const getTransport = (): Transporter => {
|
||||||
const transport = process.env.NEXT_PRIVATE_SMTP_TRANSPORT ?? 'smtp-auth';
|
const transport = process.env.NEXT_PRIVATE_SMTP_TRANSPORT ?? 'smtp-auth';
|
||||||
|
|
||||||
if (transport === 'mailchannels') {
|
if (transport === 'mailchannels') {
|
||||||
@ -53,6 +99,9 @@ const getTransport = () => {
|
|||||||
pass: process.env.NEXT_PRIVATE_SMTP_PASSWORD ?? '',
|
pass: process.env.NEXT_PRIVATE_SMTP_PASSWORD ?? '',
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
...(process.env.NEXT_PRIVATE_SMTP_SERVICE
|
||||||
|
? { service: process.env.NEXT_PRIVATE_SMTP_SERVICE }
|
||||||
|
: {}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
18
packages/lib/constants/document.ts
Normal file
18
packages/lib/constants/document.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { MessageDescriptor } from '@lingui/core';
|
||||||
|
import { msg } from '@lingui/macro';
|
||||||
|
|
||||||
|
import { DocumentStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
export const DOCUMENT_STATUS: {
|
||||||
|
[status in DocumentStatus]: { description: MessageDescriptor };
|
||||||
|
} = {
|
||||||
|
[DocumentStatus.COMPLETED]: {
|
||||||
|
description: msg`Completed`,
|
||||||
|
},
|
||||||
|
[DocumentStatus.DRAFT]: {
|
||||||
|
description: msg`Draft`,
|
||||||
|
},
|
||||||
|
[DocumentStatus.PENDING]: {
|
||||||
|
description: msg`Pending`,
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -78,13 +78,3 @@ export const RECIPIENT_ROLE_SIGNING_REASONS = {
|
|||||||
[RecipientRole.CC]: msg`I am required to receive a copy of this document`,
|
[RecipientRole.CC]: msg`I am required to receive a copy of this document`,
|
||||||
[RecipientRole.VIEWER]: msg`I am a viewer of this document`,
|
[RecipientRole.VIEWER]: msg`I am a viewer of this document`,
|
||||||
} satisfies Record<keyof typeof RecipientRole, MessageDescriptor>;
|
} satisfies Record<keyof typeof RecipientRole, MessageDescriptor>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Raw english descriptions for certificates.
|
|
||||||
*/
|
|
||||||
export const RECIPIENT_ROLE_SIGNING_REASONS_ENG = {
|
|
||||||
[RecipientRole.SIGNER]: `I am a signer of this document`,
|
|
||||||
[RecipientRole.APPROVER]: `I am an approver of this document`,
|
|
||||||
[RecipientRole.CC]: `I am required to receive a copy of this document`,
|
|
||||||
[RecipientRole.VIEWER]: `I am a viewer of this document`,
|
|
||||||
} satisfies Record<keyof typeof RecipientRole, string>;
|
|
||||||
|
|||||||
@ -115,7 +115,9 @@ export const SEND_SIGNING_EMAIL_JOB_DEFINITION = {
|
|||||||
|
|
||||||
if (isTeamDocument && team) {
|
if (isTeamDocument && team) {
|
||||||
emailSubject = i18n._(msg`${team.name} invited you to ${recipientActionVerb} a document`);
|
emailSubject = i18n._(msg`${team.name} invited you to ${recipientActionVerb} a document`);
|
||||||
emailMessage = i18n._(
|
emailMessage =
|
||||||
|
customEmail?.message ||
|
||||||
|
i18n._(
|
||||||
msg`${user.name} on behalf of ${team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`,
|
msg`${user.name} on behalf of ${team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
|
|
||||||
|
import { msg } from '@lingui/macro';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
import { mailer } from '@documenso/email/mailer';
|
||||||
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
|
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import type { Document, DocumentMeta, Recipient, User } from '@documenso/prisma/client';
|
import type { Document, DocumentMeta, Recipient, User } from '@documenso/prisma/client';
|
||||||
import { DocumentStatus, SendStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, SendStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { getI18nInstance } from '../../client-only/providers/i18n.server';
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
||||||
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
|
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
@ -192,10 +195,12 @@ const handleDocumentOwnerDelete = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [html, text] = await Promise.all([
|
const [html, text] = await Promise.all([
|
||||||
renderEmailWithI18N(template),
|
renderEmailWithI18N(template, { lang: document.documentMeta?.language }),
|
||||||
renderEmailWithI18N(template, { plainText: true }),
|
renderEmailWithI18N(template, { lang: document.documentMeta?.language, plainText: true }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const i18n = await getI18nInstance(document.documentMeta?.language);
|
||||||
|
|
||||||
await mailer.sendMail({
|
await mailer.sendMail({
|
||||||
to: {
|
to: {
|
||||||
address: recipient.email,
|
address: recipient.email,
|
||||||
@ -205,7 +210,7 @@ const handleDocumentOwnerDelete = async ({
|
|||||||
name: FROM_NAME,
|
name: FROM_NAME,
|
||||||
address: FROM_ADDRESS,
|
address: FROM_ADDRESS,
|
||||||
},
|
},
|
||||||
subject: 'Document Cancelled',
|
subject: i18n._(msg`Document Cancelled`),
|
||||||
html,
|
html,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,14 @@ import { P, match } from 'ts-pattern';
|
|||||||
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
|
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
|
||||||
import type { Document, Prisma, Team, TeamEmail, User } from '@documenso/prisma/client';
|
import type {
|
||||||
|
Document,
|
||||||
|
DocumentSource,
|
||||||
|
Prisma,
|
||||||
|
Team,
|
||||||
|
TeamEmail,
|
||||||
|
User,
|
||||||
|
} from '@documenso/prisma/client';
|
||||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
|
|
||||||
import { DocumentVisibility } from '../../types/document-visibility';
|
import { DocumentVisibility } from '../../types/document-visibility';
|
||||||
@ -16,6 +23,8 @@ export type FindDocumentsOptions = {
|
|||||||
userId: number;
|
userId: number;
|
||||||
teamId?: number;
|
teamId?: number;
|
||||||
term?: string;
|
term?: string;
|
||||||
|
templateId?: number;
|
||||||
|
source?: DocumentSource;
|
||||||
status?: ExtendedDocumentStatus;
|
status?: ExtendedDocumentStatus;
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
@ -32,6 +41,8 @@ export const findDocuments = async ({
|
|||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
term,
|
term,
|
||||||
|
templateId,
|
||||||
|
source,
|
||||||
status = ExtendedDocumentStatus.ALL,
|
status = ExtendedDocumentStatus.ALL,
|
||||||
page = 1,
|
page = 1,
|
||||||
perPage = 10,
|
perPage = 10,
|
||||||
@ -40,8 +51,7 @@ export const findDocuments = async ({
|
|||||||
senderIds,
|
senderIds,
|
||||||
search,
|
search,
|
||||||
}: FindDocumentsOptions) => {
|
}: FindDocumentsOptions) => {
|
||||||
const { user, team } = await prisma.$transaction(async (tx) => {
|
const user = await prisma.user.findFirstOrThrow({
|
||||||
const user = await tx.user.findFirstOrThrow({
|
|
||||||
where: {
|
where: {
|
||||||
id: userId,
|
id: userId,
|
||||||
},
|
},
|
||||||
@ -50,7 +60,7 @@ export const findDocuments = async ({
|
|||||||
let team = null;
|
let team = null;
|
||||||
|
|
||||||
if (teamId !== undefined) {
|
if (teamId !== undefined) {
|
||||||
team = await tx.team.findFirstOrThrow({
|
team = await prisma.team.findFirstOrThrow({
|
||||||
where: {
|
where: {
|
||||||
id: teamId,
|
id: teamId,
|
||||||
members: {
|
members: {
|
||||||
@ -73,12 +83,6 @@ export const findDocuments = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
user,
|
|
||||||
team,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const orderByColumn = orderBy?.column ?? 'createdAt';
|
const orderByColumn = orderBy?.column ?? 'createdAt';
|
||||||
const orderByDirection = orderBy?.direction ?? 'desc';
|
const orderByDirection = orderBy?.direction ?? 'desc';
|
||||||
const teamMemberRole = team?.members[0].role ?? null;
|
const teamMemberRole = team?.members[0].role ?? null;
|
||||||
@ -197,8 +201,27 @@ export const findDocuments = async ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const whereAndClause: Prisma.DocumentWhereInput['AND'] = [
|
||||||
|
{ ...termFilters },
|
||||||
|
{ ...filters },
|
||||||
|
{ ...deletedFilter },
|
||||||
|
{ ...searchFilter },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (templateId) {
|
||||||
|
whereAndClause.push({
|
||||||
|
templateId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
whereAndClause.push({
|
||||||
|
source,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const whereClause: Prisma.DocumentWhereInput = {
|
const whereClause: Prisma.DocumentWhereInput = {
|
||||||
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }, { ...searchFilter }],
|
AND: whereAndClause,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (period) {
|
if (period) {
|
||||||
|
|||||||
@ -106,17 +106,25 @@ export const resendDocument = async ({
|
|||||||
._(RECIPIENT_ROLES_DESCRIPTION[recipient.role].actionVerb)
|
._(RECIPIENT_ROLES_DESCRIPTION[recipient.role].actionVerb)
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
|
||||||
let emailMessage = msg`${customEmail?.message || ''}`;
|
let emailMessage = customEmail?.message || '';
|
||||||
let emailSubject = msg`Reminder: Please ${recipientActionVerb} this document`;
|
let emailSubject = i18n._(msg`Reminder: Please ${recipientActionVerb} this document`);
|
||||||
|
|
||||||
if (selfSigner) {
|
if (selfSigner) {
|
||||||
emailMessage = msg`You have initiated the document ${`"${document.title}"`} that requires you to ${recipientActionVerb} it.`;
|
emailMessage = i18n._(
|
||||||
emailSubject = msg`Reminder: Please ${recipientActionVerb} your document`;
|
msg`You have initiated the document ${`"${document.title}"`} that requires you to ${recipientActionVerb} it.`,
|
||||||
|
);
|
||||||
|
emailSubject = i18n._(msg`Reminder: Please ${recipientActionVerb} your document`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTeamDocument && document.team) {
|
if (isTeamDocument && document.team) {
|
||||||
emailSubject = msg`Reminder: ${document.team.name} invited you to ${recipientActionVerb} a document`;
|
emailSubject = i18n._(
|
||||||
emailMessage = msg`${user.name} on behalf of ${document.team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`;
|
msg`Reminder: ${document.team.name} invited you to ${recipientActionVerb} a document`,
|
||||||
|
);
|
||||||
|
emailMessage =
|
||||||
|
customEmail?.message ||
|
||||||
|
i18n._(
|
||||||
|
msg`${user.name} on behalf of ${document.team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const customEmailTemplate = {
|
const customEmailTemplate = {
|
||||||
@ -134,7 +142,7 @@ export const resendDocument = async ({
|
|||||||
inviterEmail: isTeamDocument ? document.team?.teamEmail?.email || user.email : user.email,
|
inviterEmail: isTeamDocument ? document.team?.teamEmail?.email || user.email : user.email,
|
||||||
assetBaseUrl,
|
assetBaseUrl,
|
||||||
signDocumentLink,
|
signDocumentLink,
|
||||||
customBody: renderCustomEmailTemplate(i18n._(emailMessage), customEmailTemplate),
|
customBody: renderCustomEmailTemplate(emailMessage, customEmailTemplate),
|
||||||
role: recipient.role,
|
role: recipient.role,
|
||||||
selfSigner,
|
selfSigner,
|
||||||
isTeamInvite: isTeamDocument,
|
isTeamInvite: isTeamDocument,
|
||||||
@ -165,7 +173,7 @@ export const resendDocument = async ({
|
|||||||
i18n._(msg`Reminder: ${customEmail.subject}`),
|
i18n._(msg`Reminder: ${customEmail.subject}`),
|
||||||
customEmailTemplate,
|
customEmailTemplate,
|
||||||
)
|
)
|
||||||
: i18n._(emailSubject),
|
: emailSubject,
|
||||||
html,
|
html,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -42,6 +42,13 @@ export const getTemplateById = async ({ id, userId, teamId }: GetTemplateByIdOpt
|
|||||||
templateMeta: true,
|
templateMeta: true,
|
||||||
Recipient: true,
|
Recipient: true,
|
||||||
Field: true,
|
Field: true,
|
||||||
|
User: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
email: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-11-05 02:04\n"
|
"PO-Revision-Date: 2024-11-05 09:34\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@ -96,6 +96,90 @@ msgstr "{memberEmail} ist dem folgenden Team beigetreten"
|
|||||||
msgid "{memberEmail} left the following team"
|
msgid "{memberEmail} left the following team"
|
||||||
msgstr "{memberEmail} hat das folgende Team verlassen"
|
msgstr "{memberEmail} hat das folgende Team verlassen"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:263
|
||||||
|
msgid "{prefix} added a field"
|
||||||
|
msgstr "{prefix} hat ein Feld hinzugefügt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:275
|
||||||
|
msgid "{prefix} added a recipient"
|
||||||
|
msgstr "{prefix} hat einen Empfänger hinzugefügt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:287
|
||||||
|
msgid "{prefix} created the document"
|
||||||
|
msgstr "{prefix} hat das Dokument erstellt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:291
|
||||||
|
msgid "{prefix} deleted the document"
|
||||||
|
msgstr "{prefix} hat das Dokument gelöscht"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
|
msgid "{prefix} moved the document to team"
|
||||||
|
msgstr "{prefix} hat das Dokument ins Team verschoben"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
|
msgid "{prefix} opened the document"
|
||||||
|
msgstr "{prefix} hat das Dokument geöffnet"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:267
|
||||||
|
msgid "{prefix} removed a field"
|
||||||
|
msgstr "{prefix} hat ein Feld entfernt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:279
|
||||||
|
msgid "{prefix} removed a recipient"
|
||||||
|
msgstr "{prefix} hat einen Empfänger entfernt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:355
|
||||||
|
msgid "{prefix} resent an email to {0}"
|
||||||
|
msgstr "{prefix} hat eine E-Mail an {0} erneut gesendet"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:356
|
||||||
|
msgid "{prefix} sent an email to {0}"
|
||||||
|
msgstr "{prefix} hat eine E-Mail an {0} gesendet"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
|
msgid "{prefix} sent the document"
|
||||||
|
msgstr "{prefix} hat das Dokument gesendet"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} signed a field"
|
||||||
|
msgstr "{prefix} hat ein Feld unterschrieben"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
|
msgid "{prefix} unsigned a field"
|
||||||
|
msgstr "{prefix} hat ein Feld ungültig gemacht"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:271
|
||||||
|
msgid "{prefix} updated a field"
|
||||||
|
msgstr "{prefix} hat ein Feld aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:283
|
||||||
|
msgid "{prefix} updated a recipient"
|
||||||
|
msgstr "{prefix} hat einen Empfänger aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
|
msgid "{prefix} updated the document"
|
||||||
|
msgstr "{prefix} hat das Dokument aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
|
msgstr "{prefix} hat die Anforderungen an die Dokumentenzugriffsautorisierung aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
|
msgid "{prefix} updated the document external ID"
|
||||||
|
msgstr "{prefix} hat die externe ID des Dokuments aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
|
msgstr "{prefix} hat die Authentifizierungsanforderungen für die Dokumentenunterzeichnung aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
|
msgid "{prefix} updated the document title"
|
||||||
|
msgstr "{prefix} hat den Titel des Dokuments aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
|
msgid "{prefix} updated the document visibility"
|
||||||
|
msgstr "{prefix} hat die Sichtbarkeit des Dokuments aktualisiert"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
||||||
msgid "{recipientName} {action} a document by using one of your direct links"
|
msgid "{recipientName} {action} a document by using one of your direct links"
|
||||||
msgstr "{recipientName} {action} ein Dokument, indem Sie einen Ihrer direkten Links verwenden"
|
msgstr "{recipientName} {action} ein Dokument, indem Sie einen Ihrer direkten Links verwenden"
|
||||||
@ -104,6 +188,26 @@ msgstr "{recipientName} {action} ein Dokument, indem Sie einen Ihrer direkten Li
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "Anfrage zur Übertragung des Eigentums von {teamName}"
|
msgstr "Anfrage zur Übertragung des Eigentums von {teamName}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:343
|
||||||
|
msgid "{userName} approved the document"
|
||||||
|
msgstr "{userName} hat das Dokument genehmigt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:344
|
||||||
|
msgid "{userName} CC'd the document"
|
||||||
|
msgstr "{userName} hat das Dokument in CC gesetzt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
|
msgid "{userName} completed their task"
|
||||||
|
msgstr "{userName} hat ihre Aufgabe abgeschlossen"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:341
|
||||||
|
msgid "{userName} signed the document"
|
||||||
|
msgstr "{userName} hat das Dokument unterschrieben"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
|
msgid "{userName} viewed the document"
|
||||||
|
msgstr "{userName} hat das Dokument angesehen"
|
||||||
|
|
||||||
#: packages/ui/primitives/data-table-pagination.tsx:41
|
#: packages/ui/primitives/data-table-pagination.tsx:41
|
||||||
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
||||||
msgstr "{visibleRows, plural, one {Eine # Ergebnis wird angezeigt.} other {# Ergebnisse werden angezeigt.}}"
|
msgstr "{visibleRows, plural, one {Eine # Ergebnis wird angezeigt.} other {# Ergebnisse werden angezeigt.}}"
|
||||||
@ -150,10 +254,34 @@ msgstr "<0>Passkey erforderlich</0> - Der Empfänger muss ein Konto haben und de
|
|||||||
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||||
msgstr "Ein Dokument wurde von deiner direkten Vorlage erstellt, das erfordert, dass du {recipientActionVerb}."
|
msgstr "Ein Dokument wurde von deiner direkten Vorlage erstellt, das erfordert, dass du {recipientActionVerb}."
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:262
|
||||||
|
msgid "A field was added"
|
||||||
|
msgstr "Ein Feld wurde hinzugefügt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:266
|
||||||
|
msgid "A field was removed"
|
||||||
|
msgstr "Ein Feld wurde entfernt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:270
|
||||||
|
msgid "A field was updated"
|
||||||
|
msgstr "Ein Feld wurde aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
||||||
msgid "A new member has joined your team"
|
msgid "A new member has joined your team"
|
||||||
msgstr "Ein neues Mitglied ist deinem Team beigetreten"
|
msgstr "Ein neues Mitglied ist deinem Team beigetreten"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:274
|
||||||
|
msgid "A recipient was added"
|
||||||
|
msgstr "Ein Empfänger wurde hinzugefügt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:278
|
||||||
|
msgid "A recipient was removed"
|
||||||
|
msgstr "Ein Empfänger wurde entfernt"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:282
|
||||||
|
msgid "A recipient was updated"
|
||||||
|
msgstr "Ein Empfänger wurde aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
||||||
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
||||||
msgstr "Eine Anfrage zur Verwendung deiner E-Mail wurde von {teamName} auf Documenso initiiert"
|
msgstr "Eine Anfrage zur Verwendung deiner E-Mail wurde von {teamName} auf Documenso initiiert"
|
||||||
@ -368,6 +496,7 @@ msgstr "Schließen"
|
|||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:35
|
#: packages/email/template-components/template-document-completed.tsx:35
|
||||||
#: packages/email/template-components/template-document-self-signed.tsx:36
|
#: packages/email/template-components/template-document-self-signed.tsx:36
|
||||||
|
#: packages/lib/constants/document.ts:10
|
||||||
msgid "Completed"
|
msgid "Completed"
|
||||||
msgstr "Abgeschlossen"
|
msgstr "Abgeschlossen"
|
||||||
|
|
||||||
@ -450,10 +579,24 @@ msgstr "Empfänger des direkten Links"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Dokumentenzugriff"
|
msgstr "Dokumentenzugriff"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
|
msgid "Document access auth updated"
|
||||||
|
msgstr "Die Authentifizierung für den Dokumentenzugriff wurde aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/server-only/document/delete-document.ts:213
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:75
|
#: packages/lib/server-only/document/super-delete-document.ts:75
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Dokument storniert"
|
msgstr "Dokument storniert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:360
|
||||||
|
msgid "Document completed"
|
||||||
|
msgstr "Dokument abgeschlossen"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:286
|
||||||
|
msgid "Document created"
|
||||||
|
msgstr "Dokument erstellt"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
||||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
||||||
msgid "Document created from direct template"
|
msgid "Document created from direct template"
|
||||||
@ -463,15 +606,55 @@ msgstr "Dokument erstellt aus direkter Vorlage"
|
|||||||
msgid "Document Creation"
|
msgid "Document Creation"
|
||||||
msgstr "Dokumenterstellung"
|
msgstr "Dokumenterstellung"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:290
|
||||||
|
msgid "Document deleted"
|
||||||
|
msgstr "Dokument gelöscht"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/send-delete-email.ts:58
|
#: packages/lib/server-only/document/send-delete-email.ts:58
|
||||||
msgid "Document Deleted!"
|
msgid "Document Deleted!"
|
||||||
msgstr "Dokument gelöscht!"
|
msgstr "Dokument gelöscht!"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
|
msgid "Document external ID updated"
|
||||||
|
msgstr "Externe ID des Dokuments aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
|
msgid "Document moved to team"
|
||||||
|
msgstr "Dokument ins Team verschoben"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
|
msgid "Document opened"
|
||||||
|
msgstr "Dokument geöffnet"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
|
msgid "Document sent"
|
||||||
|
msgstr "Dokument gesendet"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
|
msgid "Document signing auth updated"
|
||||||
|
msgstr "Dokument unterzeichnen Authentifizierung aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
|
msgid "Document title updated"
|
||||||
|
msgstr "Dokumenttitel aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
|
msgid "Document updated"
|
||||||
|
msgstr "Dokument aktualisiert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
|
msgid "Document visibility updated"
|
||||||
|
msgstr "Sichtbarkeit des Dokuments aktualisiert"
|
||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:64
|
#: packages/email/template-components/template-document-completed.tsx:64
|
||||||
#: packages/ui/components/document/document-download-button.tsx:68
|
#: packages/ui/components/document/document-download-button.tsx:68
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr "Herunterladen"
|
msgstr "Herunterladen"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:13
|
||||||
|
msgid "Draft"
|
||||||
|
msgstr "Entwurf"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-dropzone.tsx:162
|
#: packages/ui/primitives/document-dropzone.tsx:162
|
||||||
msgid "Drag & drop your PDF here."
|
msgid "Drag & drop your PDF here."
|
||||||
msgstr "Ziehen Sie Ihr PDF hierher."
|
msgstr "Ziehen Sie Ihr PDF hierher."
|
||||||
@ -504,6 +687,14 @@ msgstr "E-Mail ist erforderlich"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "E-Mail-Optionen"
|
msgstr "E-Mail-Optionen"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email resent"
|
||||||
|
msgstr "E-Mail erneut gesendet"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email sent"
|
||||||
|
msgstr "E-Mail gesendet"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
||||||
msgid "Empty field"
|
msgid "Empty field"
|
||||||
msgstr "Leeres Feld"
|
msgstr "Leeres Feld"
|
||||||
@ -564,6 +755,14 @@ msgstr "Feldbeschriftung"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Feldplatzhalter"
|
msgstr "Feldplatzhalter"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Field signed"
|
||||||
|
msgstr "Feld unterschrieben"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
|
msgid "Field unsigned"
|
||||||
|
msgstr "Feld nicht unterschrieben"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
||||||
@ -774,6 +973,10 @@ msgstr "Passwort erfolgreich zurückgesetzt"
|
|||||||
msgid "Password updated!"
|
msgid "Password updated!"
|
||||||
msgstr "Passwort aktualisiert!"
|
msgstr "Passwort aktualisiert!"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:16
|
||||||
|
msgid "Pending"
|
||||||
|
msgstr "Ausstehend"
|
||||||
|
|
||||||
#: packages/email/templates/document-pending.tsx:17
|
#: packages/email/templates/document-pending.tsx:17
|
||||||
msgid "Pending Document"
|
msgid "Pending Document"
|
||||||
msgstr "Ausstehendes Dokument"
|
msgstr "Ausstehendes Dokument"
|
||||||
@ -841,6 +1044,10 @@ msgstr "Nur lesen"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Erhält Kopie"
|
msgstr "Erhält Kopie"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
|
msgid "Recipient"
|
||||||
|
msgstr "Empfänger"
|
||||||
|
|
||||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
||||||
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
||||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
||||||
@ -1248,6 +1455,10 @@ msgstr "Wir haben dein Passwort wie gewünscht geändert. Du kannst dich jetzt m
|
|||||||
msgid "Welcome to Documenso!"
|
msgid "Welcome to Documenso!"
|
||||||
msgstr "Willkommen bei Documenso!"
|
msgstr "Willkommen bei Documenso!"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:258
|
||||||
|
msgid "You"
|
||||||
|
msgstr "Du"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
||||||
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
||||||
msgstr "Sie sind dabei, dieses Dokument an die Empfänger zu senden. Sind Sie sicher, dass Sie fortfahren möchten?"
|
msgstr "Sie sind dabei, dieses Dokument an die Empfänger zu senden. Sind Sie sicher, dass Sie fortfahren möchten?"
|
||||||
@ -1309,4 +1520,3 @@ msgstr "Dein Passwort wurde aktualisiert."
|
|||||||
#: packages/email/templates/team-delete.tsx:30
|
#: packages/email/templates/team-delete.tsx:30
|
||||||
msgid "Your team has been deleted"
|
msgid "Your team has been deleted"
|
||||||
msgstr "Dein Team wurde gelöscht"
|
msgstr "Dein Team wurde gelöscht"
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-11-05 02:04\n"
|
"PO-Revision-Date: 2024-11-05 09:34\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@ -42,7 +42,7 @@ msgstr "Dokument hinzufügen"
|
|||||||
msgid "Add More Users for {0}"
|
msgid "Add More Users for {0}"
|
||||||
msgstr "Mehr Benutzer hinzufügen für {0}"
|
msgstr "Mehr Benutzer hinzufügen für {0}"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:164
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:165
|
||||||
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
||||||
msgstr "Alle unsere Kennzahlen, Finanzen und Erkenntnisse sind öffentlich. Wir glauben an Transparenz und möchten unsere Reise mit Ihnen teilen. Mehr erfahren Sie hier: <0>Ankündigung Offene Kennzahlen</0>"
|
msgstr "Alle unsere Kennzahlen, Finanzen und Erkenntnisse sind öffentlich. Wir glauben an Transparenz und möchten unsere Reise mit Ihnen teilen. Mehr erfahren Sie hier: <0>Ankündigung Offene Kennzahlen</0>"
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ msgstr "Änderungsprotokoll"
|
|||||||
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
||||||
msgstr "Wählen Sie eine Vorlage aus dem Community-App-Store. Oder reichen Sie Ihre eigene Vorlage ein, damit andere sie benutzen können."
|
msgstr "Wählen Sie eine Vorlage aus dem Community-App-Store. Oder reichen Sie Ihre eigene Vorlage ein, damit andere sie benutzen können."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:218
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:219
|
||||||
msgid "Community"
|
msgid "Community"
|
||||||
msgstr "Gemeinschaft"
|
msgstr "Gemeinschaft"
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ msgstr "Schnell."
|
|||||||
msgid "Faster, smarter and more beautiful."
|
msgid "Faster, smarter and more beautiful."
|
||||||
msgstr "Schneller, intelligenter und schöner."
|
msgstr "Schneller, intelligenter und schöner."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:209
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:210
|
||||||
msgid "Finances"
|
msgid "Finances"
|
||||||
msgstr "Finanzen"
|
msgstr "Finanzen"
|
||||||
|
|
||||||
@ -246,15 +246,15 @@ msgstr "Fangen Sie heute an."
|
|||||||
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
||||||
msgstr "Erhalten Sie die neuesten Nachrichten von Documenso, einschließlich Produkt-Updates, Team-Ankündigungen und mehr!"
|
msgstr "Erhalten Sie die neuesten Nachrichten von Documenso, einschließlich Produkt-Updates, Team-Ankündigungen und mehr!"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:232
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
||||||
msgid "GitHub: Total Merged PRs"
|
msgid "GitHub: Total Merged PRs"
|
||||||
msgstr "GitHub: Gesamte PRs zusammengeführt"
|
msgstr "GitHub: Gesamte PRs zusammengeführt"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:250
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
||||||
msgid "GitHub: Total Open Issues"
|
msgid "GitHub: Total Open Issues"
|
||||||
msgstr "GitHub: Gesamte offene Issues"
|
msgstr "GitHub: Gesamte offene Issues"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:224
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
||||||
msgid "GitHub: Total Stars"
|
msgid "GitHub: Total Stars"
|
||||||
msgstr "GitHub: Gesamtanzahl Sterne"
|
msgstr "GitHub: Gesamtanzahl Sterne"
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ msgstr "GitHub: Gesamtanzahl Sterne"
|
|||||||
msgid "Global Salary Bands"
|
msgid "Global Salary Bands"
|
||||||
msgstr "Globale Gehaltsbänder"
|
msgstr "Globale Gehaltsbänder"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:260
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:261
|
||||||
msgid "Growth"
|
msgid "Growth"
|
||||||
msgstr "Wachstum"
|
msgstr "Wachstum"
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ msgstr "Integrierte Zahlungen mit Stripe, sodass Sie sich keine Sorgen ums Bezah
|
|||||||
msgid "Integrates with all your favourite tools."
|
msgid "Integrates with all your favourite tools."
|
||||||
msgstr "Integriert sich mit all Ihren Lieblingstools."
|
msgstr "Integriert sich mit all Ihren Lieblingstools."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:288
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:289
|
||||||
msgid "Is there more?"
|
msgid "Is there more?"
|
||||||
msgstr "Gibt es mehr?"
|
msgstr "Gibt es mehr?"
|
||||||
|
|
||||||
@ -310,11 +310,11 @@ msgstr "Standort"
|
|||||||
msgid "Make it your own through advanced customization and adjustability."
|
msgid "Make it your own through advanced customization and adjustability."
|
||||||
msgstr "Machen Sie es zu Ihrem eigenen durch erweiterte Anpassung und Einstellbarkeit."
|
msgstr "Machen Sie es zu Ihrem eigenen durch erweiterte Anpassung und Einstellbarkeit."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:198
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:199
|
||||||
msgid "Merged PR's"
|
msgid "Merged PR's"
|
||||||
msgstr "Zusammengeführte PRs"
|
msgstr "Zusammengeführte PRs"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:234
|
||||||
msgid "Merged PRs"
|
msgid "Merged PRs"
|
||||||
msgstr "Zusammengeführte PRs"
|
msgstr "Zusammengeführte PRs"
|
||||||
|
|
||||||
@ -345,8 +345,8 @@ msgstr "Keine Kreditkarte erforderlich"
|
|||||||
msgid "None of these work for you? Try self-hosting!"
|
msgid "None of these work for you? Try self-hosting!"
|
||||||
msgstr "Keines dieser Angebote passt zu Ihnen? Versuchen Sie das Selbst-Hosting!"
|
msgstr "Keines dieser Angebote passt zu Ihnen? Versuchen Sie das Selbst-Hosting!"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:193
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:194
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:252
|
||||||
msgid "Open Issues"
|
msgid "Open Issues"
|
||||||
msgstr "Offene Issues"
|
msgstr "Offene Issues"
|
||||||
|
|
||||||
@ -354,7 +354,7 @@ msgstr "Offene Issues"
|
|||||||
msgid "Open Source or Hosted."
|
msgid "Open Source or Hosted."
|
||||||
msgstr "Open Source oder Hosted."
|
msgstr "Open Source oder Hosted."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:160
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:161
|
||||||
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
||||||
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
||||||
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
||||||
@ -466,7 +466,7 @@ msgstr "Intelligent."
|
|||||||
msgid "Star on GitHub"
|
msgid "Star on GitHub"
|
||||||
msgstr "Auf GitHub favorisieren"
|
msgstr "Auf GitHub favorisieren"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:226
|
||||||
msgid "Stars"
|
msgid "Stars"
|
||||||
msgstr "Favoriten"
|
msgstr "Favoriten"
|
||||||
|
|
||||||
@ -501,7 +501,7 @@ msgstr "Vorlagen-Shop (Demnächst)."
|
|||||||
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
||||||
msgstr "Das ist großartig. Sie können sich die aktuellen <0>Issues</0> ansehen und unserer <1>Discord-Community</1> beitreten, um auf dem neuesten Stand zu bleiben, was die aktuellen Prioritäten sind. In jedem Fall sind wir eine offene Gemeinschaft und begrüßen jegliche Beiträge, technische und nicht-technische ❤️"
|
msgstr "Das ist großartig. Sie können sich die aktuellen <0>Issues</0> ansehen und unserer <1>Discord-Community</1> beitreten, um auf dem neuesten Stand zu bleiben, was die aktuellen Prioritäten sind. In jedem Fall sind wir eine offene Gemeinschaft und begrüßen jegliche Beiträge, technische und nicht-technische ❤️"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:292
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:293
|
||||||
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
||||||
msgstr "Diese Seite entwickelt sich weiter, während wir lernen, was ein großartiges Signing-Unternehmen ausmacht. Wir werden sie aktualisieren, wenn wir mehr zu teilen haben."
|
msgstr "Diese Seite entwickelt sich weiter, während wir lernen, was ein großartiges Signing-Unternehmen ausmacht. Wir werden sie aktualisieren, wenn wir mehr zu teilen haben."
|
||||||
|
|
||||||
@ -514,8 +514,8 @@ msgstr "Titel"
|
|||||||
msgid "Total Completed Documents"
|
msgid "Total Completed Documents"
|
||||||
msgstr "Insgesamt Abgeschlossene Dokumente"
|
msgstr "Insgesamt Abgeschlossene Dokumente"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:266
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
||||||
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:268
|
||||||
msgid "Total Customers"
|
msgid "Total Customers"
|
||||||
msgstr "Insgesamt Kunden"
|
msgstr "Insgesamt Kunden"
|
||||||
|
|
||||||
@ -602,4 +602,3 @@ msgstr "Sie können Documenso kostenlos selbst hosten oder unsere sofort einsatz
|
|||||||
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
||||||
msgid "Your browser does not support the video tag."
|
msgid "Your browser does not support the video tag."
|
||||||
msgstr "Ihr Browser unterstützt das Video-Tag nicht."
|
msgstr "Ihr Browser unterstützt das Video-Tag nicht."
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -91,6 +91,90 @@ msgstr "{memberEmail} joined the following team"
|
|||||||
msgid "{memberEmail} left the following team"
|
msgid "{memberEmail} left the following team"
|
||||||
msgstr "{memberEmail} left the following team"
|
msgstr "{memberEmail} left the following team"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:263
|
||||||
|
msgid "{prefix} added a field"
|
||||||
|
msgstr "{prefix} added a field"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:275
|
||||||
|
msgid "{prefix} added a recipient"
|
||||||
|
msgstr "{prefix} added a recipient"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:287
|
||||||
|
msgid "{prefix} created the document"
|
||||||
|
msgstr "{prefix} created the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:291
|
||||||
|
msgid "{prefix} deleted the document"
|
||||||
|
msgstr "{prefix} deleted the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
|
msgid "{prefix} moved the document to team"
|
||||||
|
msgstr "{prefix} moved the document to team"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
|
msgid "{prefix} opened the document"
|
||||||
|
msgstr "{prefix} opened the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:267
|
||||||
|
msgid "{prefix} removed a field"
|
||||||
|
msgstr "{prefix} removed a field"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:279
|
||||||
|
msgid "{prefix} removed a recipient"
|
||||||
|
msgstr "{prefix} removed a recipient"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:355
|
||||||
|
msgid "{prefix} resent an email to {0}"
|
||||||
|
msgstr "{prefix} resent an email to {0}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:356
|
||||||
|
msgid "{prefix} sent an email to {0}"
|
||||||
|
msgstr "{prefix} sent an email to {0}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
|
msgid "{prefix} sent the document"
|
||||||
|
msgstr "{prefix} sent the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} signed a field"
|
||||||
|
msgstr "{prefix} signed a field"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
|
msgid "{prefix} unsigned a field"
|
||||||
|
msgstr "{prefix} unsigned a field"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:271
|
||||||
|
msgid "{prefix} updated a field"
|
||||||
|
msgstr "{prefix} updated a field"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:283
|
||||||
|
msgid "{prefix} updated a recipient"
|
||||||
|
msgstr "{prefix} updated a recipient"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
|
msgid "{prefix} updated the document"
|
||||||
|
msgstr "{prefix} updated the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
|
msgstr "{prefix} updated the document access auth requirements"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
|
msgid "{prefix} updated the document external ID"
|
||||||
|
msgstr "{prefix} updated the document external ID"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
|
msgstr "{prefix} updated the document signing auth requirements"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
|
msgid "{prefix} updated the document title"
|
||||||
|
msgstr "{prefix} updated the document title"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
|
msgid "{prefix} updated the document visibility"
|
||||||
|
msgstr "{prefix} updated the document visibility"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
||||||
msgid "{recipientName} {action} a document by using one of your direct links"
|
msgid "{recipientName} {action} a document by using one of your direct links"
|
||||||
msgstr "{recipientName} {action} a document by using one of your direct links"
|
msgstr "{recipientName} {action} a document by using one of your direct links"
|
||||||
@ -99,6 +183,26 @@ msgstr "{recipientName} {action} a document by using one of your direct links"
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "{teamName} ownership transfer request"
|
msgstr "{teamName} ownership transfer request"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:343
|
||||||
|
msgid "{userName} approved the document"
|
||||||
|
msgstr "{userName} approved the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:344
|
||||||
|
msgid "{userName} CC'd the document"
|
||||||
|
msgstr "{userName} CC'd the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
|
msgid "{userName} completed their task"
|
||||||
|
msgstr "{userName} completed their task"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:341
|
||||||
|
msgid "{userName} signed the document"
|
||||||
|
msgstr "{userName} signed the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
|
msgid "{userName} viewed the document"
|
||||||
|
msgstr "{userName} viewed the document"
|
||||||
|
|
||||||
#: packages/ui/primitives/data-table-pagination.tsx:41
|
#: packages/ui/primitives/data-table-pagination.tsx:41
|
||||||
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
||||||
msgstr "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
msgstr "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
||||||
@ -145,10 +249,34 @@ msgstr "<0>Require passkey</0> - The recipient must have an account and passkey
|
|||||||
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||||
msgstr "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
msgstr "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:262
|
||||||
|
msgid "A field was added"
|
||||||
|
msgstr "A field was added"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:266
|
||||||
|
msgid "A field was removed"
|
||||||
|
msgstr "A field was removed"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:270
|
||||||
|
msgid "A field was updated"
|
||||||
|
msgstr "A field was updated"
|
||||||
|
|
||||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
||||||
msgid "A new member has joined your team"
|
msgid "A new member has joined your team"
|
||||||
msgstr "A new member has joined your team"
|
msgstr "A new member has joined your team"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:274
|
||||||
|
msgid "A recipient was added"
|
||||||
|
msgstr "A recipient was added"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:278
|
||||||
|
msgid "A recipient was removed"
|
||||||
|
msgstr "A recipient was removed"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:282
|
||||||
|
msgid "A recipient was updated"
|
||||||
|
msgstr "A recipient was updated"
|
||||||
|
|
||||||
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
||||||
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
||||||
msgstr "A request to use your email has been initiated by {teamName} on Documenso"
|
msgstr "A request to use your email has been initiated by {teamName} on Documenso"
|
||||||
@ -363,6 +491,7 @@ msgstr "Close"
|
|||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:35
|
#: packages/email/template-components/template-document-completed.tsx:35
|
||||||
#: packages/email/template-components/template-document-self-signed.tsx:36
|
#: packages/email/template-components/template-document-self-signed.tsx:36
|
||||||
|
#: packages/lib/constants/document.ts:10
|
||||||
msgid "Completed"
|
msgid "Completed"
|
||||||
msgstr "Completed"
|
msgstr "Completed"
|
||||||
|
|
||||||
@ -445,10 +574,24 @@ msgstr "Direct link receiver"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Document access"
|
msgstr "Document access"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
|
msgid "Document access auth updated"
|
||||||
|
msgstr "Document access auth updated"
|
||||||
|
|
||||||
|
#: packages/lib/server-only/document/delete-document.ts:213
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:75
|
#: packages/lib/server-only/document/super-delete-document.ts:75
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Document Cancelled"
|
msgstr "Document Cancelled"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:360
|
||||||
|
msgid "Document completed"
|
||||||
|
msgstr "Document completed"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:286
|
||||||
|
msgid "Document created"
|
||||||
|
msgstr "Document created"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
||||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
||||||
msgid "Document created from direct template"
|
msgid "Document created from direct template"
|
||||||
@ -458,15 +601,55 @@ msgstr "Document created from direct template"
|
|||||||
msgid "Document Creation"
|
msgid "Document Creation"
|
||||||
msgstr "Document Creation"
|
msgstr "Document Creation"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:290
|
||||||
|
msgid "Document deleted"
|
||||||
|
msgstr "Document deleted"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/send-delete-email.ts:58
|
#: packages/lib/server-only/document/send-delete-email.ts:58
|
||||||
msgid "Document Deleted!"
|
msgid "Document Deleted!"
|
||||||
msgstr "Document Deleted!"
|
msgstr "Document Deleted!"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
|
msgid "Document external ID updated"
|
||||||
|
msgstr "Document external ID updated"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
|
msgid "Document moved to team"
|
||||||
|
msgstr "Document moved to team"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
|
msgid "Document opened"
|
||||||
|
msgstr "Document opened"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
|
msgid "Document sent"
|
||||||
|
msgstr "Document sent"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
|
msgid "Document signing auth updated"
|
||||||
|
msgstr "Document signing auth updated"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
|
msgid "Document title updated"
|
||||||
|
msgstr "Document title updated"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
|
msgid "Document updated"
|
||||||
|
msgstr "Document updated"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
|
msgid "Document visibility updated"
|
||||||
|
msgstr "Document visibility updated"
|
||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:64
|
#: packages/email/template-components/template-document-completed.tsx:64
|
||||||
#: packages/ui/components/document/document-download-button.tsx:68
|
#: packages/ui/components/document/document-download-button.tsx:68
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr "Download"
|
msgstr "Download"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:13
|
||||||
|
msgid "Draft"
|
||||||
|
msgstr "Draft"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-dropzone.tsx:162
|
#: packages/ui/primitives/document-dropzone.tsx:162
|
||||||
msgid "Drag & drop your PDF here."
|
msgid "Drag & drop your PDF here."
|
||||||
msgstr "Drag & drop your PDF here."
|
msgstr "Drag & drop your PDF here."
|
||||||
@ -499,6 +682,14 @@ msgstr "Email is required"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "Email Options"
|
msgstr "Email Options"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email resent"
|
||||||
|
msgstr "Email resent"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email sent"
|
||||||
|
msgstr "Email sent"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
||||||
msgid "Empty field"
|
msgid "Empty field"
|
||||||
msgstr "Empty field"
|
msgstr "Empty field"
|
||||||
@ -559,6 +750,14 @@ msgstr "Field label"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Field placeholder"
|
msgstr "Field placeholder"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Field signed"
|
||||||
|
msgstr "Field signed"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
|
msgid "Field unsigned"
|
||||||
|
msgstr "Field unsigned"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
||||||
@ -769,6 +968,10 @@ msgstr "Password Reset Successful"
|
|||||||
msgid "Password updated!"
|
msgid "Password updated!"
|
||||||
msgstr "Password updated!"
|
msgstr "Password updated!"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:16
|
||||||
|
msgid "Pending"
|
||||||
|
msgstr "Pending"
|
||||||
|
|
||||||
#: packages/email/templates/document-pending.tsx:17
|
#: packages/email/templates/document-pending.tsx:17
|
||||||
msgid "Pending Document"
|
msgid "Pending Document"
|
||||||
msgstr "Pending Document"
|
msgstr "Pending Document"
|
||||||
@ -836,6 +1039,10 @@ msgstr "Read only"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Receives copy"
|
msgstr "Receives copy"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
|
msgid "Recipient"
|
||||||
|
msgstr "Recipient"
|
||||||
|
|
||||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
||||||
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
||||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
||||||
@ -1243,6 +1450,10 @@ msgstr "We've changed your password as you asked. You can now sign in with your
|
|||||||
msgid "Welcome to Documenso!"
|
msgid "Welcome to Documenso!"
|
||||||
msgstr "Welcome to Documenso!"
|
msgstr "Welcome to Documenso!"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:258
|
||||||
|
msgid "You"
|
||||||
|
msgstr "You"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
||||||
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
||||||
msgstr "You are about to send this document to the recipients. Are you sure you want to continue?"
|
msgstr "You are about to send this document to the recipients. Are you sure you want to continue?"
|
||||||
|
|||||||
@ -37,7 +37,7 @@ msgstr "Add document"
|
|||||||
msgid "Add More Users for {0}"
|
msgid "Add More Users for {0}"
|
||||||
msgstr "Add More Users for {0}"
|
msgstr "Add More Users for {0}"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:164
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:165
|
||||||
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
||||||
msgstr "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
msgstr "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ msgstr "Changelog"
|
|||||||
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
||||||
msgstr "Choose a template from the community app store. Or submit your own template for others to use."
|
msgstr "Choose a template from the community app store. Or submit your own template for others to use."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:218
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:219
|
||||||
msgid "Community"
|
msgid "Community"
|
||||||
msgstr "Community"
|
msgstr "Community"
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ msgstr "Fast."
|
|||||||
msgid "Faster, smarter and more beautiful."
|
msgid "Faster, smarter and more beautiful."
|
||||||
msgstr "Faster, smarter and more beautiful."
|
msgstr "Faster, smarter and more beautiful."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:209
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:210
|
||||||
msgid "Finances"
|
msgid "Finances"
|
||||||
msgstr "Finances"
|
msgstr "Finances"
|
||||||
|
|
||||||
@ -241,15 +241,15 @@ msgstr "Get started today."
|
|||||||
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
||||||
msgstr "Get the latest news from Documenso, including product updates, team announcements and more!"
|
msgstr "Get the latest news from Documenso, including product updates, team announcements and more!"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:232
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
||||||
msgid "GitHub: Total Merged PRs"
|
msgid "GitHub: Total Merged PRs"
|
||||||
msgstr "GitHub: Total Merged PRs"
|
msgstr "GitHub: Total Merged PRs"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:250
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
||||||
msgid "GitHub: Total Open Issues"
|
msgid "GitHub: Total Open Issues"
|
||||||
msgstr "GitHub: Total Open Issues"
|
msgstr "GitHub: Total Open Issues"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:224
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
||||||
msgid "GitHub: Total Stars"
|
msgid "GitHub: Total Stars"
|
||||||
msgstr "GitHub: Total Stars"
|
msgstr "GitHub: Total Stars"
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ msgstr "GitHub: Total Stars"
|
|||||||
msgid "Global Salary Bands"
|
msgid "Global Salary Bands"
|
||||||
msgstr "Global Salary Bands"
|
msgstr "Global Salary Bands"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:260
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:261
|
||||||
msgid "Growth"
|
msgid "Growth"
|
||||||
msgstr "Growth"
|
msgstr "Growth"
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ msgstr "Integrated payments with Stripe so you don’t have to worry about getti
|
|||||||
msgid "Integrates with all your favourite tools."
|
msgid "Integrates with all your favourite tools."
|
||||||
msgstr "Integrates with all your favourite tools."
|
msgstr "Integrates with all your favourite tools."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:288
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:289
|
||||||
msgid "Is there more?"
|
msgid "Is there more?"
|
||||||
msgstr "Is there more?"
|
msgstr "Is there more?"
|
||||||
|
|
||||||
@ -305,11 +305,11 @@ msgstr "Location"
|
|||||||
msgid "Make it your own through advanced customization and adjustability."
|
msgid "Make it your own through advanced customization and adjustability."
|
||||||
msgstr "Make it your own through advanced customization and adjustability."
|
msgstr "Make it your own through advanced customization and adjustability."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:198
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:199
|
||||||
msgid "Merged PR's"
|
msgid "Merged PR's"
|
||||||
msgstr "Merged PR's"
|
msgstr "Merged PR's"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:234
|
||||||
msgid "Merged PRs"
|
msgid "Merged PRs"
|
||||||
msgstr "Merged PRs"
|
msgstr "Merged PRs"
|
||||||
|
|
||||||
@ -340,8 +340,8 @@ msgstr "No Credit Card required"
|
|||||||
msgid "None of these work for you? Try self-hosting!"
|
msgid "None of these work for you? Try self-hosting!"
|
||||||
msgstr "None of these work for you? Try self-hosting!"
|
msgstr "None of these work for you? Try self-hosting!"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:193
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:194
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:252
|
||||||
msgid "Open Issues"
|
msgid "Open Issues"
|
||||||
msgstr "Open Issues"
|
msgstr "Open Issues"
|
||||||
|
|
||||||
@ -349,7 +349,7 @@ msgstr "Open Issues"
|
|||||||
msgid "Open Source or Hosted."
|
msgid "Open Source or Hosted."
|
||||||
msgstr "Open Source or Hosted."
|
msgstr "Open Source or Hosted."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:160
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:161
|
||||||
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
||||||
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
||||||
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
||||||
@ -461,7 +461,7 @@ msgstr "Smart."
|
|||||||
msgid "Star on GitHub"
|
msgid "Star on GitHub"
|
||||||
msgstr "Star on GitHub"
|
msgstr "Star on GitHub"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:226
|
||||||
msgid "Stars"
|
msgid "Stars"
|
||||||
msgstr "Stars"
|
msgstr "Stars"
|
||||||
|
|
||||||
@ -496,7 +496,7 @@ msgstr "Template Store (Soon)."
|
|||||||
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
||||||
msgstr "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
msgstr "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:292
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:293
|
||||||
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
||||||
msgstr "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
msgstr "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
||||||
|
|
||||||
@ -509,8 +509,8 @@ msgstr "Title"
|
|||||||
msgid "Total Completed Documents"
|
msgid "Total Completed Documents"
|
||||||
msgstr "Total Completed Documents"
|
msgstr "Total Completed Documents"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:266
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
||||||
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:268
|
||||||
msgid "Total Customers"
|
msgid "Total Customers"
|
||||||
msgstr "Total Customers"
|
msgstr "Total Customers"
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-11-05 02:04\n"
|
"PO-Revision-Date: 2024-11-05 09:34\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Spanish\n"
|
"Language-Team: Spanish\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@ -96,6 +96,90 @@ msgstr "{memberEmail} se unió al siguiente equipo"
|
|||||||
msgid "{memberEmail} left the following team"
|
msgid "{memberEmail} left the following team"
|
||||||
msgstr "{memberEmail} dejó el siguiente equipo"
|
msgstr "{memberEmail} dejó el siguiente equipo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:263
|
||||||
|
msgid "{prefix} added a field"
|
||||||
|
msgstr "{prefix} agregó un campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:275
|
||||||
|
msgid "{prefix} added a recipient"
|
||||||
|
msgstr "{prefix} agregó un destinatario"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:287
|
||||||
|
msgid "{prefix} created the document"
|
||||||
|
msgstr "{prefix} creó el documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:291
|
||||||
|
msgid "{prefix} deleted the document"
|
||||||
|
msgstr "{prefix} eliminó el documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
|
msgid "{prefix} moved the document to team"
|
||||||
|
msgstr "{prefix} movió el documento al equipo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
|
msgid "{prefix} opened the document"
|
||||||
|
msgstr "{prefix} abrió el documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:267
|
||||||
|
msgid "{prefix} removed a field"
|
||||||
|
msgstr "{prefix} eliminó un campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:279
|
||||||
|
msgid "{prefix} removed a recipient"
|
||||||
|
msgstr "{prefix} eliminó un destinatario"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:355
|
||||||
|
msgid "{prefix} resent an email to {0}"
|
||||||
|
msgstr "{prefix} reenviaron un correo electrónico a {0}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:356
|
||||||
|
msgid "{prefix} sent an email to {0}"
|
||||||
|
msgstr "{prefix} envió un correo electrónico a {0}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
|
msgid "{prefix} sent the document"
|
||||||
|
msgstr "{prefix} envió el documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} signed a field"
|
||||||
|
msgstr "{prefix} firmó un campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
|
msgid "{prefix} unsigned a field"
|
||||||
|
msgstr "{prefix} no firmó un campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:271
|
||||||
|
msgid "{prefix} updated a field"
|
||||||
|
msgstr "{prefix} actualizó un campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:283
|
||||||
|
msgid "{prefix} updated a recipient"
|
||||||
|
msgstr "{prefix} actualizó un destinatario"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
|
msgid "{prefix} updated the document"
|
||||||
|
msgstr "{prefix} actualizó el documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
|
msgstr "{prefix} actualizó los requisitos de autorización de acceso al documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
|
msgid "{prefix} updated the document external ID"
|
||||||
|
msgstr "{prefix} actualizó el ID externo del documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
|
msgstr "{prefix} actualizó los requisitos de autenticación para la firma del documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
|
msgid "{prefix} updated the document title"
|
||||||
|
msgstr "{prefix} actualizó el título del documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
|
msgid "{prefix} updated the document visibility"
|
||||||
|
msgstr "{prefix} actualizó la visibilidad del documento"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
||||||
msgid "{recipientName} {action} a document by using one of your direct links"
|
msgid "{recipientName} {action} a document by using one of your direct links"
|
||||||
msgstr "{recipientName} {action} un documento utilizando uno de tus enlaces directos"
|
msgstr "{recipientName} {action} un documento utilizando uno de tus enlaces directos"
|
||||||
@ -104,6 +188,26 @@ msgstr "{recipientName} {action} un documento utilizando uno de tus enlaces dire
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "solicitud de transferencia de propiedad de {teamName}"
|
msgstr "solicitud de transferencia de propiedad de {teamName}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:343
|
||||||
|
msgid "{userName} approved the document"
|
||||||
|
msgstr "{userName} aprobó el documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:344
|
||||||
|
msgid "{userName} CC'd the document"
|
||||||
|
msgstr "{userName} envió una copia del documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
|
msgid "{userName} completed their task"
|
||||||
|
msgstr "{userName} completó su tarea"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:341
|
||||||
|
msgid "{userName} signed the document"
|
||||||
|
msgstr "{userName} firmó el documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
|
msgid "{userName} viewed the document"
|
||||||
|
msgstr "{userName} vio el documento"
|
||||||
|
|
||||||
#: packages/ui/primitives/data-table-pagination.tsx:41
|
#: packages/ui/primitives/data-table-pagination.tsx:41
|
||||||
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
||||||
msgstr "{visibleRows, plural, one {Mostrando # resultado.} other {Mostrando # resultados.}}"
|
msgstr "{visibleRows, plural, one {Mostrando # resultado.} other {Mostrando # resultados.}}"
|
||||||
@ -150,10 +254,34 @@ msgstr "<0>Requerir clave de acceso</0> - El destinatario debe tener una cuenta
|
|||||||
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||||
msgstr "Se creó un documento a partir de tu plantilla directa que requiere que {recipientActionVerb}."
|
msgstr "Se creó un documento a partir de tu plantilla directa que requiere que {recipientActionVerb}."
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:262
|
||||||
|
msgid "A field was added"
|
||||||
|
msgstr "Se añadió un campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:266
|
||||||
|
msgid "A field was removed"
|
||||||
|
msgstr "Se eliminó un campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:270
|
||||||
|
msgid "A field was updated"
|
||||||
|
msgstr "Se actualizó un campo"
|
||||||
|
|
||||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
||||||
msgid "A new member has joined your team"
|
msgid "A new member has joined your team"
|
||||||
msgstr "Un nuevo miembro se ha unido a tu equipo"
|
msgstr "Un nuevo miembro se ha unido a tu equipo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:274
|
||||||
|
msgid "A recipient was added"
|
||||||
|
msgstr "Se añadió un destinatario"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:278
|
||||||
|
msgid "A recipient was removed"
|
||||||
|
msgstr "Se eliminó un destinatario"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:282
|
||||||
|
msgid "A recipient was updated"
|
||||||
|
msgstr "Se actualizó un destinatario"
|
||||||
|
|
||||||
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
||||||
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
||||||
msgstr "Se ha iniciado una solicitud para utilizar tu correo electrónico por {teamName} en Documenso"
|
msgstr "Se ha iniciado una solicitud para utilizar tu correo electrónico por {teamName} en Documenso"
|
||||||
@ -368,6 +496,7 @@ msgstr "Cerrar"
|
|||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:35
|
#: packages/email/template-components/template-document-completed.tsx:35
|
||||||
#: packages/email/template-components/template-document-self-signed.tsx:36
|
#: packages/email/template-components/template-document-self-signed.tsx:36
|
||||||
|
#: packages/lib/constants/document.ts:10
|
||||||
msgid "Completed"
|
msgid "Completed"
|
||||||
msgstr "Completado"
|
msgstr "Completado"
|
||||||
|
|
||||||
@ -450,10 +579,24 @@ msgstr "Receptor de enlace directo"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Acceso al documento"
|
msgstr "Acceso al documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
|
msgid "Document access auth updated"
|
||||||
|
msgstr "Se actualizó la autenticación de acceso al documento"
|
||||||
|
|
||||||
|
#: packages/lib/server-only/document/delete-document.ts:213
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:75
|
#: packages/lib/server-only/document/super-delete-document.ts:75
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Documento cancelado"
|
msgstr "Documento cancelado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:360
|
||||||
|
msgid "Document completed"
|
||||||
|
msgstr "Documento completado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:286
|
||||||
|
msgid "Document created"
|
||||||
|
msgstr "Documento creado"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
||||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
||||||
msgid "Document created from direct template"
|
msgid "Document created from direct template"
|
||||||
@ -463,15 +606,55 @@ msgstr "Documento creado a partir de plantilla directa"
|
|||||||
msgid "Document Creation"
|
msgid "Document Creation"
|
||||||
msgstr "Creación de documento"
|
msgstr "Creación de documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:290
|
||||||
|
msgid "Document deleted"
|
||||||
|
msgstr "Documento eliminado"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/send-delete-email.ts:58
|
#: packages/lib/server-only/document/send-delete-email.ts:58
|
||||||
msgid "Document Deleted!"
|
msgid "Document Deleted!"
|
||||||
msgstr "¡Documento eliminado!"
|
msgstr "¡Documento eliminado!"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
|
msgid "Document external ID updated"
|
||||||
|
msgstr "ID externo del documento actualizado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
|
msgid "Document moved to team"
|
||||||
|
msgstr "Documento movido al equipo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
|
msgid "Document opened"
|
||||||
|
msgstr "Documento abierto"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
|
msgid "Document sent"
|
||||||
|
msgstr "Documento enviado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
|
msgid "Document signing auth updated"
|
||||||
|
msgstr "Se actualizó la autenticación de firma del documento"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
|
msgid "Document title updated"
|
||||||
|
msgstr "Título del documento actualizado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
|
msgid "Document updated"
|
||||||
|
msgstr "Documento actualizado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
|
msgid "Document visibility updated"
|
||||||
|
msgstr "Visibilidad del documento actualizada"
|
||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:64
|
#: packages/email/template-components/template-document-completed.tsx:64
|
||||||
#: packages/ui/components/document/document-download-button.tsx:68
|
#: packages/ui/components/document/document-download-button.tsx:68
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr "Descargar"
|
msgstr "Descargar"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:13
|
||||||
|
msgid "Draft"
|
||||||
|
msgstr "Borrador"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-dropzone.tsx:162
|
#: packages/ui/primitives/document-dropzone.tsx:162
|
||||||
msgid "Drag & drop your PDF here."
|
msgid "Drag & drop your PDF here."
|
||||||
msgstr "Arrastre y suelte su PDF aquí."
|
msgstr "Arrastre y suelte su PDF aquí."
|
||||||
@ -504,6 +687,14 @@ msgstr "Se requiere email"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "Opciones de correo electrónico"
|
msgstr "Opciones de correo electrónico"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email resent"
|
||||||
|
msgstr "Correo electrónico reeenviado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email sent"
|
||||||
|
msgstr "Correo electrónico enviado"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
||||||
msgid "Empty field"
|
msgid "Empty field"
|
||||||
msgstr "Campo vacío"
|
msgstr "Campo vacío"
|
||||||
@ -564,6 +755,14 @@ msgstr "Etiqueta de campo"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Marcador de posición de campo"
|
msgstr "Marcador de posición de campo"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Field signed"
|
||||||
|
msgstr "Campo firmado"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
|
msgid "Field unsigned"
|
||||||
|
msgstr "Campo no firmado"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
||||||
@ -774,6 +973,10 @@ msgstr "Restablecimiento de contraseña exitoso"
|
|||||||
msgid "Password updated!"
|
msgid "Password updated!"
|
||||||
msgstr "¡Contraseña actualizada!"
|
msgstr "¡Contraseña actualizada!"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:16
|
||||||
|
msgid "Pending"
|
||||||
|
msgstr "Pendiente"
|
||||||
|
|
||||||
#: packages/email/templates/document-pending.tsx:17
|
#: packages/email/templates/document-pending.tsx:17
|
||||||
msgid "Pending Document"
|
msgid "Pending Document"
|
||||||
msgstr "Documento pendiente"
|
msgstr "Documento pendiente"
|
||||||
@ -841,6 +1044,10 @@ msgstr "Solo lectura"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Recibe copia"
|
msgstr "Recibe copia"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
|
msgid "Recipient"
|
||||||
|
msgstr "Destinatario"
|
||||||
|
|
||||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
||||||
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
||||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
||||||
@ -1248,6 +1455,10 @@ msgstr "Hemos cambiado tu contraseña como solicitaste. Ahora puedes iniciar ses
|
|||||||
msgid "Welcome to Documenso!"
|
msgid "Welcome to Documenso!"
|
||||||
msgstr "¡Bienvenido a Documenso!"
|
msgstr "¡Bienvenido a Documenso!"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:258
|
||||||
|
msgid "You"
|
||||||
|
msgstr "Tú"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
||||||
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
||||||
msgstr "Está a punto de enviar este documento a los destinatarios. ¿Está seguro de que desea continuar?"
|
msgstr "Está a punto de enviar este documento a los destinatarios. ¿Está seguro de que desea continuar?"
|
||||||
@ -1309,4 +1520,3 @@ msgstr "Tu contraseña ha sido actualizada."
|
|||||||
#: packages/email/templates/team-delete.tsx:30
|
#: packages/email/templates/team-delete.tsx:30
|
||||||
msgid "Your team has been deleted"
|
msgid "Your team has been deleted"
|
||||||
msgstr "Tu equipo ha sido eliminado"
|
msgstr "Tu equipo ha sido eliminado"
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-11-05 02:04\n"
|
"PO-Revision-Date: 2024-11-05 09:34\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Spanish\n"
|
"Language-Team: Spanish\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@ -42,7 +42,7 @@ msgstr "Agregar documento"
|
|||||||
msgid "Add More Users for {0}"
|
msgid "Add More Users for {0}"
|
||||||
msgstr "Agregar más usuarios por {0}"
|
msgstr "Agregar más usuarios por {0}"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:164
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:165
|
||||||
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
||||||
msgstr "Todos nuestros métricas, finanzas y aprendizajes son públicos. Creemos en la transparencia y queremos compartir nuestro viaje contigo. Puedes leer más sobre por qué aquí: <0>Anunciando Métricas Abiertas</0>"
|
msgstr "Todos nuestros métricas, finanzas y aprendizajes son públicos. Creemos en la transparencia y queremos compartir nuestro viaje contigo. Puedes leer más sobre por qué aquí: <0>Anunciando Métricas Abiertas</0>"
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ msgstr "Registro de cambios"
|
|||||||
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
||||||
msgstr "Elige una plantilla de la tienda de aplicaciones de la comunidad. O envía tu propia plantilla para que otros la usen."
|
msgstr "Elige una plantilla de la tienda de aplicaciones de la comunidad. O envía tu propia plantilla para que otros la usen."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:218
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:219
|
||||||
msgid "Community"
|
msgid "Community"
|
||||||
msgstr "Comunidad"
|
msgstr "Comunidad"
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ msgstr "Rápido."
|
|||||||
msgid "Faster, smarter and more beautiful."
|
msgid "Faster, smarter and more beautiful."
|
||||||
msgstr "Más rápido, más inteligente y más hermoso."
|
msgstr "Más rápido, más inteligente y más hermoso."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:209
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:210
|
||||||
msgid "Finances"
|
msgid "Finances"
|
||||||
msgstr "Finanzas"
|
msgstr "Finanzas"
|
||||||
|
|
||||||
@ -246,15 +246,15 @@ msgstr "Comienza hoy."
|
|||||||
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
||||||
msgstr "¡Obtén las últimas noticias de Documenso, incluidas actualizaciones de productos, anuncios del equipo y más!"
|
msgstr "¡Obtén las últimas noticias de Documenso, incluidas actualizaciones de productos, anuncios del equipo y más!"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:232
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
||||||
msgid "GitHub: Total Merged PRs"
|
msgid "GitHub: Total Merged PRs"
|
||||||
msgstr "GitHub: Total de PRs fusionados"
|
msgstr "GitHub: Total de PRs fusionados"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:250
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
||||||
msgid "GitHub: Total Open Issues"
|
msgid "GitHub: Total Open Issues"
|
||||||
msgstr "GitHub: Total de problemas abiertos"
|
msgstr "GitHub: Total de problemas abiertos"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:224
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
||||||
msgid "GitHub: Total Stars"
|
msgid "GitHub: Total Stars"
|
||||||
msgstr "GitHub: Total de estrellas"
|
msgstr "GitHub: Total de estrellas"
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ msgstr "GitHub: Total de estrellas"
|
|||||||
msgid "Global Salary Bands"
|
msgid "Global Salary Bands"
|
||||||
msgstr "Bandas salariales globales"
|
msgstr "Bandas salariales globales"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:260
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:261
|
||||||
msgid "Growth"
|
msgid "Growth"
|
||||||
msgstr "Crecimiento"
|
msgstr "Crecimiento"
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ msgstr "Pagos integrados con Stripe para que no tengas que preocuparte por cobra
|
|||||||
msgid "Integrates with all your favourite tools."
|
msgid "Integrates with all your favourite tools."
|
||||||
msgstr "Se integra con todas tus herramientas favoritas."
|
msgstr "Se integra con todas tus herramientas favoritas."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:288
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:289
|
||||||
msgid "Is there more?"
|
msgid "Is there more?"
|
||||||
msgstr "¿Hay más?"
|
msgstr "¿Hay más?"
|
||||||
|
|
||||||
@ -310,11 +310,11 @@ msgstr "Ubicación"
|
|||||||
msgid "Make it your own through advanced customization and adjustability."
|
msgid "Make it your own through advanced customization and adjustability."
|
||||||
msgstr "Hazlo tuyo a través de personalización y ajustabilidad avanzadas."
|
msgstr "Hazlo tuyo a través de personalización y ajustabilidad avanzadas."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:198
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:199
|
||||||
msgid "Merged PR's"
|
msgid "Merged PR's"
|
||||||
msgstr "PRs fusionados"
|
msgstr "PRs fusionados"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:234
|
||||||
msgid "Merged PRs"
|
msgid "Merged PRs"
|
||||||
msgstr "PRs fusionados"
|
msgstr "PRs fusionados"
|
||||||
|
|
||||||
@ -345,8 +345,8 @@ msgstr "No se requiere tarjeta de crédito"
|
|||||||
msgid "None of these work for you? Try self-hosting!"
|
msgid "None of these work for you? Try self-hosting!"
|
||||||
msgstr "¿Ninguna de estas opciones funciona para ti? ¡Prueba el autoalojamiento!"
|
msgstr "¿Ninguna de estas opciones funciona para ti? ¡Prueba el autoalojamiento!"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:193
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:194
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:252
|
||||||
msgid "Open Issues"
|
msgid "Open Issues"
|
||||||
msgstr "Problemas Abiertos"
|
msgstr "Problemas Abiertos"
|
||||||
|
|
||||||
@ -354,7 +354,7 @@ msgstr "Problemas Abiertos"
|
|||||||
msgid "Open Source or Hosted."
|
msgid "Open Source or Hosted."
|
||||||
msgstr "Código Abierto o Alojado."
|
msgstr "Código Abierto o Alojado."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:160
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:161
|
||||||
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
||||||
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
||||||
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
||||||
@ -466,7 +466,7 @@ msgstr "Inteligente."
|
|||||||
msgid "Star on GitHub"
|
msgid "Star on GitHub"
|
||||||
msgstr "Estrella en GitHub"
|
msgstr "Estrella en GitHub"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:226
|
||||||
msgid "Stars"
|
msgid "Stars"
|
||||||
msgstr "Estrellas"
|
msgstr "Estrellas"
|
||||||
|
|
||||||
@ -501,7 +501,7 @@ msgstr "Tienda de Plantillas (Pronto)."
|
|||||||
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
||||||
msgstr "Eso es increíble. Puedes echar un vistazo a los <0>Problemas</0> actuales y unirte a nuestra <1>Comunidad de Discord</1> para mantenerte al día sobre cuáles son las prioridades actuales. En cualquier caso, somos una comunidad abierta y agradecemos todas las aportaciones, técnicas y no técnicas ❤️"
|
msgstr "Eso es increíble. Puedes echar un vistazo a los <0>Problemas</0> actuales y unirte a nuestra <1>Comunidad de Discord</1> para mantenerte al día sobre cuáles son las prioridades actuales. En cualquier caso, somos una comunidad abierta y agradecemos todas las aportaciones, técnicas y no técnicas ❤️"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:292
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:293
|
||||||
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
||||||
msgstr "Esta página está evolucionando a medida que aprendemos lo que hace una gran empresa de firmas. La actualizaremos cuando tengamos más para compartir."
|
msgstr "Esta página está evolucionando a medida que aprendemos lo que hace una gran empresa de firmas. La actualizaremos cuando tengamos más para compartir."
|
||||||
|
|
||||||
@ -514,8 +514,8 @@ msgstr "Título"
|
|||||||
msgid "Total Completed Documents"
|
msgid "Total Completed Documents"
|
||||||
msgstr "Total de Documentos Completados"
|
msgstr "Total de Documentos Completados"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:266
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
||||||
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:268
|
||||||
msgid "Total Customers"
|
msgid "Total Customers"
|
||||||
msgstr "Total de Clientes"
|
msgstr "Total de Clientes"
|
||||||
|
|
||||||
@ -602,4 +602,3 @@ msgstr "Puedes autoalojar Documenso de forma gratuita o usar nuestra versión al
|
|||||||
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
||||||
msgid "Your browser does not support the video tag."
|
msgid "Your browser does not support the video tag."
|
||||||
msgstr "Tu navegador no soporta la etiqueta de video."
|
msgstr "Tu navegador no soporta la etiqueta de video."
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-11-05 02:04\n"
|
"PO-Revision-Date: 2024-11-05 09:34\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: French\n"
|
"Language-Team: French\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
@ -96,6 +96,90 @@ msgstr "{memberEmail} a rejoint l'équipe suivante"
|
|||||||
msgid "{memberEmail} left the following team"
|
msgid "{memberEmail} left the following team"
|
||||||
msgstr "{memberEmail} a quitté l'équipe suivante"
|
msgstr "{memberEmail} a quitté l'équipe suivante"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:263
|
||||||
|
msgid "{prefix} added a field"
|
||||||
|
msgstr "{prefix} a ajouté un champ"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:275
|
||||||
|
msgid "{prefix} added a recipient"
|
||||||
|
msgstr "{prefix} a ajouté un destinataire"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:287
|
||||||
|
msgid "{prefix} created the document"
|
||||||
|
msgstr "{prefix} a créé le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:291
|
||||||
|
msgid "{prefix} deleted the document"
|
||||||
|
msgstr "{prefix} a supprimé le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
|
msgid "{prefix} moved the document to team"
|
||||||
|
msgstr "{prefix} a déplacé le document vers l'équipe"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
|
msgid "{prefix} opened the document"
|
||||||
|
msgstr "{prefix} a ouvert le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:267
|
||||||
|
msgid "{prefix} removed a field"
|
||||||
|
msgstr "{prefix} a supprimé un champ"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:279
|
||||||
|
msgid "{prefix} removed a recipient"
|
||||||
|
msgstr "{prefix} a supprimé un destinataire"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:355
|
||||||
|
msgid "{prefix} resent an email to {0}"
|
||||||
|
msgstr "{prefix} a renvoyé un e-mail à {0}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:356
|
||||||
|
msgid "{prefix} sent an email to {0}"
|
||||||
|
msgstr "{prefix} a envoyé un email à {0}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
|
msgid "{prefix} sent the document"
|
||||||
|
msgstr "{prefix} a envoyé le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} signed a field"
|
||||||
|
msgstr "{prefix} a signé un champ"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
|
msgid "{prefix} unsigned a field"
|
||||||
|
msgstr "{prefix} n'a pas signé un champ"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:271
|
||||||
|
msgid "{prefix} updated a field"
|
||||||
|
msgstr "{prefix} a mis à jour un champ"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:283
|
||||||
|
msgid "{prefix} updated a recipient"
|
||||||
|
msgstr "{prefix} a mis à jour un destinataire"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
|
msgid "{prefix} updated the document"
|
||||||
|
msgstr "{prefix} a mis à jour le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
|
msgstr "{prefix} a mis à jour les exigences d'authentification d'accès au document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
|
msgid "{prefix} updated the document external ID"
|
||||||
|
msgstr "{prefix} a mis à jour l'ID externe du document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
|
msgstr "{prefix} a mis à jour les exigences d'authentification pour la signature du document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
|
msgid "{prefix} updated the document title"
|
||||||
|
msgstr "{prefix} a mis à jour le titre du document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
|
msgid "{prefix} updated the document visibility"
|
||||||
|
msgstr "{prefix} a mis à jour la visibilité du document"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
#: packages/email/templates/document-created-from-direct-template.tsx:55
|
||||||
msgid "{recipientName} {action} a document by using one of your direct links"
|
msgid "{recipientName} {action} a document by using one of your direct links"
|
||||||
msgstr "{recipientName} {action} un document en utilisant l'un de vos liens directs"
|
msgstr "{recipientName} {action} un document en utilisant l'un de vos liens directs"
|
||||||
@ -104,6 +188,26 @@ msgstr "{recipientName} {action} un document en utilisant l'un de vos liens dire
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "Demande de transfert de propriété de {teamName}"
|
msgstr "Demande de transfert de propriété de {teamName}"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:343
|
||||||
|
msgid "{userName} approved the document"
|
||||||
|
msgstr "{userName} a approuvé le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:344
|
||||||
|
msgid "{userName} CC'd the document"
|
||||||
|
msgstr "{userName} a mis en copie le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
|
msgid "{userName} completed their task"
|
||||||
|
msgstr "{userName} a complété sa tâche"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:341
|
||||||
|
msgid "{userName} signed the document"
|
||||||
|
msgstr "{userName} a signé le document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
|
msgid "{userName} viewed the document"
|
||||||
|
msgstr "{userName} a consulté le document"
|
||||||
|
|
||||||
#: packages/ui/primitives/data-table-pagination.tsx:41
|
#: packages/ui/primitives/data-table-pagination.tsx:41
|
||||||
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
msgid "{visibleRows, plural, one {Showing # result.} other {Showing # results.}}"
|
||||||
msgstr "{visibleRows, plural, one {Affichage de # résultat.} other {Affichage de # résultats.}}"
|
msgstr "{visibleRows, plural, one {Affichage de # résultat.} other {Affichage de # résultats.}}"
|
||||||
@ -150,10 +254,34 @@ msgstr "<0>Exiger une clé d'accès</0> - Le destinataire doit avoir un compte e
|
|||||||
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||||
msgstr "Un document a été créé par votre modèle direct qui nécessite que vous {recipientActionVerb} celui-ci."
|
msgstr "Un document a été créé par votre modèle direct qui nécessite que vous {recipientActionVerb} celui-ci."
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:262
|
||||||
|
msgid "A field was added"
|
||||||
|
msgstr "Un champ a été ajouté"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:266
|
||||||
|
msgid "A field was removed"
|
||||||
|
msgstr "Un champ a été supprimé"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:270
|
||||||
|
msgid "A field was updated"
|
||||||
|
msgstr "Un champ a été mis à jour"
|
||||||
|
|
||||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:90
|
||||||
msgid "A new member has joined your team"
|
msgid "A new member has joined your team"
|
||||||
msgstr "Un nouveau membre a rejoint votre équipe"
|
msgstr "Un nouveau membre a rejoint votre équipe"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:274
|
||||||
|
msgid "A recipient was added"
|
||||||
|
msgstr "Un destinataire a été ajouté"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:278
|
||||||
|
msgid "A recipient was removed"
|
||||||
|
msgstr "Un destinataire a été supprimé"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:282
|
||||||
|
msgid "A recipient was updated"
|
||||||
|
msgstr "Un destinataire a été mis à jour"
|
||||||
|
|
||||||
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
#: packages/lib/server-only/team/create-team-email-verification.ts:142
|
||||||
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
msgid "A request to use your email has been initiated by {teamName} on Documenso"
|
||||||
msgstr "Une demande d'utilisation de votre email a été initiée par {teamName} sur Documenso"
|
msgstr "Une demande d'utilisation de votre email a été initiée par {teamName} sur Documenso"
|
||||||
@ -368,6 +496,7 @@ msgstr "Fermer"
|
|||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:35
|
#: packages/email/template-components/template-document-completed.tsx:35
|
||||||
#: packages/email/template-components/template-document-self-signed.tsx:36
|
#: packages/email/template-components/template-document-self-signed.tsx:36
|
||||||
|
#: packages/lib/constants/document.ts:10
|
||||||
msgid "Completed"
|
msgid "Completed"
|
||||||
msgstr "Terminé"
|
msgstr "Terminé"
|
||||||
|
|
||||||
@ -450,10 +579,24 @@ msgstr "Receveur de lien direct"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Accès au document"
|
msgstr "Accès au document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
|
msgid "Document access auth updated"
|
||||||
|
msgstr "L'authentification d'accès au document a été mise à jour"
|
||||||
|
|
||||||
|
#: packages/lib/server-only/document/delete-document.ts:213
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:75
|
#: packages/lib/server-only/document/super-delete-document.ts:75
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Document Annulé"
|
msgstr "Document Annulé"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:360
|
||||||
|
msgid "Document completed"
|
||||||
|
msgstr "Document terminé"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:286
|
||||||
|
msgid "Document created"
|
||||||
|
msgstr "Document créé"
|
||||||
|
|
||||||
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
#: packages/email/templates/document-created-from-direct-template.tsx:30
|
||||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
#: packages/lib/server-only/template/create-document-from-direct-template.ts:554
|
||||||
msgid "Document created from direct template"
|
msgid "Document created from direct template"
|
||||||
@ -463,15 +606,55 @@ msgstr "Document créé à partir d'un modèle direct"
|
|||||||
msgid "Document Creation"
|
msgid "Document Creation"
|
||||||
msgstr "Création de document"
|
msgstr "Création de document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:290
|
||||||
|
msgid "Document deleted"
|
||||||
|
msgstr "Document supprimé"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/send-delete-email.ts:58
|
#: packages/lib/server-only/document/send-delete-email.ts:58
|
||||||
msgid "Document Deleted!"
|
msgid "Document Deleted!"
|
||||||
msgstr "Document Supprimé !"
|
msgstr "Document Supprimé !"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
|
msgid "Document external ID updated"
|
||||||
|
msgstr "ID externe du document mis à jour"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
|
msgid "Document moved to team"
|
||||||
|
msgstr "Document déplacé vers l'équipe"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
|
msgid "Document opened"
|
||||||
|
msgstr "Document ouvert"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
|
msgid "Document sent"
|
||||||
|
msgstr "Document envoyé"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
|
msgid "Document signing auth updated"
|
||||||
|
msgstr "Authentification de signature de document mise à jour"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
|
msgid "Document title updated"
|
||||||
|
msgstr "Titre du document mis à jour"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
|
msgid "Document updated"
|
||||||
|
msgstr "Document mis à jour"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
|
msgid "Document visibility updated"
|
||||||
|
msgstr "Visibilité du document mise à jour"
|
||||||
|
|
||||||
#: packages/email/template-components/template-document-completed.tsx:64
|
#: packages/email/template-components/template-document-completed.tsx:64
|
||||||
#: packages/ui/components/document/document-download-button.tsx:68
|
#: packages/ui/components/document/document-download-button.tsx:68
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr "Télécharger"
|
msgstr "Télécharger"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:13
|
||||||
|
msgid "Draft"
|
||||||
|
msgstr "Brouillon"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-dropzone.tsx:162
|
#: packages/ui/primitives/document-dropzone.tsx:162
|
||||||
msgid "Drag & drop your PDF here."
|
msgid "Drag & drop your PDF here."
|
||||||
msgstr "Faites glisser et déposez votre PDF ici."
|
msgstr "Faites glisser et déposez votre PDF ici."
|
||||||
@ -504,6 +687,14 @@ msgstr "L'email est requis"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "Options d'email"
|
msgstr "Options d'email"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email resent"
|
||||||
|
msgstr "Email renvoyé"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:353
|
||||||
|
msgid "Email sent"
|
||||||
|
msgstr "Email envoyé"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1123
|
||||||
msgid "Empty field"
|
msgid "Empty field"
|
||||||
msgstr "Champ vide"
|
msgstr "Champ vide"
|
||||||
@ -564,6 +755,14 @@ msgstr "Étiquette du champ"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Espace réservé du champ"
|
msgstr "Espace réservé du champ"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Field signed"
|
||||||
|
msgstr "Champ signé"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
|
msgid "Field unsigned"
|
||||||
|
msgstr "Champ non signé"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
||||||
@ -774,6 +973,10 @@ msgstr "Réinitialisation du mot de passe réussie"
|
|||||||
msgid "Password updated!"
|
msgid "Password updated!"
|
||||||
msgstr "Mot de passe mis à jour !"
|
msgstr "Mot de passe mis à jour !"
|
||||||
|
|
||||||
|
#: packages/lib/constants/document.ts:16
|
||||||
|
msgid "Pending"
|
||||||
|
msgstr "En attente"
|
||||||
|
|
||||||
#: packages/email/templates/document-pending.tsx:17
|
#: packages/email/templates/document-pending.tsx:17
|
||||||
msgid "Pending Document"
|
msgid "Pending Document"
|
||||||
msgstr "Document En Attente"
|
msgstr "Document En Attente"
|
||||||
@ -841,6 +1044,10 @@ msgstr "Lecture seule"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Recevoir une copie"
|
msgstr "Recevoir une copie"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
|
msgid "Recipient"
|
||||||
|
msgstr "Destinataire"
|
||||||
|
|
||||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
||||||
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
||||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
#: packages/ui/primitives/template-flow/add-template-settings.tsx:208
|
||||||
@ -1248,6 +1455,10 @@ msgstr "Nous avons changé votre mot de passe comme demandé. Vous pouvez mainte
|
|||||||
msgid "Welcome to Documenso!"
|
msgid "Welcome to Documenso!"
|
||||||
msgstr "Bienvenue sur Documenso !"
|
msgstr "Bienvenue sur Documenso !"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:258
|
||||||
|
msgid "You"
|
||||||
|
msgstr "Vous"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:44
|
||||||
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
msgid "You are about to send this document to the recipients. Are you sure you want to continue?"
|
||||||
msgstr "Vous êtes sur le point d'envoyer ce document aux destinataires. Êtes-vous sûr de vouloir continuer ?"
|
msgstr "Vous êtes sur le point d'envoyer ce document aux destinataires. Êtes-vous sûr de vouloir continuer ?"
|
||||||
@ -1309,4 +1520,3 @@ msgstr "Votre mot de passe a été mis à jour."
|
|||||||
#: packages/email/templates/team-delete.tsx:30
|
#: packages/email/templates/team-delete.tsx:30
|
||||||
msgid "Your team has been deleted"
|
msgid "Your team has been deleted"
|
||||||
msgstr "Votre équipe a été supprimée"
|
msgstr "Votre équipe a été supprimée"
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-11-05 02:04\n"
|
"PO-Revision-Date: 2024-11-05 09:34\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: French\n"
|
"Language-Team: French\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
@ -42,7 +42,7 @@ msgstr "Ajouter un document"
|
|||||||
msgid "Add More Users for {0}"
|
msgid "Add More Users for {0}"
|
||||||
msgstr "Ajouter plus d'utilisateurs pour {0}"
|
msgstr "Ajouter plus d'utilisateurs pour {0}"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:164
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:165
|
||||||
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
|
||||||
msgstr "Tous nos indicateurs, finances et apprentissages sont publics. Nous croyons en la transparence et souhaitons partager notre parcours avec vous. Vous pouvez en lire plus sur pourquoi ici : <0>Annonce de Open Metrics</0>"
|
msgstr "Tous nos indicateurs, finances et apprentissages sont publics. Nous croyons en la transparence et souhaitons partager notre parcours avec vous. Vous pouvez en lire plus sur pourquoi ici : <0>Annonce de Open Metrics</0>"
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ msgstr "Changelog"
|
|||||||
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
msgid "Choose a template from the community app store. Or submit your own template for others to use."
|
||||||
msgstr "Choisissez un modèle dans la boutique d'applications communautaires. Ou soumettez votre propre modèle pour que d'autres puissent l'utiliser."
|
msgstr "Choisissez un modèle dans la boutique d'applications communautaires. Ou soumettez votre propre modèle pour que d'autres puissent l'utiliser."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:218
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:219
|
||||||
msgid "Community"
|
msgid "Community"
|
||||||
msgstr "Communauté"
|
msgstr "Communauté"
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ msgstr "Rapide."
|
|||||||
msgid "Faster, smarter and more beautiful."
|
msgid "Faster, smarter and more beautiful."
|
||||||
msgstr "Plus rapide, plus intelligent et plus beau."
|
msgstr "Plus rapide, plus intelligent et plus beau."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:209
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:210
|
||||||
msgid "Finances"
|
msgid "Finances"
|
||||||
msgstr "Finances"
|
msgstr "Finances"
|
||||||
|
|
||||||
@ -246,15 +246,15 @@ msgstr "Commencez aujourd'hui."
|
|||||||
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
|
||||||
msgstr "Obtenez les dernières nouvelles de Documenso, y compris les mises à jour de produits, les annonces d'équipe et plus encore !"
|
msgstr "Obtenez les dernières nouvelles de Documenso, y compris les mises à jour de produits, les annonces d'équipe et plus encore !"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:232
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
||||||
msgid "GitHub: Total Merged PRs"
|
msgid "GitHub: Total Merged PRs"
|
||||||
msgstr "GitHub : Total des PRs fusionnées"
|
msgstr "GitHub : Total des PRs fusionnées"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:250
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
||||||
msgid "GitHub: Total Open Issues"
|
msgid "GitHub: Total Open Issues"
|
||||||
msgstr "GitHub : Total des problèmes ouverts"
|
msgstr "GitHub : Total des problèmes ouverts"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:224
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
||||||
msgid "GitHub: Total Stars"
|
msgid "GitHub: Total Stars"
|
||||||
msgstr "GitHub : Nombre total d'étoiles"
|
msgstr "GitHub : Nombre total d'étoiles"
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ msgstr "GitHub : Nombre total d'étoiles"
|
|||||||
msgid "Global Salary Bands"
|
msgid "Global Salary Bands"
|
||||||
msgstr "Bandes de salaire globales"
|
msgstr "Bandes de salaire globales"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:260
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:261
|
||||||
msgid "Growth"
|
msgid "Growth"
|
||||||
msgstr "Croissance"
|
msgstr "Croissance"
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ msgstr "Paiements intégrés avec Stripe afin que vous n'ayez pas à vous soucie
|
|||||||
msgid "Integrates with all your favourite tools."
|
msgid "Integrates with all your favourite tools."
|
||||||
msgstr "S'intègre à tous vos outils préférés."
|
msgstr "S'intègre à tous vos outils préférés."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:288
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:289
|
||||||
msgid "Is there more?"
|
msgid "Is there more?"
|
||||||
msgstr "Y a-t-il plus ?"
|
msgstr "Y a-t-il plus ?"
|
||||||
|
|
||||||
@ -310,11 +310,11 @@ msgstr "Emplacement"
|
|||||||
msgid "Make it your own through advanced customization and adjustability."
|
msgid "Make it your own through advanced customization and adjustability."
|
||||||
msgstr "Faites-en votre propre grâce à une personnalisation avancée et un ajustement."
|
msgstr "Faites-en votre propre grâce à une personnalisation avancée et un ajustement."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:198
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:199
|
||||||
msgid "Merged PR's"
|
msgid "Merged PR's"
|
||||||
msgstr "PRs fusionnées"
|
msgstr "PRs fusionnées"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:234
|
||||||
msgid "Merged PRs"
|
msgid "Merged PRs"
|
||||||
msgstr "PRs fusionnées"
|
msgstr "PRs fusionnées"
|
||||||
|
|
||||||
@ -345,8 +345,8 @@ msgstr "Aucune carte de crédit requise"
|
|||||||
msgid "None of these work for you? Try self-hosting!"
|
msgid "None of these work for you? Try self-hosting!"
|
||||||
msgstr "Aucune de ces options ne fonctionne pour vous ? Essayez l'hébergement autonome !"
|
msgstr "Aucune de ces options ne fonctionne pour vous ? Essayez l'hébergement autonome !"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:193
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:194
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:252
|
||||||
msgid "Open Issues"
|
msgid "Open Issues"
|
||||||
msgstr "Problèmes ouverts"
|
msgstr "Problèmes ouverts"
|
||||||
|
|
||||||
@ -354,7 +354,7 @@ msgstr "Problèmes ouverts"
|
|||||||
msgid "Open Source or Hosted."
|
msgid "Open Source or Hosted."
|
||||||
msgstr "Open Source ou hébergé."
|
msgstr "Open Source ou hébergé."
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:160
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:161
|
||||||
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
#: apps/marketing/src/components/(marketing)/footer.tsx:37
|
||||||
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
#: apps/marketing/src/components/(marketing)/header.tsx:64
|
||||||
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
|
||||||
@ -466,7 +466,7 @@ msgstr "Intelligent."
|
|||||||
msgid "Star on GitHub"
|
msgid "Star on GitHub"
|
||||||
msgstr "Étoile sur GitHub"
|
msgstr "Étoile sur GitHub"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:226
|
||||||
msgid "Stars"
|
msgid "Stars"
|
||||||
msgstr "Étoiles"
|
msgstr "Étoiles"
|
||||||
|
|
||||||
@ -501,7 +501,7 @@ msgstr "Boutique de modèles (Bientôt)."
|
|||||||
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
|
||||||
msgstr "C'est génial. Vous pouvez consulter les <0>Problèmes</0> actuels et rejoindre notre <1>Communauté Discord</1> pour rester à jour sur ce qui est actuellement prioritaire. Dans tous les cas, nous sommes une communauté ouverte et accueillons toutes les contributions, techniques et non techniques ❤️"
|
msgstr "C'est génial. Vous pouvez consulter les <0>Problèmes</0> actuels et rejoindre notre <1>Communauté Discord</1> pour rester à jour sur ce qui est actuellement prioritaire. Dans tous les cas, nous sommes une communauté ouverte et accueillons toutes les contributions, techniques et non techniques ❤️"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:292
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:293
|
||||||
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
|
||||||
msgstr "Cette page évolue à mesure que nous apprenons ce qui fait une grande entreprise de signature. Nous la mettrons à jour lorsque nous aurons plus à partager."
|
msgstr "Cette page évolue à mesure que nous apprenons ce qui fait une grande entreprise de signature. Nous la mettrons à jour lorsque nous aurons plus à partager."
|
||||||
|
|
||||||
@ -514,8 +514,8 @@ msgstr "Titre"
|
|||||||
msgid "Total Completed Documents"
|
msgid "Total Completed Documents"
|
||||||
msgstr "Documents totalisés complétés"
|
msgstr "Documents totalisés complétés"
|
||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:266
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
|
||||||
|
#: apps/marketing/src/app/(marketing)/open/page.tsx:268
|
||||||
msgid "Total Customers"
|
msgid "Total Customers"
|
||||||
msgstr "Total des clients"
|
msgstr "Total des clients"
|
||||||
|
|
||||||
@ -602,4 +602,3 @@ msgstr "Vous pouvez auto-héberger Documenso gratuitement ou utiliser notre vers
|
|||||||
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
||||||
msgid "Your browser does not support the video tag."
|
msgid "Your browser does not support the video tag."
|
||||||
msgstr "Votre navigateur ne prend pas en charge la balise vidéo."
|
msgstr "Votre navigateur ne prend pas en charge la balise vidéo."
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,10 @@
|
|||||||
|
import type { I18n } from '@lingui/core';
|
||||||
|
import { msg } from '@lingui/macro';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import type {
|
import type { DocumentAuditLog, DocumentMeta, Field, Recipient } from '@documenso/prisma/client';
|
||||||
DocumentAuditLog,
|
import { RecipientRole } from '@documenso/prisma/client';
|
||||||
DocumentMeta,
|
|
||||||
Field,
|
|
||||||
Recipient,
|
|
||||||
RecipientRole,
|
|
||||||
} from '@documenso/prisma/client';
|
|
||||||
|
|
||||||
import { RECIPIENT_ROLES_DESCRIPTION_ENG } from '../constants/recipient-roles';
|
|
||||||
import type {
|
import type {
|
||||||
TDocumentAuditLog,
|
TDocumentAuditLog,
|
||||||
TDocumentAuditLogDocumentMetaDiffSchema,
|
TDocumentAuditLogDocumentMetaDiffSchema,
|
||||||
@ -254,129 +250,119 @@ export const diffDocumentMetaChanges = (
|
|||||||
*
|
*
|
||||||
* Provide a userId to prefix the action with the user, example 'X did Y'.
|
* Provide a userId to prefix the action with the user, example 'X did Y'.
|
||||||
*/
|
*/
|
||||||
export const formatDocumentAuditLogActionString = (
|
export const formatDocumentAuditLogAction = (
|
||||||
|
_: I18n['_'],
|
||||||
auditLog: TDocumentAuditLog,
|
auditLog: TDocumentAuditLog,
|
||||||
userId?: number,
|
userId?: number,
|
||||||
) => {
|
) => {
|
||||||
const { prefix, description } = formatDocumentAuditLogAction(auditLog, userId);
|
const prefix = userId === auditLog.userId ? _(msg`You`) : auditLog.name || auditLog.email || '';
|
||||||
|
|
||||||
return prefix ? `${prefix} ${description}` : description;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the audit log into a description of the action.
|
|
||||||
*
|
|
||||||
* Provide a userId to prefix the action with the user, example 'X did Y'.
|
|
||||||
*/
|
|
||||||
// Todo: Translations.
|
|
||||||
export const formatDocumentAuditLogAction = (auditLog: TDocumentAuditLog, userId?: number) => {
|
|
||||||
let prefix = userId === auditLog.userId ? 'You' : auditLog.name || auditLog.email || '';
|
|
||||||
|
|
||||||
const description = match(auditLog)
|
const description = match(auditLog)
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED }, () => ({
|
||||||
anonymous: 'A field was added',
|
anonymous: msg`A field was added`,
|
||||||
identified: 'added a field',
|
identified: msg`${prefix} added a field`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_DELETED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_DELETED }, () => ({
|
||||||
anonymous: 'A field was removed',
|
anonymous: msg`A field was removed`,
|
||||||
identified: 'removed a field',
|
identified: msg`${prefix} removed a field`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED }, () => ({
|
||||||
anonymous: 'A field was updated',
|
anonymous: msg`A field was updated`,
|
||||||
identified: 'updated a field',
|
identified: msg`${prefix} updated a field`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED }, () => ({
|
||||||
anonymous: 'A recipient was added',
|
anonymous: msg`A recipient was added`,
|
||||||
identified: 'added a recipient',
|
identified: msg`${prefix} added a recipient`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_DELETED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_DELETED }, () => ({
|
||||||
anonymous: 'A recipient was removed',
|
anonymous: msg`A recipient was removed`,
|
||||||
identified: 'removed a recipient',
|
identified: msg`${prefix} removed a recipient`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED }, () => ({
|
||||||
anonymous: 'A recipient was updated',
|
anonymous: msg`A recipient was updated`,
|
||||||
identified: 'updated a recipient',
|
identified: msg`${prefix} updated a recipient`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED }, () => ({
|
||||||
anonymous: 'Document created',
|
anonymous: msg`Document created`,
|
||||||
identified: 'created the document',
|
identified: msg`${prefix} created the document`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED }, () => ({
|
||||||
anonymous: 'Document deleted',
|
anonymous: msg`Document deleted`,
|
||||||
identified: 'deleted the document',
|
identified: msg`${prefix} deleted the document`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, () => ({
|
||||||
anonymous: 'Field signed',
|
anonymous: msg`Field signed`,
|
||||||
identified: 'signed a field',
|
identified: msg`${prefix} signed a field`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED }, () => ({
|
||||||
anonymous: 'Field unsigned',
|
anonymous: msg`Field unsigned`,
|
||||||
identified: 'unsigned a field',
|
identified: msg`${prefix} unsigned a field`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VISIBILITY_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VISIBILITY_UPDATED }, () => ({
|
||||||
anonymous: 'Document visibility updated',
|
anonymous: msg`Document visibility updated`,
|
||||||
identified: 'updated the document visibility',
|
identified: msg`${prefix} updated the document visibility`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_GLOBAL_AUTH_ACCESS_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_GLOBAL_AUTH_ACCESS_UPDATED }, () => ({
|
||||||
anonymous: 'Document access auth updated',
|
anonymous: msg`Document access auth updated`,
|
||||||
identified: 'updated the document access auth requirements',
|
identified: msg`${prefix} updated the document access auth requirements`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_GLOBAL_AUTH_ACTION_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_GLOBAL_AUTH_ACTION_UPDATED }, () => ({
|
||||||
anonymous: 'Document signing auth updated',
|
anonymous: msg`Document signing auth updated`,
|
||||||
identified: 'updated the document signing auth requirements',
|
identified: msg`${prefix} updated the document signing auth requirements`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED }, () => ({
|
||||||
anonymous: 'Document updated',
|
anonymous: msg`Document updated`,
|
||||||
identified: 'updated the document',
|
identified: msg`${prefix} updated the document`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED }, () => ({
|
||||||
anonymous: 'Document opened',
|
anonymous: msg`Document opened`,
|
||||||
identified: 'opened the document',
|
identified: msg`${prefix} opened the document`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED }, () => ({
|
||||||
anonymous: 'Document title updated',
|
anonymous: msg`Document title updated`,
|
||||||
identified: 'updated the document title',
|
identified: msg`${prefix} updated the document title`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_EXTERNAL_ID_UPDATED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_EXTERNAL_ID_UPDATED }, () => ({
|
||||||
anonymous: 'Document external ID updated',
|
anonymous: msg`Document external ID updated`,
|
||||||
identified: 'updated the document external ID',
|
identified: msg`${prefix} updated the document external ID`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT }, () => ({
|
||||||
anonymous: 'Document sent',
|
anonymous: msg`Document sent`,
|
||||||
identified: 'sent the document',
|
identified: msg`${prefix} sent the document`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM }, () => ({
|
||||||
anonymous: 'Document moved to team',
|
anonymous: msg`Document moved to team`,
|
||||||
identified: 'moved the document to team',
|
identified: msg`${prefix} 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
|
const userName = prefix || _(msg`Recipient`);
|
||||||
const action = RECIPIENT_ROLES_DESCRIPTION_ENG[data.recipientRole as RecipientRole]?.actioned;
|
|
||||||
|
|
||||||
const value = action ? `${action.toLowerCase()} the document` : 'completed their task';
|
const result = match(data.recipientRole)
|
||||||
|
.with(RecipientRole.SIGNER, () => msg`${userName} signed the document`)
|
||||||
|
.with(RecipientRole.VIEWER, () => msg`${userName} viewed the document`)
|
||||||
|
.with(RecipientRole.APPROVER, () => msg`${userName} approved the document`)
|
||||||
|
.with(RecipientRole.CC, () => msg`${userName} CC'd the document`)
|
||||||
|
.otherwise(() => msg`${userName} completed their task`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
anonymous: `Recipient ${value}`,
|
anonymous: result,
|
||||||
identified: value,
|
identified: result,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT }, ({ data }) => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT }, ({ data }) => ({
|
||||||
anonymous: `Email ${data.isResending ? 'resent' : 'sent'}`,
|
anonymous: data.isResending ? msg`Email resent` : msg`Email sent`,
|
||||||
identified: `${data.isResending ? 'resent' : 'sent'} an email to ${data.recipientEmail}`,
|
identified: data.isResending
|
||||||
|
? msg`${prefix} resent an email to ${data.recipientEmail}`
|
||||||
|
: msg`${prefix} sent an email to ${data.recipientEmail}`,
|
||||||
|
}))
|
||||||
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED }, () => ({
|
||||||
|
anonymous: msg`Document completed`,
|
||||||
|
identified: msg`Document completed`,
|
||||||
}))
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED }, () => {
|
|
||||||
// Clear the prefix since this should be considered an 'anonymous' event.
|
|
||||||
prefix = '';
|
|
||||||
|
|
||||||
return {
|
|
||||||
anonymous: 'Document completed',
|
|
||||||
identified: 'Document completed',
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.exhaustive();
|
.exhaustive();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
prefix,
|
prefix,
|
||||||
description: prefix ? description.identified : description.anonymous,
|
description: _(prefix ? description.identified : description.anonymous),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import { type Field, type Recipient, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
import { type Field, type Recipient, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||||
|
|
||||||
|
export const formatSigningLink = (token: string) => `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${token}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether a recipient can be modified by the document owner.
|
* Whether a recipient can be modified by the document owner.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { createDocument } from '@documenso/lib/server-only/document/create-docum
|
|||||||
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
||||||
import { duplicateDocumentById } from '@documenso/lib/server-only/document/duplicate-document-by-id';
|
import { duplicateDocumentById } from '@documenso/lib/server-only/document/duplicate-document-by-id';
|
||||||
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
|
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
|
||||||
|
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
|
||||||
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';
|
||||||
@ -31,6 +32,7 @@ import {
|
|||||||
ZDownloadAuditLogsMutationSchema,
|
ZDownloadAuditLogsMutationSchema,
|
||||||
ZDownloadCertificateMutationSchema,
|
ZDownloadCertificateMutationSchema,
|
||||||
ZFindDocumentAuditLogsQuerySchema,
|
ZFindDocumentAuditLogsQuerySchema,
|
||||||
|
ZFindDocumentsQuerySchema,
|
||||||
ZGetDocumentByIdQuerySchema,
|
ZGetDocumentByIdQuerySchema,
|
||||||
ZGetDocumentByTokenQuerySchema,
|
ZGetDocumentByTokenQuerySchema,
|
||||||
ZGetDocumentWithDetailsByIdQuerySchema,
|
ZGetDocumentWithDetailsByIdQuerySchema,
|
||||||
@ -190,6 +192,37 @@ export const documentRouter = router({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
findDocuments: authenticatedProcedure
|
||||||
|
.input(ZFindDocumentsQuerySchema)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const { user } = ctx;
|
||||||
|
|
||||||
|
const { search, teamId, templateId, page, perPage, orderBy, source, status } = input;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const documents = await findDocuments({
|
||||||
|
userId: user.id,
|
||||||
|
teamId,
|
||||||
|
templateId,
|
||||||
|
search,
|
||||||
|
source,
|
||||||
|
status,
|
||||||
|
page,
|
||||||
|
perPage,
|
||||||
|
orderBy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return documents;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'We are unable to search for documents. Please try again later.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
findDocumentAuditLogs: authenticatedProcedure
|
findDocumentAuditLogs: authenticatedProcedure
|
||||||
.input(ZFindDocumentAuditLogsQuerySchema)
|
.input(ZFindDocumentAuditLogsQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
|||||||
@ -7,7 +7,30 @@ import {
|
|||||||
} from '@documenso/lib/types/document-auth';
|
} from '@documenso/lib/types/document-auth';
|
||||||
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
|
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||||
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
|
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
|
||||||
import { DocumentSigningOrder, FieldType, RecipientRole } from '@documenso/prisma/client';
|
import {
|
||||||
|
DocumentSigningOrder,
|
||||||
|
DocumentSource,
|
||||||
|
DocumentStatus,
|
||||||
|
FieldType,
|
||||||
|
RecipientRole,
|
||||||
|
} from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
export const ZFindDocumentsQuerySchema = ZBaseTableSearchParamsSchema.extend({
|
||||||
|
teamId: z.number().min(1).optional(),
|
||||||
|
templateId: z.number().min(1).optional(),
|
||||||
|
search: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.catch(() => undefined),
|
||||||
|
source: z.nativeEnum(DocumentSource).optional(),
|
||||||
|
status: z.nativeEnum(DocumentStatus).optional(),
|
||||||
|
orderBy: z
|
||||||
|
.object({
|
||||||
|
column: z.enum(['createdAt']),
|
||||||
|
direction: z.enum(['asc', 'desc']),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
}).omit({ query: true });
|
||||||
|
|
||||||
export const ZFindDocumentAuditLogsQuerySchema = ZBaseTableSearchParamsSchema.extend({
|
export const ZFindDocumentAuditLogsQuerySchema = ZBaseTableSearchParamsSchema.extend({
|
||||||
documentId: z.number().min(1),
|
documentId: z.number().min(1),
|
||||||
|
|||||||
82
packages/ui/components/common/copy-text-button.tsx
Normal file
82
packages/ui/components/common/copy-text-button.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { AnimatePresence } from 'framer-motion';
|
||||||
|
import { CheckSquareIcon, CopyIcon } from 'lucide-react';
|
||||||
|
|
||||||
|
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
|
||||||
|
import { cn } from '../../lib/utils';
|
||||||
|
|
||||||
|
export type CopyTextButtonProps = {
|
||||||
|
value: string;
|
||||||
|
badgeContentUncopied?: React.ReactNode;
|
||||||
|
badgeContentCopied?: React.ReactNode;
|
||||||
|
onCopySuccess?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CopyTextButton = ({
|
||||||
|
value,
|
||||||
|
onCopySuccess,
|
||||||
|
badgeContentUncopied,
|
||||||
|
badgeContentCopied,
|
||||||
|
}: CopyTextButtonProps) => {
|
||||||
|
const [, copy] = useCopyToClipboard();
|
||||||
|
|
||||||
|
const [copiedTimeout, setCopiedTimeout] = useState<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
|
const onCopy = async () => {
|
||||||
|
await copy(value).then(() => onCopySuccess?.());
|
||||||
|
|
||||||
|
if (copiedTimeout) {
|
||||||
|
clearTimeout(copiedTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCopiedTimeout(
|
||||||
|
setTimeout(() => {
|
||||||
|
setCopiedTimeout(null);
|
||||||
|
}, 2000),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="none"
|
||||||
|
className="ml-2 h-7 rounded border bg-neutral-50 px-0.5 font-normal dark:border dark:border-neutral-500 dark:bg-neutral-600"
|
||||||
|
onClick={async () => onCopy()}
|
||||||
|
>
|
||||||
|
<AnimatePresence mode="wait" initial={false}>
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-row items-center"
|
||||||
|
key={copiedTimeout ? 'copied' : 'copy'}
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0, transition: { duration: 0.1 } }}
|
||||||
|
>
|
||||||
|
{copiedTimeout ? badgeContentCopied : badgeContentUncopied}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex h-6 w-6 items-center justify-center rounded transition-all hover:bg-neutral-200 hover:active:bg-neutral-300 dark:hover:bg-neutral-500 dark:hover:active:bg-neutral-400',
|
||||||
|
{
|
||||||
|
'ml-1': Boolean(badgeContentCopied || badgeContentUncopied),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="absolute">
|
||||||
|
{copiedTimeout ? (
|
||||||
|
<CheckSquareIcon className="h-3.5 w-3.5" />
|
||||||
|
) : (
|
||||||
|
<CopyIcon className="h-3.5 w-3.5" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -105,6 +105,7 @@
|
|||||||
"NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS",
|
"NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS",
|
||||||
"NEXT_PRIVATE_SMTP_FROM_NAME",
|
"NEXT_PRIVATE_SMTP_FROM_NAME",
|
||||||
"NEXT_PRIVATE_SMTP_FROM_ADDRESS",
|
"NEXT_PRIVATE_SMTP_FROM_ADDRESS",
|
||||||
|
"NEXT_PRIVATE_SMTP_SERVICE",
|
||||||
"NEXT_PRIVATE_STRIPE_API_KEY",
|
"NEXT_PRIVATE_STRIPE_API_KEY",
|
||||||
"NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET",
|
"NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET",
|
||||||
"NEXT_PRIVATE_GITHUB_TOKEN",
|
"NEXT_PRIVATE_GITHUB_TOKEN",
|
||||||
|
|||||||
Reference in New Issue
Block a user