chore: fix merge conflicts

This commit is contained in:
ephraimduncan
2025-12-29 20:02:51 +00:00
165 changed files with 6291 additions and 2774 deletions
+2
View File
@@ -63,6 +63,7 @@ export class LocalJobProvider extends BaseJobProvider {
jobId: pendingJob.id,
jobDefinitionId: pendingJob.jobId,
data: options,
isRetry: false,
});
}),
);
@@ -198,6 +199,7 @@ export class LocalJobProvider extends BaseJobProvider {
jobId,
jobDefinitionId: backgroundJob.jobId,
data: options,
isRetry: true,
});
}
@@ -286,7 +286,7 @@ const detectFieldsFromPage = async ({
});
const result = await generateObject({
model: vertex('gemini-3-pro-preview'),
model: vertex('gemini-3-flash-preview'),
system: SYSTEM_PROMPT,
schema: ZSubmitDetectedFieldsInputSchema,
messages,
@@ -207,7 +207,7 @@ const detectRecipientsFromImages = async ({
});
const result = await generateObject({
model: vertex('gemini-2.5-flash'),
model: vertex('gemini-3-flash-preview'),
system: SYSTEM_PROMPT,
schema: ZDetectedRecipientsSchema,
messages,
+17 -5
View File
@@ -9,7 +9,10 @@ globalThis.Image = Image;
class SkiaCanvasFactory {
_createCanvas(width: number, height: number) {
return new Canvas(width, height);
const canvas = new Canvas(width, height);
canvas.gpu = false;
return canvas;
}
create(width: number, height: number) {
@@ -44,10 +47,12 @@ export type PdfToImagesOptions = {
export const pdfToImages = async (pdfBytes: Uint8Array, options: PdfToImagesOptions = {}) => {
const { scale = 2 } = options;
const pdf = await pdfjsLib.getDocument({
const task = await pdfjsLib.getDocument({
data: pdfBytes,
CanvasFactory: SkiaCanvasFactory,
}).promise;
});
const pdf = await task.promise;
const images = await pMap(
Array.from({ length: pdf.numPages }),
@@ -58,6 +63,8 @@ export const pdfToImages = async (pdfBytes: Uint8Array, options: PdfToImagesOpti
const viewport = page.getViewport({ scale });
const canvas = new Canvas(viewport.width, viewport.height);
canvas.gpu = false;
const canvasContext = canvas.getContext('2d');
await page.render({
@@ -68,18 +75,23 @@ export const pdfToImages = async (pdfBytes: Uint8Array, options: PdfToImagesOpti
viewport,
}).promise;
return {
const result = {
pageNumber,
image: await canvas.toBuffer('jpeg'),
width: Math.floor(viewport.width),
height: Math.floor(viewport.height),
mimeType: 'image/jpeg',
};
void page.cleanup();
return result;
},
{ concurrency: 10 },
);
void pdf.destroy();
void pdf.destroy().catch((e) => console.error(e));
void task.destroy().catch((e) => console.error(e));
return images;
};
@@ -20,6 +20,7 @@ export const getDocumentCertificateAuditLogs = async ({
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
],
},
},
@@ -37,6 +38,9 @@ export const getDocumentCertificateAuditLogs = async ({
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED]: auditLogs.filter(
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
),
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT]: auditLogs.filter(
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
),
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED]: auditLogs.filter(
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
),
@@ -81,6 +81,7 @@ export type CreateEnvelopeOptions = {
globalActionAuth?: TDocumentActionAuthTypes[];
recipients?: CreateEnvelopeRecipientOptions[];
folderId?: string;
delegatedDocumentOwner?: string;
};
attachments?: Array<{
label: string;
@@ -114,6 +115,7 @@ export const createEnvelope = async ({
publicTitle,
publicDescription,
visibility: visibilityOverride,
delegatedDocumentOwner,
} = data;
const team = await prisma.team.findFirst({
@@ -256,6 +258,43 @@ export const createEnvelope = async ({
? await incrementDocumentId().then((v) => v.formattedDocumentId)
: await incrementTemplateId().then((v) => v.formattedTemplateId);
const getValidatedDelegatedOwner = async () => {
if (
!settings.delegateDocumentOwnership ||
!delegatedDocumentOwner ||
requestMetadata.source === 'app'
) {
return null;
}
const delegatedOwner = await prisma.user.findFirst({
where: {
email: delegatedDocumentOwner,
},
});
if (!delegatedOwner) {
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Delegated document owner must be a member of the team',
});
}
const isTeamMember = await prisma.team.findFirst({
where: buildTeamWhereQuery({ teamId, userId: delegatedOwner.id }),
});
if (!isTeamMember) {
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Delegated document owner must be a member of the team',
});
}
return delegatedOwner;
};
const delegatedOwner = await getValidatedDelegatedOwner();
const envelopeOwnerId = delegatedOwner?.id ?? userId;
return await prisma.$transaction(async (tx) => {
const envelope = await tx.envelope.create({
data: {
@@ -285,7 +324,7 @@ export const createEnvelope = async ({
})),
},
},
userId,
userId: envelopeOwnerId,
teamId,
authOptions,
visibility,
@@ -393,6 +432,9 @@ export const createEnvelope = async ({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
envelopeId: envelope.id,
user: {
id: envelopeOwnerId,
},
metadata: requestMetadata,
data: {
title,
@@ -403,6 +445,25 @@ export const createEnvelope = async ({
}),
});
// Create audit log for delegated owner if validation passed
if (delegatedOwner) {
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELEGATED_OWNER_CREATED,
envelopeId: envelope.id,
user: {
id: userId,
},
metadata: requestMetadata,
data: {
delegatedOwnerName: delegatedOwner.name,
delegatedOwnerEmail: delegatedOwner.email,
teamName: team.name,
},
}),
});
}
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED,
data: ZWebhookDocumentSchema.parse(mapEnvelopeToWebhookDocumentPayload(createdEnvelope)),
@@ -0,0 +1,197 @@
import type {
DocumentSource,
DocumentStatus,
Envelope,
EnvelopeType,
Prisma,
} from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { TEAM_DOCUMENT_VISIBILITY_MAP } from '../../constants/teams';
import type { FindResultResponse } from '../../types/search-params';
import { maskRecipientTokensForDocument } from '../../utils/mask-recipient-tokens-for-document';
import { getTeamById } from '../team/get-team';
export type FindEnvelopesOptions = {
userId: number;
teamId: number;
type?: EnvelopeType;
templateId?: number;
source?: DocumentSource;
status?: DocumentStatus;
page?: number;
perPage?: number;
orderBy?: {
column: keyof Pick<Envelope, 'createdAt'>;
direction: 'asc' | 'desc';
};
query?: string;
folderId?: string;
};
export const findEnvelopes = async ({
userId,
teamId,
type,
templateId,
source,
status,
page = 1,
perPage = 10,
orderBy,
query = '',
folderId,
}: FindEnvelopesOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
select: {
id: true,
email: true,
name: true,
},
});
const team = await getTeamById({
userId,
teamId,
});
const orderByColumn = orderBy?.column ?? 'createdAt';
const orderByDirection = orderBy?.direction ?? 'desc';
const searchFilter: Prisma.EnvelopeWhereInput = query
? {
OR: [
{ title: { contains: query, mode: 'insensitive' } },
{ externalId: { contains: query, mode: 'insensitive' } },
{ recipients: { some: { name: { contains: query, mode: 'insensitive' } } } },
{ recipients: { some: { email: { contains: query, mode: 'insensitive' } } } },
],
}
: {};
const visibilityFilter: Prisma.EnvelopeWhereInput = {
visibility: {
in: TEAM_DOCUMENT_VISIBILITY_MAP[team.currentTeamRole],
},
};
const teamEmailFilters: Prisma.EnvelopeWhereInput[] = [];
if (team.teamEmail) {
teamEmailFilters.push(
{
user: {
email: team.teamEmail.email,
},
},
{
recipients: {
some: {
email: team.teamEmail.email,
},
},
},
);
}
const whereClause: Prisma.EnvelopeWhereInput = {
AND: [
{
OR: [
{
teamId: team.id,
...visibilityFilter,
},
{
userId,
},
...teamEmailFilters,
],
},
{
folderId: folderId ?? null,
deletedAt: null,
},
searchFilter,
],
};
if (type) {
whereClause.type = type;
}
if (templateId) {
whereClause.templateId = templateId;
}
if (source) {
whereClause.source = source;
}
if (status) {
whereClause.status = status;
}
const [data, count] = await Promise.all([
prisma.envelope.findMany({
where: whereClause,
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
orderBy: {
[orderByColumn]: orderByDirection,
},
include: {
user: {
select: {
id: true,
name: true,
email: true,
},
},
recipients: {
orderBy: {
id: 'asc',
},
},
team: {
select: {
id: true,
url: true,
},
},
},
}),
prisma.envelope.count({
where: whereClause,
}),
]);
const maskedData = data.map((envelope) =>
maskRecipientTokensForDocument({
document: envelope,
user,
}),
);
const mappedData = maskedData.map((envelope) => ({
...envelope,
recipients: envelope.Recipient,
user: {
id: envelope.user.id,
name: envelope.user.name || '',
email: envelope.user.email,
},
}));
return {
data: mappedData,
count,
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof mappedData>;
};
@@ -0,0 +1,213 @@
import type { EnvelopeType, Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { TEAM_DOCUMENT_VISIBILITY_MAP } from '../../constants/teams';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { EnvelopeIdsOptions } from '../../utils/envelope';
import { unsafeBuildEnvelopeIdsQuery } from '../../utils/envelope';
import { getTeamById } from '../team/get-team';
export type GetEnvelopesByIdsOptions = {
/**
* The envelope IDs to fetch with their type.
*/
ids: EnvelopeIdsOptions;
/**
* The user ID who has been authenticated.
*/
userId: number;
/**
* The unvalidated team ID from the request.
*/
teamId: number;
/**
* The type of envelope to get.
*
* Set to null to bypass check.
*/
type: EnvelopeType | null;
};
/**
* Fetches multiple envelopes by their IDs with proper access control.
*
* Only returns envelopes that the user has valid access to based on:
* 1. Document ownership (userId matches)
* 2. Team membership with appropriate visibility level
* 3. Team email ownership
*
* NOTE: Be extremely careful when modifying this function. Needs at minimum two reviewers to approve any changes.
*/
export const getEnvelopesByIds = async ({
ids,
userId,
teamId,
type,
}: GetEnvelopesByIdsOptions) => {
const { envelopeWhereInput } = await getMultipleEnvelopeWhereInput({
ids,
userId,
teamId,
type,
});
const envelopes = await prisma.envelope.findMany({
where: envelopeWhereInput,
include: {
envelopeItems: {
include: {
documentData: true,
},
orderBy: {
order: 'asc',
},
},
folder: true,
documentMeta: true,
user: {
select: {
id: true,
name: true,
email: true,
},
},
recipients: {
orderBy: {
id: 'asc',
},
},
fields: true,
team: {
select: {
id: true,
url: true,
},
},
directLink: {
select: {
directTemplateRecipientId: true,
enabled: true,
id: true,
token: true,
},
},
},
});
return envelopes.map((envelope) => ({
...envelope,
user: {
id: envelope.user.id,
name: envelope.user.name || '',
email: envelope.user.email,
},
}));
};
export type GetEnvelopesByIdsResponse = Awaited<ReturnType<typeof getEnvelopesByIds>>;
export type GetMultipleEnvelopeWhereInputOptions = {
/**
* The envelope IDs to fetch with their type.
*/
ids: EnvelopeIdsOptions;
/**
* The user ID who has been authenticated.
*/
userId: number;
/**
* The unknown teamId from the request.
*/
teamId: number;
/**
* The type of envelope to get.
*
* Set to null to bypass check.
*/
type: EnvelopeType | null;
};
/**
* Generate the where input for a multiple envelope Prisma query.
*
* This will return a query that allows a user to get documents if they have valid access to them.
*
* NOTE: Be extremely careful when modifying this function. Needs at minimum two reviewers to approve any changes.
*/
export const getMultipleEnvelopeWhereInput = async ({
ids,
userId,
teamId,
type,
}: GetMultipleEnvelopeWhereInputOptions) => {
// Backup validation incase something goes wrong.
if (!ids.ids || !userId || !teamId || type === undefined) {
console.error(`[CRTICAL ERROR]: MUST NEVER HAPPEN`);
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Envelope IDs not found',
});
}
// Validate that the user belongs to the team provided.
const team = await getTeamById({ teamId, userId });
const envelopeOrInput: Prisma.EnvelopeWhereInput[] = [
// Allow access if they own the document.
{
userId,
},
// Or, if they belong to the team that the document is associated with.
{
visibility: {
in: TEAM_DOCUMENT_VISIBILITY_MAP[team.currentTeamRole],
},
teamId: team.id,
},
];
// Allow access to documents sent from the team email.
if (team.teamEmail) {
envelopeOrInput.push({
user: {
email: team.teamEmail.email,
},
});
}
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// NOTE: DO NOT PUT ANY CODE AFTER THIS POINT.
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
const envelopeWhereInput: Prisma.EnvelopeWhereInput = {
...unsafeBuildEnvelopeIdsQuery(ids, type),
OR: envelopeOrInput,
};
// Final backup validation incase something goes wrong.
if (
!envelopeWhereInput.OR ||
envelopeWhereInput.OR.length < 2 ||
!userId ||
!teamId ||
!team.id ||
teamId !== team.id
) {
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Query not valid',
});
}
// Do not modify this return directly, all adjustments need to be made prior to the above if statement.
return {
envelopeWhereInput,
team,
};
};
@@ -0,0 +1,52 @@
import { DocumentStatus, EnvelopeType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { deletedAccountServiceAccount } from '../user/service-accounts/deleted-account';
export type OrphanEnvelopesOptions = {
teamId: number;
};
export const orphanEnvelopes = async ({ teamId }: OrphanEnvelopesOptions) => {
const serviceAccount = await deletedAccountServiceAccount();
// Transfer all inflight and completed envelopes to the service account.
await prisma.envelope.updateMany({
where: {
teamId,
type: EnvelopeType.DOCUMENT,
status: {
in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED],
},
deletedAt: null,
},
data: {
userId: serviceAccount.id,
teamId: serviceAccount.ownedOrganisations[0].teams[0].id,
deletedAt: new Date(),
},
});
// Transfer any remaining deleted envelopes to the service account.
await prisma.envelope.updateMany({
where: {
teamId,
type: EnvelopeType.DOCUMENT,
status: {
in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED],
},
},
data: {
userId: serviceAccount.id,
teamId: serviceAccount.ownedOrganisations[0].teams[0].id,
},
});
// Then delete anything remaining across documents and templates.
await prisma.envelope.deleteMany({
where: {
teamId,
},
});
};
@@ -0,0 +1,20 @@
import { prisma } from '@documenso/prisma';
export type TransferTeamEnvelopesOptions = {
sourceTeamId: number;
targetTeamId: number;
};
export const transferTeamEnvelopes = async ({
sourceTeamId,
targetTeamId,
}: TransferTeamEnvelopesOptions) => {
await prisma.envelope.updateMany({
where: {
teamId: sourceTeamId,
},
data: {
teamId: targetTeamId,
},
});
};
@@ -0,0 +1,42 @@
/**
* !: This is a workaround to fix the memory leak in the skia-canvas library.
* !: Internals are ported from the original `konva/skia-backend.js` file.
*/
import { Konva } from 'konva/lib/_CoreInternals';
import { Canvas, DOMMatrix, Image, Path2D } from 'skia-canvas';
// @ts-expect-error skia-canvas satisfies the requirements
global.DOMMatrix = DOMMatrix;
// @ts-expect-error skia-canvas satisfies the requirements
global.Path2D = Path2D;
Path2D.prototype.toString = () => '[object Path2D]';
Konva.Util['createCanvasElement'] = () => {
const node = new Canvas(300, 300);
node.gpu = false;
if (!('style' in node) || !node['style']) {
Object.assign(node, { style: {} });
}
node.toString = () => '[object HTMLCanvasElement]';
const ctx = node.getContext('2d');
Object.defineProperty(ctx, 'canvas', {
get: () => node,
});
return node as unknown as HTMLCanvasElement;
};
Konva.Util.createImageElement = () => {
const node = new Image();
node.toString = () => '[object HTMLImageElement]';
return node as unknown as HTMLImageElement;
};
Konva._renderBackend = 'skia-canvas';
export default Konva;
@@ -40,7 +40,10 @@ export const acceptOrganisationInvitation = async ({
const user = await prisma.user.findFirst({
where: {
email: organisationMemberInvite.email,
email: {
equals: organisationMemberInvite.email,
mode: 'insensitive',
},
},
select: {
id: true,
@@ -4,8 +4,10 @@ import {
PDFDict,
type PDFDocument,
PDFName,
PDFNumber,
PDFRadioGroup,
PDFRef,
PDFStream,
drawObject,
popGraphicsState,
pushGraphicsState,
@@ -103,6 +105,36 @@ const getAppearanceRefForWidget = (field: PDFField, widget: PDFWidgetAnnotation)
}
};
/**
* Ensures that an appearance stream has the required dictionary entries to be
* used as a Form XObject. Some PDFs have appearance streams that are missing
* the /Subtype /Form entry, which causes Adobe Reader to fail to render them.
*
* Per PDF spec, a Form XObject stream requires:
* - /Subtype /Form (required)
* - /BBox (required, but should already exist for appearance streams)
* - /FormType 1 (optional, defaults to 1)
*/
const normalizeAppearanceStream = (document: PDFDocument, appearanceRef: PDFRef) => {
const appearanceStream = document.context.lookup(appearanceRef);
if (!(appearanceStream instanceof PDFStream)) {
return;
}
const dict = appearanceStream.dict;
// Ensure /Subtype /Form is set (required for XObject Form)
if (!dict.has(PDFName.of('Subtype'))) {
dict.set(PDFName.of('Subtype'), PDFName.of('Form'));
}
// Ensure /FormType is set (optional, but good practice)
if (!dict.has(PDFName.of('FormType'))) {
dict.set(PDFName.of('FormType'), PDFNumber.of(1));
}
};
const flattenWidget = (document: PDFDocument, field: PDFField, widget: PDFWidgetAnnotation) => {
try {
const page = getPageForWidget(document, widget);
@@ -117,6 +149,9 @@ const flattenWidget = (document: PDFDocument, field: PDFField, widget: PDFWidget
return;
}
// Ensure the appearance stream has required XObject Form dictionary entries
normalizeAppearanceStream(document, appearanceRef);
const xObjectKey = page.node.newXObject('FlatWidget', appearanceRef);
const rectangle = widget.getRectangle();
@@ -1,5 +1,5 @@
// sort-imports-ignore
import 'konva/skia-backend';
import '../konva/skia-backend';
import Konva from 'konva';
import path from 'node:path';
@@ -23,6 +23,7 @@ export const insertFieldInPDFV2 = async ({
}: InsertFieldInPDFV2Options) => {
const fontPath = path.join(process.cwd(), 'public/fonts');
// eslint-disable-next-line react-hooks/rules-of-hooks
FontLibrary.use({
['Caveat']: [path.join(fontPath, 'caveat.ttf')],
['Noto Sans']: [path.join(fontPath, 'noto-sans.ttf')],
@@ -31,8 +32,8 @@ export const insertFieldInPDFV2 = async ({
['Noto Sans Korean']: [path.join(fontPath, 'noto-sans-korean.ttf')],
});
const stage = new Konva.Stage({ width: pageWidth, height: pageHeight });
const layer = new Konva.Layer();
let stage: Konva.Stage | null = new Konva.Stage({ width: pageWidth, height: pageHeight });
let layer: Konva.Layer | null = new Konva.Layer();
// Render the fields onto the layer.
for (const field of fields) {
@@ -60,5 +61,13 @@ export const insertFieldInPDFV2 = async ({
const canvas = layer.canvas._canvas as unknown as Canvas;
// Embed the SVG into the PDF
return await canvas.toBuffer('pdf');
const pdf = await canvas.toBuffer('pdf');
stage.destroy();
layer.destroy();
stage = null;
layer = null;
return pdf;
};
@@ -1,5 +1,6 @@
import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { hashString } from '../auth/hash';
export const getApiTokenByToken = async ({ token }: { token: string }) => {
@@ -38,11 +39,17 @@ export const getApiTokenByToken = async ({ token }: { token: string }) => {
});
if (!apiToken) {
throw new Error('Invalid token');
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Invalid token',
statusCode: 401,
});
}
if (apiToken.expires && apiToken.expires < new Date()) {
throw new Error('Expired token');
throw new AppError(AppErrorCode.EXPIRED_CODE, {
message: 'Expired token',
statusCode: 401,
});
}
// Handle a silly choice from many moons ago
@@ -54,7 +61,10 @@ export const getApiTokenByToken = async ({ token }: { token: string }) => {
// This will never happen but we need to narrow types
if (!user) {
throw new Error('Invalid token');
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Invalid token',
statusCode: 401,
});
}
return {
@@ -1,5 +1,6 @@
import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { hashString } from '../auth/hash';
export const getUserByApiToken = async ({ token }: { token: string }) => {
@@ -19,14 +20,20 @@ export const getUserByApiToken = async ({ token }: { token: string }) => {
});
if (!user) {
throw new Error('Invalid token');
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Invalid token',
statusCode: 401,
});
}
const retrievedToken = user.apiTokens.find((apiToken) => apiToken.token === hashedToken);
// This should be impossible but we need to satisfy TypeScript
if (!retrievedToken) {
throw new Error('Invalid token');
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Invalid token',
statusCode: 401,
});
}
if (retrievedToken.expires && retrievedToken.expires < new Date()) {
@@ -185,7 +185,7 @@ export const createDocumentFromDirectTemplate = async ({
documentAuth: directTemplateEnvelope.authOptions,
});
const directRecipientName = user?.name || initialDirectRecipientName;
let directRecipientName = user?.name || initialDirectRecipientName;
// Ensure typesafety when we add more options.
const isAccessAuthValid = match(derivedRecipientAccessAuth.at(0))
@@ -238,7 +238,7 @@ export const createDocumentFromDirectTemplate = async ({
}
if (templateField.type === FieldType.NAME && directRecipientName === undefined) {
directRecipientName === signedFieldValue?.value;
directRecipientName = signedFieldValue?.value;
}
const derivedRecipientActionAuth = await validateFieldAuth({
+54 -18
View File
@@ -1,9 +1,7 @@
import { DocumentStatus, EnvelopeType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { deletedAccountServiceAccount } from './service-accounts/deleted-account';
import { orphanEnvelopes } from '../envelope/orphan-envelopes';
export type DeleteUserOptions = {
id: number;
@@ -14,6 +12,30 @@ export const deleteUser = async ({ id }: DeleteUserOptions) => {
where: {
id,
},
include: {
ownedOrganisations: {
include: {
teams: {
select: {
id: true,
},
},
},
},
organisationMember: {
include: {
organisation: {
include: {
teams: {
select: {
id: true,
},
},
},
},
},
},
},
});
if (!user) {
@@ -22,22 +44,36 @@ export const deleteUser = async ({ id }: DeleteUserOptions) => {
});
}
const serviceAccount = await deletedAccountServiceAccount();
// Get team IDs from organisations the user owns.
const ownedTeamIds = user.ownedOrganisations.flatMap((org) => org.teams.map((team) => team.id));
// TODO: Send out cancellations for all pending docs
await prisma.envelope.updateMany({
where: {
userId: user.id,
type: EnvelopeType.DOCUMENT,
status: {
in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED],
},
},
data: {
userId: serviceAccount.id,
deletedAt: new Date(),
},
});
// Get team IDs from organisations the user is a member of (but not owner).
const memberTeams = user.organisationMember
.filter((member) => member.organisation.ownerUserId !== user.id)
.flatMap((member) =>
member.organisation.teams.map((team) => ({
teamId: team.id,
orgOwnerId: member.organisation.ownerUserId,
})),
);
// For teams where user is the org owner - orphan their envelopes.
await Promise.all(ownedTeamIds.map(async (teamId) => orphanEnvelopes({ teamId })));
// For teams where user is a member (not owner) - transfer envelopes to team owner.
await Promise.all(
memberTeams.map(async ({ teamId, orgOwnerId }) => {
return prisma.envelope.updateMany({
where: {
userId: user.id,
teamId,
},
data: {
userId: orgOwnerId,
},
});
}),
);
return await prisma.user.delete({
where: {
@@ -5,6 +5,20 @@ export const deletedAccountServiceAccount = async () => {
where: {
email: 'deleted-account@documenso.com',
},
select: {
id: true,
email: true,
ownedOrganisations: {
select: {
id: true,
teams: {
select: {
id: true,
},
},
},
},
},
});
if (!serviceAccount) {
File diff suppressed because it is too large Load Diff
+185 -95
View File
@@ -96,6 +96,11 @@ msgstr "{0, plural, one {# field} other {# fields}}"
msgid "{0, plural, one {# folder} other {# folders}}"
msgstr "{0, plural, one {# folder} other {# folders}}"
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "{0, plural, one {# recipient have been added from AI detection.} other {# recipients have been added from AI detection.}}"
msgstr "{0, plural, one {# recipient have been added from AI detection.} other {# recipients have been added from AI detection.}}"
#. placeholder {0}: template.recipients.length
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
msgid "{0, plural, one {# recipient} other {# recipients}}"
@@ -146,11 +151,44 @@ msgstr "{0, plural, one {1 matching field} other {# matching fields}}"
msgid "{0, plural, one {1 Recipient} other {# Recipients}}"
msgstr "{0, plural, one {1 Recipient} other {# Recipients}}"
#. placeholder {0}: progress.fieldsDetected
#. placeholder {1}: progress.pagesProcessed
#. placeholder {2}: progress.totalPages
#. placeholder {3}: progress.pagesProcessed
#. placeholder {4}: progress.totalPages
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "{0, plural, one {Page {1} of {2} - # field found} other {Page {3} of {4} - # fields found}}"
msgstr "{0, plural, one {Page {1} of {2} - # field found} other {Page {3} of {4} - # fields found}}"
#. placeholder {0}: progress.recipientsDetected
#. placeholder {1}: progress.pagesProcessed
#. placeholder {2}: progress.totalPages
#. placeholder {3}: progress.pagesProcessed
#. placeholder {4}: progress.totalPages
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "{0, plural, one {Page {1} of {2} - # recipient found} other {Page {3} of {4} - # recipients found}}"
msgstr "{0, plural, one {Page {1} of {2} - # recipient found} other {Page {3} of {4} - # recipients found}}"
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "{0, plural, one {Recipient added} other {Recipients added}}"
msgstr "{0, plural, one {Recipient added} other {Recipients added}}"
#. placeholder {0}: pendingRecipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0, plural, one {Waiting on 1 recipient} other {Waiting on # recipients}}"
msgstr "{0, plural, one {Waiting on 1 recipient} other {Waiting on # recipients}}"
#. placeholder {0}: detectedFields.length
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "{0, plural, one {We found # field in your document.} other {We found # fields in your document.}}"
msgstr "{0, plural, one {We found # field in your document.} other {We found # fields in your document.}}"
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "{0, plural, one {We found # recipient in your document.} other {We found # recipients in your document.}}"
msgstr "{0, plural, one {We found # recipient in your document.} other {We found # recipients in your document.}}"
#. placeholder {0}: _(FRIENDLY_FIELD_TYPE[fieldType as FieldType])
#. placeholder {0}: route.label
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
@@ -195,11 +233,6 @@ msgstr "{0} of {1} documents remaining this month."
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "{0} recipient(s) have been added from AI detection."
msgstr "{0} recipient(s) have been added from AI detection."
#. placeholder {0}: organisation.name
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "{0} Teams"
@@ -846,9 +879,6 @@ msgid "A request to use your email has been initiated by {0} on Documenso"
msgstr "A request to use your email has been initiated by {0} on Documenso"
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso"
msgstr "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso"
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso."
msgstr "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso."
@@ -1283,6 +1313,10 @@ msgstr "After submission, a document will be automatically generated and added t
msgid "AI Features"
msgstr "AI Features"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "AI features are disabled for your team. Please ask your team owner or organisation owner to enable them."
msgstr "AI features are disabled for your team. Please ask your team owner or organisation owner to enable them."
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
msgid "All"
@@ -2260,6 +2294,10 @@ msgstr "Charts"
msgid "Checkbox"
msgstr "Checkbox"
#: packages/ui/primitives/document-flow/field-content.tsx
msgid "Checkbox option"
msgstr "Checkbox option"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Checkbox Settings"
msgstr "Checkbox Settings"
@@ -2363,6 +2401,7 @@ msgstr "Client Secret"
msgid "Client secret is required"
msgstr "Client secret is required"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
@@ -2375,7 +2414,6 @@ msgstr "Client secret is required"
#: apps/remix/app/components/dialogs/team-group-delete-dialog.tsx
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-delete-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
@@ -2386,7 +2424,9 @@ msgstr "Client secret is required"
#: apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/dialog.tsx
#: packages/ui/primitives/document-flow/missing-signature-field-dialog.tsx
#: packages/ui/primitives/sheet.tsx
msgid "Close"
msgstr "Close"
@@ -3064,6 +3104,10 @@ msgstr "Default Time Zone"
msgid "Default Value"
msgstr "Default Value"
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Delegate Document Ownership"
msgstr "Delegate Document Ownership"
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete"
msgstr "delete"
@@ -3283,8 +3327,8 @@ msgid "Device"
msgstr "Device"
#: packages/email/templates/reset-password.tsx
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us.</0>"
msgstr "Didn't request a password change? We are here to help you secure your account, just <0>contact us.</0>"
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us</0>."
msgstr "Didn't request a password change? We are here to help you secure your account, just <0>contact us</0>."
#: apps/remix/app/components/general/template/template-direct-link-badge.tsx
#: apps/remix/app/components/tables/templates-table.tsx
@@ -3627,6 +3671,11 @@ msgctxt "Audit log format"
msgid "Document opened"
msgstr "Document opened"
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "Document ownership delegated"
msgstr "Document ownership delegated"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document pending"
msgstr "Document pending"
@@ -3825,6 +3874,11 @@ msgstr "Domain Name"
msgid "Don't have an account? <0>Sign up</0>"
msgstr "Don't have an account? <0>Sign up</0>"
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
msgid "Don't transfer (Delete all documents)"
msgstr "Don't transfer (Delete all documents)"
#: apps/remix/app/components/forms/2fa/enable-authenticator-app-dialog.tsx
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
@@ -3963,6 +4017,7 @@ msgstr "E.g. 100"
#: apps/remix/app/components/general/document/document-page-view-button.tsx
#: apps/remix/app/components/general/document/document-page-view-dropdown.tsx
#: apps/remix/app/components/general/teams/team-email-dropdown.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/tables/documents-table-action-button.tsx
#: apps/remix/app/components/tables/documents-table-action-dropdown.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
@@ -4179,6 +4234,15 @@ msgstr "Enable account"
msgid "Enable Account"
msgstr "Enable Account"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "Enable AI detection"
msgstr "Enable AI detection"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "Enable AI features"
msgstr "Enable AI features"
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Enable AI-powered features such as automatic recipient detection. When enabled, document content will be sent to AI providers. We only use providers that do not retain data for training and prefer European regions where available."
msgstr "Enable AI-powered features such as automatic recipient detection. When enabled, document content will be sent to AI providers. We only use providers that do not retain data for training and prefer European regions where available."
@@ -4219,6 +4283,10 @@ msgstr "Enable signing order"
msgid "Enable SSO portal"
msgstr "Enable SSO portal"
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Enable team API tokens to delegate document ownership to another team member."
msgstr "Enable team API tokens to delegate document ownership to another team member."
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
@@ -4817,10 +4885,26 @@ msgstr "Go home"
msgid "Go to document"
msgstr "Go to document"
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to first page"
msgstr "Go to first page"
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to last page"
msgstr "Go to last page"
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to next page"
msgstr "Go to next page"
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Go to owner"
msgstr "Go to owner"
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to previous page"
msgstr "Go to previous page"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "Go to team"
msgstr "Go to team"
@@ -4901,8 +4985,8 @@ msgid "Here you can set branding preferences for your organisation. Teams will i
msgstr "Here you can set branding preferences for your organisation. Teams will inherit these settings by default."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
msgid "Here you can set branding preferences for your team"
msgstr "Here you can set branding preferences for your team"
msgid "Here you can set branding preferences for your team."
msgstr "Here you can set branding preferences for your team."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
msgid "Here you can set document preferences for your organisation. Teams will inherit these settings by default."
@@ -4917,12 +5001,12 @@ msgid "Here you can set preferences and defaults for your team."
msgstr "Here you can set preferences and defaults for your team."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
msgid "Here you can set your general branding preferences"
msgstr "Here you can set your general branding preferences"
msgid "Here you can set your general branding preferences."
msgstr "Here you can set your general branding preferences."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
msgid "Here you can set your general document preferences"
msgstr "Here you can set your general document preferences"
msgid "Here you can set your general document preferences."
msgstr "Here you can set your general document preferences."
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid "Here's how it works:"
@@ -5087,6 +5171,7 @@ msgstr "Inherit authentication method"
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/email-preferences-form.tsx
#: apps/remix/app/components/forms/email-preferences-form.tsx
msgid "Inherit from organisation"
@@ -5479,6 +5564,7 @@ msgid "Looking for signature fields"
msgstr "Looking for signature fields"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/user-organisations-table.tsx
msgid "Manage"
@@ -5539,6 +5625,10 @@ msgstr "Manage linked accounts"
msgid "Manage organisation"
msgstr "Manage organisation"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "Manage Organisation"
msgstr "Manage Organisation"
#: apps/remix/app/routes/_authenticated+/admin+/organisations._index.tsx
msgid "Manage organisations"
msgstr "Manage organisations"
@@ -5702,15 +5792,15 @@ msgstr "Member Since"
msgid "Members"
msgstr "Members"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/forms/support-ticket-form.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Message"
msgstr "Message"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Message <0>(Optional)</0>"
msgstr "Message <0>(Optional)</0>"
@@ -5860,10 +5950,6 @@ msgstr "Never expire"
msgid "New Password"
msgstr "New Password"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "New Template"
msgstr "New Template"
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
@@ -5889,6 +5975,7 @@ msgstr "Next Recipient Name"
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "No"
msgstr "No"
@@ -6096,8 +6183,8 @@ msgid "Once you have scanned the QR code or entered the code manually, enter the
msgstr "Once you have scanned the QR code or entered the code manually, enter the code provided by your authenticator app below."
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button"
msgstr "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button"
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button."
msgstr "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button."
#: packages/lib/constants/template.ts
msgid "Once your template is set up, share the link anywhere you want. The person who opens the link will be able to enter their information in the direct link recipient field and complete any other fields assigned to them."
@@ -6131,6 +6218,10 @@ msgstr "Only PDF files are allowed"
msgid "Oops! Something went wrong."
msgstr "Oops! Something went wrong."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "Open menu"
msgstr "Open menu"
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
msgid "Opened"
msgstr "Opened"
@@ -6324,20 +6415,6 @@ msgstr "Ownership transferred to {organisationMemberName}."
msgid "Page {0} of {1}"
msgstr "Page {0} of {1}"
#. placeholder {0}: progress.pagesProcessed
#. placeholder {1}: progress.totalPages
#. placeholder {2}: progress.fieldsDetected
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "Page {0} of {1} - {2} field(s) found"
msgstr "Page {0} of {1} - {2} field(s) found"
#. placeholder {0}: progress.pagesProcessed
#. placeholder {1}: progress.totalPages
#. placeholder {2}: progress.recipientsDetected
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "Page {0} of {1} - {2} recipient(s) found"
msgstr "Page {0} of {1} - {2} recipient(s) found"
#. placeholder {0}: i + 1
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
#: packages/ui/primitives/pdf-viewer/base.tsx
@@ -6683,10 +6760,6 @@ msgstr "Please try a different domain."
msgid "Please try again and make sure you enter the correct email address."
msgstr "Please try again and make sure you enter the correct email address."
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "Please try again later."
msgstr "Please try again later."
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
#: packages/ui/primitives/pdf-viewer/base.tsx
@@ -6964,10 +7037,6 @@ msgstr "Recipient updated"
msgid "Recipients"
msgstr "Recipients"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "Recipients added"
msgstr "Recipients added"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Recipients metrics"
msgstr "Recipients metrics"
@@ -7141,8 +7210,8 @@ msgstr "Reply to email"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Reply To Email"
msgstr "Reply To Email"
msgid "Reply To Email <0>(Optional)</0>"
msgstr "Reply To Email <0>(Optional)</0>"
#: apps/remix/app/components/general/webhook-logs-sheet.tsx
msgid "Request"
@@ -7279,6 +7348,11 @@ msgstr "Retry"
msgid "Return"
msgstr "Return"
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/routes/_recipient+/sign.$token+/waiting.tsx
msgid "Return Home"
msgstr "Return Home"
#: apps/remix/app/routes/_unauthenticated+/o.$orgUrl.signin.tsx
msgid "Return to Documenso sign in page here"
msgstr "Return to Documenso sign in page here"
@@ -7445,6 +7519,7 @@ msgid "See the background jobs tab for the status"
msgstr "See the background jobs tab for the status"
#: apps/remix/app/components/general/document-signing/document-signing-dropdown-field.tsx
#: packages/ui/primitives/document-flow/field-content.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx
#: packages/ui/primitives/document-flow/types.ts
msgid "Select"
@@ -8084,7 +8159,6 @@ msgstr "Some signers have not been assigned a signature field. Please assign at
#: apps/remix/app/components/dialogs/team-email-delete-dialog.tsx
#: apps/remix/app/components/dialogs/team-inherit-member-disable-dialog.tsx
#: apps/remix/app/components/dialogs/team-inherit-member-enable-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-delete-dialog.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
@@ -8247,14 +8321,14 @@ msgstr "Stripe customer created successfully"
msgid "Stripe Customer ID"
msgstr "Stripe Customer ID"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/forms/support-ticket-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Subject"
msgstr "Subject"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Subject <0>(Optional)</0>"
msgstr "Subject <0>(Optional)</0>"
@@ -8575,7 +8649,6 @@ msgstr "Teams that this organisation group is currently assigned to"
msgid "Template"
msgstr "Template"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Template (Legacy)"
msgstr "Template (Legacy)"
@@ -8588,7 +8661,7 @@ msgstr "Template Created"
msgid "Template deleted"
msgstr "Template deleted"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Template document uploaded"
msgstr "Template document uploaded"
@@ -8650,10 +8723,6 @@ msgstr "Template uploaded"
msgid "Templates"
msgstr "Templates"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "Templates allow you to quickly generate documents with pre-filled recipients and fields."
msgstr "Templates allow you to quickly generate documents with pre-filled recipients and fields."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
msgid "Test"
msgstr "Test"
@@ -8787,6 +8856,12 @@ msgstr "The document owner has been notified of this rejection. No further actio
msgid "The document owner has been notified of your decision. They may contact you with further instructions if necessary."
msgstr "The document owner has been notified of your decision. They may contact you with further instructions if necessary."
#. placeholder {0}: data.delegatedOwnerName || data.delegatedOwnerEmail
#. placeholder {1}: data.teamName
#: packages/lib/utils/document-audit-logs.ts
msgid "The document ownership was delegated to {0} on behalf of {1}"
msgstr "The document ownership was delegated to {0} on behalf of {1}"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
msgid "The document was created but could not be sent to recipients."
msgstr "The document was created but could not be sent to recipients."
@@ -8821,8 +8896,8 @@ msgid "The email domain you are looking for may have been removed, renamed or ma
msgstr "The email domain you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/components/forms/signin.tsx
msgid "The email or password provided is incorrect"
msgstr "The email or password provided is incorrect"
msgid "The email or password provided is incorrect."
msgstr "The email or password provided is incorrect."
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
@@ -9020,8 +9095,8 @@ msgid "The token you have used to reset your password is either expired or it ne
msgstr "The token you have used to reset your password is either expired or it never existed. If you have still forgotten your password, please request a new reset link."
#: apps/remix/app/components/forms/signin.tsx
msgid "The two-factor authentication code provided is incorrect"
msgstr "The two-factor authentication code provided is incorrect"
msgid "The two-factor authentication code provided is incorrect."
msgstr "The two-factor authentication code provided is incorrect."
#: apps/remix/app/components/forms/editor/editor-field-signature-form.tsx
msgid "The typed signature font size"
@@ -9191,8 +9266,8 @@ msgid "This document was created using a direct link."
msgstr "This document was created using a direct link."
#: packages/email/template-components/template-footer.tsx
msgid "This document was sent using <0>Documenso.</0>"
msgstr "This document was sent using <0>Documenso.</0>"
msgid "This document was sent using <0>Documenso</0>."
msgstr "This document was sent using <0>Documenso</0>."
#: apps/remix/app/components/dialogs/envelope-duplicate-dialog.tsx
msgid "This document will be duplicated."
@@ -9523,6 +9598,10 @@ msgstr "Total Signers that Signed Up"
msgid "Total Users"
msgstr "Total Users"
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
msgid "Transfer documents to a different team"
msgstr "Transfer documents to a different team"
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
msgid "Triggers"
@@ -9535,6 +9614,10 @@ msgstr "Triggers"
msgid "Try again"
msgstr "Try again"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not retain your data for training."
msgstr "Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not retain your data for training."
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Two factor authentication"
msgstr "Two factor authentication"
@@ -9681,6 +9764,10 @@ msgstr "Unauthorized"
msgid "Uncompleted"
msgstr "Uncompleted"
#: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Undo"
msgstr "Undo"
#: apps/remix/app/routes/_authenticated+/settings+/security.linked-accounts.tsx
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
@@ -10293,8 +10380,8 @@ msgstr "Waiting for Your Turn"
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
msgid "Want to send slick signing links like this one? <0>Check out Documenso.</0>"
msgstr "Want to send slick signing links like this one? <0>Check out Documenso.</0>"
msgid "Want to send slick signing links like this one? <0>Check out Documenso</0>."
msgstr "Want to send slick signing links like this one? <0>Check out Documenso</0>."
#: apps/remix/app/routes/_profile+/_layout.tsx
msgid "Want your own public profile?"
@@ -10329,6 +10416,10 @@ msgstr "We are unable to update this passkey at the moment. Please try again lat
msgid "We couldn't create a Stripe customer. Please try again."
msgstr "We couldn't create a Stripe customer. Please try again."
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "We couldn't enable AI features right now. Please try again."
msgstr "We couldn't enable AI features right now. Please try again."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "We couldn't update the group. Please try again."
msgstr "We couldn't update the group. Please try again."
@@ -10529,16 +10620,6 @@ msgstr "We encountered an unknown error while attempting update the team email.
msgid "We encountered an unknown error while attempting update your profile. Please try again later."
msgstr "We encountered an unknown error while attempting update your profile. Please try again later."
#. placeholder {0}: detectedFields.length
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "We found {0} field(s) in your document."
msgstr "We found {0} field(s) in your document."
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "We found {0} recipient(s) in your document."
msgstr "We found {0} recipient(s) in your document."
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
msgid "We have sent a confirmation email for verification."
msgstr "We have sent a confirmation email for verification."
@@ -10788,6 +10869,7 @@ msgstr "Yearly"
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Yes"
msgstr "Yes"
@@ -10900,13 +10982,13 @@ msgid "You are currently updating <0>{0}</0>"
msgstr "You are currently updating <0>{0}</0>"
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
msgid "You are currently updating <0>{memberName}.</0>"
msgstr "You are currently updating <0>{memberName}.</0>"
msgid "You are currently updating <0>{memberName}</0>."
msgstr "You are currently updating <0>{memberName}</0>."
#: apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
msgid "You are currently updating <0>{organisationMemberName}.</0>"
msgstr "You are currently updating <0>{organisationMemberName}.</0>"
msgid "You are currently updating <0>{organisationMemberName}</0>."
msgstr "You are currently updating <0>{organisationMemberName}</0>."
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
msgid "You are currently updating the <0>{passkeyName}</0> passkey."
@@ -10967,16 +11049,16 @@ msgstr "You can enable access to allow all organisation members to access this t
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.email.tsx
msgid "You can manage your email preferences here"
msgstr "You can manage your email preferences here"
msgid "You can manage your email preferences here."
msgstr "You can manage your email preferences here."
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "You can only detect fields in draft envelopes"
msgstr "You can only detect fields in draft envelopes"
#: packages/email/templates/confirm-team-email.tsx
msgid "You can revoke access at any time in your team settings on Documenso <0>here.</0>"
msgstr "You can revoke access at any time in your team settings on Documenso <0>here.</0>"
msgid "You can revoke access at any time in your team settings on Documenso <0>here</0>."
msgstr "You can revoke access at any time in your team settings on Documenso <0>here</0>."
#: apps/remix/app/components/forms/public-profile-form.tsx
msgid "You can update the profile URL by updating the team URL in the general settings page."
@@ -11009,8 +11091,8 @@ msgid "You cannot delete a group which has a higher role than you."
msgstr "You cannot delete a group which has a higher role than you."
#: apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx
msgid "You cannot delete this item because the document has been sent to recipients"
msgstr "You cannot delete this item because the document has been sent to recipients"
msgid "You cannot delete this item because the document has been sent to recipients."
msgstr "You cannot delete this item because the document has been sent to recipients."
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
msgid "You cannot modify a group which has a higher role than you."
@@ -11036,8 +11118,8 @@ msgstr "You cannot upload documents at this time."
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
msgid "You cannot upload encrypted PDFs"
msgstr "You cannot upload encrypted PDFs"
msgid "You cannot upload encrypted PDFs."
msgstr "You cannot upload encrypted PDFs."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.billing.tsx
msgid "You currently have an inactive <0>{currentProductName}</0> subscription"
@@ -11048,8 +11130,8 @@ msgid "You currently have no access to any teams within this organisation. Pleas
msgstr "You currently have no access to any teams within this organisation. Please contact your organisation to request access."
#: apps/remix/app/components/forms/token.tsx
msgid "You do not have permission to create a token for this team"
msgstr "You do not have permission to create a token for this team"
msgid "You do not have permission to create a token for this team."
msgstr "You do not have permission to create a token for this team."
#: apps/remix/app/components/tables/user-billing-organisations-table.tsx
msgid "You don't manage billing for any organisations."
@@ -11118,8 +11200,8 @@ msgstr "You have not yet created or received any documents. To create a document
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
msgid "You have reached the limit of the number of files per envelope"
msgstr "You have reached the limit of the number of files per envelope"
msgid "You have reached the limit of the number of files per envelope."
msgstr "You have reached the limit of the number of files per envelope."
#. placeholder {0}: quota.directTemplates
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
@@ -11274,6 +11356,10 @@ msgstr "You will now be required to enter a code from your authenticator app whe
msgid "You will receive an email copy of the signed document once everyone has signed."
msgstr "You will receive an email copy of the signed document once everyone has signed."
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "You're an admin. You can enable AI features for this team right away. Everyone on the team will see AI detection once enabled."
msgstr "You're an admin. You can enable AI features for this team right away. Everyone on the team will see AI detection once enabled."
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "You've made too many detection requests. Please wait a minute before trying again."
@@ -11332,6 +11418,10 @@ msgstr "Your current plan is past due."
msgid "Your direct signing templates"
msgstr "Your direct signing templates"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "Your document content will be sent securely to our AI provider solely for detection and will not be stored or used for training."
msgstr "Your document content will be sent securely to our AI provider solely for detection and will not be stored or used for training."
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Your document failed to upload."
@@ -11367,7 +11457,7 @@ msgstr "Your document has been successfully duplicated."
msgid "Your document has been uploaded successfully."
msgstr "Your document has been uploaded successfully."
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Your document has been uploaded successfully. You will be redirected to the template page."
msgstr "Your document has been uploaded successfully. You will be redirected to the template page."
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+186 -97
View File
@@ -96,6 +96,11 @@ msgstr "{0, plural, one {# campo} other {# campos}}"
msgid "{0, plural, one {# folder} other {# folders}}"
msgstr "{0, plural, one {# pasta} other {# pastas}}"
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "{0, plural, one {# recipient have been added from AI detection.} other {# recipients have been added from AI detection.}}"
msgstr ""
#. placeholder {0}: template.recipients.length
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
msgid "{0, plural, one {# recipient} other {# recipients}}"
@@ -146,11 +151,44 @@ msgstr "{0, plural, one {1 campo correspondente} other {# campos correspondentes
msgid "{0, plural, one {1 Recipient} other {# Recipients}}"
msgstr "{0, plural, one {1 Destinatário} other {# Destinatários}}"
#. placeholder {0}: progress.fieldsDetected
#. placeholder {1}: progress.pagesProcessed
#. placeholder {2}: progress.totalPages
#. placeholder {3}: progress.pagesProcessed
#. placeholder {4}: progress.totalPages
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "{0, plural, one {Page {1} of {2} - # field found} other {Page {3} of {4} - # fields found}}"
msgstr ""
#. placeholder {0}: progress.recipientsDetected
#. placeholder {1}: progress.pagesProcessed
#. placeholder {2}: progress.totalPages
#. placeholder {3}: progress.pagesProcessed
#. placeholder {4}: progress.totalPages
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "{0, plural, one {Page {1} of {2} - # recipient found} other {Page {3} of {4} - # recipients found}}"
msgstr ""
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "{0, plural, one {Recipient added} other {Recipients added}}"
msgstr ""
#. placeholder {0}: pendingRecipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0, plural, one {Waiting on 1 recipient} other {Waiting on # recipients}}"
msgstr "{0, plural, one {Aguardando 1 destinatário} other {Aguardando # destinatários}}"
#. placeholder {0}: detectedFields.length
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "{0, plural, one {We found # field in your document.} other {We found # fields in your document.}}"
msgstr ""
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "{0, plural, one {We found # recipient in your document.} other {We found # recipients in your document.}}"
msgstr ""
#. placeholder {0}: _(FRIENDLY_FIELD_TYPE[fieldType as FieldType])
#. placeholder {0}: route.label
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
@@ -195,11 +233,6 @@ msgstr "{0} de {1} documentos restantes este mês."
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} em nome de \"{1}\" convidou você para {recipientActionVerb} o documento \"{2}\"."
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "{0} recipient(s) have been added from AI detection."
msgstr "{0} destinatário(s) foram adicionados pela detecção de IA."
#. placeholder {0}: organisation.name
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "{0} Teams"
@@ -846,9 +879,6 @@ msgid "A request to use your email has been initiated by {0} on Documenso"
msgstr "Uma solicitação para usar seu e-mail foi iniciada por {0} no Documenso"
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso"
msgstr "Um segredo que será enviado para sua URL para que você possa verificar se a solicitação foi enviada pelo Documenso"
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso."
msgstr "Um segredo que será enviado para sua URL para que você possa verificar se a solicitação foi enviada pelo Documenso."
@@ -1283,6 +1313,10 @@ msgstr "Após o envio, um documento será gerado automaticamente e adicionado à
msgid "AI Features"
msgstr "Recursos de IA"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "AI features are disabled for your team. Please ask your team owner or organisation owner to enable them."
msgstr ""
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
msgid "All"
@@ -2260,6 +2294,10 @@ msgstr "Gráficos"
msgid "Checkbox"
msgstr "Caixa de seleção"
#: packages/ui/primitives/document-flow/field-content.tsx
msgid "Checkbox option"
msgstr ""
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Checkbox Settings"
msgstr "Configurações da Caixa de Seleção"
@@ -2363,6 +2401,7 @@ msgstr "Segredo do Cliente"
msgid "Client secret is required"
msgstr "Segredo do Cliente é obrigatório"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
@@ -2375,7 +2414,6 @@ msgstr "Segredo do Cliente é obrigatório"
#: apps/remix/app/components/dialogs/team-group-delete-dialog.tsx
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-delete-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
@@ -2386,7 +2424,9 @@ msgstr "Segredo do Cliente é obrigatório"
#: apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/dialog.tsx
#: packages/ui/primitives/document-flow/missing-signature-field-dialog.tsx
#: packages/ui/primitives/sheet.tsx
msgid "Close"
msgstr "Fechar"
@@ -3064,6 +3104,10 @@ msgstr "Fuso Horário Padrão"
msgid "Default Value"
msgstr "Valor Padrão"
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Delegate Document Ownership"
msgstr ""
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete"
msgstr "excluir"
@@ -3283,8 +3327,8 @@ msgid "Device"
msgstr "Dispositivo"
#: packages/email/templates/reset-password.tsx
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us.</0>"
msgstr "Não solicitou uma alteração de senha? Estamos aqui para ajudá-lo a proteger sua conta, basta <0>entrar em contato conosco.</0>"
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us</0>."
msgstr ""
#: apps/remix/app/components/general/template/template-direct-link-badge.tsx
#: apps/remix/app/components/tables/templates-table.tsx
@@ -3627,6 +3671,11 @@ msgctxt "Audit log format"
msgid "Document opened"
msgstr "Documento aberto"
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "Document ownership delegated"
msgstr ""
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document pending"
msgstr "Documento pendente"
@@ -3825,6 +3874,11 @@ msgstr "Nome do Domínio"
msgid "Don't have an account? <0>Sign up</0>"
msgstr "Não tem uma conta? <0>Inscreva-se</0>"
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
msgid "Don't transfer (Delete all documents)"
msgstr ""
#: apps/remix/app/components/forms/2fa/enable-authenticator-app-dialog.tsx
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
@@ -3963,6 +4017,7 @@ msgstr "Ex: 100"
#: apps/remix/app/components/general/document/document-page-view-button.tsx
#: apps/remix/app/components/general/document/document-page-view-dropdown.tsx
#: apps/remix/app/components/general/teams/team-email-dropdown.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/tables/documents-table-action-button.tsx
#: apps/remix/app/components/tables/documents-table-action-dropdown.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
@@ -4179,6 +4234,15 @@ msgstr "Ativar conta"
msgid "Enable Account"
msgstr "Ativar Conta"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "Enable AI detection"
msgstr ""
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "Enable AI features"
msgstr ""
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Enable AI-powered features such as automatic recipient detection. When enabled, document content will be sent to AI providers. We only use providers that do not retain data for training and prefer European regions where available."
msgstr "Ative recursos com tecnologia de IA, como detecção automática de destinatários. Quando ativado, o conteúdo do documento será enviado para provedores de IA. Usamos apenas provedores que não retêm dados para treinamento e preferimos regiões europeias quando disponíveis."
@@ -4219,6 +4283,10 @@ msgstr "Ativar ordem de assinatura"
msgid "Enable SSO portal"
msgstr "Ativar portal SSO"
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Enable team API tokens to delegate document ownership to another team member."
msgstr ""
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
@@ -4817,10 +4885,26 @@ msgstr "Ir para o início"
msgid "Go to document"
msgstr "Ir para o documento"
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to first page"
msgstr ""
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to last page"
msgstr ""
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to next page"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Go to owner"
msgstr "Ir para o proprietário"
#: packages/ui/primitives/data-table-pagination.tsx
msgid "Go to previous page"
msgstr ""
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "Go to team"
msgstr "Ir para a equipe"
@@ -4901,8 +4985,8 @@ msgid "Here you can set branding preferences for your organisation. Teams will i
msgstr "Aqui você pode definir preferências de marca para sua organização. As equipes herdarão essas configurações por padrão."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
msgid "Here you can set branding preferences for your team"
msgstr "Aqui você pode definir preferências de marca para sua equipe"
msgid "Here you can set branding preferences for your team."
msgstr ""
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
msgid "Here you can set document preferences for your organisation. Teams will inherit these settings by default."
@@ -4917,12 +5001,12 @@ msgid "Here you can set preferences and defaults for your team."
msgstr "Aqui você pode definir preferências e padrões para sua equipe."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
msgid "Here you can set your general branding preferences"
msgstr "Aqui você pode definir suas preferências gerais de marca"
msgid "Here you can set your general branding preferences."
msgstr ""
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
msgid "Here you can set your general document preferences"
msgstr "Aqui você pode definir suas preferências gerais de documento"
msgid "Here you can set your general document preferences."
msgstr ""
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid "Here's how it works:"
@@ -5087,6 +5171,7 @@ msgstr "Herdar método de autenticação"
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/email-preferences-form.tsx
#: apps/remix/app/components/forms/email-preferences-form.tsx
msgid "Inherit from organisation"
@@ -5479,6 +5564,7 @@ msgid "Looking for signature fields"
msgstr "Procurando campos de assinatura"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/user-organisations-table.tsx
msgid "Manage"
@@ -5539,6 +5625,10 @@ msgstr "Gerenciar contas vinculadas"
msgid "Manage organisation"
msgstr "Gerenciar organização"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "Manage Organisation"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/organisations._index.tsx
msgid "Manage organisations"
msgstr "Gerenciar organizações"
@@ -5702,15 +5792,15 @@ msgstr "Membro Desde"
msgid "Members"
msgstr "Membros"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/forms/support-ticket-form.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Message"
msgstr "Mensagem"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Message <0>(Optional)</0>"
msgstr "Mensagem <0>(Opcional)</0>"
@@ -5860,10 +5950,6 @@ msgstr "Nunca expirar"
msgid "New Password"
msgstr "Nova Senha"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "New Template"
msgstr "Novo Modelo"
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
@@ -5889,6 +5975,7 @@ msgstr "Nome do Próximo Destinatário"
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "No"
msgstr "Não"
@@ -6096,8 +6183,8 @@ msgid "Once you have scanned the QR code or entered the code manually, enter the
msgstr "Depois de escanear o código QR ou inserir o código manualmente, insira o código fornecido pelo seu aplicativo autenticador abaixo."
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button"
msgstr "Depois de atualizar seus registros DNS, pode levar até 48 horas para que eles sejam propagados. Assim que a propagação do DNS estiver concluída, você precisará voltar e pressionar o botão \"Sincronizar\" domínios"
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button."
msgstr ""
#: packages/lib/constants/template.ts
msgid "Once your template is set up, share the link anywhere you want. The person who opens the link will be able to enter their information in the direct link recipient field and complete any other fields assigned to them."
@@ -6131,6 +6218,10 @@ msgstr "Apenas arquivos PDF são permitidos"
msgid "Oops! Something went wrong."
msgstr "Ops! Algo deu errado."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "Open menu"
msgstr ""
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
msgid "Opened"
msgstr "Aberto"
@@ -6324,20 +6415,6 @@ msgstr "Propriedade transferida para {organisationMemberName}."
msgid "Page {0} of {1}"
msgstr "Página {0} de {1}"
#. placeholder {0}: progress.pagesProcessed
#. placeholder {1}: progress.totalPages
#. placeholder {2}: progress.fieldsDetected
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "Page {0} of {1} - {2} field(s) found"
msgstr "Página {0} de {1} - {2} campo(s) encontrado(s)"
#. placeholder {0}: progress.pagesProcessed
#. placeholder {1}: progress.totalPages
#. placeholder {2}: progress.recipientsDetected
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "Page {0} of {1} - {2} recipient(s) found"
msgstr "Página {0} de {1} - {2} destinatário(s) encontrado(s)"
#. placeholder {0}: i + 1
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
#: packages/ui/primitives/pdf-viewer/base.tsx
@@ -6683,10 +6760,6 @@ msgstr "Por favor, tente um domínio diferente."
msgid "Please try again and make sure you enter the correct email address."
msgstr "Por favor, tente novamente e certifique-se de inserir o endereço de e-mail correto."
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "Please try again later."
msgstr "Por favor, tente novamente mais tarde."
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
#: packages/ui/primitives/pdf-viewer/base.tsx
@@ -6964,10 +7037,6 @@ msgstr "Destinatário atualizado"
msgid "Recipients"
msgstr "Destinatários"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
msgid "Recipients added"
msgstr "Destinatários adicionados"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Recipients metrics"
msgstr "Métricas de destinatários"
@@ -7141,8 +7210,8 @@ msgstr "Responder ao e-mail"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Reply To Email"
msgstr "Responder Para E-mail"
msgid "Reply To Email <0>(Optional)</0>"
msgstr ""
#: apps/remix/app/components/general/webhook-logs-sheet.tsx
msgid "Request"
@@ -7279,6 +7348,11 @@ msgstr "Tentar novamente"
msgid "Return"
msgstr "Retornar"
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/routes/_recipient+/sign.$token+/waiting.tsx
msgid "Return Home"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/o.$orgUrl.signin.tsx
msgid "Return to Documenso sign in page here"
msgstr "Retornar para a página de login do Documenso aqui"
@@ -7445,6 +7519,7 @@ msgid "See the background jobs tab for the status"
msgstr "Veja a aba de trabalhos em segundo plano para o status"
#: apps/remix/app/components/general/document-signing/document-signing-dropdown-field.tsx
#: packages/ui/primitives/document-flow/field-content.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx
#: packages/ui/primitives/document-flow/types.ts
msgid "Select"
@@ -8084,7 +8159,6 @@ msgstr "Alguns signatários não receberam um campo de assinatura. Por favor, at
#: apps/remix/app/components/dialogs/team-email-delete-dialog.tsx
#: apps/remix/app/components/dialogs/team-inherit-member-disable-dialog.tsx
#: apps/remix/app/components/dialogs/team-inherit-member-enable-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-delete-dialog.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
@@ -8247,14 +8321,14 @@ msgstr "Cliente Stripe criado com sucesso"
msgid "Stripe Customer ID"
msgstr "ID do Cliente Stripe"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/forms/support-ticket-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Subject"
msgstr "Assunto"
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "Subject <0>(Optional)</0>"
msgstr "Assunto <0>(Opcional)</0>"
@@ -8575,7 +8649,6 @@ msgstr "Equipes às quais este grupo da organização está atualmente atribuíd
msgid "Template"
msgstr "Modelo"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Template (Legacy)"
msgstr "Modelo (Legado)"
@@ -8588,7 +8661,7 @@ msgstr "Modelo Criado"
msgid "Template deleted"
msgstr "Modelo excluído"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Template document uploaded"
msgstr "Documento de modelo enviado"
@@ -8650,10 +8723,6 @@ msgstr "Modelo enviado"
msgid "Templates"
msgstr "Modelos"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "Templates allow you to quickly generate documents with pre-filled recipients and fields."
msgstr "Modelos permitem gerar documentos rapidamente com destinatários e campos pré-preenchidos."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
msgid "Test"
msgstr "Teste"
@@ -8787,6 +8856,12 @@ msgstr "O proprietário do documento foi notificado sobre esta rejeição. Nenhu
msgid "The document owner has been notified of your decision. They may contact you with further instructions if necessary."
msgstr "O proprietário do documento foi notificado sobre sua decisão. Eles podem entrar em contato com você com mais instruções, se necessário."
#. placeholder {0}: data.delegatedOwnerName || data.delegatedOwnerEmail
#. placeholder {1}: data.teamName
#: packages/lib/utils/document-audit-logs.ts
msgid "The document ownership was delegated to {0} on behalf of {1}"
msgstr ""
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
msgid "The document was created but could not be sent to recipients."
msgstr "O documento foi criado, mas não pôde ser enviado aos destinatários."
@@ -8821,8 +8896,8 @@ msgid "The email domain you are looking for may have been removed, renamed or ma
msgstr "O domínio de e-mail que você está procurando pode ter sido removido, renomeado ou nunca ter existido."
#: apps/remix/app/components/forms/signin.tsx
msgid "The email or password provided is incorrect"
msgstr "O e-mail ou senha fornecidos estão incorretos"
msgid "The email or password provided is incorrect."
msgstr ""
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
@@ -9020,8 +9095,8 @@ msgid "The token you have used to reset your password is either expired or it ne
msgstr "O token que você usou para redefinir sua senha expirou ou nunca existiu. Se você ainda esqueceu sua senha, solicite um novo link de redefinição."
#: apps/remix/app/components/forms/signin.tsx
msgid "The two-factor authentication code provided is incorrect"
msgstr "O código de autenticação de dois fatores fornecido está incorreto"
msgid "The two-factor authentication code provided is incorrect."
msgstr ""
#: apps/remix/app/components/forms/editor/editor-field-signature-form.tsx
msgid "The typed signature font size"
@@ -9099,7 +9174,6 @@ msgstr "Esta conta foi desativada. Entre em contato com o suporte."
#: apps/remix/app/components/forms/signin.tsx
msgid "This account has not been verified. Please verify your account before signing in."
msgstr "Esta conta não foi verificada. Verifique sua conta antes de entrar."
msgstr "Esta conta não foi verificada. Verifique sua conta antes de entrar."
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
msgid "This action is irreversible. Please ensure you have informed the user before proceeding."
@@ -9192,8 +9266,8 @@ msgid "This document was created using a direct link."
msgstr "Este documento foi criado usando um link direto."
#: packages/email/template-components/template-footer.tsx
msgid "This document was sent using <0>Documenso.</0>"
msgstr "Este documento foi enviado usando <0>Documenso.</0>"
msgid "This document was sent using <0>Documenso</0>."
msgstr ""
#: apps/remix/app/components/dialogs/envelope-duplicate-dialog.tsx
msgid "This document will be duplicated."
@@ -9524,6 +9598,10 @@ msgstr "Total de Signatários que se Inscreveram"
msgid "Total Users"
msgstr "Total de Usuários"
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
msgid "Transfer documents to a different team"
msgstr ""
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
msgid "Triggers"
@@ -9536,6 +9614,10 @@ msgstr "Gatilhos"
msgid "Try again"
msgstr "Tente novamente"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not retain your data for training."
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Two factor authentication"
msgstr "Autenticação de dois fatores"
@@ -9682,6 +9764,10 @@ msgstr "Não autorizado"
msgid "Uncompleted"
msgstr "Não concluído"
#: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Undo"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/security.linked-accounts.tsx
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
@@ -10294,8 +10380,8 @@ msgstr "Aguardando Sua Vez"
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
msgid "Want to send slick signing links like this one? <0>Check out Documenso.</0>"
msgstr "Quer enviar links de assinatura elegantes como este? <0>Confira o Documenso.</0>"
msgid "Want to send slick signing links like this one? <0>Check out Documenso</0>."
msgstr ""
#: apps/remix/app/routes/_profile+/_layout.tsx
msgid "Want your own public profile?"
@@ -10330,6 +10416,10 @@ msgstr "Não conseguimos atualizar esta passkey no momento. Por favor, tente nov
msgid "We couldn't create a Stripe customer. Please try again."
msgstr "Não conseguimos criar um cliente Stripe. Por favor, tente novamente."
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "We couldn't enable AI features right now. Please try again."
msgstr ""
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "We couldn't update the group. Please try again."
msgstr "Não conseguimos atualizar o grupo. Por favor, tente novamente."
@@ -10530,16 +10620,6 @@ msgstr "Encontramos um erro desconhecido ao tentar atualizar o e-mail da equipe.
msgid "We encountered an unknown error while attempting update your profile. Please try again later."
msgstr "Encontramos um erro desconhecido ao tentar atualizar seu perfil. Por favor, tente novamente mais tarde."
#. placeholder {0}: detectedFields.length
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
msgid "We found {0} field(s) in your document."
msgstr "Encontramos {0} campo(s) no seu documento."
#. placeholder {0}: detectedRecipients.length
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "We found {0} recipient(s) in your document."
msgstr "Encontramos {0} destinatário(s) no seu documento."
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
msgid "We have sent a confirmation email for verification."
msgstr "Enviamos um e-mail de confirmação para verificação."
@@ -10789,6 +10869,7 @@ msgstr "Anual"
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
#: apps/remix/app/components/forms/document-preferences-form.tsx
msgid "Yes"
msgstr "Sim"
@@ -10901,13 +10982,13 @@ msgid "You are currently updating <0>{0}</0>"
msgstr "Você está atualizando <0>{0}</0>"
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
msgid "You are currently updating <0>{memberName}.</0>"
msgstr "Você está atualizando <0>{memberName}.</0>"
msgid "You are currently updating <0>{memberName}</0>."
msgstr ""
#: apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
msgid "You are currently updating <0>{organisationMemberName}.</0>"
msgstr "Você está atualizando <0>{organisationMemberName}.</0>"
msgid "You are currently updating <0>{organisationMemberName}</0>."
msgstr ""
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
msgid "You are currently updating the <0>{passkeyName}</0> passkey."
@@ -10968,16 +11049,16 @@ msgstr "Você pode ativar o acesso para permitir que todos os membros da organiz
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.email.tsx
msgid "You can manage your email preferences here"
msgstr "Você pode gerenciar suas preferências de e-mail aqui"
msgid "You can manage your email preferences here."
msgstr ""
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "You can only detect fields in draft envelopes"
msgstr "Você só pode detectar campos em envelopes de rascunho"
#: packages/email/templates/confirm-team-email.tsx
msgid "You can revoke access at any time in your team settings on Documenso <0>here.</0>"
msgstr "Você pode revogar o acesso a qualquer momento nas configurações da sua equipe no Documenso <0>aqui.</0>"
msgid "You can revoke access at any time in your team settings on Documenso <0>here</0>."
msgstr ""
#: apps/remix/app/components/forms/public-profile-form.tsx
msgid "You can update the profile URL by updating the team URL in the general settings page."
@@ -11010,8 +11091,8 @@ msgid "You cannot delete a group which has a higher role than you."
msgstr "Você não pode excluir um grupo que tem uma função superior à sua."
#: apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx
msgid "You cannot delete this item because the document has been sent to recipients"
msgstr "Você não pode excluir este item porque o documento foi enviado aos destinatários"
msgid "You cannot delete this item because the document has been sent to recipients."
msgstr ""
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
msgid "You cannot modify a group which has a higher role than you."
@@ -11037,8 +11118,8 @@ msgstr "Você não pode enviar documentos neste momento."
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
msgid "You cannot upload encrypted PDFs"
msgstr "Você não pode enviar PDFs criptografados"
msgid "You cannot upload encrypted PDFs."
msgstr ""
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.billing.tsx
msgid "You currently have an inactive <0>{currentProductName}</0> subscription"
@@ -11049,8 +11130,8 @@ msgid "You currently have no access to any teams within this organisation. Pleas
msgstr "Você atualmente não tem acesso a nenhuma equipe dentro desta organização. Por favor, entre em contato com sua organização para solicitar acesso."
#: apps/remix/app/components/forms/token.tsx
msgid "You do not have permission to create a token for this team"
msgstr "Você não tem permissão para criar um token para esta equipe"
msgid "You do not have permission to create a token for this team."
msgstr ""
#: apps/remix/app/components/tables/user-billing-organisations-table.tsx
msgid "You don't manage billing for any organisations."
@@ -11119,8 +11200,8 @@ msgstr "Você ainda não criou ou recebeu nenhum documento. Para criar um docume
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
msgid "You have reached the limit of the number of files per envelope"
msgstr "Você atingiu o limite do número de arquivos por envelope"
msgid "You have reached the limit of the number of files per envelope."
msgstr ""
#. placeholder {0}: quota.directTemplates
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
@@ -11275,6 +11356,10 @@ msgstr "Agora será necessário inserir um código do seu aplicativo autenticado
msgid "You will receive an email copy of the signed document once everyone has signed."
msgstr "Você receberá uma cópia por e-mail do documento assinado assim que todos assinarem."
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "You're an admin. You can enable AI features for this team right away. Everyone on the team will see AI detection once enabled."
msgstr ""
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
msgid "You've made too many detection requests. Please wait a minute before trying again."
@@ -11333,6 +11418,10 @@ msgstr "Seu plano atual está vencido."
msgid "Your direct signing templates"
msgstr "Seus modelos de assinatura direta"
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
msgid "Your document content will be sent securely to our AI provider solely for detection and will not be stored or used for training."
msgstr ""
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Your document failed to upload."
@@ -11368,7 +11457,7 @@ msgstr "Seu documento foi duplicado com sucesso."
msgid "Your document has been uploaded successfully."
msgstr "Seu documento foi enviado com sucesso."
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Your document has been uploaded successfully. You will be redirected to the template page."
msgstr "Seu documento foi enviado com sucesso. Você será redirecionado para a página do modelo."
@@ -11559,4 +11648,4 @@ msgstr "Seu código de verificação:"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.sso.tsx
msgid "your-domain.com another-domain.com"
msgstr "seu-dominio.com outro-dominio.com"
msgstr "seu-dominio.com outro-dominio.com"
File diff suppressed because it is too large Load Diff
+14
View File
@@ -44,6 +44,7 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
'DOCUMENT_TITLE_UPDATED', // When the document title is updated.
'DOCUMENT_EXTERNAL_ID_UPDATED', // When the document external ID is updated.
'DOCUMENT_MOVED_TO_TEAM', // When the document is moved to a team.
'DOCUMENT_DELEGATED_OWNER_CREATED', // When the document delegated owner is created.
// ACCESS AUTH 2FA events.
'DOCUMENT_ACCESS_AUTH_2FA_REQUESTED', // When ACCESS AUTH 2FA is requested.
@@ -681,6 +682,18 @@ export const ZDocumentAuditLogEventDocumentMovedToTeamSchema = z.object({
}),
});
/**
* Event: Document delegated owner created.
*/
export const ZDocumentAuditLogEventDocumentDelegatedOwnerCreatedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELEGATED_OWNER_CREATED),
data: z.object({
delegatedOwnerName: z.string().nullable(),
delegatedOwnerEmail: z.string(),
teamName: z.string(),
}),
});
export const ZDocumentAuditLogBaseSchema = z.object({
id: z.string(),
createdAt: z.date(),
@@ -701,6 +714,7 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
ZDocumentAuditLogEventDocumentCreatedSchema,
ZDocumentAuditLogEventDocumentDeletedSchema,
ZDocumentAuditLogEventDocumentMovedToTeamSchema,
ZDocumentAuditLogEventDocumentDelegatedOwnerCreatedSchema,
ZDocumentAuditLogEventDocumentFieldsAutoInsertedSchema,
ZDocumentAuditLogEventDocumentFieldInsertedSchema,
ZDocumentAuditLogEventDocumentFieldUninsertedSchema,
+37 -2
View File
@@ -115,5 +115,40 @@ export type TEnvelopeLite = z.infer<typeof ZEnvelopeLiteSchema>;
/**
* A version of the envelope response schema when returning multiple envelopes at once from a single API endpoint.
*/
// export const ZEnvelopeManySchema = X
// export type TEnvelopeMany = z.infer<typeof ZEnvelopeManySchema>;
export const ZEnvelopeManySchema = EnvelopeSchema.pick({
internalVersion: true,
type: true,
status: true,
source: true,
visibility: true,
templateType: true,
id: true,
secondaryId: true,
externalId: true,
createdAt: true,
updatedAt: true,
completedAt: true,
deletedAt: true,
title: true,
authOptions: true,
formValues: true,
publicTitle: true,
publicDescription: true,
userId: true,
teamId: true,
folderId: true,
templateId: true,
}).extend({
user: z.object({
id: z.number(),
name: z.string(),
email: z.string(),
}),
recipients: ZEnvelopeRecipientLiteSchema.array(),
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
});
export type TEnvelopeMany = z.infer<typeof ZEnvelopeManySchema>;
+4 -4
View File
@@ -31,7 +31,7 @@ export const ZClaimFlagsSchema = z.object({
authenticationPortal: z.boolean().optional(),
allowEnvelopes: z.boolean().optional(),
allowLegacyEnvelopes: z.boolean().optional(),
});
export type TClaimFlags = z.infer<typeof ZClaimFlagsSchema>;
@@ -84,9 +84,9 @@ export const SUBSCRIPTION_CLAIM_FEATURE_FLAGS: Record<
key: 'authenticationPortal',
label: 'Authentication portal',
},
allowEnvelopes: {
key: 'allowEnvelopes',
label: 'Allow envelopes',
allowLegacyEnvelopes: {
key: 'allowLegacyEnvelopes',
label: 'Allow Legacy Envelopes',
},
};
@@ -530,6 +530,13 @@ export const formatDocumentAuditLogAction = (
anonymous: msg`Envelope item deleted`,
identified: msg`${prefix} deleted an envelope item with title ${data.envelopeItemTitle}`,
}))
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELEGATED_OWNER_CREATED }, ({ data }) => ({
anonymous: msg({
message: `Document ownership delegated`,
context: `Audit log format`,
}),
identified: msg`The document ownership was delegated to ${data.delegatedOwnerName || data.delegatedOwnerEmail} on behalf of ${data.teamName}`,
}))
.exhaustive();
return {
+97
View File
@@ -18,6 +18,8 @@ const ZDocumentIdSchema = z.string().regex(/^document_\d+$/);
const ZTemplateIdSchema = z.string().regex(/^template_\d+$/);
const ZEnvelopeIdSchema = z.string().regex(/^envelope_.{2,}$/);
const MAX_ENVELOPE_IDS_PER_REQUEST = 20;
export type EnvelopeIdOptions =
| {
type: 'envelopeId';
@@ -32,6 +34,20 @@ export type EnvelopeIdOptions =
id: number;
};
export type EnvelopeIdsOptions =
| {
type: 'envelopeId';
ids: string[];
}
| {
type: 'documentId';
ids: number[];
}
| {
type: 'templateId';
ids: number[];
};
/**
* Parses an unknown document or template ID.
*
@@ -89,6 +105,87 @@ export const unsafeBuildEnvelopeIdQuery = (
.exhaustive();
};
/**
* Parses multiple document or template IDs and builds a query filter.
*
* This is UNSAFE because it does not validate access, it only validates ID format and builds the query.
*
* @throws AppError if any ID is invalid or if the array exceeds the maximum limit
*/
export const unsafeBuildEnvelopeIdsQuery = (
options: EnvelopeIdsOptions,
expectedEnvelopeType: EnvelopeType | null,
) => {
if (!options.ids || options.ids.length === 0) {
throw new AppError(AppErrorCode.INVALID_BODY, {
message: 'At least one ID is required',
});
}
if (options.ids.length > MAX_ENVELOPE_IDS_PER_REQUEST) {
throw new AppError(AppErrorCode.INVALID_BODY, {
message: `Cannot request more than ${MAX_ENVELOPE_IDS_PER_REQUEST} envelopes at once`,
});
}
return match(options)
.with({ type: 'envelopeId' }, (value) => {
const validatedIds: string[] = [];
for (const id of value.ids) {
const parsed = ZEnvelopeIdSchema.safeParse(id);
if (!parsed.success) {
throw new AppError(AppErrorCode.INVALID_BODY, {
message: `Invalid envelope ID: ${id}`,
});
}
validatedIds.push(parsed.data);
}
if (expectedEnvelopeType) {
return {
id: { in: validatedIds },
type: expectedEnvelopeType,
};
}
return {
id: { in: validatedIds },
};
})
.with({ type: 'documentId' }, (value) => {
if (expectedEnvelopeType && expectedEnvelopeType !== EnvelopeType.DOCUMENT) {
throw new AppError(AppErrorCode.INVALID_BODY, {
message: 'Invalid document ID type',
});
}
const secondaryIds = value.ids.map((id) => mapDocumentIdToSecondaryId(id));
return {
type: EnvelopeType.DOCUMENT,
secondaryId: { in: secondaryIds },
};
})
.with({ type: 'templateId' }, (value) => {
if (expectedEnvelopeType && expectedEnvelopeType !== EnvelopeType.TEMPLATE) {
throw new AppError(AppErrorCode.INVALID_BODY, {
message: 'Invalid template ID type',
});
}
const secondaryIds = value.ids.map((id) => mapTemplateIdToSecondaryId(id));
return {
type: EnvelopeType.TEMPLATE,
secondaryId: { in: secondaryIds },
};
})
.exhaustive();
};
/**
* Maps a legacy document ID number to an envelope secondary ID.
*
+1
View File
@@ -117,6 +117,7 @@ export const generateDefaultOrganisationSettings = (): Omit<
documentLanguage: 'en',
documentTimezone: null, // Null means local timezone.
documentDateFormat: DEFAULT_DOCUMENT_DATE_FORMAT,
delegateDocumentOwnership: false,
includeSenderDetails: true,
includeSigningCertificate: true,
+1
View File
@@ -184,6 +184,7 @@ export const generateDefaultTeamSettings = (): Omit<TeamGlobalSettings, 'id' | '
documentLanguage: null,
documentTimezone: null,
documentDateFormat: null,
delegateDocumentOwnership: null,
includeSenderDetails: null,
includeSigningCertificate: null,