Compare commits

..

1 Commits

Author SHA1 Message Date
David Nguyen d5c165b22d chore: add embed angular docs 2024-10-08 16:31:19 +11:00
53 changed files with 388 additions and 7246 deletions
@@ -1,4 +1,4 @@
{
"index": "Getting Started",
"index": "Get Started",
"contributing-translations": "Contributing Translations"
}
@@ -0,0 +1,3 @@
{
"index": "Get Started"
}
@@ -0,0 +1,91 @@
---
title: Angular Integration
description: Learn how to use our embedding SDK within your Angular application.
---
# Angular Integration
Our Angular SDK provides a simple way to embed a signing experience within your Angular application. It supports both direct link templates and signing tokens.
## Installation
To install the SDK, run the following command:
```bash
npm install @documenso/embed-angular
```
## Usage
To embed a signing experience, you'll need to provide the token for the document you want to embed. This can be done in a few different ways, depending on your use case.
While the Angular components we provide are configured as standalone components, it can also be used with NgModule. The proceeding examples will assume your project is setup for standalone components.
### Direct Link Template
If you have a direct link template, you can simply provide the token for the template to the `EmbedDirectTemplate` component.
```ts
import { EmbedDirectTemplate } from '@documenso/embed-angular';
@Component({
standalone: true,
selector: 'example-component',
imports: [EmbedDirectTemplate],
template: `
<embed-direct-template token="YOUR_TOKEN_HERE" />
`,
})
export class ExampleComponent {
// Component logic.
}
```
#### Props
| Prop | Type | Description |
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
| token | string | The token for the document you want to embed |
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
| name | string (optional) | The name the signer that will be used by default for signing |
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
| email | string (optional) | The email the signer that will be used by default for signing |
| lockEmail | boolean (optional) | Whether or not the email field should be locked disallowing modifications |
| externalId | string (optional) | The external ID to be used for the document that will be created upon completion |
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
| onFieldSigned | function (optional) | A callback function that will be called when a field has been signed |
| onFieldUnsigned | function (optional) | A callback function that will be called when a field has been unsigned |
### Signing Token
If you have a signing token, you can provide it to the `EmbedSignDocument` component.
```ts
import { EmbedSignDocument } from '@documenso/embed-angular';
@Component({
standalone: true,
selector: 'example-component',
imports: [EmbedSignDocument],
template: `
<embed-sign-document token="YOUR_TOKEN_HERE" />
`,
})
export class ExampleComponent {
// Component logic.
}
```
#### Props
| Prop | Type | Description |
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
| token | string | The token for the document you want to embed |
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
| name | string (optional) | The name the signer that will be used by default for signing |
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
@@ -26,13 +26,14 @@ _For most use-cases we recommend using direct templates, however if you have a n
We support embedding across a range of popular JavaScript frameworks, including:
| Framework | Package |
| --------- | -------------------------------------------------------------------------------- |
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
| Framework | Package |
| --------- | ---------------------------------------------------------------------------------- |
| Angular | [@documenso/embed-angular](https://www.npmjs.com/package/@documenso/embed-angular) |
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested.
@@ -1,5 +1,5 @@
{
"index": "Getting Started",
"index": "Get Started",
"signing-certificate": "Signing Certificate",
"how-to": "How To",
"setting-up-oauth-providers": "Setting up OAuth Providers"
@@ -41,6 +41,8 @@ Mitosis allowed us to quickly target several popular frameworks, including [Reac
I had also hoped to include Angular, but while Mitosis makes it really easy to transpile component, we still have to take care of bundling and packaging the resulting component ourselves. While the above frameworks can all be bundled using Vite.js, Angular still has it's own set of tooling that we would need to learn and use. Given this constraint we opted to put Angular on hold for now while we wait for the newer Vite.js support to mature.
Update: Angular support is now available! Check out the [Angular integration guide](https://docs.documenso.com/developers/embedding/angular) for more information.
### Challenges and Lessons with Mitosis and more
While our experience with Mitosis was largely positive, there were some challenges along the way. For instance, certain state properties with the same names as props caused issues during the transpilation process, leading to type errors and unexpected transpilation results with some targets.
-55
View File
@@ -8,61 +8,6 @@ Check out what's new in the latest version and read our thoughts on it. For more
---
# Documenso v1.7.1: Signing order and document visibility
We're excited to introduce Documenso v1.7.1, bringing you improved control over your document signing process. Here are the key updates:
## 🌟 Key New Features
### 1. Signing Order
Specify the sequence in which recipients sign your documents. This ensures a structured signing process, particularly useful for complex agreements or hierarchical approvals.
<video
src="/changelog/signing-order-demo.mp4"
className="aspect-video w-full"
autoPlay
loop
controls
/>
### 2. Document Visibility
Manage who can view your documents and when. This feature offers greater privacy and flexibility in your document sharing workflows.
<video
src="/changelog/document-visibility-demo.mp4"
className="aspect-video w-full"
autoPlay
loop
controls
/>
## 🔧 Other Improvements
- Added language switcher for easier language selection
- Support for smaller field bounds in documents
- Improved select field UX
- Enhanced template functionality for advanced fields
- Added authOptions to the API
- Various UI refinements and bug fixes
## 💡 Recent Features
Don't forget about these powerful features from our recent v1.7.0 release:
- Embedded Signing Experience
- Copy and Paste Fields
- Customizable Signature Colors
## 👏 Thank You
As always, we're grateful for our community's contributions and feedback. Your input continues to shape Documenso into the leading open-source document signing solution.
We're eager to see how these new features enhance your document workflows. Enjoy exploring Documenso v1.7.1!
---
# Documenso v1.7.0: Embedded Signing, Copy and Paste, and More
We're thrilled to announce the release of Documenso v1.7.0, packed with exciting new features and improvements that enhance document signing flexibility, user experience, and global accessibility.
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@documenso/marketing",
"version": "1.7.2-rc.0",
"version": "1.7.1-rc.3",
"private": true,
"license": "AGPL-3.0",
"scripts": {
@@ -56,4 +56,4 @@
"@types/react": "^18",
"@types/react-dom": "^18"
}
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@documenso/web",
"version": "1.7.2-rc.0",
"version": "1.7.1-rc.3",
"private": true,
"license": "AGPL-3.0",
"scripts": {
@@ -74,4 +74,4 @@
"@types/ua-parser-js": "^0.7.39",
"typescript": "5.2.2"
}
}
}
@@ -16,7 +16,6 @@ import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-documen
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
import { DocumentSearch } from '~/components/(dashboard)/document-search/document-search';
import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-selector';
import { isPeriodSelectorValue } from '~/components/(dashboard)/period-selector/types';
import { DocumentStatus } from '~/components/formatter/document-status';
@@ -26,17 +25,16 @@ import { DataTableSenderFilter } from './data-table-sender-filter';
import { EmptyDocumentState } from './empty-state';
import { UploadDocument } from './upload-document';
export interface DocumentsPageViewProps {
export type DocumentsPageViewProps = {
searchParams?: {
status?: ExtendedDocumentStatus;
period?: PeriodSelectorValue;
page?: string;
perPage?: string;
senderIds?: string;
search?: string;
};
team?: Team & { teamEmail?: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } };
}
};
export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPageViewProps) => {
const { user } = await getRequiredServerComponentSession();
@@ -46,7 +44,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
const page = Number(searchParams.page) || 1;
const perPage = Number(searchParams.perPage) || 20;
const senderIds = parseToIntegerArray(searchParams.senderIds ?? '');
const search = searchParams.search || '';
const currentTeam = team
? { id: team.id, url: team.url, teamEmail: team.teamEmail?.email }
: undefined;
@@ -55,7 +52,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
const getStatOptions: GetStatsInput = {
user,
period,
search,
};
if (team) {
@@ -83,7 +79,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
perPage,
period,
senderIds,
search,
});
const getTabHref = (value: typeof status) => {
@@ -153,9 +148,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
<div className="flex w-48 flex-wrap items-center justify-between gap-x-2 gap-y-4">
<PeriodSelector />
</div>
<div className="flex w-48 flex-wrap items-center justify-between gap-x-2 gap-y-4">
<DocumentSearch initialValue={search} />
</div>
</div>
</div>
@@ -1,11 +1,9 @@
'use client';
import { useMemo } from 'react';
import { Trans } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { RecipientStatusType, getRecipientType } from '@documenso/lib/client-only/recipient-type';
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
import type { DocumentStatus, Recipient } from '@documenso/prisma/client';
@@ -31,26 +29,24 @@ export const StackAvatarsWithTooltip = ({
const { _ } = useLingui();
const waitingRecipients = recipients.filter(
(recipient) => getRecipientType(recipient) === RecipientStatusType.WAITING,
(recipient) => getRecipientType(recipient) === 'waiting',
);
const openedRecipients = recipients.filter(
(recipient) => getRecipientType(recipient) === RecipientStatusType.OPENED,
(recipient) => getRecipientType(recipient) === 'opened',
);
const completedRecipients = recipients.filter(
(recipient) => getRecipientType(recipient) === RecipientStatusType.COMPLETED,
(recipient) => getRecipientType(recipient) === 'completed',
);
const uncompletedRecipients = recipients.filter(
(recipient) => getRecipientType(recipient) === RecipientStatusType.UNSIGNED,
(recipient) => getRecipientType(recipient) === 'unsigned',
);
const sortedRecipients = useMemo(() => recipients.sort((a, b) => a.id - b.id), [recipients]);
return (
<PopoverHover
trigger={children || <StackAvatars recipients={sortedRecipients} />}
trigger={children || <StackAvatars recipients={recipients} />}
contentProps={{
className: 'flex flex-col gap-y-5 py-2',
side: position,
@@ -69,7 +65,7 @@ export const StackAvatarsWithTooltip = ({
type={getRecipientType(recipient)}
fallbackText={recipientAbbreviation(recipient)}
/>
<div>
<div className="">
<p className="text-muted-foreground text-sm">{recipient.email}</p>
<p className="text-muted-foreground/70 text-xs">
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
@@ -1,9 +1,6 @@
import React from 'react';
import {
getExtraRecipientsType,
getRecipientType,
} from '@documenso/lib/client-only/recipient-type';
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
import type { Recipient } from '@documenso/prisma/client';
@@ -16,27 +13,20 @@ export function StackAvatars({ recipients }: { recipients: Recipient[] }) {
const remainingItems = recipients.length - itemsToRender.length;
return itemsToRender.map((recipient: Recipient, index: number) => {
const first = index === 0;
const first = index === 0 ? true : false;
if (index === 4 && remainingItems > 0) {
return (
<StackAvatar
key="extra-recipient"
first={first}
zIndex={String(zIndex - index * 10)}
type={getExtraRecipientsType(recipients.slice(4))}
fallbackText={`+${remainingItems + 1}`}
/>
);
}
const lastItemText =
index === itemsToRender.length - 1 && remainingItems > 0
? `+${remainingItems + 1}`
: undefined;
return (
<StackAvatar
key={recipient.id}
first={first}
zIndex={String(zIndex - index * 10)}
type={getRecipientType(recipient)}
fallbackText={recipientAbbreviation(recipient)}
type={lastItemText && index === 4 ? 'unsigned' : getRecipientType(recipient)}
fallbackText={lastItemText ? lastItemText : recipientAbbreviation(recipient)}
/>
);
});
@@ -1,41 +0,0 @@
'use client';
import { useCallback, useEffect, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { Input } from '@documenso/ui/primitives/input';
export const DocumentSearch = ({ initialValue = '' }: { initialValue?: string }) => {
const router = useRouter();
const searchParams = useSearchParams();
const [searchTerm, setSearchTerm] = useState(initialValue);
const debouncedSearchTerm = useDebouncedValue(searchTerm, 500);
const handleSearch = useCallback(
(term: string) => {
const params = new URLSearchParams(searchParams?.toString() ?? '');
if (term) {
params.set('search', term);
} else {
params.delete('search');
}
router.push(`?${params.toString()}`);
},
[router, searchParams],
);
useEffect(() => {
handleSearch(searchTerm);
}, [debouncedSearchTerm]);
return (
<Input
type="search"
placeholder="Search documents..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
);
};
-3
View File
@@ -1,3 +0,0 @@
/bin/
/node_modules/
-2
View File
@@ -1,2 +0,0 @@
config:
gcp:project: scratchpad-438301
-10
View File
@@ -1,10 +0,0 @@
name: gcloud
runtime:
name: nodejs
options:
packagemanager: npm
description: documenso on google cloud
config:
app--url: 'https://app.documenso.com'
app--smtp-from-name: 'Documenso'
app--smtp-from-address: 'noreply@documenso.com'
-418
View File
@@ -1,418 +0,0 @@
import * as gcp from '@pulumi/gcp';
import * as pulumi from '@pulumi/pulumi';
import * as random from '@pulumi/random';
const config = new pulumi.Config();
// Storage Config
const storageLocation = config.get('storage--location') || 'EU';
const serviceLocation = config.get('service--location') || 'europe-west3';
// KMS Config
const algorithm = config.get('kms--algorithm') || 'RSA_SIGN_PKCS1_4096_SHA256';
// Database Config
const databasePassword = config.get('db--password') || 'password';
// App Config
// App Config
const appUrl = config.require('app--url');
const nextAuthSecret =
config.getSecret('app--nextauth-secret') ||
new random.RandomString('next-auth-secret', {
length: 32,
}).result;
const encryptionKey =
config.getSecret('app--encryption-key') ||
new random.RandomString('encryption-key', {
length: 32,
}).result;
const encryptionSecondaryKey =
config.getSecret('app--encryption-secondary-key') ||
new random.RandomString('encryption-secondary-key', {
length: 32,
}).result;
const googleClientId = config.get('app--google-client-id') || '';
const googleClientSecret = config.get('app--google-client-secret') || '';
const oidcWellKnown = config.get('app--oidc-well-known') || '';
const oidcClientId = config.get('app--oidc-client-id') || '';
const oidcClientSecret = config.get('app--oidc-client-secret') || '';
const oidcProviderLabel = config.get('app--oidc-provider-label') || 'OIDC';
const oidcAllowSignup = config.get('app--oidc-allow-signup') || '';
const oidcSkipVerify = config.get('app--oidc-skip-verify') || '';
const internalWebappUrl = config.get('app--internal-webapp-url') || '';
const smtpTransport = config.get('app--smtp-transport') || 'smtp-auth';
const smtpHost = config.get('app--smtp-host') || '';
const smtpPort = config.get('app--smtp-port') || '';
const smtpUsername = config.get('app--smtp-username') || '';
const smtpPassword = config.getSecret('app--smtp-password') || '';
const smtpApikeyUser = config.get('app--smtp-apikey-user') || '';
const smtpApikey = config.getSecret('app--smtp-apikey') || '';
const smtpSecure = config.get('app--smtp-secure') || '';
const smtpUnsafeIgnoreTls = config.get('app--smtp-unsafe-ignore-tls') || '';
const smtpFromName = config.require('app--smtp-from-name');
const smtpFromAddress = config.require('app--smtp-from-address');
const resendApiKey = config.getSecret('app--resend-api-key') || '';
const documentSizeUploadLimit = config.get('app--document-size-upload-limit') || '50';
const stripeApiKey = config.getSecret('app--stripe-api-key') || '';
const stripeWebhookSecret = config.getSecret('app--stripe-webhook-secret') || '';
const stripeEnterprisePlanMonthlyPriceId =
config.get('app--stripe-enterprise-plan-monthly-price-id') || '';
const jobsProvider = config.get('app--jobs-provider') || 'local';
const triggerApiKey = config.get('app--trigger-api-key') || '';
const triggerApiUrl = config.get('app--trigger-api-url') || '';
const inngestEventKey = config.get('app--inngest-event-key') || '';
const posthogKey = config.get('app--posthog-key') || '';
const billingEnabled = config.get('app--billing-enabled') || 'false';
new gcp.projects.Service('cloud-kms-api', {
project: gcp.config.project,
service: 'cloudkms.googleapis.com',
disableOnDestroy: false,
});
new gcp.projects.Service('cloud-run-api', {
project: gcp.config.project,
service: 'run.googleapis.com',
disableOnDestroy: false,
});
new gcp.projects.Service('compute-api', {
project: gcp.config.project,
service: 'compute.googleapis.com',
disableOnDestroy: false,
});
const signupDisabled = config.get('app--signup-disabled') || 'false';
// Create a GCS bucket for storage
const storageBucket = new gcp.storage.Bucket('documenso-storage', {
name: 'documenso-storage',
location: storageLocation,
});
// Create a service account so we can create a HMAC key to use the S3-compatible storage
// API
const storageServiceAccount = new gcp.serviceaccount.Account('documenso-storage-sa', {
accountId: 'documenso-storage-sa',
displayName: 'Storage Service Account',
});
const appServiceAccount = new gcp.serviceaccount.Account('documenso-app-sa', {
accountId: 'documenso-app-sa',
displayName: 'App Service Account',
});
// Create the HMAC key for the storage service account
const hmacKey = new gcp.storage.HmacKey('documenso-storage-key', {
serviceAccountEmail: storageServiceAccount.email,
});
// Create a Cloud HSM cluster
const hsmCluster = new gcp.kms.KeyRing('hsm-keyring', {
name: 'documenso-hsm-keyring',
location: serviceLocation,
});
const hsmKey = new gcp.kms.CryptoKey('hsm-key', {
name: 'documenso-hsm-key',
keyRing: hsmCluster.id,
purpose: 'ASYMMETRIC_SIGN',
versionTemplate: {
algorithm,
protectionLevel: 'HSM',
},
});
// Create the database
const database = new gcp.sql.DatabaseInstance('documenso-db', {
name: 'documenso-db',
databaseVersion: 'POSTGRES_15',
region: serviceLocation,
settings: {
tier: 'db-custom-2-4096',
diskSize: 50,
diskType: 'PD_SSD',
diskAutoresize: true,
ipConfiguration: {
ipv4Enabled: true,
},
backupConfiguration: {
enabled: true,
startTime: '02:00',
backupRetentionSettings: {
retainedBackups: 30,
},
pointInTimeRecoveryEnabled: true,
transactionLogRetentionDays: 7,
},
databaseFlags: [
{ name: 'max_connections', value: '100' },
{ name: 'log_min_duration_statement', value: '300' },
],
maintenanceWindow: {
day: 7,
hour: 3,
},
insightsConfig: {
queryInsightsEnabled: true,
queryStringLength: 1024,
recordApplicationTags: true,
recordClientAddress: true,
},
},
deletionProtection: true,
});
const user = new gcp.sql.User('documenso-db-user', {
name: 'documenso',
instance: database.name,
password: databasePassword,
});
// Build and deploy the containerized application using Cloud Run
const appService = new gcp.cloudrun.Service('documenso-app', {
name: 'documenso-app',
location: serviceLocation,
template: {
metadata: {
annotations: {
'run.googleapis.com/cloudsql-instances': database.connectionName,
},
},
spec: {
containers: [
{
image: 'documenso/documenso:latest',
resources: {
limits: {
memory: '4Gi',
cpu: '4',
},
},
ports: [{ containerPort: 3000 }],
envs: [
{
name: 'NEXTAUTH_URL',
value: appUrl,
},
{
name: 'NEXTAUTH_SECRET',
value: nextAuthSecret,
},
{
name: 'NEXT_PRIVATE_ENCRYPTION_KEY',
value: encryptionKey,
},
{
name: 'NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY',
value: encryptionSecondaryKey,
},
{
name: 'NEXT_PRIVATE_GOOGLE_CLIENT_ID',
value: googleClientId,
},
{
name: 'NEXT_PRIVATE_GOOGLE_CLIENT_SECRET',
value: googleClientSecret,
},
{
name: 'NEXT_PRIVATE_OIDC_WELL_KNOWN',
value: oidcWellKnown,
},
{
name: 'NEXT_PRIVATE_OIDC_CLIENT_ID',
value: oidcClientId,
},
{
name: 'NEXT_PRIVATE_OIDC_CLIENT_SECRET',
value: oidcClientSecret,
},
{
name: 'NEXT_PRIVATE_OIDC_PROVIDER_LABEL',
value: oidcProviderLabel,
},
{
name: 'NEXT_PRIVATE_OIDC_ALLOW_SIGNUP',
value: oidcAllowSignup,
},
{
name: 'NEXT_PRIVATE_OIDC_SKIP_VERIFY',
value: oidcSkipVerify,
},
{
name: 'NEXT_PUBLIC_WEBAPP_URL',
value: appUrl,
},
{
name: 'NEXT_PRIVATE_INTERNAL_WEBAPP_URL',
value: internalWebappUrl,
},
{
name: 'NEXT_PRIVATE_DATABASE_URL',
value: pulumi.interpolate`postgres://${user.name}:${user.password}@localhost:5432/documenso?host=/cloudsql/${database.connectionName}/`,
},
{
name: 'NEXT_PRIVATE_DIRECT_DATABASE_URL',
value: pulumi.interpolate`postgres://${user.name}:${user.password}@localhost:5432/documenso?host=/cloudsql/${database.connectionName}/`,
},
{
name: 'NEXT_PRIVATE_SIGNING_TRANSPORT',
value: 'gcloud-hsm',
},
{
name: 'NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH',
value: hsmKey.id,
},
{
name: 'NEXT_PUBLIC_UPLOAD_TRANSPORT',
value: 's3',
},
{
name: 'NEXT_PRIVATE_UPLOAD_ENDPOINT',
value: 'https://storage.googleapis.com',
},
{
name: 'NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE',
value: 'true',
},
{
name: 'NEXT_PRIVATE_UPLOAD_REGION',
value: 'auto',
},
{
name: 'NEXT_PRIVATE_UPLOAD_BUCKET',
value: storageBucket.name,
},
{
name: 'NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID',
value: hmacKey.accessId,
},
{
name: 'NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY',
value: hmacKey.secret,
},
{
name: 'NEXT_PRIVATE_SMTP_TRANSPORT',
value: smtpTransport,
},
{
name: 'NEXT_PRIVATE_SMTP_HOST',
value: smtpHost,
},
{
name: 'NEXT_PRIVATE_SMTP_PORT',
value: smtpPort,
},
{
name: 'NEXT_PRIVATE_SMTP_USERNAME',
value: smtpUsername,
},
{
name: 'NEXT_PRIVATE_SMTP_PASSWORD',
value: smtpPassword,
},
{
name: 'NEXT_PRIVATE_SMTP_APIKEY_USER',
value: smtpApikeyUser,
},
{
name: 'NEXT_PRIVATE_SMTP_APIKEY',
value: smtpApikey,
},
{
name: 'NEXT_PRIVATE_SMTP_SECURE',
value: smtpSecure,
},
{
name: 'NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS',
value: smtpUnsafeIgnoreTls,
},
{
name: 'NEXT_PRIVATE_SMTP_FROM_NAME',
value: smtpFromName,
},
{
name: 'NEXT_PRIVATE_SMTP_FROM_ADDRESS',
value: smtpFromAddress,
},
{
name: 'NEXT_PRIVATE_RESEND_API_KEY',
value: resendApiKey,
},
{
name: 'NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT',
value: documentSizeUploadLimit,
},
{
name: 'NEXT_PRIVATE_STRIPE_API_KEY',
value: stripeApiKey,
},
{
name: 'NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET',
value: stripeWebhookSecret,
},
{
name: 'NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID',
value: stripeEnterprisePlanMonthlyPriceId,
},
{
name: 'NEXT_PRIVATE_JOBS_PROVIDER',
value: jobsProvider,
},
{
name: 'NEXT_PRIVATE_TRIGGER_API_KEY',
value: triggerApiKey,
},
{
name: 'NEXT_PRIVATE_TRIGGER_API_URL',
value: triggerApiUrl,
},
{
name: 'NEXT_PRIVATE_INNGEST_EVENT_KEY',
value: inngestEventKey,
},
{
name: 'NEXT_PUBLIC_POSTHOG_KEY',
value: posthogKey,
},
{
name: 'NEXT_PUBLIC_FEATURE_BILLING_ENABLED',
value: billingEnabled,
},
{
name: 'NEXT_PUBLIC_DISABLE_SIGNUP',
value: signupDisabled,
},
],
},
],
timeoutSeconds: 3600,
serviceAccountName: appServiceAccount.email,
},
},
});
// Allow unauthenticated invocations
const iam = new gcp.cloudrun.IamMember('documenso-app-invoker', {
service: appService.name,
location: appService.location,
role: 'roles/run.invoker',
member: 'allUsers',
});
// Allow the Cloud Run service to use the HSM for signing
const _cryptoKeyIAMBinding = new gcp.kms.CryptoKeyIAMBinding('cryptoKeyIAMBinding', {
cryptoKeyId: hsmKey.id,
role: 'roles/cloudkms.signerVerifier',
members: [appServiceAccount.email.apply((email) => `serviceAccount:${email}`)],
});
// Allow the Cloud Run service to access the GCS Bucket
const _bucketIAMBinding = new gcp.storage.BucketIAMBinding('bucketIAMBinding', {
bucket: storageBucket.name,
role: 'roles/storage.objectAdmin',
members: [appServiceAccount.email.apply((email) => `serviceAccount:${email}`)],
});
export const serviceUrl = appService.statuses[0].url;
-3631
View File
File diff suppressed because it is too large Load Diff
-15
View File
@@ -1,15 +0,0 @@
{
"name": "gcloud",
"description": "documenso on google cloud",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"dependencies": {
"@pulumi/pulumi": "*",
"@pulumi/gcp": "7.8.0",
"@pulumi/google-native": "0.31.1",
"@pulumi/random": "^4.16.6"
}
}
-21
View File
@@ -1,21 +0,0 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"."
],
"exclude": [
"node_modules"
]
}
+137 -2449
View File
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "1.7.2-rc.0",
"version": "1.7.1-rc.3",
"scripts": {
"build": "turbo run build",
"build:web": "turbo run build --filter=@documenso/web",
@@ -59,8 +59,7 @@
"name": "@documenso/root",
"workspaces": [
"apps/*",
"packages/*",
"infra/*"
"packages/*"
],
"dependencies": {
"@documenso/pdf-sign": "^0.1.0",
@@ -75,4 +74,4 @@
"trigger.dev": {
"endpointId": "documenso-app"
}
}
}
@@ -1,249 +0,0 @@
import { expect, test } from '@playwright/test';
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
import { seedUser } from '@documenso/prisma/seed/users';
import { apiSignin, apiSignout } from '../fixtures/authentication';
import { checkDocumentTabCount } from '../fixtures/documents';
test('[TEAMS]: search respects team document visibility', async ({ page }) => {
const team = await seedTeam();
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
const managerUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MANAGER });
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
await seedDocuments([
{
sender: team.owner,
recipients: [],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: team.id,
visibility: 'EVERYONE',
title: 'Searchable Document for Everyone',
},
},
{
sender: team.owner,
recipients: [],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: team.id,
visibility: 'MANAGER_AND_ABOVE',
title: 'Searchable Document for Managers',
},
},
{
sender: team.owner,
recipients: [],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: team.id,
visibility: 'ADMIN',
title: 'Searchable Document for Admins',
},
},
]);
const testCases = [
{ user: adminUser, visibleDocs: 3 },
{ user: managerUser, visibleDocs: 2 },
{ user: memberUser, visibleDocs: 1 },
];
for (const { user, visibleDocs } of testCases) {
await apiSignin({
page,
email: user.email,
redirectPath: `/t/${team.url}/documents`,
});
await page.getByPlaceholder('Search documents...').fill('Searchable');
await page.waitForURL(/search=Searchable/);
await checkDocumentTabCount(page, 'All', visibleDocs);
await apiSignout({ page });
}
});
test('[TEAMS]: search does not reveal documents from other teams', async ({ page }) => {
const { team: teamA, teamMember2: teamAMember } = await seedTeamDocuments();
const { team: teamB } = await seedTeamDocuments();
await seedDocuments([
{
sender: teamA.owner,
recipients: [],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: teamA.id,
visibility: 'EVERYONE',
title: 'Unique Team A Document',
},
},
{
sender: teamB.owner,
recipients: [],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: teamB.id,
visibility: 'EVERYONE',
title: 'Unique Team B Document',
},
},
]);
await apiSignin({
page,
email: teamAMember.email,
redirectPath: `/t/${teamA.url}/documents`,
});
await page.getByPlaceholder('Search documents...').fill('Unique');
await page.waitForURL(/search=Unique/);
await checkDocumentTabCount(page, 'All', 1);
await expect(page.getByRole('link', { name: 'Unique Team A Document' })).toBeVisible();
await expect(page.getByRole('link', { name: 'Unique Team B Document' })).not.toBeVisible();
await apiSignout({ page });
});
test('[PERSONAL]: search does not reveal team documents in personal account', async ({ page }) => {
const { team, teamMember2 } = await seedTeamDocuments();
await seedDocuments([
{
sender: teamMember2,
recipients: [],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: null,
title: 'Personal Unique Document',
},
},
{
sender: team.owner,
recipients: [],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: team.id,
visibility: 'EVERYONE',
title: 'Team Unique Document',
},
},
]);
await apiSignin({
page,
email: teamMember2.email,
redirectPath: '/documents',
});
await page.getByPlaceholder('Search documents...').fill('Unique');
await page.waitForURL(/search=Unique/);
await checkDocumentTabCount(page, 'All', 1);
await expect(page.getByRole('link', { name: 'Personal Unique Document' })).toBeVisible();
await expect(page.getByRole('link', { name: 'Team Unique Document' })).not.toBeVisible();
await apiSignout({ page });
});
test('[TEAMS]: search respects recipient visibility regardless of team visibility', async ({
page,
}) => {
const team = await seedTeam();
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
await seedDocuments([
{
sender: team.owner,
recipients: [memberUser],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: team.id,
visibility: 'ADMIN',
title: 'Admin Document with Member Recipient',
},
},
]);
await apiSignin({
page,
email: memberUser.email,
redirectPath: `/t/${team.url}/documents`,
});
await page.getByPlaceholder('Search documents...').fill('Admin Document');
await page.waitForURL(/search=Admin(%20|\+|\s)Document/);
await checkDocumentTabCount(page, 'All', 1);
await expect(
page.getByRole('link', { name: 'Admin Document with Member Recipient' }),
).toBeVisible();
await apiSignout({ page });
});
test('[TEAMS]: search by recipient name respects visibility', async ({ page }) => {
const team = await seedTeam();
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
const memberUser = await seedTeamMember({
teamId: team.id,
role: TeamMemberRole.MEMBER,
name: 'Team Member',
});
const uniqueRecipient = await seedUser();
await seedDocuments([
{
sender: team.owner,
recipients: [uniqueRecipient],
type: DocumentStatus.COMPLETED,
documentOptions: {
teamId: team.id,
visibility: 'ADMIN',
title: 'Admin Document for Unique Recipient',
},
},
]);
// Admin should see the document when searching by recipient name
await apiSignin({
page,
email: adminUser.email,
redirectPath: `/t/${team.url}/documents`,
});
await page.getByPlaceholder('Search documents...').fill('Unique Recipient');
await page.waitForURL(/search=Unique(%20|\+|\s)Recipient/);
await checkDocumentTabCount(page, 'All', 1);
await expect(
page.getByRole('link', { name: 'Admin Document for Unique Recipient' }),
).toBeVisible();
await apiSignout({ page });
// Member should not see the document when searching by recipient name
await apiSignin({
page,
email: memberUser.email,
redirectPath: `/t/${team.url}/documents`,
});
await page.getByPlaceholder('Search documents...').fill('Unique Recipient');
await page.waitForURL(/search=Unique(%20|\+|\s)Recipient/);
await checkDocumentTabCount(page, 'All', 0);
await expect(
page.getByRole('link', { name: 'Admin Document for Unique Recipient' }),
).not.toBeVisible();
await apiSignout({ page });
});
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import type { Field } from '@documenso/prisma/client';
import { Field } from '@documenso/prisma/client';
export const useFieldPageCoords = (field: Field) => {
const [coords, setCoords] = useState({
+5 -33
View File
@@ -1,19 +1,12 @@
import type { Recipient } from '@documenso/prisma/client';
import { ReadStatus, RecipientRole, SendStatus, SigningStatus } from '@documenso/prisma/client';
export enum RecipientStatusType {
COMPLETED = 'completed',
OPENED = 'opened',
WAITING = 'waiting',
UNSIGNED = 'unsigned',
}
export const getRecipientType = (recipient: Recipient) => {
if (
recipient.role === RecipientRole.CC ||
(recipient.sendStatus === SendStatus.SENT && recipient.signingStatus === SigningStatus.SIGNED)
) {
return RecipientStatusType.COMPLETED;
return 'completed';
}
if (
@@ -21,33 +14,12 @@ export const getRecipientType = (recipient: Recipient) => {
recipient.readStatus === ReadStatus.OPENED &&
recipient.signingStatus === SigningStatus.NOT_SIGNED
) {
return RecipientStatusType.OPENED;
return 'opened';
}
if (
recipient.sendStatus === SendStatus.SENT &&
recipient.signingStatus === SigningStatus.NOT_SIGNED
) {
return RecipientStatusType.WAITING;
if (recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED') {
return 'waiting';
}
return RecipientStatusType.UNSIGNED;
};
export const getExtraRecipientsType = (extraRecipients: Recipient[]) => {
const types = extraRecipients.map((r) => getRecipientType(r));
if (types.includes(RecipientStatusType.UNSIGNED)) {
return RecipientStatusType.UNSIGNED;
}
if (types.includes(RecipientStatusType.OPENED)) {
return RecipientStatusType.OPENED;
}
if (types.includes(RecipientStatusType.WAITING)) {
return RecipientStatusType.WAITING;
}
return RecipientStatusType.COMPLETED;
return 'unsigned';
};
@@ -25,7 +25,6 @@ export type FindDocumentsOptions = {
};
period?: PeriodSelectorValue;
senderIds?: number[];
search?: string;
};
export const findDocuments = async ({
@@ -38,7 +37,6 @@ export const findDocuments = async ({
orderBy,
period,
senderIds,
search,
}: FindDocumentsOptions) => {
const { user, team } = await prisma.$transaction(async (tx) => {
const user = await tx.user.findFirstOrThrow({
@@ -94,14 +92,6 @@ export const findDocuments = async ({
})
.otherwise(() => undefined);
const searchFilter: Prisma.DocumentWhereInput = {
OR: [
{ title: { contains: search, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
],
};
const visibilityFilters = [
match(teamMemberRole)
.with(TeamMemberRole.ADMIN, () => ({
@@ -198,7 +188,7 @@ export const findDocuments = async ({
}
const whereClause: Prisma.DocumentWhereInput = {
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }, { ...searchFilter }],
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }],
};
if (period) {
+4 -33
View File
@@ -15,10 +15,9 @@ export type GetStatsInput = {
user: User;
team?: Omit<GetTeamCountsOption, 'createdAt'>;
period?: PeriodSelectorValue;
search?: string;
};
export const getStats = async ({ user, period, search, ...options }: GetStatsInput) => {
export const getStats = async ({ user, period, ...options }: GetStatsInput) => {
let createdAt: Prisma.DocumentWhereInput['createdAt'];
if (period) {
@@ -32,14 +31,8 @@ export const getStats = async ({ user, period, search, ...options }: GetStatsInp
}
const [ownerCounts, notSignedCounts, hasSignedCounts] = await (options.team
? getTeamCounts({
...options.team,
createdAt,
currentUserEmail: user.email,
userId: user.id,
search,
})
: getCounts({ user, createdAt, search }));
? getTeamCounts({ ...options.team, createdAt, currentUserEmail: user.email, userId: user.id })
: getCounts({ user, createdAt }));
const stats: Record<ExtendedDocumentStatus, number> = {
[ExtendedDocumentStatus.DRAFT]: 0,
@@ -79,18 +72,9 @@ export const getStats = async ({ user, period, search, ...options }: GetStatsInp
type GetCountsOption = {
user: User;
createdAt: Prisma.DocumentWhereInput['createdAt'];
search?: string;
};
const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
const searchFilter: Prisma.DocumentWhereInput = {
OR: [
{ title: { contains: search, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
],
};
const getCounts = async ({ user, createdAt }: GetCountsOption) => {
return Promise.all([
// Owner counts.
prisma.document.groupBy({
@@ -103,7 +87,6 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
createdAt,
teamId: null,
deletedAt: null,
AND: [searchFilter],
},
}),
// Not signed counts.
@@ -122,7 +105,6 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
},
},
createdAt,
AND: [searchFilter],
},
}),
// Has signed counts.
@@ -160,7 +142,6 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
},
},
],
AND: [searchFilter],
},
}),
]);
@@ -174,7 +155,6 @@ type GetTeamCountsOption = {
userId: number;
createdAt: Prisma.DocumentWhereInput['createdAt'];
currentTeamMemberRole?: TeamMemberRole;
search?: string;
};
const getTeamCounts = async (options: GetTeamCountsOption) => {
@@ -189,14 +169,6 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
}
: undefined;
const searchFilter: Prisma.DocumentWhereInput = {
OR: [
{ title: { contains: options.search, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: options.search, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: options.search, mode: 'insensitive' } } } },
],
};
let ownerCountsWhereInput: Prisma.DocumentWhereInput = {
userId: userIdWhereClause,
createdAt,
@@ -248,7 +220,6 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
},
},
],
...searchFilter,
};
if (teamEmail) {
@@ -1,9 +1,8 @@
import { nanoid } from '@documenso/lib/universal/id';
import { prisma } from '@documenso/prisma';
import type { DocumentSigningOrder, Field } from '@documenso/prisma/client';
import {
DocumentSigningOrder,
DocumentSource,
type Field,
type Recipient,
RecipientRole,
SendStatus,
@@ -154,7 +153,7 @@ export const createDocumentFromTemplate = async ({
const document = await tx.document.create({
data: {
source: DocumentSource.TEMPLATE,
externalId: externalId || template.externalId,
externalId,
templateId: template.id,
userId,
teamId: template.teamId,
@@ -173,9 +172,7 @@ export const createDocumentFromTemplate = async ({
dateFormat: override?.dateFormat || template.templateMeta?.dateFormat,
redirectUrl: override?.redirectUrl || template.templateMeta?.redirectUrl,
signingOrder:
override?.signingOrder ||
template.templateMeta?.signingOrder ||
DocumentSigningOrder.PARALLEL,
override?.signingOrder || template.templateMeta?.signingOrder || undefined,
},
},
Recipient: {
@@ -100,7 +100,7 @@ export const updateTemplateSettings = async ({
},
data: {
title: data.title,
externalId: data.externalId,
externalId: data.externalId || null,
type: data.type,
publicDescription: data.publicDescription,
publicTitle: data.publicTitle,
+23 -27
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: de\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-10-08 12:05\n"
"PO-Revision-Date: 2024-09-16 16:03\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -115,7 +115,7 @@ msgstr "Admin"
msgid "Advanced Options"
msgstr "Erweiterte Optionen"
#: packages/ui/primitives/document-flow/add-fields.tsx:565
#: packages/ui/primitives/document-flow/add-fields.tsx:527
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
msgid "Advanced settings"
msgstr "Erweiterte Einstellungen"
@@ -159,7 +159,7 @@ msgstr "Unterzeichner kann nicht entfernt werden"
#: packages/ui/primitives/document-flow/add-signers.tsx:221
#~ msgid "Cannot update signer because they have already signed a field"
#~ msgstr "Cannot update signer because they have already signed a field"
#~ msgstr ""
#: packages/lib/constants/recipient-roles.ts:17
msgid "Cc"
@@ -178,7 +178,7 @@ msgstr "CC'd"
msgid "Character Limit"
msgstr "Zeichenbeschränkung"
#: packages/ui/primitives/document-flow/add-fields.tsx:993
#: packages/ui/primitives/document-flow/add-fields.tsx:950
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
msgid "Checkbox"
msgstr "Checkbox"
@@ -207,7 +207,7 @@ msgstr "Schließen"
msgid "Configure Direct Recipient"
msgstr "Direkten Empfänger konfigurieren"
#: packages/ui/primitives/document-flow/add-fields.tsx:566
#: packages/ui/primitives/document-flow/add-fields.tsx:528
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
msgid "Configure the {0} field"
msgstr "Konfigurieren Sie das Feld {0}"
@@ -224,7 +224,7 @@ msgstr "In die Zwischenablage kopiert"
msgid "Custom Text"
msgstr "Benutzerdefinierter Text"
#: packages/ui/primitives/document-flow/add-fields.tsx:889
#: packages/ui/primitives/document-flow/add-fields.tsx:846
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
msgid "Date"
msgstr "Datum"
@@ -256,7 +256,7 @@ msgstr "Herunterladen"
msgid "Drag & drop your PDF here."
msgstr "Ziehen Sie Ihr PDF hierher."
#: packages/ui/primitives/document-flow/add-fields.tsx:1019
#: packages/ui/primitives/document-flow/add-fields.tsx:976
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
msgid "Dropdown"
msgstr "Dropdown"
@@ -265,7 +265,7 @@ msgstr "Dropdown"
msgid "Dropdown options"
msgstr "Dropdown-Optionen"
#: packages/ui/primitives/document-flow/add-fields.tsx:837
#: packages/ui/primitives/document-flow/add-fields.tsx:794
#: packages/ui/primitives/document-flow/add-signature.tsx:272
#: packages/ui/primitives/document-flow/add-signers.tsx:500
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
@@ -278,10 +278,6 @@ msgstr "E-Mail"
msgid "Email Options"
msgstr "E-Mail-Optionen"
#: packages/ui/primitives/document-flow/add-fields.tsx:1082
msgid "Empty field"
msgstr "Leeres Feld"
#: packages/lib/constants/template.ts:8
msgid "Enable Direct Link Signing"
msgstr "Direktlink-Signierung aktivieren"
@@ -388,7 +384,7 @@ msgstr "Nachricht <0>(Optional)</0>"
msgid "Min"
msgstr "Min"
#: packages/ui/primitives/document-flow/add-fields.tsx:863
#: packages/ui/primitives/document-flow/add-fields.tsx:820
#: packages/ui/primitives/document-flow/add-signature.tsx:298
#: packages/ui/primitives/document-flow/add-signers.tsx:535
#: packages/ui/primitives/document-flow/add-signers.tsx:541
@@ -410,12 +406,12 @@ msgstr "Muss unterzeichnen"
msgid "Needs to view"
msgstr "Muss sehen"
#: packages/ui/primitives/document-flow/add-fields.tsx:674
#: packages/ui/primitives/document-flow/add-fields.tsx:631
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
msgid "No recipient matching this description was found."
msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
#: packages/ui/primitives/document-flow/add-fields.tsx:690
#: packages/ui/primitives/document-flow/add-fields.tsx:647
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
msgid "No recipients with this role"
msgstr "Keine Empfänger mit dieser Rolle"
@@ -440,7 +436,7 @@ msgstr "Kein Unterschriftsfeld gefunden"
msgid "No value found."
msgstr "Kein Wert gefunden."
#: packages/ui/primitives/document-flow/add-fields.tsx:941
#: packages/ui/primitives/document-flow/add-fields.tsx:898
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
msgid "Number"
msgstr "Nummer"
@@ -475,7 +471,7 @@ msgstr "Wählen Sie eine Zahl"
msgid "Placeholder"
msgstr "Platzhalter"
#: packages/ui/primitives/document-flow/add-fields.tsx:967
#: packages/ui/primitives/document-flow/add-fields.tsx:924
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
msgid "Radio"
msgstr "Radio"
@@ -511,7 +507,7 @@ msgstr "Rot"
msgid "Redirect URL"
msgstr "Weiterleitungs-URL"
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
msgid "Remove"
msgstr "Entfernen"
@@ -582,7 +578,7 @@ msgstr "Erweiterte Einstellungen anzeigen"
msgid "Sign"
msgstr "Unterschreiben"
#: packages/ui/primitives/document-flow/add-fields.tsx:785
#: packages/ui/primitives/document-flow/add-fields.tsx:742
#: packages/ui/primitives/document-flow/add-signature.tsx:323
#: packages/ui/primitives/document-flow/field-icon.tsx:52
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
@@ -630,7 +626,7 @@ msgstr "Einreichen"
msgid "Template title"
msgstr "Vorlagentitel"
#: packages/ui/primitives/document-flow/add-fields.tsx:915
#: packages/ui/primitives/document-flow/add-fields.tsx:872
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
msgid "Text"
msgstr "Text"
@@ -691,7 +687,7 @@ msgstr "Der Name des Unterzeichners"
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
msgstr "Dies kann überschrieben werden, indem die Authentifizierungsanforderungen im nächsten Schritt direkt für jeden Empfänger festgelegt werden."
#: packages/ui/primitives/document-flow/add-fields.tsx:746
#: packages/ui/primitives/document-flow/add-fields.tsx:703
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
msgstr "Dieses Dokument wurde bereits an diesen Empfänger gesendet. Sie können diesen Empfänger nicht mehr bearbeiten."
@@ -703,17 +699,17 @@ msgstr "Dieses Dokument ist durch ein Passwort geschützt. Bitte geben Sie das P
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
msgstr "Dieses Feld kann nicht geändert oder gelöscht werden. Wenn Sie den direkten Link dieser Vorlage teilen oder zu Ihrem öffentlichen Profil hinzufügen, kann jeder, der darauf zugreift, seinen Namen und seine E-Mail-Adresse eingeben und die ihm zugewiesenen Felder ausfüllen."
#: packages/ui/primitives/document-flow/add-fields.tsx:1050
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
msgstr "Dieser Empfänger kann nicht mehr bearbeitet werden, da er ein Feld unterschrieben oder das Dokument abgeschlossen hat."
msgstr ""
#: packages/ui/primitives/document-flow/add-signers.tsx:165
#: packages/ui/primitives/document-flow/add-signers.tsx:195
#~ msgid "This signer has already received the document."
#~ msgstr "This signer has already received the document."
#~ msgstr "Dieser Unterzeichner hat das Dokument bereits erhalten."
#: packages/ui/primitives/document-flow/add-signers.tsx:194
msgid "This signer has already signed the document."
msgstr "Dieser Unterzeichner hat das Dokument bereits unterschrieben."
msgstr ""
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
msgid "This will override any global settings."
@@ -728,7 +724,7 @@ msgstr "Zeitzone"
msgid "Title"
msgstr "Titel"
#: packages/ui/primitives/document-flow/add-fields.tsx:1033
#: packages/ui/primitives/document-flow/add-fields.tsx:990
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
msgid "To proceed further, please set at least one value for the {0} field."
msgstr "Um fortzufahren, legen Sie bitte mindestens einen Wert für das Feld {0} fest."
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: de\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-10-08 12:05\n"
"PO-Revision-Date: 2024-09-16 14:04\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -387,7 +387,7 @@ msgstr "Unsere selbstgehostete Option ist ideal für kleine Teams und Einzelpers
#: apps/marketing/src/app/(marketing)/open/data.ts:25
#~ msgid "Part-Time"
#~ msgstr "Part-Time"
#~ msgstr "Teilzeit"
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:151
msgid "Premium Profile Name"
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: de\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-10-08 12:05\n"
"PO-Revision-Date: 2024-09-16 16:03\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@@ -789,7 +789,7 @@ msgstr "Unterzeichnung abschließen"
msgid "Complete Viewing"
msgstr "Betrachten abschließen"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:62
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:58
#: apps/web/src/components/formatter/document-status.tsx:28
msgid "Completed"
msgstr "Abgeschlossen"
@@ -1316,7 +1316,7 @@ msgstr "Dokument wird dauerhaft gelöscht"
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:119
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:114
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
#: apps/web/src/app/not-found.tsx:21
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
@@ -2143,7 +2143,7 @@ msgstr "Sobald Sie den QR-Code gescannt oder den Code manuell eingegeben haben,
msgid "Oops! Something went wrong."
msgstr "Hoppla! Etwas ist schief gelaufen."
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:101
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:97
msgid "Opened"
msgstr "Geöffnet"
@@ -3636,7 +3636,7 @@ msgstr "Unable to sign in"
msgid "Unauthorized"
msgstr "Unauthorized"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:116
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:112
msgid "Uncompleted"
msgstr "Uncompleted"
@@ -3848,7 +3848,7 @@ msgstr "Teams ansehen"
msgid "Viewed"
msgstr "Angesehen"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:86
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:82
msgid "Waiting"
msgstr "Warten"
+17 -21
View File
@@ -110,7 +110,7 @@ msgstr "Admin"
msgid "Advanced Options"
msgstr "Advanced Options"
#: packages/ui/primitives/document-flow/add-fields.tsx:565
#: packages/ui/primitives/document-flow/add-fields.tsx:527
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
msgid "Advanced settings"
msgstr "Advanced settings"
@@ -173,7 +173,7 @@ msgstr "CC'd"
msgid "Character Limit"
msgstr "Character Limit"
#: packages/ui/primitives/document-flow/add-fields.tsx:993
#: packages/ui/primitives/document-flow/add-fields.tsx:950
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
msgid "Checkbox"
msgstr "Checkbox"
@@ -202,7 +202,7 @@ msgstr "Close"
msgid "Configure Direct Recipient"
msgstr "Configure Direct Recipient"
#: packages/ui/primitives/document-flow/add-fields.tsx:566
#: packages/ui/primitives/document-flow/add-fields.tsx:528
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
msgid "Configure the {0} field"
msgstr "Configure the {0} field"
@@ -219,7 +219,7 @@ msgstr "Copied to clipboard"
msgid "Custom Text"
msgstr "Custom Text"
#: packages/ui/primitives/document-flow/add-fields.tsx:889
#: packages/ui/primitives/document-flow/add-fields.tsx:846
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
msgid "Date"
msgstr "Date"
@@ -251,7 +251,7 @@ msgstr "Download"
msgid "Drag & drop your PDF here."
msgstr "Drag & drop your PDF here."
#: packages/ui/primitives/document-flow/add-fields.tsx:1019
#: packages/ui/primitives/document-flow/add-fields.tsx:976
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
msgid "Dropdown"
msgstr "Dropdown"
@@ -260,7 +260,7 @@ msgstr "Dropdown"
msgid "Dropdown options"
msgstr "Dropdown options"
#: packages/ui/primitives/document-flow/add-fields.tsx:837
#: packages/ui/primitives/document-flow/add-fields.tsx:794
#: packages/ui/primitives/document-flow/add-signature.tsx:272
#: packages/ui/primitives/document-flow/add-signers.tsx:500
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
@@ -273,10 +273,6 @@ msgstr "Email"
msgid "Email Options"
msgstr "Email Options"
#: packages/ui/primitives/document-flow/add-fields.tsx:1082
msgid "Empty field"
msgstr "Empty field"
#: packages/lib/constants/template.ts:8
msgid "Enable Direct Link Signing"
msgstr "Enable Direct Link Signing"
@@ -383,7 +379,7 @@ msgstr "Message <0>(Optional)</0>"
msgid "Min"
msgstr "Min"
#: packages/ui/primitives/document-flow/add-fields.tsx:863
#: packages/ui/primitives/document-flow/add-fields.tsx:820
#: packages/ui/primitives/document-flow/add-signature.tsx:298
#: packages/ui/primitives/document-flow/add-signers.tsx:535
#: packages/ui/primitives/document-flow/add-signers.tsx:541
@@ -405,12 +401,12 @@ msgstr "Needs to sign"
msgid "Needs to view"
msgstr "Needs to view"
#: packages/ui/primitives/document-flow/add-fields.tsx:674
#: packages/ui/primitives/document-flow/add-fields.tsx:631
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
msgid "No recipient matching this description was found."
msgstr "No recipient matching this description was found."
#: packages/ui/primitives/document-flow/add-fields.tsx:690
#: packages/ui/primitives/document-flow/add-fields.tsx:647
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
msgid "No recipients with this role"
msgstr "No recipients with this role"
@@ -435,7 +431,7 @@ msgstr "No signature field found"
msgid "No value found."
msgstr "No value found."
#: packages/ui/primitives/document-flow/add-fields.tsx:941
#: packages/ui/primitives/document-flow/add-fields.tsx:898
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
msgid "Number"
msgstr "Number"
@@ -470,7 +466,7 @@ msgstr "Pick a number"
msgid "Placeholder"
msgstr "Placeholder"
#: packages/ui/primitives/document-flow/add-fields.tsx:967
#: packages/ui/primitives/document-flow/add-fields.tsx:924
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
msgid "Radio"
msgstr "Radio"
@@ -506,7 +502,7 @@ msgstr "Red"
msgid "Redirect URL"
msgstr "Redirect URL"
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
msgid "Remove"
msgstr "Remove"
@@ -577,7 +573,7 @@ msgstr "Show advanced settings"
msgid "Sign"
msgstr "Sign"
#: packages/ui/primitives/document-flow/add-fields.tsx:785
#: packages/ui/primitives/document-flow/add-fields.tsx:742
#: packages/ui/primitives/document-flow/add-signature.tsx:323
#: packages/ui/primitives/document-flow/field-icon.tsx:52
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
@@ -625,7 +621,7 @@ msgstr "Submit"
msgid "Template title"
msgstr "Template title"
#: packages/ui/primitives/document-flow/add-fields.tsx:915
#: packages/ui/primitives/document-flow/add-fields.tsx:872
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
msgid "Text"
msgstr "Text"
@@ -686,7 +682,7 @@ msgstr "The signer's name"
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
msgstr "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
#: packages/ui/primitives/document-flow/add-fields.tsx:746
#: packages/ui/primitives/document-flow/add-fields.tsx:703
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
msgstr "This document has already been sent to this recipient. You can no longer edit this recipient."
@@ -698,7 +694,7 @@ msgstr "This document is password protected. Please enter the password to view t
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
msgstr "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
#: packages/ui/primitives/document-flow/add-fields.tsx:1050
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
msgstr "This recipient can no longer be modified as they have signed a field, or completed the document."
@@ -723,7 +719,7 @@ msgstr "Time Zone"
msgid "Title"
msgstr "Title"
#: packages/ui/primitives/document-flow/add-fields.tsx:1033
#: packages/ui/primitives/document-flow/add-fields.tsx:990
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
msgid "To proceed further, please set at least one value for the {0} field."
msgstr "To proceed further, please set at least one value for the {0} field."
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5 -5
View File
@@ -784,7 +784,7 @@ msgstr "Complete Signing"
msgid "Complete Viewing"
msgstr "Complete Viewing"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:62
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:58
#: apps/web/src/components/formatter/document-status.tsx:28
msgid "Completed"
msgstr "Completed"
@@ -1311,7 +1311,7 @@ msgstr "Document will be permanently deleted"
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:119
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:114
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
#: apps/web/src/app/not-found.tsx:21
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
@@ -2138,7 +2138,7 @@ msgstr "Once you have scanned the QR code or entered the code manually, enter th
msgid "Oops! Something went wrong."
msgstr "Oops! Something went wrong."
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:101
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:97
msgid "Opened"
msgstr "Opened"
@@ -3631,7 +3631,7 @@ msgstr "Unable to sign in"
msgid "Unauthorized"
msgstr "Unauthorized"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:116
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:112
msgid "Uncompleted"
msgstr "Uncompleted"
@@ -3843,7 +3843,7 @@ msgstr "View teams"
msgid "Viewed"
msgstr "Viewed"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:86
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:82
msgid "Waiting"
msgstr "Waiting"
+22 -30
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: fr\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-10-08 12:05\n"
"PO-Revision-Date: 2024-09-19 09:18\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@@ -115,7 +115,7 @@ msgstr "Administrateur"
msgid "Advanced Options"
msgstr "Options avancées"
#: packages/ui/primitives/document-flow/add-fields.tsx:565
#: packages/ui/primitives/document-flow/add-fields.tsx:527
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
msgid "Advanced settings"
msgstr "Paramètres avancés"
@@ -157,10 +157,6 @@ msgstr "Annuler"
msgid "Cannot remove signer"
msgstr "Impossible de retirer le signataire"
#: packages/ui/primitives/document-flow/add-signers.tsx:221
#~ msgid "Cannot update signer because they have already signed a field"
#~ msgstr "Cannot update signer because they have already signed a field"
#: packages/lib/constants/recipient-roles.ts:17
msgid "Cc"
msgstr "Cc"
@@ -178,7 +174,7 @@ msgstr "CC'd"
msgid "Character Limit"
msgstr "Limite de caractères"
#: packages/ui/primitives/document-flow/add-fields.tsx:993
#: packages/ui/primitives/document-flow/add-fields.tsx:950
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
msgid "Checkbox"
msgstr "Case à cocher"
@@ -207,7 +203,7 @@ msgstr "Fermer"
msgid "Configure Direct Recipient"
msgstr "Configurer le destinataire direct"
#: packages/ui/primitives/document-flow/add-fields.tsx:566
#: packages/ui/primitives/document-flow/add-fields.tsx:528
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
msgid "Configure the {0} field"
msgstr "Configurer le champ {0}"
@@ -224,7 +220,7 @@ msgstr "Copié dans le presse-papiers"
msgid "Custom Text"
msgstr "Texte personnalisé"
#: packages/ui/primitives/document-flow/add-fields.tsx:889
#: packages/ui/primitives/document-flow/add-fields.tsx:846
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
msgid "Date"
msgstr "Date"
@@ -256,7 +252,7 @@ msgstr "Télécharger"
msgid "Drag & drop your PDF here."
msgstr "Faites glisser et déposez votre PDF ici."
#: packages/ui/primitives/document-flow/add-fields.tsx:1019
#: packages/ui/primitives/document-flow/add-fields.tsx:976
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
msgid "Dropdown"
msgstr "Liste déroulante"
@@ -265,7 +261,7 @@ msgstr "Liste déroulante"
msgid "Dropdown options"
msgstr "Options de liste déroulante"
#: packages/ui/primitives/document-flow/add-fields.tsx:837
#: packages/ui/primitives/document-flow/add-fields.tsx:794
#: packages/ui/primitives/document-flow/add-signature.tsx:272
#: packages/ui/primitives/document-flow/add-signers.tsx:500
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
@@ -278,10 +274,6 @@ msgstr "Email"
msgid "Email Options"
msgstr "Options d'email"
#: packages/ui/primitives/document-flow/add-fields.tsx:1082
msgid "Empty field"
msgstr "Champ vide"
#: packages/lib/constants/template.ts:8
msgid "Enable Direct Link Signing"
msgstr "Activer la signature de lien direct"
@@ -388,7 +380,7 @@ msgstr "Message <0>(Optionnel)</0>"
msgid "Min"
msgstr "Min"
#: packages/ui/primitives/document-flow/add-fields.tsx:863
#: packages/ui/primitives/document-flow/add-fields.tsx:820
#: packages/ui/primitives/document-flow/add-signature.tsx:298
#: packages/ui/primitives/document-flow/add-signers.tsx:535
#: packages/ui/primitives/document-flow/add-signers.tsx:541
@@ -410,12 +402,12 @@ msgstr "Nécessite une signature"
msgid "Needs to view"
msgstr "Nécessite une visualisation"
#: packages/ui/primitives/document-flow/add-fields.tsx:674
#: packages/ui/primitives/document-flow/add-fields.tsx:631
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
msgid "No recipient matching this description was found."
msgstr "Aucun destinataire correspondant à cette description n'a été trouvé."
#: packages/ui/primitives/document-flow/add-fields.tsx:690
#: packages/ui/primitives/document-flow/add-fields.tsx:647
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
msgid "No recipients with this role"
msgstr "Aucun destinataire avec ce rôle"
@@ -440,7 +432,7 @@ msgstr "Aucun champ de signature trouvé"
msgid "No value found."
msgstr "Aucune valeur trouvée."
#: packages/ui/primitives/document-flow/add-fields.tsx:941
#: packages/ui/primitives/document-flow/add-fields.tsx:898
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
msgid "Number"
msgstr "Numéro"
@@ -475,7 +467,7 @@ msgstr "Choisissez un numéro"
msgid "Placeholder"
msgstr "Espace réservé"
#: packages/ui/primitives/document-flow/add-fields.tsx:967
#: packages/ui/primitives/document-flow/add-fields.tsx:924
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
msgid "Radio"
msgstr "Radio"
@@ -511,7 +503,7 @@ msgstr "Rouge"
msgid "Redirect URL"
msgstr "URL de redirection"
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
msgid "Remove"
msgstr "Retirer"
@@ -582,7 +574,7 @@ msgstr "Afficher les paramètres avancés"
msgid "Sign"
msgstr "Signer"
#: packages/ui/primitives/document-flow/add-fields.tsx:785
#: packages/ui/primitives/document-flow/add-fields.tsx:742
#: packages/ui/primitives/document-flow/add-signature.tsx:323
#: packages/ui/primitives/document-flow/field-icon.tsx:52
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
@@ -630,7 +622,7 @@ msgstr "Soumettre"
msgid "Template title"
msgstr "Titre du modèle"
#: packages/ui/primitives/document-flow/add-fields.tsx:915
#: packages/ui/primitives/document-flow/add-fields.tsx:872
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
msgid "Text"
msgstr "Texte"
@@ -691,7 +683,7 @@ msgstr "Le nom du signataire"
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
msgstr "Cela peut être remplacé par le paramétrage direct des exigences d'authentification pour chaque destinataire à l'étape suivante."
#: packages/ui/primitives/document-flow/add-fields.tsx:746
#: packages/ui/primitives/document-flow/add-fields.tsx:703
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
msgstr "Ce document a déjà été envoyé à ce destinataire. Vous ne pouvez plus modifier ce destinataire."
@@ -703,17 +695,17 @@ msgstr "Ce document est protégé par mot de passe. Veuillez entrer le mot de pa
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
msgstr "Ce champ ne peut pas être modifié ou supprimé. Lorsque vous partagez le lien direct de ce modèle ou l'ajoutez à votre profil public, toute personne qui y accède peut saisir son nom et son email, et remplir les champs qui lui sont attribués."
#: packages/ui/primitives/document-flow/add-fields.tsx:1050
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
msgstr "Ce destinataire ne peut plus être modifié car il a signé un champ ou complété le document."
msgstr ""
#: packages/ui/primitives/document-flow/add-signers.tsx:165
#: packages/ui/primitives/document-flow/add-signers.tsx:195
#~ msgid "This signer has already received the document."
#~ msgstr "This signer has already received the document."
#~ msgstr "Ce signataire a déjà reçu le document."
#: packages/ui/primitives/document-flow/add-signers.tsx:194
msgid "This signer has already signed the document."
msgstr "Ce signataire a déjà signé le document."
msgstr ""
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
msgid "This will override any global settings."
@@ -728,7 +720,7 @@ msgstr "Fuseau horaire"
msgid "Title"
msgstr "Titre"
#: packages/ui/primitives/document-flow/add-fields.tsx:1033
#: packages/ui/primitives/document-flow/add-fields.tsx:990
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
msgid "To proceed further, please set at least one value for the {0} field."
msgstr "Pour continuer, veuillez définir au moins une valeur pour le champ {0}."
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: fr\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-10-08 12:05\n"
"PO-Revision-Date: 2024-09-19 09:18\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@@ -387,7 +387,7 @@ msgstr "Notre option auto-hébergée est idéale pour les petites équipes et le
#: apps/marketing/src/app/(marketing)/open/data.ts:25
#~ msgid "Part-Time"
#~ msgstr "Part-Time"
#~ msgstr "Temps partiel"
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:151
msgid "Premium Profile Name"
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -8,7 +8,7 @@ msgstr ""
"Language: fr\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-10-08 12:05\n"
"PO-Revision-Date: 2024-09-19 09:18\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@@ -789,7 +789,7 @@ msgstr "Compléter la signature"
msgid "Complete Viewing"
msgstr "Compléter la visualisation"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:62
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:58
#: apps/web/src/components/formatter/document-status.tsx:28
msgid "Completed"
msgstr "Complété"
@@ -1316,7 +1316,7 @@ msgstr "Le document sera supprimé de manière permanente"
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:119
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:114
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
#: apps/web/src/app/not-found.tsx:21
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
@@ -2143,7 +2143,7 @@ msgstr "Une fois que vous avez scanné le code QR ou saisi le code manuellement,
msgid "Oops! Something went wrong."
msgstr "Oups ! Quelque chose a mal tourné."
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:101
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:97
msgid "Opened"
msgstr "Ouvert"
@@ -3636,7 +3636,7 @@ msgstr "Impossible de se connecter"
msgid "Unauthorized"
msgstr "Non autorisé"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:116
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:112
msgid "Uncompleted"
msgstr "Non complet"
@@ -3848,7 +3848,7 @@ msgstr "Voir les équipes"
msgid "Viewed"
msgstr "Vu"
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:86
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:82
msgid "Waiting"
msgstr "En attente"
-24
View File
@@ -39,27 +39,3 @@ export const validateFieldsInserted = (fields: Field[]): boolean => {
return uninsertedFields.length === 0;
};
export const validateFieldsUninserted = (): boolean => {
const fieldCardElements = document.getElementsByClassName('react-draggable');
const errorElements: HTMLElement[] = [];
Array.from(fieldCardElements).forEach((element) => {
const innerDiv = element.querySelector('div');
const hasError = innerDiv?.getAttribute('data-error') === 'true';
if (hasError) {
errorElements.push(element as HTMLElement);
} else {
element.removeAttribute('data-error');
}
});
if (errorElements.length > 0) {
errorElements[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
return false;
}
return errorElements.length === 0;
};
+1 -3
View File
@@ -105,15 +105,13 @@ export const unseedTeam = async (teamUrl: string) => {
type SeedTeamMemberOptions = {
teamId: number;
role?: TeamMemberRole;
name?: string;
};
export const seedTeamMember = async ({
teamId,
name,
role = TeamMemberRole.ADMIN,
}: SeedTeamMemberOptions) => {
const user = await seedUser({ name });
const user = await seedUser();
await prisma.teamMember.create({
data: {
+2 -7
View File
@@ -21,13 +21,8 @@ export const seedUser = async ({
password = 'password',
verified = true,
}: SeedUserOptions = {}) => {
let url = name;
if (name) {
url = nanoid();
} else {
if (!name) {
name = nanoid();
url = name;
}
if (!email) {
@@ -40,7 +35,7 @@ export const seedUser = async ({
email,
password: hashSync(password),
emailVerified: verified ? new Date() : undefined,
url,
url: name,
},
});
};
@@ -101,7 +101,7 @@ export const templateRouter = router({
.input(ZCreateDocumentFromTemplateMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { templateId, teamId, recipients } = input;
const { templateId, teamId } = input;
const limits = await getServerLimits({ email: ctx.user.email, teamId });
@@ -115,7 +115,7 @@ export const templateRouter = router({
templateId,
teamId,
userId: ctx.user.id,
recipients,
recipients: input.recipients,
requestMetadata,
});
@@ -49,7 +49,7 @@ export function FieldToolTip({ children, color, className = '', field }: FieldTo
}}
>
<TooltipProvider>
<Tooltip delayDuration={0} open={!field.inserted || !field.fieldMeta}>
<Tooltip delayDuration={0} open={!field.inserted}>
<TooltipTrigger className="absolute inset-0 w-full"></TooltipTrigger>
<TooltipContent className={tooltipVariants({ color, className })} sideOffset={2}>
@@ -5,7 +5,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Caveat } from 'next/font/google';
import { Trans, msg } from '@lingui/macro';
import { Prisma } from '@prisma/client';
import {
CalendarDays,
Check,
@@ -33,7 +32,6 @@ import {
ZFieldMetaSchema,
} from '@documenso/lib/types/field-meta';
import { nanoid } from '@documenso/lib/universal/id';
import { validateFieldsUninserted } from '@documenso/lib/utils/fields';
import {
canRecipientBeModified,
canRecipientFieldsBeModified,
@@ -41,7 +39,6 @@ import {
import type { Field, Recipient } from '@documenso/prisma/client';
import { FieldType, RecipientRole, SendStatus } from '@documenso/prisma/client';
import { FieldToolTip } from '../../components/field/field-tooltip';
import { getSignerColorStyles, useSignerColors } from '../../lib/signer-colors';
import { cn } from '../../lib/utils';
import { Alert, AlertDescription } from '../alert';
@@ -99,6 +96,12 @@ export type AddFieldsFormProps = {
teamId?: number;
};
/*
I hate this, but due to TailwindCSS JIT, I couldnn't find a better way to do this for now.
TODO: Try to find a better way to do this.
*/
export const AddFieldsFormPartial = ({
documentFlow,
hideRecipients = false,
@@ -192,7 +195,6 @@ export const AddFieldsFormPartial = ({
const selectedSignerStyles = useSignerColors(
selectedSignerIndex === -1 ? 0 : selectedSignerIndex,
);
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
const filterFieldsWithEmptyValues = (fields: typeof localFields, fieldType: string) =>
fields
@@ -226,38 +228,6 @@ export const AddFieldsFormPartial = ({
const hasErrors =
emptyCheckboxFields.length > 0 || emptyRadioFields.length > 0 || emptySelectFields.length > 0;
const fieldsWithError = useMemo(() => {
const fields = localFields.filter((field) => {
const hasError =
((field.type === FieldType.CHECKBOX ||
field.type === FieldType.RADIO ||
field.type === FieldType.DROPDOWN) &&
field.fieldMeta === undefined) ||
(field.fieldMeta && 'values' in field.fieldMeta && field?.fieldMeta?.values?.length === 0);
return hasError;
});
const mappedFields = fields.map((field) => ({
id: field.nativeId ?? 0,
secondaryId: field.formId,
documentId: null,
templateId: null,
recipientId: 0,
type: field.type,
page: field.pageNumber,
positionX: new Prisma.Decimal(field.pageX),
positionY: new Prisma.Decimal(field.pageY),
width: new Prisma.Decimal(field.pageWidth),
height: new Prisma.Decimal(field.pageHeight),
customText: '',
inserted: true,
fieldMeta: field.fieldMeta ?? null,
}));
return mappedFields;
}, [localFields]);
const isFieldsDisabled = useMemo(() => {
if (!selectedSigner) {
return true;
@@ -545,14 +515,6 @@ export const AddFieldsFormPartial = ({
if (!everySignerHasSignature) {
setIsMissingSignatureDialogVisible(true);
return;
}
setValidateUninsertedFields(true);
const isFieldsValid = validateFieldsUninserted();
if (!isFieldsValid) {
return;
} else {
void onFormSubmit();
}
@@ -604,10 +566,6 @@ export const AddFieldsFormPartial = ({
{isDocumentPdfLoaded &&
localFields.map((field, index) => {
const recipientIndex = recipients.findIndex((r) => r.email === field.signerEmail);
const hasFieldError =
emptyCheckboxFields.find((f) => f.formId === field.formId) ||
emptyRadioFields.find((f) => f.formId === field.formId) ||
emptySelectFields.find((f) => f.formId === field.formId);
return (
<FieldItem
@@ -632,7 +590,6 @@ export const AddFieldsFormPartial = ({
handleAdvancedSettings();
}}
hideRecipients={hideRecipients}
hasErrors={!!hasFieldError}
/>
);
})}
@@ -1061,6 +1018,7 @@ export const AddFieldsFormPartial = ({
<DocumentFlowFormContainerActions
loading={isSubmitting}
disabled={isSubmitting}
disableNextStep={hasErrors}
onGoBackClick={() => {
previousStep();
remove();
@@ -1077,11 +1035,6 @@ export const AddFieldsFormPartial = ({
/>
</>
)}
{validateUninsertedFields && fieldsWithError[0] && (
<FieldToolTip key={fieldsWithError[0].id} field={fieldsWithError[0]} color="warning">
<Trans>Empty field</Trans>
</FieldToolTip>
)}
</>
);
};
@@ -44,7 +44,6 @@ export type FieldItemProps = {
onBlur?: () => void;
recipientIndex?: number;
hideRecipients?: boolean;
hasErrors?: boolean;
};
export const FieldItem = ({
@@ -62,7 +61,6 @@ export const FieldItem = ({
onAdvancedSettings,
recipientIndex = 0,
hideRecipients = false,
hasErrors,
}: FieldItemProps) => {
const [active, setActive] = useState(false);
const [coords, setCoords] = useState({
@@ -203,15 +201,10 @@ export const FieldItem = ({
<div
className={cn(
'relative flex h-full w-full items-center justify-center bg-white',
!hasErrors && signerStyles.default.base,
!hasErrors && signerStyles.default.fieldItem,
{
'rounded-lg border border-red-400 bg-red-400/20 shadow-[0_0_0_5px_theme(colors.red.500/10%),0_0_0_2px_theme(colors.red.500/40%),0_0_0_0.5px_theme(colors.red.500)]':
hasErrors,
},
!fixedSize && '[container-type:size]',
signerStyles.default.base,
signerStyles.default.fieldItem,
)}
data-error={hasErrors ? 'true' : undefined}
onClick={() => {
setSettingsActive((prev) => !prev);
onFocus?.();
+2 -6
View File
@@ -1,8 +1,4 @@
{
"extends": "./packages/tsconfig/base.json",
"include": [
"packages/**/*",
"apps/**/*",
"infra/**/*"
],
}
"include": ["packages/**/*", "apps/**/*"]
}