Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5cb03a9448 | |||
| 4fd8a767b2 | |||
| b8e08e88ac | |||
| 031a7b9e36 | |||
| 12fe045195 | |||
| 614106a5e4 | |||
| 8be7137b59 | |||
| 31e2a6443e | |||
| 400d2a2b1a | |||
| e3ce7f94e6 | |||
| cad04f26e7 | |||
| d27f0ee0ef | |||
| fd2b413ed9 | |||
| d11ec8fa2a | |||
| b1127b4f0d | |||
| be4244fb62 | |||
| 504a0893ab | |||
| 266f2eab71 | |||
| e1fc49fa49 | |||
| d213b378b8 |
@@ -49,8 +49,6 @@ Join us in creating the next generation of open trust infrastructure.
|
||||
|
||||
## Community and Next Steps 🎯
|
||||
|
||||
We're currently working on a redesign of the application, including a revamp of the codebase, so Documenso can be more intuitive to use and robust to develop upon.
|
||||
|
||||
- Check out the first source code release in this repository and test it.
|
||||
- Tell us what you think in the [Discussions](https://github.com/documenso/documenso/discussions).
|
||||
- Join the [Discord server](https://documen.so/discord) for any questions and getting to know to other community members.
|
||||
@@ -247,14 +245,14 @@ Now you can install the dependencies and build it:
|
||||
|
||||
```
|
||||
npm i
|
||||
npm run build:web
|
||||
npm run build
|
||||
npm run prisma:migrate-deploy
|
||||
```
|
||||
|
||||
Finally, you can start it with:
|
||||
|
||||
```
|
||||
cd apps/web
|
||||
cd apps/remix
|
||||
npm run start
|
||||
```
|
||||
|
||||
@@ -275,7 +273,7 @@ After=network.target
|
||||
Environment=PATH=/path/to/your/node/binaries
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/var/www/documenso/apps/web
|
||||
WorkingDirectory=/var/www/documenso/apps/remix
|
||||
ExecStart=/usr/bin/next start -p 3500
|
||||
TimeoutSec=15
|
||||
Restart=always
|
||||
|
||||
@@ -34,3 +34,8 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# next-sitemap output
|
||||
/public/sitemap.xml
|
||||
/public/robots.txt
|
||||
/public/sitemap-*.xml
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/** @type {import('next-sitemap').IConfig} */
|
||||
module.exports = {
|
||||
siteUrl: 'https://docs.documenso.com', // Replace with your actual site URL
|
||||
generateRobotsTxt: true, // Generates robots.txt
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3002",
|
||||
"build": "next build",
|
||||
"build": "next build && next-sitemap",
|
||||
"start": "next start -p 3002",
|
||||
"lint:fix": "next lint --fix",
|
||||
"clean": "rimraf .next && rimraf node_modules"
|
||||
@@ -26,6 +26,7 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"next-sitemap": "^4.2.3",
|
||||
"typescript": "5.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"title": "Development & Deployment"
|
||||
},
|
||||
"local-development": "Local Development",
|
||||
"developer-mode": "Developer Mode",
|
||||
"self-hosting": "Self Hosting",
|
||||
"contributing": "Contributing",
|
||||
"-- API & Integration Guides": {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Field Coordinates
|
||||
description: Learn how to get the coordinates of a field in a document.
|
||||
---
|
||||
|
||||
## Field Coordinates
|
||||
|
||||
Field coordinates represent the position of a field in a document. They are returned in the `pageX` and `pageY` properties of the field.
|
||||
|
||||
To enable field coordinates, you can use the `devmode` query parameter.
|
||||
|
||||
```bash
|
||||
https://app.documenso.com/documents/<document-id>/edit?devmode=true
|
||||
```
|
||||
|
||||
You should then see the coordinates on top of each field.
|
||||
|
||||

|
||||
@@ -6,11 +6,12 @@
|
||||
"title": "How To Use"
|
||||
},
|
||||
"get-started": "Get Started",
|
||||
"profile": "User Profile",
|
||||
"signing-documents": "Signing Documents",
|
||||
"profile": "Public Profile",
|
||||
"organisations": "Organisations",
|
||||
"documents": "Documents",
|
||||
"templates": "Templates",
|
||||
"branding": "Branding",
|
||||
"direct-links": "Direct Signing Links",
|
||||
"teams": "Teams",
|
||||
"-- Legal Overview": {
|
||||
"type": "separator",
|
||||
"title": "Legal Overview"
|
||||
@@ -18,4 +19,4 @@
|
||||
"fair-use": "Fair Use Policy",
|
||||
"licenses": "Licenses",
|
||||
"compliance": "Compliance"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Branding Preferences
|
||||
description: Learn how to set the branding preferences for your team account.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
# Branding Preferences
|
||||
|
||||
Branding preferences allow you to set the default settings when emailing documents to your recipients.
|
||||
|
||||
## Preferences
|
||||
|
||||
Branding preferences can be set on either the organisation or team level.
|
||||
|
||||
By default, teams inherit the preferences from the organisation. You can override these preferences on the team level at any time.
|
||||
|
||||
To access the preferences, navigate to either the organisation or teams settings page and click the **Preferences** tab. This page contains both the preferences for documents and branding, the branding section is located at the bottom of the page.
|
||||
|
||||

|
||||
|
||||
On this page, you can:
|
||||
|
||||
- **Upload a Logo** - Upload your team's logo to be displayed instead of the default Documenso logo.
|
||||
- **Set the Brand Website** - Enter the URL of your team's website to be displayed in the email communications sent by the team.
|
||||
- **Add Additional Brand Details** - You can add additional information to display at the bottom of the emails sent by the team. This can include contact information, social media links, and other relevant details.
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sending-documents": "Sending Documents",
|
||||
"document-preferences": "Document Preferences",
|
||||
"document-visibility": "Document Visibility"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Preferences
|
||||
description: Learn how to manage your team's global preferences.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
# Document Preferences
|
||||
|
||||
Document preferences allow you to set the default settings when creating new documents and templates.
|
||||
|
||||
For example, you can set the default language for documents sent by the team, or set the allowed signatures types.
|
||||
|
||||
## Preferences
|
||||
|
||||
Document preferences can be set on either the organisation or team level.
|
||||
|
||||
By default, teams inherit the preferences from the organisation. You can override these preferences on the team level at any time.
|
||||
|
||||
To access the preferences, navigate to either the organisation or teams settings page and click the **Preferences** tab.
|
||||
|
||||

|
||||
|
||||
- **Document Visibility** - Set the default visibility of the documents created by team members. Learn more about [document visibility](/users/documents/document-visibility).
|
||||
- **Default Document Language** - This setting allows you to set the default language for the documents uploaded in the organisation. The default language is used as the default language in the email communications with the document recipients.
|
||||
- **Signature Settings** - Controls what signatures are allowed to be used when signing the documents.
|
||||
- **Sender Details** - Set whether the sender's name should be included in the emails sent by the team. See more below [sender details](/users/documents/document-preferences#sender-details).
|
||||
- **Include the Signing Certificate** - This setting controls whether the signing certificate should be included in the signed documents. If enabled, the signing certificate is included in the signed documents. If disabled, the signing certificate is not included in the signed documents. Regardless of this setting, the signing certificate is always available in the document's audit log page.
|
||||
|
||||
Document visibility, language and signature settings can be overriden on a per document basis.
|
||||
|
||||
### Sender Details
|
||||
|
||||
If the **Sender Details** setting is enabled, the emails sent by the team will include the sender's name. The email will say:
|
||||
|
||||
> "Example User" on behalf of "Example Team" has invited you to sign "document.pdf"
|
||||
|
||||
If the **Sender Details** setting is disabled, the emails sent by the team will not include the sender's name. The email will say:
|
||||
|
||||
> "Example Team" has invited you to sign "document.pdf"
|
||||
@@ -5,19 +5,25 @@ description: Learn how to control the visibility of your team documents.
|
||||
|
||||
import { Callout } from 'nextra/components';
|
||||
|
||||
# Team's Document Visibility
|
||||
# Document Visibility
|
||||
|
||||
The default document visibility option allows you to control who can view and access the documents uploaded to your team account. The document visibility can be set to one of the following options:
|
||||
The default document visibility option allows you to control who can view and access the documents uploaded within a team.
|
||||
|
||||
This value can either be set in the [document preferences](/users/documents/document-preferences), or when you [create the document](/users/documents/send-document)
|
||||
|
||||
## Document Visibility Options
|
||||
|
||||
The document visibility can be set to one of the following options:
|
||||
|
||||
- **Everyone** - The document is visible to all team members.
|
||||
- **Managers and above** - The document is visible to team members with the role of _Manager or above_ and _Admin_.
|
||||
- **Admin only** - The document is only visible to the team's admins.
|
||||
|
||||

|
||||
The default document visibility is set to "_EVERYONE_" by default. You can change this setting by going to the [document preferences page](/users/documents/document-preferences) and selecting a different visibility option.
|
||||
|
||||
The default document visibility is set to "_EVERYONE_" by default. You can change this setting by going to the [team's general preferences page](/users/teams/preferences) and selecting a different visibility option.
|
||||

|
||||
|
||||
Here's how it works:
|
||||
## How it works
|
||||
|
||||
- If a user with the "_Member_" role creates a document and the default document visibility is set to "_Everyone_", the document's visibility is set to "_EVERYONE_".
|
||||
- The user can't change the visibility of the document in the document editor.
|
||||
@@ -115,7 +115,7 @@ All fields can be placed anywhere on the document and resized as needed.
|
||||
|
||||
<Callout type="info">
|
||||
Learn more about the available field types and how to use them on the [Fields
|
||||
page](signing-documents/fields).
|
||||
page](/users/documents/fields).
|
||||
</Callout>
|
||||
|
||||
#### Signature Required
|
||||
@@ -10,7 +10,7 @@ import { Callout, Steps } from 'nextra/components';
|
||||
<Steps>
|
||||
### Pick a Plan
|
||||
|
||||
The first step to start using Documenso is to pick a plan and create an account. At the moment of writing this guide, we have 3 plans available: Free, Individual, and Teams.
|
||||
The first step to start using Documenso is to pick a plan and create an account. At the moment of writing this guide, we have 3 plans available: Free, Individual, Teams and Platform.
|
||||
|
||||
Explore each plan's features and choose the one that best suits your needs. The [pricing page](https://documen.so/pricing) has more information about the plans.
|
||||
|
||||
@@ -28,6 +28,6 @@ You can claim a premium username by upgrading to a paid plan. After upgrading to
|
||||
|
||||
### Optional: Create a Team
|
||||
|
||||
If you are working with others, you can create a team and invite your team members to collaborate on your documents. More information about teams is available in the [Teams section](/users/get-started/teams).
|
||||
If you are working with others, you can create a team and invite your team members to collaborate on your documents. More information about teams is available in the [Teams section](/users/organisations/teams).
|
||||
|
||||
</Steps>
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
---
|
||||
title: Teams
|
||||
description: Learn how to create and manage teams in Documenso.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
# Teams
|
||||
|
||||
Documenso allows you to create teams to collaborate with others on creating and signing documents.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Create a New Team
|
||||
|
||||
Anyone can create a team from their account by clicking on the "+" (plus) button in the "Teams" section from the account dropdown.
|
||||
|
||||

|
||||
|
||||
Each team is a separate entity with its members, documents, and templates. You can create as many teams as you like but remember that each team is billed separately.
|
||||
|
||||
<Callout type="info">You can transfer the ownership of the team at any time.</Callout>
|
||||
|
||||
### Name and URL
|
||||
|
||||
Clicking the "+" button will open a modal where you must pick your team's name and URL. The URL is the team's identifier and will link to the team's page and settings. An example URL would be:
|
||||
|
||||
```bash
|
||||
https://app.documenso.com/t/<your-team-name>
|
||||
```
|
||||
|
||||

|
||||
|
||||
You can select a different name and URL for your team, but we recommend using the same or similar name.
|
||||
|
||||
### Invite Team Members
|
||||
|
||||
After creating the team, you can invite team members by navigating to the "Members" tab in the team settings and clicking the "Invite member" button.
|
||||
|
||||
To access the team settings, click on the team's name in the account dropdown and select the appropriate team. Lastly, click again on the avatar and then "Team Settings".
|
||||
|
||||
Or you can copy this URL:
|
||||
|
||||
```bash
|
||||
https://app.documenso.com/t/<your-team-name>/settings/members
|
||||
```
|
||||
|
||||
Once you click on the "Invite member" button, you will be prompted to enter the email address of the person you want to invite. You can also select the role of the person you are inviting.
|
||||
|
||||

|
||||
|
||||
You can also bulk-invite members by uploading a CSV file with the email addresses and roles of the people you want to invite.
|
||||
|
||||
The table below shows how the CSV file should be structured:
|
||||
|
||||
| Email address | Role |
|
||||
| -------------------------- | ------- |
|
||||
| team-admin@documenso.com | Admin |
|
||||
| team-manager@documenso.com | Manager |
|
||||
| team-member@documenso.com | Member |
|
||||
|
||||
<Callout type="info">
|
||||
The basic team plan includes 5 members. You can invite as many members as you like by upgrading
|
||||
your team's seats on the team's billing page.
|
||||
</Callout>
|
||||
|
||||
#### Roles
|
||||
|
||||
You can assign different permissions to team members based on their roles. The roles available are:
|
||||
|
||||
| Role | Create, Edit, Send Documents | Manage Users | Manage Admins | Settings | Billing | Delete/ Transfer |
|
||||
| :-----: | :--------------------------: | :----------: | :-----------: | :------: | :-----: | :--------------: |
|
||||
| Member | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Manager | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
|
||||
| Admin | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| Owner | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
### Set a Team Email
|
||||
|
||||
You can add a team email to make signing and sending documents easier. Adding a team email allows you to:
|
||||
|
||||
- See a signing request sent to this email (Team Inbox)
|
||||
- See all documents sent on behalf of the team
|
||||
|
||||
### (Optional) Transfer Team Ownership
|
||||
|
||||
You can transfer the team's ownership at any time. To do this, navigate to the "General" tab in the team settings and click the "Transfer team" button.
|
||||
|
||||
Use this URL to get to the team settings:
|
||||
|
||||
```bash
|
||||
https://app.documenso.com/t/<your-team-name>/settings
|
||||
```
|
||||
|
||||
### [Send your First Document](https://app.documenso.com/)
|
||||
|
||||
</Steps>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"index": "Introduction",
|
||||
"members": "Members",
|
||||
"groups": "Groups",
|
||||
"teams": "Teams",
|
||||
"billing": "Billing"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Billing
|
||||
description: Learn how to manage your organisation's billing and subscription.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
### Billing and Subscription Management
|
||||
|
||||
Organisations handle billing centrally, making it easier to manage:
|
||||
|
||||
- **Unified Billing**: One subscription covers all teams in the organisation
|
||||
- **Seat Management**: Add or remove seats across all teams automatically (Teams plan)
|
||||
|
||||
You can change plans, view invoices and manage your subscription from the billing page which is accessible from the organisation settings.
|
||||
|
||||

|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: Preferences
|
||||
description: Learn how to manage your team's global preferences.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
# Organisation Groups
|
||||
|
||||
Organisation groups are a powerful administrative tool that streamlines user management across your entire organisation. Instead of manually assigning individual users to multiple teams, groups allow you to manage access at scale.
|
||||
|
||||
This automated approach ensures consistent permissions while reducing administrative overhead for tasks like onboarding employees or managing contractor access.
|
||||
|
||||
## Understanding groups
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- **Instant Access Management**: New hires get immediate, appropriate access across all relevant teams
|
||||
- **Bulk Operations**: Remove an entire group (like a departing contractor team) and all members lose access simultaneously
|
||||
- **Role Consistency**: Ensure the same role is applied consistently across teams—no more accidentally giving admin access when member access was intended
|
||||
- **Audit Trail**: Easily track which groups have access to which teams
|
||||
|
||||
### Example use case: Legal Compliance Team
|
||||
|
||||
Imagine you have a legal compliance team that needs access to review documents across all departments. Instead of manually adding each legal team member to every departmental team (Sales, Marketing, HR, Operations), you can:
|
||||
|
||||
1. Create a "Legal Compliance" group with the "Member" Organisation Role
|
||||
2. Add legal team members to this group
|
||||
3. Assign the "Legal Compliance" group to the required teams
|
||||
|
||||
Now, when Sarah from Legal joins the company, you can simply add her to the "Legal Compliance" group. Once added, she automatically gains access to all teams the "Legal Compliance" group is assigned to.
|
||||
|
||||
When John from Legal leaves the company, you remove him from the group and his access is instantly revoked across all teams.
|
||||
|
||||
## Getting started with groups
|
||||
|
||||
Navigate to the "Groups" section in your organisation settings to create and manage groups.
|
||||
|
||||
There are two types of roles when using groups:
|
||||
|
||||
- **Organisation Role**: A global organisation role given to all members of the group
|
||||
- **Team Role**: A team role you select when assigning the group to a team
|
||||
|
||||
You should generally have the "Organisation Role" set to "Organisation Member", otherwise these members would by default have access to all teams anyway due to the high organisation role.
|
||||
|
||||
### Creating Custom Groups
|
||||
|
||||
When creating a custom group, you can:
|
||||
|
||||
1. **Name the Group**: Give it a descriptive name that reflects its purpose
|
||||
2. **Set Organisation Role**: Define the default **organisation role** for group members
|
||||
3. **Add Members**: Include organisation members in the group
|
||||
|
||||

|
||||
|
||||
### Manage Custom Groups
|
||||
|
||||
By clicking the "Manage" button on a custom group, you can view all teams it is assigned to and modify the group's settings.
|
||||
|
||||

|
||||
|
||||
### Assigning a group to a team
|
||||
|
||||
To assign a group to a team, you need to navigate to the team settings and click the "Groups" tab.
|
||||
|
||||

|
||||
|
||||
From here, click the "Add groups" button to begin the process of assigning a group to a team. Once you have added the group you can see that the members have been automatically added to the team in the members tab.
|
||||
|
||||
## What's next?
|
||||
|
||||
- [Create Your First Team](/users/organisations/teams)
|
||||
- [Manage Default Settings](/users/documents/document-preferences)
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Organisations
|
||||
description: Learn how to create and manage organisations in Documenso.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
# Organisations
|
||||
|
||||
Organisations allow you to manage multiple teams and users under a single managed entity. This powerful feature enables enterprise-level collaboration and streamlined management across your entire organisation.
|
||||
|
||||
## What are Organisations?
|
||||
|
||||
Organisations are the top-level entity in Documenso's hierarchy structure:
|
||||
|
||||

|
||||
|
||||
Each organisation can contain multiple teams, and each team can have multiple members. This structure provides:
|
||||
|
||||
- **Centralized Management**: Control multiple teams from a single organisational dashboard
|
||||
- **Unified Billing**: Manage billing and subscriptions at the organisation level
|
||||
- **Access Control**: Define roles and groups across the entire organisation
|
||||
- **Group Management**: Create custom groups to organise members and control team access
|
||||
- **Global Settings**: Apply consistent settings across all teams in your organisation
|
||||
|
||||
## Create a new organisation
|
||||
|
||||
You can create multiple organisations, but each organisation will be billed separately.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Creating Organisations
|
||||
|
||||
To create a new organisation, navigate to the organisation section in your account settings and click the "Create Organisation" button.
|
||||
|
||||

|
||||
|
||||
### Select your plan
|
||||
|
||||
Choose from our range of plans for your new organisation. If you want to instead upgrade your current organisation, you can do so by going into your settings billing page and upgrade it there.
|
||||
|
||||
### Name setup
|
||||
|
||||
When creating an organisation, you'll need to provide:
|
||||
|
||||
- **Organisation Name**: The display name for your organisation
|
||||
|
||||
</Steps>
|
||||
|
||||
Once your organisation is established, you can create teams to organise your work and collaborate effectively. Each team operates independently but inherits organisation-level settings and branding.
|
||||
|
||||
## Best Practices for Organisation Management
|
||||
|
||||
1. **Use groups effectively**: Leverage groups to simplify permission management
|
||||
2. **Set default settings**: Configure organisation-wide settings for consistency
|
||||
|
||||
## What's next?
|
||||
|
||||
- [Create Your First Team](/users/organisations/teams)
|
||||
- [Invite Organisation Members](/users/organisations/members)
|
||||
- [Create Organisation Groups](/users/organisations/groups)
|
||||
- [Manage Default Settings](/users/documents/document-preferences)
|
||||
- [Manage Default Branding](/users/branding)
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Members
|
||||
description: Learn how to invite and manage your organisation's members.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
# Organisation Members
|
||||
|
||||
Organisation members are the core users of your organisation. They are the ones who can access the team resources and collaborate with other members.
|
||||
|
||||
## Organisation Roles
|
||||
|
||||
You can assign different permissions to organisation members by using roles. The roles available are:
|
||||
|
||||
| Role | Manage Settings/Teams/Members | Billing | Delete Organisation |
|
||||
| :------------------: | :---------------------------: | :-----: | ------------------- |
|
||||
| Organisation Owner | ✅ | ✅ | ✅ |
|
||||
| Organisation Admin | ✅ | ✅ | ✅ |
|
||||
| Organisation Manager | ✅ | ❌ | ❌ |
|
||||
| Organisation Member | ❌ | ❌ | ❌ |
|
||||
|
||||
<Callout type="info">
|
||||
Organisation admins and managers will automatically have access to all teams as the "Team Admin"
|
||||
role. When creating a team you can also decide whether to automatically allow normal members to
|
||||
access it by default as well.
|
||||
</Callout>
|
||||
|
||||
## Invite Organisation Members
|
||||
|
||||
To invite organisation members, you need to be an organisation owner, admin or manager.
|
||||
|
||||
1. Open the menu switcher top right
|
||||
2. Hover over your new organisation and click the settings icon
|
||||
3. Navigate to the "Members" tab
|
||||
4. Click "Invite Member"
|
||||
|
||||
Once you click on the "Invite member" button, you will be prompted to enter the email address of the person you want to invite. You can also select the role of the person you are inviting.
|
||||
|
||||

|
||||
|
||||
You can also bulk-invite members by uploading a CSV file with the email addresses and roles of the people you want to invite.
|
||||
|
||||
The table below shows how the CSV file should be structured:
|
||||
|
||||
| Email address | Role |
|
||||
| ------------------------- | ------- |
|
||||
| org-admin@documenso.com | Admin |
|
||||
| org-manager@documenso.com | Manager |
|
||||
| org-member@documenso.com | Member |
|
||||
|
||||
<Callout type="info">
|
||||
The basic team plan includes 5 organisation members. Going over the 5 members will charge your
|
||||
organisation according to the seat plan pricing.
|
||||
</Callout>
|
||||
|
||||
## Manage Organisation Members
|
||||
|
||||
On the same page, you can change the organisation member's roles or remove them from the organisation.
|
||||
|
||||
## What's next?
|
||||
|
||||
- [Use groups to organise your members](/users/organisations/groups)
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Teams
|
||||
description: Learn how to create and manage teams in Documenso.
|
||||
---
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Callout, Steps } from 'nextra/components';
|
||||
|
||||
# Teams
|
||||
|
||||
Documenso teams allow you to collaborate with others on creating, sending and receiving documents within your organisation. Teams operate within the organisational structure and inherit settings and branding from their parent organisation.
|
||||
|
||||
## Team Structure
|
||||
|
||||
Teams provide focused collaboration spaces while benefiting from organisation-level management and settings.
|
||||
|
||||
Each team within an organisation has its own:
|
||||
|
||||
- Team members and roles
|
||||
- Documents and templates
|
||||
- Team-specific settings (that can override organisation defaults)
|
||||
- Team email and branding (if enabled)
|
||||
|
||||
## Creating a Team
|
||||
|
||||
Only members with the "Organisation Admin" or "Organisation Manager" role can create teams.
|
||||
|
||||
<Steps>
|
||||
|
||||
### Create Team
|
||||
|
||||
To create a team, navigate to the organisation settings page and click the "Teams" tab. Then you can click the "Create Team" button.
|
||||
|
||||

|
||||
|
||||
### Name and URL
|
||||
|
||||
When creating a team, you'll need to provide:
|
||||
|
||||
- **Team Name**: The display name for your team
|
||||
- **Team URL**: A unique identifier for your team
|
||||
|
||||
The team URL will follow this format:
|
||||
|
||||
```bash
|
||||
https://app.documenso.com/t/<team-url>
|
||||
```
|
||||
|
||||
You can select different names and URLs for your team, but we recommend using the same or similar names for consistency.
|
||||
|
||||

|
||||
|
||||
You can also decide whether to automatically inherit members from the organisation into the team. This means that all members of the organisation will have access to this team.
|
||||
|
||||
Members with the "Organisation Admin" or "Organisation Manager" role will be assigned as "Team Admin" regardless of this setting. This will only affect members with the "Organisation Member" role, who will be added to the team as a "Team Member".
|
||||
|
||||
Disabling this setting will remove all these members automatically. This can always be turned on or off later in the teams member settings page.
|
||||
|
||||
</Steps>
|
||||
|
||||
## Team Members
|
||||
|
||||
After creating the team, you can add organisation members into your team using two methods:
|
||||
|
||||
- Directly adding members to the team
|
||||
- Add members using groups
|
||||
|
||||
### Directly adding members
|
||||
|
||||
1. Navigate to the team settings member page
|
||||
2. Click the "Add Members" button
|
||||
3. Choose which members you want to add
|
||||
4. Assign the team roles for each team member
|
||||
|
||||
If you want to add people outside of the organisation, you will need to invite them to the organisation first.
|
||||
|
||||
See the [organisation members](/users/organisations/members#invite-organisation-members) page for more information.
|
||||
|
||||
### Adding members using groups
|
||||
|
||||
1. Navigate to the teams settings groups page
|
||||
2. Click the "Add groups" button
|
||||
3. Choose which groups you want to add
|
||||
4. Assign the team roles for each group
|
||||
|
||||
See the [organisation groups](/users/organisations/groups) page for more information.
|
||||
|
||||
### Team Member Roles
|
||||
|
||||
You can assign different permissions to team members based on their roles. The roles available are:
|
||||
|
||||
| Role | Manage Documents | Manage Team | Delete Team |
|
||||
| :----------: | :--------------: | :---------: | :---------: |
|
||||
| Team Admin | ✅ | ✅ | ✅ |
|
||||
| Team Manager | ✅ | ✅ | ❌ |
|
||||
| Team Member | ✅ | ❌ | ❌ |
|
||||
|
||||
These roles can be used for document visibility and management as well.
|
||||
|
||||
## Set a Team Email
|
||||
|
||||
You can add a team email which allows you to:
|
||||
|
||||
- See signing requests sent to this email (Team Inbox)
|
||||
- See documents sent from this email
|
||||
- Send documents on behalf of the team
|
||||
- Maintain consistent team branding in communications
|
||||
|
||||
## Team Settings and Branding
|
||||
|
||||
You can override preferences and settings from the Organisation on the Team level. See the following pages for more information:
|
||||
|
||||
- [Document preferences](/users/documents/document-preferences)
|
||||
- [Branding preferences](/users/branding/branding-preferences)
|
||||
|
||||
## What's next?
|
||||
|
||||
- [Send your first document](/users/documents/sending-documents)
|
||||
- [Setup your default document preferences](/users/documents/document-preferences)
|
||||
- [Setup your default branding preferences](/users/branding)
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: User Profile
|
||||
title: Public Profile
|
||||
description: Learn how to set up your public profile on Documenso.
|
||||
---
|
||||
|
||||
@@ -15,7 +15,7 @@ Documenso allows you to create a public profile to share your templates for anyo
|
||||
|
||||
### Navigate to Your Profile Settings
|
||||
|
||||
Click on your profile picture in the top right corner and select "User settings". Then, navigate to the "Public Profile" tab to configure your profile.
|
||||
Click on your profile picture in the top right corner and select "Settings" or "Team Settings". Then, navigate to the "Public Profile" tab to configure your profile.
|
||||
|
||||

|
||||
|
||||
@@ -45,6 +45,8 @@ You can choose to make your profile public or private. Only you can access it if
|
||||
|
||||
To make your profile public, toggle the switch to the right ("Show") at the top right-hand side of the page.
|
||||
|
||||

|
||||
|
||||
### (Optional) Link Templates
|
||||
|
||||
Linking templates to your profile is optional, but it's what makes your profile helpful. Linking templates allow people to sign documents directly from your profile. As a result, we recommend linking at least one template you want to share with others.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"index": "Send Documents",
|
||||
"fields": "Document Fields"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"preferences": "Preferences",
|
||||
"document-visibility": "Document Visibility",
|
||||
"sender-details": "Email Sender Details",
|
||||
"branding-preferences": "Branding Preferences"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: Branding Preferences
|
||||
description: Learn how to set the branding preferences for your team account.
|
||||
---
|
||||
|
||||
# Branding Preferences
|
||||
|
||||
You can set the branding preferences for your team account by going to the **Branding Preferences** tab in the team's settings dashboard.
|
||||
|
||||

|
||||
|
||||
On this page, you can:
|
||||
|
||||
- **Upload a Logo** - Upload your team's logo to be displayed instead of the default Documenso logo.
|
||||
- **Set the Brand Website** - Enter the URL of your team's website to be displayed in the email communications sent by the team.
|
||||
- **Add Additional Brand Details** - You can add additional information to display at the bottom of the emails sent by the team. This can include contact information, social media links, and other relevant details.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Preferences
|
||||
description: Learn how to manage your team's global preferences.
|
||||
---
|
||||
|
||||
# Preferences
|
||||
|
||||
You can manage your team's global preferences by clicking on the **Preferences** tab in the team's settings dashboard.
|
||||
|
||||

|
||||
|
||||
The preferences page allows you to update the following settings:
|
||||
|
||||
- **Document Visibility** - Set the default visibility of the documents created by team members. Learn more about [document visibility](/users/teams/document-visibility).
|
||||
- **Default Document Language** - This setting allows you to set the default language for the documents uploaded in the team account. The default language is used as the default language in the email communications with the document recipients. You can change the language for individual documents when uploading them.
|
||||
- **Sender Details** - Set whether the sender's name should be included in the emails sent by the team. Learn more about [sender details](/users/teams/sender-details).
|
||||
- **Typed Signature** - It controls whether the document recipients can sign the documents with a typed signature or not. If enabled, the recipients can sign the document using either a drawn or a typed signature. If disabled, the recipients can only sign the documents usign a drawn signature. This setting can also be changed for individual documents when uploading them.
|
||||
- **Include the Signing Certificate** - This setting controls whether the signing certificate should be included in the signed documents. If enabled, the signing certificate is included in the signed documents. If disabled, the signing certificate is not included in the signed documents. Regardless of this setting, the signing certificate is always available in the document's audit log page.
|
||||
- **Branding Preferences** - Set the branding preferences and defaults for the team account. Learn more about [branding preferences](/users/teams/branding-preferences).
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: Email Sender Details
|
||||
description: Learn how to update the sender details for your team's email notifications.
|
||||
---
|
||||
|
||||
## Sender Details
|
||||
|
||||
If the **Sender Details** setting is enabled, the emails sent by the team will include the sender's name. The email will say:
|
||||
|
||||
> "Example User" on behalf of "Example Team" has invited you to sign "document.pdf"
|
||||
|
||||
If the **Sender Details** setting is disabled, the emails sent by the team will not include the sender's name. The email will say:
|
||||
|
||||
> "Example Team" has invited you to sign "document.pdf"
|
||||
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 165 KiB |
@@ -83,7 +83,9 @@ export const OrganisationCreateDialog = ({ trigger, ...props }: OrganisationCrea
|
||||
|
||||
const { mutateAsync: createOrganisation } = trpc.organisation.create.useMutation();
|
||||
|
||||
const { data: plansData } = trpc.billing.plans.get.useQuery();
|
||||
const { data: plansData } = trpc.billing.plans.get.useQuery(undefined, {
|
||||
enabled: IS_BILLING_ENABLED(),
|
||||
});
|
||||
|
||||
const onFormSubmit = async ({ name }: TCreateOrganisationFormSchema) => {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { authClient } from '@documenso/auth/client';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
type SessionLogoutAllDialogProps = {
|
||||
onSuccess?: () => Promise<unknown>;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SessionLogoutAllDialog = ({ onSuccess, disabled }: SessionLogoutAllDialogProps) => {
|
||||
const { t } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleSignOutAllSessions = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
await authClient.signOutAllSessions();
|
||||
|
||||
if (onSuccess) {
|
||||
await onSuccess();
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t`Sessions have been revoked`,
|
||||
});
|
||||
|
||||
setIsOpen(false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
toast({
|
||||
title: t`Error`,
|
||||
description: t`Failed to sign out all sessions`,
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={(value) => (isLoading ? undefined : setIsOpen(value))}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="secondary" disabled={disabled}>
|
||||
<Trans>Revoke all sessions</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>Revoke all sessions</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
This will sign you out of all other devices. You will need to sign in again on those
|
||||
devices to continue using your account.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button variant="secondary" disabled={isLoading}>
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
|
||||
<Button loading={isLoading} variant="destructive" onClick={handleSignOutAllSessions}>
|
||||
<Trans>Revoke all sessions</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -142,11 +142,11 @@ export const TeamGroupCreateDialog = ({ ...props }: TeamGroupCreateDialogProps)
|
||||
.with('SELECT', () => (
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>Add members</Trans>
|
||||
<Trans>Add groups</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
<Trans>Select members or groups of members to add to the team.</Trans>
|
||||
<Trans>Select groups of members to add to the team.</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
))
|
||||
|
||||
@@ -171,6 +171,27 @@ export default function SettingsSecurity({ loaderData }: Route.ComponentProps) {
|
||||
</Link>
|
||||
</Button>
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
className="mt-6 flex flex-col justify-between p-6 sm:flex-row sm:items-center"
|
||||
variant="neutral"
|
||||
>
|
||||
<div className="mb-4 mr-4 sm:mb-0">
|
||||
<AlertTitle>
|
||||
<Trans>Active sessions</Trans>
|
||||
</AlertTitle>
|
||||
|
||||
<AlertDescription className="mr-2">
|
||||
<Trans>View and manage all active sessions for your account.</Trans>
|
||||
</AlertDescription>
|
||||
</div>
|
||||
|
||||
<Button asChild variant="outline" className="bg-background">
|
||||
<Link to="/settings/security/sessions">
|
||||
<Trans>Manage sessions</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { DateTime } from 'luxon';
|
||||
import { UAParser } from 'ua-parser-js';
|
||||
|
||||
import { authClient } from '@documenso/auth/client';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { Badge } from '@documenso/ui/primitives/badge';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
|
||||
import { DataTable } from '@documenso/ui/primitives/data-table';
|
||||
import { Skeleton } from '@documenso/ui/primitives/skeleton';
|
||||
import { TableCell } from '@documenso/ui/primitives/table';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { SessionLogoutAllDialog } from '~/components/dialogs/session-logout-all-dialog';
|
||||
import { SettingsHeader } from '~/components/general/settings-header';
|
||||
import { appMetaTags } from '~/utils/meta';
|
||||
|
||||
export function meta() {
|
||||
return appMetaTags('Active Sessions');
|
||||
}
|
||||
|
||||
const parser = new UAParser();
|
||||
|
||||
export default function SettingsSecuritySessions() {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { data, isLoading, isLoadingError, refetch } = useQuery({
|
||||
queryKey: ['active-sessions'],
|
||||
queryFn: async () => await authClient.getSessions(),
|
||||
});
|
||||
|
||||
const { session } = useSession();
|
||||
|
||||
const results = data?.sessions ?? [];
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
header: t`Device`,
|
||||
accessorKey: 'userAgent',
|
||||
cell: ({ row }) => {
|
||||
const userAgent = row.original.userAgent || '';
|
||||
parser.setUA(userAgent);
|
||||
|
||||
const result = parser.getResult();
|
||||
const browser = result.browser.name || t`Unknown`;
|
||||
const os = result.os.name || t`Unknown`;
|
||||
const isCurrentSession = row.original.id === session?.id;
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>
|
||||
{browser} ({os})
|
||||
</span>
|
||||
{isCurrentSession && (
|
||||
<Badge>
|
||||
<Trans>Current</Trans>
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: t`IP Address`,
|
||||
accessorKey: 'ipAddress',
|
||||
cell: ({ row }) => row.original.ipAddress || t`Unknown`,
|
||||
},
|
||||
{
|
||||
header: t`Last Active`,
|
||||
accessorKey: 'updatedAt',
|
||||
cell: ({ row }) => DateTime.fromJSDate(row.original.updatedAt).toRelative(),
|
||||
},
|
||||
{
|
||||
header: t`Created`,
|
||||
accessorKey: 'createdAt',
|
||||
cell: ({ row }) => DateTime.fromJSDate(row.original.createdAt).toRelative(),
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => (
|
||||
<SessionRevokeButton
|
||||
sessionId={row.original.id}
|
||||
isCurrentSession={row.original.id === session?.id}
|
||||
onSuccess={refetch}
|
||||
/>
|
||||
),
|
||||
},
|
||||
] satisfies DataTableColumnDef<(typeof results)[number]>[];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SettingsHeader
|
||||
title={t`Active sessions`}
|
||||
subtitle={t`View and manage all active sessions for your account.`}
|
||||
>
|
||||
<SessionLogoutAllDialog onSuccess={refetch} disabled={results.length === 1 || isLoading} />
|
||||
</SettingsHeader>
|
||||
|
||||
<div className="mt-4">
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={results}
|
||||
hasFilters={false}
|
||||
error={{
|
||||
enable: isLoadingError,
|
||||
}}
|
||||
skeleton={{
|
||||
enable: isLoading,
|
||||
rows: 3,
|
||||
component: (
|
||||
<>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4 w-40 rounded-full" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4 w-24 rounded-full" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4 w-24 rounded-full" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-4 w-24 rounded-full" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="h-8 w-16 rounded" />
|
||||
</TableCell>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type SessionRevokeButtonProps = {
|
||||
sessionId: string;
|
||||
isCurrentSession: boolean;
|
||||
onSuccess: () => Promise<unknown>;
|
||||
};
|
||||
|
||||
const SessionRevokeButton = ({
|
||||
sessionId,
|
||||
isCurrentSession,
|
||||
onSuccess,
|
||||
}: SessionRevokeButtonProps) => {
|
||||
const { toast } = useToast();
|
||||
const { t } = useLingui();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleRevoke = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await authClient.signOutSession({
|
||||
sessionId,
|
||||
redirectPath: isCurrentSession ? '/signin' : undefined,
|
||||
});
|
||||
|
||||
if (!isCurrentSession) {
|
||||
await onSuccess();
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t`Session revoked`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
toast({
|
||||
title: t`Error`,
|
||||
description: t`Failed to revoke session`,
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button variant="destructive" size="sm" onClick={handleRevoke} loading={isLoading}>
|
||||
<Trans>Revoke</Trans>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Link, redirect } from 'react-router';
|
||||
|
||||
import { extractCookieFromHeaders } from '@documenso/auth/server/lib/utils/cookies';
|
||||
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
import {
|
||||
IS_GOOGLE_SSO_ENABLED,
|
||||
@@ -20,13 +21,18 @@ export function meta() {
|
||||
|
||||
export async function loader({ request }: Route.LoaderArgs) {
|
||||
const { isAuthenticated } = await getOptionalSession(request);
|
||||
const redirectToCookie = extractCookieFromHeaders('redirectTo', request.headers);
|
||||
const redirectToAfterLogin = redirectToCookie ? decodeURIComponent(redirectToCookie) : '';
|
||||
|
||||
// SSR env variables.
|
||||
const isGoogleSSOEnabled = IS_GOOGLE_SSO_ENABLED;
|
||||
const isOIDCSSOEnabled = IS_OIDC_SSO_ENABLED;
|
||||
const oidcProviderLabel = OIDC_PROVIDER_LABEL;
|
||||
|
||||
if (isAuthenticated) {
|
||||
if (redirectToAfterLogin) {
|
||||
throw redirect(redirectToAfterLogin);
|
||||
}
|
||||
|
||||
throw redirect('/');
|
||||
}
|
||||
|
||||
@@ -34,11 +40,13 @@ export async function loader({ request }: Route.LoaderArgs) {
|
||||
isGoogleSSOEnabled,
|
||||
isOIDCSSOEnabled,
|
||||
oidcProviderLabel,
|
||||
redirectToAfterLogin,
|
||||
};
|
||||
}
|
||||
|
||||
export default function SignIn({ loaderData }: Route.ComponentProps) {
|
||||
const { isGoogleSSOEnabled, isOIDCSSOEnabled, oidcProviderLabel } = loaderData;
|
||||
const { isGoogleSSOEnabled, isOIDCSSOEnabled, oidcProviderLabel, redirectToAfterLogin } =
|
||||
loaderData;
|
||||
|
||||
return (
|
||||
<div className="w-screen max-w-lg px-4">
|
||||
@@ -56,6 +64,7 @@ export default function SignIn({ loaderData }: Route.ComponentProps) {
|
||||
isGoogleSSOEnabled={isGoogleSSOEnabled}
|
||||
isOIDCSSOEnabled={isOIDCSSOEnabled}
|
||||
oidcProviderLabel={oidcProviderLabel}
|
||||
returnTo={redirectToAfterLogin ?? undefined}
|
||||
/>
|
||||
|
||||
{env('NEXT_PUBLIC_DISABLE_SIGNUP') !== 'true' && (
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
|
||||
import type { Route } from './+types/team.verify.transfer.$token';
|
||||
|
||||
export default function VerifyTeamTransferPage({ loaderData }: Route.ComponentProps) {
|
||||
return (
|
||||
<div className="w-screen max-w-lg px-4">
|
||||
<div className="w-full">
|
||||
<h1 className="text-4xl font-semibold">
|
||||
<Trans>Invalid link</Trans>
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground mb-4 mt-2 text-sm">
|
||||
<Trans>This link is invalid or has expired.</Trans>
|
||||
</p>
|
||||
|
||||
<Button asChild>
|
||||
<Link to="/">
|
||||
<Trans>Return</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -100,5 +100,5 @@
|
||||
"vite-plugin-babel-macros": "^1.0.6",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"version": "1.12.0-rc.3"
|
||||
"version": "1.12.0-rc.4"
|
||||
}
|
||||
|
||||
@@ -20,12 +20,33 @@ export const appMiddleware = async (c: Context, next: Next) => {
|
||||
const { req } = c;
|
||||
const { path } = req;
|
||||
|
||||
// PRE-HANDLER CODE: Place code here to execute BEFORE the route handler runs.
|
||||
const redirectTo = req.query('redirectTo');
|
||||
|
||||
if (redirectTo) {
|
||||
if (redirectTo.startsWith('/') && !redirectTo.startsWith('//') && !redirectTo.includes('..')) {
|
||||
debug.log('Setting redirectTo cookie to:', redirectTo);
|
||||
setCookie(c, 'redirectTo', redirectTo, {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
sameSite: 'Lax',
|
||||
maxAge: 150,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
});
|
||||
|
||||
debug.log('Redirecting to (from param):', redirectTo);
|
||||
return c.redirect(redirectTo, 307);
|
||||
} else {
|
||||
debug.log('Invalid redirectTo parameter encountered:', redirectTo);
|
||||
}
|
||||
}
|
||||
|
||||
// Paths to ignore.
|
||||
if (nonPagePathRegex.test(path)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// PRE-HANDLER CODE: Place code here to execute BEFORE the route handler runs.
|
||||
// Handle team-based routing redirects (documents/templates to team URLs)
|
||||
const redirectPath = await handleRedirects(c);
|
||||
|
||||
if (redirectPath) {
|
||||
@@ -34,7 +55,6 @@ export const appMiddleware = async (c: Context, next: Next) => {
|
||||
|
||||
return c.redirect(redirectPath);
|
||||
}
|
||||
|
||||
await next();
|
||||
|
||||
// POST-HANDLER CODE: Place code here to execute AFTER the route handler completes.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@documenso/root",
|
||||
"version": "1.12.0-rc.3",
|
||||
"version": "1.12.0-rc.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@documenso/root",
|
||||
"version": "1.12.0-rc.3",
|
||||
"version": "1.12.0-rc.4",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
@@ -69,6 +69,7 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"next-sitemap": "^4.2.3",
|
||||
"typescript": "5.6.2"
|
||||
}
|
||||
},
|
||||
@@ -88,7 +89,7 @@
|
||||
},
|
||||
"apps/remix": {
|
||||
"name": "@documenso/remix",
|
||||
"version": "1.12.0-rc.3",
|
||||
"version": "1.12.0-rc.4",
|
||||
"dependencies": {
|
||||
"@documenso/api": "*",
|
||||
"@documenso/assets": "*",
|
||||
@@ -2122,6 +2123,13 @@
|
||||
"node": ">=v14"
|
||||
}
|
||||
},
|
||||
"node_modules/@corex/deepmerge": {
|
||||
"version": "4.0.43",
|
||||
"resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-4.0.43.tgz",
|
||||
"integrity": "sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
@@ -25341,6 +25349,41 @@
|
||||
"react-dom": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/next-sitemap": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.2.3.tgz",
|
||||
"integrity": "sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/iamvishnusankar/next-sitemap.git"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@corex/deepmerge": "^4.0.43",
|
||||
"@next/env": "^13.4.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
"minimist": "^1.2.8"
|
||||
},
|
||||
"bin": {
|
||||
"next-sitemap": "bin/next-sitemap.mjs",
|
||||
"next-sitemap-cjs": "bin/next-sitemap.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/next-sitemap/node_modules/@next/env": {
|
||||
"version": "13.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.11.tgz",
|
||||
"integrity": "sha512-fbb2C7HChgM7CemdCY+y3N1n8pcTKdqtQLbC7/EQtPdLvlMUT9JX/dBYl8MMZAtYG4uVMyPFHXckb68q/NRwqg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/next-themes": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "1.12.0-rc.3",
|
||||
"version": "1.12.0-rc.4",
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev --filter=@documenso/remix",
|
||||
|
||||
@@ -176,8 +176,16 @@ export const ZCreateDocumentMutationSchema = z.object({
|
||||
.default({}),
|
||||
authOptions: z
|
||||
.object({
|
||||
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional().default([]),
|
||||
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional().default([]),
|
||||
globalAccessAuth: z
|
||||
.union([ZDocumentAccessAuthTypesSchema, z.array(ZDocumentAccessAuthTypesSchema)])
|
||||
.transform((val) => (Array.isArray(val) ? val : [val]))
|
||||
.optional()
|
||||
.default([]),
|
||||
globalActionAuth: z
|
||||
.union([ZDocumentActionAuthTypesSchema, z.array(ZDocumentActionAuthTypesSchema)])
|
||||
.transform((val) => (Array.isArray(val) ? val : [val]))
|
||||
.optional()
|
||||
.default([]),
|
||||
})
|
||||
.optional()
|
||||
.openapi({
|
||||
@@ -236,8 +244,16 @@ export const ZCreateDocumentFromTemplateMutationSchema = z.object({
|
||||
.optional(),
|
||||
authOptions: z
|
||||
.object({
|
||||
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional().default([]),
|
||||
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional().default([]),
|
||||
globalAccessAuth: z
|
||||
.union([ZDocumentAccessAuthTypesSchema, z.array(ZDocumentAccessAuthTypesSchema)])
|
||||
.transform((val) => (Array.isArray(val) ? val : [val]))
|
||||
.optional()
|
||||
.default([]),
|
||||
globalActionAuth: z
|
||||
.union([ZDocumentActionAuthTypesSchema, z.array(ZDocumentActionAuthTypesSchema)])
|
||||
.transform((val) => (Array.isArray(val) ? val : [val]))
|
||||
.optional()
|
||||
.default([]),
|
||||
})
|
||||
.optional(),
|
||||
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
||||
@@ -309,8 +325,16 @@ export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
|
||||
.optional(),
|
||||
authOptions: z
|
||||
.object({
|
||||
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional().default([]),
|
||||
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional().default([]),
|
||||
globalAccessAuth: z
|
||||
.union([ZDocumentAccessAuthTypesSchema, z.array(ZDocumentAccessAuthTypesSchema)])
|
||||
.transform((val) => (Array.isArray(val) ? val : [val]))
|
||||
.optional()
|
||||
.default([]),
|
||||
globalActionAuth: z
|
||||
.union([ZDocumentActionAuthTypesSchema, z.array(ZDocumentActionAuthTypesSchema)])
|
||||
.transform((val) => (Array.isArray(val) ? val : [val]))
|
||||
.optional()
|
||||
.default([]),
|
||||
})
|
||||
.optional(),
|
||||
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
||||
@@ -349,7 +373,11 @@ export const ZCreateRecipientMutationSchema = z.object({
|
||||
signingOrder: z.number().nullish(),
|
||||
authOptions: z
|
||||
.object({
|
||||
actionAuth: z.array(ZRecipientActionAuthTypesSchema).optional().default([]),
|
||||
actionAuth: z
|
||||
.union([ZRecipientActionAuthTypesSchema, z.array(ZRecipientActionAuthTypesSchema)])
|
||||
.transform((val) => (Array.isArray(val) ? val : [val]))
|
||||
.optional()
|
||||
.default([]),
|
||||
})
|
||||
.optional()
|
||||
.openapi({
|
||||
|
||||
@@ -197,7 +197,7 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
id: numberField.id,
|
||||
type: 'number',
|
||||
label: 'Prefilled Number',
|
||||
value: '42',
|
||||
value: '98765',
|
||||
},
|
||||
{
|
||||
id: radioField.id,
|
||||
@@ -256,7 +256,7 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
expect(documentNumberField?.fieldMeta).toMatchObject({
|
||||
type: 'number',
|
||||
label: 'Prefilled Number',
|
||||
value: '42',
|
||||
value: '98765',
|
||||
});
|
||||
|
||||
const documentRadioField = document?.fields.find(
|
||||
@@ -329,7 +329,7 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
await expect(page.getByText('This is prefilled')).toBeVisible();
|
||||
|
||||
// Number field
|
||||
await expect(page.getByText('42')).toBeVisible();
|
||||
await expect(page.getByText('98765', { exact: true })).toBeVisible();
|
||||
|
||||
// Radio field
|
||||
await expect(page.getByText('Option A')).toBeVisible();
|
||||
@@ -383,7 +383,7 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
|
||||
// 5. Add fields to the template
|
||||
// Add TEXT field
|
||||
const textField = await prisma.field.create({
|
||||
await prisma.field.create({
|
||||
data: {
|
||||
templateId: template.id,
|
||||
recipientId: recipient.id,
|
||||
@@ -403,7 +403,7 @@ test.describe('Template Field Prefill API v1', () => {
|
||||
});
|
||||
|
||||
// Add NUMBER field
|
||||
const numberField = await prisma.field.create({
|
||||
await prisma.field.create({
|
||||
data: {
|
||||
templateId: template.id,
|
||||
recipientId: recipient.id,
|
||||
|
||||
@@ -194,7 +194,7 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
id: numberField.id,
|
||||
type: 'number',
|
||||
label: 'Prefilled Number',
|
||||
value: '42',
|
||||
value: '98765',
|
||||
},
|
||||
{
|
||||
id: radioField.id,
|
||||
@@ -253,7 +253,7 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
expect(documentNumberField?.fieldMeta).toMatchObject({
|
||||
type: 'number',
|
||||
label: 'Prefilled Number',
|
||||
value: '42',
|
||||
value: '98765',
|
||||
});
|
||||
|
||||
const documentRadioField = document?.fields.find(
|
||||
@@ -326,7 +326,7 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
await expect(page.getByText('This is prefilled')).toBeVisible();
|
||||
|
||||
// Number field
|
||||
await expect(page.getByText('42')).toBeVisible();
|
||||
await expect(page.getByText('98765', { exact: true })).toBeVisible();
|
||||
|
||||
// Radio field
|
||||
await expect(page.getByText('Option A')).toBeVisible();
|
||||
@@ -380,7 +380,7 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
|
||||
// 5. Add fields to the template
|
||||
// Add TEXT field
|
||||
const textField = await prisma.field.create({
|
||||
await prisma.field.create({
|
||||
data: {
|
||||
templateId: template.id,
|
||||
recipientId: recipient.id,
|
||||
@@ -400,7 +400,7 @@ test.describe('Template Field Prefill API v2', () => {
|
||||
});
|
||||
|
||||
// Add NUMBER field
|
||||
const numberField = await prisma.field.create({
|
||||
await prisma.field.create({
|
||||
data: {
|
||||
templateId: template.id,
|
||||
recipientId: recipient.id,
|
||||
|
||||
@@ -633,7 +633,7 @@ test('[DOCUMENT_FLOW]: should be able to create and sign a document with 3 recip
|
||||
}
|
||||
|
||||
// Wait for the document to be signed.
|
||||
await page.waitForTimeout(5000);
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
const finalDocument = await prisma.document.findFirst({
|
||||
where: { id: createdDocument?.id },
|
||||
|
||||
@@ -283,10 +283,10 @@ test('[DOCUMENTS]: deleting documents as a recipient should only hide it for the
|
||||
}).toPass();
|
||||
|
||||
// Delete document.
|
||||
await page.getByRole('menuitem', { name: 'Hide' }).click();
|
||||
await page.getByRole('button', { name: 'Hide' }).click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByRole('menuitem', { name: 'Hide' }).waitFor({ state: 'visible' });
|
||||
await page.getByRole('menuitem', { name: 'Hide' }).click({ force: true });
|
||||
await page.getByRole('button', { name: 'Hide' }).click({ force: true });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await expect(async () => {
|
||||
await page
|
||||
@@ -300,8 +300,10 @@ test('[DOCUMENTS]: deleting documents as a recipient should only hide it for the
|
||||
}).toPass();
|
||||
|
||||
// Delete document.
|
||||
await page.getByRole('menuitem', { name: 'Hide' }).click();
|
||||
await page.getByRole('button', { name: 'Hide' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Hide' }).waitFor({ state: 'visible' });
|
||||
await page.getByRole('menuitem', { name: 'Hide' }).click({ force: true });
|
||||
await page.getByRole('button', { name: 'Hide' }).click({ force: true });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check document counts.
|
||||
await expect(page.getByRole('row', { name: /Document 1 - Completed/ })).not.toBeVisible();
|
||||
|
||||
@@ -49,9 +49,11 @@ test.describe('Signing Certificate Tests', () => {
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Complete' }).click();
|
||||
await page.getByRole('button', { name: 'Sign' }).click();
|
||||
await page.getByRole('button', { name: 'Sign' }).click({ force: true });
|
||||
await page.waitForURL(`/sign/${recipient.token}/complete`);
|
||||
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
await expect(async () => {
|
||||
const { status } = await getDocumentByToken({
|
||||
token: recipient.token,
|
||||
|
||||
@@ -3,8 +3,7 @@ import path from 'node:path';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentVisibility, FolderType, TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { seedTeamDocuments } from '@documenso/prisma/seed/documents';
|
||||
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
|
||||
import { seedBlankDocument, seedTeamDocuments } from '@documenso/prisma/seed/documents';
|
||||
import { seedBlankFolder } from '@documenso/prisma/seed/folders';
|
||||
import { seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
@@ -1328,7 +1327,7 @@ test('[TEAMS]: team admin can move manager document to admin folder', async ({ p
|
||||
|
||||
const managerDocRow = page.getByRole('row', { name: /\[TEST\] Manager Document/ });
|
||||
await managerDocRow.getByTestId('document-table-action-btn').click();
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click({ force: true });
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Admin Folder' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Admin Folder' }).click();
|
||||
@@ -1379,7 +1378,7 @@ test('[TEAMS]: team admin can move manager document to manager folder', async ({
|
||||
|
||||
const managerDocRow = page.getByRole('row', { name: /\[TEST\] Manager Document/ });
|
||||
await managerDocRow.getByTestId('document-table-action-btn').click();
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click({ force: true });
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Manager Folder' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Manager Folder' }).click();
|
||||
@@ -1430,7 +1429,7 @@ test('[TEAMS]: team admin can move manager document to everyone folder', async (
|
||||
|
||||
const managerDocRow = page.getByRole('row', { name: /\[TEST\] Manager Document/ });
|
||||
await managerDocRow.getByTestId('document-table-action-btn').click();
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Move to Folder' }).click({ force: true });
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Everyone Folder' })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Everyone Folder' }).click();
|
||||
|
||||
@@ -31,7 +31,7 @@ test('[ORGANISATIONS]: create and delete organisation', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
await page.waitForURL(`/settings/organisations`);
|
||||
await expect(page.getByText('No results found')).toBeVisible();
|
||||
await expectTextToBeVisible(page, 'No results found');
|
||||
await page.getByRole('button', { name: 'Create organisation' }).click();
|
||||
|
||||
await page.getByLabel('Organisation Name*').fill('test');
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { DocumentStatus, DocumentVisibility, TeamMemberRole } from '@prisma/client';
|
||||
|
||||
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
|
||||
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
|
||||
import {
|
||||
seedBlankDocument,
|
||||
seedDocuments,
|
||||
seedTeamDocuments,
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedTeam, seedTeamEmail, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
@@ -314,9 +317,9 @@ test('[TEAMS]: delete pending team document', async ({ page }) => {
|
||||
await expect(page.getByRole('menuitem', { name: 'Delete' })).toBeVisible();
|
||||
}).toPass();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click({ force: true });
|
||||
await page.getByPlaceholder("Type 'delete' to confirm").fill('delete');
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await page.getByRole('button', { name: 'Delete' }).click({ force: true });
|
||||
|
||||
await checkDocumentTabCount(page, 'Pending', 1);
|
||||
|
||||
@@ -359,9 +362,9 @@ test('[TEAMS]: delete completed team document', async ({ page }) => {
|
||||
await expect(page.getByRole('menuitem', { name: 'Delete' })).toBeVisible();
|
||||
}).toPass();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click({ force: true });
|
||||
await page.getByPlaceholder("Type 'delete' to confirm").fill('delete');
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
await page.getByRole('button', { name: 'Delete' }).click({ force: true });
|
||||
|
||||
await checkDocumentTabCount(page, 'Completed', 0);
|
||||
|
||||
|
||||
@@ -80,18 +80,27 @@ test('[TEAMS]: check signature modes can be disabled', async ({ page }) => {
|
||||
|
||||
await page.getByRole('button', { name: 'Update' }).first().click();
|
||||
|
||||
// Wait for the update to complete
|
||||
const toast = page.locator('li[role="status"][data-state="open"]').first();
|
||||
await expect(toast).toBeVisible();
|
||||
await expect(toast.getByText('Document preferences updated', { exact: true })).toBeVisible();
|
||||
|
||||
const document = await seedTeamDocumentWithMeta(team);
|
||||
|
||||
// Go to document and check that the signatured tabs are correct.
|
||||
// Go to document and check that the signature tabs are correct.
|
||||
await page.goto(`/sign/${document.recipients[0].token}`);
|
||||
await page.getByTestId('signature-pad-dialog-button').click();
|
||||
|
||||
// Wait for signature dialog to fully load
|
||||
await page.waitForSelector('[role="dialog"]');
|
||||
|
||||
// Check the tab values
|
||||
for (const tab of allTabs) {
|
||||
if (tabs.includes(tab)) {
|
||||
await expect(page.getByRole('tab', { name: tab })).toBeVisible();
|
||||
} else {
|
||||
await expect(page.getByRole('tab', { name: tab })).not.toBeVisible();
|
||||
// await expect(page.getByRole('tab', { name: tab })).not.toBeVisible();
|
||||
await expect(page.getByRole('tab', { name: tab })).toHaveCount(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,10 +297,22 @@ test('[TEMPLATE]: should create a document from a template with custom document'
|
||||
},
|
||||
});
|
||||
|
||||
const expectedDocumentDataType =
|
||||
process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT === 's3'
|
||||
? DocumentDataType.S3_PATH
|
||||
: DocumentDataType.BYTES_64;
|
||||
|
||||
expect(document.title).toEqual('TEMPLATE_WITH_CUSTOM_DOC');
|
||||
expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64);
|
||||
expect(document.documentData.data).toEqual(pdfContent);
|
||||
expect(document.documentData.initialData).toEqual(pdfContent);
|
||||
expect(document.documentData.type).toEqual(expectedDocumentDataType);
|
||||
|
||||
if (expectedDocumentDataType === DocumentDataType.BYTES_64) {
|
||||
expect(document.documentData.data).toEqual(pdfContent);
|
||||
expect(document.documentData.initialData).toEqual(pdfContent);
|
||||
} else {
|
||||
// For S3, we expect the data/initialData to be the S3 path (non-empty string)
|
||||
expect(document.documentData.data).toBeTruthy();
|
||||
expect(document.documentData.initialData).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -378,11 +390,23 @@ test('[TEMPLATE]: should create a team document from a template with custom docu
|
||||
},
|
||||
});
|
||||
|
||||
const expectedDocumentDataType =
|
||||
process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT === 's3'
|
||||
? DocumentDataType.S3_PATH
|
||||
: DocumentDataType.BYTES_64;
|
||||
|
||||
expect(document.teamId).toEqual(team.id);
|
||||
expect(document.title).toEqual('TEAM_TEMPLATE_WITH_CUSTOM_DOC');
|
||||
expect(document.documentData.type).toEqual(DocumentDataType.BYTES_64);
|
||||
expect(document.documentData.data).toEqual(pdfContent);
|
||||
expect(document.documentData.initialData).toEqual(pdfContent);
|
||||
expect(document.documentData.type).toEqual(expectedDocumentDataType);
|
||||
|
||||
if (expectedDocumentDataType === DocumentDataType.BYTES_64) {
|
||||
expect(document.documentData.data).toEqual(pdfContent);
|
||||
expect(document.documentData.initialData).toEqual(pdfContent);
|
||||
} else {
|
||||
// For S3, we expect the data/initialData to be the S3 path (non-empty string)
|
||||
expect(document.documentData.data).toBeTruthy();
|
||||
expect(document.documentData.initialData).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -178,4 +178,7 @@ test('[DIRECT_TEMPLATES]: use direct template link with 1 recipient', async ({ p
|
||||
await page.getByRole('button', { name: 'Sign' }).click();
|
||||
await page.waitForURL(/\/sign/);
|
||||
await expect(page.getByRole('heading', { name: 'Document Signed' })).toBeVisible();
|
||||
|
||||
// Add a longer waiting period to ensure document status is updated
|
||||
await page.waitForTimeout(3000);
|
||||
});
|
||||
|
||||
@@ -27,6 +27,9 @@ test('[USER] can sign up with email and password', async ({ page }: { page: Page
|
||||
|
||||
await page.waitForURL('/unverified-account');
|
||||
|
||||
// Wait to ensure token is created in the database
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const { token } = await extractUserVerificationToken(email);
|
||||
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { type Page, expect, test } from '@playwright/test';
|
||||
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
import { expectTextToBeVisible } from '../fixtures/generic';
|
||||
|
||||
test('[USER] revoke sessions', async ({ page }: { page: Page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
password: 'password',
|
||||
redirectPath: '/settings/security/sessions',
|
||||
});
|
||||
|
||||
// Expect 2 rows length (header + 1)
|
||||
await expect(page.getByRole('row')).toHaveCount(2);
|
||||
|
||||
// Clear cookies
|
||||
await page.context().clearCookies();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
password: 'password',
|
||||
redirectPath: '/settings/security/sessions',
|
||||
});
|
||||
|
||||
await page.context().clearCookies();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
password: 'password',
|
||||
redirectPath: '/settings/security/sessions',
|
||||
});
|
||||
|
||||
// Expect 4 (3 sessions + 1 header) rows length
|
||||
await expect(page.getByRole('row')).toHaveCount(4);
|
||||
|
||||
// Revoke all sessions
|
||||
await page.getByRole('button', { name: 'Revoke all sessions' }).click();
|
||||
await page.getByRole('button', { name: 'Revoke all sessions' }).click();
|
||||
|
||||
await expectTextToBeVisible(page, 'Sessions have been revoked');
|
||||
|
||||
// Expect (1 sessions + 1 header) rows length
|
||||
await expect(page.getByRole('row')).toHaveCount(2);
|
||||
|
||||
await page.context().clearCookies();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
password: 'password',
|
||||
redirectPath: '/settings/security/sessions',
|
||||
});
|
||||
|
||||
// Find table row which does not have text 'Current' and click the button called Revoke within the row.
|
||||
await page
|
||||
.getByRole('row')
|
||||
.filter({ hasNotText: 'Current' })
|
||||
.nth(1)
|
||||
.getByRole('button', { name: 'Revoke' })
|
||||
.click();
|
||||
await expectTextToBeVisible(page, 'Session revoked');
|
||||
|
||||
// Expect (1 sessions + 1 header) rows length
|
||||
await expect(page.getByRole('row')).toHaveCount(2);
|
||||
|
||||
// Revoke own session.
|
||||
await page
|
||||
.getByRole('row')
|
||||
.filter({ hasText: 'Current' })
|
||||
.first()
|
||||
.getByRole('button', { name: 'Revoke' })
|
||||
.click();
|
||||
|
||||
await expect(page).toHaveURL('/signin');
|
||||
});
|
||||
@@ -17,9 +17,14 @@ test('[USER] can reset password via forgot password', async ({ page }: { page: P
|
||||
|
||||
await page.goto('http://localhost:3000/signin');
|
||||
await page.getByRole('link', { name: 'Forgot your password?' }).click();
|
||||
|
||||
await page.getByRole('textbox', { name: 'Email' }).click();
|
||||
await page.getByRole('textbox', { name: 'Email' }).fill(user.email);
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Reset Password' })).toBeEnabled();
|
||||
await page.getByRole('button', { name: 'Reset Password' }).click();
|
||||
await expect(page.locator('body')).toContainText('Reset email sent');
|
||||
|
||||
await expect(page.locator('body')).toContainText('Reset email sent', { timeout: 10000 });
|
||||
|
||||
const foundToken = await prisma.passwordResetToken.findFirstOrThrow({
|
||||
where: {
|
||||
@@ -33,16 +38,26 @@ test('[USER] can reset password via forgot password', async ({ page }: { page: P
|
||||
await page.goto(`http://localhost:3000/reset-password/${foundToken.token}`);
|
||||
|
||||
// Assert that password cannot be same as old password.
|
||||
await page.getByRole('textbox', { name: 'Password', exact: true }).fill(oldPassword);
|
||||
await page.getByRole('textbox', { name: 'Repeat Password' }).fill(oldPassword);
|
||||
await page.getByLabel('Password', { exact: true }).fill(oldPassword);
|
||||
await page.getByLabel('Repeat Password').fill(oldPassword);
|
||||
|
||||
// Ensure both fields are filled before clicking
|
||||
await expect(page.getByLabel('Password', { exact: true })).toHaveValue(oldPassword);
|
||||
await expect(page.getByLabel('Repeat Password')).toHaveValue(oldPassword);
|
||||
|
||||
await page.getByRole('button', { name: 'Reset Password' }).click();
|
||||
await expect(page.locator('body')).toContainText(
|
||||
'Your new password cannot be the same as your old password.',
|
||||
);
|
||||
|
||||
// Assert password reset.
|
||||
await page.getByRole('textbox', { name: 'Password', exact: true }).fill(newPassword);
|
||||
await page.getByRole('textbox', { name: 'Repeat Password' }).fill(newPassword);
|
||||
await page.getByLabel('Password', { exact: true }).fill(newPassword);
|
||||
await page.getByLabel('Repeat Password').fill(newPassword);
|
||||
|
||||
// Ensure both fields are filled before clicking
|
||||
await expect(page.getByLabel('Password', { exact: true })).toHaveValue(newPassword);
|
||||
await expect(page.getByLabel('Repeat Password')).toHaveValue(newPassword);
|
||||
|
||||
await page.getByRole('button', { name: 'Reset Password' }).click();
|
||||
await expect(page.locator('body')).toContainText('Your password has been updated successfully.');
|
||||
|
||||
@@ -73,9 +88,9 @@ test('[USER] can reset password via user settings', async ({ page }: { page: Pag
|
||||
redirectPath: '/settings/security',
|
||||
});
|
||||
|
||||
await page.getByRole('textbox', { name: 'Current password' }).fill(oldPassword);
|
||||
await page.getByRole('textbox', { name: 'New password' }).fill(newPassword);
|
||||
await page.getByRole('textbox', { name: 'Repeat password' }).fill(newPassword);
|
||||
await page.getByLabel('Current password').fill(oldPassword);
|
||||
await page.getByLabel('New password').fill(newPassword);
|
||||
await page.getByLabel('Repeat password').fill(newPassword);
|
||||
await page.getByRole('button', { name: 'Update password' }).click();
|
||||
await expect(page.locator('body')).toContainText('Password updated');
|
||||
|
||||
|
||||
@@ -24,19 +24,23 @@ export default defineConfig({
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 4 : 1,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
reporter: [['html'], ['list']],
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: 'http://localhost:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
trace: 'on',
|
||||
|
||||
video: 'retain-on-failure',
|
||||
|
||||
/* Add explicit timeouts for actions */
|
||||
actionTimeout: 15_000,
|
||||
navigationTimeout: 30_000,
|
||||
},
|
||||
|
||||
timeout: 30_000,
|
||||
timeout: 60_000,
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AppError } from '@documenso/lib/errors/app-error';
|
||||
|
||||
import type { AuthAppType } from '../server';
|
||||
import type { SessionValidationResult } from '../server/lib/session/session';
|
||||
import type { ActiveSession } from '../server/lib/utils/get-session';
|
||||
import { handleSignInRedirect } from '../server/lib/utils/redirect';
|
||||
import type {
|
||||
TDisableTwoFactorRequestSchema,
|
||||
@@ -47,6 +48,26 @@ export class AuthClient {
|
||||
window.location.href = redirectPath ?? this.signOutredirectPath;
|
||||
}
|
||||
|
||||
public async signOutAllSessions() {
|
||||
await this.client['signout-all'].$post();
|
||||
}
|
||||
|
||||
public async signOutSession({
|
||||
sessionId,
|
||||
redirectPath,
|
||||
}: {
|
||||
sessionId: string;
|
||||
redirectPath?: string;
|
||||
}) {
|
||||
await this.client['signout-session'].$post({
|
||||
json: { sessionId },
|
||||
});
|
||||
|
||||
if (redirectPath) {
|
||||
window.location.href = redirectPath;
|
||||
}
|
||||
}
|
||||
|
||||
public async getSession() {
|
||||
const response = await this.client['session-json'].$get();
|
||||
|
||||
@@ -57,6 +78,16 @@ export class AuthClient {
|
||||
return superjson.deserialize<SessionValidationResult>(result);
|
||||
}
|
||||
|
||||
public async getSessions() {
|
||||
const response = await this.client['sessions'].$get();
|
||||
|
||||
await this.handleError(response);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return superjson.deserialize<{ sessions: ActiveSession[] }>(result);
|
||||
}
|
||||
|
||||
private async handleError<T>(response: ClientResponse<T>): Promise<void> {
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from './server/lib/errors/errors';
|
||||
export * from './server/lib/errors/error-codes';
|
||||
|
||||
@@ -2,6 +2,7 @@ import { sha256 } from '@oslojs/crypto/sha2';
|
||||
import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding';
|
||||
import { type Session, type User, UserSecurityAuditLogType } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
@@ -129,18 +130,46 @@ export const validateSessionToken = async (token: string): Promise<SessionValida
|
||||
return { session, user, isAuthenticated: true };
|
||||
};
|
||||
|
||||
export const invalidateSession = async (
|
||||
sessionId: string,
|
||||
metadata: RequestMetadata,
|
||||
): Promise<void> => {
|
||||
const session = await prisma.session.delete({ where: { id: sessionId } });
|
||||
type InvalidateSessionsOptions = {
|
||||
userId: number;
|
||||
sessionIds: string[];
|
||||
metadata: RequestMetadata;
|
||||
isRevoke?: boolean;
|
||||
};
|
||||
|
||||
await prisma.userSecurityAuditLog.create({
|
||||
data: {
|
||||
userId: session.userId,
|
||||
ipAddress: metadata.ipAddress,
|
||||
userAgent: metadata.userAgent,
|
||||
type: UserSecurityAuditLogType.SIGN_OUT,
|
||||
},
|
||||
export const invalidateSessions = async ({
|
||||
userId,
|
||||
sessionIds,
|
||||
metadata,
|
||||
isRevoke,
|
||||
}: InvalidateSessionsOptions): Promise<void> => {
|
||||
if (sessionIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const { count } = await tx.session.deleteMany({
|
||||
where: {
|
||||
userId,
|
||||
id: { in: sessionIds },
|
||||
},
|
||||
});
|
||||
|
||||
if (count !== sessionIds.length) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'One or more sessions are not valid.',
|
||||
});
|
||||
}
|
||||
|
||||
await tx.userSecurityAuditLog.createMany({
|
||||
data: sessionIds.map(() => ({
|
||||
userId,
|
||||
ipAddress: metadata.ipAddress,
|
||||
userAgent: metadata.userAgent,
|
||||
type: isRevoke
|
||||
? UserSecurityAuditLogType.SESSION_REVOKED
|
||||
: UserSecurityAuditLogType.SIGN_OUT,
|
||||
})),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { Session } from '@prisma/client';
|
||||
import type { Context } from 'hono';
|
||||
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AuthenticationErrorCode } from '../errors/error-codes';
|
||||
import type { SessionValidationResult } from '../session/session';
|
||||
@@ -37,6 +39,33 @@ export const getOptionalSession = async (
|
||||
return await validateSessionToken(sessionId);
|
||||
};
|
||||
|
||||
export type ActiveSession = Omit<Session, 'sessionToken'>;
|
||||
|
||||
export const getActiveSessions = async (c: Context | Request): Promise<ActiveSession[]> => {
|
||||
const { user } = await getSession(c);
|
||||
|
||||
return await prisma.session.findMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
expiresAt: {
|
||||
gt: new Date(),
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: 'desc',
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
expiresAt: true,
|
||||
updatedAt: true,
|
||||
createdAt: true,
|
||||
ipAddress: true,
|
||||
userAgent: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Todo: (RR7) Rethink, this is pretty sketchy.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Hono } from 'hono';
|
||||
import superjson from 'superjson';
|
||||
|
||||
import type { SessionValidationResult } from '../lib/session/session';
|
||||
import { getOptionalSession } from '../lib/utils/get-session';
|
||||
import { getActiveSessions, getOptionalSession } from '../lib/utils/get-session';
|
||||
|
||||
export const sessionRoute = new Hono()
|
||||
.get('/session', async (c) => {
|
||||
@@ -10,6 +10,11 @@ export const sessionRoute = new Hono()
|
||||
|
||||
return c.json(session);
|
||||
})
|
||||
.get('/sessions', async (c) => {
|
||||
const sessions = await getActiveSessions(c);
|
||||
|
||||
return c.json(superjson.serialize({ sessions }));
|
||||
})
|
||||
.get('/session-json', async (c) => {
|
||||
const session: SessionValidationResult = await getOptionalSession(c);
|
||||
|
||||
|
||||
@@ -1,27 +1,114 @@
|
||||
import { sValidator } from '@hono/standard-validator';
|
||||
import { Hono } from 'hono';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { invalidateSession, validateSessionToken } from '../lib/session/session';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { invalidateSessions, validateSessionToken } from '../lib/session/session';
|
||||
import { deleteSessionCookie, getSessionCookie } from '../lib/session/session-cookies';
|
||||
import type { HonoAuthContext } from '../types/context';
|
||||
|
||||
export const signOutRoute = new Hono<HonoAuthContext>().post('/signout', async (c) => {
|
||||
const metadata = c.get('requestMetadata');
|
||||
|
||||
const sessionId = await getSessionCookie(c);
|
||||
|
||||
if (!sessionId) {
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
const { session } = await validateSessionToken(sessionId);
|
||||
|
||||
if (!session) {
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
await invalidateSession(session.id, metadata);
|
||||
|
||||
deleteSessionCookie(c);
|
||||
|
||||
return c.status(200);
|
||||
const ZSignoutSessionSchema = z.object({
|
||||
sessionId: z.string().trim().min(1),
|
||||
});
|
||||
|
||||
export const signOutRoute = new Hono<HonoAuthContext>()
|
||||
.post('/signout', async (c) => {
|
||||
const metadata = c.get('requestMetadata');
|
||||
|
||||
const sessionToken = await getSessionCookie(c);
|
||||
|
||||
if (!sessionToken) {
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
const { session } = await validateSessionToken(sessionToken);
|
||||
|
||||
if (!session) {
|
||||
deleteSessionCookie(c);
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
await invalidateSessions({
|
||||
userId: session.userId,
|
||||
sessionIds: [session.id],
|
||||
metadata,
|
||||
isRevoke: false,
|
||||
});
|
||||
|
||||
deleteSessionCookie(c);
|
||||
|
||||
return c.status(200);
|
||||
})
|
||||
.post('/signout-all', async (c) => {
|
||||
const metadata = c.get('requestMetadata');
|
||||
|
||||
const sessionToken = await getSessionCookie(c);
|
||||
|
||||
if (!sessionToken) {
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
const { session } = await validateSessionToken(sessionToken);
|
||||
|
||||
if (!session) {
|
||||
deleteSessionCookie(c);
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
const userId = session.userId;
|
||||
|
||||
const userSessionIds = await prisma.session
|
||||
.findMany({
|
||||
where: {
|
||||
userId,
|
||||
id: {
|
||||
not: session.id,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((sessions) => sessions.map((session) => session.id));
|
||||
|
||||
await invalidateSessions({
|
||||
userId,
|
||||
sessionIds: userSessionIds,
|
||||
metadata,
|
||||
isRevoke: true,
|
||||
});
|
||||
|
||||
return c.status(200);
|
||||
})
|
||||
.post('/signout-session', sValidator('json', ZSignoutSessionSchema), async (c) => {
|
||||
const metadata = c.get('requestMetadata');
|
||||
|
||||
const { sessionId: sessionIdToRevoke } = c.req.valid('json');
|
||||
|
||||
const sessionToken = await getSessionCookie(c);
|
||||
|
||||
if (!sessionToken) {
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
const { session } = await validateSessionToken(sessionToken);
|
||||
|
||||
if (!session) {
|
||||
deleteSessionCookie(c);
|
||||
return new Response('No session found', { status: 401 });
|
||||
}
|
||||
|
||||
await invalidateSessions({
|
||||
userId: session.userId,
|
||||
sessionIds: [sessionIdToRevoke],
|
||||
metadata,
|
||||
isRevoke: true,
|
||||
});
|
||||
|
||||
if (session.id === sessionIdToRevoke) {
|
||||
deleteSessionCookie(c);
|
||||
}
|
||||
|
||||
return c.status(200);
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ export const getServerLimits = async ({
|
||||
};
|
||||
}
|
||||
|
||||
// If plan expired.
|
||||
// Early return for users with an expired subscription.
|
||||
if (subscription && subscription.status !== SubscriptionStatus.ACTIVE) {
|
||||
return {
|
||||
quota: INACTIVE_PLAN_LIMITS,
|
||||
@@ -74,52 +74,46 @@ export const getServerLimits = async ({
|
||||
};
|
||||
}
|
||||
|
||||
if (subscription && organisation.organisationClaim.flags.unlimitedDocuments) {
|
||||
// Allow unlimited documents for users with an unlimited documents claim.
|
||||
// This also allows "free" claim users without subscriptions if they have this flag.
|
||||
if (organisation.organisationClaim.flags.unlimitedDocuments) {
|
||||
return {
|
||||
quota: PAID_PLAN_LIMITS,
|
||||
remaining: PAID_PLAN_LIMITS,
|
||||
};
|
||||
}
|
||||
|
||||
// If free tier or plan does not have unlimited documents.
|
||||
if (!subscription || !organisation.organisationClaim.flags.unlimitedDocuments) {
|
||||
const [documents, directTemplates] = await Promise.all([
|
||||
prisma.document.count({
|
||||
where: {
|
||||
team: {
|
||||
organisationId: organisation.id,
|
||||
},
|
||||
createdAt: {
|
||||
gte: DateTime.utc().startOf('month').toJSDate(),
|
||||
},
|
||||
source: {
|
||||
not: DocumentSource.TEMPLATE_DIRECT_LINK,
|
||||
},
|
||||
const [documents, directTemplates] = await Promise.all([
|
||||
prisma.document.count({
|
||||
where: {
|
||||
team: {
|
||||
organisationId: organisation.id,
|
||||
},
|
||||
}),
|
||||
prisma.template.count({
|
||||
where: {
|
||||
team: {
|
||||
organisationId: organisation.id,
|
||||
},
|
||||
directLink: {
|
||||
isNot: null,
|
||||
},
|
||||
createdAt: {
|
||||
gte: DateTime.utc().startOf('month').toJSDate(),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
source: {
|
||||
not: DocumentSource.TEMPLATE_DIRECT_LINK,
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.template.count({
|
||||
where: {
|
||||
team: {
|
||||
organisationId: organisation.id,
|
||||
},
|
||||
directLink: {
|
||||
isNot: null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
remaining.documents = Math.max(remaining.documents - documents, 0);
|
||||
remaining.directTemplates = Math.max(remaining.directTemplates - directTemplates, 0);
|
||||
|
||||
return {
|
||||
quota,
|
||||
remaining,
|
||||
};
|
||||
}
|
||||
remaining.documents = Math.max(remaining.documents - documents, 0);
|
||||
remaining.directTemplates = Math.max(remaining.directTemplates - directTemplates, 0);
|
||||
|
||||
return {
|
||||
quota: PAID_PLAN_LIMITS,
|
||||
remaining: PAID_PLAN_LIMITS,
|
||||
quota,
|
||||
remaining,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -31,6 +31,7 @@ export const USER_SECURITY_AUDIT_LOG_MAP: Record<string, string> = {
|
||||
PASSKEY_UPDATED: 'Passkey updated',
|
||||
PASSWORD_RESET: 'Password reset',
|
||||
PASSWORD_UPDATE: 'Password updated',
|
||||
SESSION_REVOKED: 'Session revoked',
|
||||
SIGN_OUT: 'Signed Out',
|
||||
SIGN_IN: 'Signed In',
|
||||
SIGN_IN_FAIL: 'Sign in attempt failed',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
@@ -83,9 +83,46 @@ export const getDocumentWhereInput = async ({
|
||||
}: GetDocumentWhereInputOptions) => {
|
||||
const team = await getTeamById({ teamId, userId });
|
||||
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
|
||||
const teamVisibilityFilters = match(team.currentTeamRole)
|
||||
.with(TeamMemberRole.ADMIN, () => [
|
||||
DocumentVisibility.EVERYONE,
|
||||
DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
DocumentVisibility.ADMIN,
|
||||
])
|
||||
.with(TeamMemberRole.MANAGER, () => [
|
||||
DocumentVisibility.EVERYONE,
|
||||
DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
])
|
||||
.otherwise(() => [DocumentVisibility.EVERYONE]);
|
||||
|
||||
const documentOrInput: Prisma.DocumentWhereInput[] = [
|
||||
// Allow access if they own the document.
|
||||
{
|
||||
teamId: team.id,
|
||||
userId,
|
||||
},
|
||||
// Or, if they belong to the team that the document is associated with.
|
||||
{
|
||||
visibility: {
|
||||
in: teamVisibilityFilters,
|
||||
},
|
||||
teamId,
|
||||
},
|
||||
// Or, if they are a recipient of the document.
|
||||
{
|
||||
status: {
|
||||
not: DocumentStatus.DRAFT,
|
||||
},
|
||||
recipients: {
|
||||
some: {
|
||||
email: user.email,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -112,45 +149,8 @@ export const getDocumentWhereInput = async ({
|
||||
OR: documentOrInput,
|
||||
};
|
||||
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
|
||||
const visibilityFilters = [
|
||||
...match(team.currentTeamRole)
|
||||
.with(TeamMemberRole.ADMIN, () => [
|
||||
{ visibility: DocumentVisibility.EVERYONE },
|
||||
{ visibility: DocumentVisibility.MANAGER_AND_ABOVE },
|
||||
{ visibility: DocumentVisibility.ADMIN },
|
||||
])
|
||||
.with(TeamMemberRole.MANAGER, () => [
|
||||
{ visibility: DocumentVisibility.EVERYONE },
|
||||
{ visibility: DocumentVisibility.MANAGER_AND_ABOVE },
|
||||
])
|
||||
.otherwise(() => [{ visibility: DocumentVisibility.EVERYONE }]),
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
recipients: {
|
||||
some: {
|
||||
email: user.email,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
userId: user.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
documentWhereInput: {
|
||||
...documentWhereInput,
|
||||
OR: [...visibilityFilters],
|
||||
},
|
||||
documentWhereInput,
|
||||
team,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type GetApiTokenByIdOptions = {
|
||||
id: number;
|
||||
userId: number;
|
||||
};
|
||||
|
||||
export const getApiTokenById = async ({ id, userId }: GetApiTokenByIdOptions) => {
|
||||
return await prisma.apiToken.findFirstOrThrow({
|
||||
where: {
|
||||
id,
|
||||
userId,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -11,7 +11,6 @@ export type GetApiTokensOptions = {
|
||||
export const getApiTokens = async ({ userId, teamId }: GetApiTokensOptions) => {
|
||||
return await prisma.apiToken.findMany({
|
||||
where: {
|
||||
userId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
|
||||
@@ -752,6 +752,11 @@ msgstr "Aktionen"
|
||||
msgid "Active"
|
||||
msgstr "Aktiv"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "Aktive Abonnements"
|
||||
@@ -817,13 +822,13 @@ msgstr "Felder hinzufügen"
|
||||
msgid "Add group roles"
|
||||
msgstr "Gruppenrollen hinzufügen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "Gruppen hinzufügen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "Mitglieder hinzufügen"
|
||||
|
||||
@@ -1614,6 +1619,7 @@ msgstr "Kann vorbereiten"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -2221,6 +2227,7 @@ msgstr "Erstellen Sie Ihr Konto und beginnen Sie mit dem modernen Dokumentensign
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "Erstellen Sie Ihr Konto und beginnen Sie mit dem modernen Dokumentensignieren. Offenes und schönes Signieren liegt in Ihrer Reichweite."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2260,6 +2267,10 @@ msgstr "Erstellt am {0}"
|
||||
msgid "CSV Structure"
|
||||
msgstr "CSV-Struktur"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "Aktuelles Passwort"
|
||||
@@ -2453,6 +2464,7 @@ msgid "Details"
|
||||
msgstr "Einzelheiten"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "Gerät"
|
||||
@@ -3179,6 +3191,7 @@ msgstr "Unternehmen"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3221,6 +3234,7 @@ msgstr "Unternehmen"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3286,10 +3300,18 @@ msgstr "Fehler beim Laden des Dokuments"
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "Dokument konnte nicht erneut versiegelt werden"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Einstellungen konnten nicht gespeichert werden."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "Dokument konnte nicht aktualisiert werden"
|
||||
@@ -3747,7 +3769,6 @@ msgstr "Ungültiger Code. Bitte versuchen Sie es erneut."
|
||||
msgid "Invalid email"
|
||||
msgstr "Ungültige E-Mail"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "Ungültiger Link"
|
||||
@@ -3811,6 +3832,7 @@ msgid "Invoice"
|
||||
msgstr "Rechnung"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "IP-Adresse"
|
||||
@@ -3881,6 +3903,10 @@ msgstr "Die letzten 30 Tage"
|
||||
msgid "Last 7 days"
|
||||
msgstr "Die letzten 7 Tage"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4030,6 +4056,10 @@ msgstr "Passkeys verwalten"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "Verwalten Sie Berechtigungen und Zugangskontrollen"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -5305,7 +5335,6 @@ msgstr "Aufbewahrung von Dokumenten"
|
||||
msgid "Retry"
|
||||
msgstr "Wiederholen"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5321,6 +5350,7 @@ msgstr "Zurück zur Startseite"
|
||||
msgid "Return to sign in"
|
||||
msgstr "Zurück zur Anmeldung"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "Zugriff widerrufen"
|
||||
@@ -5329,6 +5359,12 @@ msgstr "Zugriff widerrufen"
|
||||
msgid "Revoke access"
|
||||
msgstr "Zugriff widerrufen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5481,6 +5517,10 @@ msgstr "Standardoption auswählen"
|
||||
msgid "Select groups"
|
||||
msgstr "Gruppen auswählen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "Wählen Sie Gruppen aus, die diesem Team hinzugefügt werden sollen"
|
||||
@@ -5492,7 +5532,6 @@ msgid "Select members"
|
||||
msgstr "Mitglieder auswählen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "Wählen Sie Mitglieder oder Gruppen von Mitgliedern, die dem Team hinzugefügt werden sollen."
|
||||
|
||||
@@ -5602,6 +5641,14 @@ msgstr "Senden..."
|
||||
msgid "Sent"
|
||||
msgstr "Gesendet"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "Ein Passwort festlegen"
|
||||
@@ -6476,7 +6523,8 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "Das folgende Team wurde gelöscht. Sie können nicht mehr auf dieses Team und seine Dokumente zugreifen."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Die Organisationsgruppe, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
@@ -6485,12 +6533,14 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "Die Organisationsrolle, die auf alle Mitglieder in dieser Gruppe angewendet wird."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Die Organisation, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Die Organisation, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
@@ -6574,12 +6624,14 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "Die Team-E-Mail <0>{teamEmail}</0> wurde aus dem folgenden Team entfernt"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Das Team, das Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Das Team, das Sie suchen, könnte entfernt, umbenannt oder nie existiert haben."
|
||||
|
||||
@@ -6617,7 +6669,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "Die URL für Documenso, um Webhook-Ereignisse zu senden."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Der Benutzer, nach dem Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
@@ -6784,10 +6837,6 @@ msgstr "So wird das Dokument die Empfänger erreichen, sobald es zum Unterschrei
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "Dies ist der Anspruch, mit dem diese Organisation ursprünglich erstellt wurde. Alle Änderungen an Funktionsflags für diesen Anspruch werden in diese Organisation übernommen."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "Dieser Link ist ungültig oder abgelaufen."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "Dieser Link ist ungültig oder abgelaufen. Bitte kontaktieren Sie Ihr Team, um eine neue Bestätigungsanfrage zu senden."
|
||||
@@ -6858,6 +6907,10 @@ msgstr "Dies wird an den Dokumenteneigentümer gesendet, sobald das Dokument vol
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "Diese werden NUR Funktionsflags zurückspielen, die auf wahr gesetzt sind; alles, was im ursprünglichen Anspruch deaktiviert ist, wird nicht zurückportiert."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7144,6 +7197,9 @@ msgstr "Unvollendet"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
@@ -7497,6 +7553,11 @@ msgstr "Alle verwandten Dokumente anzeigen"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "Sehen Sie sich alle Sicherheitsaktivitäten in Ihrem Konto an."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "Codes ansehen"
|
||||
@@ -8563,4 +8624,3 @@ msgstr "Ihr Token wurde erfolgreich erstellt! Stellen Sie sicher, dass Sie es ko
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "Ihre Tokens werden hier angezeigt, sobald Sie sie erstellt haben."
|
||||
|
||||
|
||||
@@ -747,6 +747,11 @@ msgstr "Actions"
|
||||
msgid "Active"
|
||||
msgstr "Active"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr "Active sessions"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "Active Subscriptions"
|
||||
@@ -812,13 +817,13 @@ msgstr "Add Fields"
|
||||
msgid "Add group roles"
|
||||
msgstr "Add group roles"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "Add groups"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "Add members"
|
||||
|
||||
@@ -1609,6 +1614,7 @@ msgstr "Can prepare"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -2216,6 +2222,7 @@ msgstr "Create your account and start using state-of-the-art document signing."
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2255,6 +2262,10 @@ msgstr "Created on {0}"
|
||||
msgid "CSV Structure"
|
||||
msgstr "CSV Structure"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr "Current"
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "Current Password"
|
||||
@@ -2448,6 +2459,7 @@ msgid "Details"
|
||||
msgstr "Details"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "Device"
|
||||
@@ -3174,6 +3186,7 @@ msgstr "Enterprise"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3216,6 +3229,7 @@ msgstr "Enterprise"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3281,10 +3295,18 @@ msgstr "Failed to load document"
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "Failed to reseal document"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr "Failed to revoke session"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Failed to save settings."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr "Failed to sign out all sessions"
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "Failed to update document"
|
||||
@@ -3742,7 +3764,6 @@ msgstr "Invalid code. Please try again."
|
||||
msgid "Invalid email"
|
||||
msgstr "Invalid email"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "Invalid link"
|
||||
@@ -3806,6 +3827,7 @@ msgid "Invoice"
|
||||
msgstr "Invoice"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "IP Address"
|
||||
@@ -3876,6 +3898,10 @@ msgstr "Last 30 days"
|
||||
msgid "Last 7 days"
|
||||
msgstr "Last 7 days"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "Last Active"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4025,6 +4051,10 @@ msgstr "Manage passkeys"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "Manage permissions and access controls"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr "Manage sessions"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -5300,7 +5330,6 @@ msgstr "Retention of Documents"
|
||||
msgid "Retry"
|
||||
msgstr "Retry"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5316,6 +5345,7 @@ msgstr "Return to Home"
|
||||
msgid "Return to sign in"
|
||||
msgstr "Return to sign in"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "Revoke"
|
||||
@@ -5324,6 +5354,12 @@ msgstr "Revoke"
|
||||
msgid "Revoke access"
|
||||
msgstr "Revoke access"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr "Revoke all sessions"
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5476,6 +5512,10 @@ msgstr "Select default option"
|
||||
msgid "Select groups"
|
||||
msgstr "Select groups"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr "Select groups of members to add to the team."
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "Select groups to add to this team"
|
||||
@@ -5487,7 +5527,6 @@ msgid "Select members"
|
||||
msgstr "Select members"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "Select members or groups of members to add to the team."
|
||||
|
||||
@@ -5597,6 +5636,14 @@ msgstr "Sending..."
|
||||
msgid "Sent"
|
||||
msgstr "Sent"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr "Session revoked"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr "Sessions have been revoked"
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "Set a password"
|
||||
@@ -6797,10 +6844,6 @@ msgstr "This is how the document will reach the recipients once the document is
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "This link is invalid or has expired."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
@@ -6871,6 +6914,10 @@ msgstr "This will be sent to the document owner once the document has been fully
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7157,6 +7204,9 @@ msgstr "Uncompleted"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "Unknown"
|
||||
|
||||
@@ -7510,6 +7560,11 @@ msgstr "View all related documents"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "View all security activity related to your account."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr "View and manage all active sessions for your account."
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "View Codes"
|
||||
|
||||
@@ -752,6 +752,11 @@ msgstr "Acciones"
|
||||
msgid "Active"
|
||||
msgstr "Activo"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "Suscripciones Activas"
|
||||
@@ -817,13 +822,13 @@ msgstr "Agregar Campos"
|
||||
msgid "Add group roles"
|
||||
msgstr "Agregar roles de grupo"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "Agregar grupos"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "Agregar miembros"
|
||||
|
||||
@@ -1614,6 +1619,7 @@ msgstr "Puede preparar"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -2221,6 +2227,7 @@ msgstr "Crea tu cuenta y comienza a utilizar la firma de documentos de última g
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "Crea tu cuenta y comienza a utilizar la firma de documentos de última generación. La firma abierta y hermosa está al alcance de tu mano."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2260,6 +2267,10 @@ msgstr "Creado el {0}"
|
||||
msgid "CSV Structure"
|
||||
msgstr "Estructura CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "Contraseña actual"
|
||||
@@ -2453,6 +2464,7 @@ msgid "Details"
|
||||
msgstr "Detalles"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "Dispositivo"
|
||||
@@ -3179,6 +3191,7 @@ msgstr "Enterprise"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3221,6 +3234,7 @@ msgstr "Enterprise"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3286,10 +3300,18 @@ msgstr "Error al cargar el documento"
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "Falló al volver a sellar el documento"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Fallo al guardar configuraciones."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "No se pudo actualizar el documento"
|
||||
@@ -3747,7 +3769,6 @@ msgstr "Código inválido. Por favor, intenta nuevamente."
|
||||
msgid "Invalid email"
|
||||
msgstr "Email inválido"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "Enlace inválido"
|
||||
@@ -3811,6 +3832,7 @@ msgid "Invoice"
|
||||
msgstr "Factura"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "Dirección IP"
|
||||
@@ -3881,6 +3903,10 @@ msgstr "Últimos 30 días"
|
||||
msgid "Last 7 days"
|
||||
msgstr "Últimos 7 días"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4030,6 +4056,10 @@ msgstr "Gestionar claves de acceso"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "Gestiona permisos y controles de acceso"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -5305,7 +5335,6 @@ msgstr "Retención de Documentos"
|
||||
msgid "Retry"
|
||||
msgstr "Reintentar"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5321,6 +5350,7 @@ msgstr "Regresar a la página de inicio"
|
||||
msgid "Return to sign in"
|
||||
msgstr "Regresar para iniciar sesión"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "Revocar"
|
||||
@@ -5329,6 +5359,12 @@ msgstr "Revocar"
|
||||
msgid "Revoke access"
|
||||
msgstr "Revocar acceso"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5481,6 +5517,10 @@ msgstr "Seleccionar opción predeterminada"
|
||||
msgid "Select groups"
|
||||
msgstr "Seleccionar grupos"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "Seleccionar grupos para añadir a este equipo"
|
||||
@@ -5492,7 +5532,6 @@ msgid "Select members"
|
||||
msgstr "Seleccione miembros"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "Seleccione miembros o grupos de miembros para agregar al equipo."
|
||||
|
||||
@@ -5602,6 +5641,14 @@ msgstr "Enviando..."
|
||||
msgid "Sent"
|
||||
msgstr "Enviado"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "Establecer una contraseña"
|
||||
@@ -6476,7 +6523,8 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "El siguiente equipo ha sido eliminado. Ya no podrá acceder a este equipo y sus documentos"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El grupo de organización que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
|
||||
|
||||
@@ -6485,12 +6533,14 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "El rol de organización que se aplicará a todos los miembros de este grupo."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "La organización que está buscando puede haber sido eliminada, renombrada o puede que nunca haya existido."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "La organización que está buscando puede haber sido eliminada, renombrada o puede que nunca haya existido."
|
||||
|
||||
@@ -6574,14 +6624,17 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "El correo electrónico del equipo <0>{teamEmail}</0> ha sido eliminado del siguiente equipo"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El equipo que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El equipo que buscas puede haber sido eliminado, renombrado o quizás nunca\n"
|
||||
msgstr ""
|
||||
"El equipo que buscas puede haber sido eliminado, renombrado o quizás nunca\n"
|
||||
" existió."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
@@ -6618,7 +6671,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "La URL para Documenso para enviar eventos de webhook."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El usuario que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
|
||||
|
||||
@@ -6785,10 +6839,6 @@ msgstr "Así es como el documento llegará a los destinatarios una vez que esté
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "Esta es la reclamo con la que se creó inicialmente esta organización. Cualquier cambio en la bandera de características de esta reclamo se retroalimentará en esta organización."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "Este enlace no es válido o ha expirado."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "Este enlace es inválido o ha expirado. Por favor, contacta a tu equipo para reenviar una verificación."
|
||||
@@ -6859,6 +6909,10 @@ msgstr "Esto se enviará al propietario del documento una vez que el documento s
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "Esto solo retroalimentará las banderas de características que estén configuradas como verdaderas, cualquier cosa desactivada en la reclamo inicial no será retroalimentada"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7145,6 +7199,9 @@ msgstr "Incompleto"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "Desconocido"
|
||||
|
||||
@@ -7498,6 +7555,11 @@ msgstr "Ver todos los documentos relacionados"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "Ver toda la actividad de seguridad relacionada con tu cuenta."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "Ver Códigos"
|
||||
@@ -8564,4 +8626,3 @@ msgstr "¡Tu token se creó con éxito! ¡Asegúrate de copiarlo porque no podr
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "Tus tokens se mostrarán aquí una vez que los crees."
|
||||
|
||||
|
||||
@@ -752,6 +752,11 @@ msgstr "Actions"
|
||||
msgid "Active"
|
||||
msgstr "Actif"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "Abonnements actifs"
|
||||
@@ -817,13 +822,13 @@ msgstr "Ajouter des champs"
|
||||
msgid "Add group roles"
|
||||
msgstr "Ajouter des rôles de groupe"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "Ajouter des groupes"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "Ajouter des membres"
|
||||
|
||||
@@ -1614,6 +1619,7 @@ msgstr "Peut préparer"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -2221,6 +2227,7 @@ msgstr "Créez votre compte et commencez à utiliser la signature de documents
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "Créez votre compte et commencez à utiliser la signature de documents à la pointe de la technologie. Une signature ouverte et magnifique est à votre portée."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2260,6 +2267,10 @@ msgstr "Créé le {0}"
|
||||
msgid "CSV Structure"
|
||||
msgstr "Structure CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "Mot de passe actuel"
|
||||
@@ -2453,6 +2464,7 @@ msgid "Details"
|
||||
msgstr "Détails"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "Appareil"
|
||||
@@ -3179,6 +3191,7 @@ msgstr "Entreprise"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3221,6 +3234,7 @@ msgstr "Entreprise"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3286,10 +3300,18 @@ msgstr "Échec du chargement du document"
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "Échec du reseal du document"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Échec de l'enregistrement des paramètres."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "Échec de la mise à jour du document"
|
||||
@@ -3747,7 +3769,6 @@ msgstr "Code invalide. Veuillez réessayer."
|
||||
msgid "Invalid email"
|
||||
msgstr "Email invalide"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "Lien invalide"
|
||||
@@ -3811,6 +3832,7 @@ msgid "Invoice"
|
||||
msgstr "Facture"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "Adresse IP"
|
||||
@@ -3881,6 +3903,10 @@ msgstr "30 derniers jours"
|
||||
msgid "Last 7 days"
|
||||
msgstr "7 derniers jours"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4030,6 +4056,10 @@ msgstr "Gérer les clés d'accès"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "Gérez les autorisations et les contrôles d'accès"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -5305,7 +5335,6 @@ msgstr "Conservation des documents"
|
||||
msgid "Retry"
|
||||
msgstr "Réessayer"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5321,6 +5350,7 @@ msgstr "Retour à l'accueil"
|
||||
msgid "Return to sign in"
|
||||
msgstr "Retour à la connexion"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "Révoquer"
|
||||
@@ -5329,6 +5359,12 @@ msgstr "Révoquer"
|
||||
msgid "Revoke access"
|
||||
msgstr "Révoquer l'accès"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5481,6 +5517,10 @@ msgstr "Sélectionner l'option par défaut"
|
||||
msgid "Select groups"
|
||||
msgstr "Sélectionnez des groupes"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "Sélectionnez des groupes à ajouter à cette équipe"
|
||||
@@ -5492,7 +5532,6 @@ msgid "Select members"
|
||||
msgstr "Sélectionnez des membres"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "Sélectionnez des membres ou groupes de membres à ajouter à l'équipe."
|
||||
|
||||
@@ -5602,6 +5641,14 @@ msgstr "Envoi..."
|
||||
msgid "Sent"
|
||||
msgstr "Envoyé"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "Définir un mot de passe"
|
||||
@@ -6476,7 +6523,8 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "L'équipe suivante a été supprimée. Vous ne pourrez plus accéder à cette équipe et à ses documents"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Le groupe d'organisation que vous cherchez peut avoir été supprimé, renommé ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6485,12 +6533,14 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "Le rôle d'organisation qui sera appliqué à tous les membres de ce groupe."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organisation que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organisation que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6574,12 +6624,14 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "L'email d'équipe <0>{teamEmail}</0> a été supprimé de l'équipe suivante"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'équipe que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'équipe que vous cherchez a peut-être été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6617,7 +6669,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "L'URL pour Documenso pour envoyer des événements webhook."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'utilisateur que vous cherchez peut avoir été supprimé, renommé ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6784,10 +6837,6 @@ msgstr "Voici comment le document atteindra les destinataires une fois qu'il ser
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "Il s'agit de la réclamation avec laquelle cette organisation a initialement été créée. Tout changement de drapeau de fonctionnalité à cette revendication sera rétroporté dans cette organisation."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "Ce lien est invalide ou a expiré."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "Ce lien est invalide ou a expiré. Veuillez contacter votre équipe pour renvoyer une vérification."
|
||||
@@ -6858,6 +6907,10 @@ msgstr "Cela sera envoyé au propriétaire du document une fois que le document
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "Cela ne fera que rétroporter les drapeaux de fonctionnalité qui sont activés, tout ce qui est désactivé dans la réclamation initiale ne sera pas rétroporté"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7144,6 +7197,9 @@ msgstr "Non complet"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "Inconnu"
|
||||
|
||||
@@ -7497,6 +7553,11 @@ msgstr "Voir tous les documents associés"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "Voir toute l'activité de sécurité liée à votre compte."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "Voir les codes"
|
||||
@@ -8563,4 +8624,3 @@ msgstr "Votre token a été créé avec succès ! Assurez-vous de le copier car
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "Vos tokens seront affichés ici une fois que vous les aurez créés."
|
||||
|
||||
|
||||
@@ -752,6 +752,11 @@ msgstr "Azioni"
|
||||
msgid "Active"
|
||||
msgstr "Attivo"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "Abbonamenti attivi"
|
||||
@@ -817,13 +822,13 @@ msgstr "Aggiungi campi"
|
||||
msgid "Add group roles"
|
||||
msgstr "Aggiungi ruoli di gruppo"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "Aggiungi gruppi"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "Aggiungi membri"
|
||||
|
||||
@@ -1614,6 +1619,7 @@ msgstr "Può preparare"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -2221,6 +2227,7 @@ msgstr "Crea il tuo account e inizia a utilizzare firme digitali all'avanguardia
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "Crea il tuo account e inizia a utilizzare firme digitali all'avanguardia. Una firma aperta e bella è a tua portata."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2260,6 +2267,10 @@ msgstr "Creato il {0}"
|
||||
msgid "CSV Structure"
|
||||
msgstr "Struttura CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "Password attuale"
|
||||
@@ -2453,6 +2464,7 @@ msgid "Details"
|
||||
msgstr "Dettagli"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "Dispositivo"
|
||||
@@ -3179,6 +3191,7 @@ msgstr "Impresa"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3221,6 +3234,7 @@ msgstr "Impresa"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3286,10 +3300,18 @@ msgstr "Caricamento documento fallito."
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "Fallito il risigillo del documento"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Impossibile salvare le impostazioni."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "Aggiornamento documento fallito"
|
||||
@@ -3747,7 +3769,6 @@ msgstr "Codice non valido. Riprova."
|
||||
msgid "Invalid email"
|
||||
msgstr "Email non valida"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "Link non valido"
|
||||
@@ -3811,6 +3832,7 @@ msgid "Invoice"
|
||||
msgstr "Fattura"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "Indirizzo IP"
|
||||
@@ -3881,6 +3903,10 @@ msgstr "Ultimi 30 giorni"
|
||||
msgid "Last 7 days"
|
||||
msgstr "Ultimi 7 giorni"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4030,6 +4056,10 @@ msgstr "Gestisci chiavi di accesso"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "Gestisci le autorizzazioni e i controlli di accesso"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -5305,7 +5335,6 @@ msgstr "Conservazione dei documenti"
|
||||
msgid "Retry"
|
||||
msgstr "Riprova"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5321,6 +5350,7 @@ msgstr "Torna alla home"
|
||||
msgid "Return to sign in"
|
||||
msgstr "Torna a accedere"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "Revoca"
|
||||
@@ -5329,6 +5359,12 @@ msgstr "Revoca"
|
||||
msgid "Revoke access"
|
||||
msgstr "Revoca l'accesso"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5481,6 +5517,10 @@ msgstr "Seleziona opzione predefinita"
|
||||
msgid "Select groups"
|
||||
msgstr "Seleziona gruppi"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "Seleziona i gruppi da aggiungere a questo team"
|
||||
@@ -5492,7 +5532,6 @@ msgid "Select members"
|
||||
msgstr "Seleziona membri"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "Seleziona membri o gruppi di membri da aggiungere al team."
|
||||
|
||||
@@ -5602,6 +5641,14 @@ msgstr "Invio..."
|
||||
msgid "Sent"
|
||||
msgstr "Inviato"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "Imposta una password"
|
||||
@@ -6476,9 +6523,11 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "Il seguente team è stato eliminato. Non potrai più accedere a questo team e ai suoi documenti"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Il gruppo organizzativo che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"Il gruppo organizzativo che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
" esistito."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
@@ -6486,15 +6535,19 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "Il ruolo organizzativo che verrà applicato a tutti i membri in questo gruppo."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
" esistita."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
" esistita."
|
||||
|
||||
#: apps/remix/app/components/general/generic-error-layout.tsx
|
||||
@@ -6577,15 +6630,19 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "L'email del team <0>{teamEmail}</0> è stata rimossa dal seguente team"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Il team che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"Il team che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
" esistito."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "La squadra che stai cercando potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"La squadra che stai cercando potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
" esistita."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
@@ -6622,9 +6679,11 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "L'URL per Documenso per inviare eventi webhook."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'utente che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"L'utente che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
" esistito."
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
@@ -6790,10 +6849,6 @@ msgstr "È così che il documento raggiungerà i destinatari una volta pronto pe
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "Questo è il reclamo con cui questa organizzazione è stata inizialmente creata. Qualunque cambiamento agli indicatori delle funzionalità di questo reclamo sarà retroportato in questa organizzazione."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "Questo link non è valido o è scaduto."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "Questo link è invalido o è scaduto. Si prega di contattare il tuo team per inviare nuovamente una verifica."
|
||||
@@ -6864,6 +6919,10 @@ msgstr "Questo sarà inviato al proprietario del documento una volta che il docu
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "Questo farà SOLO il retroporting degli indicatori delle funzionalità impostati su vero, qualsiasi cosa disabilitata nel reclamo iniziale non sarà retroportata"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7150,6 +7209,9 @@ msgstr "Incompleto"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "Sconosciuto"
|
||||
|
||||
@@ -7503,6 +7565,11 @@ msgstr "Visualizza tutti i documenti correlati"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "Visualizza tutte le attività di sicurezza relative al tuo account."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "Visualizza Codici"
|
||||
@@ -8569,4 +8636,3 @@ msgstr "Il tuo token è stato creato con successo! Assicurati di copiarlo perch
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "I tuoi token verranno mostrati qui una volta creati."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: pl\n"
|
||||
"Project-Id-Version: documenso-app\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-06-10 02:27\n"
|
||||
"PO-Revision-Date: 2025-06-10 12:05\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Polish\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
@@ -752,6 +752,11 @@ msgstr "Akcje"
|
||||
msgid "Active"
|
||||
msgstr "Aktywne"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "Aktywne subskrypcje"
|
||||
@@ -817,13 +822,13 @@ msgstr "Dodaj pola"
|
||||
msgid "Add group roles"
|
||||
msgstr "Dodaj role grupowe"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "Dodaj grupy"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "Dodaj członków"
|
||||
|
||||
@@ -1614,6 +1619,7 @@ msgstr "Może przygotować"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -1795,7 +1801,7 @@ msgstr "Porównaj szczegóły wszystkich planów i funkcji"
|
||||
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
|
||||
#: apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx
|
||||
msgid "Complete"
|
||||
msgstr "Zakończono"
|
||||
msgstr "Zakończ"
|
||||
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
|
||||
msgid "Complete Approval"
|
||||
@@ -2221,6 +2227,7 @@ msgstr "Utwórz swoje konto i zacznij korzystać z nowoczesnego podpisywania dok
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "Utwórz swoje konto i zacznij korzystać z nowoczesnego podpisywania dokumentów. Otwarty i piękny podpis jest w zasięgu ręki."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2260,6 +2267,10 @@ msgstr "Utworzono {0}"
|
||||
msgid "CSV Structure"
|
||||
msgstr "Struktura CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "Obecne hasło"
|
||||
@@ -2453,6 +2464,7 @@ msgid "Details"
|
||||
msgstr "Szczegóły"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "Urządzenie"
|
||||
@@ -3179,6 +3191,7 @@ msgstr "Enterprise"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3221,6 +3234,7 @@ msgstr "Enterprise"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3286,10 +3300,18 @@ msgstr "Nie udało się załadować dokumentu"
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "Nie udało się ponownie zaplombować dokumentu"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Nie udało się zapisać ustawień."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "Nie udało się aktualizować dokumentu"
|
||||
@@ -3747,7 +3769,6 @@ msgstr "Nieprawidłowy kod. Proszę spróbuj ponownie."
|
||||
msgid "Invalid email"
|
||||
msgstr "Nieprawidłowy email"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "Nieprawidłowy link"
|
||||
@@ -3811,6 +3832,7 @@ msgid "Invoice"
|
||||
msgstr "Faktura"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "Adres IP"
|
||||
@@ -3881,6 +3903,10 @@ msgstr "Ostatnie 30 dni"
|
||||
msgid "Last 7 days"
|
||||
msgstr "Ostatnie 7 dni"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4030,6 +4056,10 @@ msgstr "Zarządzaj kluczami dostępu"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "Zarządzaj uprawnieniami i kontrolą dostępu"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -5305,7 +5335,6 @@ msgstr "Przechowywanie dokumentów"
|
||||
msgid "Retry"
|
||||
msgstr "Spróbuj ponownie"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5321,6 +5350,7 @@ msgstr "Powrót do strony głównej"
|
||||
msgid "Return to sign in"
|
||||
msgstr "Powrót do logowania"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "Cofnij"
|
||||
@@ -5329,6 +5359,12 @@ msgstr "Cofnij"
|
||||
msgid "Revoke access"
|
||||
msgstr "Cofnij dostęp"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5481,6 +5517,10 @@ msgstr "Wybierz domyślną opcję"
|
||||
msgid "Select groups"
|
||||
msgstr "Wybierz grupy"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "Wybierz grupy, które chcesz dodać do tego zespołu"
|
||||
@@ -5492,7 +5532,6 @@ msgid "Select members"
|
||||
msgstr "Wybierz członków"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "Wybierz członków lub grupy członków, aby dodać do zespołu."
|
||||
|
||||
@@ -5602,6 +5641,14 @@ msgstr "Wysyłanie..."
|
||||
msgid "Sent"
|
||||
msgstr "Wysłano"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "Ustaw hasło"
|
||||
@@ -6476,7 +6523,8 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "Poniższy zespół został usunięty. Nie będziesz mógł już uzyskać dostępu do tego zespołu i jego dokumentów."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Grupa organizacji, której szukasz, mogła zostać usunięta, zmieniona nazwa lub mogła nigdy nie istnieć."
|
||||
|
||||
@@ -6485,12 +6533,14 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "Rola organizacji, która zostanie zastosowana do wszystkich członków tej grupy."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Organizacja, której szukasz, mogła zostać usunięta, zmieniona nazwa lub mogła nigdy nie istnieć."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Organizacja, której szukasz, mogła zostać usunięta, zmieniona nazwa lub mogła nigdy nie istnieć."
|
||||
|
||||
@@ -6574,12 +6624,14 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "Email zespołowy <0>{teamEmail}</0> został usunięty z następującego zespołu"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Zespół, którego szukasz, mógł zostać usunięty, zmienić nazwę lub mógł nigdy nie istnieć."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Zespół, którego szukasz, mógł zostać usunięty, zmieniony na, albo nigdy nie istniał."
|
||||
|
||||
@@ -6617,7 +6669,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "URL dla Documenso do wysyłania zdarzeń webhook."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Użytkownik, którego szukasz, mógł zostać usunięty, zmieniony nazwę lub mógł nigdy nie istnieć."
|
||||
|
||||
@@ -6784,10 +6837,6 @@ msgstr "W ten sposób dokument dotrze do odbiorców, gdy tylko dokument będzie
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "To jest roszczenie, z którym pierwotnie została utworzona ta organizacja. Wszelkie zmiany flag funkcji w tym roszczeniu zostaną przeniesione do tej organizacji."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "Ten link jest nieprawidłowy lub wygasł."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "Ten link jest nieprawidłowy lub wygasł. Proszę skontaktować się ze swoim zespołem, aby ponownie wysłać weryfikację."
|
||||
@@ -6858,6 +6907,10 @@ msgstr "To zostanie wysłane do właściciela dokumentu, gdy dokument zostanie w
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "To będzie TYLKO przenoś funkcje flag, które są ustawione na true, wszystko, co wyłączone w początkowym roszczeniu, nie zostanie przeniesione"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7144,6 +7197,9 @@ msgstr "Niezakończony"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "Nieznany"
|
||||
|
||||
@@ -7497,6 +7553,11 @@ msgstr "Zobacz wszystkie powiązane dokumenty"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "Wyświetl wszystkie aktywności związane z bezpieczeństwem twojego konta."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "Wyświetl kody"
|
||||
@@ -8563,4 +8624,3 @@ msgstr "Twój token został pomyślnie utworzony! Upewnij się, że go skopiujes
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "Twoje tokeny będą tutaj wyświetlane po ich utworzeniu."
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "UserSecurityAuditLogType" ADD VALUE 'SESSION_REVOKED';
|
||||
@@ -97,6 +97,7 @@ enum UserSecurityAuditLogType {
|
||||
PASSKEY_UPDATED
|
||||
PASSWORD_RESET
|
||||
PASSWORD_UPDATE
|
||||
SESSION_REVOKED
|
||||
SIGN_OUT
|
||||
SIGN_IN
|
||||
SIGN_IN_FAIL
|
||||
|
||||
@@ -1,31 +1,15 @@
|
||||
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
||||
import { deleteTokenById } from '@documenso/lib/server-only/public-api/delete-api-token-by-id';
|
||||
import { getApiTokenById } from '@documenso/lib/server-only/public-api/get-api-token-by-id';
|
||||
import { getApiTokens } from '@documenso/lib/server-only/public-api/get-api-tokens';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
ZCreateTokenMutationSchema,
|
||||
ZDeleteTokenByIdMutationSchema,
|
||||
ZGetApiTokenByIdQuerySchema,
|
||||
} from './schema';
|
||||
import { ZCreateTokenMutationSchema, ZDeleteTokenByIdMutationSchema } from './schema';
|
||||
|
||||
export const apiTokenRouter = router({
|
||||
getTokens: authenticatedProcedure.query(async ({ ctx }) => {
|
||||
return await getApiTokens({ userId: ctx.user.id, teamId: ctx.teamId });
|
||||
}),
|
||||
|
||||
getTokenById: authenticatedProcedure
|
||||
.input(ZGetApiTokenByIdQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { id } = input;
|
||||
|
||||
return await getApiTokenById({
|
||||
id,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
}),
|
||||
|
||||
createToken: authenticatedProcedure
|
||||
.input(ZCreateTokenMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZGetApiTokenByIdQuerySchema = z.object({
|
||||
id: z.number().min(1),
|
||||
});
|
||||
|
||||
export type TGetApiTokenByIdQuerySchema = z.infer<typeof ZGetApiTokenByIdQuerySchema>;
|
||||
|
||||
export const ZCreateTokenMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
tokenName: z.string().min(3, { message: 'The token name should be 3 characters or longer' }),
|
||||
|
||||
@@ -166,7 +166,6 @@ export const AddFieldsFormPartial = ({
|
||||
|
||||
const [selectedField, setSelectedField] = useState<FieldType | null>(null);
|
||||
const [selectedSigner, setSelectedSigner] = useState<Recipient | null>(null);
|
||||
const [showRecipientsSelector, setShowRecipientsSelector] = useState(false);
|
||||
const [lastActiveField, setLastActiveField] = useState<TAddFieldsFormSchema['fields'][0] | null>(
|
||||
null,
|
||||
);
|
||||
@@ -465,6 +464,7 @@ export const AddFieldsFormPartial = ({
|
||||
|
||||
append({
|
||||
...copiedField,
|
||||
nativeId: undefined,
|
||||
formId: nanoid(12),
|
||||
signerEmail: selectedSigner?.email ?? copiedField.signerEmail,
|
||||
pageX: copiedField.pageX + 3,
|
||||
@@ -661,6 +661,8 @@ export const AddFieldsFormPartial = ({
|
||||
passive={isFieldWithinBounds && !!selectedField}
|
||||
onFocus={() => setLastActiveField(field)}
|
||||
onBlur={() => setLastActiveField(null)}
|
||||
onMouseEnter={() => setLastActiveField(field)}
|
||||
onMouseLeave={() => setLastActiveField(null)}
|
||||
onResize={(options) => onFieldResize(options, index)}
|
||||
onMove={(options) => onFieldMove(options, index)}
|
||||
onRemove={() => remove(index)}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FieldType } from '@prisma/client';
|
||||
import { CopyPlus, Settings2, SquareStack, Trash } from 'lucide-react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Rnd } from 'react-rnd';
|
||||
import { useSearchParams } from 'react-router';
|
||||
|
||||
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
||||
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
@@ -35,6 +36,8 @@ export type FieldItemProps = {
|
||||
onAdvancedSettings?: () => void;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
onMouseEnter?: () => void;
|
||||
onMouseLeave?: () => void;
|
||||
recipientIndex?: number;
|
||||
hasErrors?: boolean;
|
||||
active?: boolean;
|
||||
@@ -69,6 +72,7 @@ export const FieldItem = ({
|
||||
onFieldDeactivate,
|
||||
}: FieldItemProps) => {
|
||||
const { _ } = useLingui();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const [coords, setCoords] = useState({
|
||||
pageX: 0,
|
||||
@@ -81,6 +85,8 @@ export const FieldItem = ({
|
||||
|
||||
const signerStyles = useRecipientColors(recipientIndex);
|
||||
|
||||
const isDevMode = searchParams.get('devmode') === 'true';
|
||||
|
||||
const advancedField = [
|
||||
'NUMBER',
|
||||
'RADIO',
|
||||
@@ -233,6 +239,8 @@ export const FieldItem = ({
|
||||
bounds={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`}
|
||||
onDragStart={() => onFieldActivate?.()}
|
||||
onResizeStart={() => onFieldActivate?.()}
|
||||
onMouseEnter={() => onFocus?.()}
|
||||
onMouseLeave={() => onBlur?.()}
|
||||
enableResizing={!fixedSize}
|
||||
resizeHandleStyles={{
|
||||
bottom: { bottom: -8, cursor: 'ns-resize' },
|
||||
@@ -303,6 +311,12 @@ export const FieldItem = ({
|
||||
(field.signerEmail?.charAt(1)?.toUpperCase() ?? '')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isDevMode && (
|
||||
<div className="text-muted-foreground absolute -top-6 left-0 right-0 text-center text-[10px]">
|
||||
{`x: ${field.pageX.toFixed(2)}, y: ${field.pageY.toFixed(2)}`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!disabled && settingsActive && (
|
||||
|
||||
@@ -209,6 +209,7 @@ export const AddTemplateFieldsFormPartial = ({
|
||||
append({
|
||||
...copiedField,
|
||||
formId: nanoid(12),
|
||||
nativeId: undefined,
|
||||
signerEmail: selectedSigner?.email ?? copiedField.signerEmail,
|
||||
signerId: selectedSigner?.id ?? copiedField.signerId,
|
||||
signerToken: selectedSigner?.token ?? copiedField.signerToken,
|
||||
|
||||