fix: make invite and confirmations long lived (#1309)

Previously we would delete all invites and confirmation tokens upon
completing the action that they represent.

This change instead adds a flag on each token indicating whether it has
been completed so we can action a
completed token differently in the UI to reduce confusion for users.

This had been brought up a number of times where confirmation emails,
team member invites and other items
may have been actioned and forgotten about causing an error toast/page
upon subsequent revisit.
This commit is contained in:
Lucas Smith
2024-08-28 14:08:35 +10:00
committed by GitHub
parent 7943ed5353
commit dfa89ffe44
18 changed files with 352 additions and 97 deletions

View File

@ -17,6 +17,7 @@ import { AppError, AppErrorCode } from '../errors/app-error';
import { jobsClient } from '../jobs/client';
import { isTwoFactorAuthenticationEnabled } from '../server-only/2fa/is-2fa-availble';
import { validateTwoFactorAuthentication } from '../server-only/2fa/validate-2fa';
import { decryptSecondaryData } from '../server-only/crypto/decrypt';
import { getMostRecentVerificationTokenByUserId } from '../server-only/user/get-most-recent-verification-token-by-user-id';
import { getUserByEmail } from '../server-only/user/get-user-by-email';
import type { TAuthenticationResponseJSONSchema } from '../types/webauthn';
@ -267,6 +268,55 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
},
});
return {
id: Number(user.id),
email: user.email,
name: user.name,
emailVerified: user.emailVerified?.toISOString() ?? null,
} satisfies User;
},
}),
CredentialsProvider({
id: 'manual',
name: 'Manual',
credentials: {
credential: { label: 'Credential', type: 'credential' },
},
async authorize(credentials, req) {
const credential = credentials?.credential;
if (typeof credential !== 'string' || credential.length === 0) {
throw new AppError(AppErrorCode.INVALID_REQUEST);
}
const decryptedCredential = decryptSecondaryData(credential);
if (!decryptedCredential) {
throw new AppError(AppErrorCode.INVALID_REQUEST);
}
const parsedCredential = JSON.parse(decryptedCredential);
if (typeof parsedCredential !== 'object' || parsedCredential === null) {
throw new AppError(AppErrorCode.INVALID_REQUEST);
}
const { userId, email } = parsedCredential;
if (typeof userId !== 'number' || typeof email !== 'string') {
throw new AppError(AppErrorCode.INVALID_REQUEST);
}
const user = await prisma.user.findFirst({
where: {
id: userId,
},
});
if (!user) {
throw new AppError(AppErrorCode.INVALID_REQUEST);
}
return {
id: Number(user.id),
email: user.email,

View File

@ -1,6 +1,7 @@
import { updateSubscriptionItemQuantity } from '@documenso/ee/server-only/stripe/update-subscription-item-quantity';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { prisma } from '@documenso/prisma';
import { TeamMemberInviteStatus } from '@documenso/prisma/client';
import { jobs } from '../../jobs/client';
@ -22,6 +23,9 @@ export const acceptTeamInvitation = async ({ userId, teamId }: AcceptTeamInvitat
where: {
teamId,
email: user.email,
status: {
not: TeamMemberInviteStatus.DECLINED,
},
},
include: {
team: {
@ -37,6 +41,10 @@ export const acceptTeamInvitation = async ({ userId, teamId }: AcceptTeamInvitat
},
});
if (teamMemberInvite.status === TeamMemberInviteStatus.ACCEPTED) {
return;
}
const { team } = teamMemberInvite;
const teamMember = await tx.teamMember.create({
@ -47,10 +55,13 @@ export const acceptTeamInvitation = async ({ userId, teamId }: AcceptTeamInvitat
},
});
await tx.teamMemberInvite.delete({
await tx.teamMemberInvite.update({
where: {
id: teamMemberInvite.id,
},
data: {
status: TeamMemberInviteStatus.ACCEPTED,
},
});
if (IS_BILLING_ENABLED() && team.subscription) {

View File

@ -28,11 +28,24 @@ export const transferTeamOwnership = async ({ token }: TransferTeamOwnershipOpti
const { team, userId: newOwnerUserId } = teamTransferVerification;
await tx.teamTransferVerification.delete({
where: {
teamId: team.id,
},
});
await Promise.all([
tx.teamTransferVerification.updateMany({
where: {
teamId: team.id,
},
data: {
completed: true,
},
}),
tx.teamTransferVerification.deleteMany({
where: {
teamId: team.id,
expiresAt: {
lt: new Date(),
},
},
}),
]);
const newOwnerUser = await tx.user.findFirstOrThrow({
where: {

View File

@ -4,6 +4,13 @@ import { prisma } from '@documenso/prisma';
import { jobsClient } from '../../jobs/client';
export const EMAIL_VERIFICATION_STATE = {
NOT_FOUND: 'NOT_FOUND',
VERIFIED: 'VERIFIED',
EXPIRED: 'EXPIRED',
ALREADY_VERIFIED: 'ALREADY_VERIFIED',
} as const;
export type VerifyEmailProps = {
token: string;
};
@ -19,7 +26,7 @@ export const verifyEmail = async ({ token }: VerifyEmailProps) => {
});
if (!verificationToken) {
return null;
return EMAIL_VERIFICATION_STATE.NOT_FOUND;
}
// check if the token is valid or expired
@ -48,10 +55,14 @@ export const verifyEmail = async ({ token }: VerifyEmailProps) => {
});
}
return valid;
return EMAIL_VERIFICATION_STATE.EXPIRED;
}
const [updatedUser, deletedToken] = await prisma.$transaction([
if (verificationToken.completed) {
return EMAIL_VERIFICATION_STATE.ALREADY_VERIFIED;
}
const [updatedUser] = await prisma.$transaction([
prisma.user.update({
where: {
id: verificationToken.userId,
@ -60,16 +71,28 @@ export const verifyEmail = async ({ token }: VerifyEmailProps) => {
emailVerified: new Date(),
},
}),
prisma.verificationToken.updateMany({
where: {
userId: verificationToken.userId,
},
data: {
completed: true,
},
}),
// Tidy up old expired tokens
prisma.verificationToken.deleteMany({
where: {
userId: verificationToken.userId,
expires: {
lt: new Date(),
},
},
}),
]);
if (!updatedUser || !deletedToken) {
if (!updatedUser) {
throw new Error('Something went wrong while verifying your email. Please try again.');
}
return !!updatedUser && !!deletedToken;
return EMAIL_VERIFICATION_STATE.VERIFIED;
};

View File

@ -748,4 +748,3 @@ msgstr "Sie können derzeit keine Dokumente hochladen."
#: packages/ui/primitives/document-dropzone.tsx:69
msgid "You have reached your document limit."
msgstr "Sie haben Ihr Dokumentenlimit erreicht."

File diff suppressed because one or more lines are too long

View File

@ -617,4 +617,3 @@ msgstr "Sie können Documenso kostenlos selbst hosten oder unsere sofort einsatz
#: apps/marketing/src/components/(marketing)/carousel.tsx:265
msgid "Your browser does not support the video tag."
msgstr "Ihr Browser unterstützt das Video-Tag nicht."

File diff suppressed because one or more lines are too long

View File

@ -845,8 +845,10 @@ msgstr ""
#: apps/web/src/app/(dashboard)/settings/security/passkeys/create-passkey-dialog.tsx:252
#: apps/web/src/app/(unauthenticated)/team/invite/[token]/page.tsx:135
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:108
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:99
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:69
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:143
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:72
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:122
#: apps/web/src/components/templates/manage-public-template-dialog.tsx:330
msgid "Continue"
msgstr ""
@ -1406,7 +1408,7 @@ msgstr ""
msgid "Email Address"
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:113
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/client.tsx:36
msgid "Email Confirmed!"
msgstr ""
@ -1577,9 +1579,9 @@ msgstr ""
msgid "Go Back"
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:64
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:95
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:124
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/client.tsx:48
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:73
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:101
#: apps/web/src/app/(unauthenticated)/verify-email/page.tsx:38
msgid "Go back home"
msgstr ""
@ -1730,7 +1732,7 @@ msgstr ""
msgid "It looks like {0} hasn't added any documents to their profile yet."
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:87
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:93
msgid "It seems that the provided token has expired. We've just sent you another token, please check your email and try again."
msgstr ""
@ -1738,7 +1740,7 @@ msgstr ""
msgid "It seems that there is no token provided, if you are trying to verify your email please follow the link in your email."
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:31
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:40
msgid "It seems that there is no token provided. Please check your email and try again."
msgstr ""
@ -2025,7 +2027,7 @@ msgstr ""
msgid "No results found."
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:28
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:37
msgid "No token provided"
msgstr ""
@ -2805,7 +2807,7 @@ msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-auth-page.tsx:43
#: apps/web/src/app/(teams)/t/[teamUrl]/layout-billing-banner.tsx:53
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/team-email-dropdown.tsx:39
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:52
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:61
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:50
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:99
#: apps/web/src/components/(teams)/dialogs/invite-team-member-dialog.tsx:210
@ -2818,11 +2820,11 @@ msgstr ""
msgid "Something went wrong"
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:75
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:98
msgid "Something went wrong while attempting to transfer the ownership of team <0>{0}</0> to your. Please try again later or contact support."
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:85
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:120
msgid "Something went wrong while attempting to verify your email address for <0>{0}</0>. Please try again later."
msgstr ""
@ -2932,15 +2934,19 @@ msgstr ""
msgid "Team Email"
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:57
msgid "Team email already verified!"
msgstr ""
#: apps/web/src/components/(teams)/dialogs/remove-team-email-dialog.tsx:58
msgid "Team email has been removed"
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:81
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:116
msgid "Team email verification"
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:97
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:132
msgid "Team email verified!"
msgstr ""
@ -2975,11 +2981,15 @@ msgstr ""
msgid "Team only templates are not linked anywhere and are visible only to your team."
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:71
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:94
msgid "Team ownership transfer"
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:87
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:60
msgid "Team ownership transfer already completed!"
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:110
msgid "Team ownership transferred!"
msgstr ""
@ -3109,7 +3119,7 @@ msgstr ""
msgid "The events that will trigger a webhook to be sent to your URL."
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:91
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:114
msgid "The ownership of team <0>{0}</0> has been successfully transferred to you."
msgstr ""
@ -3955,7 +3965,7 @@ msgstr ""
msgid "We were unable to verify your details. Please try again or contact support"
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:56
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:65
msgid "We were unable to verify your email. If your email is not verified already, please try again."
msgstr ""
@ -4131,6 +4141,14 @@ msgstr ""
msgid "You have accepted an invitation from <0>{0}</0> to join their team."
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:64
msgid "You have already completed the ownership transfer for <0>{0}</0>."
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:61
msgid "You have already verified your email address for <0>{0}</0>."
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/decline/[token]/page.tsx:95
#: apps/web/src/app/(unauthenticated)/team/invite/[token]/page.tsx:100
msgid "You have been invited by <0>{0}</0> to join their team."
@ -4187,7 +4205,7 @@ msgstr ""
msgid "You have updated {teamMemberName}."
msgstr ""
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:101
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:136
msgid "You have verified your email address for <0>{0}</0>."
msgstr ""
@ -4289,7 +4307,7 @@ msgstr ""
msgid "Your documents"
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:117
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/client.tsx:40
msgid "Your email has been successfully confirmed! You can now use all features of Documenso."
msgstr ""
@ -4364,7 +4382,7 @@ msgstr ""
msgid "Your templates has been saved successfully."
msgstr ""
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:83
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:89
msgid "Your token has expired!"
msgstr ""
@ -4376,4 +4394,3 @@ msgstr ""
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/tokens/page.tsx:87
msgid "Your tokens will be shown here once you create them."
msgstr ""

File diff suppressed because one or more lines are too long

View File

@ -840,8 +840,10 @@ msgstr "Content"
#: apps/web/src/app/(dashboard)/settings/security/passkeys/create-passkey-dialog.tsx:252
#: apps/web/src/app/(unauthenticated)/team/invite/[token]/page.tsx:135
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:108
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:99
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:69
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:143
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:72
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:122
#: apps/web/src/components/templates/manage-public-template-dialog.tsx:330
msgid "Continue"
msgstr "Continue"
@ -1401,7 +1403,7 @@ msgstr "Email address"
msgid "Email Address"
msgstr "Email Address"
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:113
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/client.tsx:36
msgid "Email Confirmed!"
msgstr "Email Confirmed!"
@ -1572,9 +1574,9 @@ msgstr "General"
msgid "Go Back"
msgstr "Go Back"
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:64
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:95
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:124
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/client.tsx:48
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:73
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:101
#: apps/web/src/app/(unauthenticated)/verify-email/page.tsx:38
msgid "Go back home"
msgstr "Go back home"
@ -1725,7 +1727,7 @@ msgstr "Invoice"
msgid "It looks like {0} hasn't added any documents to their profile yet."
msgstr "It looks like {0} hasn't added any documents to their profile yet."
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:87
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:93
msgid "It seems that the provided token has expired. We've just sent you another token, please check your email and try again."
msgstr "It seems that the provided token has expired. We've just sent you another token, please check your email and try again."
@ -1733,7 +1735,7 @@ msgstr "It seems that the provided token has expired. We've just sent you anothe
msgid "It seems that there is no token provided, if you are trying to verify your email please follow the link in your email."
msgstr "It seems that there is no token provided, if you are trying to verify your email please follow the link in your email."
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:31
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:40
msgid "It seems that there is no token provided. Please check your email and try again."
msgstr "It seems that there is no token provided. Please check your email and try again."
@ -2020,7 +2022,7 @@ msgstr "No recipients"
msgid "No results found."
msgstr "No results found."
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:28
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:37
msgid "No token provided"
msgstr "No token provided"
@ -2800,7 +2802,7 @@ msgstr "Site Settings"
#: apps/web/src/app/(signing)/sign/[token]/signing-auth-page.tsx:43
#: apps/web/src/app/(teams)/t/[teamUrl]/layout-billing-banner.tsx:53
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/team-email-dropdown.tsx:39
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:52
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:61
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:50
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:99
#: apps/web/src/components/(teams)/dialogs/invite-team-member-dialog.tsx:210
@ -2813,11 +2815,11 @@ msgstr "Site Settings"
msgid "Something went wrong"
msgstr "Something went wrong"
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:75
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:98
msgid "Something went wrong while attempting to transfer the ownership of team <0>{0}</0> to your. Please try again later or contact support."
msgstr "Something went wrong while attempting to transfer the ownership of team <0>{0}</0> to your. Please try again later or contact support."
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:85
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:120
msgid "Something went wrong while attempting to verify your email address for <0>{0}</0>. Please try again later."
msgstr "Something went wrong while attempting to verify your email address for <0>{0}</0>. Please try again later."
@ -2927,15 +2929,19 @@ msgstr "Team email"
msgid "Team Email"
msgstr "Team Email"
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:57
msgid "Team email already verified!"
msgstr "Team email already verified!"
#: apps/web/src/components/(teams)/dialogs/remove-team-email-dialog.tsx:58
msgid "Team email has been removed"
msgstr "Team email has been removed"
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:81
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:116
msgid "Team email verification"
msgstr "Team email verification"
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:97
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:132
msgid "Team email verified!"
msgstr "Team email verified!"
@ -2970,11 +2976,15 @@ msgstr "Team Only"
msgid "Team only templates are not linked anywhere and are visible only to your team."
msgstr "Team only templates are not linked anywhere and are visible only to your team."
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:71
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:94
msgid "Team ownership transfer"
msgstr "Team ownership transfer"
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:87
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:60
msgid "Team ownership transfer already completed!"
msgstr "Team ownership transfer already completed!"
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:110
msgid "Team ownership transferred!"
msgstr "Team ownership transferred!"
@ -3104,7 +3114,7 @@ msgstr "The document will be immediately sent to recipients if this is checked."
msgid "The events that will trigger a webhook to be sent to your URL."
msgstr "The events that will trigger a webhook to be sent to your URL."
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:91
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:114
msgid "The ownership of team <0>{0}</0> has been successfully transferred to you."
msgstr "The ownership of team <0>{0}</0> has been successfully transferred to you."
@ -3950,7 +3960,7 @@ msgstr "We were unable to submit this document at this time. Please try again la
msgid "We were unable to verify your details. Please try again or contact support"
msgstr "We were unable to verify your details. Please try again or contact support"
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:56
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:65
msgid "We were unable to verify your email. If your email is not verified already, please try again."
msgstr "We were unable to verify your email. If your email is not verified already, please try again."
@ -4126,6 +4136,14 @@ msgstr "You do not currently have a customer record, this should not happen. Ple
msgid "You have accepted an invitation from <0>{0}</0> to join their team."
msgstr "You have accepted an invitation from <0>{0}</0> to join their team."
#: apps/web/src/app/(unauthenticated)/team/verify/transfer/[token]/page.tsx:64
msgid "You have already completed the ownership transfer for <0>{0}</0>."
msgstr "You have already completed the ownership transfer for <0>{0}</0>."
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:61
msgid "You have already verified your email address for <0>{0}</0>."
msgstr "You have already verified your email address for <0>{0}</0>."
#: apps/web/src/app/(unauthenticated)/team/decline/[token]/page.tsx:95
#: apps/web/src/app/(unauthenticated)/team/invite/[token]/page.tsx:100
msgid "You have been invited by <0>{0}</0> to join their team."
@ -4182,7 +4200,7 @@ msgstr "You have successfully revoked access."
msgid "You have updated {teamMemberName}."
msgstr "You have updated {teamMemberName}."
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:101
#: apps/web/src/app/(unauthenticated)/team/verify/email/[token]/page.tsx:136
msgid "You have verified your email address for <0>{0}</0>."
msgstr "You have verified your email address for <0>{0}</0>."
@ -4284,7 +4302,7 @@ msgstr "Your document has been uploaded successfully. You will be redirected to
msgid "Your documents"
msgstr "Your documents"
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:117
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/client.tsx:40
msgid "Your email has been successfully confirmed! You can now use all features of Documenso."
msgstr "Your email has been successfully confirmed! You can now use all features of Documenso."
@ -4359,7 +4377,7 @@ msgstr "Your template will be duplicated."
msgid "Your templates has been saved successfully."
msgstr "Your templates has been saved successfully."
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:83
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:89
msgid "Your token has expired!"
msgstr "Your token has expired!"